Pregunta

¿Hay alguna manera en C # para reproducir audio (por ejemplo, MP3) directamente desde un System.IO.Stream que, por ejemplo, se retiró de una solicitud web sin guardar los datos temporalmente en el disco?


Solución con NAudio

Con la ayuda de NAudio 1.3 es posible:

  1. Cargue un archivo MP3 desde una URL en un MemoryStream
  2. Convierta datos MP3 en datos de onda después de que se haya cargado completamente
  3. Reproduzca los datos de onda utilizando la clase WaveOut de NAudio

Hubiera sido bueno poder reproducir incluso un archivo MP3 medio cargado, pero esto parece ser imposible debido a NAudio diseño de biblioteca.

Y esta es la función que hará el trabajo:

    public static void PlayMp3FromUrl(string url)
    {
        using (Stream ms = new MemoryStream())
        {
            using (Stream stream = WebRequest.Create(url)
                .GetResponse().GetResponseStream())
            {
                byte[] buffer = new byte[32768];
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
            }

            ms.Position = 0;
            using (WaveStream blockAlignedStream =
                new BlockAlignReductionStream(
                    WaveFormatConversionStream.CreatePcmStream(
                        new Mp3FileReader(ms))))
            {
                using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                {
                    waveOut.Init(blockAlignedStream);
                    waveOut.Play();                        
                    while (waveOut.PlaybackState == PlaybackState.Playing )                        
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
        }
    }
¿Fue útil?

Solución

Editar: respuesta actualizada para reflejar los cambios en las versiones recientes de NAudio

Es posible usar la NAudio biblioteca de audio .NET de código abierto que he escrito. Busca un códec ACM en su PC para realizar la conversión. El Mp3FileReader suministrado con NAudio actualmente espera poder reposicionarse dentro de la transmisión de origen (crea un índice de tramas MP3 por adelantado), por lo que no es apropiado para la transmisión a través de la red. Sin embargo, aún puede usar las clases MP3Frame y AcmMp3FrameDecompressor en NAudio para descomprimir MP3 transmitidos sobre la marcha.

He publicado un artículo en mi blog que explica cómo reproducir una transmisión de MP3 con NAudio . Esencialmente tiene un hilo que descarga marcos MP3, descomprimiéndolos y almacenándolos en un BufferedWaveProvider . Luego se reproduce otro subproceso utilizando el BufferedWaveProvider como entrada.

Otros consejos

La clase SoundPlayer puede hacer esto. Parece que todo lo que tienes que hacer es establecer su propiedad Stream en la transmisión, luego llamar a Play .

editar
Sin embargo, no creo que pueda reproducir archivos MP3; parece limitado a .wav. No estoy seguro de si hay algo en el marco que pueda reproducir un archivo MP3 directamente. Todo lo que encuentro sobre eso implica usar un control WMP o interactuar con DirectX.

Bass puede hacer exactamente esto. Reproduzca desde el byte [] en la memoria o en un archivo a través de delegados donde devuelva los datos, para que pueda reproducirlos tan pronto como tenga suficientes datos para comenzar la reproducción ...

Modifiqué ligeramente la fuente de inicio del tema, por lo que ahora puede reproducir un archivo no cargado completamente. Aquí está (tenga en cuenta que es solo una muestra y es un punto de partida; debe hacer algunas excepciones y manejo de errores aquí):

private Stream ms = new MemoryStream();
public void PlayMp3FromUrl(string url)
{
    new Thread(delegate(object o)
    {
        var response = WebRequest.Create(url).GetResponse();
        using (var stream = response.GetResponseStream())
        {
            byte[] buffer = new byte[65536]; // 64KB chunks
            int read;
            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
            {
                var pos = ms.Position;
                ms.Position = ms.Length;
                ms.Write(buffer, 0, read);
                ms.Position = pos;
            }
        }
    }).Start();

    // Pre-buffering some data to allow NAudio to start playing
    while (ms.Length < 65536*10)
        Thread.Sleep(1000);

    ms.Position = 0;
    using (WaveStream blockAlignedStream = new BlockAlignReductionStream(WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader(ms))))
    {
        using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
        {
            waveOut.Init(blockAlignedStream);
            waveOut.Play();
            while (waveOut.PlaybackState == PlaybackState.Playing)
            {
                System.Threading.Thread.Sleep(100);
            }
        }
    }
}

NAudio envuelve la API WaveOutXXXX. No he mirado la fuente, pero si NAudio expone la función waveOutWrite () de una manera que no detiene automáticamente la reproducción en cada llamada, entonces debería poder hacer lo que realmente quiere, que es comenzar a reproducir el transmisión de audio antes de recibir todos los datos.

El uso de la función waveOutWrite () le permite "leer con antelación" y volcar fragmentos más pequeños de audio en la cola de salida: Windows reproducirá automáticamente los fragmentos sin problemas. Su código tendría que tomar el flujo de audio comprimido y convertirlo en pequeños fragmentos de audio WAV sobre la marcha; Esta parte sería realmente difícil: todas las bibliotecas y componentes que he visto hacen la conversión de MP3 a WAV un archivo completo a la vez. Probablemente, su única posibilidad realista es hacer esto usando WMA en lugar de MP3, porque puede escribir envoltorios simples de C # alrededor del SDK multimedia.

He modificado la fuente publicada en la pregunta para permitir el uso con la API TTS de Google para responder a la pregunta aquí :

bool waiting = false;
AutoResetEvent stop = new AutoResetEvent(false);
public void PlayMp3FromUrl(string url, int timeout)
{
    using (Stream ms = new MemoryStream())
    {
        using (Stream stream = WebRequest.Create(url)
            .GetResponse().GetResponseStream())
        {
            byte[] buffer = new byte[32768];
            int read;
            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
        }
        ms.Position = 0;
        using (WaveStream blockAlignedStream =
            new BlockAlignReductionStream(
                WaveFormatConversionStream.CreatePcmStream(
                    new Mp3FileReader(ms))))
        {
            using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
            {
                waveOut.Init(blockAlignedStream);
                waveOut.PlaybackStopped += (sender, e) =>
                {
                    waveOut.Stop();
                };
                waveOut.Play();
                waiting = true;
                stop.WaitOne(timeout);
                waiting = false;
            }
        }
    }
}

Invocar con:

var playThread = new Thread(timeout => PlayMp3FromUrl("http://translate.google.com/translate_tts?q=" + HttpUtility.UrlEncode(relatedLabel.Text), (int)timeout));
playThread.IsBackground = true;
playThread.Start(10000);

Terminar con:

if (waiting)
    stop.Set();

Observe que estoy usando el ParameterizedThreadDelegate en el código anterior, y el hilo se inicia con playThread.Start (10000); . El 10000 representa un máximo de 10 segundos de audio que se reproducirá, por lo que será necesario ajustarlo si la transmisión tarda más que eso en reproducirse. Esto es necesario porque la versión actual de NAudio (v1.5.4.0) parece tener un problema para determinar cuándo finaliza la reproducción. Puede que se solucione en una versión posterior o tal vez haya una solución alternativa que no me tomé el tiempo de encontrar.

Envolví la biblioteca de decodificadores de MP3 y la puse a disposición de los desarrolladores de .NET como < a href = "http://sourceforge.net/projects/mpg123net/" rel = "nofollow noreferrer"> mpg123.net .

Se incluyen las muestras para convertir archivos MP3 a PCM , y leer etiquetas ID3 .

Siempre he usado FMOD para cosas como esta porque es gratis para uso no comercial y funciona bien.

Dicho esto, con mucho gusto cambiaría a algo más pequeño (FMOD es ~ 300k) y de código abierto. Súper puntos de bonificación si está completamente administrado para que pueda compilarlo / fusionarlo con mi .exe y no tener que tener mucho cuidado para llevarlo a otras plataformas ...

(FMOD también hace portabilidad pero obviamente necesitaría diferentes binarios para diferentes plataformas)

No lo he probado desde un WebRequest, pero tanto el Windows Media Player < a href = "http://en.wikipedia.org/wiki/ActiveX" rel = "nofollow noreferrer"> ActiveX y MediaElement (de WPF ) son capaces de reproducir y almacenar en búfer transmisiones MP3.

Lo uso para reproducir datos provenientes de una transmisión SHOUTcast y funcionó muy bien. Sin embargo, no estoy seguro de si funcionará en el escenario que propone.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top