我最近发布了有关在iOS上使用多路由的问题,我以为我解决了这个问题,但是我发现这不太可行:AVAudioEngine Multichannel mapping

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

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

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

最佳答案

我相信问题是线

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

这为您提供了数组对象的大小,本质上是指针的大小,而不是数组中对象的大小。参见the discussion in the Apple docs

该属性的大小应为数组中包含的 channel 数乘以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))

关于ios - 为什么我的多 channel 映射无法正常工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62230997/

10-14 20:27
查看更多