Author Topic: USB Mic inputs to mixer to soundcard output [VB.net]  (Read 534 times)

Keiji

  • Posts: 9
I've been re-learning how to program over the past few years and I've been doing ok. I've so far gotten bass to do what I needed it to do with the the help of documentation and examples. But I'm stuck on what I'm trying to do right now. I have USB mics that I have been able to detect, and get signal levels on just fine. Now what i'm trying to do is get that input from the mics and output that to a sound card. I've been coming across a few examples written in C, but I've been unsuccessful in translating that into VB, which I've already written thousands of lines in.

The following code finds the desired mics, and monitors the signal levels;

Code: [Select]
Imports Un4seen.Bass
Imports Un4seen.Bass.Misc
Imports Un4seen.Bass.AddOn.Mix

Partial Class main
    Dim _record = New RECORDPROC(AddressOf MyRecording)
    Dim recordingInputs As New ArrayList

    Dim recChannel As Integer = 0

Dim maxlevel As Integer = 32768
    Static Dim maxspectrumlevel As Single = 0
   
    Sub InitAudio()
        Un4seen.BassAsio.BassAsio.BASS_ASIO_Init(-1, BASSFlag.BASS_DEFAULT)
        Dim samplerate As Single = Un4seen.BassAsio.BassAsio.BASS_ASIO_GetRate

        Me.SetStyle(ControlStyles.UserPaint, True)
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        AudioDevicesCB.DataSource = GetAudioDevices()
        AudioDevicesCB.DisplayMember = "Name"
        AudioDevicesCB.ValueMember = "ID"


        Dim n As Integer = 0
        Dim info As New BASS_DEVICEINFO()
        While (Bass.BASS_GetDeviceInfo(n, info))
            Console.WriteLine(info.ToString())
            n += 1
        End While
        n = 0
        Dim defaultDevice As Integer = -1

        While Not (info Is Nothing)
            info = Bass.BASS_GetDeviceInfo(n)
            If Not (info Is Nothing) And info.IsDefault Then
                defaultDevice = n
                Exit While
            End If
            n += 1
        End While

        Bass.BASS_Init(1, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, IntPtr.Zero, Nothing)
        Bass.BASS_Init(2, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, IntPtr.Zero, Nothing)
        Bass.BASS_Init(3, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, IntPtr.Zero, Nothing)


        System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

        'NotificationVolumeTrackBar.Value = My.Settings.NotificationVolume

        n = 0

        'Dim info As New BASS_DEVICEINFO()
        Dim rinfo As New BASS_RECORDINFO()

        While n < Bass.BASS_RecordGetDeviceCount()
            Bass.BASS_RecordGetDeviceInfo(n, info)
            Debug.WriteLine(n & info.name)
            If info.name.Contains("Microsoft LifeCam") Or info.name.Contains("C-Media") Then
                Debug.WriteLine("initilizing " & info.name)
                Bass.BASS_RecordInit(n)
                Bass.BASS_RecordGetInfo(rinfo)
                recChannel = Bass.BASS_RecordStart(48000, 2, BASSFlag.BASS_RECORD_PAUSE And BASSFlag.BASS_STREAM_DECODE, _record, IntPtr.Zero)
                If Bass.BASS_ChannelPlay(recChannel, False) Then
                    Debug.WriteLine("initilized " & info.name & " as " & recChannel.ToString)
                    recordingInputs.Add(New AudioDevice(recChannel, info, rinfo))
                Else
                    Debug.WriteLine("initilized " & info.name & " Failed. :: " & Bass.BASS_ErrorGetCode().ToString)
                End If
            End If
            n += 1
        End While



    End Sub
   
    Private Sub GetRecLevels() Handles AudioLevelTimer.Tick
        Dim i As Int16 = 1
        For Each mic In recordingInputs

            Dim level As Integer = mic.level()
            Dim spectrum As ArrayList = mic.spectrumdata()
            ....


        Next
    End Sub
End Class


Public Class AudioDevice
    Private deviceinfo As New BASS_DEVICEINFO()
    Private recordinfo As New BASS_RECORDINFO

    Private mID As Integer
    Private mName As String

    Dim _fadelevel As Integer = 0
    Dim _peaklevel As Integer = Integer.MaxValue
    Dim _peak As Integer
    Dim maxlevel As Integer = Integer.MaxValue


    Public Sub New(ByVal id As Integer, ByVal info As BASS_DEVICEINFO, rinfo As BASS_RECORDINFO)
        mID = id
        deviceinfo = info
        recordinfo = rinfo
        mName = deviceinfo.name
        ...
    End Sub

    Public ReadOnly Property level As Integer
        Get
...Return level
        End Get
    End Property

    Public Function spectrumdata() As ArrayList
        ...
    End Function
End Class   

The above code works great for what I want it to do. It's not perfect, I know, and I omitted a huge chunk of irrelevant code, but it works. But now what I want to do is plug those mic inputs into a mixer, and I can't figure out how to do a push stream, or whatever it is I need to do to monitor the mics via sound card. Any assistance or sample code would be appreciated!

Ian @ un4seen

  • Administrator
  • Posts: 20437
When using standard BASS recording (eg. not ASIO or WASAPI), you won't usually need a "push" stream as you can plug the recording channel directly into the mixer (after removing the RECORDPROC from the BASS_RecordStart call). But you will need to change the level monitoring code to use BASS_Mixer_ChannelGetLevel/Data instead of BASS_ChannelGetLevel/Data, otherwise the level monitoring would be taking data from the recording and making it unavailable to the mixer (resulting in skipping). Please see the BASS_Mixer_ChannelGetLevel/Data documentation for details.

Keiji

  • Posts: 9
Thanks for the reply!

The part about having to get my levels from BASS_Mixer_ChannelGetLevel I totally understand and knew I would have to. The part I'm not understanding is what you mean by removing the RECORDPROC from the BASS_RecordStart call.   Does BASS_RecordStart not require a RECORDPROC  ???

Keiji

  • Posts: 9
After playing with the code for a few hours, I figured this out;
Code: [Select]
        n = 0

        Dim rinfo As New BASS_RECORDINFO()
        Dim mixer As Integer = BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT)
        While n < Bass.BASS_RecordGetDeviceCount()
            Bass.BASS_RecordGetDeviceInfo(n, info)
            Debug.WriteLine(n & info.name)
            If info.name.Contains("C-Media") Then
                Debug.WriteLine("initilizing " & info.name)
                Bass.BASS_RecordInit(n)
                Bass.BASS_RecordGetInfo(rinfo)
                Dim recinfo = Bass.BASS_RecordGetInfo
                mixerRecChannel = Bass.BASS_RecordStart(recinfo.freq, recinfo.Channels, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_DECODE, Nothing, Nothing)
                Dim streamA As Integer = Bass.BASS_StreamCreate(mixerRecChannel, 0, 0, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SAMPLE_FLOAT)
                If BassMix.BASS_Mixer_StreamAddChannel(mixer, mixerRecChannel, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_MIXER_BUFFER) Then
                    Debug.WriteLine("initilized " & info.name & " as " & mixerRecChannel.ToString)
                    recordingInputs.Add(New AudioDevice(mixerRecChannel, info, rinfo))
                Else
                    Debug.WriteLine("initilized " & info.name & " Failed. :: " & Bass.BASS_ErrorGetCode().ToString)
                End If
            End If
            n += 1
        End While

        Debug.WriteLine(Bass.BASS_ErrorGetCode)
        Bass.BASS_SetDevice(1)
        Bass.BASS_ChannelPlay(mixer, False)

This allows me to listen to my mics thru my soundcard as I wanted.

Code: [Select]
Dim level As Integer = recordingInputs(0).mixerlevel()
Code: [Select]
        Public ReadOnly Property mixerlevel As Integer
        Get
            mixerlevel = Utils.LowWord32(BassMix.BASS_Mixer_ChannelGetLevel(ID))
            Return level
        End Get

But I'm getting a considerable delay. Is there any way to reduce or eliminate this?

Ian @ un4seen

  • Administrator
  • Posts: 20437
The part about having to get my levels from BASS_Mixer_ChannelGetLevel I totally understand and knew I would have to. The part I'm not understanding is what you mean by removing the RECORDPROC from the BASS_RecordStart call.   Does BASS_RecordStart not require a RECORDPROC  ???

Yes, in order to plug a recording channel into a mixer, it needs to not have a RECORDPROC. The BASS_Mixer_StreamAddChannel call will otherwise fail. A recording channel not using a RECORDPROC is equivalent to a stream using the BASS_STREAM_DECODE flag. It means that BASS_ChannelGetData can be used to get the recorded data (instead of the data being sent to a RECORDPROC), which is what the mixer will do.

I see a few issues in the posted cade:

Code: [Select]
                mixerRecChannel = Bass.BASS_RecordStart(recinfo.freq, recinfo.Channels, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_DECODE, Nothing, Nothing)
                Dim streamA As Integer = Bass.BASS_StreamCreate(mixerRecChannel, 0, 0, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SAMPLE_FLOAT)
...
        Bass.BASS_SetDevice(1)

The BASS_STREAM_DECODE flag shouldn't be used in the BASS_RecordStart call, and the BASS_StreamCreate call is unnecessary. The BASS_SetDevice call isn't really needed either unless there are multiple output devices initialized (via BASS_Init), in which case it should be called before BASS_Mixer_StreamCreate rather than BASS_ChannelPlay, as a stream's device is set at creation (can later be changed via BASS_ChannelSetDevice).

But I'm getting a considerable delay. Is there any way to reduce or eliminate this?

The mixer will unfortunately add some latency due to buffering. The latency will be determined by the mixer's playback buffer length, which is determined by the BASS_CONFIG_BUFFER setting (default 500ms) at its creation. When using a small buffer, will you will also need to lower the update period via the BASS_CONFIG_UPDATEPERIOD setting (default 100ms). For example, for mimimal buffering, you could do this:

Code: [Select]
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 10) ' 10ms update period
Dim info As BASS_INFO = Bass.BASS_GetInfo()
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_BUFFER, 10 + info.minbuf + 1) ' playback buffer = update period + minbuf + 1ms margin

Please see the documentation for details on the mentioned functions/options. Note you will also need to use the BASS_DEVICE_LATENCY flag in your BASS_Init call(s) to make the "minbuf" value available.

As I mentioned earlier, if you don't need to support Windows XP, it may be better to use WASAPI (via the BASSWASAPI add-on) rather than standard BASS/DirectSound for this. That would remove the playback buffer from the mixer; the mixer would become a "decoding channel" (use the BASS_STREAM_DECODE flag), which your output WASAPIPROC would get its data from via BASS_ChannelGetData. In this case you would need the "push" stream that you mentioned earlier, to receive the data from the input WASAPIPROC and feed it into the mixer, ie. the push stream is plugged into the mixer.

Keiji

  • Posts: 9
I was going to post asking how to initialize a device via BassWasapi and plug it in, but I'm going to try and figure it out.
« Last Edit: 9 Aug '17 - 17:23 by Keiji »

Ian @ un4seen

  • Administrator
  • Posts: 20437
To minimize the changes, you could actually keep the existing recording channel code as it won't really affect latency very much, and just use WASAPI for output. The WASAPI output code could look something like this (in C/C++):

Code: [Select]
BASS_WASAPI_Init(-1, 0, 0, BASS_WASAPI_EVENT, 0, 0, WasapiProc, NULL); // initialize default output device in event-driven shared mode
BASS_WASAPI_INFO wi;
BASS_WASAPI_GetInfo(&wi); // get output info
mixer = BASS_Mixer_StreamCreate(wi.freq, wi.chans, BASS_SAMPLE_FLOAT|BASS_STREAM_DECODE); // create a mixer with the same sample format
// start the recording and add it to the mixer here
BASS_WASAPI_Start(); // start the output

...

DWORD CALLBACK WasapiProc(void *buffer, DWORD length, void *user)
{
DWORD c = BASS_ChannelGetData(mixer, buffer, length); // get data from the mixer
if (c == -1) c = 0; // an error, no data
return c;
}

Please see the documentation for details on the mentioned functions.

Keiji

  • Posts: 9
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #7 on: 11 Aug '17 - 03:38 »
First, thanks for the continued help.

Second, I translated your code so that everything seems to check out;
Code: [Select]
Imports Un4seen.BassWasapi.BassWasapi
Imports Un4seen.Bass.AddOn.Mix
Public Class Main
    Dim _WASAPIPROC = New WASAPIPROC(AddressOf WASAPIPROC)
    Dim mixer As Integer = 0

        Sub InitMixerAudio()
        Dim info As New BASS_WASAPI_INFO()
        Debug.WriteLine(BASS_WASAPI_Init(-1, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0, 0, _WASAPIPROC, Nothing)) '; // initialize Default output device In Event-driven Shared mode
        Debug.WriteLine(BASS_WASAPI_GetInfo(info))
        Debug.WriteLine(Bass.BASS_ErrorGetCode())
        Debug.WriteLine(info.ToString)

        Bass.BASS_Init(-1, info.freq, info.chans, IntPtr.Zero, Nothing)
        mixer = BassMix.BASS_Mixer_StreamCreate(info.freq, info.chans, BASSFlag.BASS_SAMPLE_FLOAT)

        Debug.WriteLine(Bass.BASS_ErrorGetCode())



        Dim n As Integer = 0
        Dim Dinfo As New BASS_WASAPI_DEVICEINFO()
        While (BassWasapi.BASS_WASAPI_GetDeviceInfo(n, Dinfo))
            If Dinfo.IsEnabled And Dinfo.SupportsRecording Then
                Debug.WriteLine("---" & Dinfo.name)
                Debug.WriteLine(Dinfo.ToString())
                BASS_WASAPI_Init(n, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0, 0, _WASAPIPROC, Nothing) '; // initialize Default output device In Event-driven Shared mode
                Debug.WriteLine(Bass.BASS_ErrorGetCode) '; // create a mixer With the same sample format
            End If
            n += 1
        End While

        Debug.WriteLine(BassWasapi.BASS_WASAPI_Start()) ' ; // start the output
    End Sub

    Public Function WASAPIPROC(buffer As IntPtr, length As Integer, user As IntPtr) As Integer
        Dim c As Integer = Bass.BASS_ChannelGetData(mixer, buffer, length) '; // Get data from the mixer
        If (c = -1) Then c = 0 '; // an Error, no data
        ' Debug.WriteLine(c)
        Return c
    End Function


And it so far works ok. What I can't figure out is the push stream and how to plug my WASAPI devices to my mixer. I've spent about four hours looking for instructions on how to do it, it's just not coming to me very easy.

Ian @ un4seen

  • Administrator
  • Posts: 20437
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #8 on: 11 Aug '17 - 17:47 »
Shared mode WASAPI will have about the same latency as DirectSound for recording, so switching won't really affect latency. If you would like to switch anyway, the "push" stream needs to have the same sample format as the WASAPI input device, which you can get from BASS_WASAPI_GetInfo (as in the WASAPI output case). It could look something like this:

Code: [Select]
BASS_WASAPI_Init(indevice, 0, 0, 0, 0.1, 0, InputWasapiProc, NULL); // initialize input device in shared mode (100ms buffer, default period)
BASS_WASAPI_GetInfo(&wi); // get input info
instream = BASS_StreamCreate(wi.freq, wi.chans, BASS_SAMPLE_FLOAT|BASS_STREAM_DECODE, STREAMPROC_PUSH, NULL); // create a push stream with the same sample format
BASS_Mixer_StreamAddChannel(mixer, instream, 0); // add it to the mixer

...

DWORD CALLBACK InputWasapiProc(void *buffer, DWORD length, void *user)
{
BASS_StreamPutData(instream, buffer, length); // pass the captured data to the push stream
return 1;
}

Keiji

  • Posts: 9
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #9 on: 12 Aug '17 - 03:26 »
Last question, probably. What would STREAMPROC_PUSH reference?

Ian @ un4seen

  • Administrator
  • Posts: 20437
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #10 on: 14 Aug '17 - 17:57 »
STREAMPROC_PUSH is a constant (defined as -1). With BASS.Net, I believe you would use "BASSStreamProc.STREAMPROC_PUSH", or use BASS_StreamCreatePush instead of BASS_StreamCreate.

Keiji

  • Posts: 9
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #11 on: 20 Sep '17 - 01:06 »
Hello. I've been away, but now I'm back, with a Behringer U-Phoria 4-channel audio interface.
So my code now is this;

Code: [Select]
   
    Dim _inputWASAPIPROC = New WASAPIPROC(AddressOf InputWASAPIPROC)
    Dim mixer As Integer = 0
    Dim instream As Integer = 0
Sub InitMixerAudio()
        Dim info As New BASS_WASAPI_INFO()
        Debug.WriteLine(BASS_WASAPI_Init(-1, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0, 0, _WASAPIPROC, Nothing)) '; // initialize Default output device In Event-driven Shared mode
        Debug.WriteLine(BASS_WASAPI_GetInfo(info))
        Debug.WriteLine(Bass.BASS_ErrorGetCode())
        Debug.WriteLine(info.ToString)

        Bass.BASS_Init(-1, info.freq, info.chans, IntPtr.Zero, Nothing)
        mixer = BassMix.BASS_Mixer_StreamCreate(info.freq, info.chans, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_MIXER_BUFFER)

        Debug.WriteLine("mixer " & mixer.ToString & " : " & Bass.BASS_ErrorGetCode())


        Dim n As Integer = 0
        Dim Dinfo As New BASS_WASAPI_DEVICEINFO()
        While (BassWasapi.BASS_WASAPI_GetDeviceInfo(n, Dinfo))
            If Dinfo.IsEnabled And Dinfo.SupportsRecording And Dinfo.name.ToLower.Contains("line") Then
                Debug.WriteLine("---" & Dinfo.name)
                BASS_WASAPI_Init(n, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0, 0, _inputWASAPIPROC, Nothing) '; // initialize Default output device In Event-driven Shared mode
                Debug.WriteLine(Bass.BASS_ErrorGetCode)
                BASS_WASAPI_GetInfo(info) '; // Get input info
                instream = Bass.BASS_StreamCreatePush(info.freq, 2, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_MIXER_BUFFER, BASSStreamProc.STREAMPROC_PUSH) '; // create a push stream With the same sample format
                Debug.WriteLine(Bass.BASS_ErrorGetCode)
                BassMix.BASS_Mixer_StreamAddChannel(mixer, instream, BASSFlag.BASS_MIXER_BUFFER) '; // add it To the mixer
                Debug.WriteLine(Bass.BASS_ErrorGetCode) '; // create a mixer With the same sample format
                Debug.WriteLine(Bass.BASS_ErrorGetCode)

            End If
            n += 1
        End While

        Debug.WriteLine(BassWasapi.BASS_WASAPI_Start()) ' ; // start the output
        Debug.WriteLine(Bass.BASS_ErrorGetCode)

        BassMix.BASS_Mixer_ChannelPlay(instream)
        Debug.WriteLine(Bass.BASS_ErrorGetCode)
        Debug.WriteLine(BassMix.BASS_Mixer_ChannelIsActive(instream))



End Sub
Public Function InputWASAPIPROC(buffer As IntPtr, length As Integer, user As IntPtr) As Integer
    Bass.BASS_StreamPutData(instream, buffer, length) '; // pass the captured data To the push stream
    Return 1 ';
End Function

BASS_Mixer_ChannelIsActive gives me  BASS_ACTIVE_PLAYING. BASS_Mixer_ChannelGetLevel(instream) returns -1 with the error BASS_ERROR_NOPLAY. Why?

Ian @ un4seen

  • Administrator
  • Posts: 20437
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #12 on: 20 Sep '17 - 13:29 »
BASS_Mixer_ChannelGetLevel will produce a BASS_ERROR_NOPLAY error if the mixer is not playing. Are you calling BASS_ChannelPlay on the mixer handle? If not, doing so should fix it.

Please note that the BASS_MIXER_BUFFER flag should only be used in BASS_Mixer_StreamAddChannel calls, not in BASS_Mixer_StreamCreate or BASS_StreamCreateFile calls. It won't do any harm in this case, but using an invalid flag can cause unexpected results because it happens to have the same value as another flag that the function supports. A list of valid flags can be found in each function's documentation.

Keiji

  • Posts: 9
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #13 on: 6 Oct '17 - 02:00 »
Ok, so now I have this:

Code: [Select]
    Sub InitWASAPIaudio()

        Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 100)
        Dim n As Integer = 0

        n = 0
        Dim OutputDeviceInfo As New BASS_WASAPI_DEVICEINFO()
        While (BassWasapi.BASS_WASAPI_GetDeviceInfo(n, OutputDeviceInfo))
            If OutputDeviceInfo.IsEnabled And OutputDeviceInfo.IsInput = False Then '   And (OutputDeviceInfo.type.BASS_WASAPI_TYPE_SPEAKERS Or OutputDeviceInfo.type.BASS_WASAPI_TYPE_HDMI) Then
                If OutputDeviceInfo.name.ToLower.Contains("asus") Or OutputDeviceInfo.name.ToLower.Contains("blackmagic") Or OutputDeviceInfo.name.ToLower.Contains("umc404hd") Then
                    Dim i As Integer = 0
                    Dim Initinfo As New BASS_DEVICEINFO()
                    While (Bass.BASS_GetDeviceInfo(i, Initinfo))
                        If Initinfo.name = OutputDeviceInfo.name Then
                            Bass.BASS_Init(i, 44800, BASSInit.BASS_DEVICE_CPSPEAKERS Or BASSInit.BASS_DEVICE_LATENCY, IntPtr.Zero, Nothing)
                            'Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 10) ' 10ms update period
                            Dim bassinfo As BASS_INFO = Bass.BASS_GetInfo()

                            'Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_BUFFER, 10 + bassinfo.minbuf + 50) ' playback buffer = update period + minbuf + 1ms margin
                            BASS_WASAPI_Init(n, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0, 0, _WASAPIPROC, Nothing) '; // initialize Default output device In Event-driven Shared mode
                            Debug.WriteLine("Starting WASAPI Device {0} ::: {1}", OutputDeviceInfo.name, Bass.BASS_ErrorGetCode())
                            If OutputDeviceInfo.name.ToLower.Contains("umc404hd") Then
                                BroadcastMixer = BassMix.BASS_Mixer_StreamCreate(48000, 2, BASSFlag.BASS_SAMPLE_FLOAT)
                                Debug.WriteLine("Creating Broadcast Mixer {0} ::: {1}", Initinfo.name, Bass.BASS_ErrorGetCode())
                                Bass.BASS_ChannelPlay(BroadcastMixer, False)
                                Debug.WriteLine("Starting Broadcast Mixer {0} ::: {1}", Initinfo.name, Bass.BASS_ErrorGetCode())
                            End If
                            Exit While
                        End If
                        i += 1
                    End While
                End If
            End If
            n += 1
        End While


        Dim matrix(,) As Single = { ' stereo to quad matrix
            {1, 1, 1, 1},
            {1, 1, 1, 1}
            }

        Dim info As New BASS_WASAPI_INFO()
        n = 0
        Dim Inputinfo As New BASS_WASAPI_DEVICEINFO()
        While (BassWasapi.BASS_WASAPI_GetDeviceInfo(n, Inputinfo))
            If Inputinfo.IsEnabled And Inputinfo.IsInput Then
                If Inputinfo.name.ToLower.Contains("line") Then
                    BASS_WASAPI_Init(n, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0, 0, _inputWASAPIPROC, Nothing) '; // initialize Default output device In Event-driven Shared mode
                    Debug.WriteLine("Starting Input Device {0} ::: {1}", Inputinfo.name, Bass.BASS_ErrorGetCode)
                    BASS_WASAPI_GetInfo(info) '; // Get input info
                    instream = Bass.BASS_StreamCreatePush(info.freq, 4, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SAMPLE_SOFTWARE, BASSStreamProc.STREAMPROC_PUSH) '; // create a push stream With the same sample format
                    Debug.WriteLine("Starting Push Stream {0} ::: {1}", instream, Bass.BASS_ErrorGetCode)
                    BassMix.BASS_Mixer_StreamAddChannel(BroadcastMixer, instream, BASSFlag.BASS_MIXER_BUFFER Or BASSFlag.BASS_MIXER_MATRIX) '; // add it To the mixer
                    Debug.WriteLine("Adding Stream to Mixer {0} ::: {1}", BroadcastMixer, Bass.BASS_ErrorGetCode)
                End If

            End If
            n += 1
        End While
        System.Threading.Thread.Sleep(1000)
        Debug.WriteLine(BassWasapi.BASS_WASAPI_Start()) '
        Debug.WriteLine(Bass.BASS_ErrorGetCode)

        BassMix.BASS_Mixer_ChannelSetMatrix(instream, matrix)
        Debug.WriteLine(Bass.BASS_ErrorGetCode)
        Debug.WriteLine(BassMix.BASS_Mixer_ChannelIsActive(instream))


    End Sub

... and it mainly functions the way it's supposed to, with the exception of some audio problems that come out of the mixer. It's a clicking problem that sounds like it may be a buffer issue. and it's not consistent. It will happen for a few seconds to a minute, and then it will stop for a few seconds to a minute. I posted a video demonstrating the audio problem I'm having. It's 20 minutes, so feel free to skip around and notice it has a intermitent problem for about half of it.

https://youtu.be/EEI_GUzWZ-s

Thanks everyone!
« Last Edit: 6 Oct '17 - 02:33 by Keiji »

Ian @ un4seen

  • Administrator
  • Posts: 20437
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #14 on: 6 Oct '17 - 17:40 »
Although you are initializing a WASAPI output device, it looks like you are actually using standard BASS/DirectSound output to play the mixer. If you would like to use the WASAPI output, you should add the BASS_STREAM_DECODE flag to your BASS_Mixer_StreamCreate call, and then call BASS_ChannelGetData on the mixer in the output's WASAPIPROC callback function.

The problem currently is probably that there is not enough data buffered from the input. If you would like to stick with using BASS/DirectSound output, you could try pre-buffering a bit more data from the input before starting the mixer (calling BASS_ChannelPlay). You could do that something like this:

Code: [Select]
Public Function InputWASAPIPROC(buffer As IntPtr, length As Integer, user As IntPtr) As Integer
    Dim got As Integer = Bass.BASS_StreamPutData(instream, buffer, length) '; // pass the captured data To the push stream
    If started = False And got >= Bass.BASS_ChannelSeconds2Bytes(instream, 0.03) Then // have pre-buffered 30ms?
        Bass.BASS_ChannelPlay(BroadcastMixer, 0) ' start the mixer
        started = True
    End If
    Return 1 ';
End Function

You would set a "started" variable to False at the start.

Also, "instream" should have the same format as the WASAPI input device that's feeding it, ie. use "info.chans" (instead of 4) in its BASS_StreamCreatePush call.

Keiji

  • Posts: 9
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #15 on: 11 Oct '17 - 22:41 »
Thanks, that eliminated the crackling issue, but only after changing some configuration as it was earlier suggested ;
Code: [Select]
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 10) ' 10ms update period
Dim bassinfo As BASS_INFO = Bass.BASS_GetInfo()
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_BUFFER, 10 + bassinfo.minbuf + 1) ' playback buffer = update period + minbuf + 1ms margin

And the latency is low initially, but after the program runs for a while the output delay gets longer and longer, with up to three seconds between input and output after the program runs for about 20 minutes. ( I already fixed a previous error in the code having different frequencies for input and output) It otherwise sounds good.

Ian @ un4seen

  • Administrator
  • Posts: 20437
Re: USB Mic inputs to mixer to soundcard output [VB.net]
« Reply #16 on: 13 Oct '17 - 17:11 »
That sounds like the input device is delivering data slightly faster than the output device is playing it, resulting in a build-up of data in the buffers (probably mostly the push stream's buffer). There can be small differences even if both are set to the same rate. You can try checking/logging the BASS_StreamPutData call's return value to confirm whether that is the case. If it is indeed rising, you can try compensating for that by slightly speeding up the output. You can do that by raising either the mixer's BASS_ATTRIB_FREQ value or the push stream's BASS_ATTRIB_FREQ value (via BASS_ChannelSetAttribute). When using WASAPI output, only the push stream option will be available.