2013-02-24 1 views
1

현재 "지연된 청각 피드백"(DAF)에 관한 프로젝트를 진행하고 있습니다. 기본적으로 마이크에서 소리를 녹음하고 특정 시간만큼 지연시킨 다음 다시 재생하고 싶습니다. 이 피드백은 약 200ms의 지연과 헤드셋을 가진 사람을 사용하여 사람이 유창하게 말 할 수있는 능력을 차단합니다. (꽤 많은 재미 : DAF on youtube)지연된 오디오를 재생하면

지금 나는 256 바이트의 바이트 [] - 버퍼를 사용하여 SourceDataLine 및 TargetDataLine을 사용하여이 루프를 만들려고합니다. 버퍼가 커지면 지연도 커집니다. 내 문제는 지금이다 : 나는 밀리 세컨드의 지연이 무엇인지 말할 수 없다.

실제 지연을 ms 단위로 계산할 수있는 방법이 있습니까? 아니면이 결과를 얻는 또 다른 접근법이 있습니까?

이 내 루프가 순간에 모습입니다 :하지만 그 옆에서,

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

} 

답변

1

지연은 오디오의 샘플 속도에 따라 다르므로 쉽게 계산할 수 있습니다. CD 품질 (모노) 오디오라고 가정하면 샘플 속도는 초당 44,100 샘플입니다. 200 밀리 초는 0.2 초이므로 44,100 X 0.2 = 8820입니다.

오디오 재생은 8820 샘플 (또는 17640 바이트)만큼 지연되어야합니다. 녹음 및 재생 버퍼를 정확히이 크기 (17640 바이트)로 만들면 코드가 매우 단순 해집니다. 각 레코딩 버퍼가 채워지면 재생 버퍼로 전달합니다. 이것은 정확히 하나의 버퍼 지속 시간의 재생 지연을 달성합니다.

+0

사소한 질문 : 아마도 오디오 API에서 이중 버퍼링이 발생했을 것입니다.재생시 틈이 생길 위험이있는 다른 것이 있어야합니다. :) –

+0

음, 정말 합리적이고 예쁜 청초한 소리. 고마워요. – gtRfnkN

+0

MusiGenesis의 제안을 먼저 시도해 보겠습니다. 문제가 생기면 FIFO로 광산을 시도하고 더 작은 버퍼를 사용하십시오. 나는 안드로이드로만 작업 했으므로 어떤 종류의 오디오 레이어를 'Java와 MusiGenesis'의 제안이 잘 작동하는지 확실하지 않습니다. FIFO에 대한 코드를 곧 게시 할 예정입니다. –

0

당신이 고려해야합니다 안드로이드에 내재 된 약간의 지연이 있습니다 ...

원형 버퍼를 만들기 . 얼마나 큰지는 중요하지 않습니다. N 0 샘플만큼 충분합니다. 이제 N '0'샘플로 작성하십시오.

N은 (초 단위 지연) * (샘플 속도 (헤르쯔))입니다.

예 : 16kHz의 스테레오와 200 밀리 :

0.2 초 * 16000Hz * (2 개 채널) = 3200 개 * 2 개 샘플 = 6400 개 샘플

당신은 아마 너무 PCM 데이터로 작업 될 것입니다 16 바이트가 아닌 바이트를 사용하십시오.

올바른 양의 0으로 버퍼를 채운 후 마이크에서 데이터를 채우면서 스피커에 대한 데이터를 읽기 시작합니다.

PCM들 Fifo :

그들은 단지, 바이트 배열을 처리 바이트 대신 짧은을 위해 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와 경험이 없다.

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

} 
+0

사소한 질문 : PCM은 가변 형식이므로 유효한 PCM 데이터는 샘플 당 8 비트 또는 샘플 당 16 비트가 될 수 있습니다 (다른 값도 가능). 그러나 * 대부분 * PCM (CD 품질 포함)은 16 비트입니다. – MusiGenesis

+0

사실이에요. 심지어 떠있을 수도 있습니다. 그것은 일종의 시간 기록 된 데이터라는 것을 의미합니다. 당신이 올바른지. –

+0

고마워! 사실 안드로이드에 대한 devolop 없지만 일반 자바 1.7에 대한, 이것도 이것에 적용됩니까? 코드 스 니펫을 게시 할 수 있습니까? – gtRfnkN

관련 문제