Bouw een MP3-speler met AV Foundation

Wat je gaat creëren

AV Foundation is een raamwerk voor het werken met audio- en visuele media op iOS en OSX. Met AV Foundation kunt u media afspelen, vastleggen en coderen. Het is een vrij uitgebreid kader en voor het doel van deze tutorial zullen we ons concentreren op het audiogedeelte. Concreet zullen we de AVAudioPlayer klasse om MP3-bestanden af ​​te spelen.

Startersproject

Ik heb een startersproject geleverd waarbij alle acties en stopcontacten al zijn geconfigureerd en de juiste methoden zijn uitgewerkt. De klassen die het project gebruikt, zijn ook al uitgedost, zodat we direct in de code kunnen duiken. Je kunt het startersproject downloaden van GitHub.

1. Koppeling van het AV Foundation Framework

Voordat u AV Foundation kunt gebruiken, moet u het project koppelen aan het framework. In de Project Navigator, zorg ervoor dat uw project is geselecteerd. Onder de Algemeen tab, ga naar Gekoppelde kaders en bibliotheken en van daaruit kies je AVFoundation.framework.

2. FileReader Klasse

In het startersproject vindt u een bestand met de naam FileReader.swift. Open dit bestand om de inhoud te bekijken.

import UIKit-klasse FileReader: NSObject 

Dit is een eenvoudig gedeelte van de klas dat we zullen gebruiken om bestanden van schijf te lezen. Het erft van NSObject. We zullen een methode implementeren, readFiles, welke een typemethode zal zijn. Met typen kunt u een methode in de klasse zelf aanroepen, het type, in tegenstelling tot een instantie van de klasse. Hieronder is de implementatie van de readFiles methode.

class func readFiles () -> [String] retourneer NSBundle.mainBundle (). pathsForResourceOfType ("mp3", inDirectory: nihil) als! [Draad] 

De hoofdbundel bevat de code en bronnen voor uw project en hier zullen we de MP3's vinden. We gebruiken de methode pathsForResourcesOfType (_: inDirectory :) methode, die een array retourneert met de padnamen voor het opgegeven resourcetype. In dit geval zoeken we naar type "Mp3". Omdat we niet geïnteresseerd zijn in een specifieke directory, komen we binnen nul.

Deze klasse wordt gebruikt door de MP3 speler klas, waar we volgende aan zullen werken.

3. MP3 speler Klasse

Open vervolgens MP3Player.swift en bekijk de inhoud ervan.

importeer UIKit import AVFoundation class MP3Player: NSObject, AVAudioPlayerDelegate 

Merk op dat we de AVAudioPlayerDelegate protocol. Dit protocol declareert een aantal bruikbare methoden, waarvan er één is audioPlayerDidFinishPlaying (_: met succes :). Door de audioPlayerDidFinishPlaying (_: met succes :) methode, ontvangen we een melding wanneer het afspelen is voltooid.

Stap 1: Eigenschappen

Voeg het volgende toe aan MP3Player.swift.

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

De speler eigenschap wordt een instantie van de AVAudioPlayer klasse, die we zullen gebruiken om de MP3's af te spelen, te pauzeren en te stoppen. De currentTrackIndex variabele houdt bij welke MP3 momenteel wordt afgespeeld. eindelijk, de sporen variabele is een array van paden naar de lijst met MP3's die zijn opgenomen in de bundel van de toepassing.

Stap 2: in het

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

Tijdens de initialisatie roepen we de FileReader's readFiles methode om de paden van de MP3's op te halen en deze lijst op te slaan in de sporen matrix. Omdat dit een aangewezen initialisator is, moeten we de in het methode van de superklasse. Eindelijk bellen we queueTrack, die we hierna zullen schrijven.

Stap 3: queueTrack

Voeg de volgende implementatie toe voor de queueTrack methode om de MP3 speler klasse.

func queueTrack () if (player! = nil) player = nil var error: NSError? laat url = NSURL.fileURLWithPath (tracks [currentTrackIndex] als tekenreeks) player = AVAudioPlayer (contentsOfURL: url, error: & error) if let hasError = error // LAAT ALERT OF IETS else player? .delegate = self player ?. prepareToPlay ()

Omdat we een nieuw maken AVAudioPlayer elke keer dat deze methode wordt aangeroepen, doen we een beetje huishoudelijk door te stellen speler naar nul.

We verklaren een optioneel NSError en een constante url. We roepen fileURLWithPath (_ :) om het pad naar de huidige MP3 op te halen en de waarde op te slaan url. We passeren de sporen array als een parameter met currentTrackIndex als het subscript. Vergeet niet dat de array tracks de paden naar de MP3's bevat, en niet een verwijzing naar de MP3-bestanden zelf.

Om de speler, we passeren de url constant en fout verander in de AVAudioPlayer's initializer. Als de initialisatie toevallig mislukt, de fout variabele wordt gevuld met een beschrijving van de fout.

Als we geen fout tegenkomen, stellen we de gedelegeerde van de speler in zelf en roep de prepareToPlay methode op de speler. De prepareToPlay methode vooraf laadt de buffers en verwerft de audio-hardware, die elke vertraging minimaliseert bij het aanroepen van de spelen methode.

Stap 4: spelen

De spelen methode controleert eerst of het geluid al speelt of niet, door de toepasselijke naam te controleren spelen eigendom. Als het geluid niet wordt afgespeeld, roept het de spelen methode van de speler eigendom.

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

Stap 5: hou op

De hou op methode controleert eerst of de audiospeler al aan het spelen is. Als dat zo is, roept het de hou op methode en stelt de huidige tijd eigendom aan 0. Wanneer u de hou op methode, het stopt gewoon de audio. Het zet de audio niet terug naar het begin, daarom moeten we dit handmatig doen.

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

Stap 6: pauze

Net als de hou op methode, controleren we eerst of de audiospeler speelt. Als dat zo is, roepen we het pauze methode.

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

Stap 7: volgende liedje

De nextSong (_: Bool) methode maakt een wachtrij voor het volgende nummer en, als de speler speelt, wordt dat nummer afgespeeld. We willen niet dat het volgende nummer wordt afgespeeld als de speler is onderbroken. Deze methode wordt echter ook aangeroepen wanneer een nummer wordt afgespeeld. In dat geval willen we het volgende nummer spelen, wat de parameter is songFinishedPlaying is voor.

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

De playerWasPlaying variabele wordt gebruikt om aan te geven of de speler aan het spelen was toen deze methode werd aangeroepen. Als het nummer werd afgespeeld, roepen we het hou op methode op de speler En instellen playerWasPlaying naar waar.

Vervolgens verhogen we de currentTrackIndex en controleer om te zien of het groter dan of gelijk aan is tracks.count. De tellen eigenschap van een array geeft ons het totale aantal items in de array. We moeten zeker weten dat we niet proberen toegang te krijgen tot een element dat niet bestaat in de sporen matrix. Om dit te voorkomen, hebben we ingesteld currentTrackIndex terug naar het eerste element van de array als dat het geval is.

Ten slotte roepen we aan queueTrack om het volgende nummer klaar te maken en dat nummer af te spelen als een van beide playerWasPlaying of songFinishedPlaying is waar.

Stap 8: previousSong

De previousSong methode werkt erg op volgende liedje. Het enige verschil is dat we het currentTrackIndex en controleer of het gelijk is aan 0. Als dat zo is, stellen we het in op de index van het laatste element in de array.

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

Door beide te gebruiken volgende liedje en previousSong methodes, kunnen we alle MP3's doorlopen en opnieuw beginnen wanneer we het begin of het einde van de lijst bereiken.

Stap 9: getCurrentTrackName

De getCurrentTrackName methode krijgt de naam van de MP3 zonder de extensie.

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

We krijgen een verwijzing naar wat de huidige MP3 is door te gebruiken titels [currentTrackIndex]. Onthoud echter dat dit de paden naar de MP3's zijn en niet de eigenlijke bestanden zelf. De paden zijn vrij lang, omdat het het volledige pad naar de MP3-bestanden is.

Op mijn computer, bijvoorbeeld, het eerste element van de sporen array is gelijk aan "/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"Dit pad zou natuurlijk anders zijn op een echt apparaat.

We hebben een grote reeks die het pad naar de MP3 bevat, maar we willen gewoon de naam van de MP3 zelf. De NSString klasse definieert twee eigenschappen die ons kunnen helpen. Zoals de naam al aangeeft, de lastPathComponent eigenschap retourneert het laatste onderdeel van een pad. Zoals je misschien al geraden had, de stringByDeletingPathExtension eigenschap verwijdert de extensie.

Stap 10: getCurrentTimeAsString

De getCurrentTimeAsString methode gebruikt de huidige tijd eigendom van de speler instantie en retourneert het als een voor mensen leesbare string (bijv., 01:02).

 func getCurrentTimeAsString () -> String var seconds = 0 var minutes = 0 if let time = player? .currentTime seconds = Int (time)% 60 minutes = (Int (time) / 60)% 60 return String (format : "% 0.2d:% 0.2d", minuten, seconden)

De huidige tijd eigendom is van het type NSTimeInterval, dat is gewoon een typealias voor een Dubbele. We gebruiken wat wiskunde om de seconden en notulen, ervoor zorgen dat we converteren tijd aan een Int omdat we met hele getallen moeten werken. Als u niet bekend bent met de restoperator (%), vindt deze de rest na deling van het ene getal door het andere. Als het tijd variabele was gelijk aan 65, dan seconden zou gelijk zijn aan 5 omdat we gebruiken 60.

Stap 11: getProgress

De getProgress methode wordt gebruikt door de UIProgressView bijvoorbeeld om een ​​indicatie te geven van hoeveel van de MP3 heeft gespeeld. Deze voortgang wordt weergegeven door een waarde van 0.0 naar 1.0 als een Vlotter.

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

Om deze waarde te krijgen, verdelen we de speler's huidige tijd eigendom door de speler's looptijd property, slaan we deze waarden op in de variabelen theCurrentTime en theCurrentDuration. Net zoals huidige tijd, de looptijd eigendom is van het type NSTimeInterval en het vertegenwoordigt de duur van het nummer in seconden.

Stap 12: setVolume

De setVolume (_: Float) methode roept de setVolume methode van de speler aanleg.

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

Stap 13: audioPlayerDidFinishPlaying (_: met succes :)

De audioPlayerDidFinishPlaying (_: met succes :) methode is een methode van de AVAudioPlayerDelegate protocol. Deze methode neemt als parameters de AVAudioPlayer instance en een boolean. De boolean is ingesteld op waar als de audiospeler het huidige nummer heeft afgespeeld.

func audioPlayerDidFinishPlaying (speler: AVAudioPlayer, succesvol vlag: Bool) if flag == true nextSong (true)

Als het nummer met succes is afgespeeld, noemen we het volgende liedje methode, binnenkomen waar sinds het liedje op zichzelf is gespeeld.

Hiermee is het MP3 speler klasse. We zullen het later nog een keer bekijken, na het implementeren van de acties van de ViewController klasse.

4. ViewController Klasse

Open ViewController.swift en bekijk de inhoud ervan.

mport UIKit import AVFoundation class ViewController: UIViewController var mp3Player: MP3Player? var timer: NSTimer? @IBOutlet zwakke var trackName: UILabel! @IBOutlet zwakke var trackTime: UILabel! @IBOutlet zwakke var-voortgangBar: UIProgressView! override func viewDidLoad () super.viewDidLoad () @IBAction func playSong (afzender: AnyObject)  @IBAction func stopSong (afzender: AnyObject)  @IBAction func pauseSong (afzender: AnyObject)  @IBAction func playNextSong ( afzender: AnyObject)  @IBAction func setVolume (afzender: UISlider)  @IBAction func playPreviousSong (afzender: AnyObject)  override func didRiveMemoryWarning () super.didReceiveMemoryWarning () // Beschik alle bronnen die opnieuw kunnen worden gemaakt. 

De MP3 speler variabele is een instantie van de MP3 speler klasse die we eerder hebben geïmplementeerd. De timer variabele zal worden gebruikt om de tracktime en voortgangsbalk bekeken elke seconde.

In de volgende paar stappen zullen we de acties van de ViewController klasse. Maar eerst moeten we de MP3 speler aanleg. Update de implementatie van de viewDidLoad methode zoals hieronder getoond.

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

Stap 1: playSong (_: AnyObject)

Voer het volgende in de playSong (_: AnyObject) methode.

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

In deze methode roepen we de spelen methode op de MP3 speler voorwerp. We zijn nu zover dat we de app nu kunnen gaan testen. Start de app en druk op de afspeelknop. Het nummer zou moeten beginnen te spelen.

Stap 2: stopSong (_: AnyObject)

De stopSong (_: AnyObject) methode roept de stop methode op de MP3 speler voorwerp.

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

Start de app opnieuw en tik op de afspeelknop. Je zou nu het nummer kunnen stoppen door op de stopknop te tikken.

Stap 3: pauseSong (_: AnyObject)

Zoals je misschien al geraden had, de pauseSong (_: AnyObject) methode roept de pauze methode op de MP3 speler voorwerp.

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

Stap 4: playNextSong (_: AnyObject)

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

In playNextSong (_: AnyObject), we roepen het volgende liedje methode op de MP3 speler voorwerp. Merk op dat we slagen vals als een parameter, omdat het nummer niet zelfstandig is afgespeeld. We beginnen het volgende nummer handmatig door op de volgende knop te drukken.

Stap 5: previousSong (_: AnyObject)

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

Zoals u kunt zien, is de implementatie van de previousSong (_: AnyObject) methode lijkt erg op die van nextSong (_: AnyObject). Alle knoppen van de MP3-speler moeten nu functioneel zijn. Als je de app nog niet hebt getest, zou het nu een goed moment zijn om te zorgen dat alles werkt zoals verwacht.

Stap 6: setVolume (_: UISlider)

De setVolume (_: UISlider) methode roept de setVolume methode op de MP3 speler voorwerp. De volume-eigenschap is van het type Vlotter. De waarde varieert van 0.0 naar 1.0. De UISlider object is ingesteld met 0.0 als de minimumwaarde en 1.0 als maximale waarde.

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

Start de app nog een keer en speel met de volumeregelaar om te testen of alles correct werkt.

Stap 7: startTimer

De startTimer methode maakt een nieuw NSTimer aanleg.

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

De scheduledTimerWithTimeInterval (_: doel: selector: UserInfo: herhalingen :) initializer neemt als parameters het aantal seconden tussen het afvuren van de timer, het object waaraan een methode moet worden aangeroepen, gespecificeerd met keuzeschakelaar, de methode die wordt aangeroepen wanneer de timer start, een optionele gebruikers informatie woordenboek en of de timer wordt herhaald of niet totdat deze ongeldig wordt.

We gebruiken een methode genaamd updateViewsWithTimer (_: NSTimer) als de selector, dus we zullen het volgende creëren.

Stap 8: updateViewsWithTimer (_: NSTimer)

De updateViewsWithTimer (_: NSTimer) methode noemt het updateViews methode, die we in de volgende stap zullen implementeren.

func updateViewsWithTimer (theTimer: NSTimer) updateViews ()

Stap 9: updateViews

De updateViews methode werkt het tracktime en voortgangsbalk keer bekeken.

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

De tekst eigendom van tracktime is bijgewerkt met de huidige tijd eigenschap, opgemaakt als een tekenreeks door de getCurrentTimeAsString methode. We verklaren een constante vooruitgang de ... gebruiken MP3 speler's getProgress methode en stel in progressBar.progress gebruik die constante.

Stap 10: Bedrading van de timer

Nu moeten we de startTimer methode op de juiste plaatsen. We moeten de timer starten in de playSong (_: AnyObject) methode, de playNextSong (_: AnyObject) methode, en de playPreviousSong (_: AnyObject) methode.

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

Stap 11: De timer stoppen

We moeten ook stoppen met de timer wanneer de pauze- en stopknoppen worden ingedrukt. Je kunt het stoppen timer object door de ontzenuwen methode op de NSTimer aanleg.

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

Stap 12: setTrackName

De setTrackName methode stelt de tekst eigendom van trackname door aan te roepen getCurrentTrackName op de MP3 speler voorwerp.

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

Stap 13: setupNotificationCenter

Als een nummer is afgelopen, wordt de naam van het volgende nummer automatisch weergegeven en wordt het nummer afgespeeld. Wanneer de gebruiker op de knoppen play, next of vorige drukt, wordt de setTrackName methode moet worden aangeroepen. De ideale plek om dit te doen is het queueTrack methode van de MP3 speler klasse.

We hebben een manier nodig om de MP3 speler klasse vertel het ViewController klasse om het setTrackName methode. Om dat te doen, zullen we de NSNotificationCenter klasse. Het meldpunt biedt een manier om informatie door een programma heen uit te zenden. Door zich als waarnemer bij het meldpunt te registreren, kan een object deze uitzendingen ontvangen en een bewerking uitvoeren. Een andere manier om deze taak te volbrengen is om het delegatiepatroon te gebruiken.

Voeg de volgende methode toe aan de ViewController klasse.

 func setupNotificationCenter () NSNotificationCenter.defaultCenter (). addObserver (zelf, selector: "setTrackName", naam: "SetTrackNameText", object: nihil) 

We krijgen eerst een verwijzing naar het standaard meldingscentrum. We roepen vervolgens de addObserver (_: selector: naam: object :) methode op het meldingscentrum. Deze methode accepteert vier parameters:

  • het object dat registreert als de waarnemer, zelf in dit geval
  • het bericht dat wordt verzonden naar de waarnemer wanneer de melding is geplaatst
  • de naam van de kennisgeving waarvoor de waarnemer geregistreerd moet worden
  • het object waarvan de notificaties de waarnemer wil ontvangen

Door binnen te komen nul als het laatste argument, luisteren we naar elke melding die een naam heeft SetTrackNameText.

Nu moeten we deze methode in de view-controller's noemen viewDidLoad methode.

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

Stap 14: De melding plaatsen

Om de melding te plaatsen, roepen we de postNotificationName (_: object :) methode op het standaard meldingscentrum. Zoals ik eerder al zei, zullen we dit doen in de queueTrack methode van de MP3 speler klasse. Open MP3Player.swift en werk het queueTrack methode zoals hieronder getoond.

 func queueTrack () if (player! = nil) player = nil var error: NSError? laat url = NSURL.fileURLWithPath (tracks [currentTrackIndex] als tekenreeks) player = AVAudioPlayer (contentsOfURL: url, error: & error) if let hasError = error // LAAT ALERT OF IETS else player? .delegate = self player ?. prepareToPlay () NSNotificationCenter.defaultCenter (). postNotificationName ("SetTrackNameText", object: nil)

Als je de app nu test en een nummer helemaal door laat spelen, zou het het volgende nummer automatisch moeten spelen. Maar je vraagt ​​je misschien af ​​waarom de naam van het nummer niet verschijnt tijdens het eerste nummer. De in het methode van de MP3 speler klasse noemt het queueTrack methode, maar omdat het niet is voltooid, heeft het geen effect.

Het enige wat we moeten doen is handmatig de setTrackName methode nadat we de MP3 speler voorwerp. Voeg de volgende code toe aan de viewDidLoad methode in ViewController.swift.

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

Je zult merken dat ik ook de updateViews methode. Op deze manier toont de speler een tijd van 00:00 aan het begin. Als je de app nu test, zou je een volledig functionele MP3-speler moeten hebben.

Conclusie

Dit was een vrij lange tutorial, maar je hebt nu een functionele MP3-speler om te bouwen en uit te breiden. Een suggestie is om de gebruiker toe te staan ​​een nummer te kiezen om te spelen door een UITableView onder de speler. Bedankt voor het lezen en ik hoop dat je iets nuttigs hebt geleerd.