Go Back   Cockos Incorporated Forums > Other Software Discussion > OSCII-bot forum

Reply
 
Thread Tools Display Modes
Old 10-24-2020, 04:38 AM   #1
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default Script to change incoming MIDI velocities and send it to Reaper?

I'm trying to use OSCII-bot to take a MIDI keyboard controller's output and increase the velocity level before sending the key information to Reaper. I don't like the velocity curve on my MIDI controller, so I want to make my own curve in software (before it hits Reaper).

I've been able to get OSCII-bot to work with the included "show-io.txt" script so that I can see the output values of my MIDI controller. However, I'm not sure where to go after this.

Can someone provide an example script that would increase the key velocity levels by 25 and then feed that result into Reaper? (Obviously, if the new velocity level adds up to be over 127, the value of 127 should be used.)

Thank you!
__________________
Jeff Boller
Sundrift Productions
simplecarnival is offline   Reply With Quote
Old 10-24-2020, 01:07 PM   #2
TonE
Human being with feelings
 
Join Date: Feb 2009
Location: Reaper HAS send control via midi !!!
Posts: 2,921
Default

Did you try Midi CC Mapper X? It can do this and million other things.
TonE is offline   Reply With Quote
Old 10-25-2020, 07:54 AM   #3
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default

TonE -- I looked that up, and it appears it's just a VST plug-in. I don't want to use a VST plug-in for this purpose, because -- unless there's some other way of doing this that I'm not aware of -- that means I'd have to insert this on every track where I want to use this particular keyboard controller.

Reaper has a built-in JS plug-in called "MIDI Velocity Scaler/Compressor" that does exactly what I'm looking to do...except, again, it's a plug-in. That would mean that I'd need to insert it on every track where I want to use this keyboard controller.

I have three keyboard controllers, so when I switch between them, I don't want to have to remember to enable/disable a plug-in on every track where this might be used. That's why I'm trying to change the velocity before it hits Reaper. It would be much easier to just start up OSCII-bot and have it run its script than enabling/disabling plug-ins on multiple tracks.

I believe MIDI-OX plus MIDI Yoke can theoretically do what I'm looking to do, but neither of those applications look like they've been updated to work with anything newer than Windows 2000. (I'm running Windows 10.)
__________________
Jeff Boller
Sundrift Productions
simplecarnival is offline   Reply With Quote
Old 10-25-2020, 11:24 AM   #4
goldenarpharazon
Human being with feelings
 
Join Date: Feb 2016
Posts: 161
Default

MidiYoke and Midi-OX both work quite happily on Windows 10 so that way is possible.

To bump up the velocity with OSCII-bot then something like this script fragment should work

Code:
@midimsg
status = msg1 & $xF0;  // mask out channel to derive status 
channel = msg1 & $x0F; // mask out status to derive channel (just in case it is relevant or needed)
status == $x90 ?       // Note on - so there must be a key press
(
  msg3 = msg3 + 25;         // Bump up the velocity 
  msg3 > 127 ? msg3 = 127;  // Keep the velocity in byte range
  midisend(out);            // Where "out" is to be the same as the name of the     midi output that was opened
):                          // Just in case: msg2 contains the note : but that isn't changing
( midisend(out);            // Pass through any other midi unmodified 
);
This is only the core of the script - the opening of the midi inputs and outputs etc is omitted.

Last edited by goldenarpharazon; 10-25-2020 at 11:40 AM. Reason: Added pass through
goldenarpharazon is offline   Reply With Quote
Old 10-25-2020, 12:04 PM   #5
TonE
Human being with feelings
 
Join Date: Feb 2009
Location: Reaper HAS send control via midi !!!
Posts: 2,921
Default

Yes, it is a jsfx. Good luck with the oscii-bot idea. You might look into Midi CC Mapper X to take whatever you like into oscii-bot. Their codes should be similar if not same.
TonE is offline   Reply With Quote
Old 10-25-2020, 01:56 PM   #6
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default

Quote:
Originally Posted by goldenarpharazon View Post
MidiYoke and Midi-OX both work quite happily on Windows 10 so that way is possible.

To bump up the velocity with OSCII-bot then something like this script fragment should work

Code:
@midimsg
status = msg1 & $xF0;  // mask out channel to derive status 
channel = msg1 & $x0F; // mask out status to derive channel (just in case it is relevant or needed)
status == $x90 ?       // Note on - so there must be a key press
(
  msg3 = msg3 + 25;         // Bump up the velocity 
  msg3 > 127 ? msg3 = 127;  // Keep the velocity in byte range
  midisend(out);            // Where "out" is to be the same as the name of the     midi output that was opened
):                          // Just in case: msg2 contains the note : but that isn't changing
( midisend(out);            // Pass through any other midi unmodified 
);
This is only the core of the script - the opening of the midi inputs and outputs etc is omitted.
Thank you, goldenarpharazon...that is helpful.

I'm trying to bolt the code you provided into the show-io script, and apparently I'm doing something wrong with the output (the variable originally called "out" in your code). Every time I press a key I get this error message the OSCII-bot output window (probably because my output is wrong somehow):

Code:
midisend(): device 0.000000 invalid


Here's my code:

Code:
@input omni_midi_out OMNI-MIDI-OUTPUT
@input omni_osc_out OMNI-OSC-OUTPUT
@input omni_midi OMNI-MIDI
@input omni_osc OMNI-OSC
@init


@input midi_in MIDI "SAMSON Graphite 49"

@output devicehandle MIDI "SAMSON Graphite 49"


gfx_init("show input and output", 320,200);


function showmsg(type, desc, wp) local(colpos,w,h,str) 
(
  gfx_setfont(1,"Verdana",max(gfx_h/16,10));
  gfx_a=1;
  gfx_mode=0;
  sprintf(str=#,"%s message: %s\n",type,desc);
  gfx_measurestr(str,w,h);
  gfx_x=gfx_w/2-w/2+2;
  gfx_y=wp - h/2;
  gfx_r=gfx_g=gfx_b=0;
  gfx_drawstr(str);

  gfx_r=0.5+0.5*cos(colpos);
  gfx_g=0.7+0.3*cos(colpos+$pi/4);
  gfx_b=0.6+0.4*cos(colpos+$pi/8);
  colpos+=0.03;

  gfx_x=gfx_w/2-w/2;
  gfx_y-=2;
  gfx_drawstr(str);

);

@oscmsg

showmsg(msgdev==omni_osc ? "OSC-IN" : "OSC-OUT", oscstr,gfx_h*.4);

@midimsg

status = msg1 & $xF0;  // mask out channel to derive status 
channel = msg1 & $x0F; // mask out status to derive channel (just in case it is relevant or needed)
status == $x90 ?       // Note on - so there must be a key press
(
  msg3 = msg3 + 25;         // Bump up the velocity 
  msg3 > 127 ? msg3 = 127;  // Keep the velocity in byte range
  midisend(output);            // Where "output" is to be the same as the name of the midi output that was opened
):                          // Just in case: msg2 contains the note : but that isn't changing
( midisend(output);            // Pass through any other midi unmodified 
);

showmsg(msgdev==omni_midi ? "MIDI-IN" : "MIDI-OUT", sprintf(#,"%02x %02x %02x",msg1,msg2,msg3),gfx_h*.6);

@timer

gfx_clear=-1;
gfx_x=gfx_y=0;

dx=gfx_w*0.01;
dy=gfx_h*0.01;
gfx_a=1.0;
gfx_blit(-1,0,0.003,dx,dy,gfx_w-2*dx,gfx_h-2*dy,0,0,gfx_w,gfx_h);
gfx_a=0.01;
gfx_r=gfx_g=gfx_b=0;
gfx_rect(0,0,gfx_w,gfx_h);

I've tried setting the output to this...

Code:
@output midi_out MIDI "SAMSON Graphite 49"
...but no luck.

Any suggestions?
__________________
Jeff Boller
Sundrift Productions
simplecarnival is offline   Reply With Quote
Old 10-25-2020, 02:22 PM   #7
goldenarpharazon
Human being with feelings
 
Join Date: Feb 2016
Posts: 161
Default

Given the names used for the keyboard where it uses @output devicehandle MIDI "SAMSON Graphite 49" then

Code:
midisend(out);
should be instead

Code:
midisend(devicehandle);
This should stop the error. But the script is sending the Midi output back to the keyboard which is probably not what you intend and that might create some kind of Midi loop (usually a bad thing). You need to be sending the midi via MIDI-Yoke to route this into Reaper. i.e.

Keyboard -> MIDI -> OSCII-bot -> MIDI -> MIDI-Yoke-> MIDI -> Reaper

Alternatively, to avoid passing on Midi you could open an OSC output to Reaper and instead use the Reaper OSC feature called VKB_MIDI_NOTE to send the note. This feature can be found in the Default.ReaperOSC OSC pattern config file. This post should help too https://forum.cockos.com/showthread.php?t=155109

Last edited by goldenarpharazon; 10-26-2020 at 02:05 AM. Reason: Made using the same device variable name clearer + added VKB_MIDI_NOTE idea
goldenarpharazon is offline   Reply With Quote
Old 10-25-2020, 03:31 PM   #8
TonE
Human being with feelings
 
Join Date: Feb 2009
Location: Reaper HAS send control via midi !!!
Posts: 2,921
Default

replacing output
with devicehandle ?
TonE is offline   Reply With Quote
Old 10-27-2020, 04:44 AM   #9
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 9,791
Default

Quote:
Originally Posted by simplecarnival View Post
unless there's some other way of doing this that I'm not aware of -- that means I'd have to insert this on every track where I want to use this particular keyboard controller.
No problem at all. I do this all the time with my live setups.
The controller uses a single track with appropriate midi preprocessing plugins. The midi output of that track is routed by Reaper's very versatile midi routing features to as many tracks as necessary that e.g. hold the VST instrument plugins.

-Michael
mschnell is offline   Reply With Quote
Old 10-28-2020, 07:31 AM   #10
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default

Thanks everybody for the responses so far. You've given me a few different avenues to try, but I still have questions.

Quote:
Originally Posted by goldenarpharazon View Post
You need to be sending the midi via MIDI-Yoke to route this into Reaper. i.e.

Keyboard -> MIDI -> OSCII-bot -> MIDI -> MIDI-Yoke-> MIDI -> Reaper
I theoretically installed MIDI-Yoke on my Windows 10 computer but...I'm at a loss on how to send the signal to it. I don't see the device name in Device Manager. I don't see MIDI-Yoke in Reaper's MIDI preferences, since it doesn't appear to show up like a normal MIDI device. (That is, assuming that the driver was actually installed and is ready to work.) I've been unable to find good step-by-step instructions on how to make this work.

Quote:
Originally Posted by goldenarpharazon View Post
Alternatively, to avoid passing on Midi you could open an OSC output to Reaper and instead use the Reaper OSC feature called VKB_MIDI_NOTE to send the note. This feature can be found in the Default.ReaperOSC OSC pattern config file. This post should help too https://forum.cockos.com/showthread.php?t=155109
This sounds like an interesting idea, but, again, I've been unable to find step-by-step instructions to make this work, and I'm not sure if I want to spend days diving into the OSC feature when I was looking for basically a quick fix for my velocity levels.

*EDIT #1*: Sadly, the script that's linked in that forum post is no longer downloadable from DropBox (https://forum.cockos.com/showpost.ph...2&postcount=99 ). Having something like that would go a long way toward figuring out how to implement this.

*EDIT #2*: I have MIDI to OSC almost figured out. Jump down to the next post in this thread.

Quote:
Originally Posted by mschnell View Post
No problem at all. I do this all the time with my live setups.
The controller uses a single track with appropriate midi preprocessing plugins. The midi output of that track is routed by Reaper's very versatile midi routing features to as many tracks as necessary that e.g. hold the VST instrument plugins.
Hmmm...I'm using the Orchestral Template for Reaper (http://otr.storyteller.im ) and part of the appeal of working with this setup is that you simply choose a track with the VSTi you want and it automatically arms that track for recording. It's an excellent, fast, efficient way to work. Is there any way to keep this sort of workflow (just click on a track, it's armed for recording) but still do what you're suggesting with a single track of MIDI preprocessing plugins?
__________________
Jeff Boller
Sundrift Productions

Last edited by simplecarnival; 10-28-2020 at 09:10 AM.
simplecarnival is offline   Reply With Quote
Old 10-28-2020, 09:04 AM   #11
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default

OK, I'm getting pretty close. Any MIDI key from the controller triggers a single note on event in Reaper:

Code:
@input omni_midi_out OMNI-MIDI-OUTPUT
@input omni_osc_out OMNI-OSC-OUTPUT
@input omni_midi OMNI-MIDI
@input omni_osc OMNI-OSC
@init

destdevice = localhost; // can also be -1 for broadcast

@input midi_in MIDI "SAMSON Graphite 49"

@output localhost OSC "127.0.0.1:8000"

gfx_init("show input and output", 320,200);


function showmsg(type, desc, wp) local(colpos,w,h,str) 
(
  gfx_setfont(1,"Verdana",max(gfx_h/16,10));
  gfx_a=1;
  gfx_mode=0;
  sprintf(str=#,"%s message: %s\n",type,desc);
  gfx_measurestr(str,w,h);
  gfx_x=gfx_w/2-w/2+2;
  gfx_y=wp - h/2;
  gfx_r=gfx_g=gfx_b=0;
  gfx_drawstr(str);

  gfx_r=0.5+0.5*cos(colpos);
  gfx_g=0.7+0.3*cos(colpos+$pi/4);
  gfx_b=0.6+0.4*cos(colpos+$pi/8);
  colpos+=0.03;

  gfx_x=gfx_w/2-w/2;
  gfx_y-=2;
  gfx_drawstr(str);

);

@oscmsg

showmsg(msgdev==omni_osc ? "OSC-IN" : "OSC-OUT", oscstr,gfx_h*.4);

@midimsg

status = msg1 & $xF0;  // mask out channel to derive status 
channel = msg1 & $x0F; // mask out status to derive channel (just in case it is relevant or needed)
status == $x90 ?       // Note on - so there must be a key press
(
  msg3 = msg3 + 25;         // Bump up the velocity 
  msg3 > 127 ? msg3 = 127;  // Keep the velocity in byte range
  oscsend(destdevice, sprintf(str=#, "i/vkb_midi/0/note/%i",60), msg3); // THIS ONLY SENDS ONE PITCH. I don't know how to extract the current pitch from the msgs yet.

):                          // Just in case: msg2 contains the note : but that isn't changing
(
//midisend(destdevice);            // Pass through any other midi unmodified 
  status = status // TODO: REMOVE -- placeholder so we can keep logic
);

showmsg(msgdev==omni_midi ? "MIDI-IN" : "MIDI-OUT", sprintf(#,"%02x %02x %02x",msg1,msg2,msg3),gfx_h*.6);

@timer

gfx_clear=-1;
gfx_x=gfx_y=0;

dx=gfx_w*0.01;
dy=gfx_h*0.01;
gfx_a=1.0;
gfx_blit(-1,0,0.003,dx,dy,gfx_w-2*dx,gfx_h-2*dy,0,0,gfx_w,gfx_h);
gfx_a=0.01;
gfx_r=gfx_g=gfx_b=0;
gfx_rect(0,0,gfx_w,gfx_h);
I've set up my control surface settings like the screenshot attached to this post.

So everything is now wired up. Here are my remaining questions:

1. How do I extract the pitch information from msg1/msg2/msg3?
2. How do I determine if there is a true note off event? (I will need to call something like `oscsend(destdevice, "i/vkb_midi/0/note/60", 0)` to send a note off event to OSC.)
3. How do I pass through all of the OTHER events (cc, pitch bend, mod wheel, etc.) to OSC?
Attached Images
File Type: jpg control surface settings.jpg (30.8 KB, 28 views)
__________________
Jeff Boller
Sundrift Productions

Last edited by simplecarnival; 10-28-2020 at 10:10 AM.
simplecarnival is offline   Reply With Quote
Old 10-28-2020, 12:37 PM   #12
goldenarpharazon
Human being with feelings
 
Join Date: Feb 2016
Posts: 161
Default

Quote:
Originally Posted by simplecarnival View Post
1. How do I extract the pitch information from msg1/msg2/msg3?
msg2 already contains the midi note as an integer number in the @MIDI block when the midi input is a "note on".

Quote:
Originally Posted by simplecarnival View Post
2. How do I determine if there is a true note off event? (I will need to call something like `oscsend(destdevice, "i/vkb_midi/0/note/60", 0)` to send a note off event to OSC.)
Write a test like the one for "note on" but instead test for status == $x80
Beware that some devices send a note on but with a velocity of 0 which is the same as "note off".

Quote:
Originally Posted by simplecarnival View Post
3. How do I pass through all of the OTHER events (cc, pitch bend, mod wheel, etc.) to OSC?
You can extract data from msg2 and msg3 and use VKB_MIDI_PITCH or VKB_MIDI_CC for the OSC: see these in the .ReaperOSC file

Note that Michael suggested ways to handle the simple velocity uplift for all tracks without needing an OSCII-bot script.
Select the appropriate means to an end. OSC scripting is very rich in functionality but might be a bit complicated depending on your routing, overall MIDI needs or its interaction with tracks/instruments/effects.

Midiyoke doesn't show up in Reaper x64 or Windows Device Manager but will work with other 32 bit applications like MidiOX or OSCII-bot. See https://forum.cockos.com/showthread.php?t=55787. This does limit MidiYoke's use in the MIDI routing I suggested in an earlier post.

Last edited by goldenarpharazon; 10-29-2020 at 01:04 AM.
goldenarpharazon is offline   Reply With Quote
Old 10-28-2020, 04:55 PM   #13
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default

THANK YOU, goldenarpharazon! I'm getting VERY close to figuring this out!

For what I'm looking to do, I think going the OSC route is going to work the best. I think I'll be able to manipulate the velocity curve of my controller so it'll be really comfortable to play. For my needs, as long as I pass in note on, note off, pitchbend, and all cc messages to OSC, this will work really well.

I now have note on and note off working. I can detect pitchbend and cc changes, but I'm not sure how to manipulate msg1/msg2/msg3 and feed it to the pitchbend and cc OSC calls. Here's the relevant part of the code:


Code:
@midimsg

status = msg1 & $xF0;  // mask out channel to derive status 
channel = msg1 & $x0F; // mask out status to derive channel (just in case it is relevant or needed)
status == $x90 ?       // Note on - so there must be a key press
(
  msg3 = msg3 + 25;         // Bump up the velocity 
  msg3 > 127 ? msg3 = 127;  // Keep the velocity in byte range
  oscsend(destdevice, sprintf(str=#, "i/vkb_midi/0/note/%i",msg2), msg3); 
):                          
status == $x80 ?	// Note off
(
  oscsend(destdevice, sprintf(str=#, "i/vkb_midi/0/note/%i",msg2), 0); 
):
status == $xE0 ?	// Pitch bend
(
  // TODO: I think I have to do something like this...
  //
  // oscsend(destdevice, "i/vkb_midi/50/pitch", 1);
  //
  // ...but I'm not sure how to manipulate msg1/msg2/msg3 and feed it to OSC.
):
status == $xB0 ?	// cc
(
  // TODO: I think I have to do something like this...
  //
  // oscsend(destdevice, "i/vkb_midi/@/cc/@", 1);
  //
  // ...but I'm not sure how to manipulate msg1/msg2/msg3 and feed it to OSC.
)
I see the definitions of VKB_MIDI_PITCH and VKB_MIDI_CC in the ReaperOSC file, but it's still not clear to me what I need to do with msg1/msg2/msg3 under these two conditions, so I'm not sure what to feed into the OSC calls. I've tried a bunch of things but nothing has worked so far.

Any suggestions?
__________________
Jeff Boller
Sundrift Productions
simplecarnival is offline   Reply With Quote
Old 10-29-2020, 01:07 AM   #14
goldenarpharazon
Human being with feelings
 
Join Date: Feb 2016
Posts: 161
Default

Quote:
Originally Posted by simplecarnival View Post

I now have note on and note off working. I can detect pitchbend and cc changes, but I'm not sure how to manipulate msg1/msg2/msg3 and feed it to the pitchbend and cc OSC calls.

I see the definitions of VKB_MIDI_PITCH and VKB_MIDI_CC in the ReaperOSC file, but it's still not clear to me what I need to do with msg1/msg2/msg3 under these two conditions, so I'm not sure what to feed into the OSC calls. I've tried a bunch of things but nothing has worked so far.

Any suggestions?
Great to hear of the progress.

For CC msg2 is the Controller ID and msg3 contains the parameter value 0-127
For Pitch Bend msg2 and msg3 contain the LSB and MSB of the pitch bend. The code may need to add this together with some experimentation has to how to map onto Reaper's input. This may help for the Pitch Bend https://forums.cockos.com/showthread.php?p=2262555
Suggestion: For Pitch Bend's integer parameter try the range 0-0x3fff bend where 0x2000 is the centre

To achieve the final results the following steps may help:-

1. Write code that uses printf() to see the correct Midi values [this gives confidence in the MIDI decoding]

2. Then write code that simply sends a predefined VKB_MIDI_PITCH or VKB_MIDI_CC [this give confidence in the OSC]

3. Then finally put the two together to send variable parameters

Last edited by goldenarpharazon; 10-29-2020 at 02:03 AM. Reason: Added pitch bend range suggestion
goldenarpharazon is offline   Reply With Quote
Old 10-30-2020, 08:38 PM   #15
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default

Thanks again, goldenarpharazon -- I finally got everything working!

One thing I didn't realize is that I had to make sure "Allow binding messages to REAPER actions and FX learn" was checked in the Control Surface Settings for OSC -- otherwise I was unable to control Reaper with my controller. I've attached an image of what the Control Surface Settings should look like.

Here's my final script:

Code:
@input omni_midi_out OMNI-MIDI-OUTPUT
@input omni_osc_out OMNI-OSC-OUTPUT
@input omni_midi OMNI-MIDI
@input omni_osc OMNI-OSC
@init

function get_key(msg2) local(leftover, key, octave) 
(
	// Get the key.
	leftover = msg2 % 12;
	leftover == 1 ? key = "C#" :
	leftover == 2 ? key = "D" :
	leftover == 3 ? key = "Eb" :
	leftover == 4 ? key = "E" :
	leftover == 5 ? key = "F" :
	leftover == 6 ? key = "F#" :
	leftover == 7 ? key = "G" :
	leftover == 8 ? key = "Ab" :
	leftover == 9 ? key = "A" :
	leftover == 10 ? key = "Bb" :
	leftover == 11 ? key = "B" :
	key = "C"; // leftover == 0
	
	// Get the octave.
	msg2 < 12 ? octave="-1" :
	msg2 < 24 ? octave="0" :
	msg2 < 36 ? octave="1" :
	msg2 < 48 ? octave="2" :
	msg2 < 60 ? octave="3" :
	msg2 < 72 ? octave="4" :
	msg2 < 84 ? octave="5" :
	msg2 < 96 ? octave="6" :
	msg2 < 108 ? octave="7" :
	msg2 < 120 ? octave="8" :
	octave="9";

	printf("%s%s", key, octave); 
);


destdevice = localhost; 

@input midi_in MIDI "SAMSON Graphite 49"	// "Name" is the full device name from Device Manager
@output localhost OSC "127.0.0.1:8000"

@midimsg

status = msg1 & $xF0;  // Mask out channel to derive status.
channel = msg1 & $x0F; // Mask out status to derive channel.
real_channel = channel + 1; // Make the channel 1-based for displaying in the log.

status == $x90 ? // Note on
(
	// msg2 contains the note.
	// msg3 contains the velocity.

	// Make sure we don't have a 0 velocity, which means "note off". (Releasing a drum pad on the Graphite 49 will trigger a 0 velocity.)
	msg3 == 0 ? 
	(
		oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/note/%i", channel, msg2), 0); 

		printf("ch%i %i %i %i\t NOTE OFF:   ", real_channel, msg1, msg2, msg3); 
		get_key(msg2);
		printf(" (NOTE ON with 0 velocity)\n"); 
	):
	(
		orig_msg3 = msg3; // Save the original msg3 because most likely we'll be altering it and we'll want to log the original velocity.

		// Because the Samson Graphite 49's keyboard velocity is so wonky (no matter which internal velocity curve you use), alter the 
		// curve to something more natural. This assumes that you are using the "Normal" (0) velocity curve in the keyboard.

		msg3 == 127 ? msg3 = 127 : 
		msg3 == 126 ? msg3 = 120 :
		msg3 == 125 ? msg3 = 114 :
		msg3 == 124 ? msg3 = 109 :
		msg3 == 123 ? msg3 = 105 :
		msg3 == 122 ? msg3 = 102 :
		msg3 == 121 ? msg3 = 100 :
		msg3 == 120 ? msg3 =  99 :
		msg3 == 119 ? msg3 =  99 :
		msg3 == 118 ? msg3 =  98 :
		msg3 == 117 ? msg3 =  98 :
		msg3 == 116 ? msg3 =  98 :
		msg3 == 115 ? msg3 =  97 :
		msg3 == 114 ? msg3 =  97 :
		msg3 == 113 ? msg3 =  97 :
		msg3 == 112 ? msg3 =  97 :
		msg3 == 111 ? msg3 =  96 :
		msg3 == 110 ? msg3 =  96 :
		msg3 == 109 ? msg3 =  96 :
		msg3 == 108 ? msg3 =  96 :
		msg3 == 107 ? msg3 =  95 :
		msg3 == 106 ? msg3 =  95 :
		msg3 == 105 ? msg3 =  95 :
		msg3 == 104 ? msg3 =  95 :
		msg3 == 103 ? msg3 =  94 :
		msg3 == 102 ? msg3 =  94 :
		msg3 == 101 ? msg3 =  94 :
		msg3 == 100 ? msg3 =  94 :
		msg3 ==  99 ? msg3 =  93 :
		msg3 ==  98 ? msg3 =  93 :
		msg3 ==  97 ? msg3 =  93 :
		msg3 ==  96 ? msg3 =  93 :
		msg3 ==  95 ? msg3 =  92 :
		msg3 ==  94 ? msg3 =  92 :
		msg3 ==  93 ? msg3 =  92 :
		msg3 ==  92 ? msg3 =  92 :
		msg3 ==  91 ? msg3 =  91 :
		(
			// Otherwise, cut the velocity range by half and start at 64.
			msg3 = msg3 * 0.5 + 64;
		);

		oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/note/%i", channel, msg2), msg3); 

		printf("ch%i %i %i %i\t NOTE ON:    ", real_channel, msg1, msg2, orig_msg3); 
		get_key(msg2);
		printf("  ORIG VEL: %i  ALTERED VEL: %i\n", orig_msg3, msg3); 
	);

):
status == $x80 ? // Note off
(
	oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/note/%i", channel, msg2), 0); 

	printf("ch%i %i %i %i\t NOTE OFF:   ", real_channel, msg1, msg2, msg3); 
	get_key(msg2);
	printf("\n"); 
):
status == $xE0 ? // Pitch bend
(
	// msg2 contains the LSB. msg3 contains the MSB.
	// In a pitch bend message, we combine the LSB and MSB to make a single 14-bit value (0-16383, rest position is 8192).
	// We do this by bit-shifting the MSB 7 bits to left and combining that with the LSB using a bitwise OR operation.
	val = (msg3 << 7) | msg2; 

	oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/pitch", channel), val); 

	printf("ch%i %i %i %i\t PITCH BEND: %i\n", real_channel, msg1, msg2, msg3, val); 
):
status == $xB0 ? // CC
(
	oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/cc/%i", channel, msg2), msg3); 

	printf("ch%i %i %i %i\t CC:         %i\tVALUE: %i\n", real_channel, msg1, msg2, msg3, msg2, msg3); 
);

The script contains a custom keyboard velocity curve that works well with my particular Samson Graphite 49 controller. I cannot guarantee that the velocity curve will feel OK on any other keyboard except the one that I own. If someone else uses this script, you'll need to modify the code starting with "msg3 == 127 ?" to create your own curve.

The script passes all pitch bend and CC messages through to Reaper, unaltered. It does not pass through aftertouch, patch changes, or any other messages. Note that if you already have MIDI events set up with Reaper without using this script, you'll probably have to set them up again so that they have MIDI events that look like "/vkb_midi/16/cc/28" in the Actions window instead of like "MIDI Chan 16 CC 28".

Thanks again to everyone on this thread. This was more work than I expected, but I'm pleased with the result and it's made a great, cheap keyboard controller with one big flaw (a wonky velocity curve) very playable.
Attached Images
File Type: jpg control surface settings.jpg (29.1 KB, 22 views)
__________________
Jeff Boller
Sundrift Productions

Last edited by simplecarnival; 10-31-2020 at 07:45 AM. Reason: Fixed typo in script
simplecarnival is offline   Reply With Quote
Old 10-31-2020, 09:28 AM   #16
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default

Unfortunately, there's still one more thing to figure out (if indeed it can be done). I'm not seeing an obvious way to set this up with OSC, and from searching the forum posts, it looks like it may not be possible to do (https://forum.cockos.com/showpost.ph...93&postcount=3 ).

My script passes on CC messages (like, from buttons) just fine. However, I cannot use the Graphite 49's knobs to send relative CC messages to Reaper's commands. In comparison, if I don't use OSCII-bot and just run the controller directly into Reaper, I can assign and use the relative CC messages from the controller knobs just fine.

I tried experimenting with ACTION_RELATIVE, which appears like it's meant to affect the relative value of a cc (though it doesn't let you specify a MIDI channel for some odd reason).

Based on what I see in the sample_script.txt file included with OSCII-bot, it appears you can send a relative cc message like this...

oscsend(destdevice, "f/action/992/cc/relative", -1);

...or like this:

oscsend(destdevice, "f/action/992/cc/relative", 1);

When I try to implement something similar (using 992 or a number under 128), Reaper lets me assign an action to the knob in the Action window. However, Reaper doesn't act on this message when I send it.

If there is no solution for this, a workaround would be to halve the number of knobs I have and use them for scrolling in one direction only. So instead of assigning one knob to "View: Go to track (MIDI CC/OSC only)", I would assign one knob to "Track: Go to previous track" and another knob to "Track: Go to next track". I really don't want to do that, but if there's no other option...

Anyway, does anyone have any tips on how to send a relative change CC message via OSCII-bot that Reaper can understand as an Action?
__________________
Jeff Boller
Sundrift Productions
simplecarnival is offline   Reply With Quote
Old 10-31-2020, 02:29 PM   #17
goldenarpharazon
Human being with feelings
 
Join Date: Feb 2016
Posts: 161
Default

Great to hear of progress and that the keyboard velocity curve is now usable. This might help

Disclaimer: What follows is speculation from working with REAPER soft takeover and ACTION_SOFT
One of the downsides of Reaper's OSC is how things are supposed to work is not defined beyond the tantalising comments in the .ReaperOSC file
Trial & error, experimentation, repeat is the only way.

Suggestion: Experiment with and investigate the values of Reaper OSC parameter DEVICE_TRACK_FOLLOWS at the top of .ReaperOSC file

Assertion: You can possibly only assign ACTION_RELATIVE to the few Reaper actions that allow "Midi CC Relative"

A better way to tackle your desired knob function may be to say "Which Reaper action or set of actions do I want to trigger via OSC from my controller's input".

You can then detect the midi coming from the keyboard knob (i.e data in mgs2 & msg3) and send the appropriate reaper action or actions using the ACTION parameter in .ReaperOSC

Something like this stylised example for your keyboard use case:-

Code:
 
If keyboard knob sends -1 then 
   Goto previous track [action 40286] 
else if +1 
   Goto next track [action 40285]
goldenarpharazon is offline   Reply With Quote
Old 10-31-2020, 06:18 PM   #18
simplecarnival
Human being with feelings
 
simplecarnival's Avatar
 
Join Date: Feb 2007
Posts: 375
Default

Got it, goldenarpharazon!

I ended up not using actions at all. Instead, I translated the CC of the knob so that, if I'm moving it clockwise, the script sends out one particular CC, but if I'm moving it counter-clockwise, the script sends out a different CC. Then it was a matter of mapping the clockwise and counter-clockwise CCs that were sent to individual actions in Reaper's Action window.

Anyway, NOW I think I'm done with this.

Thanks again for your help!


Code:
@input omni_midi_out OMNI-MIDI-OUTPUT
@input omni_osc_out OMNI-OSC-OUTPUT
@input omni_midi OMNI-MIDI
@input omni_osc OMNI-OSC
@init

function get_key(msg2) local(leftover, key, octave) 
(
	// Get the key.
	leftover = msg2 % 12;
	leftover == 1 ? key = "C#" :
	leftover == 2 ? key = "D" :
	leftover == 3 ? key = "Eb" :
	leftover == 4 ? key = "E" :
	leftover == 5 ? key = "F" :
	leftover == 6 ? key = "F#" :
	leftover == 7 ? key = "G" :
	leftover == 8 ? key = "Ab" :
	leftover == 9 ? key = "A" :
	leftover == 10 ? key = "Bb" :
	leftover == 11 ? key = "B" :
	key = "C"; // leftover == 0
	
	// Get the octave.
	msg2 < 12 ? octave="-1" :
	msg2 < 24 ? octave="0" :
	msg2 < 36 ? octave="1" :
	msg2 < 48 ? octave="2" :
	msg2 < 60 ? octave="3" :
	msg2 < 72 ? octave="4" :
	msg2 < 84 ? octave="5" :
	msg2 < 96 ? octave="6" :
	msg2 < 108 ? octave="7" :
	msg2 < 120 ? octave="8" :
	octave="9";

	printf("%s%s", key, octave); 
);


destdevice = localhost; 

@input midi_in MIDI "SAMSON Graphite 49"	// "Name" is the full device name from Device Manager
@output localhost OSC "127.0.0.1:8000"

@midimsg

status = msg1 & $xF0;  // Mask out channel to derive status.
channel = msg1 & $x0F; // Mask out status to derive channel.
real_channel = channel + 1; // Make the channel 1-based for displaying in the log.

status == $x90 ? // Note on
(
	// msg2 contains the note.
	// msg3 contains the velocity.

	// Make sure we don't have a 0 velocity, which means "note off". (Releasing a drum pad on the Graphite 49 will trigger a 0 velocity.)
	msg3 == 0 ? 
	(
		oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/note/%i", channel, msg2), 0); 

		printf("ch%i %i %i %i\t NOTE OFF:   ", real_channel, msg1, msg2, msg3); 
		get_key(msg2);
		printf(" (NOTE ON with 0 velocity)\n"); 
	):
	(
		orig_msg3 = msg3; // Save the original msg3 because most likely we'll be altering it and we'll want to log the original velocity.

		// Because the Samson Graphite 49's keyboard velocity is so wonky (no matter which internal velocity curve you use), alter the 
		// curve to something more natural. This assumes that you are using the "Normal" (0) velocity curve in the keyboard.

		msg3 == 127 ? msg3 = 127 : 
		msg3 == 126 ? msg3 = 120 :
		msg3 == 125 ? msg3 = 114 :
		msg3 == 124 ? msg3 = 109 :
		msg3 == 123 ? msg3 = 105 :
		msg3 == 122 ? msg3 = 102 :
		msg3 == 121 ? msg3 = 100 :
		msg3 == 120 ? msg3 =  99 :
		msg3 == 119 ? msg3 =  99 :
		msg3 == 118 ? msg3 =  98 :
		msg3 == 117 ? msg3 =  98 :
		msg3 == 116 ? msg3 =  98 :
		msg3 == 115 ? msg3 =  97 :
		msg3 == 114 ? msg3 =  97 :
		msg3 == 113 ? msg3 =  97 :
		msg3 == 112 ? msg3 =  97 :
		msg3 == 111 ? msg3 =  96 :
		msg3 == 110 ? msg3 =  96 :
		msg3 == 109 ? msg3 =  96 :
		msg3 == 108 ? msg3 =  96 :
		msg3 == 107 ? msg3 =  95 :
		msg3 == 106 ? msg3 =  95 :
		msg3 == 105 ? msg3 =  95 :
		msg3 == 104 ? msg3 =  95 :
		msg3 == 103 ? msg3 =  94 :
		msg3 == 102 ? msg3 =  94 :
		msg3 == 101 ? msg3 =  94 :
		msg3 == 100 ? msg3 =  94 :
		msg3 ==  99 ? msg3 =  93 :
		msg3 ==  98 ? msg3 =  93 :
		msg3 ==  97 ? msg3 =  93 :
		msg3 ==  96 ? msg3 =  93 :
		msg3 ==  95 ? msg3 =  92 :
		msg3 ==  94 ? msg3 =  92 :
		msg3 ==  93 ? msg3 =  92 :
		msg3 ==  92 ? msg3 =  92 :
		msg3 ==  91 ? msg3 =  91 :
		(
			// Otherwise, cut the velocity range by half and start at 64.
			msg3 = msg3 * 0.5 + 64;
		);

		oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/note/%i", channel, msg2), msg3); 

		printf("ch%i %i %i %i\t NOTE ON:    ", real_channel, msg1, msg2, orig_msg3); 
		get_key(msg2);
		printf("  ORIG VEL: %i  ALTERED VEL: %i\n", orig_msg3, msg3); 
	);

):
status == $x80 ? // Note off
(
	oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/note/%i", channel, msg2), 0); 

	printf("ch%i %i %i %i\t NOTE OFF:   ", real_channel, msg1, msg2, msg3); 
	get_key(msg2);
	printf("\n"); 
):
status == $xE0 ? // Pitch bend
(
	// msg2 contains the LSB. msg3 contains the MSB.
	// In a pitch bend message, we combine the LSB and MSB to make a single 14-bit value (0-16383, rest position is 8192).
	// We do this by bit-shifting the MSB 7 bits to left and combining that with the LSB using a bitwise OR operation.
	val = (msg3 << 7) | msg2; 

	oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/pitch", channel), val); 

	printf("ch%i %i %i %i\t PITCH BEND: %i\n", real_channel, msg1, msg2, msg3, val); 
):
status == $xB0 ? // CC
(
	// We send specific relative cc messages for messages that are on MIDI channel 16:
	//
	// Input  Knob Function			Send CC Down (counterclockwise)	Send CC Up (clockwise)                     
	// -----  -------------------------	-------------------------------	----------------------
	// CC 14  Go to Track			CC 14				CC 15
	// CC 20  Rewind/FF			CC 20				CC 21
	// CC 22  Scroll Up/Down		CC 22				CC 23
	// CC 24  Scroll Left/Right		CC 24				CC 25
	// CC 26  Zoom				CC 26				CC 27
	// CC 28  Arrow Keys Down/Up		CC 28				CC 29
	// CC 30  Selected Track Volume		CC 30				CC 31
	// CC 32  Master volume			CC 32				CC 33

	repeat_cc = 1; // How many times do we want to repeat the cc? (In the case of changing volume, we want it to move faster than Reaper's default.)

	real_channel == 16 && (msg2 == 14 ||
		               msg2 == 20 ||
			       msg2 == 22 ||
			       msg2 == 24 ||
			       msg2 == 26 ||
			       msg2 == 28 ||
			       msg2 == 30 ||
			       msg2 == 32)? 
	(
		// Send a relative cc message.
		previous_msg2 == msg2 ?
		(
			// We need to compare where we've been with where we're going.
			direction = 0;
			previous_msg3 == 127 && msg3 == 127 ? direction = 1 : // We're at the high end of the knob and we keep turning it higher.
			previous_msg3 == 0 && msg3 == 0 ? direction = -1 : // We're at the low end of the knob and we keep turning it lower.
			previous_msg3 < msg3 ? direction = 1 :
			previous_msg3 > msg3 ? direction = -1;
			
			cc_out = -1;

			msg2 == 14 ? // Go to Track
			(
				direction == -1 ? cc_out = 14 :
				direction == 1 ? cc_out = 15;
			) :
			msg2 == 20 ? // Rewind/FF
			(

				direction == -1 ? cc_out = 20 :
				direction == 1 ? cc_out = 21;		
			) :
			msg2 == 22 ? // Scroll Up/Down
			(

				direction == -1 ? cc_out = 22 :
				direction == 1 ? cc_out = 23;		
			) :
			msg2 == 24 ? // Scroll Left/Right
			(
				direction == -1 ? cc_out = 24 :
				direction == 1 ? cc_out = 25;		
			) :
			msg2 == 26 ? // Zoom
			(
				direction == -1 ? cc_out = 26 :
				direction == 1 ? cc_out = 27;		
			) :
			msg2 == 28 ? // Arrow Keys Down/Up
			(
				direction == -1 ? cc_out = 28 :
				direction == 1 ? cc_out = 29;		
			) :
			msg2 == 30 ? // Selected Track Volume
			(
				direction == -1 ? cc_out = 30 :
				direction == 1 ? cc_out = 31;		
				repeat_cc = 10;
			) :
			msg2 == 32 ? // Master Volume
			(
				direction == -1 ? cc_out = 32 :
				direction == 1 ? cc_out = 33;		
				repeat_cc = 10;
			);

			cc_out != -1 ? 
			(
				loop(repeat_cc, (
							oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/cc/%i", channel, cc_out), 127); 			
							printf("ch%i %i %i %i\t CC:         %i\tVALUE: 127\tALTERED CC OUT:%i\n", real_channel, msg1, msg2, msg3, msg2, cc_out); 
						)
				);
			);
		); // If we don't have two of the same msg2 messages in a row, then we don't know what direction we're going in and we'll wait until the next iteration.

		previous_msg2 = msg2;
		previous_msg3 = msg3;
	):
	(

		real_channel == 16 && (msg2 == 103 || msg2 == 111)? 
		(
			// Metronome click volume + or - needs a lot of repeating.
			repeat_cc = 25;
		);

		// Send a straight cc message.
		loop(repeat_cc, (
					oscsend(destdevice, sprintf(str=#, "i/vkb_midi/%i/cc/%i", channel, msg2), msg3); 
					printf("ch%i %i %i %i\t CC:         %i\tVALUE: %i\n", real_channel, msg1, msg2, msg3, msg2, msg3); 
				)
		);
	);

);
__________________
Jeff Boller
Sundrift Productions

Last edited by simplecarnival; 11-04-2020 at 09:37 AM. Reason: Updated code with latest fixes
simplecarnival 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:20 AM.


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