This is a complicated script. It sits in the first slot of every vst rack in my wkp, and decides which midi channel will be sent on to the vst, does note limiting, and rechannelizes the output to the vst. It also has smart transpose and can convert the transpose input to a CC for pitching drum sounds etc. If the value of input vstChIN is zero, then incoming midi messages are sent out with their original channel unchanged, for controlling multi-channel vsts. Otherwise they are rechannelized to the value of vstChIN.
It works perfectly! Except for one thing.
If (and ONLY if) the value of vstChIN = '0', I get a repeated internal error that fills up the trace, even if there is NO midi activity at all! The error shows up in the next patch down in the rack, gui.pat, which contains all the gui controls. The only midi in that patch is an input wired directly to a midi output to pass midi on to the 3rd patch which hosts the vst. The error looks like this:
Error : E901 patch process : Process of MIDI In (rack: 3) patch: gui.pat
It's not a malformed message as no messages are sent. It is happening as soon as the vstChIN changes to 0. The output of the script is cabled directly to a MIDI OUT module.
One hint that might be helpful. If HH is set to 'NO AUDIO' the error only occurs once.
If there are any script gurus that have any idea what may be going on, please let me know! I can't think of anything else to try at the moment.
Thanks as always!!!
=eric
Code: Select all
/////////////////////////////////////////////////////////////////////////////
// vstChIN parameter value of 0 is causing error
// Error : E901 patch process : Process of MIDI In (rack: 3) patch: gui.pat //next patch down
/////////////////////////////////////////////////////////////////////////////
VAR midiIN, midiOUT : Tparameter;
// from NS
var transposeIN: Tparameter;
var lowNoteIN : Tparameter;
var hiNoteIN : Tparameter;
var chanIN : Tparameter;
var statusOUT : Tparameter;
var vstChIN : Tparameter;
// from mixer
var handsIN : Tparameter;
var holdIN : Tparameter;
var trackIN : Tparameter;
var enabledIN : Tparameter;
// from inst
var transToCtlIN : Tparameter;
var resetClkIN : Tparameter;
var resetVelIN : Tparameter;
//arrays
var nSolosIN : Tparameter;
var noSusIN : Tparameter;
var nsLowNotesIN : Tparameter;
var nsHiNotesIN : Tparameter;
var resetClkOUT : Tparameter;
var clearIN : Tparameter;
VAR transpositions : ARRAY OF integer;
VAR noteONs : ARRAY OF boolean;
VAR midi : tMidi;
VAR midiCount, transpose,transCtl,i, trackCount, trackNum,status : integer;
VAR newTranspose,hands,hold,resetClk,resetting: boolean;
var lowNote,hiNote,nSoloLow,nSoloHi, inputCh, vstChan, vel, resetVel: integer;
var nSolo, clearNotes, enabled,noSus: boolean;
PROCEDURE Init;
BEGIN
//from NS
midiIN := CreateParam('midi in', ptMidi); SetIsOutput(midiIN, FALSE);
chanIN := CreateParam('midi chan', ptDatafield); SetIsOutput(chanIN, FALSE);
lowNoteIN := CreateParam('low note', ptDataField); SetIsOutput(lowNoteIN, FALSE);
hiNoteIN := CreateParam('hi note', ptDataField); SetIsOutput(hiNoteIN, FALSE);
//from mixer
transposeIN := CreateParam('transpose', ptDataField);SetIsOutput(transposeIN, FALSE);
handsIN := CreateParam('hands on',ptSwitch); SetIsOutput(handsIN, false);
holdIN := CreateParam('hold on',ptSwitch); SetIsOutput(holdIN, false);
//from inst
transToCtlIN := CreateParam('transToCtl',ptDatafield); SetIsOutput(transToCtlIN, false);
//ns array is an array [1..trackcount] of midi channels. 0 means either the chan is disabled, or it is not a ns channel
nSolosIN := CreateParam('nsSolo arr',ptArray); SetIsOutput(nSolosIN, false);
nsLowNotesIN := CreateParam('ns low arr',ptArray); SetIsOutput(nsLowNotesIN, false);
nsHiNotesIN := CreateParam('ns high arr',ptArray); SetIsOutput(nsHiNotesIN, false);
// what ch the inst wants. 0 for multichan... CAUSING ERROR!!!!!
vstChIN := CreateParam('vst chan', ptDatafield); SetIsOutput(vstChIN, FALSE);
clearIN := CreateParam('clear notes',ptSwitch); SetIsOutput(clearIN, false);
resetClkIN := CreateParam('vel clock reset', ptDatafield); SetIsOutput(resetClkIN, FALSE);
resetVelIN := CreateParam('reset vel thresh', ptDatafield); SetIsOutput(resetVelIN, FALSE);
trackIN := CreateParam('track num', ptDatafield); SetIsOutput(trackIN, FALSE);
enabledIN := CreateParam('enable', ptDatafield); SetIsOutput(enabledIN, FALSE);
noSusIN := CreateParam('no sus', ptDatafield); SetIsOutput(noSusIN, FALSE);
resetClkOUT := CreateParam('reset clock', ptDatafield); SetIsInput(resetClkOUT, FALSE);
midiOUT := CreateParam('midi out', ptMidi); SetIsInput(midiOUT, FALSE);
statusOUT := CreateParam('status', ptDatafield); SetIsInput(statusOUT, FALSE);
SetArrayLength(transpositions, 128);
SetArrayLength(noteONs, 128);
for i := 0 to 127 DO BEGIN
transpositions[i] := 0;
noteOns[i] := FALSE;
END;
clearNotes := FALSE;
newTranspose := FALSE;
setLength(resetClkOUT,0);
END; // Init
procedure makeHands;
var note : integer;
begin
if hands then begin
note := midi.data1;
if note > 60 then midi.data1 := note - 12
else midi.data1 := note + 12;
end;
end;
procedure setStatus;
var compromised : boolean;
BEGIN
compromised := false;
if not enabled THEN status := 0
ELSE
BEGIN
//check our own status
trackCount := getLength(nSolosIN);
//figure key range based on NS
nSolo := getDataArrayValue(nSolosIN,trackNum - 1) = inputCh;
IF nSolo THEN
BEGIN
nSoloHI := trunc(getDataArrayValue(nsHiNotesIN, trackNum - 1));
nSoloLow := trunc(getDataArrayValue(nsLowNotesIN, trackNum - 1));
IF nSoloHI < hiNote THEN hiNote := nSoloHI;
IF nSoloLow > lowNote THEN lowNote := nSoloLow;
END ELSE
BEGIN // reset the keyboard range...
//strace('resetting keyboard range for channel ' + inttostr(trackNum));
hiNote := trunc(getValue(hiNoteIN));
lowNote := trunc(getValue(lowNoteIN));
END;
//No notesoloing for FX (ch 17)
IF (inputCH = 17) THEN
BEGIN
IF enabled THEN status := 1 ELSE status := 0;
END
//cannot be note solo in omni mode!
ELSE IF (inputCH > 0) and nSolo THEN
BEGIN
//we are a note solo inst
//strace('note solo ON for track number ' + intToStr(trackNum));
status := 2;
END
ELSE BEGIN
// compute status and nsInfo from arrays
nSoloHI := 0;
nSoloLow := 127
for i := 0 to trackCount - 1 do
BEGIN
if (inputCH > 0) and (getDataArrayValue(nSolosIN,i) = inputCH) then
BEGIN //we are note solo compromised.
//strace('note solo affecting track number ' + intToStr(trackNum));
compromised := true;
if (getDataArrayValue(nsHiNotesIN,i) > nSoloHI) then
nSoloHI := trunc(getDataArrayValue(nsHiNotesIN,i));
if (getDataArrayValue(nsLowNotesIN,i) < nSoloLow) then
nSoloLow := trunc(getDataArrayValue(nsLowNotesIN,i));
END;
END;
//strace('notesoloLow = ' + intToStr(nSoloLow));
//strace('notesoloHi = ' + intToStr(nSoloHi));
if compromised then status := 3 ELSE status := 1;
END;
END;
setValue(statusOUT,status)
END;
procedure callback(n: integer);
BEGIN
//strace('n = ' + inttostr(n));
IF (n = 0) THEN //ignore
ELSE IF (n = transposeIN) THEN newTranspose := TRUE
ELSE IF (n = trackIN) THEN trackNum := trunc(getValue(trackIN))
ELSE IF (n = transToCtlIN) THEN transCtl := trunc(getValue(transToCtlIN))
ELSE IF (n = resetClkIN) THEN resetClk := trunc(getValue(resetClkIN)) = 1
ELSE IF (n = resetVelIN) THEN resetVel := trunc(getValue(resetVelIN))
ELSE IF (n = noSusIN) THEN noSus := trunc(getValue(noSusIN)) > 0
ELSE IF (n = holdIN) THEN
BEGIN
hold := trunc(getValue(holdIN)) = 1;
//strace('hold setting clear notes if it is off');
//if we just turned of hold, clear notes
IF NOT hold then clearNotes := TRUE;
END
ELSE IF (n = enabledIN) THEN
BEGIN
enabled := trunc(getValue(enabledIN)) = 1;
//strace('enabled setting clear notes if not enabled and not hold');
if NOT enabled and NOT hold then clearNotes := TRUE;
setStatus;
END
//some other inst has changed, so we need to check status
ELSE IF (n = nsHiNotesIN) OR (n= nsLowNotesIN) OR (n = nSolosIN) THEN setStatus
ELSE IF (n = resetClkOUT) OR (n = midiOUT) OR (n = statusOUT) THEN //why,it's an output??????
ELSE BEGIN
IF (n = handsIN) THEN hands := trunc(getValue(handsIN))
ELSE IF (n = vstChIN) THEN vstChan := trunc(getValue(vstChIN))
ELSE IF (n = lowNoteIN) THEN lowNote := trunc(getValue(lowNoteIN))
ELSE IF (n = hiNoteIN) THEN hiNote := trunc(getValue(hiNoteIN))
ELSE IF (n = chanIN) THEN inputCh := trunc(getValue(chanIN));
setStatus;
clearNotes := TRUE;
//strace('setting clearNotes to true via hands, outch, lownote hinote, or chan');
END
END;
// process
procedure process;
var outCount,note,chan: integer;
var ccTrans,countOffset: integer;
var newMidi: tMidi;
var hasMidi: boolean;
var minCH,maxCH,ch : integer;
BEGIN
IF (vstChan = 17) THEN SetLength(midiOUT, 0) // no midi processing at all
ELSE BEGIN
//should filter all but notes, sustain and pb prev to script
midiCount := GetLength(midiIN);
hasMidi := midiCount > 0;
outCount := 0;
if resetting = true then begin
resetting := false; //turn reset back off
setValue(resetClkOUT, 0); //null event for off
end
IF clearNotes and (vstChan < 17) then BEGIN
//strace('clearing notes');
//for omni sources, send noteoffs on every channel...
IF vstChan = 0 then BEGIN minCH := 1; maxCH := 16; END
ELSE BEGIN minCH = vstChan; maxCH := vstChan; END;
FOR ch := minCH to maxCH DO
BEGIN
for i := 0 to 127 DO BEGIN
// send note off s
if noteONs[i] := TRUE THEN BEGIN
midi.channel := ch; midi.msg := 128; midi.data1 := i; midi.data2 := 0;
setMidiArrayValue(midiOUT, outCount, midi);
outCount := outCount + 1;
noteONs[i] := FALSE;
END;
END;
// send sus pedal off!
midi.channel := ch; midi.msg := 176; midi.data1 := 64; midi.data2 := 0;
end
setMidiArrayValue(midiOUT, outCount, midi);
outCount := outCount + 1;
setLength(midiOUT, outCount);
clearNotes := FALSE;
//if we are clearing, we ignore other midi input for this cycle....
END
ELSE
BEGIN
if newTranspose and (vstChan < 17) then
begin
strace('in NEW TRANSPOSE');
IF (transCtl = 0) then transpose := round(getValue(transposeIN))
ELSE BEGIN
transpose := 0;
// create midi cc out for drum tuning (cc value is transCtl)
ccTrans := (round((transpose * 1.76) + 64));
//strace('____________CC TRANS = ' + intToStr(ccTrans));
newMidi.msg := 176; newMidi.channel := 1; newMidi.data1 := transCtl; newMidi.data2 := round(ccTrans);
SetMidiArrayValue(midiOUT, outCount, newMidi);
outCount := outCount + 1;
END;
newTranspose := FALSE;
end;
// filter everything but notes for this script!
//Hold keeps new notes from being triggered
IF hasMidi and (hold = false) and enabled and (vstChan < 17) THEN
BEGIN
countOffset := outCount;
FOR i := 0 TO (midiCount - 1) DO
BEGIN
GetMidiArrayValue(midiIN, i, midi);
note := midi.data1;
vel := midi.data2;
chan := midi.channel;
IF (inputCh > 0) then midi.channel := vstChan; //if midiCH is 0 leave unchanged
IF (midi.msg = 224) OR ((midi.msg = 172) AND (noSus = false)) THEN
BEGIN
//strace('message = ' + inttoStr(midi.msg));
SetMidiArrayValue(midiOUT, outcount, midi);
outCount := outCount + 1;
END
//TODO: how does this work with PB and SUS on multi ch sources?
ELSE IF (inputCH > 0) AND (chan <> inputCH) THEN //ignore notes from other channels. 0 is omni.
ELSE IF (note < lowNote) or (note > hiNote) THEN //ignore notes outside range. True for any status. Includes NS limiting
ELSE IF (status = 3) and (nSoloLow <= note) and (note <= nSoloHI) THEN //ignore note solo range on NS channel
//strace('tracknum = ' + inttostr(trackNum) +', nsolo lo = ' + inttostr(nSoloLow) + ', nSoloHI = ' + inttostr(nSoloHi) + ',note = ' + intToStr(note));
ELSE
BEGIN
IF ((midi.msg = 144) AND (midi.data2 > 0)) THEN
BEGIN // NoteOn
if hands then makeHands;
if resetClk and (vel > resetVel) then
begin
//strace('resetting------------------------------------- ');
setLength(resetClkOUT,1);
setValue(resetClkOUT,1);
resetting := true;
end;
transpositions[midi.data1] := transpose; // Store transpose value used for NoteOn
noteOns[midi.data1] := TRUE;
midi.data1 := midi.data1 + transpose;
END
ELSE IF ( (midi.msg = 128)
OR ((midi.msg = 144) AND (midi.data2 = 0))) THEN
BEGIN // NoteOff
if hands then makeHands;
if (transCtl = 0) then
begin
midi.data1 := midi.data1 + transpositions[midi.data1]; // Retrieve stored transpose and add to NoteOff
noteOns[midi.data1] := FALSE;
end;
END;
//ELSE IF (midi.msg = 172) and (midi.data1 = 64)
SetMidiArrayValue(midiOUT, outcount, midi);
outCount := outCount + 1;
END;
END;
setLength(midiOUT, outCount);
END
ELSE
BEGIN
SetLength(midiOUT, 0);
END;
END;
END;
END;