Ping Pong Looping Sample help.

Started by AndyMK,

AndyMK

Hi. I am trying to create ping pong looping on a sample (BASS_SAMCHAN_STREAM)

Procedure LoopSyncForward(handle.l, channel.l, dta.l, *user.loop)
  Select *user\type
    Case 1; <- Forward
      BASS_Mixer_ChannelSetPosition(channel, *user\start, #BASS_POS_BYTE)
    Case 2; <- Ping Pong
      BASS_ChannelSetAttribute(channel, #BASS_ATTRIB_REVERSE_DIR, #BASS_FX_RVS_REVERSE)
  EndSelect
EndProcedure

Procedure LoopSyncReverse(handle.l, channel.l, dta.l, *user.loop)
  Protected val.f
  BASS_ChannelGetAttribute(channel, #BASS_ATTRIB_REVERSE_DIR, @val.f)
 
  If val = #BASS_FX_RVS_REVERSE
    BASS_ChannelSetAttribute(channel, #BASS_ATTRIB_REVERSE_DIR, #BASS_FX_RVS_FORWARD)
  EndIf
 
EndProcedure

ProcedureCDLL SampleSetLoop(sample.l, *my.loop)
  Select *my\type
    Case 1
      BASS_ChannelSetSync(sample, #BASS_SYNC_POS, *my\stop, @LoopSyncForward(), *my)
    Case 2
      BASS_ChannelSetSync(sample, #BASS_SYNC_POS, *my\stop, @LoopSyncForward(), *my)
      BASS_ChannelSetSync(sample, #BASS_SYNC_POS, *my\start, @LoopSyncReverse(), *my)
  EndSelect
EndProcedure

When the stream reverses, what happens to BASS_ChannelGetPosition? Is the position read backwards?
I also noticed the forward sync is triggered slightly early. For example, triggering at 80000 actually triggers at 79784

Ian @ un4seen

Yes, the BASS_ChannelGetPosition value will be going backwards when playing in reverse.

The BASS_POS_SYNC syncs should have BASS_SYNC_MIXTIME set so that they get triggered when the decoder reaches the position rather than when playback does. That'll ensure that the looping happens at exactly the wanted positions.

AndyMK

I tried BASS_SYNC_MIXTIME. When i try getting the position inside the syncproc, it still gives me a slightly smaller value

AndyMK

Forgot to mention the source channel is plugged in to a child mixer.

Ian @ un4seen

Quote from: AndyMKI tried BASS_SYNC_MIXTIME. When i try getting the position inside the syncproc, it still gives me a slightly smaller value

Make sure you use the BASS_POS_DECODE flag in that BASS_ChannelGetPosition call to get the decoding position rather than the playback position (playback will be behind due to buffering).

Quote from: AndyMKForgot to mention the source channel is plugged in to a child mixer.

Does that mean the BASS_POS_DECODE flag is set on the reverse stream? If so, BASS_SYNC_MIXTIME will automatically apply to its syncs (and BASS_POS_DECODE to BASS_ChannelGetPosition) because it has no playback buffer.

If the syncs still don't seem to be working properly, and you aren't already using this latest BASS_FX build, please give it a try:

    www.un4seen.com/stuff/bass_fx.zip

AndyMK

Replaced bass_fx with the one you sent and it works now. All my channels and mixers are decoding ones. Original code seems to work now without BASS_POS_DECODE or BASS_SYNC_MIXTIME. Thanks.

AndyMK

I thought i had the latest version from the website so i re downloaded it to check and the version on your website gives the result i was getting. The one you linked me to in this post works.

Ian @ un4seen

Quote from: AndyMKReplaced bass_fx with the one you sent and it works now. All my channels and mixers are decoding ones. Original code seems to work now without BASS_POS_DECODE or BASS_SYNC_MIXTIME. Thanks.

Great. The update does include some changes in sync handling.

Quote from: AndyMKI thought i had the latest version from the website so i re downloaded it to check and the version on your website gives the result i was getting. The one you linked me to in this post works.

Yeah, the update above hasn't been officially released yet. All builds in the "stuff" directory are generally not yet released updates/betas.

AndyMK

#8
The only issue i have now is if i set the loop start to zero, when the reverse starts, the sample position never gets to zero to fire the other syncproc.
*edit*
It gets to zero and frees the channel because i had BASS_STREAM_AUTOFREE set on the channel. I removed it and the sync that was set to trigger at zero still does not fire when the position gets there. I set up a BASS_SYNC_END and that fires but the strange thing is, with a BASS_SYNC_END and an empty proc on it, the syncproc set to zero now fires. Can you explain this?

Procedure LoopSyncForward(handle.l, channel.l, dta.l, *user.loop)
  Select *user\type
    Case 1
      BASS_Mixer_ChannelSetPosition(channel, *user\start, #BASS_POS_BYTE)
    Case 2
      BASS_ChannelSetAttribute(channel, #BASS_ATTRIB_REVERSE_DIR, #BASS_FX_RVS_REVERSE)
  EndSelect
EndProcedure

Procedure LoopSyncReverse(handle.l, channel.l, dta.l, *user.loop)
  Protected val.f
  BASS_ChannelGetAttribute(channel, #BASS_ATTRIB_REVERSE_DIR, @val.f)
 
  If val = #BASS_FX_RVS_REVERSE
    BASS_ChannelSetAttribute(channel, #BASS_ATTRIB_REVERSE_DIR, #BASS_FX_RVS_FORWARD)
  EndIf
EndProcedure

Procedure FreeSync(handle.l, channel.l, dta.l, *user.loop)
  Protected val.f
  BASS_ChannelGetAttribute(channel, #BASS_ATTRIB_REVERSE_DIR, @val.f)
 
  If val = #BASS_FX_RVS_REVERSE
    BASS_ChannelSetAttribute(channel, #BASS_ATTRIB_REVERSE_DIR, #BASS_FX_RVS_FORWARD)
  EndIf
EndProcedure

ProcedureCDLL SampleSetLoop(sample.l, *my.loop)
  Select *my\type
    Case 1
      BASS_ChannelSetSync(sample, #BASS_SYNC_POS, *my\stop, @LoopSyncForward(), *my)
    Case 2
      BASS_ChannelSetSync(sample, #BASS_SYNC_POS, *my\stop, @LoopSyncForward(), *my)
      If *my\start > 0
        BASS_ChannelSetSync(sample, #BASS_SYNC_POS, *my\start, @LoopSyncReverse(), *my); <- this never fires when set to zero after a reverse.
      Else
        BASS_ChannelSetSync(sample, #BASS_SYNC_END, 0, @FreeSync(), #Null)
      EndIf
  EndSelect
EndProcedure

Ian @ un4seen

Quote from: AndyMKThe only issue i have now is if i set the loop start to zero, when the reverse starts, the sample position never gets to zero to fire the other syncproc.
*edit*
It gets to zero and frees the channel because i had BASS_STREAM_AUTOFREE set on the channel. I removed it and the sync that was set to trigger at zero still does not fire when the position gets there. I set up a BASS_SYNC_END and that fires but the strange thing is, with a BASS_SYNC_END and an empty proc on it, the syncproc set to zero now fires. Can you explain this?

Ah yes, I think I see the issue you're having there. Here's another update for you to try:

    www.un4seen.com/stuff/bass_fx.zip

Btw, these BASS_FX updates add support for BASS_POS_LOOP and BASS_POS_END (with BASS 2.4.18), and I think it'd also be possible to implement your looping using them and a BASS_SYNC_END sync to switch direction at each end. Something like this:

BASS_ChannelSetPosition(revstream, loopstart, BASS_POS_LOOP);
BASS_ChannelSetPosition(revstream, loopend, BASS_POS_END);
BASS_ChannelSetSync(revstream, BASS_SYNC_END | BASS_SYNC_MIXTIME, 0, EndSyncProc, 0);

void CALLBACK EndSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user)
{
    float dir;
    BASS_ChannelGetAttribute(channel, BASS_ATTRIB_REVERSE_DIR, &dir); // get the current direction
    BASS_ChannelSetAttribute(channel, BASS_ATTRIB_REVERSE_DIR, -dir); // switch it
}

AndyMK

Quote from: Ian @ un4seen
Quote from: AndyMKThe only issue i have now is if i set the loop start to zero, when the reverse starts, the sample position never gets to zero to fire the other syncproc.
*edit*
It gets to zero and frees the channel because i had BASS_STREAM_AUTOFREE set on the channel. I removed it and the sync that was set to trigger at zero still does not fire when the position gets there. I set up a BASS_SYNC_END and that fires but the strange thing is, with a BASS_SYNC_END and an empty proc on it, the syncproc set to zero now fires. Can you explain this?

Ah yes, I think I see the issue you're having there. Here's another update for you to try:

    www.un4seen.com/stuff/bass_fx.zip

Btw, these BASS_FX updates add support for BASS_POS_LOOP and BASS_POS_END (with BASS 2.4.18), and I think it'd also be possible to implement your looping using them and a BASS_SYNC_END sync to switch direction at each end. Something like this:

BASS_ChannelSetPosition(revstream, loopstart, BASS_POS_LOOP);
BASS_ChannelSetPosition(revstream, loopend, BASS_POS_END);
BASS_ChannelSetSync(revstream, BASS_SYNC_END | BASS_SYNC_MIXTIME, 0, EndSyncProc, 0);

void CALLBACK EndSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user)
{
    float dir;
    BASS_ChannelGetAttribute(channel, BASS_ATTRIB_REVERSE_DIR, &dir); // get the current direction
    BASS_ChannelSetAttribute(channel, BASS_ATTRIB_REVERSE_DIR, -dir); // switch it
}


Hi Ian. I have BASS_SAMPLE_LOOP enabled. I tried the code above and it does work to ping pong the loop but as the sample is playing in reverse, it plays all the way back to the beginning instead of switching forward at the BASS_POS_LOOP position. If i have to set position it seems there is no point enabling sample looping. Am i missing something?

AndyMK

I removed BASS_SAMPLE_LOOP from the sample creation code and added
BASS_ChannelFlags(revstream, #BASS_SAMPLE_LOOP, #BASS_SAMPLE_LOOP)as the first line of code to the code you posted and all is good

AndyMK

When i stop a sound, i do
ProcedureCDLL SampleStop(sample.l)
  pos = BASS_Mixer_ChannelGetPosition(sample, #BASS_POS_BYTE)
  Dim node.BASS_MIXER_NODE(0)
  node(0)\pos = pos
  node(0)\value = 0
  BASS_Mixer_ChannelSetEnvelope(sample, #BASS_MIXER_ENV_VOL | #BASS_MIXER_ENV_REMOVE, node(), 1)
  FreeArray(node())
EndProcedure

which works to prevent any clicking on note release but BASS_MIXER_ENV_REMOVE does not seem to fire the BASS_SYNC_FREE syncProc i have set on the stream.

Ian @ un4seen

Are you including the BASS_STREAM_AUTOFREE flag in the sample's BASS_Mixer_StreamAddChannel call? If not, please try that (to have the sample freed when removed from the mixer).

AndyMK

That fixed it. Sorry, after re reading the help on BASS_MIXER_ENV_REMOVE, i realized it only removes it from the mixer. I thought it was freeing it.