如何使用 NAudio 将声音文件加载到内存中并稍后使用?
Posted
技术标签:
【中文标题】如何使用 NAudio 将声音文件加载到内存中并稍后使用?【英文标题】:How can I load a sound file into memory using NAudio and use it later? 【发布时间】:2018-05-08 19:50:32 【问题描述】:我每秒播放 5 次相同的声音文件(在它们之间随机选择),而且我总是加载到内存中,因此程序占用了大量内存。如何将声音文件加载到内存中,然后从那里开始?我正在使用 NAudio。当前代码:
var sound = "sounds/test.mp3";
using (var audioFile = new AudioFileReader(sound))
using (var outputDevice = new WaveOutEvent())
outputDevice.Init(audioFile);
outputDevice.Play();
while (outputDevice.PlaybackState == PlaybackState.Playing)
Thread.Sleep(1000);
threadStop();
【问题讨论】:
【参考方案1】:如果您删除 using
块,则不会释放 audioFile
和 outputDevice
。然后您可以将它们保留在内存中,每次播放音频时都会使用相同的引用。
使用using
块,您会重复实例化其内存可能不会立即释放的 NAudio 对象。
var sound = "sounds/test.mp3";
var audioFile = new AudioFileReader(sound);
var outputDevice = new WaveOutEvent();
outputDevice.Init(audioFile);
outputDevice.Play();
while (outputDevice.PlaybackState == PlaybackState.Playing)
Thread.Sleep(1000);
threadStop();
【讨论】:
但是这些变量每秒被替换约 5 次,所以整个地方都会出现内存泄漏。 然后为每个唯一的音频文件实例化一组对象。更新您的问题可能有助于澄清您在做什么。您的问题只是使用单个音频文件源,并且您声明它是“相同的声音文件”。 你没看错,我有多个声音文件。它们在按下一个键时播放。如果我理解正确,我应该为每个声音文件实例化一个新的 AudioFileReader 吗?(但只有一次) 这将是我的方法,为每个文件实例化一个新的AudioFileReader
。【参考方案2】:
我使用article 中的代码解决了整个问题。
它使用MixingSampleProvider
。我将声音加载到一个名为:CachedSound
的自定义类中。然后我使用另一个名为AudioPlaybackEngine
的类来播放它们。它处理混音器,我使用CachedSoundSampleProvider
类来读取缓存的声音。
代码如下所示:
class AudioPlaybackEngine : IDisposable
private readonly IWavePlayer outputDevice;
private readonly MixingSampleProvider mixer;
public AudioPlaybackEngine(int sampleRate = 44100, int channelCount = 2)
outputDevice = new WaveOutEvent();
mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channelCount));
mixer.ReadFully = true;
outputDevice.Init(mixer);
outputDevice.Play();
public void PlaySound(string fileName)
var input = new AudioFileReader(fileName);
AddMixerInput(new AutoDisposeFileReader(input));
private ISampleProvider ConvertToRightChannelCount(ISampleProvider input)
if (input.WaveFormat.Channels == mixer.WaveFormat.Channels)
return input;
if (input.WaveFormat.Channels == 1 && mixer.WaveFormat.Channels == 2)
return new MonoToStereoSampleProvider(input);
throw new NotImplementedException("Not yet implemented this channel count conversion");
public void PlaySound(CachedSound sound)
AddMixerInput(new CachedSoundSampleProvider(sound));
private void AddMixerInput(ISampleProvider input)
mixer.AddMixerInput(ConvertToRightChannelCount(input));
public void Dispose()
outputDevice.Dispose();
public static readonly AudioPlaybackEngine Instance = new AudioPlaybackEngine(44100, 2);
class CachedSound
public float[] AudioData get; private set;
public WaveFormat WaveFormat get; private set;
public CachedSound(string audioFileName)
using (var audioFileReader = new AudioFileReader(audioFileName))
// TODO: could add resampling in here if required
WaveFormat = audioFileReader.WaveFormat;
var wholeFile = new List<float>((int)(audioFileReader.Length / 4));
var readBuffer= new float[audioFileReader.WaveFormat.SampleRate * audioFileReader.WaveFormat.Channels];
int samplesRead;
while((samplesRead = audioFileReader.Read(readBuffer,0,readBuffer.Length)) > 0)
wholeFile.AddRange(readBuffer.Take(samplesRead));
AudioData = wholeFile.ToArray();
class CachedSoundSampleProvider : ISampleProvider
private readonly CachedSound cachedSound;
private long position;
public CachedSoundSampleProvider(CachedSound cachedSound)
this.cachedSound = cachedSound;
public int Read(float[] buffer, int offset, int count)
var availableSamples = cachedSound.AudioData.Length - position;
var samplesToCopy = Math.Min(availableSamples, count);
Array.Copy(cachedSound.AudioData, position, buffer, offset, samplesToCopy);
position += samplesToCopy;
return (int)samplesToCopy;
public WaveFormat WaveFormat get return cachedSound.WaveFormat;
// This class automatically disposes the file reader that it contains.
class AutoDisposeFileReader : ISampleProvider
private readonly AudioFileReader reader;
private bool isDisposed;
public AutoDisposeFileReader(AudioFileReader reader)
this.reader = reader;
this.WaveFormat = reader.WaveFormat;
public int Read(float[] buffer, int offset, int count)
if (isDisposed)
return 0;
int read = reader.Read(buffer, offset, count);
if (read == 0)
reader.Dispose();
isDisposed = true;
return read;
public WaveFormat WaveFormat get; private set;
【讨论】:
您好,我尝试使用您发布的此解决方案,但我无法通过扬声器听到任何声音,尽管我可以看到绿色条在“音量混合器”中上下移动,但没有显示任何内容在“设置”->“扬声器音量”中。帮忙? 你还能听到什么吗? (Youtube,音乐之类的东西) 是的,我可以,其他所有应用程序都可以,但我的应用程序没有输出真实的声音,尽管它显示在“音量混合器”中。 好的,我终于发现了我的问题,AudioPlaybackEngine 是静态的,它实际上是在另一个线程中实例化的,而不是我调用它的线程。而且我不完全知道为什么,但这阻止了它的工作。它现在可以工作,因为我在与调用 PlaySound() 的线程相同的线程中实例化 AudioPlaybackEngine。以上是关于如何使用 NAudio 将声音文件加载到内存中并稍后使用?的主要内容,如果未能解决你的问题,请参考以下文章
如何将带有 dll 的应用程序从内存加载到 AppDomain 中并执行它?
NAudio 拆分 wav 文件问题。其他所有音频文件声音失真,而其他音频文件清晰
如何使用内存流、NAudio 和 LameMP3 将音频 aiff 转换为 MP3