// $Id: midi_velocity_viewer 489 2011-12-17 13:32:17Z erwin $

// License: "AS IS" without any warranty.

//////////////////////////////////////////////////////////////////////
desc: MIDI velocity viewer.

//////////////////////////////////////////////////////////////////////
//                              Readme
//
// Copy this file to: REAPER\Effects\MIDI\midi_velocity_viewer
// Click on the FX button on a (MIDI) track
// Browse to this file: JS: MIDI\midi_velocity_viewer
// Select the MIDI input channel 1-16 or Any
// Select the MIDI input note 0-127 or Any
// Select the number of velocities with the third slider 1-32
// Left mouse click to hide/show the minimum/maximum velocities
// Right mouse click to reset

//////////////////////////////////////////////////////////////////////
slider1:0<0,15,1{Any,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Input channel:
slider2:0<0<0,128,1{Any,0: C0,1: C#0,2: D0,3: Eb0,4: E0,5: F0,6: F#0,7: G0,8: G#0,9: A0,10: Bb0,11: B0,12: C1,13: C#1,14: D1,15: Eb1,16: E1,17: F1,18: F#1,19: G1,20: G#1,21: A1,22: Bb1,23: B1,24: C2,25: C#2,26: D2,27: Eb2,28: E2,29: F2,30: F#2,31: G2,32: G#2,33: A2,34: Bb2,35: B2,36: C3,37: C#3,38: D3,39: Eb3,40: E3,41: F3,42: F#3,43: G3,44: G#3,45: A3,46: Bb3,47: B3,48: C4,49: C#4,50: D4,51: Eb4,52: E4,53: F4,54: F#4,55: G4,56: G#4,57: A4,58: Bb4,59: B4,60: C5,61: C#5,62: D5,63: Eb5,64: E5,65: F5,66: F#5,67: G5,68: G#5,69: A5,70: Bb5,71: B5,72: C6,73: C#6,74: D6,75: Eb6,76: E6,77: F6,78: F#6,79: G6,80: G#6,81: A6,82: Bb6,83: B6,84: C7,85: C#7,86: D7,87: Eb7,88: E7,89: F7,90: F#7,91: G7,92: G#7,93: A7,94: Bb7,95: B7,96: C8,97: C#8,98: D8,99: Eb8,100: E8,101: F8,102: F#8,103: G8,104: G#8,105: A8,106: Bb8,107: B8,108: C9,109: C#9,110: D9,111: Eb9,112: E9,113: F9,114: F#9,115: G9,116: G#9,117: A9,118: Bb9,119: B9,120: C10,121: C#10,122: D10,123: Eb10,124: E10,125: F10,126: F#10,127: G10}>Input note:
slider3:16<1,32,1>Velocities:

//////////////////////////////////////////////////////////////////////
@init

// MIDI status defines
statNoteOff = $x80;
statNoteOn  = $x90;

// Left mouse button state (LMB)
lmbState = 0;

// Show/hide drawing min/max lines
drawMinMax = 1;

// Index in velocity and notes buffers
index = 0;
// Number of velocities and notes to draw
numVelocities = 16;
// Velocity and note buffers
// Thanks to everybody who helped me with this stuff on the forum:
// http://forum.cockos.com/showthread.php?t=93135
velocities = 0;
notes = velocities + 32;

// Initialize velocities and notes buffers
i = 0;
loop(numVelocities,
	index = 0;
	velocities[i] = 0;
	notes[i] = 128;
	i += 1;
);

//////////////////////////////////////////////////////////////////////
@slider

// Slider3 changed?
(numVelocities != slider3) ?
(
	// Update number of velocities
	numVelocities = slider3;
	
	// Clear velocities and notes buffers
	i = 0;
	loop(numVelocities,
		index = 0;
		velocities[i] = 0;
		notes[i] = 128;
		i += 1;
	);
);

//////////////////////////////////////////////////////////////////////
@block
while
(
	// Receive MIDI data
	midirecv(offset, msg1, msg23) ?
	(
		// Check status byte
		status = (msg1 & $xF0);  // High four bits is message type (240 == 11110000)
		channel = (msg1 & $x0F); // Low four bits is channel number (15 == 00001111)
		
		// Is it on our channel ?
		(channel == (slider1-1)) || (slider1 == 0) ?
		(
			// Is it note on?
			(status == statNoteOn) ?
			(
				// Get note number
				noteIn = (msg23 & $x7F);
				
				// Store velocity?
				(noteIn == (slider2-1)) || (slider2 == 0) ?
				(
					// Get note velocity
					velocityIn = ((msg23/$x100) & $x7F);
		
					// Store velocity and note in buffers
					velocities[index] = velocityIn;
					notes[index] = noteIn;
					
					// Wrap index
					index += 1;
					(index >= numVelocities) ?
					(
						index = 0;
					);
				);
			);
		);
		
		// Send MIDI data
		midisend(offset, msg1, msg23);
	);
);

//////////////////////////////////////////////////////////////////////
@gfx
// Capture mouse
(mouse_cap == 1) && (lmbState == 0) ?
(
	// Left mouse button down: Toggle min/max draw enable
	(drawMinMax) ? drawMinMax = 0 : drawMinMax = 1;
	
	// Execute left mouse button only once
	lmbState = 1;
):
(mouse_cap == 0) ?
(
	// Left mouse button up
	lmbState = 0;
);

(mouse_cap == 2) ?
(
	// Right mouse button down: Clear velocities
	i = 0;
	loop(numVelocities,
		index = 0;
		velocities[i] = 0;
		notes[i] = 128;
		i += 1;
	);
);

// Set draw mode
gfx_a = 1;
gfx_mode = 0;
	
//Set green text colour
gfx_r=0; gfx_g=1; gfx_b=0;

// Draw vertical line
gfx_x = 40; gfx_y = 10;
gfx_lineto(40, gfx_h-40, 0);

// Draw horizontal line
gfx_x = 40; gfx_y = gfx_h-40;
gfx_lineto(gfx_w-40, gfx_y, 0);

// Draw velocity 127 text
gfx_x = 10; gfx_y = 20;
gfx_drawnumber(127, 0);

// Draw velocity 64 text
gfx_x = 15; gfx_y = (gfx_h-25)/2;
gfx_drawnumber(64, 0);

// Draw velocity 0 text
gfx_x = 25; gfx_y = gfx_h-45;
gfx_drawnumber(0, 0);

// Draw Vel: text
gfx_x = 13; gfx_y = gfx_h-30;
gfx_drawchar($'V');
gfx_drawchar($'e');
gfx_drawchar($'l');
gfx_drawchar($':');

// Draw Note: text
gfx_x = 5; gfx_y = gfx_h-16;
gfx_drawchar($'N');
gfx_drawchar($'o');
gfx_drawchar($'t');
gfx_drawchar($'e');
gfx_drawchar($':');

// Draw dotted lines
gfx_x = 40;
while(	
	// Draw dotted velocity 64
	gfx_y = (gfx_h-20)/2;
	gfx_setpixel(0,1,0);
	
	// Draw dotted velocity 127 
	gfx_y = 25;
	gfx_setpixel(0,1,0);
	
	// Calculate end of dotted line to break the while loop
	gfx_x += 10;
	gfx_x < (gfx_w-40);
);

// Draw velocities and notes
i = 0;
loop(numVelocities,
	// Draw velocity value
	gfx_x = 60 + ((gfx_w-80)/(numVelocities)) * i; 
	// Align text
	(velocities[i] < 10) ?
	(
		gfx_x += 1; 
	):
	(velocities[i] < 100) ?
	(
		gfx_x -= 2; 
	):
	(
		gfx_x -= 5; 
	);
	gfx_y = gfx_h-30;
	gfx_drawnumber(velocities[i], 0);
	
	// Draw note number
	gfx_x = 60 + ((gfx_w-80)/(numVelocities)) * i; 
	gfx_y = gfx_h-15;
	// Align text
	(notes[i] == 128) ?
	(
		gfx_x += 1; 
		gfx_drawchar($'-');
	):
	(
		(notes[i] < 10) ?
		(
			gfx_x += 1; 
		):
		(notes[i] < 100) ?
		(
			gfx_x -= 2; 
		):
		(
			gfx_x -= 5; 
		);
		gfx_drawnumber(notes[i], 0);
	);
	
	// Draw velocity rectangle
	gfx_x = 60 + ((gfx_w-80)/(numVelocities)) * i; 
	gfx_y = (gfx_h-40) - (((gfx_h-50) - 15) / 127) * velocities[i];
	gfx_rectto(gfx_x+10, gfx_h-40);
	
	i += 1;
);

// Calculate minimum and maximum velocity
i = 0;
minVel = 128;
maxVel = 0;
loop(numVelocities,
	(velocities[i] != 0) ?
	(
		minVel = min(velocities[i], minVel);
	);
	
	maxVel = max(velocities[i], maxVel);
	
	i += 1;
);

(drawMinMax) ?
(
	// Draw maximum velocity
	(maxVel != 0) ?
	(
		// Draw maximum velocity line
		gfx_r=1; gfx_g=0; gfx_b=0;
		y = (gfx_h-40) - (((gfx_h-50) - 15) / 127) * maxVel;
		gfx_x = 40;
		gfx_y = y;
		gfx_lineto((gfx_w-40), y, 0);
		
		// Draw maximum velocity value
		gfx_x += 10;
		gfx_y -= 4;
		gfx_drawnumber(maxVel, 0);
	);

	// Draw minimum velocity
	(minVel <= 127) ?
	(
		// Draw minimum velocity line
		gfx_r=1; gfx_g=1; gfx_b=0;
		y = (gfx_h-40) - (((gfx_h-50) - 15) / 127) * minVel;
		gfx_x = 40;
		gfx_y = y;
		gfx_lineto((gfx_w-40), y, 0);

		// Draw minimum velocity value
		gfx_x += 10;
		gfx_y -= 4;
		gfx_drawnumber(minVel, 0);
	);
);
