Author Topic: Custom Playback Questions regarding channel locking and buffer flushing  (Read 59 times)


  • Posts: 45
Hello BASS friends,

First a bit of context regarding my questions. I'm using BASS primarily to sidestep the convoluted nature of enumerating and initializing audio devices, and for easy access directly to the audio hardware output buffer. In my program I have two concurrently running decoding streams whose output can be listened to via either of two different audio devices. For the sake of discussion, I'll refer to the audio streams as Main and Cue, and the output devices could be Speakers and Headphones.

The code using BASS is roughly designed as follows:

  • Create two decode only streams on the NoSound device using BASS_StreamCreate. This is the Main and Cue stream
  • Using BASSMix, split both streams for each of the two output devices using BASS_Split_StreamCreate. The two devices now each have their own split version of the Main and Cue Stream.

A previous discussion exists on this forum that lead to this design:

So far so good... I have a complex data structure in which I keep the audio I wish to playback, but for the sake of simplicity we'll just pretend it's one giant array of floats which I keep a simple pointer to. It can be called our BufferedToPointerMain and BufferedtoPointerCue. These pointers are an 'ok' approximation of where playback is occurring. But I've had it with 'ok'. I want good or possibly great. To achieve this I now use BASS' own BASS_ChannelGetPosition to get a more accurate estimate of what am I actively hearing (as opposed to where has the application actively buffered to). BASS_ChannelGetPosition is called on the device I'm interested in getting a more realistic playback position from.

My question regards how to best/properly reset the position of my playback if I suddenly decide I want to jump to somewhere new in the giant array of floats I have.

I currently perform the following steps listed in order, to set the playback position of my Main stream to 0 (forget about the Cue stream for now):

  • Call BASS_SetDevice for the NoSound Device.
  • Lock the Main Stream on the NoSound device using BASS_ChannelLock.
  • Call BASS_ChannelSetPosition on the Main Stream with position 0.
  • Call BASS_ChannelPlay on the Main Stream with the restart flag set to true.
  • Repeat the previous two steps for the split Main streams given to the Speakers and Headphones Devices.
  • Set BufferedToPointerMain to 0.
  • Unlock the Main Stream on all the main streams.

Now I wonder:
  • From the previous forum topic, I believe I only need to worry about locking streams on the NoSound device, correct?
  • How can I be sure that when I call the BASS_ChannelLock, that my custom callback function isn't actively being executed? Does the BASS_ChannelLock function block execution until the callback is finished?
  • Am I resetting the playback properly to flush the buffers? I know that using BASS_ChannelSetPosition is sort of 'functionless' in my case, as I'm the one providing (potentially endless) data. But BASS's internal playback position is most helpful if I can set the channel position to match my own BufferedToPointer positions.
  • If I am resetting the playback buffers properly to flush them, I'm surprised that I still get some residue audio after resetting playback. I have silence at the beginning of my giant array of floats, but if I'm actively hearing music (later in the giant array of floats) and reset the playback, I always get a bit extra audio left over. Is this avoidable without changing the size of BASS' playback buffer?

Hopefully all of this made some sense.

Thanks in advance,


Ian @ un4seen

  • Administrator
  • Posts: 20063
You should also call BASS_Split_StreamReset with the source handle to reset all/both of its splitters. I don't think you will need to lock anything, you can instead stop the splitters before seeking. Something like this:

Code: [Select]
BASS_ChannelPause(split[0]); // pause the splitters
// set the source's new position here
BASS_Split_StreamReset(source); // reset the splitters
BASS_ChannelPlay(split[0], false); // resume the splitters

Note this is assuming that the splitters have been linked via BASS_ChannelSetLink:

Code: [Select]
BASS_ChannelSetLink(split[0], split[1]); // link the 2nd splitter to the 1st splitter

If not, add BASS_ChannelPause/Play calls for the 2nd splitter.