Author Topic: Estimate samples for DSPPROC  (Read 307 times)

Timtam

  • Posts: 23
Estimate samples for DSPPROC
« on: 9 May '19 - 23:46 »
Hello,

some background information: i'm planning to add a DSP function to a BASS channel. The DSP function is supposed to use further 3rd libraries to process the audio data further to add certain effects. This whole project should be used to provide e.g. in-game audio.

Problem: the 3rd party llibrary needs to know the frame size of the data. BASS however works with milliseconds only, and it seems like I cannot estimate a certain frame size to initialize the 3rd party library. I just tried:

Code: [Select]
BASS_SetConfig(BASS_CONFIG_FLOATDSP, 1); // the 3rd party software uses 32-bit floating-point samples

BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10); // 10 ms updateperiod

BASS_Init(-1, 48000, 0, nullptr, nullptr);

HSTREAM sound = BASS_StreamCreateFile(false, std::string{"music.ogg"}.c_str(), 0, 0, BASS_SAMPLE_LOOP|BASS_SAMPLE_MONO); // further processing requires the data to be mono

BASS_ChannelSetDSP(sound, dsp, nullptr, 0);

BASS_ChannelPlay(sound, true);

The dspproc function only printing the length parameter shows that the first call wants to retrieve 3528 bytes (which is 882 samples (3528/4)) and all future calls want to process 2644 bytes (661 samples by 2644/4). 882 and 661 don't seem to have any frame size in common. Am I doing anything wrong?

I also thought about using BASSASIO, since BASSASIO allows to work with frame sizes directly, but since this application should process in-game audio to play this audio during a game and using ASIO for in-game audio seems more than just a bit overkill, there must be another way... other applications like Unity manage to use such 3rd party libraries as well, so there clearly must be a way...

Thanks.

Best Regards.

Timtam

Ian @ un4seen

  • Administrator
  • Posts: 21861
Re: Estimate samples for DSPPROC
« Reply #1 on: 10 May '19 - 17:42 »
For normal playback streams with buffering enabled (see BASS_ATTRIB_BUFFER), the BASS_CONFIG_UPDATEPERIOD setting determines how much data is processed at a time. When starting playback, BASS will request data at a faster rate to fill the playback buffer, and then however much is needed to keep the buffer filled (which will correspond approximately to the BASS_CONFIG_UPDATEPERIOD setting). That's why you're seeing higher numbers at first. When playback buffering is disabled, the device period (see BASS_CONFIG_DEV_PERIOD) determines how much data is processed at a time.

Currently, the only way to process streams in fixed amounts is to make them decoding channels (BASS_STREAM_DECODE flag) and use BASS_ChannelGetData (which allows you to specify a byte amount). But here's a little update for you to try:

   www.un4seen.com/stuff/bass.zip

It adds this new attribute to set the granularity (in samples) of a stream's processing (0=none/default):

Code: [Select]
#define BASS_ATTRIB_GRANULE 14

For example, if you want 512 samples, you would do this:

Code: [Select]
BASS_ChannelSetAttribute(stream, BASS_ATTRIB_GRANULE, 512);

Note that this does not necessarily mean that your DSP function will always receive 512 samples but rather a multiple of 512. Also, this setting only affects normal playback (not decoding channels), with and without buffering enabled (via BASS_ATTRIB_BUFFER). Let me know how you get on with it.

Timtam

  • Posts: 23
Re: Estimate samples for DSPPROC
« Reply #2 on: 10 May '19 - 18:36 »
Hi Ian,

that indeed sounds exactly how I would need it. I thought of the same solution you mentioned by implementing a decoding stream and by using BASS_ChannelGetData by always processing data in chunks as large as a frame with e.g. 512 samples.
That of course results in multiple troublesome things, like you'd always have to take care to correctly free the decoding stream. Imagining that I want to build an addon out of this later on, i'd have to take care to dynamically select a frame size and adapt to the user changing the BASS_CONFIG_UPDATEPERIOD value lower than my current frame size (e.g. when update period is at 10 ms, 1024 as a frame size will more or less never occur due to too fast buffering).

Adding a granularity via attribute is indeed a great solution. I'll rewrite my application to do it that way then, thanks. I'll write back as soon as this is finished, which will probably be next monday.

Best Regards.

Timtam

Timtam

  • Posts: 23
Re: Estimate samples for DSPPROC
« Reply #3 on: 11 May '19 - 11:05 »
Hi there,

I just rewrote my application to use the new attribute you introduced and it works fine as far as I can say.
There is just one thing and i'm not sure if i'm right about this, i'm pretty new to all of this.

Now, when applying a dsp after setting BASS_ATTRIB_GRANULE to e.g. 480, the dsp function offers a buffer with length 8640, which is a multiple of the frame size I told it to provide. But we're talking about bytes here. Since I started the stream with BASS_SAMPLE_FLOAT, all data is in 32-bit floating-point, which means that each sample is 4 bytes in size. 8640 bytes / 480 frame size = 18 frames. Each frame however should consist of 4 bytes per sample for each channel. My signal is mono, so only one channel. That however would end up in a total size of 18 * 480 * 4 = 34560 bytes, or am I wrong about this?

Since my 3rd party library requests frames, but in 32-bit floating-point too, I think I need to have a buffer as large as a frame multiplied by 4 (4 byte per sample) for each channel. So the current way wouldn't work either, since 8640 cannot be devided by 4 / 480 properly (would end up in 4 and a half frame).

Am I wrong here, or is the actual problem on your end of the line?

Best Regards.

Timtam

Timtam

  • Posts: 23
Re: Estimate samples for DSPPROC
« Reply #4 on: 12 May '19 - 15:18 »
Hi there, its me again.

I once again changed back to the decoding stream / STREAMPROC callback method, since I encountered at least one more obstacle which would actually become a bit tricky by using DSP as far as I can tell.

Its just like that my DSP will change the actual amount of channels in the stream, typically upmixing. It will initialize the stream with BASS_SAMPLE_MONO, downmixing the stream to a deffinitive channel, and afterwards processing the audio to become stereo (or even more) when processed within the DSP function. I don't believe that it is possible to tell BASS about a change in channels during a DSP function without disturbing the future dsp calls, thats why i'm now back using a decoding stream to manually process the data and manage the output stream as a definitive stereo stream all the time.

If you got a better idea how to handle this situation, i'd be happy to hear it, i'm otherwise happy with the situation like it is now :).

Best Regards.

Timtam

Ian @ un4seen

  • Administrator
  • Posts: 21861
Re: Estimate samples for DSPPROC
« Reply #5 on: 13 May '19 - 14:04 »
Now, when applying a dsp after setting BASS_ATTRIB_GRANULE to e.g. 480, the dsp function offers a buffer with length 8640, which is a multiple of the frame size I told it to provide. But we're talking about bytes here. Since I started the stream with BASS_SAMPLE_FLOAT, all data is in 32-bit floating-point, which means that each sample is 4 bytes in size. 8640 bytes / 480 frame size = 18 frames. Each frame however should consist of 4 bytes per sample for each channel. My signal is mono, so only one channel. That however would end up in a total size of 18 * 480 * 4 = 34560 bytes, or am I wrong about this?

Those numbers look a bit strange. The BASS_ATTRIB_GRANULE setting is in samples/frames, while the DSPPROC "length" parameter is in bytes. Mono 32-bit means 4 bytes per sample/frame. 8640 / 4 = 2160, and 2160 / 480 = 4.5, which shouldn't be possible. Are those actual numbers that you saw or just examples, and if the former, did you set the BASS_ATTRIB_GRANULE value before starting playback?

I once again changed back to the decoding stream / STREAMPROC callback method, since I encountered at least one more obstacle which would actually become a bit tricky by using DSP as far as I can tell.

Its just like that my DSP will change the actual amount of channels in the stream, typically upmixing. It will initialize the stream with BASS_SAMPLE_MONO, downmixing the stream to a deffinitive channel, and afterwards processing the audio to become stereo (or even more) when processed within the DSP function. I don't believe that it is possible to tell BASS about a change in channels during a DSP function without disturbing the future dsp calls, thats why i'm now back using a decoding stream to manually process the data and manage the output stream as a definitive stereo stream all the time.

Yes, if your DSP needs to change the number of channels (or change the amount of data in any other way) then you would indeed need to implement that in a STREAMPROC function that takes data from a decoder (via BASS_ChannelGetData) and processes it.

Timtam

  • Posts: 23
Re: Estimate samples for DSPPROC
« Reply #6 on: 13 May '19 - 14:08 »
Hi there,

all the numbers I mentioned above were actual results I encountered during testing. After initializing the stream with BASS_SAMPLE_FLOAT, I set BASS_ATTRIB_GRANULE to 480 and started playback afterwards. The numbers I got within the DSPPROC were just the ones I mentioned earlier. If that looks confusing to you, i'd expect some bug on your end, maybe when working with BASS_SAMPLE_FLOAT only, or because of the downmixing via BASS_SAMPLE_MONO.

Best regards and thanks for your help.

Timtam

Ian @ un4seen

  • Administrator
  • Posts: 21861
Re: Estimate samples for DSPPROC
« Reply #7 on: 13 May '19 - 15:58 »
Please confirm what BASS_CONFIG_UPDATEPERIOD setting you're currently using and what the file's sample rate and length is. When I try the same as in your first post (eg. BASS_CONFIG_UPDATEPERIOD=10) with a mono 44100 Hz file (the numbers in the post suggest a 44100 Hz file) and BASS_ATTRIB_GRANULE=480, I'm seeing 5760 bytes (3 granules) in the first DSPPROC call, then 3840 (2 granules) until the playback buffer is filled, and then 1920 (1 granule).

One thing that may be an issue in your case is that the final DSPPROC call at the end of the file may contain a partial granule even if the file is being looped, as DSP functions are called before looping.