Plug'n'Script - Midi Delay
- KVRian
- Topic Starter
- 1045 posts since 3 Jul, 2006
Hello,
Firstly, I'm really excited about this product! Great job, Blue Cat!
I've been using an old version of EnergyXT and a lot of MIDI VST2 plugins in order to create customized MIDI processing chains. Some of these are quite complex, so the thought of making them more simple and resource friendly by incorporating those into scripts is a very interesting alternative for me.
Having said that, I've built my first script, saving about 30 instances of other 'building-block' VST (e.g. pizmidi) and it works great.
Now coming to my question: the next thing I would need is the ability to push some midi events to the output, but with some delay: think about creating some sort of strumming effect.
Is there any way to create a Midi Delay in a script, where the input parameter is the delay time in ms?
A push method which would include some delay would be interesting (but maybe not efficient).
I've studied the way you make delays for audio, and it involves calculating the delay in number of samples, based on Sample Rate and the delay time. Then it involves a circular sample buffer which helps `looking into the past`. I'm thinking to use this approach to build my MIDI delay, but have not been very successful, because I want to deal with midi events instead of samples in the audio buffer.
Do you have any hints?
I think it would also be nice to add a Midi Delay script as an example tutorial along with your other example scripts!
Thanks in advance for your feedback!
Firstly, I'm really excited about this product! Great job, Blue Cat!
I've been using an old version of EnergyXT and a lot of MIDI VST2 plugins in order to create customized MIDI processing chains. Some of these are quite complex, so the thought of making them more simple and resource friendly by incorporating those into scripts is a very interesting alternative for me.
Having said that, I've built my first script, saving about 30 instances of other 'building-block' VST (e.g. pizmidi) and it works great.
Now coming to my question: the next thing I would need is the ability to push some midi events to the output, but with some delay: think about creating some sort of strumming effect.
Is there any way to create a Midi Delay in a script, where the input parameter is the delay time in ms?
A push method which would include some delay would be interesting (but maybe not efficient).
I've studied the way you make delays for audio, and it involves calculating the delay in number of samples, based on Sample Rate and the delay time. Then it involves a circular sample buffer which helps `looking into the past`. I'm thinking to use this approach to build my MIDI delay, but have not been very successful, because I want to deal with midi events instead of samples in the audio buffer.
Do you have any hints?
I think it would also be nice to add a Midi Delay script as an example tutorial along with your other example scripts!
Thanks in advance for your feedback!
-
Blue Cat Audio Blue Cat Audio https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=39981
- KVRAF
- 5821 posts since 8 Sep, 2004 from Paris (France)
A MIDI delay is quite different from an audio delay, as MIDI events may arrive at random times. You need to keep a buffer of events and fire them later, when the delay has passed, and the appropriate offset should also be set.
I guess the easiest way to do it is to store events when they arrive, with a timestamp equal to delay+offset, then everytime a process() call happens, remove the samplesToProcess value to the timeStamp. When the timestamp reaches 0, it's time to send the event.
We'll try to add a sample in a future release.
I guess the easiest way to do it is to store events when they arrive, with a timestamp equal to delay+offset, then everytime a process() call happens, remove the samplesToProcess value to the timeStamp. When the timestamp reaches 0, it's time to send the event.
We'll try to add a sample in a future release.
- KVRian
- 698 posts since 7 Dec, 2009 from GWB
Greetings-
Not Plug 'n' Script, but JSFX: the "MIDI Delay" effect that comes with Reaper might be of some interest:
HTH, Charles
Not Plug 'n' Script, but JSFX: the "MIDI Delay" effect that comes with Reaper might be of some interest:
Code: Select all
desc:MIDI Delay
slider1:0<0,1000,1>Delay (ms)
slider2:0<0,16,0.25>Delay (QN)
slider3:0<0,10000,1>Delay (samples)
slider4:0<0,16,1>Channel (0=omni)
slider5:0<0,16,1>Bus (0=all buses)
in_pin:none
out_pin:none
@init
ext_midi_bus=ext_nodenorm=1;
buf_l=buf_r=0;
buf_hdr = 3; // position, length (incl header), bus
max_ram = __memtop();
@slider
chan = slider4-1;
bus = slider5-1;
@block
delay_samples = floor((slider1*0.001 + slider2*60.0/tempo)*srate + slider3 + 0.5);
delay_sc = (delay_samples + samplesblock);
delay_isc = 1.0 / delay_sc;
// process incoming events
while((l=midirecv_buf(offs,buf_r+buf_hdr,max_ram-buf_r-buf_hdr))>0)
(
(bus<0 || midi_bus == bus) &&
(chan<0 || (l <= 3 && (fb=buf_r[buf_hdr])<0xf0 && (fb&0xf) == chan)) ? (
buf_r[0] = (delay_samples+offs) * delay_isc;
buf_r[1] = buf_hdr + l;
buf_r[2] = midi_bus;
buf_r += buf_hdr + l;
) : (
midisend_buf(offs,buf_r+buf_hdr,l);
);
);
// process outgoing events
rd = buf_l;
while (rd<buf_r)
(
rd==buf_l && (offs=floor(rd[0]*delay_sc+0.5)) < samplesblock ? (
midi_bus=rd[2];
l = rd[1];
midisend_buf(max(offs,0),rd+buf_hdr,l-buf_hdr);
buf_l = (rd += l);
) : (
rd[0] -= samplesblock * delay_isc;
rd += rd[1];
);
);
// compact buf if needed
buf_l >= buf_r ? (
buf_l=buf_r=0;
) : (
buf_l > max(1024,buf_r*.5) ? (
(buf_r-=buf_l) > 0 ? memcpy(0,buf_l,buf_r) : buf_r=0;
buf_l=0;
);
);
-
- KVRian
- 628 posts since 18 May, 2010
I wrote this as one of my first experiments. It delays MIDI, although not in ms, but in number of callbacks:
Code: Select all
#include "library/Midi.hxx"
string name = "MIDI Delay";
string description = "Simple script to delay midi events";
// Input parameter section
// One input parameter: the delay time
array<string> inputParametersNames = {
"Delay"
};
array<double> inputParametersMin = {
0
};
array<double> inputParametersMax = {
250
};
array<double> inputParametersDefault = {
6
};
array<int> inputParametersSteps = {
6
};
array<string> inputParametersFormats = {
".0"
};
array<double> inputParameters(inputParametersNames.length);
// Global variables
/// maximum delay + 1, unit is number of calls
const uint16 maxDelay = 251;
/// array of event lists; each slot is a time step corresponding to a call to
/// processBlock.
array< array<MidiEvent> > eventQueue(251);
/// current position in the array, walks over eventQueue in circular fashion
uint16 curPos = 0;
// global variables set by updateInputParameters()
uint16 delay = 0;
/** processBlock plays the queue of events arriving at its call after "delay"
more calls. First it appends the incoming events to the delayed position,
then plays the current position, which allows delay to be 0, as well as
modification of delay during playing. The only thing it doesn't compensate
for is balanced events, e.g. note on/off; when the delay is shortened
between such events, the note off may occur before the note on, resulting
in a stuck note.
*/
void processBlock(BlockData& data) {
uint delayedPosition = (curPos + delay) % maxDelay;
array<MidiEvent>@ delayQueue = @eventQueue[delayedPosition];
for (uint i = 0; i < data.inputMidiEvents.length; i++) {
delayQueue.insertLast(data.inputMidiEvents[i]);
}
array<MidiEvent>@ curQueue = @eventQueue[curPos];
for (uint i = 0; i < curQueue.length; i++) {
data.outputMidiEvents.push(curQueue[i]);
}
curQueue.resize(0);
curPos = (curPos + 1) % maxDelay;
}
void updateInputParameters() {
delay = uint16(inputParameters[0] + 0.1);
print(delay + " " + inputParameters[0]);
}
- KVRian
- Topic Starter
- 1045 posts since 3 Jul, 2006
Thank you all for your valuable inputs! Greatly appreciated!