Old 09-20-2018, 08:58 PM   #1
woodslanding
Human being with feelings
 
woodslanding's Avatar
 
Join Date: Mar 2007
Location: Denver, CO
Posts: 633
Default Problems with voice allocation script

I'm working on another in my MPE series of JSFX. This one goes along with the MPE pitchbend scaler I wrote about elsewhere in the forums.

This script is designed to rechannelize data from an MPE device, which will typically be sending on channels 2-15, and rechannelize it to channels 2-X, in situations where the receiving instrument is not MPE compatible, and/or cannot receive on so many channels. For instance, Kontakt requires you make a copy of your instrument for each channel you want to receive on. In this case, I would limit the number of channels to something less than 15 in many cases, as I only have 10 fingers anyway. But my 3 Seaboard Blocks are set up to send on channels 2-6,7-11 and 12-15 respectively, so if I want full range, I have to receive on all 15 channels. Similarly Spectrasonics gear only receives on channels 1-8, so that limits each block to 2 or 3 channels. (Roli says they are going to fix the issue of different blocks having to send on different channels, but I don't know when this may happen.)

You can adjust for this somewhat by going into the Roli Dashboard, and changing the number of channels you are sending on, but what if you want to layer 2 sounds with different polyphony settings? And constantly opening up the dashboard to make adjustments is annoying, even in cases where that would be sufficient.

Also, if you'd like to control an MPE instrument with a conventional controller or vice-versa, there are other hoops to jump through.

This plugin is designed to address these problems.

But I have 2 issues with it.

First, my algorithm for finding the oldest voice is not working at all, and I can't figure out why. For now I'm just modding by note count, which is less elegant, but works.

Second, I am getting guaranteed stuck notes when I hold down the maximum number of notes. (Max-1) simultaneous notes can be played all day, but add that one extra voice, and it sticks. It looks like the last note off is going to the wrong channel, but I can't see how that's happening.

Eventually, I hope this plugin will convert from MPE-Standard MIDI or vice-versa, but that functionality is not tested yet (it may work...) For now, I'm just trying to get MPE->MPE to work (with base channel set to 2 to use with typical MPE devices.)

I'd be most grateful for any help from the serious coders out there, thanks in advance!!! When it's done, I'll post it, and maybe someone will find it useful.

Code:
desc:MPE Rechannelizer
//tags: MIDI processing
//author: Eric Moon
slider1:s_mute=0<0,1,1{no,yes}>INPUT CONTROL:                     mute input
//slider3:s_midiport=0<0,16,1>midi port
//just set min, max and global channel the same for normal input?
slider5:s_ctrlrType=0<0,1,1{normal,mpe}>controller midi type
//chan out for normal vst, first chan out for mpe
slider6:s_baseChanOut=0<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>base channel out
slider7:s_mpePoly=1<1,15,1>mpe channel count out

slider10:s_vstType=0<0,1,1{normal,mpe}>VST midi type
slider11:s_ATtoCC=0<0,12,1{off,1,2,3,4,5,6,7,8,9,10,11}>Convert AT to CC
//slider16:s_loNote=0<0,127,1>low note
//slider17:s_hiNote=127<0,127,1>high note

//slider35:s_glideToNotes=0<0,1,1{no,yes}>mpe glides to notes

// these lines tell Reaper the effect has no audio input/output,
// which enables processing optimizations.
// MIDI-only FX should always have these lines.
in_pin:none
out_pin:none
options:no_meter

/********************************* INIT - midi methods  ************************/
@init
noteOff = $x80;
noteOn = $x90;
CC = $xB0;
PChg = $xC0;
ATouch = $xD0;
PBend = $xE0;
roliCC = 74;
mod = 1;
function getStatus (msg)  ( msg & $xF0; );
function getChannel (msg) ( msg & $x0F; );
function isMpeControl(status,msg2,msg3)  (
    (((status == CC) && (msg2 == roliCC))
    || ((status == CC) && (msg2 == mod))
    || (status == ATouch) 
    || (status == PBend))  ? 1 : 0;  
);

/************************************** chan array ******************************/
order = 0;
voiceCount = 0;

chInput = 0; 
ch_size = 3;  // 2 entries per note: channel, order,note
//buflen = r_size * 16 midi chans;
function clearArray() local(i) (
    i = 0;
    loop
    (   
    ch_size * 16,  
        chInput[i] = -1;
        i = i + 1;
    )
);
function storeChan(chan,voice,order,note)  (
    chInput[ch_size * chan] = voice;
    chInput[ch_size * chan + 1] = order;
    chInput[ch_size * chan + 2] = note;
);
function ch_voice(chan)( chInput[ch_size * chan    ]; );
function ch_order(chan)( chInput[ch_size * chan + 1]; );
function ch_note(chan) ( chInput[ch_size * chan + 2]; );

function voiceForNote(note)  (
    i = 0;
    while
    (  ch_note(i) !=  note && i < s_mpePoly ?
            i = i + 1;
    );
    ch_voice(i);
);

function getVoice(chan,note) local(voice)  (
    s_vstType == 0 ? voice = s_baseChanOut :
    s_ctrlrType = 1 && s_vstType == 1 ? voice = ch_voice(chan) + s_baseChanOut :
    //if the source channel was constant, can't sort by channel in....
    s_ctrlrType = 0 && s_vstType == 1 ? voice = voiceForNote(note) + s_baseChanOut :

    voice;
);
//should look for the first note we played to steal
function getOldestVoice()  local(first,age) (
   /* i = 0;
    first = 1000000; //unlikely
    loop
    //for each mpe midi channel do
    (   s_mpePoly,   
        //check for lowest note order value....
        age = ch_order(i);
        age < first ? first = age;
        i = i + 1;
    );  
    i = 0;
    while  (
        ch_order(i) != first;
        i = i + 1;
    );
    i;*/
    order % s_mpePoly;  //hack for testing, simplest voice allocation
 ); 

clearArray();

@slider


@block
//if all keys are up, reset the voice count, so it doesn't get huge.
voiceCount == 0 ? 
(   order = 0;
    clearArray;
);
while 
(
    midirecv(offs, msg1, msg2, msg3) ?
    (
        status = getStatus(msg1);
        channel = getChannel(msg1);
        status == noteOn && msg3 > 0 ? // note-on
        (
            note = msg2;
            //(key <= s_hiNote) && (key >= s_loNote) && (!s_mute) ?
            (
                vel = msg3; // velocity
                oldest = getOldestVoice();
                //storeChan to rechan pb and mod from Roli 
                storeChan(channel, oldest, order, note);
                voice = getVoice(channel,note);//ignores oldest chan in normal mode
                midisend(offs,status + voice, note, vel);                
                order = order + 1;
                voiceCount = voiceCount + 1;
            )
        )
        :  
        (status == noteOn && msg3 = 0 ) || status == noteOff ? // note-off, convert pitch
        (
            note = msg2;
            vel = msg3;
            voice = getVoice(channel,note);
            midisend(offs,status + voice,note,vel);
            voiceCount = voiceCount - 1;
        ) 
        :  //mpe controls
        (isMpeControl(status,msg2,msg3))  ?
        (   // what do we do in normal->mpe mode?  shouldn't have any of this data.... 
            voice = getVoice(channel,note); //rechannelize!
            (status == ATouch) && (s_ATtoCC > 0) ?  
            (
                status = CC;
                msg3 = msg2;
                msg2 = s_ATtoCC;
            );          
            midisend(offs,status + voice,msg2, msg3);
        );
    );
);
__________________
eric moon
Very Stable Genius
https://gogolab.com/
woodslanding is offline   Reply With Quote
Old 09-22-2018, 10:01 PM   #2
woodslanding
Human being with feelings
 
woodslanding's Avatar
 
Join Date: Mar 2007
Location: Denver, CO
Posts: 633
Default

Okay, I thought about this some more, and realized that the line:

order % s_mpePoly;

which was my 'hack' for voice allocation, actually does give the oldest note. So one bug solved! I think I might try and come up with an algorithm that will give the highest note priority, and the lowest note second priority, and then eliminate the oldest note from the remaining voices.

But I still don't know why the last voice is giving stuck notes....
__________________
eric moon
Very Stable Genius
https://gogolab.com/
woodslanding 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 02:00 AM.


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