Old 03-15-2015, 12:03 PM   #1
plamuk
Human being with feelings
 
Join Date: Feb 2007
Posts: 3,221
Default "midi legato by range" bug help?

i've always used this plugin useful plugin by user "dumant" as part of larger bidule based midi programs or on monophonic synth lines, so i never realized that it has a bug with notes never receiving their noteoff.

the plugin delays noteoffs as specified, by ms or tick.
i found that if you trigger multiple notes near simultaneously while applying a delayed noteoff, the last couple notes never receive their noteoffs.

does anyone have a spare moment to show me what is wrong here, if anything? my hunch is that it has to do with "leastfreeindex"

Code:
/*
  Midi Legato for a Note Range
  ----------------------------
  
  This effect lets you increase the length of all notes in a given range.
  
  Usage
  -----
  - Input Channel Filter: select the channel of the notes you wish to modify.
  - Note min: lowest note that will be altered by the effect.
  - Note max: highest note that will be altered by the effect.        
  - Delay: value of the delay for the currently selected range.
  - Delay unit: the unit in which the delay is expressed (ms or ticks: there 
                are 960 ticks per beat.)
  - Deviation: some randomness may be added (or subtracted) from the delay. 
               The effective delay will be comprised between delay*(1 - deviation) 
               and delay*(1+ deviation).
               
  Presets may be saved using the standard preset mechanism.

  Version History
  ---------------
  1.1.2a: defined deviation as a maximum deviation, removed average correction
  1.1.1a: bug corrections, normal randomization
  1.1.0a: delay by tick, preset management
  1.0.0a: Delay note off in ms   
  
  Thanks to IXix for delivering very inspiring js midi effects.

  ----------------------------
  Copyright 2008, Bruno Dumant
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted 
  provided that the following conditions are met:

  Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer. 

  Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
  and the following disclaimer in the documentation and/or other materials provided with the distribution. 

  The name of Bruno Dumant may not be used to endorse or promote products derived from this
  software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
////////////////////////////////////////////////////////////////////////////////////////////
desc:Midi Legato for a Note Range - Press edit for documentation.

// Filters
slider1:0<0,16,1{All,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Input channel filter
slider2:0<0,127,1>Note min
slider3:127<0,127,1>Note max

// Delay
slider4:0<0,1920,120>Delay (in ms or ticks)
slider5:0<0,1,1{ms,tick}>Delay unit (double it!)
slider6:0<0,100,1>Deviation in %

//slider7:0<0,1000,1>LFI
//slider8:0<0,1000,1>MFI

//------------------
@init
  ext_noinit = 1;

  version = 1; // storage version

  // store events that will be released
  events = 0;
  leastFreeIndex = 0;
  maxFreeIndex = 0;

  // delay, msg1, msg23
  eventSize = 3;

  statNoteOn = $x90;
  statNoteOff = $x80;
  statController = $xB0;

  noteMax = 127;
  
  // generate a pseudo normal data set
  normalData = 128;
  i = 0;
  loop (
    128,
    while ( a = rand(1); a != 0 ? (v = sqrt(-2*log(a))*cos(6.28318530717959*rand(1))); a == 0 || abs(v) > 3);
    normalData[i] = v;
    i += 1;
  );
  i = 0;
  // make sure values are contained between -1 and 1
  loop ( 128, normalData[i] /= 3; i += 1; );

//------------------
@slider

  inChannel != slider1 - 1 ? (
    slider1 = min(16,max(0,floor(slider1)));
    inChannel = slider1 - 1;
  );

  noteMin != slider2 ? (
    slider2 = noteMin = min(noteMax,max(0,floor(slider2)));
  );

  noteMax != slider3 ? (
    slider3 = noteMax = min(127,max(noteMin,floor(slider3)));
  );

  delay != slider4 ? (
    delay = slider4;
  );

  delayUnit != slider5 ? (
    slider5 = delayUnit = min(1,max(0,floor(slider5)));
  );

  // variance is global
  variance != slider6 / 100 ? (
    variance = slider6 / 100;
  );

//------------------
@serialize

  file_var(0,version);
  file_var(0,inChannel);
  file_var(0,noteMin);
  file_var(0,noteMax);
  file_var(0,delay);
  file_var(0,delayUnit);
  file_var(0,variance);

//------------------
@block

  delay != 0 || maxFreeIndex > 0 ? (

    // send delayed events that expire within this block
    i = 0;
    while (
      evtDelay = events[i];
      evtDelay != 0 ? (
        evtDelay < samplesblock ? (
          // send the event
          midisend(max(0, evtDelay), events[i + 1], events[i + 2]);
          leastFreeIndex == maxFreeIndex ? (
            // update maxFreeIndex
            while (
              maxFreeIndex -= eventSize;
              events[maxFreeIndex] == 0 && maxFreeIndex >= 0;
            );
          );
          leastFreeIndex = min(i, leastFreeIndex);
          events[i] = 0; // disable the event
          //slider7 = leastFreeIndex; sliderchange(slider7);
          //slider8 = maxFreeIndex; sliderchange(slider8);
        ) : (
          events[i] -= samplesblock;
        )
      );
      i += eventSize;
      i < maxFreeIndex;
    );
    
    while (  
      midirecv(offset, msg1, msg23) ? (

        channel = msg1 & $xF;  // Low four bits is channel number
            
        //Is it on our channel
        inChannel == -1 || channel == inChannel ?  (

          //Check status byte
          status = msg1 & $xF0;  // High four bits is message type (240 == 11110000)
          //Is it a note event
          status == statNoteOn || status == statNoteOff ?
          (
            note = msg23 & $x7F;

            // is the note managed ?
            note >= noteMin && note <= noteMax ? (
              status == statNoteOff || ((msg23/$x100) & $x7F) == 0 ? (
              
                realDelay = 0;
                delay != 0 ? (
                  // compute the delay in samples
                  delayUnit == 0 ? (
                    realDelay = delay / 1000 * srate;
                  ) : (
                    realDelay = delay / 16 / tempo * srate; // 1/16 = 60/960
                  );
                  variance != 0 ? realDelay *= (1 + normalData[floor(rand(128.99))] * variance); 
                );
                // offset of event % beginning of block
                date = offset + realDelay;
                // this is a noteOff or 0 velocity message
                date < samplesblock ? (
                  // the event can be immediately sent
                  midisend(max(0, date), msg1, msg23);
                ) : (
                  // otherwise store in the events (offset % beginning of next block)
                  events[leastFreeIndex] = date - samplesblock;
                  events[leastFreeIndex + 1] = msg1;
                  events[leastFreeIndex + 2] = msg23;
                  
                  // update leastFreeIndex
                  leastFreeIndex == maxFreeIndex ? (
                    leastFreeIndex = maxFreeIndex += eventSize;
                  ) : (
                    while(
                      leastFreeIndex += eventSize;
                      events[leastFreeIndex] != 0 || leastFreeIndex < maxFreeIndex;
                    );
                    leastFreeIndex == maxFreeIndex ? (
                      leastFreeIndex = maxFreeIndex += eventSize;
                    )
                  );
                  //slider7 = leastFreeIndex; sliderchange(slider7);
                  //slider8 = maxFreeIndex; sliderchange(slider8);
                );
              ) : (
                // not a noteOff
                midisend(offset,msg1,msg23); 
              );
            ) : (
              // not a managed note
              midisend(offset,msg1,msg23);
            )
          ) : (
            // Not a note
            status == statController ? (
              ccId = msg23 & $x7F;
              ccId == 123 || ccId == 120 ? (
                // all sounds off or all notes off
                leastFreeIndex = 0;
                maxFreeIndex = 0;
             
                //slider7 = leastFreeIndex; sliderchange(slider7);
                //slider8 = maxFreeIndex; sliderchange(slider8);
              );
            );
            // pass thru
            midisend(offset,msg1,msg23);
          );
        ) : (
          // pass thru
          midisend(offset,msg1,msg23);
        );
      )
    );
  );
plamuk is offline   Reply With Quote
Old 04-09-2016, 02:32 PM   #2
DruMunkey
Human being with feelings
 
Join Date: Feb 2016
Posts: 232
Default

HUGE BUMP!!! I'm running into the same issue and I really need this to work... Although I suppose an infinite note is technically "legato"
DruMunkey 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 01:45 AM.


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