Dependencies:   mbed

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
+