Domanda

Esiste un modo in C # per riprodurre l'audio (ad esempio, MP3) direttamente da un System.IO.Stream che ad esempio è stato ripreso da una richiesta Web senza salvare i dati temporaneamente sul disco?


Soluzione con NAudio

Con l'aiuto di NAudio 1.3 è possibile:

  1. Carica un file MP3 da un URL in un MemoryStream
  2. Converti i dati MP3 in dati wave dopo che sono stati completamente caricati
  3. Riproduci i dati wave usando la classe WaveOut di NAudio

Sarebbe stato bello poter anche riprodurre un file MP3 a metà carico, ma questo sembra impossibile a causa del NAudio progetto della biblioteca.

E questa è la funzione che farà il lavoro:

    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);
                    }
                }
            }
        }
    }
È stato utile?

Soluzione

Modifica: risposta aggiornata per riflettere le modifiche nelle versioni recenti di NAudio

È possibile utilizzare la NAudio libreria audio .NET open source che ho scritto. Cerca un codec ACM sul PC per eseguire la conversione. Il Mp3FileReader fornito con NAudio attualmente prevede di essere in grado di riposizionarsi all'interno del flusso sorgente (crea un indice di frame MP3 in primo piano), quindi non è appropriato per lo streaming sulla rete. Tuttavia, puoi comunque utilizzare le classi MP3Frame e AcmMp3FrameDecompressor in NAudio per decomprimere MP3 in streaming al volo.

Ho pubblicato un articolo sul mio blog che spiega come riprodurre un flusso MP3 usando NAudio . Essenzialmente hai un thread per scaricare i frame MP3, decomprimerli e archiviarli in un BufferedWaveProvider . Un altro thread viene quindi riprodotto utilizzando BufferedWaveProvider come input.

Altri suggerimenti

La classe SoundPlayer può farlo. Sembra che tutto ciò che devi fare è impostare la sua proprietà Stream sullo stream, quindi chiamare Riproduci .

modifica
Non penso che possa riprodurre file MP3 però; sembra limitato a .wav. Non sono sicuro che ci sia qualcosa nel framework in grado di riprodurre direttamente un file MP3. Tutto ciò che trovo riguarda l'utilizzo di un controllo WMP o l'interazione con DirectX.

Bass può fare proprio questo. Riproduci da Byte [] in memoria o da un delegato di file attraverso il quale restituisci i dati, in modo da poter giocare non appena hai abbastanza dati per avviare la riproduzione.

Ho leggermente modificato la fonte di avvio dell'argomento, in modo che ora possa riprodurre un file non completamente caricato. Eccolo (nota, che è solo un esempio ed è un punto da cui iniziare; devi fare alcune eccezioni e gestione degli errori qui):

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 racchiude l'API WaveOutXXXX. Non ho guardato la fonte, ma se NAudio espone la funzione waveOutWrite () in un modo che non interrompe automaticamente la riproduzione ad ogni chiamata, allora dovresti essere in grado di fare quello che vuoi davvero, ovvero iniziare a giocare flusso audio prima di aver ricevuto tutti i dati.

L'uso della funzione waveOutWrite () ti consente di " leggere in anticipo " e scaricare piccoli pezzi di audio nella coda di output - Windows riprodurrà automaticamente i blocchi senza soluzione di continuità. Il tuo codice dovrebbe prendere il flusso audio compresso e convertirlo in piccoli pezzi di audio WAV al volo; questa parte sarebbe davvero difficile - tutte le librerie e i componenti che abbia mai visto fare la conversione da MP3 a WAV un intero file alla volta. Probabilmente la tua unica possibilità realistica è farlo usando WMA anziché MP3, perché puoi scrivere semplici wrapper C # attorno all'SDK multimediale.

Ho ottimizzato la fonte pubblicata nella domanda per consentire l'utilizzo con l'API TTS di Google al fine di rispondere alla domanda qui :

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;
            }
        }
    }
}

Richiama 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);

Termina con:

if (waiting)
    stop.Set();

Nota che sto usando ParameterizedThreadDelegate nel codice sopra, e il thread è iniziato con playThread.Start (10000); . Il 10000 rappresenta un massimo di 10 secondi di audio da riprodurre, quindi sarà necessario modificarlo se lo streaming richiede più tempo di quello per essere riprodotto. Ciò è necessario perché l'attuale versione di NAudio (v1.5.4.0) sembra avere un problema nel determinare quando lo streaming è terminato. Potrebbe essere risolto in una versione successiva o forse c'è una soluzione alternativa che non ho impiegato del tempo per trovare.

Ho avvolto la libreria di decodificatori MP3 e l'ho resa disponibile per gli sviluppatori .NET come < a href = "http://sourceforge.net/projects/mpg123net/" rel = "nofollow noreferrer"> mpg123.net .

Sono inclusi gli esempi per convertire i file MP3 in PCM e leggere ID3 tag.

Ho sempre usato FMOD per cose del genere perché è gratuito per uso non commerciale e funziona bene.

Detto questo, sarei felice di passare a qualcosa che è più piccolo (FMOD è ~ 300k) e open-source. Super punti bonus se è completamente gestito in modo da poterlo compilare / unire con il mio .exe e non devo prestare particolare attenzione per ottenere la portabilità su altre piattaforme ...

(Anche FMOD fa portabilità ma ovviamente avresti bisogno di binari diversi per piattaforme diverse)

Non l'ho provato da un WebRequest, ma entrambi Windows Media Player < a href = "http://en.wikipedia.org/wiki/ActiveX" rel = "nofollow noreferrer"> ActiveX e MediaElement (da WPF ) sono in grado di riprodurre e bufferizzare stream MP3.

Lo uso per riprodurre dati provenienti da un SHOUTcast e ha funzionato benissimo. Tuttavia, non sono sicuro che funzionerà nello scenario che proponi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top