【问题标题】:How to play WAV file from Embedded Resources with NAudio?如何使用 NAudio 播放嵌入式资源中的 WAV 文件?
【发布时间】:2018-08-17 02:27:15
【问题描述】:

我正在使用 C#、WPF 和 NAudio 播放 wav 文件。

我在 Resources 文件夹中有 sound_1.wav 并包含在项目中。编译完成后,它将 exe 和资源导出到一个文件夹,并从硬盘驱动器上的路径播放 wav。

string sound1 = "Resources\\sound_1.wav";

NAudio.Wave.WaveFileReader wav = new NAudio.Wave.WaveFileReader(sound1);
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();

但我想在exe 中嵌入wav 文件并使用类似:

UnmanagedMemoryStream sound1 = Properties.Resources.sound_1; //embedded resource
NAudio.Wave.WaveFileReader wav = new NAudio.Wave.WaveFileReader(sound1);

我怎样才能让它通过WaveFileReader 播放?它只接受string 路径。


解决方案

这可行,但声音以慢动作播放,听起来像混响。

UnmanagedMemoryStream sound1 = Properties.Resources.sound_1;

WaveIn wavin = new WaveIn();
NAudio.Wave.RawSourceWaveStream wav = new NAudio.Wave.RawSourceWaveStream(sound1, wavin.WaveFormat);         
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();

这适用于声音结束时响亮的爆裂声。

将流转换为字节数组
https://stackoverflow.com/a/1080445/6806643

byte[] b = ReadToEnd(sound1);

WaveStream wav = new RawSourceWaveStream(new MemoryStream(b), new WaveFormat(44100, 16, 2));
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();

【问题讨论】:

  • @PaulF 它似乎仍然需要一个字符串路径。
  • 我不确定你的意思 - WaveFileReader 接受 Stream 作为构造函数 - 这可以是从字节数组创建的 MemoryStream。您通过提取嵌入资源来创建字节数组。
  • @PaulF 他们使用String filename。当我将其更改为 UnmanagedMemoryStream filename 以提取嵌入式资源时,它仍然需要 String 用于 a.GetManifestResourceStream(filename)
  • @MattMcManis:像 PaulF 提供的第一个链接(Rotem 接受的答案)一样,将 sound1 传递给 GetManifestResourceStream 不起作用吗?

标签: c# naudio


【解决方案1】:

我想出了如何让它发挥作用。

此解决方案的问题是内存使用率高。

WAV

// embedded resource sound.wav

UnmanagedMemoryStream sound = Properties.Resources.sound;

MemoryStream ms = new MemoryStream(StreamToBytes(sound));

WaveStream ws = new WaveFileReader(ms);

WaveOutEvent output = new WaveOutEvent();

output.PlaybackStopped += new EventHandler<StoppedEventArgs>(Media_Ended);
output.Init(ws);
output.Play();

MP3

// embedded resource sound.mp3

MemoryStream sound = new MemoryStream(Properties.Resources.sound);

MemoryStream ms = new MemoryStream(StreamToBytes(sound));

WaveStream ws = new Mp3FileReader(ms);

WaveOutEvent output = new WaveOutEvent();

output.PlaybackStopped += new EventHandler<StoppedEventArgs>(Media_Ended);
output.Init(ws);
output.Play();

流到字节数组

https://stackoverflow.com/a/1080445/6806643

我用它把MemoryStream转换成byte[],否则如果Exception "Not a WAVE file - no RIFF header"同时播放2个声音,它会崩溃。


处置

似乎对减少 RAM 没有任何影响。

public static void Media_Ended(object sender, EventArgs e)
{
    if (output.PlaybackState == PlaybackState.Stopped)
    {
        if (ms != null)
        {
            ms.Close();
            ms.Flush();
        }
        if (ws != null)
        {
            ws.Close();
        }
        if (output != null)
        {
            output.Dispose();
        }
    }
}

【讨论】:

  • 您可能需要为每个流实例调用 Dispose,因为它们都实现了 IDisposable。
  • @PaulF 我尝试在Media_Ended() 中调用Close()Dispose()。这样它就不会在播放时关闭声音。但是内存使用量永远不会下降。
  • 是的,我可以在每次按键后将其放入Media_Ended(),但这有点矫枉过正。没区别。此处的黄线显示了调用 GC 的时间。 i.stack.imgur.com/Mat1a.jpg
  • 还尝试将输出设置为 null 并清除停止的事件处理程序,这可能使波保持活动状态,从而使输入流保持活动状态
  • @MarkHeath 如何清除已停止的事件处理程序?它说它是只读的,如果我这样做output.PlaybackState = null
猜你喜欢
  • 1970-01-01
  • 2012-04-16
  • 2022-06-21
  • 1970-01-01
  • 2013-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-06
相关资源
最近更新 更多