├── .classpath ├── .github └── FUNDING.yml ├── .project ├── AndroidManifest.xml ├── README.md ├── ic_launcher-web.png ├── proguard-project.txt ├── project.properties ├── res ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── drawable-xhdpi │ └── ic_launcher.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── layout │ └── activity_main.xml └── values │ └── strings.xml └── src ├── com ├── Cluster │ └── SpeechRecognizer │ │ └── SpeechRecognizer.java └── example │ └── speechtests │ └── MainActivity.java └── javaFlacEncoder ├── ArrayRecycler.java ├── BlockEncodeRequest.java ├── BlockThreadManager.java ├── COPYING ├── CRC16.java ├── CRC8.java ├── ChannelData.java ├── EncodedElement.java ├── EncodedElement_32.java ├── EncodingConfiguration.java ├── FLACEncoder.java ├── FLACFileOutputStream.java ├── FLACOutputStream.java ├── FLACStreamController.java ├── FLACStreamIdentifier.java ├── FLACStreamOutputStream.java ├── FLAC_MD5.java ├── Frame.java ├── FrameHeader.java ├── FrameThread.java ├── LPC.java ├── MetadataBlockHeader.java ├── MetadataBlockStreamInfo.java ├── README ├── RiceEncoder.java ├── StreamConfiguration.java ├── Subframe.java ├── Subframe_Constant.java ├── Subframe_Fixed.java ├── Subframe_LPC.java ├── Subframe_Verbatim.java ├── UTF8Modified.java └── package.html /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [ClusterM] 2 | custom: ["https://www.buymeacoffee.com/cluster", "https://boosty.to/cluster"] 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | SpeechTests 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | android-speech-recognition 2 | ========================== 3 | 4 | Continuous speech recognition for Android demo 5 | 6 | **WARNING**: You must insert your Google Speech API key into SpeechRecognizer.java. 7 | How to obtain key: [http://www.chromium.org/developers/how-tos/api-keys](http://www.chromium.org/developers/how-tos/api-keys) 8 | -------------------------------------------------------------------------------- /ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClusterM/android-speech-recognition/ff9256f48f67bac33438dbec4425558265f01e5a/ic_launcher-web.png -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-20 15 | -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClusterM/android-speech-recognition/ff9256f48f67bac33438dbec4425558265f01e5a/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClusterM/android-speech-recognition/ff9256f48f67bac33438dbec4425558265f01e5a/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClusterM/android-speech-recognition/ff9256f48f67bac33438dbec4425558265f01e5a/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClusterM/android-speech-recognition/ff9256f48f67bac33438dbec4425558265f01e5a/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Continuous voice recognition demo 5 | Tss! Keep quiet and wait few seconds... 6 | Speak to me! 7 | Settings 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/com/Cluster/SpeechRecognizer/SpeechRecognizer.java: -------------------------------------------------------------------------------- 1 | package com.Cluster.SpeechRecognizer; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.io.OutputStream; 8 | import java.net.URL; 9 | import java.net.URLConnection; 10 | import java.nio.charset.Charset; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | import javaFlacEncoder.FLACEncoder; 17 | import javaFlacEncoder.FLACStreamOutputStream; 18 | import javaFlacEncoder.StreamConfiguration; 19 | 20 | import android.media.AudioFormat; 21 | import android.media.AudioRecord; 22 | import android.media.MediaRecorder; 23 | import android.util.Log; 24 | 25 | public class SpeechRecognizer 26 | { 27 | final String API_KEY = "YOUR_API_KEY"; 28 | final String TAG = "SpeechRecognizer"; 29 | // Частота дискретизации 30 | final int SAMPLE_RATE = 8000; 31 | // Длительность анализируемых сэмплов 32 | final int TIMER_INTERVAL = 100; 33 | // Время, в течении которого анализируется громкость 34 | final int MAX_ANALYZE_LENGTH = 3000; 35 | // Соотношение громкости речи к громкости фона 36 | final float VOLUME_RATIO = 1.25f; 37 | // Максимальное кол-во ошибок, после которого увеличивается амплитуда 38 | // срабатывания 39 | final float MAX_ERROR_COUNT = 3; 40 | // Если в течении этого времени нет ошибок, сбрасываем счётчик ошибок 41 | final int MAX_NO_ERROR_TIME = 10000; 42 | 43 | // Слушатель 44 | VoiceRecognizedListener voiceRecognizedListener = null; 45 | // Какой процент значений громкости должен превышать уровень определения 46 | float detectRatio = 0.25f; 47 | // Сколько нужно молчать перед распознаванием фразы в миллисекундах 48 | int maxSilenceLength = 500; 49 | // Минимальная длина фразы в миллисекундах 50 | int minRecordLength = 500; 51 | // Максимальная длина фразы в миллисекундах 52 | int maxRecordLength = 5000; 53 | // Языка 54 | String language = "en-US"; 55 | // Максимальное кол-во результатов 56 | int maxResults = 5; 57 | 58 | // Громкость, с которой должен звучать голос 59 | int detectLevel = 32767; 60 | // Пишем ли звук в данных момент 61 | boolean recording = false; 62 | // Длина записи 63 | int recordLength = 0; 64 | // Длина тишины 65 | int silenceLength = 0; 66 | // Сколько звука мы проанализировали на громкость 67 | int analyzeLength = 0; 68 | // Количество ошибок распознавания подряд 69 | int errorCount = 0; 70 | // Время, в течении которого нет ошибок 71 | int noErrorTimer = 0; 72 | // Максимальная громкость фона, когда человек молчит 73 | int maxSilenceLevel = 0; 74 | // Максимальная громкость 75 | int maxLevel = 0; 76 | 77 | AudioRecord aRecorder; 78 | byte[] buffer; 79 | ByteArrayOutputStream record = new ByteArrayOutputStream(); 80 | 81 | public float getDetectRatio() 82 | { 83 | return detectRatio; 84 | } 85 | 86 | public void setDetectRatio(float detectRatio) 87 | { 88 | this.detectRatio = detectRatio; 89 | } 90 | 91 | public int getMaxSilenceLength() 92 | { 93 | return maxSilenceLength; 94 | } 95 | 96 | public void setMaxSilenceLength(int maxSilenceLength) 97 | { 98 | this.maxSilenceLength = maxSilenceLength; 99 | } 100 | 101 | public int getMinRecordLength() 102 | { 103 | return minRecordLength; 104 | } 105 | 106 | public void setMinRecordLength(int minRecordLength) 107 | { 108 | this.minRecordLength = minRecordLength; 109 | } 110 | 111 | public int getMaxRecordLength() 112 | { 113 | return maxRecordLength; 114 | } 115 | 116 | public void setMaxRecordLength(int maxRecordLength) 117 | { 118 | this.maxRecordLength = maxRecordLength; 119 | } 120 | 121 | public String getLanguage() 122 | { 123 | return language; 124 | } 125 | 126 | public void setLanguage(String language) 127 | { 128 | this.language = language; 129 | } 130 | 131 | public int getMaxResults() 132 | { 133 | return maxResults; 134 | } 135 | 136 | public void setMaxResults(int maxResults) 137 | { 138 | this.maxResults = maxResults; 139 | } 140 | 141 | public void setVoiceRecognizedListener(VoiceRecognizedListener voiceRecognizedListener) 142 | { 143 | this.voiceRecognizedListener = voiceRecognizedListener; 144 | } 145 | 146 | public int getDetectLevel() 147 | { 148 | return detectLevel; 149 | } 150 | 151 | public void start() throws Exception 152 | { 153 | if (aRecorder != null) 154 | return; // Уже запущены 155 | // Параметры захвата 156 | int audioSource = MediaRecorder.AudioSource.MIC; 157 | final int channelConfig = AudioFormat.CHANNEL_IN_MONO; 158 | int nChannels = 1; 159 | int audioFormat = AudioFormat.ENCODING_PCM_16BIT; 160 | int bSamples = 16; 161 | int framePeriod = SAMPLE_RATE * TIMER_INTERVAL / 1000; 162 | int bufferSize = framePeriod * 2 * bSamples * nChannels / 8; 163 | if (bufferSize < AudioRecord.getMinBufferSize(SAMPLE_RATE, channelConfig, audioFormat)) 164 | { 165 | bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, channelConfig, audioFormat); 166 | Log.w(TAG, "Increasing buffer size to " + Integer.toString(bufferSize)); 167 | } 168 | 169 | // Запускаем захват аудио 170 | aRecorder = new AudioRecord(audioSource, SAMPLE_RATE, channelConfig, audioFormat, bufferSize); 171 | if (aRecorder.getState() != AudioRecord.STATE_INITIALIZED) 172 | { 173 | Log.e(TAG, "AudioRecord initialization failed"); 174 | aRecorder = null; 175 | throw new Exception("AudioRecord initialization failed"); 176 | } 177 | aRecorder.setRecordPositionUpdateListener(updateListener); 178 | aRecorder.setPositionNotificationPeriod(framePeriod); 179 | buffer = new byte[framePeriod * bSamples / 8 * nChannels]; 180 | aRecorder.startRecording(); 181 | // Нужно для некоторых старых версий Android 182 | aRecorder.read(buffer, 0, buffer.length); 183 | Log.i(TAG, "Started"); 184 | } 185 | 186 | public void stop() 187 | { 188 | if (aRecorder != null) 189 | { 190 | aRecorder.stop(); 191 | aRecorder = null; 192 | Log.i(TAG, "Stopped"); 193 | } 194 | } 195 | 196 | private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener() 197 | { 198 | @Override 199 | public void onPeriodicNotification(AudioRecord recorder) 200 | { 201 | if (aRecorder == null) 202 | return; // Stopped 203 | // Читаем данные 204 | int len = aRecorder.read(buffer, 0, buffer.length); 205 | int maxAmplitude = 0; 206 | int detects = 0; 207 | // Проходимся вдоль записанного сэмпла 208 | for (int p = 0; p < len - 1; p += 2) 209 | { 210 | // WTF. Конвертируем little-endian signed bytes в int 211 | int level = buffer[p + 1] * 256 + ((buffer[p] >= 0) ? buffer[p] : (256 + buffer[p])); 212 | int amplitude = Math.abs(level); 213 | if (amplitude > maxAmplitude) 214 | maxAmplitude = amplitude; 215 | if (amplitude > detectLevel) 216 | detects++; 217 | } 218 | 219 | // Анализ фоновой громоксти 220 | // Запоминаем максимальную громкость 221 | if (maxAmplitude > maxLevel) 222 | { 223 | maxLevel = maxAmplitude; 224 | } 225 | // Если достаточно тихо, то смотрим - не понизить ли громкость 226 | // срабатывания? 227 | if (detects == 0) 228 | { 229 | if (analyzeLength >= MAX_ANALYZE_LENGTH) 230 | { 231 | if (maxSilenceLevel * VOLUME_RATIO < detectLevel) 232 | { 233 | Log.i(TAG, "Decreasing detect level from " + detectLevel + " to " + maxSilenceLevel * VOLUME_RATIO); 234 | detectLevel = (int) (maxSilenceLevel * VOLUME_RATIO); 235 | } 236 | analyzeLength = 0; 237 | maxSilenceLevel = 0; 238 | } else 239 | { 240 | if (maxAmplitude > maxSilenceLevel) 241 | maxSilenceLevel = maxAmplitude; 242 | analyzeLength += TIMER_INTERVAL; 243 | } 244 | } 245 | 246 | // Log.d(TAG, "Data: " + len + ", max: " + maxAmplitude + 247 | // ", detects: " + detects); 248 | 249 | // Достигнуто ли нужное количество превышений амплитуды 250 | // срабатывания? 251 | boolean voiceDetected = (detects > len / 2 * detectRatio); 252 | 253 | if (!recording) // Если запись не идёт... 254 | { 255 | if (voiceDetected) // И мы обнаружили вспышку амплитуды 256 | { 257 | // Запускаем запись 258 | recording = true; 259 | recordLength = TIMER_INTERVAL; 260 | Log.d(TAG, "Voice record started"); 261 | } else 262 | { 263 | // Если запись не идёт, мы всегда держим в запасе один семпл 264 | record.reset(); 265 | record.write(buffer, 0, len); 266 | // Тишина, ошибок точно нет 267 | noErrorTimer++; 268 | if (noErrorTimer >= MAX_NO_ERROR_TIME) errorCount = 0; 269 | } 270 | } 271 | if (recording) // Если запись идёт (или началась только что) 272 | { 273 | // Пишем звук в буфер 274 | recordLength += TIMER_INTERVAL; 275 | record.write(buffer, 0, len); 276 | 277 | // Если в этот раз голос не обнаружили 278 | // Или если пишем его уже слишком долго 279 | if (!voiceDetected || (maxRecordLength < recordLength)) 280 | { 281 | // Считаем как долго 282 | silenceLength += TIMER_INTERVAL; 283 | if ((silenceLength >= maxSilenceLength) || (maxRecordLength < recordLength)) 284 | { 285 | // Пора прекражать запись 286 | recording = false; 287 | Log.d(TAG, "Voice record stopped, length: " + (recordLength - silenceLength)); 288 | if (recordLength - silenceLength >= minRecordLength) 289 | { 290 | try 291 | { 292 | new Thread(new ProceedRecordThread(record.toByteArray())).start(); 293 | } catch (Exception e) 294 | { 295 | e.printStackTrace(); 296 | } 297 | } 298 | } 299 | } else 300 | silenceLength = 0; // Не молчат, обнуляем счётчик тишины 301 | } 302 | } 303 | 304 | @Override 305 | public void onMarkerReached(AudioRecord recorder) 306 | { 307 | // Не нужно 308 | } 309 | }; 310 | 311 | class ProceedRecordThread implements Runnable 312 | { 313 | byte[] record; 314 | 315 | public ProceedRecordThread(byte[] record) 316 | { 317 | this.record = record; 318 | } 319 | 320 | public void run() 321 | { 322 | try 323 | { 324 | byte[] flac = flacEncode(record); 325 | String results[] = request(flac, 5); 326 | if ((voiceRecognizedListener != null) && (results.length > 0)) 327 | voiceRecognizedListener.onVoiceRecognized(results); 328 | } catch (Exception e) 329 | { 330 | e.printStackTrace(); 331 | } 332 | } 333 | } 334 | 335 | byte[] flacEncode(byte[] sampleData) throws IOException 336 | { 337 | Log.d(TAG, "Encoding..."); 338 | FLACEncoder flacEncoder = new FLACEncoder(); 339 | ByteArrayOutputStream flacData = new ByteArrayOutputStream(); 340 | FLACStreamOutputStream flacOutputStream = new FLACStreamOutputStream(flacData); 341 | // FLACFileOutputStream flacOutputStream = new 342 | // FLACFileOutputStream("/mnt/sdcard/test.flac"); 343 | StreamConfiguration streamConfiguration = new StreamConfiguration(); 344 | streamConfiguration.setSampleRate(SAMPLE_RATE); 345 | streamConfiguration.setBitsPerSample(16); 346 | streamConfiguration.setChannelCount(1); 347 | flacEncoder.setStreamConfiguration(streamConfiguration); 348 | flacEncoder.setOutputStream(flacOutputStream); 349 | flacEncoder.openFLACStream(); 350 | int[] sampleDataInt = new int[sampleData.length / 2]; 351 | for (int p = 0; p < sampleData.length - 1; p += 2) 352 | { 353 | // WTF, Java? Two little-endian signed bytes to int conversion 354 | sampleDataInt[p / 2] = sampleData[p + 1] * 256 + ((sampleData[p] >= 0) ? sampleData[p] : (256 + sampleData[p])); 355 | } 356 | flacEncoder.addSamples(sampleDataInt, sampleDataInt.length); 357 | flacEncoder.encodeSamples(sampleDataInt.length, false); 358 | flacEncoder.encodeSamples(flacEncoder.samplesAvailableToEncode(), true); 359 | flacOutputStream.close(); 360 | Log.d(TAG, "Encoded"); 361 | return flacData.toByteArray(); 362 | } 363 | 364 | public String[] request(byte[] flac, int maxResults) throws Exception 365 | { 366 | Log.d(TAG, "Requesting..."); 367 | final String googleurl = "https://www.google.com/speech-api/v2/recognize?output=json"; 368 | StringBuilder sb = new StringBuilder(googleurl); 369 | sb.append("&key=" + API_KEY); 370 | sb.append("&lang=" + language); 371 | sb.append("&maxresults=" + maxResults); 372 | sb.append("&pfilter=0"); // ;) 373 | 374 | URL url = new URL(sb.toString()); 375 | URLConnection urlCon = url.openConnection(); 376 | urlCon.setDoOutput(true); 377 | urlCon.setUseCaches(false); 378 | 379 | urlCon.setRequestProperty("Content-Type", "audio/x-flac; rate=" + SAMPLE_RATE); 380 | OutputStream outputStream = urlCon.getOutputStream(); 381 | outputStream.write(flac); 382 | outputStream.close(); 383 | BufferedReader br = new BufferedReader(new InputStreamReader(urlCon.getInputStream(), Charset.forName("UTF-8"))); 384 | 385 | StringBuilder result = new StringBuilder(); 386 | String line; 387 | while ((line = br.readLine()) != null) 388 | { 389 | result.append(line); 390 | } 391 | br.close(); 392 | // Log.d(TAG, "Response: " + result.toString()); 393 | Pattern regex = Pattern.compile("\"transcript\":\"(.+?)\""); 394 | Matcher m = regex.matcher(result.toString()); 395 | List results = new ArrayList(); 396 | while (m.find()) 397 | { 398 | String r = m.group(1); 399 | results.add(r); 400 | Log.d(TAG, "Result: " + r); 401 | } 402 | // Результат пустой, может слишком громко? 403 | if (results.isEmpty()) 404 | { 405 | Log.d(TAG, "Google can't understand you"); 406 | errorCount++; 407 | noErrorTimer = 0; 408 | if (errorCount >= MAX_ERROR_COUNT) // А бывает иначе? 409 | { 410 | if (maxLevel > detectLevel) 411 | { 412 | Log.i(TAG, "Increasing detect level from " + detectLevel + " to " + maxLevel); 413 | detectLevel = (int) (maxLevel); 414 | } 415 | errorCount = 0; 416 | maxLevel = 0; 417 | } 418 | } else 419 | { 420 | errorCount = 0; 421 | maxLevel = 0; 422 | } 423 | return (String[]) results.toArray(new String[results.size()]); 424 | } 425 | 426 | public interface VoiceRecognizedListener 427 | { 428 | void onVoiceRecognized(String[] results); 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /src/com/example/speechtests/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.speechtests; 2 | 3 | import com.Cluster.SpeechRecognizer.SpeechRecognizer; 4 | import com.Cluster.SpeechRecognizer.SpeechRecognizer.VoiceRecognizedListener; 5 | import com.example.voicetests.R; 6 | 7 | import android.app.Activity; 8 | import android.os.Bundle; 9 | import android.os.Handler; 10 | import android.os.Message; 11 | import android.widget.TextView; 12 | 13 | public class MainActivity extends Activity implements VoiceRecognizedListener 14 | { 15 | static SpeechRecognizer recognizer; 16 | boolean ready = false; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) 20 | { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main); 23 | if (recognizer == null) 24 | recognizer = new SpeechRecognizer(); 25 | recognizer.setLanguage("ru-RU"); 26 | recognizer.setVoiceRecognizedListener(this); 27 | try 28 | { 29 | recognizer.start(); 30 | } catch (Exception e) 31 | { 32 | e.printStackTrace(); 33 | } 34 | handlerShowDetectLevel.sendEmptyMessage(0); 35 | } 36 | 37 | @Override 38 | protected void onDestroy() 39 | { 40 | recognizer.stop(); 41 | recognizer = null; 42 | super.onDestroy(); 43 | } 44 | 45 | @Override 46 | public void onVoiceRecognized(String[] results) 47 | { 48 | Message msg = new Message(); 49 | msg.obj = results; 50 | handlerShowResults.sendMessage(msg); 51 | } 52 | 53 | Handler handlerShowResults = new Handler() 54 | { 55 | @Override 56 | public void handleMessage(Message msg) 57 | { 58 | StringBuilder out = new StringBuilder(); 59 | String[] results = (String[]) msg.obj; 60 | for (String result : results) 61 | { 62 | out.append(result); 63 | out.append("\r\n"); 64 | } 65 | ((TextView) findViewById(R.id.text)).setText(out.toString()); 66 | } 67 | }; 68 | 69 | Handler handlerShowDetectLevel = new Handler() 70 | { 71 | @Override 72 | public void handleMessage(Message msg) 73 | { 74 | if (recognizer != null) 75 | { 76 | int detectLevel = recognizer.getDetectLevel(); 77 | ((TextView) findViewById(R.id.detect_level)).setText("Current detect volume level: " + detectLevel); 78 | if (!ready && detectLevel < 32767) 79 | { 80 | ready = true; 81 | ((TextView) findViewById(R.id.text)).setText(R.string.speak); 82 | } 83 | } 84 | handlerShowDetectLevel.sendEmptyMessageDelayed(0, 500); 85 | } 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/ArrayRecycler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | import java.util.concurrent.LinkedBlockingQueue; 23 | 24 | /** 25 | * The purpose of this class is to provide a source for reusable int arrays. 26 | * When using large numbers of arrays in succession, it is inefficient to 27 | * constantly go in an allocate/free loop. This way, we may pass a single, 28 | * thread-safe recycler to all objects. No matter where the arrays end their 29 | * life, we can then add it to the same resource store. 30 | * 31 | * @author Preston Lacey 32 | */ 33 | public class ArrayRecycler { 34 | LinkedBlockingQueue usedIntArrays = null; 35 | 36 | ArrayRecycler() { 37 | usedIntArrays = new LinkedBlockingQueue(); 38 | } 39 | 40 | public void add(int[] array) { 41 | usedIntArrays.add(array); 42 | } 43 | 44 | /** 45 | * 46 | * @param size 47 | * @return 48 | */ 49 | public int[] getArray(int size) { 50 | int[] result = usedIntArrays.poll(); 51 | if(result == null) { 52 | result = new int[size]; 53 | } 54 | else if(result.length < size) { 55 | usedIntArrays.offer(result); 56 | result = new int[size]; 57 | } 58 | return result; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/BlockEncodeRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | /** 22 | * BlockEncodeRequests are used to store a full block and necessary information 23 | * to encode such block. It is assumed member variables will be accessed 24 | * directly(for speed considerations). This Class simply gathers all values 25 | * into a single object, but handles no encoding logic itself. 26 | * @author Preston Lacey 27 | */ 28 | public class BlockEncodeRequest { 29 | /* Sample data, may be interleaved if multiple channels exist.*/ 30 | volatile int[] samples; 31 | /* Number of valid samples in this request */ 32 | volatile int count; 33 | /* Index of samples[] where the first valid sample exists */ 34 | volatile int start; 35 | /* Number of indices to skip between valid samples */ 36 | volatile int skip; 37 | /* Frame-number this block is assigned */ 38 | volatile long frameNumber; 39 | /* Location to store results to. For safety, use an empty element*/ 40 | volatile EncodedElement result; 41 | /* Stores whether the result should be valid */ 42 | volatile boolean valid; 43 | /* Number of elements actually encoded. */ 44 | volatile int encodedSamples; 45 | 46 | /** 47 | * Set all values, preparing this object to be sent to an encoder. Member 48 | * variable "valid" is set to false by this call. 49 | * 50 | * @param samples Sample data, interleaved if multiple channels are used 51 | * @param count Number of valid samples 52 | * @param start Index of first valid sample 53 | * @param skip Number of samples to skip between samples(this should be 54 | * equal to number-of-channels minus 1. 55 | * @param frameNumber Framenumber assigned to this block. 56 | * @param result Location to store result of encode. 57 | */ 58 | public void setAll(int[] samples, int count, int start, int skip, 59 | long frameNumber, EncodedElement result) { 60 | // assert(start == 0); 61 | this.samples = samples; 62 | this.count = count; 63 | this.start = start; 64 | this.skip = skip; 65 | this.frameNumber = frameNumber; 66 | this.result = result; 67 | valid = false; 68 | this.encodedSamples = 0; 69 | } 70 | 71 | public int addInterleavedSamples(int[] newSamples, int offset, int addCount, int max) { 72 | //System.err.println("offset:addCount:max :: " +offset+":"+addCount+":"+max); 73 | assert(max <= this.samples.length/(this.skip+1)); 74 | if(max > this.samples.length) 75 | max = this.samples.length/(skip+1); 76 | 77 | int remaining = count; 78 | int spaceLeft = max - count; 79 | int toEncode = (addCount < spaceLeft) ? addCount:spaceLeft; 80 | remaining = addCount-toEncode; 81 | 82 | int[] src = newSamples; 83 | int[] dest = samples; 84 | int destPos = count*(skip+1); 85 | int srcPos = offset; 86 | int length = toEncode*(skip+1); 87 | System.arraycopy(src, srcPos, dest, destPos, length); 88 | 89 | this.count = count+toEncode; 90 | return remaining; 91 | } 92 | 93 | public boolean isFull(int max) { 94 | boolean full = false; 95 | if(this.count >= max) 96 | full = true; 97 | return full; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/BlockThreadManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | import java.util.concurrent.LinkedBlockingQueue; 22 | import java.util.Map; 23 | import java.util.HashMap; 24 | import java.util.Collections; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.Vector; 27 | 28 | /** 29 | * BlockThreadManager is used by FLACEncoder(when encoding with threads), to 30 | * dispatch BlockEncodeRequests to ThreadFrames which do the actual encode. 31 | * 32 | * 33 | * BlockThreadManager accepts BlockEncodeRequest objects to encode. 34 | * For each Frame object given to encode with, a Thread is supplied. This thread 35 | * will take a BlockEncodeRequest, encode it, then give it back to the BlockThreadManager 36 | * The thread will then take another BlockencodeRequest, and repeat. If no block 37 | * encode requests are available, it will block till available. 38 | * 39 | * 40 | * The main thread of the BlockThreadManager will be waiting for additions to 41 | * the finished queue. If the item is not the next in line to be written to file, 42 | * it will be saved temporarily. If it is the next item to be written, it will 43 | * be given back to the FLACEncoder to be written to output, and any saved objects 44 | * will be searched to see if more can be written. The main thread will then wait 45 | * for the next frame again. 46 | * 47 | * 48 | * 49 | * 50 | * @author Preston Lacey 51 | */ 52 | public class BlockThreadManager implements Runnable{ 53 | /* unassignedEncodeRequests: Requests waiting to be assigned a thread */ 54 | LinkedBlockingQueue unassignedEncodeRequests = null; 55 | 56 | /* requests that have just finished encoding, and are ready for writing. 57 | * Since run() polls against this for newly finished requests, requests 58 | * recieved out of order will be temporarily stored in finishedRequestStore 59 | */ 60 | LinkedBlockingQueue finishedEncodeRequests = null; 61 | 62 | /* pending requests in the order that we must pass them to the FLACEncoder. 63 | * The top object is moved to nextTarget until it is found in 64 | * finishedEncodeRequests and passed to the encoder. */ 65 | LinkedBlockingQueue orderedEncodeRequests = null; 66 | 67 | /* frameThreadMap: Keep track of which thread is handling which frame */ 68 | Map frameThreadMap = null; 69 | 70 | /* Thread which watches for finished encodes and alerts FLACEncoder of their 71 | * finished state. This thread will die when there is no data to encode, but 72 | * should remain valid and unchanged so long as blocks are encoding or 73 | * queued. It may therefore be used to monitor/interrupt, an encode process. 74 | */ 75 | volatile Thread managerThread = null; 76 | 77 | /* FLACEncoder object we will send finished requests to */ 78 | volatile FLACEncoder encoder = null; 79 | 80 | /* Must be false if we've been explicitly told to stop. Adding new requests 81 | * will reset this value to true */ 82 | volatile boolean process = true; 83 | 84 | /* FrameThreads that are not currently assigned to a Thread */ 85 | Vector inactiveFrameThreads = null; 86 | 87 | /* Lock ensures that only one FrameThread may get a new request at a time */ 88 | private final Object getLock = new Object(); 89 | 90 | /* Store finished requests that are not yet passed back to the FLACEncoder*/ 91 | Vector finishedRequestStore = null; 92 | 93 | /* blockWhileQueueExceeds() waits on this lock for changes to queue size */ 94 | private final Object outstandingCountLock = new Object(); 95 | 96 | /* Next request which must be found and returned to the FLACEncoder. */ 97 | volatile BlockEncodeRequest nextTarget = null; 98 | 99 | /* Number of requests added but not yet returned to FLACEncoder */ 100 | volatile int outstandingCount = 0; 101 | 102 | /** 103 | * Constructor. Must supply a valid FLACEncoder object which will be alerted 104 | * when a block is finished encoding. 105 | * @param encoder FLACEncoder to use in encoding process. 106 | */ 107 | public BlockThreadManager(FLACEncoder encoder) { 108 | this.encoder = encoder; 109 | unassignedEncodeRequests = new LinkedBlockingQueue(); 110 | finishedEncodeRequests = new LinkedBlockingQueue(); 111 | orderedEncodeRequests = new LinkedBlockingQueue(); 112 | frameThreadMap = Collections.synchronizedMap(new HashMap()); 113 | inactiveFrameThreads = new Vector(); 114 | finishedRequestStore = new Vector(); 115 | managerThread = null; 116 | } 117 | 118 | /** 119 | * Get total number of BlockEncodeRequests added to this manager, but not 120 | * yet passed back to the FLACEncoder object. 121 | * @return number of BlockEncodeRequests remaining in this manager. 122 | */ 123 | synchronized public int getTotalManagedCount() { 124 | return outstandingCount; 125 | } 126 | 127 | /** 128 | * This function is used to help control flow of BlockEncodeRequests into 129 | * this manager. It will block so long as their is at least as many 130 | * unprocessed blocks waiting to be encoded as the value given. 131 | * 132 | * @param count Maximum number of outstanding requests that may exist before 133 | * this method may return. 134 | */ 135 | public void blockWhileQueueExceeds(int count) { 136 | boolean loop = true; 137 | boolean interrupted = false; 138 | try { 139 | do { 140 | synchronized(outstandingCountLock) { 141 | if(outstandingCount > count) { 142 | try { 143 | outstandingCountLock.wait(); 144 | }catch(InterruptedException e) { 145 | //ignore interruption, loop again. 146 | interrupted = true; 147 | } 148 | } 149 | else 150 | loop = false; 151 | } 152 | }while(loop); 153 | }finally { 154 | if(interrupted) 155 | Thread.currentThread().interrupt(); 156 | } 157 | } 158 | 159 | /** 160 | * Add a Frame to this manager, which it will use to encode a block. Each 161 | * Frame added allows one more thread to be used for encoding. At least one 162 | * Frame must be added for this manager to encode. 163 | * @param frame Frame to use for encoding. 164 | * @return boolean false if there was an error adding the frame, true 165 | * otherwise. 166 | */ 167 | synchronized public boolean addFrameThread(Frame frame) { 168 | FrameThread ft = new FrameThread(frame, this); 169 | inactiveFrameThreads.add(ft); 170 | boolean r = true; 171 | startFrameThreads(); 172 | return r; 173 | } 174 | 175 | /** 176 | * Start any available FrameThread objects encoding, so long as there are 177 | * waiting BlockEncodeRequest objects. 178 | * 179 | */ 180 | synchronized private void startFrameThreads() { 181 | if(!process) 182 | return; 183 | int requests = unassignedEncodeRequests.size(); 184 | int frames = inactiveFrameThreads.size(); 185 | frames = (requests <= frames) ? requests:frames; 186 | for(int i = 0; i < frames; i++) { 187 | FrameThread ft = inactiveFrameThreads.remove(0); 188 | Thread thread = new Thread(ft); 189 | frameThreadMap.put(ft, thread); 190 | thread.start(); 191 | } 192 | } 193 | 194 | /** 195 | * Notify this manager that a FrameThread has ended it's run() method, 196 | * returning the FrameThread object to the manager for use in future Threads. 197 | * 198 | * @param ft FrameThread object which is ending. 199 | */ 200 | synchronized public void notifyFrameThreadExit(FrameThread ft) { 201 | frameThreadMap.remove(ft); 202 | inactiveFrameThreads.add(ft); 203 | startFrameThreads(); 204 | } 205 | 206 | /** 207 | * Get a BlockEncodeRequest object which is queued for encoding, pausing for 208 | * up to 0.5 seconds till one is available. It is expected that this object 209 | * will later(after encoding to a FLAC frame) be returned to this manager 210 | * through the returnFinishedRequest method. Failure to return the finished 211 | * object will cause the encoding process to hang. 212 | * 213 | * @return BlockEncodingRequest to encode, null if none available. 214 | */ 215 | public BlockEncodeRequest getWaitingRequest() { 216 | BlockEncodeRequest result = null; 217 | synchronized(getLock) { 218 | boolean loop = true; 219 | try { 220 | while(loop) { 221 | result = unassignedEncodeRequests.poll(500, TimeUnit.MILLISECONDS); 222 | if(result != null) { 223 | synchronized(outstandingCountLock) { 224 | outstandingCountLock.notifyAll(); 225 | } 226 | orderedEncodeRequests.add(result); 227 | } 228 | loop = false; 229 | } 230 | } catch(InterruptedException e) { 231 | Thread.currentThread().interrupt(); 232 | } 233 | } 234 | return result; 235 | } 236 | 237 | /** 238 | * Notify this manager that it may stop as soon as all currently outstanding 239 | * requests are completed. Future calls to addRequest() will clear this stop 240 | * state. 241 | */ 242 | synchronized public void stop() { 243 | process = false; 244 | BlockEncodeRequest temp = new BlockEncodeRequest(); 245 | temp.setAll(null, -1, -1, -1, -1, null); 246 | int count = frameThreadMap.size(); 247 | for(int i = 0; i < count; i++) { 248 | unassignedEncodeRequests.add(temp); 249 | } 250 | } 251 | 252 | /** 253 | * Used to return a finished BlockEncodeRequest from a FrameThread. This 254 | * must only be called with a finished request, which was originally added 255 | * to this manager through the addRequest() method. 256 | * 257 | * @param ber finished BlockEncodeRequest that needs passed back to the 258 | * FLACEncoder object. 259 | * 260 | */ 261 | synchronized public void returnFinishedRequest(BlockEncodeRequest ber) { 262 | try { 263 | finishedEncodeRequests.put(ber); 264 | restartManager(); 265 | }catch(InterruptedException e) { 266 | returnFinishedRequest(ber); 267 | Thread.currentThread().interrupt(); 268 | } 269 | } 270 | 271 | /** 272 | * Waits for the next BlockEncodeRequest that needs to be sent back to the 273 | * FLACEncoder for finalizing. If no request is finished, or currently 274 | * assigned to an encoding thread, will timeout after 0.5 seconds and end. 275 | */ 276 | public void run () { 277 | //wait for finished item 278 | //send finished item to encoder 279 | //loop to top 280 | boolean loop = true; 281 | boolean interrupted = false; 282 | try { 283 | while(loop) { 284 | try { 285 | if(nextTarget == null) 286 | nextTarget = orderedEncodeRequests.poll(500,TimeUnit.MILLISECONDS); 287 | if(nextTarget == null) { 288 | loop = false; 289 | } 290 | else if(nextTarget.frameNumber < 0) { 291 | loop = false; 292 | nextTarget = null; 293 | orderedEncodeRequests.clear(); 294 | } 295 | else if(finishedRequestStore.remove(nextTarget)) { 296 | encoder.blockFinished(nextTarget); 297 | nextTarget = null; 298 | synchronized(outstandingCountLock) { 299 | outstandingCount--; 300 | outstandingCountLock.notifyAll(); 301 | } 302 | } 303 | else { 304 | BlockEncodeRequest ber = finishedEncodeRequests.poll(500, TimeUnit.MILLISECONDS); 305 | if(ber == null) {//nothing to process yet, let this thread end. 306 | loop = false; 307 | } 308 | else if(nextTarget == ber) { 309 | encoder.blockFinished(ber); 310 | nextTarget = null; 311 | synchronized(outstandingCountLock) { 312 | outstandingCount--; 313 | outstandingCountLock.notifyAll(); 314 | } 315 | } 316 | else { 317 | finishedRequestStore.add(ber); 318 | } 319 | } 320 | }catch(InterruptedException e) { 321 | interrupted = true; 322 | } 323 | } 324 | }finally { 325 | if(interrupted) 326 | Thread.currentThread().interrupt(); 327 | } 328 | synchronized(this) { 329 | managerThread = null; 330 | restartManager(); 331 | } 332 | } 333 | 334 | /** 335 | * Attempt to restart the managerThread if possible/needed. This is the only 336 | * function that should *ever* call managerThread.start(). We should call 337 | * this at any time a managerThread may be needed but not started. For 338 | * example, after returning a BlockEncodeRequest from an encoding thread. 339 | */ 340 | synchronized private void restartManager() { 341 | if(managerThread == null && orderedEncodeRequests.size() > 0 ) { 342 | managerThread = new Thread(this); 343 | managerThread.start(); 344 | } 345 | } 346 | 347 | /** 348 | * Add a BlockEncodeRequest to the manager. This will immediately attempt 349 | * to assign a request to an encoding thread(which may not occur if no 350 | * threads are currently available). Requests are passed back to the 351 | * currently set FLACEncoder object when finished and ready to be written 352 | * to output. 353 | * 354 | * @param ber Block request to encode 355 | * @return boolean true if block added, false if an error occured. 356 | */ 357 | synchronized public boolean addRequest(BlockEncodeRequest ber) { 358 | //add request to the manager(requests are automatically removed when complete) 359 | process = true; 360 | boolean r = true; 361 | try { 362 | unassignedEncodeRequests.put(ber); 363 | synchronized(outstandingCountLock) { 364 | outstandingCount++; 365 | } 366 | startFrameThreads(); 367 | }catch(InterruptedException e) { 368 | r = false; 369 | Thread.currentThread().interrupt(); 370 | } 371 | return r; 372 | } 373 | } -------------------------------------------------------------------------------- /src/javaFlacEncoder/CRC16.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * Class to calculate a CRC16 checksum. 24 | * @author Preston Lacey 25 | */ 26 | public class CRC16 { 27 | /** For Debugging: Higher level equals more debug statements */ 28 | public static int DEBUG_LEV = 0; 29 | 30 | /** CRC Divisor: 0x8005(implicit 1 at MSB for 0x18005) */ 31 | static final int divisorCRC16 = 0x8005; 32 | 33 | /** working checksum stored between calls to update(..) */ 34 | protected int workingCRC; 35 | 36 | private static final short xorTable[] = generateTable(); 37 | 38 | /** 39 | * Constructor. Creates a CRC16 object that is ready to be used. Next step 40 | * would be to call update(...) with appropriate data. 41 | */ 42 | public CRC16() { 43 | reset(); 44 | } 45 | 46 | /** 47 | * Resets stored data, preparing object for a new checksum. 48 | */ 49 | public void reset() { 50 | workingCRC = 0; 51 | } 52 | 53 | public short checksum() { 54 | return (short)(workingCRC & 0xFFFF); 55 | } 56 | 57 | public int update(byte input) { 58 | workingCRC = (workingCRC << 8)^xorTable[((workingCRC >>> 8)^input) & 0xFF]; 59 | return workingCRC; 60 | } 61 | 62 | public int update(byte[] input, int start, int stop) { 63 | for(int i = start; i < stop; i++) { 64 | byte b = input[i]; 65 | workingCRC = (workingCRC << 8) ^ xorTable[((workingCRC >>> 8)^b) & 0xFF]; 66 | } 67 | return workingCRC; 68 | } 69 | 70 | private static short[] generateTable() { 71 | short[] table = new short[256]; 72 | for(int i = 0; i < table.length; i++) { 73 | int polynomial = divisorCRC16; 74 | int xorVal = i << 8; 75 | int topmask = 1 << 16; 76 | for(int x = 0; x < 8; x++ ) { 77 | xorVal = xorVal << 1; 78 | if( (xorVal & topmask) > 0) xorVal = (xorVal) ^ polynomial; 79 | } 80 | table[i] = (short)(xorVal & 0xFFFF); 81 | } 82 | return table; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/CRC8.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | 23 | /** 24 | * Class to calculate a CRC8 checksum. 25 | * @author Preston Lacey 26 | */ 27 | public class CRC8 { 28 | 29 | /** For Debugging: Higher level equals more debug statements */ 30 | public static int DEBUG_LEV = 0; 31 | 32 | /** CRC Divisor: 0x107 */ 33 | static final int divisorCRC8 = 0x107 << 23; 34 | 35 | /** working checksum stored between calls to update(..) */ 36 | int workingCRC8; 37 | 38 | /** number of valid bits stored in workingCRC8(valid bits are packed towards 39 | * high-order side of int */ 40 | int workingCRC8Count; 41 | 42 | private static final byte[] fake = {0}; 43 | 44 | /** 45 | * Constructor. Creates a CRC8 object that is ready to be used. Next step 46 | * would be to call updateCRC8 with appropriate data. 47 | */ 48 | public CRC8() { 49 | reset(); 50 | } 51 | 52 | /** 53 | * Add data to the crc. Immediately creates checksum on given data. Can be 54 | * called multiple times as it won't finalize the checksum until the method 55 | * checksum() is called. 56 | * 57 | * @param inSet Array holding data to checksum. 58 | * @param start Index of array holding first element 59 | * @param end Index to stop at. Last index used will be end-1. 60 | * @return intermediate result of checksum to this point. This is *not* a 61 | * finalized result, as non-summed data must remain in workingCRC8 until 62 | * checksum() is called. 63 | */ 64 | public byte updateCRC8(byte[] inSet, int start, int end) { 65 | //we need at least one byte to work on. And cache between calls. 66 | // Follow md5 style, updating many times, finalizing once. 67 | 68 | //Copy in saved value(starts out zero). 69 | //Shift value, and divisor to top end of int. When we drop below 17 in 70 | // working int, "OR" it with another byte, or save and return. 71 | // I won't do this, just "assume" it's already there and use instance 72 | // variables. 73 | //While bits available is more than 8: 74 | //while top bit is zero, and >8 bits in buffer. Shift Left. 75 | //if >8 bits remain in working int, divide 76 | //else if 8 bits remain, "OR" in another byte. 77 | //store working int, return; 78 | if(DEBUG_LEV > 10 ) 79 | System.err.println("CRC8::updateCRC8: Begin"); 80 | if(DEBUG_LEV > 20) { 81 | System.err.println("Start:End : "+start+":"+end); 82 | } 83 | int current = start; 84 | int topBit = 0; 85 | int topMask = 1<<31; 86 | while(current < end) {//this should leave 8 bits in workingCRC8. 87 | if(DEBUG_LEV > 40 ) { 88 | System.err.println("CRC8::updateCRC8: looping bytes. current: "+current); 89 | System.err.println("workingCRC8Count : " +workingCRC8Count); 90 | } 91 | topBit = workingCRC8 & topMask; 92 | while(workingCRC8Count > 8 && topBit == 0) { 93 | if(DEBUG_LEV > 40) 94 | System.err.println("CRC8::updateCRC8: shifting left"); 95 | workingCRC8Count--; 96 | workingCRC8 = workingCRC8 << 1; 97 | topBit = workingCRC8 & topMask; 98 | } 99 | if( workingCRC8Count > 8 ) { 100 | workingCRC8 = workingCRC8 ^ divisorCRC8; 101 | } 102 | else {//workingCRC8Count < 9 103 | if(DEBUG_LEV > 30) { 104 | System.err.println("CRC8: Adding byte with workingCRC of: "+ 105 | (workingCRC8 >>> 24)); 106 | } 107 | int temp = inSet[current++]; 108 | temp = temp << 24; 109 | temp = temp >>> 8; 110 | workingCRC8 = workingCRC8 | temp; 111 | workingCRC8Count+=8; 112 | } 113 | } 114 | return (byte)(workingCRC8 >>> 24); 115 | } 116 | 117 | /** 118 | * Finalize the checksum, and return the value. After this is called, you 119 | * must call reset() before attempting a new checksum. 120 | * @return finalized checksum. 121 | */ 122 | public byte checksum() { 123 | if(DEBUG_LEV > 10 ) 124 | System.err.println("CRC8::checksum : Begin"); 125 | //add 8 to the count, 126 | //call update with a byte constructed to add no more. 127 | //byte[] fake = {0}; 128 | workingCRC8Count += 8; 129 | byte val = updateCRC8(fake, 0, 1); 130 | if(DEBUG_LEV > 10 ) 131 | System.err.println("CRC8::checksum : End"); 132 | return val; 133 | } 134 | 135 | /** 136 | * Resets all stored data, preparing object for a new checksum. 137 | */ 138 | public void reset() { 139 | workingCRC8 = 0; 140 | workingCRC8Count = 8; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/ChannelData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package javaFlacEncoder; 7 | 8 | /** 9 | * 10 | * @author preston 11 | */ 12 | public class ChannelData { 13 | public enum ChannelName { 14 | LEFT, 15 | RIGHT, 16 | MID, 17 | SIDE, 18 | INDEPENDENT 19 | } 20 | private int[] samples = null; 21 | private int count; 22 | private int sampleSize; 23 | private ChannelName name; 24 | 25 | public ChannelData(int[] samples, int count, int sampleSize, ChannelName n) { 26 | this.count = count; 27 | this.samples = samples; 28 | this.sampleSize = sampleSize; 29 | this.name = n; 30 | } 31 | 32 | public int[] getSamples() { return samples; } 33 | public int getCount() { return count; } 34 | public int getSampleSize() { return sampleSize; } 35 | public ChannelName getChannelName() { return name; } 36 | 37 | public int setData(int[] newSamples, int count, int sampleSize, ChannelName n) { 38 | samples = newSamples; 39 | this.sampleSize = sampleSize; 40 | this.name = n; 41 | return setCount(count); 42 | } 43 | 44 | public int setCount(int count) { 45 | this.count = (count <= samples.length) ? count:samples.length; 46 | return this.count; 47 | } 48 | 49 | public void setChannelName(ChannelName cn) { 50 | this.name = cn; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/EncodedElement_32.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * EncodedElement which uses an integer array as a backing, rather than the 24 | * byte array. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | @Deprecated 29 | public class EncodedElement_32 extends EncodedElement { 30 | int[] data_32 = null; 31 | boolean byteArrayValid = false; 32 | public EncodedElement_32() { 33 | offset = 0; 34 | usableBits = 0; 35 | data = null; 36 | data_32 = new int[100]; 37 | } 38 | 39 | public EncodedElement_32(int size, int off) { 40 | data = null; 41 | usableBits = off; 42 | offset = off; 43 | data_32 = new int[size]; 44 | } 45 | 46 | @Override 47 | public void clear(int size, int off) { 48 | next = null; 49 | previous = null; 50 | data = null; 51 | data_32 = new int[size]; 52 | offset = off; 53 | usableBits = off; 54 | byteArrayValid = false; 55 | } 56 | 57 | 58 | /** 59 | * This method adds a given number of bits of an int to a byte array. 60 | * @param value int to store bits from 61 | * @param count number of low-order bits to store 62 | * @param startPos start bit location in array to begin writing 63 | * @param dest array to store bits in. dest MUST have enough space to store 64 | * the given data, or this function will fail. 65 | */ 66 | public static void addInt(int value, int count, int startPos, int[] dest) { 67 | assert(count <= 32); 68 | assert(count > 0); 69 | assert(startPos >= 0); 70 | //System.err.println("addInt("+value+", "+count + ", "+startPos+")"); 71 | /* 72 | * Because we're using both 32 bit input and 32 bit output, we only have 73 | * two cases to handle: 74 | * 1) All bits fit in one index, appropriately shifted up first. 75 | * Mask upper bits we'll merge with(Ones in upper). 76 | * Mask lower bits we'll merge with(Ones in lower). 77 | * destMask = OR Upper and lower masks together. 78 | * currentIndex = currentIndex & destMask 79 | * destMask = ~destMask; 80 | * upshift by 32-count-currentOffset; 81 | * inputValue = inputValue & destMask; 82 | * currentIndex = currentIndex | inputValue 83 | * 2) Some bits merge in top of one index, remaining bits merge in 84 | * bottom of second index 85 | * A) CurrentIndex 86 | * Create inputValueHigh, with high order bits which will enter 87 | * currentIndex. 88 | * Handle this as we handled case one(pretend it's a new value) 89 | * B) CurrentIndex++ 90 | * We fill from upper edge. No upper mask needed. 91 | * upshift value by 32-count; 92 | * Mask lower bits we'll merge with(Ones in lower) 93 | * currentIndex = currentIndex & destMask 94 | * currentIndex = currentIndex | destMask 95 | */ 96 | int currentIndex = startPos/32; 97 | int currentOffset = startPos%32; 98 | 99 | int totalSize = count+currentOffset; 100 | 101 | if(totalSize > 32) { 102 | //System.err.println("totalSize > 32"); 103 | int secondIndex = currentIndex + 1; 104 | int secondSize = totalSize - 32; 105 | int secondValue = value << (32-secondSize); 106 | int lowerMask = -1 >>> secondSize; 107 | int temp = dest[secondIndex] & lowerMask; 108 | dest[secondIndex] = temp | secondValue; 109 | totalSize = 32; 110 | value = value >>> secondSize; 111 | } 112 | 113 | if(totalSize <= 32) {//Case 1 114 | int upperMask = -2 << (31-currentOffset); 115 | //int lowerMask = Integer.MAX_VALUE >>> (totalSize-1); 116 | int lowerMask = 0x7FFFFFFF >>> (totalSize-1); 117 | int destMask = upperMask | lowerMask; 118 | int temp = dest[currentIndex] & destMask; 119 | destMask = ~destMask; 120 | value = value << (32-totalSize); 121 | value = value & destMask; 122 | dest[currentIndex] = temp | value; 123 | } 124 | } 125 | 126 | public int[] getData32() { return data_32; } 127 | private static byte[] convertIntArrayToByteArray(int[] input) { 128 | byte[] result = new byte[input.length*4]; 129 | for(int i = 0; i < input.length; i++) { 130 | //byte 3 = byte 0 131 | //byte 2 = byte 1 132 | //byte 1 = byte 2 133 | //byte 0 = byte 3 134 | int byteBase = i*4; 135 | int value = input[i]; 136 | result[byteBase+0] = (byte)(value >> 24); 137 | result[byteBase+1] = (byte)(value >> 16); 138 | result[byteBase+2] = (byte)(value >> 8); 139 | result[byteBase+3] = (byte)(value); 140 | } 141 | return result; 142 | } 143 | 144 | 145 | 146 | /** 147 | * Pack a number of bits from each int of an array(within given limits)to 148 | * the end of this list. 149 | * 150 | * @param inputA Array containing input values. 151 | * @param inputBits Array containing number of bits to use for each index 152 | * packed. This array should be equal in size to the inputA array. 153 | * @param inputOffset Index of first usable index. 154 | * @param countA Number of indices to pack. 155 | * @param startPosIn First usable bit-level index in destination array(byte 156 | * index = startPosIn/8, bit within that byte = startPosIn%8) 157 | * @param dest Destination array to store input values in. This array *must* 158 | * be large enough to store all values or this method will fail in an 159 | * undefined manner. 160 | */ 161 | public static void packIntByBits(int[] inputA, int[] inputBits, int inputOffset, 162 | int countA, int startPosIn, int[] dest) { 163 | if(DEBUG_LEV > 30) 164 | System.err.println("EncodedElement::packIntByBits : Begin"); 165 | //int offsetCounter = 0; 166 | int startPos = startPosIn;//the position to write to in output array 167 | int inputStop = countA+inputOffset; 168 | for(int valI = inputOffset; valI < inputStop; valI++) { 169 | //inputIter = valI+inputOffset; 170 | //inputIter += valI; 171 | //int input = inputA[valI];//value to encode 172 | int value = inputA[valI]; 173 | int count = inputBits[valI];//bits of value to encode 174 | //EncodedElement_32.addInt(input, count, startPos, dest); 175 | int currentIndex = startPos/32; 176 | int currentOffset = startPos%32; 177 | 178 | int totalSize = count+currentOffset; 179 | 180 | if(totalSize > 32) { 181 | //System.err.println("totalSize > 32"); 182 | int secondIndex = currentIndex + 1; 183 | int secondSize = totalSize - 32; 184 | int secondValue = value << (32-secondSize); 185 | int lowerMask = -1 >>> secondSize; 186 | int temp = dest[secondIndex] & lowerMask; 187 | dest[secondIndex] = temp | secondValue; 188 | totalSize = 32; 189 | value = value >>> secondSize; 190 | } 191 | 192 | //if(totalSize <= 32) {//Case 1 193 | int upperMask = -2 << (31-currentOffset); 194 | int lowerMask = 0x7FFFFFFF >>> (totalSize-1); 195 | int destMask = upperMask | lowerMask; 196 | int temp = dest[currentIndex] & destMask; 197 | destMask = ~destMask; 198 | value = value << (32-totalSize); 199 | value = value & destMask; 200 | dest[currentIndex] = temp | value; 201 | startPos += count;//startPos must not be referenced again below here! 202 | } 203 | if(DEBUG_LEV > 30) 204 | System.err.println("EncodedElement::addInt : End"); 205 | } 206 | 207 | /** 208 | * Pack a number of bits from each int of an array(within given limits)to 209 | * the end of this list. 210 | * 211 | * @param inputA Array containing input values. 212 | * @param inputBits Array containing number of bits to use for each index 213 | * packed. This array should be equal in size to the inputA array. 214 | * @param inputOffset Index of first usable index. 215 | * @param countA Number of indices to pack. 216 | * @return EncodedElement containing end of packed data. Data may flow 217 | * between multiple EncodedElement's, if an existing element was not large 218 | * enough for all values. 219 | */ 220 | @Override 221 | public EncodedElement packIntByBits(int[] inputA, int[] inputBits, int inputOffset, 222 | int countA) { 223 | byteArrayValid = false; 224 | //go to end if we're not there. 225 | if(next != null) { 226 | EncodedElement end = EncodedElement_32.getEnd_S(next); 227 | return end.packIntByBits(inputA, inputBits, inputOffset, countA); 228 | } 229 | 230 | //calculate how many we can pack into current. 231 | int writeBitsRemaining = data_32.length*32 - usableBits; 232 | int willWrite = 0; 233 | int writeCount = 0; 234 | //System.err.println("writeBitsRemaining: " + writeBitsRemaining); 235 | for(int i = 0; i < countA; i++) { 236 | writeBitsRemaining -= inputBits[inputOffset+i]; 237 | if(writeBitsRemaining >= 0) { 238 | writeCount++; 239 | willWrite += inputBits[inputOffset+i]; 240 | } 241 | else 242 | break; 243 | } 244 | //pack them and update usable bits. 245 | if(writeCount > 0) { 246 | EncodedElement_32.packIntByBits(inputA, inputBits, inputOffset, 247 | writeCount, usableBits, data_32); 248 | usableBits += willWrite; 249 | } 250 | //if more remain, create child object and add there 251 | countA -= writeCount; 252 | if(countA > 0) { 253 | inputOffset += writeCount; 254 | int tOff = usableBits %32; 255 | int size = data_32.length/2+1; 256 | //guarantee that our new element can store our given value 257 | int remainingToWrite = 0; 258 | for(int i = 0; i < countA; i++) { 259 | remainingToWrite += inputBits[inputOffset+i]; 260 | } 261 | remainingToWrite = remainingToWrite / 8 + 1; 262 | if(size < remainingToWrite) size = remainingToWrite+10; 263 | //System.err.println("remaining: "+remainingToWrite); 264 | //System.err.println("creating size/offset : "+size+":"+tOff); 265 | next = new EncodedElement_32(size, tOff); 266 | //add int to child 267 | return next.packIntByBits(inputA, inputBits, inputOffset, countA); 268 | } 269 | else { 270 | //System.err.println("returning....done"); 271 | //return if this is last object we wrote to. 272 | return this; 273 | } 274 | } 275 | 276 | @Override 277 | public byte[] getData() { 278 | // return convertIntArrayToByteArray(data_32); 279 | if(byteArrayValid) 280 | return data; 281 | byteArrayValid = true; 282 | if(data == null) 283 | data = new byte[data_32.length*4]; 284 | byte[] result = data; 285 | for(int i = 0; i < data_32.length; i++) { 286 | //byte 3 = byte 0 287 | //byte 2 = byte 1 288 | //byte 1 = byte 2 289 | //byte 0 = byte 3 290 | int byteBase = i*4; 291 | int value = data_32[i]; 292 | result[byteBase+0] = (byte)(value >> 24); 293 | result[byteBase+1] = (byte)(value >> 16); 294 | result[byteBase+2] = (byte)(value >> 8); 295 | result[byteBase+3] = (byte)(value); 296 | } 297 | data = result; 298 | return result; 299 | } 300 | 301 | public EncodedElement convertToEncodedElement() { 302 | EncodedElement ele = new EncodedElement(); 303 | byte[] result = new byte[data_32.length*4]; 304 | int byteBase = -4; 305 | int valueIndex = 0; 306 | /*for(int i = 0; i < data_32.length/4; i++) { 307 | int value0 = data_32[valueIndex++]; 308 | int value1 = data_32[valueIndex++]; 309 | int value2 = data_32[valueIndex++]; 310 | int value3 = data_32[valueIndex++]; 311 | 312 | byteBase += 7; 313 | result[byteBase--] = (byte)(value0); 314 | value0 = value0 >> 8; 315 | result[byteBase--] = (byte)(value0); 316 | value0 = value0 >> 8; 317 | result[byteBase--] = (byte)(value0); 318 | value0 = value0 >> 8; 319 | result[byteBase] = (byte)(value0); 320 | 321 | byteBase += 7; 322 | result[byteBase--] = (byte)(value1); 323 | value1 = value1 >> 8; 324 | result[byteBase--] = (byte)(value1); 325 | value1 = value1 >> 8; 326 | result[byteBase--] = (byte)(value1); 327 | value1 = value1 >> 8; 328 | result[byteBase] = (byte)(value1); 329 | 330 | byteBase += 7; 331 | result[byteBase--] = (byte)(value2); 332 | value2 = value2 >> 8; 333 | result[byteBase--] = (byte)(value2); 334 | value2 = value2 >> 8; 335 | result[byteBase--] = (byte)(value2); 336 | value2 = value2 >> 8; 337 | result[byteBase] = (byte)(value2); 338 | 339 | byteBase += 7; 340 | result[byteBase--] = (byte)(value3); 341 | value3 = value3 >> 8; 342 | result[byteBase--] = (byte)(value3); 343 | value3 = value3 >> 8; 344 | result[byteBase--] = (byte)(value3); 345 | value3 = value3 >> 8; 346 | result[byteBase] = (byte)(value3); 347 | }*/ 348 | 349 | for(int i = 3 ; i < data_32.length*4; i+=7) { 350 | //byte 3 = byte 0 351 | //byte 2 = byte 1 352 | //byte 1 = byte 2 353 | //byte 0 = byte 3 354 | //int byteBase = i*4+3; 355 | //byteBase += 7; 356 | int value = data_32[valueIndex++]; 357 | result[i--] = (byte)(value); 358 | value = value >> 8; 359 | result[i--] = (byte)(value); 360 | value = value >> 8; 361 | result[i--] = (byte)(value); 362 | value = value >> 8; 363 | result[i] = (byte)(value); 364 | 365 | /*result[byteBase++] = (byte)(value >> 24); 366 | result[byteBase++] = (byte)(value >> 16); 367 | result[byteBase++] = (byte)(value >> 8); 368 | result[byteBase] = (byte)(value);*/ 369 | } 370 | 371 | //ele.data = convertIntArrayToByteArray(data_32); 372 | ele.data = result; 373 | ele.offset = offset; 374 | ele.usableBits = usableBits; 375 | return ele; 376 | } 377 | /** 378 | * Add a number of bits from an int to the end of this list's data. Will 379 | * add a new element if necessary. The bits stored are taken from the lower- 380 | * order of input. 381 | * 382 | * @param input Int containing bits to append to end. 383 | * @param bitCount Number of bits to append. 384 | * @return EncodedElement which actually contains the appended value. 385 | */ 386 | @Override 387 | public EncodedElement addInt(int input, int bitCount) { 388 | byteArrayValid = false; 389 | if(next != null) { 390 | EncodedElement end = EncodedElement_32.getEnd_S(next); 391 | return end.addInt(input, bitCount); 392 | } 393 | else if(data_32.length*32 < usableBits+bitCount) { 394 | //create child and attach to next. 395 | //Set child's offset appropriately(i.e, manually set usable bits) 396 | int tOff = usableBits %32; 397 | //int size = data.length/2+1; 398 | int size = 1000; 399 | //guarantee that our new element can store our given value 400 | //if(size <= bitCount+tOff) size = (size+tOff+bitCount)*10; 401 | 402 | next = new EncodedElement_32(size, tOff); 403 | System.err.println("creating next node of size:bitCount "+size+ 404 | ":"+bitCount+":"+usableBits+":"+data_32.length); 405 | System.err.println("value: "+input); 406 | //+this.toString()+"::"+next.toString()); 407 | //add int to child 408 | return next.addInt(input, bitCount); 409 | } 410 | else { 411 | //At this point, we have the space, and we are the end of the chain. 412 | int startPos = this.usableBits; 413 | int[] dest = this.data_32; 414 | EncodedElement_32.addInt(input, bitCount, startPos, dest); 415 | usableBits += bitCount; 416 | return this; 417 | } 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/EncodingConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * This class defines the configuration options that are allowed to change 24 | * within a FLAC stream. Options here may be changed from one frame to the next. 25 | * In general, the settings should not need altered, but the option to do so 26 | * remains. 27 | * 28 | * @author Preston Lacey 29 | */ 30 | public class EncodingConfiguration implements Cloneable { 31 | 32 | /** 33 | * Defines the options for channel configuration to use. LEFT & RIGHT 34 | * channels refers to stereo audio as expected. INDEPENDENT channels refer to 35 | * each channel encoded separately. SIDE channel is the difference of the 36 | * LEFT and RIGHT channels(LEFT-RIGHT). MID channel is the integer average of 37 | * LEFT and RIGHT channels( (LEFT+RIGHT)/2 ). Options using MID and/or SIDE 38 | * channels may benefit encoding by taking advantage of similarities between 39 | * the channels, and are available in FLAC for STEREO streams only. In 40 | * general, ENCODER_CHOICE should be chosen. 41 | */ 42 | public enum ChannelConfig { 43 | /** Encode channels independently **/ 44 | INDEPENDENT, 45 | /** Encode LEFT and SIDE channels for stereo stream */ 46 | LEFT_SIDE, 47 | /** Encode RIGHT and SIDE channels for stereo stream */ 48 | RIGHT_SIDE, 49 | /** Encode MID and SIDE channels for stereo stream */ 50 | MID_SIDE, 51 | /** Encode all options possible, and take the best(slow)*/ 52 | EXHAUSTIVE, 53 | /** Let the encoder decide which to use.(recommended) */ 54 | ENCODER_CHOICE 55 | }; 56 | 57 | /** 58 | * Defines the various subframe types that may be used. If you don't know 59 | * what these are, choose EXHAUSTIVE(for description of subframe types, see 60 | * the flac format documentation at http://flac.sourceforge.net/format.html) 61 | */ 62 | public enum SubframeType { 63 | /** Constant subframe, do not choose unless you are sure you're encoding 64 | * digital silence */ 65 | CONSTANT, 66 | /** Decent compression, fasted option */ 67 | FIXED, 68 | /** Better compression, slower */ 69 | LPC, 70 | /** No compression, simply wraps unencoded audio into a FLAC stream */ 71 | VERBATIM, 72 | /** Best compression, slightly slower than LPC alone, lets encoder choose 73 | * the best(Recommended). */ 74 | EXHAUSTIVE 75 | } 76 | 77 | /** Maximum LPC order possible(as defined by FLAC format) */ 78 | public static final int MAX_LPC_ORDER = 32; 79 | /** Minimum LPC order possible(as defined by FLAC format) */ 80 | public static final int MIN_LPC_ORDER = 1; 81 | /** Maximum Rice Partition order possible(as defined by FLAC Format) */ 82 | public static final int MAX_RICE_PARTITION_ORDER = 15; 83 | /** Default subframe type to use*/ 84 | public static final SubframeType DEFAULT_SUBFRAME_TYPE = SubframeType.EXHAUSTIVE; 85 | /** Default channel configuration */ 86 | public static final ChannelConfig DEFAULT_CHANNEL_CONFIG = ChannelConfig.ENCODER_CHOICE; 87 | /** Default maximum lpc order to use */ 88 | public static final int DEFAULT_MAX_LPC_ORDER = 12; 89 | /** Default minimum lpc order to use */ 90 | public static final int DEFAULT_MIN_LPC_ORDER = 1; 91 | /** Default maximum Rice partition order */ 92 | public static final int DEFAULT_MAX_RICE_ORDER = 0; 93 | 94 | 95 | ChannelConfig channelConfig; 96 | SubframeType subframeType; 97 | int minimumLPCOrder = 1; 98 | int maximumLPCOrder = 16; 99 | int maximumRicePartitionOrder = 0; 100 | 101 | /** 102 | * Constructor, uses defaults for all options. These defaults should be good 103 | * for most purposes. 104 | */ 105 | public EncodingConfiguration() { 106 | subframeType = DEFAULT_SUBFRAME_TYPE; 107 | channelConfig = DEFAULT_CHANNEL_CONFIG; 108 | maximumLPCOrder = DEFAULT_MAX_LPC_ORDER; 109 | minimumLPCOrder = DEFAULT_MIN_LPC_ORDER; 110 | maximumRicePartitionOrder = DEFAULT_MAX_RICE_ORDER; 111 | } 112 | 113 | /** 114 | * Copy constructor. 115 | * @param e EncodingConfiguration object to copy. Must not be null. 116 | */ 117 | public EncodingConfiguration(EncodingConfiguration e) { 118 | subframeType = e.subframeType; 119 | channelConfig = e.channelConfig; 120 | minimumLPCOrder = e.minimumLPCOrder; 121 | maximumLPCOrder = e.maximumLPCOrder; 122 | maximumRicePartitionOrder = e.maximumRicePartitionOrder; 123 | } 124 | 125 | /** 126 | * Set the channel configuration to use. Channel configuration refers to the 127 | * way multiple channels are processed. See documentation for 128 | * {@link ChannelConfig ChannelConfig} for more info on choices. 129 | * @param conf Channel configuration to use. 130 | */ 131 | public void setChannelConfig(ChannelConfig conf) { 132 | channelConfig = conf; 133 | } 134 | 135 | /** 136 | * Get the current channel configuration value. 137 | * @return current channel configuration value 138 | */ 139 | public ChannelConfig getChannelConfig() { 140 | return channelConfig; 141 | } 142 | 143 | /** 144 | * Set the subframe type to use. This refers to the way each subframe(channel) 145 | * is compressed. See documentation for {@link SubframeType SubframeType} for 146 | * more info on choices. 147 | * @param type 148 | */ 149 | public void setSubframeType(SubframeType type) { 150 | subframeType = type; 151 | } 152 | 153 | /** 154 | * Get the current subframe type 155 | * @return current subframe type 156 | */ 157 | public SubframeType getSubframeType() { 158 | return subframeType; 159 | } 160 | 161 | /** 162 | * Get current minimum LPC order 163 | * @return current minimum lpc order 164 | */ 165 | public int getMinLPCOrder() { return minimumLPCOrder; } 166 | 167 | /** 168 | * Get maximum LPC order 169 | * @return current maximum lpc order 170 | */ 171 | public int getMaxLPCOrder() { return maximumLPCOrder; } 172 | /** 173 | * Set the minimum LPC order. If order given is out of the valid range(as 174 | * defined by {@link EncodingConfiguration#MAX_LPC_ORDER MAX_LPC_ORDER} and 175 | * {@link EncodingConfiguration#MIN_LPC_ORDER MIN_LPC_ORDER}), it will be 176 | * set to the closest valid value instead. 177 | * @param order minimum LPC order to use 178 | */ 179 | public void setMinLPCOrder(int order) { 180 | minimumLPCOrder = (order < MIN_LPC_ORDER) ? MIN_LPC_ORDER:order; 181 | minimumLPCOrder = (minimumLPCOrder > MAX_LPC_ORDER) ? 182 | MAX_LPC_ORDER:minimumLPCOrder; 183 | } 184 | /** 185 | * Set the maximum LPC order. If order given is out of the valid range 186 | * (as defined by {@link EncodingConfiguration#MAX_LPC_ORDER MAX_LPC_ORDER} 187 | * and {@link EncodingConfiguration#MIN_LPC_ORDER MIN_LPC_ORDER}), it will be 188 | * set to the closest valid value instead. 189 | * @param order maximum LPC order to use 190 | */ 191 | public void setMaxLPCOrder(int order) { 192 | maximumLPCOrder = (order < MIN_LPC_ORDER) ? MIN_LPC_ORDER:order; 193 | maximumLPCOrder = (maximumLPCOrder > MAX_LPC_ORDER) ? 194 | MAX_LPC_ORDER:maximumLPCOrder; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLACFileOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | import java.io.FileOutputStream; 22 | import java.io.IOException; 23 | import java.io.File; 24 | import java.nio.channels.FileChannel; 25 | import java.io.Closeable; 26 | 27 | /** 28 | * This class provides basic file output for writing from a FLACEncoder. 29 | * 30 | * @author Preston Lacey 31 | */ 32 | public class FLACFileOutputStream implements FLACOutputStream,Closeable{ 33 | 34 | FileOutputStream fos = null; 35 | long position; 36 | long size = 0; 37 | boolean valid; 38 | 39 | /** 40 | * Constructor. Create a FLACFileOutputStream using the given filename. If 41 | * file exists, file will be overwritten. 42 | * 43 | * @param filename file to connect to output stream. 44 | */ 45 | public FLACFileOutputStream(String filename) throws IOException { 46 | position = 0; 47 | fos = new FileOutputStream(filename); 48 | valid = true; 49 | } 50 | 51 | public FLACFileOutputStream(File file) throws IOException { 52 | position = 0; 53 | fos = new FileOutputStream(file); 54 | valid = true; 55 | } 56 | 57 | /** 58 | * Constructor. Create a FLACFileOutputStream using the given FileOutputStream. 59 | * @param fos FileOutputStream to write to, must be open. Current position 60 | * of fos will be used as this object's position. 61 | * @throws IOException 62 | */ 63 | public FLACFileOutputStream(FileOutputStream fos) throws IOException { 64 | FileChannel fc = fos.getChannel(); 65 | position = fc.position(); 66 | this.fos = fos; 67 | valid = true; 68 | } 69 | 70 | /** 71 | * Get the status of this file stream(whether the file was successfully open 72 | * or not). 73 | * @return true if file was successfully opened, false otherwise. 74 | */ 75 | @Deprecated 76 | public boolean isValid() { return valid; } 77 | 78 | /** 79 | * Attempt to seek to the given location within this stream. It is not 80 | * guaranteed that all implementations can or will support seeking. Use the 81 | * method canSeek() 82 | * 83 | * @param pos target position to seek to. 84 | * @return current position after seek attempt. 85 | */ 86 | public long seek(long pos) throws IOException { 87 | FileChannel fc = fos.getChannel(); 88 | fc.position(pos); 89 | return pos; 90 | } 91 | 92 | /** 93 | * Write a byte to this stream. 94 | * @param data byte to write. 95 | * @throws IOException IOException will be raised if an error occurred while 96 | * writing. 97 | */ 98 | public void write(byte data) throws IOException { 99 | fos.write(data); 100 | if(position + 1 > size) 101 | size = position+1; 102 | position+= 1; 103 | } 104 | /** 105 | * Write the given number of bytes from the byte array. Return number of 106 | * bytes written. 107 | * @param data array containing bytes to be written. 108 | * @param offset start index of array to begin reading from. 109 | * @param count number of bytes to write. 110 | * @return number of bytes written. 111 | * @throws IOException IOException upon a write error. 112 | */ 113 | public int write(byte[] data, int offset, int count) throws IOException { 114 | int result = count; 115 | try { 116 | fos.write(data,offset,count); 117 | if(position + count > size) 118 | size = position+count; 119 | position+= count; 120 | }catch(IOException e) { 121 | throw e; 122 | } 123 | return result; 124 | } 125 | 126 | /** 127 | * Get the number of bytes that have been written in length! 128 | * This takes into account seeking to different portions. 129 | * @return total length written. 130 | */ 131 | public long size() { 132 | return size; 133 | } 134 | 135 | /** 136 | * Test whether this stream is seekable. 137 | * @return true if stream is seekable, false otherwise 138 | */ 139 | public boolean canSeek() { 140 | return true; 141 | } 142 | 143 | /** 144 | * Get the current write position of this stream. 145 | * @return current write position. 146 | */ 147 | public long getPos() { 148 | return position; 149 | } 150 | 151 | /** 152 | * Close FileOutputStream owned by this object. 153 | * @throws IOException 154 | */ 155 | public void close() throws IOException { 156 | fos.close(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLACOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | import java.io.IOException; 23 | /** 24 | * This interface defines a location to write the output of the FLAC 25 | * encoder to. We don't want to require that the entire stream is buffered in the 26 | * encoder prior to being written, as that could require significant memory and 27 | * would make live handling of streams impossible. However, we can't write the 28 | * stream headers completely until the entire stream is encoded(specifically 29 | * because the MD5 hash which appears at the beginning of the FLAC stream, 30 | * isn't known till the last audio value is given to the encoder). Therefore, 31 | * the output stream would ideally be seekable, which prevents us from 32 | * outputting to just a standard "OutputStream". So we can't guarantee the 33 | * stream is seekable, can't write everything in order given, but can't always 34 | * buffer till we have the data for the stream headers. This interface allows 35 | * the implementation to determine the proper tradeoffs. Following is a 36 | * description of how the FLACEncoder class will treat objects of this type 37 | *

38 | * If canSeek() returns false: The file will be written as normal, but the 39 | * headers will not be updated once the stream is closed. This means the FLAC 40 | * file will not contain a count of the total number of samples, nor the MD5 41 | * hash of the original input(used for verifying the data).
42 | * If canSeek() returns true: Data will be written as it becomes available, and 43 | * the encoder will seek() to a point near the beginning of the stream to fix 44 | * the stream headers once the stream is closed.
45 | *
46 | * @author Preston Lacey 47 | * 48 | * 49 | */ 50 | public interface FLACOutputStream { 51 | 52 | /** 53 | * Attempt to seek to the given position. 54 | * 55 | * @param pos target position. 56 | * @return current position after seek. 57 | */ 58 | public long seek(long pos) throws IOException; 59 | 60 | /** 61 | * Write the given number of bytes from a byte array. 62 | * 63 | * @param data array containing source bytes to write 64 | * @param offset index of source array to begin reading from. 65 | * @param count number of bytes to write. 66 | * @return number of bytes written. 67 | * @throws IOException IOException raised upon write error. 68 | */ 69 | public int write(byte[] data, int offset, int count) throws IOException; 70 | 71 | /** 72 | * Get the number of bytes that have been written in length. 73 | * This takes into account seeking to different portions. 74 | * 75 | * @return total writtne length of stream. 76 | */ 77 | public long size(); 78 | 79 | /** 80 | * Write a single byte to the stream. 81 | * 82 | * @param data byte to write. 83 | * @throws IOException IOException raised upon write error. 84 | */ 85 | public void write(byte data) throws IOException; 86 | 87 | /** 88 | * Test whether this object allows seeking. 89 | * 90 | * @return true if seeking is allowed, false otherwise. 91 | */ 92 | public boolean canSeek(); 93 | 94 | /** 95 | * Get current write position of this stream. If stream cannot seek, then 96 | * this will return 0; 97 | * 98 | * @return current write position. 99 | */ 100 | public long getPos(); 101 | } 102 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLACStreamController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | import java.util.concurrent.locks.ReentrantLock; 22 | import java.io.IOException; 23 | /** 24 | * 25 | * @author preston 26 | */ 27 | public class FLACStreamController { 28 | public static int DEBUG_LEV = 0; 29 | /** we set this to true while a flac stream has been opened and not 30 | * officially closed. Must use a streamLock to set and get this. Some actions 31 | * must not be taken while a stream is opened */ 32 | volatile boolean flacStreamIsOpen = false; 33 | 34 | private final ReentrantLock streamLock = new ReentrantLock(); 35 | 36 | /* Object to write results to. Must be set before opening stream */ 37 | private FLACOutputStream out = null; 38 | 39 | /* contains FLAC_id used in the flac stream header to signify FLAC format */ 40 | EncodedElement FLAC_id = FLACStreamIdentifier.getIdentifier(); 41 | 42 | /* total number of samples encoded to output. Used in stream header */ 43 | volatile long samplesInStream; 44 | 45 | /* next frame number to use */ 46 | volatile long nextFrameNumber = 0; 47 | 48 | /* position of header in output stream location(needed so we can update 49 | * the header info(md5, minBlockSize, etc), once encoding is done 50 | */ 51 | long streamHeaderPos = 0; 52 | 53 | /* minimum frame size seen so far. Used in the stream header */ 54 | volatile int minFrameSize = 0x7FFFFFFF; 55 | 56 | /* maximum frame size seen so far. Used in stream header */ 57 | volatile int maxFrameSize = 0; 58 | 59 | /* minimum block size used so far. Used in stream header */ 60 | volatile int minBlockSize = 0x7FFFFFFF; 61 | 62 | /* maximum block size used so far. Used in stream header */ 63 | volatile int maxBlockSize = 0; 64 | 65 | StreamConfiguration streamConfig = null; 66 | public FLACStreamController(FLACOutputStream fos, StreamConfiguration sc) { 67 | out = fos; 68 | streamConfig = new StreamConfiguration(sc); 69 | minFrameSize = 0x7FFFFFFF; 70 | maxFrameSize = 0; 71 | minBlockSize = 0x7FFFFFFF; 72 | maxBlockSize = 0; 73 | samplesInStream = 0; 74 | streamHeaderPos = 0; 75 | nextFrameNumber = 0; 76 | } 77 | public void setFLACOutputStream(FLACOutputStream fos) { 78 | streamLock.lock(); 79 | try { 80 | if(flacStreamIsOpen) 81 | throw new IllegalStateException("Cannot set new output stream while flac stream is open"); 82 | out = fos; 83 | }finally { 84 | streamLock.unlock(); 85 | } 86 | } 87 | public FLACOutputStream getFLACOutputStream() { return out; } 88 | 89 | 90 | /** 91 | * Close the current FLAC stream. Updates the stream header information. 92 | * If called on a closed stream, operation is undefined. Do not do this. 93 | */ 94 | public void closeFLACStream(byte[] md5Hash, StreamConfiguration streamConfig) 95 | throws IOException { 96 | //reset position in output stream to beginning. 97 | //re-write the updated stream info. 98 | streamLock.lock(); 99 | try { 100 | if(!flacStreamIsOpen) 101 | throw new IllegalStateException("Error. Cannot close a non-opened stream"); 102 | StreamConfiguration tempSC = new StreamConfiguration(streamConfig); 103 | tempSC.setMaxBlockSize(maxBlockSize); 104 | tempSC.setMinBlockSize(minBlockSize); 105 | EncodedElement streamInfo = MetadataBlockStreamInfo.getStreamInfo( 106 | tempSC, minFrameSize, maxFrameSize, samplesInStream, md5Hash); 107 | if(out.canSeek()) { 108 | out.seek(streamHeaderPos); 109 | this.writeDataToOutput(streamInfo); 110 | } 111 | flacStreamIsOpen = false; 112 | } finally { 113 | streamLock.unlock(); 114 | } 115 | } 116 | 117 | 118 | /** 119 | * Write the data stored in an EncodedElement to the output stream. 120 | * All data will be written along byte boundaries, but the elements in the 121 | * given list need not end on byte boundaries. If the data of an element 122 | * does not end on a byte boundary, then the space remaining in that last 123 | * byte will be used as an offset, and merged(using an "OR"), with the first 124 | * byte of the following element. 125 | * 126 | * @param data 127 | * @return 128 | * @throws IOException 129 | */ 130 | private int writeDataToOutput(EncodedElement data) throws IOException { 131 | 132 | int writtenBytes = 0; 133 | int offset = 0; 134 | EncodedElement current = data; 135 | int currentByte = 0; 136 | byte unfullByte = 0; 137 | byte[] eleData = null; 138 | int usableBits = 0; 139 | int lastByte = 0; 140 | while(current != null) { 141 | eleData = current.getData(); 142 | usableBits = current.getUsableBits(); 143 | currentByte = 0; 144 | //if offset is not zero, merge first byte with existing byte 145 | if(offset != 0) { 146 | unfullByte = (byte)(unfullByte | eleData[currentByte++]); 147 | out.write(unfullByte); 148 | } 149 | //write all full bytes of element. 150 | lastByte = usableBits/8; 151 | if(lastByte > 0) 152 | out.write(eleData, currentByte, lastByte-currentByte); 153 | //save non-full byte(if present), and set "offset" for next element. 154 | offset = usableBits %8; 155 | if(offset != 0) { 156 | unfullByte = eleData[lastByte]; 157 | } 158 | //update current. 159 | current = current.getNext(); 160 | } 161 | //if non-full byte remains. write. 162 | if(offset != 0) { 163 | out.write(eleData, lastByte, 1); 164 | } 165 | return writtenBytes; 166 | } 167 | 168 | public long incrementFrameNumber() { 169 | return nextFrameNumber++; 170 | } 171 | 172 | /** 173 | * Begin a new FLAC stream. Prior to calling this, you must have already 174 | * set the StreamConfiguration and the output stream, both of which must not 175 | * change until encoding is finished and the stream is closed. If this 176 | * FLACEncoder object has already been used to encode a stream, unencoded 177 | * samples may still be stored. Use clear() to dump them prior to calling 178 | * this method(if clear() not called, and samples are instead retained, the 179 | * StreamConfiguration must NOT have changed from the prior stream. 180 | * 181 | * @throws IOException if there is an error writing the headers to output. 182 | */ 183 | public void openFLACStream() throws IOException { 184 | streamLock.lock(); 185 | try { 186 | //reset all data. 187 | reset(); 188 | flacStreamIsOpen = true; 189 | //write FLAC stream identifier 190 | out.write(FLAC_id.getData(), 0, FLAC_id.getUsableBits()/8); 191 | //write stream headers. These must be updated at close of stream 192 | byte[] md5Hash = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//blank hash. Don't know it yet. 193 | EncodedElement streamInfo = MetadataBlockStreamInfo.getStreamInfo( 194 | streamConfig, minFrameSize, maxFrameSize, samplesInStream, md5Hash); 195 | //mark stream info location(so we can return to it and re-write headers, 196 | // assuming stream is seekable. Then write header. 197 | int size = streamInfo.getUsableBits()/8; 198 | EncodedElement metadataBlockHeader = 199 | MetadataBlockHeader.getMetadataBlockHeader(false, 200 | MetadataBlockHeader.MetadataBlockType.STREAMINFO, size); 201 | this.writeDataToOutput(metadataBlockHeader); 202 | streamHeaderPos = out.getPos(); 203 | out.write(streamInfo.getData(), 0, size); 204 | writePaddingToFoolJFlac(); 205 | }finally { 206 | streamLock.unlock(); 207 | } 208 | } 209 | private void reset() { 210 | minFrameSize = 0x7FFFFFFF; 211 | maxFrameSize = 0; 212 | minBlockSize = 0x7FFFFFFF; 213 | maxBlockSize = 0; 214 | samplesInStream = 0; 215 | streamHeaderPos = 0; 216 | nextFrameNumber = 0; 217 | } 218 | private void writePaddingToFoolJFlac() throws IOException { 219 | int size = 40; 220 | byte[] padding = new byte[size]; 221 | EncodedElement metadataBlockHeader = 222 | MetadataBlockHeader.getMetadataBlockHeader(true, 223 | MetadataBlockHeader.MetadataBlockType.PADDING, 40); 224 | this.writeDataToOutput(metadataBlockHeader); 225 | out.write(padding, 0,size); 226 | } 227 | 228 | public void writeBlock(BlockEncodeRequest ber) throws IOException { 229 | if(!flacStreamIsOpen) 230 | throw new IllegalStateException("Cannot write on a non-opened stream"); 231 | writeDataToOutput(ber.result.getNext()); 232 | //update encodedCount and count, and blocks, MD5 233 | if(ber.count != ber.encodedSamples) { 234 | System.err.println("Error encoding frame number: "+ 235 | ber.frameNumber+", FLAC stream potentially invalid"); 236 | } 237 | samplesInStream += ber.encodedSamples; 238 | if(ber.encodedSamples > maxBlockSize) 239 | maxBlockSize = ber.encodedSamples; 240 | if(ber.encodedSamples < minBlockSize) 241 | minBlockSize = ber.encodedSamples; 242 | int frameSize = ber.result.getTotalBits()/8; 243 | if(frameSize > maxFrameSize) maxFrameSize = frameSize; 244 | if(frameSize < minFrameSize) minFrameSize = frameSize; 245 | } 246 | 247 | } 248 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLACStreamIdentifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * Provides the stream identifier used at beginning of flac streams. 24 | * @author Preston Lacey 25 | */ 26 | public class FLACStreamIdentifier { 27 | static final byte streamMarkerByte1 = 0x66; 28 | static final byte streamMarkerByte2 = 0x4c; 29 | static final byte streamMarkerByte3 = 0x61; 30 | static final byte streamMarkerByte4 = 0x43; 31 | static final byte[] marker = { 32 | streamMarkerByte1, 33 | streamMarkerByte2, 34 | streamMarkerByte3, 35 | streamMarkerByte4, 36 | }; 37 | 38 | /** 39 | * Get an EncodedElement containing the marker(which is itself in a byte 40 | * array). 41 | * @return EncodedElement containing the marker. 42 | */ 43 | public static EncodedElement getIdentifier() { 44 | EncodedElement ele = new EncodedElement(); 45 | ele.setData(marker.clone()); 46 | ele.setUsableBits(32); 47 | return ele; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLACStreamOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | import java.io.OutputStream; 22 | import java.io.IOException; 23 | import java.io.Closeable; 24 | /** 25 | * This class provides basic OutputStream support for writing from a FLACEncoder. 26 | * 27 | * @author Preston Lacey 28 | */ 29 | public class FLACStreamOutputStream implements FLACOutputStream,Closeable { 30 | 31 | OutputStream out = null; 32 | long size = 0; 33 | boolean valid; 34 | 35 | /** 36 | * Constructor. Create a FLACStreamOutputStream using the given OutputStream. 37 | * @param out OutputStream to write the FLAC stream to. 38 | */ 39 | public FLACStreamOutputStream(OutputStream out) throws IOException { 40 | this.out = out; 41 | size = 0; 42 | } 43 | 44 | /** 45 | * Attempt to seek to the given location within this stream. It is not 46 | * guaranteed that all implementations can or will support seeking. Use the 47 | * method canSeek() 48 | * 49 | * @param pos target position to seek to. 50 | * @return current position after seek attempt. 51 | */ 52 | public long seek(long pos) { 53 | throw new UnsupportedOperationException("seek(long) is not supported on by FLACStreamOutputStream"); 54 | } 55 | 56 | /** 57 | * Write a byte to this stream. 58 | * @param data byte to write. 59 | * @throws IOException IOException will be raised if an error occurred while 60 | * writing. 61 | */ 62 | public void write(byte data) throws IOException { 63 | out.write(data); 64 | size++; 65 | } 66 | /** 67 | * Write the given number of bytes from the byte array. Return number of 68 | * bytes written. 69 | * @param data array containing bytes to be written. 70 | * @param offset start index of array to begin reading from. 71 | * @param count number of bytes to write. 72 | * @return number of bytes written. 73 | * @throws IOException IOException upon a write error. 74 | */ 75 | public int write(byte[] data, int offset, int count) throws IOException { 76 | int result = count; 77 | out.write(data,offset,count); 78 | size += count; 79 | return result; 80 | } 81 | 82 | /** 83 | * Get the number of bytes that have been written by this object. 84 | * @return total length written. 85 | */ 86 | public long size() { 87 | return size; 88 | } 89 | 90 | /** 91 | * Test whether this stream is seekable. 92 | * @return true if stream is seekable, false otherwise 93 | */ 94 | public boolean canSeek() { 95 | return false; 96 | } 97 | 98 | /** 99 | * Get the current write position of this stream. If this stream cannot seek, 100 | * this will return 0; 101 | * @return current write position. 102 | */ 103 | public long getPos() { 104 | return 0; 105 | } 106 | 107 | /** 108 | * Close OutputStream owned by this object. 109 | * @throws IOException 110 | */ 111 | public void close() throws IOException { 112 | out.close(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLAC_MD5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | import java.security.MessageDigest; 23 | 24 | 25 | /** 26 | * 27 | * @author preston 28 | */ 29 | public class FLAC_MD5 { 30 | private MessageDigest md = null; 31 | private byte[] _dataMD5 = null; 32 | 33 | public FLAC_MD5() throws java.security.NoSuchAlgorithmException { 34 | md = MessageDigest.getInstance("md5"); 35 | } 36 | 37 | public MessageDigest getMD() { 38 | return md; 39 | } 40 | 41 | /** 42 | * Add samples to the MD5 hash. 43 | * CURRENTLY ONLY MAY WORK FOR: sample sizes which are divisible by 8. Need 44 | * to create some audio to test with. 45 | * @param samples 46 | * @param count 47 | * @param channels 48 | */ 49 | public void addSamplesToMD5(int[] samples, int count, int channels, 50 | int sampleSize) { 51 | int bytesPerSample = sampleSize/8; 52 | if(sampleSize%8 != 0) 53 | bytesPerSample++; 54 | if(_dataMD5 == null || _dataMD5.length < count*bytesPerSample*channels) { 55 | _dataMD5 = new byte[count*bytesPerSample*channels]; 56 | } 57 | byte[] dataMD5 = _dataMD5; 58 | splitSamplesToBytes(samples, count*channels, bytesPerSample, dataMD5); 59 | md.update(dataMD5, 0, count*bytesPerSample*channels); 60 | } 61 | 62 | /* Split Samples to bytes(for sending to MD5) 63 | * CURRENTLY ONLY MAY WORK FOR: sample sizes which are divisible by 8. Need 64 | * to create some audio to test with.*/ 65 | private static final void splitSamplesToBytes(int[] samples, int totalSamples, 66 | int bytesPerSample, byte[] dataMD5) { 67 | int destIndexBase = 0; 68 | int i = 0; 69 | 70 | switch(bytesPerSample) { 71 | case 3: 72 | for(; i < totalSamples; i++) { 73 | dataMD5[destIndexBase++] = (byte)(samples[i]); 74 | dataMD5[destIndexBase++] = (byte)(samples[i] >> 8); 75 | dataMD5[destIndexBase++] = (byte)(samples[i] >> 16); 76 | } 77 | break; 78 | case 2: 79 | for(; i < totalSamples; i++) { 80 | dataMD5[destIndexBase++] = (byte)(samples[i]); 81 | dataMD5[destIndexBase++] = (byte)(samples[i] >> 8); 82 | } 83 | break; 84 | case 1: 85 | for(; i < totalSamples; i++) { 86 | dataMD5[i] = (byte)samples[i]; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FrameHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | 23 | /** 24 | * This class is used to generate a Frame Header for a FLAC Frame. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class FrameHeader { 29 | 30 | /** For Debugging: Higher level equals more debug statements */ 31 | public static int DEBUG_LEV = 0; 32 | 33 | private static final int definedBlockSizes[] = { 34 | -1, 35 | 192, 36 | 576, 37 | 1152, 38 | 2304, 39 | 4608, 40 | -1, 41 | -1, 42 | 256, 43 | 512, 44 | 1024, 45 | 2048, 46 | 4096, 47 | 8192, 48 | 16384, 49 | 32768 50 | }; 51 | 52 | private static final int definedSampleRates[] = { 53 | 0, 54 | 88200, 55 | 176400, 56 | 192000, 57 | 8000, 58 | 16000, 59 | 22050, 60 | 24000, 61 | 32000, 62 | 44100, 63 | 48000, 64 | 96000, 65 | -1, 66 | -1, 67 | -1, 68 | -1 69 | }; 70 | 71 | /** Maximum size a header can be according to FLAC specification. */ 72 | public static final int MAX_HEADER_SIZE = 128;//in bytes 73 | 74 | /** Synchronization code used at beginning of frame(low-order 14 bits 75 | * used) */ 76 | public static final short syncCode = 0x3FFE;//14 bits used 77 | 78 | static final byte reserved = 0;//1 bit used; 0 mandatory value 79 | 80 | byte blockingStrategy = 0;//1 bit used; 0=fixed-blocksize. 1=variable 81 | byte blockSize = 0xC;//4 bits used; see format docs 82 | byte sampleRate = 4;//0;//4 bits used; see format docs 83 | //byte channelAssignment = 0;//4 bits used; see format docs 84 | byte sampleSize = 4;//3 bits used; see format docs 85 | 86 | static final byte reserved2 = 0;//1 bit used; 0 mandatory value 87 | long frameNumber = 0;//8-56 bits used; UTF-8 coded sample number 88 | int blockSizeMod = 0;//if(blocksize bits == 011x) 8/16 bit (blocksize-1) 89 | int SampleRateMod = 0;//if(sample rate bits == 11xx) 8/16 bit sample rate 90 | byte crc8 = 0; 91 | CRC8 crcCalculator; 92 | 93 | /** 94 | * Constructor creates a new FrameHeader object which is ready to 95 | * generate headers. We can't use static functions to do this, since the 96 | * process uses a CRC8 object which must be instantiated. 97 | * 98 | */ 99 | public FrameHeader() { 100 | crcCalculator = new CRC8(); 101 | } 102 | 103 | /** 104 | * Create the header for a frame with the given parameters. Header data is 105 | * stored out to an EncodedElement, in the proper form for a FLAC stream. 106 | * 107 | * @param fixBlock True to use a fixed block size, false to use variable. At 108 | * this time, this *must* be set to True, as variable block size is 109 | * not yet implemented. 110 | * @param blockSize Block Size of this frame. 111 | * @param sampleRate Sample rate of this frame. 112 | * @param channelAssign Channel assignment used in this frame's encoding. 113 | * See EncodingConfiguration class documentation for 114 | * more information. 115 | * @param sampleSize Bits per sample. 116 | * @param frameNumber For fixed block-size encodings, this is the frame-number 117 | * starting at zero and incrementing by one. For variable 118 | * block encodings, this is the sample number of the 119 | * first sample in the frame. 120 | * @param channelCount Number of channels in the stream. 121 | * @return EncodedElement where the header is saved to. 122 | */ 123 | public EncodedElement createHeader(boolean fixBlock, int blockSize, 124 | int sampleRate, EncodingConfiguration.ChannelConfig channelAssign, 125 | int sampleSize, long frameNumber, int channelCount, EncodedElement result) { 126 | if(DEBUG_LEV > 0 ) 127 | System.err.println("FrameHeader::createHeader : Begin"); 128 | 129 | //EncodedElement result = new EncodedElement(); 130 | boolean useEndBlockSize = false; 131 | boolean useEndSampleRate = false; 132 | result.clear(MAX_HEADER_SIZE,0); 133 | int blockingStrat = (fixBlock)? 0:1; 134 | byte[] encodedFrameNumber = UTF8Modified.convertToExtendedUTF8(frameNumber); 135 | //set block size bits 136 | byte encodedBlockSize = encodeBlockSize(blockSize); 137 | if(encodedBlockSize == 0x6 || encodedBlockSize == 0x7) 138 | useEndBlockSize = true; 139 | 140 | //set sample rate bits 141 | byte encodedSampleRate = encodeSampleRate(sampleRate); 142 | if(encodedSampleRate >= 12 && encodedSampleRate <= 14) 143 | useEndSampleRate = true; 144 | 145 | //set channelAssignment bits 146 | int channelAssignment = 0; 147 | if(channelAssign == EncodingConfiguration.ChannelConfig.INDEPENDENT) 148 | channelAssignment = channelCount-1; 149 | else if(channelAssign == EncodingConfiguration.ChannelConfig.LEFT_SIDE) 150 | channelAssignment = 8; 151 | else if(channelAssign == EncodingConfiguration.ChannelConfig.RIGHT_SIDE) 152 | channelAssignment = 9; 153 | else if(channelAssign == EncodingConfiguration.ChannelConfig.MID_SIDE) 154 | channelAssignment = 10; 155 | 156 | //set sample size bits 157 | byte encodedSampleSize = 0; 158 | switch(sampleSize) { 159 | case 8: encodedSampleSize = 0x1; break; 160 | case 12: encodedSampleSize = 0x2; break; 161 | case 16: encodedSampleSize = 0x4; break; 162 | case 20: encodedSampleSize = 0x5; break; 163 | case 24: encodedSampleSize = 0x6; break; 164 | default: encodedSampleSize = 0x0; 165 | } 166 | 167 | result.addInt(syncCode,14); 168 | result.addInt(reserved, 1); 169 | result.addInt(blockingStrat, 1); 170 | result.addInt(encodedBlockSize, 4); 171 | result.addInt(encodedSampleRate, 4); 172 | result.addInt(channelAssignment, 4); 173 | result.addInt(encodedSampleSize, 3); 174 | result.addInt(reserved2, 1); 175 | 176 | for(int i = 0; i < encodedFrameNumber.length; i++) { 177 | result.addInt(encodedFrameNumber[i], 8); 178 | } 179 | 180 | //write blockSize if needed(two formats possible) 181 | if(useEndBlockSize) { 182 | if(encodedBlockSize == 0x6) { 183 | result.addInt(blockSize-1, 8); 184 | } 185 | else { 186 | result.addInt(blockSize-1, 16); 187 | } 188 | } 189 | //write sampleRate if needed(three formats possible) 190 | if(useEndSampleRate) { 191 | switch(encodedSampleRate) { 192 | case 0xC: 193 | result.addInt(sampleRate/1000, 8); 194 | break; 195 | case 0xD: 196 | result.addInt(sampleRate, 16); 197 | break; 198 | case 0xE: 199 | result.addInt(sampleRate/10, 16); 200 | break; 201 | } 202 | } 203 | if(DEBUG_LEV > 20 ) 204 | System.err.println("FrameHeader::createHeader : pre-CRC"); 205 | crcCalculator.reset(); 206 | crcCalculator.updateCRC8(result.getData(), 0, result.getTotalBits()/8); 207 | crc8 = crcCalculator.checksum(); 208 | if(DEBUG_LEV > 20 ) 209 | System.err.println("FrameHeader::createHeader : post-CRC"); 210 | result.addInt(crc8, 8); 211 | 212 | if(DEBUG_LEV > 0 ) 213 | System.err.println("FrameHeader::createHeader : End"); 214 | return result; 215 | } 216 | 217 | /** 218 | * Given a block size, select the proper bit settings to use according to 219 | * the FLAC stream. 220 | * @param blockSize 221 | * @return 222 | */ 223 | private static byte encodeBlockSize(int blockSize) { 224 | if(DEBUG_LEV > 0 ) 225 | System.err.println("FrameHeader::encodeBlockSize : Begin"); 226 | byte value = 0; 227 | int i; 228 | for(i = 0; i < definedBlockSizes.length; i++) { 229 | if(blockSize == definedBlockSizes[i]) { 230 | value = (byte)i; 231 | break; 232 | } 233 | } 234 | if(i >= definedBlockSizes.length) { 235 | if(blockSize <= 255) 236 | value = 0x6; 237 | else 238 | value = 0x7; 239 | } 240 | 241 | if(DEBUG_LEV > 0 ) 242 | System.err.println("FrameHeader::encodeBlockSize : End"); 243 | 244 | return value; 245 | } 246 | 247 | 248 | private static byte encodeSampleRate(int sampleRate) { 249 | if(DEBUG_LEV > 0 ) 250 | System.err.println("FrameHeader::encodeSampleRate : Begin"); 251 | byte value = 0; 252 | int i; 253 | for(i = 0; i < definedSampleRates.length; i++) { 254 | if(sampleRate == definedSampleRates[i]) { 255 | value = (byte)i; 256 | break; 257 | } 258 | } 259 | if(i >= definedSampleRates.length) { 260 | if(sampleRate % 1000 == 0 && sampleRate < 256000) 261 | value = 0xC; 262 | else if(sampleRate < 65536) 263 | value = 0xD; 264 | else if(sampleRate % 10 == 0 && sampleRate <= 655350) 265 | value = 0xE; 266 | else 267 | value = 0x0; 268 | } 269 | if(DEBUG_LEV > 0 ) 270 | System.err.println("FrameHeader::encodeSampleRate : End"); 271 | 272 | return value; 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FrameThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | import java.util.concurrent.locks.ReentrantLock; 22 | 23 | /** 24 | * The FrameThread class provides threading support for a Frame object, allowing 25 | * multi-threaded encodings of FLAC frames. It's job is to repeatedly get a 26 | * BlockEncodeRequest from a BlockThreadManager, and encode it. 27 | * 28 | * @author Preston Lacey 29 | */ 30 | public class FrameThread implements Runnable { 31 | Frame frame = null; 32 | ReentrantLock runLock = null; 33 | BlockThreadManager manager = null; 34 | /** 35 | * Constructor. Private to prevent it's use, as a Frame must be provided for 36 | * this FrameThread to be of any use. 37 | */ 38 | private FrameThread() {} 39 | 40 | /** 41 | * Constructor. Sets the Frame object that this FrameThread will use for 42 | * encodings. 43 | * 44 | * @param f Frame object to use for encoding. 45 | * @param manager BlockThreadManager to use as the BlockEncodeRequest source 46 | * and destination. 47 | */ 48 | public FrameThread(Frame f, BlockThreadManager manager) { 49 | super(); 50 | if(f == null) 51 | System.err.println("Frame is null. Error."); 52 | frame = f; 53 | runLock = new ReentrantLock(); 54 | this.manager = manager; 55 | } 56 | 57 | /** 58 | * Run method. This FrameThread will get a BlockEncodeRequest from the 59 | * BlockThreadManager, encode the block, return it to the manager, then 60 | * repeat. If no BlockEncodeRequest is available, or if it recieves a 61 | * request with the "frameNumber" field set to a negative value, it will 62 | * break the loop and end, notifying the manager it has ended. 63 | * 64 | */ 65 | public void run() { 66 | boolean process = true; 67 | synchronized(this) { 68 | BlockEncodeRequest ber = manager.getWaitingRequest(); 69 | if(ber != null && ber.frameNumber < 0) 70 | ber = null; 71 | while(ber != null && process) { 72 | if(ber.frameNumber < 0) { 73 | process = false; 74 | } 75 | else {//get available BlockEncodeRequest from manager 76 | ber.encodedSamples = frame.encodeSamples(ber.samples, ber.count, 77 | ber.start, ber.skip, ber.result, ber.frameNumber); 78 | ber.valid = true; 79 | manager.returnFinishedRequest(ber); 80 | ber = manager.getWaitingRequest(); 81 | } 82 | } 83 | manager.notifyFrameThreadExit(this); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/LPC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * This class is used to calculate LPC Coefficients for a FLAC stream. 24 | * 25 | * @author Preston Lacey 26 | */ 27 | public class LPC { 28 | /** The error calculated by the LPC algorithm */ 29 | protected double rawError; 30 | /** The coefficients as calculated by the LPC algorithm */ 31 | protected double[] rawCoefficients; 32 | private double[] tempCoefficients; 33 | /** The order of this LPC calculation */ 34 | protected int order; 35 | static int[] tempSum = null; 36 | /** 37 | * Constructor creates an LPC object of the given order. 38 | * @param order Order for this LPC calculation. 39 | */ 40 | public LPC(int order) { 41 | this.order = order; 42 | rawError = 0; 43 | rawCoefficients = new double[order+1]; 44 | tempCoefficients = new double[order+1]; 45 | } 46 | /** 47 | * Get this LPC object's order 48 | * @return order used for this LPC calculation. 49 | */ 50 | public int getOrder () { return order; } 51 | /** 52 | * Get the error for this LPC calculation 53 | * @return lpc error 54 | */ 55 | public double getError() { return rawError; } 56 | 57 | /** 58 | * Get the calculated LPC Coefficients as an array. 59 | * @return lpc coefficients in an array. 60 | */ 61 | public double[] getCoefficients() { return rawCoefficients; } 62 | 63 | /** 64 | * Calculate an LPC using the given Auto-correlation data. Static method 65 | * used since this is slightly faster than a more strictly object-oriented 66 | * approach. 67 | * 68 | * @param lpc LPC to calculate 69 | * @param R Autocorrelation data to use 70 | */ 71 | public static void calculate(LPC lpc, long[] R) { 72 | int coeffCount = lpc.order; 73 | 74 | //calculate first iteration directly 75 | double[] A = lpc.rawCoefficients; 76 | for(int i = 0; i < coeffCount+1; i++) A[i] = 0.0; 77 | A[0] = 1; 78 | double E = R[0]; 79 | 80 | //calculate remaining iterations 81 | 82 | if(R[0] == 0) { 83 | for(int i = 0; i < coeffCount+1; i++) 84 | A[i] = 0.0; 85 | } 86 | else { 87 | double[] ATemp = lpc.tempCoefficients; 88 | for(int i = 0; i < coeffCount+1; i++) ATemp[i] = 0.0; 89 | 90 | for(int k = 0; k < coeffCount; k++) { 91 | double lambda = 0.0; 92 | double temp = 0; 93 | for(int j = 0; j <= k; j++) { 94 | temp += A[j]*R[k+1-j]; 95 | } 96 | lambda = -temp/E; 97 | 98 | for(int i = 0; i <= k+1; i++) { 99 | ATemp[i] = A[i]+lambda*A[k+1-i]; 100 | } 101 | System.arraycopy(ATemp, 0, A, 0, coeffCount+1); 102 | E = (1-lambda*lambda)*E; 103 | } 104 | } 105 | lpc.rawError = E; 106 | } 107 | 108 | /** 109 | * Calculate an LPC using a prior order LPC's values to save calculations. 110 | * 111 | * @param lpc LPC to calculate 112 | * @param R Auto-correlation data to use. 113 | * @param priorLPC Prior order LPC to use(may be any order lower than our 114 | * target LPC) 115 | * 116 | */ 117 | public static void calculateFromPrior(LPC lpc, long[] R, LPC priorLPC) { 118 | int coeffCount = lpc.order; 119 | 120 | //calculate first iteration directly 121 | double[] A = lpc.rawCoefficients; 122 | for(int i = 0; i < coeffCount+1; i++) A[i] = 0.0; 123 | A[0] = 1; 124 | double E = R[0]; 125 | int startIter = 0; 126 | if(priorLPC != null && priorLPC.order < lpc.order) { 127 | startIter = priorLPC.order; 128 | E = priorLPC.rawError; 129 | System.arraycopy(priorLPC.rawCoefficients, 0, A, 0, startIter+1); 130 | } 131 | //calculate remaining iterations 132 | if(R[0] == 0) { 133 | for(int i = 0; i < coeffCount+1; i++) 134 | A[i] = 0.0; 135 | } 136 | else { 137 | double[] ATemp = lpc.tempCoefficients; 138 | for(int i = 0; i < coeffCount+1; i++) ATemp[i] = 0.0; 139 | 140 | for(int k = startIter; k < coeffCount; k++) { 141 | double lambda = 0.0; 142 | double temp = 0.0; 143 | for(int j = 0; j <= k; j++) { 144 | temp -= A[j]*R[k-j+1]; 145 | } 146 | lambda = temp/E; 147 | 148 | for(int i = 0; i <= k+1; i++) { 149 | ATemp[i] = A[i]+lambda*A[k+1-i]; 150 | } 151 | System.arraycopy(ATemp, 0, A, 0, coeffCount+1); 152 | E = (1-lambda*lambda)*E; 153 | } 154 | } 155 | lpc.rawError = E; 156 | } 157 | 158 | /** 159 | * Create auto-correlation coefficients(up to a maxOrder of 32). 160 | * @param R Array to put results in. 161 | * @param samples Samples to calculate the auto-correlation for. 162 | * @param count number of samples to use 163 | * @param start index of samples array to start at 164 | * @param increment number of indices to increment between valid samples(for 165 | * interleaved arrays) 166 | * @param maxOrder maximum order to calculate. 167 | */ 168 | public static void createAutoCorrelation(long[] R, int []samples, int count, 169 | int start, int increment, int maxOrder) { 170 | if(increment == 1 && start == 0) { 171 | for(int i = 0; i <= maxOrder; i++) { 172 | R[i] = 0; 173 | long temp = 0; 174 | for(int j = 0; j < count-i; j++) { 175 | temp += (long)samples[j]*(long)samples[j+i]; 176 | } 177 | R[i] += temp; 178 | } 179 | } 180 | else { 181 | for(int i = 0; i <= maxOrder; i++) { 182 | R[i] = 0; 183 | int baseIndex = increment*i; 184 | long temp = 0; 185 | int innerLimit = (count-i)*increment; 186 | for(int j = start; j < innerLimit; j+=increment) { 187 | temp += (long)samples[j]*(long)samples[j+baseIndex]; 188 | } 189 | R[i] += temp; 190 | } 191 | } 192 | } 193 | 194 | /** 195 | * Apply a window function to sample data 196 | * @param samples Samples to apply window to. Values in this array are left 197 | * unaltered. 198 | * @param count number of samples to use 199 | * @param start index of samples array to start at 200 | * @param increment number of indices to increment between valid samples(for 201 | * interleaved arrays) 202 | * @param windowedSamples array containing windowed values. Return values 203 | * are packed(increment of one). 204 | * 205 | */ 206 | public static void window(int[] samples, int count, int start, int increment, 207 | int[] windowedSamples) { 208 | int[] values = windowedSamples; 209 | int loopCount = 0; 210 | float halfway = count/2.0f; 211 | float hth = halfway*halfway; 212 | float windowCount = -halfway; 213 | int limit = count*increment+start; 214 | for(int i = start; i < limit; i+=increment) { 215 | //float innerCount = (windowCount < 0) ? -windowCount:windowCount; 216 | float innerCountSquared = windowCount*windowCount; 217 | windowCount++; 218 | //double val = 1.0-(double)(innerCount/halfway); 219 | float val = 1.0f-( innerCountSquared/hth ); 220 | double temp = ((double)samples[i])*val; 221 | temp = (temp >0) ? temp+0.5:temp-0.5; 222 | values[loopCount++] = (int)temp; 223 | } 224 | } 225 | 226 | } 227 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/MetadataBlockHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * The MetadataBlockHeader class is used to creat FLAC compliant Metadata Block 24 | * Headers. See the FLAC specification for more information. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class MetadataBlockHeader { 29 | 30 | //boolean lastMetadataBlockFlag;//1 bit used 31 | //byte blockType;//7 bits used 32 | //int length;//24 bits used 33 | 34 | /** 35 | * Enum containing the different Metadata block types. See the FLAC spec 36 | * for more information on the various types. 37 | */ 38 | public enum MetadataBlockType { 39 | /** A meta-block containing stream configuration information */ 40 | STREAMINFO, 41 | /** A meta-block to pad the stream, allowing other meta-data to be 42 | * written in the future without re-writing the entire stream. 43 | */ 44 | PADDING, 45 | /** Application meta-block*/ 46 | APPLICATION, 47 | /** A meta-block which aids in seeking in the stream */ 48 | SEEKTABLE, 49 | /** A meta-block for tags/comments */ 50 | VORBIS_COMMENT, 51 | /** Cuesheet meta-block */ 52 | CUESHEET, 53 | /** A meta-block to store an image, such as cover-art */ 54 | PICTURE 55 | }; 56 | 57 | /** 58 | * Constructor. This class defines no instance variables and only static 59 | * methods. 60 | */ 61 | public MetadataBlockHeader() { 62 | 63 | } 64 | 65 | 66 | /** 67 | * Create a meta-data block header of the given type, and return the result 68 | * in a new EncodedElement(so data is ready to be placed directly in FLAC 69 | * stream) 70 | * 71 | * @param lastBlock True if this is the last meta-block in the stream. False 72 | * otherwise. 73 | * 74 | * @param type enum indicating which type of block we're creating. 75 | * @param length Length of the meta-data block which follows this header. 76 | * @return EncodedElement containing the header. 77 | */ 78 | public static EncodedElement getMetadataBlockHeader(boolean lastBlock, 79 | MetadataBlockType type, int length) { 80 | EncodedElement ele = new EncodedElement(4, 0); 81 | int encodedLastBlock = (lastBlock) ? 1:0; 82 | ele.addInt(encodedLastBlock, 1); 83 | int encodedType = 0; 84 | MetadataBlockType[] vals = MetadataBlockType.values(); 85 | for(int i = 0; i < vals.length; i++) { 86 | if(vals[i] == type) { 87 | encodedType = i; 88 | break; 89 | } 90 | } 91 | 92 | ele.addInt(encodedType, 7); 93 | ele.addInt(length, 24); 94 | return ele; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/MetadataBlockStreamInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * MetadataBlockStreamInfo is used to declare the initial stream parameters, 24 | * such as Sample Rate, bits per sample, and number of channels, as well as 25 | * information on the encoded stream such as total number of samples, minimum 26 | * and maximum block and frame sizes, and md5sum of raw audio samples. A 27 | * StreamInfo block must be the first meta-data block in a FLAC stream, and only 28 | * one StreamInfo block may exist. 29 | * 30 | * @author Preston Lacey 31 | */ 32 | public class MetadataBlockStreamInfo { 33 | /** For Debugging: Higher level equals more debug statements */ 34 | static int DEBUG_LEV = 0; 35 | /* int minimumBlockSize = 4096;//32768;//16 bits used; 16 minimum valid 36 | int maximumBlockSize = 4096;//32768;//16 bits used; 65535 maximum valid 37 | int minimumFrameSize = 0;//24 bits used; zero implies unknown 38 | int maximumFrameSize = 0;//24 bits used; zero implies unknown 39 | int sampleRate = 8000;//4096;//20 bits used, but 655350Hz max. 0 is invalid. 40 | byte numberOfChannels = 1;//3 bits used 41 | byte bitsPerSample = 16;//5 bits used. values 4-32 valid 42 | long totalSamplesInStream = 0;//36 bits used. 0 implies unknown. 43 | byte[] md5Hash; 44 | */ 45 | /** 46 | * Constructor. This class defines only static methods and fields. 47 | */ 48 | public MetadataBlockStreamInfo() { 49 | 50 | } 51 | 52 | /** 53 | * Create a FLAC StreamInfo metadata block with the given parameters. Because 54 | * of the data stored in a StreamInfo block, this should generally be created 55 | * only after all encoding is done. 56 | * 57 | * @param sc StreamConfiguration used in this FLAC stream. 58 | * @param minFrameSize Size of smallest frame in FLAC stream. 59 | * @param maxFrameSize Size of largest frame in FLAC stream. 60 | * @param samplesInStream Total number of inter-channel audio samples in 61 | * FLAC stream. 62 | * @param md5Hash MD5 hash of the raw audio samples. 63 | * @return EncodedElement containing created StreamInfo block. 64 | */ 65 | public static EncodedElement getStreamInfo(StreamConfiguration sc, 66 | int minFrameSize, int maxFrameSize, long samplesInStream, byte[] md5Hash) { 67 | int bytes = getByteSize(); 68 | EncodedElement ele = new EncodedElement(bytes, 0); 69 | int encodedBitsPerSample = sc.getBitsPerSample()-1; 70 | ele.addInt(sc.getMinBlockSize(), 16); 71 | ele.addInt(sc.getMaxBlockSize(), 16); 72 | ele.addInt(minFrameSize, 24); 73 | ele.addInt(maxFrameSize, 24); 74 | ele.addInt(sc.getSampleRate(), 20); 75 | ele.addInt(sc.getChannelCount()-1, 3); 76 | ele.addInt(encodedBitsPerSample, 5); 77 | ele.addLong(samplesInStream, 36); 78 | for(int i = 0; i < 16; i++) { 79 | ele.addInt(md5Hash[i], 8); 80 | } 81 | return ele; 82 | } 83 | 84 | /** 85 | * Get the expected size of a properly formed STREAMINFO metadata block. 86 | * 87 | * @return size of properly formed FLAC STREAMINFO metadata block. 88 | */ 89 | static public int getByteSize() { 90 | int size = 0; 91 | size += 16; 92 | size += 16; 93 | size += 24; 94 | size += 24; 95 | size += 20; 96 | size += 3; 97 | size += 5; 98 | size += 36; 99 | size += 64; 100 | size += 64; 101 | size = size/8; 102 | return size; 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/README: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | The javaFlacEncoder project provides a FLAC encoder implemented in java. 21 | It is designed to enable easy addition of FLAC encoding support in java 22 | applications, though a command line file encoding utility is included as well. 23 | For usage of the command line encoder, see the "Console Usage" section below. 24 | 25 | For more information, go to http://javaflacencoder.sourceforge.net. 26 | See javadocs for information on how to make use of this library in your own 27 | application. 28 | 29 | 30 | 31 | Console Usage: 32 | [options] inputFilename outputFilename 33 | note: will depend on how your version is packaged, if it's 34 | in a tar file, it will be similar to 35 | java -classpath javaFlacEncoder/FLAC_ConsoleFileEncoder 36 | 37 | options: 38 | -bmin minimum block size, where is an integer in range (16-65535) 39 | -bmax maximum block size, where is an integer in range (16-65535) 40 | -lpcmin minimum LPC order, where is an integer in range (1-32) 41 | -lpcmax maximum LPC order, where is an integer in range (1-32) 42 | -Threads Specify whether to use threads. 0 turns threading off, greater 43 | than 0 turns threading on 44 | -sf Specify which subframe type to use, where may be: 45 | exhaustive(this is default and recommended) 46 | fixed 47 | lpc 48 | verbatim; 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/RiceEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * The RiceEncoder class is used to create FLAC-compliant rice-encoded 24 | * residuals. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class RiceEncoder { 29 | /** For debugging: Higher values equals greater output, generally in 30 | * increments of 10 */ 31 | public static int DEBUG_LEV = 0; 32 | 33 | private static final int POSITIVE = 0; 34 | private static final int NEGATIVE = 1; 35 | private static final int STOP_BIT = 0xFFFFFFFF; 36 | private static final int UNARY_BIT = 0; 37 | 38 | private int[] _dataToEncode = null; 39 | private int[] _bitsToEncode = null; 40 | 41 | /** 42 | * Constructor. A RiceEncoder object is used(as opposed to potentially 43 | * faster static methods), for memory considerations; some temporary, 44 | * dynamically created arrays are kept between calls to the encode methods 45 | * to prevent frequently allocating and freeing similarly sized arrays. 46 | */ 47 | public RiceEncoder() { 48 | 49 | } 50 | 51 | /** 52 | * Create the residual headers for a FLAC stream. 53 | * 54 | * @param useFiveBitParam Set TRUE if using a five-bit parameter size, FALSE 55 | * for a four-bit parameter 56 | * @param order Specify order of partitions to be used(actual number of 57 | * partitions will be 2^order. 58 | * @param ele EncodedElement to write header to. 59 | * @return total written size of header. 60 | */ 61 | public static int beginResidual(boolean useFiveBitParam, byte order, 62 | EncodedElement ele) { 63 | ele = ele.getEnd(); 64 | int paramSize = (useFiveBitParam) ? 1:0; 65 | ele.addInt(paramSize, 2); 66 | ele.addInt(order, 4); 67 | return 6; 68 | } 69 | 70 | public static int encodeRicePartitionEscaped(int[] values, int inputOffset, 71 | int inputStep, int inputCount, EncodedElement destEle, 72 | int bitParam, boolean fiveBitParam) { 73 | if(DEBUG_LEV > 0) 74 | System.err.println("RiceEncoder::encode : Begin"); 75 | /* Currently, we're passing in an EncodedElement with a set byte[], and 76 | filling that array. We should therefore ensure that we're not writing 77 | too much to it. We *can* add another element to the given one if need.*/ 78 | 79 | //write headers(i.e, write the parameter) 80 | int bitsWritten = 0; 81 | if(fiveBitParam) { 82 | destEle.addInt(255, 5); 83 | destEle.addInt(16,5); 84 | bitsWritten += 10; 85 | } 86 | else { 87 | destEle.addInt(255, 4); 88 | destEle.addInt(16,5); 89 | bitsWritten += 9; 90 | } 91 | 92 | for(int i = 0; i < inputCount; i++) { 93 | destEle.addInt(values[i*inputStep+inputOffset], 16); 94 | bitsWritten += 16; 95 | } 96 | 97 | if(DEBUG_LEV > 0) 98 | System.err.println("RiceEncoder::encode : End"); 99 | return bitsWritten; 100 | } 101 | 102 | /** 103 | * Rice-encode a set of values, adding necessary headers for FLAC format. This 104 | * encodes a single rice-partition. In general, beginResidual(...) should be 105 | * used before this method. 106 | * 107 | * @param values array of integer values to save 108 | * @param inputOffset start index in input array 109 | * @param inputStep number of values to skip between target values(for 110 | * interleaved data. 111 | * @param inputCount number of total values to encode 112 | * @param bitParam rice-parameter to use. This value should be based upon 113 | * the distribution of the input data(roughly speeking, each value 114 | * will require at least bitParam+1 bits to save, so this value 115 | * should reflect the average magnitude of input values. 116 | * @param destEle EncodedElement to save result to. 117 | * @param fiveBitParam Set true if this header should use a five-bit 118 | * rice-parameter, false for a four bit parameter. 119 | * @return total encoded size(including headers) 120 | */ 121 | public int encodeRicePartition(int[] values, int inputOffset, 122 | int inputStep, int inputCount, EncodedElement destEle, 123 | final int bitParam, boolean fiveBitParam) { 124 | //Pack int version of encode partition 125 | if(DEBUG_LEV > 0) { 126 | System.err.println("RiceEncoder::encode : Begin"); 127 | System.err.println("-- bitParam: " + bitParam); 128 | } 129 | 130 | //write headers(i.e, write the parameter) 131 | int startBits = destEle.getTotalBits(); 132 | if(fiveBitParam) { 133 | destEle.addInt(bitParam, 5); 134 | } 135 | else { 136 | destEle.addInt(bitParam, 4); 137 | } 138 | //encode each input value; 139 | if(_dataToEncode == null || _bitsToEncode == null) { 140 | _dataToEncode = new int[values.length*4]; 141 | _bitsToEncode = new int[values.length*4]; 142 | } 143 | int[] dataToEncode = _dataToEncode; 144 | int[] bitsToEncode = _bitsToEncode; 145 | int nextToEncode = 0; 146 | int maxToEncode = dataToEncode.length; 147 | int inputIndex = inputOffset-inputStep; 148 | final int stopbit = (bitParam >0) ? STOP_BIT << bitParam:STOP_BIT; 149 | final int maskParam = (bitParam == 0) ? 0:0xFFFFFFFF>>>(32-bitParam); 150 | for(int i = 0; i < inputCount; i++) { 151 | inputIndex +=inputStep; 152 | int value = values[inputIndex]; 153 | value = (value < 0) ? -2*value-1:2*value; 154 | int upperBits = value >> bitParam; 155 | //make sure we won't write to much. Handle if we will. 156 | int dataToEncodeSpaceNeeded = (2+upperBits/32); 157 | if(upperBits%32 != 0) 158 | dataToEncodeSpaceNeeded++; 159 | if(dataToEncodeSpaceNeeded+nextToEncode >= maxToEncode) { 160 | //write everything we have: 161 | destEle.packIntByBits(dataToEncode, bitsToEncode, 0, nextToEncode); 162 | nextToEncode = 0; 163 | } 164 | //write unary upper bits: 165 | int count = 0; 166 | while(upperBits > 0) { 167 | int tempVal = (upperBits > 32) ? 32:upperBits;//can only write 32 bits at a time. 168 | dataToEncode[nextToEncode] = UNARY_BIT; 169 | bitsToEncode[nextToEncode++] = tempVal; 170 | upperBits -= tempVal; 171 | count++; 172 | } 173 | dataToEncode[nextToEncode] = (value&maskParam) | stopbit ; 174 | bitsToEncode[nextToEncode++] = bitParam+1; 175 | } 176 | //System.err.println("end loop"); 177 | //write remaining data to encode 178 | if(nextToEncode > 0) { 179 | destEle.packIntByBits(dataToEncode, bitsToEncode, 0, nextToEncode); 180 | nextToEncode = 0; 181 | } 182 | int bitsWritten = destEle.getTotalBits() - startBits; 183 | //System.err.println("RiceENcoder encode end:"); 184 | return bitsWritten; 185 | } 186 | 187 | /** 188 | * Calculate how large a given set of values will be once it has been 189 | * rice-encoded. While this method duplicates much of the process of 190 | * rice-encoding, it is faster than an actual encode since the data is not 191 | * actually written to the flac bitstream format(a rather costly write). 192 | * 193 | * @param values array of integer values to save 194 | * @param inputOffset start index in input array 195 | * @param inputStep number of values to skip between target values(for 196 | * interleaved data. 197 | * @param inputCount number of total values to encode 198 | * @param bitParam rice-parameter to use. This value should be based upon 199 | * the distribution of the input data(roughly speeking, each value 200 | * will require at least bitParam+1 bits to save, so this value 201 | * should reflect the average magnitude of input values. 202 | * @return total encoded-size with given data and rice-parameter. 203 | */ 204 | public static int calculateEncodeSize(int[] values, int inputOffset, 205 | int inputStep, int inputCount, int bitParam) { 206 | //Pack int version of encode partition 207 | if(DEBUG_LEV > 0) { 208 | System.err.println("RiceEncoder::calculateEncodeSize : Begin"); 209 | System.err.println("-- bitParam: " + bitParam); 210 | } 211 | int totalEncodeLength = inputCount*(bitParam+1); 212 | int index = inputOffset-inputStep; 213 | for(int i = 0; i < inputCount; i++) { 214 | index += inputStep; 215 | int value = values[index]; 216 | value = (value < 0) ? -2*value-1:2*value; 217 | int upperBits = value >> bitParam; 218 | totalEncodeLength += upperBits; 219 | } 220 | if(bitParam > 14) 221 | totalEncodeLength += 5+6; 222 | else 223 | totalEncodeLength += 4+6; 224 | return totalEncodeLength; 225 | } 226 | 227 | } 228 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/StreamConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * This class defines the configuration options that may not change throughout 24 | * a FLAC stream. In general, these setting must be set to match the input 25 | * audio used(sample rate, sample size, channels, etc). After a stream has 26 | * started, these settings must not change. 27 | * 28 | * @author Preston Lacey 29 | */ 30 | public class StreamConfiguration implements Cloneable { 31 | 32 | /** Maximum Block size allowed(defined by flac spec) */ 33 | public static final int MAX_BLOCK_SIZE = 65535; 34 | /** Minimum block size allowed(defined by flac spec) */ 35 | public static final int MIN_BLOCK_SIZE = 16 ; 36 | /** Maximum channel count allowed(defined by flac spec) */ 37 | public static final int MAX_CHANNEL_COUNT = 8; 38 | /** Minimum sample rate allowed(defined by flac spec) */ 39 | public static final int MIN_SAMPLE_RATE = 1; 40 | /** Maximum sample rate allowed(defined by flac spec) */ 41 | public static final int MAX_SAMPLE_RATE = 655350; 42 | /** Minimum bits per sample allowed(defined by flac spec) */ 43 | public static final int MIN_BITS_PER_SAMPLE = 4; 44 | /** Maximum bits per sample allowed(FLAC spec allows 32, limited to 24 here 45 | * due to limits in code) */ 46 | public static final int MAX_BITS_PER_SAMPLE = 24; 47 | /** Default channel count */ 48 | public static final int DEFAULT_CHANNEL_COUNT = 2; 49 | /** Default maximum block size */ 50 | public static final int DEFAULT_MAX_BLOCK_SIZE = 4096; 51 | /** Default minimum block size */ 52 | public static final int DEFAULT_MIN_BLOCK_SIZE = 4096; 53 | /** Default sample rate */ 54 | public static final int DEFAULT_SAMPLE_RATE = 44100; 55 | /** Default sample size */ 56 | public static final int DEFAULT_BITS_PER_SAMPLE = 16; 57 | 58 | int channelCount; 59 | int maxBlockSize; 60 | int minBlockSize; 61 | int sampleRate; 62 | int bitsPerSample; 63 | 64 | /* is the currently set configuration valid? Encoders should not attempt to 65 | * use an invalid configuration */ 66 | boolean validConfig = false; 67 | 68 | /** 69 | * Constructor, sets defaults for most values. Some default values must be 70 | * changed to match the audio characteristics(channel count, sample rate, 71 | * sample size). 72 | */ 73 | public StreamConfiguration() { 74 | channelCount = DEFAULT_CHANNEL_COUNT; 75 | maxBlockSize = DEFAULT_MAX_BLOCK_SIZE; 76 | minBlockSize = DEFAULT_MIN_BLOCK_SIZE; 77 | sampleRate = DEFAULT_SAMPLE_RATE; 78 | bitsPerSample = DEFAULT_BITS_PER_SAMPLE; 79 | validConfig = true; 80 | } 81 | 82 | /** 83 | * Copy Constructor. No values are altered or verified for sanity 84 | * @param sc StreamConfiguration object to copy 85 | */ 86 | public StreamConfiguration(StreamConfiguration sc) { 87 | channelCount = sc.channelCount; 88 | maxBlockSize = sc.maxBlockSize; 89 | minBlockSize = sc.minBlockSize; 90 | sampleRate = sc.sampleRate; 91 | bitsPerSample = sc.bitsPerSample; 92 | validConfig = sc.validConfig; 93 | } 94 | 95 | /** 96 | * Constructor, allows setting of all options. In general, parameters given 97 | * must match the input audio characteristics. minBlock and maxBlock may be 98 | * set as desired, though minBlock is expected to be less than or equal to 99 | * maxBlock. If minBlock or maxBlock is out of a valid range, it will be 100 | * automatically adjusted to the closest valid value. 101 | * 102 | * @param channelCount number of channels in source audio stream 103 | * @param minBlock minimum block to use in FLAC stream. 104 | * @param maxBlock maximum block size to use in FLAC stream 105 | * @param sampleRate sample rate in Hz of audio stream 106 | * @param bitsPerSample sample size of audio stream 107 | */ 108 | public StreamConfiguration(int channelCount, int minBlock, int maxBlock, 109 | int sampleRate, int bitsPerSample) { 110 | validConfig = true; 111 | validConfig &= setChannelCount(channelCount); 112 | validConfig &= setSampleRate(sampleRate); 113 | validConfig &= setBitsPerSample(bitsPerSample); 114 | setMaxBlockSize(maxBlock); 115 | setMinBlockSize(minBlock); 116 | } 117 | 118 | /** 119 | * Test if the current configuration is valid. While most set methods will 120 | * ensure the values are in a valid range before setting them, some 121 | * settings(such as number of channels), cannot be guessed. This method may 122 | * alter current values to make them valid if possible. 123 | * @return true if configuration defines a valid FLAC stream, false othwerise. 124 | */ 125 | public boolean isValid() { 126 | validConfig = true; 127 | setMinBlockSize(minBlockSize); 128 | setMaxBlockSize(maxBlockSize); 129 | validConfig &= (minBlockSize <= maxBlockSize); 130 | validConfig &= setChannelCount(channelCount); 131 | validConfig &= setSampleRate(sampleRate); 132 | validConfig &= setBitsPerSample(bitsPerSample); 133 | return validConfig; 134 | } 135 | 136 | /** 137 | * Set number of channels in stream. Because this is not a value that may be 138 | * guessed and corrected, the value will be set to that given even if it is 139 | * not valid. 140 | * @param count Number of channels 141 | * @return true if the channel count is within the valid range, false 142 | * otherwise. 143 | */ 144 | public boolean setChannelCount(int count) { 145 | boolean result = count > 0 && count <= MAX_CHANNEL_COUNT; 146 | channelCount = count; 147 | return result; 148 | } 149 | 150 | /** 151 | * Get the currently set channel count 152 | * @return channel count 153 | */ 154 | public int getChannelCount() { 155 | return channelCount; 156 | } 157 | 158 | /** 159 | * Get the currently set maximum block size 160 | * @return maximum block size 161 | */ 162 | public int getMaxBlockSize() { 163 | return maxBlockSize; 164 | } 165 | 166 | /** 167 | * Get the currently set minimum block size 168 | * @return minimum block size 169 | */ 170 | public int getMinBlockSize() { 171 | return minBlockSize; 172 | } 173 | 174 | /** 175 | * Get the currently set sample rate 176 | * @return sample rate(in Hz) 177 | */ 178 | public int getSampleRate() { 179 | return sampleRate; 180 | } 181 | 182 | /** 183 | * Set the sample rate. Because this is not a value that may be 184 | * guessed and corrected, the value will be set to that given even if it is 185 | * not valid. 186 | * @param rate sample rate(in Hz) 187 | * @return true if given rate was within the valid range, false otherwise. 188 | */ 189 | public boolean setSampleRate(int rate) { 190 | boolean result = (rate <= MAX_SAMPLE_RATE && rate >= MIN_SAMPLE_RATE); 191 | sampleRate = rate; 192 | return result; 193 | } 194 | 195 | /** 196 | * Get the number of bits per sample 197 | * @return bits per sample 198 | */ 199 | public int getBitsPerSample() { 200 | return bitsPerSample; 201 | } 202 | 203 | /** 204 | * Set the bits per sample. Because this is not a value that may be 205 | * guessed and corrected, the value will be set to that given even if it is 206 | * not valid. 207 | * @param bitsPerSample number of bits per sample 208 | * @return true if value given is within the valid range, false otherwise. 209 | */ 210 | public boolean setBitsPerSample(int bitsPerSample) { 211 | boolean result = ((bitsPerSample <= MAX_BITS_PER_SAMPLE) && 212 | (bitsPerSample >= MIN_BITS_PER_SAMPLE) ); 213 | this.bitsPerSample = bitsPerSample; 214 | return result; 215 | } 216 | 217 | /** 218 | * Set the maximum block size to use. If this value is out of a valid range, 219 | * it will be set to the closest valid value. User must ensure that this 220 | * value is set above or equal to the minimum block size. 221 | * @param size maximum block size to use. 222 | * @return actual size set 223 | */ 224 | public int setMaxBlockSize(int size) { 225 | maxBlockSize = (size <= MAX_BLOCK_SIZE) ? size:MAX_BLOCK_SIZE; 226 | maxBlockSize = (maxBlockSize >= MIN_BLOCK_SIZE) ? maxBlockSize:MIN_BLOCK_SIZE; 227 | return maxBlockSize; 228 | } 229 | 230 | /** 231 | * Set the minimum block size to use. If this value is out of a valid range, 232 | * it will be set to the closest valid value. User must ensure that this 233 | * value is set below or equal to the maximum block size. 234 | * @param size minimum block size to use. 235 | * @return actual size set 236 | */ 237 | public int setMinBlockSize(int size) { 238 | minBlockSize = (size <= MAX_BLOCK_SIZE) ? size:MAX_BLOCK_SIZE; 239 | minBlockSize = (minBlockSize >= MIN_BLOCK_SIZE) ? maxBlockSize:MIN_BLOCK_SIZE; 240 | return minBlockSize; 241 | } 242 | 243 | /** 244 | * Test if stream is Subset compliant. FLAC defines a subset of options to 245 | * ensure resulting FLAC streams are streamable. Not all options in that 246 | * subset are defined by the StreamConfiguration class, however, but exist 247 | * in the EncodingConfiguration class. Therefore, the alternative method 248 | * {@link StreamConfiguration#isEncodingSubsetCompliant(javaFlacEncoder.EncodingConfiguration) 249 | * isEncodingSubsetCompliant} 250 | * should be checked as well to ensure the combined Stream/Encoding 251 | * configurations are BOTH valid. 252 | * @return true if this configuration is Subset compliant, false otherwise. 253 | */ 254 | public boolean isStreamSubsetCompliant() { 255 | boolean result = true; 256 | result &= (maxBlockSize < 16384); 257 | if(sampleRate <= 48000) 258 | result &= (maxBlockSize < 4608); 259 | switch(bitsPerSample) { 260 | case 8: 261 | case 12: 262 | case 16: 263 | case 20: 264 | case 24: break; 265 | default: result = false; 266 | } 267 | return result; 268 | } 269 | 270 | /** 271 | * Test if this StreamConfiguration and a paired EncodingConfiguration define 272 | * a Subset compliant stream. FLAC defines a subset of options to 273 | * ensure resulting FLAC streams are streamable. 274 | * @param ec EncodingConfiguration object to check against 275 | * @return true if these configurations are Subset compliant, false otherwise. 276 | */ 277 | public boolean isEncodingSubsetCompliant(EncodingConfiguration ec) { 278 | boolean result = true; 279 | result = isStreamSubsetCompliant(); 280 | if(this.sampleRate <= 48000) { 281 | result &= ec.maximumLPCOrder <= 12; 282 | result &= ec.maximumRicePartitionOrder <= 8; 283 | } 284 | return result; 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/Subframe.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * Description: This abstract class declares the methods needed to retrieve 24 | * encoded data in a standard format(across the different implemented Subframe 25 | * types), as well as the generic methods needed to write the subframe header. 26 | * It is assumed that objects of this type will be reused in future frames, 27 | * rather than being destroyed and made anew. This is to avoid the overhead 28 | * associated with creating and destroying objects in Java. 29 | * @author Preston Lacey 30 | */ 31 | public abstract class Subframe { 32 | 33 | /** StreamConfiguration used for encoding. The same StreamConfiguration 34 | * MUST be used throughout an entire FLAC stream */ 35 | protected StreamConfiguration sc; 36 | 37 | /** EncodingConfiguration used for encoding. This may be altered between 38 | * frames, but must be the same for each subframe within that frame */ 39 | protected EncodingConfiguration ec; 40 | /** Store for size of last subframe encoded(in bits). */ 41 | protected int lastEncodedSize; 42 | 43 | /** 44 | * Constructor is private to prevenet it's use, as a subframe is not usable 45 | * without first setting a StreamConfiguration(therefore, use other 46 | * constructor. 47 | */ 48 | private Subframe() { 49 | 50 | } 51 | 52 | /** 53 | * Constructor. Sets StreamConfiguration to use. If the StreamConfiguration 54 | * must later be changed, a new Subframe object must be created as well. A 55 | * default EncodingConfiguration is created using EncodingConfiguration's 56 | * default Constructor. This configuration should create a decent encode, 57 | * but may be altered if desired. 58 | * 59 | * @param sc StreamConfiguration to use for encoding. 60 | */ 61 | public Subframe(StreamConfiguration sc) { 62 | this.sc = sc; 63 | ec = new EncodingConfiguration(); 64 | } 65 | 66 | /** 67 | * This method is used to set the encoding configuration. 68 | * @param ec encoding configuration to use. 69 | * @return true if configuration was changed, false otherwise 70 | */ 71 | public boolean registerConfiguration(EncodingConfiguration ec) { 72 | this.ec = ec; 73 | return true; 74 | } 75 | 76 | /** 77 | * Encodes samples into the appropriate compressed format, saving the result 78 | * in the given “data” EncodedElement list. Encodes 'count' samples, from 79 | * index 'start', to index 'start' times 'skip', where “skip” is the format 80 | * that samples may be packed in an array. For example, 'samples' may 81 | * include both left and right samples of a stereo stream, while this 82 | * SubFrame is only encoding the 'right' channel(channel 2). Therefore, 83 | * “skip” would equal 2, resulting in the valid indices being only those 84 | * where “index mod 2 equals 1”. 85 | * @param samples the audio samples to encode. This array may contain 86 | * samples for multiple channels, interleaved; only one of these channels is 87 | * encoded by a subframe. 88 | * @param count the number of samples to encode. 89 | * @param start the index to start at in the array. 90 | * @param skip the number of indices to skip between successive samples 91 | * (for use when channels are interleaved in the given 92 | * array). 93 | * @param data the EncodedElement to attach encoded data to. Data in 94 | * EncodedElement given is not altered. New data is 95 | * attached starting with “data.getNext()”. If “data” 96 | * already has a “next” set, it will be lost! 97 | * @param offset 98 | * @param bitsPerSample Number of bits per single-channel sample. This may 99 | * differ from the StreamConfiguration's sample size, depending on the 100 | * subframe used(i.e, the "side-channel" of a FLAC stream uses one extra bit 101 | * compared to the input channels). 102 | * 103 | * @return number of encoded samples, or negative value indicating 104 | * an error has occurred. 105 | * 106 | */ 107 | public abstract int encodeSamples(int[] samples, int count, int start, int skip, 108 | EncodedElement data, int offset, int bitsPerSample); 109 | 110 | /** 111 | * Returns the total number of valid bits used in the last encoding(i.e, the 112 | * number of compressed bits used). This is here for convenience, as the 113 | * calling object may also loop through the resulting EncodingElement from 114 | * the encoding process and sum the valid bits. 115 | * 116 | * @return an integer with value of the number of bits used in last 117 | * encoding. 118 | */ 119 | public int getEncodedSize() { 120 | return lastEncodedSize; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/Subframe_Constant.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * Implements the Subframe abstract class, providing encoding support for the 24 | * FLAC Constant Subframe. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class Subframe_Constant extends Subframe { 29 | /** For debugging: Higher values equals greater output, generally by 30 | * increments of 10. */ 31 | public static int DEBUG_LEV = 0; 32 | /** Subframe type supported by this implementation. */ 33 | public static final EncodingConfiguration.SubframeType type = 34 | EncodingConfiguration.SubframeType.VERBATIM; 35 | int sampleSize = 0; 36 | 37 | /** 38 | * Constructor. Sets StreamConfiguration to use. If the StreamConfiguration 39 | * must later be changed, a new Subframe object must be created as well. 40 | * 41 | * @param sc StreamConfiguration to use for encoding. 42 | */ 43 | public Subframe_Constant(StreamConfiguration sc) { 44 | super(sc); 45 | sampleSize = sc.getBitsPerSample(); 46 | } 47 | 48 | /** 49 | * This method is used to set the encoding configuration. 50 | * @param ec encoding configuration to use. 51 | * @return true if configuration was changed, false otherwise 52 | */ 53 | @Override 54 | public boolean registerConfiguration(EncodingConfiguration ec) { 55 | super.registerConfiguration(ec); 56 | return true; 57 | } 58 | 59 | 60 | public int encodeSamples(int[] samples, int count, int start, int skip, 61 | EncodedElement data, int offset, int bitsPerSample ) { 62 | if(DEBUG_LEV > 0) { 63 | System.err.println("Subframe_Verbatim::encodeSamples(...)"); 64 | } 65 | int encodedSamples = count; 66 | int bits = bitsPerSample+offset+8; 67 | int bytesNeeded = bits/8; 68 | if(bits%8 != 0) 69 | bytesNeeded++; 70 | data.clear(bytesNeeded, offset); 71 | data.addInt(0,1); 72 | data.addInt(0,6); 73 | data.addInt(0,1); 74 | 75 | int value = samples[start]; 76 | int increment = skip+1; 77 | int end = start+increment*count; 78 | int lastValid = end-increment;//assume all were the same 79 | for(int i = start; i < end; i+= increment) { 80 | if(samples[i] != value) { 81 | lastValid = i-increment;//if one differed, find where 82 | break; 83 | } 84 | } 85 | encodedSamples = (lastValid-start)/increment+1; 86 | data.addInt(value,bitsPerSample); 87 | lastEncodedSize = bits - offset; 88 | System.out.flush(); 89 | if(DEBUG_LEV > 0) 90 | System.err.println("Subframe_Verbatim::encodeSamples(...): End"); 91 | if(DEBUG_LEV > 10) { 92 | System.err.println("--: bitsUsed : "+bits+" : Bytes : "+bytesNeeded); 93 | } 94 | return encodedSamples; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/Subframe_Fixed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * Implements the Subframe abstract class, providing encoding support for the 24 | * FLAC Fixed-predictor Subframe. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class Subframe_Fixed extends Subframe { 29 | /** For debugging: Higher values equals greater output, generally in 30 | * increments of 10 */ 31 | public static int DEBUG_LEV = 0; 32 | /** Subframe type supported by this implementation. */ 33 | public static final EncodingConfiguration.SubframeType type = 34 | EncodingConfiguration.SubframeType.FIXED; 35 | int sampleSize = 0; 36 | RiceEncoder rice = null; 37 | int [] bits; 38 | int [] lowOrderBits; 39 | long [] sum; 40 | 41 | int _error1[] = null; 42 | int _error2[] = null; 43 | int _error3[] = null; 44 | int _error4[] = null; 45 | int _lastCount = 0; 46 | int _order; 47 | int[] _errors = null; 48 | int _offset = 0; 49 | int _start = 0; 50 | int _skip = 0; 51 | int _errorStep = 0; 52 | int _totalBits; 53 | int[] _samples = null; 54 | int _errorOffset = 0; 55 | int _errorCount = 0; 56 | int _frameSampleSize = 0; 57 | 58 | private static final double LOG_2 = Math.log(2); 59 | 60 | /** 61 | * Constructor. Sets StreamConfiguration to use. If the StreamConfiguration 62 | * must later be changed, a new Subframe object must be created as well. 63 | * 64 | * @param sc StreamConfiguration to use for encoding. 65 | */ 66 | public Subframe_Fixed(StreamConfiguration sc) { 67 | super(sc); 68 | sampleSize = sc.getBitsPerSample(); 69 | rice = new RiceEncoder(); 70 | bits = new int[5]; 71 | lowOrderBits = new int[5]; 72 | sum = new long[5]; 73 | _lastCount = -1; 74 | } 75 | 76 | /** 77 | * This method is used to set the encoding configuration. 78 | * @param ec encoding configuration to use. 79 | * @return true if configuration was changed, false otherwise 80 | */ 81 | @Override 82 | public boolean registerConfiguration(EncodingConfiguration ec) { 83 | super.registerConfiguration(ec); 84 | return true; 85 | } 86 | 87 | public int encodeSamples(int[] samples, int count, int start, int skip, 88 | int offset, int unencSampleSize ) { 89 | int encodedSamples = count; 90 | if(DEBUG_LEV > 0) { 91 | System.err.println("Subframe_Fixed::encodeSamples(...) : Begin"); 92 | if(DEBUG_LEV > 10) { 93 | System.err.println("--count : " +count); 94 | System.err.println("start:skip:offset:::"+start+":"+skip+":"+offset); 95 | } 96 | } 97 | int increment = skip+1; 98 | //create space for results: Need four sets for the 5 different versions, 99 | // the e0 is sampe as input samples, so no duplicate needed. 100 | if(count != _lastCount) { 101 | _error1 = new int[count]; 102 | _error2 = new int[count]; 103 | _error3 = new int[count]; 104 | _error4 = new int[count]; 105 | _lastCount = count; 106 | } 107 | int [] error1 = _error1; 108 | int [] error2 = _error2; 109 | int [] error3 = _error3; 110 | int [] error4 = _error4; 111 | long sum0 = 0; 112 | long sum1 = 0; 113 | long sum2 = 0; 114 | long sum3 = 0; 115 | long sum4 = 0; 116 | //apply the algorithm to determine errors, summing abs vals as we go 117 | int tempI; 118 | int index = start; 119 | for(int i = 0; i < count; i++) { 120 | tempI = samples[index]; 121 | if(tempI < 0) tempI = -tempI; 122 | sum0 += tempI; 123 | index += increment; 124 | } 125 | for(int i = 1; i < 5; i++) { 126 | error1[i] = samples[start+i*increment]-samples[start+(i-1)*increment]; 127 | tempI = error1[i]; 128 | tempI = (tempI < 0) ? -tempI:tempI; 129 | sum1 += tempI; 130 | if(i > 1) { 131 | error2[i] = error1[i]-error1[(i-1)]; 132 | tempI = error2[i]; 133 | tempI = (tempI < 0) ? -tempI:tempI; 134 | sum2 += tempI; 135 | } 136 | if(i > 2) { 137 | error3[i] = error2[i]-error2[(i-1)]; 138 | tempI = error3[i]; 139 | tempI = (tempI < 0) ? -tempI:tempI; 140 | sum3 += tempI; 141 | } 142 | if(i > 3) { 143 | error4[i] = error3[i]-error3[(i-1)]; 144 | tempI = error4[i]; 145 | tempI = (tempI < 0) ? -tempI:tempI; 146 | sum4 += tempI; 147 | } 148 | } 149 | index = start+5*increment; 150 | for(int i = 5; i < count; i++) { 151 | //error1[i] = samples[start+i*increment]-samples[start+(i-1)*increment]; 152 | error1[i] = samples[index]-samples[index-increment]; 153 | tempI = error1[i]; 154 | tempI = (tempI < 0) ? -tempI:tempI; 155 | sum1 += tempI; 156 | error2[i] = error1[i]-error1[(i-1)]; 157 | tempI = error2[i]; 158 | tempI = (tempI < 0) ? -tempI:tempI; 159 | sum2 += tempI; 160 | error3[i] = error2[i]-error2[(i-1)]; 161 | tempI = error3[i]; 162 | tempI = (tempI < 0) ? -tempI:tempI; 163 | sum3 += tempI; 164 | error4[i] = error3[i]-error3[(i-1)]; 165 | tempI = error4[i]; 166 | tempI = (tempI < 0) ? -tempI:tempI; 167 | sum4 += tempI; 168 | index += increment; 169 | } 170 | //select best algorithm as indicated by bits needed from sum of values 171 | // and number of priming samples needed. 172 | int order = 0; 173 | long sumsX; 174 | for(int i = 0; i < 5; i++) { 175 | if(i == 0) 176 | sumsX = sum0; 177 | else if(i == 1) 178 | sumsX = sum1; 179 | else if(i == 2) 180 | sumsX = sum2; 181 | else if(i == 3) 182 | sumsX = sum3; 183 | else 184 | sumsX = sum4; 185 | 186 | double tempLowOrderBits = LOG_2*(sumsX/(count-i)); 187 | lowOrderBits[i] = (int)(Math.ceil(Math.log(tempLowOrderBits)/LOG_2)); 188 | if(lowOrderBits[i] < 1) 189 | lowOrderBits[i] = 1; 190 | else if (lowOrderBits[i] > sampleSize) 191 | lowOrderBits[i] = sampleSize; 192 | //lowOrderBits[i]++;//DOUBLE CHECK VALIDITY OF THIS. Decreases the bits needed, but "shouldn't" 193 | //bits[i] = (int)(Math.log(sum[i])/Math.log(10))*(count-1)+sampleSize*i; 194 | bits[i] = (int)(lowOrderBits[i]*(count-i)+sampleSize*i+1); 195 | order = (bits[i] < bits[order]) ? i:order; 196 | } 197 | 198 | int[] errors = null; 199 | int errorCount = count-order; 200 | int errorOffset = order; 201 | int errorStep = 1; 202 | switch(order) { 203 | case 0: errors = samples; 204 | errorStep+=skip; 205 | errorOffset=start;break; 206 | case 1: errors = error1;break; 207 | case 2: errors = error2;break; 208 | case 3: errors = error3;break; 209 | case 4: errors = error4;break; 210 | } 211 | _order = order; 212 | _offset = offset; 213 | _start = start; 214 | _errorStep = errorStep; 215 | _errorOffset = errorOffset; 216 | _errorCount = errorCount; 217 | _skip = skip; 218 | _samples = samples; 219 | _frameSampleSize = unencSampleSize; 220 | _errors = errors; 221 | _totalBits = unencSampleSize*order+8+ RiceEncoder.calculateEncodeSize( 222 | errors,errorOffset, errorStep, errorCount, lowOrderBits[order]); 223 | return encodedSamples; 224 | } 225 | 226 | /** 227 | * Return the estimated size of the previous encode attempt in bits. Since 228 | * returning the data from an encode is costly(due to the rice encoding and FLAC 229 | * compliant bit-packing), this allows us to estimate the size first, and 230 | * therefore choose another subframe type if this is larger. 231 | * 232 | * @return estimated size in bits of encoded subframe. 233 | */ 234 | public int estimatedSize() { 235 | return _totalBits; 236 | } 237 | 238 | public EncodedElement getData() { 239 | EncodedElement dataEle = new EncodedElement(_totalBits/8+1,_offset); 240 | getData(dataEle); 241 | return dataEle; 242 | } 243 | /** 244 | * Get the data from the last encode attempt. Data is returned in an 245 | * EncodedElement, properly packed at the bit-level to be added directly to 246 | * a FLAC stream. 247 | * 248 | * @return EncodedElement containing encoded subframe 249 | */ 250 | public EncodedElement getData(EncodedElement dataEle) { 251 | //EncodedElement dataEle = new EncodedElement(_totalBits/8+1,_offset); 252 | int startSize = dataEle.getTotalBits(); 253 | int unencSampleSize = _frameSampleSize; 254 | //write headers 255 | int encodedType = 1<<3 | _order; 256 | dataEle.addInt(0, 1); 257 | dataEle.addInt(encodedType, 6); 258 | dataEle.addInt(0, 1); 259 | if(_order > 0) { 260 | dataEle.packInt(_samples, unencSampleSize, _start, _skip, _order); 261 | } 262 | 263 | //send best data to rice encoder 264 | int paramSize = (lowOrderBits[_order] > 14) ? 5:4; 265 | boolean fiveBitParam = (paramSize < 5) ? false:true; 266 | RiceEncoder.beginResidual(fiveBitParam, (byte)0, dataEle); 267 | /*for(int i = 0; i < errorCount; i++) { 268 | int error = errors[errorOffset+i*errorStep]; 269 | if(error >= 32767 || error <= -32767) 270 | System.err.println("Error Bound issue?: " + error); 271 | }*/ 272 | rice.encodeRicePartition(_errors, _errorOffset, _errorStep, 273 | _errorCount, dataEle, lowOrderBits[_order], fiveBitParam); 274 | 275 | this.lastEncodedSize = dataEle.getTotalBits()-startSize; 276 | if(DEBUG_LEV > 0) 277 | System.err.println("Subframe_Fixed::encodeSamples(...): End"); 278 | return dataEle; 279 | } 280 | 281 | public int encodeSamples(int[] samples, int count, int start, int skip, 282 | EncodedElement dataEle, int offset, int unencSampleSize ) { 283 | int encodedSamples = 0; 284 | encodedSamples = encodeSamples(samples, count, start, skip, offset, unencSampleSize); 285 | dataEle.clear(_totalBits/8+1,offset); 286 | getData(dataEle); 287 | return encodedSamples; 288 | } 289 | 290 | /*private int[] calculatePartitionsCount(int[] errors, int errorOffset, int errorStep, 291 | int errorCount) { 292 | int maxPartitions = 8; 293 | int[][] sums = new int[(int)Math.pow(2,maxPartitions)][]; 294 | for(int i = 0; i < maxPartitions; i++) { 295 | sums[i] = new int[(int)Math.pow(2, i)]; 296 | } 297 | int[] counts = new int[maxPartitions]; 298 | int[] usedPartitions = new int[maxPartitions]; 299 | int[] lastPartitionCount = new int[maxPartitions]; 300 | for(int i = 0; i < maxPartitions; i++) { 301 | counts[i] = errorCount/sums[i].length; 302 | if(errorCount % sums[i].length != 0) counts[i]++; 303 | usedPartitions[i] = errorCount/counts[i]; 304 | if(errorCount %counts[i] != 0) usedPartitions[i]++; 305 | } 306 | for(int i = 0; i < maxPartitions; i++) { 307 | int temp = errorCount+errorOffset/ 308 | } 309 | int[] sizes = new int[maxPartitions]; 310 | 311 | int temp = 0; 312 | double log2 = Math.log(2); 313 | for(int i = 0; i < errorCount; i++) { 314 | temp = errors[errorOffset+i*errorStep]; 315 | if(temp < 0) temp = -temp; 316 | for(int x = 0; x < maxPartitions; x++) { 317 | float destDiv = i/counts[x]; 318 | int destIndex = (int)(i/destDiv); 319 | sums[x][destIndex] += temp; 320 | } 321 | 322 | } 323 | //sum up all bit sizes per partition, choose best size. 324 | for(int i = 0; i < maxPartitions; i++) { 325 | int tempTotal = 0; 326 | sizes[i] = 0; 327 | 328 | for(int x = 0; x < usedPartitions[i]; x++) { 329 | tempTotal = (int)(Math.log(sums[i][x])/Math.log(2)); 330 | float destDiv = (float)errorCount/sums[x].length; 331 | int destCount = errorCount/sums[x].length; 332 | int destIndex = (int)(i/destDiv); 333 | sizes[i] += tempTotal+4+; 334 | } 335 | } 336 | return results; 337 | }*/ 338 | 339 | } 340 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/Subframe_LPC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * Implements the Subframe abstract class, providing encoding support for the 24 | * FLAC LPC Subframe. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class Subframe_LPC extends Subframe { 29 | public static long totalTime = 0; 30 | private class PartialResult { 31 | int[] samples; 32 | int start; 33 | int increment; 34 | int count; 35 | int subframeSampleSize; 36 | 37 | int lpcOrder; 38 | int lowOrderBits; 39 | int totalBits; 40 | int precision; 41 | int lastCount; 42 | } 43 | 44 | /* Following values used frequently, let's calculate just once */ 45 | private static final double LOGE_2 = Math.log(2); 46 | private static final double SQRT_2 = Math.sqrt(2); 47 | 48 | /** Maximum LPC order that is supported by this subframe */ 49 | public static final int MAX_LPC_ORDER = 32; 50 | 51 | /** For debugging: Higher values equals greater output, generally in 52 | * increments of 10 */ 53 | public static int DEBUG_LEV = 0; 54 | 55 | /** Subframe type implemented by this subframe. */ 56 | public static final EncodingConfiguration.SubframeType type = 57 | EncodingConfiguration.SubframeType.LPC; 58 | 59 | int sampleSize = 0; 60 | RiceEncoder rice = null; 61 | int _lpcOrder = 0; 62 | int _lowOrderBits = 0; 63 | int _totalBits = 0; 64 | int _precision = 15; 65 | int _lastCount = 0; 66 | int[] _errors = null; 67 | int[] tempErrors = null; 68 | int[] _quantizedCoeffs = null; 69 | int[] tempCoeffs = null; 70 | int _shift = 0; 71 | LPC[] lpcs = null; 72 | int[] _samples = null; 73 | int _offset = 0; 74 | int _frameSampleSize; 75 | int _start = 0; 76 | int _increment = 0; 77 | long[] correlations = null; 78 | int[] _windowedSamples = null; 79 | 80 | Subframe_LPC(StreamConfiguration sc) { 81 | super(sc); 82 | sampleSize = sc.getBitsPerSample(); 83 | rice = new RiceEncoder(); 84 | lpcs = new LPC[MAX_LPC_ORDER+1]; 85 | for(int i = 0; i < MAX_LPC_ORDER+1; i++) 86 | lpcs[i] = new LPC(i); 87 | _lastCount = -1; 88 | _quantizedCoeffs = new int[MAX_LPC_ORDER+1]; 89 | tempCoeffs = new int[MAX_LPC_ORDER+1]; 90 | } 91 | 92 | /** 93 | * This method is used to set the encoding configuration. 94 | * @param ec encoding configuration to use. 95 | * @return true if configuration was changed, false otherwise 96 | */ 97 | @Override 98 | public boolean registerConfiguration(EncodingConfiguration ec) { 99 | return super.registerConfiguration(ec); 100 | } 101 | 102 | public int encodeSamples(int[] samples, int count, int start, int skip, 103 | int offset, int unencSampleSize) { 104 | int encodedSamples = count; 105 | if(DEBUG_LEV > 0) { 106 | System.err.println("Subframe_LPC::encodeSamples(...) : Begin"); 107 | if(DEBUG_LEV > 10) { 108 | System.err.println("--count : " +count); 109 | System.err.println("start:skip:offset:::"+start+":"+skip+":"+offset); 110 | } 111 | } 112 | int increment = skip+1; 113 | if(count != _lastCount) { 114 | _errors = new int[count]; 115 | tempErrors = new int[count]; 116 | _lastCount = count; 117 | _windowedSamples = new int[count]; 118 | } 119 | int minOrder = ec.getMinLPCOrder(); 120 | int maxOrder = ec.getMaxLPCOrder(); 121 | int frameSampleSize = unencSampleSize; 122 | int order = -1; 123 | int totalBits = 0; 124 | long[] R = correlations; 125 | if(R == null || R.length < maxOrder+1) { 126 | R = new long[maxOrder+1]; 127 | correlations = R; 128 | } 129 | LPC.window(samples, count, start, increment, _windowedSamples); 130 | LPC.createAutoCorrelation(R, _windowedSamples, count, 0, 1, maxOrder); 131 | int[] coefficients = tempCoeffs; 132 | int[] errors = tempErrors; 133 | int lowOrderBits = 0; 134 | int precision = 0; 135 | int shift = 0; 136 | int watchCount = 2; 137 | for(int i = maxOrder; i >= minOrder; i--) { 138 | LPC.calculate(lpcs[i], R); 139 | int tempTotalBits = partialEncodeLPC(samples, count, start, increment, 140 | lpcs[i], this,frameSampleSize); 141 | //compare to current order: If last not set or size < last, replace 142 | if(tempTotalBits < totalBits || order == -1) { 143 | order = i; 144 | totalBits = tempTotalBits; 145 | lowOrderBits = _lowOrderBits; 146 | precision = _precision; 147 | shift = _shift; 148 | int[] temp = coefficients; 149 | coefficients = _quantizedCoeffs; 150 | _quantizedCoeffs = temp; 151 | temp = errors; 152 | errors = _errors; 153 | _errors = temp; 154 | //priorLPC = lpcs[i]; 155 | watchCount = 2; 156 | } 157 | else { 158 | if(--watchCount == 0) 159 | break; 160 | } 161 | } 162 | _lowOrderBits = lowOrderBits; 163 | _precision = precision; 164 | _shift = shift; 165 | tempCoeffs = _quantizedCoeffs; 166 | _quantizedCoeffs = coefficients; 167 | tempErrors = _errors; 168 | _errors = errors; 169 | _samples = samples; 170 | _offset = offset; 171 | _frameSampleSize = unencSampleSize; 172 | _start = start; 173 | _increment = increment; 174 | _totalBits = totalBits; 175 | _lpcOrder = order; 176 | return encodedSamples; 177 | } 178 | 179 | /** 180 | * Return the estimated size of the previous encode attempt in bits. Since 181 | * returning the data from an encode is costly(due to the rice encoding and FLAC 182 | * compliant bit-packing), this allows us to estimate the size first, and 183 | * therefore choose another subframe type if this is larger. 184 | * 185 | * @return estimated size in bits of encoded subframe. 186 | */ 187 | public int estimatedSize() { 188 | return _totalBits; 189 | } 190 | 191 | /** 192 | * Get the data from the last encode attempt. Data is returned in an 193 | * EncodedElement, properly packed at the bit-level to be added directly to 194 | * a FLAC stream. 195 | * 196 | * @return EncodedElement containing encoded subframe 197 | */ 198 | public EncodedElement getData() { 199 | EncodedElement result = new EncodedElement(_totalBits/8+1,_offset); 200 | //result.clear((int)_totalBits+1, _offset); 201 | writeLPC(_samples, _lastCount, _start, 202 | _increment, result, _frameSampleSize, _lowOrderBits, 203 | _precision, _shift, _quantizedCoeffs, _errors, _lpcOrder,rice); 204 | int totalBits = result.getTotalBits(); 205 | this.lastEncodedSize = (int)totalBits; 206 | 207 | if(DEBUG_LEV > 0) { 208 | System.err.println("lastencodedSize set: "+this.lastEncodedSize); 209 | System.err.println("Subframe_LPC::getData(...): End"); 210 | } 211 | return result; 212 | } 213 | 214 | public int encodeSamples(int[] samples, int count, int start, int skip, 215 | EncodedElement dataEle, int offset, int unencSampleSize ) { 216 | encodeSamples(samples, count, start, skip, offset, unencSampleSize); 217 | EncodedElement result = getData(); 218 | int totalBits = result.getTotalBits(); 219 | dataEle.data = result.data; 220 | dataEle.usableBits = result.usableBits; 221 | dataEle.offset = result.offset; 222 | dataEle.previous = result.previous; 223 | dataEle.next = result.next; 224 | this.lastEncodedSize = (int)totalBits; 225 | 226 | return count; 227 | } 228 | 229 | private static void writeHeadersAndData(EncodedElement dataEle, int order, 230 | int[] coeff, int precision, int shift, int[] samples, 231 | int sampleSize, int start, int skip) { 232 | //write headers 233 | int encodedType = 1<<5 | (order-1); 234 | dataEle.addInt(0, 1); 235 | dataEle.addInt(encodedType, 6); 236 | dataEle.addInt(0, 1); 237 | if(order > 0) { 238 | dataEle.packInt(samples, sampleSize, start, skip, order); 239 | } 240 | dataEle.addInt(precision-1, 4); 241 | dataEle.addInt(shift, 5); 242 | //System.err.println("shift:order:type::"+shift+":"+order+":"+encodedType); 243 | for(int i = 1; i <= order; i++) { 244 | int val = (int)-coeff[i]; 245 | dataEle.addInt(val, precision); 246 | } 247 | } 248 | 249 | /** 250 | * Quantize coefficients to integer values of the given precision, and 251 | * calculate the shift needed. 252 | * @param coefficients values to quantize. These values will not be changed. 253 | * @param dest destination for quantized values. 254 | * @param order number of values to quantize. First value skipped, coefficients 255 | * array must be at least order+1 in length. 256 | * @param precision number of signed bits to use for coefficients(must be in range 2-15, inclusive). 257 | * @return 258 | */ 259 | private static int quantizeCoefficients(double[] coefficients, int[] dest, 260 | int order, int precision) { 261 | assert(precision >= 2 && precision <= 15); 262 | assert(coefficients.length >= order+1); 263 | assert(dest.length >= order+1); 264 | if(precision < 2 || precision > 15) 265 | throw new IllegalArgumentException("Error! precision must be between 2 and 15, inclusive."); 266 | 267 | int shiftApplied = 0; 268 | int maxValAllowed = (1<<(precision-2))-1;//minus an extra bit for sign. 269 | int minValAllowed = -1*maxValAllowed-1; 270 | double maxVal = 0; 271 | for(int i = 1; i <= order; i++) { 272 | double temp = coefficients[i]; 273 | if(temp < 0) temp*= -1; 274 | if(temp > maxVal) 275 | maxVal = temp; 276 | } 277 | //find shift to use(by max value) 278 | for(shiftApplied = 15; shiftApplied > 0; shiftApplied--) { 279 | int temp = (int)(maxVal * (1< maxValAllowed) {//no shift should have been applied 284 | //ensure max value is not too large, cap all necessary // 285 | for(int i = 1; i <= order; i++) { 286 | double temp = coefficients[i]; 287 | if(temp < 0) 288 | temp = temp * -1; 289 | if(temp > maxValAllowed) { 290 | if(coefficients[i] < 0) 291 | dest[i] = minValAllowed; 292 | else 293 | dest[i] = maxValAllowed; 294 | } 295 | else 296 | dest[i] = (int)coefficients[i]; 297 | } 298 | } 299 | else { 300 | //shift and quantize all values by found shift 301 | for(int i = 1; i <= order; i++) { 302 | double temp = coefficients[i]*(1< 0) ? temp+0.5:temp-0.5; 304 | dest[i] = (int)temp; 305 | } 306 | } 307 | return shiftApplied; 308 | } 309 | 310 | private static void writeLPC(int[] samples, int count, int start, 311 | int increment, EncodedElement ele, int frameSampleSize, int riceParam, 312 | int precision, int shift, int[] coeffs, int[] errors, int order, 313 | RiceEncoder rice) { 314 | writeHeadersAndData(ele, order, coeffs, precision, shift, 315 | samples, frameSampleSize, start, increment-1); 316 | int paramSize = (riceParam > 14) ? 5:4; 317 | boolean fiveBitParam = (paramSize < 5) ? false:true; 318 | RiceEncoder.beginResidual(fiveBitParam, (byte)0, ele); 319 | rice.encodeRicePartition(errors, order,1, count-order, ele, 320 | riceParam, fiveBitParam); 321 | } 322 | 323 | private static int getParam(int[] vals, int end, int start, int max) { 324 | long sum = 0; 325 | for(int i = start; i < end; i++) { 326 | int temp = vals[i]; 327 | temp = (temp < 0) ? -temp:temp; 328 | sum += temp; 329 | } 330 | float mean = (float)sum/(float)(end-start); 331 | double temp = LOGE_2*(mean); 332 | if(temp < 1) 333 | temp = 0; 334 | else 335 | temp = Math.ceil(Math.log(temp)/LOGE_2); 336 | int param = (int)temp; 337 | param++; 338 | if(param < 0) { 339 | param = 1; 340 | System.err.println("Subframe_LPC::param negative?"); 341 | } 342 | else if(param > max) 343 | param = max; 344 | return param; 345 | } 346 | 347 | private static int partialEncodeLPC(int[] samples, int count, int start, 348 | int increment, LPC lpc, Subframe_LPC lpcSubframe, 349 | int frameSampleSize) { 350 | //System.err.println("encodeLPC begin"); 351 | int order = lpc.order; 352 | //double error = (lpc.rawError < 0) ? -lpc.rawError:lpc.rawError; 353 | double tempLowOrderBits = 0; 354 | 355 | //following commented out because the getParam() method appears to be 356 | //more accurate for high-order lpc's, causing the search to end sooner 357 | //and resulting in smaller files. win-win. On second thought, that can't 358 | //be why it's quicker. The profile is showing *more* invocatiosn of this 359 | //function rather than fewer(by 3000!), which means it's something else 360 | //that's causing it to be quicker....strange. 361 | /*double deviation = Math.sqrt((int)error/count); 362 | double tempBits = LOGE_2*deviation/SQRT_2; 363 | tempLowOrderBits = (Math.ceil(Math.log(tempBits)/LOGE_2)); 364 | if(java.lang.Double.isNaN(tempLowOrderBits)) { 365 | System.err.println("tempLowOrderBits is NaN"); 366 | if(Double.isNaN(deviation)) 367 | System.err.println("deviation is NaN"); 368 | System.err.println("Error: "+(int)error/count); 369 | System.exit(0); 370 | } 371 | if(tempLowOrderBits < 1) 372 | tempLowOrderBits = 1; 373 | else if (tempLowOrderBits > frameSampleSize) { 374 | tempLowOrderBits = frameSampleSize; 375 | }*/ 376 | int precision = 15; 377 | //calculate total estimated size of frame 378 | int headerSize = order*frameSampleSize+precision*order+9+8; 379 | int[] coeffs = lpcSubframe._quantizedCoeffs; 380 | int shift = quantizeCoefficients(lpc.rawCoefficients, coeffs, order, precision); 381 | //for(int i = 0; i <= order; i++) 382 | // System.err.println("coef i:val :: "+i+":"+coeffs[i]); 383 | //use integer coefficients to predict samples 384 | //compare prediction to original, storing error. 385 | 386 | /** We save ~7% by accessing local vars instead of array in next loop */ 387 | int coeff1 = coeffs[1]; 388 | int coeff2 = coeffs[2]; 389 | int coeff3 = coeffs[3]; 390 | int coeff4 = coeffs[4]; 391 | int coeff5 = coeffs[5]; 392 | int coeff6 = coeffs[6]; 393 | int coeff7 = coeffs[7]; 394 | int coeff8 = coeffs[8]; 395 | int coeff9 = coeffs[9]; 396 | int coeff10 = coeffs[10]; 397 | int coeff11 = coeffs[11]; 398 | int coeff12 = coeffs[12]; 399 | 400 | int baseIndex = start; 401 | int targetSampleBase = start+order*increment-increment; 402 | int tempOrder = order; 403 | for(int i = order; i < count; i++) { 404 | long temp = 0; 405 | targetSampleBase += increment; 406 | int sampleIndex = baseIndex; 407 | baseIndex += increment; 408 | if(order > 12) { 409 | switch(order) { 410 | case 32: temp -= (long)coeffs[32]*samples[sampleIndex]; 411 | sampleIndex+=increment; 412 | case 31: temp -= (long)coeffs[31]*samples[sampleIndex]; 413 | sampleIndex+=increment; 414 | case 30: temp -= (long)coeffs[30]*samples[sampleIndex]; 415 | sampleIndex+=increment; 416 | case 29: temp -= (long)coeffs[29]*samples[sampleIndex]; 417 | sampleIndex+=increment; 418 | case 28: temp -= (long)coeffs[28]*samples[sampleIndex]; 419 | sampleIndex+=increment; 420 | case 27: temp -= (long)coeffs[27]*samples[sampleIndex]; 421 | sampleIndex+=increment; 422 | case 26: temp -= (long)coeffs[26]*samples[sampleIndex]; 423 | sampleIndex+=increment; 424 | case 25: temp -= (long)coeffs[25]*samples[sampleIndex]; 425 | sampleIndex+=increment; 426 | case 24: temp -= (long)coeffs[24]*samples[sampleIndex]; 427 | sampleIndex+=increment; 428 | case 23: temp -= (long)coeffs[23]*samples[sampleIndex]; 429 | sampleIndex+=increment; 430 | case 22: temp -= (long)coeffs[22]*samples[sampleIndex]; 431 | sampleIndex+=increment; 432 | case 21: temp -= (long)coeffs[21]*samples[sampleIndex]; 433 | sampleIndex+=increment; 434 | case 20: temp -= (long)coeffs[20]*samples[sampleIndex]; 435 | sampleIndex+=increment; 436 | case 19: temp -= (long)coeffs[19]*samples[sampleIndex]; 437 | sampleIndex+=increment; 438 | case 18: temp -= (long)coeffs[18]*samples[sampleIndex]; 439 | sampleIndex+=increment; 440 | case 17: temp -= (long)coeffs[17]*samples[sampleIndex]; 441 | sampleIndex+=increment; 442 | case 16: temp -= (long)coeffs[16]*samples[sampleIndex]; 443 | sampleIndex+=increment; 444 | case 15: temp -= (long)coeffs[15]*samples[sampleIndex]; 445 | sampleIndex+=increment; 446 | case 14: temp -= (long)coeffs[14]*samples[sampleIndex]; 447 | sampleIndex+=increment; 448 | case 13: temp -= (long)coeffs[13]*samples[sampleIndex]; 449 | sampleIndex+=increment; 450 | } 451 | tempOrder = 12; 452 | } 453 | switch(tempOrder) { 454 | case 12: temp -= (long)coeff12*samples[sampleIndex]; 455 | sampleIndex+=increment; 456 | case 11: temp -= (long)coeff11*samples[sampleIndex]; 457 | sampleIndex+=increment; 458 | case 10: temp -= (long)coeff10*samples[sampleIndex]; 459 | sampleIndex+=increment; 460 | case 9: temp -= (long)coeff9*samples[sampleIndex]; 461 | sampleIndex+=increment; 462 | case 8: temp -= (long)coeff8*samples[sampleIndex]; 463 | sampleIndex+=increment; 464 | case 7: temp -= (long)coeff7*samples[sampleIndex]; 465 | sampleIndex+=increment; 466 | case 6: temp -= (long)coeff6*samples[sampleIndex]; 467 | sampleIndex+=increment; 468 | case 5: temp -= (long)coeff5*samples[sampleIndex]; 469 | sampleIndex+=increment; 470 | case 4: temp -= (long)coeff4*samples[sampleIndex]; 471 | sampleIndex+=increment; 472 | case 3: temp -= (long)coeff3*samples[sampleIndex]; 473 | sampleIndex+=increment; 474 | case 2: temp -= (long)coeff2*samples[sampleIndex]; 475 | sampleIndex+=increment; 476 | case 1: temp -= (long)coeff1*samples[sampleIndex]; 477 | sampleIndex+=increment;break; 478 | default: 479 | } 480 | temp = temp >> shift;//with precision fixed at 15, this should always 481 | //shift back down to int territory for bitsize <=24 482 | lpcSubframe._errors[i] = samples[targetSampleBase]-(int)temp; 483 | } 484 | tempLowOrderBits = getParam(lpcSubframe._errors, count, order,frameSampleSize); 485 | int riceSize = RiceEncoder.calculateEncodeSize(lpcSubframe._errors, 486 | order, 1, count-order, (int)tempLowOrderBits); 487 | int totalSize = headerSize + riceSize; 488 | 489 | lpcSubframe._precision = precision; 490 | lpcSubframe._lowOrderBits = (int)tempLowOrderBits; 491 | lpcSubframe._shift = shift; 492 | lpcSubframe._totalBits = totalSize; 493 | 494 | return totalSize; 495 | } 496 | 497 | } 498 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/Subframe_Verbatim.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * Implements the Subframe abstract class, providing encoding support for the 24 | * FLAC Verbatim Subframe. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class Subframe_Verbatim extends Subframe { 29 | /** For debugging: Higher value equals more output, typically by increments 30 | * of 10 */ 31 | public static int DEBUG_LEV = 0; 32 | /** Subframe type supported by this implementation. */ 33 | public static final EncodingConfiguration.SubframeType type = 34 | EncodingConfiguration.SubframeType.VERBATIM; 35 | 36 | //int sampleSize = 0; 37 | 38 | /** 39 | * Constructor. Sets StreamConfiguration to use. If the StreamConfiguration 40 | * must later be changed, a new Subframe object must be created as well. 41 | * @param sc StreamConfiguration to use for encoding. 42 | */ 43 | Subframe_Verbatim(StreamConfiguration sc) { 44 | super(sc); 45 | //sampleSize = sc.getBitsPerSample(); 46 | } 47 | 48 | /** 49 | * This method is used to set the encoding configuration. 50 | * @param ec encoding configuration to use. 51 | * @return true if configuration was changed, false otherwise 52 | */ 53 | @Override 54 | public boolean registerConfiguration(EncodingConfiguration ec) { 55 | super.registerConfiguration(ec); 56 | return true; 57 | } 58 | 59 | public int estimateSize(int count, int bitsPerSample) { 60 | int estimatedSize=8+count*bitsPerSample;//header size + unencoded data. 61 | return estimatedSize; 62 | } 63 | 64 | public int encodeSamples(int[] samples, int count, int start, int skip, 65 | EncodedElement data, int offset, int bitsPerSample ) { 66 | if(DEBUG_LEV > 0) { 67 | System.err.println("Subframe_Verbatim::encodeSamples(...)"); 68 | } 69 | int encodedSamples = count; 70 | int bits = bitsPerSample*count+offset+1*8; 71 | int bytesNeeded = bits/8; 72 | if(bits%8 != 0) 73 | bytesNeeded++; 74 | data.clear(bytesNeeded, offset); 75 | data.addInt(0, 1); 76 | data.addInt(1, 6); 77 | data.addInt(0, 1); 78 | data.packInt(samples, bitsPerSample, start, skip, count); 79 | lastEncodedSize = bits-offset; 80 | 81 | if(DEBUG_LEV > 0) 82 | System.err.println("Subframe_Verbatim::encodeSamples(...): End"); 83 | if(DEBUG_LEV > 10) { 84 | System.err.println("--: bitsUsed : "+bits+" : Bytes : "+bytesNeeded); 85 | } 86 | return encodedSamples; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/UTF8Modified.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * This is a utility class that provides methods to both encode to and decode 24 | * from the extended version of UTF8 used by the FLAC format. All functions 25 | * should work with standard UTF8 as well, since this only extends it to handle 26 | * larger input values. 27 | * 28 | * @author Preston Lacey 29 | */ 30 | public class UTF8Modified { 31 | static final long oneByteLimit = (long)Math.pow(2, 7); 32 | static final long twoByteLimit = (long)Math.pow(2, 11); 33 | static final long threeByteLimit = (long)Math.pow(2, 16); 34 | static final long fourByteLimit = (long)Math.pow(2, 21); 35 | static final long fiveByteLimit = (long)Math.pow(2, 26); 36 | static final long sixByteLimit = (long)Math.pow(2, 31); 37 | static final long sevenByteLimit = (long)Math.pow(2, 36); 38 | static long[] limits = { 39 | oneByteLimit, 40 | twoByteLimit, 41 | threeByteLimit, 42 | fourByteLimit, 43 | fiveByteLimit, 44 | sixByteLimit, 45 | sevenByteLimit 46 | }; 47 | 48 | /** For debugging: Higher value equals more output, generally by increments 49 | * of 10 */ 50 | public static int DEBUG_LEV = 0; 51 | 52 | /** 53 | * Constructor. This Class provides only static methods and static fields. 54 | */ 55 | public UTF8Modified() { 56 | } 57 | 58 | /** 59 | * Decode an extended UTF8(as used in FLAC), to a long value. 60 | * @param input extended UTF8 encoded value. 61 | * @return value represented by the UTF8 input. 62 | */ 63 | public static long decodeFromExtendedUTF8(byte[] input) { 64 | int leadOnes = 0; 65 | int leadMask = 128; 66 | int work = input[0]; 67 | while((work & leadMask) > 0) { 68 | leadOnes++; 69 | work = work << 1; 70 | } 71 | int valMask = 255 >>> (leadOnes+1); 72 | long val = input[0] & valMask; 73 | for(int i = 1; i < leadOnes; i++) { 74 | int midMask = 0x3F; 75 | val = val << 6; 76 | val = (input[i] & midMask) | val; 77 | } 78 | return val; 79 | } 80 | 81 | /** 82 | * Convert a value to an extended UTF8 format(as used in FLAC). 83 | * @param value value to convert to extended UTF8(value must be positive 84 | * and 36 bits or less in size) 85 | * @return extended UTF8 encoded value(array size is equal to the number of 86 | * usable bytes) 87 | */ 88 | public static byte[] convertToExtendedUTF8(long value) { 89 | //calculate bytes needed 90 | int bytesNeeded = 1; 91 | for(int i = 0; i < 7; i++) { 92 | if(value >= limits[i] ) { 93 | bytesNeeded++; 94 | } 95 | } 96 | //create space 97 | byte [] result = new byte[bytesNeeded]; 98 | int byteIndex = 0; 99 | int inputIndex = 0; 100 | int bytesLeft = bytesNeeded; 101 | while(bytesLeft > 1) { 102 | int midByteMarker = 0x80;//10 in leftmost bits 103 | int midByteMask = 0x3F;//00111111 104 | int val = ((int)(value >>> inputIndex) & midByteMask) | midByteMarker; 105 | result[byteIndex++] = (byte)val; 106 | inputIndex += 6; 107 | bytesLeft--; 108 | } 109 | int onesNeeded = inputIndex/6; 110 | if(onesNeeded > 0) 111 | onesNeeded++; 112 | int startMask = 255 >>> (onesNeeded + 1); 113 | int ones = 255 << (8-onesNeeded); 114 | int val = ((int)(value >>> inputIndex) & startMask) | ones; 115 | result[byteIndex++] = (byte)val; 116 | 117 | byte[] finalResult = new byte[bytesNeeded]; 118 | for(int i = 0; i < bytesNeeded; i++) { 119 | int sourceIndex = bytesNeeded-1-i; 120 | int destIndex = i; 121 | finalResult[destIndex] = result[sourceIndex]; 122 | } 123 | if(DEBUG_LEV > 10) { 124 | System.err.print("input:result_length:result :: " +value+":"+finalResult.length+"::"); 125 | for(int i = 0; i < finalResult.length; i++) 126 | System.err.print(Integer.toHexString(finalResult[i])+":"); 127 | System.err.println(); 128 | } 129 | return finalResult; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/package.html: -------------------------------------------------------------------------------- 1 | 2 | This package defines a FLAC encoder with a simple interface for enabling FLAC 3 | encoding support in an application. This class is appropriate for use in the 4 | case where you have raw pcm audio samples that you wish to encode. Currently, 5 | fixed-blocksize only is implemented, with the "Maximum Block Size" set in the 6 | StreamConfiguration object used as the actual block size. 7 |

8 | To use this library, there are several options:
9 |

10 | If you're simply needing to convert a file(as opposed to a stream), you may 11 | want to use the FLAC_FileEncoder class.
12 |

13 |

14 | For applications using the javax.sound API, this library includes basic support.
15 | FLACFileWriter provides the implementation of 16 | javax.sound.sampled.spi.AudioFileWriter. After installing a release jar in the 17 | appropriate location, the FLAC encoder should be available for use by any application 18 | which makes use of the sound api for transcoding purposes. Use this if you need basic 19 | encoding support(with default configuration), and have a 20 | javax.sound.sampled.AudioInputStream as your source.

21 |

22 | The remaining example is for those who need more control over the encoding 23 | process, including supplying a non-default EncodingConfiguration object, or 24 | for whom the prior methods are not otherwise suitable. For direct, low-level 25 | access, an application should primarily use the classes FLACEncoder, 26 | EncodingConfiguration, StreamConfiguration, and FLACOutputStream. 27 |

28 | An encoding process is simple, and should follow these steps:
29 |
30 | 1) Set StreamConfiguration to appropriate values. After a stream is opened, 31 | this must not be altered until the stream is closed.
32 | 2) Set FLACOutputStream, object to write results to.
33 | 3) Open FLAC Stream
34 | 4) Set EncodingConfiguration(if defaults are insufficient).
35 | 5) Add samples to encoder
36 | 6) Encode Samples
37 | 7) Close stream
38 | (note: steps 4,5, and 6 may be done repeatedly, in any order. However, see 39 | related method documentation for info on concurrent use) 40 |


41 | 42 | --------------------------------------------------------------------------------