Divirtiéndonos con sonidos en C#

En este artículo os enseñamos cómo puedes hacer uso de archivos de sonido en tu aplicación, reproduciendo múltiples archivos de sonido al mismo tiempo. Existen al menos 3 maneras de reproducir archivos de sonido en una aplicación Windows Forms:

  • Utilizando System.Media.SoundPlayer
  • Utilizando el control de Windows Media Player
  • Utilizando winmm.dll mciSendString

Si necesitas reproducir un solo archivo de sonido en formato wav, System.Media.SoundPlayer sería la manera más simple. Pero con esto no puedes reproducir archivos mp3 y no se pueden reproducir más de un archivo al mismo tiempo. El control de Windows Media Player es lo ideal si quieres tener una interfaz de usuario bonita para controlar la reproducción de archivos multimedia. Si aún así deseas hacer uso de una manera más simple para reproducir tus archivos de sonido y necesitas más flexibilidad de la que te puede ofrecer System.Media.SoundPlayer, tal vez te interese la función mciSendString winmm.dll.

Puedes encontrar la documentación de MSDN para winmm.dll mciSendString aquí mismo:

Documentación de MSDN mcSendString
Documentación mcSendString command string

La función mciSendString de winmm.dll se puede utilizar para reproducir diferentes tipos de archivos multimedia y cuenta con funciones bastante extensas. En este artículo trataremos sólo con archivos de sonido en formato wav y mp3.

El código

El código que puedes ver más abajo es la implementación de la clase MciPlayer para envolver las funciones necesarias que se utilizan.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace MCIDEMO
{
    class MciPlayer
    {

        [DllImport("winmm.dll")]
        private static extern int mciSendString(String strCommand, StringBuilder strReturn, int iReturnLength, IntPtr hwndCallback);
        [DllImport("winmm.dll")]
        public static extern int mciGetErrorString(int errCode, StringBuilder errMsg, int buflen);
        [DllImport("winmm.dll")]
        public static extern int mciGetDeviceID(string lpszDevice);

        public MciPlayer()
        {

        }

        public MciPlayer(string filename, string alias)
        {
            _medialocation = filename;
            _alias = alias;
            LoadMediaFile(_medialocation, _alias);
        }

        int _deviceid = 0;

        public int Deviceid
        {
            get { return _deviceid; }
        }

        private bool _isloaded = false;

        public bool Isloaded
        {
            get { return _isloaded; }
            set { _isloaded = value; }
        }

        private string _medialocation = "";

        public string MediaLocation
        {
            get { return _medialocation; }
            set { _medialocation = value; }
        }
        private string _alias = "";

        public string Alias
        {
            get { return _alias; }
            set { _alias = value; }
        }

        public bool LoadMediaFile(string filename, string alias)
        {
            _medialocation = filename;
            _alias = alias;
            StopPlaying();
            CloseMediaFile();
            string Pcommand = "open "" + filename  + "" alias " + alias;
            int ret = mciSendString(Pcommand, null, 0, IntPtr.Zero); 
            _isloaded = (ret == 0) ? true : false;
            if (_isloaded)
                _deviceid = mciGetDeviceID(_alias);
            return _isloaded;
        }

        public void PlayFromStart()
        {
            if (_isloaded)
            {
                string Pcommand = "play " + Alias + " from 0";
                int ret = mciSendString(Pcommand, null, 0, IntPtr.Zero);
            }
        }

        public void PlayFromStart(IntPtr callback)
        {
            if (_isloaded)
            {
                string Pcommand = "play " + Alias + " from 0 notify";
                int ret = mciSendString(Pcommand, null, 0, callback);
            }
        }

        public void PlayLoop()
        {
            if (_isloaded)
            {
                string Pcommand = "play " + Alias + " repeat";
                int ret = mciSendString(Pcommand, null, 0, IntPtr.Zero);
            }
        }

        public void CloseMediaFile()
        {
            string Pcommand = "close " + Alias;
            int ret = mciSendString(Pcommand, null, 0, IntPtr.Zero);
            _isloaded = false;

        }

        public void StopPlaying()
        {
            string Pcommand = "stop " + Alias;
            int ret = mciSendString(Pcommand, null, 0, IntPtr.Zero);
        }

    }
}

Para utilizar la clase MciPlayer para reproducir un archivo de sonido, llamamos a su constructor, proporcionandole la ruta completa del sonido y un alias para el archivo, entonces llamamos a una de las funciones de reproducción. En el código de a continuación, el MciPlayer es instanciado por el constructor de MciPlayer (string filename, string alias) y el archivo se reproduce usando la función de reproducción de PlayFromStart.

string filename =  "Accordion-SoundBible.com-74362576.mp3";
MciPlayer m = new MciPlayer(Application.StartupPath + @"" + filename, "1");
m.PlayFromStart();

Ten en cuenta que el alias debe ser único para cada archivo. Hacemos uso del alias para informar al sistema MCI del archivo que queremos reproducir. Para reproducir varios archivos, sólo tenemos que crear una instancia de un MciPlayer para cada archivo, cada una con un alias único. Luego puedes reproducir cualquiera de los MciPlayer simultáneamente.

string filename =  "Accordion-SoundBible.com-74362576.mp3";
MciPlayer m = new MciPlayer(Application.StartupPath + @"" + filename, "1"); 
filename="Music_Box-Big_Daddy-1389738694.mp3";
MciPlayer m1 = new MciPlayer(Application.StartupPath + @"" + filename, "2");
m.PlayLoop();
m1.PlayLoop();  

Si se está reproduciendo un archivo y necesitas ser informado de si el archivo ha terminado de reproducirse, puedes utilizar PlayFromStart(IntPtr callback), donde el callback es un identificador de un formulario de Windows Forms que es el que recibirá la devolución de llamada. El sistema MCI desencadena la devolución de llamada enviando un mensaje de Windows MCINOTIFY MM (valor = 953) Para recibir este mensaje de Windows, el controlador callback reemplaza la función de WinProc() por defecto para el formulario.

protected override void WndProc(ref Message m)
{
    if (m.Msg == MM_MCINOTIFY)
    {
        // The file is done playing, do whatever
        System.Diagnostics.Debug.WriteLine(m.ToString());
        foreach (Form1.ListItem itm in ((Form1)this.parent).listBox1.Items)
        {
            if (itm.DeviceId == (int)m.LParam)
            {
                //To handle wav file play looping
                if (
                    (itm.Filename.Substring(itm.Filename.Length - 4).ToUpper() == ".WAV")
                    && ((int)m.WParam == MCI_NOTIFY_SUCCESSFUL)
                    && (itm.Playlooping)
                    )
                {
                    MciPlayer p = new MciPlayer();
                    p.Alias = itm.Alias;
                    p.Isloaded = true;
                    p.PlayFromStart(this.Handle);
                    break;
                }
                else
                {
                    listBox1.Items.Add(DateTime.Now.ToString() + " " + (string)itm.Filename);
                    break;
                }
            }
        }
    }
    base.WndProc(ref m);
}

En nuestra demostración, Form2 se utiliza para recibir la devolución de llamada cuando pulsa el botón de Play.

Form2 f2;    
f2 = new Form2();
....
    

        private void button1_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex < 0) return;
           
            ListItem itm=(ListItem)listBox1.SelectedItem;
            string filename = itm.ToString();
            MciPlayer m=null;

            if (itm.Alias != "")
            {
                m = new MciPlayer();
                m.Alias = itm.Alias;
                m.Isloaded = true;
            }
            else
            {
                string alias = "";
                m = CreateMCIPlayer(filename, ref alias);
                itm.Alias = alias;
                itm.DeviceId = m.Deviceid;
            }
            itm.Playlooping = false;
            m.PlayFromStart(f2.Handle);
        }

Nuestra demo solo muestra el tiempo en el que el sonido ha dejado de reproducirse y el nombre del archivo del archivo de sonido. Como "play... repeat" no es compatible con miciSendString() para archivos wav, también hacemos uso de la devolución de llamada para implementar un bucle de reproducción para archivos wav en nuestra reemplazada función Winproc().

Consideración en la carga de archivos

public bool LoadMediaFile(string filename, string alias)
{
    _medialocation = filename;
    _alias = alias;
    StopPlaying();
    CloseMediaFile();
    string Pcommand = "open "" + filename  + "" alias " + alias;

    int ret = mciSendString(Pcommand, null, 0, IntPtr.Zero);
    _isloaded = (ret == 0) ? true : false;

    if (_isloaded)
        _deviceid = mciGetDeviceID(_alias);

    return _isloaded;

}

Ten en cuenta que antes de cargar un archivo en el sistema MCI a través del comando abrir, tenemos que asegurarnos que ese alias no se ha utilizado. Para ello, primero deja de reproducir el archivo y cierra el archivo multimedia usando su alias.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP