2015-05-03 4 views
2

У меня есть два iOS AudioQueues - один вход, который подает отсчеты непосредственно на один выход. К сожалению, есть эффект эха, который довольно заметен :(Вход/выход с низкой задержкой AudioQueue

Возможно ли использовать звук с низкой задержкой с использованием AudioQueue или мне действительно нужно использовать AudioUnits? (Я пробовал фреймворк Novocaine, который использует AudioUnits, и здесь латентность намного меньше. Я также заметил, что эта структура, по-видимому, использует меньше ресурсов ЦП. К сожалению, мне не удалось использовать эту инфраструктуру в моем проекте Swift без существенных изменений.)

Вот несколько отрывков моего кода, который в основном выполняется в Свифт, за исключением тех обратных вызовов, которые должны быть реализованы в C.

private let audioStreamBasicDescription = AudioStreamBasicDescription(
    mSampleRate: 16000, 
    mFormatID: AudioFormatID(kAudioFormatLinearPCM), 
    mFormatFlags: AudioFormatFlags(kAudioFormatFlagsNativeFloatPacked), 
    mBytesPerPacket: 4, 
    mFramesPerPacket: 1, 
    mBytesPerFrame: 4, 
    mChannelsPerFrame: 1, 
    mBitsPerChannel: 32, 
    mReserved: 0) 

private let numberOfBuffers = 80 
private let bufferSize: UInt32 = 256 

private var active = false 

private var inputQueue: AudioQueueRef = nil 
private var outputQueue: AudioQueueRef = nil 

private var inputBuffers = [AudioQueueBufferRef]() 
private var outputBuffers = [AudioQueueBufferRef]() 
private var headOfFreeOutputBuffers: AudioQueueBufferRef = nil 

// callbacks implemented in Swift 
private func audioQueueInputCallback(inputBuffer: AudioQueueBufferRef) { 
    if active { 
     if headOfFreeOutputBuffers != nil { 
      let outputBuffer = headOfFreeOutputBuffers 
      headOfFreeOutputBuffers = AudioQueueBufferRef(outputBuffer.memory.mUserData) 
      outputBuffer.memory.mAudioDataByteSize = inputBuffer.memory.mAudioDataByteSize 
      memcpy(outputBuffer.memory.mAudioData, inputBuffer.memory.mAudioData, Int(inputBuffer.memory.mAudioDataByteSize)) 
      assert(AudioQueueEnqueueBuffer(outputQueue, outputBuffer, 0, nil) == 0) 
     } else { 
      println(__FUNCTION__ + ": out-of-output-buffers!") 
     } 

     assert(AudioQueueEnqueueBuffer(inputQueue, inputBuffer, 0, nil) == 0) 
    } 
} 

private func audioQueueOutputCallback(outputBuffer: AudioQueueBufferRef) { 
    if active { 
     outputBuffer.memory.mUserData = UnsafeMutablePointer<Void>(headOfFreeOutputBuffers) 
     headOfFreeOutputBuffers = outputBuffer 
    } 
} 

func start() { 
    var error: NSError? 
    audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: .allZeros, error: &error) 
    dumpError(error, functionName: "AVAudioSessionCategoryPlayAndRecord") 
    audioSession.setPreferredSampleRate(16000, error: &error) 
    dumpError(error, functionName: "setPreferredSampleRate") 
    audioSession.setPreferredIOBufferDuration(0.005, error: &error) 
    dumpError(error, functionName: "setPreferredIOBufferDuration") 

    audioSession.setActive(true, error: &error) 
    dumpError(error, functionName: "setActive(true)") 

    assert(active == false) 
    active = true 

    // cannot provide callbacks to AudioQueueNewInput/AudioQueueNewOutput from Swift and so need to interface C functions 
    assert(MyAudioQueueConfigureInputQueueAndCallback(audioStreamBasicDescription, &inputQueue, audioQueueInputCallback) == 0) 
    assert(MyAudioQueueConfigureOutputQueueAndCallback(audioStreamBasicDescription, &outputQueue, audioQueueOutputCallback) == 0) 

    for (var i = 0; i < numberOfBuffers; i++) { 
     var audioQueueBufferRef: AudioQueueBufferRef = nil 
     assert(AudioQueueAllocateBuffer(inputQueue, bufferSize, &audioQueueBufferRef) == 0) 
     assert(AudioQueueEnqueueBuffer(inputQueue, audioQueueBufferRef, 0, nil) == 0) 
     inputBuffers.append(audioQueueBufferRef) 

     assert(AudioQueueAllocateBuffer(outputQueue, bufferSize, &audioQueueBufferRef) == 0) 
     outputBuffers.append(audioQueueBufferRef) 

     audioQueueBufferRef.memory.mUserData = UnsafeMutablePointer<Void>(headOfFreeOutputBuffers) 
     headOfFreeOutputBuffers = audioQueueBufferRef 
    } 

    assert(AudioQueueStart(inputQueue, nil) == 0) 
    assert(AudioQueueStart(outputQueue, nil) == 0) 
} 

И тогда мой C-код, чтобы установить обратные вызовы обратно Swift:

static void MyAudioQueueAudioInputCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp * inStartTime, 
            UInt32 inNumberPacketDescriptions, const AudioStreamPacketDescription * inPacketDescs) { 
    void(^block)(AudioQueueBufferRef) = (__bridge void(^)(AudioQueueBufferRef))inUserData; 
    block(inBuffer); 
} 

static void MyAudioQueueAudioOutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { 
    void(^block)(AudioQueueBufferRef) = (__bridge void(^)(AudioQueueBufferRef))inUserData; 
    block(inBuffer); 
} 

OSStatus MyAudioQueueConfigureInputQueueAndCallback(AudioStreamBasicDescription inFormat, AudioQueueRef *inAQ, void(^callback)(AudioQueueBufferRef)) { 
    return AudioQueueNewInput(&inFormat, MyAudioQueueAudioInputCallback, (__bridge_retained void *)([callback copy]), nil, nil, 0, inAQ); 
} 

OSStatus MyAudioQueueConfigureOutputQueueAndCallback(AudioStreamBasicDescription inFormat, AudioQueueRef *inAQ, void(^callback)(AudioQueueBufferRef)) { 
    return AudioQueueNewOutput(&inFormat, MyAudioQueueAudioOutputCallback, (__bridge_retained void *)([callback copy]), nil, nil, 0, inAQ); 
} 

ответ

2

После того, как я нашел this отличное сообщение, используя AudioUnits вместо AudioQueues. Я просто портировал его на Swift, а затем просто добавил:

audioSession.setPreferredIOBufferDuration(0.005, error: &error) 
+0

Не могли бы вы поделиться версией Swift где-нибудь? – Joel

+0

Помогло ли это решить проблему с задержкой? Что относительно ресурсов процессора? – rsp1984

+0

@ rsp1984 Я просмотрел свой код и нашел этот комментарий: «значение 5 мс, кажется, представляет ~ 1% использования ЦП на iPhone 5». Затем я вспомнил, что, если я уменьшу его, использование ЦП начало увеличиваться. И, конечно, латентность не может быть устранена, но она была уменьшена до приемлемого уровня :) BTW: латентность на iPod Touch больше, чем, например, на iPhone 5. –

1

Если вы запись звука с микрофона и его воспроизведения в пределах слышимости этого микрофона, а затем из-за аудио пропускной способности не является мгновенный, некоторые из ваших предыдущих выходных данных превратят его в новый вход, следовательно, эхо. Это явление называется feedback.

Это структурная проблема, поэтому изменение API записи не поможет (хотя изменение размеров буфера записи/воспроизведения даст вам возможность контролировать задержку в эхо). Вы можете воспроизводить звук таким образом, чтобы микрофон не мог его слышать (например, совсем нет, или через наушники), или спуститься по кроличьему отверстию echo cancellation.

+0

Привет и спасибо за ваш ввод. Нет, это не обратная связь, с которой у меня проблемы. Я использую наушники;) И, как уже упоминалось в моем вопросе, я не получаю проблему с задержкой (только очень незначительную), если я использую структуру Novocaine. –

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