Author Topic: Add delay to a channel  (Read 357 times)

nelson13

  • Posts: 12
Add delay to a channel
« on: 26 Apr '21 - 05:54 »
Hello,

Is there a way to add a certain delay in the audio to the playback of a channel?

I have 2 streams that reproduce exactly the same content (in 2 bass channels), but a primary in high quality and a secondary as backup. The idea is to synchronize the audio by adding delay to the output, so that when there is a cut and they switch it is transparent.
Each codec has a different delay and I would like to compensate by delaying the audio of one channel.

PS: there is no automatic way to synchronize this with the BASS functions, is there?

Thanks

Ian @ un4seen

  • Administrator
  • Posts: 23640
Re: Add delay to a channel
« Reply #1 on: 26 Apr '21 - 17:59 »
Would both streams be playing on the same device? If so, you could use a mixer (with the BASSmix add-on) to play them, which allows sample-accurate timing of its sources (eg. your 2 streams) via the BASS_Mixer_StreamAddChannelEx "start" parameter. Please see the BASS_Mixer_StreamAddChannelEx documentation for details.

nelson13

  • Posts: 12
Re: Add delay to a channel
« Reply #2 on: 28 Apr '21 - 02:34 »
Thanks for your response Ian!.

Yes, the two streams are played by the same device, the source is icecast.

I've been trying with BASS_Mixer_StreamAddChannelEx , but I get BASS_ERROR_DECODE

this is my code
      
Code: [Select]
_Stream2 = Bass.BASS_StreamCreateURL(textBoxURL2.Text, 0, BASSFlag.BASS_STREAM_STATUS, null, IntPtr.Zero);
//Buffer code...

long start = Bass.BASS_ChannelSeconds2Bytes(mixer, 15.0); // delay
long length = Bass.BASS_ChannelSeconds2Bytes(mixer, 0); // duration

if (BassMix.BASS_Mixer_StreamAddChannelEx(mixer, _Stream2, 0, start, length))
{// add the channel
Librerias.Mensajes.Advertencia("Oks!");
}
Bass.BASS_ChannelPlay(_Stream2, false);


//Form load:
mixer = BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT);
        Bass.BASS_ChannelPlay(mixer, false);

Chris

  • Posts: 1945
Re: Add delay to a channel
« Reply #3 on: 28 Apr '21 - 11:11 »
Hi
Code: [Select]
_Stream2 = Bass.BASS_StreamCreateURL(textBoxURL2.Text, 0, BASSFlag.BASS_STREAM_STATUS, null, IntPtr.Zero);should be
Code: [Select]
_Stream2 = Bass.BASS_StreamCreateURL(textBoxURL2.Text, 0, BASSFlag.BASS_STREAM_STATUS or BASS_STREAM_DECODE, null, IntPtr.Zero);A Stream inside a Mixer must have allways the Flag BASS_STREAM_DECODE.

Chris

  • Posts: 1945
Re: Add delay to a channel
« Reply #4 on: 28 Apr '21 - 11:14 »
Hi
Code: [Select]
_Stream2 = Bass.BASS_StreamCreateURL(textBoxURL2.Text, 0, BASSFlag.BASS_STREAM_STATUS, null, IntPtr.Zero);should to be
Code: [Select]
_Stream2 = Bass.BASS_StreamCreateURL(textBoxURL2.Text, 0, BASSFlag.BASS_STREAM_STATUS or BASS_STREAM_DECODE, null, IntPtr.Zero);A Stream inside a Mixer must have allways the Flag BASS_STREAM_DECODE.

nelson13

  • Posts: 12
Re: Add delay to a channel
« Reply #5 on: 28 Apr '21 - 16:05 »
Thank you very much Chris, now the audio now plays fast and the buffer is flushed.

You shouldn't use BASS_ChannelPlay(chan, false), right?, just start mixer playback

Ian @ un4seen

  • Administrator
  • Posts: 23640
Re: Add delay to a channel
« Reply #6 on: 28 Apr '21 - 16:25 »
Thank you very much Chris, now the audio now plays fast and the buffer is flushed.

Do you happen to be calling BASS_ChannelGetData/Level on the streams? That can cause fast playback because the data used in those calls won't be seen by the mixer. You can use BASS_Mixer_ChannelGetData/Level instead (also include the BASS_MIXER_CHAN_BUFFER flag in the BASS_Mixer_StreamAddChannelEx calls).

You shouldn't use BASS_ChannelPlay(chan, false), right?, just start mixer playback

That is correct. You only need to call BASS_ChannelPlay on the mixer.

nelson13

  • Posts: 12
Re: Add delay to a channel
« Reply #7 on: 29 Apr '21 - 03:31 »
Thank you very much Chris, now the audio now plays fast and the buffer is flushed.

Do you happen to be calling BASS_ChannelGetData/Level on the streams? That can cause fast playback because the data used in those calls won't be seen by the mixer. You can use BASS_Mixer_ChannelGetData/Level instead (also include the BASS_MIXER_CHAN_BUFFER flag in the BASS_Mixer_StreamAddChannelEx calls).
Thanks for your help  :)

It was indeed a BASS_ChannelGetLevel

Last question: is there any way to tell if a mixer channel is BASS_ACTIVE_STALLED?
The following flag did not work: BASSFlag.BASS_STREAM_AUTOFREE

Code always BASSActive.BASS_ACTIVE_PLAYING:
if BassMix.BASS_Mixer_ChannelIsActive(_Stream1) != BASSActive.BASS_ACTIVE_PLAYING

Ian @ un4seen

  • Administrator
  • Posts: 23640
Re: Add delay to a channel
« Reply #8 on: 29 Apr '21 - 17:10 »
BASSmix doesn't actually have a BASS_Mixer_ChannelIsActive function but BASS.Net implements one by using BASS_Mixer_ChannelFlags. The BASS_ACTIVE_STOPPED/PLAYING/PAUSED states can be detected that way but unfortunately not BASS_ACTIVE_STALLED. I'll see if a proper BASS_Mixer_ChannelIsActive function can be added in the next BASSmix release. In the meantime, you could set a BASS_SYNC_STALL sync via BASS_Mixer_ChannelSetSync to be informed when a source stalls/resumes.

nelson13

  • Posts: 12
Re: Add delay to a channel
« Reply #9 on: 29 Apr '21 - 19:07 »
Thanks Ian that would be a good addition.
I tried syncing when I started this project, but it was more comfortable this way. I will have to reveal it.

Another way I had to achieve this, (synchronize stream) was to use a different buffer for each chain. The parameter is global, according to the documentation says that once the reproduction begins it does not affect modifying the value. But while buffering something, it does.

Ian @ un4seen

  • Administrator
  • Posts: 23640
Re: Add delay to a channel
« Reply #10 on: 3 May '21 - 15:47 »
Here's a BASSmix update that adds a BASS_Mixer_ChannelIsActive function:

   www.un4seen.com/stuff/bassmix.zip

As well as adding support for BASS_ACTIVE_STALL, it also adds a new BASS_ACTIVE_WAITING status for when a source is waiting to start (delayed by BASS_Mixer_StreamAddChannelEx). Note that the status is as the mixer currently sees it, which will be ahead of what's heard during playback due to buffering of the mixer output (the same applies to BASS_Mixer_ChannelFlags).

To use it in .Net, you will need to import it directly rather than through BASS.Net (until BASS.Net is updated to support it). I think you would do that like this:

Code: [Select]
[DllImport("bassmix")]
static extern int BASS_Mixer_ChannelIsActive(int handle);

...

int active = BASS_Mixer_ChannelIsActive(handle);

Let me know if you have any trouble with it.

nelson13

  • Posts: 12
Re: Add delay to a channel
« Reply #11 on: 4 May '21 - 04:14 »
Here's a BASSmix update that adds a BASS_Mixer_ChannelIsActive function:


Thank you very much Ian, faster than I thought!

I will comment on my experience with this shortly. ;)


As well as adding support for BASS_ACTIVE_STALL, it also adds a new BASS_ACTIVE_WAITING status for when a source is waiting to start (delayed by BASS_Mixer_StreamAddChannelEx). Note that the status is as the mixer currently sees it, which will be ahead of what's heard during playback due to buffering of the mixer output (the same applies to BASS_Mixer_ChannelFlags).
This is great  :o , I had this problem in my list of things to solve .. temporarily I was going to use a variable with the seconds it had been playing

nelson13

  • Posts: 12
Re: Add delay to a channel
« Reply #12 on: 4 May '21 - 05:31 »
You are great! Everything works wonders ;)

An off-topic question.
I'm trying to get the seconds I have to buffer in the channels. For this I have the following code:

Code: [Select]
Bass.BASS_ChannelBytes2Seconds(_Stream1, Bass.BASS_StreamGetFilePosition(_Stream1, BASSStreamFilePosition.BASS_FILEPOS_BUFFER))
With stream flac it works fine, but with mp3, ogg and aac I get 10 times less. If the buffer is 20 seconds, I get 2.
I can easily multiply by 10, but I wonder if there is something wrong or I am doing something wrong.

Ian @ un4seen

  • Administrator
  • Posts: 23640
Re: Add delay to a channel
« Reply #13 on: 4 May '21 - 17:09 »
Good to hear that the new function is working well.

I'm trying to get the seconds I have to buffer in the channels. For this I have the following code:

Code: [Select]
Bass.BASS_ChannelBytes2Seconds(_Stream1, Bass.BASS_StreamGetFilePosition(_Stream1, BASSStreamFilePosition.BASS_FILEPOS_BUFFER))
With stream flac it works fine, but with mp3, ogg and aac I get 10 times less. If the buffer is 20 seconds, I get 2.
I can easily multiply by 10, but I wonder if there is something wrong or I am doing something wrong.

BASS_ChannelBytes2Seconds deals in decoded sample data, while BASS_StreamGetFilePosition deals in encoded file data, so they can't be mixed.

Perhaps there are other ways to calculate what you want, or even avoid the need for it. What exactly are you trying to calculate and what will you be using that information for?

nelson13

  • Posts: 12
Re: Add delay to a channel
« Reply #14 on: 4 May '21 - 19:59 »
I understand. I'm looking for a way to get the remaining seconds of audio in the buffer. (Beyond the buffer percentage).
The purpose is to switch to channel b (backup with the same content), one second before it is emptied.
Also showing this information is quite useful for testing.
Surely there is a better way to do this  :)

Ian @ un4seen

  • Administrator
  • Posts: 23640
Re: Add delay to a channel
« Reply #15 on: 5 May '21 - 15:35 »
If the BASS_STREAM_BLOCK flag is set (automatic for Icecast/Shoutcast streams) then BASS_FILEPOS_END will give you the size of the download buffer and you could approximately calculate the duration of the buffered data like this:

Code: [Select]
float bufsecs = 5.0 * BASS_StreamGetFilePosition(stream, BASS_FILEPOS_BUFFER) /  BASS_StreamGetFilePosition(stream, BASS_FILEPOS_END);

If you've changed the BASS_CONFIG_NET_BUFFER setting then change the 5.0 value accordingly. Note the decoder may also have its own buffers, which means there's actually more data than calculated. You could perhaps just switch the streams when the download buffer is empty?

Are you sure you can get the 2 Icecast streams in perfect sync though, eg. have you already done that part? It seems unlikely that would be possible reliably because the buffering/latency of the 2 servers might be slightly different each time you connect. If the 2 streams aren't in perfect sync anyway then it might be better to just use a BASS_SYNC_STALL sync to handle the stream switching, even if it does mean there's a little break in sound. It might even be possible to avoid a gap by using the BASS_MIXER_CHAN_LIMIT flag in the main stream's BASS_Mixer_StreamAddChannel call (you should remove that flag with BASS_Mixer_ChannelFlags after switching streams or set it on the backup stream).

nelson13

  • Posts: 12
Re: Add delay to a channel
« Reply #16 on: 24 May '21 - 22:41 »
If you've changed the BASS_CONFIG_NET_BUFFER setting then change the 5.0 value accordingly. Note the decoder may also have its own buffers, which means there's actually more data than calculated. You could perhaps just switch the streams when the download buffer is empty?
Thanks Ian, the above worked correctly..
I have to avoid silence by all means. (Even if both streams fail, a local backup file plays)


Are you sure you can get the 2 Icecast streams in perfect sync though, eg. have you already done that part? It seems unlikely that would be possible reliably because the buffering/latency of the 2 servers might be slightly different each time you connect. If the 2 streams aren't in perfect sync anyway then it might be better to just use a BASS_SYNC_STALL sync to handle the stream switching, even if it does mean there's a little break in sound.
A perfect synchronization was not possible, but an improvement. (The lag is not several seconds).
The difference between the included server and icecast is quite large, and I managed to minimize it.  :)
What I do is assume that when the buffer is less than 1 second after a crossfader I release the channel and reconnect. I assume it is going to be emptied, but I make sure there is no sharp cut.

It might even be possible to avoid a gap by using the BASS_MIXER_CHAN_LIMIT flag in the main stream's BASS_Mixer_StreamAddChannel call (you should remove that flag with BASS_Mixer_ChannelFlags after switching streams or set it on the backup stream)
I'm reading the documentation for BASS_MIXER_CHAN_LIMIT and I can't understand how it could help me.
What it does is stop the mixer when the data on a channel runs out. (But I had another muted channel ready to serve)

Ian @ un4seen

  • Administrator
  • Posts: 23640
Re: Add delay to a channel
« Reply #17 on: 25 May '21 - 13:13 »
My thought was that using the BASS_MIXER_CHAN_LIMIT flag and a BASS_SYNC_STALL sync on the main stream would allow you to wait until it stalls before switching to the backup without introducing any gap because the mixer will stop mixing when the main stream stalls, ie. it won't output silence in the meantime. But thinking about that a bit more now, it would probably actually introduce a slight overlap rather than a perfectly seamless switch, because the backup stream would be mixed in the same processing cycle as the main stream stalled in (rather than the next cycle). There may be a way around that, but if you already have a crossfade working well then you may as well stick with that :)