10-24-2020, 04:38 AM | #1 |
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
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! |
10-24-2020, 01:07 PM | #2 |
Human being with feelings
Join Date: Feb 2009
Location: Reaper HAS send control via midi !!!
Posts: 4,032
|
Did you try Midi CC Mapper X? It can do this and million other things.
|
10-25-2020, 07:54 AM | #3 |
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
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.) |
10-25-2020, 11:24 AM | #4 |
Human being with feelings
Join Date: Feb 2016
Posts: 189
|
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 ); Last edited by goldenarpharazon; 10-25-2020 at 11:40 AM. Reason: Added pass through |
10-25-2020, 12:04 PM | #5 |
Human being with feelings
Join Date: Feb 2009
Location: Reaper HAS send control via midi !!!
Posts: 4,032
|
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.
|
10-25-2020, 01:56 PM | #6 | |
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
Quote:
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" Any suggestions? |
|
10-25-2020, 02:22 PM | #7 |
Human being with feelings
Join Date: Feb 2016
Posts: 189
|
Given the names used for the keyboard where it uses @output devicehandle MIDI "SAMSON Graphite 49" then
Code:
midisend(out); Code:
midisend(devicehandle); 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 |
10-25-2020, 03:31 PM | #8 |
Human being with feelings
Join Date: Feb 2009
Location: Reaper HAS send control via midi !!!
Posts: 4,032
|
replacing output
with devicehandle ? |
10-27-2020, 04:44 AM | #9 | |
Human being with feelings
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,770
|
Quote:
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 |
|
10-28-2020, 07:31 AM | #10 | |||
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
Thanks everybody for the responses so far. You've given me a few different avenues to try, but I still have questions.
Quote:
Quote:
*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:
Last edited by simplecarnival; 10-28-2020 at 09:10 AM. |
|||
10-28-2020, 09:04 AM | #11 |
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
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); 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? Last edited by simplecarnival; 10-28-2020 at 10:10 AM. |
10-28-2020, 12:37 PM | #12 | |||
Human being with feelings
Join Date: Feb 2016
Posts: 189
|
Quote:
Quote:
Beware that some devices send a note on but with a velocity of 0 which is the same as "note off". Quote:
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. |
|||
10-28-2020, 04:55 PM | #13 |
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
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. ) Any suggestions? |
10-29-2020, 01:07 AM | #14 | |
Human being with feelings
Join Date: Feb 2016
Posts: 189
|
Quote:
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 |
|
10-30-2020, 08:38 PM | #15 |
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
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. Last edited by simplecarnival; 10-31-2020 at 07:45 AM. Reason: Fixed typo in script |
10-31-2020, 09:28 AM | #16 |
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
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? |
10-31-2020, 02:29 PM | #17 |
Human being with feelings
Join Date: Feb 2016
Posts: 189
|
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] |
10-31-2020, 06:18 PM | #18 |
Human being with feelings
Join Date: Feb 2007
Posts: 375
|
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); ) ); ); ); Last edited by simplecarnival; 11-04-2020 at 09:37 AM. Reason: Updated code with latest fixes |
Thread Tools | |
Display Modes | |
|
|