0

I'm using NAudio and the WasapiLoopbackCapture in combination with WaveOut in order to create an echo effect that is global, i.e. affects the current audio output.

However, my implementation, which seems pretty poor to me, doesn't work well at all. On every machine it sounds different, sometimes it even crashes.

This is the code I came up with. It records the audio output and every 75ms it plays the recorded stream using WaveOut.

What am I doing wrong here, how can this be improved?

private static void CreateReverb()
{
    MemoryStream loopbackWave = new MemoryStream();
    DateTime lastEcho = DateTime.Now;

    WasapiLoopbackCapture waveIn = new WasapiLoopbackCapture();
    waveIn.StartRecording();
    waveIn.DataAvailable += delegate(object sender, WaveInEventArgs e)
    {
        loopbackWave.Write(e.Buffer, 0, e.BytesRecorded);
        if ((DateTime.Now - lastEcho).TotalMilliseconds > 75)
        {
            lastEcho = DateTime.Now;
            byte[] loopbackBytes = loopbackWave.ToArray();
            loopbackWave.Dispose();
            loopbackWave = new MemoryStream();

            using (MemoryStream waveStream = new MemoryStream())
            {
                using (WaveFileWriter waveFileWriter = new WaveFileWriter(waveStream, waveIn.WaveFormat))
                {
                    waveFileWriter.Write(loopbackBytes, 0, loopbackBytes.Length);
                }
                loopbackBytes = waveStream.ToArray();
            }

            for (int i = 0; i < 3; i++)
            {
                ThreadPool.QueueUserWorkItem(delegate
                {
                    try
                    {
                        WaveOut waveOut = new WaveOut();
                        waveOut.Init(new WaveFileReader(new MemoryStream(loopbackBytes)));
                        waveOut.Volume = ReverbIntensity * .5f;
                        waveOut.Play();
                        Thread.Sleep(200);
                        waveOut.Dispose();
                    }
                    catch { }
                });
            }
        }
    };
}

Edit: Second attempt:

This time, I tried using BufferedWaveProvider as suggested by Mark. The problem is that the playback has no delay, thus creating a feedback loop instead of an echo. The output has to be about .5f in volume and have to have about 75ms delay in order to make it work.

private static void CreateReverb()
{
    DateTime lastEcho = DateTime.Now;

    WasapiLoopbackCapture waveIn = new WasapiLoopbackCapture();
    BufferedWaveProvider bufferedWaveProvider = new BufferedWaveProvider(waveIn.WaveFormat);
    WasapiOut wasapiOut = new WasapiOut(AudioClientShareMode.Shared, 0);

    bufferedWaveProvider.DiscardOnBufferOverflow = true;
    wasapiOut.Init(bufferedWaveProvider);
    wasapiOut.Play();
    waveIn.StartRecording();

    waveIn.DataAvailable += delegate(object sender, WaveInEventArgs e)
    {
        bufferedWaveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
    };
}
bytecode77
  • 14,163
  • 30
  • 110
  • 141

1 Answers1

1

Don't keep opening hundreds of instances of WaveOut. You should have just one instance of WaveOut, that is playing from a custom stream that starts with the recorded audio (a BufferedWaveProvider would be a good starting point), and then pass that through your effect. The best way to do this is to create an implementation of ISampleProvider that in its Read method reads from a source provider, and then performs the effect.

Finally, you are capturing sound with loopback audio and then playing it back out of the same device, which in itself is likely to be creating a nasty feedback loop.

Mark Heath
  • 48,273
  • 29
  • 137
  • 194
  • So when I use `bufferedWaveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);` in my `DataAvailable` method, how do I make the buffered wave provider play the recorded audio with a delay of 75ms? – bytecode77 Mar 12 '15 at 21:19
  • I posted the code using BufferedWaveProvider. Yes, it does carry some problems. I think the only thing that's left to do is creating a delay and lowering the volume. – bytecode77 Mar 12 '15 at 21:34
  • The problem is the feedback loop because there's neither a delay, nor does it play back with lower volume. Any idea? – bytecode77 Mar 14 '15 at 00:43
  • If you are trying to apply a reverb effect to the output of all applications, I don't think it is possible with NAudio, as you can't avoid creating a feedback loop. – Mark Heath Mar 14 '15 at 09:02
  • But it already (kinda) worked with my poor solution. All I have to do is output the input with a delay and with lower volume. This will create an echo effect, but my updated version doesn't have a delay or lower volume, so therefore it has a feedback loop. Do you have any idea on how to solve this? – bytecode77 Mar 14 '15 at 12:53
  • Got it now, I had to use a `VolumeSampleProvider`. Thanks for your help! – bytecode77 Mar 14 '15 at 23:07