SostenutoSustain
This fastscript provides true Sostenuto & Sustain and their corresponding interactions with each other. The goal is to make them behave like true high-end grand piano middle & right pedals.
Sostenuto: Basically holds notes whose dampers were up at the time the Sostenuto pedal was pressed. They will sustain until the Sostenuto pedal is released. (CC66) Other notes will release normally. I.e., some notes hold and some don't...
Sustain: All notes are held & dampers are up until the pedal is released.(CC64).
CC64 & CC66 are not passed downstream so they wont get confused with any VSTi logic.
Sostenuto: Basically holds notes whose dampers were up at the time the Sostenuto pedal was pressed. They will sustain until the Sostenuto pedal is released. (CC66) Other notes will release normally. I.e., some notes hold and some don't...
Sustain: All notes are held & dampers are up until the pedal is released.(CC64).
CC64 & CC66 are not passed downstream so they wont get confused with any VSTi logic.
Address the process rather than the outcome. Then, the outcome becomes more likely. - Fripp
I had to do BOTH Sostenuto & Sustain in order to reproduce the exact behavior, as they do interact with each other.
Test #1: Without holding any key, press sustain, then sostenuto, then release sustain. The sostenuto pedal now acts like a simple sustain, not sostenuto, since all dampers were up at the time the sostenuto bar was engaged. I've read that folks buying a grand piano use this simple test to confirm the sostenuto bar is working as designed.
Test #2: Two stage sustain... Hold a chord & engage sostenuto. Press the sustain pedal. Hold another chord. Releasing the sustain pedal only releases the 2nd chord - the first chord is still held by the sostenuto pedal.
Test #3: Basic sostenuto... Hold a chord & press the sostenuto pedal. Play other keys... They should NOT be held. I.e., you get a mix of held & non-held notes on a properly implemented sostenuto.
Test #1: Without holding any key, press sustain, then sostenuto, then release sustain. The sostenuto pedal now acts like a simple sustain, not sostenuto, since all dampers were up at the time the sostenuto bar was engaged. I've read that folks buying a grand piano use this simple test to confirm the sostenuto bar is working as designed.
Test #2: Two stage sustain... Hold a chord & engage sostenuto. Press the sustain pedal. Hold another chord. Releasing the sustain pedal only releases the 2nd chord - the first chord is still held by the sostenuto pedal.
Test #3: Basic sostenuto... Hold a chord & press the sostenuto pedal. Play other keys... They should NOT be held. I.e., you get a mix of held & non-held notes on a properly implemented sostenuto.
Address the process rather than the outcome. Then, the outcome becomes more likely. - Fripp
Thanks a lot shawnb for this script, will be useful for sure !
i've edited your page to suit the wiki style
i've edited your page to suit the wiki style
Thanks!
& Thanks!
& Thanks!
Address the process rather than the outcome. Then, the outcome becomes more likely. - Fripp
-
damstraversaz
- Member
- Posts: 159
- Location: Chambéry
- Contact:
very useful, thanks a lot !
From Handyman:
Input is all MIDI traffic, including notes and pedal messages for sostenuto (CC66) & sustain (CC64).
Send the output to your sound module/VSTi.
This module interprets the pedal messages for you, and will sustain notes by eating Note OFF messages. The Note OFFs will be sent when the appropriate pedal is released.
CC64 and CC66 messages are not passed downstream, as this may confuse your VSTi (if it has similar logic).
Make sure to specify your MIDI channel; this is an input parameter which defaults to channel 1.
Hope this helps. Any further questions let me know,
Basically place this somewhere between your keyboard (or other MIDI controller/source) and your sound module. It is safe to run *all* of your MIDI traffic thru this module, no need to pre-filter or anything like that unless you really want to.Hi:
I dropped the SostenutoSustain.fastscript into my patch but I can't get it to work.
Can someone tell me how to set this up?
Thanks.
Input is all MIDI traffic, including notes and pedal messages for sostenuto (CC66) & sustain (CC64).
Send the output to your sound module/VSTi.
This module interprets the pedal messages for you, and will sustain notes by eating Note OFF messages. The Note OFFs will be sent when the appropriate pedal is released.
CC64 and CC66 messages are not passed downstream, as this may confuse your VSTi (if it has similar logic).
Make sure to specify your MIDI channel; this is an input parameter which defaults to channel 1.
Hope this helps. Any further questions let me know,
Address the process rather than the outcome. Then, the outcome becomes more likely. - Fripp
-
Charlie O.
- New member
- Posts: 8
- Contact:
Hello, is there a way to find this SostenutoSustain.fastscript in 2025 ?
Or even better as a script ?
Thank's a lot !
Or even better as a script ?
Thank's a lot !
Hi,
Found it in my archives !
Found it in my archives !
Code: Select all
// SostenutoSustain
//
// This script provides true Sostenuto & Sustain and their
// corresponding interactions with each other. The goal is
// to make them behave like true grand piano middle &
// right pedals.
//
// Sostenuto: Basically holds notes whose dampers were up
// at the time the Sostenuto pedal was pressed. They will
// sustain until the Sostenuto pedal is released. (CC66)
// Other notes will release normally. I.e., some notes hold
// and some don't...
//
// Sustain: All notes are held & dampers are up until the
// pedal is released.(CC64)
//
// In both cases, if keys are being pressed at the time the
// pedal is released, they will not be affected - their
// dampers are still up.
//
// CC64 & CC66 are not passed downstream so they wont get
// confused with any VSTi logic.
//
// Parameters:
// Channel - Only works on the channel specified here
// BlockIfHeld - If a held note is repeated,
// this will prevent retrigger of the note
//
// 6/2016, by shawnb
//
//////////////////////////////////////////////////////
// Parameters
//////////////////////////////////////////////////////
var pMidiIn : Tparameter;
var pMidiOut : Tparameter;
var pChannel : Tparameter;
var pBlockIfHeld : Tparameter;
//////////////////////////////////////////////////////
// Global Variables
//////////////////////////////////////////////////////
var iSostPedalPressed : single; // sostenuto pedal state
var iSustPedalPressed : single; // sustain pedal state
var inputIX : integer; // always current record just read
var outputIX : integer; // always current record about to be written
var iChannel : single; // holds parameter value
var iBlockIfHeld : single; // holds parameter value
var KeyPressed : array [0..127] of Integer; // reflects keyboard state
var MonkeyBar : array [0..127] of integer; // reflects dampers held by sostenuto bar
var NoteHeld : array [0..127] of integer; // reflects if note is being held
var MidiTmp : TMidi; // current midi message buffer
//////////////////////////////////////////////////////
// initialization procedure
//////////////////////////////////////////////////////
procedure init;
var i : integer;
begin
pMidiIn := CreateParam('MidiIn',ptMidi);
SetIsOutput(pMidiIn,false);
pMidiOut := CreateParam('MidiOut',ptMidi);
SetIsInput(pMidiOut,false);
pChannel := CreateParam('Channel',ptDataField);
SetIsOutput(pChannel,false);
SetMin(pChannel,1);
SetMax(pChannel,16);
SetDefaultValue(pChannel,1);
pBlockIfHeld := CreateParam('BlockIfHeld',ptDataField);
SetIsOutput(pBlockIfHeld,false);
SetMin(pBlockIfHeld,0);
SetMax(pBlockIfHeld,1);
SetDefaultValue(pBlockIfHeld,0);
iSostPedalPressed := 0;
iSustPedalPressed := 0;
inputIX := 0;
outputIX := 0;
iChannel := 1;
iBlockIfHeld := 0;
for i := 0 to 127 do KeyPressed[i] := 0;
for i := 0 to 127 do MonkeyBar[i] := 0;
for i := 0 to 127 do NoteHeld[i] := 0;
end;
//////////////////////////////////////////////////////
// Send_Msg
// Just write what's in the buffer...
//////////////////////////////////////////////////////
procedure Send_Msg;
begin
SetMidiArrayValue(pMidiOut,outputIX,MidiTmp);
outputIX := outputIX + 1;
end;
//////////////////////////////////////////////////////
// Send_Note_Off
//////////////////////////////////////////////////////
procedure Send_Note_Off(note:integer);
begin
MidiTmp.Channel := trunc(iChannel);
MIdiTmp.Msg := 128;
MIdiTmp.Data1 := note;
MIdiTmp.Data2 := 0;
SetMidiArrayValue(pMidiOut,outputIX,MidiTmp);
outputIX := outputIX + 1;
end;
//////////////////////////////////////////////////////
// Sost_Press
//////////////////////////////////////////////////////
procedure Sost_Press;
var i : integer;
begin
for i := 0 to 127 do begin
if (KeyPressed[i]= 1) or (iSustPedalPressed = 1) then
MonkeyBar[i]:= 1;
end;
end;
//////////////////////////////////////////////////////
// Sost_Release
//////////////////////////////////////////////////////
procedure Sost_Release;
var i,j : integer;
begin
for i := 0 to 127 do begin
if (KeyPressed [i]= 0) and (iSustPedalPressed = 0) and (NoteHeld [i]= 1) then begin
Send_Note_Off(i);
NoteHeld[i] := 0;
end;
MonkeyBar [i]:= 0;
end;
end;
//////////////////////////////////////////////////////
// Sost_Pedal
// Trigger pressed or released logic when CC values change
// Note no Send_Msg, as we eat all CC64 & CC66 Msgs
// so downstream VSTis don't get confused...
//////////////////////////////////////////////////////
procedure Sost_Pedal;
begin
if iSostPedalPressed = 0 then begin
if MidiTmp.Data2 > 63 then begin
Sost_Press;
iSostPedalPressed := 1;
end;
end
else begin
if MidiTmp.Data2 <= 63 then begin
Sost_Release;
iSostPedalPressed := 0;
end;
end;
end;
//////////////////////////////////////////////////////
// Sust_Release
// Release held notes
//////////////////////////////////////////////////////
procedure Sust_Release;
var i : integer;
begin
for i := 0 to 127 do begin
if (KeyPressed[i] = 0) and (MonkeyBar[i] = 0) and (NoteHeld[i] = 1) then begin
Send_Note_Off(i);
NoteHeld[i] := 0;
end;
end;
end;
//////////////////////////////////////////////////////
// Sust_Pedal
// Trigger pressed or released logic when CC values change
// Note no Send_Msg, as we eat all CC64 & CC66 Msgs
// so downstream VSTis don't get confused...
//////////////////////////////////////////////////////
procedure Sust_Pedal;
begin
if iSustPedalPressed = 0 then begin
if MidiTmp.Data2 > 63 then begin
iSustPedalPressed := 1;
end;
end
else begin
if MidiTmp.Data2 <= 63 then begin
Sust_Release;
iSustPedalPressed := 0;
end;
end;
end;
//////////////////////////////////////////////////////
// Note_On
//////////////////////////////////////////////////////
procedure Note_On;
begin
// Maintain picture of actual keyboard...
KeyPressed[MidiTmp.Data1] := 1;
// Honor iBlockIfHeld... Only pass note on if appropriate
if (iBlockIfHeld = 0) or (NoteHeld[MidiTmp.Data1] = 0) then
Send_Msg;
end;
//////////////////////////////////////////////////////
// Note_Off
//////////////////////////////////////////////////////
procedure Note_Off;
begin
// Maintain picture of actual keyboard...
KeyPressed[MidiTmp.Data1] := 0;
// Pass note off Msg if note is not sustained...
if (MonkeyBar[MidiTmp.Data1] = 0) and (iSustPedalPressed = 0) then begin
Send_Msg;
end
else begin
// Note is being sustained, flag in NoteHeld.
// Note that you've eaten the 'Note Off'
// by not doing a Send_Msg
NoteHeld[MidiTmp.Data1] := 1;
end;
end;
//////////////////////////////////////////////////////
// Do_My_Channel
//////////////////////////////////////////////////////
procedure Do_My_Channel;
begin
if MidiTmp.Msg = 144 then Note_On
else if MidiTmp.Msg = 128 then Note_Off
else if (MidiTmp.Msg = 176) and (MidiTmp.Data1 = 64) then Sust_Pedal
else if (MidiTmp.Msg = 176) and (MidiTmp.Data1 = 66) then Sost_Pedal
else Send_Msg; // pass everything else
end;
//////////////////////////////////////////////////////
// DoMidiProc
//////////////////////////////////////////////////////
procedure DoMidiProc;
var len : integer;
begin
outputIX := 0;
len := GetLength(pMidiIn);
if len > 0 then begin
for inputIX := 0 to len-1 do begin
GetMidiArrayValue(pMidiIn, inputIX, MidiTmp);
// Only work on selected channel, pass everything else thru
if MidiTmp.Channel = iChannel then
Do_My_Channel
else
Send_Msg;
end;
end;
// Always properly close out the midi output stream...
SetLength(pMidiOut,outputIX);
end;
//////////////////////////////////////////////////////
// Main procedure
//////////////////////////////////////////////////////
procedure Process;
begin
//Do it all in the Callback...
end;
//////////////////////////////////////////////////////
// Callback
//////////////////////////////////////////////////////
Procedure Callback(n:integer);
begin
case n of
pMidiIn : DoMidiProc;
pChannel : iChannel := GetValue(n);
pBlockIfHeld : iBlockIfHeld := GetValue(n);
end;
end
Last edited by oli_lab on 13 Oct 2025, 08:14, edited 2 times in total.
http://oli-lab.org
Win11 Ryzen9/32GB RAM - RME MADIFACE - SSL alpha link 4-16 - OSC capable interfaces
follow OLI_LAB adventures on Mastodon
@olivar_premier@mastodon.social
Win11 Ryzen9/32GB RAM - RME MADIFACE - SSL alpha link 4-16 - OSC capable interfaces
follow OLI_LAB adventures on Mastodon
@olivar_premier@mastodon.social
-
Charlie O.
- New member
- Posts: 8
- Contact:
Hey thanks a lot ! I didn't see your answer, that's very kind of you.
I will try it asap.
I will try it asap.
-
Charlie O.
- New member
- Posts: 8
- Contact:
Alors c'est plus épineux que ce que je craignais, j'ai l'impression qu'il y a eu du changement de syntaxe depuis que le script a été écrit en 2016.
var KeyPressed : array [0..127] of single; // reflects keyboard state
procedure init;
var i : integer;
for i := 0 to 127 do KeyPressed := 0;
et là ça bloque : "Can't assign "Int64" to array [0..127] of Single" at line 84
var KeyPressed : array [0..127] of single; // reflects keyboard state
procedure init;
var i : integer;
for i := 0 to 127 do KeyPressed := 0;
et là ça bloque : "Can't assign "Int64" to array [0..127] of Single" at line 84
yes, you're right, but unfortunately I am not yet fluent in the Pascal scripting lingo of Usine.
I'll try my best soonish
Olivar
I'll try my best soonish
Olivar
http://oli-lab.org
Win11 Ryzen9/32GB RAM - RME MADIFACE - SSL alpha link 4-16 - OSC capable interfaces
follow OLI_LAB adventures on Mastodon
@olivar_premier@mastodon.social
Win11 Ryzen9/32GB RAM - RME MADIFACE - SSL alpha link 4-16 - OSC capable interfaces
follow OLI_LAB adventures on Mastodon
@olivar_premier@mastodon.social
-
Charlie O.
- New member
- Posts: 8
- Contact:
> I am not yet fluent in the Pascal scripting lingo of Usine
Neither am I, for sure. That's extremely kind of you to try !
Neither am I, for sure. That's extremely kind of you to try !
I fixed the code, and edited my previous post. It compiled alright but need some testing with MIDI.Charlie O. wrote: ↑12 Oct 2025, 17:46> I am not yet fluent in the Pascal scripting lingo of Usine
Neither am I, for sure. That's extremely kind of you to try !
Can you test it and tell us how it goes ?
Olivar
http://oli-lab.org
Win11 Ryzen9/32GB RAM - RME MADIFACE - SSL alpha link 4-16 - OSC capable interfaces
follow OLI_LAB adventures on Mastodon
@olivar_premier@mastodon.social
Win11 Ryzen9/32GB RAM - RME MADIFACE - SSL alpha link 4-16 - OSC capable interfaces
follow OLI_LAB adventures on Mastodon
@olivar_premier@mastodon.social
-
Charlie O.
- New member
- Posts: 8
- Contact:
Wonderful ! You're pretty good for someone not fluent.
i've tested with two midi pedals, sustain and sostenuto together, everything works as expected.
Thank's a lot, a lot, a lot !!
i've tested with two midi pedals, sustain and sostenuto together, everything works as expected.
Thank's a lot, a lot, a lot !!
Who is online
Users browsing this forum: No registered users and 5 guests
