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