Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. iOS
Code

Desarrolla un reproductor MP3 con AV Foundation

by
Difficulty:IntermediateLength:LongLanguages:

Spanish (Español) translation by CYC (you can also view the original English article)

Final product image
What You'll Be Creating

AV Foundation es un framework para trabajar con medios audiovisuales en iOS y OSX. Usando AV Foundation, puedes reproducir, capturar y codificar medios. Es un framework bastante amplio y, a los fines de este tutorial, nos centraremos en la parte de audio. Específicamente, usaremos la clase AVAudioPlayer para reproducir archivos MP3.

Proyecto inicial

He proporcionado un proyecto inicial que tiene todas las acciones y salidas ya configuradas, y con los métodos apropiados protegidos. Las clases que usa el proyecto ya están configuradas para que podamos sumergirnos en el código. Puedes descargar el proyecto inicial desde GitHub.

1. Integrando el Framework AV Foundation

Antes de poder usar AV Foundation, debes integrar el proyecto con el framework. En Project Navigator, asegúrate de que tu proyecto esté seleccionado. Debajo de la pestaña General, ve a Frameworks Vinculados y Bibliotecas y desde allí elige AVFoundation.framework.

Linking the project against the AV Foundation framework

2. Clase FileReader

En el proyecto inicial, encontrarás un archivo llamado FileReader.swift. Abre este archivo para ver su contenido.

Este es un simple apéndice de la clase que usaremos para leer archivos del disco. Hereda de NSObject. Implementaremos un método, readFiles, que será un método type. Los métodos type te permiten llamar a un método en la clase en sí, el type, a diferencia de una instancia de la clase. A continuación se muestra la implementación del método readFiles.

El paquete principal contiene el código y los recursos para tu proyecto, y es aquí donde encontraremos los MP3. Usamos el método método pathsForResourcesOfType(_:inDirectory:), que devuelve una matriz que contiene las rutas para el tipo de recurso especificado. En este caso, estamos buscando el tipo "mp3". Como no estamos interesados en un directorio específico, lo pasamos en nil.

Esta clase será utilizada por la clase MP3Player, en la que trabajaremos a continuación.

3. Clase MP3Player

A continuación, abre MP3Player.swift y mira su contenido.

Ten en cuenta que estamos adoptando el protocolo AVAudioPlayerDelegate. Este protocolo declara una cantidad de métodos útiles, uno de los cuales es audioPlayerDidFinishPlaying(_:successfully:). Al implementar el método audioPlayerDidFinishPlaying(_:successfully:), se nos notificará cuando el audio haya terminado de reproducirse.

Paso 1: Propiedades

Agrega lo siguiente a MP3Player.swift.

La propiedad player será una instancia de la clase AVAudioPlayer, que usaremos para reproducir, pausar y detener los MP3. La variable currentTrackIndex realiza un seguimiento de qué MP3 se está reproduciendo actualmente. Finalmente, la variable tracks será una matriz de las rutas a la lista de MP3 que se incluyen en el paquete de la aplicación.

Paso 2: init

Durante la inicialización, invocamos el método readFiles de FileReader para buscar las rutas de los MP3 y almacenar esta lista en la matriz tracks. Como este es un inicializador designado, debemos llamar al método init de la superclase. Finalmente, llamamos al quequeTrack, que escribiremos a continuación.

Paso 3: queueTrack

Agrega la siguiente implementación para el método queueTrack a la clase MP3Player.

Debido a que crearemos instancias de una nueva instancia de AVAudioPlayer cada vez que se llame a este método, haremos un pequeño mantenimiento al configurar player en nil.

Declaramos un NSError opcional y una url constante. Invocamos fileURLWithPath(_:) para recuperar la ruta al MP3 actual y almacenar el valor en url. Estamos pasando la matriz tracks como parámetro usando currentTrackIndex como subíndice. Recuerda que la matriz de pistas contiene las rutas a los MP3 y no una referencia a los archivos MP3.

Para crear una instancia de player, pasamos la constante url y la variable error en el inicializador AVAudioPlayer. Si la inicialización falla, la variable error se rellena con una descripción del error.

Si no encontramos un error, configuramos el delegado del reproductor como self e invocamos el método prepareToPlay en el reproductor. El método prepareToPlay precarga los búferes y adquiere el hardware de audio, lo que minimiza cualquier retraso al llamar al método play.

Paso 4: play

El método play primero verifica si el audio ya se está reproduciendo al marcar la propiedad playing con un nombre apropiado. Si el audio no se está reproduciendo, invoca el método play de la propiedad player.

Paso 5: stop

El método stop primero verifica si el reproductor de audio ya está en reproducción. Si es así, invoca el método stop y establece la propiedad currentTime en 0. Cuando invocas el método stop, esto simplemente detiene el audio. Esto no reinicia el audio al principio, por lo que debemos hacerlo manualmente.

Paso 6: pause

Al igual que el método stop, primero verificamos si el reproductor de audio está en reproducción. Si es así, invocamos el método pause.

Paso 7: nextSong

El método nextSong(_:Bool) pone en cola la siguiente canción y, si el reproductor está ejecutándose, reproduce esa canción. No queremos que la próxima canción se reproduzca si el reproductor está en pausa. Sin embargo, este método también se invoca cuando una canción termina de reproducirse. En ese caso, queremos reproducir la siguiente canción. Para eso es el parámetro songFinishedPlaying.

La variable playerWasPlaying se usa para indicar si el reproductor se está ejecutando cuando se invocó este método. Si la canción se estaba reproduciendo, invocamos el método stop en player y configuramos PlayerWasPlaying en true.

A continuación, incrementamos el currentTrackIndex y verificamos si es mayor o igual que tracks.count. La propiedad count de una matriz nos da la cantidad total de elementos en la matriz. Necesitamos estar seguros de que no intentamos acceder a un elemento que no existe en la matriz tracks. Para evitar esto, establecemos currentTrackIndex de nuevo en el primer elemento de la matriz, si ese es el caso.

Finalmente, invocamos queueTrack para preparar la siguiente canción y reproducirla si PlayerWasPlaying o songFinishedPlaying son true.

Paso 8: previousSong

El método previousSong funciona muy similar a nextSong. La única diferencia es que disminuimos currentTrackIndex y verificamos si es igual a 0. Si lo es, lo configuramos en el índice del último elemento de la matriz.

Al utilizar los métodos nextSong y previousSong, podemos recorrer todos los MP3 y comenzar de nuevo cuando lleguemos al principio o al final de la lista.

Paso 9: getCurrentTrackName

El método getCurrentTrackName obtiene el nombre del MP3 sin la extensión.

Obtenemos una referencia a cualquiera que sea el MP3 actual utilizando tracks[currentTrackIndex]. Sin embargo, recuerda que estos son los caminos hacia los archivos MP3 y no los archivos mismos. Las rutas son bastante largas, porque es la ruta completa a los archivos MP3.

En mi máquina, por ejemplo, el primer elemento de la matriz tracks es igual 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". Esta ruta sería diferente en un dispositivo real, por supuesto.

Tenemos una cadena grande que contiene la ruta al MP3, pero solo queremos el nombre del MP3. La clase NSString define dos propiedades que pueden ayudarnos. Como su nombre lo indica, la propiedad lastPathComponent devuelve el último componente de una ruta. Como ya habrás adivinado, la propiedad stringByDeletingPathExtension elimina la extensión.

Paso 10: getCurrentTimeAsString

El método getCurrentTimeAsString utiliza la propiedad currentTime de la instancia player y la devuelve como un string legible para el ser humano (por ejemplo, 1:02).

La propiedad currentTime es de tipo NSTimeInterval, que es solo un typealias para un Double. Usamos algunos cálculos matemáticos para obtener los segundos y minutos, asegurándonos de convertir el tiempo en un Int ya que necesitamos trabajar con números enteros. Si no estás familiarizado con el operador modular (%), este es el residuo de una división entre dos números. Si la variable time fuera igual a 65, entonces los segundos serían 5 porque estamos usando 60.

Paso 11: getProgress

El método getProgress es utilizado por la instancia UIProgressView para dar una indicación de cuánto ha reproducido el MP3. Este progreso está representado por un valor de 0.0 a 1.0 como Float.

Para obtener este valor, dividimos la propiedad de tiempo actual del reproductor por la propiedad de duración del reproductor, almacenamos estos valores en las variables theCurrentTime y theCurrentDuration. Como currentTime, la propiedad de duración es de tipo NSTimeInterval y representa la duración de la canción en segundos.

Paso 12: setVolume

El método setVolume(_:Float) invoca el método setVolume de la instancia player.

Paso 13: audioPlayerDidFinishPlaying(_:successfully:)

El método audioPlayerDidFinishPlaying(_:successfully:) es un método del protocolo AVAudioPlayerDelegate. Este método toma como parámetros la instancia AVAudioPlayer y un booleano. El valor booleano se establece en verdadero si el reproductor de audio ha terminado de reproducir la canción actual.

Si la canción terminó de reproducirse con éxito, llamamos al método nextSong, pasando a true porque la canción terminó de reproducirse por sí misma.

Esto completa la clase MP3Player. Lo revisaremos un poco más tarde, después de implementar las acciones de la clase ViewController.

4. Clase ViewController

Abre ViewController.swift y mira su contenido.

La variable mp3Player es una instancia de la clase MP3Player que implementamos anteriormente. La variable del temporizador se usará para actualizar las vistas trackTime y progressBar cada segundo.

En los próximos pasos, implementaremos las acciones de la clase ViewController. Pero primero, debemos instanciar MP3Player. Actualiza la implementación del método viewDidLoad como se muestra a continuación.

Paso 1: playSong(_: AnyObject)

Ingresa lo siguiente en el método playSong(_:AnyObject).

En este método, invocamos el método play en el objeto mp3Player. Estamos en un punto en el que ya podemos comenzar a probar la aplicación. Ejecuta la aplicación y presiona el botón de reproducción. La canción debería comenzar a reproducirse.

Paso 2: stopSong(_: AnyObject)

El método stopSong(_:AnyObject) invoca el método stop en el objeto mp3Player.

Ejecuta la aplicación nuevamente y toca el botón de reproducción. Ahora deberías poder detener la canción tocando el botón de detener.

Paso 3: pauseSong(_: AnyObject)

Como ya habrás adivinado, el método pauseSong(_:AnyObject) invoca el método de pausa en el objeto mp3Player.

Paso 4: playNextSong(_: AnyObject)

En playNextSong(_:AnyObject), invocamos el método nextSong en el objeto mp3player. Ten en cuenta que pasamos false como parámetro, porque la canción no terminó de reproducirse por sí misma. Estamos comenzando manualmente la siguiente canción presionando el botón siguiente.

Paso 5: previousSong(_: AnyObject)

Como puedes ver, la implementación del método previousSong(_: AnyObject) es muy similar a la de nextSong(_: AnyObject). Todos los botones del reproductor de MP3 deberían ser funcionales ahora. Si aún no has probado la aplicación, este sería un buen momento para asegurarte de que todo funcione como se espera.

Paso 6: setVolume(_: UISlider)

El método setVolume(_: UISlider) invoca el método setVolume en el objeto mp3Player. La propiedad de volumen es de tipo Float. El valor varía de 0.0 a 1.0. El objeto UISlider se configura con 0.0 como su valor mínimo y 1.0 como su valor máximo.

Ejecuta la aplicación una vez más y juega con el deslizador de volumen para probar que todo funciona correctamente.

Paso 7: startTimer

El método startTimer inicia una nueva instancia de NSTimer.

El inicializador scheduledTimerWithTimeInterval(_:target:selector:userInfo:repeats:) toma como parámetros el número de segundos transcurridos entre la activación del temporizador, el objeto al que se llama un método especificado por el selector, el método que se invoca cuando se dispara el temporizador, un diccionario userInfo opcional, y si el temporizador se repite o no hasta que se invalide.

Estamos utilizando un método denominado updateViewsWithTimer(_: NSTimer) como selector, por lo que crearemos el siguiente.

Paso 8: updateViewsWithTimer(_: NSTimer)

El método updateViewsWithTimer(_: NSTimer) llama al método updateViews, que implementaremos en el siguiente paso.

Paso 9: updateViews

El método updateViews actualiza las vistas trackTime y progressBar.

La propiedad text de trackTime se actualiza con la propiedad currentTime, formateada como un String invocando el método getCurrentTimeAsString. Declaramos una constante progress utilizando el método getProgress de mp3Player, y establecemos progressBar.progress usando esa constante.

Paso 10: Conexión del temporizador

Ahora necesitamos llamar al método startTimer en los lugares apropiados. Necesitamos iniciar el temporizador en el método playSong(_: AnyObject), el método playNextSong(_: AnyObject) y el método playPreviousSong(_: AnyObject).

Paso 11: Detener el temporizador

También debemos detener el temporizador cuando se presionan los botones de pausa y detener. Puedes detener el objeto timer invocando el método invalidate en la instancia NSTimer.

Paso 12: setTrackName

El método setTrackName establece la propiedad text de trackName invocando getCurrentTrackName en el objeto mp3Player.

Paso 13: setupNotificationCenter

Cuando una canción termina de reproducirse, debes mostrar automáticamente el nombre de la canción siguiente y comenzar a reproducir esa canción. Además, cuando el usuario presiona los botones reproducir, siguiente o anterior, debe invocarse el método setTrackName. El lugar ideal para hacer esto es el método queueTrack de la clase MP3Player.

Necesitamos una forma de que la clase MP3Player le indique a la clase ViewController que invoque el método setTrackName. Para hacer eso, usaremos la clase NSNotificationCenter. El centro de notificaciones proporciona una forma de transmitir información a través de un programa. Al registrarse como observador en el centro de notificaciones, un objeto puede recibir estas transmisiones y realizar una operación. Otra forma de lograr esta tarea sería usar el patrón de delegación.

Agrega el siguiente método a la clase ViewController.

Primero obtenemos una referencia al centro de notificaciones predeterminado. A continuación, invocamos el método addObserver(_:selector:name:object:) en el centro de notificaciones. Este método acepta cuatro parámetros:

  • el objeto que se registra como el observador, self en este caso
  • el mensaje que se enviará al observador cuando se publique la notificación
  • el nombre de la notificación para la cual registrar al observador
  • el objeto cuyas notificaciones el observador quiere recibir

Al pasar en nil como último argumento, escuchamos cada notificación que tenga un nombre de SetTrackNameText.

Ahora necesitamos llamar a este método en el método viewDidLoad del controlador de vista.

Paso 14: Publicación de la notificación

Para publicar la notificación, invocamos el método postNotificationName(_:object:) en el centro de notificaciones predeterminado. Como mencioné anteriormente, haremos esto en el método queueTrack de la clase MP3Player. Abre MP3Player.swift y actualiza el método queueTrack como se muestra a continuación.

Si pruebas la aplicación ahora y dejas que la canción se reproduzca completamente, esto debería comenzar a reproducir la siguiente canción automáticamente. Pero quizás te preguntes por qué el nombre de la canción no se muestra durante la primera canción. El método init de la clase MP3Player llama al método queueTrack, pero como no ha terminado de inicializarse, no tiene impacto.

Todo lo que tenemos que hacer es llamar manualmente al método setTrackName después de inicializar el objeto mp3Player. Agrega el siguiente código al método viewDidLoad en ViewController.swift.

Notarás que también llamé al método updateViews. De esta forma, el reproductor muestra un tiempo de 0:00 al comienzo. Si pruebas la aplicación ahora, debes tener un reproductor MP3 completamente funcional.

Conclusión

Este fue un tutorial bastante largo, pero ahora tienes un reproductor MP3 funcional por desarrollar y expandir. Una sugerencia es permitir al usuario elegir una canción para reproducir implementando un UITableView debajo del reproductor. Gracias por leer y espero que hayas aprendido algo útil.



Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.