Sync audio sample to start with next bar

I’m wondering if there’s a way to start the global transport, and later trigger either a short audio sample or audio loop to start playing at the beginning of the next bar. I’m pretty new to Cantabile performer, but I’ve got a gig this Friday that I’d really like to have a working setup for.

I’m sending a click (using Cantabile’s metronome routed to an aux out) to the drummer, and running my keyboard patches through the mains. In one tune, I need to trigger an audio sample 8-bar loop partway through the song, which continues until the end; in another, I trigger a short audio sample every 8 measures. Unfortunately, in all cases, there are open repeat sections before each of these events, so I can’t just pad the audio file with silence, as it won’t always start/end in the same place. So far, we’ve managed pretty well with me just starting the loop partway through, but if our tempo drifts in the sections before the sample, it could cause issues. And when I need to trigger the short sample, my part is pretty involved, so getting it to start at the exact right instant has been almost impossible.

For the looped tune, I’ve created a region of exactly the right length, and created a binding from transport to the media player, and set up a button on my midi controller to start and stop transport. The problem is that this way, there’s no click for the first half of the tune, leaving us open to tempo drifts. Someone suggested having transport on the whole time with the sample muted, and unmute the sample on cue. But if the open repeats aren’t a multiple of 8, we might end up unmuting in the middle of the track. I know there are bindings for “Next Play Range” and “Next marker”, but I’m not totally sure how they work. I also tried using the sync options, but that seemed to kill the looping functions. Any suggestions?

For the other tune with the short sample, I tried using the sync options, but when those were enabled, the track played as soon as I started Transport, and I couldn’t get it to start again. Muting/unmuting isn’t an option here because a) the sample has a pretty long tail, so looping won’t work; and b) pressing a button once is already a lot of work given my involved part, so twice is out of the question. Ideally, I’d like to be able to hit the cue button a little early in the preceding bar, and have the sample play when transport gets to the next bar. I know you can delay state loading, but I can’t fully conceptualize the binding/routing I’d need to do to get that to work as described; also, that sounds like creating a lot of states, which I don’t want to do.

Any help or suggestions are much appreciated! Thanks!

Hi Anatol and Welcome to the forum!

To do this with Cantabile you need to use Transport Position bindings and they are available on the Performer version. What version are you running?

Cheers,

Dave

Hi Dave, I’m using Cantabile Performer (64-bit, Windows 10).

Hi Anatol,

First it would help to set the transport to musical time

Then open the timeline window. It shows the track of the currently selected media player. On the right side there is a drop down menu if you want to turn on snapping and zoom control. I would in your case use snapping since you want the cues on measure markers.

Once the cursor is where you want it you go to the bindings tab and create a binding to start the playback of any given media player. First you set the source to Transport and select “on cross position”. In the dialog box that appears you either type in the measure and beat numbers or use the “Use Current Position” to set the trigger point.

Then set the target to which ever media player you want to trigger.

Create a binding for each trigger point you need in the Song for each media player using this way.

I hope this gets you started on exploring how to do this in Cantabile …

Dave

1 Like

Wow, thanks for the super fast and detailed reply! I’ll try this when I get home and let you know how it goes!

One potential wrinkle I foresee: the looped sample I was provided with had a slight pad at the start and tail at the end (about 25 ms each), so the audio file doesn’t quite align metrically, but I suppose I could just trim the audio file in a DAW first. Is there a way to use the range I’ve already defined?

Also, as I’m writing this out, I’m not sure your solution really gets at the problem I’m having. If I’ve understood correctly, this will play at specific points in the audio file once the Transport gets to that specified position? I kinda need the opposite. The audio files I’m dealing with are pretty short (5-20 seconds); most posts on the forum with similar questions are about full-length backing tracks, which these definitely are not. Let me try to explain another way:

  • I want Transport to start and continue for an unspecified number of bars;
  • When I press a button on my MIDI controller, I want Transport to continue, and (ideally at the start of the next bar, rather than immediately) have an audio file play from a specified point;
  • When the audio file plays, depending on the song, it needs to either:
    a) loop until I stop Transport; or
    b) play to the end, and be ready to be cued again by the MIDI controller.

Basically, the binding source should originate from a controller input, potentially through some intermediate bindings, then to the media player to cue at a specific point. Does that make sense?

Yes, in the timeline you can use the range brackets to compensate for this and also set looping repeats up to infinite.

Oops, I didn’t get it totally, sorry. I think what you want to do can be done but to be honest I have never tested a setup that had these requirements. I’ll see if I can come up with a way forward.

A question is since you are using the metronome to sync drummer are your audio clips also synced tempo wise? Or are they MIDI tracks?

Dave

1 Like

Thanks Dave,

Yes, it is an audio file. The composer exported an mp3 created with MIDI in their DAW, aligned to a grid, but they lost the session when they updated their OS. The good news though is that we know the exact tempi, so I was able to make the calculations to know exactly where to trim the audio file so that it stays in sync with the metronome/Transport.

Is this question worthy of @brad?

Hi Anantol,

I think it would be a good idea to see if @brad has any thoughts on your usage requirements. I have cobbled together some test cases using your list of needs and some of it can be done I think but I need to try some more things out and see if the triggering system I am devising holds sync with a free running metronome. We’ll see if Brad has any ideas but I will try to get together something if I can …

Cheers,

Dave

I got a working solution! I had to script a plugin using reajs, but it works. I routed the MIDI from my controller to MIDI In on the Reajs plugin. The plugin listens for messages from a specific controller number, stores the value, and waits until the next measure before sending the stored value. Then there’s a binding from Reajs triggered on “Controller (Switch)”, sent to the media player to Play/Stop. Here’s my code, if anyone ever finds this useful. Beware, it’s very rough and hacky:

desc:MIDI Delay until next bar

slider1:CTRL_NUM=20<0,127,1>Controller number to delay

in_pin:none
out_pin:none

@init
  CONTROLLER = $xB0;
  delayed_message = -1;
  delayBool = 0;

@block
  currBeat_i = ((floor(beat_position)) % ts_num);   //integer part
  currBeat_f = beat_position - floor(beat_position);//float part

  function calcDelay() (                            //calculates samples remaining until next bar
    rem_beats = ts_num - (currBeat_i + currBeat_f);
    rem_samples = floor((rem_beats / ((tempo+0.0)/60.0)) * srate);
    rem_samples;
  );
    
  (play_state == 1 || play_state == 5) ? (        //check transport is on
    while (midirecv(offset,msg1,msg2,msg3)) (     //receive MIDI messages
      (msg1 == CONTROLLER && msg2 == CTRL_NUM) ? ( //check if this is the right controller
        delayed_message = msg3;                                 //if yes, store value and change boolean
        delayBool = 1;
      ) : (
        midisend(offset,msg1,msg2,msg3);                        //otherwise, passthrough
      );
    );

    (delayBool != 0 && delayed_message >= 0) ? ( //if there's a delayed message...
      rem = calcDelay();                  //calculate how many samples remain until next beat
      (rem < samplesblock) ? (                  //if next beat is within this block...
        midisend(rem,CONTROLLER,CTRL_NUM,delayed_message); //...send delayed message...
        delayBool=0;                              //...and reset booleans.
        delayed_message = -1;
      );                                       //otherwise, do nothing
    );
  );

One potential issue for others wanting to use this code is that only one message can be stored, so this only really works with switches and gates, but that’s by design; if I accidentally stop the sample a section too early, I have until the end of the bar to correct without any changes happening!

Thanks for your help, @dave_dore!

3 Likes

Glad you made a cool solution Anatol, have a good performance this weekend!

Dave

FWIW, I came up with a Cantabile based solution. It allows the metronome to free run but provide trigger signals on each measure that can be used to do other things. It uses an embedded rack to act as a filter that allows the next Measure Flasher signal to pass through the rack when you use a controller switch to enable it and then it will start a media player and then disables the rack to block all other Measure flasher signals until you send another control signal or stop the main transport. It is entirely asynchronous with no Master Slave on the media players. This is because the metronome needs to run all the time to the drummer but the playback tracks need to start in sync with the metronome hence the trigger based on the measure flasher.

initialized trigger racks

internal routing in the embedded racks

The bindings below are marked to explain their functions. The first 2 initialize the 2 triggering racks to disabled so they won’t pass triggers. The next 2 stop the 2 media players along with the metronome, The reason for 2 media players is because each use case needed a separate one and the looping function of Cantabile is used. (an infinite loop on media player 1 and re-trigger-able track on media player 2). The next 2 bindings are to route the controller switches to enable the trigger racks so they can pass a trigger. The next 2 bindings receive the trigger CC that loops through the rack and start the one or the other media players. The last 2 bindings use the same return trigger that starts the media player to disable the trigger racks until they receive a new manual switch from the physical controller.

Anyway that’s what I cooked up over here. Good puzzle!

Dave

3 Likes

Wow, that’s great! In the long run, I’ll implement this, because it’s probably more stable, but for right now, I probably should spend more time practicing than patching.

I’ve got to say, I love this program, and I really love this community! Thanks for your help Dave!

1 Like

This is all very cool - always surprised at what you guys manage to come up with.

fwiw: Cantabile’s support to sync playback with the next beat/bar isn’t great. Cantabile 2 used to have better support for this but it was a bit of a mess so I left it out of v3 pending hearing more real-world requirements and it never really came up, well occasionally but not as often as I thought.

My plans to improve this are tied with with support for multi-track media file support.

1 Like