2016-04-27 11 views
11

Mam niektóre obiekty NSSound, które chciałbym przekonwertować na instancje AVAudioPlayer. Mam ścieżki plików (NSURL s) powiązane z obiektami NSSound, ale oryginalny plik może nie istnieć. Oto co mam do tej pory:Konwersja NSSound na 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: ...)? 
} 

Jak przekonwertować obiekt NSSound do instancji AVAudioPlayer?

+0

Jeśli plik audio nie istnieje, czy ta linia nie zawiesi się? 'aSound.sound = NSSound (contentsOfURL: aSound.path)!' – brimstone

+0

Tak, ale w tym przypadku '.sound' nigdy nie jest puste. – Matt

+0

Szczerze mówiąc, nie sądzę, że można uzyskać dane dźwiękowe z NSSound. – brimstone

Odpowiedz

2

Więc nie widziałem interfejsu publicznego, aby uzyskać adres URL z obiektu NSSound, więc poszedłem przebić się przez private headers, aby zobaczyć, co mogłem znaleźć. Okazuje się, że istnieją prywatne metody instancji url i _url, które zwracają adres URL NSSound. Prawdopodobnie są to moduły pobierające dla ivar lub własności.

Z Objective-C byłoby to łatwe: wystarczyłoby dodać metody do nowego interfejsu lub rozszerzenia. Z czystymi Swift rzeczy są trochę trudniejsze, a my musimy narażać akcesor za pośrednictwem protokołu Objective-C:

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

Od url jest metoda instancji, można uzyskać lepsze wyniki z func url() -> NSURL? zamiast używać zmiennej. Twój przebieg może się różnić: używanie var do emulowania zachowania właściwości tylko do odczytu wydaje się działać dla mnie.

pisałem nową ogólnospożywczy inicjator jej przedłużenia na 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 
     } 
    } 
} 

Zastosowanie:

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

Po próbując znaleźć coś związanego NSData w Ivars, metody instancji i właściwości NSSound, doszedłem do wniosku, że część danych jakiejkolwiek używasz do zainicjowania NSSound jest zaciemniana gdzieś w implementacji klasy i nie jest dostępna tak jak URL to.