Costruisci un lettore MP3 con AV Foundation

Cosa starai creando

Fondazione AV è un framework per lavorare con supporti audio e video su iOS e OSX. Utilizzando AV Foundation, è possibile riprodurre, acquisire e codificare i media. È una struttura piuttosto ampia e per lo scopo di questo tutorial ci concentreremo sulla parte audio. Nello specifico, useremo il AVAudioPlayer classe per riprodurre file MP3.

Progetto iniziale

Ho fornito un progetto iniziale con tutte le azioni e le prese già configurate e con i metodi appropriati estrapolati. Anche le classi utilizzate dal progetto sono già state eliminate, così possiamo entrare nel codice. Puoi scaricare il progetto iniziale da GitHub.

1. Collegamento di AV Foundation Framework

Prima di poter utilizzare AV Foundation, devi collegare il progetto al framework. Nel Project Navigator, assicurati che il tuo progetto sia selezionato. Sotto il Generale scheda, vai a Quadri e biblioteche collegati e da lì scegli AVFoundation.framework.

2. FileReader Classe

Nel progetto iniziale, troverai un file chiamato FileReader.swift. Apri questo file per vederne il contenuto.

importare la classe UIKit FileReader: NSObject 

Questo è uno stub semplice della classe che useremo per leggere i file dal disco. Da esso eredita NSObject. Implementeremo un metodo, readFiles, quale sarà un metodo di tipo. I metodi Type consentono di chiamare un metodo sulla classe stessa, il tipo, al contrario di un'istanza della classe. Di seguito è l'implementazione del readFiles metodo.

class func readFiles () -> [String] return NSBundle.mainBundle (). pathsForResourcesOfType ("mp3", inDirectory: nil) as! [Stringa]

Il pacchetto principale contiene il codice e le risorse per il tuo progetto, ed è qui che troveremo gli MP3. Usiamo il metodo pathsForResourcesOfType (_: inDirectory :) metodo, che restituisce una matrice contenente i nomi di percorso per il tipo di risorsa specificato. In questo caso, stiamo cercando il tipo "Mp3". Perché non siamo interessati a una directory specifica, passiamo zero.

Questa classe sarà utilizzata da Lettore mp3 classe, su cui lavoreremo successivamente.

3. Lettore mp3 Classe

Avanti, aperto MP3Player.swift e vedere il suo contenuto.

import UIKit import AVFoundation class MP3Player: NSObject, AVAudioPlayerDelegate 

Si noti che stiamo adottando il AVAudioPlayerDelegate protocollo. Questo protocollo dichiara un numero di metodi utili, uno dei quali è audioPlayerDidFinishPlaying (_: con successo :). Implementando il audioPlayerDidFinishPlaying (_: con successo :) metodo, verremo avvisati al termine della riproduzione dell'audio.

Passaggio 1: Proprietà

Aggiungi il seguente a MP3Player.swift.

class MP3Player: NSObject, AVAudioPlayerDelegate var player: AVAudioPlayer? var currentTrackIndex = 0 tracce var: [String] = [String] ()

Il giocatore la proprietà sarà un'istanza del AVAudioPlayer classe, che useremo per riprodurre, mettere in pausa e fermare gli MP3. Il currentTrackIndex variabile tiene traccia di quale MP3 è attualmente in riproduzione. Finalmente, il brani variabile sarà una matrice dei percorsi all'elenco di MP3 inclusi nel pacchetto dell'applicazione.

Passo 2: dentro

override init () tracks = FileReader.readFiles () super.init () queueTrack (); 

Durante l'inizializzazione, invochiamo il FileReader'S readFiles metodo per recuperare i percorsi degli MP3 e memorizzare questo elenco in brani array. Poiché questo è un inizializzatore designato, dobbiamo chiamare il dentro metodo della superclasse. Infine, chiamiamo queueTrack, che scriverò in seguito.

Passaggio 3: queueTrack

Aggiungi la seguente implementazione per queueTrack metodo per il Lettore mp3 classe.

func queueTrack () if (player! = nil) player = nil var error: NSError? let url = NSURL.fileURLWithPath (tracce [currentTrackIndex] as String) player = AVAudioPlayer (contentsOfURL: url, error: & error) se let hasError = error // SHOW ALERT O SOMETHING else player? .delegate = self player ?. prepareToPlay ()

Perché creeremo una nuova istanza AVAudioPlayer Ogni volta che viene chiamato questo metodo, facciamo un po 'di pulizie impostando giocatore a zero.

Dichiariamo un facoltativo NSError e una costante url. Invochiamo fileURLWithPath (_ :) per recuperare il percorso verso l'attuale MP3 e memorizzarne il valore url. Stiamo passando il brani array come parametro usando currentTrackIndex come pedice. Ricorda che l'array delle tracce contiene i percorsi degli MP3, non un riferimento ai file MP3 stessi.

Per istanziare il giocatore, noi passiamo il url costante e errore variabile nel AVAudioPlayerl'inizializzatore. Se l'inizializzazione dovesse fallire, il errore la variabile è popolata con una descrizione dell'errore.

Se non riscontriamo un errore, impostiamo il delegato del giocatore su se stesso e invoca il prepareToPlay metodo sul giocatore. Il prepareToPlay metodo precarica i buffer e acquisisce l'hardware audio, che riduce al minimo qualsiasi ritardo durante la chiamata giocare metodo.

Passaggio 4: giocare

Il giocare il metodo controlla prima se l'audio sta già riproducendo o meno controllando il nome corretto giocando proprietà. Se l'audio non viene riprodotto, richiama il giocare metodo del giocatore proprietà.

 func play () if player? .playing == false player? .play ()

Passaggio 5: Stop

Il Stop il metodo controlla innanzitutto se il lettore audio sta già suonando. Se lo è, invoca il Stop metodo e imposta il ora attuale proprietà a 0. Quando invochi il Stop metodo, interrompe semplicemente l'audio. Non ripristina l'audio all'inizio, ecco perché è necessario farlo manualmente.

func stop () if player? .playing == true player? .stop () player? .currentTime = 0

Passaggio 6: pausa

Proprio come il Stop metodo, prima controlliamo per vedere se il lettore audio sta giocando. Se lo è, invochiamo il pausa metodo.

 func pause () if player? .playing == true player? .pause ()

Step 7: prossima canzone

Il nextSong (_: Bool) il metodo accoda il brano successivo e, se il lettore sta suonando, suona quella canzone. Non vogliamo che il brano successivo venga riprodotto se il lettore è in pausa. Tuttavia, questo metodo viene anche chiamato quando una canzone termina la riproduzione. In tal caso, vogliamo suonare la canzone successiva, che è il parametro songFinishedPlaying è per.

func nextSong (songFinishedPlaying: Bool) var playerWasPlaying = false se player? .playing == true player? .stop () playerWasPlaying = true currentTrackIndex ++ se currentTrackIndex> = tracks.count currentTrackIndex = 0 queueTrack () se playerWasPlaying | | songFinishedPlaying player? .play ()

Il playerWasPlaying variabile è usata per dire se il giocatore stava giocando o meno quando questo metodo è stato invocato. Se la canzone stava suonando, invochiamo il Stop metodo sul giocatore e impostare playerWasPlaying a vero.

Successivamente, incrementiamo il currentTrackIndex e controlla se è maggiore o uguale a tracks.count. Il contare la proprietà di un array ci fornisce il numero totale di elementi nell'array. Dobbiamo essere sicuri di non provare ad accedere ad un elemento che non esiste nel brani array. Per evitare ciò, abbiamo impostato currentTrackIndex torna al primo elemento dell'array se questo è il caso.

Infine, invochiamo queueTrack per preparare il brano successivo e suonare quella canzone, se necessario playerWasPlaying o songFinishedPlaying è vero.

Passaggio 8: previousSong

Il previousSong metodo funziona molto simile a prossima canzone. L'unica differenza è che decrementiamo il currentTrackIndex e controlla se è uguale a 0. Se lo è, lo impostiamo sull'indice dell'ultimo elemento dell'array.

func previousSong () var playerWasPlaying = false se player? .playing == true player? .stop () playerWasPlaying = true currentTrackIndex-- se currentTrackIndex < 0  currentTrackIndex = tracks.count - 1  queueTrack() if playerWasPlaying  player?.play()   

Utilizzando entrambi i prossima canzone e previousSong metodi, siamo in grado di scorrere tutti gli MP3 e ricominciare da capo quando raggiungiamo l'inizio o la fine dell'elenco.

Passaggio 9: getCurrentTrackName

Il getCurrentTrackName metodo ottiene il nome del MP3 senza l'estensione.

func getCurrentTrackName () -> String let trackName = tracks [currentTrackIndex] .lastPathComponent.stringByDeletingPathExtension return trackName

Otteniamo un riferimento a qualunque sia l'attuale MP3 utilizzando tracce [currentTrackIndex]. Ricorda, tuttavia, che questi sono i percorsi degli MP3 e non i file stessi. I percorsi sono piuttosto lunghi, perché è il percorso completo per i file MP3.

Sulla mia macchina, ad esempio, il primo elemento del brani array è uguale a "/Users/jamestyner/Library/Developer/CoreSimulator/Devices/80C8CD34-22AE-4F00-862E-FD41E2D8D6BA/data/Containers/Bundle/Application/3BCF8543-BA1B-4997-9777-7EC56B1C4348/MP3Player.app/Lonesome Road Blues.mp3"Questo percorso sarebbe diverso su un dispositivo reale, naturalmente.

Abbiamo una grande stringa che contiene il percorso per l'MP3, ma vogliamo solo il nome dello stesso MP3. Il NSString la classe definisce due proprietà che possono aiutarci. Come suggerisce il nome, il lastPathComponent proprietà restituisce l'ultimo componente di un percorso. Come avrai intuito, il stringByDeletingPathExtension proprietà rimuove l'estensione.

Passaggio 10: getCurrentTimeAsString

Il getCurrentTimeAsString il metodo usa il ora attuale proprietà del giocatore istanza e la restituisce come una stringa leggibile dall'uomo (ad es., 01:02).

 func getCurrentTimeAsString () -> String var seconds = 0 var minuti = 0 se let time = player? .currentTime seconds = Int (tempo)% 60 minutes = (Int (tempo) / 60)% 60 return String (formato : "% 0.2d:% 0.2d", minuti, secondi)

Il ora attuale la proprietà è di tipo NSTimeInterval, che è solo un typealias per un Doppio. Usiamo un po 'di matematica per ottenere il secondi e minuti, assicurandoci di convertirci tempo ad Int dal momento che abbiamo bisogno di lavorare con numeri interi. Se non si ha familiarità con l'operatore rimanente (%), trova il resto dopo la divisione di un numero con un altro. Se la tempo la variabile era uguale a 65, poi secondi sarebbe uguale a 5 perché stiamo usando 60.

Passaggio 11: getProgress

Il getProgress il metodo è usato dal UIProgressView istanza per dare un'indicazione della quantità di MP3 riprodotta. Questo progresso è rappresentato da un valore da 0.0 a 1.0 come un Galleggiante.

 func getProgress () -> Float var theCurrentTime = 0.0 var theCurrentDuration = 0.0 se let currentTime = player? .currentTime, duration = player? .duration theCurrentTime = currentTime theCurrentDuration = duration return Float (theCurrentTime / theCurrentDuration)

Per ottenere questo valore, dividiamo il giocatore'S ora attuale proprietà dal giocatore'S durata proprietà, memorizziamo questi valori nelle variabili theCurrentTime e theCurrentDuration. Piace ora attuale, il durata la proprietà è di tipo NSTimeInterval e rappresenta la durata della canzone in secondi.

Passaggio 12: setVolume

Il setVolume (_: Float) il metodo invoca il setVolume metodo del giocatore esempio.

func setVolume (volume: Float) player? .volume = volume 

Step 13: audioPlayerDidFinishPlaying (_: con successo :)

Il audioPlayerDidFinishPlaying (_: con successo :) il metodo è un metodo di AVAudioPlayerDelegate protocollo. Questo metodo prende come parametri il AVAudioPlayer istanza e un booleano. Il booleano è impostato su vero se il lettore audio ha finito di suonare la canzone corrente.

func audioPlayerDidFinishPlaying (player: AVAudioPlayer, flag successfully: Bool) if flag == true nextSong (true)

Se la canzone ha terminato con successo la riproduzione, chiamiamo il prossima canzone metodo, passando dentro vero dal momento che la canzone ha terminato di suonare da sola.

Questo completa il Lettore mp3 classe. Lo rivedremo un po 'più tardi, dopo aver implementato le azioni del ViewController classe.

4. ViewController Classe

Aperto ViewController.swift e vedere il suo contenuto.

mport UIKit import AVFoundation class ViewController: UIViewController var mp3Player: MP3Player? var timer: NSTimer? @IBOutlet weak var trackName: UILabel! @IBOutlet weak var trackTime: UILabel! @IBOutlet weak var progressBar: UIProgressView! override func viewDidLoad () super.viewDidLoad () @IBAction func playSong (mittente: AnyObject)  @IBAction func stopSong (mittente: AnyObject)  @IBAction func pauseSong (mittente: AnyObject)  @IBAction func playNextSong ( mittente: AnyObject)  @IBAction func setVolume (mittente: UISlider)  @IBAction func playPreviousSong (mittente: AnyObject)  override func didReceiveMemoryWarning () super.didReceiveMemoryWarning () // Elimina le risorse che possono essere ricreate. 

Il lettore mp3 variabile è un'istanza di Lettore mp3 classe che abbiamo implementato in precedenza. Il Timer la variabile verrà utilizzata per aggiornare il file TrackTime e barra di avanzamento visualizzazioni ogni secondo.

Nei prossimi passi, implementeremo le azioni del ViewController classe. Ma prima, dovremmo istanziare il Lettore mp3 esempio. Aggiorna l'implementazione di viewDidLoad metodo come mostrato di seguito.

override func viewDidLoad () super.viewDidLoad () mp3Player = MP3Player () 

Passo 1: playSong (_: AnyObject)

Inserisci quanto segue in playSong (_: AnyObject) metodo.

@IBAction func playSong (mittente: AnyObject) mp3Player? .Play ()

In questo metodo, invochiamo il giocare metodo sul lettore mp3 oggetto. Siamo al punto in cui possiamo iniziare a testare l'app ora. Esegui l'app e premi il pulsante di riproduzione. La canzone dovrebbe iniziare a suonare.

Passo 2: stopSong (_: AnyObject)

Il stopSong (_: AnyObject) metodo richiama il metodo stop sul lettore mp3 oggetto.

 @IBAction func stopSong (mittente: AnyObject) mp3Player? .Stop ()

Esegui nuovamente l'app e tocca il pulsante Riproduci. Ora dovresti essere in grado di interrompere la canzone toccando il pulsante stop.

Passaggio 3: pauseSong (_: AnyObject)

Come avrai intuito, il pauseSong (_: AnyObject) il metodo invoca il pausa metodo sul lettore mp3 oggetto.

@IBAction func pauseSong (mittente: AnyObject) mp3Player? .Pause ()

Passaggio 4: playNextSong (_: AnyObject)

IBAction func playNextSong (mittente: AnyObject) mp3Player? .NextSong (false)

Nel playNextSong (_: AnyObject), invochiamo il prossima canzone metodo sul lettore mp3 oggetto. Nota che passiamo falso come parametro, perché la canzone non ha terminato di suonare da sola. Stiamo iniziando manualmente il brano successivo premendo il pulsante successivo.

Passaggio 5: previousSong (_: AnyObject)

 @IBAction func playPreviousSong (mittente: AnyObject) mp3Player? .PreviousSong ()

Come puoi vedere, l'implementazione di previousSong (_: AnyObject) il metodo è molto simile a quello di nextSong (_: AnyObject). Tutti i pulsanti del lettore MP3 dovrebbero essere funzionali ora. Se non hai ancora provato l'app, ora sarebbe il momento giusto per assicurarti che tutto funzioni come previsto.

Passaggio 6: setVolume (_: UISlider)

Il setVolume (_: UISlider) il metodo invoca il setVolume metodo sul lettore mp3 oggetto. La proprietà del volume è di tipo Galleggiante. Il valore varia da 0.0 a 1.0. Il UISlider oggetto è impostato con 0.0 come il suo valore minimo e 1.0 come il suo valore massimo.

 @IBAction func setVolume (mittente: UISlider) mp3Player? .SetVolume (sender.value)

Esegui l'app ancora una volta e gioca con il cursore del volume per verificare che tutto funzioni correttamente.

Step 7: startTimer

Il startTimer il metodo istanzia un nuovo NSTimer esempio.

 func startTimer () timer = NSTimer.scheduledTimerWithTimeInterval (1.0, target: self, selector: Selector ("updateViewsWithTimer:"), userInfo: nil, ripetizioni: true)

Il scheduledTimerWithTimeInterval (_: target: selettore: UserInfo: ripete :) l'inizializzatore prende come parametri il numero di secondi tra l'accensione del timer, l'oggetto a cui chiamare un metodo su specificato da selettore, il metodo che viene chiamato quando scatta il timer, un optional userInfo dizionario e se il timer si ripete o meno fino a quando non viene invalidato.

Stiamo usando un metodo chiamato updateViewsWithTimer (_: NSTimer) come selettore, quindi lo creeremo in seguito.

Passaggio 8: updateViewsWithTimer (_: NSTimer)

Il updateViewsWithTimer (_: NSTimer) metodo chiama il updateViews metodo, che implementeremo nel passaggio successivo.

func updateViewsWithTimer (theTimer: NSTimer) updateViews ()

Passaggio 9: updateViews

Il updateViews metodo aggiorna il TrackTime e barra di avanzamento visualizzazioni.

func updateViews () trackTime.text = mp3Player? .getCurrentTimeAsString () se let progresso = mp3Player? .getProgress () progressBar.progress = progress 

Il testo proprietà di TrackTime è aggiornato con il ora attuale proprietà, formattata come stringa invocando il getCurrentTimeAsString metodo. Dichiariamo una costante progresso usando il lettore mp3'S getProgress metodo e impostare progressBar.progress usando quella costante.

Passaggio 10: cablaggio del timer

Ora dobbiamo chiamare il startTimer metodo nei luoghi appropriati. Abbiamo bisogno di avviare il timer nel playSong (_: AnyObject) metodo, il playNextSong (_: AnyObject) metodo e il playPreviousSong (_: AnyObject) metodo.

@IBAction func playSong (mittente: AnyObject) mp3Player? .Play () startTimer () 
 @IBAction func playNextSong (mittente: AnyObject) mp3Player? .NextSong (false) startTimer () 
@IBAction func playPreviousSong (mittente: AnyObject) mp3Player? .PreviousSong () startTimer () 

Passaggio 11: arresto del timer

Dobbiamo anche fermare il Timer quando vengono premuti i pulsanti pausa e stop. Puoi fermare il Timer oggetto invocando il infirmare metodo sul NSTimer esempio.

@IBAction func stopSong (mittente: AnyObject) mp3Player? .Stop () updateViews () timer? .Invalidate () 
@IBAction func pauseSong (mittente: AnyObject) mp3Player? .Pause () timer? .Invalidate ()

Passaggio 12: setTrackName

Il setTrackName il metodo imposta il testo proprietà di trackname invocando getCurrentTrackName sul lettore mp3 oggetto.

func setTrackName () trackName.text = mp3Player? .getCurrentTrackName ()

Step 13: setupNotificationCenter

Quando una canzone finisce di suonare, dovrebbe mostrare automaticamente il nome del brano successivo e iniziare a suonare quella canzone. Inoltre, quando l'utente preme i pulsanti play, next o previous, il pulsante setTrackName il metodo dovrebbe essere invocato. Il posto ideale per farlo è il queueTrack metodo del Lettore mp3 classe.

Abbiamo bisogno di un modo per avere il Lettore mp3 la classe dice il ViewController classe per invocare il setTrackName metodo. Per farlo, useremo il NSNotificationCenter classe. Il centro di notifica fornisce un modo per trasmettere informazioni attraverso un programma. Registrandosi come osservatore con il centro di notifica, un oggetto può ricevere queste trasmissioni ed eseguire un'operazione. Un altro modo per eseguire questa operazione sarebbe utilizzare il modello di delega.

Aggiungi il seguente metodo al ViewController classe.

 func setupNotificationCenter () NSNotificationCenter.defaultCenter (). addObserver (self, selector: "setTrackName", nome: "SetTrackNameText", oggetto: nil) 

Prima otteniamo un riferimento al centro di notifica predefinito. Quindi invochiamo il addObserver (_: selettore: Nome: oggetto :) metodo sul centro di notifica. Questo metodo accetta quattro parametri:

  • l'oggetto che registra come l'osservatore, se stesso in questo caso
  • il messaggio che verrà inviato all'osservatore quando viene inviata la notifica
  • il nome della notifica per cui registrare l'osservatore
  • l'oggetto di cui l'osservatore desidera ricevere le notifiche

Entrando zero come ultimo argomento, ascoltiamo ogni notifica che ha un nome SetTrackNameText.

Ora dobbiamo chiamare questo metodo nel controller della vista viewDidLoad metodo.

override func viewDidLoad () super.viewDidLoad () mp3Player = MP3Player () setupNotificationCenter () 

Passaggio 14: registrazione della notifica

Per pubblicare la notifica, invochiamo il postNotificationName (_: oggetto :) metodo sul centro di notifica predefinito. Come ho detto prima, lo faremo nel queueTrack metodo del Lettore mp3 classe. Aperto MP3Player.swift e aggiorna il queueTrack metodo come mostrato di seguito.

 func queueTrack () if (player! = nil) player = nil var error: NSError? let url = NSURL.fileURLWithPath (tracce [currentTrackIndex] as String) player = AVAudioPlayer (contentsOfURL: url, error: & error) se let hasError = error // SHOW ALERT O SOMETHING else player? .delegate = self player ?. prepareToPlay () NSNotificationCenter.defaultCenter (). postNotificationName ("SetTrackNameText", object: nil)

Se provi l'app ora e fai suonare una canzone fino in fondo, dovrebbe iniziare a suonare automaticamente il brano successivo. Ma potresti chiederti perché il nome del brano non viene visualizzato durante la prima canzone. Il dentro metodo del Lettore mp3 la classe chiama il queueTrack metodo, ma poiché non ha terminato l'inizializzazione, non ha alcun effetto.

Tutto quello che dobbiamo fare è chiamare manualmente il setTrackName metodo dopo aver inizializzato il lettore mp3 oggetto. Aggiungi il seguente codice al viewDidLoad metodo in ViewController.swift.

 override func viewDidLoad () super.viewDidLoad () mp3Player = MP3Player () setupNotificationCenter () setTrackName () updateViews ()

Noterai che ho anche chiamato il updateViews metodo. In questo modo, il giocatore mostra un tempo di 00:00 all'inizio. Se testate l'app ora, dovreste avere un lettore MP3 pienamente funzionante.

Conclusione

Questo è stato un tutorial piuttosto lungo, ma ora hai un lettore MP3 funzionale da costruire ed espandere. Un suggerimento è quello di consentire all'utente di scegliere un brano da suonare implementando un UITableView sotto il giocatore. Grazie per la lettura e spero che tu abbia imparato qualcosa di utile.