2016-07-26 43 views
1

Wie kann ich mit AVFoundation auf andere Audio-Hardware-Ausgänge als 1-2 zugreifen? Ich schreibe SWIFT-Code für einen Mac OS-X-App, die MP3-Dateien über verschiedene Ausgabegeräte spielt (USB-Schnittstelle, dante, Soundflower), die wie folgt aussieht:Zugriff auf mehrere Audio-Hardware-Ausgänge/-Kanäle mit AVFoundation und Swift

myPlayer = AVPlayer(URL: myFilePathURL) 
myPlayer.audioOutputDeviceUniqueID = myAudioOutputDevices[1].deviceUID() 
myPlayer.play() 

Aber, ich bin nicht sicher, wie um die Audiodatei auf anderen Kanälen als nur 1-2 abzuspielen. Zum Beispiel möchte ich ein MP3 abspielen, um 3-4 auszugeben.

Kann ich dies über AVPlayer tun? Oder muss ich woanders suchen? Vielleicht AVAudioEngine zusammen mit Mixer-Knoten? Ich habe die AVAudioEngine-Beispiele durchgesehen und konnte nirgendwo referenzierte Hardwarekanäle finden. Danke für jede Hilfe!

+0

Es ist mein Verständnis, dass AVAudioEngine ist nur ein iOS Rahmen - so das wird nicht funktionieren. Die Apple-Dokumentation für iOS- und OS-X-Frameworks ist sehr eng miteinander verbunden - leider ohne viel Erklärung dafür, was für jede Plattform separat verfügbar ist. –

+0

Ich versuche einen anderen Pfad, der AVAudioEngine und AVAudioPlayerNode zusammen mit dem mainMixerNode verwendet. Ich habe nicht herausgefunden, ob ich den MainMixerNode einem anderen Hardwarekanal als dem Standard zuweisen kann. Vielleicht ist das ein besserer Weg als der AVPlayer? –

+0

Die Arbeit mit Channels Maps wurde wie hier [hier] begonnen (https://developer.apple.com/library/prerelease/content/technotes/tn2091/_index.html#//apple_ref/doc/uid/DTS10003118-CH1-CHANNELMAPPING) Arbeitet noch nicht, aber hoffentlich bald. –

Antwort

1

Ich habe eine schnelle Version, die mit 2 Kanälen arbeitet, die die channel map Eigenschaft einstellen. Ich habe es nicht mit einem vollständigen Mehrkanalsystem getestet, aber die Prinzipien sollten die gleichen sein.

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() 



} 
+0

Wissen Sie, wie man das mit den Eingängen macht? Ich habe eine 8-Kanal-Schnittstelle, möchte aber nur, dass mein Mixer-Knoten einen bestimmten Kanal auf der Schnittstelle hört. – JoeBayLD

+0

Ich habe dieses Beispiel versucht und es spielt immer noch die Datei aus beiden linken und rechten Kanälen ... – tsugua

+0

Ich habe dies nicht mit der Eingangsseite getan. Aber ich habe es mit der Ausgangsseite verwendet, um Feeds an 16 Stereo-Ausgänge zu senden (Hardware oder über Sound Flower/Virtual Dante). –

0

Ich habe diesen Code im Laufe der Zeit wiederholt - aber die grundlegende Gliederung funktioniert. Hier ist mein aktueller Setup-Code, um Audio zu Mehrkanal-Setups zu senden. Ich tue dies derzeit mit Dante virtueller Soundkarte mit 16 Stereo-instanzierten Strömen des nachfolgenden Code ein:

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)") 
}