Dependencies:   mbed

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