Reproductor de archivos MP3 en VB (3)

Reproductor MP3 con capacidad de reproducción aleatoria y navegación por unidades de disco y carpetas en controles ComboBox y TreeView junto con selección de canción en control ComboBox, en Visual Studio 2017 con VB

En 2 artículos anteriores se mostraba la forma de construir, sin conocimientos avanzados de programación, un reproductor de archivos MP3 añadiendo al proyecto el control Active X de Windows Media Player 9:

En este ejercicio se añaden 2 nuevas características.

ComboBox y TreeView
Una es la de elegir la carpeta con los archivos MP3 usando árbol de directorios con 3 controles:
ComboBox que permite elegir una unidad de disco
TreeView que permite desplegar los directorios del disco, recorrer las carpetas de forma recursiva y seleccionar una con archivos MP3 cuya reproducción aleatoria comienza automáticamente
ComboBox que despliega las canciones MP3 de la carpeta seleccionada y permite elegir una de las canciones de la lista para que sea reproducida.

My.Settings
Otra es la de guardar la configuración del programa al cerrarlo usando My.Settings, en concreto se guarda la última carpeta utilizada para que sea con la que arranca el programa sin tener que elegir necesariamente una al comenzar.

Centrar la ventana

Una forma sencilla de centrar la ventana principal del programa es la de utilizar por código las propiedades Widht y Height de Screen.PrimaryScreen.WorkingArea.

<code lang="vb" class="language-vb">'posición x.y del formulario: centrado en la pantalla Left = (Screen.PrimaryScreen.WorkingArea.Width - Me.Width) \ 2 Top = (Screen.PrimaryScreen.WorkingArea.Height - Me.Height) \ 2
Lenguaje del código: HTML, XML (xml)


ComboBox con los discos

Se rellena un array de cadenas con las letras de las unidades disponibles en el ordenador. Las letras de unidad se obtienen con GetLogicalDrives de System.Environment.

<code lang="vb" class="language-vb">' rellenar el Combobox con las letras de unidad del sistema ' array de cadenas para contener los nombres de las unidades Dim drives() As String drives = System.Environment.GetLogicalDrives() ' rellenar el Combobox desde el array CbDrive.Items.AddRange(drives)
Lenguaje del código: HTML, XML (xml)

Carpeta inicial con My.Settings

Si hay una carpeta guardada en la configuración, se recupera con My.Settings como ruta inicial para la reproducción. En caso contrario, se elige la letra C:\ (primera unidad) y se continúa con la selección de carpeta.

<code lang="vb" class="language-vb"> ' si hay carpeta guardada en la configuración al cerrar la aplicación ' usarla como carpeta de inicio sin tener que seleccionar en el árbol de carpetas If Not My.Settings.Ruta = "" Then strPath = My.Settings.Ruta ' rellenar el Combobox con el primer carácter de strPath (puede ser la letra C, D, etc.) añadiendo :\ ' Substring, si lleva un parámetro, es el índice empezando en cero a partir del cual se coge la cadena ' si lleva 2 parámetros el 1º es como en la línea superior y el 2º es el nº de carácteres que se cogen ' en este ejemplo (0,1) se coge 1 carácter empezando por la izquierda, es decir el primer carácter CbDrive.Text = strPath.Substring(0, 1) & ":\" Else CbDrive.SelectedItem = "C:\" End If
Lenguaje del código: HTML, XML (xml)

Carpetas de la unidad seleccionada en TreeView

Al elegir unidad de disco se desplegan las carpetas de esa unidad en un TreeView.
Primero se obtienen las carpetas de la unidad con GetDirectories de DirectoryInfo. Se utiliza la clase DirectoryInfo para operaciones típicas con carpetas y archivos como copiar, mover, cambiar de nombre, listar, crear y eliminar directorios, etc. Cada carpera de la undiad se va añadiendo al TreeView como un nodo nuevo hasta completar el árbol completo. Como no se desea listar carpetas o archivos ocultos, se usa FileAttributes.Hidden y FileAttributes.System junto con la característica de que el nombre empiece por un punto para no incluir esos ítems en la lista.

<code lang="vb" class="language-vb">Dim combo As New DirectoryInfo(Me.CbDrive.Text) Dim carpetas As DirectoryInfo() carpetas = combo.GetDirectories() ' limpiar el TreeView TvDir.Nodes.Clear() ' rellenar el TreeView con los nombres de los directorios; ' TreeNode representa a los nodos del TreeView Dim carpetas2 As DirectoryInfo ' para que no muestre carpetas o archivos ocultos o de sistema ' ni tampoco los que comienzan por un punto For Each carpetas2 In carpetas If Not (carpetas2.Attributes And FileAttributes.Hidden) = FileAttributes.Hidden And Not (carpetas2.Attributes And FileAttributes.System) = FileAttributes.System And Not carpetas2.FullName.StartsWith(".") Then ' el constructor de la clase Treenode inicializa una nueva instancia ' aquí se usa con 1 solo parámetro: el texto de etiqueta especificado nodo = New TreeNode(carpetas2.FullName) TvDir.Nodes.Add(nodo) End If Next
Lenguaje del código: HTML, XML (xml)

Al elegir un elemento del TreeView hay que cambiar a él para listar las carpetas que contiene. Se hace detectando el evento AfterSelect del TreeView. Se obienen los elementos del nodo elegido y se rellena el TreeView con los nombres de los subdirectorios; TreeNode representa a cada nodo del TreeView. Se emplea de nuevo FileAttributes.Hidden y FileAttributes.System para omitir ítems ocultos.

<code lang="vb" class="language-vb">' obtener los subdirectorios del nodo seleccionado Dim carpetas3 As DirectoryInfo carpetas3 = New DirectoryInfo(e.Node.FullPath) Dim carpetas4() As DirectoryInfo carpetas4 = carpetas3.GetDirectories() ' rellenar el TreeView con los nombres de los subdirectorios; ' TreeNode representa a los nodos del TreeView Dim subcarpetas As DirectoryInfo ' para que no muestre carpetas o archivos ocultos o de sistema ' ni tampoco los que comienzan por un punto For Each subcarpetas In carpetas4 If Not (subcarpetas.Attributes And FileAttributes.Hidden) = FileAttributes.Hidden And Not (subcarpetas.Attributes And FileAttributes.System) = FileAttributes.System And Not subcarpetas.Name.StartsWith(".") Then ' el constructor de la clase Treenode inicializa una nueva instancia ' aquí se usa con 1 solo parámetro: el texto de etiqueta especificado nodo = New TreeNode(subcarpetas.Name) e.Node.Nodes.Add(nodo.Text) End If Next ' obtener los archivos del subdirectorio Dim archivos() As FileInfo archivos = carpetas3.GetFiles()
Lenguaje del código: HTML, XML (xml)

Al obtener los archivos de la carpeta finalmente elegida sólo deseo los que tienen extensión MP3 o mp3, se hace con un bucle For Each … Next.

<code lang="vb" class="language-vb">' rellenar el Combobox con los nombres de los archivos ' que cumplan la condición: terminar por la extensión mp3 Dim cancion As FileInfo For Each cancion In archivos ' elegir solamente los archivos con extensión MP3 o mp3 Select Case cancion.Extension Case ".MP3", ".mp3" CbMp3.Items.Add(cancion.Name) End Select Next
Lenguaje del código: HTML, XML (xml)

La ruta a la carpeta elegida con los archivos MP3 coincide con la ruta del nodo actualmente seleccionado en el TreeView (SelectedNode.FullPath) y ese es el texto que se asigna a una variable String que se pasa al procedimiento que lista los MP3 de la carpeta y subcarpetas y empieza la reproducción aleatoria.

<code lang="vb" class="language-vb">strPath = TvDir.SelectedNode.FullPath ' limpiar listaMp3 de su contenido anterior If listaMp3.Count > 0 Then listaMp3.Clear() End If ' función recursiva que rellena listaMp3 con los archivos MP3 de carpetas y subcarpetas ' comienza listando archivos y carpetas de strPath (directorio raíz) Call ListarArchivos(strPath) ' comenzar a reproducir una canción al azar Call Aleatorio()
Lenguaje del código: HTML, XML (xml)

Rellenar recursivamente la lista de canciones

La función ListarArchivos se utiliza para obtener los archivos MP3 de froma recursiva desde la carpeta elegida y las subcarpetas que contenga. es una función sobrecargada (overloaded) que se ejecuta de 2 formas diferentes:

  • con un parámetro strpath(string): lista los archivos y carpetas del directorio raíz – ListarArchivos(ByVal strPath As String)
  • con 2 parámetros odir(directoryinfo) y nivel(integer): lista los archivos y carpetas de las subcarpetas descendiendo de nivel – ListarArchivos(ByVal oDir As System.IO.DirectoryInfo, ByVal nivel As Integer).
<code lang="vb" class="language-vb">Private Overloads Function ListarArchivos(ByVal strPath As String) As ArrayList Dim oDir As New System.IO.DirectoryInfo(strPath) ' propiedades del directorio raíz Dim oSubDir() As System.IO.DirectoryInfo ' propiedades de los subdirectorios Dim oFiles() As System.IO.FileInfo ' propiedades de los archivos Dim i As Integer ' para contar elementos en bucles oFiles = oDir.GetFiles ' archivos del directorio raíz ' añadir al ArrayList cada uno de los archivos MP3 del directorio raíz ' se emplea la propiedad FullName pues contiene la ruta completa al archivo ' necesaria para que el reproductor pueda abrir el archivo mediante su propiedad URL For i = 0 To oFiles.Length - 1 If Not oFiles(i).Name.StartsWith(".") Then If oFiles(i).Name.EndsWith("mp3") OrElse oFiles(i).Name.EndsWith("MP3") Then listaMp3.Add(oFiles(i).FullName) End If End If Next ' obtener las subcarpetas del directorio raíz oSubDir = oDir.GetDirectories ' en cada subcarpeta, se llama a la función recursiva con 2 parámetros: ' - la subcarpeta actual ' - la profundidad de la subcarpeta respecto al directorio raíz (se empieza por 1) For i = 0 To oSubDir.Length - 1 Call ListarArchivos(oSubDir(i), 1) Next End Function Private Overloads Function ListarArchivos(ByVal oDir As System.IO.DirectoryInfo, ByVal nivel As Integer) As ArrayList Dim oSubDir() As System.IO.DirectoryInfo ' propiedades de los subdirectorios Dim oFiles() As System.IO.FileInfo ' propiedades de los archivos Dim i As Integer ' para contar elementos en bucles oFiles = oDir.GetFiles ' archivos que contiene el directorio en cada llamada recursiva ' añadir a listaMp3 cada uno de los archivos MP3 del subdirectorio ' se emplea la propiedad FullName pues contiene la ruta completa al archivo For i = 0 To oFiles.Length - 1 If oFiles(i).Name.EndsWith("mp3") OrElse oFiles(i).Name.EndsWith("MP3") Then listaMp3.Add(oFiles(i).FullName) End If Next ' obtener las subcarpetas del subdirectorio oSubDir = oDir.GetDirectories() ' en cada subcarpeta, se llama a la función recursiva con 2 parámetros: ' - la carpeta actual ' - la profundidad de la carpeta respecto al directorio raíz (avanza 1 en cada pasada) For i = 0 To oSubDir.Length - 1 Call ListarArchivos(oSubDir(i), nivel + 1) Next End Function
Lenguaje del código: HTML, XML (xml)

Reproducción aleatoria

Para que el reproductor vaya cargando de forma aleatoria (shuffle) las canciones de la/s carpeta/s se usa un procedimiento que primero llama al método Randomize que inicia el generador de números aleatorios, después se elige con la función Rnd un número al azar entre los correspondientes a los archivos contenidos en el array de MP3 y el archivo que coincide con el índice seleccionado se asigna al control OCX del reproductor.

<code lang="vb" class="language-vb">Randomize() ' saber el número de archivos MP3 -> límite superior para usar con Rnd() Dim n As Integer n = listaMp3.Count ' obtener un índice aleatorio ' (se usa -1 porque el índice del ArrayList empieza en cero y listaMp3.Count empieza en 1) intCancion = CInt(n * Rnd() + 1) - 1 ' asignar el archivo al reproductor si el índice está dentro del rango adecuado If intCancion >= 0 And intCancion < listaMp3.Count Then strArchivo1 = listaMp3(intCancion) OcxPlayer.URL = strArchivo1 End If
Lenguaje del código: HTML, XML (xml)

Mostrar información de la canción

Se usan las propiedades title y author del archivo que se reproduce para mostrar la información en las etiquetas. Para mostrar en la barra de título de la ventana el título de la canción + el autor de la canción + la ruta a la carpeta se utilizan 2 variables enteras h y k, h para saber dónde está la última aparición de \ en el item que reproduce el control OCX, aquí terminaría la ruta a la carpeta y empezaría el nombre del archivo, k para saber la longitud completa de la cadena con la ruta incluyendo el nombre del archivo, con la función Remove se quitan los caracteres que hay después de la última aparición de \ incluido este carácter para dejar la ruta a la carpeta sin el nombre del archivo.

<code lang="vb" class="language-vb">strAutor = OcxPlayer.currentMedia.getItemInfobyType("author", "", 0) strTitulo = OcxPlayer.currentMedia.getItemInfobyType("title", "", 0) h = OcxPlayer.currentMedia.sourceURL.LastIndexOf("\") k = OcxPlayer.currentMedia.sourceURL.Length strArchivo_sin1 = OcxPlayer.currentMedia.sourceURL.Remove(h, k - h) Me.Text = strTitulo & " - " & strAutor & " - " & strArchivo_sin1
Lenguaje del código: HTML, XML (xml)

Estado de la reproducción

Detectando el evento PlayStateChange del control OCX se puede saber cuándo el reproductor está:

  • reproduciendo (WMPLib.WMPPlayState.wmppsPlaying)
  • detenido (WMPLib.WMPPlayState.wmppsStopped)
  • cambiando de canción (WMPLib.WMPPlayState.wmppsTransitioning).

Ello permte configurar tareas dependiendo del estado en que se encuentre.

<code lang="vb" class="language-vb">Private Sub OcxPlayer_PlayStateChange(ByVal sender As Object, ByVal e As AxWMPLib._WMPOCXEvents_PlayStateChangeEvent) Handles OcxPlayer.PlayStateChange ' dependiendo del estado del reproductor Select Case e.newState ' REPRODUCIENDO Case WMPLib.WMPPlayState.wmppsPlaying ' código que se desea ejecutar ' DETENIDO Case WMPLib.WMPPlayState.wmppsStopped ' código que se desea ejecutar ' PREPARANDO UN NUEVO ITEM Case WMPLib.WMPPlayState.wmppsTransitioning ' código que se desea ejecutar End Select End Sub
Lenguaje del código: HTML, XML (xml)

Cronómetro para el tiempo de reproducción

Se usa un control temporizador Timer y su evento Tick que se produce cuando ha trascurrido el período de tiempo especificado. Se comienza a contar al comenzar la reproducción y se van añadiendo segundos cambiando de minuto cada 59 segundos.

<code lang="vb" class="language-vb">' para el tiempo de reproducción transcurrido Private dtTime As DateTime ' ' Código para el cronómetro del tiempo del programa Dim Minutos, Segundos As Integer ' variables para mostrar el tiempo ' variable Segundos = diferencia en segundos entre una hora y la hora actual Segundos = DateDiff(DateInterval.Second, dtTime, DateTime.Now) Minutos = Segundos \ 60 Segundos = Segundos - (Minutos * 60) ' para que no supere 59 ' presentación formateada del tiempo transcurrido lbTime.Text = Format(Minutos, "00") & ":" & Format(Segundos, "00")
Lenguaje del código: HTML, XML (xml)

Seleccionar ítem en el ComboBox de canciones

El formulario principal tiene un ComboBox debajo del TreeView, en él se muestran las canciones existentes en la carpeta elegida. Al seleccionar una canción en el ComboBox se produce su evento SelectedIndexChanged, al detectarlo es posible iniciar la reproducción del ítem que se ha seleccionado.

<code lang="vb" class="language-vb">' para la ruta al directorio seleccionado Private strPath As String ' ' asignar a strPath la ruta completa al archivo elegido en el Combobox strPath = strPath & "\" & CbMp3.SelectedItem ' reproducir el archivo elegido OcxPlayer.URL = strPath
Lenguaje del código: HTML, XML (xml)

Guardar configuración al salir

Para que el programa al cerrarse guarde la ruta a la última carpeta usada se llama al método ChangeAndPersistSettings que se encarga de guardar las variables que se hayan creado en la pestaña Configuración de las propiedades del proyecto. En este ejercicio se ha creado una variable de tipo String llamada Ruta que guarda la ruta a la carpeta obteniéndola desde la variable strPath. El valor de Ruta se almacena cuando el programa está cerrado y está disponible cuando vuelve a ser iniciado.

<code lang="vb" class="language-vb">' AL CERRAR EL FORMULARIO, GUARDAR LA CONFIGURACIÓN Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing ' guardar la configuración Call ChangeAndPersistSettings() End Sub ' método específico que guarda la configuración, también se puede utilizar en su lugar la opción ' <Save My.Settings on shutdown> de la pestaña Aplicación en las propiedades del proyecto Sub ChangeAndPersistSettings() My.Settings.Ruta = strPath ' guardar la configuración My.Settings.Save() End Sub
Lenguaje del código: HTML, XML (xml)

Puedes descargar el proyecto en Visual Studo 2017 desde aquí: ZIP.

Nota: el ajuste automático de escala del formulario que muestra la ventana de la aplicación se puede configurar de varias maneras, lo tienes en las propiedades del formulario como AutoScaleMode, la opción con la que obtengo el mejor resultado es con Dpi.

Imagen del programa con el ComboBox de las canciones desplegado: