org.tritonus.share.sampled
Class FloatSampleBuffer

java.lang.Object
  extended byorg.tritonus.share.sampled.FloatSampleBuffer

public class FloatSampleBuffer
extends Object

A class for small buffers of samples in linear, 32-bit floating point format.

It is supposed to be a replacement of the byte[] stream architecture of JavaSound, especially for chains of AudioInputStreams. Ideally, all involved AudioInputStreams handle reading into a FloatSampleBuffer.

Specifications:

  1. Channels are separated, i.e. for stereo there are 2 float arrays with the samples for the left and right channel
  2. All data is handled in samples, where one sample means one float value in each channel
  3. All samples are normalized to the interval [-1.0...1.0]

When a cascade of AudioInputStreams use FloatSampleBuffer for processing, they may implement the interface FloatSampleInput. This signals that this stream may provide float buffers for reading. The data is not converted back to bytes, but stays in a single buffer that is passed from stream to stream. For that serves the read(FloatSampleBuffer) method, which is then used as replacement for the byte-based read functions of AudioInputStream.
However, backwards compatibility must always be retained, so even when an AudioInputStream implements FloatSampleInput, it must work the same way when any of the byte-based read methods is called.
As an example, consider the following set-up:

So, what happens when a block of samples is read from pcmAIS2 ?
  1. the read(byte[]) method of pcmAIS2 is called
  2. pcmAIS2 always operates on floating point samples, so it uses an own instance of FloatSampleBuffer and initializes it with the number of samples requested in the read(byte[]) method.
  3. It queries pcmAIS1 for the FloatSampleInput interface. As it implements it, pcmAIS2 calls the read(FloatSampleBuffer) method of pcmAIS1.
  4. pcmAIS1 notes that its underlying stream does not support floats, so it instantiates a byte buffer which can hold the number of samples of the FloatSampleBuffer passed to it. It calls the read(byte[]) method of auAIS.
  5. auAIS fills the buffer with the bytes.
  6. pcmAIS1 calls the initFromByteArray method of the float buffer to initialize it with the 8 bit data.
  7. Then pcmAIS1 processes the data: as the float buffer is normalized, it does nothing with the buffer - and returns control to pcmAIS2. The SampleSizeInBits field of the AudioFormat of pcmAIS1 defines that it should be 16 bits.
  8. pcmAIS2 receives the filled buffer from pcmAIS1 and does its processing on the buffer - it adds the reverb.
  9. As pcmAIS2's read(byte[]) method had been called, pcmAIS2 calls the convertToByteArray method of the float buffer to fill the byte buffer with the resulting samples.

To summarize, here are some advantages when using a FloatSampleBuffer for streaming:

Simple benchmarks showed that the processing requirements for the conversion to and from float is about the same as when converting it to shorts or ints without dithering, and significantly higher with dithering. An own implementation of a random number generator may improve this.

"Lazy" deletion of samples and channels:

The lazy mechanism can save many array instantiation (and copy-) operations for the sake of performance. All relevant methods exist in a second version which allows explicitely to disable lazy deletion.

Use the reset functions to clear the memory and remove hidden samples and channels.

Note that the lazy mechanism implies that the arrays returned from getChannel(int) may have a greater size than getSampleCount(). Consequently, be sure to never rely on the length field of the sample arrays.

As an example, consider a chain of converters that all act on the same instance of FloatSampleBuffer. Some converters may decrease the sample count (e.g. sample rate converter) and delete channels (e.g. PCM2PCM converter). So, processing of one block will decrease both. For the next block, all starts from the beginning. With the lazy mechanism, all float arrays are only created once for processing all blocks.
Having lazy disabled would require for each chunk that is processed

  1. new instantiation of all channel arrays at the converter chain beginning as they have been either deleted or decreased in size during processing of the previous chunk, and
  2. re-instantiation of all channel arrays for the reduction of the sample count.

Dithering:
By default, this class uses dithering for reduction of sample width (e.g. original data was 16bit, target data is 8bit). As dithering may be needed in other cases (especially when the float samples are processed using DSP algorithms), or it is preferred to switch it off, dithering can be explicitely switched on or off with the method setDitherMode(int).
For a discussion about dithering, see here and here.

Author:
Florian Bomers

Field Summary
static int DITHER_MODE_AUTOMATIC
          Constant for setDitherMode: dithering will be enabled if sample size is decreased
static int DITHER_MODE_OFF
          Constant for setDitherMode: dithering will not be done
static int DITHER_MODE_ON
          Constant for setDitherMode: dithering will be done
 
Constructor Summary
FloatSampleBuffer()
          Create an instance with initially no channels.
FloatSampleBuffer(byte[] buffer, int offset, int byteCount, AudioFormat format)
          Creates a new instance of FloatSampleBuffer and initializes it with audio data given in the interleaved byte array buffer.
FloatSampleBuffer(int channelCount, int sampleCount, float sampleRate)
          Create an empty FloatSampleBuffer with the specified number of channels, samples, and the specified sample rate.
 
Method Summary
 void addChannel(boolean silent)
           
 void changeSampleCount(int newSampleCount, boolean keepOldSamples)
          Resizes this buffer.
 byte[] convertToByteArray(AudioFormat format)
          Creates a new byte[] buffer, fills it with the audio data, and returns it.
 int convertToByteArray(byte[] buffer, int offset, AudioFormat format)
          Writes this sample buffer's audio data to buffer as an interleaved byte array.
 void copy(int sourceIndex, int destIndex, int length)
          Copies data inside all channel.
 void copy(int channel, int sourceIndex, int destIndex, int length)
          Copies data inside a channel.
 void copyChannel(int sourceChannel, int targetChannel)
          both source and target channel have to exist. targetChannel will be overwritten
 void expandChannel(int targetChannelCount)
          Mix up of 1 channel to n channels.
 Object[] getAllChannels()
           
 int getByteArrayBufferSize(AudioFormat format)
           
 float[] getChannel(int channel)
          NOTE: the returned array may be larger than sampleCount.
 int getChannelCount()
           
protected  float getConvertDitherBits(int newFormatType)
           
 float getDitherBits()
           
 int getDitherMode()
           
 int getSampleCount()
           
 float getSampleRate()
           
protected  void init(int channelCount, int sampleCount, float sampleRate)
           
protected  void init(int channelCount, int sampleCount, float sampleRate, boolean lazy)
           
 void initFromByteArray(byte[] buffer, int offset, int byteCount, AudioFormat format)
          Resets this buffer with the audio data specified in the arguments.
 void initFromByteArray(byte[] buffer, int offset, int byteCount, AudioFormat format, boolean lazy)
          Resets this buffer with the audio data specified in the arguments.
 void initFromFloatSampleBuffer(FloatSampleBuffer source)
          Resets this sample buffer with the data in source.
 void insertChannel(int index, boolean silent)
          Insert a (silent) channel at position index.
 void insertChannel(int index, boolean silent, boolean lazy)
          Inserts a channel at position index.
 void makeSilence()
           
 void makeSilence(int channel)
           
 void mixDownChannels()
          Mix down of n channels to one channel.
 void removeChannel(int channel)
          performs a lazy remove of the channel
 void removeChannel(int channel, boolean lazy)
          Removes a channel.
 void reset()
          Deletes all channels, frees memory...
 void reset(int channels, int sampleCount, float sampleRate)
          Destroys any existing data and creates new channels.
 void setDitherBits(float ditherBits)
          Set the number of bits for dithering.
 void setDitherMode(int mode)
          Sets the mode for dithering.
 void setSampleRate(float sampleRate)
          Sets the sample rate of this buffer.
 void setSamplesFromBytes(byte[] input, int inByteOffset, AudioFormat format, int floatOffset, int frameCount)
          Initializes audio data from the provided byte array.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

DITHER_MODE_AUTOMATIC

public static final int DITHER_MODE_AUTOMATIC
Constant for setDitherMode: dithering will be enabled if sample size is decreased

See Also:
Constant Field Values

DITHER_MODE_ON

public static final int DITHER_MODE_ON
Constant for setDitherMode: dithering will be done

See Also:
Constant Field Values

DITHER_MODE_OFF

public static final int DITHER_MODE_OFF
Constant for setDitherMode: dithering will not be done

See Also:
Constant Field Values
Constructor Detail

FloatSampleBuffer

public FloatSampleBuffer()
Create an instance with initially no channels.


FloatSampleBuffer

public FloatSampleBuffer(int channelCount,
                         int sampleCount,
                         float sampleRate)
Create an empty FloatSampleBuffer with the specified number of channels, samples, and the specified sample rate.


FloatSampleBuffer

public FloatSampleBuffer(byte[] buffer,
                         int offset,
                         int byteCount,
                         AudioFormat format)
Creates a new instance of FloatSampleBuffer and initializes it with audio data given in the interleaved byte array buffer.

Method Detail

init

protected void init(int channelCount,
                    int sampleCount,
                    float sampleRate)

init

protected void init(int channelCount,
                    int sampleCount,
                    float sampleRate,
                    boolean lazy)

initFromByteArray

public void initFromByteArray(byte[] buffer,
                              int offset,
                              int byteCount,
                              AudioFormat format)
Resets this buffer with the audio data specified in the arguments. This FloatSampleBuffer's sample count will be set to byteCount / format.getFrameSize(). If LAZY_DEFAULT is true, it will use lazy deletion.

Throws:
IllegalArgumentException

initFromByteArray

public void initFromByteArray(byte[] buffer,
                              int offset,
                              int byteCount,
                              AudioFormat format,
                              boolean lazy)
Resets this buffer with the audio data specified in the arguments. This FloatSampleBuffer's sample count will be set to byteCount / format.getFrameSize().

Parameters:
lazy - if true, then existing channels will be tried to be re-used to minimize garbage collection.
Throws:
IllegalArgumentException

initFromFloatSampleBuffer

public void initFromFloatSampleBuffer(FloatSampleBuffer source)
Resets this sample buffer with the data in source.


reset

public void reset()
Deletes all channels, frees memory... This also removes hidden channels by lazy remove.


reset

public void reset(int channels,
                  int sampleCount,
                  float sampleRate)
Destroys any existing data and creates new channels. It also destroys lazy removed channels and samples.


getByteArrayBufferSize

public int getByteArrayBufferSize(AudioFormat format)
Returns:
the required size of the buffer for calling convertToByteArray(..) is called

convertToByteArray

public int convertToByteArray(byte[] buffer,
                              int offset,
                              AudioFormat format)
Writes this sample buffer's audio data to buffer as an interleaved byte array. buffer must be large enough to hold all data.

Returns:
number of bytes written to buffer
Throws:
IllegalArgumentException - when buffer is too small or format doesn't match

convertToByteArray

public byte[] convertToByteArray(AudioFormat format)
Creates a new byte[] buffer, fills it with the audio data, and returns it.

Throws:
IllegalArgumentException - when sample rate or channels do not match
See Also:
convertToByteArray(byte[], int, AudioFormat)

changeSampleCount

public void changeSampleCount(int newSampleCount,
                              boolean keepOldSamples)
Resizes this buffer.

If keepOldSamples is true, as much as possible samples are retained. If the buffer is enlarged, silence is added at the end. If keepOldSamples is false, existing samples are discarded and the buffer contains random samples.


makeSilence

public void makeSilence()

makeSilence

public void makeSilence(int channel)

addChannel

public void addChannel(boolean silent)

insertChannel

public void insertChannel(int index,
                          boolean silent)
Insert a (silent) channel at position index. If LAZY_DEFAULT is true, this is done lazily.


insertChannel

public void insertChannel(int index,
                          boolean silent,
                          boolean lazy)
Inserts a channel at position index.

If silent is true, the new channel will be silent. Otherwise it will contain random data.

If lazy is true, hidden channels which have at least getSampleCount() elements will be examined for reusage as inserted channel.
If lazy is false, still hidden channels are reused, but it is assured that the inserted channel has exactly getSampleCount() elements, thus not wasting memory.


removeChannel

public void removeChannel(int channel)
performs a lazy remove of the channel


removeChannel

public void removeChannel(int channel,
                          boolean lazy)
Removes a channel. If lazy is true, the channel is not physically removed, but only hidden. These hidden channels are reused by subsequent calls to addChannel or insertChannel.


copyChannel

public void copyChannel(int sourceChannel,
                        int targetChannel)
both source and target channel have to exist. targetChannel will be overwritten


copy

public void copy(int sourceIndex,
                 int destIndex,
                 int length)
Copies data inside all channel. When the 2 regions overlap, the behavior is not specified.


copy

public void copy(int channel,
                 int sourceIndex,
                 int destIndex,
                 int length)
Copies data inside a channel. When the 2 regions overlap, the behavior is not specified.


expandChannel

public void expandChannel(int targetChannelCount)
Mix up of 1 channel to n channels.
It copies the first channel to all newly created channels.

Parameters:
targetChannelCount - the number of channels that this sample buffer will have after expanding. NOT the number of channels to add !
Throws:
IllegalArgumentException - if this buffer does not have one channel before calling this method.

mixDownChannels

public void mixDownChannels()
Mix down of n channels to one channel.
It uses a simple mixdown: all other channels are added to first channel.
The volume is NOT lowered ! Be aware, this might cause clipping when converting back to integer samples.


setSamplesFromBytes

public void setSamplesFromBytes(byte[] input,
                                int inByteOffset,
                                AudioFormat format,
                                int floatOffset,
                                int frameCount)
Initializes audio data from the provided byte array. The float samples are written at destOffset. This FloatSampleBuffer must be big enough to accomodate the samples.

srcBuffer is read from index srcOffset to (srcOffset + (lengthInSamples * format.getFrameSize()))

Parameters:
input - the input buffer in interleaved audio data
inByteOffset - the offset in input
format - input buffer's audio format
floatOffset - the offset where to write the float samples
frameCount - number of samples to write to this sample buffer

getChannelCount

public int getChannelCount()

getSampleCount

public int getSampleCount()

getSampleRate

public float getSampleRate()

setSampleRate

public void setSampleRate(float sampleRate)
Sets the sample rate of this buffer. NOTE: no conversion is done. The samples are only re-interpreted.


getChannel

public float[] getChannel(int channel)
NOTE: the returned array may be larger than sampleCount. So in any case, sampleCount is to be respected.


getAllChannels

public Object[] getAllChannels()

setDitherBits

public void setDitherBits(float ditherBits)
Set the number of bits for dithering. Typically, a value between 0.2 and 0.9 gives best results.

Note: this value is only used, when dithering is actually performed.


getDitherBits

public float getDitherBits()

setDitherMode

public void setDitherMode(int mode)
Sets the mode for dithering. This can be one of:
  • DITHER_MODE_AUTOMATIC: it is decided automatically, whether dithering is necessary - in general when sample size is decreased.
  • DITHER_MODE_ON: dithering will be forced
  • DITHER_MODE_OFF: dithering will not be done.


getDitherMode

public int getDitherMode()

getConvertDitherBits

protected float getConvertDitherBits(int newFormatType)
Returns:
the ditherBits parameter for the float2byte functions