我正在使用 C#、WPF 和 NAudio 来播放
wav
文件。
我在资源文件夹中有
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();
但我想将
wav
文件嵌入到 exe
中并使用类似以下内容:
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();
我想出了如何让它发挥作用。
此解决方案的问题是内存使用率较高。
// 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();
// 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();
}
}
}
这是一个简单的解决方案,它也适用于 .aif 和 aiff 文件,并且不需要拆分 .mp3 和 .wav 文件。可以使用
AudioFileReader
类来完成,但该类不支持 Stream
,仅支持文件路径。但是,我们可以通过使用一些反射来创建带有 AudioFileReader
的 Stream
实例来跳过这个问题。只需将代码粘贴到代码中的某个位置即可:
namespace NAudio.Extensions
{
public static class Extensions
{
public static AudioFileReader GetAudioFileReaderFromStream(Stream stream, string fileExt)
{
try
{
AudioFileReader reader = (AudioFileReader)FormatterServices.GetUninitializedObject(typeof(AudioFileReader));
// this is how the constructor looks
//lockObject = new object();
//FileName = fileName;
//CreateReaderStream(fileName);
//sourceBytesPerSample = readerStream.WaveFormat.BitsPerSample / 8 * readerStream.WaveFormat.Channels;
//sampleChannel = new SampleChannel(readerStream, forceStereo: false);
//destBytesPerSample = 4 * sampleChannel.WaveFormat.Channels;
//length = SourceToDest(readerStream.Length);
Type type = reader.GetType();
type.GetField("lockObject", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(reader, new object());
var readerStream = GetWaveStream(stream, fileExt);
type.GetField("readerStream", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(reader, readerStream);
var sbps = readerStream.WaveFormat.BitsPerSample / 8 * readerStream.WaveFormat.Channels;
type.GetField("sourceBytesPerSample", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(reader, sbps);
var sampleChannel = new SampleChannel(readerStream, forceStereo: false);
type.GetField("sampleChannel", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(reader, sampleChannel);
var dbps = 4 * sampleChannel.WaveFormat.Channels;
type.GetField("destBytesPerSample", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(reader, dbps);
var std = dbps * (readerStream.Length / sbps);
type.GetField("length", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(reader, std);
return reader;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
return null;
}
}
private static WaveStream GetWaveStream(Stream stream, string fileExt)
{
WaveStream readerStream = null;
if (fileExt.Equals(".wav", StringComparison.OrdinalIgnoreCase))
{
readerStream = new WaveFileReader(stream);
if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm && readerStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
{
readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
readerStream = new BlockAlignReductionStream(readerStream);
}
}
else if (fileExt.Equals(".mp3", StringComparison.OrdinalIgnoreCase))
{
if (Environment.OSVersion.Version.Major < 6)
{
readerStream = new Mp3FileReader(stream);
}
else
{
readerStream = new StreamMediaFoundationReader(stream);
}
}
else if (fileExt.Equals(".aiff", StringComparison.OrdinalIgnoreCase) || fileExt.Equals(".aif", StringComparison.OrdinalIgnoreCase))
{
readerStream = new AiffFileReader(stream);
}
else
{
readerStream = new StreamMediaFoundationReader(stream);
}
return readerStream;
}
}
}
然后你就可以像这样使用它,只需指定文件类型(“.wav”,“.mp3”,“.aif”或“.aiff”):
AudioFileReader reader = NAudio.Extensions.Extensions.GetAudioFileReaderFromStream(myStream, ".wav");