Java中音频的循环短缓冲区

Java中音频的循环短缓冲区,java,audio,buffer,Java,Audio,Buffer,我正在实现一个音频跟踪类,我需要一个好的循环缓冲区实现。我的音频样本使用Short,所以我更喜欢使用ShortBuffer类作为实际的缓冲区。这个轨道将需要线程安全,但我可以保证只有一个线程将读取和另一个将写入轨道。 我当前的实现是这样的(它不处理包装) 这是我提出的解决方案,我认为使用带有短数组的head-and-tail属性比使用带有短缓冲区的读取位置更容易。我还从Java collections类中得到了一个想法,即检测缓冲区何时已满。以下是来源,以防粘贴箱消失: public class

我正在实现一个音频跟踪类,我需要一个好的循环缓冲区实现。我的音频样本使用Short,所以我更喜欢使用ShortBuffer类作为实际的缓冲区。这个轨道将需要线程安全,但我可以保证只有一个线程将读取和另一个将写入轨道。 我当前的实现是这样的(它不处理包装)


这是我提出的解决方案,我认为使用带有短数组的head-and-tail属性比使用带有短缓冲区的读取位置更容易。我还从Java collections类中得到了一个想法,即检测缓冲区何时已满。以下是来源,以防粘贴箱消失:

public class Track {

private static Logger log = LoggerFactory.getLogger(Track.class);

private final long id = System.nanoTime();

// number of channels
private int channelCount;

// maximum seconds to buffer
private int bufferedSeconds = 5;

private AtomicInteger count = new AtomicInteger(0);

private ReentrantLock lock;

private volatile short[] buffer;

private int capacity = 0;

private int head = 0;

private int tail = 0;

public Track(int samplingRate, int channelCount) {
    // set the number of channels
    this.channelCount = channelCount;
    // size the buffer
    capacity = (samplingRate * channelCount) * bufferedSeconds;
    buffer = new short[capacity];
    // use a "fair" lock
    lock = new ReentrantLock(true);
}

/**
 * Returns the number of samples currently in the buffer.
 * 
 * @return
 */
public int getSamplesCount() {
    int i = count.get();
    return i > 0 ? i / channelCount : 0;
}

/**
 * Removes and returns the next sample in the buffer.
 * 
 * @return single sample or null if a buffer underflow occurs
 */
public Short remove() {
    Short sample = null;
    if (count.get() > 0) {
        // decrement sample counter
        count.addAndGet(-1);
        // reposition the head
        head = (head + 1) % capacity;
        // get the sample at the head
        sample = buffer[head];
    } else {
        log.debug("Buffer underflow");
    }
    return sample;
}

/**
 * Adds a sample to the buffer.
 * 
 * @param sample
 * @return true if added successfully and false otherwise
 */
public boolean add(short sample) {
    boolean result = false;
    if ((count.get() + 1) < capacity) {
        // increment sample counter
        count.addAndGet(1);
        // reposition the tail
        tail = (tail + 1) % capacity;
        // add the sample to the tail
        buffer[tail] = sample;
        // added!
        result = true;
    } else {
        log.debug("Buffer overflow");
    }
    return result;
}

/**
 * Offers the samples for addition to the buffer, if there is enough capacity to 
 * contain them they will be added.
 * 
 * @param samples
 * @return true if the samples can be added and false otherwise
 */
public boolean offer(short[] samples) {
    boolean result = false;
    if ((count.get() + samples.length) <= capacity) {
        pushSamples(samples);
        result = true;
    }
    return result;
}

/**
 * Adds an array of samples to the buffer.
 * 
 * @param samples
 */
public void pushSamples(short[] samples) {
    log.trace("[{}] pushSamples - count: {}", id, samples.length);
    try {
        lock.tryLock(10, TimeUnit.MILLISECONDS);
        for (short sample : samples) {
            log.trace("Position at write: {}", tail);
            if (!add(sample)) {
                log.warn("Sample could not be added");
                break;
            }
        }
    } catch (InterruptedException e) {
        log.warn("Exception getting samples", e);
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

/**
 * Returns a single from the buffer.
 * 
 * @return
 */
public Short popSample(int channel) {
    log.trace("[{}] popSample - channel: {}", id, channel);
    Short sample = null;
    if (channel < channelCount) {
        log.trace("Position at read: {}", head);
        try {
            lock.tryLock(10, TimeUnit.MILLISECONDS);
            sample = remove();
        } catch (InterruptedException e) {
            log.warn("Exception getting sample", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    return sample;
}
公共类曲目{
私有静态记录器log=LoggerFactory.getLogger(Track.class);
private final long id=System.nanoTime();
//频道数
私有整数信道计数;
//到缓冲区的最长秒数
私有int bufferedSeconds=5;
私有AtomicInteger计数=新的AtomicInteger(0);
私有重入锁;
私有易失性短[]缓冲区;
专用int容量=0;
私用int头=0;
私有int-tail=0;
公共轨道(整数采样、整数信道计数){
//设置通道数
this.channelCount=channelCount;
//调整缓冲区大小
容量=(采样率*通道计数)*缓冲秒;
缓冲区=新的短路[容量];
//使用“普通”锁
锁定=新的可重入锁定(true);
}
/**
*返回缓冲区中当前的样本数。
* 
*@返回
*/
public int getSamplesCount(){
int i=count.get();
返回i>0?i/通道计数:0;
}
/**
*移除并返回缓冲区中的下一个样本。
* 
*@如果发生缓冲区下溢,返回单个样本或null
*/
公共短消息删除(){
短样本=空;
if(count.get()>0){
//减量采样计数器
count.addAndGet(-1);
//重新定位头部
水头=(水头+1)%容量;
//从头部取样品
样本=缓冲液[头];
}否则{
调试(“缓冲区下溢”);
}
返回样品;
}
/**
*将样本添加到缓冲区。
* 
*@param样本
*@如果添加成功返回true,否则返回false
*/
公共布尔加法(简短示例){
布尔结果=假;
如果((count.get()+1)<容量){
//增量采样计数器
count.addAndGet(1);
//重新定位尾部
尾部=(尾部+1)%容量;
//将样本添加到尾部
缓冲区[尾]=样本;
//补充!
结果=真;
}否则{
调试(“缓冲区溢出”);
}
返回结果;
}
/**
*如果有足够的容量,则提供要添加到缓冲区的样本
*包含它们,它们将被添加。
* 
*@param样本
*@如果可以添加样本,则返回true,否则返回false
*/
公开报价(短[]个样本){
布尔结果=假;

如果((count.get()+samples.length)这真的很好。我用这个例子来帮助我在Android中构建一个线程安全的音频样本循环数组。谢谢你们的发布!你们能给我指一个更完整的代码来记录到循环缓冲区吗?
public class Track {

private static Logger log = LoggerFactory.getLogger(Track.class);

private final long id = System.nanoTime();

// number of channels
private int channelCount;

// maximum seconds to buffer
private int bufferedSeconds = 5;

private AtomicInteger count = new AtomicInteger(0);

private ReentrantLock lock;

private volatile short[] buffer;

private int capacity = 0;

private int head = 0;

private int tail = 0;

public Track(int samplingRate, int channelCount) {
    // set the number of channels
    this.channelCount = channelCount;
    // size the buffer
    capacity = (samplingRate * channelCount) * bufferedSeconds;
    buffer = new short[capacity];
    // use a "fair" lock
    lock = new ReentrantLock(true);
}

/**
 * Returns the number of samples currently in the buffer.
 * 
 * @return
 */
public int getSamplesCount() {
    int i = count.get();
    return i > 0 ? i / channelCount : 0;
}

/**
 * Removes and returns the next sample in the buffer.
 * 
 * @return single sample or null if a buffer underflow occurs
 */
public Short remove() {
    Short sample = null;
    if (count.get() > 0) {
        // decrement sample counter
        count.addAndGet(-1);
        // reposition the head
        head = (head + 1) % capacity;
        // get the sample at the head
        sample = buffer[head];
    } else {
        log.debug("Buffer underflow");
    }
    return sample;
}

/**
 * Adds a sample to the buffer.
 * 
 * @param sample
 * @return true if added successfully and false otherwise
 */
public boolean add(short sample) {
    boolean result = false;
    if ((count.get() + 1) < capacity) {
        // increment sample counter
        count.addAndGet(1);
        // reposition the tail
        tail = (tail + 1) % capacity;
        // add the sample to the tail
        buffer[tail] = sample;
        // added!
        result = true;
    } else {
        log.debug("Buffer overflow");
    }
    return result;
}

/**
 * Offers the samples for addition to the buffer, if there is enough capacity to 
 * contain them they will be added.
 * 
 * @param samples
 * @return true if the samples can be added and false otherwise
 */
public boolean offer(short[] samples) {
    boolean result = false;
    if ((count.get() + samples.length) <= capacity) {
        pushSamples(samples);
        result = true;
    }
    return result;
}

/**
 * Adds an array of samples to the buffer.
 * 
 * @param samples
 */
public void pushSamples(short[] samples) {
    log.trace("[{}] pushSamples - count: {}", id, samples.length);
    try {
        lock.tryLock(10, TimeUnit.MILLISECONDS);
        for (short sample : samples) {
            log.trace("Position at write: {}", tail);
            if (!add(sample)) {
                log.warn("Sample could not be added");
                break;
            }
        }
    } catch (InterruptedException e) {
        log.warn("Exception getting samples", e);
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

/**
 * Returns a single from the buffer.
 * 
 * @return
 */
public Short popSample(int channel) {
    log.trace("[{}] popSample - channel: {}", id, channel);
    Short sample = null;
    if (channel < channelCount) {
        log.trace("Position at read: {}", head);
        try {
            lock.tryLock(10, TimeUnit.MILLISECONDS);
            sample = remove();
        } catch (InterruptedException e) {
            log.warn("Exception getting sample", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    return sample;
}