Author Topic: Better option than BASS_MIXER_NONSTOP for capturing "zero" levels?  (Read 521 times)

CraigG

  • Posts: 12
Hi all,

Using BASS.NET.

I'm using a series of mixer and split streams to route audio by inserting and removing the source streams to said mixers/splits. I'm able to implement DSP_PeakLevelMeter or BASS_ChannelGetLevel/s to retrieve level information from the mixers/splits with one issue that I've only been able to solve with BASS_MIXER_NONSTOP. As soon as the mixer/split has no active audio source streams, it stops, which is expected. But the last values returned by DSP_PeakLevelMeter or BASS_ChannelGetLevel appear to be what is still in the buffer, even though the audio source has been removed, and of course DSP_PeakLevelMeter Notifications stop. Adding BASS_MIXER_NONSTOP solves this problem and I'm able to get zero's for the levels, but I've never used this flag before, so I don't want to introduce an unknown at this point if there is a better way to go about this? I've created a generic wrapper to accomplish all of my metering needs, so any solution should ultimately work for mixers/splits/streams. For example, I can easily see if a push or file stream is BASS_ChannelIsActive, but or if a mixer BASS_Mixer_StreamGetChannelCount  > 0, but splits don't seem to have this same functionality anywhere and I can't really trigger the DSP_PeakLevelMeter this way anyhow.

Thanks as always for any guidance here.
« Last Edit: 20 Mar '23 - 01:07 by CraigG »

radio42

  • Posts: 4837
From the docs… have you tried thi äs already?
…This latency can be reduced by making use of the BASS_CONFIG_BUFFER and BASS_CONFIG_UPDATEPERIOD config options. The playback buffer can be flushed by calling BASS_ChannelPlay(Int32, Boolean) (restart = TRUE) or BASS_ChannelSetPosition(Int32, Int64, BASSMode) (pos = 0). That can also be done to restart a mixer that has ended.

Ie. try to reset the mixer when all sources have ended.

CraigG

  • Posts: 12
Hi @Radio42,

Yes, I've tried several combinations of those options.

If the only option is to take a specific approach for each stream type, then it looks like the split streams are my biggest obstacle. I can't find a way to determine when those streams should even be flushed, unless I were to keep a separate collection of all the streams added up stream. Thus far, I've relied on the built-in methods to determine if a stream/channel BASS_ChannelIsActive, or if a mixer had BASS_Mixer_StreamGetChannelCount  > 0.  Split streams just don't seem to have any method to determine if they're actively decoding/reading. Now, with that being said, I've tried so many combinations of things that I may have just missed something along the way.

In my setup, I'm ultimately dealing with multiple split streams. Some are sourced by another split stream, and the others from mixers. The DLP_PeakLevelMeter assigned to a split stream knows when to stop providing Notification events, so I'm trying to accomplish the same thing. How do I know when a split stream is not active, but still has a source (or not)?

Here is an example of how some of the splits are setup:

Code: [Select]
            //Mixer setup
            //Mainmix
            //      |-> Split stream -> Main Output
            //      |-> Split stream -> Monitor Mix <- Cue Mix
            //                              |-> Split Stream -> Headphone Output
            //                              |-> Split Stream -> Monitor Output
            _bassMainMix = BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE);
            _bassMonitorMix = BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE);
            _bassCueMix = BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE);

            //Split Streams for MainMix
            _bassMainToMainReader = BassMix.BASS_Split_StreamCreate(_bassMainMix, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, null);           
            _bassMainToMonitorReader = BassMix.BASS_Split_StreamCreate(_bassMainMix, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, null);
           
            //Monitor Mixer configuration
            BassMix.BASS_Mixer_StreamAddChannel(_bassMonitorMix, _bassMainToMonitorReader, BASSFlag.BASS_DEFAULT);           
            BassMix.BASS_Mixer_StreamAddChannel(_bassMonitorMix, _bassCueMix, BASSFlag.BASS_DEFAULT);

            //Split Streams for MonitorMix
            _bassMonitorToHeadphoneReader = BassMix.BASS_Split_StreamCreate(_bassMonitorMix, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, null);
            _bassMonitorToMonitorReader = BassMix.BASS_Split_StreamCreate(_bassMonitorMix, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, null);
« Last Edit: 20 Mar '23 - 14:37 by CraigG »

Ian @ un4seen

  • Administrator
  • Posts: 25866
Please confirm how you are playing the mixers/splitters. I notice they're decoding channels (with BASS_STREAM_DECODE set), so I guess you're playing them via ASIO or something? If you are using ASIO (via BASSASIO) then you could use BASS_ASIO_ChannelGetLevel (instead of BASS_ChannelGetLevel) to get the current levels.

If you were using normal BASS playback (via BASS_ChannelPlay), then they would stall while there's no data to play and BASS_ChannelGetLevel would then return 0.

CraigG

  • Posts: 12
@Ian

Ah yes, correct: ASIO.

BASS_ASIO_Channel(GetLevel) only works with the ASIO channels right, not (split) streams?

BASS_ChannelGetLevel works here, but clears the buffer data resulting in 2x reading (playback). Maybe there is a way around that while using ASIO?

Or maybe I just stick with BASS_MIXER_NONSTOP.

Thanks again all.
« Last Edit: 20 Mar '23 - 15:50 by CraigG »

Ian @ un4seen

  • Administrator
  • Posts: 25866
Which handle(s) are you using in the BASS_ChannelGetLevel calls, and how are you playing those handles? From what you say about "resulting in 2x reading", I guess they're played through a mixer? If so, you should use BASS_Mixer_ChannelGetLevel (and include BASS_MIXER_CHAN_BUFFER in the BASS_Mixer_StreamAddChannel call) instead of BASS_ChannelGetLevel to get their level, because the data used by BASS_ChannelGetLevel won't be seen by the mixer (which makes it sound fast).

CraigG

  • Posts: 12
I'm applying the metering on the "final" outputs, highlighted in the attachment. They are all split streams.

Ian @ un4seen

  • Administrator
  • Posts: 25866
Are those splitters being played via ASIO? If so, you could get the levels from the ASIO channels (via BASS_ASIO_ChannelGetLevel) instead of directly from the splitters (via BASS_ChannelGetLevel). That way the ASIO output doesn't miss any of the sample data (due to BASS_ChannelGetLevel taking it).

CraigG

  • Posts: 12
Hi Ian,

That is correct, played via ASIO. It's a little bit more work to deal with the ASIO channels, but it sounds like that's the only other option. Since the DSP(_PeakLevelMeter) features somehow know the split/mixer/etc streams are active, passing that info to the consumer would be great: DSP_PeakLevelMeter.IsActive. Just an idea for the short list ;)
« Last Edit: 20 Mar '23 - 17:19 by CraigG »

Ian @ un4seen

  • Administrator
  • Posts: 25866
I believe DSP_PeakLevelMeter works by measuring the level of the data within a DSPPROC callback (set via BASS_ChannelSetDSP), rather than fetching data itself. That means it can work with decoding channels, without taking data away from whatever else is using it (ASIO output in this case). A DSPPROC receives data as it is generated, so if there's no data then it won't be called. If you're comfortable dealing with sample data, you could do similar yourself (ie. implement your own DSPPROC callback), but using BASS_ASIO_ChannelGetLevel will probably be more efficient in this case.

radio42

  • Posts: 4837
It is like Ian described. It is a simple DSPPROC which is being called whenever data arrives. So there is no knowledge about an active flag or alike. It is simple: data is being passed through the DSPPROC…

But I got a bit lost here. Why is this active flag that important/needed?
Either you calculate the Level when data is being generated (DSPPROC) or when it is passed to the output (Ian’s suggestion). Both approaches are valid in one or the other scenario.

CraigG

  • Posts: 12
@Radio42, I've got a solution in place, so I don't intend to beat this death... but I do want to answer your question:

I'm dealing with a (ASIO) split streams here, which immediately stop triggering DSP events when nothing is routed through it. If I'm relying on the DSP event for, as an example, audio levels, I'll never get a zero level reading unless I use BASS_MIXER_NONSTOP, which (as you know) just sends zeros to the buffer. Using BASS_ChannelGetLevel, without BASS_MIXER_NONSTOP, works great for the levels but it wipes out the buffer, so it plays back the audio 2x. BASS_ASIO_ChannelGetLevel works ok too, but it doesn't work with the stream handle, only the ASIO channels, which might be too far down the chain in some cases.

If I simplify my previous example to something like this:

[Mixer 1] -> [Split Stream 1] -> [Mixer 2] -> [Split Stream 2]

How would I get [Split Stream 1]'s audio level when it's not active (zero) without tracking everything in [Mixer 1] and without using BASS_MIXER_NONSTOP?

Real world example is a VUMeter that will show the last data buffered in to [Split Stream 1], but not "zero" once the split stream has stopped receiving data. Hope that is a little clearer.
« Last Edit: 25 Mar '23 - 17:43 by CraigG »