Construiți un player MP3 cu fundația AV

Ce veți crea

AV Foundation este un cadru pentru lucrul cu medii audio și vizuale pe iOS și OSX. Folosind AV Foundation, puteți reda, captura și codifica suporturi media. Este un cadru destul de extins și în scopul acestui tutorial ne vom concentra pe partea audio. În mod specific, vom folosi AVAudioPlayer clasă pentru a reda fișiere MP3.

Proiect de inițiere

Am oferit un proiect demarat, care are toate acțiunile și punctele de desfacere deja configurate și cu metodele adecvate stubate. Clasele pe care le folosește proiectul, sunt deja stubate, astfel încât să ne putem arunca cu grijă în cod. Puteți descărca proiectul starter de la GitHub.

1. Legarea cadrului Fundației AV

Înainte de a putea utiliza Fundația AV, trebuie să conectați proiectul la cadru. În Project Navigator, asigurați-vă că proiectul dvs. este selectat. Sub General , mergeți la Cadrele și bibliotecile asociate și de acolo alegeți AVFoundation.framework.

2. FileReader Clasă

În proiectul de pornire, veți găsi un fișier numit FileReader.swift. Deschideți acest fișier pentru a vedea conținutul acestuia.

importați clasa UIKit FileReader: NSObject 

Acesta este un simplu stub al clasei pe care o vom folosi pentru a citi fișierele de pe disc. Moșteneste de la NSObject. Vom implementa o metodă, readFiles, care va fi o metodă de tip. Tipurile de metode vă permit să apelați o metodă pe clasa însăși, tipul, spre deosebire de o instanță a clasei. Mai jos este implementarea readFiles metodă.

class func readFiles () -> [String] returnați NSBundle.mainBundle () pathsForResourcesOfType ("mp3", inDirectory: nil) ca! [String]

Banda principală conține codul și resursele pentru proiectul dvs. și aici găsim MP3-urile. Folosim metoda pathsForResourcesOfType (_: inDirectory :) , care returnează o matrice care conține numele de cale pentru tipul de resursă specificat. În acest caz, căutăm tipul "Mp3". Pentru că nu suntem interesați de un anumit director, intrăm zero.

Această clasă va fi utilizată de către Mp3 player clasa, pe care vom lucra la următoarea.

3. Mp3 player Clasă

În continuare, deschideți-vă MP3Player.swift și vizualizați conținutul acestuia.

import import UIKit clasă AVFoundation MP3Player: NSObject, AVAudioPlayerDelegate 

Observați că adoptăm AVAudioPlayerDelegate protocol. Acest protocol declară o serie de metode utile, dintre care una este audioPlayerDidFinishPlaying (_: cu succes :). Prin implementarea audioPlayerDidFinishPlaying (_: cu succes :) , vom fi anunțați când sunetul a terminat de redat.

Pasul 1: Proprietăți

Adăugați următoarele la MP3Player.swift.

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

jucător proprietate va fi o instanță a AVAudioPlayer clasa pe care o vom folosi pentru a juca, a întrerupe și a opri MP3-urile. currentTrackIndex variabila ține evidența jocurilor MP3 care se joacă în prezent. În cele din urmă, piese variabila va fi o serie de căi spre lista de MP3-uri care sunt incluse în pachetul aplicației.

Pasul 2: init

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

În timpul inițializării, invocăm FileReader„s readFiles pentru a prelua căile MP3 și pentru a stoca această listă în piese matrice. Deoarece acesta este un inițializator desemnat, trebuie să sunăm init metoda superclasei. În cele din urmă, sunăm queueTrack, pe care o vom scrie în continuare.

Pasul 3: queueTrack

Adăugați următoarea implementare pentru queueTrack metoda pentru a Mp3 player clasă.

func queueTrack () if (player! = nil) player = nil var eroare: NSError? lasă url = NSURL.fileURLWithPath (urmărește [currentTrackIndex] ca String) player = AVAudioPlayer (contentsOfURL: url, eroare: & eroare) ifError = eroare // SHOW ALERT SAU NICIODATĂ altceva player? .delegate = self player? prepareToPlay ()

Pentru că vom institui un nou AVAudioPlayer de exemplu, de fiecare dată când se numește această metodă, facem puțină gospodărie prin setare jucător la zero.

Declarăm un opțional NSError și o constantă URL-ul. Noi invocăm fileURLWithPath (_ :) pentru a prelua calea către MP3-ul curent și pentru a stoca valoarea în URL-ul. Noi trecem piese array ca parametru folosind currentTrackIndex ca indicele. Amintiți-vă că matricea pieselor conține căile spre MP3, nu o referință la fișierele MP3.

Pentru a instantiza jucător, noi trecem URL-ul constantă și eroare variabilă în AVAudioPlayera inițializatorului. Dacă inițializarea se întâmplă să eșueze, eroare variabila este populate cu o descriere a erorii.

Dacă nu întâlnim o eroare, setăm delegatul jucătorului de sine și invocați prepareToPlay pe player. prepareToPlay metoda preloads tampoanele și achiziționează hardware audio, care minimizează orice întârziere atunci când apelează Joaca metodă.

Pasul 4: Joaca

Joaca metoda verifică mai întâi pentru a vedea dacă audio-ul este deja redat sau nu prin verificarea numelui potrivit joc proprietate. Dacă sunetul nu se redă, acesta invocă Joaca metodă a jucător proprietate.

 func play () dacă jucătorul? .playing == false player? .play ()

Pasul 5: Stop

Stop metoda verifică mai întâi dacă playerul audio este deja redat. Dacă este, invocă Stop și stabilește metoda ora curentă proprietate la 0. Când invocați Stop metoda, aceasta oprește doar sunetul. Nu restabilește sunetul la început, motiv pentru care trebuie să îl facem manual.

func stop () dacă jucătorul? .playing == true player? .stop () player? .currentTime = 0

Pasul 6: pauză

La fel ca și Stop , verificăm mai întâi să vedem dacă redarea audio este redată. Dacă este, invocăm pauză metodă.

 functie pauza () daca jucatorul? .playing == true player? .pause ()

Pasul 7: Următorul cântec

nextSong (_: Bool) metoda cade în sus cântecul următor și, dacă jucătorul joacă, cânte cântecul respectiv. Nu vrem ca următoarea melodie să fie redată dacă playerul este întrerupt. Cu toate acestea, această metodă este, de asemenea, numită atunci când o melodie termină să se joace. În acest caz, dorim să jucăm următorul cântec, care este parametrul songFinishedPlaying este pentru.

func nextSong (cântecFinishedPlaying: Bool) var playerWasPlaying = false dacă jucătorul? .playing == true player? .stop () playerWasPlaying = true currentTrackIndex ++ dacă currentTrackIndex> = tracks.count currentTrackIndex = 0 queueTrack () if playerWasPlaying | | songFinishedPlaying player? .play ()

playerWasPlaying variabila este utilizată pentru a spune dacă jucătorul a fost sau nu jucat atunci când a fost invocată această metodă. Dacă piesa se joacă, vom invoca Stop metoda pe jucător și stabilit playerWasPlaying la Adevărat.

Apoi, noi incrementăm currentTrackIndex și verificați dacă este mai mare sau egală cu tracks.count. numara proprietatea unei matrice ne dă numărul total de elemente din matrice. Trebuie să fim siguri că nu încercăm să accesăm un element care nu există în piese matrice. Pentru a preveni acest lucru, am stabilit currentTrackIndex înapoi la primul element al matricei, dacă este cazul.

În cele din urmă, invocăm queueTrack pentru a primi următoarea melodie și pentru a reda melodia respectivă, dacă vreți playerWasPlaying sau songFinishedPlaying este Adevărat.

Pasul 8: previousSong

previousSong metoda funcționează foarte asemănătoare Următorul cântec. Singura diferență este că noi decrementăm currentTrackIndex și verificați dacă este egal cu 0. Dacă este, îl setăm la indexul ultimului element din matrice.

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

Utilizând ambele Următorul cântec și previousSong metode, suntem capabili de a naviga prin toate MP3-urile și de a începe peste când ajungem la începutul sau la sfârșitul listei.

Pasul 9: getCurrentTrackName

getCurrentTrackName metoda primește numele fișierului MP3 fără extensie.

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

Avem o referință la ceea ce folosește MP3-ul curent piese [currentTrackIndex]. Amintiți-vă, totuși, că acestea sunt căile către MP3-uri și nu la fișierele în sine. Calele sunt destul de lungi, pentru că este calea completă către fișierele MP3.

Pe mașina mea, de exemplu, primul element al mașinii piese array este egal cu "/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"Această cale ar fi diferită pe un dispozitiv real, desigur.

Avem un șir mare care conține calea către MP3, dar vrem doar numele MP3-ului în sine. NSString clasa definește două proprietăți care ne pot ajuta. După cum sugerează și numele, lastPathComponent proprietatea returnează ultima componentă a unei căi. După cum probabil ați ghicit, stringByDeletingPathExtension proprietatea elimină extensia.

Pasul 10: getCurrentTimeAsString

getCurrentTimeAsString utilizează metoda ora curentă proprietate a jucător instanță și o returnează ca un șir citit de om (de ex., 01:02).

 funcția get () = var = secunde = var secunde = 0 var minute = 0 dacă timpul = player =. : "% 0.2d:% 0.2d", minute, secunde)

ora curentă proprietatea este de tip NSTimeInterval, care este doar o typealias Pentru o Dubla. Folosim niște matematici pentru a obține secunde și minute, asigurându-ne că vom converti timp la un Int deoarece trebuie să lucrăm cu numere întregi. Dacă nu sunteți familiarizat cu operatorul rămas (%), acesta găsește restul după divizarea unui număr de altul. În cazul în care timp variabila era egală cu 65, atunci secunde ar fi egal cu 5 pentru că noi folosim 60.

Pasul 11: getProgress

getProgress metoda este folosită de UIProgressView exemplu pentru a da o indicație despre cât de mult a jucat MP3-ul. Acest progres este reprezentat de o valoare de la 0.0 la 1.0 ca Pluti.

 func getProgress () -> Float var theCurrentTime = 0.0 var theCurrentDuration = 0.0 dacă permite curentTime = player? .currentTime, duration = player? .duration theCurrentTime = currentTime theCurrentDuration = duration return Float (CurrentTime /CurrentDuration)

Pentru a obține această valoare, împărțim jucător„s ora curentă proprietate de către jucător„s durată proprietăți, stocăm aceste valori în variabile theCurrentTime și theCurrentDuration. Ca ora curentă, durată proprietatea este de tip NSTimeInterval și reprezintă durata cantecului în câteva secunde.

Pasul 12: setVolume

setVolume (_: float) metoda invocă setVolume metodă a jucător instanță.

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

Pasul 13: audioPlayerDidFinishPlaying (_: cu succes :)

audioPlayerDidFinishPlaying (_: cu succes :) metoda este o metodă a AVAudioPlayerDelegate protocol. Această metodă ia ca parametri AVAudioPlayer instanță și booleană. Booleanul este setat la Adevărat dacă playerul audio a terminat de redat melodia curentă.

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

Dacă melodia a terminat cu succes redarea, sunăm Următorul cântec metodă, care trece Adevărat deoarece cântecul a terminat să joace singur.

Acest lucru completează Mp3 player clasă. O vom revizui puțin mai tîrziu, după ce am implementat acțiunile ViewController clasă.

4. ViewController Clasă

Deschis ViewController.swift și vizualizați conținutul acestuia.

mport Import UIKit AVFoundation clasa ViewController: UIViewController var mp3Player: MP3Player? var timer: NSTimer? @IBOutlet slab var trackName: UILabel! @IBOutlet slab var trackTime: UILabel! @ IBOutlet slab var progresBar: UIProgressView! override function viewDidLoad () super.viewDidLoad () @IBAction func playSong (expeditor: AnyObject)  @IBAction func stopSong (expeditor: AnyObject)  @IBAction func pauseSong (expeditor: AnyObject)  @IBAction func playNextSong expeditor: AnyObject)  @IBAction func setVolume (expeditor: UISlider)  @IBAction func playPreviousSong (expeditor: AnyObject)  override func didReceiveMemoryWarning () super.didReceiveMemoryWarning () // Elimina orice resurse care pot fi recreate. 

mp3 player variabila este o instanță a Mp3 player pe care am implementat-o ​​mai devreme. timer variabila va fi utilizată pentru a actualiza trackTime și bara de progres vizionează în fiecare secundă.

În următorii pași, vom implementa acțiunile ViewController clasă. Dar, mai întâi, ar trebui să instanțiăm Mp3 player instanță. Actualizați punerea în aplicare a viewDidLoad așa cum se arată mai jos.

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

Pasul 1: playSong (_: AnyObject)

Introduceți următoarele în playSong (_: AnyObject) metodă.

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

În această metodă, invocăm Joaca metoda pe mp3 player obiect. Suntem într-un moment în care putem începe testarea aplicației acum. Rulați aplicația și apăsați butonul de redare. Cântecul ar trebui să înceapă să joace.

Pasul 2: stopSong (_: AnyObject)

stopSong (_: AnyObject) metoda invocă metoda de oprire pe mp3 player obiect.

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

Rulați din nou aplicația și atingeți butonul de redare. Acum ar trebui să puteți opri melodia prin atingerea butonului stop.

Pasul 3: pauzăSong (_: AnyObject)

După cum probabil ați ghicit, pauzăSong (_: AnyObject) metoda invocă pauză metoda pe mp3 player obiect.

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

Pasul 4: playNextSong (_: AnyObject)

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

În playNextSong (_: AnyObject), noi invocăm Următorul cântec metoda pe mp3 player obiect. Rețineți că trecem fals ca parametru, deoarece piesa nu a terminat să joace pe cont propriu. Pornim manual următoarea melodie apăsând butonul următor.

Pasul 5: anteriorSong (_: AnyObject)

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

După cum puteți vedea, punerea în aplicare a anteriorSong (_: AnyObject) metoda este foarte asemănătoare cu cea a lui nextSong (_: AnyObject). Toate butoanele playerului MP3 ar trebui să funcționeze acum. Dacă nu ați testat încă aplicația, acum ar fi un moment bun pentru a vă asigura că totul funcționează conform așteptărilor.

Pasul 6: setVolume (_: UISlider)

setVolume (_: UISlider) metoda invocă setVolume metoda pe mp3 player obiect. Proprietatea volumului este de tip Pluti. Valoarea variază de la 0.0 la 1.0. UISlider obiect este configurat cu 0.0 ca valoare minimă și 1.0 ca valoare maximă.

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

Rulați aplicația încă o dată și jucați cu glisorul de volum pentru a testa că totul funcționează corect.

Pasul 7: startTimer

startTimer metoda instantiates un nou NSTimer instanță.

 func startTimer () timer = NSTimer.scheduledTimerWithTimeInterval (1.0, țintă: self, selector: Selector ("updateViewsWithTimer:"), userInfo: nil, repetă: true)

scheduledTimerWithTimeInterval (_: țintă: selector: userinfo: se repetă :) inițializatorul ia ca parametri numărul de secunde dintre arderea temporizatorului, obiectul la care se poate apela o metodă specificată de selector, metoda care se numește când se declanșează cronometrul, o opțiune userinfo dicționar și dacă se repetă sau nu timerul până când acesta este invalidat.

Folosim o metodă numită updateViewsWithTimer (_: NSTimer) ca selector, așa că vom crea asta în continuare.

Pasul 8: updateViewsWithTimer (_: NSTimer)

updateViewsWithTimer (_: NSTimer) metoda solicită updateViews metodă pe care o vom implementa în etapa următoare.

func updateViewsWithTimer (Timer: NSTimer) updateViews ()

Pasul 9: updateViews

updateViews actualizează metoda trackTime și bara de progres vizualizari.

func updateViews () trackTime.text = mp3Player? .getCurrentTimeAsString () dacă permiteți progresul = mp3Player? .getProgress () progressBar.progress = progress 

text proprietatea trackTime este actualizat cu ora curentă proprietate, formatat ca un șir prin invocarea getCurrentTimeAsString metodă. Declarăm o constantă progres folosind mp3 player„s getProgress metoda și setul progressBar.progress folosind acea constantă.

Pasul 10: Cablarea cronometrului

Acum trebuie să sunăm startTimer în locurile potrivite. Trebuie să pornim cronometrul în playSong (_: AnyObject) metodă, playNextSong (_: AnyObject) metoda, și playPreviousSong (_: AnyObject) metodă.

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

Pasul 11: Oprirea cronometrului

De asemenea, trebuie să oprim timer când sunt apăsate butoanele de întrerupere și oprire. Puteți opri timer obiecte invocând invalida metoda pe NSTimer instanță.

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

Pasul 12: setTrackName

setTrackName metoda stabilește text proprietatea trackName invocând getCurrentTrackName pe mp3 player obiect.

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

Pasul 13: setupNotificationCenter

Când o melodie termină să fie redată, ar trebui să afișeze automat numele următorului cântec și să înceapă să cânte melodia respectivă. De asemenea, când utilizatorul apasă butoanele de redare, următor sau anterioare, setTrackName trebuie invocată metoda. Locul ideal pentru a face acest lucru este queueTrack metodă a Mp3 player clasă.

Avem nevoie de o modalitate de a avea Mp3 player clasa spune ViewController clasa pentru a invoca setTrackName metodă. Pentru a face asta, vom folosi NSNotificationCenter clasă. Centrul de notificare oferă o modalitate de difuzare a informațiilor în cadrul unui program. Prin înregistrarea ca observator la centrul de notificare, un obiect poate primi aceste emisiuni și poate efectua o operație. O altă modalitate de a îndeplini această sarcină ar fi utilizarea modelului de delegare.

Adăugați următoarea metodă la ViewController clasă.

 func setNotificationCenter () NSNotificationCenter.defaultCenter (). addObserver (auto, selector: "setTrackName", nume: "SetTrackNameText", obiect: nil) 

Mai întâi obținem o referință la centrul implicit de notificare. Apoi invocăm addObserver (_: selector: Nume: obiect :) pe centrul de notificare. Această metodă acceptă patru parametri:

  • obiectul înregistrării ca observator, de sine în acest caz
  • mesajul care va fi trimis către observator la afișarea notificării
  • numele notificării pentru înregistrarea observatorului
  • obiectul ale cărui notificări vrea să le primească observatorul

Trecând zero ca ultimul argument, ascultăm pentru fiecare notificare care are un nume de SetTrackNameText.

Acum trebuie să apelăm această metodă în controlerul de vizualizare viewDidLoad metodă.

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

Pasul 14: Detașarea notificării

Pentru a posta notificarea, vom invoca postNotificationName (_: obiect :) la centrul implicit de notificare. Așa cum am menționat mai devreme, vom face acest lucru în queueTrack metodă a Mp3 player clasă. Deschis MP3Player.swift și actualizați queueTrack așa cum se arată mai jos.

 func queueTrack () if (player! = nil) player = nil var eroare: NSError? lasă url = NSURL.fileURLWithPath (urmărește [currentTrackIndex] ca String) player = AVAudioPlayer (contentsOfURL: url, eroare: & eroare) ifError = eroare // SHOW ALERT SAU NICIODATĂ altceva player? .delegate = self player? NNotificationName ("SetTrackNameText", obiect: nil) NNotificationCenter.defaultCenter ()

Dacă testați aplicația acum și lăsați o melodie să se joace tot timpul, ar trebui să începeți să redați automat următoarea melodie. Dar s-ar putea să te întrebi de ce numele cântecului nu apare în timpul primei melodii. init metodă a Mp3 player clasa apeluri queueTrack dar din moment ce nu a terminat inițializarea, nu are niciun efect.

Tot ce trebuie să facem este să sunăm manual setTrackName după ce inițializăm mp3 player obiect. Adăugați următorul cod la viewDidLoad metoda în ViewController.swift.

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

Veți observa că am sunat și eu updateViews metodă. În acest fel, jucătorul prezintă un timp de 0:00 la inceput. Dacă testați acum aplicația, ar trebui să aveți un MP3 player complet funcțional.

Concluzie

Acesta a fost un tutorial destul de lung, dar acum aveți un MP3 player funcțional pentru a construi și extinde. O sugestie este aceea de a permite utilizatorului să aleagă o melodie pe care să o joace prin implementarea unui UITableView sub jucator. Vă mulțumim pentru lectură și sper că ați învățat ceva util.


Cod