Reproduzir áudio de um stream usando c #

Existe uma maneira em C # para reproduzir áudio (por exemplo, MP3) diretamente de um System.IO.Stream que, por exemplo, foi returend de um WebRequest sem salvar os dados temporariamente para o disco?


Solução com o NAudio

Com a ajuda do NAudio 1.3, é possível:

  1. Carregar um arquivo MP3 de um URL em um MemoryStream
  2. Converta dados MP3 em dados de onda depois de ter sido completamente carregado
  3. Reproduza os dados de onda usando a class WaveOut do NAudio

Teria sido bom poder tocar até mesmo um arquivo MP3 carregado pela metade, mas isso parece ser impossível devido ao design da biblioteca do NAudio .

E esta é a function que fará o trabalho:

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

Editar: resposta atualizada para refletir as alterações nas versões recentes do NAudio

É possível usar a biblioteca de áudio .NET open source do NAudio que escrevi. Ele procura por um codec ACM no seu PC para fazer a conversão. O Mp3FileReader fornecido com o NAudio atualmente espera ser capaz de reposicionar dentro do stream de origem (ele cria um índice de frameworks MP3 na frente), portanto, não é apropriado para o streaming pela rede. No entanto, você ainda pode usar as classs AcmMp3FrameDecompressor e AcmMp3FrameDecompressor no NAudio para descompactar o MP3 transmitido em tempo real.

Eu publiquei um artigo no meu blog explicando como reproduzir um stream de MP3 usando o NAudio . Essencialmente, você tem um thread de download de frameworks MP3, descompactando-os e armazenando-os em um BufferedWaveProvider . Outro thread, em seguida, é reproduzido usando o BufferedWaveProvider como uma input.

A class SoundPlayer pode fazer isso. Parece que tudo o que você precisa fazer é definir sua propriedade Stream para o stream e chamar Play .

editar
Eu não acho que ele pode reproduzir arquivos MP3; parece limitado a .wav. Não tenho certeza se há algo na estrutura que possa reproduzir um arquivo MP3 diretamente. Tudo o que eu acho sobre isso envolve usar um controle WMP ou interagir com o DirectX.

Bass pode fazer isso. Tocar a partir de Byte [] na memory ou através de um arquivo delegates onde você retornar os dados, então com isso você pode jogar assim que você tiver dados suficientes para iniciar a reprodução ..

Eu modifiquei ligeiramente a fonte inicial do tópico, para que agora ele possa reproduzir um arquivo não totalmente carregado. Aqui está (note que é apenas uma amostra e é um ponto para começar; você precisa fazer algumas exceções e manipular erros aqui):

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

O NAudio envolve a API do WaveOutXXXX. Eu não olhei para a fonte, mas se o NAudio expõe a function waveOutWrite () de uma maneira que não pare automaticamente a reprodução em cada chamada, então você deve ser capaz de fazer o que você realmente quer, que é começar a tocar stream de áudio antes de você receber todos os dados.

O uso da function waveOutWrite () permite que você “leia adiante” e despeje pequenos trechos de áudio na fila de saída – o Windows reproduzirá automaticamente os trechos sem problemas. Seu código teria que levar o stream de áudio compactado e convertê-lo em pequenos pedaços de áudio WAV em tempo real; essa parte seria realmente difícil – todas as bibliotecas e componentes que eu já vi fazem a conversão de MP3 para WAV, um arquivo inteiro de cada vez. Provavelmente, sua única chance real é fazer isso usando WMA em vez de MP3, porque você pode escrever simples invólucros C # em torno do SDK de multimídia.

Alterei a fonte postada na pergunta para permitir o uso da API TTS do Google para responder à pergunta aqui :

 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 com:

 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 com:

 if (waiting) stop.Set(); 

Observe que estou usando o ParameterizedThreadDelegate no código acima e o thread é iniciado com playThread.Start(10000); . O 10000 representa um máximo de 10 segundos de áudio a ser reproduzido, portanto ele precisará ser ajustado se seu stream demorar mais do que o necessário para tocar. Isso é necessário porque a versão atual do NAudio (v1.5.4.0) parece ter um problema ao determinar quando o stream é executado. Pode ser corrigido em uma versão posterior ou talvez haja uma solução alternativa que eu não reservei tempo para encontrar.

Eu embrulhei a biblioteca de decodificadores de MP3 e a disponibilizei para desenvolvedores .NET como mpg123.net .

Incluem-se as amostras para converter arquivos MP3 para PCM e ler tags ID3 .

Eu sempre usei FMOD para coisas assim porque é grátis para uso não comercial e funciona bem.

Dito isso, eu gostaria de mudar para algo menor (FMOD é ~ 300k) e open-source. Pontos de bônus super se for totalmente gerenciado para que eu possa compilar / mesclar com o meu .exe e não ter que tomar cuidado extra para obter portabilidade para outras plataformas …

(FMOD faz portabilidade também, mas você obviamente precisa de binários diferentes para diferentes plataformas)

Eu não tentei isso de um WebRequest, mas tanto o Windows Media Player ActiveX quanto o MediaElement (do WPF ) componentes são capazes de reproduzir e armazenar em buffer streams de MP3.

Eu o uso para reproduzir dados vindos de um stream do SHOUTcast e funcionou muito bem. No entanto, não tenho certeza se isso funcionará no cenário que você propõe.