package ddf.minim; /** * MultiChannelBuffer represents a chunk of multichannel (or mono) audio data. * It is primarily used internally when passing buffers of audio around, but * you will need to create one to use things like the loadFileIntoBuffer method of Minim * and the setSample method of Sampler. When thinking about a buffer of audio * we usually consider how many <em>sample frames</em> long that buffer is. This * is not the same as the actual number of values stored in the buffer. Mono, or * single channel audio, contains one sample per sample frame, but stereo is * two, quadraphonic is four, and so forth. The buffer size of a MultiChannelBuffer * is how many sample frames it stores, so when retrieving and setting values * it is required to indicate which channel should be operated upon. * * @example Advanced/loadFileIntoBuffer * * @related Minim * * @author Damien Di Fede * */ public class MultiChannelBuffer { // TODO: consider just wrapping a FloatSampleBuffer private float[][] channels; private int bufferSize; /** * Construct a MultiChannelBuffer, providing a size and number of channels. * * @param bufferSize * int: The length of the buffer in sample frames. * @param numChannels * int: The number of channels the buffer should contain. */ public MultiChannelBuffer(int bufferSize, int numChannels) { channels = new float[numChannels][bufferSize]; this.bufferSize = bufferSize; } /** * Copy the data in the provided MultiChannelBuffer to this MultiChannelBuffer. * Doing so will change both the buffer size and channel count of this * MultiChannelBuffer to be the same as the copied buffer. * * @shortdesc Copy the data in the provided MultiChannelBuffer to this MultiChannelBuffer. * * @param otherBuffer * the MultiChannelBuffer to copy */ public void set( MultiChannelBuffer otherBuffer ) { bufferSize = otherBuffer.bufferSize; channels = otherBuffer.channels.clone(); } /** * Returns the length of this buffer in samples. * * @return the length of this buffer in samples */ public int getBufferSize() { return bufferSize; } /** * Returns the number of channels in this buffer. * * @return the number of channels in this buffer */ public int getChannelCount() { return channels.length; } /** * Returns the value of a sample in the given channel, * at the given offset from the beginning of the buffer. * When sampleIndex is a float, this returns an interpolated * sample value. For instance, getSample( 0, 30.5f ) will * return an interpolated sample value in channel 0 that is * between the value at 30 and the value at 31. * * @shortdesc Returns the value of a sample in the given channel, * at the given offset from the beginning of the buffer. * * @param channelNumber * int: the channel to get the sample value from * @param sampleIndex * int: the offset from the beginning of the buffer, in samples. * @return * float: the value of the sample */ public float getSample( int channelNumber, int sampleIndex ) { return channels[channelNumber][sampleIndex]; } /** * Returns the interpolated value of a sample in the given channel, * at the given offset from the beginning of the buffer, * For instance, getSample( 0, 30.5f ) will * return an interpolated sample value in channel 0 that is * between the value at 30 and the value at 31. * * @param channelNumber * int: the channel to get the sample value from * @param sampleIndex * float: the offset from the beginning of the buffer, in samples. * @return * float: the value of the sample */ public float getSample( int channelNumber, float sampleIndex ) { int lowSamp = (int)sampleIndex; int hiSamp = lowSamp + 1; if ( hiSamp == bufferSize ) { return channels[channelNumber][lowSamp]; } float lerp = sampleIndex - lowSamp; return channels[channelNumber][lowSamp] + lerp*(channels[channelNumber][hiSamp] - channels[channelNumber][lowSamp]); } /** * Sets the value of a sample in the given channel at the given * offset from the beginning of the buffer. * * @param channelNumber * int: the channel of the buffer * @param sampleIndex * int: the sample offset from the beginning of the buffer * @param value * float: the sample value to set */ public void setSample( int channelNumber, int sampleIndex, float value ) { channels[channelNumber][sampleIndex] = value; } /** * Returns the requested channel as a float array. * You should not necessarily assume that the * modifying the returned array will modify * the values in this buffer. * * @shortdesc Returns the requested channel as a float array. * * @param channelNumber * int: the channel to return * @return * float[]: the channel represented as a float array */ public float[] getChannel(int channelNumber) { return channels[channelNumber]; } /** * Sets all of the values in a particular channel using * the values of the provided float array. The array * should be at least as long as the current buffer size * of this buffer and this will only copy as many samples * as fit into its current buffer size. * * @shortdesc Sets all of the values in a particular channel using * the values of the provided float array. * * @param channelNumber * int: the channel to set * @param samples * float[]: the array of values to copy into the channel */ public void setChannel(int channelNumber, float[] samples) { System.arraycopy( samples, 0, channels[channelNumber], 0, bufferSize ); } /** * Set the number of channels this buffer contains. * Doing this will retain any existing channels * under the new channel count. * * @shortdesc Set the number of channels this buffer contains. * * @param numChannels * int: the number of channels this buffer should contain */ public void setChannelCount(int numChannels) { if ( channels.length != numChannels ) { float[][] newChannels = new float[numChannels][bufferSize]; for( int c = 0; c < channels.length && c < numChannels; ++c ) { newChannels[c] = channels[c]; } channels = newChannels; } } /** * Set the length of this buffer in sample frames. * Doing this will retain all of the sample data * that can fit into the new buffer size. * * @shortdesc Set the length of this buffer in sample frames. * * @param bufferSize * int: the new length of this buffer in sample frames */ public void setBufferSize(int bufferSize) { if ( this.bufferSize != bufferSize ) { this.bufferSize = bufferSize; for( int i = 0; i < channels.length; ++i ) { float[] newChannel = new float[bufferSize]; // copy existing data into the new channel array System.arraycopy( channels[i], 0, newChannel, 0, (bufferSize < channels[i].length ? bufferSize : channels[i].length) ); channels[i] = newChannel; } } } }