This project has moved. For the latest updates, please go here.

Using VolumeMeter while using WaveMixerStream32

Jun 14, 2013 at 2:36 AM
I'm working on a drum sequencer and would like to have a volume meter for each sample. I know how to setup a single VolumeMeter but am unable to figure out how I would go about init the MeteringSampleProvider when using the WaveMixerStream32 for multiple samples. I've tried adding the MeteringSampleProvider to the mixer but the mixer wants a WaveStream. I have also looked at the demo program but that only deals with 1 volume meter not using a mixer.

This is my current audio flow:
AudioFileReader > WaveChannel32 (used for panning) > WaveMixerStream32 > AsioOut

How would I go about fitting in MeteringSampleProvider in that mix?
Coordinator
Jun 14, 2013 at 10:30 AM
WaveChannel32 and WaveMixerStream32 are both fairly old classes, and you might find it much easier going the SampleProvider route throughout. Use a PanningSampleProvider, and a MixingSampleProvider.

Convert between WaveProvider and SampleProvider using SampleProviderConverters.ConvertWaveProviderIntoSampleProvider, and SampleToWaveProvider or SampleToWaveProvider16. There are extension methods in the latest 1.7 alpha releases to simplify this process.
Jun 14, 2013 at 3:41 PM
Yeah I was thinking the anwser was probably to use the MixingSampleProvider. As for the PanningSampleProvider it will only take a mono input. Is there anything else that does panning that will take a stereo input?

Over all would this seem right using SampleProvider's?

AudioFileReader > [Something here for panning] > MeteringSampleProvider > MixingSampleProvider > AsioOut

Or if there is nothing that will take stereo input for panning would this seem right?

AudioFileReader > WaveChannel32 > WaveToSampleProvider > MeteringSampleProvider > MixingSampleProvider > AsioOut
Coordinator
Jun 14, 2013 at 3:45 PM
depends what you mean by "panning" with a stereo signal. Is it more like balance you want? I'd probably just make my own SampleProvider that let me adjust left and right channel volumes separately.
Your second pipeline should work OK too.
Jun 14, 2013 at 5:14 PM
After some thought I see what you mean now. I decided to try out my second pipeline to see the results but ran into a problem. How would I go about generating a constant stream of silence to keep the AsioOut open since MixingSampleProvider does not have .AutoStop . I get "Object reference not set to an instance of an object." when trying to add an input after the asioOut.Init(mixer) . Here is my test code if it helps (sorry it's in vb). "mixer.AddMixerInput(meterSampleProvider)" is the line that throws the exception.
    Dim asioOut As AsioOut
    Dim reader As AudioFileReader
    Dim waveChannel As WaveChannel32
    Dim waveToSample As WaveToSampleProvider
    Dim meterSampleProvider As MeteringSampleProvider
    Dim mixer As MixingSampleProvider

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        For Each device In AsioOut.GetDriverNames()
            comboBoxAsioDevice.Items.Add(device)
        Next
        If comboBoxAsioDevice.Items.Count > 0 Then
            comboBoxAsioDevice.SelectedIndex = 0
        End If

        asioOut = New AsioOut(comboBoxAsioDevice.Text)
        asioOut.ChannelOffset = GetUserSpecifiedChannelOffset()
        asioOut.Init(mixer)
        asioOut.Play()
    End Sub

    Private Function GetUserSpecifiedChannelOffset() As Integer
        Dim channelOffset As Integer = 0
        Integer.TryParse(textBoxChannelOffset.Text, channelOffset)
        Return channelOffset
    End Function

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            reader = New AudioFileReader("C:\samples\snare.wav")
            waveChannel = New WaveChannel32(reader)
            waveToSample = New WaveToSampleProvider(waveChannel)
            meterSampleProvider = New MeteringSampleProvider(waveToSample)
            mixer.AddMixerInput(meterSampleProvider)

            waveChannel.Position = 0
    End Sub
Jun 14, 2013 at 7:16 PM
I think I may have found the problem.

"asioOut.Init(mixer)" throws "SampleRate is not supported". I'm not sure where the sample rate for the MixingSampleProvider is set.
Jun 14, 2013 at 8:54 PM
Ok I got it working after much trial and error and demo referencing. The working code is below if anyone needs a VB example.
Dim asioOut As AsioOut

    Dim reader As AudioFileReader
    Dim waveChannel As WaveChannel32
    Dim waveToSample As WaveToSampleProvider
    Dim meterSampleProvider As MeteringSampleProvider
    Dim mixer As MixingSampleProvider
    Dim sampleToWave As SampleToWaveProvider
    Dim waveFormat As WaveFormat

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        For Each device In AsioOut.GetDriverNames()
            comboBoxAsioDevice.Items.Add(device)
        Next
        If comboBoxAsioDevice.Items.Count > 0 Then
            comboBoxAsioDevice.SelectedIndex = 0
        End If

        waveFormat = NAudio.Wave.WaveFormat.CreateIeeeFloatWaveFormat(44100, 2)
      
        mixer = New MixingSampleProvider(waveFormat)

        reader = New AudioFileReader("C:\samples\snare.wav")
        waveChannel = New WaveChannel32(reader)
        waveToSample = New WaveToSampleProvider(waveChannel)
        meterSampleProvider = New MeteringSampleProvider(waveToSample)
        AddHandler meterSampleProvider.StreamVolume, AddressOf OnPostVolumeMeter
        mixer.AddMixerInput(meterSampleProvider)

        sampleToWave = New SampleToWaveProvider(mixer)

        asioOut = New AsioOut(comboBoxAsioDevice.Text)
        asioOut.ChannelOffset = GetUserSpecifiedChannelOffset()
        asioOut.Init(sampleToWave)

        asioOut.Play()
    End Sub

    Private Function GetUserSpecifiedChannelOffset() As Integer
        Dim channelOffset As Integer = 0
        Integer.TryParse(textBoxChannelOffset.Text, channelOffset)
        Return channelOffset
    End Function

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      'play the sample
        waveChannel.Position = 0
      
    End Sub

    Public Sub OnPostVolumeMeter(sender As Object, e As StreamVolumeEventArgs)
        VolumeMeter1.Amplitude = e.MaxSampleValues(0)
    End Sub
Jun 14, 2013 at 11:54 PM
One problem I noticed with the above is that meterSampleProvider.StreamVolume event is continually raised despite any sound playing or not. Would this be because I'm using WaveChannel32 in the pipeline? Any ideas how I would get the .StreamVolume event to only trigger when a sample is played? I fear once many samples are added the continual triggering of the .StreamVolume event may start to cause performance issues.
Coordinator
Jun 23, 2013 at 6:37 AM
if the event is being raised, it means that audio is being pulled through the meterSampleProvider - you are probably playing silence.
Jun 23, 2013 at 3:38 PM
Edited Jun 23, 2013 at 3:39 PM
Yeah that's what I was thinking. So would it be the AudioSampleProvider or WaveChannel32 outputting silence? I'm thinking the WaveChannel32. I'd like to keep the mixer always initialized and I'm assuming if I stop the silence output that it will auto stop the Asio. Is there a way around this?
Coordinator
Jun 24, 2013 at 1:08 PM
the StreamVolume event is meant to fire - this is the design of MeteringSampleProvider. If you don't want that, then stop playing, or unsubscribe from the event. Or ignroe the event when the volume is 0