A technical support community for Apple Logic Pro users.

 
User avatar
Dewdman42
Topic Author
Posts: 3093
Joined: Tue Sep 09, 2014 3:01 pm
Location: Salt Lake City, UT

Kontakt Multi-Port

Mon Aug 31, 2020 2:11 pm

I have worked out a way to use up to 64 midi channels into a single instance of Kontakt, using ports A,B,C,D, which normally are only accessible through the standalone version, but this tutorial and supplied templates will let you access them in the plugin version.

Here are two LogicPro projects provided, one is an empty template, and the other is a working example using the factory kontakt instruments as an example case using 28 midi channels into a single Kontakt instance.

Template:
KontaktMulti64.zip
(1.18 MiB) Downloaded 49 times

Example Project:
KontaktMulti64Example.logicx.zip
(2.74 MiB) Downloaded 45 times


Truthfully, its probably a better idea in LogicPro to spread the cpu load to multiple kontakt instances in most cases, but I offer this solution for anyone seeking anything similar or to build upon further. One case where this kind of thing can make a lot of sense is when working with an instrument that has more than 16 articulations as separate kontakt instruments.

Working Example

In this case Kontakt is loaded with 28 instruments in slots A and B. Here is a screenshot of the B slot with 14 channels B1->B14 using some instruments. Slot A also has 14 instruments.

kontakt.jpg
kontakt.jpg (358.12 KiB) Viewed 2126 times


Here is the arrange page showing the tracks.. Clicking on any track will find midi routed to the right instrument in kontakt, including in the B slot...

tracklist.jpg
tracklist.jpg (225.84 KiB) Viewed 2126 times


The mixer can accommodate up to 25 stereo returns, so in this example I have 28 instruments (a few are consolidated in the audio returns) and their separate returns are giving me all the mixer channels for each one:

mixer.jpg
mixer.jpg (673.19 KiB) Viewed 2126 times


How it works

This is enabled by two special tricks. One is that I am using the old VePro6 multiport macro to insert CC99 events in front of all events coming from each track.
env.jpg
env.jpg (138.16 KiB) Viewed 2126 times


In addition to that, a special Scripter script is being used on the kontakt channel. This script translates the CC99 events into ProgramChange events containing the port number. It also handles a few other housekeeping tasks related to ALL NOTES OFF that were part of the old VePro multiport macro templates. This script is already included in the above project templates. (Note, it might be possible to modify the environment multiport macro to generate PC messages directly instead of CC99 and then avoid needing this special script, but the ALL NOTES OFF handling is still somewhat needed. I just reused the old multiport macros as is for now.)


//=============================================================
// Handle NoteOff, like we had to do with CC99 stuff in VePro
// but also convert CC99 to PC messages 1-4
// don't index port value by zero.
//=============================================================


var NeedsTimingInfo = true; //only needed to detect play state

var TRACE = false;
var TRACE99 = false;
var MAXFLUSH = 20;

// array to track the last received port for any given channel
var lastPort = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
var tempLastPort = 0;
var maxPorts = 0;

var pc = new ProgramChange;

//=============================================================
// Actual HandleMIDI function
//=============================================================

function HandleMIDI(event) {
   
    // keep track of the current port for each channel
    // don't forward on the CC99 event
    if(event instanceof ControlChange && event.number == 99) {
   
        //if( event.value+1 != lastPort[event.channel]) {
        if( event.value+1 != tempLastPort) {
           
            tempLastPort = event.value+1;
            lastPort[event.channel] = tempLastPort;
             
            // convert to PC by number       
            pc.number = event.value+1;
            pc.channel = event.channel;
            pc.port = event.value+1;
            pc.send();
            if(TRACE99) {
                logEvent(event);
            }               
        }
        return;       

    }
    else {
        event.port = lastPort[event.channel];
        if(event instanceof NoteOn && event.port > maxPorts) {
            maxPorts = event.port;
        }
    }
   
    //================================================
    // If all Notes Off for channel, handle specially,
    // forward to all ports
    //================================================
   
    if (event instanceof ControlChange
            && event.number == 123
            && event.value == 0) {

        AllNotesOffByChannel(event.channel);
        return;
    }

    //==============================================
    // INSERT code here for sending keyswitches, etc
    // when inserting keyswitches, send the port attr
    // and call SendByPort(event).  For example:
    //     var keyswitch = new NoteOn;
    //     keyswitch.port = lastPort[event.channel];
    //     SendByPort(keyswitch);
    //==============================================

    // Send actual event
    event.send();
    logEvent(event);

}


//=========================================
// send VEP multi port encoder in front
// of actual event
//=========================================


function SendByPort(event) {
   
    if(event.port != undefined && event.port > 0) {
        pc.channel = event.channel;
        pc.number = event.port;
        pc.send();
        if(TRACE99) {
            logEvent(cc99);
        }
    }

    event.send();
    logEvent(event);
}


//===================================================
// Prcoess MIDI is used to detact transport STOP
// in order to send AllNotesOff to all ports/channels
//===================================================

var playState = false;

function ProcessMIDI() {
    var ctx = GetTimingInfo();
   
    if (ctx.playing) {
        playState = true;
    }
   
    else {
        if (playState == true) {
            // stop recently happened
            AllNotesOff();
            playState = false;
        }
    }
}

//====================================
// AllNotesOff all Channels
//====================================

function AllNotesOff() {
    for (var chan = 1; chan <= 16; chan++) {
        AllNotesOffByChannel(chan);
    }
    maxPorts = 0;
}

//=======================================
// Propagate AllNotesOff to all ports
// for a given midi channel
//=======================================

var ccOff = new ControlChange;
ccOff.number = 123;
ccOff.value = 0;
// var note = new NoteOn;

function AllNotesOffByChannel(channel) {
    for (var port = 1; port <= maxPorts; port++) {
        ccOff.channel = channel;
        ccOff.port = port;
        SendByPort(ccOff);
    }
}

//===================================
// Buffered Tracing if turned on
//===================================

var buffer = [];

function logEvent(event) {
    var port = (event.port == undefined || event.port < 1)
                ? "" : (" [port:"+event.port.toString().padStart(2, "0")+"]");

    logMsg(event.toString() + port);
}

function logMsg(str) {
    if(TRACE) {
        buffer.push(str);
    }
}

function logErr(str) {
        buffer.push(str);
}

function Idle() {
    for (var i = 0; i < MAXFLUSH; i++) {
        if (buffer.length > 0) {
            Trace(buffer.shift());
        }
    }
}



In addition to the above, Kontakt is using a special KSP multi-script which translates the ProgramChange messages into appropriate midi routing to ports A,B,C,D. The preloaded Kontakt multi has this KSP script included in it.

on init
    declare $runningPort
    declare $channel
    $runningPort := 1
end on

on midi_in   
    { Detect PC#127, only for BYTE2 values 1-4 }
    if ($MIDI_COMMAND = $MIDI_COMMAND_PROGRAM_CHANGE)
        if( $MIDI_BYTE_1 < 5 )
            $runningPort := $MIDI_BYTE_1
            ignore_midi
            exit
        end if
    end if

    {All other midi events, rechannelize to port based on last running port# }
    $channel := ($runningPort-1) * 16 + $MIDI_CHANNEL
    set_event_par($EVENT_ID,$EVENT_PAR_MIDI_CHANNEL,$channel)
   
end on


That's all there is to say about it really. It works like a charm for the most part. 64 tracks to one kontakt instance.

The general approach can be further enhanced in combination with Articulation Sets to provide, for example, more than 16 articulations in a single kontakt instance.

Hope anyone finds this useful or generates further ideas or discussion. As I said, I think this is somewhat a hypothetical exercise to see if it was possible, in actuality, I would rather run separate instances of Kontakt per instrument in LogicPro in order to spread CPU load around. Generally.
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.15 on OpenCore, Logic Pro 10.5, Mainstage3, Cubase10.5, StudioOne4, Reaper, DP10, VEP7, VSL, too many plugins to list
 
User avatar
Dewdman42
Topic Author
Posts: 3093
Joined: Tue Sep 09, 2014 3:01 pm
Location: Salt Lake City, UT

Re: Kontakt Multi-Port

Mon Aug 31, 2020 2:35 pm

I also made some scripts and templates for handling more than 16 articulations to a large Kontakt multi. This is more interesting actually then the above example, and does not require any environment multi-port macro. I will be sharing more information about that at a future time, but suffice it to say for now that its possible to have up to 64 articulationID's that drive events to up to 64 separate listening midi channels within a single kontakt instance.. Nice. That is definitely interesting to me.

However, I also found that in some ways it might be easier to just send 64 articulationID's to multiple instances of kontakt..which is also possible in LogicPro by using a simple environment splitter or even a track stack and then a small script to filter out which articulationID's are handled by each kontakt instance. That avoids needing any special KSP script and is probably the way to go for this kind of task... but anyway, if someone wants it all in one simple channel..then the above KSP trick is definitely useful for that.
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.15 on OpenCore, Logic Pro 10.5, Mainstage3, Cubase10.5, StudioOne4, Reaper, DP10, VEP7, VSL, too many plugins to list