diff options
Diffstat (limited to 'songdbj/org/tritonus/share/sampled/FloatSampleTools.java')
-rw-r--r-- | songdbj/org/tritonus/share/sampled/FloatSampleTools.java | 696 |
1 files changed, 696 insertions, 0 deletions
diff --git a/songdbj/org/tritonus/share/sampled/FloatSampleTools.java b/songdbj/org/tritonus/share/sampled/FloatSampleTools.java new file mode 100644 index 0000000000..76913ba39e --- /dev/null +++ b/songdbj/org/tritonus/share/sampled/FloatSampleTools.java | |||
@@ -0,0 +1,696 @@ | |||
1 | /* | ||
2 | * FloatSampleTools.java | ||
3 | * | ||
4 | * This file is part of Tritonus: http://www.tritonus.org/ | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * Copyright (c) 2000,2004 by Florian Bomers <http://www.bomers.de> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU Library General Public License as published | ||
12 | * by the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU Library General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU Library General Public | ||
21 | * License along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | */ | ||
24 | |||
25 | /* | ||
26 | |<--- this code is formatted to fit into 80 columns --->| | ||
27 | */ | ||
28 | |||
29 | package org.tritonus.share.sampled; | ||
30 | |||
31 | import java.util.*; | ||
32 | import javax.sound.sampled.*; | ||
33 | import org.tritonus.share.TDebug; | ||
34 | |||
35 | /** | ||
36 | * Utility functions for handling data in normalized float arrays. | ||
37 | * Each sample is linear in the range of [-1.0f, +1.0f]. | ||
38 | * <p> | ||
39 | * Currently, the following bit sizes are supported: | ||
40 | * <ul> | ||
41 | * <li>8-bit | ||
42 | * <li>16-bit | ||
43 | * <li>packed 24-bit (stored in 3 bytes) | ||
44 | * <li>32-bit | ||
45 | * </ul> | ||
46 | * 8-bit data can be unsigned or signed. All other data is only | ||
47 | * supported in signed encoding. | ||
48 | * | ||
49 | * @see FloatSampleBuffer | ||
50 | * @author Florian Bomers | ||
51 | */ | ||
52 | |||
53 | public class FloatSampleTools { | ||
54 | |||
55 | /** default number of bits to be dithered: 0.7f */ | ||
56 | public static final float DEFAULT_DITHER_BITS = 0.7f; | ||
57 | |||
58 | private static Random random = null; | ||
59 | |||
60 | // sample width (must be in order !) | ||
61 | static final int F_8=1; | ||
62 | static final int F_16=2; | ||
63 | static final int F_24=3; | ||
64 | static final int F_32=4; | ||
65 | static final int F_SAMPLE_WIDTH_MASK=F_8 | F_16 | F_24 | F_32; | ||
66 | |||
67 | // format bit-flags | ||
68 | static final int F_SIGNED=8; | ||
69 | static final int F_BIGENDIAN=16; | ||
70 | |||
71 | // supported formats | ||
72 | static final int CT_8S=F_8 | F_SIGNED; | ||
73 | static final int CT_8U=F_8; | ||
74 | static final int CT_16SB=F_16 | F_SIGNED | F_BIGENDIAN; | ||
75 | static final int CT_16SL=F_16 | F_SIGNED; | ||
76 | static final int CT_24SB=F_24 | F_SIGNED | F_BIGENDIAN; | ||
77 | static final int CT_24SL=F_24 | F_SIGNED; | ||
78 | static final int CT_32SB=F_32 | F_SIGNED | F_BIGENDIAN; | ||
79 | static final int CT_32SL=F_32 | F_SIGNED; | ||
80 | |||
81 | // ////////////////////////////// initialization /////////////////////////////// // | ||
82 | |||
83 | /** prevent instanciation */ | ||
84 | private FloatSampleTools() { | ||
85 | } | ||
86 | |||
87 | |||
88 | // /////////////////// FORMAT / FORMAT TYPE /////////////////////////////////// // | ||
89 | |||
90 | /** | ||
91 | * only allow "packed" samples -- currently no support for 18, 20, 24_32 bits. | ||
92 | * @throws IllegalArgumentException | ||
93 | */ | ||
94 | static void checkSupportedSampleSize(int ssib, int channels, int frameSize) { | ||
95 | if ((ssib*channels) != frameSize * 8) { | ||
96 | throw new IllegalArgumentException("unsupported sample size: "+ssib | ||
97 | +" stored in "+(frameSize/channels)+" bytes."); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | |||
102 | /** | ||
103 | * Get the formatType code from the given format. | ||
104 | * @throws IllegalArgumentException | ||
105 | */ | ||
106 | static int getFormatType(AudioFormat format) { | ||
107 | boolean signed = format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED); | ||
108 | if (!signed && | ||
109 | !format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) { | ||
110 | throw new IllegalArgumentException | ||
111 | ("unsupported encoding: only PCM encoding supported."); | ||
112 | } | ||
113 | if (!signed && format.getSampleSizeInBits() != 8) { | ||
114 | throw new IllegalArgumentException | ||
115 | ("unsupported encoding: only 8-bit can be unsigned"); | ||
116 | } | ||
117 | checkSupportedSampleSize(format.getSampleSizeInBits(), | ||
118 | format.getChannels(), | ||
119 | format.getFrameSize()); | ||
120 | |||
121 | int formatType = getFormatType(format.getSampleSizeInBits(), | ||
122 | signed, format.isBigEndian()); | ||
123 | return formatType; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * @throws IllegalArgumentException | ||
128 | */ | ||
129 | static int getFormatType(int ssib, boolean signed, boolean bigEndian) { | ||
130 | int bytesPerSample=ssib/8; | ||
131 | int res=0; | ||
132 | if (ssib==8) { | ||
133 | res=F_8; | ||
134 | } else if (ssib==16) { | ||
135 | res=F_16; | ||
136 | } else if (ssib==24) { | ||
137 | res=F_24; | ||
138 | } else if (ssib==32) { | ||
139 | res=F_32; | ||
140 | } | ||
141 | if (res==0) { | ||
142 | throw new IllegalArgumentException | ||
143 | ("FloatSampleBuffer: unsupported sample size of " | ||
144 | +ssib+" bits per sample."); | ||
145 | } | ||
146 | if (!signed && bytesPerSample>1) { | ||
147 | throw new IllegalArgumentException | ||
148 | ("FloatSampleBuffer: unsigned samples larger than " | ||
149 | +"8 bit are not supported"); | ||
150 | } | ||
151 | if (signed) { | ||
152 | res|=F_SIGNED; | ||
153 | } | ||
154 | if (bigEndian && (ssib!=8)) { | ||
155 | res|=F_BIGENDIAN; | ||
156 | } | ||
157 | return res; | ||
158 | } | ||
159 | |||
160 | static int getSampleSize(int formatType) { | ||
161 | switch (formatType & F_SAMPLE_WIDTH_MASK) { | ||
162 | case F_8: return 1; | ||
163 | case F_16: return 2; | ||
164 | case F_24: return 3; | ||
165 | case F_32: return 4; | ||
166 | } | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * Return a string representation of this format | ||
172 | */ | ||
173 | static String formatType2Str(int formatType) { | ||
174 | String res=""+formatType+": "; | ||
175 | switch (formatType & F_SAMPLE_WIDTH_MASK) { | ||
176 | case F_8: | ||
177 | res+="8bit"; | ||
178 | break; | ||
179 | case F_16: | ||
180 | res+="16bit"; | ||
181 | break; | ||
182 | case F_24: | ||
183 | res+="24bit"; | ||
184 | break; | ||
185 | case F_32: | ||
186 | res+="32bit"; | ||
187 | break; | ||
188 | } | ||
189 | res+=((formatType & F_SIGNED)==F_SIGNED)?" signed":" unsigned"; | ||
190 | if ((formatType & F_SAMPLE_WIDTH_MASK)!=F_8) { | ||
191 | res+=((formatType & F_BIGENDIAN)==F_BIGENDIAN)? | ||
192 | " big endian":" little endian"; | ||
193 | } | ||
194 | return res; | ||
195 | } | ||
196 | |||
197 | |||
198 | // /////////////////// BYTE 2 FLOAT /////////////////////////////////// // | ||
199 | |||
200 | private static final float twoPower7=128.0f; | ||
201 | private static final float twoPower15=32768.0f; | ||
202 | private static final float twoPower23=8388608.0f; | ||
203 | private static final float twoPower31=2147483648.0f; | ||
204 | |||
205 | private static final float invTwoPower7=1/twoPower7; | ||
206 | private static final float invTwoPower15=1/twoPower15; | ||
207 | private static final float invTwoPower23=1/twoPower23; | ||
208 | private static final float invTwoPower31=1/twoPower31; | ||
209 | |||
210 | |||
211 | /** | ||
212 | * Conversion function to convert an interleaved byte array to | ||
213 | * a List of interleaved float arrays. The float arrays will contain normalized | ||
214 | * samples in the range [-1.0f, +1.0f]. The input array | ||
215 | * provides bytes in the format specified in <code>format</code>. | ||
216 | * <p> | ||
217 | * Only PCM formats are accepted. The method will convert all | ||
218 | * byte values from | ||
219 | * <code>input[inByteOffset]</code> to | ||
220 | * <code>input[inByteOffset + (frameCount * format.getFrameSize()) - 1]</code> | ||
221 | * to floats from | ||
222 | * <code>output(n)[outOffset]</code> to | ||
223 | * <code>output(n)[outOffset + frameCount - 1]</code> | ||
224 | * | ||
225 | * @param input the audio data in an byte array | ||
226 | * @param inByteOffset index in input where to start the conversion | ||
227 | * @param output list of float[] arrays which receive the converted audio data. | ||
228 | * if the list does not contain enough elements, or individual float arrays | ||
229 | * are not large enough, they are created. | ||
230 | * @param outOffset the start offset in <code>output</code> | ||
231 | * @param frameCount number of frames to be converted | ||
232 | * @param format the input format. Only packed PCM is allowed | ||
233 | * @throws IllegalArgumentException if one of the parameters is out of bounds | ||
234 | * | ||
235 | * @see #byte2floatInterleaved(byte[],int,float[],int,int,AudioFormat) | ||
236 | */ | ||
237 | public static void byte2float(byte[] input, int inByteOffset, | ||
238 | List<float[]> output, int outOffset, int frameCount, | ||
239 | //List output, int outOffset, int frameCount, | ||
240 | AudioFormat format) { | ||
241 | for (int channel = 0; channel < format.getChannels(); channel++) { | ||
242 | float[] data; | ||
243 | if (output.size() < channel) { | ||
244 | data = new float[frameCount + outOffset]; | ||
245 | output.add(data); | ||
246 | } else { | ||
247 | data = output.get(channel); | ||
248 | if (data.length < frameCount + outOffset) { | ||
249 | data = new float[frameCount + outOffset]; | ||
250 | output.set(channel, data); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | byte2floatGeneric(input, inByteOffset, format.getFrameSize(), | ||
255 | data, outOffset, | ||
256 | frameCount, format); | ||
257 | inByteOffset += format.getFrameSize() / format.getChannels(); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | |||
262 | /** | ||
263 | * Conversion function to convert an interleaved byte array to | ||
264 | * an interleaved float array. The float array will contain normalized | ||
265 | * samples in the range [-1.0f, +1.0f]. The input array | ||
266 | * provides bytes in the format specified in <code>format</code>. | ||
267 | * <p> | ||
268 | * Only PCM formats are accepted. The method will convert all | ||
269 | * byte values from | ||
270 | * <code>input[inByteOffset]</code> to | ||
271 | * <code>input[inByteOffset + (frameCount * format.getFrameSize()) - 1]</code> | ||
272 | * to floats from | ||
273 | * <code>output[outOffset]</code> to | ||
274 | * <code>output[outOffset + (frameCount * format.getChannels()) - 1]</code> | ||
275 | * | ||
276 | * @param input the audio data in an byte array | ||
277 | * @param inByteOffset index in input where to start the conversion | ||
278 | * @param output the float array that receives the converted audio data | ||
279 | * @param outOffset the start offset in <code>output</code> | ||
280 | * @param frameCount number of frames to be converted | ||
281 | * @param format the input format. Only packed PCM is allowed | ||
282 | * @throws IllegalArgumentException if one of the parameters is out of bounds | ||
283 | * | ||
284 | * @see #byte2float(byte[],int,List,int,int,AudioFormat) | ||
285 | */ | ||
286 | public static void byte2floatInterleaved(byte[] input, int inByteOffset, | ||
287 | float[] output, int outOffset, int frameCount, | ||
288 | AudioFormat format) { | ||
289 | |||
290 | byte2floatGeneric(input, inByteOffset, format.getFrameSize() / format.getChannels(), | ||
291 | output, outOffset, frameCount * format.getChannels(), | ||
292 | format); | ||
293 | } | ||
294 | |||
295 | |||
296 | |||
297 | /** | ||
298 | * Generic conversion function to convert a byte array to | ||
299 | * a float array. | ||
300 | * <p> | ||
301 | * Only PCM formats are accepted. The method will convert all | ||
302 | * bytes from | ||
303 | * <code>input[inByteOffset]</code> to | ||
304 | * <code>input[inByteOffset + (sampleCount * (inByteStep - 1)]</code> | ||
305 | * to samples from | ||
306 | * <code>output[outOffset]</code> to | ||
307 | * <code>output[outOffset+sampleCount-1]</code>. | ||
308 | * <p> | ||
309 | * The <code>format</code>'s channel count is ignored. | ||
310 | * <p> | ||
311 | * For mono data, set <code>inByteOffset</code> to <code>format.getFrameSize()</code>.<br> | ||
312 | * For converting interleaved input data, multiply <code>sampleCount</code> | ||
313 | * by the number of channels and set inByteStep to | ||
314 | * <code>format.getFrameSize() / format.getChannels()</code>. | ||
315 | * | ||
316 | * @param sampleCount number of samples to be written to output | ||
317 | * @param inByteStep how many bytes advance for each output sample in <code>output</code>. | ||
318 | * @throws IllegalArgumentException if one of the parameters is out of bounds | ||
319 | * | ||
320 | * @see #byte2floatInterleaved(byte[],int,float[],int,int,AudioFormat) | ||
321 | * @see #byte2float(byte[],int,List,int,int,AudioFormat) | ||
322 | */ | ||
323 | static void byte2floatGeneric(byte[] input, int inByteOffset, int inByteStep, | ||
324 | float[] output, int outOffset, int sampleCount, | ||
325 | AudioFormat format) { | ||
326 | int formatType = getFormatType(format); | ||
327 | |||
328 | byte2floatGeneric(input, inByteOffset, inByteStep, | ||
329 | output, outOffset, sampleCount, | ||
330 | formatType); | ||
331 | } | ||
332 | |||
333 | |||
334 | /** | ||
335 | * Central conversion function from | ||
336 | * a byte array to a normalized float array. In order to accomodate | ||
337 | * interleaved and non-interleaved | ||
338 | * samples, this method takes inByteStep as parameter which | ||
339 | * can be used to flexibly convert the data. | ||
340 | * <p> | ||
341 | * E.g.:<br> | ||
342 | * mono->mono: inByteStep=format.getFrameSize()<br> | ||
343 | * interleaved_stereo->interleaved_stereo: inByteStep=format.getFrameSize()/2, | ||
344 | * sampleCount*2<br> | ||
345 | * stereo->2 mono arrays:<br> | ||
346 | * ---inByteOffset=0, outOffset=0, inByteStep=format.getFrameSize()<br> | ||
347 | * ---inByteOffset=format.getFrameSize()/2, outOffset=1, inByteStep=format.getFrameSize()<br> | ||
348 | */ | ||
349 | static void byte2floatGeneric(byte[] input, int inByteOffset, int inByteStep, | ||
350 | float[] output, int outOffset, int sampleCount, | ||
351 | int formatType) { | ||
352 | //if (TDebug.TraceAudioConverter) { | ||
353 | // TDebug.out("FloatSampleTools.byte2floatGeneric, formatType=" | ||
354 | // +formatType2Str(formatType)); | ||
355 | //} | ||
356 | int endCount = outOffset + sampleCount; | ||
357 | int inIndex = inByteOffset; | ||
358 | for (int outIndex = outOffset; outIndex < endCount; outIndex++, inIndex+=inByteStep) { | ||
359 | // do conversion | ||
360 | switch (formatType) { | ||
361 | case CT_8S: | ||
362 | output[outIndex]= | ||
363 | ((float) input[inIndex])*invTwoPower7; | ||
364 | break; | ||
365 | case CT_8U: | ||
366 | output[outIndex]= | ||
367 | ((float) ((input[inIndex] & 0xFF)-128))*invTwoPower7; | ||
368 | break; | ||
369 | case CT_16SB: | ||
370 | output[outIndex]= | ||
371 | ((float) ((input[inIndex]<<8) | ||
372 | | (input[inIndex+1] & 0xFF)))*invTwoPower15; | ||
373 | break; | ||
374 | case CT_16SL: | ||
375 | output[outIndex]= | ||
376 | ((float) ((input[inIndex+1]<<8) | ||
377 | | (input[inIndex] & 0xFF)))*invTwoPower15; | ||
378 | break; | ||
379 | case CT_24SB: | ||
380 | output[outIndex]= | ||
381 | ((float) ((input[inIndex]<<16) | ||
382 | | ((input[inIndex+1] & 0xFF)<<8) | ||
383 | | (input[inIndex+2] & 0xFF)))*invTwoPower23; | ||
384 | break; | ||
385 | case CT_24SL: | ||
386 | output[outIndex]= | ||
387 | ((float) ((input[inIndex+2]<<16) | ||
388 | | ((input[inIndex+1] & 0xFF)<<8) | ||
389 | | (input[inIndex] & 0xFF)))*invTwoPower23; | ||
390 | break; | ||
391 | case CT_32SB: | ||
392 | output[outIndex]= | ||
393 | ((float) ((input[inIndex]<<24) | ||
394 | | ((input[inIndex+1] & 0xFF)<<16) | ||
395 | | ((input[inIndex+2] & 0xFF)<<8) | ||
396 | | (input[inIndex+3] & 0xFF)))*invTwoPower31; | ||
397 | break; | ||
398 | case CT_32SL: | ||
399 | output[outIndex]= | ||
400 | ((float) ((input[inIndex+3]<<24) | ||
401 | | ((input[inIndex+2] & 0xFF)<<16) | ||
402 | | ((input[inIndex+1] & 0xFF)<<8) | ||
403 | | (input[inIndex] & 0xFF)))*invTwoPower31; | ||
404 | break; | ||
405 | default: | ||
406 | throw new IllegalArgumentException | ||
407 | ("unsupported format="+formatType2Str(formatType)); | ||
408 | } | ||
409 | } | ||
410 | } | ||
411 | |||
412 | // /////////////////// FLOAT 2 BYTE /////////////////////////////////// // | ||
413 | |||
414 | private static byte quantize8(float sample, float ditherBits) { | ||
415 | if (ditherBits!=0) { | ||
416 | sample+=random.nextFloat()*ditherBits; | ||
417 | } | ||
418 | if (sample>=127.0f) { | ||
419 | return (byte) 127; | ||
420 | } else if (sample<=-128.0f) { | ||
421 | return (byte) -128; | ||
422 | } else { | ||
423 | return (byte) (sample<0?(sample-0.5f):(sample+0.5f)); | ||
424 | } | ||
425 | } | ||
426 | |||
427 | private static int quantize16(float sample, float ditherBits) { | ||
428 | if (ditherBits!=0) { | ||
429 | sample+=random.nextFloat()*ditherBits; | ||
430 | } | ||
431 | if (sample>=32767.0f) { | ||
432 | return 32767; | ||
433 | } else if (sample<=-32768.0f) { | ||
434 | return -32768; | ||
435 | } else { | ||
436 | return (int) (sample<0?(sample-0.5f):(sample+0.5f)); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | private static int quantize24(float sample, float ditherBits) { | ||
441 | if (ditherBits!=0) { | ||
442 | sample+=random.nextFloat()*ditherBits; | ||
443 | } | ||
444 | if (sample>=8388607.0f) { | ||
445 | return 8388607; | ||
446 | } else if (sample<=-8388608.0f) { | ||
447 | return -8388608; | ||
448 | } else { | ||
449 | return (int) (sample<0?(sample-0.5f):(sample+0.5f)); | ||
450 | } | ||
451 | } | ||
452 | |||
453 | private static int quantize32(float sample, float ditherBits) { | ||
454 | if (ditherBits!=0) { | ||
455 | sample+=random.nextFloat()*ditherBits; | ||
456 | } | ||
457 | if (sample>=2147483647.0f) { | ||
458 | return 2147483647; | ||
459 | } else if (sample<=-2147483648.0f) { | ||
460 | return -2147483648; | ||
461 | } else { | ||
462 | return (int) (sample<0?(sample-0.5f):(sample+0.5f)); | ||
463 | } | ||
464 | } | ||
465 | |||
466 | |||
467 | /** | ||
468 | * Conversion function to convert a non-interleaved float audio data to | ||
469 | * an interleaved byte array. The float arrays contains normalized | ||
470 | * samples in the range [-1.0f, +1.0f]. The output array | ||
471 | * will receive bytes in the format specified in <code>format</code>. | ||
472 | * Exactly <code>format.getChannels()</code> channels are converted | ||
473 | * regardless of the number of elements in <code>input</code>. If <code>input</code> | ||
474 | * does not provide enough channels, an </code>IllegalArgumentException<code> is thrown. | ||
475 | * <p> | ||
476 | * Only PCM formats are accepted. The method will convert all | ||
477 | * samples from <code>input(n)[inOffset]</code> to | ||
478 | * <code>input(n)[inOffset + frameCount - 1]</code> | ||
479 | * to byte values from <code>output[outByteOffset]</code> to | ||
480 | * <code>output[outByteOffset + (frameCount * format.getFrameSize()) - 1]</code> | ||
481 | * <p> | ||
482 | * Dithering should be used when the output resolution is significantly | ||
483 | * lower than the original resolution. This includes if the original | ||
484 | * data was 16-bit and it is now converted to 8-bit, or if the | ||
485 | * data was generated in the float domain. No dithering need to be used | ||
486 | * if the original sample data was in e.g. 8-bit and the resulting output | ||
487 | * data has a higher resolution. If dithering is used, a sensitive value | ||
488 | * is DEFAULT_DITHER_BITS. | ||
489 | * | ||
490 | * @param input a List of float arrays with the input audio data | ||
491 | * @param inOffset index in the input arrays where to start the conversion | ||
492 | * @param output the byte array that receives the converted audio data | ||
493 | * @param outByteOffset the start offset in <code>output</code> | ||
494 | * @param frameCount number of frames to be converted. | ||
495 | * @param format the output format. Only packed PCM is allowed | ||
496 | * @param ditherBits if 0, do not dither. Otherwise the number of bits to be dithered | ||
497 | * @throws IllegalArgumentException if one of the parameters is out of bounds | ||
498 | * | ||
499 | * @see #DEFAULT_DITHER_BITS | ||
500 | * @see #float2byteInterleaved(float[],int,byte[],int,int,AudioFormat,float) | ||
501 | */ | ||
502 | //public static void float2byte(List<float[]> input, int inOffset, | ||
503 | public static void float2byte(List input, int inOffset, | ||
504 | byte[] output, int outByteOffset, | ||
505 | int frameCount, | ||
506 | AudioFormat format, float ditherBits) { | ||
507 | for (int channel = 0; channel < format.getChannels(); channel++) { | ||
508 | float[] data = (float[]) input.get(channel); | ||
509 | float2byteGeneric(data, inOffset, | ||
510 | output, outByteOffset, format.getFrameSize(), | ||
511 | frameCount, format, ditherBits); | ||
512 | outByteOffset += format.getFrameSize() / format.getChannels(); | ||
513 | } | ||
514 | } | ||
515 | |||
516 | /** | ||
517 | * Conversion function to convert an interleaved float array to | ||
518 | * an interleaved byte array. The float array contains normalized | ||
519 | * samples in the range [-1.0f, +1.0f]. The output array | ||
520 | * will receive bytes in the format specified in <code>format</code>. | ||
521 | * <p> | ||
522 | * Only PCM formats are accepted. The method will convert all | ||
523 | * samples from <code>input[inOffset]</code> to | ||
524 | * <code>input[inOffset + (frameCount * format.getChannels()) - 1]</code> | ||
525 | * to byte values from <code>output[outByteOffset]</code> to | ||
526 | * <code>output[outByteOffset + (frameCount * format.getFrameSize()) - 1]</code> | ||
527 | * <p> | ||
528 | * Dithering should be used when the output resolution is significantly | ||
529 | * lower than the original resolution. This includes if the original | ||
530 | * data was 16-bit and it is now converted to 8-bit, or if the | ||
531 | * data was generated in the float domain. No dithering need to be used | ||
532 | * if the original sample data was in e.g. 8-bit and the resulting output | ||
533 | * data has a higher resolution. If dithering is used, a sensitive value | ||
534 | * is DEFAULT_DITHER_BITS. | ||
535 | * | ||
536 | * @param input the audio data in normalized samples | ||
537 | * @param inOffset index in input where to start the conversion | ||
538 | * @param output the byte array that receives the converted audio data | ||
539 | * @param outByteOffset the start offset in <code>output</code> | ||
540 | * @param frameCount number of frames to be converted. | ||
541 | * @param format the output format. Only packed PCM is allowed | ||
542 | * @param ditherBits if 0, do not dither. Otherwise the number of bits to be dithered | ||
543 | * @throws IllegalArgumentException if one of the parameters is out of bounds | ||
544 | * | ||
545 | * @see #DEFAULT_DITHER_BITS | ||
546 | * @see #float2byte(List,int,byte[],int,int,AudioFormat,float) | ||
547 | */ | ||
548 | public static void float2byteInterleaved(float[] input, int inOffset, | ||
549 | byte[] output, int outByteOffset, | ||
550 | int frameCount, | ||
551 | AudioFormat format, float ditherBits) { | ||
552 | float2byteGeneric(input, inOffset, | ||
553 | output, outByteOffset, format.getFrameSize() / format.getChannels(), | ||
554 | frameCount * format.getChannels(), | ||
555 | format, ditherBits); | ||
556 | } | ||
557 | |||
558 | |||
559 | |||
560 | /** | ||
561 | * Generic conversion function to convert a float array to | ||
562 | * a byte array. | ||
563 | * <p> | ||
564 | * Only PCM formats are accepted. The method will convert all | ||
565 | * samples from <code>input[inOffset]</code> to | ||
566 | * <code>input[inOffset+sampleCount-1]</code> | ||
567 | * to byte values from <code>output[outByteOffset]</code> to | ||
568 | * <code>output[outByteOffset + (sampleCount * (outByteStep - 1)]</code>. | ||
569 | * <p> | ||
570 | * The <code>format</code>'s channel count is ignored. | ||
571 | * <p> | ||
572 | * For mono data, set <code>outByteOffset</code> to <code>format.getFrameSize()</code>.<br> | ||
573 | * For converting interleaved input data, multiply <code>sampleCount</code> | ||
574 | * by the number of channels and set outByteStep to | ||
575 | * <code>format.getFrameSize() / format.getChannels()</code>. | ||
576 | * | ||
577 | * @param sampleCount number of samples in input to be converted. | ||
578 | * @param outByteStep how many bytes advance for each input sample in <code>input</code>. | ||
579 | * @throws IllegalArgumentException if one of the parameters is out of bounds | ||
580 | * | ||
581 | * @see #float2byteInterleaved(float[],int,byte[],int,int,AudioFormat,float) | ||
582 | * @see #float2byte(List,int,byte[],int,int,AudioFormat,float) | ||
583 | */ | ||
584 | static void float2byteGeneric(float[] input, int inOffset, | ||
585 | byte[] output, int outByteOffset, int outByteStep, | ||
586 | int sampleCount, | ||
587 | AudioFormat format, float ditherBits) { | ||
588 | int formatType = getFormatType(format); | ||
589 | |||
590 | float2byteGeneric(input, inOffset, | ||
591 | output, outByteOffset, outByteStep, | ||
592 | sampleCount, | ||
593 | formatType, ditherBits); | ||
594 | } | ||
595 | |||
596 | |||
597 | /** | ||
598 | * Central conversion function from normalized float array to | ||
599 | * a byte array. In order to accomodate interleaved and non-interleaved | ||
600 | * samples, this method takes outByteStep as parameter which | ||
601 | * can be used to flexibly convert the data. | ||
602 | * <p> | ||
603 | * E.g.:<br> | ||
604 | * mono->mono: outByteStep=format.getFrameSize()<br> | ||
605 | * interleaved stereo->interleaved stereo: outByteStep=format.getFrameSize()/2, sampleCount*2<br> | ||
606 | * 2 mono arrays->stereo:<br> | ||
607 | * ---inOffset=0, outByteOffset=0, outByteStep=format.getFrameSize()<br> | ||
608 | * ---inOffset=1, outByteOffset=format.getFrameSize()/2, outByteStep=format.getFrameSize()<br> | ||
609 | */ | ||
610 | static void float2byteGeneric(float[] input, int inOffset, | ||
611 | byte[] output, int outByteOffset, int outByteStep, | ||
612 | int sampleCount, int formatType, float ditherBits) { | ||
613 | //if (TDebug.TraceAudioConverter) { | ||
614 | // TDebug.out("FloatSampleBuffer.float2byteGeneric, formatType=" | ||
615 | // +"formatType2Str(formatType)); | ||
616 | //} | ||
617 | |||
618 | if (inOffset < 0 | ||
619 | || inOffset + sampleCount > input.length | ||
620 | || sampleCount < 0) { | ||
621 | throw new IllegalArgumentException("invalid input index: " | ||
622 | +"input.length="+input.length | ||
623 | +" inOffset="+inOffset | ||
624 | +" sampleCount="+sampleCount); | ||
625 | } | ||
626 | if (outByteOffset < 0 | ||
627 | || outByteOffset + (sampleCount * outByteStep) > output.length | ||
628 | || outByteStep < getSampleSize(formatType)) { | ||
629 | throw new IllegalArgumentException("invalid output index: " | ||
630 | +"output.length="+output.length | ||
631 | +" outByteOffset="+outByteOffset | ||
632 | +" sampleCount="+sampleCount | ||
633 | +" format="+formatType2Str(formatType)); | ||
634 | } | ||
635 | |||
636 | if (ditherBits!=0.0f && random==null) { | ||
637 | // create the random number generator for dithering | ||
638 | random=new Random(); | ||
639 | } | ||
640 | int endSample = inOffset + sampleCount; | ||
641 | int iSample; | ||
642 | int outIndex = outByteOffset; | ||
643 | for (int inIndex = inOffset; | ||
644 | inIndex < endSample; | ||
645 | inIndex++, outIndex+=outByteStep) { | ||
646 | // do conversion | ||
647 | switch (formatType) { | ||
648 | case CT_8S: | ||
649 | output[outIndex]=quantize8(input[inIndex]*twoPower7, ditherBits); | ||
650 | break; | ||
651 | case CT_8U: | ||
652 | output[outIndex]=(byte) (quantize8((input[inIndex]*twoPower7), ditherBits)+128); | ||
653 | break; | ||
654 | case CT_16SB: | ||
655 | iSample=quantize16(input[inIndex]*twoPower15, ditherBits); | ||
656 | output[outIndex]=(byte) (iSample >> 8); | ||
657 | output[outIndex+1]=(byte) (iSample & 0xFF); | ||
658 | break; | ||
659 | case CT_16SL: | ||
660 | iSample=quantize16(input[inIndex]*twoPower15, ditherBits); | ||
661 | output[outIndex+1]=(byte) (iSample >> 8); | ||
662 | output[outIndex]=(byte) (iSample & 0xFF); | ||
663 | break; | ||
664 | case CT_24SB: | ||
665 | iSample=quantize24(input[inIndex]*twoPower23, ditherBits); | ||
666 | output[outIndex]=(byte) (iSample >> 16); | ||
667 | output[outIndex+1]=(byte) ((iSample >>> 8) & 0xFF); | ||
668 | output[outIndex+2]=(byte) (iSample & 0xFF); | ||
669 | break; | ||
670 | case CT_24SL: | ||
671 | iSample=quantize24(input[inIndex]*twoPower23, ditherBits); | ||
672 | output[outIndex+2]=(byte) (iSample >> 16); | ||
673 | output[outIndex+1]=(byte) ((iSample >>> 8) & 0xFF); | ||
674 | output[outIndex]=(byte) (iSample & 0xFF); | ||
675 | break; | ||
676 | case CT_32SB: | ||
677 | iSample=quantize32(input[inIndex]*twoPower31, ditherBits); | ||
678 | output[outIndex]=(byte) (iSample >> 24); | ||
679 | output[outIndex+1]=(byte) ((iSample >>> 16) & 0xFF); | ||
680 | output[outIndex+2]=(byte) ((iSample >>> 8) & 0xFF); | ||
681 | output[outIndex+3]=(byte) (iSample & 0xFF); | ||
682 | break; | ||
683 | case CT_32SL: | ||
684 | iSample=quantize32(input[inIndex]*twoPower31, ditherBits); | ||
685 | output[outIndex+3]=(byte) (iSample >> 24); | ||
686 | output[outIndex+2]=(byte) ((iSample >>> 16) & 0xFF); | ||
687 | output[outIndex+1]=(byte) ((iSample >>> 8) & 0xFF); | ||
688 | output[outIndex]=(byte) (iSample & 0xFF); | ||
689 | break; | ||
690 | default: | ||
691 | throw new IllegalArgumentException | ||
692 | ("unsupported format="+formatType2Str(formatType)); | ||
693 | } | ||
694 | } | ||
695 | } | ||
696 | } | ||