我正在播放相同的声音文件(在它们之间随机选择)每秒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();
}
答案 0 :(得分:4)
如果您移除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();
答案 1 :(得分:1)
我使用此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; }
}