What's new

Importing MIDI sequences into Kontakt (and playing them back)

Mike Greene

Senior Member
Moderator
I seem to remember people talking about being able to import MIDI sequences into K5. I've been doing a little hunting and the only method I can see for doing this is the factory "Session Recorder" script. Is this indeed what everyone has been referring to? In other words, there isn't some special "Import MIDI file" button hiding somewhere, is there?

Question #2 - I want Kontakt to play back a preprogrammed sequence (drum loop) that I include with my Kontakt instrument, but I'd like to add a feature where you can assure that this drum loop plays in sync with the user's sequencer, even if you don't start the Kontakt drum loop exactly on the beat. (My plan would be that a user hits the "Go" button on my instrument slightly *before* the downbeat.)

I plan to accomplish this by using the "on listener" callback with $NI_SIGNAL_TIMER_BEAT as the relevant parameter. In this callback, there would be an "if loop is triggered" statement, in which case my scrupt would play the first note, then use "wait" to delay an 1/8 note's worth of time to play the second note. Then $NI_SIGNAL_TIMER_BEAT gets triggered again for the next beat by the on listener callback, where the sequence continues for the stuff that occurs during beat 2. And so on.

Does this methodology make sense? Or, as is so often the case, am I missing some much easier way of playing back MIDI sequences in Kontakt?
 

Lindon

VST/AU Developer
OK so loading and playing back midi files is dealt with in the KSP manual in the section "Working with MIDI Files", where I think you will find you can just load a miidi file then detect the start event you want and have a loop that reads thru the midi file playing notes and issuing wait() statements.. abbreviated as {do stuff} below

As to starting/stopping your "play back activities" what I do (and your mileage may vary) is allow the "event" (here = your start to play the midi file) to be triggered either immediately, on the next beat or on the next bar...the user selects a menu option for this.....

so in the init

set_listener($NI_SIGNAL_TRANSP_START,1)
set_listener($NI_SIGNAL_TIMER_BEAT,1)


and in the listener itself:



Code:
{if starting straight away= true}
if ($NI_SIGNAL_TYPE = $NI_SIGNAL_TRANSP_START)
       {do stuff..}      
end if

{if starting on next beat...}
if ($NI_SIGNAL_TYPE = $NI_SIGNAL_TIMER_BEAT)
    {do stuff}
end if

{if starting on next bar start...}
if ($NI_SIGNAL_TYPE = $NI_SIGNAL_TIMER_BEAT)
     $bar_position := $NI_SONG_POSITION/960
     if ($bar_position mod $sig_num = 0)
         {do stuff..}
      end if           
end if
 
OP
Mike Greene

Mike Greene

Senior Member
Moderator
D-oh! I can't believe I didn't see the MIDI section of the KSP manual. I must be going blind, because believe me, I looked! :oops:

Thanks for your example, Lindon. An on listener callback along those lines is basically what I was thinking, so it's good to know I'm headed down the right path. What I didn't know was importing MIDI files could be done as a command. This should be interesting as I decide whether I really want to import MIDI files, or just have stuff preloaded as arrays. (My MIDI sequences are pretty basic.)

In any even, now I know the procedure, so thank you! 8)

Justin, thanks for that link. I remember that from some time ago and had forgotten. It's PC-only, which is a problem for me, but I might fire up my old HP laptop and give it a whirl. I wonder if it's basically the same as the factory MIDI recorder script.
 

MacQ

Senior Member
You might be better off with the arrays option, Mike. It's probably easier, and K5.1 introduces a way to load .NKA files from the browser they introduced with Studio Drummer. Previously you could SEE the files, you just couldn't do anything with them.
 

Sasje

Active Member
I fiddled a bit around with it, and thrown together various MIDI scripts and came up with this:

Download: http://www.mediafire.com/download/8sxu5 ... player.rar (requires K5+)

it _requires_ a midi file folder at: C:/midi/ otherwise the midi menu will not populate with the midi files to choose from. If you don't have windows, you'll need to edit the script to set the correct path.
As an added bonus it also has a pianoroll viewer. It's a bit hacked together, so it's not a stable player. I got pretty bored with it and abandoned it. :)o

To load a midi file, double click a midi file in the list, then click "play".
 
i'm confused about what the command is to actually play the midi file!? i can load it fine.

to start on transport start:



Code:
if ($NI_SIGNAL_TYPE = $NI_SIGNAL_TRANSP_START)
          THEN WHAT??????
 

Lindon

VST/AU Developer
OK, so if you read my initial response above it points you at the correct section of the manual. Once the MIDI file is loaded you can then move thru the file with a set of commands to go to each "event" in the file, find out what type of thing it is and respond as you need.

There is no MIDI player in Kontakt, there is however a set of commands in KSP to load a MIDI file, read/inspect each piece of data in it(including finding out when -in relative terms- these events are "scheduled"), and process them.
 
Hi Lindon,
Thanks for your reply. So, people are not using this "load midi file feature" to play midi files???!!! What are they using it for? I'm very perplexed by this.
 

Lindon

VST/AU Developer
Yes they are using load MIDI file, for exactly the purpose I outlined, you have to load the file then inspect it.
 

olmerk

Member
I fiddled a bit around with it, and thrown together various MIDI scripts and came up with this:

Download: http://www.mediafire.com/download/8sxu5 ... player.rar (requires K5+)

it _requires_ a midi file folder at: C:/midi/ otherwise the midi menu will not populate with the midi files to choose from. If you don't have windows, you'll need to edit the script to set the correct path.
As an added bonus it also has a pianoroll viewer. It's a bit hacked together, so it's not a stable player. I got pretty bored with it and abandoned it. :)o

To load a midi file, double click a midi file in the list, then click "play".
Why in this script there is a delay between the moment I hit "Play_MIDI" button and the first note playback?
Seems like on listener call back checks in equal time intervals if play_midi:=1 and if it is, then it triggers play_note
And if one clicks the button between those checks there is a delay. How to get rid of it?
 

EvilDragon

KSP Wizard
You can't, listener goes at a predefined rate, and if you clicked a button somewhere before the next listener tick, you're gonna have a slight delay until it starts.
 

olmerk

Member
You can't, listener goes at a predefined rate, and if you clicked a button somewhere before the next listener tick, you're gonna have a slight delay until it starts.
So If I want to have a midi player being triggered by a pressed key, I need to move on listener piece of code to on note call back (with some alterations of course), right?

But as I understand correctly, it's exactly the listener itself provides the rhythmical playback of a midi events being read from a midi file...
 
Last edited:

EvilDragon

KSP Wizard
Correct, it's the listener that goes through MIDI ticks. You cannot work around it. You can do it without listener callback and just using waits, but this is imprecise and can drift off in timing pretty easily (wait() in KSP is not absolutely precise down to the microsecond).
 

olmerk

Member
Can I make listener "to listen" like every ms or so? Or will it ruin the playback of a midi-file at a certain tempo?
 

EvilDragon

KSP Wizard
You can, but it's not natural for MIDI, which is based in regular tick intervals (which also ties in to BPM changes). If you run listener in milliseconds, then it's tempo-invariant and you need to handle BPM changes yourself, with listener in ticks, it does this automagically...


Besides, is a delay of less than one 1/32T (fastest listener tick rate currently) really that important? You can always input quantize if you want your playback to end up spot on the grid...
 

olmerk

Member
Besides, is a delay of less than one 1/32T (fastest listener tick rate currently) really that important? You can always input quantize if you want your playback to end up spot on the grid...
Depending on tempo, 1/32T can a noticeable delay....

By the way, as I've plunged in this script I still can't get where from it takes information about absolute time intervals between, say, beats (quarter notes)? If I'm in standalone or transport is not running?

I set the highest resolution with set_listener($NI_SIGNAL_TIMER_BEAT, 24) to decrease the delay and got a super fast playing of midi file.
 

EvilDragon

KSP Wizard
http://vi-control.net/community/threads/anyone-try-scripting-using-the-new-k5-ksp-features.23228/#post-3596492

Here's a different take on MIDI player, you might understand it better since it has more comments.


Changing the listener tick rate shouldn't make the MIDI playback faster. That tells me the script you're using has something weird going on (EDIT: got it - $MIDI_TEMPO should be 960 / $MIDI_BARS, and $MIDI_BARS should be 24 to get the best listener resolution - but in any case, sonaht's script is better, just needs listener being ran at 24 ticks per quarter as well)...
 

olmerk

Member
I’ve checked already 3 versions of MIDI players and their on listener block is pretty much the same from version to version


Below is the excerpts from NI Studio Drummer MIDI player:

Code:
on init
declare const $LISTENER_RESOL := 24 
declare const $LISTENER_BEAT_LENGTH_TICKS := 960 / ($LISTENER_RESOL)
set_listener($NI_SIGNAL_TIMER_BEAT, $LISTENER_RESOL)
end on

on listener
    $cur_listener_id := $NI_CALLBACK_ID
    if ($play = 1)

        if ($NI_TRANSPORT_RUNNING = 0)
            $cur_song_pos := $cur_song_pos + $LISTENER_BEAT_LENGTH_TICKS
        else
            $cur_song_pos := $NI_SONG_POSITION
        end if
 
        $beat_start_time := $ENGINE_UPTIME
        mf_get_next_at(0, $cur_song_pos mod $mf_length)
        $start_of_beat := $cur_song_pos mod $mf_length
        $end_of_beat := $cur_song_pos mod $mf_length + $LISTENER_BEAT_LENGTH_TICKS

        while (mf_get_pos() < $end_of_beat and $play = 1 and $cur_listener_id = $NI_CALLBACK_ID)

            $diff := ticks_to_ms(mf_get_pos() - $start_of_beat) - ($ENGINE_UPTIME - $beat_start_time) * 1000
            if ($diff > 0)
                wait($diff)
            end if

 
            if (mf_get_command() = $MIDI_COMMAND_NOTE_ON and mf_get_byte_two() # 0)
                ...play_note...
            else
                if (mf_get_command() = $MIDI_COMMAND_NOTE_OFF or (mf_get_command() = $MIDI_COMMAND_NOTE_ON and mf_get_byte_two() = 0))
                    ...note_off...
                end if
            end if

            mf_get_next(0)

            if (mf_get_command() = 0)
                if ($end_of_beat > $mf_length)
                    mf_get_first(0)
                    $end_of_beat := $end_of_beat mod $mf_length
                    $start_of_beat := $end_of_beat - $LISTENER_BEAT_LENGTH_TICKS
                else
                    exit
                end if
            end if
        end while
    end if
    $play_flag := 0
end on
Even with the highest resolution $LISTENER_RESOL := 24 there is a significant delay. Moreover the delay stays the same even when I change $LISTENER_RESOL, I think because it appears in $LISTENER_BEAT_LENGTH_TICKS := 960 / ($LISTENER_RESOL)

When I put simply $LISTENER_BEAT_LENGTH_TICKS := 960 the midi playback speeds up tremendously, though “reaction time”, i.e. the delay, gets almost unnoticeable.

So I don’t know how to fix it. And it’s still not clear for me where KSP takes info about absolute time measure for a beat when playing back a midi in standalone version or when transport is not running? I supposed that wait($diff) in the code above was responsible for that. But when I commented out that block nothing happened in the script action. So I still can’t get what this wait($diff) is necessary for?

Would really appreciate a clarification...
 

EvilDragon

KSP Wizard
You cannot fix it (at least AFAIK) - it's inherent to the design of listener callback. It is what it is, that delay is something you can't go without. However I don't think that 1/32T is anywhere near "significant", since that's the worst case scenario if you hit a key RIGHT after a listener tick has happened... As mentioned, if you need your MIDI playback to start right smack on the grid, you need to use input quantize in a script slot before your MIDI playback engine. Sure you'll get a delay, but it will be on grid. Not sure if this is your intention, though.

When playing without transport, it just increments $cur_song_pos variable on every listener tick (listener runs even if transport is off).

As for wait($diff), I think this is for MIDI events that are not exactly happening on the listener tick grid. As you know, Kontakt runs at a maximum of 24 ticks per quarter note (which is 96 ticks per 4/4 bar), but most often found, MIDI files have 960 ticks per bar (so, 10 times more), this is what that part of code caters to, I think.
 
Top Bottom