# Note off and Stuck notes



## d.healey (Oct 7, 2012)

Hello, 

I'm writing a mono legato script and it's nearly there but I have a problem with some notes turning off when I don't want them to and the keys on Kontakt's keyboard staying down. I'm pretty sure the problem is caused by the release trigger.

I've disabled Kontakt's automatic control of release triggers and I've put this code in to test


```
on init
    declare KeyDown
end on

on release

    KeyDown := 0
    for i := 0 to 127
        if KEY_DOWN[i] = 1
            KeyDown := 1
        end if
    end for
    
    if KeyDown = 0 {If no keys are down}
        note_off(ALL_EVENTS)
    end if
end on
```

I must be misunderstanding something with the release triggers because I don't know how I can have stuck notes if I turn all notes off.

Any help greatly appreciated!


----------



## Raptor4 (Oct 7, 2012)

> I'm writing a mono legato script


If I see that correctly you are trying to write some kind of script which is similar to the Kontakt Factory->Performance->Midi Latch one with "Mono" button enabled behavior?
Note: you can stop latching via toggling the same playing note key# or via the Panic button in that factory script.
Another scenario is the "True Midi Latch" which can be combined with a Mono button as well and a "Clutch key KS". You can find more info about that and complex free scrips in this forum link here.
Regards,
R4


----------



## d.healey (Oct 7, 2012)

Hi Raptor, 

I'm not sure a MIDI latch is what I'm looking for. When I press a key my script plays a note, then if I play another while the first is held it plays another and fades between them (it does some other stuff too) - this all works great. However if a third note is played during the fade, the fade is cancelled and the oldest note is stopped, then a new note is played. Sometimes though the new note gets cut off and I can't work out why :(


----------



## Raptor4 (Oct 7, 2012)

> When I press a key my script plays a note, then if I play another while the first is held it plays another and fades between them (it does some other stuff too) - this all works great. However if a third note is played during the fade, the fade is cancelled and the oldest note is stopped


Hi dude,
You had to start your OP with that description. If I got that correctly here is my example code below (the Mono button must be ON).

```
on init
  make_perfview
  declare ui_button $Mono
  set_text($Mono,"Mono Xfade")
  declare $last_id
  declare const $fade_time := 500000
  make_persistent($Mono)
end on

on note
  if ($Mono=1)
    fade_in($EVENT_ID,$fade_time)
    fade_out($last_id,$fade_time,1)
  end if
  $last_id := $EVENT_ID
end on
```

The X fade time is set to 0.5sec but you can re-program that as you want.
Regards,
R4


----------



## d.healey (Oct 7, 2012)

Hi, 

Thanks Raptor. I made a test instrument to try out your code, which worked very well  However it isn't the solution for my current project unfortunatly.

It's a tricky one because the code is far to complex to post here and I can't really simplify it. Oh what the heck, I'll post it up and see if it helps... This probably won't make sense to anybody but me


```
function ScriptedLegato(CCV,ArrayNoteID,ArrayNote0db,NoteNum,LastNote,FadeTime,Offset,Microtune,Tuning)
    
    declare i {counter}
    declare ii {counter}
    declare TempNoteID[128] {Temp array to store note ID's}
    declare Temp0db[128] {Temp array to store note volume references}
    declare XFadeFlag
    declare NumLoops
    declare WaitTime {Wait delay} 

    for i := 0 to NumLoops   
        
        if XFadeFlag = 0
            PlayNoteLayer(NoteNum,TempNoteID,Temp0db,Offset,Microtune+Tuning,num_elements(ArrayNoteID)-1,0) {Generate new note}
            WaitTime := FadeTime/127 {Wait time is 1/127 of FadeTime because we have 127 steps in our crossfade}
            NumLoops := 0
        else
            WaitTime := 1 {Make the delay time really short, that way the crossfade will end almost instantly}
            NumLoops := 1
        end if  

        XFadeFlag := 1
        for ii := 0 to 127
            NoteArrayMultiCrossfade(CCV,ii,ArrayNoteID,TempNoteID,ArrayNote0db,Temp0db,num_elements(ArrayNoteID)-1)   
            wait(WaitTime)
        end for  
        
        if XFadeFlag = 1
            NoteArrayOff(ArrayNoteID){Kill Old Note}
            CopyArray(TempNoteID,ArrayNoteID) {Save the current note as the new old note}
            CopyArray(Temp0db,ArrayNote0db) {And save it's volume reference}
        end if       
        
        XFadeFlag := 0
    end for
 
end function
```

There's some tuning stuff which I've removed from this code. My script uses arrays of Note ID's and uses some custom functions that handle these arrays which allows me to treat them almost like individual variables. So PlayNoteLayer() plays several notes and puts there ids in an array. 

And all I've got in the release callback now is 


```
if search(KEY_DOWN,1) = -1 {All keys are up}
        note_off(ALL_EVENTS)
    end if
```

and in on note


```
on note
 ignore_event(EVENT_ID)
    
    LastNote := CurrentNote
    CurrentNote := EVENT_NOTE

    if PLAYED_VOICES_INST = 0 or LastNote = CurrentNote {If there are no notes already playing}
        PlayNoteLayer(EVENT_NOTE,NoteID,Note0db,0,0,4,0) {Play a note array}
        Crossfade(0,4,NoteID,Note0db)
    else  
        ScriptedLegato(ModWheel,NoteID,Note0db,CurrentNote,LastNote,MsToUs(500),0,0,15)
    end if 

end on
```

I just want to know why notes keep hanging even though I turn them all off - or atleast I think I do.


----------



## Raptor4 (Oct 7, 2012)

> Thanks Raptor. I made a test instrument to try out your code, which worked very well Smile However it isn't the solution for my current project unfortunatly.


Heh, known issue - for instance I had developed a great midi tool in the last two weeks which works quite well as a stand alone and now it is so difficult to embed it in my main project cause it destroys too many existing things :shock: 
So, I need time for my own developments and have to think how to fix the main code to that tool. To my opinion you have to do the same. Follow my main example ant try to fix it to your main code or vice versa.
Regards


----------



## d.healey (Oct 9, 2012)

Hmm, I'm still not having any luck with this. I've simplified my code a lot now; taken out the complex note arrays. I think I must be misunderstanding how the release callback works so if anyone can enlighten me that would be great.

This is my RCB, how is it possible to have stuck notes when no keys are pressed? shouldn't this callback turn off all the notes and reset the keys on Kontakt's keyboard?


```
on release

    if search(KEY_DOWN,1) = -1 {All keys are up} 
        note_off(ALL_EVENTS) 
    end if   

end on
```
Thanks


----------



## Big Bob (Oct 9, 2012)

Hi TC

One known mechanism for stuck keys (on Kontakt's keyboard display), is using the change_note command. Since you haven't shown any of your NCB stuff, I don't know if you are using change_note or not.

In any case, that stuck key problem only applies to visually stuck keys and not to actual stuck notes that are sounding. If that is what you have, then your problem is most likely related to one or more 'race conditions' with blip notes. Blip notes are notes that don't persist long enough for Kontakt's packet handling to pick them up and properly process them.

I do know that trying to achieve a bullet-proof mono mode behavior is very tricky. The acid test is to feed it MIDI data created by brushing your fingers rapidly across the keyboard up and down. This often exposes weaknesses in the scripting logic because it usually creates a few MIDI on/off pairs of very short duration.

Without actually seeing your code for the NCB and taking the time to study it, it's difficult to pin point what you can do to fix your problem. Why don't you post your code and maybe someone can find the time to go over it.

In the meantime, if you haven't seen the following Note/Release Study I did a long time ago, you may find it worth while reading. The data was taken with K2 but as far as I know it is still applicable to K4/5 also.

https://dl.dropbox.com/u/80404485/Kontakt/Docs/Studies/NoteReleaseStudy.pdf (https://dl.dropbox.com/u/80404485/Konta ... eStudy.pdf)

Actually, far from being simple and straight forward, Kontakt's handling of note-on/off events can be quite complex and full of twists and turns and heavily dependent on the context in which these events occur (as you will see when you read the above document). 

Rejoice,

Bob


----------



## MozillaUser (Oct 9, 2012)

I used to have similar problems, which I was able to cure by using a simple and peasant method. This method is not my invention, it's rather a mimmic of the "enter critical section" or "exit critical section" from Windows's API.
It implies creating two "flag-variables" - one named, say, "$busy_in_legato", which is set at the beginning of the legato-phase and reset at the end of it; second flag, called, say, "$busy_in_release", set in the beginning of the release phase and reset after the completion of it. Both flags are "monophonic", any callback can access, read and modify them. 
Now: the note callback must - before starting the decisive plase (legato-by-means-of-fade-out/fade-in or any other time-consuming operations like glissando or whatever) - must verify if BOTH these variables are reseted ("am I safe to proceed?"); no other gliss is in the critical phase, no other release is in the critical phase - and if not, wait in a loop until they both become inactive. Then - and only then - the note cb sets its own "$busy_in_legato" flag (forbidding any other callback to interfere from now on), does its own critical things, and resets it after.
The release cb has to do the same.
Both the the note cb and the release cb must inspect BOTH variables. Any attempt to inspect just one of them, although drasticaly minimizing the spurious artifacts, was not able to cure them totally.
My 2 pence,
Mozil


----------



## MozillaUser (Oct 9, 2012)

deleted by me


----------



## d.healey (Oct 9, 2012)

Thanks for all the replies.

I had a go with the double flag idea but it didn't seem to solve my problem, maybe I implemented it incorrectly.

As Bob suggested I shall post my code here. The main problem I have is tracking the cause of the stuck notes and of repeating the stuck notes, I can play the same thing and get different results, very frustrating. 



```
import "KSPMathV105.txt" {Maths library}
    import "Unit Conversion Library v1.0.txt" {Unit Conversions}

on init
        declare i
    
        family CurrentNote
            declare ID
            declare Num
            declare Vel
            declare 0db
        end family
end on

on note
  ignore_event(EVENT_ID)

  CurrentNote.Num := EVENT_NOTE
  
    if PLAYED_VOICES_INST = 0
        CurrentNote.ID := play_note(CurrentNote.Num,1,0,0)
    else
        ScriptedLegato(CurrentNote.ID,CurrentNote.Num,MsToUs(1000))
    end if 
end on

on release
    if search(KEY_DOWN,1) = -1 {All keys are up}
        note_off(ALL_EVENTS)
    end if  
end on

function ScriptedLegato(NoteID,NoteNum,FadeTime)
    declare i
    declare ii
    declare NumLoops
    declare Flag
    declare WaitTime
    declare TempID[2]
    declare Temp0db[2] 

    for i := 0 to NumLoops
            
        if Flag = 1 {If a crossfade is in progress}
            WaitTime := 1
            NumLoops := 1
        else
             TempID[0] := NoteID {Put old note ID into array for crossfading}
             TempID[1] := play_note(NoteNum,1,0,0) {Play a new note}
             Temp0db[0] := 0 {Reset volume references}
             Temp0db[1] := 0
             WaitTime := FadeTime/127
             NumLoops := 0
        end if
        
        Flag := 1 {We are performing a crossfade}
        for ii := 0 to 127
            Crossfade(ii,TempID,Temp0db) {Crossfade old and new note}                        
            wait(WaitTime)
        end for
        
        if Flag = 1
            note_off(TempID[0])
            NoteID := TempID[1]
        end if
        
        Flag := 0 {No longer crossfading}       
    end for
end function

function Crossfade(Angle,XID,0db)
{This function performs equal power crossfades on two notes.
Angle -     The angle at which we are currently fading, this could be a CC value
XID[2] -    1D array, two note ID's to crossfade
0db[2] -    1D array volume references for notes to crossfade}

    declare i       {General Loop Index}
    declare Atn[2]  { Attenuation from 0 db reference for notes 0 and 1 }
    declare Vol[2]  { Volume levels for Notes 0 and 1 }
    declare XFV     {For storing the Xfade value}
        
    XFV := Ang90*Angle/127  {Map ModWheel from 0-1000}
    SinCos(XFV,Vol[1],Vol[0]) {Get XFade volume levels}                   

    for i := 0 to 1
        Get_db(Vol[i],Atn[i])               {Convert XFade volume levels to mdb}
        Vol[i] := Atn[i]                    {Add Master Volume to both notes}
        change_vol(XID[i],0db[i]+Vol[i],1)  {Set new volume of note[i]}  
        0db[i] := -Vol[i]                   {Update db accums to get back to 0 db}  
    end for
end function
```

Mostly standard stuff except the MsToUs() function which is just a simple unit conversion function. I'm using an equal power crossfade function rather than the more simple fade in/out functions because I have other things I want to add to the fade later on.

If anyone has the time to have a look at this and help me out that's wonderful, also hopefully other people will find some of this code useful in their own programs.

Bob:
I'm having a read through that document; very insightful stuff, hopefully I can find a solution in there, although the more I look into this the less I think the problem is with the RCB and more with my Legato function.


----------



## Big Bob (Oct 9, 2012)

Hey David,

What does the library function *MsToUs*(1000) return as a value? At least I guess that MsToUs is a one-line return value function?


----------



## d.healey (Oct 10, 2012)

```
function MsToUs(Ms) -> result
    result := Ms * 1000
end function
```


----------



## Big Bob (Oct 10, 2012)

Hi David,

I took a quick look at your posted script logic and I see several problems with it. However, before I comment, I would like you to clarify some of your reported issues as collected below. Henceforth let's refer to these by number as a 'short hand' convenience.

#1


> but I have a problem with some notes turning off when I don't want them to and the keys on Kontakt's keyboard staying down.



#2


> However if a third note is played during the fade, the fade is cancelled and the oldest note is stopped, then a new note is played. Sometimes though the new note gets cut off and I can't work out why



#3


> I just want to know why notes keep hanging even though I turn them all off - or atleast I think I do.



#4


> This is my RCB, how is it possible to have stuck notes when no keys are pressed? shouldn't this callback turn off all the notes and reset the keys on Kontakt's keyboard?



#5


> The main problem I have is tracking the cause of the stuck notes and of repeating the stuck notes, I can play the same thing and get different results, very frustrating.



It might be helpful if you would exemplify what kind of runs you are playing when these various problems occur. Surely all the above problems don't occur for a single played phrase do they?

Here are some questions. When a note audibly 'sticks' after all keys are lifted, does the keyboard display always show one or more keys still down? If so, that might explain why your RCB doesn't execute the note_off(ALL_EVENTS) because the *if* condition may not be satisfied. Alternatively, even if no display keys are still down when you have an audibly stuck note, perhaps that note occured after the last RCB was triggered and it was never retriggered. 

To zero in on this latter mechanism, you might want to insert a button callback that unconditionally executes note_off(ALL_EVENTS). Then when you get a stuck note, click the button and see if it kills the note. If it does, then one or more RCB triggers didn't occur (which is quite possible).

I could ask about a dozen similar questions but my time is rather limited. So, I think you should first be more specific with a case by case description of which of the 5 numbered issues above occurs under what conditions of played notes, etc.

I can tell you that when you play a 3rd note while the two prior notes are still crossfading, you have a timing 'race' condition when ScriptedLegato re-enters itself. This directly or indirectly is no doubt responsible for the somewhat unpredictable behavior when the same run is played several times with differing results. There will be times when note 2 will exit the NCB before note 3 and vice versa.


----------



## d.healey (Oct 10, 2012)

Hi Bob, 

Thanks again for taking the time to help me with KSP.

I can run my hand up and down the keys, and mash the keyboard and everythings fine. But when I try and play a melody, various pieces, I get stuck notes. For testing I am playing one particular short melody and using violin samples.

#1 Keys stick, sometimes the note continues to sound, othertimes it is cutoff. 

#2 Since simplifying my code this problem has gone and cutoff notes happen regardless of if it's the third note.

#3 I created a button like you suggested, when I click it it does turn off any still sounding notes - however the keyboard key stays depressed.

#4 Still puzzled about this, what tells Kontakts keyboard to lift up the keys?

#5 I'm going to try and create a MIDI file that will produce the effect everytime, that way I should be able to track the cause more easily.

Other notes:
The frequency of the problem doesn't seem to change if I increase or decrease the wait time.

Since the button did turn off the notes that would, as you suggested, indicate that the RCB trigger isn't occuring. So this means it's either the Key Up or the note_off call - I reckon it must be the note off call in the legato function that is getting missed? But why does the key stay down on the keyboard? and what about when the key sticks but no note sounds?

I'll keep going at this, and see how I get on with making a MIDI file that allows me to repeat the error.

Thanks again!


----------



## d.healey (Oct 10, 2012)

Ok I seem to be getting somewhere now, I've created a MIDI file that allows me to repeat the problem, it seems if I play two legato notes followed quickly by a non legato note, or a non-legato followed by two legato notes, the problem occurs. So now I think I will be able to solve this. I think it's in the note callbacks main if statement.

I shall let you know how I get on


----------



## Big Bob (Oct 10, 2012)

Hi David,

Definately the right approach to try to create a MIDI file that induces the problem reliably. The worst thing about these kinds of problems is that when you think you've found a fix, you never quite know for sure because it could have just decided to work for a while (but often shows up again later) :lol: 

Just in case your problem ends up being related to the 2nd and 3rd note threads running together in the xfade while loop (along with the uncertainty of which will exit first), you might want to consider rearranging your logic something like this:

*function* ScriptedLegato(NoteID,NoteNum,FadeTime) 
````*declare* i 
````*declare* ii 
````*declare* NumLoops 
````*declare* Flag 
````*declare* WaitTime 
````*declare* TempID[2] 
````*declare* Temp0db[2] 

````*while* Flag = 1```_{If a crossfade is in progress}_ 
``````WaitTime := 1``_{ quickly finish prior xfade }_
``````wait(1)
````*end while* 
````TempID[0] := NoteID _{Put old note ID into array for crossfading}_ 
````TempID[1] := play_note(NoteNum,1,0,0) _{Play a new note}_ 
````Temp0db[0] := 0 _{Reset volume references}_ 
````Temp0db[1] := 0 
````WaitTime := FadeTime/127 
````Flag := 1 _{We are performing a crossfade}_ 
````*for* ii := 0 *to* 127 
``````Crossfade(ii,TempID,Temp0db) _{Crossfade old and new note}_````````````````````````
``````wait(WaitTime) 
````*end* *for* 
````*if* Flag = 1 
``````note_off(TempID[0]) 
``````NoteID := TempID[1] 
````*end* *if* 
````Flag := 0 _{No longer crossfading}_````````
*end* *function* 

This way, when a third note comes along while the xfade is still running, you just hold while you quickly finish the prior xfade before starting the note 2 to 3 xfade.

Of course maybe you have some reason that you need to do it your way to accomodate 'other' things.

In any case, I'm glad you are 'hot on the trail' and I wish you the best.

Rejoice,

Bob


----------



## d.healey (Oct 10, 2012)

Ah, that's a good idea Bob! I'm ironing out the problem. I now have no stuck keys! but I still have hanging notes - I know the cause now so I just have to find a solution.

Basically because there is a gap between the notes playing this is triggering the RCB and because the gap is so short this is turning of the next note too. So I put another flag in as mozilla user suggested and this has greatly helped! 

Many thanks once more!


----------



## d.healey (Oct 11, 2012)

Yippie I finally got this working!!! As so often happens the solution turned out to be simple but took me the best part of a day to find :evil: but at least I'll never have to do it again lol

In case anyone else wants to use this code here are the two adjustments that were required.


```
on release     
    if KEY_DOWN[CurrentNote.Num] = 0 
       note_off(CurrentNote.ID)
    end if  
end on
```

and in the ScriptedLegato Function I changed this part like so


```
for i := 0 to 127
        if KEY_DOWN[NoteNum] = 0 {If the key that triggered the function is lifted}
            note_off(TempID[0]) {Turn off playing notes}
            note_off(TempID[1]) {Turn off playing notes}
            i := 127 {Exit the XFade}
        end if
        Crossfade(i,TempID,Temp0db) {Crossfade old and new note}  
        PitchFade(TempID,Tuning,i/2)                      
        wait(WaitTime)
    end for
```

Thanks once more everyone, especially Bob - you have changed the world for the better with all the KSP work you have done, NI should build a statue of you and one of Nils


----------

