5
votes

I have an application which plays the streamed audio data (like a chat client). The workflow involves three simple steps:

  1. The file header info (sample rate, bits per sample and num of channels) is sent first.
  2. Audio waveout device is initialized based on the above parameters.
  3. Audio (pcm) data is sent and is played on the above device.

The data receiving code is native (C code). and it reads data on a socket. Then it calls the managed C# code, which uses Naudio library to initialize device and play audio.

Now the problem is that, I am seeing some delay in audio playback. I have already instrumented the rest of my code (specifically: transferring data on socket and passing it back to managed code) and that seems to be okay. The whole transfer process is taking around 600 micro seconds, but after I assign the buffer to Naudio, it seems to start playing it after some time (around 200-250 milliseconds).

Here is my C# class that handles the audio playing part:

class foo
{
    static  IWavePlayer     s_WaveOut;
    static  WaveFormat      s_WaveOutFormat;
    static  BufferedWaveProvider    s_WaveProvider;
    static  byte[]          s_Samples       = new byte[10000];

    // called from native code to init deivce with specified sample rate and num of channels
    private static void DeviceInit(int rate, int bits, int channels)
    {
        s_WaveOut   = new WaveOut(WaveCallbackInfo.FunctionCallback());
        s_WaveOutFormat = new WaveFormat(rate, bits, channels);
        s_WaveProvider  = new BufferedWaveProvider(s_WaveOutFormat);

        s_WaveProvider.DiscardOnBufferOverflow      = true;
        s_WaveProvider.BufferLength         = 5 * 1024 * 1024;

        s_WaveOut.Init(s_WaveProvider);
        s_WaveOut.Play();
    }

    // called from native 'C' code upon receiving audio packates
    private unsafe static void PlayDataCallback(
        IntPtr buff,
        Int32 size) 
    {
        Marshal.Copy(buff, s_Samples, 0, size);
        s_WaveProvider.AddSamples(s_Samples, 0, size);
    }
}

Anyone has any idea on what might be causing the delay or am I using it (Naudio) in some wrong way.

I tried the same Naudio library to play a wav file and that seems to work perfect, the problem is coming only when I am adding samples after initialing the device myself.

[update] If I change s_WaveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()); to s_WaveOut = new DirectSound();, the performance is much better. If after this, I modify the Naudio source to set playback thread priority to Highest (default is Normal), the performace improves further, but as expected, the process starts consuming high resources.

Thank you,

Vikram

2
I think the buffering in the audio subsystem adds the delay. It may be purely for sound continuity reasons (the device must have enough data prebuffered or else it will play silence noticeable as gaps and pops).Alexey Frunze
@ALex, I was thinking the same, but then, there is no delay when I play a video file, the audio and video is in sync. Also if I play a game, the sound for different actions comes instantly. May be I am missing some configuration?Vikram.exe

2 Answers

4
votes

I also develop audio streaming application using NAudio. We also have latency issue. It reaches 300 ms.

The capture happens 10 times per second (once a 100 ms).

Using the advice of Vikram.exe to use DirectSoundOut instead of WaveOut helped a bit. The latency decreased by 50 or 100 ms, but only if I set Desired Latency to 50 ms.

new DirectSoundOut(guid, 50);

One more trick has lowered the latency by 100 or 200 ms. We check if there is a sound being played and skip new frames if it is.

if (s_WaveProvider.BufferedDuration <= 100)
    s_WaveProvider.AddSamples(s_Samples, 0, size);

There is still some work to be done in regards of sound smoothness, but generally we have no latency now.

1
votes

There is always latency introduced with audio playback. For WaveOut, you can specify the number of buffers and the overall desired latency. You can also specify buffer sizes for the other driver models too. For most playback scenarios, two buffers of 100ms each is ideal as it is reasonably responsive and will not stutter except under extreme load. However, you can go lower if you need to, but run the risk of it not being able to fill the next buffer in time. Don't expect as low latencies as you could get in a DAW (e.g. 5ms) with NAudio, it is not so highly optimised, and the .NET framework is not particularly well suited for this kind of application anyway due to the garbage collector.

Here's an example of setting the output latency for WaveOut:

var waveOut = new WaveOut();
waveout.DesiredLatency = 50; // 50ms latency