public class FloatSampleBuffer
extends java.lang.Object
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:
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:
initFromByteArray
method of the float
buffer to initialize it with the 8 bit data.
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:
sampleCount
is reduced. A subsequent
increase of the sample count (which will occur frequently), will check that
and eventually reuse the existing array.
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
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.
Modifier and Type | Field and Description |
---|---|
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 and Description |
---|
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.
|
Modifier and Type | Method and Description |
---|---|
void |
addChannel(boolean silent)
Add a channel to this buffer, e.g.
|
void |
changeSampleCount(int newSampleCount,
boolean keepOldSamples)
Resizes this buffer.
|
static void |
checkFormatSupported(AudioFormat format)
Verify that the specified AudioFormat can be converted to and from.
|
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. |
int |
convertToByteArray(int readOffset,
int lenInSamples,
byte[] buffer,
int writeOffset,
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)
Copy sourceChannel's audio data to targetChannel, identified by their
indices in the channel list.
|
void |
copyChannel(int sourceChannel,
int sourceOffset,
int targetChannel,
int targetOffset,
int aSampleCount)
Copy sampleCount samples from sourceChannel at position srcOffset to
targetChannel at position targetOffset.
|
int |
copyTo(FloatSampleBuffer dest,
int destOffset,
int count)
Copies the contents of this buffer to the destination buffer at the
destOffset.
|
int |
copyTo(int srcOffset,
FloatSampleBuffer dest,
int destOffset,
int count)
Copies the specified part of this buffer to the destination buffer.
|
void |
expandChannel(int targetChannelCount)
Mix up of 1 channel to n channels.
It copies the first channel to all newly created channels. |
java.lang.Object[] |
getAllChannels()
Get an array of all channels.
|
int |
getByteArrayBufferSize(AudioFormat format) |
int |
getByteArrayBufferSize(AudioFormat format,
int lenInSamples) |
float[] |
getChannel(int channel)
Get the actual audio data of one channel.
Modifying this array will modify the audio samples of this FloatSampleBuffer. |
int |
getChannelCount() |
float |
getDitherBits() |
int |
getDitherMode() |
int |
getSampleCount() |
float |
getSampleRate() |
void |
init(int newChannelCount,
int newSampleCount,
float newSampleRate)
Initialize this sample buffer to have the specified channels, sample
count, and sample rate.
|
void |
init(int newChannelCount,
int newSampleCount,
float newSampleRate,
boolean lazy)
Initialize this sample buffer to have the specified channels, sample
count, and sample rate.
|
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 |
linearFade(float startVol,
float endVol)
Fade the volume level of this buffer from the given start volume to the end volume.
|
void |
linearFade(float startVol,
float endVol,
int offset,
int count)
Fade the volume level of this buffer from the given start volume to the end volume.
|
void |
linearFade(int channel,
float startVol,
float endVol,
int offset,
int count)
Fade the volume level of the specified channel from the given start volume to
the end volume.
|
void |
makeSilence()
Silence the entire audio buffer.
|
void |
makeSilence(int channel)
Silence the specified channel
|
void |
makeSilence(int offset,
int count)
Silence the entire buffer in the specified range on all channels.
|
void |
makeSilence(int channel,
int offset,
int count)
Silence the specified channel in the specified range
|
void |
mix(FloatSampleBuffer source)
Mixes
source to this buffer by adding all samples. |
void |
mix(FloatSampleBuffer source,
int sourceOffset,
int thisOffset,
int count)
Mixes
source samples to this buffer by adding the sample values. |
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. |
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 newChannels,
int newSampleCount,
float newSampleRate)
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.
|
float[] |
setRawChannel(int channel,
float[] data)
Low-level method to directly set the array for the given channel.
|
void |
setSampleCount(int newSampleCount,
boolean keepOldSamples)
Alias for changeSampleCount
|
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.
|
int |
writeByteBuffer(byte[] buffer,
int srcByteOffset,
AudioFormat format,
int dstSampleOffset,
int aSampleCount)
Write the contents of the byte array to this buffer, overwriting existing
data.
|
public static final int DITHER_MODE_AUTOMATIC
public static final int DITHER_MODE_ON
public static final int DITHER_MODE_OFF
public FloatSampleBuffer()
public FloatSampleBuffer(int channelCount, int sampleCount, float sampleRate)
public FloatSampleBuffer(byte[] buffer, int offset, int byteCount, AudioFormat format)
buffer
.public void init(int newChannelCount, int newSampleCount, float newSampleRate)
newChannelCount
- newSampleCount
- newSampleRate
- java.lang.IllegalArgumentException
- if newChannelCount or newSampleCount are
negative, or newSampleRate is not positive.public void init(int newChannelCount, int newSampleCount, float newSampleRate, boolean lazy)
newChannelCount
- newSampleCount
- newSampleRate
- lazy
- java.lang.IllegalArgumentException
- if newChannelCount or newSampleCount are
negative, or newSampleRate is not positive.public static void checkFormatSupported(AudioFormat format)
java.lang.IllegalArgumentException
- if the format is not supportedpublic void initFromByteArray(byte[] buffer, int offset, int byteCount, AudioFormat format)
byteCount / format.getFrameSize()
. If LAZY_DEFAULT is
true, it will use lazy deletion.java.lang.IllegalArgumentException
public void initFromByteArray(byte[] buffer, int offset, int byteCount, AudioFormat format, boolean lazy)
byteCount / format.getFrameSize()
.lazy
- if true, then existing channels will be tried to be re-used
to minimize garbage collection.java.lang.IllegalArgumentException
public void initFromFloatSampleBuffer(FloatSampleBuffer source)
source
.public int writeByteBuffer(byte[] buffer, int srcByteOffset, AudioFormat format, int dstSampleOffset, int aSampleCount)
The format and the number of samples of this float buffer are not changed, so if the byte array has more samples than fit into this float buffer, it is not expanded.
buffer
- the byte buffer to write to this float buffersrcByteOffset
- the offset in bytes in buffer where to start readingformat
- the audio format of the bytes in bufferdstSampleOffset
- the offset in samples where to start writing the
converted float data into this float bufferaSampleCount
- the number of samples to writepublic void reset()
public void reset(int newChannels, int newSampleCount, float newSampleRate)
public int getByteArrayBufferSize(AudioFormat format)
public int getByteArrayBufferSize(AudioFormat format, int lenInSamples)
lenInSamples
- how many samples to be consideredpublic int convertToByteArray(byte[] buffer, int offset, AudioFormat format)
buffer
as an
interleaved byte array. buffer
must be large enough to
hold all data.buffer
java.lang.IllegalArgumentException
- when buffer is too small or
format
doesn't matchpublic int convertToByteArray(int readOffset, int lenInSamples, byte[] buffer, int writeOffset, AudioFormat format)
buffer
as an
interleaved byte array. buffer
must be large enough to
hold all data.readOffset
- the sample offset from where samples are read from this
FloatSampleBufferlenInSamples
- how many samples are convertedbuffer
- the byte buffer written towriteOffset
- the byte offset in bufferbuffer
java.lang.IllegalArgumentException
- when buffer is too small or
format
doesn't matchpublic byte[] convertToByteArray(AudioFormat format)
java.lang.IllegalArgumentException
- when sample rate or channels do not
matchconvertToByteArray(byte[], int, AudioFormat)
public void changeSampleCount(int newSampleCount, boolean keepOldSamples)
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 may get
discarded, the buffer may then contain random samples.
public void makeSilence()
public void makeSilence(int offset, int count)
public void makeSilence(int channel)
public void makeSilence(int channel, int offset, int count)
public void linearFade(float startVol, float endVol)
startVol
- the start volume as a linear factor [0..1]endVol
- the end volume as a linear factor [0..1]public void linearFade(float startVol, float endVol, int offset, int count)
startVol
- the start volume as a linear factor [0..1]endVol
- the end volume as a linear factor [0..1]offset
- the offset in this buffer where to start the fade (in samples)count
- the number of samples to fadepublic void linearFade(int channel, float startVol, float endVol, int offset, int count)
channel
- the channel to do the fadestartVol
- the start volume as a linear factor [0..1]endVol
- the end volume as a linear factor [0..1]offset
- the offset in this buffer where to start the fade (in samples)count
- the number of samples to fadepublic void addChannel(boolean silent)
silent
- if true, the channel is explicitly silenced. Otherwise the new channel may contain random data.public void insertChannel(int index, boolean silent)
index
. If
LAZY_DEFAULT is true, this is done lazily.public void insertChannel(int index, boolean silent, boolean lazy)
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.
public void removeChannel(int channel)
public void removeChannel(int channel, boolean lazy)
public void copyChannel(int sourceChannel, int targetChannel)
public void copyChannel(int sourceChannel, int sourceOffset, int targetChannel, int targetOffset, int aSampleCount)
public void copy(int sourceIndex, int destIndex, int length)
public void copy(int channel, int sourceIndex, int destIndex, int length)
public void expandChannel(int targetChannelCount)
targetChannelCount
- the number of channels that this sample buffer
will have after expanding. NOT the number of channels to add !java.lang.IllegalArgumentException
- if this buffer does not have one
channel before calling this method.public void mixDownChannels()
public void mix(FloatSampleBuffer source)
source
to this buffer by adding all samples. At
most, source
's number of samples, number of channels are
mixed. None of the sample count, channel count or sample rate of either
buffer are changed. In particular, the caller needs to assure that the
sample rate of the buffers match.source
- the buffer to be mixed to this bufferpublic void mix(FloatSampleBuffer source, int sourceOffset, int thisOffset, int count)
source
samples to this buffer by adding the sample values.
None of the sample count, channel count or sample rate of either
buffer are changed. In particular, the caller needs to assure that the
sample rate of the buffers match.
This method is not error tolerant, in particular, runtime exceptions will be thrown if the channel counts do not match, or if the offsets and count exceed the buffer's capacity.
source
- the source buffer from where to take samples and mix to this onesourceOffset
- offset in source where to start reading samplesthisOffset
- offset in this buffer from where to start mixing samplescount
- number of samples to mixpublic int copyTo(FloatSampleBuffer dest, int destOffset, int count)
dest
's number of samples, number of
channels are copied. None of the sample count, channel count or sample
rate of either buffer are changed. In particular, the caller needs to
assure that the sample rate of the buffers match.dest
- the buffer to write todestOffset
- the position in dest
where to start
writing the samples of this buffercount
- the number of samples to be copiedpublic int copyTo(int srcOffset, FloatSampleBuffer dest, int destOffset, int count)
dest
's number of samples, number of
channels are copied. None of the sample count, channel count or sample
rate of either buffer are changed. In particular, the caller needs to
assure that the sample rate of the buffers match.srcOffset
- the start position in this buffer, where to start reading samplesdest
- the buffer to write todestOffset
- the position in dest
where to start
writing the samplescount
- the number of samples to be copiedpublic void setSamplesFromBytes(byte[] input, int inByteOffset, AudioFormat format, int floatOffset, int frameCount)
destOffset
. This FloatSampleBuffer must be
big enough to accomodate the samples.
Note: this value is only used, when dithering is actually performed.srcBuffer
is read from index srcOffset
to
(srcOffset + (lengthInSamples * format.getFrameSize()))
input
- the input buffer in interleaved audio datainByteOffset
- the offset in input
format
- input buffer's audio formatfloatOffset
- the offset where to write the float samplesframeCount
- number of samples to write to this sample buffer
getChannelCount
public int getChannelCount()
getSampleCount
public int getSampleCount()
getSampleRate
public float getSampleRate()
setSampleCount
public void setSampleCount(int newSampleCount,
boolean keepOldSamples)
newSampleCount
- the new number of samples for this bufferkeepOldSamples
- if true, the new buffer will keep the current
samples in the arrayschangeSampleCount(int, boolean)
setSampleRate
public void setSampleRate(float sampleRate)
getChannel
public float[] getChannel(int channel)
Modifying this array will modify the audio samples of this
FloatSampleBuffer.
NOTE: the returned array may be larger than sampleCount. So in any case,
sampleCount is to be respected.
java.lang.IllegalArgumentException
- if channel is out of bounds
setRawChannel
public float[] setRawChannel(int channel,
float[] data)
changeSampleCount()
. This method
may be useful for advanced optimization techniques.
channel
- the channel to replacedata
- the audio sample arrayjava.lang.IllegalArgumentException
- if channel is out of bounds or data is nullchangeSampleCount(int, boolean)
getAllChannels
public java.lang.Object[] getAllChannels()
setDitherBits
public void setDitherBits(float ditherBits)
getDitherBits
public float getDitherBits()
setDitherMode
public void setDitherMode(int mode)
getDitherMode
public int getDitherMode()