使用 AVFoundation 和 Swift 访问多个音频硬件输出/通道
Posted
技术标签:
【中文标题】使用 AVFoundation 和 Swift 访问多个音频硬件输出/通道【英文标题】:Accessing multiple audio hardware outputs/channels using AVFoundation and Swift 【发布时间】:2016-07-26 23:55:00 【问题描述】:如何使用 AVFoundation 访问除 1-2 之外的其他音频硬件输出?我正在为 Mac OS-X 应用程序编写 swift 代码,该应用程序通过各种输出设备(USB 接口、dante、soundflower)播放 mp3 文件,如下所示:
myPlayer = AVPlayer(URL: myFilePathURL)
myPlayer.audioOutputDeviceUniqueID = myAudioOutputDevices[1].deviceUID()
myPlayer.play()
但是,我不确定如何将音频文件播放到 1-2 以外的频道。例如,我想播放 mp3 到输出 3-4。
我可以通过 AVPlayer 做到这一点吗?还是我需要去别处看看?也许 AVAudioEngine 以及混音器节点?我查看了 AVAudioEngine 示例,找不到任何地方引用的硬件通道。感谢您的帮助!
【问题讨论】:
我的理解是 AVAudioEngine 只是一个 ios 框架 - 所以这行不通。 iOS 和 OS-X 框架的 Apple 文档非常交织在一起 - 遗憾的是,没有太多解释分别适用于每个平台的内容。 我正在尝试使用 AVAudioEngine 和 AVAudioPlayerNode 以及 mainMixerNode 的另一条路径。我还没有弄清楚是否可以将 mainMixerNode 分配给默认以外的其他硬件通道。也许这比使用 AVPlayer 更好? 按照here 的描述开始使用频道地图,但希望很快。 This post 在使用 Objective-c 中的通道映射的音频通道路由方面也有很大帮助。正在尝试将其转换为 Swift。 还有几个站点包含有关路由通道的信息,包括通过苹果开发者论坛的WWDC session 和this discussion。它们都没有 Swift 代码,但它很好地解释了这些过程。 【参考方案1】:随着时间的推移,我已经对这段代码进行了迭代 - 但基本大纲将起作用。这是我当前的设置代码,用于将音频发送到多通道设置。我目前正在使用以下代码使用具有 16 个立体声实例流的 Dante Virtual Soundcard 执行此操作:
func setupAudioPath()
//print("setupAudioPath")
// get output hardware format
let output = engine.outputNode
outputHWFormat = output.outputFormat(forBus: 0)
//print("outputHWFormat = \(outputHWFormat)")
//print("outputHWFormat.channelCount = \(outputHWFormat.channelCount)")
// connect mixer to output
mixer = engine.mainMixerNode
//then work on the player end by first attaching the player to the engine
engine.attach(player)
engine.connect(mixer, to: output, format: outputHWFormat)
var channelMap: [sint32] = []
//UInt32 numOfChannels = fileFormat.NumberChannels();
let numOfChannels: UInt32 = UInt32(numberOfStreams) * UInt32(2); // Number of output device channels
let mapSize: UInt32 = numOfChannels * UInt32(MemoryLayout<sint32>.size);
for _ in 0...(numOfChannels-1)
channelMap.append(-1)
//channelMap[desiredInputChannel] = deviceOutputChannel;
channelMap[leftChannel - 1] = 0;
channelMap[leftChannel] = 1;
//print(channelMap)
//print("number of channels in my map: \(channelMap.count)")
let code: OSStatus = AudioUnitSetProperty((engine.outputNode.audioUnit)!,
kAudioOutputUnitProperty_ChannelMap,
kAudioUnitScope_Global,
1,
channelMap,
mapSize);
print("osstatus = \(code)")
【讨论】:
【参考方案2】:我有一个使用 2 个通道设置通道映射属性的 swift 版本。我没有用完整的多通道系统测试过,但原理应该是一样的。
let engine = AVAudioEngine()
let player = AVAudioPlayerNode()
func testCode()
// get output hardware format
let output = engine.outputNode
let outputHWFormat = output.outputFormatForBus(0)
// connect mixer to output
let mixer = engine.mainMixerNode
engine.connect(mixer, to: output, format: outputHWFormat)
//then work on the player end by first attaching the player to the engine
engine.attachNode(player)
//find the audiofile
guard let audioFileURL = NSBundle.mainBundle().URLForResource("tones", withExtension: "wav") else
fatalError("audio file is not in bundle.")
var songFile:AVAudioFile?
do
songFile = try AVAudioFile(forReading: audioFileURL)
print(songFile!.processingFormat)
// connect player to mixer
engine.connect(player, to: mixer, format: songFile!.processingFormat)
catch
fatalError("canot create AVAudioFile \(error)")
let channelMap: [Int32] = [0, 1] //left out left, right out right
//let channelMap: [Int32] = [1, 0] //right out left, left out right
let propSize: UInt32 = UInt32(channelMap.count) * UInt32(sizeof(sint32))
let code: OSStatus = AudioUnitSetProperty((engine.inputNode?.audioUnit)!,
kAudioOutputUnitProperty_ChannelMap,
kAudioUnitScope_Global,
1,
channelMap,
propSize);
print(code)
do
try engine.start()
catch
fatalError("Could not start engine. error: \(error).")
player.scheduleFile(songFile!, atTime: nil)
print("done")
self.player.play()
player.play()
【讨论】:
你知道如何处理输入吗?我有一个 8ch 接口,但只希望我的混音器节点监听接口上的特定通道。 我试过这个例子,它仍然从左右声道播放文件... 我没有在输入端做这个。但我已经将它与输出端一起使用,以将馈送发送到 16 个立体声输出(硬件或通过声音花/虚拟 dante)。 @MichaelSweet 我正在尝试对 15 个单声道文件做类似的事情,并将它们路由到 15 个离散的单声道输出,但我运气不佳。我以您的示例为基础,但我不确定路由是如何工作的。你有这样的工作吗? ***.com/questions/46041563/…以上是关于使用 AVFoundation 和 Swift 访问多个音频硬件输出/通道的主要内容,如果未能解决你的问题,请参考以下文章
Swift IOS 使用 AVFoundation 录制视频和音频
在 Swift 的 AVFoundation 中使用夜间模式
swift avfoundation AVCapturePhotoCaptureDelegate 捕获方法
Swift 3:如何在使用 AVFoundation 录制视频期间将麦克风静音/取消静音
Swift 4 - 在 mac os 上使用 AVAssetWriter 进行 avfoundation 屏幕和音频录制 - 视频冻结