Bernard Escaillas
/
MidiTee
filter.h
- Committer:
- Midimetric
- Date:
- 2011-06-07
- Revision:
- 0:71d791204057
File content as of revision 0:71d791204057:
#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