Join multiple SampleProvider instances together with a gap between each in NAudio

Viewed 57

I am making a class that takes a character array and generates a DTMF tone sequence using NAudio. The tones get generated, each returned as a WaveProvider. My problem is I need to add a gap between each tone, and nothing I've tried has worked.

This is (currently) how I'm generating the sequence:

public static MemoryStream CreateStreamFromDtmfSequence(DtmfTone[] dtmfTones, int toneDuration, int pauseDuration, int sampleRate, double amplitude)
    {
        WaveFormat waveFormat = Config.getWaveFormat(sampleRate);
        MemoryStream memoryStream = new MemoryStream();
        //WaveFileWriter waveFileWriter = new WaveFileWriter(new IgnoreDisposeStream(memoryStream), waveFormat);

        // Combine tones / sample providers

        MixingWaveProvider32 mixer = new MixingWaveProvider32();

        int delayValue = 0;
        foreach (DtmfTone tone in dtmfTones)
        {
            OffsetSampleProvider offsetSampleProvider = new OffsetSampleProvider(tone.AsWaveProvider(sampleRate, toneDuration, amplitude, pauseDuration).ToSampleProvider());
            offsetSampleProvider.DelayBy = TimeSpan.FromMilliseconds(toneDuration + delayValue);
            SampleToWaveProvider samples = new SampleToWaveProvider(offsetSampleProvider);

            mixer.AddInputStream(samples);

            delayValue += pauseDuration;
        }

        WaveFileWriter.WriteWavFileToStream(memoryStream, mixer);
        //waveFileWriter.Close();
        return memoryStream;
    }

There's silence at the beginning of the final stream, but not between the tones themselves like there's supposed to be, and I cannot for the hell of me figure out why.

Here are a couple screenshots of the generated waveform in Adobe Audition. For reference, each tone is 300ms long and is supposed to have a 300ms pause following.

Generated waveform Single tone selected

Anyone see a mistake I made somewhere? Is there something I'm missing?

1 Answers

Turns out the answer was just RTFM. They updated the documentation since I last looked (a while ago), and it's now more thorough. From that, I was able to modify my method to work properly.

Instead of using the mixer, I switched to using ConcatenatingSampleProvider to schedule each ISampleProvider instance to play in order... including the generated silence!

Here's the revised code:

public static MemoryStream CreateStreamFromDtmfSequence(DtmfTone[] dtmfTones, int toneDuration, int pauseDuration, int sampleRate, double amplitude)
    {
        WaveFormat waveFormat = Config.getWaveFormat(sampleRate);
        MemoryStream memoryStream = new MemoryStream();

        // Join sample providers together (silence between tones)
        List<ISampleProvider> master = new List<ISampleProvider>();

        foreach (DtmfTone tone in dtmfTones)
        {                
            // Group sample providers
            List<ISampleProvider> sampleProviders = new List<ISampleProvider>();
            sampleProviders.Add(tone.AsWaveProvider(sampleRate, toneDuration, amplitude).ToSampleProvider());

            // Generate silence track
            var silence = new SilenceProvider(waveFormat).ToSampleProvider().Take(TimeSpan.FromMilliseconds(pauseDuration));
            sampleProviders.Add(silence);
            
            // Combine everything
            ISampleProvider playlist = new ConcatenatingSampleProvider(sampleProviders.ToArray());
            master.Add(playlist);
        }

        WaveFileWriter.WriteWavFileToStream(memoryStream, new ConcatenatingSampleProvider(master).ToWaveProvider());
        return memoryStream;
    }

The new waveform now looks like this: Waveform: 300ms tones; 300ms silence; 0.8 amplitude ...as it should.

Related