Я хочу создать приложение, которое принимает входные данные с микрофона, выполняет обработку входного аудиовектора и немедленно воспроизводит его через выход. Я хочу в конечном итоге выполнить обработку голоса, поэтому мне нужно решение с низкой задержкой. Однако мое текущее решение перезванивает примерно каждые 100 мс, что слишком медленно для моего варианта использования. Вместо этого я надеюсь получить доступ к буферу для воспроизведения каждые 8 мс.
Мое текущее решение:
var audioEngine: AVAudioEngine
var inputNode: AVAudioInputNode
var playerNode: AVAudioPlayerNode
var bufferDuration: AVAudioFrameCount
init() {
audioEngine = AVAudioEngine()
inputNode = audioEngine.inputNode
playerNode = AVAudioPlayerNode()
bufferDuration = 960// AVAudioFrameCount(352)
}
func startStreaming() -> Void {
// Configure the session
do {
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.defaultToSpeaker])
try audioSession.setPreferredSampleRate(96000)
try audioSession.setPreferredIOBufferDuration(0.008)
try audioSession.setActive(true)
try audioSession.overrideOutputAudioPort(.speaker)
} catch {
print("Audio Session error: \(error)")
}
let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: AVAudioSession.sharedInstance().sampleRate, channels: 2, interleaved: false)
// Set the playerNode to immediately queue/play the recorded buffer
inputNode.installTap(onBus: 0, bufferSize: bufferDuration, format: fmt) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
// Schedule the buffer for playback
playerNode.scheduleBuffer(buffer, at: nil, options: [], completionHandler: nil)
}
// Start the engine
do {
audioEngine.attach(playerNode)
audioEngine.connect(playerNode, to: audioEngine.outputNode, format: fmt/*inputNode.inputFormat(forBus: 0)*/)
try audioEngine.start()
playerNode.play()
} catch {
print("Audio Engine start error: \(error)")
}
Я пробовал такие варианты, как установка buffer.frameLength, но, похоже, ничто не меняет частоту обратного вызова. Мне неясно, связана ли эта проблема с тем, что фреймворк не позволяет использовать такие маленькие буферы, или мне не хватает решения. Другие решенные решения на этом сайте приводят доводы в пользу того, что небольшие размеры буфера не требуются, но мне нужно очень быстрое решение. Если это невозможно с помощью AVFAudio, есть ли потенциальное решение в AudioKit.io и API Core Audio C?
Попробуйте напрямую соединить входной узел с выходным узлом и вообще отказаться от крана и AVAudioPlayerNode
. (В любом случае, для чего нужен AVAudioPlayerNode
? Не зависящие от времени вещи.)
Это работает для меня:
let engine = AVAudioEngine()
init() {
let session = AVAudioSession.sharedInstance()
try! session.setCategory(.playAndRecord)
try! session.setPreferredIOBufferDuration(0.008)
try! session.setActive(true)
engine.connect(engine.inputNode, to: engine.outputNode, format: nil)
try! engine.start()
}
Если вы примените свою обработку как AVAudioNode
или AUAudioUnit
, вы сможете подключить ее к графику/движку и поддерживать аналогичные свойства задержки.
Это сработало отлично, спасибо!! В моем приложении я хотел бы выполнять обработку голоса в реальном времени, поэтому мне нужно получать доступ к 8-миллисекундному буферу каждые 8 мс и манипулировать вектором внутри, прежде чем передавать его на выход. Есть ли удобный способ сделать это при такой настройке?