C#中有没有办法直接从 System.IO.Stream ,例如从WebRequest回退而不将数据临时保存到磁盘?


NAudio

的解决方案

NAudio 1.3的帮助下,可以:

  1. 将URL中的MP3文件加载到MemoryStream
  2. 完成加载后将MP3数据转换为wave数据
  3. 使用 NAudio 的WaveOut课程播放wave数据
  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);
                        }
                    }
                }
            }
        }
    
有帮助吗?

解决方案

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

的变化

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

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

其他提示

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

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

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

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

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()函数,那么你应该能够做你真正想要的,即开始播放收到所有数据之前的音频流。

使用waveOutWrite()函数可以让您“预先读取”并将较小的音频块转储到输出队列中--Windows将自动无缝地播放这些块。您的代码必须采用压缩音频流并将其转换为小块WAV音频;这部分真的很难 - 我见过的所有库和组件都是MP3-to-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表示最多播放10秒的音频,因此如果您的流需要比播放的时间更长,则需要调整它。这是必要的,因为当前版本的NAudio(v1.5.4.0)似乎在确定流何时完成播放时存在问题。它可以在以后的版本中修复,或者可能有一个我没有花时间查找的解决方法。

我将MP3解码器库包装起来,并将其作为 .NET 开发人员提供给< a href =“http://sourceforge.net/projects/mpg123net/”rel =“nofollow noreferrer”> mpg123.net 。

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

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

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

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

我没有尝试过WebRequest,但是 Windows Media Player < a href =“http://en.wikipedia.org/wiki/ActiveX”rel =“nofollow noreferrer”> ActiveX 和MediaElement(来自 WPF )组件能够播放和缓冲MP3流。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top