Old 01-30-2017, 09:29 AM   #1
akademie
Human being with feelings
 
Join Date: Mar 2007
Posts: 4,018
Default "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
akademie is offline   Reply With Quote
Old 01-30-2017, 04:18 PM   #2
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

I'm not really sure it will work, but you could try this:

Code:
@block
gpitchsc=2*slider3*srate;
Tale is offline   Reply With Quote
Old 01-30-2017, 09:14 PM   #3
akademie
Human being with feelings
 
Join Date: Mar 2007
Posts: 4,018
Default

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
akademie is offline   Reply With Quote
Old 01-31-2017, 12:25 AM   #4
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by akademie View Post
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?
Tale is offline   Reply With Quote
Old 01-31-2017, 02:41 AM   #5
akademie
Human being with feelings
 
Join Date: Mar 2007
Posts: 4,018
Default

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
akademie is offline   Reply With Quote
Old 01-31-2017, 04:53 AM   #6
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by akademie View Post
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!
Tale is offline   Reply With Quote
Old 01-31-2017, 05:40 AM   #7
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

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
Tale is offline   Reply With Quote
Old 01-31-2017, 10:17 AM   #8
akademie
Human being with feelings
 
Join Date: Mar 2007
Posts: 4,018
Default

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
akademie is offline   Reply With Quote
Old 01-31-2017, 10:20 AM   #9
akademie
Human being with feelings
 
Join Date: Mar 2007
Posts: 4,018
Default

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
akademie is offline   Reply With Quote
Old 01-31-2017, 12:25 PM   #10
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by akademie View Post
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 View Post
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.
Tale is offline   Reply With Quote
Old 01-31-2017, 02:03 PM   #11
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by akademie View Post
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
Tale is offline   Reply With Quote
Old 01-31-2017, 03:40 PM   #12
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

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.
Tale is offline   Reply With Quote
Old 02-01-2017, 05:12 AM   #13
akademie
Human being with feelings
 
Join Date: Mar 2007
Posts: 4,018
Default

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
akademie is offline   Reply With Quote
Old 02-01-2017, 02:59 PM   #14
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by akademie View Post
Hope you have a good time
Yeah, that was fun.
Tale is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 07:06 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.