Can you please help me create code for a "Tape Stop" emulation. This is were the PITCH + SPEED slows down together on all notes on all channels at the same time. Just as if you had put your finger on a record while playing on a record player, thus slowing everything down, eventually to a stop I will be using Stream Byter in Midi Designer
It is possible to transpose the notes and apply delays according to some other trigger (say a CC control) dynamically.
The main issue I see is that you want this to apply to all channels. When a note is transposed, the script has to remember that transposition and apply that to the note off that comes later. For this you need a lookup table of 16 x 128 size to cater for all channels and all notes. The Stream Byter in MDP is a little older and doesn't have enough variable space for such a table. However, the newer StreamByter app has exactly such an array for this purpose - (the 'W' (wide) array). For this reason it would be a better choice for this. The StreamByter app can be run standalone and be fed the musical and control events via CoreMIDI. StreamByter app also has GUI sliders which could be used to control the slowdown. Alternatively it can be hosted in something like AUM and controlled either from StreamByter's GUI or remote MIDI control via a CC.
I can give you a preset for StreamByter (standlone or AU) easily enough that gives you the slowdown on all channels and a GUI slider. However, if you can live with just one or two channels of slowdown, then I can give you code for MDP.
Thanks Nic If you can give me 2 channels for MDP that would be great. I do also have AUM installed as well, if you could give me both I could see which one would best suit my requirements Regards, Rick
I have not forgotten about this; it's proved a little trickier than I had anticipated but I am getting there.
Rather than transpose note events it seems to work better by using pitchbend to drop the pitch of whatever is playing, so the first issue I flagged isn't an issue with this method.
There will be some limitations on how this will work; we can only slow down events (not the other way round) since we're adjusting events in real time and we can't we can't speed up events we haven't received yet. We can reduce the flow of events only in a downwards ratchet style; if you try and speed up after slowing down then events will be incorrectly ordered too.
Anyhow, once I have something for you to try out (that I'm reasonably happy with) I will be back.
OK, I have had a stab at this and have something that works reasonably well for me using pitchbend to control the pitch.
The code to copy and paste is attached. This should work with MidiFire, MDP and StreamByter app/AU. I tested with the latter inside AUM with a loop of events generated by Nimble Looper feeding into the Tape Stop script and playing bs-16i on 2 concurrent MIDI channels.
There are some things you need to be aware of:
First, the slowdown is controlled using an external continuous controller. I used controller 14 on MIDI channel 1 which matched my hardware controller. If you wish to control with a different controller number/channel then you modify this at the top of the script:
ASS K0 = B0 0E # cc used to control slowdown
to match whatever controller you choose. Or if using via MDP then setup your control to match the script.
Now, once you have setup the correct controller, then this is how it works.
When the controller is at 127 (7F) all the events pass through unaltered. Once you start moving the controller downwards, the pitch and speed slow down. The further you move the controller down, the slower the rate of change is. A slowdown starts on the first event received after the controller moves from 127. Note and controller events passing through the script are slowed down. No other events will be delayed.
When a slowdown is in effect you can only move the controller further down - ie. make it slower. Moving it up has no effect unless you move it up to 127 again in which case the slowdown stops completely.
Once you move the controller to 0, then all new events get blocked but any existing events still get delayed and played. The quicker you move to 0, the less extra notes there will be in the pipeline. Once the controller hits zero then the script sends an 'All Notes Off' message on all 16 channels. To restore everything after a stop, move the controller back to 127 and events will start flowing again and the tape stop script will be ready for another dive bomb.
The left label shows the current slowdown value or 'RDY' (for no slowdown) or 'STP' when the controller goes to 0 and it is in 'stop' mode. The right hand label (which I used for debugging) shows the number of ms that the most recent event was delayed. Might be of interest in getting a feel for the degree of slowdown in effect.
Finally, there are a few more constants at the top that you might wish to play around with:
ASS K2 = 100 # magic delay factor
This is an internal factor that more or less controls the sensitivity of the slowdown. The higher value, the less the slowdown range and vice versa.
ASS K3 = 18 # pb range (24 semis)
And this sets the pitchbend range and thus controls how far the pitch will drop over the slowdown range.
Both of these values I set by trial and error which seemed to work well for me. It did sound a bit like a tape slowing down to my ears.
# Tape Stop by audeonic IF LOAD # constants ASS K0 = B0 0E # cc used to control slowdown ASS K2 = 100 # magic delay factor ASS K3 = 18 # pb range (24 semis)
# locals ASS L80 = 7F # current cc value SET LB0 SRDY
# current time position (ms) ASS L81 = FFFF
# set pb range on each channel # using RPN 0 and neutral ASS I0 = B0 IF I0 < C0 +L # set range SND I0 64 00 SND I0 65 00 SND I0 06 K3 SND I0 64 7F SND I0 65 7F
# set neutral MAT I1 = I0 + 30 SND I1 00 40
MAT I0 = I0 + 1 END END
# force note offs 9X XX 00 = 8X
# slow note & CC events (except control) IF MT < C0 IF M0 != K0 K1 IF L80 > 0 # event delay into I82 ASS I82 = 0
IF L80 != 7F # get timing point ASS I0 = T0
# first event in timeline? IF L81 == FFFF ASS I0 = 0 ASS L81 = 0 END
# update current positio - calculate delay into I82 # is linear good enough? MAT I2 = 7F - L80 MAT I3 = L81 * 7F MAT I3 = I3 / I2 MAT I82 = I3 - L81
MAT I82 = L81 / K2 MAT I82 = I82 * I2 SET LB1 I82 +D
# just in case IF I82 > FA00 ASS I82 = FA00 END END
# clone+delay and block original SND M0 M1 M2 +DI82 XX = XX +B END END END
# handle CC changes IF M0 == K0 K1 IF M2 != L80 ASS I80 = 0 # cc change flag
# CC gone to 127 IF M2 == 7F ASS I80 = 1 # reset timeline ASS L81 = FFFF END
# CC gone to 0 IF M2 == 00 ASS I80 = 1
# turn off all notes ASS I0 = B0 IF I0 < C0 +L SND I0 7B 00 MAT I0 = I0 + 1 END END
# slowing down IF M2 > 0 IF M2 < 7F IF M2 < L80 ASS I80 = 1 END END END
# supported change IF I80 == 1 # calculate pitchbend # fine (LSB) is 0 or 40 MAT I1 = M2 % 2 MAT I1 = 1 - I1 MAT I1 = I1 * 40
# coarse (MSB) is M2+1 / 2 MAT I2 = M2 + 1 MAT I2 = I2 / 2
# apply pitchbend on all channels ASS I0 = E0 IF I0 < F0 +L SND I0 I1 I2 MAT I0 = I0 + 1 END
# retain value into L80 ASS L80 = M2
# update label SET LB0 L80 +D IF L80 == 0 SET LB0 SSTP END IF L80 == 7F SET LB0 SRDY END
# block CC event XX = XX +B END
# CC is at zero, block all events IF L80 == 0 XX = XX +B END