AudioUnit - 在 Swift 中控制左声道和右声道输出
Posted
技术标签:
【中文标题】AudioUnit - 在 Swift 中控制左声道和右声道输出【英文标题】:AudioUnit - Control left channel and right channel output in Swift 【发布时间】:2017-11-24 07:57:22 【问题描述】:我正在尝试与 swift 同时录制和播放。我需要分别在左声道和右声道播放。我成功地使用 AudioUnit 在一个通道中录制和播放。但是在我尝试使用两个缓冲区来控制两个通道之后,它们都是静音的。我是这样设置格式的:
var audioFormat = AudiostreamBasicDescription()
audioFormat.mSampleRate = Double(sampleRate)
audioFormat.mFormatID = kAudioFormatLinearPCM
audioFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked
audioFormat.mChannelsPerFrame = 2
audioFormat.mFramesPerPacket = 1
audioFormat.mBitsPerChannel = 32
audioFormat.mBytesPerPacket = 8
audioFormat.mReserved = 0
这是我的输入回调
private let inputCallback: AURenderCallback = (
inRefCon,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
ioData) -> OSStatus in
let audioRAP:AudioUnitSample = Unmanaged<AudioUnitSample>.fromOpaque(inRefCon).takeUnretainedValue()
var status = noErr;
var buf = UnsafeMutableRawPointer.allocate(bytes: Int(inNumberFrames * 4),
alignedTo: MemoryLayout<Int8>.alignment)
let bindptr = buf.bindMemory(to: Float.self,
capacity: Int(inNumberFrames * 4))
bindptr.initialize(to: 0)
var buffer: AudioBuffer = AudioBuffer(mNumberChannels: 2,
mDataByteSize: inNumberFrames * 4,
mData: buf)
memset(buffer.mData, 0, Int(buffer.mDataByteSize))
var bufferList: AudioBufferList = AudioBufferList(mNumberBuffers: 1,
mBuffers: buffer)(Int(bufferList.mBuffers.mDataByteSize))")
status = AudioUnitRender(audioRAP.newAudioUnit!,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
&bufferList)
audioRAP.audioBuffers.append((bufferList.mBuffers,Int(inNumberFrames * 4)))
return status
这是我的输出回调:
private let outputCallback:AURenderCallback =
(inRefCon,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
ioData) -> OSStatus in
let audioRAP:AudioUnitSample = Unmanaged<AudioUnitSample>.fromOpaque(inRefCon).takeUnretainedValue()
if ioData == nil
return noErr
ioData!.pointee.mNumberBuffers = 2
var bufferCount = ioData!.pointee.mNumberBuffers
var tempBuffer = audioRAP.audioBuffers[0]
var monoSamples = [Float]()
let ptr1 = tempBuffer.0.mData?.assumingMemoryBound(to: Float.self)
monoSamples.removeAll()
monoSamples.append(contentsOf: UnsafeBufferPointer(start: ptr1, count: Int(inNumberFrames)))
let abl = UnsafeMutableAudioBufferListPointer(ioData)
let bufferLeft = abl![0]
let bufferRight = abl![1]
let pointerLeft: UnsafeMutableBufferPointer<Float32> = UnsafeMutableBufferPointer(bufferLeft)
let pointerRight: UnsafeMutableBufferPointer<Float32> = UnsafeMutableBufferPointer(bufferRight)
for frame in 0..<inNumberFrames
let pointerIndex = pointerLeft.startIndex.advanced(by: Int(frame))
pointerLeft[pointerIndex] = monoSamples[Int(frame)]
for frame in 0..<inNumberFrames
let pointerIndex = pointerRight.startIndex.advanced(by: Int(frame))
pointerRight[pointerIndex] = monoSamples[Int(frame)]
tempBuffer.0.mData?.deallocate(bytes:tempBuffer.1, alignedTo: MemoryLayout<Int8>.alignment)
audioRAP.audioBuffers.removeFirst()
return noErr
这里是audiobuffer的声明:
private var audioBuffers = [(AudioBuffer, Int)]()
我错过了输出或输入部分的内容吗?任何帮助将不胜感激!
【问题讨论】:
在 2017 年 WWDC 会议上,关于 Core Audio 的新功能(视频可用),Apple 工程师建议不要在音频上下文中使用 Swift 或 Objective C 代码。直接的 C 代码(或者可能转换为没有内存管理的直接 C 代码的 Swift 代码)在音频单元回调中可能没问题。 是的,Core Audio 似乎不能很好地转换为 swift。但出于某种原因,我仍然需要找到一些方法。 【参考方案1】:第一个大问题是你的代码在里面做内存分配 音频回调。 Apple 文档明确指出,不应该在音频上下文中完成内存管理、同步甚至对象消息传递。在音频回调中,您可能只想坚持只对预先分配的缓冲区进行音频样本的数据复制。其他一切(尤其是缓冲区创建和释放)都应该在音频回调之外完成。
【讨论】:
感谢您的回答,是的,您是对的。我不应该在音频回调中管理内存。我要做的是在 inputCallback 中执行 AudioUnitRender 之后。我可以从缓冲区列表中获取数据。当我在 outputcallback 中将它发送回 ioData 时,我确实听到了扬声器的声音。我猜ioData应该有两个可以控制左声道和右声道的缓冲区。所以我更改 ioData!.pointee.mNumberBuffers = 2,我发现第二个缓冲区没有分配。(这样我尝试管理内存)我相信 ioData 没有通过两个缓冲区将数据设置到左通道和右通道。 我还是不知道如何静音左声道或右声道?有什么提示吗? @hotpaw2以上是关于AudioUnit - 在 Swift 中控制左声道和右声道输出的主要内容,如果未能解决你的问题,请参考以下文章