From 7039a05147b8bbfc829babea1c65bd436450b505 Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Mon, 8 Jan 2007 23:53:00 +0000 Subject: Splitting out songdbj git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11953 a1c6a512-1295-4272-9138-f99709370657 --- .../tritonus/share/sampled/FloatSampleBuffer.java | 734 --------------------- 1 file changed, 734 deletions(-) delete mode 100644 songdbj/org/tritonus/share/sampled/FloatSampleBuffer.java (limited to 'songdbj/org/tritonus/share/sampled/FloatSampleBuffer.java') 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 @@ -/* - * FloatSampleBuffer.java - * - * This file is part of Tritonus: http://www.tritonus.org/ - */ - -/* - * Copyright (c) 2000,2004 by Florian Bomers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* -|<--- this code is formatted to fit into 80 columns --->| -*/ - -package org.tritonus.share.sampled; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Random; - -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioFileFormat; -import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.spi.AudioFileWriter; - -import org.tritonus.share.TDebug; - -/** - * 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 - */ - -public class FloatSampleBuffer { - - /** Whether the functions without lazy parameter are lazy or not. */ - private static final boolean LAZY_DEFAULT=true; - - private ArrayList channels = new ArrayList(); // contains for each channel a float array - private int sampleCount=0; - private int channelCount=0; - private float sampleRate=0; - private int originalFormatType=0; - - /** Constant for setDitherMode: dithering will be enabled if sample size is decreased */ - public static final int DITHER_MODE_AUTOMATIC=0; - /** Constant for setDitherMode: dithering will be done */ - public static final int DITHER_MODE_ON=1; - /** Constant for setDitherMode: dithering will not be done */ - public static final int DITHER_MODE_OFF=2; - - private float ditherBits = FloatSampleTools.DEFAULT_DITHER_BITS; - - // e.g. the sample rate converter may want to force dithering - private int ditherMode = DITHER_MODE_AUTOMATIC; - - //////////////////////////////// initialization ///////////////////////////////// - - /** - * Create an instance with initially no channels. - */ - public FloatSampleBuffer() { - this(0,0,1); - } - - /** - * Create an empty FloatSampleBuffer with the specified number of channels, - * samples, and the specified sample rate. - */ - public FloatSampleBuffer(int channelCount, int sampleCount, float sampleRate) { - init(channelCount, sampleCount, sampleRate, LAZY_DEFAULT); - } - - /** - * Creates a new instance of FloatSampleBuffer and initializes - * it with audio data given in the interleaved byte array buffer. - */ - public FloatSampleBuffer(byte[] buffer, int offset, int byteCount, - AudioFormat format) { - this(format.getChannels(), - byteCount/(format.getSampleSizeInBits()/8*format.getChannels()), - format.getSampleRate()); - initFromByteArray(buffer, offset, byteCount, format); - } - - protected void init(int channelCount, int sampleCount, float sampleRate) { - init(channelCount, sampleCount, sampleRate, LAZY_DEFAULT); - } - - protected void init(int channelCount, int sampleCount, float sampleRate, boolean lazy) { - if (channelCount<0 || sampleCount<0) { - throw new IllegalArgumentException( - "invalid parameters in initialization of FloatSampleBuffer."); - } - setSampleRate(sampleRate); - if (getSampleCount()!=sampleCount || getChannelCount()!=channelCount) { - createChannels(channelCount, sampleCount, lazy); - } - } - - private void createChannels(int channelCount, int sampleCount, boolean lazy) { - this.sampleCount=sampleCount; - // lazy delete of all channels. Intentionally lazy ! - this.channelCount=0; - for (int ch=0; ch

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. - */ - public void changeSampleCount(int newSampleCount, boolean keepOldSamples) { - int oldSampleCount=getSampleCount(); - if (oldSampleCount==newSampleCount) { - return; - } - Object[] oldChannels=null; - if (keepOldSamples) { - oldChannels=getAllChannels(); - } - init(getChannelCount(), newSampleCount, getSampleRate()); - if (keepOldSamples) { - // copy old channels and eventually silence out new samples - int copyCount=newSampleCount0) { - makeSilence(0); - for (int ch=1; chindex. - * If LAZY_DEFAULT is true, this is done lazily. - */ - public void insertChannel(int index, boolean silent) { - insertChannel(index, silent, LAZY_DEFAULT); - } - - /** - * 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. - */ - public void insertChannel(int index, boolean silent, boolean lazy) { - int physSize=channels.size(); - int virtSize=getChannelCount(); - float[] newChannel=null; - if (physSize>virtSize) { - // there are hidden channels. Try to use one. - for (int ch=virtSize; ch=getSampleCount()) - || (!lazy && thisChannel.length==getSampleCount())) { - // we found a matching channel. Use it ! - newChannel=thisChannel; - channels.remove(ch); - break; - } - } - } - if (newChannel==null) { - newChannel=new float[getSampleCount()]; - } - channels.add(index, newChannel); - this.channelCount++; - if (silent) { - makeSilence(index); - } - } - - /** performs a lazy remove of the channel */ - public void removeChannel(int channel) { - removeChannel(channel, LAZY_DEFAULT); - } - - - /** - * 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. - */ - public void removeChannel(int channel, boolean lazy) { - if (!lazy) { - channels.remove(channel); - } else if (channelbufferCount || destIndex+length>bufferCount - || sourceIndex<0 || destIndex<0 || length<0) { - throw new IndexOutOfBoundsException("parameters exceed buffer size"); - } - System.arraycopy(data, sourceIndex, data, destIndex, length); - } - - /** - * Mix up of 1 channel to n channels.
- * It copies the first channel to all newly created channels. - * @param targetChannelCount the number of channels that this sample buffer - * will have after expanding. NOT the number of - * channels to add ! - * @exception IllegalArgumentException if this buffer does not have one - * channel before calling this method. - */ - public void expandChannel(int targetChannelCount) { - // even more sanity... - if (getChannelCount()!=1) { - throw new IllegalArgumentException( - "FloatSampleBuffer: can only expand channels for mono signals."); - } - for (int ch=1; ch - * 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. - */ - public void mixDownChannels() { - float[] firstChannel=getChannel(0); - int sampleCount=getSampleCount(); - int channelCount=getChannelCount(); - for (int ch=channelCount-1; ch>0; ch--) { - float[] thisChannel=getChannel(ch); - for (int i=0; idestOffset. - * This FloatSampleBuffer must be big enough to accomodate the samples. - *

- * srcBuffer is read from index srcOffset - * to (srcOffset + (lengthInSamples * format.getFrameSize()))input - * @param format input buffer's audio format - * @param floatOffset the offset where to write the float samples - * @param frameCount number of samples to write to this sample buffer - */ - public void setSamplesFromBytes(byte[] input, int inByteOffset, AudioFormat format, - int floatOffset, int frameCount) { - if (floatOffset < 0 || frameCount < 0 || inByteOffset < 0) { - throw new IllegalArgumentException - ("FloatSampleBuffer.setSamplesFromBytes: negative inByteOffset, floatOffset, or frameCount"); - } - if (inByteOffset + (frameCount * format.getFrameSize()) > input.length) { - throw new IllegalArgumentException - ("FloatSampleBuffer.setSamplesFromBytes: input buffer too small."); - } - if (floatOffset + frameCount > getSampleCount()) { - throw new IllegalArgumentException - ("FloatSampleBuffer.setSamplesFromBytes: frameCount too large"); - } - - FloatSampleTools.byte2float(input, inByteOffset, channels, floatOffset, frameCount, format); - } - - //////////////////////////////// properties ///////////////////////////////// - - public int getChannelCount() { - return channelCount; - } - - public int getSampleCount() { - return sampleCount; - } - - public float getSampleRate() { - return sampleRate; - } - - /** - * Sets the sample rate of this buffer. - * NOTE: no conversion is done. The samples are only re-interpreted. - */ - public void setSampleRate(float sampleRate) { - if (sampleRate<=0) { - throw new IllegalArgumentException - ("Invalid samplerate for FloatSampleBuffer."); - } - this.sampleRate=sampleRate; - } - - /** - * NOTE: the returned array may be larger than sampleCount. So in any case, - * sampleCount is to be respected. - */ - public float[] getChannel(int channel) { - if (channel<0 || channel>=getChannelCount()) { - throw new IllegalArgumentException( - "FloatSampleBuffer: invalid channel number."); - } - return (float[]) channels.get(channel); - } - - public Object[] getAllChannels() { - Object[] res=new Object[getChannelCount()]; - for (int ch=0; chNote: this value is only used, when dithering is actually performed. - */ - public void setDitherBits(float ditherBits) { - if (ditherBits<=0) { - throw new IllegalArgumentException("DitherBits must be greater than 0"); - } - this.ditherBits=ditherBits; - } - - public float getDitherBits() { - return ditherBits; - } - - /** - * Sets the mode for dithering. - * This can be one of: - *

- */ - public void setDitherMode(int mode) { - if (mode!=DITHER_MODE_AUTOMATIC - && mode!=DITHER_MODE_ON - && mode!=DITHER_MODE_OFF) { - throw new IllegalArgumentException("Illegal DitherMode"); - } - this.ditherMode=mode; - } - - public int getDitherMode() { - return ditherMode; - } - - - /** - * @return the ditherBits parameter for the float2byte functions - */ - protected float getConvertDitherBits(int newFormatType) { - // let's see whether dithering is necessary - boolean doDither = false; - switch (ditherMode) { - case DITHER_MODE_AUTOMATIC: - doDither=(originalFormatType & FloatSampleTools.F_SAMPLE_WIDTH_MASK)> - (newFormatType & FloatSampleTools.F_SAMPLE_WIDTH_MASK); - break; - case DITHER_MODE_ON: - doDither=true; - break; - case DITHER_MODE_OFF: - doDither=false; - break; - } - return doDither?ditherBits:0.0f; - } -} -- cgit v1.2.3