Welcome to %s forums

BrainModular Users Forum

Login Register

Remapping MIDI channels ( [1 to 2] [1 to 3],etc )

General Discussion about whatever fits..
Post Reply
Ben J
New member
Posts: 9
Contact:

Unread post by Ben J » 21 Feb 2010, 09:59

Today is my first day with Usine, and I'm having some troubles, unsurprisingly.

However, I did figure out how to transpose MIDI without affecting CC, complete with a fancy UI button, so I'm a little proud of myself for that. :)

But I've spent hours reading posts, checking out various MIDI addons, and basically trying to figure it out for myself, but I've had no such luck with MIDI remapping.

Here's what I'm trying to accomplish:
One of the VSTi I'm using is Kontakt, and rather than setting up multiple instances of the plugin, I want to load several samples into Kontakt that are set to various MIDI channels. To change sounds I want to be able to switch MIDI channels in Usine to play the respective samples.

I'd use my controller to change MIDI channels, but it wouldn't be as fast as pressing a button on the UI.

woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 21 Feb 2010, 10:22

Soon there will be a script in the library just for this, I think. But for now:

Take a midi input object, turn on 'unpack'. Plug message,code1,code2,and receive into the related inputs on a 'create midi message' module (receive goes to create)

Now drag a cable from the 'chan' input and create a combobox (or what have you) to change channel....

This is not too smart, in that you must stop playing all notes before you switch channels, or you will get hanging notes. The script will be smarter.....

For me, I use multi mode in kontakt, and then I can run 80+ instruments instead of just 16, and I can use regular patch change commands ;)
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

User avatar
nay-seven
Site Admin
Posts: 5684
Location: rennes France
Contact:

Unread post by nay-seven » 21 Feb 2010, 10:27

Welcome Ben j !
you can also use several midi transformer module...;-)

nofish
Member
Posts: 114
Contact:

Unread post by nofish » 21 Feb 2010, 15:26

I do it using the Midi channeliser found here.
http://www.niallmoody.com/ndcplugs/ndcmidi.htm
If you connect a slider or similar to the Midi channel inlet you can store it in the conductor.

Not saying that it isn't possible with Usine's own modules, it was just the quickest way I've found when being new to Usine and I keep it out of laziness. :)

Ben J
New member
Posts: 9
Contact:

Unread post by Ben J » 22 Feb 2010, 04:18

Thanks for the help everybody!

Clearscreen
Member
Posts: 482
Location: Australia
Contact:

Unread post by Clearscreen » 22 Feb 2010, 11:42

woodslanding wrote:This is not too smart, in that you must stop playing all notes before you switch channels, or you will get hanging notes. The script will be smarter.....
if you put a 'has changed' triggering an 'all notes off' from the knob/fader you use to change channels you can get around this pretty easily. hope it helps... :)

bsork
Site Admin
Posts: 1334
Location: Asker, Norway
Contact:

Unread post by bsork » 22 Feb 2010, 13:46

There will be some "packs" available in a few days, amongst others a MIDI Utility pack which contains a script that can do this. It's designed for transposing, but also do rechannelising, and keeps track of the note number and channel of a NoteOn and maps any subsequent NoteOff (and optionally key aftertouch) to the right note and channel.

Take the code below, copy it into an empty script overwriting the existing text and run compile (ctrl-F9].

Code: Select all

(*****************************************************************************************************
Transpose.script

Script for inserting into a MIDI stream to transpose all or just a part of the notes received.
NoteOffs are mapped to the same channel and note number as the previous NoteOns, avoiding
hanging notes even when changing parameters before the note is released.

The input channel/note can be sent together with the transposed notes.

Key/poly aftertouch can optionally also be transposed together with the notes.
*****************************************************************************************************)
CONST BUFFER_SIZE = 128;

// MIDI channel messages
CONST NOTEOFF = Byte(128);
CONST NOTEON  = Byte(144);
CONST KA      = Byte(160); // Key (poly) Aftertouch

TYPE tMidiArr = ARRAY OF tMidi;

TYPE tNoteOns = RECORD
        origChannel : Byte;
        origNoteNo  : Byte;
        outChannel  : Byte;
        outNoteNo   : Byte;
        hasKA       : Boolean;
     END;

////////////////////////////////////////////////////////////////////////////////////////

// In-/out parameters
VAR pMidiIn, pMidiOut1 : tParameter;
VAR pBypass, pChIn, pTrspLoLim, pTrspHiLim, pInclKA : tParameter;
VAR pChOut, pTranspose, pOctFoldBack, pKeep : tParameter;

// Global variables
VAR bypass, inclKA : Boolean;
VAR sentNoteOns : ARRAY OF tNoteOns;
VAR numIn, numSentNoteOns, numOut1 : Integer;
VAR chIn, trspLoLim, trspHiLim, chOut : Byte;
VAR transpose : Integer;
VAR octFoldback, keepOrig : Boolean;

////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE Init;
   VAR i : Integer;
BEGIN
   trspLoLim := 0;
   trspHiLim := 127;
   chIn := 0;
   chOut := 0;

   pMidiIn := CreateParam('midi in', ptMidi); SetIsOutput(pMidiIn, FALSE);
   pMidiOut1 := CreateParam('midi out', ptMidi); SetIsInput(pMidiOut1, FALSE);

   pBypass := CreateParam('bypass', ptSwitch); SetIsOutput(pBypass, FALSE);

   pChIn := CreateParam('channel in', ptListbox); SetIsOutput(pChIn, FALSE);
   SetListboxString(pChIn, '"all","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16"');
   SetValue(pChIn, Single(chIn));

   pTrspLoLim := CreateParam('note lo limit', ptMidiNoteFader); SetIsOutput(pTrspLoLim, FALSE);
   SetDefaultValue(pTrspLoLim, trspLoLim); SetValue(pTrspLoLim, trspLoLim);

   pTrspHiLim := CreateParam('note hi limit', ptMidiNoteFader); SetIsOutput(pTrspHiLim, FALSE);
   SetDefaultValue(pTrspHiLim, trspHiLim); SetValue(pTrspHiLim, trspHiLim);

   pInclKA := CreateParam('incl KA', ptSwitch); SetIsOutput(pInclKA, FALSE);
   
   pChOut := CreateParam('channel out', ptListbox); SetIsOutput(pChOut, FALSE);
   SetListboxString(pChOut, '"orig","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16"');
   SetValue(pChOut, Single(chOut));

   pTranspose := CreateParam('transpose', ptDataFader); SetIsOutput(pTranspose, FALSE);
   SetMin(pTranspose, -48); SetMax(pTranspose, 48);
   SetFormat(pTranspose, '%.0f'); SetSymbol(pTranspose, 'sm');

   pOctFoldback := CreateParam('oct foldback', ptSwitch); SetIsOutput(pOctFoldback, FALSE);

   pKeep := CreateParam('keep input', ptSwitch); SetIsOutput(pKeep, FALSE);

   SetArrayLength(sentNoteOns, BUFFER_SIZE);

   numSentNoteOns := 0;
   numOut1 := 0;
END; // Init

/////////////////////////////////  Checking channel message types  /////////////////////////////////

FUNCTION IsNote (currMsg : tMidi) : Boolean;
BEGIN
   IsNote := ((currMsg.msg = NOTEON) OR (currMsg.msg = NOTEOFF));
END;

FUNCTION IsNoteOn (currMsg : tMidi) : Boolean;
BEGIN
   IsNoteOn := ((currMsg.msg = NOTEON) AND (currMsg.data2 > 0));
END;

FUNCTION IsNoteOff (currMsg : tMidi) : Boolean;
BEGIN
   IsNoteOff := (currMsg.msg = NOTEOFF) OR ((currMsg.msg = NOTEON) AND (currMsg.data2 = 0));
END;

FUNCTION IsKA (currMsg : tMidi) : Boolean;
BEGIN
   IsKA := (currMsg.msg = KA);
END;

////////////////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE mTrace(s : String; m : tMidi);
BEGIN
   WITH m DO 
      WriteLn(s + ': msg:' + IntToStr(msg) 
                + ' ch:' + IntToStr(channel) 
                + ' d1:' + IntToStr(data1) 
                + ' d2:' + IntToStr(data2));
END; // mTrace

////////////////////////////////////////////////////////////////////////////////////////////////////

FUNCTION WithinRange(value : Byte) : Boolean;
   VAR retVal : Boolean;
BEGIN
   retVal := FALSE;
   IF &#40;&#40;value >= trspLoLim&#41; AND &#40;value <= trspHiLim&#41;&#41; THEN BEGIN
      retVal &#58;= TRUE;
   END
   ELSE IF &#40;value = trspLoLim&#41; OR &#40;value = trspHiLim&#41; THEN BEGIN
      retVal &#58;= TRUE;
   END
   ELSE IF &#40;trspLoLim > trspHiLim&#41; THEN BEGIN
      retVal &#58;= &#40;&#40;value < trspHiLim&#41; OR &#40;value > trspLoLim&#41;&#41;;
   END;
   WithinRange &#58;= retVal;
END; // WithinRange

////////////////////////////////////////////////////////////////////////////////////////////////////

FUNCTION CalcTranspose&#40;NoteNo &#58; Byte&#41; &#58; Byte;
   VAR note &#58; Integer; 
BEGIN
   note &#58;= Integer&#40;NoteNo&#41; + transpose;
   IF &#40;note < 0&#41; THEN BEGIN
      IF &#40;NOT octFoldback&#41; THEN BEGIN
         note &#58;= 0;
      END
      ELSE BEGIN
         note &#58;= abs&#40;12 + note&#41; MOD 12;
      END;
   END
   ELSE IF &#40;note > 127&#41; THEN BEGIN
      IF &#40;NOT octFoldback&#41; THEN BEGIN
         note &#58;= 127;
      END
      ELSE BEGIN
        note &#58;= &#40;&#40;note - 128&#41; MOD 12&#41; + 116;
      END;
   END;
   CalcTranspose &#58;= Byte&#40;note&#41;;
END; // CalcTranspose

////////////////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE CreateOut&#40;currMsg &#58; tMidi&#41;;
BEGIN
   SetMidiArrayValue&#40;pMidiOut1, numOut1, currMsg&#41;;
   numOut1 &#58;= numOut1 + 1;
END; // CreateOut

////////////////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE CreateNoteOnOut&#40;origMsg, outMsg &#58; tMidi&#41;;
   VAR curr &#58; tNoteOns;
BEGIN
   WITH curr DO BEGIN
      origChannel &#58;= origMsg.channel;
      origNoteNo  &#58;= origMsg.data1;
      outChannel  &#58;= outMsg.channel;
      outNoteNo   &#58;= outMsg.data1;
      hasKA       &#58;= FALSE;
   END;
   sentNoteOns&#91;numSentNoteOns&#93; &#58;= curr;
   numSentNoteOns &#58;= numSentNoteOns + 1;
   CreateOut&#40;outMsg&#41;;
END; // CreateNoteOnOut

///////////////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE CheckSendNoteOff&#40;currMsg &#58; tMidi&#41;;
   VAR i, j &#58; Integer;
   VAR outMsg &#58; tMidi;
   VAR curr &#58; tNoteOns;
   VAR found &#58; Boolean;
BEGIN
   j &#58;= 0;
   found &#58;= FALSE;
   FOR i &#58;= 0 TO &#40;numSentNoteOns - 1&#41; DO BEGIN
      curr &#58;= sentNoteOns&#91;i&#93;;
      IF &#40;&#40;currMsg.channel = curr.origChannel&#41; AND &#40;currMsg.data1 = curr.origNoteNo&#41;&#41; THEN BEGIN
         outMsg.msg     &#58;= currMsg.msg;
         outMsg.channel &#58;= curr.outChannel;
         outMsg.data1   &#58;= curr.outNoteNo;
         outMsg.data2   &#58;= currMsg.data2;
         CreateOut&#40;outMsg&#41;;
         IF &#40;curr.hasKA&#41; THEN BEGIN
            outMsg.msg  &#58;= KA;
            outMsg.data2 &#58;= 0;
            CreateOut&#40;outMsg&#41;; // Resets any key aftertouch <> 0
         END;
         j &#58;= j + 1;
         found &#58;= TRUE;
      END
      ELSE BEGIN
         sentNoteOns&#91;i - j&#93; &#58;= curr;
      END;
   END;
   numSentNoteOns &#58;= numSentNoteOns - j;
   IF &#40;NOT found&#41; THEN CreateOut&#40;currMsg&#41;;
END; // CheckSendNoteOff

////////////////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE CheckSendKA&#40;currMsg &#58; tMidi&#41;;
   VAR i &#58; Integer;
   VAR outMsg &#58; tMidi;
   VAR curr &#58; tNoteOns;
BEGIN
   FOR i &#58;= 0 TO &#40;numSentNoteOns - 1&#41; DO BEGIN
      curr &#58;= sentNoteOns&#91;i&#93;;
      IF &#40;&#40;currMsg.channel = curr.origChannel&#41; AND &#40;currMsg.data1 = curr.origNoteNo&#41;&#41; THEN BEGIN
         outMsg.msg     &#58;= currMsg.msg;
         outMsg.channel &#58;= curr.outChannel;
         outMsg.data1   &#58;= curr.outNoteNo;
         outMsg.data2   &#58;= currMsg.data2;
         sentNoteOns&#91;i&#93;.hasKA &#58;= TRUE;
         CreateOut&#40;outMsg&#41;;
      END;
   END;
END; // CheckSendKA

////////////////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE CheckInput&#40;currMsg &#58; tMidi&#41;;
   VAR newMsg &#58; tMidi;
BEGIN
   IF &#40;bypass AND IsNoteOn&#40;currMsg&#41;&#41; THEN BEGIN
      CreateNoteOnOut&#40;currMsg, currMsg&#41;;
   END
   ELSE IF &#40;    &#40;NOT bypass&#41; 
            AND IsNoteOn&#40;currMsg&#41;
            AND &#40;&#40;chIn = 0&#41; OR &#40;chIn = currMsg.channel&#41;&#41;
            AND WithinRange&#40;currMsg.data1&#41;&#41; THEN BEGIN
      newMsg &#58;= currMsg;
      IF &#40;transpose <> 0&#41; THEN 
         newMsg.data1 &#58;= CalcTranspose&#40;newMsg.data1&#41;;
      IF &#40;chOut <> 0&#41; THEN 
         newMsg.channel &#58;= chOut;
      IF &#40;keepOrig AND &#40;&#40;currMsg.channel <> newMsg.channel&#41; OR &#40;currMsg.data1 <> newMsg.data1&#41;&#41;&#41; THEN
         CreateNoteOnOut&#40;currMsg, currMsg&#41;;
      CreateNoteOnOut&#40;currMsg, newMsg&#41;;
   END
   ELSE IF &#40;IsNoteOff&#40;currMsg&#41;&#41; THEN BEGIN
      CheckSendNoteOff&#40;currMsg&#41;;
   END
   ELSE IF &#40;inclKA AND IsKA&#40;currMsg&#41;&#41; THEN BEGIN
      CheckSendKA&#40;currMsg&#41;;
   END
   ELSE BEGIN
      CreateOut&#40;currMsg&#41;;
   END;
END; // CheckInput

////////////////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE Callback&#40;n &#58; Integer&#41;;
BEGIN
   //strace&#40;'callback&#58; ' + IntToStr&#40;n&#41;&#41;;
   CASE n OF
      pMidiIn       &#58; numIn       &#58;= GetLength&#40;n&#41;;
      pBypass       &#58; bypass      &#58;= &#40;GetValue&#40;n&#41; > 0&#41;;
      pChIn         &#58; chIn        &#58;= Byte&#40;trunc&#40;GetValue&#40;n&#41;&#41;&#41;;
      pTrspLoLim    &#58; trspLoLim   &#58;= Byte&#40;trunc&#40;GetValue&#40;n&#41;&#41;&#41;;
      pTrspHiLim    &#58; trspHiLim   &#58;= Byte&#40;trunc&#40;GetValue&#40;n&#41;&#41;&#41;;
      pInclKA       &#58; inclKA      &#58;= &#40;GetValue&#40;n&#41; > 0&#41;;
      pChOut        &#58; chOut       &#58;= Byte&#40;trunc&#40;GetValue&#40;n&#41;&#41;&#41;;
      pTranspose    &#58; transpose   &#58;= round&#40;GetValue&#40;n&#41;&#41;;
      pOctFoldback  &#58; octFoldback &#58;= &#40;GetValue&#40;n&#41; > 0&#41;;
      pKeep         &#58; keepOrig    &#58;= &#40;GetValue&#40;n&#41; > 0&#41;;
   END;
END; // Callback

////////////////////////////////////////////////////////////////////////////////////////////////////

PROCEDURE Process;
   VAR i &#58; Integer;
   VAR currMsg &#58; tMidi;
BEGIN
   FOR i &#58;= 0 TO &#40;numIn - 1&#41; DO BEGIN
      GetMidiArrayValue&#40;pMidiIn, i, currMsg&#41;;
      CheckInput&#40;currMsg&#41;;
   END;
   SetLength&#40;pMidiOut1, numOut1&#41;;
   numOut1 &#58;= 0;
END; // Process
(Hope it's the right version - I'm away from home and haven't got time to check)
Bjørn S

Post Reply

Who is online

Users browsing this forum: No registered users and 14 guests