# How to bounce a sound from left to right on every note ? (I tried Reaper's Transient Auto-Pan, but...)



## Fredeke (Jan 27, 2020)

Hello.

I am currently working on a mix project in Reaper for which I'm using only JS and Rea* plugins. 
One reason for that is portability across all the team's computers, and another reason is exercise.

I didn't post my question in the "DAW" forum because, although my question seems Reaper-specific at first, and I hope there is a Reaper-specific answer, I would also welcome a more general solution if there's one.

Anyway, I discovered Reaper's Transient-detecting Auto-Pan (or some such name) when trying to get a staccato instrument to play one note fully in one speaker, the next note in the other speaker, and so on: Basically, to bounce from one speaker to the other on every note.

I've tried fiddling with all settings for half an hour, and I can't get it to do exactly that. The best I could achieve was to bounce from one speaker to the other on each note, but not panned 100%: it would bounce from, say, 40% left to 40% right - and after more fiddling some other -apparently random- percentage...

*Does anyone know how to bounce from 100% left to 100% right and back and so on and so forth, on every note* (which are not at constant time interval - hence the transient detection seemed the way to go) ? 

Thanks


----------



## jacobthestupendous (Jan 27, 2020)

It'd be a hassle, but you could automate the panning.

Alternatively, depending on what instrument you're using (something smooth and connected wouldn't work for this), you could make two tracks that are identical but panned like you want, and you could play every other note in each. Not as easy as a plugin that just does it for you, but this wouldn't be as hard as it sounds. I'd write the line in one track, select every other note, copy the region into the other track, delete selected notes in the selected region, shift select in the other region to select all the other notes, and delete.

The way you decide to do it will have consequences for things like reverb, depending on how you're routing things.

Either option should translate to anyone else's Reaper session without any trouble.


----------



## Fredeke (Jan 30, 2020)

Your suggestion could work well with Reaper's automatic split of items (I think it detects silences between notes). Then I would just have to delete every other bit.

Also, I've been looking under the hood of JS plugins, and I think maybe I could modify the autopan plugin to my taste...


----------



## pderbidge (Feb 4, 2020)

If the bars are repetitive then you could take one bar and manually do some panning automation and then turn it into an Automation Item. After it becomes an automation item just copy and paste that automation item as many times as you need it to repeat. If the instrument you want to pan is not consinstent and repetitive then this won't help much.


----------



## Gerhard Westphalen (Feb 9, 2020)

Would midi panning work as well (whichever CC that is) or does your specific instrument not react to that? I'm not familiar with Reaper but I think in Cubase you might be able to do that with the logical editor and have it change the panning CC so maybe Reaper has a similar programming thing.


----------



## Royosho (Feb 9, 2020)

You could duplicate the track, pan one track left and the other right, then in each track delete every other note, so the two tracks alternate notes, and boom, panning every other note.

Plus in Reaper when you duplicate a track, it keeps the MIDI notes selected. So just select every other note once, then duplicate the track and delete selected notes from the first track. Then, in the next track, simply flip the selection and delete those notes.


----------



## tav.one (Feb 9, 2020)

This is one feature of FL Studio that I miss a lot. Every note can be panned individually and easily in Piano Roll, just like note velocity.


----------



## Fredeke (Feb 10, 2020)

Gerhard Westphalen said:


> Would midi panning work as well (whichever CC that is) or does your specific instrument not react to that? I'm not familiar with Reaper but I think in Cubase you might be able to do that with the logical editor and have it change the panning CC so maybe Reaper has a similar programming thing.


I forgot to mention it was an audio track.

To all: I'm trying to automate the process


----------



## vitocorleone123 (Feb 10, 2020)

If it's a somewhat regular interval, then Shaperbox can do it. If there's enough distinction between the notes, then PanMan, maybe.


----------



## Fredeke (Feb 10, 2020)

vitocorleone123 said:


> If it's a somewhat regular interval, then Shaperbox can do it. If there's enough distinction between the notes, then PanMan, maybe.


Yes, that's the case. I'll check those out.


----------



## pderbidge (Feb 11, 2020)

Gerhard Westphalen said:


> Would midi panning work as well (whichever CC that is) or does your specific instrument not react to that? I'm not familiar with Reaper but I think in Cubase you might be able to do that with the logical editor and have it change the panning CC so maybe Reaper has a similar programming thing.


You can pan the track with midi on it in an automation lane but it's not doing it with a midi cc but just affecting the audio output of the track. There's probably a JS plugin that can affect the actual midi note but in this case using the automation lane should yeild the same result.

Edit: This method only works on a midi track if the track is not a multi midi track meaning if you use Kontakt as single midi track. If you use an instance of Kontakt for multiple instruments ie; 1rst 2nd and 3rd violins then you need to bounce the instrument as an audio track and then automate the panning


----------



## Fredeke (Feb 15, 2020)

Finally I "just" modified Till's Transient-Driven Auto-Pan. I didn't know Lua but reading his rather clean code was pretty straightforward. The modification was minimal: I just added a "ping-pong" mode that is basically a copy of the bounce mode, except it now ignores step size, and just toggles between maxpan and -maxpan.


Here's the whole code (which I'll remove if Till comes complaining):


```
// based on Till's Transient Driven Auto-Pan v1.1 (Ping Pong mod by FSC)
//
// note: the effect sends data to regx. x is specified by the "send pan data to" slider
//
// note: the lookahead must be compensated manually in REAPER versions < 2
//
//

desc: Transient-Driven Auto-Pan v1.11 (Master)
//tags: processing stereo modulation
//author: Till (mostly)

slider1:0<0,2,1{Stereo,Left,Right}>Input
slider2:3<0,4,1{Left to Right,Right to Left,Bounce,Ping Pong (+/- Max overrides step sizes),Random (Ignores Pan/Random step size)}>Pan Mode
slider3:10<0,100,1>Pan Step Size (%)
slider4:0<0,100,1>Random Step Size (%)
slider5:100<1,100,1>Max Pan
slider6:20<0.1,1000,0.1>Fade Time (ms)
slider7:0<0,10,1>Sloppiness (Master Only)
slider8:250<20,500,1>Min Pause Between Pans (ms)
slider9:0<0,10,1>Max Delay (ms)
slider11:0<0,5,1{Left+Right,Left,Right,Sidechain Left+Right,Sidechain Left,Sidechain Right}>Director
slider12:0<0,1,1{No,Yes}>Preview Director
slider13:5<0,10,.1>Sensitivity
slider14:0<0,100,1>Look Ahead (ms)
slider15:0<0,10,1{Off,reg00,reg01,reg02,reg03,reg04,reg05,reg06,reg07,reg08,reg09}>Send Pan Data To
slider16:0<-100,100,1>Current Pan

in_pin:left input
in_pin:right input
in_pin:sidechain left input
in_pin:sidechain right input
out_pin:left output
out_pin:right output


/////////////////
///           ///
///  I N I T  ///
///           ///
/////////////////

@init

  log2 = log(2);
  sqrt2 = sqrt(2);

  curgain = 1;
  curgaindb = 0;

  sincelastpan = 0;
  now = 0;
  curpan = 0;
  sloppypan = 0;
  targetpan = 0;
  lastpan = 0;
  forth = 1;

  maxdelay = ceil((10 + 100) / 1000 * srate);

  delaybufferl = 0;
  delaybufferr = 2 * maxdelay;

  delaypointer = 0;

  rmswindow = 50 / 1000 * srate;
  rmsbuffer = 4 * maxdelay;

  rmssum = 0;
  rmsp = 0;

  pdc_bot_ch = 0;
  pdc_top_ch = 2;


/////////////////
///           ///
///  SLIDER   ///
///           ///
/////////////////

@slider

  // SLIDER VALUES

  input = slider1;
  mode = slider2;
  stepsize = slider3;
  randomstepsize = slider4;
  maxpan = slider5;
  fadetime = slider6 / 1000 * srate;
  sloppiness = slider7;
  pause = slider8 / 1000 * srate;
  delay = slider9 / 1000 * srate;
  director = slider11;
  preview = slider12;
  sensitivity = slider13;
  lookahead = slider14 / 1000 * srate;
  sendto = slider15;

  attack = max(1, sqr(sensitivity) * 0.003 * srate);
  release = max(1, pause / 2);

  tolerancedb = 12 - sensitivity;
  tolerance = 2 ^ (tolerancedb / 6);

  minrms = 2 ^ (-5 - sensitivity / 2);

  pdc_delay = lookahead;


/////////////////
///           ///
///  SAMPLE   ///
///           ///
/////////////////

@sample

  // choosing input

  input == 0 ? (
    l = spl0;
    r = spl1;
  ) : input == 1 ? (
    l = spl0;
    r = spl0;
  ) : input == 2 ? (
    l = spl1;
    r = spl1;
  ) : 0;


  // buffering

  delaybufferl[delaypointer] = l;
  delaybufferl[delaypointer + maxdelay] = l;
  delaybufferr[delaypointer] = r;
  delaybufferr[delaypointer + maxdelay] = r;


  // choosing director

  director == 0 ? (
    dl = spl0;
    dr = spl1;
  ) : director == 1 ? (
    dl = spl0;
    dr = spl0;
  ) : director == 2 ? (
    dl = spl1;
    dr = spl1;
  ) : director == 3 ? (
    dl = spl2;
    dr = spl3;
  ) : director == 4 ? (
    dl = spl2;
    dr = spl2;
  ) : director == 5 ? (
    dl = spl3;
    dr = spl3;
  ) : 0;

  cursample = (abs(dl) + abs(dr)) / 2;


// calcing rms power

  rmssum -= rmsbuffer[rmsp];

  rmssum += sqr(cursample);

  rmsbuffer[rmsp] = sqr(cursample);

  rms = max(sqrt(rmssum / rmswindow), minrms);

  rmsp+=1;
  rmsp >= rmswindow && rmsp = 0;

  threshold = rms * 1.68;
  thresholddb = 6 * log(threshold) / log2 + 3;


// detecting

  (cursample >= threshold) ? (
    cursampledb = 6 * log(cursample) / log2;
    targetgaindb = thresholddb - cursampledb;
  ) : (
    targetgaindb = 0;
  );

  targetgaindb > curgaindb ? (
    curgaindb += (targetgaindb - curgaindb) / release;
  ) : (
    curgaindb += (targetgaindb - curgaindb) / attack;
  );

  curgain = exp((curgaindb / 6) * log2);

  detector = cursample * curgain;


// panning

  pop = 0;
  now ? (
    (splsincelastpan > pause) && now = 0;
  ) : (
    detector > threshold * tolerance && (
      pop = 1;
      now = 1;
      splsincelastpan = 0;
      lastpan = curpan;
      mode == 0 ? (
        targetpan = curpan + stepsize + (rand(2) - 1) * randomstepsize;
        targetpan < -maxpan && (targetpan += 2 * maxpan);
        targetpan > maxpan && (targetpan -= 2 * maxpan);
      ) : mode == 1 ? (
        targetpan = curpan - stepsize - (rand(2) - 1) * randomstepsize;
        targetpan > maxpan && (targetpan -= 2 * maxpan);
        targetpan < -maxpan && (targetpan += 2 * maxpan);
      ) : mode == 2 ? (
        forth ? (
          targetpan = curpan + stepsize + (rand(2) - 1) * randomstepsize;
        ) : (
          targetpan = curpan - stepsize - (rand(2) - 1) * randomstepsize;
        );
        targetpan > maxpan && (
          targetpan = 2 * maxpan - targetpan;
          forth = 0;
        );
        targetpan < -maxpan && (
          targetpan = - 2* maxpan - targetpan;
          forth = 1;
        );
      ) : mode == 3 ? (
        forth ? (
          targetpan = maxpan ;
          forth = 0
        ) : (
          targetpan = -maxpan ;
          forth = 1
        );
      ) : (
        targetpan = ceil((rand(2) - 1) * maxpan);
      );
    );
  );

  splsincelastpan += 1;


//fading the pan

  curpan != targetpan && (
    fadepos = splsincelastpan / fadetime;
    fadepos = (-cos(min(fadepos, 1) * $pi) + 1) / 2;
    curpan = lastpan + (targetpan - lastpan) * fadepos;
  );


// sending pan info

  sendto > 0 && (
    sendto == 1 ? (
      reg00 = curpan;
    ) : sendto == 2 ? (
      reg01 = curpan;
    ) : sendto == 3 ? (
      reg02 = curpan;
    ) : sendto == 4 ? (
      reg03 = curpan;
    ) : sendto == 5 ? (
      reg04 = curpan;
    ) : sendto == 6 ? (
      reg05 = curpan;
    ) : sendto == 7 ? (
      reg06 = curpan;
    ) : sendto == 8 ? (
      reg07 = curpan;
    ) : sendto == 9 ? (
      reg08 = curpan;
    ) : sendto == 10 ? (
      reg09 = curpan;
    ) : 0;
  );


// sloppiness

  s = sloppiness * sloppiness * sloppiness * sloppiness;
  sloppypan = (curpan + s * sloppypan) / (s + 1);


// calculating the actual volumes

  abspan = abs(sloppypan);
  dominantpandb = 6 * abspan / 100;
  dominantpan = exp((dominantpandb / 6) * log2);
  recessivepan = 2 - dominantpan;


// leveling

  sloppypan > 0 ? (
    r = dominantpan * delaybufferr[maxdelay + delaypointer - lookahead];
    x = abspan / 100 * delay;
    f = x - floor(x);
    c = floor(x + 1) - x;
    l = recessivepan * (c * delaybufferl[maxdelay + delaypointer - floor(abspan / 100 * delay) - lookahead] + f * delaybufferl[maxdelay + delaypointer - ceil(abspan / 100 * delay) - lookahead]);
  ) : (
    l = dominantpan * delaybufferl[maxdelay + delaypointer - lookahead];
    x = abspan / 100 * delay;
    f = x - floor(x);
    c = floor(x + 1) - x;
    r = recessivepan * (c * delaybufferr[maxdelay + delaypointer - floor(abspan / 100 * delay) - lookahead] + f * delaybufferr[maxdelay + delaypointer - ceil(abspan / 100 * delay) - lookahead]);
  );

// outputting

  preview ? (
    spl0 = dl;
    spl1 = pop;
  ) : (
    spl0 = l;
    spl1 = r;
  );

  slider16 = curpan;

  delaypointer += 1;
  delaypointer >= maxdelay && (delaypointer = 0);
```

(Then while at it I tried to canibalize code from Stillwell's HPF/LPF plugin to add filters to the detector, but it didn't work so I guess I need to learn more)


----------



## Dex (Feb 16, 2020)

Thanks for the code! Your effect works nicely.

Edit: Fixed. See below. (_There's one error in the code you posted, though, that needs to be fixed for the effect to work - the "sloppypan > 0 ?" block needs a semicolon at the end of the last closed parenthesis.)_


----------



## storyteller (Feb 16, 2020)

Royosho said:


> You could duplicate the track, pan one track left and the other right, then in each track delete every other note, so the two tracks alternate notes, and boom, panning every other note.
> 
> Plus in Reaper when you duplicate a track, it keeps the MIDI notes selected. So just select every other note once, then duplicate the track and delete selected notes from the first track. Then, in the next track, simply flip the selection and delete those notes.


This.


----------



## Fredeke (Feb 17, 2020)

Dex said:


> Thanks for the code! Your effect works nicely. There's one error in the code you posted, though, that needs to be fixed for the effect to work - the "sloppypan > 0 ?" block needs a semicolon at the end of the last closed parenthesis.


Sorry. Bad copypasting I guess. I corrected it.


----------

