/*******************************************************************************
*  Copyright 2007 - 2011, Philip S. Considine                                  *
*  This program is free software: you can redistribute it and/or modify        *
*  it under the terms of the GNU General Public License as published by        *
*  the Free Software Foundation, either version 3 of the License, or           *
*  (at your option) any later version.                                         *
*                                                                              *
*  This program is distributed in the hope that it will be useful,             *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                *
*  GNU General Public License (http://www.gnu.org/licenses/)for more details.  *
*******************************************************************************/

desc: MIDI Tool
desc: MIDI Tool [IXix]
//tags: MIDI processing
//author: IXix

slider1:0<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Input Channel
slider2:0<0,127,1>Note Min
slider3:127<0,127,1>Note Max

// Note min/max (names) (disabled)
//slider2:0<0,127,1{C0,C#0,D0,D#0,E0,F0,F#0,G0,G#0,A0,A#0,B0,C1,C#1,D1,D#1,E1,F1,F#1,G1,G#1,A1,A#1,B1,C2,C#2,D2,D#2,E2,F2,F#2,G2,G#2,A2,A#2,B2,C3,C#3,D3,D#3,E3,F3,F#3,G3,G#3,A3,A#3,B3,C4,C#4,D4,D#4,E4,F4,F#4,G4,G#4,A4,A#4,B4,C5,C#5,D5,D#5,E5,F5,F#5,G5,G#5,A5,A#5,B5,C6,C#6,D6,D#6,E6,F6,F#6,G6,G#6,A6,A#6,B6,C7,C#7,D7,D#7,E7,F7,F#7,G7,G#7,A7,A#7,B7,C8,C#8,D8,D#8,E8,F8,F#8,G8,G#8,A8,A#8,B8,C9,C#9,D9,D#9,E9,F9,F#9,G9,G#9,A9,A#9,B9,C10,C#10,D10,D#10,E10,F10,F#10,G10}>Note Min
//slider3:127<0,127,1{C0,C#0,D0,D#0,E0,F0,F#0,G0,G#0,A0,A#0,B0,C1,C#1,D1,D#1,E1,F1,F#1,G1,G#1,A1,A#1,B1,C2,C#2,D2,D#2,E2,F2,F#2,G2,G#2,A2,A#2,B2,C3,C#3,D3,D#3,E3,F3,F#3,G3,G#3,A3,A#3,B3,C4,C#4,D4,D#4,E4,F4,F#4,G4,G#4,A4,A#4,B4,C5,C#5,D5,D#5,E5,F5,F#5,G5,G#5,A5,A#5,B5,C6,C#6,D6,D#6,E6,F6,F#6,G6,G#6,A6,A#6,B6,C7,C#7,D7,D#7,E7,F7,F#7,G7,G#7,A7,A#7,B7,C8,C#8,D8,D#8,E8,F8,F#8,G8,G#8,A8,A#8,B8,C9,C#9,D9,D#9,E9,F9,F#9,G9,G#9,A9,A#9,B9,C10,C#10,D10,D#10,E10,F10,F#10,G10}>Note Max

slider4:0<0,127,1>Input Velocity Min
slider5:127<0,127,1>Input Velocity Max
slider6:0<0,100,1>Random Velocity (%)
slider7:0<0,127,1>Output Velocity Min
slider8:127<0,127,1>Output Velocity Max
slider9:0<-60,60,1>Transpose (st)
slider10:0<0,100,1>Random Pitch (%)
slider11:0<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Output Channel

in_pin:none
out_pin:none

@init
statNoteOff  = $x80;
statNoteOn  = $x90;
statPitch  = $xE0;
pitchCentre = 16384;

@slider

// Discard fractions
slider2 |= 0;
slider3 |= 0;
slider4 |= 0;
slider5 |= 0;
slider6 |= 0;
slider7 |= 0;
slider8 |= 0;
slider9 |= 0;
slider10 |= 0;

// Clamp to respectable values
slider2 = min(slider2, slider3);
slider3 = max(slider2, slider3);
slider4 = min(slider4, slider5);
slider5 = max(slider4, slider5);
slider6 = min(max(slider6, 0), 100);
slider7 = min(slider7, slider8);
slider8 = max(slider7, slider8);
slider9 = min(max(slider9, -60), 60);
slider10 = min(max(slider10, 0), 100);

// Set variables
inChannel = slider1;
outChannel = slider11;
noteMin = slider2;
noteMax = slider3;
inVelMin = slider4;
inVelMax = slider5;
velocityRand = slider6 / 100;
outVelMin = slider7;
outVelMax = slider8;
transpose = slider9;
pitchRand = slider10 / 100;

////////////////////////////////////////////////////////////////////////////////////////////
@block
while
(
  midirecv(offset, msg1, msg23) ?
  (
    doPitch = 0; // Reset pitch bend flag

    // Extract message type and channel
    status = msg1 & $xF0;
    channel = msg1 & $x0F;

    // Is it on our channel?
    channel == inChannel ?
    (
      // Is it a note event?
      status == statNoteOn || status == statNoteOff ?
      (
        // Extract note number
        note = msg23 & $x7F;
        
        // Is it within our note range?
        !(note < noteMin || note > noteMax) ?
        (
          t += 1;
          osc = sin(t);  // used to make random values positive or negative 
          
          // Extract velocity
          velocity = msg23 >> 8;
                
          // Do velocity. Zero velocity is considered a note-off so don't clamp it
          velocity > 0 ?
          (        
            // Clamp input velocity
            velocity = min(max(velocity, inVelMin), inVelMax);
          
            // Randomise clamped velocity by percentage
            velocityRand > 0 ? velocity = floor(velocity + (rand(velocity * velocityRand) * osc));
          
            // Clamp output velocity
            velocity = min(max(velocity, outVelMin), outVelMax);
          );
                  
          // Do transpose and clamp result if necessary
          note += transpose;
          note = min(max(note, 0), 127);
          
          // Adjust message values
          msg1 = status + outChannel;
          msg23 = note | (velocity << 8);
          
          // Flag to send pitchbend if needed
          pitchRand > 0 ? doPitch = 1;
        );
      );
    );
    
    // Generate and send pitch bend message if necessary
    doPitch ?
    (
      pitch = pitchCentre + floor(rand(pitchCentre * pitchRand) * osc);
      midisend(offset, statPitch + outChannel, pitch);
    );
    
    // Pass message on
    midisend(offset, msg1, msg23);
    
    1; // Force loop to continue until all messages have been processed
  );
);
