Author Topic: Bass.NET BassAsioHandler initialization and latency Issues  (Read 178 times)

Dev01

  • Posts: 28
Hi everyone!

My goal is to put together a simple ASIO mixer application using Bass.Net library, where you have:
-   Multiple ASIO audio inputs playing on the same ASIO output
-   The same ASIO input playing on different ASIO outputs

I managed to achieve the first feature (despite the very long latency, at least 4 seconds), however I'm stuck with the second use case.
I'm doing tests on a 8 in / 8 out ASIO audio card.
This example illustrates the methods I'm using to start input channel 0 and 1 (as a single stereo channel) and playing it on output channel 0, 1, 2 and 3 (as two different stereo channels)
The result I'm having is stereo audio coming out only from the second pair of output channels, nothing from the first pair of channels.
Also the big latency is still present.


Code: [Select]
int iDevice = 1;
int iChannelInput = 0;
int iChannelOutput1 = 0;
int iChannelOutput2 = 2;
int mixerHandle1;
int mixerHandle2;

BassAsioHandler asioInputHandler1;
BassAsioHandler asioInputHandler2;
BassAsioHandler asioOutputHandler1;
BassAsioHandler asioOutputHandler2;

//INIT BASS
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 20);
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATETHREADS, 4);
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_BUFFER, 40);
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_MIXER_BUFFER, 2);

Bass.BASS_Init(0, 44100, 0, IntPtr.Zero);
BassAsio.BASS_ASIO_Init(iDevice , BASSASIOInit.BASS_ASIO_THREAD);

//INIT MIXERS
BASSFlag bassMixerInitFlags = BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_MIXER_NONSTOP | BASSFlag.BASS_MIXER_POSEX;
mixerHandle1 = BassMix.BASS_Mixer_StreamCreate(44100, 2, bassMixerInitFlags);
mixerHandle2 = BassMix.BASS_Mixer_StreamCreate(44100, 2, bassMixerInitFlags);

//INIT INPUT
asioInputHandler1 = new BassAsioHandler(true, iDevice, iChannelInput, 2, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT, 44100);
asioInputHandler1.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, true);
BassMix.BASS_Mixer_StreamAddChannel(mixerHandle1, asioInputHandler1.OutputChannel, BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_STREAM_AUTOFREE);
asioInputHandler1.Start(0, 8);
asioInputHandler2 = new BassAsioHandler(true, iDevice, iChannelInput, 2, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT, 44100);
asioInputHandler2.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, true);
BassMix.BASS_Mixer_StreamAddChannel(mixerHandle2, asioInputHandler2.OutputChannel, BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_STREAM_AUTOFREE);
asioInputHandler2.Start(0, 8);

//This is when the user would interact with the GUI to select the output channels, skipping this “sleep” resolves latency. Is it really necessary to execute everything without pauses?
System.Threading.Thread.Sleep(5000);           

//INIT OUTPUT
asioOutputHandler1 = new BassAsioHandler(iDevice, iChannelOutput1, mixerHandle1, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT);
asioOutputHandler1.Start(0, 1);
asioOutputHandler2 = new BassAsioHandler(iDevice, iChannelOutput2, mixerHandle2, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT);
asioOutputHandler2.Start(0, 1);

Can someone help me with the multiple output and the latency issues?


Ian @ un4seen

  • Administrator
  • Posts: 21731
Regarding latency, it looks like you're starting the outputs a long time (5 seconds) after the inputs. That would result in a build-up of buffered data from the inputs and therefore high latency. Try removing the Sleep call, or start the inputs after it (just before the outputs).

radio42

  • Posts: 4647
Looks like we discussed this issue already by email and I also suggested to use a mixed-mode assembly for minimizing any latency as well.
But yes, the sleep between the input and output should also be avoided.

Dev01

  • Posts: 28
Thank you both for the reply, I managed to fix the latency by moving all the Start() calls after the sleep. This is the updated example code:

Code: [Select]
int iDevice = 1;
int iChannelInput = 2;
int iChannelOutput1 = 0;
int iChannelOutput2 = 2;
int mixerHandle1;
int mixerHandle2;

BassAsioHandler asioInputHandler1;
BassAsioHandler asioInputHandler2;
BassAsioHandler asioOutputHandler1;
BassAsioHandler asioOutputHandler2;

//INIT BASS
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 20);
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATETHREADS, 4);
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_BUFFER, 40);
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_MIXER_BUFFER, 2);
Bass.BASS_Init(0, 44100, 0, IntPtr.Zero);
BassAsio.BASS_ASIO_Init(iDevice , BASSASIOInit.BASS_ASIO_THREAD);

//INIT MIXERS
BASSFlag bassMixerInitFlags = BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_MIXER_NONSTOP | BASSFlag.BASS_MIXER_POSEX;
mixerHandle1 = BassMix.BASS_Mixer_StreamCreate(44100, 2, bassMixerInitFlags);
mixerHandle2 = BassMix.BASS_Mixer_StreamCreate(44100, 2, bassMixerInitFlags);

//INIT INPUTS
asioInputHandler1 = new BassAsioHandler(true, iDevice, iChannelInput, 2, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT, 44100);
asioInputHandler2 = new BassAsioHandler(true, iDevice, iChannelInput, 2, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT, 44100);
//set input full duplex mode
asioInputHandler1.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, true);
asioInputHandler2.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, true);           

// PLUG INPUTS INTO MIXERS
BassMix.BASS_Mixer_StreamAddChannel(mixerHandle1, asioInputHandler1.OutputChannel, BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_STREAM_AUTOFREE);
BassMix.BASS_Mixer_StreamAddChannel(mixerHandle2, asioInputHandler2.OutputChannel, BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_STREAM_AUTOFREE);           

//This is when the user interacts with the GUI
System.Threading.Thread.Sleep(5000);

//INIT OUTPUT           
asioOutputHandler1 = new BassAsioHandler(iDevice, iChannelOutput1, mixerHandle1, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT);
asioOutputHandler2 = new BassAsioHandler(iDevice, iChannelOutput2, mixerHandle2, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT);


asioInputHandler1.Start(0, 8);
asioInputHandler2.Start(0, 8);
asioOutputHandler1.Start(0, 1);
asioOutputHandler2.Start(0, 1);



The mixed mode assembly solution is very interesting, but first I have to solve the main issue as I can hear audio only from output 2, nothing from 1. After some tinkering I noticed that switching the SetFullDuplex calls causes the audio to come out only from the last one.

With this I can hear only output 2:
Code: [Select]
//set input full duplex mode
asioInputHandler1.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, true);
asioInputHandler2.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, true);   

With this I can hear only output 1:
Code: [Select]
//set input full duplex mode
asioInputHandler2.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, true);
asioInputHandler1.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, true);   



Dev01

  • Posts: 28
I think I got this, instead of creating multiple BassAsioHandler, I had to use BASS_Split_StreamCreate for every mixer input.
Now I hear the same input on both outputs.


Code: [Select]
asioInputHandler1 = new BassAsioHandler(true, iDevice, iChannelInput1, 2, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT, 44100);
asioInputHandler1.UseInput = false;           
asioInputHandler1.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE, false);   
inputHandle1_1 = BassMix.BASS_Split_StreamCreate(asioInputHandler1.OutputChannel, BASSFlag.BASS_STREAM_DECODE, null);
inputHandle1_2 = BassMix.BASS_Split_StreamCreate(asioInputHandler1.OutputChannel, BASSFlag.BASS_STREAM_DECODE, null);

BassMix.BASS_Mixer_StreamAddChannel(mixerHandle1, inputHandle1_1, BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_STREAM_AUTOFREE);
BassMix.BASS_Mixer_StreamAddChannel(mixerHandle2, inputHandle1_2, BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_STREAM_AUTOFREE);