|
|
|
01-30-2017, 09:29 AM
|
#1
|
Human being with feelings
Join Date: Mar 2007
Posts: 4,018
|
"midi_drumseq" samplerate dependancy fix?
Hi,
I often use this older (in new versions of Reaper already not included) JS plugin "midi_drumseq" ("MIDI drum sequencer") which serve me very well for drummachine sounds (and because it is JS, it can be included in project folder for future use or backup).
Bad thing is that as it is now, the pitch of the sound changes with the audio interface / project sample rate (so 44,1kHz or 48kHz does make difference of course).
Could anybody "fix" the code to the point that it plays the sample(s) independently of sample rate set in audio interface, please?
Thanx a lot
akademie
here is the original code:
Code:
// This effect Copyright (C) 2004 and later Cockos Incorporated
// License: GPL - http://www.gnu.org/licenses/gpl.html
desc:MIDI drum sequencer
slider1:-6<-120,6,1>volume (dB)
slider2:/drum_patchsets:patchset_pacific1_adpcm.wav:Patch set
slider3:1<0.1,4,0.05>pitch scaling
slider4:16<1,64,1>max voices
slider5:-24<-120,0,1>gain at 0 velocity (dB)
slider6:60<0,127,1>base note
slider7:0<0,16,1>channel (0=omni)
slider8:0,meter
@init
max_voices=128; // allocate enough ram for 128 samples
ext_noinit=1;
voxlist=0;
voxsize=0;
voxentsize=4; // cur position, start position, end position, volume
sampleinfotab=voxentsize*max_voices; // list of samples with start/end pos
lslider2=-1000;
@slider
voxmaxsize=min(max_voices,slider4);
midichan=slider7;
midibase=slider6;
zerogain=2^(slider5/6);
vol=2^(slider1/6);
slider3=max(0.1,slider3);
actsize=0|min(min(actsize,slider4),4096);
tmp=slider2|0;
lslider2 != tmp ?
(
lslider2=tmp;
pcm_buffer=sampleinfotab+128*2;
num_samples=0;
filehandle=file_open(slider2);
filehandle > 0 ?
(
file_riff(filehandle,spl_nch,spl_srate);
spl_srate/=srate;
spl_nch==2 ?
(
numsilent=0;
pairs=min(file_avail(filehandle),(8*1024*1024 - 65536*2))*0.5;
inspl=0;
pos=0;
sampleinfotab[num_samples*2]=pcm_buffer;
loop(pairs,
file_var(filehandle,l);
file_var(filehandle,r);
quiet=max(abs(l),abs(r)) < 0.0001;
inspl || !quiet ?
(
pcm_buffer[0]=l;
pcm_buffer[1]=r;
inspl=1;
pos+=2;
pcm_buffer+=2;
);
inspl && quiet ?
(
(numsilent += 1) >= 2048 ? (
// new sample time
pcm_buffer-=numsilent*2;
sampleinfotab[num_samples*2+1]=(pos-numsilent*2);
num_samples+=1;
sampleinfotab[num_samples*2]=pcm_buffer;
inspl=0;
pos=0;
);
) : numsilent=0;
);
sampleinfotab[num_samples*2+1]=pos-numsilent*2;
num_samples+=1;
);
file_close(filehandle);
);
actsize=0;
);
slider8=num_samples;
sliderchange(slider8);
@block
gpitchsc=2*slider3*spl_srate;
spl_nch == 2 ? (
while(
midirecv(pos,msg1,msg23) ? (
midisend(pos,msg1,msg23); // pass through MIDI
ws=((msg23&127)-midibase)|0;
((midichan > 0 && msg1 == 9*16 + midichan-1) || // if MIDI noteon
(midichan==0 && msg1>=9*16 && msg1 < 10*16)) &&
ws >= 0 && ws < num_samples ? (
voxsize>=voxmaxsize ? (
voxsize=voxmaxsize-1;
memcpy(voxlist,voxlist+voxentsize,voxentsize*voxsize);
);
p=voxlist+voxentsize*voxsize;
p[1] = sampleinfotab[ws*2];
p[0] = p[1] -pos / gpitchsc;
p[2] = p[1]+sampleinfotab[ws*2+1];
vel = ((msg23 / 256.0)|0)/127.0;
p[3] = 1.0*vel + (zerogain*(1.0-vel));
voxsize+=1;
); //was noteon
1;
) // midi recv
: 0
); // while
);
@sample
spl_nch == 2 ? (
s0=s1=0;
a=voxlist;
loop(voxsize,
cpos = a[0];
cpos >= a[1] ? (
// samples, yeah!
gain=a[3];
addr=cpos|0;
addr-=addr&1;
s0+=addr[0]*gain; // todo: interpolation
s1+=addr[1]*gain;
);
cpos += gpitchsc;
cpos >= a[2]-1 ? (
voxsize-=1;
memcpy(a,a+voxentsize,voxsize*voxentsize - a);
) : (
a[0]=cpos;
a+=voxentsize;
);
);
spl0+=s0*vol;
spl1+=s1*vol;
); // spl_nch == 2
|
|
|
01-30-2017, 04:18 PM
|
#2
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
I'm not really sure it will work, but you could try this:
Code:
@block
gpitchsc=2*slider3*srate;
|
|
|
01-30-2017, 09:14 PM
|
#3
|
Human being with feelings
Join Date: Mar 2007
Posts: 4,018
|
Thanks Tale,
but unfortunately it does not change the behavior
Also it is already defined here, I think:
Code:
file_riff(filehandle,spl_nch,spl_srate);
spl_srate/=srate;
akademie
|
|
|
01-31-2017, 12:25 AM
|
#4
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by akademie
Also it is already defined here, I think:
Code:
file_riff(filehandle,spl_nch,spl_srate);
spl_srate/=srate;
|
Oops, I missed that... Can I download patchset_pacific1_adpcm.wav, so I can have a play with this myself?
|
|
|
01-31-2017, 02:41 AM
|
#5
|
Human being with feelings
Join Date: Mar 2007
Posts: 4,018
|
Tale,
effect loads any WAV file (with specific properties) from reaper's "Data\drum_patchsets". That adpcm file is only writen in code, but plugin will list and allow select other files that exist in that folder. It is little tricky to have correct file though (audio must have 1sec silence between hits, stereo 44.1kHz, I think = if I remeber correctly :-/).
Anyway, I will post working WAV file later today, because I am on a way to work right now.
Thank you
akademie
|
|
|
01-31-2017, 04:53 AM
|
#6
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by akademie
effect loads any WAV file (with specific properties) from reaper's "Data\drum_patchsets". That adpcm file is only writen in code, but plugin will list and allow select other files that exist in that folder. It is little tricky to have correct file though (audio must have 1sec silence between hits, stereo 44.1kHz, I think = if I remeber correctly :-/).
|
Thanks, I managed to get the plug-in up and running with a random drum sample.
Reloading the JSFX after changing the sample rate seems to fix things here. To overcome this I have made 2 simple changes:
Code:
file_riff(filehandle,spl_nch,spl_srate);
// spl_srate/=srate;
Code:
@block
gpitchsc=2*slider3*spl_srate/srate;
BTW, this script seems to incorrectly react to Note On with velocity 0, which it should treat as Note Off with velocity 64. I have also fixed that:
Code:
((midichan > 0 && msg1 == 9*16 + midichan-1) || // if MIDI noteon
(midichan==0 && msg1>=9*16 && msg1 < 10*16)) && (msg23 & (127*256) > 0) &&
ws >= 0 && ws < num_samples ? (
EDIT: Looking just a little longer/better I realize that the Note On velocity 0 is actually not a bug, it's a feature! So you probably won't want this change... However, then IMHO it should treat Note Off the same as Note On with velocity 0, so I am adding that to the interpolated version below.
Last edited by Tale; 01-31-2017 at 09:30 AM.
Reason: It's nog a bug, it's a feature!
|
|
|
01-31-2017, 05:40 AM
|
#7
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
And here is a version that also adds linear interpolation:
Code:
// This effect Copyright (C) 2004 and later Cockos Incorporated
// License: GPL - http://www.gnu.org/licenses/gpl.html
desc:MIDI drum sequencer
slider1:-6<-120,6,1>volume (dB)
slider2:/drum_patchsets:patchset_pacific1_adpcm.wav:Patch set
slider3:1<0.1,4,0.05>pitch scaling
slider4:16<1,64,1>max voices
slider5:-24<-120,0,1>gain at 0 velocity (dB)
slider6:60<0,127,1>base note
slider7:0<0,16,1>channel (0=omni)
slider8:0,meter
slider9:1<0,1,1{off,on}>interpolation
@init
max_voices=128; // allocate enough ram for 128 samples
ext_noinit=1;
voxlist=0;
voxsize=0;
voxentsize=4; // cur position, start position, end position, volume
sampleinfotab=voxentsize*max_voices; // list of samples with start/end pos
lslider2=-1000;
@slider
voxmaxsize=min(max_voices,slider4);
midichan=slider7;
midibase=slider6;
zerogain=2^(slider5/6);
vol=2^(slider1/6);
slider3=max(0.1,slider3);
actsize=0|min(min(actsize,slider4),4096);
tmp=slider2|0;
lslider2 != tmp ?
(
lslider2=tmp;
pcm_buffer=sampleinfotab+128*2;
num_samples=0;
filehandle=file_open(slider2);
filehandle > 0 ?
(
file_riff(filehandle,spl_nch,spl_srate);
// spl_srate/=srate;
spl_nch==2 ?
(
numsilent=0;
pairs=min(file_avail(filehandle),(8*1024*1024 - 65536*2))*0.5;
inspl=0;
pos=0;
sampleinfotab[num_samples*2]=pcm_buffer;
loop(pairs,
file_var(filehandle,l);
file_var(filehandle,r);
quiet=max(abs(l),abs(r)) < 0.0001;
inspl || !quiet ?
(
pcm_buffer[0]=l;
pcm_buffer[1]=r;
inspl=1;
pos+=2;
pcm_buffer+=2;
);
inspl && quiet ?
(
(numsilent += 1) >= 2048 ? (
// new sample time
pcm_buffer-=numsilent*2;
sampleinfotab[num_samples*2+1]=(pos-numsilent*2);
num_samples+=1;
sampleinfotab[num_samples*2]=pcm_buffer;
inspl=0;
pos=0;
);
) : numsilent=0;
);
sampleinfotab[num_samples*2+1]=pos-numsilent*2;
num_samples+=1;
);
file_close(filehandle);
);
actsize=0;
);
slider8=num_samples;
sliderchange(slider8);
@block
gpitchsc=2*slider3*spl_srate/srate;
spl_nch == 2 ? (
while(
midirecv(pos,msg1,msg23) ? (
midisend(pos,msg1,msg23); // pass through MIDI
ws=((msg23&127)-midibase)|0;
((midichan > 0 && msg1 == 8*16 + midichan-1) || // if MIDI noteon
(midichan==0 && msg1>=8*16 && msg1 < 10*16)) &&
ws >= 0 && ws < num_samples ? (
voxsize>=voxmaxsize ? (
voxsize=voxmaxsize-1;
memcpy(voxlist,voxlist+voxentsize,voxentsize*voxsize);
);
p=voxlist+voxentsize*voxsize;
p[1] = sampleinfotab[ws*2];
p[0] = p[1] -pos / gpitchsc;
p[2] = p[1]+sampleinfotab[ws*2+1];
vel = msg1 == 9*16 ? ((msg23 / 256.0)|0)/127.0;
p[3] = 1.0*vel + (zerogain*(1.0-vel));
voxsize+=1;
); //was noteon
1;
) // midi recv
: 0
); // while
);
@sample
spl_nch == 2 ? (
s0=s1=0;
a=voxlist;
loop(voxsize,
cpos = a[0];
cpos >= a[1] ? (
// samples, yeah!
gain=a[3];
addr=cpos|0;
addr-=addr&1;
slider9 < 0.5 ? (
// truncation
s0+=addr[0]*gain;
s1+=addr[1]*gain;
) : (
// linear interpolation
x=0.5*cpos;
x-=x|0;
s0+=(1-x)*addr[0]*gain;
s1+=(1-x)*addr[1]*gain;
(addr+=2) < a[2] ? (
s0+=x*addr[0]*gain;
s1+=x*addr[1]*gain;
);
);
);
cpos += gpitchsc;
cpos >= a[2]-1 ? (
voxsize-=1;
memcpy(a,a+voxentsize,voxsize*voxentsize - a);
) : (
a[0]=cpos;
a+=voxentsize;
);
);
spl0+=s0*vol;
spl1+=s1*vol;
); // spl_nch == 2
Last edited by Tale; 01-31-2017 at 03:28 PM.
Reason: Restored Note On vel 0, added Note Off, fixed interpolation
|
|
|
01-31-2017, 10:17 AM
|
#8
|
Human being with feelings
Join Date: Mar 2007
Posts: 4,018
|
Hi Tale,
wow, You're the man! :-)
Thank a million for sample rate fixing.
I just tried it quickly (still at work now), with loaded original and new version and seems to be OK.
But about Note Off. That your last version with "Note Off" fix and Interpolation addition plays as it was with Echo .. so I investigated little and yes - press note on VKB and sample plays, then release the key on VKB and it plays again (softer). So when running a sequence from e.g. Megababy then it does sort of MIDI echo :-/
I do not exactly understand what was wrong with original implementation of MIDI note off. Can you elaborate, in what it could possibly cause problems or incompatibility, please?
In all cases, once again: "Thank You So Much!"
akademie
|
|
|
01-31-2017, 10:20 AM
|
#9
|
Human being with feelings
Join Date: Mar 2007
Posts: 4,018
|
Ohh and BTW, while we are at it. Would it be difficult to implement multioutput, so every "sample slice" whould go into separate individual output? (lets say fixed number of outputs .. 8 or 12 - maybe "cycled" so if number of sample slices is 16, then 1st and 9th go to out1,.. in 8 output version).
akademie
|
|
|
01-31-2017, 12:25 PM
|
#10
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by akademie
Thank a million for sample rate fixing.
I just tried it quickly (still at work now), with loaded original and new version and seems to be OK.
|
Cool, and you're welcome.
Quote:
Originally Posted by akademie
I do not exactly understand what was wrong with original implementation of MIDI note off. Can you elaborate, in what it could possibly cause problems or incompatibility, please?
|
The version in your original post reacts only to MIDI message 0x90, which is Note On when the velocity is 1..127, and Note Off when velocity is zero. However, there is also a dedicated MIDI message (0x80) for Note Off, which wasn't implemented at all, so I added it as an alias for Note On with 0 velocity.
But you could also remove it again, and the same goes for interpolation.
Last edited by Tale; 02-01-2017 at 12:11 AM.
|
|
|
01-31-2017, 02:03 PM
|
#11
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by akademie
Ohh and BTW, while we are at it. Would it be difficult to implement multioutput, so every "sample slice" whould go into separate individual output? (lets say fixed number of outputs .. 8 or 12 - maybe "cycled" so if number of sample slices is 16, then 1st and 9th go to out1,.. in 8 output version).
|
Something like this you mean?
Code:
// This effect Copyright (C) 2004 and later Cockos Incorporated
// License: GPL - http://www.gnu.org/licenses/gpl.html
desc:MIDI drum sequencer
slider1:-6<-120,6,1>volume (dB)
slider2:/drum_patchsets:patchset_pacific1_adpcm.wav:Patch set
slider3:1<0.1,4,0.05>pitch scaling
slider4:16<1,64,1>max voices
slider5:-24<-120,0,1>gain at 0 velocity (dB)
slider6:60<0,127,1>base note
slider7:0<0,16,1>channel (0=omni)
slider8:0,meter
slider9:1<0,1,1{off,on}>interpolation
slider10:2<2,64,2>outputs channels
@init
max_voices=128; // allocate enough ram for 128 samples
ext_noinit=1;
voxlist=0;
voxsize=0;
voxentsize=5; // cur position, start position, end position, volume, sample index
sampleinfotab=voxentsize*max_voices; // list of samples with start/end pos
lslider2=-1000;
@slider
voxmaxsize=min(max_voices,slider4);
midichan=slider7;
midibase=slider6;
zerogain=2^(slider5/6);
vol=2^(slider1/6);
slider3=max(0.1,slider3);
actsize=0|min(min(actsize,slider4),4096);
outpairs=max(1,min(32,(slider10/2)|0));
tmp=slider2|0;
lslider2 != tmp ?
(
lslider2=tmp;
pcm_buffer=sampleinfotab+128*2;
num_samples=0;
filehandle=file_open(slider2);
filehandle > 0 ?
(
file_riff(filehandle,spl_nch,spl_srate);
// spl_srate/=srate;
spl_nch==2 ?
(
numsilent=0;
pairs=min(file_avail(filehandle),(8*1024*1024 - 65536*2))*0.5;
inspl=0;
pos=0;
sampleinfotab[num_samples*2]=pcm_buffer;
loop(pairs,
file_var(filehandle,l);
file_var(filehandle,r);
quiet=max(abs(l),abs(r)) < 0.0001;
inspl || !quiet ?
(
pcm_buffer[0]=l;
pcm_buffer[1]=r;
inspl=1;
pos+=2;
pcm_buffer+=2;
);
inspl && quiet ?
(
(numsilent += 1) >= 2048 ? (
// new sample time
pcm_buffer-=numsilent*2;
sampleinfotab[num_samples*2+1]=(pos-numsilent*2);
num_samples+=1;
sampleinfotab[num_samples*2]=pcm_buffer;
inspl=0;
pos=0;
);
) : numsilent=0;
);
sampleinfotab[num_samples*2+1]=pos-numsilent*2;
num_samples+=1;
);
file_close(filehandle);
);
actsize=0;
);
slider8=num_samples;
sliderchange(slider8);
@block
gpitchsc=2*slider3*spl_srate/srate;
spl_nch == 2 ? (
while(
midirecv(pos,msg1,msg23) ? (
midisend(pos,msg1,msg23); // pass through MIDI
ws=((msg23&127)-midibase)|0;
((midichan > 0 && msg1 == 8*16 + midichan-1) || // if MIDI noteon
(midichan==0 && msg1>=8*16 && msg1 < 10*16)) &&
ws >= 0 && ws < num_samples ? (
voxsize>=voxmaxsize ? (
voxsize=voxmaxsize-1;
memcpy(voxlist,voxlist+voxentsize,voxentsize*voxsize);
);
p=voxlist+voxentsize*voxsize;
p[1] = sampleinfotab[ws*2];
p[0] = p[1] -pos / gpitchsc;
p[2] = p[1]+sampleinfotab[ws*2+1];
vel = msg1 == 9*16 ? ((msg23 / 256.0)|0)/127.0;
p[3] = 1.0*vel + (zerogain*(1.0-vel));
p[4] = ws;
voxsize+=1;
); //was noteon
1;
) // midi recv
: 0
); // while
);
@sample
spl_nch == 2 ? (
a=voxlist;
loop(voxsize,
cpos = a[0];
cpos >= a[1] ? (
// samples, yeah!
gain=a[3]*vol;
addr=cpos|0;
addr-=addr&1;
s0=addr[0];
s1=addr[1];
slider9 >= 0.5 ? (
// linear interpolation
x=0.5*cpos;
x-=x|0;
s0*=(1-x);
s1*=(1-x);
(addr+=2) < a[2] ? (
s0+=x*addr[0];
s1+=x*addr[1];
);
);
i=2*(a[4]%outpairs);
spl(i)+=s0*gain;
spl(i+1)+=s1*gain;
);
cpos += gpitchsc;
cpos >= a[2]-1 ? (
voxsize-=1;
memcpy(a,a+voxentsize,voxsize*voxentsize - a);
) : (
a[0]=cpos;
a+=voxentsize;
);
(i+=2) >= max_out ? i=0;
);
); // spl_nch == 2
Last edited by Tale; 01-31-2017 at 03:27 PM.
Reason: Optimized & fixed interpolation
|
|
|
01-31-2017, 03:40 PM
|
#12
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
FYI: The linear interpolation I posted earlier wasn't working correctly (oops!), so I have just edited and fixed it.
EDIT: For those interested I have uploaded my test sample set:
http://www.taletn.com/reaper/midi_drumseq.wv
Last edited by Tale; 01-31-2017 at 03:46 PM.
|
|
|
02-01-2017, 05:12 AM
|
#13
|
Human being with feelings
Join Date: Mar 2007
Posts: 4,018
|
Well, Mr. Tale, this is GREAT!
I tried new multioutput version right now and it is really much more fun than using multiple instances of the effect and limiting/separating their MIDI channels only to have individual outputs.
Also the memory is not wasted this way since WAV files are not loaded multiple times.
I appreciate your kindness and Thank you very much for your great and fast work
Hope you have a good time
akademie
|
|
|
02-01-2017, 02:59 PM
|
#14
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by akademie
Hope you have a good time
|
Yeah, that was fun.
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 04:34 AM.
|