2013-02-24 2 views
1

В настоящее время я работаю над проектом, касающимся «отложенной слуховой обратной связи» (DAF). В основном я хочу записывать звуки с микрофона, задерживать его на определенное количество времени, а затем воспроизводить его. Используя задержку около 200 мс и человека с гарнитурой, эта обратная связь отключает способность людей свободно говорить. (Довольно весело: DAF on youtube)Воспроизведение звука с задержкой

Прямо сейчас я пытаюсь сделать этот цикл с SourceDataLine и TargetDataLine, используя байт [] - буфер с 256 байтами. Если буфер становится больше, то и задержка. Моя проблема сейчас: я не могу сказать, что такое задержка в миллисекундах.

Есть ли способ вычислить реальную задержку в мс от размера буфера? Или может быть, есть другой подход, чтобы получить этот результат?

Это то, что мой цикл выглядит на данный момент:

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 (моно), частота дискретизации составляет 44100 выборок в секунду. 200 миллисекунд составляет 0,2 секунды, поэтому 44 100 X 0,2 = 8820.

Таким образом, ваше воспроизведение звука должно быть отложено на 8820 выборок (или 17640 байт). Если вы сделаете буферы для записи и воспроизведения точно такого размера (17640 байт), это сделает ваш код довольно простым. По мере заполнения каждого буфера записи вы передаете его для воспроизведения; это обеспечит задержку воспроизведения ровно на один буфер.

+0

Незначительный каламбур: как насчет двойной буферизации, которая, вероятно, происходит под аудио API.Должен быть какой-то другой, существует риск пробелов в воспроизведении. :) –

+0

Ну, это звучит разумно и довольно аккуратно. Спасибо :) – gtRfnkN

+0

Я бы попробовал сначала предложение MusiGenesis, так как это намного проще. Если вы столкнулись с проблемами, попробуйте мой с FIFO и используйте гораздо меньшие буферы. Я только работал с Android, поэтому не уверен, какой звуковой уровень, который вы получаете под понятием «Java и MusiGenesis», может работать очень хорошо. Я скоро отправлю код для FIFO. –

0

Существует некоторая задержка, присущие Android, которые вы должны учитывать, но помимо этого ...

Создание кольцевой буфер , Неважно, насколько большой, если он более чем достаточно для N 0 образцов. Теперь напишите его с образцами N '0'.

N в этом случае (задержка в секундах) * (частота дискретизации в герцах).

Пример: 200мс с 16 кГц стерео:

0.2s * * 16000 Гц (2 канала) = 3200 * 2 = 6400 образцов образцы

Вы, вероятно, будет работать с данными ИКМ тоже, что 16- бит, поэтому используйте вместо байта короткий.

После заполнения буфера нужным количеством нулей начните считывать данные для динамика при заполнении данными с микрофона.

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

} 
+0

Незначительный приговор: PCM - это переменный формат, поэтому действительные данные PCM могут быть либо 8 бит на выборку, либо 16 бит на выборку (возможны и другие значения). Но * самый * PCM (включая качество CD) - 16 бит. – MusiGenesis

+0

Правда, что. Он может даже плавать. Это просто означает, что это временные штампы данных. Ты прав. –

+0

Спасибо! На самом деле, я не devolop для android, но для простой java 1.7, это тоже относится к этому? Не могли бы вы разместить фрагмент кода, пожалуйста? – gtRfnkN

Смежные вопросы