Visual Basic Graphing a Naudio WaveIn (does not add samples)

1.1k views Asked by At

I am looking into graphing a waveform from the WaveIn function in nAudio. I have found an example online that achieves exactly what I am attempting to do, however it uses an external graphing library (Scottplot), and it is programmed in C# (I need VB). I have successfully got it go graph on the standard chart, and basically translated all of the code, and although it compiles, my stream is empty. I have tried to solve this for weeks with no luck.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using NAudio.Wave;
using NAudio.CoreAudioApi;
using System.Numerics;

namespace microphone;

public partial class Form1 : Form
{
    public WaveIn wi;
    public BufferedWaveProvider bwp;
    //public int envelopeMax;

    private int RATE = 44100; // sample rate of the sound card
    private int BUFFERSIZE = 1 << 13; // must be a multiple of 2
    //private int BUFFERSIZE = 2048; // must be a multiple of 2

    public Form1()
    {
        InitializeComponent();

        WaveIn wi = new WaveIn();
        wi.DeviceNumber = 0;
        wi.WaveFormat = new NAudio.Wave.WaveFormat(RATE, 1);
        
        bwp = new BufferedWaveProvider(wi.WaveFormat);
        bwp.BufferLength = BUFFERSIZE * 2;
        bwp.DiscardOnBufferOverflow = true;

        wi.DataAvailable += (sender, e) => bwp.AddSamples(e.Buffer, 0, e.BytesRecorded);;
        wi.StartRecording();
    }

    public void timer1_Tick(object sender, EventArgs e)
    {
        int frameSize = BUFFERSIZE;
        byte[] frames = new byte[frameSize];

        bwp.Read(frames, 0, frameSize);

        if (frames.Length == 0)
            return;
        else if (frames[frameSize - 2] == 0)
        {
            label1.Text = "removed";
            return;
        }
        else
            label1.Text = "graphing";

        // convert it to int32 manually (and a double for scottplot)
        int SAMPLE_RESOLUTION = 16;
        int BYTES_PER_POINT = SAMPLE_RESOLUTION / 8;
        int[] vals = new int[frames.Length / BYTES_PER_POINT];
        double[] Ys = new double[frames.Length / BYTES_PER_POINT];
        double[] Xs = new double[frames.Length / BYTES_PER_POINT];
        // double[] Ys2 = new double[frames.Length / BYTES_PER_POINT];
        // double[] Xs2 = new double[frames.Length / BYTES_PER_POINT];

        for (int i = 0; i < vals.Length; ++i)
        {
            // bit shift the byte buffer into the right variable format
            byte hByte = frames[i * 2 + 1];
            byte lByte = frames[i * 2 + 0];
            vals[i] = (int)((hByte << 8) | lByte);
            Xs[i] = i;
            Ys[i] = vals[i];
            // Xs2[i] = i * .001 * RATE / Ys.Length; // units are in kHz
        }

        chart1.ChartAreas[0].AxisX.Maximum = 400;
        chart1.ChartAreas[0].AxisX.Minimum = 0;
        chart1.Series[0].Points.DataBindXY(Xs, Ys);

        /* Ys2 = FFT(Ys);
        // update scottplot (FFT, frequency domain)
        chart2.ChartAreas[0].AxisX.Maximum = 10;
        chart2.ChartAreas[0].AxisX.Minimum = 0;
        chart2.Series[0].Points.DataBindXY(Xs2.Take(Xs2.Length / 2).ToArray(), Ys2.Take(Ys2.Length / 2).ToArray());

        // update the displays
        Application.DoEvents();
        */
    }

    //public double[] FFT(double[] data)
    //{
    //    double[] fft = new double[data.Length]; // this is where we will store the output (fft)
    //    Complex[] fftComplex = new Complex[data.Length]; // the FFT function requires complex format
    //
    //    for (int i = 0; i < data.Length; ++i)
    //        fftComplex[i] = new(data[i], 0); // make it complex format (imaginary = 0)
    //
    //    Accord.Math.FourierTransform.FFT(fftComplex, Accord.Math.FourierTransform.Direction.Forward);
    //
    //    for (int i = 0; i < data.Length; ++i)
    //    {
    //        fft[i] = fftComplex[i].Magnitude; // back to double
    //        //fft[i] = Math.Log10(fft[i]); // convert to dB
    //    }
    //
    //    return fft;
    //}


    public void chart1_Click(object sender, EventArgs e) { }
}

That was the original code I've modified, and got to work successfully, without the extra bits (commented out).

Imports NAudio
Imports NAudio.CoreAudioApi

Public Class Form1
    Public WithEvents wi As New NAudio.Wave.WaveIn() 'Wave 'in Stream Generates 'by Naudio
    Public WaveFormat As New NAudio.Wave.WaveFormat(44100, 1) 'Wave In format
    'AddHandler wi.DataAvailable, AddressOf StreamWavein_DataAvailable
    Public bwp As New NAudio.Wave.BufferedWaveProvider(wi.WaveFormat)

    Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'wi.DeviceNumber = 0
        'Dim waveforamt As New NAudio.Wave.WaveFormat(44100, 1)
        'AddHandler wi.DataAvailable, AddressOf StreamWavein_DataAvailable
        'Dim bwp As New NAudio.Wave.BufferedWaveProvider(wi.WaveFormat)

        bwp.BufferLength = 1 << 13
        bwp.DiscardOnBufferOverflow = True
        wi.StartRecording()

        Chart1.ChartAreas(0).AxisX.Maximum = 400
        Chart1.ChartAreas(0).AxisX.Minimum = 0
    End Sub

    Public Sub StreamWavein_DataAvailable(ByVal sender As Object, ByVal e As NAudio.Wave.WaveInEventArgs) Handles wi.DataAvailable
        bwp.AddSamples(e.Buffer, 0, e.BytesRecorded)
    End Sub

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        Dim frameSize As Integer = 1 << 13
        Dim frames(frameSize) As Byte
        bwp.Read(frames, 0, frameSize)

        If frames.Length = 0 Then
            Return
        Else If frames(frameSize - 2) = 0 Then
            Label1.Text = "removed"
            Return
        Else
            Label1.Text = "graphing"
        End If

        Dim SAMPLE_RESOLUTION As Integer = 16
        Dim BYTES_PER_POINT As Integer = SAMPLE_RESOLUTION / 8
        Dim vals(frames.Length / BYTES_PER_POINT) As Integer
        Dim Ys(frames.Length / BYTES_PER_POINT) As Double
        Dim Xs(frames.Length / BYTES_PER_POINT) As Double
        Dim i As Integer

        For i = 0 To vals.Length Step 1
            Dim hbyte As Byte = frames(i * 2 + 1)
            Dim lbyte As Byte = frames(i * 2 + 0)
            vals(i) = CInt(CShort((hbyte << 8) Or lbyte))
            Xs(i) = i
            Ys(i) = vals(i)
        Next

        Chart1.Series("Series1").Points.DataBindXY(Xs, Ys)
    End Sub
End Class

I'm not sure where I went wrong, but any help would be greatly appreciated.

0

There are 0 answers