Author Topic: WASAPI to multiple output  (Read 131 times)

Dev01

  • Posts: 24
WASAPI to multiple output
« on: 18 Oct '17 - 14:36 »
I'm trying to redirect a WASAPI device (input or output) to N output devices (WASAPI or standard).
I've looked around in the forum, tried hundreds of times, but I don't understand how do this.
I've reached to perform a 1:1 WASAPI but not 1:N WASAPI (nor standard output).
I've tried with BASS_StreamCreate(), BASS_StreamCreatePush(), BASS_Mixer_StreamAddChannel(), various flags, but no success.
In one try, I've got no error but also no audio, typical of a loopback device, but also using the -1 non-loopback device no success.
 :'(

Can you help me with a "magic hint"?
Regards.

Ian @ un4seen

  • Administrator
  • Posts: 20400
Re: WASAPI to multiple output
« Reply #1 on: 18 Oct '17 - 18:01 »
Do you already have the forwarding of a loopback input device to one output device working, by having the input WASAPIPROC feed a "push" stream, which is plugged into a mixer that is feeding the output WASAPIPROC? If so, adding outputs should just be a matter of initializing the additional output devices and creating mixers to feed each of them. You would also create splitters on the "push" stream to send the input data to all of the output mixers. It could look something like this:

Code: [Select]
// initialize the input device
BASS_WASAPI_Init(indevice, 0, 0, BASS_WASAPI_EVENT, 0.5, 0, InWasapiProc, NULL);
// get its sample format
BASS_WASAPI_INFO wi;
BASS_WASAPI_GetInfo(&wi);
// create a push stream with same sample format to receive the input data
instream = BASS_StreamCreate(wi.freq, wi.chans, BASS_STREAM_DECODE|BASS_SAMPLE_FLOAT, STREAMPROC_PUSH, NULL);

for (int n = 0; n < num_outputs; n++) {
// initialize the n'th output device
BASS_WASAPI_Init(outdevice[n], 0, 0, BASS_WASAPI_EVENT, 0.1, 0, OutWasapiProc, (void*)n);
// get its sample format
BASS_WASAPI_INFO wi;
BASS_WASAPI_GetInfo(&wi);
// create a mixer with same format
mixer[n] = BASS_Mixer_StreamCreate(wi.freq, wi.chans, BASS_STREAM_DECODE|BASS_SAMPLE_FLOAT);
// create a splitter on the input push stream and plug it into the mixer
DWORD splitter = BASS_Split_StreamCreate(instream, BASS_STREAM_DECODE, NULL);
BASS_Mixer_StreamAddChannel(mixer[n], splitter, BASS_STREAM_AUTOFREE);
}

// start the input device
BASS_WASAPI_SetDevice(indevice);
BASS_WASAPI_Start();
// start the output devices
for (int n = 0; n < num_outputs; n++) {
BASS_WASAPI_SetDevice(outdevice[n]);
BASS_WASAPI_Start();
}

...

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

DWORD CALLBACK OutWasapiProc(void *buffer, DWORD length, void *user)
{
// get data from the output's mixer
int n = (int)user;
int got = BASS_ChannelGetData(mixer[n], buffer, length);
if (got < 0) got = 0;
return got;
}

Please see the documentation for details on the mentioned functions.
« Last Edit: 19 Oct '17 - 14:17 by Ian @ un4seen »

Dev01

  • Posts: 24
Re: WASAPI to multiple output
« Reply #2 on: 19 Oct '17 - 13:21 »
Thanks Ian for your patience.
I've tried to go from yout C hints to C# code, trying to fill the gaps.
Everything seems to work, no error, but also no audio in output.

Code: [Select]
        private int instream;
        private int[] mixer = new int[10];
        private WASAPIPROC mInWasapiProc;

        private int InWasapiProc(IntPtr buffer, int length, IntPtr user)
        {
            // pass the data to the push stream
            int got = Bass.BASS_StreamPutData(instream, buffer, length);
            return 1;
        }

        private int OutWasapiProc(IntPtr buffer, int length, IntPtr user)
        {
            // get data from the output's mixer
            int n = (int)user;
            int got = Bass.BASS_ChannelGetData(mixer[n], buffer, length);
            if (got < 0)
                got = 0;
            return got;
        }

        private int STREAMPROC_PUSH(int handle, IntPtr buffer, int length, IntPtr user)
        {
            return 1;
        }

        public void Test(int WASAPIdeviceIn)
        {
            BASS_DEVICEINFO[] outdevice = Bass.BASS_GetDeviceInfos();
            bool ret = false;

            // initialize the input device
            mInWasapiProc = new WASAPIPROC(InWasapiProc);
            ret = BassWasapi.BASS_WASAPI_Init(WASAPIdeviceIn, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0.5f, 0, mInWasapiProc, IntPtr.Zero);

            // create a push stream with same sample format to receive the input data           
            instream = Bass.BASS_StreamCreate(mFrequency, mChannels, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, STREAMPROC_PUSH, IntPtr.Zero);

            for (int n = 0; n < outdevice.Length; n++)
            {
                // initialize the n'th output device
                ret = BassWasapi.BASS_WASAPI_Init(n, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0.1f, 0, OutWasapiProc, new IntPtr(n));
                // create a mixer with same format
                mixer[n] = BassMix.BASS_Mixer_StreamCreate(mFrequency, mChannels, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT);
                ret = BassMix.BASS_Mixer_StreamAddChannel(mixer[n], instream, 0); // plug the push stream into it
                // create a splitter on the input push stream and plug it into the mixer
                int splitter = BassMix.BASS_Split_StreamCreate(instream, BASSFlag.BASS_STREAM_DECODE, null);
                ret = BassMix.BASS_Mixer_StreamAddChannel(mixer[n], splitter, BASSFlag.BASS_STREAM_AUTOFREE);
            }

            // start the input device
            ret = BassWasapi.BASS_WASAPI_SetDevice(WASAPIdeviceIn);
            ret = BassWasapi.BASS_WASAPI_Start();
            // start the output devices
            for (int n = 0; n < outdevice.Length; n++)
            {
                ret = BassWasapi.BASS_WASAPI_SetDevice(n);
                ret = BassWasapi.BASS_WASAPI_Start();
            }
        }


I've found a similar way, passing audio from WASAPI to standard Bass.
That mode works somehow but...
sometimes the audio is very good, sometimes is full of disturb, sometimes starts good, but if I add another output, then it gets a lot of noise.
I cannot find a stable schema when the audio is good or not.

Ian @ un4seen

  • Administrator
  • Posts: 20400
Re: WASAPI to multiple output
« Reply #3 on: 19 Oct '17 - 14:26 »
Oops! I had left an extra BASS_Mixer_StreamAddChannel call in the code when copy'n'pasting it. I have removed that call from the code in my post now. In your C# code, it's the call with the "instream" handle.

Regarding the no sound problem... STREAMPROC_PUSH is a special value that you can use in BASS_StreamCreate calls to create a "push" stream; you should not be implementing a function called STREAMPROC_PUSH. With BASS.Net, you can use BASS_StreamCreatePush instead. Also, I don't think you should be simply initializing the first 'n' devices for output. The WASAPI device list includes both output and input devices, and may also include devices that are currently unavailable/unplugged. You should have an array of the output device numbers that you want to use ("outdevice" in my post), eg. enumerate the available output devices and put them in the array.

Dev01

  • Posts: 24
Re: WASAPI to multiple output
« Reply #4 on: 19 Oct '17 - 15:26 »
Thanks Ian, with your help the application worked!
At least for some seconds, then mysterious crashes happened:
Code: [Select]
System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

Managed Debugging Assistant 'CallbackOnCollectedDelegate' : 'A callback was made on a garbage collected delegate of type 'Bass.Net!Un4seen.BassWasapi.WASAPIPROC::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.'

They doesn't fire in a specific statement, just sometimes happen.
They are not catchable, so the application is not stable now.

It seems enough to move the mouse over the form to crash the program!

The reason seem to be the callback:
Code: [Select]
ret = BassWasapi.BASS_WASAPI_Init(mOutdevice[n].Id, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0.1f, 0, OutWasapiProc, (IntPtr)n);   //Crash
ret = BassWasapi.BASS_WASAPI_Init(mOutdevice[n].Id, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0.1f, 0, null, (IntPtr)n);            //No crash, but no audio, of course

even if I change the function in a dummy
Code: [Select]
private int OutWasapiProc(IntPtr buffer, int length, IntPtr user)
{
      return 1;
}

Any ideas?

Dev01

  • Posts: 24
Re: WASAPI to multiple output
« Reply #5 on: 19 Oct '17 - 15:36 »
Solved!

Code: [Select]
ret = BassWasapi.BASS_WASAPI_Init(WASAPIdeviceIn, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0.5f, 0, OutWasapiProc, IntPtr.Zero);     //CRASH!

mOutWasapiProc = new WASAPIPROC(OutWasapiProc);
ret = BassWasapi.BASS_WASAPI_Init(WASAPIdeviceIn, 0, 0, BASSWASAPIInit.BASS_WASAPI_EVENT, 0.5f, 0, mOutWasapiProc, IntPtr.Zero);     //OK

Thank you Ian, really.
« Last Edit: 19 Oct '17 - 17:10 by Dev01 »