2016-04-27 10 views
11

Ich habe einige NSSound Objekte, die ich in AVAudioPlayer Instanzen konvertieren möchte. Ich habe Dateipfade (NSURL s) mit den NSSound Objekten verbunden, aber die ursprüngliche Datei möglicherweise nicht vorhanden. Hier ist, was ich bisher:Convert NSSound in AVAudioPlayer

class SoundObj: NSObject { 
    var path: NSURL? 
    var sound: NSSound? 
    var player: AVAudioPlayer 
} 

let aSound = SoundObj() 
aSound.path = NSURL(fileURLWithPath: "file:///path/to/sound.m4a") 
aSound.sound = NSSound(contentsOfURL: aSound.path)! 

do { 
    try aSound.player = AVAudioPlayer(contentsOfURL: aSound.path) 
} catch { 
    // perhaps use AVAudioPlayer(data: ...)? 
} 

Wie konvertiere ich ein NSSound Objekt zu einer AVAudioPlayer Instanz?

+0

Wenn die Audiodatei nicht existiert, wird diese Zeile nicht abstürzen? 'aSound.sound = NSSound (contentsOfURL: aSound.path)!' – brimstone

+0

Ja, aber für diesen Fall ist '.sound' niemals null. – Matt

+0

Ehrlich gesagt glaube ich nicht, dass Sie Sounddaten von einem NSSound erhalten können. – brimstone

Antwort

2

Also ich habe keine öffentliche Schnittstelle, um die URL von einem NSSound Objekt zu bekommen, also ging ich graben durch die private headers um zu sehen, was ich finden konnte. Stellt sich heraus, gibt es private Instanz Methode url und _url, die die URL eines NSSound zurückgeben. Vermutlich sind dies Getter für eine NSURL Ivar oder Eigentum.

Mit Objective-C wäre das einfach: Wir würden die Methoden einfach zu einer neuen Schnittstelle oder Erweiterung hinzufügen. Mit reinen Swift Dinge ein wenig komplizierter sind, und müssen wir die Accessor über ein Objective-C-Protokoll entlarven:

@objc protocol NSSoundPrivate { 
    var url: NSURL? { get } 
} 

Da url ist eine Instanzmethode, können Sie bessere Ergebnisse mit func url() -> NSURL? statt mit einer Variablen erhalten. Ihr Kilometerstand kann variieren: Die Verwendung einer var zum Emulieren des Verhaltens einer schreibgeschützten Eigenschaft schien für mich zu funktionieren.

schrieb ich einen neuen Komfort Initialisierer in einer Erweiterung auf AVAudioPlayer:

extension AVAudioPlayer { 
    convenience init?(sound: NSSound) throws {  
     let privateSound = unsafeBitCast(sound, NSSoundPrivate.self)  
     guard let url = privateSound.url else { return nil } 
     do { 
      try self.init(contentsOfURL: url) 
     } catch { 
      throw error 
     } 
    } 
} 

Verbrauch:

let url = NSURL(...)  
if let sound = NSSound(contentsOfURL: url, byReference: true) { 
    do { 
     let player = try AVAudioPlayer(sound: sound) 
     player?.play() 
    } catch { 
     print(error) 
    }   
} 

Nach dem Versuch, etwas zu NSData in dem ivars, Instanzmethoden im Zusammenhang zu finden, und die Eigenschaften ein NSSound, ich bin zu der Schlussfolgerung gekommen, dass der Datenteil von allem, was Sie verwenden, um ein NSSound zu initialisieren, irgendwo in der Implementierung der Klasse verschleiert wird und nicht wie das verfügbar ist URL ist.