playNote with -1 ignores key release if onRelease function is present

In my script I use both: playNote with -1 and playNote with certain duration. And I found out that a playNote with -1 ignores physical release of a key if onRelease function is present in the script (even an empty one). So once it’s there must I handle all releases myself with a fade function?

It works ok here. Can you post an example?

This code gives hanging notes:

layers = this.parent:findLayers(true)

played_note_id={}
for i = 0, 127 do
  played_note_id[i] = {}
end

function onRelease(event)

end

function onNote(event)
 played_note_id[event.note][15]= playNote(event.note, event.velocity, -1, layers[15])
 played_note_id[event.note][18]= playNote(event.note, event.velocity, -1, layers[18])
end

However proper release works if I remove either

function onRelease(event)
end

or one of these two lines:

 played_note_id[event.note][15]= playNote(event.note, event.velocity, -1, layers[15])
 played_note_id[event.note][18]= playNote(event.note, event.velocity, -1, layers[18])

This is strange. Just tried it and it works ok here. With or without onRelease.

Are you sure there’s nothing else going on there? You said you also use playNote with duration other than -1.

You don’t have the onNote function twice in the same script, do you?

Ok, after more trials I’ve traced that problem originates in the layer’s structure of my patch. For example, the code above works fine, without hangingn notes, for this structure (no sublayers):

However notes are hung up for the structure below (with sublayers and additional busses):

If you add search function to findLayers?

layers = this.parent:findLayers(true, function(layer) return layer.level == this.level + 2 end)

for i, layer in ipairs(layers) do
	print(layer.name)
end

They code return the following list and some layers are missed:

01_Tonal_src1
02_Tonal_src2
03_Tonal_src3
04_Noise_src1
05_Noise_src2
06_Noise_src3
07_mic_FX_src1
08_mic_FX_src2
09_mic_FX_src3
10_Damper_strings_down_src1
11_Damper_strings_down_src2
12_Damper_strings_down_src3
13_Damper_strings_up_src1
14_Damper_strings_up_src2
15_Damper_strings_up_src3
Release_tonal
Release_noise
Res_Tonal
Res_Noise
28_keys_FX_down_src1
29_keys_FX_down_src2
30_keys_FX_down_src3
31_keys_FX_up_src1
32_keys_FX_up_src2
33_keys_FX_up_src3
34_pedal_foot_FX_down_src1
35_pedal_foot_FX_down_src2
36_pedal_foot_FX_down_src3
37_pedal_foot_FX_up_src1
38_pedal_foot_FX_up_src2
39_pedal_foot_FX_up_src3
40_harmonics_2_up_src1
41_harmonics_2_up_src2
42_harmonics_2_up_src3
43_harmonics_3_up_src1
44_harmonics_3_up_src2
45_harmonics_3_up_src3
46_harmonics_4_up_src1
47_harmonics_4_up_src2
48_harmonics_4_up_src3
49_harmonics_5_up_src1
50_harmonics_5_up_src2
51_harmonics_5_up_src3
52_harmonics_6_up_src1
53_harmonics_6_up_src2
54_harmonics_6_up_src3
55_harmonics_7_up_src1
56_harmonics_7_up_src2
57_harmonics_7_up_src3
58_harmonics_8_up_src1
59_harmonics_8_up_src2
60_harmonics_8_up_src3
61_Res_Tonal_src1
62_Res_Tonal_src2
63_Res_Tonal_src3

The missing layers - are they at the same hierarchy level?

Script module and your Main layer are at the same level. Each nested sublayer adds 1 to the level. So the idea was to only include the sublayers.

If you use findLayers(true) it will return all layers including the parent layers. So layers[1] would probably be your Main layer.

For the reference I used this short script to get layers’ numbers:

layers = this.parent:findLayers(true)
layerNames = {}

for i = 1, #layers do
  layerNames[i] = layers[i].name
print(i .. ", " .. layerNames[i])
end

And the returned list was like this:
1, Main
2, Tonal
3, Noise
4, Mic_FX
5, Damper_down
6, Damper_up
7, Release_General
8, Res_General
9, Keys_FX_down
10, Keys_FX_up
11, Pedal_foot_down
12, Pedal_foot_up
13, Harmonics
14, Res_Tonal_2
15, 01_Tonal_src1
16, 02_Tonal_src2
17, 03_Tonal_src3
18, 04_Noise_src1
19, 05_Noise_src2
20, 06_Noise_src3
21, 07_mic_FX_src1
22, 08_mic_FX_src2
23, 09_mic_FX_src3
24, 10_Damper_strings_down_src1
25, 11_Damper_strings_down_src2
26, 12_Damper_strings_down_src3
27, 13_Damper_strings_up_src1
28, 14_Damper_strings_up_src2
29, 15_Damper_strings_up_src3
30, Release_tonal
31, Release_noise
32, 16_Release_tonal_src1
33, 17_Release_tonal_src2
34, 18_Release_tonal_src3
35, 19_Release_noise_src1
36, 20_Release_niose_src2
37, 21_Release_noise_src3
38, Res_Tonal
39, Res_Noise
40, 22_Res_Tonal_src1
41, 23_Res_Tonal_src2
42, 24_Res_Tonal_src3
43, 25_Res_Noise_src1
44, 26_Res_Noise_src2
45, 27_Res_Noise_src3
46, 28_keys_FX_down_src1
47, 29_keys_FX_down_src2
48, 30_keys_FX_down_src3
49, 31_keys_FX_up_src1
50, 32_keys_FX_up_src2
51, 33_keys_FX_up_src3
52, 34_pedal_foot_FX_down_src1
53, 35_pedal_foot_FX_down_src2
54, 36_pedal_foot_FX_down_src3
55, 37_pedal_foot_FX_up_src1
56, 38_pedal_foot_FX_up_src2
57, 39_pedal_foot_FX_up_src3
58, 40_harmonics_2_up_src1
59, 41_harmonics_2_up_src2
60, 42_harmonics_2_up_src3
61, 43_harmonics_3_up_src1
62, 44_harmonics_3_up_src2
63, 45_harmonics_3_up_src3
64, 46_harmonics_4_up_src1
65, 47_harmonics_4_up_src2
66, 48_harmonics_4_up_src3
67, 49_harmonics_5_up_src1
68, 50_harmonics_5_up_src2
69, 51_harmonics_5_up_src3
70, 52_harmonics_6_up_src1
71, 53_harmonics_6_up_src2
72, 54_harmonics_6_up_src3
73, 55_harmonics_7_up_src1
74, 56_harmonics_7_up_src2
75, 57_harmonics_7_up_src3
76, 58_harmonics_8_up_src1
77, 59_harmonics_8_up_src2
78, 60_harmonics_8_up_src3
79, 61_Res_Tonal_src1
80, 62_Res_Tonal_src2
81, 63_Res_Tonal_src3

And in my initial script (1st message) I would like to play layers #15 and #18, which is 15, 01_Tonal_src1 and 18, 04_Noise_src1. I’m puzzled why notes are hanging?

I see. That looks correct.

I still cannot reproduce the hanging notes. I tried to recreate the same program structure with nested layers as in your picture and it works ok. No hanging notes. I tried the script exactly as you posted it. So this is strange. You don’t have the samples set to oneshot mode, maybe by mistake?

Have checked it. No, all zones have Normal playback mode.

The patch was created with a script, because I have a lot of layers and tons of zones. The only thing comes to my mind is that when I added all zones I noticed that they had Sample End set to 0. So I had to run another script to set Sample End as a really big number, since I didn’t know how to get the actual sample length and relay it to Sample End parameter. Maybe that’s the issue?

You can use AudioFile.open to get information about sample and use that to set sample end. I would use the .length for sample end.

AudioFile - HALion Script - Steinberg Developer Help

But this shouldn’t cause hanging notes.

Ok, I simplified the patch to this structure:
изображение

and then replaced -1 in PlayNote to a certain duration value:

    layers = this.parent:findLayers(true)

    function onRelease(event)
    end

    function onNote(event)
    playNote(event.note, event.velocity, 1000, layers[4])
    playNote(event.note, event.velocity, 1000, layers[7])
    end

Now with this if I release a key sooner than 1 sec I got a hanging voice. If I keep holding the key, release happens after 1 sec.

If I remove either the empty

function onRelease(event)
end 

or remove one of playNote lines, say:

playNote(event.note, event.velocity, 1000, layers[7])

the hanging notes are gone.

When you say you get hanging voice, do you mean it plays indefinitely or it plays for 1 second as specified by duration?

The empty onRelease eats the note off. So it plays until duration has passed. Removing the empty onRelease fixes it, as you say. So does if you post the note off.

function onRelease(e)
    postEvent(e)
end

It is a bit confusing because it behaves differently when duration is -1. In this case it doesn’t matter if empty onRelease is present or not.

I couldn’t reproduce this. It made no difference when I tried. But the onRelease did.

Hanging voice plays until the end of the sample, not 1 sec as it should.

Ok, the postEvent(event) does iliminate the problem.

Could you please explain me more detailed what postEvent exactly means? In documentation it’s said “Function to post the event to the engine”. What is the contextual meaning of “to post”? to send? And what is "the engine? Halion underhood?

Can you upload an example preset?

postEvent means to post (play) the event as is. You can do the same with onNote, onConttoller

function onNote(e)
    postEvent(e)
end

If you used empty onNote then Halion would play no notes. As the script wouldn’t pass (post) any note on messages.

v14-short-2.vstpreset (692.8 KB)

So post means like “send the event through”, bypassing the script?

Yes.

Although it’s not bypassing the script. You can still inspect the event, store the values in variables…

I guess this is what happens when you don’t use onRelease, it just sends the event through.

Thanks for the example preset. It really behaves as you describe. If I use empty onRelease and set the duration of playNote to 1000, one voice stops after 1 second but the other one keeps playing indefinitely.

To me this looks like a bug. It seems to be caused by sostenuto setting of your layers. You have this set to off. To fix this turn the sostenuto on for all your layers.

This is the default setting when you create new layer:

  • repedaling off
  • sustain off
  • sostenuto on

Why it affects the playNote? I don’t know.

But after changing the sostenuto for all layers (actually it doesn’t matter for the Main layer, but for all other sublayers it does) playNote works reliably. When I used the empty onRelease then playNote with duration -1 played notes until release and playNote with specified duration played notes for the given duration.

I think I couldn’t reproduce this before because I created the layers manually and the sostenuto was on by default.