1

現在、「遅延聴覚フィードバック」(DAF)に関するプロジェクトに取り組んでいます。基本的には、マイクからの音を録音し、特定の時間だけ遅らせてから再生したいと思います。約200msの遅延とヘッドセットを持っている人を使用して、このフィードバックは流暢に話す人の能力をシャットダウンします。(かなり楽しい:YouTubeのDAF

現在、256バイトのbyte []-bufferを使用して、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 ...

}
4

2 に答える 2

2

オーディオのサンプルレートに依存するため、遅延を簡単に計算できます。これがCD品質(モノラル)オーディオであると仮定すると、サンプルレートは毎秒44,100サンプルです。200ミリ秒は0.2秒なので、44,100 X 0.2=8820です。

したがって、オーディオの再生を8820サンプル(または17640バイト)遅らせる必要があります。記録バッファと再生バッファを正確にこのサイズ(17640バイト)にすると、コードが非常に単純になります。各記録バッファがいっぱいになると、それを再生に渡します。これにより、ちょうど1つのバッファの継続時間の再生ラグが実現します。

于 2013-02-24T13:28:16.140 に答える
0

Androidに固有の遅延があり、それを考慮する必要がありますが、それ以外は...

循環バッファを作成します。N 0サンプルに対して十分な大きさである限り、どれだけ大きくてもかまいません。ここで、N個の「0」サンプルを使用して記述します。

この場合のNは(秒単位の遅延)*(ヘルツ単位のサンプルレート)です。

例:16kHzステレオで200ms:

0.2s * 16000Hz *(2チャンネル)= 3200*2サンプル=6400サンプル

おそらく16ビットのpcmデータも使用するので、バイトではなくショートを使用します。

バッファに適切な量のゼロを入力した後、マイクからのデータを入力しながら、スピーカーのデータの読み取りを開始します。

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をshortではなくbyteに変更します。

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 ...

} 
于 2013-02-24T13:12:52.720 に答える