Estoy tratando de convertir el formato del nodo de entrada al formato S16LE. Lo he probado con AVAudioMixerNode
Primero creo una sesión de audio
do { try audioSession.setCategory(.record) try audioSession.setActive(true) } catch { ... } //Define formats let inputNodeOutputFormat = audioEngine.inputNode.outputFormat(forBus: 0) guard let wantedFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: 16000, channels: 1, interleaved: false) else { return; } //Create mixer node and attach it to the engine audioEngine.attach(mixerNode) //Connect the input node to mixer node and mixer node to mainMixerNode audioEngine.connect(audioEngine.inputNode, to: mixerNode, format: inputNodeOutputFormat) audioEngine.connect(mixerNode, to: audioEngine.mainMixerNode, format: wantedFormat) //Install the tab on the output of the mixerNode mixerNode.installTap(onBus: 0, bufferSize: bufferSize, format: wantedFormat) { (buffer, time) in let theLength = Int(buffer.frameLength) var bufferData: [Int16] = [] for i in 0 ..< theLength { let char = Int16((buffer.int16ChannelData?.pointee[i])!) bufferData.append(char) } }Obtuve el siguiente error.
Exception 'I[busArray objectAtindexedSubscript: (NSUlnteger)element] setFormat:format error:&nsErr]: returned false, error Error Domain=NSOSStatusErrorDomain Code=-10868 "(null)"' was thrown¿En qué parte del gráfico me equivoqué?
Debe establecer el formato de los nodos para que coincida con el formato real de los datos. Establecer el formato del nodo no provoca ninguna conversión, excepto que los nodos mezcladores pueden convertir frecuencias de muestreo (pero no formatos de datos). Deberá usar un AVAudioConverter en su toque para realizar la conversión.
Como ejemplo de cómo se vería este código, para manejar conversiones arbitrarias:
let inputNode = audioEngine.inputNode let inputFormat = inputNode.inputFormat(forBus: 0) let outputFormat = AVAudioFormat(... define your format ...) guard let converter = AVAudioConverter(from: inputFormat, to: outputFormat) else { throw ...some error... } inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) {[weak self] (buffer, time) in let inputBlock: AVAudioConverterInputBlock = {inNumPackets, outStatus in outStatus.pointee = AVAudioConverterInputStatus.haveData return buffer } let targetFrameCapacity = AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate) if let convertedBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: targetFrameCapacity) { var error: NSError? let status = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputBlock) assert(status != .error) let sampleCount = convertedBuffer.frameLength let rawData = convertedBuffer.int16ChannelData![0] // ... and here you have your data ... } } Si no necesita cambiar la frecuencia de muestreo y está convirtiendo de audio sin comprimir a audio sin comprimir, es posible que pueda usar el método de convert(to:from:) en su toque.
Desde iOS 13, también puede hacer esto con AVAudioSinkNode en lugar de un toque, lo que puede ser más conveniente.