summaryrefslogtreecommitdiff
path: root/songdbj/org/tritonus/share/sampled/FloatSampleBuffer.java
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/org/tritonus/share/sampled/FloatSampleBuffer.java')
-rw-r--r--songdbj/org/tritonus/share/sampled/FloatSampleBuffer.java734
1 files changed, 0 insertions, 734 deletions
diff --git a/songdbj/org/tritonus/share/sampled/FloatSampleBuffer.java b/songdbj/org/tritonus/share/sampled/FloatSampleBuffer.java
deleted file mode 100644
index d1fe534613..0000000000
--- a/songdbj/org/tritonus/share/sampled/FloatSampleBuffer.java
+++ /dev/null
@@ -1,734 +0,0 @@
1/*
2 * FloatSampleBuffer.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
29package org.tritonus.share.sampled;
30
31import java.util.ArrayList;
32import java.util.Iterator;
33import java.util.Random;
34
35import javax.sound.sampled.AudioSystem;
36import javax.sound.sampled.AudioFormat;
37import javax.sound.sampled.AudioFileFormat;
38import javax.sound.sampled.AudioInputStream;
39import javax.sound.sampled.spi.AudioFileWriter;
40
41import org.tritonus.share.TDebug;
42
43/**
44 * A class for small buffers of samples in linear, 32-bit
45 * floating point format.
46 * <p>
47 * It is supposed to be a replacement of the byte[] stream
48 * architecture of JavaSound, especially for chains of
49 * AudioInputStreams. Ideally, all involved AudioInputStreams
50 * handle reading into a FloatSampleBuffer.
51 * <p>
52 * Specifications:
53 * <ol>
54 * <li>Channels are separated, i.e. for stereo there are 2 float arrays
55 * with the samples for the left and right channel
56 * <li>All data is handled in samples, where one sample means
57 * one float value in each channel
58 * <li>All samples are normalized to the interval [-1.0...1.0]
59 * </ol>
60 * <p>
61 * When a cascade of AudioInputStreams use FloatSampleBuffer for
62 * processing, they may implement the interface FloatSampleInput.
63 * This signals that this stream may provide float buffers
64 * for reading. The data is <i>not</i> converted back to bytes,
65 * but stays in a single buffer that is passed from stream to stream.
66 * For that serves the read(FloatSampleBuffer) method, which is
67 * then used as replacement for the byte-based read functions of
68 * AudioInputStream.<br>
69 * However, backwards compatibility must always be retained, so
70 * even when an AudioInputStream implements FloatSampleInput,
71 * it must work the same way when any of the byte-based read methods
72 * is called.<br>
73 * As an example, consider the following set-up:<br>
74 * <ul>
75 * <li>auAIS is an AudioInputStream (AIS) that reads from an AU file
76 * in 8bit pcm at 8000Hz. It does not implement FloatSampleInput.
77 * <li>pcmAIS1 is an AIS that reads from auAIS and converts the data
78 * to PCM 16bit. This stream implements FloatSampleInput, i.e. it
79 * can generate float audio data from the ulaw samples.
80 * <li>pcmAIS2 reads from pcmAIS1 and adds a reverb.
81 * It operates entirely on floating point samples.
82 * <li>The method that reads from pcmAIS2 (i.e. AudioSystem.write) does
83 * not handle floating point samples.
84 * </ul>
85 * So, what happens when a block of samples is read from pcmAIS2 ?
86 * <ol>
87 * <li>the read(byte[]) method of pcmAIS2 is called
88 * <li>pcmAIS2 always operates on floating point samples, so
89 * it uses an own instance of FloatSampleBuffer and initializes
90 * it with the number of samples requested in the read(byte[])
91 * method.
92 * <li>It queries pcmAIS1 for the FloatSampleInput interface. As it
93 * implements it, pcmAIS2 calls the read(FloatSampleBuffer) method
94 * of pcmAIS1.
95 * <li>pcmAIS1 notes that its underlying stream does not support floats,
96 * so it instantiates a byte buffer which can hold the number of
97 * samples of the FloatSampleBuffer passed to it. It calls the
98 * read(byte[]) method of auAIS.
99 * <li>auAIS fills the buffer with the bytes.
100 * <li>pcmAIS1 calls the <code>initFromByteArray</code> method of
101 * the float buffer to initialize it with the 8 bit data.
102 * <li>Then pcmAIS1 processes the data: as the float buffer is
103 * normalized, it does nothing with the buffer - and returns
104 * control to pcmAIS2. The SampleSizeInBits field of the
105 * AudioFormat of pcmAIS1 defines that it should be 16 bits.
106 * <li>pcmAIS2 receives the filled buffer from pcmAIS1 and does
107 * its processing on the buffer - it adds the reverb.
108 * <li>As pcmAIS2's read(byte[]) method had been called, pcmAIS2
109 * calls the <code>convertToByteArray</code> method of
110 * the float buffer to fill the byte buffer with the
111 * resulting samples.
112 * </ol>
113 * <p>
114 * To summarize, here are some advantages when using a FloatSampleBuffer
115 * for streaming:
116 * <ul>
117 * <li>no conversions from/to bytes need to be done during processing
118 * <li>the sample size in bits is irrelevant - normalized range
119 * <li>higher quality for processing
120 * <li>separated channels (easy process/remove/add channels)
121 * <li>potentially less copying of audio data, as processing
122 * the float samples is generally done in-place. The same
123 * instance of a FloatSampleBuffer may be used from the original data source
124 * to the final data sink.
125 * </ul>
126 * <p>
127 * Simple benchmarks showed that the processing requirements
128 * for the conversion to and from float is about the same as
129 * when converting it to shorts or ints without dithering,
130 * and significantly higher with dithering. An own implementation
131 * of a random number generator may improve this.
132 * <p>
133 * &quot;Lazy&quot; deletion of samples and channels:<br>
134 * <ul>
135 * <li>When the sample count is reduced, the arrays are not resized, but
136 * only the member variable <code>sampleCount</code> is reduced. A subsequent
137 * increase of the sample count (which will occur frequently), will check
138 * that and eventually reuse the existing array.
139 * <li>When a channel is deleted, it is not removed from memory but only
140 * hidden. Subsequent insertions of a channel will check whether a hidden channel
141 * can be reused.
142 * </ul>
143 * The lazy mechanism can save many array instantiation (and copy-) operations
144 * for the sake of performance. All relevant methods exist in a second
145 * version which allows explicitely to disable lazy deletion.
146 * <p>
147 * Use the <code>reset</code> functions to clear the memory and remove
148 * hidden samples and channels.
149 * <p>
150 * Note that the lazy mechanism implies that the arrays returned
151 * from <code>getChannel(int)</code> may have a greater size
152 * than getSampleCount(). Consequently, be sure to never rely on the
153 * length field of the sample arrays.
154 * <p>
155 * As an example, consider a chain of converters that all act
156 * on the same instance of FloatSampleBuffer. Some converters
157 * may decrease the sample count (e.g. sample rate converter) and
158 * delete channels (e.g. PCM2PCM converter). So, processing of one
159 * block will decrease both. For the next block, all starts
160 * from the beginning. With the lazy mechanism, all float arrays
161 * are only created once for processing all blocks.<br>
162 * Having lazy disabled would require for each chunk that is processed
163 * <ol>
164 * <li>new instantiation of all channel arrays
165 * at the converter chain beginning as they have been
166 * either deleted or decreased in size during processing of the
167 * previous chunk, and
168 * <li>re-instantiation of all channel arrays for
169 * the reduction of the sample count.
170 * </ol>
171 * <p>
172 * Dithering:<br>
173 * By default, this class uses dithering for reduction
174 * of sample width (e.g. original data was 16bit, target
175 * data is 8bit). As dithering may be needed in other cases
176 * (especially when the float samples are processed using DSP
177 * algorithms), or it is preferred to switch it off,
178 * dithering can be explicitely switched on or off with
179 * the method setDitherMode(int).<br>
180 * For a discussion about dithering, see
181 * <a href="http://www.iqsoft.com/IQSMagazine/BobsSoapbox/Dithering.htm">
182 * here</a> and
183 * <a href="http://www.iqsoft.com/IQSMagazine/BobsSoapbox/Dithering2.htm">
184 * here</a>.
185 *
186 * @author Florian Bomers
187 */
188
189public class FloatSampleBuffer {
190
191 /** Whether the functions without lazy parameter are lazy or not. */
192 private static final boolean LAZY_DEFAULT=true;
193
194 private ArrayList<float[]> channels = new ArrayList<float[]>(); // contains for each channel a float array
195 private int sampleCount=0;
196 private int channelCount=0;
197 private float sampleRate=0;
198 private int originalFormatType=0;
199
200 /** Constant for setDitherMode: dithering will be enabled if sample size is decreased */
201 public static final int DITHER_MODE_AUTOMATIC=0;
202 /** Constant for setDitherMode: dithering will be done */
203 public static final int DITHER_MODE_ON=1;
204 /** Constant for setDitherMode: dithering will not be done */
205 public static final int DITHER_MODE_OFF=2;
206
207 private float ditherBits = FloatSampleTools.DEFAULT_DITHER_BITS;
208
209 // e.g. the sample rate converter may want to force dithering
210 private int ditherMode = DITHER_MODE_AUTOMATIC;
211
212 //////////////////////////////// initialization /////////////////////////////////
213
214 /**
215 * Create an instance with initially no channels.
216 */
217 public FloatSampleBuffer() {
218 this(0,0,1);
219 }
220
221 /**
222 * Create an empty FloatSampleBuffer with the specified number of channels,
223 * samples, and the specified sample rate.
224 */
225 public FloatSampleBuffer(int channelCount, int sampleCount, float sampleRate) {
226 init(channelCount, sampleCount, sampleRate, LAZY_DEFAULT);
227 }
228
229 /**
230 * Creates a new instance of FloatSampleBuffer and initializes
231 * it with audio data given in the interleaved byte array <code>buffer</code>.
232 */
233 public FloatSampleBuffer(byte[] buffer, int offset, int byteCount,
234 AudioFormat format) {
235 this(format.getChannels(),
236 byteCount/(format.getSampleSizeInBits()/8*format.getChannels()),
237 format.getSampleRate());
238 initFromByteArray(buffer, offset, byteCount, format);
239 }
240
241 protected void init(int channelCount, int sampleCount, float sampleRate) {
242 init(channelCount, sampleCount, sampleRate, LAZY_DEFAULT);
243 }
244
245 protected void init(int channelCount, int sampleCount, float sampleRate, boolean lazy) {
246 if (channelCount<0 || sampleCount<0) {
247 throw new IllegalArgumentException(
248 "invalid parameters in initialization of FloatSampleBuffer.");
249 }
250 setSampleRate(sampleRate);
251 if (getSampleCount()!=sampleCount || getChannelCount()!=channelCount) {
252 createChannels(channelCount, sampleCount, lazy);
253 }
254 }
255
256 private void createChannels(int channelCount, int sampleCount, boolean lazy) {
257 this.sampleCount=sampleCount;
258 // lazy delete of all channels. Intentionally lazy !
259 this.channelCount=0;
260 for (int ch=0; ch<channelCount; ch++) {
261 insertChannel(ch, false, lazy);
262 }
263 if (!lazy) {
264 // remove hidden channels
265 while (channels.size()>channelCount) {
266 channels.remove(channels.size()-1);
267 }
268 }
269 }
270
271
272 /**
273 * Resets this buffer with the audio data specified
274 * in the arguments. This FloatSampleBuffer's sample count
275 * will be set to <code>byteCount / format.getFrameSize()</code>.
276 * If LAZY_DEFAULT is true, it will use lazy deletion.
277 *
278 * @throws IllegalArgumentException
279 */
280 public void initFromByteArray(byte[] buffer, int offset, int byteCount,
281 AudioFormat format) {
282 initFromByteArray(buffer, offset, byteCount, format, LAZY_DEFAULT);
283 }
284
285
286 /**
287 * Resets this buffer with the audio data specified
288 * in the arguments. This FloatSampleBuffer's sample count
289 * will be set to <code>byteCount / format.getFrameSize()</code>.
290 *
291 * @param lazy if true, then existing channels will be tried to be re-used
292 * to minimize garbage collection.
293 * @throws IllegalArgumentException
294 */
295 public void initFromByteArray(byte[] buffer, int offset, int byteCount,
296 AudioFormat format, boolean lazy) {
297 if (offset+byteCount>buffer.length) {
298 throw new IllegalArgumentException
299 ("FloatSampleBuffer.initFromByteArray: buffer too small.");
300 }
301
302 int thisSampleCount = byteCount/format.getFrameSize();
303 init(format.getChannels(), thisSampleCount, format.getSampleRate(), lazy);
304
305 // save format for automatic dithering mode
306 originalFormatType = FloatSampleTools.getFormatType(format);
307
308 FloatSampleTools.byte2float(buffer, offset,
309 channels, 0, sampleCount, format);
310 }
311
312 /**
313 * Resets this sample buffer with the data in <code>source</code>.
314 */
315 public void initFromFloatSampleBuffer(FloatSampleBuffer source) {
316 init(source.getChannelCount(), source.getSampleCount(), source.getSampleRate());
317 for (int ch=0; ch<getChannelCount(); ch++) {
318 System.arraycopy(source.getChannel(ch), 0, getChannel(ch), 0, sampleCount);
319 }
320 }
321
322 /**
323 * Deletes all channels, frees memory...
324 * This also removes hidden channels by lazy remove.
325 */
326 public void reset() {
327 init(0,0,1, false);
328 }
329
330 /**
331 * Destroys any existing data and creates new channels.
332 * It also destroys lazy removed channels and samples.
333 */
334 public void reset(int channels, int sampleCount, float sampleRate) {
335 init(channels, sampleCount, sampleRate, false);
336 }
337
338 //////////////////////////////// conversion back to bytes /////////////////////////////////
339
340 /**
341 * @return the required size of the buffer
342 * for calling convertToByteArray(..) is called
343 */
344 public int getByteArrayBufferSize(AudioFormat format) {
345 // make sure this format is supported
346 FloatSampleTools.getFormatType(format);
347 return format.getFrameSize() * getSampleCount();
348 }
349
350 /**
351 * Writes this sample buffer's audio data to <code>buffer</code>
352 * as an interleaved byte array.
353 * <code>buffer</code> must be large enough to hold all data.
354 *
355 * @throws IllegalArgumentException when buffer is too small or <code>format</code> doesn't match
356 * @return number of bytes written to <code>buffer</code>
357 */
358 public int convertToByteArray(byte[] buffer, int offset, AudioFormat format) {
359 int byteCount = getByteArrayBufferSize(format);
360 if (offset + byteCount > buffer.length) {
361 throw new IllegalArgumentException
362 ("FloatSampleBuffer.convertToByteArray: buffer too small.");
363 }
364 if (format.getSampleRate()!=getSampleRate()) {
365 throw new IllegalArgumentException
366 ("FloatSampleBuffer.convertToByteArray: different samplerates.");
367 }
368 if (format.getChannels()!=getChannelCount()) {
369 throw new IllegalArgumentException
370 ("FloatSampleBuffer.convertToByteArray: different channel count.");
371 }
372 FloatSampleTools.float2byte(channels, 0, buffer, offset, getSampleCount(),
373 format, getConvertDitherBits(FloatSampleTools.getFormatType(format)));
374
375 return byteCount;
376 }
377
378
379 /**
380 * Creates a new byte[] buffer, fills it with the audio data, and returns it.
381 * @throws IllegalArgumentException when sample rate or channels do not match
382 * @see #convertToByteArray(byte[], int, AudioFormat)
383 */
384 public byte[] convertToByteArray(AudioFormat format) {
385 // throws exception when sampleRate doesn't match
386 // creates a new byte[] buffer and returns it
387 byte[] res = new byte[getByteArrayBufferSize(format)];
388 convertToByteArray(res, 0, format);
389 return res;
390 }
391
392 //////////////////////////////// actions /////////////////////////////////
393
394 /**
395 * Resizes this buffer.
396 * <p>If <code>keepOldSamples</code> is true, as much as possible samples are
397 * retained. If the buffer is enlarged, silence is added at the end.
398 * If <code>keepOldSamples</code> is false, existing samples are discarded
399 * and the buffer contains random samples.
400 */
401 public void changeSampleCount(int newSampleCount, boolean keepOldSamples) {
402 int oldSampleCount=getSampleCount();
403 if (oldSampleCount==newSampleCount) {
404 return;
405 }
406 Object[] oldChannels=null;
407 if (keepOldSamples) {
408 oldChannels=getAllChannels();
409 }
410 init(getChannelCount(), newSampleCount, getSampleRate());
411 if (keepOldSamples) {
412 // copy old channels and eventually silence out new samples
413 int copyCount=newSampleCount<oldSampleCount?
414 newSampleCount:oldSampleCount;
415 for (int ch=0; ch<getChannelCount(); ch++) {
416 float[] oldSamples=(float[]) oldChannels[ch];
417 float[] newSamples=(float[]) getChannel(ch);
418 if (oldSamples!=newSamples) {
419 // if this sample array was not object of lazy delete
420 System.arraycopy(oldSamples, 0, newSamples, 0, copyCount);
421 }
422 if (oldSampleCount<newSampleCount) {
423 // silence out new samples
424 for (int i=oldSampleCount; i<newSampleCount; i++) {
425 newSamples[i]=0.0f;
426 }
427 }
428 }
429 }
430 }
431
432 public void makeSilence() {
433 // silence all channels
434 if (getChannelCount()>0) {
435 makeSilence(0);
436 for (int ch=1; ch<getChannelCount(); ch++) {
437 copyChannel(0, ch);
438 }
439 }
440 }
441
442 public void makeSilence(int channel) {
443 float[] samples=getChannel(channel);
444 for (int i=0; i<getSampleCount(); i++) {
445 samples[i]=0.0f;
446 }
447 }
448
449 public void addChannel(boolean silent) {
450 // creates new, silent channel
451 insertChannel(getChannelCount(), silent);
452 }
453
454 /**
455 * Insert a (silent) channel at position <code>index</code>.
456 * If LAZY_DEFAULT is true, this is done lazily.
457 */
458 public void insertChannel(int index, boolean silent) {
459 insertChannel(index, silent, LAZY_DEFAULT);
460 }
461
462 /**
463 * Inserts a channel at position <code>index</code>.
464 * <p>If <code>silent</code> is true, the new channel will be silent.
465 * Otherwise it will contain random data.
466 * <p>If <code>lazy</code> is true, hidden channels which have at least getSampleCount()
467 * elements will be examined for reusage as inserted channel.<br>
468 * If <code>lazy</code> is false, still hidden channels are reused,
469 * but it is assured that the inserted channel has exactly getSampleCount() elements,
470 * thus not wasting memory.
471 */
472 public void insertChannel(int index, boolean silent, boolean lazy) {
473 int physSize=channels.size();
474 int virtSize=getChannelCount();
475 float[] newChannel=null;
476 if (physSize>virtSize) {
477 // there are hidden channels. Try to use one.
478 for (int ch=virtSize; ch<physSize; ch++) {
479 float[] thisChannel=(float[]) channels.get(ch);
480 if ((lazy && thisChannel.length>=getSampleCount())
481 || (!lazy && thisChannel.length==getSampleCount())) {
482 // we found a matching channel. Use it !
483 newChannel=thisChannel;
484 channels.remove(ch);
485 break;
486 }
487 }
488 }
489 if (newChannel==null) {
490 newChannel=new float[getSampleCount()];
491 }
492 channels.add(index, newChannel);
493 this.channelCount++;
494 if (silent) {
495 makeSilence(index);
496 }
497 }
498
499 /** performs a lazy remove of the channel */
500 public void removeChannel(int channel) {
501 removeChannel(channel, LAZY_DEFAULT);
502 }
503
504
505 /**
506 * Removes a channel.
507 * If lazy is true, the channel is not physically removed, but only hidden.
508 * These hidden channels are reused by subsequent calls to addChannel
509 * or insertChannel.
510 */
511 public void removeChannel(int channel, boolean lazy) {
512 if (!lazy) {
513 channels.remove(channel);
514 } else if (channel<getChannelCount()-1) {
515 // if not already, move this channel at the end
516 channels.add(channels.remove(channel));
517 }
518 channelCount--;
519 }
520
521 /**
522 * both source and target channel have to exist. targetChannel
523 * will be overwritten
524 */
525 public void copyChannel(int sourceChannel, int targetChannel) {
526 float[] source=getChannel(sourceChannel);
527 float[] target=getChannel(targetChannel);
528 System.arraycopy(source, 0, target, 0, getSampleCount());
529 }
530
531 /**
532 * Copies data inside all channel. When the 2 regions
533 * overlap, the behavior is not specified.
534 */
535 public void copy(int sourceIndex, int destIndex, int length) {
536 for (int i=0; i<getChannelCount(); i++) {
537 copy(i, sourceIndex, destIndex, length);
538 }
539 }
540
541 /**
542 * Copies data inside a channel. When the 2 regions
543 * overlap, the behavior is not specified.
544 */
545 public void copy(int channel, int sourceIndex, int destIndex, int length) {
546 float[] data=getChannel(channel);
547 int bufferCount=getSampleCount();
548 if (sourceIndex+length>bufferCount || destIndex+length>bufferCount
549 || sourceIndex<0 || destIndex<0 || length<0) {
550 throw new IndexOutOfBoundsException("parameters exceed buffer size");
551 }
552 System.arraycopy(data, sourceIndex, data, destIndex, length);
553 }
554
555 /**
556 * Mix up of 1 channel to n channels.<br>
557 * It copies the first channel to all newly created channels.
558 * @param targetChannelCount the number of channels that this sample buffer
559 * will have after expanding. NOT the number of
560 * channels to add !
561 * @exception IllegalArgumentException if this buffer does not have one
562 * channel before calling this method.
563 */
564 public void expandChannel(int targetChannelCount) {
565 // even more sanity...
566 if (getChannelCount()!=1) {
567 throw new IllegalArgumentException(
568 "FloatSampleBuffer: can only expand channels for mono signals.");
569 }
570 for (int ch=1; ch<targetChannelCount; ch++) {
571 addChannel(false);
572 copyChannel(0, ch);
573 }
574 }
575
576 /**
577 * Mix down of n channels to one channel.<br>
578 * It uses a simple mixdown: all other channels are added to first channel.<br>
579 * The volume is NOT lowered !
580 * Be aware, this might cause clipping when converting back
581 * to integer samples.
582 */
583 public void mixDownChannels() {
584 float[] firstChannel=getChannel(0);
585 int sampleCount=getSampleCount();
586 int channelCount=getChannelCount();
587 for (int ch=channelCount-1; ch>0; ch--) {
588 float[] thisChannel=getChannel(ch);
589 for (int i=0; i<sampleCount; i++) {
590 firstChannel[i]+=thisChannel[i];
591 }
592 removeChannel(ch);
593 }
594 }
595
596 /**
597 * Initializes audio data from the provided byte array.
598 * The float samples are written at <code>destOffset</code>.
599 * This FloatSampleBuffer must be big enough to accomodate the samples.
600 * <p>
601 * <code>srcBuffer</code> is read from index <code>srcOffset</code>
602 * to <code>(srcOffset + (lengthInSamples * format.getFrameSize()))</code.
603 *
604 * @param input the input buffer in interleaved audio data
605 * @param inByteOffset the offset in <code>input</code>
606 * @param format input buffer's audio format
607 * @param floatOffset the offset where to write the float samples
608 * @param frameCount number of samples to write to this sample buffer
609 */
610 public void setSamplesFromBytes(byte[] input, int inByteOffset, AudioFormat format,
611 int floatOffset, int frameCount) {
612 if (floatOffset < 0 || frameCount < 0 || inByteOffset < 0) {
613 throw new IllegalArgumentException
614 ("FloatSampleBuffer.setSamplesFromBytes: negative inByteOffset, floatOffset, or frameCount");
615 }
616 if (inByteOffset + (frameCount * format.getFrameSize()) > input.length) {
617 throw new IllegalArgumentException
618 ("FloatSampleBuffer.setSamplesFromBytes: input buffer too small.");
619 }
620 if (floatOffset + frameCount > getSampleCount()) {
621 throw new IllegalArgumentException
622 ("FloatSampleBuffer.setSamplesFromBytes: frameCount too large");
623 }
624
625 FloatSampleTools.byte2float(input, inByteOffset, channels, floatOffset, frameCount, format);
626 }
627
628 //////////////////////////////// properties /////////////////////////////////
629
630 public int getChannelCount() {
631 return channelCount;
632 }
633
634 public int getSampleCount() {
635 return sampleCount;
636 }
637
638 public float getSampleRate() {
639 return sampleRate;
640 }
641
642 /**
643 * Sets the sample rate of this buffer.
644 * NOTE: no conversion is done. The samples are only re-interpreted.
645 */
646 public void setSampleRate(float sampleRate) {
647 if (sampleRate<=0) {
648 throw new IllegalArgumentException
649 ("Invalid samplerate for FloatSampleBuffer.");
650 }
651 this.sampleRate=sampleRate;
652 }
653
654 /**
655 * NOTE: the returned array may be larger than sampleCount. So in any case,
656 * sampleCount is to be respected.
657 */
658 public float[] getChannel(int channel) {
659 if (channel<0 || channel>=getChannelCount()) {
660 throw new IllegalArgumentException(
661 "FloatSampleBuffer: invalid channel number.");
662 }
663 return (float[]) channels.get(channel);
664 }
665
666 public Object[] getAllChannels() {
667 Object[] res=new Object[getChannelCount()];
668 for (int ch=0; ch<getChannelCount(); ch++) {
669 res[ch]=getChannel(ch);
670 }
671 return res;
672 }
673
674 /**
675 * Set the number of bits for dithering.
676 * Typically, a value between 0.2 and 0.9 gives best results.
677 * <p>Note: this value is only used, when dithering is actually performed.
678 */
679 public void setDitherBits(float ditherBits) {
680 if (ditherBits<=0) {
681 throw new IllegalArgumentException("DitherBits must be greater than 0");
682 }
683 this.ditherBits=ditherBits;
684 }
685
686 public float getDitherBits() {
687 return ditherBits;
688 }
689
690 /**
691 * Sets the mode for dithering.
692 * This can be one of:
693 * <ul><li>DITHER_MODE_AUTOMATIC: it is decided automatically,
694 * whether dithering is necessary - in general when sample size is
695 * decreased.
696 * <li>DITHER_MODE_ON: dithering will be forced
697 * <li>DITHER_MODE_OFF: dithering will not be done.
698 * </ul>
699 */
700 public void setDitherMode(int mode) {
701 if (mode!=DITHER_MODE_AUTOMATIC
702 && mode!=DITHER_MODE_ON
703 && mode!=DITHER_MODE_OFF) {
704 throw new IllegalArgumentException("Illegal DitherMode");
705 }
706 this.ditherMode=mode;
707 }
708
709 public int getDitherMode() {
710 return ditherMode;
711 }
712
713
714 /**
715 * @return the ditherBits parameter for the float2byte functions
716 */
717 protected float getConvertDitherBits(int newFormatType) {
718 // let's see whether dithering is necessary
719 boolean doDither = false;
720 switch (ditherMode) {
721 case DITHER_MODE_AUTOMATIC:
722 doDither=(originalFormatType & FloatSampleTools.F_SAMPLE_WIDTH_MASK)>
723 (newFormatType & FloatSampleTools.F_SAMPLE_WIDTH_MASK);
724 break;
725 case DITHER_MODE_ON:
726 doDither=true;
727 break;
728 case DITHER_MODE_OFF:
729 doDither=false;
730 break;
731 }
732 return doDither?ditherBits:0.0f;
733 }
734}