ArrayArrayArrayArrayArrayArrayArrayArrayArrayArray
CODE: Statistics: Posted by amiga909 — 12 Nov 2008, 15:35 Statistics: Posted by amiga909 — 07 Nov 2008, 20:25 Statistics: Posted by amiga909 — 04 Nov 2008, 22:52
- almost all algos replaced
- convert CC or PC to Notes
- removed functionality: compress note, convert midi message to notes (polyphonic)
- more demos: noteLegato.mp3 , noteLegato.stepMod.mp3 (LFO->+createMidi->MEP.script->T-Pulse VSTii)(* no compress, no multinoteConvertMEP 0.95 by amiga909// 2008-10-29: first version// 2008-11-12: release version// credits to: bSork, senso (www.sensomusic.com/forums)// <Description> midi tool roughly inspired by Yamaha MEP-4 Midi Event Processor. MEP was designed for experimental purposes but it can be also used as a midi utility. download the original MEP-4 manual here: http://www2.yamaha.co.jp/manual/pdf/emi/english/synth/MEP4E.PDF // <Features> prevent hanging notes, swap data bytes, convert to notes, ..// <Processing chain> <input> -> select -> // filter input (if midi thru on: pass filtered data) swapBytes -> inverse -> // modify input bytes expand -> transpose -> // calculate input bytes wrap -> step(x%y) -> limit -> // limit/rescale input bytes affectByte -> // modify output bytes convert -> // change output type <output> <-// <Parameters> // select event : select an event type for processing (if event type is NoteOn+Off and convert is not bypassed, NoteOff messages are filtered in some cases). // mute : filter selected event. // midi thru : output all other event types. // affect byte : process first or second data byte or both. if only one byte is selected, then the other byte is output unprocessed.// inv. byte1/2 : inverse incoming data byte. high value -> low, low -> high.// swap bytes : swap first and second byte (eg. convert velocity to noteOn).// expand : choose a multiplication factor from 0.0625 to 16.// transpose : add or subtract value.// wrap around : if a value as a result of expanding or transposing is not between 1 and 128, and if wrap around is off: result is 128 or 1, if wrap around is on: (1-1=128;128+1=1;).// step : only messages with a data byte that is a multiple of the Step will be allowed through.// limit low/hi : specify the output data range. use a range fader (interface design) for control. // limit mode : limit hard limits the output and filters values out of range, scale rescales to desired range.// convert : transform input event to another midi message. (mode 1: convert to notes: even events are noteOns, odd events are noteOffs hanging notes; in contrary NoteOff! or NoteOn! create invalid midi notes). // <Notes> - Aftertouch, PC and other messages are not tested as much as Notes and CC - compress is very beta. maybe it will be removed... (not original MEP anyway) - the interface will maybe be reworked. there is the idea to have 8 toggle buttons that can each hold an operation parameter (à la instajungle).*)VAR pIn, pOut, pMult, pEvent, pDest, pWrap, pInv1, pInv2, pTransp, pBypass, pNotesCount, pThru, pConv, pStep, pLimitHi, pLimitLo, pLimit, pSwap : TParameter;TYPE TMult = ARRAY OF integer;VAR mult : ARRAY OF TMult;VAR pMultAr : ARRAY OF double; VAR tmp : TMidi; VAR multiply : double; VAR len,i,tmpX,tmpY,tmpXX,tmpYY,tmpMsg,dest,transpose,limit,convert,event,step,convCnt, limitLo,limitHi,tmpLimitLo, notesCount,filterByte,lenCount,lastConvNoteOn: integer; VAR isSwap, isWrap, isInv1, isInv2, isLimit, isConvNoteOff, isFirstByte, isSecondByte, isConvert, isConvertToNotes : boolean;CONST NOTE_ON = 144;CONST NOTE_OFF = 128;CONST CONTROL_CHG = 176;CONST PROGRAM_CHG = 192;CONST PITCHBEND = 224;CONST AFTER_MONO = 208;CONST AFTER_POLY = 160;CONST MIDI_CLOCK = 248;CONST MIDI_START = 250;CONST MIDI_CONT = 251;CONST MIDI_STOP = 252;CONST ACTIVE_SENS = 254;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE Init;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// VAR f_c1,f_c2: integer; BEGIN pIn := createParam('midi in', ptMidi); pOut := createParam('midi out', ptMidi); pEvent := createParam('select', ptListBox); setListBoxString(pEvent, '"ALL","Note On+Off", "Control Change", "Program Change", "Pitch Bend", "Aftertouch"'); pInv1 := createParam('inverse byte1', ptSwitch); pInv2 := createParam('inverse byte2', ptSwitch); pSwap := createParam('swap bytes', ptSwitch); pMult := createParam('expand', ptListBox); setListBoxString(pMult, ' "-bypass-", "1/16", "1/8", "1/4", "1/2","1","2","4","8","16"'); pMultAr:=[1, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16]; pTransp := createParam('transpose',ptDataFader);setMin(pTransp,-128);setMax(pTransp,127); setSymbol(pTransp, '');setFormat(pTransp,'-bypass-'); pWrap := createParam('wrap around', ptSwitch); pStep := createParam('step modulo', ptDataFader);setMin(pStep,1);setMax(pStep, 16); setSymbol(pStep,'');setFormat(pStep,'-bypass-'); pLimitLo := createParam('lo limit', ptMidiNoteFader); setMax(pLimitLo, 126); setMin(pLimitLo, 0); pLimitHi := createParam('hi limit', ptMidiNoteFader); setMin(pLimitHi, 1);setMax(pLimitHi, 127); pLimit := createParam('limit mode', ptListBox); setListBoxString(pLimit,'"-bypass-","filter","rescale","compress"');// if compress: faders = ratio, threshold setValue(pLimit,1); setDefaultValue(pLimit,0);pDest := createParam('affect', ptListBox); setListBoxString(pDest,'"1st byte (note,cc.num,..)", "2nd byte (velo,cc.val,..)", "1st and 2nd byte"'); pConv := createParam('convert', ptListBox); // take all from 'Create Midi?' setListBoxString(pConv, '"-bypass-","Note Legato","Note Poly (mode1)","Note Poly (mode2)","Note Poly (random)",'+ '"Control Change","Program Change", "Pitch Bend","Aftertouch Channel","Aftertouch Poly",'+ '"Timing Clock","Start","Stop","Continue","Active Sensing", "NoteOn !","NoteOff !"');pThru := createParam('midi thru', ptSwitch); pBypass := createParam('mute', ptSwitch); pNotesCount := createParam('open notes', ptDataField); setReadOnly(pNotesCount,true); setMin(pNotesCount,0);setMax(pNotesCount,999);f_c1:= 90099;f_c2:= 60099; setColor(pLimitHi,f_c1);setColor(pLimitLo,f_c1);setColor(pTransp,f_c1);setColor(pThru,f_c2);setColor(pStep,f_c1);setColor(pWrap,f_c2);setColor(pInv1,f_c2);setColor(pInv2,f_c2);setColor(pSwap,f_c2);setIsInput(pOut,false);setIsInput(pNotesCount,false);setIsOutput(pIn,false);setIsOutput(pEvent,false);setIsOutput(pThru,false);setIsOutput(pDest,false);setIsOutput(pInv1,false);setIsOutput(pInv2,false);setIsOutput(pSwap,false);setIsOutput(pMult,false); setIsOutput(pTransp,false); setIsOutput(pStep,false); setIsOutput(pWrap,false);setIsOutput(pLimit,false);setIsOutput(pLimitLo,false);setIsOutput(pLimitHi,false);setIsOutput(pConv,false); setIsOutput(pBypass,false); setValue(pEvent,1); setValue(pDest,0); setValue(pInv1,0);setValue(pInv2,0);setValue(pMult,GetLength(pMult)-1);setDefaultValue(pMult,0);setValue(pTransp,-128);setValue(pStep,1); setValue(pWrap,0);setValue(pLimitLo,0);setValue(pLimitHi,127); setValue(pLimit,0);setValue(pConv,0);// array initsetArrayLength(mult, 16); FOR i := 0 TO 15 DO BEGIN setArrayLength(mult[i], 128); // init [16 channels][128 multiply values] END;notesCount:=0;convCnt:=0;END; //^^^^^^^^^^^^^^^^^^^^^^^<F>^^^^^^^^^^^^^^^^^^^^^//// <F> isMidiMsg: 'event' is a list of pEvent (0=ALL)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isMidiMsg(msg: integer; e: integer): boolean;BEGIN IF ((e=1)OR(e=0)) AND ((msg=NOTE_ON)OR(msg=NOTE_OFF)) THEN BEGIN result := true; END // Notes ELSE IF ((e=2)OR(e=0)) AND (msg=CONTROL_CHG) THEN BEGIN result := true; END //CtrlChg ELSE IF ((e=3)OR(e=0)) AND (msg=PROGRAM_CHG) THEN BEGIN result := true; END //PrgrChg ELSE IF ((e=4)OR(e=0)) AND (msg=PITCHBEND) THEN BEGIN result := true; END //PitchBend ELSE IF ((e=5)OR(e=0)) AND ((msg=AFTER_MONO) OR (msg=AFTER_POLY)) THEN BEGIN result := true; END //Aftertouch (Channel, Poly) ELSE BEGIN result:=false; END; END;// <F> isStep: step limit data: filter out (? or compress->max, or compress->min)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isStep(val: integer; stp: integer): boolean;BEGIN IF NOT((val+1) MOD stp=0) THEN BEGIN result:=true; END ELSE BEGIN result:=false; END;END;// <F> isNoteOn: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isNoteOn(message: byte;velocity: byte): boolean;BEGIN IF (message=NOTE_ON)AND(velocity>0) THEN BEGIN result:=true; END ELSE BEGIN result:=false; END;END;// <F> isNoteOff: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isNoteOff(message: byte;velocity: byte): boolean;BEGIN IF (message=NOTE_OFF)OR((message=NOTE_ON)AND(velocity=0)) THEN BEGIN result:=true; END ELSE BEGIN result:=false; END;END;// <F> setWrap: wrap or limit byte range //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION setWrap (t: integer; wrap: boolean): integer; // 777777 rename 'setWrap' BEGIN IF isWrap THEN BEGIN IF t<0 THEN BEGIN result:= 128 - ((t*-1) MOD 128); END ELSE IF t>127 THEN BEGIN result := t MOD 128; END ELSE BEGIN result:=t; END; END ELSE BEGIN IF t<0 THEN BEGIN result:=0; END ELSE IF t>127 THEN BEGIN result:=127; END ELSE BEGIN result:=t; END; END;END;// : setTransp: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION setTransp(val: integer; trsp: integer; wrap: boolean):integer;BEGIN val:=val+trsp; IF ((val>127)OR(val<0)) THEN BEGIN IF isWrap THEN BEGIN IF val>127 THEN BEGIN result:=val-127; END ELSE BEGIN result:=val+127; END; END ELSE BEGIN IF val>127 THEN BEGIN result:=127; END ELSE BEGIN // val<0 result:=0; END; END; END ELSE BEGIN result:=0; END;END;// <F> convert://^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// // setConvert: (why not convert to: clock , start, stop, cont?)FUNCTION setConvert(srcMsg: byte;typeId: integer): byte;BEGINCASE typeId OF 0: result:=srcMsg; // bypass 1: result:=NOTE_ON; // NotesLegato 2: result:=NOTE_ON; // NotesPoly1 3: result:=NOTE_ON; // NotesPoly2 4: result:=NOTE_ON; // NotesPoly rnd 5: result:=CONTROL_CHG; 6: result:=PROGRAM_CHG; 7: result:=PITCHBEND; //(Modwheel) 8: result:=AFTER_MONO; // Aftert Channel 9: result:=AFTER_POLY; // Aftert Poly 10: result:=MIDI_CLOCK; 11: result:=MIDI_START; 12: result:=MIDI_STOP; 13: result:=MIDI_CONT; 14: result:=ACTIVE_SENS; 15: result:=NOTE_ON; // NoteON ONLY! 16: result:=NOTE_OFF; // NoteOFF ONLY! ELSE BEGIN result:=srcMsg; END;END;END;// <F> calcLimited: returns multiplication value for limiting, or return 1 (x*1=x; x>0)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION calcLimited (val: integer;lo: integer;hi: integer;mode: integer): integer; VAR r : double; BEGIN //writeln('val:'+inttostr(val)) IF mode<=1 THEN BEGIN // filter, bypass, not used here result:=val; END ELSE IF mode=2 THEN BEGIN // scale r := 128/(hi-lo); val:= trunc((val+1)/r); result:= val+lo; END ELSE BEGIN result:=val; END;END; //^^^^^^^^^^^^^^^^^^^^^^^<P>^^^^^^^^^^^^^^^^^^^^^//// <P> countNotes: PROCEDURE countNotes(isANoteAndON:boolean);//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//BEGIN IF isANoteAndON THEN BEGIN notesCount:=notesCount+1 END ELSE BEGIN notesCount:=notesCount-1; END; END;// <P> convertToLegatoNotes: isConvert AND isNoteOff=false (dont process noteOff for eg. note->cc)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE convertToLegatoNotes;BEGIN IF convCnt MOD 2 = 1 THEN BEGIN tmp.data1:=byte(lastConvNoteOn); tmp.msg:=byte(NOTE_OFF); END ELSE BEGIN lastConvNoteOn:=tmp.data1; tmp.msg:=byte(NOTE_ON); END; convCnt:=convCnt+1; IF convCnt=1028 THEN BEGIN convCnt:=0; END;END;// <P> initLimitRange: expect: limitHi and limitLo are getValue(p) //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE initLimitRange;BEGIN IF NOT(limit=3) THEN BEGIN // if limit is compress: thres/ratio fader functions IF limitHi-limitLo< 0 THEN BEGIN tmpLimitLo:=limitLo; limitLo:=limitHi; limitHi:=tmpLimitLo END ELSE IF limitHi-limitLo=0 THEN BEGIN IF limitHi=128 THEN BEGIN limitLo:=127; END ELSE BEGIN limitHi:=limitHi+1; END; END; END; END; // <P> setDataSymbols: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE setDataSymbols;VAR tr: integer;BEGIN IF (trunc(getValue(pStep))<= 1) THEN BEGIN setSymbol(pStep,'');setFormat(pStep,'-bypass-'); END ELSE BEGIN setSymbol(pStep,'%'); setFormat(pStep,'%.0f') END; tr:=trunc(getValue(pTransp)); IF tr>0 THEN BEGIN setSymbol(pTransp,'+'); setFormat(pTransp,'%.0f'); END ELSE IF (tr=(-128))OR(tr=0) THEN BEGIN setSymbol(pTransp,''); setFormat(pTransp,'-bypass-'); END ELSE BEGIN setSymbol(pTransp,'');setFormat(pTransp,'%.0f'); END; END;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| // main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^|BEGIN len := getLength(pIn); lenCount:= len; IF len > 0 THEN BEGIN multiply:=pMultAr[trunc(getValue(pMult))]; transpose:= trunc(getValue(pTransp)); IF (transpose=(-128)) THEN BEGIN // control: bypass at the left end for convenience transpose:=0; END; event:=trunc(getValue(pEvent)); dest:=trunc(getValue(pDest)); isFirstByte:=(dest=2)OR(dest=0); isSecondByte:=(dest=2)OR(dest=1); limit:=trunc(getValue(pLimit)); limitLo:=trunc(getValue(pLimitLo)); limitHi:=trunc(getValue(pLimitHi)); isLimit:=(limit>0) AND ((limitLo>=1)OR(limitHi<=126)); initLimitRange(); convert:=trunc(getValue(pConv)); isConvert:=convert>0; isConvertToNotes:=convert=1; isConvNoteOff:=false; step:=trunc(getValue(pStep)); isInv1:=getValue(pInv1)>0; isInv2:=getValue(pInv2)>0; isSwap:=getValue(pSwap)>0; isWrap:=getValue(pWrap)>0; FOR i := 0 TO (len - 1) DO BEGIN getMidiArrayValue(pIn, i, tmp); IF (getValue(pBypass)=0) AND (isMidiMsg(tmp.msg,event)) THEN BEGIN tmpMsg:= tmp.msg; tmpX := tmp.data1; tmpY := tmp.data2; tmpXX := tmpX; // store orig. val for noteList tmpYY := tmpY; IF isInv1 THEN BEGIN tmpX:= 127 - tmpX; END; IF isInv2 THEN BEGIN tmpY:= 127 - tmpY; END; IF isSwap THEN BEGIN tmpX := tmpY; tmpY := tmpXX; END; // affect byte 1 IF isFirstByte THEN BEGIN // 1st byte: process 1st+2nd byte if dest=2 tmpX := trunc(multiply*tmpX); tmpX := tmpX + (transpose); tmpX := setWrap(tmpX, isWrap); // wrap or hard limit IF isLimit THEN BEGIN tmpX := calcLimited(tmpX,limitLo,limitHi,limit); END; tmp.data1 := byte(tmpX); END; // affect byte 2 IF isSecondByte THEN BEGIN tmpY := trunc(multiply*tmpY); tmpY := tmpY + (transpose); tmpY := setWrap(tmpY, isWrap); // wrap or hard limit IF isLimit THEN BEGIN tmpY := calcLimited(tmpY,limitLo,limitHi,limit); END; tmp.data2 := byte(tmpY); END; // prevent hanging notes (tmpXX for swapped note values) IF isNoteOn(tmpMsg,tmpYY) THEN BEGIN mult[tmp.channel - 1][tmpXX] := tmp.data1; // tmpXX(old 1st byte) needed for noteOff value END ELSE IF ((isConvertToNotes=false) AND (isNoteOff(tmpMsg,tmpYY)=true)) THEN BEGIN //! if convert=>Notes: tmp.data1 := mult[tmp.channel - 1][tmpXX]; // set NoteOff value from original NoteOn value END; // affect msg: convert event IF isConvert THEN BEGIN IF isConvertToNotes THEN BEGIN convertToLegatoNotes(); END ELSE BEGIN // isConvertToNotes=false IF isNoteOff(tmpMsg,tmpYY)=true THEN BEGIN isConvNoteOff:=true; // filter flag for output END ELSE BEGIN tmp.msg:=setConvert(tmpMsg, convert); END; END; END; IF isFirstByte OR (isFirstByte AND isSecondByte) THEN BEGIN filterByte:= tmpX; END ELSE BEGIN filterByte:= tmpY; END; IF (isConvNoteOff) OR // if convert to notes dont convert noteOff ((limit=1)AND(limitLo>filterByte)) OR (limitHi<filterByte) OR (step>1)AND(isStep(filterByte,step)) THEN BEGIN // filter message lenCount:=lenCount-1; END ELSE BEGIN IF isNoteOn(tmp.msg,tmp.data2) THEN BEGIN countNotes(true); END ELSE IF isNoteOff(tmp.msg,tmp.data2) THEN BEGIN countNotes(false); END; setLength(pOut,lenCount); setMidiArrayValue(pOut, i, tmp); // write modified message END; END // ! isMidiMsg() ELSE BEGIN IF (getValue(pThru)>0) THEN BEGIN setMidiArrayValue(pOut, i, tmp); END ELSE BEGIN setLength(pOut, 0); END; END; END; // (FOR i := 0 .. END ELSE BEGIN setLength(pOut, 0); END; // len = 0 setValue(pNotesCount, notesCount);setDataSymbols();END.
]]>
trying it with vsti's bSorks noteoff solution works great, here from a first try.
http://thaumat.org/_permShare/mp3/mep.draft.t1.mp3
used: a piano roll with a drum pattern, ephonic drumatic , tpmusic t-pulse, and 2x mep script for each plugin.
]]>
its not a MEP-4 emulation, btw. its an experimental midi tool to learn the script language,
the only thing it has in common are most parameters and some of their ranges.
i try to avoid the hanging notes problem which the MEP-4 does not. on the other hand the MEP features channel filter/routing, mid delay, data presetting or stuff regarding its 4 ins and outs. personally I like to deal with channel filtering before or after a midi effect, midi delay already exists as a script and if you'd need 4in/outs you can use 4 instances of the script.
do you think if something essential from the MEP-4 is missing in the script?
guess i'll post a running beta with all features implemented here. to finish this script it will take some more time..
]]>
Statistics: Posted by senso — 04 Nov 2008, 21:29
Statistics: Posted by woodslanding — 04 Nov 2008, 20:10
Statistics: Posted by amiga909 — 04 Nov 2008, 15:55
CODE:
setIsInput(NotesCount,false);bysetIsInput(pNotesCount,false);CODE:
setIsInput(pIn,false);is totally equivalent to setIsInput(0,false);Statistics: Posted by senso — 04 Nov 2008, 14:17
CODE:
(* MEP 0.037// 2008-11-29: @amiga909// credits to: bSork, senso (www.sensomusic.com/forums)// <Description>// MEP: midi tool roughly inspired by Yamaha MEP-4 Midi Event Processor. download the original MEP-4 manual here: http://www2.yamaha.co.jp/manual/pdf/emi/english/synth/MEP4E.PDF // Features: prevent hanging notes, swap data bytes, convert to notes or system messages, ..// Processing chain: <input> -> select -> // filter input (if midi thru on: pass filtered data) inverse -> swapBytes -> // modify input bytes expand -> transpose -> // calculate input bytes wrap -> step(x%y) -> limit -> // limit/rescale input bytes affectByte -> // modify output bytes convert -> // change output type <output> <-// <Parameters> // select event : select an event type for processing // midi thru : output all other event types. !{sysex, clock and activesensing are always filtered}// affect byte : process first or second data byte or both. if only one byte is selected, then the other byte is output unprocessed.// inv. byte1/2 : inverse incoming data byte. high value -> low, low -> high.// swap bytes : swap first and second byte (eg. convert velocity to noteOn).// expand : choose a multiplication factor between 0.0625 to 16// transpose : add or subtract value// wrap around : if a value as a result of expanding or transposing is not between 1 and 128, and if wrap around is off: result is 128 or 1, if wrap around is on: (1-1=128;128+1=1;).// step : only messages with a data byte that is a multiple of the Step will be allowed through.// limit low/hi : specify the output data range. use a range fader (interface design) for control. // limit mode : limit hard limits the output and filters values out of range scale rescales to desired range: value/128 => value/newRange // convert : transform input event to CC, PC, PitchBend or Aftertouch// <Notes> Aftertouch, PC and other messages are not tested. *)VAR pIn, pOut, pMult, pEvent, pDest, pWrap,pThru, pSwap, pInv1, pInv2, pTransp, pNotesCount, pStep, pLimitHi, pLimitLo, pLimit, pConv : TParameter;TYPE TMult = ARRAY OF integer;VAR mult : ARRAY OF TMult;VAR pMultAr: ARRAY OF double; TYPE TBool = ARRAY OF boolean; // convert -> notesVAR notesList: ARRAY OF TBool;VAR tmp: TMidi; VAR multiply: double; VAR len, i, tmpX ,tmpY, tmpXX, tmpDest,tmpMsg, transpose, limit, convert, tmpEvent,limitLo, limitHi, tmpLimitLo, step, notesCount,filterByte,lenCount: integer; VAR isNoteOff, isNoteOn, isSwap, isThru,isWrap, isStepActive, isInv1,isInv2,isConvert,isLimit,isFirstByte,isSecondByte: boolean;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE Init;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// VAR f_c1,f_c2,f_c3: integer;BEGINpIn := createParam('midi in', ptMidi); pOut := createParam('midi out', ptMidi); pEvent := createParam('select', ptListBox); setListBoxString(pEvent, '"ALL","Note On+Off", "Control Change", "Program Change", "Pitch Bend", "Aftertouch"'); pInv1 := createParam('inverse byte1', ptSwitch); pInv2 := createParam('inverse byte2', ptSwitch); pSwap := createParam('swap bytes', ptSwitch); pMult := createParam('expand', ptListBox); setListBoxString(pMult, ' "-bypass-", "1/16", "1/8", "1/4", "1/2","1","2","4","8","16"'); pMultAr:=[1, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16]; pTransp := createParam('transpose',ptDataFader);setMin(pTransp,-128);setMax(pTransp,127); setSymbol(pTransp, '');setFormat(pTransp,'-bypass-'); pStep := createParam('step', ptDataFader);setMin(pStep,1);setMax(pStep, 16); setSymbol(pStep,'');setFormat(pStep,'-bypass-'); pWrap := createParam('wrap around', ptSwitch); pLimitLo := createParam('lo limit', ptMidiNoteFader); setMax(pLimitLo, 126); setMin(pLimitLo, 0); pLimitHi := createParam('hi limit', ptMidiNoteFader); setMin(pLimitHi, 1);setMax(pLimitHi, 127); pLimit := createParam('limit mode', ptListBox); setListBoxString(pLimit,'"-bypass-","filter","rescale","compress"');// if compress: faders = ratio, threshold setValue(pLimit,1); setDefaultValue(pLimit,0);pDest := createParam('affect byte', ptListBox); setListBoxString(pDest,'"1(note,cc.num,..)", "2(velo,cc.val,..)", "1 + 2"'); pConv := createParam('convert', ptListBox); // take all from 'Create Midi?' setListBoxString(pConv, '"-bypass-","Note Legato","Note Poly (mode1)","Note Poly (mode2)","Control Change","Program Change", "Pitch Bend","Aftertouch Channel","Aftertouch Poly", "Timing Clock","Start","Stop","Continue","Active Sensing", "NoteOn !","NoteOff !"');pThru := createParam('midi thru', ptSwitch); pNotesCount := createParam('open notes', ptDataField); setReadOnly(pNotesCount,true); setMin(pNotesCount,0);setMax(pNotesCount,999);f_c1:= 90099;f_c2:= 60099;f_c3:= 6666666; setColor(pLimitHi,f_c1); setColor(pLimitLo,f_c1);setColor(pTransp,f_c1);setColor(pStep,f_c1); setColor(pWrap,f_c2);setColor(pThru,f_c2);setColor(pInv1,f_c2);setColor(pInv2,f_c2);setColor(pSwap,f_c2); //setColor(pNotesCount,f_c3);setIsInput(pOut,false);setIsInput(NotesCount,false);setIsOutput(pIn,false);setIsOutput(pEvent,false);setIsOutput(pThru,false);setIsOutput(pDest,false);setIsOutput(pInv1,false);setIsOutput(pInv2,false);setIsOutput(pSwap,false);setIsOutput(pMult,false);//setIsOutput(pMultSw,false);setIsOutput(pTransp,false);//setIsOutput(pTranspSw,false);setIsOutput(pStep,false);//setIsOutput(pStepSw,false);setIsOutput(pWrap,false);setIsOutput(pLimit,false);setIsOutput(pLimitLo,false);setIsOutput(pLimitHi,false);setIsOutput(pConv,false); setValue(pEvent,1); setValue(pDest,0); setValue(pInv1,0);setValue(pInv2,0);setValue(pMult,GetLength(pMult)-1);setDefaultValue(pMult,0);setValue(pTransp,-128);setValue(pStep,1); setValue(pWrap,0);setValue(pLimitLo,0);setValue(pLimitHi,127); setValue(pLimit,0);setValue(pConv,0);setArrayLength(mult, 16); setArrayLength(notesList, 16); FOR i := 0 TO 15 DO BEGIN setArrayLength(mult[i], 128); // init [16 channels][128 multiply values] SetArrayLength(notesList[i], 128); END;notesCount:=0;END; // <F> isMidiMsg: 'event' is a list of pEvent (0=ALL). filters sysex, clock and system messages//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isMidiMsg(msg: integer; e: integer): boolean;BEGIN IF ((e=1)OR(e=0)) AND ((msg=144)OR(msg=128)) THEN BEGIN result := true; END // Notes ELSE IF ((e=2)OR(e=0)) AND (msg=176) THEN BEGIN result := true; END //CtrlChg ELSE IF ((e=3)OR(e=0)) AND (msg=192) THEN BEGIN result := true; END //PrgrChg ELSE IF ((e=4)OR(e=0)) AND (msg=224) THEN BEGIN result := true; END //PitchBend ELSE IF ((e=5)OR(e=0)) AND ((msg=208) OR (msg=160)) THEN BEGIN result := true; END //Aftertouch (Channel, Poly) ELSE BEGIN result:=false; END;END;// <F> setWrap: wrap or limit byte range //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION setWrap (t: integer; wrap: boolean): integer; // 777777 rename 'setWrap' BEGIN IF t < 0 THEN BEGIN t:=t*(-1); END; IF t > 127 THEN BEGIN IF isWrap THEN BEGIN result := round((t+1) MOD 128); // TEST END ELSE BEGIN result:= 127; END; END ELSE IF t < 0 THEN BEGIN /// ADD for transpose! END ELSE BEGIN result:=t; END; END;// : setTransp: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION setTransp(val: integer; trsp: integer; wrap: boolean):integer;BEGIN val:=val+trsp; IF ((val>127)OR(val<0)) THEN BEGIN IF isWrap THEN BEGIN IF val>127 THEN BEGIN result:=val-127; END ELSE BEGIN result:=val+127; END; END ELSE BEGIN IF val>127 THEN BEGIN result:=127; END ELSE BEGIN // val<0 result:=0; END; END; END ELSE BEGIN result:=0; END;END;// <F> calcLimited: returns multiplication value for limiting, or return 1 (x*1=x; x>0)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION calcLimited (val:integer; lo:integer; hi:integer; mode:integer):double; BEGIN IF mode=0 THEN BEGIN result:=1 END //IF (val<=hi)AND(val>=lo) ELSE IF mode=1 THEN BEGIN result:= (hi-lo)/128; //result:= (lo + round(val*r) - 1); END ELSE IF mode = 2 THEN BEGIN // compress END ELSE BEGIN result:=1 END;END; // <F> isStep: step limit data: filter out (? or compress->max, or compress->min)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isStep(val: integer; stp: integer):boolean;BEGIN IF ((val+1) MOD stp=0) THEN BEGIN result:=true; END ELSE BEGIN result:=false; END;END;// <F> convert//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// // setConvert: (why not convert to: clock , start, stop, cont?)FUNCTION setConvert(srcMsg:byte;typeId:integer):byte;BEGIN(*IF = notes: Note Legato","Note Poly (mode1)","Note Poly (mode2) 1-3; CC = 4generate events! if replace with current, loose data...Legato: if new event arrives, last event is closed: if not first event generate noteOff for the previous event structures: noteConvertTmpMidi:= tmp // just before FOR loop ends.Mode1: if new event arrives, add_output noteOFF for oldest even noteOn, or if it is the only note present. remove closed note from array structures: event array: boolean[chn][note] for currently open notes // ..already got this?Mode2: if new event arrives, add_output noteOFF for oldest odd noteOn, or if it there are only even notes. remove closed note from array -> even notes will endure until all odd notes are finished*) IF typeId=4 THEN BEGIN result:=176; // CC END ELSE IF typeId=14 THEN BEGIN result:=144; // leg play: send noteOff Value if next noteOn value arrives tmp.Msg var holding last value END ELSE IF typeId=5 THEN BEGIN result:=192; // PC (no 2nd byte) END ELSE IF typeId=6 THEN BEGIN result:=224; // PitchBend (Modwheel) END ELSE IF typeId=7 THEN BEGIN result:=208; // Aftert Channel END ELSE IF typeId=8 THEN BEGIN result:=160; // Aftert Poly END ELSE BEGIN result:=srcMsg; END; END;(*// <F>getOpenNotes//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//FUNCTION getOpenNotes():integer;// global variables!!BEGIN //boolNotesArray[][] END; //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//// <P> writeNoteOff: for convert to Notes//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//PROCEDURE writeNoteOff;// global variables!!VAR extra: TMidi;BEGIN extra.msg:=byte(128); extra.data1:=tmp.data1; extra.data2:=0; extra.channel:=tmp.channel; // write out //setLength(pOut):= getLength(pOut)+1; // ?? TEST!! ?? setMidiArrayData(pOut,trunc(getLength(pOut))-1),extraMidi) // remove from array //NotesArray[aVal]:= false; //countNotes(false); END; *)// <P> countNotes: PROCEDURE countNotes(isANoteAndON:boolean);//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//BEGIN IF isANoteAndON THEN BEGIN notesCount:=notesCount+1 END ELSE BEGIN notesCount:=notesCount-1; END; END;// <P> initLimitRange: expect: limitHi and limitLo are getValue(p) //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE initLimitRange;BEGIN IF limitHi-limitLo< 0 THEN BEGIN tmpLimitLo:=limitLo; limitLo:=limitHi; limitHi:=tmpLimitLo END ELSE IF limitHi-limitLo=0 THEN BEGIN IF limitHi=128 THEN BEGIN limitLo:=127; END ELSE BEGIN limitHi:=limitHi+1; END; END; END; // <P> setDataSymbols: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE setDataSymbols;VAR tr: integer;BEGIN IF (trunc(getValue(pStep))<= 1) THEN BEGIN setSymbol(pStep,'');setFormat(pStep,'-bypass-'); END ELSE BEGIN setSymbol(pStep,'%'); setFormat(pStep,'%.0f') END; tr:=trunc(getValue(pTransp)); IF tr>0 THEN BEGIN setSymbol(pTransp,'+'); setFormat(pTransp,'%.0f'); END ELSE IF (tr=(-128))OR(tr=0) THEN BEGIN setSymbol(pTransp,''); setFormat(pTransp,'-bypass-'); END ELSE BEGIN setSymbol(pTransp,'');setFormat(pTransp,'%.0f'); END; (* IF trunc(getValue(pLimitHi)) = 127 THEN BEGIN setColor(pLimitHi,81099); END ELSE BEGIN setColor(pLimitHi,90099); END; IF trunc(getValue(pLimitLo)) = 0 THEN BEGIN setOffColor(pLimitLo,90099); END ELSE BEGIN // setColor(pLimitLo,90099); END; *) END;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| // main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^|BEGIN len := getLength(pIn); lenCount:= len; IF len > 0 THEN BEGIN multiply:=pMultAr[trunc(getValue(pMult))]; transpose:= trunc(getValue(pTransp)); IF (transpose=(-128)) THEN BEGIN // control: bypass at the left end for convenience transpose:=0; END; tmpEvent:=trunc(getValue(pEvent)); tmpDest:=trunc(getValue(pDest)); isFirstByte:=(tmpDest=2)OR(tmpDest=0); isSecondByte:=(tmpDest=2)OR(tmpDest=1); limitLo:=trunc(getValue(pLimitLo)); limitHi:=trunc(getValue(pLimitHi)); limit:=trunc(getValue(pLimit)); isLimit:=(limit>0) AND ((limitLo>=1)AND(limitHi<=126)); initLimitRange(); convert:=trunc(getValue(pConv)); step:=trunc(getValue(pStep)); isStepActive:=step>1; isInv1:=getValue(pInv1)>0; isInv2:=getValue(pInv2)>0; isSwap:=getValue(pSwap)>0; isWrap:=trunc(getValue(pWrap))>0; isThru:=trunc(getValue(pThru))>0; FOR i := 0 TO (len - 1) DO BEGIN getMidiArrayValue(pIn, i, tmp); IF isMidiMsg(tmp.msg, tmpEvent) THEN BEGIN // process on IF. if pThru is 1, process on ELSE tmpMsg :=tmp.msg; tmpX := tmp.data1; tmpY := tmp.data2; tmpXX := tmpX; // store global for note off array IF isInv1 THEN BEGIN tmpX:= 127 - tmpX;END; IF isInv2 THEN BEGIN tmpY:= 127 - tmpY;END; IF isSwap THEN BEGIN tmpX := tmpY; tmpY := tmpXX; END; // affect byte 1 IF isFirstByte THEN BEGIN // 1st byte: process 1st+2nd byte if tmpDest=2 tmpX := trunc(multiply*tmpX) + (transpose); tmpX := setWrap(tmpX, isWrap); IF isLimit THEN BEGIN tmpX := trunc(tmpX * calcLimited(tmpX,limitLo,limitHi,limit)); END; tmp.data1 := byte(tmpX); END; // affect byte 2 IF isSecondByte THEN BEGIN // 2nd byte: process 1st+2nd byte if tmpDest=2 tmpY := trunc(multiply*tmpY) + (transpose); tmpY := setWrap(tmpY, isWrap); IF isLimit THEN BEGIN tmpY := trunc(tmpY * calcLimited(tmpY,limitLo,limitHi,limit)); END; tmp.data2 := byte(tmpY); END; // affect msg: convert event IF isConvert THEN BEGIN tmp.msg := setConvert(tmp.msg,convert); END; isNoteOff:= (tmpMsg=128) OR ((tmpMsg=144)AND(tmpY=0)); isNoteOn := (isNoteOff=false)AND((tmpMsg=144)AND(tmpY>0)); // only when Notes are output // prevent hanging notes (tmpXX for swapped note values); ! if convert=>Notes: IF isNoteOn THEN BEGIN mult[tmp.channel - 1][tmpXX] := tmp.data1; // tmpXX(old 1st byte) needed for noteOff value IF notesList[tmp.channel - 1][tmpXX]=false THEN BEGIN notesList[tmp.channel - 1][tmpXX]:= true; END; //IF ((convert<=3)AND(convert>0)) THEN BEGIN //addToBoolNotes(tmp.data1, tmp.channel, tmpXX); // END //ELSE BEGIN //END; END ELSE IF isNoteOff THEN BEGIN // ! if convert=>Notes: tmp.data1 := mult[tmp.channel - 1][tmpXX]; // set NoteOff value from original NoteOn value IF notesList[tmp.channel - 1][tmpXX]=true THEN BEGIN notesList[tmp.channel - 1][tmpXX]:=false; END; END; IF isFirstByte OR (isFirstByte AND isSecondByte) THEN BEGIN filterByte:= tmpX; END //(tempDest=1) ELSE BEGIN filterByte:= tmpY; END; IF ((limit=1) AND ((limitLo>filterByte) OR (limitHi<filterByte))) OR (isStepActive AND(isStep(filterByte,step)=true)) THEN BEGIN // filter message lenCount:=lenCount-1; END ELSE BEGIN IF isNoteOn THEN BEGIN countNotes(true); END ELSE IF isNoteOff THEN BEGIN countNotes(false); END; setLength(pOut,lenCount); setMidiArrayValue(pOut, i, tmp); // write modified message END; END // ! isMidiMsg() ELSE BEGIN IF isThru THEN BEGIN setMidiArrayValue(pOut, i, tmp); END ELSE BEGIN setLength(pOut, 0); END; END; END; // (FOR i := 0 .. END ELSE BEGIN setLength(pOut, 0); END; // len = 0 setValue(pNotesCount, notesCount);setDataSymbols();END.Statistics: Posted by amiga909 — 04 Nov 2008, 08:47
CODE:
(* no compress, no multinoteConvertMEP 0.95 by amiga909// 2008-10-29: first version// 2008-11-12: release version// credits to: bSork, senso (www.sensomusic.com/forums)// <Description> midi tool roughly inspired by Yamaha MEP-4 Midi Event Processor. MEP was designed for experimental purposes but it can be also used as a midi utility. download the original MEP-4 manual here: http://www2.yamaha.co.jp/manual/pdf/emi/english/synth/MEP4E.PDF // <Features> prevent hanging notes, swap data bytes, convert to notes, ..// <Processing chain> <input> -> select -> // filter input (if midi thru on: pass filtered data) swapBytes -> inverse -> // modify input bytes expand -> transpose -> // calculate input bytes wrap -> step(x%y) -> limit -> // limit/rescale input bytes affectByte -> // modify output bytes convert -> // change output type <output> <-// <Parameters> // select event : select an event type for processing (if event type is NoteOn+Off and convert is not bypassed, NoteOff messages are filtered in some cases). // mute : filter selected event. // midi thru : output all other event types. // affect byte : process first or second data byte or both. if only one byte is selected, then the other byte is output unprocessed.// inv. byte1/2 : inverse incoming data byte. high value -> low, low -> high.// swap bytes : swap first and second byte (eg. convert velocity to noteOn).// expand : choose a multiplication factor from 0.0625 to 16.// transpose : add or subtract value.// wrap around : if a value as a result of expanding or transposing is not between 1 and 128, and if wrap around is off: result is 128 or 1, if wrap around is on: (1-1=128;128+1=1;).// step : only messages with a data byte that is a multiple of the Step will be allowed through.// limit low/hi : specify the output data range. use a range fader (interface design) for control. // limit mode : limit hard limits the output and filters values out of range, scale rescales to desired range.// convert : transform input event to another midi message. (mode 1: convert to notes: even events are noteOns, odd events are noteOffs hanging notes; in contrary NoteOff! or NoteOn! create invalid midi notes). // <Notes> - Aftertouch, PC and other messages are not tested as much as Notes and CC - compress is very beta. maybe it will be removed... (not original MEP anyway) - the interface will maybe be reworked. there is the idea to have 8 toggle buttons that can each hold an operation parameter (à la instajungle).*)VAR pIn, pOut, pMult, pEvent, pDest, pWrap, pInv1, pInv2, pTransp, pBypass, pNotesCount, pThru, pConv, pStep, pLimitHi, pLimitLo, pLimit, pSwap : TParameter;TYPE TMult = ARRAY OF integer;VAR mult : ARRAY OF TMult;VAR pMultAr : ARRAY OF double; VAR tmp : TMidi; VAR multiply : double; VAR len,i,tmpX,tmpY,tmpXX,tmpYY,tmpMsg,dest,transpose,limit,convert,event,step,convCnt, limitLo,limitHi,tmpLimitLo, notesCount,filterByte,lenCount,lastConvNoteOn: integer; VAR isSwap, isWrap, isInv1, isInv2, isLimit, isConvNoteOff, isFirstByte, isSecondByte, isConvert, isConvertToNotes : boolean;CONST NOTE_ON = 144;CONST NOTE_OFF = 128;CONST CONTROL_CHG = 176;CONST PROGRAM_CHG = 192;CONST PITCHBEND = 224;CONST AFTER_MONO = 208;CONST AFTER_POLY = 160;CONST MIDI_CLOCK = 248;CONST MIDI_START = 250;CONST MIDI_CONT = 251;CONST MIDI_STOP = 252;CONST ACTIVE_SENS = 254;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE Init;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// VAR f_c1,f_c2: integer; BEGIN pIn := createParam('midi in', ptMidi); pOut := createParam('midi out', ptMidi); pEvent := createParam('select', ptListBox); setListBoxString(pEvent, '"ALL","Note On+Off", "Control Change", "Program Change", "Pitch Bend", "Aftertouch"'); pInv1 := createParam('inverse byte1', ptSwitch); pInv2 := createParam('inverse byte2', ptSwitch); pSwap := createParam('swap bytes', ptSwitch); pMult := createParam('expand', ptListBox); setListBoxString(pMult, ' "-bypass-", "1/16", "1/8", "1/4", "1/2","1","2","4","8","16"'); pMultAr:=[1, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16]; pTransp := createParam('transpose',ptDataFader);setMin(pTransp,-128);setMax(pTransp,127); setSymbol(pTransp, '');setFormat(pTransp,'-bypass-'); pWrap := createParam('wrap around', ptSwitch); pStep := createParam('step modulo', ptDataFader);setMin(pStep,1);setMax(pStep, 16); setSymbol(pStep,'');setFormat(pStep,'-bypass-'); pLimitLo := createParam('lo limit', ptMidiNoteFader); setMax(pLimitLo, 126); setMin(pLimitLo, 0); pLimitHi := createParam('hi limit', ptMidiNoteFader); setMin(pLimitHi, 1);setMax(pLimitHi, 127); pLimit := createParam('limit mode', ptListBox); setListBoxString(pLimit,'"-bypass-","filter","rescale","compress"');// if compress: faders = ratio, threshold setValue(pLimit,1); setDefaultValue(pLimit,0);pDest := createParam('affect', ptListBox); setListBoxString(pDest,'"1st byte (note,cc.num,..)", "2nd byte (velo,cc.val,..)", "1st and 2nd byte"'); pConv := createParam('convert', ptListBox); // take all from 'Create Midi?' setListBoxString(pConv, '"-bypass-","Note Legato","Note Poly (mode1)","Note Poly (mode2)","Note Poly (random)",'+ '"Control Change","Program Change", "Pitch Bend","Aftertouch Channel","Aftertouch Poly",'+ '"Timing Clock","Start","Stop","Continue","Active Sensing", "NoteOn !","NoteOff !"');pThru := createParam('midi thru', ptSwitch); pBypass := createParam('mute', ptSwitch); pNotesCount := createParam('open notes', ptDataField); setReadOnly(pNotesCount,true); setMin(pNotesCount,0);setMax(pNotesCount,999);f_c1:= 90099;f_c2:= 60099; setColor(pLimitHi,f_c1);setColor(pLimitLo,f_c1);setColor(pTransp,f_c1);setColor(pThru,f_c2);setColor(pStep,f_c1);setColor(pWrap,f_c2);setColor(pInv1,f_c2);setColor(pInv2,f_c2);setColor(pSwap,f_c2);setIsInput(pOut,false);setIsInput(pNotesCount,false);setIsOutput(pIn,false);setIsOutput(pEvent,false);setIsOutput(pThru,false);setIsOutput(pDest,false);setIsOutput(pInv1,false);setIsOutput(pInv2,false);setIsOutput(pSwap,false);setIsOutput(pMult,false); setIsOutput(pTransp,false); setIsOutput(pStep,false); setIsOutput(pWrap,false);setIsOutput(pLimit,false);setIsOutput(pLimitLo,false);setIsOutput(pLimitHi,false);setIsOutput(pConv,false); setIsOutput(pBypass,false); setValue(pEvent,1); setValue(pDest,0); setValue(pInv1,0);setValue(pInv2,0);setValue(pMult,GetLength(pMult)-1);setDefaultValue(pMult,0);setValue(pTransp,-128);setValue(pStep,1); setValue(pWrap,0);setValue(pLimitLo,0);setValue(pLimitHi,127); setValue(pLimit,0);setValue(pConv,0);// array initsetArrayLength(mult, 16); FOR i := 0 TO 15 DO BEGIN setArrayLength(mult[i], 128); // init [16 channels][128 multiply values] END;notesCount:=0;convCnt:=0;END; //^^^^^^^^^^^^^^^^^^^^^^^<F>^^^^^^^^^^^^^^^^^^^^^//// <F> isMidiMsg: 'event' is a list of pEvent (0=ALL)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isMidiMsg(msg: integer; e: integer): boolean;BEGIN IF ((e=1)OR(e=0)) AND ((msg=NOTE_ON)OR(msg=NOTE_OFF)) THEN BEGIN result := true; END // Notes ELSE IF ((e=2)OR(e=0)) AND (msg=CONTROL_CHG) THEN BEGIN result := true; END //CtrlChg ELSE IF ((e=3)OR(e=0)) AND (msg=PROGRAM_CHG) THEN BEGIN result := true; END //PrgrChg ELSE IF ((e=4)OR(e=0)) AND (msg=PITCHBEND) THEN BEGIN result := true; END //PitchBend ELSE IF ((e=5)OR(e=0)) AND ((msg=AFTER_MONO) OR (msg=AFTER_POLY)) THEN BEGIN result := true; END //Aftertouch (Channel, Poly) ELSE BEGIN result:=false; END; END;// <F> isStep: step limit data: filter out (? or compress->max, or compress->min)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isStep(val: integer; stp: integer): boolean;BEGIN IF NOT((val+1) MOD stp=0) THEN BEGIN result:=true; END ELSE BEGIN result:=false; END;END;// <F> isNoteOn: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isNoteOn(message: byte;velocity: byte): boolean;BEGIN IF (message=NOTE_ON)AND(velocity>0) THEN BEGIN result:=true; END ELSE BEGIN result:=false; END;END;// <F> isNoteOff: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isNoteOff(message: byte;velocity: byte): boolean;BEGIN IF (message=NOTE_OFF)OR((message=NOTE_ON)AND(velocity=0)) THEN BEGIN result:=true; END ELSE BEGIN result:=false; END;END;// <F> setWrap: wrap or limit byte range //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION setWrap (t: integer; wrap: boolean): integer; // 777777 rename 'setWrap' BEGIN IF isWrap THEN BEGIN IF t<0 THEN BEGIN result:= 128 - ((t*-1) MOD 128); END ELSE IF t>127 THEN BEGIN result := t MOD 128; END ELSE BEGIN result:=t; END; END ELSE BEGIN IF t<0 THEN BEGIN result:=0; END ELSE IF t>127 THEN BEGIN result:=127; END ELSE BEGIN result:=t; END; END;END;// : setTransp: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION setTransp(val: integer; trsp: integer; wrap: boolean):integer;BEGIN val:=val+trsp; IF ((val>127)OR(val<0)) THEN BEGIN IF isWrap THEN BEGIN IF val>127 THEN BEGIN result:=val-127; END ELSE BEGIN result:=val+127; END; END ELSE BEGIN IF val>127 THEN BEGIN result:=127; END ELSE BEGIN // val<0 result:=0; END; END; END ELSE BEGIN result:=0; END;END;// <F> convert://^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// // setConvert: (why not convert to: clock , start, stop, cont?)FUNCTION setConvert(srcMsg: byte;typeId: integer): byte;BEGINCASE typeId OF 0: result:=srcMsg; // bypass 1: result:=NOTE_ON; // NotesLegato 2: result:=NOTE_ON; // NotesPoly1 3: result:=NOTE_ON; // NotesPoly2 4: result:=NOTE_ON; // NotesPoly rnd 5: result:=CONTROL_CHG; 6: result:=PROGRAM_CHG; 7: result:=PITCHBEND; //(Modwheel) 8: result:=AFTER_MONO; // Aftert Channel 9: result:=AFTER_POLY; // Aftert Poly 10: result:=MIDI_CLOCK; 11: result:=MIDI_START; 12: result:=MIDI_STOP; 13: result:=MIDI_CONT; 14: result:=ACTIVE_SENS; 15: result:=NOTE_ON; // NoteON ONLY! 16: result:=NOTE_OFF; // NoteOFF ONLY! ELSE BEGIN result:=srcMsg; END;END;END;// <F> calcLimited: returns multiplication value for limiting, or return 1 (x*1=x; x>0)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION calcLimited (val: integer;lo: integer;hi: integer;mode: integer): integer; VAR r : double; BEGIN //writeln('val:'+inttostr(val)) IF mode<=1 THEN BEGIN // filter, bypass, not used here result:=val; END ELSE IF mode=2 THEN BEGIN // scale r := 128/(hi-lo); val:= trunc((val+1)/r); result:= val+lo; END ELSE BEGIN result:=val; END;END; //^^^^^^^^^^^^^^^^^^^^^^^<P>^^^^^^^^^^^^^^^^^^^^^//// <P> countNotes: PROCEDURE countNotes(isANoteAndON:boolean);//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//BEGIN IF isANoteAndON THEN BEGIN notesCount:=notesCount+1 END ELSE BEGIN notesCount:=notesCount-1; END; END;// <P> convertToLegatoNotes: isConvert AND isNoteOff=false (dont process noteOff for eg. note->cc)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE convertToLegatoNotes;BEGIN IF convCnt MOD 2 = 1 THEN BEGIN tmp.data1:=byte(lastConvNoteOn); tmp.msg:=byte(NOTE_OFF); END ELSE BEGIN lastConvNoteOn:=tmp.data1; tmp.msg:=byte(NOTE_ON); END; convCnt:=convCnt+1; IF convCnt=1028 THEN BEGIN convCnt:=0; END;END;// <P> initLimitRange: expect: limitHi and limitLo are getValue(p) //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE initLimitRange;BEGIN IF NOT(limit=3) THEN BEGIN // if limit is compress: thres/ratio fader functions IF limitHi-limitLo< 0 THEN BEGIN tmpLimitLo:=limitLo; limitLo:=limitHi; limitHi:=tmpLimitLo END ELSE IF limitHi-limitLo=0 THEN BEGIN IF limitHi=128 THEN BEGIN limitLo:=127; END ELSE BEGIN limitHi:=limitHi+1; END; END; END; END; // <P> setDataSymbols: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE setDataSymbols;VAR tr: integer;BEGIN IF (trunc(getValue(pStep))<= 1) THEN BEGIN setSymbol(pStep,'');setFormat(pStep,'-bypass-'); END ELSE BEGIN setSymbol(pStep,'%'); setFormat(pStep,'%.0f') END; tr:=trunc(getValue(pTransp)); IF tr>0 THEN BEGIN setSymbol(pTransp,'+'); setFormat(pTransp,'%.0f'); END ELSE IF (tr=(-128))OR(tr=0) THEN BEGIN setSymbol(pTransp,''); setFormat(pTransp,'-bypass-'); END ELSE BEGIN setSymbol(pTransp,'');setFormat(pTransp,'%.0f'); END; END;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| // main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^|BEGIN len := getLength(pIn); lenCount:= len; IF len > 0 THEN BEGIN multiply:=pMultAr[trunc(getValue(pMult))]; transpose:= trunc(getValue(pTransp)); IF (transpose=(-128)) THEN BEGIN // control: bypass at the left end for convenience transpose:=0; END; event:=trunc(getValue(pEvent)); dest:=trunc(getValue(pDest)); isFirstByte:=(dest=2)OR(dest=0); isSecondByte:=(dest=2)OR(dest=1); limit:=trunc(getValue(pLimit)); limitLo:=trunc(getValue(pLimitLo)); limitHi:=trunc(getValue(pLimitHi)); isLimit:=(limit>0) AND ((limitLo>=1)OR(limitHi<=126)); initLimitRange(); convert:=trunc(getValue(pConv)); isConvert:=convert>0; isConvertToNotes:=convert=1; isConvNoteOff:=false; step:=trunc(getValue(pStep)); isInv1:=getValue(pInv1)>0; isInv2:=getValue(pInv2)>0; isSwap:=getValue(pSwap)>0; isWrap:=getValue(pWrap)>0; FOR i := 0 TO (len - 1) DO BEGIN getMidiArrayValue(pIn, i, tmp); IF (getValue(pBypass)=0) AND (isMidiMsg(tmp.msg,event)) THEN BEGIN tmpMsg:= tmp.msg; tmpX := tmp.data1; tmpY := tmp.data2; tmpXX := tmpX; // store orig. val for noteList tmpYY := tmpY; IF isInv1 THEN BEGIN tmpX:= 127 - tmpX; END; IF isInv2 THEN BEGIN tmpY:= 127 - tmpY; END; IF isSwap THEN BEGIN tmpX := tmpY; tmpY := tmpXX; END; // affect byte 1 IF isFirstByte THEN BEGIN // 1st byte: process 1st+2nd byte if dest=2 tmpX := trunc(multiply*tmpX); tmpX := tmpX + (transpose); tmpX := setWrap(tmpX, isWrap); // wrap or hard limit IF isLimit THEN BEGIN tmpX := calcLimited(tmpX,limitLo,limitHi,limit); END; tmp.data1 := byte(tmpX); END; // affect byte 2 IF isSecondByte THEN BEGIN tmpY := trunc(multiply*tmpY); tmpY := tmpY + (transpose); tmpY := setWrap(tmpY, isWrap); // wrap or hard limit IF isLimit THEN BEGIN tmpY := calcLimited(tmpY,limitLo,limitHi,limit); END; tmp.data2 := byte(tmpY); END; // prevent hanging notes (tmpXX for swapped note values) IF isNoteOn(tmpMsg,tmpYY) THEN BEGIN mult[tmp.channel - 1][tmpXX] := tmp.data1; // tmpXX(old 1st byte) needed for noteOff value END ELSE IF ((isConvertToNotes=false) AND (isNoteOff(tmpMsg,tmpYY)=true)) THEN BEGIN //! if convert=>Notes: tmp.data1 := mult[tmp.channel - 1][tmpXX]; // set NoteOff value from original NoteOn value END; // affect msg: convert event IF isConvert THEN BEGIN IF isConvertToNotes THEN BEGIN convertToLegatoNotes(); END ELSE BEGIN // isConvertToNotes=false IF isNoteOff(tmpMsg,tmpYY)=true THEN BEGIN isConvNoteOff:=true; // filter flag for output END ELSE BEGIN tmp.msg:=setConvert(tmpMsg, convert); END; END; END; IF isFirstByte OR (isFirstByte AND isSecondByte) THEN BEGIN filterByte:= tmpX; END ELSE BEGIN filterByte:= tmpY; END; IF (isConvNoteOff) OR // if convert to notes dont convert noteOff ((limit=1)AND(limitLo>filterByte)) OR (limitHi<filterByte) OR (step>1)AND(isStep(filterByte,step)) THEN BEGIN // filter message lenCount:=lenCount-1; END ELSE BEGIN IF isNoteOn(tmp.msg,tmp.data2) THEN BEGIN countNotes(true); END ELSE IF isNoteOff(tmp.msg,tmp.data2) THEN BEGIN countNotes(false); END; setLength(pOut,lenCount); setMidiArrayValue(pOut, i, tmp); // write modified message END; END // ! isMidiMsg() ELSE BEGIN IF (getValue(pThru)>0) THEN BEGIN setMidiArrayValue(pOut, i, tmp); END ELSE BEGIN setLength(pOut, 0); END; END; END; // (FOR i := 0 .. END ELSE BEGIN setLength(pOut, 0); END; // len = 0 setValue(pNotesCount, notesCount);setDataSymbols();END.Statistics: Posted by amiga909 — 12 Nov 2008, 15:35
Statistics: Posted by amiga909 — 07 Nov 2008, 20:25
Statistics: Posted by amiga909 — 04 Nov 2008, 22:52
Statistics: Posted by senso — 04 Nov 2008, 21:29
Statistics: Posted by woodslanding — 04 Nov 2008, 20:10
Statistics: Posted by amiga909 — 04 Nov 2008, 15:55
CODE:
setIsInput(NotesCount,false);bysetIsInput(pNotesCount,false);CODE:
setIsInput(pIn,false);is totally equivalent to setIsInput(0,false);Statistics: Posted by senso — 04 Nov 2008, 14:17
CODE:
(* MEP 0.037// 2008-11-29: @amiga909// credits to: bSork, senso (www.sensomusic.com/forums)// <Description>// MEP: midi tool roughly inspired by Yamaha MEP-4 Midi Event Processor. download the original MEP-4 manual here: http://www2.yamaha.co.jp/manual/pdf/emi/english/synth/MEP4E.PDF // Features: prevent hanging notes, swap data bytes, convert to notes or system messages, ..// Processing chain: <input> -> select -> // filter input (if midi thru on: pass filtered data) inverse -> swapBytes -> // modify input bytes expand -> transpose -> // calculate input bytes wrap -> step(x%y) -> limit -> // limit/rescale input bytes affectByte -> // modify output bytes convert -> // change output type <output> <-// <Parameters> // select event : select an event type for processing // midi thru : output all other event types. !{sysex, clock and activesensing are always filtered}// affect byte : process first or second data byte or both. if only one byte is selected, then the other byte is output unprocessed.// inv. byte1/2 : inverse incoming data byte. high value -> low, low -> high.// swap bytes : swap first and second byte (eg. convert velocity to noteOn).// expand : choose a multiplication factor between 0.0625 to 16// transpose : add or subtract value// wrap around : if a value as a result of expanding or transposing is not between 1 and 128, and if wrap around is off: result is 128 or 1, if wrap around is on: (1-1=128;128+1=1;).// step : only messages with a data byte that is a multiple of the Step will be allowed through.// limit low/hi : specify the output data range. use a range fader (interface design) for control. // limit mode : limit hard limits the output and filters values out of range scale rescales to desired range: value/128 => value/newRange // convert : transform input event to CC, PC, PitchBend or Aftertouch// <Notes> Aftertouch, PC and other messages are not tested. *)VAR pIn, pOut, pMult, pEvent, pDest, pWrap,pThru, pSwap, pInv1, pInv2, pTransp, pNotesCount, pStep, pLimitHi, pLimitLo, pLimit, pConv : TParameter;TYPE TMult = ARRAY OF integer;VAR mult : ARRAY OF TMult;VAR pMultAr: ARRAY OF double; TYPE TBool = ARRAY OF boolean; // convert -> notesVAR notesList: ARRAY OF TBool;VAR tmp: TMidi; VAR multiply: double; VAR len, i, tmpX ,tmpY, tmpXX, tmpDest,tmpMsg, transpose, limit, convert, tmpEvent,limitLo, limitHi, tmpLimitLo, step, notesCount,filterByte,lenCount: integer; VAR isNoteOff, isNoteOn, isSwap, isThru,isWrap, isStepActive, isInv1,isInv2,isConvert,isLimit,isFirstByte,isSecondByte: boolean;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE Init;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// VAR f_c1,f_c2,f_c3: integer;BEGINpIn := createParam('midi in', ptMidi); pOut := createParam('midi out', ptMidi); pEvent := createParam('select', ptListBox); setListBoxString(pEvent, '"ALL","Note On+Off", "Control Change", "Program Change", "Pitch Bend", "Aftertouch"'); pInv1 := createParam('inverse byte1', ptSwitch); pInv2 := createParam('inverse byte2', ptSwitch); pSwap := createParam('swap bytes', ptSwitch); pMult := createParam('expand', ptListBox); setListBoxString(pMult, ' "-bypass-", "1/16", "1/8", "1/4", "1/2","1","2","4","8","16"'); pMultAr:=[1, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16]; pTransp := createParam('transpose',ptDataFader);setMin(pTransp,-128);setMax(pTransp,127); setSymbol(pTransp, '');setFormat(pTransp,'-bypass-'); pStep := createParam('step', ptDataFader);setMin(pStep,1);setMax(pStep, 16); setSymbol(pStep,'');setFormat(pStep,'-bypass-'); pWrap := createParam('wrap around', ptSwitch); pLimitLo := createParam('lo limit', ptMidiNoteFader); setMax(pLimitLo, 126); setMin(pLimitLo, 0); pLimitHi := createParam('hi limit', ptMidiNoteFader); setMin(pLimitHi, 1);setMax(pLimitHi, 127); pLimit := createParam('limit mode', ptListBox); setListBoxString(pLimit,'"-bypass-","filter","rescale","compress"');// if compress: faders = ratio, threshold setValue(pLimit,1); setDefaultValue(pLimit,0);pDest := createParam('affect byte', ptListBox); setListBoxString(pDest,'"1(note,cc.num,..)", "2(velo,cc.val,..)", "1 + 2"'); pConv := createParam('convert', ptListBox); // take all from 'Create Midi?' setListBoxString(pConv, '"-bypass-","Note Legato","Note Poly (mode1)","Note Poly (mode2)","Control Change","Program Change", "Pitch Bend","Aftertouch Channel","Aftertouch Poly", "Timing Clock","Start","Stop","Continue","Active Sensing", "NoteOn !","NoteOff !"');pThru := createParam('midi thru', ptSwitch); pNotesCount := createParam('open notes', ptDataField); setReadOnly(pNotesCount,true); setMin(pNotesCount,0);setMax(pNotesCount,999);f_c1:= 90099;f_c2:= 60099;f_c3:= 6666666; setColor(pLimitHi,f_c1); setColor(pLimitLo,f_c1);setColor(pTransp,f_c1);setColor(pStep,f_c1); setColor(pWrap,f_c2);setColor(pThru,f_c2);setColor(pInv1,f_c2);setColor(pInv2,f_c2);setColor(pSwap,f_c2); //setColor(pNotesCount,f_c3);setIsInput(pOut,false);setIsInput(NotesCount,false);setIsOutput(pIn,false);setIsOutput(pEvent,false);setIsOutput(pThru,false);setIsOutput(pDest,false);setIsOutput(pInv1,false);setIsOutput(pInv2,false);setIsOutput(pSwap,false);setIsOutput(pMult,false);//setIsOutput(pMultSw,false);setIsOutput(pTransp,false);//setIsOutput(pTranspSw,false);setIsOutput(pStep,false);//setIsOutput(pStepSw,false);setIsOutput(pWrap,false);setIsOutput(pLimit,false);setIsOutput(pLimitLo,false);setIsOutput(pLimitHi,false);setIsOutput(pConv,false); setValue(pEvent,1); setValue(pDest,0); setValue(pInv1,0);setValue(pInv2,0);setValue(pMult,GetLength(pMult)-1);setDefaultValue(pMult,0);setValue(pTransp,-128);setValue(pStep,1); setValue(pWrap,0);setValue(pLimitLo,0);setValue(pLimitHi,127); setValue(pLimit,0);setValue(pConv,0);setArrayLength(mult, 16); setArrayLength(notesList, 16); FOR i := 0 TO 15 DO BEGIN setArrayLength(mult[i], 128); // init [16 channels][128 multiply values] SetArrayLength(notesList[i], 128); END;notesCount:=0;END; // <F> isMidiMsg: 'event' is a list of pEvent (0=ALL). filters sysex, clock and system messages//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isMidiMsg(msg: integer; e: integer): boolean;BEGIN IF ((e=1)OR(e=0)) AND ((msg=144)OR(msg=128)) THEN BEGIN result := true; END // Notes ELSE IF ((e=2)OR(e=0)) AND (msg=176) THEN BEGIN result := true; END //CtrlChg ELSE IF ((e=3)OR(e=0)) AND (msg=192) THEN BEGIN result := true; END //PrgrChg ELSE IF ((e=4)OR(e=0)) AND (msg=224) THEN BEGIN result := true; END //PitchBend ELSE IF ((e=5)OR(e=0)) AND ((msg=208) OR (msg=160)) THEN BEGIN result := true; END //Aftertouch (Channel, Poly) ELSE BEGIN result:=false; END;END;// <F> setWrap: wrap or limit byte range //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION setWrap (t: integer; wrap: boolean): integer; // 777777 rename 'setWrap' BEGIN IF t < 0 THEN BEGIN t:=t*(-1); END; IF t > 127 THEN BEGIN IF isWrap THEN BEGIN result := round((t+1) MOD 128); // TEST END ELSE BEGIN result:= 127; END; END ELSE IF t < 0 THEN BEGIN /// ADD for transpose! END ELSE BEGIN result:=t; END; END;// : setTransp: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION setTransp(val: integer; trsp: integer; wrap: boolean):integer;BEGIN val:=val+trsp; IF ((val>127)OR(val<0)) THEN BEGIN IF isWrap THEN BEGIN IF val>127 THEN BEGIN result:=val-127; END ELSE BEGIN result:=val+127; END; END ELSE BEGIN IF val>127 THEN BEGIN result:=127; END ELSE BEGIN // val<0 result:=0; END; END; END ELSE BEGIN result:=0; END;END;// <F> calcLimited: returns multiplication value for limiting, or return 1 (x*1=x; x>0)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION calcLimited (val:integer; lo:integer; hi:integer; mode:integer):double; BEGIN IF mode=0 THEN BEGIN result:=1 END //IF (val<=hi)AND(val>=lo) ELSE IF mode=1 THEN BEGIN result:= (hi-lo)/128; //result:= (lo + round(val*r) - 1); END ELSE IF mode = 2 THEN BEGIN // compress END ELSE BEGIN result:=1 END;END; // <F> isStep: step limit data: filter out (? or compress->max, or compress->min)//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// FUNCTION isStep(val: integer; stp: integer):boolean;BEGIN IF ((val+1) MOD stp=0) THEN BEGIN result:=true; END ELSE BEGIN result:=false; END;END;// <F> convert//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// // setConvert: (why not convert to: clock , start, stop, cont?)FUNCTION setConvert(srcMsg:byte;typeId:integer):byte;BEGIN(*IF = notes: Note Legato","Note Poly (mode1)","Note Poly (mode2) 1-3; CC = 4generate events! if replace with current, loose data...Legato: if new event arrives, last event is closed: if not first event generate noteOff for the previous event structures: noteConvertTmpMidi:= tmp // just before FOR loop ends.Mode1: if new event arrives, add_output noteOFF for oldest even noteOn, or if it is the only note present. remove closed note from array structures: event array: boolean[chn][note] for currently open notes // ..already got this?Mode2: if new event arrives, add_output noteOFF for oldest odd noteOn, or if it there are only even notes. remove closed note from array -> even notes will endure until all odd notes are finished*) IF typeId=4 THEN BEGIN result:=176; // CC END ELSE IF typeId=14 THEN BEGIN result:=144; // leg play: send noteOff Value if next noteOn value arrives tmp.Msg var holding last value END ELSE IF typeId=5 THEN BEGIN result:=192; // PC (no 2nd byte) END ELSE IF typeId=6 THEN BEGIN result:=224; // PitchBend (Modwheel) END ELSE IF typeId=7 THEN BEGIN result:=208; // Aftert Channel END ELSE IF typeId=8 THEN BEGIN result:=160; // Aftert Poly END ELSE BEGIN result:=srcMsg; END; END;(*// <F>getOpenNotes//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//FUNCTION getOpenNotes():integer;// global variables!!BEGIN //boolNotesArray[][] END; //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//// <P> writeNoteOff: for convert to Notes//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//PROCEDURE writeNoteOff;// global variables!!VAR extra: TMidi;BEGIN extra.msg:=byte(128); extra.data1:=tmp.data1; extra.data2:=0; extra.channel:=tmp.channel; // write out //setLength(pOut):= getLength(pOut)+1; // ?? TEST!! ?? setMidiArrayData(pOut,trunc(getLength(pOut))-1),extraMidi) // remove from array //NotesArray[aVal]:= false; //countNotes(false); END; *)// <P> countNotes: PROCEDURE countNotes(isANoteAndON:boolean);//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//BEGIN IF isANoteAndON THEN BEGIN notesCount:=notesCount+1 END ELSE BEGIN notesCount:=notesCount-1; END; END;// <P> initLimitRange: expect: limitHi and limitLo are getValue(p) //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE initLimitRange;BEGIN IF limitHi-limitLo< 0 THEN BEGIN tmpLimitLo:=limitLo; limitLo:=limitHi; limitHi:=tmpLimitLo END ELSE IF limitHi-limitLo=0 THEN BEGIN IF limitHi=128 THEN BEGIN limitLo:=127; END ELSE BEGIN limitHi:=limitHi+1; END; END; END; // <P> setDataSymbols: //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// PROCEDURE setDataSymbols;VAR tr: integer;BEGIN IF (trunc(getValue(pStep))<= 1) THEN BEGIN setSymbol(pStep,'');setFormat(pStep,'-bypass-'); END ELSE BEGIN setSymbol(pStep,'%'); setFormat(pStep,'%.0f') END; tr:=trunc(getValue(pTransp)); IF tr>0 THEN BEGIN setSymbol(pTransp,'+'); setFormat(pTransp,'%.0f'); END ELSE IF (tr=(-128))OR(tr=0) THEN BEGIN setSymbol(pTransp,''); setFormat(pTransp,'-bypass-'); END ELSE BEGIN setSymbol(pTransp,'');setFormat(pTransp,'%.0f'); END; (* IF trunc(getValue(pLimitHi)) = 127 THEN BEGIN setColor(pLimitHi,81099); END ELSE BEGIN setColor(pLimitHi,90099); END; IF trunc(getValue(pLimitLo)) = 0 THEN BEGIN setOffColor(pLimitLo,90099); END ELSE BEGIN // setColor(pLimitLo,90099); END; *) END;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| // main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^|BEGIN len := getLength(pIn); lenCount:= len; IF len > 0 THEN BEGIN multiply:=pMultAr[trunc(getValue(pMult))]; transpose:= trunc(getValue(pTransp)); IF (transpose=(-128)) THEN BEGIN // control: bypass at the left end for convenience transpose:=0; END; tmpEvent:=trunc(getValue(pEvent)); tmpDest:=trunc(getValue(pDest)); isFirstByte:=(tmpDest=2)OR(tmpDest=0); isSecondByte:=(tmpDest=2)OR(tmpDest=1); limitLo:=trunc(getValue(pLimitLo)); limitHi:=trunc(getValue(pLimitHi)); limit:=trunc(getValue(pLimit)); isLimit:=(limit>0) AND ((limitLo>=1)AND(limitHi<=126)); initLimitRange(); convert:=trunc(getValue(pConv)); step:=trunc(getValue(pStep)); isStepActive:=step>1; isInv1:=getValue(pInv1)>0; isInv2:=getValue(pInv2)>0; isSwap:=getValue(pSwap)>0; isWrap:=trunc(getValue(pWrap))>0; isThru:=trunc(getValue(pThru))>0; FOR i := 0 TO (len - 1) DO BEGIN getMidiArrayValue(pIn, i, tmp); IF isMidiMsg(tmp.msg, tmpEvent) THEN BEGIN // process on IF. if pThru is 1, process on ELSE tmpMsg :=tmp.msg; tmpX := tmp.data1; tmpY := tmp.data2; tmpXX := tmpX; // store global for note off array IF isInv1 THEN BEGIN tmpX:= 127 - tmpX;END; IF isInv2 THEN BEGIN tmpY:= 127 - tmpY;END; IF isSwap THEN BEGIN tmpX := tmpY; tmpY := tmpXX; END; // affect byte 1 IF isFirstByte THEN BEGIN // 1st byte: process 1st+2nd byte if tmpDest=2 tmpX := trunc(multiply*tmpX) + (transpose); tmpX := setWrap(tmpX, isWrap); IF isLimit THEN BEGIN tmpX := trunc(tmpX * calcLimited(tmpX,limitLo,limitHi,limit)); END; tmp.data1 := byte(tmpX); END; // affect byte 2 IF isSecondByte THEN BEGIN // 2nd byte: process 1st+2nd byte if tmpDest=2 tmpY := trunc(multiply*tmpY) + (transpose); tmpY := setWrap(tmpY, isWrap); IF isLimit THEN BEGIN tmpY := trunc(tmpY * calcLimited(tmpY,limitLo,limitHi,limit)); END; tmp.data2 := byte(tmpY); END; // affect msg: convert event IF isConvert THEN BEGIN tmp.msg := setConvert(tmp.msg,convert); END; isNoteOff:= (tmpMsg=128) OR ((tmpMsg=144)AND(tmpY=0)); isNoteOn := (isNoteOff=false)AND((tmpMsg=144)AND(tmpY>0)); // only when Notes are output // prevent hanging notes (tmpXX for swapped note values); ! if convert=>Notes: IF isNoteOn THEN BEGIN mult[tmp.channel - 1][tmpXX] := tmp.data1; // tmpXX(old 1st byte) needed for noteOff value IF notesList[tmp.channel - 1][tmpXX]=false THEN BEGIN notesList[tmp.channel - 1][tmpXX]:= true; END; //IF ((convert<=3)AND(convert>0)) THEN BEGIN //addToBoolNotes(tmp.data1, tmp.channel, tmpXX); // END //ELSE BEGIN //END; END ELSE IF isNoteOff THEN BEGIN // ! if convert=>Notes: tmp.data1 := mult[tmp.channel - 1][tmpXX]; // set NoteOff value from original NoteOn value IF notesList[tmp.channel - 1][tmpXX]=true THEN BEGIN notesList[tmp.channel - 1][tmpXX]:=false; END; END; IF isFirstByte OR (isFirstByte AND isSecondByte) THEN BEGIN filterByte:= tmpX; END //(tempDest=1) ELSE BEGIN filterByte:= tmpY; END; IF ((limit=1) AND ((limitLo>filterByte) OR (limitHi<filterByte))) OR (isStepActive AND(isStep(filterByte,step)=true)) THEN BEGIN // filter message lenCount:=lenCount-1; END ELSE BEGIN IF isNoteOn THEN BEGIN countNotes(true); END ELSE IF isNoteOff THEN BEGIN countNotes(false); END; setLength(pOut,lenCount); setMidiArrayValue(pOut, i, tmp); // write modified message END; END // ! isMidiMsg() ELSE BEGIN IF isThru THEN BEGIN setMidiArrayValue(pOut, i, tmp); END ELSE BEGIN setLength(pOut, 0); END; END; END; // (FOR i := 0 .. END ELSE BEGIN setLength(pOut, 0); END; // len = 0 setValue(pNotesCount, notesCount);setDataSymbols();END.Statistics: Posted by amiga909 — 04 Nov 2008, 08:47