使用C#播放stream中的audio

有没有办法在C#播放audio(例如,MP3)直接从一个System.IO.Stream ,例如从WebRequest returend没有临时保存到磁盘的数据?


NAudio解决scheme

在NAudio 1.3的帮助下,可以:

  1. 从一个URL加载一个MP3文件到一个MemoryStream
  2. 完全加载后,将MP3数据转换为波形数据
  3. 使用NAudio的WaveOut类播放波形数据

如果能够播放一个半载的MP3文件本来就不错,但由于NAudio库的devise,这似乎是不可能的。

这是做这项工作的function:

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

编辑:答案更新,以反映近期版本的NAudio的变化

有可能使用我写的NAudio开源.NETaudio库。 它会在PC上寻找一个ACM编解码器来完成转换。 NAudio提供的Mp3FileReader目前希望能够在源码stream中重新定位(它先build立了MP3帧的索引),所以它不适合在networking上传输。 但是,您仍然可以使用MP3FrameAcmMp3FrameDecompressor类来即时解压缩stream式MP3。

我在博客上发布了一篇文章,解释如何使用NAudio播放MP3stream 。 基本上你有一个线程下载MP3帧,解压缩并将它们存储在BufferedWaveProvider 。 另一个线程然后使用BufferedWaveProvider作为input进行回放。

SoundPlayer类可以做到这一点。 它看起来像你所要做的就是将其Stream属性设置为stream,然后调用Play

编辑
我不认为它可以播放MP3文件; 似乎仅限于.wav。 我不确定框架中是否有可以直接播放MP3文件的内容。 我发现的一切都涉及到使用WMP控件或与DirectX交互。

低音可以做到这一点。 从内存中的字节[]或通过文件播放代表您返回数据的位置,因此只要有足够的数据即可开始播放。

我稍微修改了主题源代码,所以现在可以播放未完全加载的文件。 在这里(注意,这仅仅是一个示例,是一个开始点,你需要在这里做一些exception和error handling):

 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包装WaveOutXXXX API。 我没有看到源代码,但是如果NAudio公开waveOutWrite()函数的方式不会在每次调用时自动停止播放,那么你应该能够做到你真正想要的,也就是开始播放audiostream在您收到所有数据之前。

使用waveOutWrite()函数可以“提前读取”并将较小的audio块转储到输出队列中 – Windows会自动无缝地播放这些块。 你的代码将不得不采取压缩audiostream,并将其转换成小块的WAVaudio飞行; 这部分将是非常困难的 – 我见过的所有库和组件一次完成MP3到WAV转换整个文件。 也许你唯一现实的机会就是使用WMA而不是MP3,因为你可以在多媒体SDK上编写简单的C#包装器。

我已经调整了问题中发布的来源,以允许与Google的TTS API一起使用,以回答以下问题:

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

调用:

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

终止:

 if (waiting) stop.Set(); 

注意,我在上面的代码中使用了ParameterizedThreadDelegate ,并且线程是以playThread.Start(10000); 。 10000表示要播放的audio的最大时间为10秒,因此如果您的数据stream比播放时间长,则需要对其进行调整。 这是必要的,因为当前版本的NAudio(v1.5.4.0)在确定stream播放完成时似乎有问题。 它可能会在更高版本中修复,也可能有一个解决方法,我没有花时间去查找。

我包装了MP3解码器库,并将它作为mpg123.net提供给.NET开发人员。

包括将MP3文件转换为PCM的示例,以及读取ID3标签。

我一直使用FMOD来做这样的事情,因为它对于非商业用途是免费的,并且运作良好。

也就是说,我很乐意改用更小的(FMOD约为300k)和开源的。 超级奖励点,如果它是完全托pipe,以便我可以编译/合并它与我的.exe,而不必格外小心,以获得其他平台的便携性…

(FMOD也具有可移植性,但是对于不同的平台,您显然需要不同的二进制文件)

我没有从WebRequest尝试过,但Windows Media Player ActiveX和MediaElement(来自WPF )组件都能够播放和缓冲MP3stream。

我用它来播放来自SHOUTcaststream的数据,效果很好。 但是,我不确定它是否会在您提出的scheme中起作用。