Author Topic: SYNCPROC at start of playback when using BASS_Mixer_StreamAddChannelEx  (Read 533 times)

djex81

  • Posts: 6
Sorry for another post. How would I raise an event as soon as a stream starts playing when it is delayed using BASS_Mixer_StreamAddChannelEx? I tired to use BASSSync.BASS_SYNC_POS with 0 as the data but that doesn't work. If I set it to something like 4000 then it might work for some songs and some not but it's always delayed if it does work. Any ideas? Should I just write my own timing function that checks the playback position? but then it might not trigger right on the exact start.

Ian @ un4seen

  • Administrator
  • Posts: 22121
You could set a BASS_SYNC_POS sync on the mixer at the position that the source is due to start. Something like this:

Code: [Select]
BASS_ChannelLock(mixer, TRUE); // lock mixer to block processing
BASS_Mixer_StreamAddChannelEx(mixer, source, flags, start, 0); // add source
QWORD pos = BASS_ChannelGetPosition(mixer, BASS_POS_BYTE | BASS_POS_DECODE); // get mixer's position
BASS_ChannelSetSync(mixer, BASS_SYNC_POS | BASS_SYNC_ONETIME, pos + start, SourceStartSync, NULL); // set a sync at source's start position (add BASS_SYNC_MIXTIME if wanted)
BASS_ChannelLock(mixer, FALSE); // unlock mixer to resume processing

djex81

  • Posts: 6
You could set a BASS_SYNC_POS sync on the mixer at the position that the source is due to start. Something like this:

Thanks. This works well. I really appreciate your help. The BASS library is quite complex and I've read a lot of the documentation but it helps to have someone who understands it thoroughly.

What I'm trying to do and please let me know if I'm going about it the wrong way but I'm trying to make a music player that will auto mix a playlist of EDM music. The last 64 beats mixed with the first 64 beats of the next song with the beats aligning. The beat matching is the difficult part. What I first thought would be easy is turning into be quite the challenge. Any ways I believe I can get this done with the BASS library. What I'm doing is this currently:

1. Load PlayerA, PlayerB and PlayerC with a song.

Code: [Select]
streamChannel = Bass.BASS_StreamCreateFile(filename, 0L, 0L, BASSFlag.BASS_DEFAULT | BASSFlag.BASS_STREAM_DECODE);
streamChannelFX = BassFx.BASS_FX_TempoCreate(streamChannel, BASSFlag.BASS_DEFAULT | BASSFlag.BASS_STREAM_DECODE);

2. Adjust tempos to match a master BPM so both songs are the same BPM

Code: [Select]
//Adjust tempo by sample rate (found this is more accurate than using BASS_ATTRIB_TEMPO)
float songABeatLength = 60f / Global.MASTER_BPM;
float songBBeatLength = 60f / GetTrackBPM();

float newSampleRate = 44100f * (songBBeatLength / songABeatLength);

Bass.BASS_ChannelSetAttribute(streamChannelFX, BASSAttribute.BASS_ATTRIB_TEMPO_FREQ, newSampleRate);

3. Start PlayerA playback now and start PlayerB playback with a set start delay of (PlayerA_Beat_Position + PlayerA_Position_of_First_Beat) using BASS_Mixer_StreamAddChannelEx

Code: [Select]
StartPlay = new SYNCPROC(onStartPlay);
Bass.BASS_ChannelLock(mixer, true);
BassMix.BASS_Mixer_StreamAddChannelEx(mixer, loadedTrack.GetStreamChannel(), BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_MIXER_DOWNMIX | BASSFlag.BASS_STREAM_AUTOFREE, startDelay, 0);
long pos = Bass.BASS_ChannelGetPosition(mixer, BASSMode.BASS_POS_BYTE | BASSMode.BASS_POS_DECODE);
Bass.BASS_ChannelSetSync(mixer, BASSSync.BASS_SYNC_POS | BASSSync.BASS_SYNC_ONETIME, pos + startDelay, StartPlay, IntPtr.Zero);
Bass.BASS_ChannelLock(mixer, false);

BassMix.BASS_Mixer_ChannelPlay(loadedTrack.GetStreamChannel());

4. PlayerB has a start SYNCPROC using your code above.

PlayerA will play then PlayerB will begin playing at the delayed beat position I set. This works well and both PlayerA and B are in sync. Now when PlayerB's StartPlay SYNCPROC fires I want to delay start PlayerC using BASS_Mixer_StreamAddChannelEx as I did with PlayerB. Unfortunately there is too much latency when starting a channel in a SYNCPROC that when PlayerC begins playing after its set delay PlayerB and C are not in sync.

If this worked I would continue the cycle and setup PlayerA to play after C and so on so I could have continuous play. Is there a better method of doing this? or a way to get very little delay when using a SYNCPROC?

I was also playing around with using a SYNCPROC at a certain beat position on PlayerA to attempt to sync PlayerB. I could get it to work if I set both PlayerA and B positions but this creates a skip in the playing audio and is not desired. What I really need is accuracy and low latency. Ideally if there was a way to start a stream at a set position to align with another playing stream with very little (10ms or less) latency this would work. Using the delayed start with BASS_Mixer_StreamAddChannelEx can achieve this but only with the first two songs. There does not seem to be a way to start a third song to be in sync with the second song.


Edit: I was able to get PlayerC to sync with B. I forgot to add in BASS_SYNC_MIXTIME in the ChannelSetSync flags. I've been working on this for three days now and the code is becoming a mess with test code everywhere.
« Last Edit: 23 Apr '19 - 01:54 by djex81 »

Ian @ un4seen

  • Administrator
  • Posts: 22121
Good to hear that you've got things working well now, if I'm reading it right :)

Regarding tempo adjustment, using BASS_ATTRIB_TEMPO_FREQ will change the pitch too (not only the tempo) but if the adjustment is small then it will probably sound fine. You could actually use the standard BASS_ATTRIB_FREQ attribute instead, removing the need for any BASS_FX tempo processing, ie. you could remove the BASS_FX_TempoCreate call.

djex81

  • Posts: 6
Good to hear that you've got things working well now, if I'm reading it right :)

Regarding tempo adjustment, using BASS_ATTRIB_TEMPO_FREQ will change the pitch too (not only the tempo) but if the adjustment is small then it will probably sound fine. You could actually use the standard BASS_ATTRIB_FREQ attribute instead, removing the need for any BASS_FX tempo processing, ie. you could remove the BASS_FX_TempoCreate call.

Yeah I got it working now. Really pleased with that. I found the time stretching with BASS_ATTRIB_TEMPO had more latency with it and would sometimes have artifacts in the audio which I'm guessing is because I'm not using an ASIO driver and its trying to time stretch in real time. If I'm correct BASS_ATTRIB_TEMPO_FREQ changes the sampling rate which increases the speed and pitch as you said. Though there is a slight loss in quality I found this to work a lot better unless I'm missing something with BASS_ATTRIB_TEMPO in order to reduce the latency and artifacts. Possibly increasing the buffer might help?

Another quick question. I see that you suggest using the base Bass library functions rather than BassMix functions. Should I be using Bass or BassMix? For example BASS_ChannelGetPosition and BASS_Mixer_ChannelGetPosition do the same thing and BASS_ChannelSetSync and BASS_Mixer_ChannelSetSync seem to be the same as well. Should I only use BassMixer functions when there is no Bass equivalent?

Ian @ un4seen

  • Administrator
  • Posts: 22121
Yeah I got it working now. Really pleased with that. I found the time stretching with BASS_ATTRIB_TEMPO had more latency with it and would sometimes have artifacts in the audio which I'm guessing is because I'm not using an ASIO driver and its trying to time stretch in real time. If I'm correct BASS_ATTRIB_TEMPO_FREQ changes the sampling rate which increases the speed and pitch as you said. Though there is a slight loss in quality I found this to work a lot better unless I'm missing something with BASS_ATTRIB_TEMPO in order to reduce the latency and artifacts. Possibly increasing the buffer might help?

Tempo processing can introduce artifacts to the sound. Unless it's stuttering (you can use a BASS_SYNC_STALL sync to detect that), increasing the buffer length won't affect that. When the tempo adjustment is small, it may be better to just change the playback rate, as you are doing.

Another quick question. I see that you suggest using the base Bass library functions rather than BassMix functions. Should I be using Bass or BassMix? For example BASS_ChannelGetPosition and BASS_Mixer_ChannelGetPosition do the same thing and BASS_ChannelSetSync and BASS_Mixer_ChannelSetSync seem to be the same as well. Should I only use BassMixer functions when there is no Bass equivalent?

In this case, where you want to precisely synchronize the start of multiple files, it will probably be best to stick with using a mixer for that. When using a mixer, you would still use BASS_ChannelGetPosition to get the mixer's positon, and BASS_Mixer_ChannelGetPosition to get the position of mixer's sources. The same thing applies to setting syncs.