目前我正在开展一个关于“延迟听觉反馈”(DAF)的项目。基本上我想录制麦克风的声音,将其延迟特定的时间,然后播放。使用大约200ms的延迟和带耳机的人,这种反馈关闭了人们流利说话的能力。 (非常有趣:DAF on youtube)
现在我正在尝试使用一个byte [] - 256字节的缓冲区,使用SourceDataLine和TargetDataLine进行此循环。如果缓冲区变大,延迟也会变大。我现在的问题是:我无法确定延迟的毫秒数。
有没有办法从缓冲区大小计算毫秒的实际延迟?或者可能有另一种方法来获得这个结果?
这就是我的循环现在的样子:
private int mBufferSize; // 256
private TargetDataLine mLineOutput;
private SourceDataLine mLineInput;
public void run() {
... creating the DataLines and getting the lines from AudioSystem ...
// byte buffer for audio
byte[] data = new byte[mBufferSize];
// start the data lines
mLineOutput.start();
mLineInput.start();
// start recording and playing back
while (running) {
mLineOutput.read(data, 0, mBufferSize);
mLineInput.write(data, 0, mBufferSize);
}
... closing the lines and exiting ...
}
答案 0 :(得分:1)
您可以轻松计算延迟,因为它取决于音频的采样率。假设这是CD质量(单声道)音频,则采样率为每秒44,100个样本。 200毫秒是0.2秒,所以44,100 X 0.2 = 8820。
因此,您的音频播放需要延迟8820个样本(或17640个字节)。如果你使你的录音和播放缓冲区恰好这个大小(17640字节),它将使你的代码非常简单。当每个录音缓冲区都被填满时,你将它传递给回放;这将实现恰好一个缓冲区持续时间的回放延迟。
答案 1 :(得分:0)
Android应该考虑到一些固有的延迟,但除此之外......
创建循环缓冲区。无论多大,只要它对于N 0样品来说足够大。现在用N'0'样本写它。
在这种情况下,N是(以秒为单位的延迟)*(以赫兹为单位的采样率)。
示例:200ms,16kHz立体声:
0.2s * 16000Hz *(2通道)= 3200 * 2个样本= 6400个样本
您可能也在使用pcm数据,这是16位,因此请使用short而不是byte。
在用正确的零填充缓冲区后,在填充麦克风数据的同时开始读取扬声器的数据。
PCM Fifo:
public class PcmQueue
{
private short mBuf[] = null;
private int mWrIdx = 0;
private int mRdIdx = 0;
private int mCount = 0;
private int mBufSz = 0;
private Object mSync = new Object();
private PcmQueue(){}
public PcmQueue( int nBufSz )
{
try {
mBuf = new short[nBufSz];
} catch (Exception e) {
Log.e(this.getClass().getName(), "AudioQueue allocation failed.", e);
mBuf = null;
mBufSz = 0;
}
}
public int doWrite( final short pWrBuf[], final int nWrBufIdx, final int nLen )
{
int sampsWritten = 0;
if ( nLen > 0 ) {
int toWrite;
synchronized(mSync) {
// Write nothing if there isn't room in the buffer.
toWrite = (nLen <= (mBufSz - mCount)) ? nLen : 0;
}
// We can definitely read toWrite shorts.
while (toWrite > 0)
{
// Calculate how many contiguous shorts to the end of the buffer
final int sampsToCopy = Math.min( toWrite, (mBufSz - mWrIdx) );
// Copy that many shorts.
System.arraycopy(pWrBuf, sampsWritten + nWrBufIdx, mBuf, mWrIdx, sampsToCopy);
// Circular buffering.
mWrIdx += sampsToCopy;
if (mWrIdx >= mBufSz) {
mWrIdx -= mBufSz;
}
// Increment the number of shorts sampsWritten.
sampsWritten += sampsToCopy;
toWrite -= sampsToCopy;
}
synchronized(mSync) {
// Increment the count.
mCount = mCount + sampsWritten;
}
}
return sampsWritten;
}
public int doRead( short pcmBuffer[], final int nRdBufIdx, final int nRdBufLen )
{
int sampsRead = 0;
final int nSampsToRead = Math.min( nRdBufLen, pcmBuffer.length - nRdBufIdx );
if ( nSampsToRead > 0 ) {
int sampsToRead;
synchronized(mSync) {
// Calculate how many shorts can be read from the RdBuffer.
sampsToRead = Math.min(mCount, nSampsToRead);
}
// We can definitely read sampsToRead shorts.
while (sampsToRead > 0)
{
// Calculate how many contiguous shorts to the end of the buffer
final int sampsToCopy = Math.min( sampsToRead, (mBufSz - mRdIdx) );
// Copy that many shorts.
System.arraycopy( mBuf, mRdIdx, pcmBuffer, sampsRead + nRdBufIdx, sampsToCopy);
// Circular buffering.
mRdIdx += sampsToCopy;
if (mRdIdx >= mBufSz) {
mRdIdx -= mBufSz;
}
// Increment the number of shorts read.
sampsRead += sampsToCopy;
sampsToRead -= sampsToCopy;
}
// Decrement the count.
synchronized(mSync) {
mCount = mCount - sampsRead;
}
}
return sampsRead;
}
}
你的代码,为FIFO修改...我没有使用TargetDataLine / SourceDataLine的经验,所以如果它们只处理字节数组,则修改FIFO为字节而不是短。
private int mBufferSize; // 256
private TargetDataLine mLineOutput;
private SourceDataLine mLineInput;
public void run() {
... creating the DataLines and getting the lines from AudioSystem ...
// short buffer for audio
short[] data = new short[256];
final int emptySamples = (int)(44100.0 * 0.2);
final int bufferSize = emptySamples*2;
PcmQueue pcmQueue = new PcmQueue( bufferSize );
// Create a temporary empty buffer to write to the PCM queue
{
short[] emptyBuf = new short[emptySamples];
Arrays.fill(emptyBuf, (short)emptySamples );
pcmQueue.doWrite(emptyBuf, 0, emptySamples);
}
// start recording and playing back
while (running) {
mLineOutput.read(data, 0, mBufferSize);
pcmQueue.doWrite(data, 0, mBufferSize);
pcmQueue.doRead(data, 0, mBufferSize);
mLineInput.write(data, 0, mBufferSize);
}
... closing the lines and exiting ...
}