我一直在使用NAudio,特别是我的Spotify客户端应用程序Blindspot的旧WaveOut输出API。一切都很和谐,很好。或者至少就我所知。
最近,我转而使用DirectSound,因为它可以让我轻松有效地让用户选择和更改他们的输出设备,并在应用程序和计算机重启之间保留这些设置,这是一个请求的功能。
在我的开发机器,我的笔记本电脑上一切正常。所以我忙着把它安装在另一台机器上,我曾经将它作为测试机器用于在发布之前进行健全性检查。我很高兴在这种情况下,因为自从切换到DirectSound后声音质量大幅下降。我在大多数音轨上每隔几秒左右就会收到一种剪辑声。如果我的CPU(或者可能只是.NET)变得特别繁忙而且它通常感觉更糟,我会得到不好的口吃,即使这个API应该更新更好。在使用WaveOut之前,我在这两台机器上都没有注意到这些症状。
获取两台机器上的一些背景信息。两者都运行Windows 7,均为64位,均采用DX11。开发机器(它可以正常工作)是带有SSD的I7超极本,而它没有工作的机器是I5台式机而且没有固态硬盘,但如果这样的话,我会感到惊讶。这个问题。有趣的是,在它没有工作的机器上,如果我使用一组带有自己声卡而不是普通耳机插孔的USB耳机,问题就会消失。但是,在切换到DirectSound之前使用我的程序的旧版本仍然可以在没有USB耳机的情况下完美运行,所以我不认为它是专属的。
我做错了什么?是我的代码错了,还是DirectSound可能不是这项工作的合适工具?
我不会在这里发布我的所有代码,因为我已经有几个类来处理这个项目中的回放。但是,我会发布一些展示我正在做的事情。如果这不是最好的音频播放代码,我也很抱歉。我承认,我只是在播放声音或者最多在3D空间中播放音频时播放音频。流媒体对我来说是一个新领域。
编辑:此代码已按建议进行了简化。看起来好多了吧?
// this is the main object that controls playback in the application
public class PlaybackManager : IDisposable
{
private IWavePlayer waveOut;
private Guid _playbackDeviceID;
...
public void AddBytesToPlayingStream(byte[] bytes)
{
if (bufferedWaveProvider != null)
bufferedWaveProvider.AddSamples(bytes, 0, bytes.Length);
}
...
// modified from NAudio sample code but working with incoming bytes
private void GetStreaming(object state)
{
this.fullyDownloaded = false;
try
{
do
{
if (bufferedWaveProvider == null)
{
this.bufferedWaveProvider = new BufferedWaveProvider(new WaveFormat(44100, 2));
this.bufferedWaveProvider.BufferDuration = TimeSpan.FromMinutes(20); // allow us to get well ahead of ourselves
Logger.WriteDebug("Creating buffered wave provider");
}
// in case its still null
if (bufferedWaveProvider == null)
continue;
var freeBufferBytes = bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes;
if (freeBufferBytes < bufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4)
{
Logger.WriteDebug("Buffer getting full, taking a break");
Thread.Sleep(450);
}
Thread.Sleep(250);
} while (playbackState != StreamingPlaybackState.Stopped);
Logger.WriteDebug("Playback stopped");
}
finally
{
// no post-processing work here, right?
}
}
private void timer1_Tick(object sender, System.Timers.ElapsedEventArgs e)
{
if (playbackState != StreamingPlaybackState.Stopped)
{
if (this.waveOut == null && this.bufferedWaveProvider != null)
{
Logger.WriteDebug("Creating WaveOut Device");
this.waveOut = CreateWaveOut();
waveOut.PlaybackStopped += waveOut_PlaybackStopped;
this.volumeProvider = new VolumeWaveProvider16(bufferedWaveProvider);
this.volumeProvider.Volume = this.volume;
waveOut.Init(volumeProvider);
}
if (bufferedWaveProvider != null)
{
var bufferedSeconds = bufferedWaveProvider.BufferedDuration.TotalSeconds;
// make it stutter less if we buffer up a decent amount before playing
if (bufferedSeconds < 0.5 && this.playbackState == StreamingPlaybackState.Playing && !this.fullyDownloaded)
{
this.playbackState = StreamingPlaybackState.Buffering;
waveOut.Pause();
Logger.WriteDebug(String.Format("Paused to buffer, waveOut.PlaybackState={0}", waveOut.PlaybackState));
}
else if (bufferedSeconds > secondsToBuffer && this.playbackState == StreamingPlaybackState.Buffering)
{
waveOut.Play();
Logger.WriteDebug(String.Format("Started playing, waveOut.PlaybackState={0}", waveOut.PlaybackState));
this.playbackState = StreamingPlaybackState.Playing;
}
// stop when right at the end
else if (this.fullyDownloaded && bufferedSeconds == 0)
{
Logger.WriteDebug("Reached end of stream");
Stop();
// handle end of track logic
if (OnEndOfTrack != null)
OnEndOfTrack();
}
}
}
}
private IWavePlayer CreateWaveOut()
{
// used to be return new WaveOutEvent();
return new DirectSoundOut(PlaybackDeviceID);
}
...
}
如果在完整和上下文中查看代码有帮助,您可以查看new code,或查看旧版本中的diff of the changes。
当然,您也欢迎下载源代码并尝试使用Blindspot.Playback项目通过人工将字节发送到Playback Manager来播放文件。如果你的机器受到类似的折磨,这会导致你经历过口吃行为。
感谢任何帮助,因为我怀疑这是一个复杂的问题。无论是那个还是我是个白痴,我都错过了一些明显的东西。