如何指定 AVAudioEngine Mic-Input 的格式?
How can I specify the format of AVAudioEngine Mic-Input?
我想使用 AVAudioEngine
和用户麦克风录制一些音频。我已经有了一个工作示例,但就是不知道如何指定我想要的输出格式...
我的要求是我需要AVAudioPCMBuffer
,正如我所说的那样……
我是否需要添加一个单独的节点来进行一些转码?关于这个问题,我找不到太多 documentation/samples...
在音频方面,我也是一个菜鸟。我知道我想要 NSData
包含最大采样率为 16000 的 PCM-16 位(8000 会更好)
这是我的工作示例:
private var audioEngine = AVAudioEngine()
func startRecording() {
let format = audioEngine.inputNode!.inputFormatForBus(bus)
audioEngine.inputNode!.installTapOnBus(bus, bufferSize: 1024, format: format) { (buffer: AVAudioPCMBuffer, time:AVAudioTime) -> Void in
let audioFormat = PCMBuffer.format
print("\(audioFormat)")
}
audioEngine.prepare()
do {
try audioEngine.start()
} catch { /* Imagine some super awesome error handling here */ }
}
如果我改变格式让'说
let format = AVAudioFormat(commonFormat: AVAudioCommonFormat.PCMFormatInt16, sampleRate: 8000.0, channels: 1, interleaved: false)
然后如果会产生错误,说明采样率需要与 hwInput 相同...
非常感谢任何帮助!!!
编辑: 我刚找到 AVAudioConverter
但我也需要与 iOS8 兼容...
您不能更改输入节点的配置,尝试创建一个具有您想要的格式的混合器节点,将其附加到引擎,然后将其连接到输入节点,然后将 mainMixer 连接到您想要的节点刚刚创建。现在你可以在这个节点上安装一个tap来获取PCM数据。
请注意,由于某些奇怪的原因,您没有太多的采样率选择!至少在 iOS 9.1 上不行,使用标准的 11025、22050 或 44100。任何其他采样率都会失败!
您不能直接在输入或输出节点上更改音频格式。对于麦克风,格式始终为 44KHz、1 通道、32 位。为此,您需要在两者之间插入一个混音器。然后当你连接inputNode > changeformatMixer > mainEngineMixer时,你可以指定你想要的格式的细节。
类似于:
var inputNode = audioEngine.inputNode
var downMixer = AVAudioMixerNode()
//I think you the engine's I/O nodes are already attached to itself by default, so we attach only the downMixer here:
audioEngine.attachNode(downMixer)
//You can tap the downMixer to intercept the audio and do something with it:
downMixer.installTapOnBus(0, bufferSize: 2048, format: downMixer.outputFormatForBus(0), block: //originally 1024
{ (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
print(NSString(string: "downMixer Tap"))
do{
print("Downmixer Tap Format: "+self.downMixer.outputFormatForBus(0).description)//buffer.audioBufferList.debugDescription)
})
//let's get the input audio format right as it is
let format = inputNode.inputFormatForBus(0)
//I initialize a 16KHz format I need:
let format16KHzMono = AVAudioFormat.init(commonFormat: AVAudioCommonFormat.PCMFormatInt16, sampleRate: 11050.0, channels: 1, interleaved: true)
//connect the nodes inside the engine:
//INPUT NODE --format-> downMixer --16Kformat--> mainMixer
//as you can see I m downsampling the default 44khz we get in the input to the 16Khz I want
audioEngine.connect(inputNode, to: downMixer, format: format)//use default input format
audioEngine.connect(downMixer, to: audioEngine.outputNode, format: format16KHzMono)//use new audio format
//run the engine
audioEngine.prepare()
try! audioEngine.start()
不过,我建议改用 EZAudio 等开放式框架。
我发现唯一可以改变采样率的方法是
AVAudioSettings.sharedInstance().setPreferredSampleRate(...)
您可以点击 engine.inputNode 并使用输入节点的输出格式:
engine.inputNode.installTap(onBus: 0, bufferSize: 2048,
format: engine.inputNode.outputFormat(forBus: 0))
遗憾的是,虽然 8000、12000、16000、22050、44100 似乎都有效,但无法保证您会获得所需的采样率。
以下无效:
- 点击关闭 engine.inputNode 设置我的自定义格式。 (例外)
- 使用我的自定义格式添加混音器并点击它。 (例外)
- 添加一个混音器,将其与inputNode的格式连接,将混音器连接到主混音器以我的自定义格式,然后移除outputNode的输入,以免将音频发送到扬声器并获得即时反馈。 (有效,但全部为零)
- 在 AVAudioEngine 中完全没有使用我的自定义格式,而是使用 AVAudioConverter 从我的水龙头中的硬件速率进行转换。 (未设置缓冲区的长度,无法判断结果是否正确)
这是 iOS 12.3.1.
如果您只需要更改采样率和通道,我建议使用行级 API。您不需要使用混频器或转换器。在这里你可以找到关于低级录音的 Apple 文档。如果需要,您可以转换为 Objective-C class 并添加协议。
要更改输入节点的采样率,您必须先将输入节点连接到混合器节点,并在参数中指定新格式。
let input = avAudioEngine.inputNode
let mainMixer = avAudioEngine.mainMixerNode
let newAudioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true)
avAudioEngine.connect(input, to: mainMixer, format: newAudioFormat)
现在您可以使用新的音频格式在输入节点上调用 installTap 函数。
还有一点我想指出的是,自从iPhone12新推出以来,输入节点的默认采样率不再是44100了。已升级到48000
如果您的目标只是简单地以包含所需格式音频的 AVAudioPCMBuffers 结束,您可以使用 AVAudioConverter 转换 tap 块中返回的缓冲区。这样,你实际上不需要知道或关心inputNode的格式是什么。
class MyBufferRecorder {
private let audioEngine:AVAudioEngine = AVAudioEngine()
private var inputNode:AVAudioInputNode!
private let audioQueue:DispatchQueue = DispatchQueue(label: "Audio Queue 5000")
private var isRecording:Bool = false
func startRecording() {
if (isRecording) {
return
}
isRecording = true
// must convert (unknown until runtime) input format to our desired output format
inputNode = audioEngine.inputNode
let inputFormat:AVAudioFormat! = inputNode.outputFormat(forBus: 0)
// 9600 is somewhat arbitrary... min seems to be 4800, max 19200... it doesn't matter what we set
// because we don't re-use this value -- we query the buffer returned in the tap block for it's true length.
// Using [weak self] in the tap block is probably a better idea, but it results in weird warnings for now
inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(9600), format: inputFormat) { (buffer, time) in
// not sure if this is necessary
if (!self.isRecording) {
print("\nDEBUG - rejecting callback, not recording")
return }
// not really sure if/why this needs to be async
self.audioQueue.async {
// Convert recorded buffer to our preferred format
let convertedPCMBuffer = AudioUtils.convertPCMBuffer(bufferToConvert: buffer, fromFormat: inputFormat, toFormat: AudioUtils.desiredFormat)
// do something with converted buffer
}
}
do {
// important not to start engine before installing tap
try audioEngine.start()
} catch {
print("\nDEBUG - couldn't start engine!")
return
}
}
func stopRecording() {
print("\nDEBUG - recording stopped")
isRecording = false
inputNode.removeTap(onBus: 0)
audioEngine.stop()
}
}
分开 class:
import Foundation
import AVFoundation
// assumes we want 16bit, mono, 44100hz
// change to what you want
class AudioUtils {
static let desiredFormat:AVAudioFormat! = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: Double(44100), channels: 1, interleaved: false)
// PCM <--> PCM
static func convertPCMBuffer(bufferToConvert: AVAudioPCMBuffer, fromFormat: AVAudioFormat, toFormat: AVAudioFormat) -> AVAudioPCMBuffer {
let convertedPCMBuffer = AVAudioPCMBuffer(pcmFormat: toFormat, frameCapacity: AVAudioFrameCount(bufferToConvert.frameLength))
var error: NSError? = nil
let inputBlock:AVAudioConverterInputBlock = {inNumPackets, outStatus in
outStatus.pointee = AVAudioConverterInputStatus.haveData
return bufferToConvert
}
let formatConverter:AVAudioConverter = AVAudioConverter(from:fromFormat, to: toFormat)!
formatConverter.convert(to: convertedPCMBuffer!, error: &error, withInputFrom: inputBlock)
if error != nil {
print("\nDEBUG - " + error!.localizedDescription)
}
return convertedPCMBuffer!
}
}
这绝不是生产就绪代码 -- 我也在学习 IOS 音频...所以请让我知道该代码中发生的任何错误、最佳实践或危险事情我会及时更新这个答案。
我想使用 AVAudioEngine
和用户麦克风录制一些音频。我已经有了一个工作示例,但就是不知道如何指定我想要的输出格式...
我的要求是我需要AVAudioPCMBuffer
,正如我所说的那样……
我是否需要添加一个单独的节点来进行一些转码?关于这个问题,我找不到太多 documentation/samples...
在音频方面,我也是一个菜鸟。我知道我想要 NSData
包含最大采样率为 16000 的 PCM-16 位(8000 会更好)
这是我的工作示例:
private var audioEngine = AVAudioEngine()
func startRecording() {
let format = audioEngine.inputNode!.inputFormatForBus(bus)
audioEngine.inputNode!.installTapOnBus(bus, bufferSize: 1024, format: format) { (buffer: AVAudioPCMBuffer, time:AVAudioTime) -> Void in
let audioFormat = PCMBuffer.format
print("\(audioFormat)")
}
audioEngine.prepare()
do {
try audioEngine.start()
} catch { /* Imagine some super awesome error handling here */ }
}
如果我改变格式让'说
let format = AVAudioFormat(commonFormat: AVAudioCommonFormat.PCMFormatInt16, sampleRate: 8000.0, channels: 1, interleaved: false)
然后如果会产生错误,说明采样率需要与 hwInput 相同...
非常感谢任何帮助!!!
编辑: 我刚找到 AVAudioConverter
但我也需要与 iOS8 兼容...
您不能更改输入节点的配置,尝试创建一个具有您想要的格式的混合器节点,将其附加到引擎,然后将其连接到输入节点,然后将 mainMixer 连接到您想要的节点刚刚创建。现在你可以在这个节点上安装一个tap来获取PCM数据。
请注意,由于某些奇怪的原因,您没有太多的采样率选择!至少在 iOS 9.1 上不行,使用标准的 11025、22050 或 44100。任何其他采样率都会失败!
您不能直接在输入或输出节点上更改音频格式。对于麦克风,格式始终为 44KHz、1 通道、32 位。为此,您需要在两者之间插入一个混音器。然后当你连接inputNode > changeformatMixer > mainEngineMixer时,你可以指定你想要的格式的细节。
类似于:
var inputNode = audioEngine.inputNode
var downMixer = AVAudioMixerNode()
//I think you the engine's I/O nodes are already attached to itself by default, so we attach only the downMixer here:
audioEngine.attachNode(downMixer)
//You can tap the downMixer to intercept the audio and do something with it:
downMixer.installTapOnBus(0, bufferSize: 2048, format: downMixer.outputFormatForBus(0), block: //originally 1024
{ (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
print(NSString(string: "downMixer Tap"))
do{
print("Downmixer Tap Format: "+self.downMixer.outputFormatForBus(0).description)//buffer.audioBufferList.debugDescription)
})
//let's get the input audio format right as it is
let format = inputNode.inputFormatForBus(0)
//I initialize a 16KHz format I need:
let format16KHzMono = AVAudioFormat.init(commonFormat: AVAudioCommonFormat.PCMFormatInt16, sampleRate: 11050.0, channels: 1, interleaved: true)
//connect the nodes inside the engine:
//INPUT NODE --format-> downMixer --16Kformat--> mainMixer
//as you can see I m downsampling the default 44khz we get in the input to the 16Khz I want
audioEngine.connect(inputNode, to: downMixer, format: format)//use default input format
audioEngine.connect(downMixer, to: audioEngine.outputNode, format: format16KHzMono)//use new audio format
//run the engine
audioEngine.prepare()
try! audioEngine.start()
不过,我建议改用 EZAudio 等开放式框架。
我发现唯一可以改变采样率的方法是
AVAudioSettings.sharedInstance().setPreferredSampleRate(...)
您可以点击 engine.inputNode 并使用输入节点的输出格式:
engine.inputNode.installTap(onBus: 0, bufferSize: 2048,
format: engine.inputNode.outputFormat(forBus: 0))
遗憾的是,虽然 8000、12000、16000、22050、44100 似乎都有效,但无法保证您会获得所需的采样率。
以下无效:
- 点击关闭 engine.inputNode 设置我的自定义格式。 (例外)
- 使用我的自定义格式添加混音器并点击它。 (例外)
- 添加一个混音器,将其与inputNode的格式连接,将混音器连接到主混音器以我的自定义格式,然后移除outputNode的输入,以免将音频发送到扬声器并获得即时反馈。 (有效,但全部为零)
- 在 AVAudioEngine 中完全没有使用我的自定义格式,而是使用 AVAudioConverter 从我的水龙头中的硬件速率进行转换。 (未设置缓冲区的长度,无法判断结果是否正确)
这是 iOS 12.3.1.
如果您只需要更改采样率和通道,我建议使用行级 API。您不需要使用混频器或转换器。在这里你可以找到关于低级录音的 Apple 文档。如果需要,您可以转换为 Objective-C class 并添加协议。
要更改输入节点的采样率,您必须先将输入节点连接到混合器节点,并在参数中指定新格式。
let input = avAudioEngine.inputNode
let mainMixer = avAudioEngine.mainMixerNode
let newAudioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true)
avAudioEngine.connect(input, to: mainMixer, format: newAudioFormat)
现在您可以使用新的音频格式在输入节点上调用 installTap 函数。
还有一点我想指出的是,自从iPhone12新推出以来,输入节点的默认采样率不再是44100了。已升级到48000
如果您的目标只是简单地以包含所需格式音频的 AVAudioPCMBuffers 结束,您可以使用 AVAudioConverter 转换 tap 块中返回的缓冲区。这样,你实际上不需要知道或关心inputNode的格式是什么。
class MyBufferRecorder {
private let audioEngine:AVAudioEngine = AVAudioEngine()
private var inputNode:AVAudioInputNode!
private let audioQueue:DispatchQueue = DispatchQueue(label: "Audio Queue 5000")
private var isRecording:Bool = false
func startRecording() {
if (isRecording) {
return
}
isRecording = true
// must convert (unknown until runtime) input format to our desired output format
inputNode = audioEngine.inputNode
let inputFormat:AVAudioFormat! = inputNode.outputFormat(forBus: 0)
// 9600 is somewhat arbitrary... min seems to be 4800, max 19200... it doesn't matter what we set
// because we don't re-use this value -- we query the buffer returned in the tap block for it's true length.
// Using [weak self] in the tap block is probably a better idea, but it results in weird warnings for now
inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(9600), format: inputFormat) { (buffer, time) in
// not sure if this is necessary
if (!self.isRecording) {
print("\nDEBUG - rejecting callback, not recording")
return }
// not really sure if/why this needs to be async
self.audioQueue.async {
// Convert recorded buffer to our preferred format
let convertedPCMBuffer = AudioUtils.convertPCMBuffer(bufferToConvert: buffer, fromFormat: inputFormat, toFormat: AudioUtils.desiredFormat)
// do something with converted buffer
}
}
do {
// important not to start engine before installing tap
try audioEngine.start()
} catch {
print("\nDEBUG - couldn't start engine!")
return
}
}
func stopRecording() {
print("\nDEBUG - recording stopped")
isRecording = false
inputNode.removeTap(onBus: 0)
audioEngine.stop()
}
}
分开 class:
import Foundation
import AVFoundation
// assumes we want 16bit, mono, 44100hz
// change to what you want
class AudioUtils {
static let desiredFormat:AVAudioFormat! = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: Double(44100), channels: 1, interleaved: false)
// PCM <--> PCM
static func convertPCMBuffer(bufferToConvert: AVAudioPCMBuffer, fromFormat: AVAudioFormat, toFormat: AVAudioFormat) -> AVAudioPCMBuffer {
let convertedPCMBuffer = AVAudioPCMBuffer(pcmFormat: toFormat, frameCapacity: AVAudioFrameCount(bufferToConvert.frameLength))
var error: NSError? = nil
let inputBlock:AVAudioConverterInputBlock = {inNumPackets, outStatus in
outStatus.pointee = AVAudioConverterInputStatus.haveData
return bufferToConvert
}
let formatConverter:AVAudioConverter = AVAudioConverter(from:fromFormat, to: toFormat)!
formatConverter.convert(to: convertedPCMBuffer!, error: &error, withInputFrom: inputBlock)
if error != nil {
print("\nDEBUG - " + error!.localizedDescription)
}
return convertedPCMBuffer!
}
}
这绝不是生产就绪代码 -- 我也在学习 IOS 音频...所以请让我知道该代码中发生的任何错误、最佳实践或危险事情我会及时更新这个答案。