Reproductor de mp3
Creación de una clase para la reproducción de archivos mp3.

29 de Junio de 2003 (08/Jul/2003)
Autor: Gonzalo Antonio sosa M. y gonzaloantonio@iespana.es


Codificación de la clase Reproductor.

En este punto mostraremos como se ha codificado la clase Reproductor, por supuesto que todo esto se muestra a manera de ejemplo, dado que todos tenemos nuestra propia manera de hacer las cosas, sobre todo en esto de la programación.

Primero importamos los espacios de nombres que necesitaremos...

using System;        
using System.IO;     
using System.Text;     
using System.Runtime.InteropServices; 

Ahora declaramos a las funciones de la Api que se necesitarán, cada una de ellas tiene un propósito específico...

[DllImport("winmm.dll")]
public static extern int mciSendString(string lpstrCommand, 
StringBuilder lpstrReturnString, int uReturnLengh, int hwndCallback);

La función mciSendString envía una cadena de comando a un dispositivo MCI. El dispositivo al que va destinado se específica en la cadena de comando. La función recibe 4 parámetros: lpstrCommand, una cadena terminada en nulo, dónde se especifica la cadena de comando MCI; lpstrReturnString, un puntero a un buffer que recibe la información de retorno, sino se espera dicha información, el parámetro puede ser nulo; uReturnLength, tamaño en caracteres del buffer especificado en el parámetro lpstrReturnString y, hwndCallback, manejador de retorno si la bandera de callback ha sido especificada en la cadena de comando. La función retorna 0 si ha tenido éxito y diferente de 0, en cualquier otro caso.

Para codificar la función necesitamos pasar como segundo parámetro un puntero a una cadena modificable, y cómo probablemente conozcas, las cadenas de tipo string, son del tipo inmutable, esto es, cuando se modifican sus valores en realidad devuelven una string  nueva. No siendo así con las creadas como instancia de StringBuilder, que siempre devuelven una referencia a la misma. Por ello hemos optado por utilizar la clase StringBuilder, del espacio de nombres System.Text. Dicha clase ha sido utilizada para todas la funciones que requieran un parámetro similar.

[DllImport("winmm.dll")]
public static extern string mciGetErrorString(int fwdError, StringBuilder lpszErrorText, 
int cchErrorText);

Esta función devuelve una cadena  que describe el código de error MCI especificado. Toma 3 parámetros: fwdError, el código de error (diferente de 0), devuelto por la función mciSendString; lpszErrorText, un puntero a un buffer que recibe la cadena terminada en nulo, dónde se describe el código de error y, cchErrorText, la longitud del buffer apuntado por el parámetro lpszTextError. La función devuelve verdadero (un valor diferente de 0) sí ha tenido éxito, ó falso (valor igual con 0), si ha fallado.

[DllImport("winmm.dll")]
public static extern int waveOutGetNumDevs();

Esta función devuelve el número de dispositivos de salida de audio presentes en el sistema. La función no requiere parámetros y devuelve el número de dispositivos o cero si no se ha encontrado ninguno.

[DllImport("kernel32.dll")]
public static extern int GetShortPathName(string lpszLongPath,
StringBuilder lpszShortPath, int cchBuffer);

Devuelve una cadena conteniendo la ruta en formato corto, de la ruta especificada como entrada. Requiere 3 parámetros: lpszLongPath, cadena terminada en nulo, conteniendo la ruta origen. En la versión ANSI de esta función, este parámetro está limitado a un número constante (MAX_PATH) de caracteres, para extender este límite a 32, 767 caracteres, se requiere una llamada a la versión Unicode de la función y anteponer "\\?\" a la ruta; lpShortPath, puntero a un buffer terminado en nulo, que almacenará la ruta en formato corto; cchBuffer, tamaño del buffer apuntado en lpShortPath.


[DllImport("kernel32.dll")]
public static extern int GetLongPathName(string
lpszShortPath, StringBuilder lpszLongPath, int cchBuffer);

Convierte la ruta especificada a su formato largo. Sí la cadena en formato largo no es encontrada, simplemente devuelve la cadena original. Igualmente requiere 3 parámetros: lpszShortPath, cadena terminada en nulo, conteniendo la ruta a convertir. En su formato ANSI, la longitud de este parámetro está limitada a MAX_PATH caracteres, para extender dicho valor se requiere llamar a su versión Unicode; lpszLongPath, puntero a un buffer que almacenará la cadena convertida; cchBuffer, Tamaño del buffer.

Ahora declaremos las variables y constantes que se requieren...

// Constante con la longitud máxima de un nombre de archivo.     
const int MAX_PATH = 260;                                                
// Constante con el formato de archivo a reproducir.
const string Tipo = "MPEGVIDEO";                                          
// Alias asignado al archivo especificado.
const string sAlias = "ArchivoDeSonido"; 
private string fileName; //Nombre de archivo a reproducir

Necesitamos que la clase se  comunique con el usuario de la misma, para comunicarle el estado de las operaciones efectuadas; y para ello, haremos uso de un evento.

//Especificamos el delegado al que se va a asociar el evento.
public delegate void ReproductorMessage(string Msg); 
//Declaramos nuestro evento.
public event ReproductorMessage ReproductorEstado;

No haremos ninguna modificación al constructor por defecto.

public Reproductor()
{
}

Ahora agregamos una propiedad de lectura / escritura, que nos permita establecer el nombre del archivo que deseamos reproducir...

public string NombreDeArchivo 
{
    get
    {
        return fileName; 
    }
    set
    {
        fileName = value;
    }
}

Ahora necesitamos algunos métodos que nos permitan manejar la reproducción del archivo más detalladamente. Necesitamos métodos para abrir, Reproducir, detener, cerrar,  pausar, buscar dentro, saber el estado de la reproducción. Para todos ellos la función MciSendString realiza el trabajo, pasando como parámetros, la cadena de comando adecuada, el nombre de archivo y/o el alias con el que se maneja dicho archivo. 

Bien, este el código de los métodos citados para la clase Reproductor:

/// <summary>
/// Método para convertir un nombre de archivo largo en uno corto,
/// necesario para usarlo como parámetro de la función MciSendString.
/// </summary>
/// <param name="nombreLargo">Nombre y ruta del archivo a convertir.</param>
/// <returns>Nombre corto del archivo especificado.</returns>
private string NombreCorto(string NombreLargo)
{
    // Creamos un buffer usando un constructor de la clase StringBuider.
    StringBuilder sBuffer = new StringBuilder(MAX_PATH);
    // intentamos la conversión del archivo.
    if (GetShortPathName(NombreLargo, sBuffer, MAX_PATH) > 0)
        // si la función ha tenido éxito devolvemos el buffer formateado
        // a tipo string.
        return sBuffer.ToString();
    else // en caso contrario, devolvemos una cadena vacía.
        return "";
}
/// <summary>
/// Método que convierte un nombre de archivo corto, en uno largo.
/// </summary>
/// <param name="NombreCorto">Nombre del archivo a convertir.</param>
/// <returns>Cadena con el nombre de archivo resultante.</returns>
public string NombreLargo(string NombreCorto)
{
    StringBuilder sbBuffer = new StringBuilder(MAX_PATH);
    if (GetLongPathName(NombreCorto, sbBuffer, MAX_PATH) > 0)
        return sbBuffer.ToString();
    else 
        return "";
}
/// <summary>
/// Método para convertir los mensajes de error numéricos, generados por la
/// función mciSendString, en su correspondiente cadena de caracteres.
/// </summary>
/// <param name="ErrorCode">Código de error devuelto por la función 
/// mciSendString</param>
/// <returns>Cadena de tipo string, con el mensaje de error</returns>
private string MciMensajesDeError(int ErrorCode)
{
// Creamos un buffer, con suficiente espacio, para almacenar el mensaje
// devuelto por la función.
    StringBuilder sbBuffer = new StringBuilder(MAX_PATH);
    // Obtenemos la cadena de mensaje.
    if (mciGetErrorString(ErrorCode, sbBuffer, MAX_PATH) != 0)
        // Sí la función ha tenido éxito, valor devuelto diferente de 0,
        // devolvemos el valor del buffer, formateado a string.
        return sbBuffer.ToString();
    else // si no, devolvemos una cadena vacía.
        return "";
}
/// <summary>
/// Devuelve el número de dispositivos de salida, 
/// instalados en nuestro sistema.
/// </summary>
/// <returns>Número de dispositivos.</returns>
public int DispositivosDeSonido()
{
    return waveOutGetNumDevs();
}
/// <summary>
/// Abre el archivo específicado.
/// </summary>
/// <returns>Verdadero si se tuvo éxito al abrir el archivo
/// falso en caso contrario.</returns>
private bool Abrir()
{
    // verificamos que el archivo existe; si no, regresamos falso.
    if (!File.Exists(fileName)) return false;
    // obtenemos el nombre corto del archivo.
        string nombreCorto = NombreCorto(fileName);
        // intentamos abrir el archivo, utilizando su nombre corto
        // y asignándole un alias para trabajar con él.
        if (mciSendString("open " + nombreCorto + " type " + Tipo + 
        " alias " + sAlias, null, 0, 0) == 0)
        // si el resultado es igual a 0, la función tuvo éxito,
        // devolvemos verdadero.
            return true;
        else
            // en caso contrario, falso.
            return false;
}
/// <summary>
/// Inicia la reproducción del archivo especificado.
/// </summary>
public void Reproducir()
{
    // Nos cersioramos que hay un archivo que reproducir.
    if (fileName != "")
    {
        // intentamos iniciar la reproducción.
        if (Abrir())
        {
            int mciResul = mciSendString("play " + sAlias, null, 0, 0);
            if (mciResul == 0)
            // si se ha tenido éxito, devolvemos el mensaje adecuado,
                ReproductorEstado("Ok");
            else // en caso contrario, la cadena de mensaje de error.
                ReproductorEstado(MciMensajesDeError(mciResul));
        }
    else // sí el archivo no ha sido abierto, indicamos el mensaje de error.
    ReproductorEstado("No se ha logrado abrir el archivo especificado");
    }
    else // si no hay archivo especificado, devolvemos la cadena indicando
        // el evento.
        ReproductorEstado("No se ha especificado ningún nombre de archivo");
}
/// <summary>
/// Inicia la reproducción desde una posición específica.
/// </summary>
/// <param name="Desde">Nuevo valor de la posición a iniciar</param>
public void ReproducirDesde(long Desde)
{
    int mciResul = mciSendString("play " + sAlias + " from " +
    (Desde * 1000).ToString(), null, 0, 0);
    if (mciResul == 0)
        ReproductorEstado("Nueva Posición: " + Desde.ToString());
    else
        ReproductorEstado(MciMensajesDeError(mciResul));
}
/// <summary>
/// Modifica la velocidad actual de reproducción.
/// </summary>
/// <param name="Tramas">Nuevo valor de la velocidad.</param>
public void Velocidad(int Tramas)
{
    // Establecemos la nueva velocidad pasando como parámetro,
    // la cadena adecuada, incluyendo el nuevo valor de la velocidad,
    // medido en tramas por segundo.
    int mciResul = mciSendString("set " + sAlias + " tempo " +
    Tramas.ToString(), null, 0, 0);
    if (mciResul == 0)
        // informamos el evento de la modificación éxitosa,
        ReproductorEstado("Velocidad modificada.");
    else // de lo contrario, enviamos el mensaje de error correspondiente.
        ReproductorEstado(MciMensajesDeError(mciResul));
}
/// <summary>
/// Mueve el apuntador del archivo a la posición especificada.
/// </summary>
/// <param name="NuevaPosicion">Nueva posición</param>
public void Reposicionar(int NuevaPosicion)
{
// Enviamos la cadena de comando adecuada a la función mciSendString,
// pasando como parte del mismo, la cantidad a mover el apuntador de
// archivo.
    int mciResul = mciSendString("seek " + sAlias + " to " +
    (NuevaPosicion * 1000).ToString(), null, 0, 0);
    if (mciResul == 0)
        ReproductorEstado("Nueva Posición: " + NuevaPosicion.ToString());
    else 
        ReproductorEstado(MciMensajesDeError(mciResul));
}
/// <summary>
/// Mueve el apuntador de archivo al inicio del mismo.
/// </summary>
public void Principio()
{
// Establecemos la cadena de comando para mover el apuntador del archivo,
// al inicio de este.
int mciResul = mciSendString("seek " + sAlias + " to start", null, 0, 0);
if (mciResul == 0)
ReproductorEstado("Inicio de"+Path.GetFileNameWithoutExtension(fileName));
else 
ReproductorEstado(MciMensajesDeError(mciResul));
}
/// <summary>
/// Mueve el apuntador de archivo al final del mismo.
/// </summary>
public void Final()
{
// Establecemos la cadena de comando para mover el apuntador del archivo,
// al final de este.
int mciResul = mciSendString("seek " + sAlias + " to end", null, 0, 0);
if (mciResul == 0)
ReproductorEstado("Final de"+Path.GetFileNameWithoutExtension(fileName));
else 
ReproductorEstado(MciMensajesDeError(mciResul));
}
/// <summary>
/// Pausa la reproducción en proceso.
/// </summary>
public void Pausar()
{
    // Enviamos el comando de pausa mediante la función mciSendString,
    int mciResul = mciSendString("pause " + sAlias, null, 0, 0);
    if (mciResul == 0)
        ReproductorEstado("Pausada");
    else 
        ReproductorEstado(MciMensajesDeError(mciResul));
}
/// <summary>
/// Continúa con la reproducción actual.
/// </summary>
public void Continuar()
{
    // Enviamos el comando de pausa mediante la función mciSendString,
    int mciResul = mciSendString("resume " + sAlias, null, 0, 0);
    if (mciResul == 0)
        // informamos del evento,
        ReproductorEstado("Continuar");
    else // si no, comunicamos el error.
        ReproductorEstado(MciMensajesDeError(mciResul));
}
/// <summary>
/// Detiene la reproducción actual y cierra el archivo de audio.
/// </summary>
public void Cerrar()
{
    // Establecemos los comando detener reproducción y cerrar archivo.
    mciSendString("stop " + sAlias, null, 0, 0);
    mciSendString("close " + sAlias, null, 0, 0);
}
/// <summary>
/// Detiene la reproducción del archivo de audio.
/// </summary>
public void Detener()
{
    // Detenemos la reproducción, mediante el comando adecuado.
    mciSendString("stop " + sAlias, null, 0, 0);
}
/// <summary>
/// Obtiene el estado de la reproducción en proceso.
/// </summary>
/// <returns>Cadena con la información requerida.</returns>
public string Estado()
{
    StringBuilder sbBuffer = new StringBuilder(MAX_PATH);
    // Obtenemos la información mediante el comando adecuado.
    mciSendString("status " + sAlias + " mode", sbBuffer, MAX_PATH, 0);

    // Devolvemos la información.
    return sbBuffer.ToString();
} 
/// <summary>
/// Obtiene un valor que indica si la reproducción está en marcha.
/// </summary>
/// <returns>Verdadero si se encuentra en marcha, falso si no.</returns>
public bool EstadoReproduciendo()
{
    return Estado() == "playing" ? true : false;
}
/// <summary>
/// Obtiene un valor que indica si la reproducción está pausada.
/// </summary>
/// <returns>Verdadero si se encuentra en marcha, falso si no.</returns>
public bool EstadoPausado()
{
    return Estado() == "paused" ? true : false;
}
/// <summary>
/// Obtiene un valor que indica si la reproducción está detenida.
/// </summary>
/// <returns>Verdadero si se encuentra en marcha, falso si no.</returns>
public bool EstadoDetenido()
{
    return Estado() == "stopped" ? true : false;
}
/// <summary>
/// Obtiene un valor que indica si el archivo se encuentra abierto.
/// </summary>
/// <returns>Verdadero si se encuentra en marcha, falso si no.</returns>
public bool EstadoAbierto()
{
    return Estado() == "open" ? true : false;
}
/// <summary>
/// Calcula la posición actual del apuntador del archivo.
/// </summary>
/// <returns>Posición actual</returns>
public long CalcularPosicion()
{
StringBuilder sbBuffer = new StringBuilder(MAX_PATH);
// Establecemos el formato de tiempo.
mciSendString("set " + sAlias + " time format milliseconds", null, 0, 0);
// Enviamos el comando para conocer la posición del apuntador.
mciSendString("status " + sAlias + " position", sbBuffer, MAX_PATH, 0);

// Sí hay información en el buffer,
if (sbBuffer.ToString() != "")
// la devolvemos, eliminando el formato de milisegundos
// y formateando la salida a entero largo;
return long.Parse(sbBuffer.ToString()) / 1000;
else // si no, devolvemos cero.
return 0L;
}
/// <summary>
/// Devuelve una cadena con la información de posición del apuntador del archivo.
/// </summary>
/// <returns>Cadena con la información.</returns>
public string Posicion()
{
    // obtenemos los segundos.
    long sec = CalcularPosicion();
    long mins;

    // Si la cantidad de segundos es menor que 60 (1 minuto),
    if (sec < 60)
        // devolvemos la cadena formateada a 0:Segundos.
        return "0:" + String.Format("{0:D2}", sec);
    // Si los segundos son mayores que 59 (60 o más),
    else if (sec > 59)
    {
        // calculamos la cantidad de minutos,
        mins = (int)(sec / 60);
        // restamos los segundos de la cantida de minutos obtenida,
        sec = sec - (mins * 60);
        // devolvemos la cadena formateada a Minustos:Segundos.
        return String.Format("{0:D2}", mins) + ":" + String.Format("{0:D2}", sec);
    }
    else // en caso de obtener un valor menos a 0, devolvemos una cadena vacía.
        return "";
}
/// <summary>
/// Cálcula el tamaño del archivo abierto para reproducción.
/// </summary>
/// <returns>Tamaño en segundos del archivo.</returns>
public long CalcularTamaño()
{
StringBuilder sbBuffer = new StringBuilder(MAX_PATH);
mciSendString("set " + sAlias + " time format milliseconds", null, 0, 0);
// Obtenemos el largo del archivo, en millisegundos.
mciSendString("status " + sAlias + " length", sbBuffer, MAX_PATH, 0);

// Sí el buffer contiene información,
if (sbBuffer.ToString() != "")
// la devolvemos, formateando la salida a entero largo;
return long.Parse(sbBuffer.ToString()) / 1000;
else // si no, devolvemos cero.
return 0L;
}
/// <summary>
/// Obtiene una cadena con la información sobre el tamaño (largo) del archivo.
/// </summary>
/// <returns>Largo del archivo de audio.</returns>
public string Tamaño()
{
    long sec = CalcularTamaño();
    long mins;

    // Si la cantidad de segundos es menor que 60 (1 minuto),
    if (sec < 60)
        // devolvemos la cadena formateada a 0:Segundos.
        return "0:" + String.Format("{0:D2}", sec);
        // Si los segundos son mayores que 59 (60 o más),
    else if (sec > 59)
    {
        mins = (int)(sec / 60);
        sec = sec - (mins * 60);
        // devolvemos la cadena formateada a Minustos:Segundos.
        return String.Format("{0:D2}", mins) + ":" + String.Format("{0:D2}", sec);
    }
    else 
        return "";
}

Anterior

Menú

Siguiente


ir al índice