Mixer query.

Started by Chris Oakley,

Chris Oakley

Where am I going wrong here?
        Bass.BASS_Init(0, 48000, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero)
        Dim _nosoundmixerid As Integer = BassMix.BASS_Mixer_StreamCreate(48000, 2, BASSFlag.BASS_DEFAULT) 'Create mixer.
        Bass.BASS_ChannelPlay(_nosoundmixerid, False) 'Start the mixer playing.
        Bass.BASS_Init(5, 48000, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero)
        Dim _submixerid As Integer = BassMix.BASS_Mixer_StreamCreate(48000, 2, BASSFlag.BASS_STREAM_DECODE) 'Create mixer.
        BassMix.BASS_Mixer_StreamAddChannel(_nosoundmixerid, _submixerid, BASSFlag.BASS_DEFAULT) 'Add submixer to no sound mixer.
        Dim _streamid As Integer = Bass.BASS_StreamCreateFile("myfile.mp3", 0, 0, BASSFlag.BASS_STREAM_DECODE)
        BassMix.BASS_Mixer_StreamAddChannel(_submixerid, _streamid, BASSFlag.BASS_DEFAULT)
        BassMix.BASS_Mixer_ChannelPlay(_streamid)

I'm trying something new and I want to make a mixer on the No Sound device which is always playing. But because it's not possible to split from a non decode mixer, I need to get creative.

You see I can't have the main mixer just outputting to the physical device, because if I want to route it somewhere else too, then I can't, because I can't split.

So I'm making a _submixerid which is a DECODE and will take all the opened streams and also allow me to split.

So I'm trying to feed this submix into the _nosoundmixerid so it can be heard.

I'm getting nothing coming out though, so something isn't correct or I'm misunderstanding how this all fits.

Ian @ un4seen

If you would like to also play the _nosoundmixerid stream on another device then you could achieve that with a DSP function that feeds a push stream (with the same sample format) on the other device. The DSPPROC would just call BASS_StreamPutData:

void CALLBACK CloneDSP(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user)
{
    BASS_StreamPutData(pushstream, buffer, length);
}

An example of doing this can be found in the MULTI.C example included in the BASS package (see its clone option).

Chris Oakley

Ahhh that's fine then. I was just making sure I wasn't missing something. I have used the StreamCopy feature of Bass.Net but doing this using CloneDSP may be a better option.

Ian @ un4seen

In case you would prefer, I think it should also be possible to achieve what you want with splitters. You would just put a splitter between the mixer (_nosoundmixerid) and each device. For example:

// initialize "No Sound" device
BASS_Init(0, 48000, 0, 0, 0);
// create the mixer
mixer = BASS_Mixer_StreamCreate(48000, 2, BASS_STREAM_DECODE | BASS_SAMPLE_FLOAT);
// create and start a splitter
split0 = BASS_Split_StreamCreate(mixer, 0, NULL);
BASS_ChannelStart(split0);
// initialize another device and start a splitter on it
BASS_Init(5, 48000, 0, 0, 0);
split5 = BASS_Split_StreamCreate(mixer, 0, NULL);
BASS_ChannelStart(split5);

If you're pre-initializing multiple devices then use BASS_SetDevice before BASS_Split_StreamCreate to set the device for the splitter.

Chris Oakley

What I should have mentioned as well is the No Sound mixer is the main end destination, but I want to send its output to a physical device, because if the physical device disappears, I don't want the output to stop. It needs to keep going like nothing has happened.

Ian @ un4seen

Yep, the "No Sound" device's splitter would continue playing the mixer as the other device's splitters come and go.

Chris Oakley

Thanks again Ian and Happy New Year.

I think I may have finally solved the dilema I had which meant I've been using the StreamCopy function in BASS.Net instead of using the Splits and Mixers as directed.

So essentially for every Mixer I make which is Decode, I need to split off the mixer with a Non-Decode split going to the No Sound device. This means I can then make more splits off the mixer and add other decode streams into it without issue.

For clarity, if I have another decode mixer with a non-decode split going to the No Sound device and then I split from the first mixer and add that into the second decode mixer, will the first mixer having it's own Non-Decode split cause any issues?

Ian @ un4seen

If I understand correctly, mixer1 has a splitter playing on the "No Sound" device, and mixer2 has a splitter plugged into mixer1. And you want to know if it'll be okay for mixer2 to also have a splitter playing on the "No Sound" device? If so, it shouldn't do any harm, but perhaps it's unnecessary? mixer2 is effectively already playing on the "No Sound" device through mixer1.

Chris Oakley

That's correct, but I need to actively switch things around. If the user changes the routing and mapping of the mixer 2 to a different mixer (or no mixer at all), it still needs to continue.

Chris Oakley

I have another question regarding a build up of a delay on a physical output and I think it could be because I've got a mixer outputting DEFAULT NONSTOP to the NoSound device. I just have one of these for the whole application.

I then have 16 DECODE mixers. These mixers can be routed to physical outputs. If a mixer needs to go to a physical device a DEFAULT split is made from the mixer using the physical device index.

This all works great.

Now, my question is this... If I make two splits, one to the physical device as above, and a DECODE split from the same mixer which is added to the main NoSound mixer, would this cause a delay due to tiny clocking differences between the NoSound mixer and the physical device?

Asking, because we're seeing a delay on a physical output after 48 hours and I suspect this could be why.

Ian @ un4seen

Yes, when you have splitters going to multiple devices, it is possible for them to drift apart over time due to clock differences between the devices. In your case, it sounds like the real device is going slightly slower than the "No Sound" device, leading to a build-up of data in the real device's splitter. You can check that with BASS_Split_StreamGetAvailable. If there is a build-up of data, you can remedy that by speeding up the splitter slightly (via BASS_ATTRIB_FREQ) to gradually bring it back down, or more drastically using BASS_Split_StreamReset to bring it down instantly.

Another option is to set the BASS_SPLIT_SLAVE flag on the "No Sound" splitter (via BASS_ChannelFlags) to prevent it getting ahead of the real device splitter(s). Note you would only do that while there are other splitters, otherwise the source won't get processed at all.

Chris Oakley

Okay, I've changed everything to use the default device now as a mix engine. I'm not using the NoSound device at all, but I'm still getting a delay after 12 hours.

If I pause a file that's playing and then resume it, the delay goes away.

Any more thoughts where I can look?

I'm already setting the BASS_CONFIG_SPLIT_BUFFER to 100 before anything is setup, so I can't see it being that.

Ian @ un4seen

How big is the delay, and how are you detecting/measuring it?

Chris Oakley

When I see it, it's around 1 second. It's obvious because when you look at the Windows Sound Panel and look at the VU's in the application, the one's on the VU are plainly not the same. Then when you pause the audio in the app, the levels drop in the app, but the Windows level takes a second to drop. Then when you unpause the audio the levels are then in sync and so is the audible audio.

Ian @ un4seen

Please clarify what function calls (and handles) you're using to get the VU levels and to pause the audio. And what playback buffering you're using, ie. BASS_CONFIG_BUFFER and/or BASS_ATTRIB_BUFFER settings.

Chris Oakley

On the mixers I always set BASS_ATTRIB_BUFFER to 0, otherwise there is a latency all over and I don't want that.

BASS_CONFIG_BUFFER I don't touch, so that is 500.

Levels are monitored using DSP_PeakLevelMeter on the required mixers.

When audio is paused it's simply a call to set the channel flag on the item we're playing that we want to pause:
Ret = CType(BassMix.BASS_Mixer_ChannelFlags(_StreamId, BASSFlag.BASS_MIXER_CHAN_PAUSE, BASSFlag.BASS_MIXER_CHAN_PAUSE), BASSError)

Ian @ un4seen

Can you try using BASS_ChannelGetLevelEx instead of DSP_PeakLevelMeter? Or BASS_Mixer_ChannelGetLevelEx if you're getting the level of a mixer source (requires the BASS_MIXER_CHAN_BUFFER flag set). Please see their documentation for details.

Chris Oakley

Doesn't that decode data? Won't that cause audio glitches if the data is already being pulled?

Ian @ un4seen

BASS_ChannelGetLevelEx will decode data if called on a decoding channel, but will otherwise use the data in the channel's playback buffer. BASS_Mixer_ChannelGetLevelEx always uses the buffered data enabled by BASS_MIXER_CHAN_BUFFER.

Chris Oakley

Just so I'm clear, should every bus mixer / any mixer I want to monitor the level of have the BASS_MIXER_CHAN_BUFFER flag on it when created?