使用C#播放流中的音频

时间:2008-10-08 20:19:23

标签: .net audio stream mp3 naudio

C#中是否有一种方法可以直接从System.IO.Stream播放音频(例如MP3),例如从WebRequest返回而不将数据暂时保存到磁盘?


NAudio

的解决方案

NAudio 1.3的帮助下,可以:

  1. 将URL中的MP3文件加载到MemoryStream
  2. 完成加载后将MP3数据转换为wave数据
  3. 使用NAudio的WaveOut类
  4. 播放波形数据

    能够播放半载MP3文件本来不错,但由于NAudio库设计,这似乎是不可能的。

    这是完成工作的功能:

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

9 个答案:

答案 0 :(得分:48)

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

的变化

可以使用我编写的NAudio开源.NET音频库。它会在您的PC上查找ACM编解码器以进行转换。 NAudio提供的Mp3FileReader目前希望能够在源流中重新定位(它预先构建MP3帧的索引),因此不适合通过网络进行流式传输。但是,您仍然可以使用NAudio中的MP3FrameAcmMp3FrameDecompressor类来动态解压缩流式MP3。

我在我的博客上发布了一篇文章,解释how to play back an MP3 stream using NAudio。基本上你有一个线程下载MP3帧,解压缩并存储在BufferedWaveProvider。然后另一个线程使用BufferedWaveProvider作为输入进行回放。

答案 1 :(得分:8)

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

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

答案 2 :(得分:5)

Bass可以做到这一点。从内存中的Byte []或在您返回数据的直通文件代理中播放,这样您就可以在有足够数据开始播放时立即播放..

答案 3 :(得分:3)

我稍微修改了主题入门源,因此它现在可以播放未完全加载的文件。这是(注意,它只是一个示例,是一个从头开始的点;你需要在这里做一些异常和错误处理):

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

答案 4 :(得分:1)

NAudio包装了WaveOutXXXX API。我没有查看源代码,但是如果NAudio以不会在每次调用时自动停止播放的方式暴露waveOutWrite()函数,那么你应该能够做你真正想要的,即开始播放收到所有数据之前的音频流。

使用waveOutWrite()函数可以“预读”并将较小的音频块转储到输出队列中--Windows将自动无缝地播放块。您的代码必须采用压缩音频流并将其转换为小块WAV音频;这部分真的很难 - 我见过的所有库和组件都是MP3-to-WAV一次转换整个文件。可能你唯一现实的机会是用WMA而不是MP3来做这件事,因为你可以在多媒体SDK上编写简单的C#包装。

答案 5 :(得分:1)

我将MP3解码器库包装起来,并以.NET的形式提供给mpg123.net开发人员。

包含将MP3文件转换为PCM并阅读ID3标签的示例。

答案 6 :(得分:1)

我已经调整了问题中发布的来源,允许使用Google的TTS API来回答问题here

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表示最多播放10秒的音频,因此如果您的流需要比播放的时间更长,则需要调整它。这是必要的,因为当前版本的NAudio(v1.5.4.0)似乎在确定流何时完成播放时存在问题。它可以在以后的版本中修复,或者可能有一个我没有花时间查找的解决方法。

答案 7 :(得分:0)

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

我用它来播放来自SHOUTcast流的数据,效果很好。但是,我不确定它是否适用于您提出的方案。

答案 8 :(得分:0)

我一直把FMOD用于这样的事情,因为它可以免费用于非商业用途并且效果很好。

那就是说,我很乐意转向更小(FMOD约为300k)和开源的东西。超级奖励积分,如果它是完全管理,以便我可以编译/合并我的.exe而不必格外小心,以获得其他平台的可移植性......

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