Bernard Escaillas
/
MidiTee
Diff: filter.h
- Revision:
- 0:71d791204057
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/filter.h Tue Jun 07 13:32:20 2011 +0000 @@ -0,0 +1,471 @@ +#include <vector> +#include "midi.h" +#include "stack.h" +#include "memory.h" + +#define TO_OUT1 1 +#define TO_OUT2 2 +#define TO_OUT3 4 + +#define BitSet( flag, bit ) ( ( flag >> bit ) & 1 ) +#define BitPut( flag, bit ) ( flag | ( 1 << bit ) ) +#define BitDel( flag, bit ) ( flag & ~( 1 << bit ) ) + +// literals are either 8 bits ( 0 to 255 ) or 14 bits ( 0 to 16383 / -8192 to 8191 ) +// commands are coded with bit 14 set (above 16383 ) but not with bit 15 to leave negative down to -8192 to literals and to avoid ambiguity + +#define ISCOMMAND(x) ( (x & 0xF000) == 0x4000 ) + +// sequence delimiter +#define SEQ 0x4000 // escape; introducing a sequence in a Chain: +#define E7_ 0x4001 // return 7 bits byte from computation +#define E8_ 0x4002 // return 8 bits byte from computation +#define E14 0x4003 // return 14 bits word from computation (msb then lsb) +#define NOP 0x4004 // nul terminator in sequence array +#define RAW 0x4005 // output is raw input (no change) +// VALUES +#define VMC 0x4010 // =%mc Full midi command (base command + channel), can be used in sequence or alone +#define VM_ 0x4011 // =%m Base midi command (4 msb of VMC ), can be used in sequence or alone +#define VC_ 0x4012 // =%c Value of Channel (4 lsb of VMC ) +#define VA_ 0x4013 // Value A, can be used in sequence or alone +#define VB_ 0x4014 // Value B +#define VN_ 0x4015 // RPN or NRPN 14bits number +#define VD_ 0x4016 // RPN or NRPN 14bits data +#define VK_ 0x4017 // Bank 14bits data + +// FUNCTIONS +#define ADD 0x4020 +#define SUB 0x4021 // Add 14 bits value as signed integer, bound to 0 - 0xFFFF +#define MUL 0x4022 +#define DIV 0x4023 +#define MOD 0x4024 +#define BIT 0x4125 // folowing int is index value then bit shift then bits count +#define BOR 0x4026 // bynary OR +#define BAN 0x4027 // bynary AND +#define BNO 0x4028 // bynary NOT +#define BSL 0x4029 // bynary shift left (<<) +#define BSR 0x402A // bynary shift right (>>) +#define MAP 0x402B // discrete value mapping +#define RPN 0x402C // RPN type formated output +#define NPN 0x402D // NRPN type formated output +#define BNK 0x402E // Bank type formated output +#define MSB 0x402F // msb part of value +#define LSB 0x4030 // lsb part of value +#define NP8 0x4031 // 8 bit value NRPN (sends only CC6 not CC38) + +// checkSUm +// - compute checksum from value after 'SUB' to value before 'SUE' +#define CSB 0x4040 // checkSUm Begin +#define CSE 0x4041 // checkSUm End +#define CS1 0x4042 // insert 8 bit checksum at position (careful:may appear before SUB-SUE range) +#define CS2 0x4043 // 32 bits checksum +// Checksum Type1 (Roland) : -( sum( SUB -> SUE ) & 127 ) & 127 +// Checksum Type2 (Alesis) : -( sum( SUB -> SUE ) & 0xFFFF ) & 0xFFFF + +// custom identifiers +#define CID 0x4100 // end of Custom IDentifier +// 0x41cc // cc = ascii char index +#define CUL 0x4200 // Custom identifier character set Upper Limit + +#if _DEBUGFILTER +#include "filter_debug.h" +#endif + +// error codes returned by Compute +#define CHAIN_SKIPROUTE -1 +#define CHAIN_NOERROR 0 +#define CHAIN_STACK_FAILURE 1 +#define CHAIN_MISSING_SUB 2 +#define CHAIN_MISSING_SUE 3 +#define CHAIN_SUB_AFTER_SUE 4 +#define CHAIN_NOT_A_BYTE 5 +#define CHAIN_UNKNOWN_OP 6 +#define CHAIN_SYNTAXE_ERROR 7 +#define CHAIN_DIVIDE_BY_ZERO 8 + +#define NEXT i++; break; +#define PUSH(x) Out.push_back(x) + +short bitmasking[] = {0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383}; + +//_____________________________________________________________________________ +class Chain +{//"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +// a chain defines for each port a message composed of fixed bytes or symbols +public: + vector<short> Msg[3]; + vector<byte> Out; + + Chain() { } + Chain( vector<short> &msg1, vector<short> &msg2, vector<short> &msg3 ) { Msg[0] = msg1; Msg[1] = msg2; Msg[2] = msg3; } + ~Chain() { } + + void Done() // clear output vector until next compute to avoid keeping computation for all filters in memory + { + Out.clear(); + } + short Compute( byte port, MidiM &cur, MidiM &prev ) // 0=ok, else return error code + { + short max = Msg[port].size(); + if( max == 0 ) return -1; + + Stack<long> k; + long a,b,c,d; // temp + short i = 0; // next position in Msg + bool s = false; // true when "in sequence" + short csb = -1; // position of check sum begin + short cse = -1; // position of check sum end + short csu = 0; // position where to insert check sum + short cst = 0; // check sum type + + Out.clear(); + + while( i < max ) + { + short n = Msg[port][i]; + if( ! s ) // out of sequence, expect a byte value or a custom identifier or SEQ + { + if( n < 256 ) { PUSH( (byte)n ); i++; } // just copy fixed message byte + else if( ! ISCOMMAND( n ) ) return CHAIN_NOT_A_BYTE; // short values are not allowed outside of sequence + else switch( n ) { + case SEQ : s = true; NEXT + case VMC : PUSH( cur.Command | ( cur.Channel == NAKN ? 0 : cur.Channel ) ); NEXT + case VM_ : PUSH( cur.Command ); NEXT + case VC_ : PUSH( cur.Channel == NAKN ? 0 : cur.Channel ); NEXT + case VA_ : PUSH( cur.ValA ); NEXT + case VB_ : PUSH( cur.ValB ); NEXT + case VN_ : PUSH( prev.ValA ); PUSH( prev.ValB ); NEXT + case VD_ : PUSH( cur.ValA ); PUSH( cur.ValB ); NEXT + case VK_ : PUSH( cur.ValA ); PUSH( cur.ValB ); NEXT + + case RAW : if( ( cur.Type == DATA || cur.Type == INCR || cur.Type == DECR ) + && ( prev.Type == NRPN || prev.Type == RPN_ ) ) + { + Out = prev.Raw; Out.insert( Out.end(), cur.Raw.begin(), cur.Raw.end() ); + } + else Out = cur.Raw; + return 0; + + default : {char id[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; short idi = 0; + while( n > CID && n < CUL ) { id[idi++] = (char)(n - CID); n = Msg[port][++i]; } + PUSH( (byte)ML.Get( id ) ); } NEXT + } + } + else if( ! ISCOMMAND( n ) ) // 14 bit literal inside a sequence, push on stack + { + k.Push( n ); i++; + } + else switch( n ) + { + case NOP : NEXT + case E7_ : k.Pull(a); if(a < 0) a=0; else if(a > 127) a=127; PUSH(a); s = false; NEXT + case E8_ : k.Pull(a); if(a < 0) a=0; else if(a > 255) a=255; PUSH(a); s = false; NEXT + case E14 : k.Pull(a); PUSH( (byte)(a >> 7) & 127 ); PUSH(a & 127); s = false; NEXT + case VMC : k.Push( cur.Command|( cur.Channel == NAKN ? 0 : cur.Channel ) ); NEXT + case VM_ : k.Push( cur.Command ); NEXT + case VC_ : k.Push( ( cur.Channel == NAKN ? 0 : cur.Channel ) ); NEXT + case VA_ : k.Push( cur.ValA ); NEXT + case VB_ : k.Push( cur.ValB ); NEXT + case VN_ : k.Push( prev.Get14() ); NEXT + case VD_ : k.Push( cur.Get14() ); NEXT + case VK_ : k.Push( cur.Get14() ); NEXT + case ADD : k.Pull( b, a ); k.Push( a + b ); NEXT + case SUB : k.Pull( b, a ); k.Push( a - b ); NEXT + case MUL : k.Pull( b, a ); k.Push( a * b ); NEXT + case DIV : k.Pull( b, a ); if(b==0) return CHAIN_DIVIDE_BY_ZERO; k.Push( a / b ); NEXT + case MOD : k.Pull( b, a ); if(b==0) return CHAIN_DIVIDE_BY_ZERO; k.Push( a % b ); NEXT + case BIT : k.Pull( c,b,a); k.Push(cur.Raw[a] >> b & bitmasking[c]); NEXT + case BOR : k.Pull( b, a ); k.Push( a | b ); NEXT + case BAN : k.Pull( b, a ); k.Push( a & b ); NEXT + case BNO : k.Pull( a ); k.Push( ~a ); NEXT + case BSL : k.Pull( b, a ); k.Push( a << b ); NEXT + case BSR : k.Pull( b, a ); k.Push( a >> b ); NEXT + case MSB : k.Pull( a ); k.Push( a >> 7 ); NEXT + case LSB : k.Pull( a ); k.Push( a & 127 ); NEXT + case CSB : csb=Out.size(); s = false; /*quit sequence not waiting for END*/ NEXT + case CSE : cse=Out.size(); s = false; NEXT + case CS1 : csu=Out.size(); cst=1; PUSH(0); NEXT + case CS2 : csu=Out.size(); cst=2; PUSH(0); PUSH(0); PUSH(0); PUSH(0); NEXT + case MAP : k.Pull( d, a, c, b ); d--; + while( a != b ) + if( ( d -= 2 ) == 0 ) return CHAIN_SKIPROUTE; + else k.Pull( c, b ); + while( d -= 2 ) k.Pull(a,b); /* unstack unused values */ k.Push( c ); NEXT + case NPN : k.Pull( c,b,a); a &= 15; + if( b > 127 ) { PUSH( 0xB0 + a ); PUSH( 99 ); PUSH( ( b >> 7 ) & 127 ); } + PUSH( 0xB0 + a ); PUSH( 98 ); PUSH( b & 127 ); + PUSH( 0xB0 + a ); PUSH( 6 ); PUSH( ( c >> 7 ) & 127 ); + PUSH( 0xB0 + a ); PUSH( 38 ); PUSH( c & 127 ); NEXT + + case NP8 : k.Pull( c,b,a); a &= 15; + if( b > 127 ) { PUSH( 0xB0 + a ); PUSH( 99 ); PUSH( ( b >> 7 ) & 127 ); } + PUSH( 0xB0 + a ); PUSH( 98 ); PUSH( b & 127 ); + PUSH( 0xB0 + a ); PUSH( 6 ); PUSH( c & 127 ); NEXT + + case RPN : k.Pull( c,b,a); a &= 15; + if( b > 127 ) { PUSH( 0xB0 + a ); PUSH(101 ); PUSH( ( b >> 7 ) & 127 ); } + PUSH( 0xB0 + a ); PUSH(100 ); PUSH( b & 127 ); + PUSH( 0xB0 + a ); PUSH( 6 ); PUSH( ( c >> 7 ) & 127 ); + PUSH( 0xB0 + a ); PUSH( 38 ); PUSH( c & 127 ); NEXT + + case BNK : k.Pull( b, a ); a &= 15; + if( b > 127 ) { PUSH( 0xB0 + a ); PUSH( 0 ); PUSH( ( b >> 7 ) & 127 ); } + PUSH( 0xB0 + a ); PUSH( 32 ); PUSH( b & 127 ); NEXT + default: + if( n <= CID && n > CID + 0xFF )return CHAIN_UNKNOWN_OP; + {char id[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; short idi = 0; + while( n > CID && n < CUL ) { id[idi++] = (char)(n - CID); + n = Msg[port][++i]; } k.Push( (byte)ML.Get( id ) ); } NEXT + } + } + c = 0; + if( cst ) + { + if( csb == -1 ) return CHAIN_MISSING_SUB; + if( cse == -1 ) return CHAIN_MISSING_SUE; + if( csb >= cse ) return CHAIN_SUB_AFTER_SUE; + } + if( cst == 1 ) + { + for( i = csb ; i < cse ; i++ ) + c -= Out[i]; + Out[csu]= (byte)( c & 127 ); + } + if( cst == 2 ) + { + for( i = csb ; i < cse ; i += 4 ) + c -= Out[i] + ( Out[i+1] << 8 ) + ( Out[i+2] << 16 ) + ( Out[i+3] << 24 ); + Out[csu++]= (byte)( c ); + Out[csu++]= (byte)( c >> 8 ); + Out[csu++]= (byte)( c >> 16 ); + Out[csu ]= (byte)( c >> 24 ); + } + + return 0; + } +}; +//_____________________________________________________________________________ +class Assignment +{//"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +public: + char Name[17]; + vector<short> Msg; + short Value; + + Assignment() { Name[0] = 0; Value = NOP; } + Assignment( char* name, vector<short> &msg ) { strncpy( Name, name, 17 ); Msg = msg; Value = NOP; } + ~Assignment() { } + + short Compute( MidiM &cur, MidiM &prev ) // 0=ok, else return error code + { + Stack<long> k; + long a,b,c,d; // temp + short i = 0; // next position in Msg + bool s = false; // true when "in sequence" + + while( i < Msg.size() ) + { + short n = Msg[i]; + if( ! s ) // out of sequence, expect a byte value or a custom identifier or SEQ + { + if( n < 256 ) { Value = n; return 0; } // just copy fixed message byte + else if( ! ISCOMMAND( n ) ) return CHAIN_NOT_A_BYTE; // short values are not allowed outside of sequence + else switch( n ) { + case SEQ : s = true; NEXT + case VMC : Value = cur.Command | ( cur.Channel == NAKN ? 0 : cur.Channel ); return 0; + case VM_ : Value = cur.Command; return 0; + case VC_ : Value = ( cur.Channel == NAKN ? 0 : cur.Channel ); return 0; + case VA_ : Value = cur.ValA; return 0; + case VN_ : Value = prev.Get14(); return 0; + case VB_ : Value = cur.ValB; return 0; + case VD_ : + case VK_ : Value = cur.Get14(); return 0; + default : char id[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; short idi = 0; + while( n > CID && n < CUL ) { id[idi++] = (char)(n - CID); + n = Msg[i++]; } Value = ML.Get( id ); return 0; + } + } + else if( ! ISCOMMAND( n ) ) // 14 bit literal inside a sequence, push on stack + { + k.Push( n ); i++; + } + else switch( n ) + { + case E7_ : + case E8_ : + case E14 : Value = (short)k.Pull(); return 0; + + case VMC : k.Push( cur.Command|( cur.Channel == NAKN ? 0 : cur.Channel ) ); NEXT + case VM_ : k.Push( cur.Command ); NEXT + case VC_ : k.Push( ( cur.Channel == NAKN ? 0 : cur.Channel ) ); NEXT + case VA_ : k.Push( cur.ValA ); NEXT + case VB_ : k.Push( cur.ValB ); NEXT + case VN_ : k.Push( prev.Get14() ); NEXT + case VD_ : + case VK_ : k.Push( cur.Get14() ); NEXT + case ADD : k.Pull( b, a ); k.Push( a + b ); NEXT + case SUB : k.Pull( b, a ); k.Push( a - b ); NEXT + case MUL : k.Pull( b, a ); k.Push( a * b ); NEXT + case DIV : k.Pull( b, a ); k.Push( a / b ); NEXT + case MOD : k.Pull( b, a ); k.Push( a % b ); NEXT + case BIT : k.Pull( c,b,a); k.Push( cur.Raw[a] >> b & bitmasking[c] ); NEXT + case BOR : k.Pull( b, a ); k.Push( a | b ); NEXT + case BAN : k.Pull( b, a ); k.Push( a & b ); NEXT + case BNO : k.Pull( a ); k.Push( ~a ); NEXT + case BSL : k.Pull( b, a ); k.Push( a << b ); NEXT + case BSR : k.Pull( b, a ); k.Push( a >> b ); NEXT + case MSB : k.Pull( a ); k.Push( a >> 7 ); NEXT + case LSB : k.Pull( a ); k.Push( a & 127 ); NEXT + case MAP : k.Pull( d, a, c, b ); d--; + while( a != b ) + if( ( d -= 2 ) == 0 ) { k.Push( NAKN ); NEXT } + else k.Pull( c, b ); + while( d -= 2 ) k.Pull(a,b); /* unstack unused values */ k.Push( c ); NEXT + default: + if( n <= CID && n > CID + 0xFF )return CHAIN_UNKNOWN_OP; + {char id[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; short idi = 0; + while( n > CID && n < CUL ) { id[idi++] = (char)(n - CID); + n = Msg[++i]; } k.Push( (byte)ML.Get( id ) ); } NEXT + } + } + return CHAIN_SYNTAXE_ERROR; + } +}; +//_____________________________________________________________________________ +class Filter +{//"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +public: + byte Port; // use constants TO_OUT1...3 + byte Type; // use constants NOTE, POLA, ... from midi.h + short Channel; + short Amin; // may be a 14 bits value for RPN, NRPN identifier + short Amax; + short Bmin; // may be a 14 bits value for RPN, NRPN value + short Bmax; + vector<byte> Head; + Chain Out; + vector<Assignment*> Assigns; + + Filter() : Port(0), Type(0), Channel(NAKW), Amin(NAKN), Amax(NAKN), Bmin(NAKN), Bmax(NAKN), Head( 1, (short)0xF0 ) { } + ~Filter() { } + + Filter( Filter & f ) + : Port(f.Port),Type(f.Type),Channel(f.Channel),Amin(f.Amin),Amax(f.Amax),Bmin(f.Bmin),Bmax(f.Bmax) + { + Head = f.Head; Out.Msg[0] = f.Out.Msg[0]; Out.Msg[1] = f.Out.Msg[1]; Out.Msg[2] = f.Out.Msg[2]; Assigns = f.Assigns; + } + + Filter( byte p, byte t, short c, short ai, short ax, short bi, short bx, vector<byte> &head, vector<short> &o1, vector<short> &o2, vector<short> &o3, vector<Assignment*> &assigns ) + : Port(p), Type(t), Channel(c), Amin(ai), Amax(ax), Bmin(bi), Bmax(bx) + { + Head = head; Out.Msg[0] = o1; Out.Msg[1] = o2; Out.Msg[2] = o3; Assigns = assigns; + } +}; + +//_____________________________________________________________________________ +class FilterList +{//"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +public: + vector<Filter*> List; + + void Add( byte inport, byte message, short channels, short mina, short maxa, short minb, short maxb, vector<byte> &head, vector<short> &sequence1, vector<short> &sequence2, vector<short> &sequence3, vector<Assignment*> &assigns ) + { + List.push_back( new Filter( inport, message, channels, mina, maxa, minb, maxb, head, sequence1, sequence2, sequence3, assigns ) ); + HasBank |= message == BANK; + HasData |= ( message >= DATA ) && ( message <= NRPN ); + HasSysx |= message == SYSX; + } + Filter* operator[] ( short index ) + { + return List[index]; + } + void Route( byte from, MidiM &cur, MidiM &prev ) + { + for( short i = 0 ; i < List.size() ; i++ ) + { + Filter* f = List[i]; + if( f->Port != from ) continue; + + if( HasSysx && ( f->Type == SYSX ) && ( cur.Type == SYSX ) ) + { + bool ok = f->Head.size() > 0; + if( ok ) + if( f->Head[0] != RAW ) + for( int j = 0 ; j < f->Head.size() ; j++ ) + if( j == cur.Raw.size() || f->Head[j] != cur.Raw[j] ) + ok=false; + if( ok ) + { + for( int assign = 0 ; assign < f->Assigns.size() ; assign++ ) + if( f->Assigns[assign]->Compute( cur, prev ) == 0 ) + ML.Add( f->Assigns[assign]->Name, f->Assigns[assign]->Value ); + + for( int port = 0 ; port < 3 ; port++ ) + if( f->Out.Compute( port, cur, prev ) == 0 ) + { + led[3] = 1; + for( short j = 0 ; j < f->Out.Out.size() ; j++ ) + SL[port].putc( f->Out.Out[j] ); + led[3] = 0; + } + f->Out.Done(); + return; + } + } + else if( HasData && ( f->Type == DATA || f->Type == INCR || f->Type == DECR ) && ( cur.Type == f->Type ) && ( prev.Type == NRPN || prev.Type == RPN_ ) ) + { + short number = prev.Get14(), data = cur.Get14(); + if( f->Channel == NAKW || ( f->Channel & ( 1 << cur.Channel ) & ( 1 << prev.Channel ) ) ) + if( f->Amin==NAKW || ( f->Amax==NAKW && number==f->Amin ) || ( f->Amax!=NAKW && number>=f->Amin && number<=f->Amax ) ) + if( f->Bmin==NAKW || ( f->Bmax==NAKW && data==f->Bmin ) || ( f->Bmax!=NAKW && data>=f->Bmin && data<=f->Bmax ) ) + { + for( int assign = 0 ; assign < f->Assigns.size() ; assign++ ) + if( f->Assigns[assign]->Compute( cur, prev ) == 0 ) + ML.Add( f->Assigns[assign]->Name, f->Assigns[assign]->Value ); + for( int port = 0 ; port < 3 ; port++ ) + if( f->Out.Compute( port, cur, prev ) == 0 ) + { + for( short j = 0 ; j < f->Out.Out.size() ; j++ ) // check for unrountable message + { + short n = f->Out.Out[j]; + if( f->Out.Out[j] == NAKN ) return; + } + led[3] = 1; + for( short j = 0 ; j < f->Out.Out.size() ; j++ ) + SL[port].putc( f->Out.Out[j] ); + led[3] = 0; + } + f->Out.Done(); + return; + } + } + else if( f->Type == cur.Type ) + if( ( f->Channel == NAKW ) || ( f->Channel & ( 1 << cur.Channel ) ) ) + if( cur.ValCount()==0 || f->Amin==NAKN || ( f->Amax==NAKN && cur.ValA==f->Amin ) || ( f->Amax!=NAKN && cur.ValA>=f->Amin && cur.ValA<=f->Amax ) ) + if( cur.ValCount()==1 || f->Bmin==NAKN || ( f->Bmax==NAKN && cur.ValB==f->Bmin ) || ( f->Bmax!=NAKN && cur.ValB>=f->Bmin && cur.ValB<=f->Bmax ) ) + { + for( int assign = 0 ; assign < f->Assigns.size() ; assign++ ) + if( f->Assigns[assign]->Compute( cur, prev ) == 0 ) + ML.Add( f->Assigns[assign]->Name, f->Assigns[assign]->Value ); + + for( int port = 0 ; port < 3 ; port++ ) + if( f->Out.Compute( port, cur, prev ) == 0 ) + { + for( short j = 0 ; j < f->Out.Out.size() ; j++ ) // check for unrountable message + if( f->Out.Out[j] == NAKN ) return; + led[3] = 1; + for( short j = 0 ; j < f->Out.Out.size() ; j++ ) + SL[port].putc( f->Out.Out[j] ); + led[3] = 0; + } + f->Out.Done(); + return; + } + + } + } +}; + +#undef NEXT +#undef PUSH +