为啥我的多通道映射不能正常工作?

Posted

技术标签:

【中文标题】为啥我的多通道映射不能正常工作?【英文标题】:Why isn't my multichannel mapping working correctly?为什么我的多通道映射不能正常工作? 【发布时间】:2020-09-25 14:01:33 【问题描述】:

我最近发布了这个关于在 ios 中使用多路由的问题,我想我已经解决了,但是我发现它不太好用:AVAudioEngine Multichannel mapping

我遇到的问题是多路由仅适用于前两个输出通道。我正在尝试使其适用于 4 通道音频接口。

我已经设法使用 AVAudioPlayer 将音频路由到 USB 接口的每个输出:

var avplayer = AVAudioPlayer()

@IBAction func avAudioPlayerPlay(_ sender: Any)

    let audioSession = AVAudioSession.sharedInstance()
    let route = audioSession.currentRoute

    // set the session category
    do
    
        //try audioSession.setCategory(.multiRoute)
        try audioSession.setCategory(.multiRoute, options: .mixWithOthers)
    
    catch
    
        print("unable to set category", error)
        return
    

    // activate the audio session - turns on multiroute I believe
    do
    
        try audioSession.setActive(true)
        //try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
    
    catch
    
        print("unable to set active", error)
        return
    

    //audio interface + headphone jack
    let outputs:[AVAudioSessionChannelDescription] = [
        route.outputs[0].channels![2], // 3rd channel on Audio Interface
        route.outputs[1].channels![1]  // Right Channel of Headphones
    ]

    guard let filePath: String = Bundle.main.path(forResource: "audio", ofType: "m4a") else  return 
    let fileURL: URL = URL(fileURLWithPath: filePath)

    do
    
        avplayer = try AVAudioPlayer(contentsOf: fileURL)
    
    catch
    
        print("play error", error)
        return
    

    avplayer.channelAssignments = outputs

    let result = avplayer.play()
    print(result)

但我无法使用 AVAudioEngine 让它工作:

private func getOutputChannelMapIndices(_ names:[String?]) -> [Int]

    let session = AVAudioSession.sharedInstance()
    let route = session.currentRoute
    let outputPorts = route.outputs

    var channelMapIndices:[Int] = []

    for name in names
    
        var chIndex = 0
        for outputPort in outputPorts
        
            guard let channels = outputPort.channels else
            
                continue
            
            for channel in channels
            
                print(channel.channelName)
                if channel.channelName == name
                
                    if names.count > channelMapIndices.count
                    
                        channelMapIndices.append(chIndex)
                    
                
                chIndex += 1
            
        
    
    return channelMapIndices


@IBAction func nodesPlay(_ sender: Any)

    let channelNames = [
        "UMC204HD 192k 3",
        "Headphones Left",
        "Headphones Right",
        nil
    ]

    let audioSession = AVAudioSession.sharedInstance()

    // set the session category
    do
    
        //try audioSession.setCategory(.multiRoute)
        try audioSession.setCategory(.multiRoute, options: .mixWithOthers)
    
    catch
    
        print("unable to set category", error)
        return
    

    // activate the audio session - turns on multiroute I believe
    do
    
        try audioSession.setActive(true)
        //try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
    
    catch
    
        print("unable to set active", error)
        return
    

    let channelMapIndices = getOutputChannelMapIndices(channelNames)

    print("channelMapIndices: ", channelMapIndices)

    engine = AVAudioEngine()
    output = engine.outputNode
    mixer = engine.mainMixerNode

    player = AVAudioPlayerNode()

    engine.attach(player)

    guard let filePath: String = Bundle.main.path(forResource: "audio", ofType: "m4a") else  return 
    let fileURL: URL = URL(fileURLWithPath: filePath)
    let file = try! AVAudioFile(forReading: fileURL)

    let outputNumChannels = output.outputFormat(forBus: 0).channelCount
    print("outputNumChannels:" , outputNumChannels)

    var outputChannelMap:[Int] = Array(repeating: -1, count: Int(outputNumChannels))

    let numberOfSourceChannels = file.processingFormat.channelCount
    print("numberOfSourceChannels: ", numberOfSourceChannels)

    var sourceChIndex = 0
    for chIndex in channelMapIndices
    
        if chIndex < outputNumChannels && sourceChIndex < numberOfSourceChannels
        
            outputChannelMap[chIndex] = sourceChIndex
            sourceChIndex += 1
        
    

    print("outputChannelMap: ", outputChannelMap)

    if let au = output.audioUnit
    
        let propSize = UInt32(MemoryLayout.size(ofValue: outputChannelMap))
        print("propSize:", propSize)
        let result = AudioUnitSetProperty(au, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Global, 0, &outputChannelMap, propSize)
        print("result: ", result)
    

    let channelLayout = AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_DiscreteInOrder | UInt32(numberOfSourceChannels))
    let format = AVAudioFormat(streamDescription: file.processingFormat.streamDescription, channelLayout: channelLayout)

    engine.connect(player, to: mixer, format:format)
    engine.connect(mixer, to: output, format:format)

    player.scheduleFile(file, at: nil, completionHandler: nil)

    do
    
        try engine.start()
    
    catch
    
        print("can't start", error)
        return
    

    player.play()

如果有人能解释为什么我似乎无法播放任何音频到输出 3 或 4,我将不胜感激。

注意,这里的很多代码都是从这里翻译的:https://forums.developer.apple.com/thread/15416

【问题讨论】:

kAudioOutputUnitProperty_ChannelMap设置前的值是多少?输出单元kAudioUnitProperty_StreamFormat有多少通道? kAudioOutputUnitProperty_ChannelMap 在我设置它之前似乎是空白的。如果我在设置后得到它,那么似乎只设置了数组的第一个值。输出上似乎有 4 个通道。 我发现如果我将 propSize 乘以 4,然后得到通道图会返回正确的结果......但输出仍然不正确 【参考方案1】:

我认为问题出在线路

let propSize = UInt32(MemoryLayout.size(ofValue: outputChannelMap))

这是给你数组对象的大小,本质上是指针的大小,而不是数组中对象的大小。见the discussion in the Apple docs。

属性的大小应该是数组中包含的通道数乘以Int32 的大小,因为AudioUnitSetProperty 是一个 C API,这将是相应 C 数组的大小。

let propSize = UInt32(MemoryLayout<Int32>.stride * outputChannelMap.count)

您还应该将outputChannelMap 声明为Int32 的数组,因为这是kAudioOutputUnitProperty_ChannelMap 所期望的类型:

var outputChannelMap:[Int32] = Array(repeating: -1, count: Int(outputNumChannels))

【讨论】:

是的。这是输出:0:-1、1:-1。如果我将 propSize 乘以 2,我会得到:0:-1、1:-1、2:-1、3:-1。 另外..这是 outputChannelMap 的样子:[-1, -1, 0, -1] 如果我不乘以 2,我只会得到两个数组值。 我想你可能已经为我指明了正确的方向。我刚刚将 outputChannelMap 更改为 Int32 值数组,现在它输出正确的映射 我刚刚尝试了第二个 USB 接口,这似乎适用于 propSize:let propSize:UInt32 = UInt32(MemoryLayout.size(ofValue: outputChannelMap)) * (outputNumChannels / numberOfSourceChannels) - 这有意义吗?

以上是关于为啥我的多通道映射不能正常工作?的主要内容,如果未能解决你的问题,请参考以下文章

Numpy:跨越多通道图像

为啥 jQuery click 事件在我的多步骤表单中不能多次工作?

SAN光纤通道的多路径选择

对 Windows Media Foundation AAC 编码器的多通道支持

CNN卷积层里的多输入多输出通道channel 动手学深度学习v2 pytorch

RA生态之基于 DTC 的多通道 ADC 采集