Riproduci audio da un flusso usando C #


99

C'è un modo in C # per riprodurre l'audio (ad esempio, MP3) direttamente da un System.IO.Stream che ad esempio è stato ritrasmesso da un WebRequest senza salvare temporaneamente i dati 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 è stato completamente caricato
  3. Riprodurre i dati d'onda utilizzando NAudio s' class WaveOut

Sarebbe stato bello poter riprodurre anche un file MP3 caricato a metà, ma questo sembra essere impossibile a causa del design della libreria NAudio .

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

4
bello vedere che hai funzionato. Non sarebbe troppo faticoso riprodurlo correttamente durante lo streaming. Il problema principale è che Mp3FileReader attualmente si aspetta di conoscere la lunghezza in anticipo. Cercherò di aggiungere una demo per la prossima versione di NAudio
Mark Heath

@Mark Heath hai già risolto il problema e aggiunto la demo nell'attuale versione di NAudio o è ancora nella tua pipline?
Martin

paura non ancora, anche se con le modifiche apportate in NAudio 1.3 non richiederà troppe modifiche per farlo funzionare.
Mark Heath

Mark: Devo modificare in NAudio per farlo funzionare, perché ho appena scaricato NAudio1.3 ma accetta il codice precedente senza modifiche, ma d'altra parte lanciando un'eccezione che dice qualcosa come "ACM Conversion not possible".
Mubashar

a proposito, sto cercando di giocare seguendo translate.google.com/translate_tts?q=I+love+techcrunch
Mubashar

Risposte:


56

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

È possibile utilizzare la libreria audio .NET open source di NAudio che ho scritto. Cerca un codec ACM sul tuo PC per eseguire la conversione. L'Mp3FileReader fornito con NAudio attualmente si aspetta 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 ancora usare le classi MP3Framee AcmMp3FrameDecompressorin NAudio per decomprimere MP3 in streaming al volo.

Ho pubblicato un articolo sul mio blog che spiega come riprodurre un flusso MP3 utilizzando NAudio . Essenzialmente hai un thread che scarica frame MP3, decomprimendoli e memorizzandoli in un file BufferedWaveProvider. Un altro thread viene quindi riprodotto utilizzando BufferedWaveProvidercome input.


3
La libreria NAudio viene ora fornita con un'applicazione di esempio chiamata Mp3StreamingDemo che dovrebbe fornire tutto ciò di cui si ha bisogno per trasmettere in streaming un MP3 dalla rete.
Martin

È possibile utilizzare la tua libreria per trasmettere in live streaming microfono / linea in ingresso a un dispositivo Android?
realtebo

10

La classe SoundPlayer può farlo. Sembra che tutto ciò che devi fare sia impostare la sua proprietà Stream sul flusso, quindi chiamare Play.

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


1
Sono anni che cerco di trovare una libreria .NET che codifica e decodifica MP3, ma non credo che esista.
MusiGenesis

NAudio dovrebbe fare il lavoro per te - almeno per la decodifica (vedi primo post)
Martin

4
SoundPlayer ha problemi con GC e riprodurrà in modo casuale spazzatura a meno che non si utilizzi la soluzione alternativa ufficiale di Microsoft (fare clic su Soluzioni alternative): connect.microsoft.com/VisualStudio/feedback/…
Roman Starkov

SoundPlayer + memoryStream ha un bug di design. quando si riproduce la prima seconda o terza volta, si aggiunge uno strano rumore nel mezzo dell'audio.
bh_earth0

Il collegamento della soluzione alternativa non funziona più, ma è su Wayback Machine: web.archive.org/web/20120722231139/http://connect.microsoft.com/…
Forestrf

5

Il basso può fare proprio questo. Riproduci da Byte [] in memoria o un file delegato in cui restituisci i dati, in modo che tu possa suonare non appena hai abbastanza dati per avviare la riproduzione ..


3

Ho leggermente modificato la fonte di avvio dell'argomento, in modo che ora possa riprodurre un file non completamente caricato. Eccolo qui (nota che è solo un esempio ed è un punto da cui iniziare; devi fare alcune eccezioni e la 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);
            }
        }
    }
}

Hai testato il tuo codice? Se è così, con quale versione di NAudio ha funzionato?
Martin,

Inoltre, il tuo codice sembra essere abbastanza soggetto a errori di threading. Sei sicuro di sapere cosa stai facendo?
Martin

1) Sì, l'ho fatto. 2) Con l'ultima versione. 3) È solo una prova di concetto come ho affermato nel mio messaggio precedente.
ReVolly

Come si definisce "ultima versione"? È la versione 1.3 o l'origine alla revisione 68454?
Martin

@Martin Scusa, non sono qui da molto tempo. Il NAudio.dll che ho usato ha la versione 1.3.8.
ReVolly

2

Ho ottimizzato la fonte pubblicata nella domanda per consentirne l'utilizzo con l'API TTS di Google per 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;
            }
        }
    }
}

Invoca 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();

Si noti che sto usando ParameterizedThreadDelegatenel codice sopra e il thread viene avviato con playThread.Start(10000);. Il 10000 rappresenta un massimo di 10 secondi di audio da riprodurre, quindi dovrà essere ottimizzato se lo streaming impiega più tempo per essere riprodotto. Ciò è necessario perché la versione corrente 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 avuto il tempo di trovare.


1

NAudio avvolge l'API WaveOutXXXX. Non ho esaminato la fonte, ma se NAudio espone la funzione waveOutWrite () in un modo che non interrompe automaticamente la riproduzione ad ogni chiamata, dovresti essere in grado di fare ciò che vuoi veramente, ovvero iniziare a riprodurre il flusso audio prima di aver ricevuto tutti i dati.

L'utilizzo della funzione waveOutWrite () ti consente di "leggere" e scaricare porzioni più piccole di audio nella coda di output: Windows riprodurrà automaticamente le parti senza interruzioni. 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 io abbia mai visto eseguire la conversione da MP3 a WAV un intero file alla volta. Probabilmente la tua unica possibilità realistica è di farlo usando WMA invece di MP3, perché puoi scrivere semplici wrapper C # attorno all'SDK multimediale.


1

Ho inserito la libreria del decodificatore MP3 e l'ho resa disponibile per gli sviluppatori .NET come mpg123.net .

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


Grande! Non vedo l'ora di darci un'occhiata questo fine settimana.
Larry Smithmier

1

Non l'ho provato da un WebRequest, ma entrambi i componenti ActiveX di Windows Media Player e MediaElement (da WPF ) sono in grado di riprodurre e memorizzare i flussi MP3.

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


0

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

Detto questo, passerei volentieri a qualcosa di 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 dover prestare particolare attenzione per ottenere la portabilità su altre piattaforme ...

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.