Introducción
En agosto de 2003 publiqué un artículo sobre un reproductor de archivos MP3 realizado en Visual Basic .NET 2003 utilizando el OCX de Windows Media Player 9. Retomando ese ejercicio, he añadido la funcionalidad de reproducir los archivos de forma aleatoria. El programa crea un Array con todas las canciones existentes en una determinada carpeta y, mediante las funciones Randomize() y Rnd(), genera un índice aleatorio que asigna al reproductor. En este artículo comentaré los apectos que difieran del artículo anteriormente publicado para evitar repeticiones innecesarias.
Requisito imprescindible: tener instalado Windows Media Player 9 o 10 y añadir su control OCX al proyecto (explicado en el artículo anterior).
Variables globales
Se crean unas variables con visibilidad a nivel de clase para poder utilizarlas en cualquier procedimiento:
' para el nombre y la ruta de los archivos Private strFilename, strPath As String ' el evento WMPLib.WMPPlayState.wmppsTransitioning se produce ' cuando Windows Media Player está preparando un nuevo ítem ' pero sólo ha de suceder cuando la variable blShuffle sea True Private blShuffle As Boolean = False ' para el tiempo de reproducción transcurrido Private dtTime As DateTime ' Array para contener los nombres de los archivos MP3 Private strLista(), strArchivo As String ' para el índice de las canciones Private intCancion As Integer Private a As Integer = 1 ' ' para escribir en un archivo de texto el índice de los archivos reproducidos Private objEscritor As StreamWriterProcedimiento para abrir un archivo
El procedimiento abrirArchivo() presenta al usuario un cuadro de diálogo para elegir entre archivos MP3, WPL y WAV. El resultado de la elección se pasa al reproductor. La variable strPath guarda la ruta a la carpeta que contiene la canción que suena y la variable strLista una matriz con los archivos contenidos en esa carpeta.
Private Sub abrirArchivo() ' procedimiento para buscar y abrir un archivo ' limpiar variables y parar el reproductor ocxPlayer.Ctlcontrols.stop() ocxPlayer.URL = "" strPath = "" lbIndex.Text = "" ' presentar al usuario un cuadro de diálogo Dim openFile As New OpenFileDialog ' mostrar solamente archivos MP3, WAV y WPL openFile.Filter = "Canciones (*.mp3; *.wav; *.wpl)|*.mp3; *.wav; *.wpl" ' si elegimos algún archivo If openFile.ShowDialog() = Windows.Forms.DialogResult.OK Then ' abrir el archivo elegido en el reproductor ocxPlayer.URL = openFile.FileName ' strPath = ruta a la carpeta que contiene el archivo MP3 hasta la última aparición de \ ' (ruta sin el nombre de archivo) Dim i As Integer ' índice de la última aparición de \ i = openFile.FileName.LastIndexOf("\") strPath = openFile.FileName.Remove(i, openFile.FileName.Length - i) ' strArchivo = ruta a la carpeta que contiene el archivo MP3 a partir de la última aparición de \ '(nombre de archivo sin la ruta) strArchivo = openFile.FileName.Substring(i + 1) ' Array de String para los archivos MP3 existentes en la carpeta strLista = System.IO.Directory.GetFiles(strPath) ' índice de la canción que suena en el Array strLista 'intCancion = Array.IndexOf(strLista, strArchivo) ' ' para poder llamar al método Aleatorio() en el evento wmppsTransitioning ' ' si es una lista de reproducción WPL If ocxPlayer.URL.EndsWith("wpl") Then blShuffle = False lbIndex.Text = "Lista en reproducción" ' si no es una lista de reproducción (archivo MP3 o VAW) Else blShuffle = True lbIndex.Text = "Carpeta con " & strLista.Length & " canciones" End If ' ' para contar el número de canciones que se van reproduciendo en la sesión, ' cada vez que se abre un archivo nuevo, se incrementa en 1 a += 1 End If End SubEstado del reproductor
El procedimiento ocxPlayer_PlayStateChange() detecta los cambios en el estado del reproductor (reproduciendo, detenido...) para mostrar información de la canción al cambiar el estado del reproductor (PlayStateChange). Como el OCX tiene configurado su inicio automático, simplemente con elegir un archivo ya cambia el estado de reproducción y comienza a tocar. De este procedimiento, existente en el artículo anterior, sólo comentaré la inclusión de un nuevo estado wmppsTransitioning que tiene lugar cada vez que el reproductor está preparando un nuevo ítem; pero en algunas ocasiones no debe ser detectado por lo que se crea una variable booleana blShuffle que dice al programa cuándo detectar wmppsTransitioning y cuándo no hacerlo.
Por otro lado, el programa puede leer metadatos de los archivos MP3 tales como el título de la canción (.currentMedia.getItemInfobyType("title", "", 0)) o el nombre del autor (.currentMedia.getItemInfobyType("author", "", 0)) y mostrarlos en unas etiquetas de texto. Pero si el archivo MP3 carece de esos tags (particularmente del tag author ya que el tag title se rellena a partir del nombre de archivo) el programa no muestra correctamente información sobre la duración de la canción y el tiempo transcurrido y aparecen errores respecto a los controles de la aplicación que han de verse habilitados o deshabilitados, por ese motivo se captura la excepción al rellenar esas etiquetas informativas.
Los controles del formulario que se muestran como habilitados difieren si el archivo reproducido es una canción independiente (MP3 o VAW) o si es una lista de canciones (WPL).Private Sub ocxPlayer_PlayStateChange(ByVal sender As Object, ByVal e As AxWMPLib._WMPOCXEvents_PlayStateChangeEvent) Handles ocxPlayer.PlayStateChange ' dtTime = DateTime.Now ' para empezar a contar el tiempo ' vaciar la información de la canción anterior lbTitulo.Text = "" lbTituloa.Text = "Reproducción en curso:" lbAutor.Text = "" lbAutora.Text = "Intérprete:" ' ' dependiendo del estado del reproductor Select Case e.newState ' ' REPRODUCIENDO Case WMPLib.WMPPlayState.wmppsPlaying ' 'cadena con el nombre del archivo que suena strArchivo = ocxPlayer.URL ' Array de String para los archivos MP3 existentes en la carpeta strLista = System.IO.Directory.GetFiles(strPath) ' índice de la canción que suena en el Array strLista intCancion = Array.IndexOf(strLista, strArchivo) + 1 ' ' mostrar en la barra de título: '- el número de orden del archivo en la carpeta '- el título de la canción If intCancion > 0 Then Me.Text = intCancion & ". " & ocxPlayer.currentMedia.getItemInfobyType("title", "", 0) ' título Else MsgBox("Imposible reproducir el archivo:" & vbCrLf & strArchivo & vbCrLf & _ "que se corresponde con strLista[" & (intCancion - 1) & "].", MsgBoxStyle.Information, "Error") End If ' evitar errores con archivos MP3 que no tengan metainformación sobre el ítem Author ' (la etiqueta lbAutor se rellena con el nombre del archivo cuando no existe metainformación sobre ese ítem) Try lbTituloa.Text = "Reproducción en curso:" lbTitulo.Text = ocxPlayer.currentMedia.getItemInfobyType("title", "", 0) ' título lbAutora.Text = "Intérprete:" lbAutor.Text = ocxPlayer.currentMedia.getItemInfobyType("author", "", 0) ' autor Catch pollo As Exception lbAutor.Text = "" End Try ' para mostrar la duración de la canción Dim minutos As Integer Dim segundos As Integer minutos = ocxPlayer.currentMedia.duration \ 60 ' división entera entre la duración en segundos y 60 ' resto entre la duración total y el resultado de la división entera anterior segundos = ocxPlayer.currentMedia.duration - (minutos * 60) lbTotal.Text = Format(minutos, "00") & ":" & Format(segundos, "00") Timer1.Enabled = True ' iniciar el temporizador para cronometrar la canción Timer1.Start() ' btAbrir.Enabled = False ' desactivar el botón Abrir btStop.Enabled = True ' activar el botón Abrir btPlay.Enabled = False ' desactivar el botón Play ' botones Prev y Next activos sólo si reproduce una lista If ocxPlayer.URL.EndsWith("wpl") Then btPrev.Enabled = True ' activar el botón Prev btNext.Enabled = True ' activar el botón Next ' si es una lista, no se muestra información con el nº de archivos en la carpeta lbIndex.Text = "Lista en reproducción" lbCuenta.Text = "" Else btPrev.Enabled = False ' desactivar el botón Prev btNext.Enabled = False ' desactivar el botón Next btSaltar.Enabled = True ' activar el botón Saltar ' si no es una lista, se muestra información con el nº de archivos en la carpeta 'lbIndex.Text = "Canción " & (intCancion + 1) & " de " & strLista.Length lbIndex.Text = "Carpeta con " & strLista.Length & " canciones" If a = 0 Then a = 1 End If lbCuenta.Text = "Suena la canción nº " & a End If ' ' DETENIDO Case WMPLib.WMPPlayState.wmppsStopped btAbrir.Enabled = True ' activar el botón Abrir btStop.Enabled = False ' desactivar botón Stop btPlay.Enabled = True ' activar botón Play btPrev.Enabled = False ' desactivar botón Prev btNext.Enabled = False ' desactivar botón Next btSaltar.Enabled = False ' desactivar botón Saltar ' barra de título de la ventana vacía Me.Text = "emiPlayer" Timer1.Stop() ' detener temporizador Timer1.Enabled = False lbTotal.Text = "" lbTime.Text = "" lbIndex.Text = "" lbCuenta.Text = "" ' ' desactivar reproducción aleatoria If blShuffle = True Then blShuffle = False End If ' ' PREPARANDO UN NUEVO ITEM Case WMPLib.WMPPlayState.wmppsTransitioning ' actuar en el evento wmppsTransitioning sólo si blShuffle es True If blShuffle = True Then Call Aleatorio() End If End Select End SubReproducción aleatoria
El procedimiento Aleatorio() se encarga de generar un número aleatorio entre 1 y el número de elementos que componen la matriz de canciones (es decir, entre 1 y el número total de canciones en la carpeta).
La función Randomize() sin argumento arranca el generador de números aleatorios con un valor de inicialización basado en el temporizador del sistema.
La función Rnd() devuelve un número aleatorio de tipo Single (devuelve un valor menor que 1 pero mayor o igual que cero). Para producir enteros aleatorios en un intervalo dado, se utiliza la siguiente fórmula:
aleatorio = CInt(Int((límite_superior - límite_inferior + 1) * Rnd() + límite_inferior))
En el siguiente ejemplo se genera un entero aleatorio en el intervalo entre 1 y 6:
Dim value As Integer = CInt(Int((6 * Rnd()) + 1))Como comprobación accesoria y para repasar cómo escribir en un archivo de texto con la clase StreamWriter, cada vez que arranca el programa se crea un archivo de texto en el que se van anotando los índices de las canciones según van siendo reproducidas. El archivo se llama Lista.txt y se crea en el directorio temporal. Si se reproduce una lista (WPL) solamente escribe el título y hora de comienzo.
El constructor de StreamWriter lleva un 2 parámetros:
- la ruta al archivo en que se va a escribir
- un parámetro booleano que puede ser False (lo que se escribe substituye a lo que ya está escrito) o True (lo que se escribe se añade al final de lo que ya está escrito).Private Sub Aleatorio() Try ' ejecutar este método cuando blShuffle es True If blShuffle = True Then ' para que no llame al método Aleatorio() en el evento wmppsTransitioning ' hasta que termine el propio método Aleatorio() blShuffle = False ' Randomize() ' ' saber el número de archivos MP3 -> límite superior para usar con Rnd() Dim n As Integer n = strLista.Length - 1 ' obtener un índice aleatorio ' (se usa -1 porque el índice del array empieza en cero) intCancion = CInt(n * Rnd() + 1) - 1 strArchivo = strLista(intCancion) ' ' utilizar solamente archivos MP3, WAV y WPL If strArchivo.EndsWith("mp3") Or strArchivo.EndsWith("wav") Or strArchivo.EndsWith("wpl") Then ocxPlayer.URL = strArchivo Else ' si tiene otra extensión diferente, volver a generar un índice aleatorio blShuffle = True ' para que se pueda ejecutar Aleatorio() Call Aleatorio() End If ' objEscritor = New StreamWriter(Environment.GetEnvironmentVariable("TEMP") & "\Lista.txt", True) objEscritor.WriteLine(a & " - strLista[" & intCancion & "]") a = a + 1 objEscritor.Close() ' ' para poder llamar al método Aleatorio() en el evento wmppsTransitioning blShuffle = True End If ' Catch pollo2 As Exception MsgBox("Error en el índice: " & intCancion & "." & vbCrLf & pollo2.Message) End Try ' End Sub
Imagen del programa en funcionamiento