Streaming audio contained in arrays to NAudio

21 Views Asked by At

I am having difficulty streaming data from my application to an audio device. In my application, the audio is captured from a software defined radio as two arrays of doubles, one array representing the left channel and one the right channel. The sampling rate of the signal is 2MHz. Each array is 32768 in size. I need to down convert this to 192kHz and then output it to a sound device. I am using NAudio as shown in the code below, to downs ample and then output the data to the sound device. The problem is that there are gaps in the output each timde an array is transferred. Here is the code that uses NAudio

using System;
using System.Linq;
using System.Windows.Forms;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using System.Threading;
class CWSkimmer
{
    public static void SkimmerOutput(double[] leftChannel, double[] rightChannel)
    {
        int desiredSampleRate = 192000;    // 192kHz
        int originalSampleRate = 2000000;
        // Downsample the signals
        double[] downsampledLeft = Downsample(leftChannel, originalSampleRate, desiredSampleRate);
        double[] downsampledRight = Downsample(rightChannel, originalSampleRate, desiredSampleRate);

        // Output the downsampled stereo signal to the sound device
        PlayAudio(downsampledLeft, downsampledRight, desiredSampleRate);
    }

    static double[] Downsample(double[] signal, int originalSampleRate, int desiredSampleRate)
    {
        int downsamplingFactor = originalSampleRate / desiredSampleRate;
        int newLength = signal.Length / downsamplingFactor;

        double[] downsampledSignal = new double[newLength];

        for (int i = 0, j = 0; i < signal.Length && j < newLength; i += downsamplingFactor, j++)
        {
            downsampledSignal[j] = signal[i];
        }

        return downsampledSignal;
    }

    static void PlayAudio(double[] leftChannel, double[] rightChannel, int sampleRate, int outputDeviceIndex = 2)
    {
        int length = Math.Min(leftChannel.Length, rightChannel.Length);

        using (var waveOut = new WaveOutEvent())
        
          
        using (var waveProvider = new StereoSampleProvider(leftChannel, rightChannel, sampleRate))
        {

            waveOut.DeviceNumber = 2;

            // Set the correct WaveFormat
            waveProvider.WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, 2); // Stereo audio

            waveOut.Init(waveProvider);

            waveOut.Play();

            while (waveOut.PlaybackState == PlaybackState.Playing)
            {
                // Wait for playback to complete
                System.Threading.Thread.Sleep(100);
            }
        }
    }

    public static WaveOutCapabilities[] GetOutputDevices()
    {
        return Enumerable.Range(0, WaveOut.DeviceCount)
            .Select(i => WaveOut.GetCapabilities(i))
            .ToArray();
    }


}

class StereoSampleProvider : IWaveProvider, IDisposable
{
    private readonly double[] leftChannel;
    private readonly double[] rightChannel;
    private readonly int sampleRate;
    private int position;

    public StereoSampleProvider(double[] leftChannel, double[] rightChannel, int sampleRate)
    {
        this.leftChannel = leftChannel;
        this.rightChannel = rightChannel;
        this.sampleRate = sampleRate;
    }

    public int Read(byte[] buffer, int offset, int count)
    {
        int samplesRead = 0;

        for (int i = 0; i < count / 8; i++)
        {
            if (position < leftChannel.Length && position < rightChannel.Length)
            {
                float leftSample = (float)leftChannel[position];
                float rightSample = (float)rightChannel[position];

                BitConverter.GetBytes(leftSample).CopyTo(buffer, offset + i * 8);
                BitConverter.GetBytes(rightSample).CopyTo(buffer, offset + i * 8 + 4);

                position++;
                samplesRead += 8; // 4 bytes per channel (left and right)
            }
            else
            {
                break; // End of data
            }
        }

        return samplesRead;
    }

    public WaveFormat WaveFormat { get; set; }
  
}

To simplify things, I created a sine wave generator that works for 10 seconds and generated a single packet representing 10 seconds of 400Hz signal. This works perfectly fine, and I get 10 seconds of uninterrupted audio.

private void button1_Click(object sender, EventArgs e)
{
    double frequency = 100.0;     // Frequency of the sine wave in Hz
    int sampleRate = 2000000;  // Sampling rate in Hz (2 MHz)
    double durationSeconds = 5.0; // Duration of the signal in seconds

    double[] generatedSignal = SignalGenerator.GenerateSineWave(frequency, sampleRate, durationSeconds);

    CWSkimmer.SkimmerOutput(generatedSignal, generatedSignal);

    // Now 'generatedSignal' contains the samples of a 400 Hz sine wave at a 2 MHz sampling rate
    
}

To see where I am having the issue with audio gaps I broke up this generated signal into two arrays instead of one and sent it to the output. It now has a gap in the audio output. Here is the code

private void button1_Click(object sender, EventArgs e)
{
    double frequency = 400.0;     // Frequency of the sine wave in Hz
    int sampleRate = 2000000;  // Sampling rate in Hz (2 MHz)
    double durationSeconds = 5.0; // Duration of the signal in seconds

    double[] packet1 = new double[5000000];
    double[] packet2 = new double[5000000];


    double[] generatedSignal = SignalGenerator.GenerateSineWave(frequency, sampleRate, durationSeconds);

    //create two packets and see what happens

    for (int i = 0; i < 5000000; i++)
    {
        packet1[i] = generatedSignal[i];
    }

    for (int i = 5000000; i < 10000000; i++)
    {
        packet2[i-5000000] = generatedSignal[i];
    }

    CWSkimmer.SkimmerOutput(packet1, packet1);
    CWSkimmer.SkimmerOutput(packet2, packet2);

    // Now 'generatedSignal' contains the samples of a 400 Hz sine wave at a 2 MHz sampling rate
    // You can use this array for further processing or output.
}

In the real life case, the packets are 32768 double in size and every time a packet is sent to the output class there is a gap. What I need is a way to put this in a buffer and not keep calling the output. What am I doing wrong? Many thanks.

0

There are 0 best solutions below