切换到DirectSound后流式回放口吃问题

时间:2015-02-14 00:45:14

标签: audio-streaming naudio directsound

我一直在使用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来播放文件。如果你的机器受到类似的折磨,这会导致你经历过口吃行为。

感谢任何帮助,因为我怀疑这是一个复杂的问题。无论是那个还是我是个白痴,我都错过了一些明显的东西。

0 个答案:

没有答案