/*
 * NrpnData.java
 * Copyright (c) 2008 kbinani
 *
 * This file is part of jp.sourceforge.lipsync.vsq.
 *
 * jp.sourceforge.lipsync.vsq is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * jp.sourceforge.lipsync.vsq is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
package jp.sourceforge.lipsync.vsq;

import java.io.*;
    class MidiFile {
        /* MIDI status commands most significant bit is 1 */
        final int note_off = 0x80;
        final int note_on = 0x90;
        final int poly_aftertouch = 0xa0;
        final int control_change = 0xb0;
        final int program_chng = 0xc0;
        final int channel_aftertouch = 0xd0;
        final int pitch_wheel = 0xe0;
        final int system_exclusive = 0xf0;
        final int delay_packet = 1111;

        /* 7 bit controllers */
        final int damper_pedal = 0x40;
        final int portamento = 0x41;
        final int sostenuto = 0x42;
        final int soft_pedal = 0x43;
        final int general_4 = 0x44;
        final int hold_2 = 0x45;
        final int general_5 = 0x50;
        final int general_6 = 0x51;
        final int general_7 = 0x52;
        final int general_8 = 0x53;
        final int tremolo_depth = 0x5c;
        final int chorus_depth = 0x5d;
        final int detune = 0x5e;
        final int phaser_depth = 0x5f;

        /* parameter values */
        final int data_inc = 0x60;
        final int data_dec = 0x61;

        /* parameter selection */
        final int non_reg_lsb = 0x62;
        final int non_reg_msb = 0x63;
        final int reg_lsb = 0x64;
        final int reg_msb = 0x65;

        /* Standard MIDI Files meta event definitions */
        final int meta_event = 0xFF;
        final int sequence_number = 0x00;
        final int text_event = 0x01;
        final int copyright_notice = 0x02;
        final int sequence_name = 0x03;
        final int instrument_name = 0x04;
        final int lyric = 0x05;
        final int marker = 0x06;
        final int cue_point = 0x07;
        final int channel_prefix = 0x20;
        final int end_of_track = 0x2f;
        final int set_tempo = 0x51;
        final int smpte_offset = 0x54;
        final int time_signature = 0x58;
        final int key_signature = 0x59;
        final int sequencer_specific = 0x7f;

        /* Manufacturer's ID number */
        final int Seq_Circuits = 0x01; /* Sequential Circuits Inc. */
        final int Big_Briar = 0x02; /* Big Briar Inc. */
        final int Octave = 0x03; /* Octave/Plateau */
        final int Moog = 0x04; /* Moog Music */
        final int Passport = 0x05; /* Passport Designs */
        final int Lexicon = 0x06; /* Lexicon */
        final int Tempi = 0x20; /* Bon Tempi */
        final int Siel = 0x21; /* S.I.E.L.  */
        final int Kawai = 0x41;
        final int Roland = 0x42;
        final int Korg = 0x42;
        final int Yamaha = 0x43;

        /* miscellaneous definitions */
        final long MThd = 0x4d546864L;
        final long MTrk = 0x4d54726bL;

        final int MTHD = 256;
        final int MTRK = 257;
        final int TRKEND = 258;

        final int ON = note_on;//#define ON note_on
        final int OFF = note_off;
        final int POPR = poly_aftertouch;
        final int PAR = control_change;
        final int PB = pitch_wheel;
        final int PRCH = program_chng;
        final int CHPR = channel_aftertouch;
        final int SYSEX = system_exclusive;

        final int ARB = 259;
        final int MINOR = 260;
        final int MAJOR = 261;

        final int CH = 262;
        final int NOTE = 263;
        final int VAL = 264;
        final int CON = 265;
        final int PROG = 266;

        static final int INT = 267;
        static final int STRING = 268;
        final int STRESC = 269;
        final int ERR = 270;
        final int NOTEVAL = 271;
        final int EOL = 272;

        final int META = 273;
        final int SEQSPEC = (META + 1 + sequencer_specific);
        final int TEXT = (META + 1 + text_event);
        final int COPYRIGHT = (META + 1 + copyright_notice);
        final int SEQNAME = (META + 1 + sequence_name);
        final int INSTRNAME = (META + 1 + instrument_name);
        final int LYRIC = (META + 1 + lyric);
        final int MARKER = (META + 1 + marker);
        final int CUE = (META + 1 + cue_point);
        final int SEQNR = (META + 1 + sequence_number);
        final int KEYSIG = (META + 1 + key_signature);
        final int TEMPO = (META + 1 + set_tempo);
        final int TIMESIG = (META + 1 + time_signature);
        final int SMPTE = (META + 1 + smpte_offset);

        private final int EOF = -1;
        private RandomAccessFile F = null;
        //private StreamWriter sw = null;
        private StringBuilder sb = null;
        //private StreamReader yyin = null;

        private final int FALSE = 0;
        private final int TRUE = 1;

        static int err_cont = 0;

        private int fold = 0;		/* fold long lines */
        private int notes = 0;		/* print notes as a-g */
        private int times = 0;		/* print times as Measure/beat/click */
        private static int Measure, M0, Beat, Clicks;
        private int TrksToDo = 1;
        private int TrkNr;
        private static long T0;
        static char[] buffer = new char[] { };
        static int bufsiz = 0, buflen;

        private static String note = "n";
        private static String vol = "v";
        private static String con = "c";
        private static String val = "v";
        private static String prog = "p";

        private static String PolyPr = "PoPr";
        private static String Param = "Par";
        private static String ProgCh = "PrCh";
        private static String ChanPr = "ChPr";

        static int Format, Ntrks;

        private long Mf_currtime;
        private int Mf_nomerge;
        private long Mf_toberead;
        private String Msgbuff;
        private int Msgindex;

        //wtrackDelegate Mf_wtrack = null;
        //wtrackDelegate Mf_wtempotrack = null;
        //putcDelegate Mf_putc = null;
        static long Mf_numbyteswritten = 0L;

        /*extern */
        static long yyval;
        /*extern */
        static int eol_seen;
        /*extern */
        static int lineno;
        /*extern */
        static int yyleng;
        /*extern */
        static String yytext;
        /*extern */
        static int do_hex;
        /*extern */
        static int Mf_RunStat;

        static int laststat;		/* last status code */
        static int lastmeta;		/* last meta event type */

        static char[] data = new char[5];//changed to static
        static int chan;//staticに変更

        private int m_nrpn_msb, m_nrpn_lsb;

        public String ReadToEnd() {
            return sb.toString();
        }


        public MidiFile( String smf_path/*, String text_path, Mode mode*/ ) {
            //if ( mode == Mode.Read ) {
                F = new RandomAccessFile( smf_path, "r" );
                //sw = new StreamWriter( text_path );
                sb = new StringBuilder();
                Mf_toberead = 0L;
                Mf_currtime = 0L;
                Mf_nomerge = 0;
                Mf_nomerge = 1;
                TrkNr = 0;
                Measure = 4;
                Beat = 0x60;
                Clicks = 0x60;
                T0 = 0L;
                M0 = 0;
                readheader();
                while ( readtrack() != 0 ) {
                }
                //sw.Close();
            /*} else if ( mode == Mode.Write ) {
                yyin = new StreamReader( text_path );
                F = new FileStream( smf_path, FileMode.Create, FileAccess.Write );
                Mf_putc = fileputc;
                Mf_wtrack = mywritetrack;
                TrkNr = 0;
                Measure = 4;
                Beat = 96;
                Clicks = 96;
                M0 = 0;
                T0 = 0;
                translate();
                F.Close();
                yyin.Close();
            }*/
        }

        public void close() {
            if ( F != null ) {
                F.close();
            }
        }

        // region t2mf

        int getbyte( String mess ) {
            //char ermesg[100];
            getint( mess );
            if ( yyval < 0 || yyval > 127 ) {
                String ermesg = "Wrong value (" + yyval + ") for " + mess;//	sprintf (ermesg, "Wrong value (%ld) for %s", yyval, mess);
                error( ermesg );
                yyval = 0;
            }
            return (int)yyval;
        }


        static void gethex() {
            int c;
            buflen = 0;
            do_hex = 1;
            c = yylex();
            if ( c == STRING ) {
                /* Note: yytext includes the trailing, but not the starting quote */
                int i = 0;
                if ( yyleng - 1 > bufsiz ) {
                    bufsiz = yyleng - 1;
                    if ( buffer != null ) {
                        buffer = realloc( buffer, bufsiz );
                    } else {
                        buffer = malloc( bufsiz );
                    }
                    /*if ( !buffer ){//buffer == TRUE => buffer != FALSE -> buffer != 0
                        error( "Out of memory" );
                    }さくじょしました。。*/
                }
                while ( i < yyleng - 1 ) {
                    c = yytext[i++];
                rescan:
                    if ( c == '\\' ) {
                        switch ( c = yytext[i++] ) {
                            case '0':
                                c = '\0';
                                break;
                            case 'n':
                                c = '\n';
                                break;
                            case 'r':
                                c = '\r';
                                break;
                            case 't':
                                c = '\t';
                                break;
                            case 'x':
                                //yytext[i]の位置の文字が16進数で使われる文字じゃないとき
                                try {
                                    Int32.Parse( "" + yytext[i] );
                                    c = yytext[i];
                                } catch(Exception ex ) {
                                    prs_error( "Illegal \\x in String" );
                                }
                                /*if ( sscanf( yytext + i, "%2x", &c ) != 1 )
                                    prs_error( "Illegal \\x in String" );*/
                                i += 2;
                                break;
                            case '\r':
                            case '\n':
                                c = yytext.charAt( i++ );
                                while ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ){
                                    /* skip whitespace */
                                    c = yytext.charAt( i++ );
                                }
                                goto rescan; /* sorry EWD :=) */
                        }
                    }
                    buffer[buflen++] = (char)c;
                }
            } else if ( c == INT ) {
                do {
                    if ( buflen >= bufsiz ) {
                        bufsiz += 128;
                        if ( buffer != null ) {// buffer == TRUE -> buffer != FALSE => buffer != 0
                            buffer = realloc( buffer, bufsiz );
                        } else {
                            buffer = malloc( bufsiz );
                        }
                        /*if ( !buffer ) {
                            error( "Out of memory" );
                        }削除しました。。*/
                    }
                    /* This test not applicable for sysex
                            if (yyval < 0 || yyval > 127)
                                error ("Illegal hex value"); */
                    buffer[buflen++] = (char)yyval;
                    c = yylex();
                } while ( c == INT );
                if ( c != EOL )
                    prs_error( "Unknown hex input" );
            } else
                prs_error( "String or hex input expected" );
        }


        static int yylex() {
            // TODO: THIS IS DUMY!!
            return 0;
        }

        static void prs_error( String s ) {//static二変更j
            int c = 0;
            int count;
            int ln = (eol_seen != FALSE ? lineno - 1 : lineno);
            Console.WriteLine( ln + ": " + s );//        fprintf( stderr, "%d: %s\n", ln, s );
            if ( yyleng > 0 && yytext != "\n" ) {
                Console.WriteLine( "*** " + yyleng + " " + yytext + " ***" );//fprintf( stderr, "*** %*s ***\n", yyleng, yytext );
            }
            count = 0;
            while ( count < 100 && (c = yylex()) != EOL && c != EOF ) {
                count++/* skip rest of line */;
            }
            if ( c == EOF ) {
                Environment.Exit( 1 );// exit( 1 );
            }
            if ( err_cont != FALSE ) {
                //longjmp( erjump, 1 );
                throw new Exception( s );
            }
        }


        static char[] realloc( char[] source, int size ) {
            char[] result = new char[size];
            for ( int i = 0; i < size; i++ ) {
                if ( i < source.Length ) {
                    result[i] = source[i];
                } else {
                    break;
                }
            }
            return result;
        }


        static char[] malloc( int size ) {
            return new char[size];
        }


        static void checkeol() {
            if ( eol_seen != FALSE ) {
                return;
            }
            if ( yylex() != EOL ) {
                prs_error( "Garbage deleted" );
                while ( eol_seen == FALSE/*!eol_seen*/ ) {
                    yylex();	 /* skip rest of line */
                }
            }
        }

        private void translate() {
            if ( yylex() == MTHD ) {
                Format = getint( "MFile format" );
                Ntrks = getint( "MFile #tracks" );
                Clicks = getint( "MFile Clicks" );
                if ( Clicks < 0 ) {
                    Clicks = (Clicks & 0xff) << 8 | getint( "MFile SMPTE division" );
                }
                checkeol();
                mfwrite( Format, Ntrks, Clicks, F );
            } else {
                Console.WriteLine( "Missing MFile - can't continue\n" );
                Environment.Exit( 1 );
            }
        }


        private void mfwrite( int format, int ntracks, int division, RandomAccessFile fp ) {
            int i;
            //void mf_w_track_chunk(), mf_w_header_chunk();

            if ( Mf_putc == null/*NULLFUNC*/ ) {
                mferror( "mfmf_write() called without setting Mf_putc" );
            }

            if ( Mf_wtrack == null/*NULLFUNC*/ ) {
                mferror( "mfmf_write() called without setting Mf_mf_writetrack" );
            }

            /* every MIDI file starts with a header */
            mf_w_header_chunk( format, ntracks, division );

            /* In format 1 files, the first track is a tempo map */
            if ( format == 1 && (Mf_wtempotrack != null) ) {
                mf_w_track_chunk( -1, fp, Mf_wtempotrack );
                ntracks--;
            }

            /* The rest of the file is a series of tracks */
            for ( i = 0; i < ntracks; i++ ) {
                mf_w_track_chunk( i, fp, Mf_wtrack );
            }
        }


        void mf_w_header_chunk( int format, int ntracks, int division ) {
            ulong ident, length;
            //void write16bit(),write32bit();

            ident = MThd;           /* Head chunk identifier                    */
            length = 6;             /* Chunk length                             */

            /* individual bytes of the header must be written separately
               to preserve byte order across cpu types :-( */
            write32bit( ident );
            write32bit( length );
            write16bit( format );
            write16bit( ntracks );
            write16bit( division );
        } /* end gen_header_chunk() */


        //Delegate int wtrackDelegate();
        //delegate int putcDelegate( int c );

        private void mf_w_track_chunk( int which_track, RandomAccessFile fp, wtrackDelegate wtrack /* int (*wtrack) wtrack*/) {
            ulong trkhdr, trklength;
            long offset, place_marker;
            //void write16bit(),write32bit();

            trkhdr = MTrk;
            trklength = 0;

            /* Remember where the length was written, because we don't
               know how long it will be until we've finished writing */
            offset = fp.Position;//ftell( fp );
            /* Write the track chunk header */
            write32bit( trkhdr );
            write32bit( trklength );

            Mf_numbyteswritten = 0L; /* the header's length doesn't count */
            laststat = 0;

            /* Note: this calls Mf_writetempotrack with an unused parameter (-1)
               But this is innocent */

            wtrack();//引数無くていいんかな。。。   (*wtrack)( which_track );

            if ( laststat != meta_event || lastmeta != end_of_track ) {
                /* mf_write End of track meta event */
                eputc( 0 );
                eputc( meta_event );
                eputc( end_of_track );
                eputc( 0 );
            }

            laststat = 0;

            /* It's impossible to know how long the track chunk will be beforehand,
                   so the position of the track length data is kept so that it can
                   be written after the chunk has been generated */
            place_marker = fp.Position;// ftell( fp );

            /* This method turned out not to be portable because the
                   parameter returned from ftell is not guaranteed to be
                   in bytes on every machine */
            /* track.length = place_marker - offset - (long) sizeof(track); */


            fp.seek( offset  );

            trklength = Mf_numbyteswritten;

            /* Re-mf_write the track chunk header with right length */
            write32bit( trkhdr );
            write32bit( trklength );

            fp.seek( place_marker );//            fseek( fp, place_marker, 0 );
        }

        private void write32bit( long data ) {
            eputc( (int)((data >> 24) & 0xff) );
            eputc( (int)((data >> 16) & 0xff) );
            eputc( (int)((data >> 8) & 0xff) );
            eputc( (int)(data & 0xff) );
        }


        void write16bit( int data ) {
            eputc( (int)((data & 0xff00) >> 8) );
            eputc( (int)(data & 0xff) );
        }


        private int eputc( int c )/*unsigned char c*/{
            if ( Mf_putc == null )/* == NULLFUNC*/ {
                mferror( "Mf_putc undefined" );
                return (-1);
            }

            //return_val = (*Mf_putc)( c );
            int return_val = Mf_putc( c );

            if ( return_val == EOF ) {
                mferror( "error writing" );
            }

            Mf_numbyteswritten++;
            return (return_val);
        }


        static void checkchan() {
            if ( yylex() != CH || yylex() != INT ) {
                syntax();
            }
            if ( yyval < 1 || yyval > 16 ) {
                error( "Chan must be between 1 and 16" );
            }
            chan = (int)yyval - 1;
        }


        static void checknote() {
            int c = 0;
            if ( yylex() != NOTE || ((c = yylex()) != INT && c != NOTEVAL) ) {
                syntax();
            }
            if ( c == NOTEVAL ) {
                /*static */
                int[] notes = new int[]{
                9,		/* a */
                11,		/* b */
                0,		/* c */
                2,		/* d */
                4,		/* e */
                5,		/* f */
                7		/* g */
            };

                //char* p = yytext;
                String p = yytext;
                int index = 0;
                c = p[index];//*p++;
                if ( Char.IsUpper( (char)c )/* isupper( c )*/ ) {
                    String temp = c + "";
                    temp = temp.ToLower();
                    c = (int)temp[0];//tolower( c );
                }
                yyval = notes[c - 'a'];
                switch ( p[index]/**p*/ ) {
                    case '#':
                    case '+':
                        yyval++;
                        //p++;
                        index++;
                        break;
                    case 'b':
                    case 'B':
                    case '-':
                        yyval--;
                        //p++;
                        index++;
                        break;
                }
                yyval += 12 * Intger.parseInt( p );//atoi( p );
            }
            if ( yyval < 0 || yyval > 127 )
                error( "Note must be between 0 and 127" );
            data[0] = (char)yyval;
        }


        static void syntax() {//staticに変更した
            prs_error( "Syntax error" );
        }


        static void checkval() {
            if ( yylex() != VAL || yylex() != INT )
                syntax();
            if ( yyval < 0 || yyval > 127 )
                error( "Value must be between 0 and 127" );
            data[1] = (char)yyval;
        }


        static void splitval() {
            if ( yylex() != VAL || yylex() != INT )
                syntax();
            if ( yyval < 0 || yyval > 16383 )
                error( "Value must be between 0 and 16383" );
            data[0] = (char)(yyval % 128);
            data[1] = (char)(yyval / 128);
        }


        static void get16val() {
            if ( yylex() != VAL || yylex() != INT )
                syntax();
            if ( yyval < 0 || yyval > 65535 )
                error( "Value must be between 0 and 65535" );
            data[0] = (char)((yyval >> 8) & 0xff);
            data[1] = (char)(yyval & 0xff);
        }


        static void checkcon() {
            if ( yylex() != CON || yylex() != INT )
                syntax();
            if ( yyval < 0 || yyval > 127 )
                error( "Controller must be between 0 and 127" );
            data[0] = (char)yyval;
        }


        void WriteVarLen( long value ) {
            long buffer;

            buffer = value & 0x7f;
            while ( (value >>= 7) > 0 ) {
                buffer <<= 8;
                buffer |= 0x80;
                buffer += (value & 0x7f);
            }
            while ( true/*1*/ ) {
                eputc( (int)(buffer & 0xff) );

                if ( (buffer & 0x80) != FALSE )
                    buffer >>= 8;
                else
                    return;
            }
        }/* end of WriteVarLen */


        int mf_w_midi_event( long delta_time, int type, int chan, char[] data, long size ) {
            //int i;
            /*unsigned */
            char c;

            WriteVarLen( delta_time );

            /* all MIDI events start with the type in the first four bits,
               and the channel in the lower four bits */
            c = (char)(type | chan);

            if ( chan > 15 ) {
                //perror( "error: MIDI channel greater than 16\n" );
                Console.Error.WriteLine( "error: MIDI channel greater than 16" );
            }

            if ( Mf_RunStat == FALSE || laststat != c )
                eputc( c );

            laststat = c;

            /* write out the data bytes */
            for ( ulong i = 0; i < size; i++ ) {
                eputc( (int)(data[i]) );
            }

            return (int)size;
        } /* end mf_write MIDI event */


        int mf_w_meta_event( long delta_time, int type, char[] data, long size ) {
            //int i;

            WriteVarLen( delta_time );

            /* This marks the fact we're writing a meta-event */
            eputc( meta_event );
            laststat = meta_event;

            /* The type of meta event */
            eputc( (int)type );
            lastmeta = (int)type;

            /* The length of the data bytes to follow */
            WriteVarLen( size );

            for ( ulong i = 0; i < size; i++ ) {
                if ( eputc( data[i] ) != data[i] ) {
                    return (-1);
                }
            }
            return (int)size;
        } /* end mf_w_meta_event */


        static void checkprog() {
            if ( yylex() != PROG || yylex() != INT )
                syntax();
            if ( yyval < 0 || yyval > 127 )
                error( "Program number must be between 0 and 127" );
            data[0] = (char)yyval;
        }


        int mf_w_sysex_event(long delta_time, char[] data, long size ) {
            //int i;

            WriteVarLen( delta_time );

            /* The type of sysex event */
            eputc( data[0] );//eputc( *data );
            laststat = 0;

            /* The length of the data bytes to follow */
            WriteVarLen( size - 1 );

            for ( ulong i = 1; i < size; i++ ) {
                if ( eputc( data[i] ) != data[i] )
                    return (-1);
            }
            return (int)size;
        } /* end mf_w_sysex_event */


        void mf_w_tempo( long delta_time, long tempo ) {
            /* Write tempo */
            /* all tempos are written as 120 beats/minute, */
            /* expressed in microseconds/quarter note     */

            WriteVarLen( delta_time );

            eputc( meta_event );
            laststat = meta_event;
            eputc( set_tempo );

            eputc( 3 );
            eputc( (int)(0xff & (tempo >> 16)) );
            eputc( (int)(0xff & (tempo >> 8)) );
            eputc( (int)(0xff & tempo) );
        }


        int mywritetrack() {
            int opcode, c;
            long currtime = 0;
            long newtime, delta;
            int i, k;

            while ( (opcode = yylex()) == EOL )
                ;
            if ( opcode != MTRK )
                prs_error( "Missing MTrk" );
            checkeol();
            while ( true/*1*/ ) {
                err_cont = 1;
                //setjmp( erjump );
                try {
                    switch ( yylex() ) {
                        case MTRK:
                            prs_error( "Unexpected MTrk" );
                            continue;
                        case EOF:
                            err_cont = 0;
                            error( "Unexpected EOF" );
                            return -1;
                        case TRKEND:
                            err_cont = 0;
                            checkeol();
                            return 1;
                        case INT:
                            newtime = yyval;
                            if ( (opcode = yylex()) == '/' ) {
                                if ( yylex() != INT )
                                    prs_error( "Illegal time value" );
                                newtime = (newtime - M0) * Measure + yyval;
                                if ( yylex() != '/' || yylex() != INT )
                                    prs_error( "Illegal time value" );
                                newtime = T0 + newtime * Beat + yyval;
                                opcode = yylex();
                            }
                            delta = newtime - currtime;
                            switch ( opcode ) {
                                case ON:
                                case OFF:
                                case POPR:
                                    checkchan();
                                    checknote();
                                    checkval();
                                    mf_w_midi_event( (ulong)delta, (uint)opcode, (uint)chan, data, (char)2L );
                                    break;

                                case PAR:
                                    checkchan();
                                    checkcon();
                                    checkval();
                                    mf_w_midi_event( (ulong)delta, (uint)opcode, (uint)chan, data, (char)2L );
                                    break;

                                case PB:
                                    checkchan();
                                    splitval();
                                    mf_w_midi_event( (ulong)delta, (uint)opcode, (uint)chan, data, (char)2L );
                                    break;

                                case PRCH:
                                    checkchan();
                                    checkprog();
                                    mf_w_midi_event( (ulong)delta, (uint)opcode, (uint)chan, data, (char)1L );
                                    break;

                                case CHPR:
                                    checkchan();
                                    checkval();
                                    data[0] = data[1];
                                    mf_w_midi_event( (ulong)delta, (uint)opcode, (uint)chan, data, (char)1L );
                                    break;

                                case SYSEX:
                                case ARB:
                                    gethex();
                                    mf_w_sysex_event( (ulong)delta, buffer, (ulong)buflen );
                                    break;

                                case TEMPO:
                                    if ( yylex() != INT )
                                        syntax();
                                    mf_w_tempo( (ulong)delta, (ulong)yyval );
                                    break;

                                case TIMESIG: {
                                        int nn, denom, cc, bb;
                                        if ( yylex() != INT || yylex() != '/' )
                                            syntax();
                                        nn = (int)yyval;
                                        denom = getbyte( "Denom" );
                                        cc = getbyte( "clocks per click" );
                                        bb = getbyte( "32nd notes per 24 clocks" );
                                        for ( i = 0, k = 1; k < denom; i++, k <<= 1 )
                                            ;
                                        if ( k != denom )
                                            error( "Illegal TimeSig" );
                                        data[0] = (char)nn;
                                        data[1] = (char)i;
                                        data[2] = (char)cc;
                                        data[3] = (char)bb;
                                        M0 += (int)((newtime - T0) / (Beat * Measure));
                                        T0 = newtime;
                                        Measure = nn;
                                        Beat = 4 * Clicks / denom;
                                        mf_w_meta_event( (ulong)delta, time_signature, data, 4L );
                                    }
                                    break;

                                case SMPTE:
                                    for ( i = 0; i < 5; i++ ) {
                                        data[i] = (char)getbyte( "SMPTE" );
                                    }
                                    mf_w_meta_event( (ulong)delta, smpte_offset, data, 5L );
                                    break;

                                case KEYSIG:
                                    data[0] = (char)getint( "Keysig" );
                                    if ( data[0] < -7 || data[0] > 7 )
                                        error( "Key Sig must be between -7 and 7" );
                                    if ( (c = yylex()) != MINOR && c != MAJOR ) {
                                        syntax();
                                    }
                                    if ( c == MINOR ) {
                                        data[1] = (char)TRUE;//(c == MINOR);
                                    } else {
                                        data[1] = (char)FALSE;
                                    }
                                    mf_w_meta_event( (ulong)delta, key_signature, data, 2L );
                                    break;

                                case SEQNR:
                                    //get16val( "SeqNr" );
                                    get16val();//
                                    mf_w_meta_event( (ulong)delta, sequence_number, data, 2L );
                                    break;

                                case META: {
                                        int type = yylex();
                                        switch ( type ) {
                                            case TRKEND:
                                                type = end_of_track;
                                                break;
                                            case TEXT:
                                            case COPYRIGHT:
                                            case SEQNAME:
                                            case INSTRNAME:
                                            case LYRIC:
                                            case MARKER:
                                            case CUE:
                                                type -= (META + 1);
                                                break;
                                            case INT:
                                                type = (int)yyval;
                                                break;
                                            default:
                                                prs_error( "Illegal Meta type" );
                                                break;
                                        }
                                        if ( type == end_of_track )
                                            buflen = 0;
                                        else
                                            gethex();
                                        mf_w_meta_event( (ulong)delta, (uint)type, buffer, (ulong)buflen );
                                        break;
                                    }
                                case SEQSPEC:
                                    gethex();
                                    mf_w_meta_event( (ulong)delta, sequencer_specific, buffer, (ulong)buflen );
                                    break;
                                default:
                                    prs_error( "Unknown input" );
                                    break;
                            }
                            currtime = newtime;
                            break;
                        case EOL:
                            break;
                        default:
                            prs_error( "Unknown input" );
                            break;
                    }
                } catch (Exception ex ) {
                    continue;
                }
                checkeol();
            }
        }


        static int getint( String mess ) {//changed to static
            //char[] ermesg = new char[100];
            if ( yylex() != INT ) {
                String ermesg = "Integer expected for " + mess;//sprintf( ermesg, "Integer expected for %s", mess );
                error( ermesg );
                yyval = 0;
            }
            return (int)yyval;
        }


        int fileputc( int c ) {
            //return  putc(c, F);
            F.WriteByte( (byte)c );
            return 0;
        }


        // Functions to be called while processing the MIDI file.

        private int getc() {
            try {
                int res;
                res = F.ReadByte();
                return res;
            } catch ( Exception e ) {
                Console.Error.WriteLine( e.StackTrace );
                return EOF;
            }
        }

        private static void error( String s ) {//static二変更
            /*if ( TrksToDo <= 0 ) {
                Console.Error.WriteLine( "Error: Garbage at end" );
            } else {
                Console.Error.WriteLine( "Error: " + s );
            }*/
        }


        private void header( int format, int ntrks, int division ) {
            if ( (division & 0x8000) != FALSE ) {
                // SMPTE
                times = 0;   /* Can't do beats */
                sb.append( "MFile " + format + " " + ntrks + " " + (-((-(division >> 8)) & 0xff)) + " " + (division & 0xff) + "\n" );
            } else {
                sb.append( "MFile " + format + " " + ntrks + " " + division + "\n" );
            }
            //Console.Writef("MFile %d %d %d\n",format,ntrks,division);
            if ( format > 2 ) {
                //fprintf(stderr, "Can't deal with format %d files\n", format);
                Console.Error.WriteLine( "Can't deal with format " + format + " files" );
                //Console.Writef("Can't deal with format %d files\n", format);
                //System.Environment.Exit( 1 );
                return;
            }
            Beat = Clicks = division;
            TrksToDo = ntrks;
        }

        private void starttrack() {
            //sw.WriteLine( "MTrk" );
            sb.append( "MTrk\n" );
            TrkNr++;
        }

        private void endtrack() {
            //sw.WriteLine( "TrkEnd" );
            sb.append( "TrkEnd\n" );
            --TrksToDo;
        }

        private void on( int chan, int pitch, int v ) {
            prtime();
            //sw.WriteLine( "On ch=" + (chan + 1) + " " + note + "=" + mknote( pitch ) + " " + vol + "=" + v );
            sb.append( "On ch=" + (chan + 1) + " " + note + "=" + mknote( pitch ) + " " + vol + "=" + v + "\n" );
        }

        private void off( int chan, int pitch, int v ) {
            prtime();
            //sw.WriteLine( "Off ch=" + (chan + 1) + " " + note + "=" + mknote( pitch ) + " " + vol + "=" + v );
            sb.append( "Off ch=" + (chan + 1) + " " + note + "=" + mknote( pitch ) + " " + vol + "=" + v + "\n" );
        }

        private void pressure( int chan, int pitch, int press ) {
            prtime();
            //sw.WriteLine( PolyPr + " ch=" + (chan + 1) + " " + note + "=" + mknote( pitch ) + " " + val + "=" + press );
            sb.append( PolyPr + " ch=" + (chan + 1) + " " + note + "=" + mknote( pitch ) + " " + val + "=" + press + "\n" );
        }

        class Comment {
            public int Index;
            public String Msb;
            public String Lsb;
            public Comment( int index, String msb, String lsb ) {
                Index = index;
                Msb = msb;
                Lsb = lsb;
            }
        }
        
        private void parameter( int chan, int control, int value ) {
            prtime();
            //sw.WriteLine( Param + " ch=" + (chan + 1) + " " + con + "=" + control + " " + val + "=" + value );
            sb.append( Param + " ch=" + (chan + 1) + " " + con + "=" + control + " " + val + "=" + value + "\n" );
        }

        private void pitchbend( int chan, int lsb, int msb ) {
            prtime();
            //sw.WriteLine( "Pb ch=" + (chan + 1) + " " + val + "=" + (128 * msb + lsb) );
            sb.append( "Pb ch=" + (chan + 1) + " " + val + "=" + (128 * msb + lsb) + "\n" );
        }

        private void program( int chan, int program ) {
            prtime();
            //sw.WriteLine( ProgCh + " ch=" + (chan + 1) + " " + prog + "=" + program );
            sb.append( ProgCh + " ch=" + (chan + 1) + " " + prog + "=" + program + "\n" );
        }

        private void chanpressure( int chan, int press ) {
            prtime();
            //sw.WriteLine( ChanPr + " ch=" + (chan + 1) + " " + val + "=" + press );
            sb.append( ChanPr + " ch=" + (chan + 1) + " " + val + "=" + press + "\n" );
        }

        private void sysex() {
            sysex( msgleng(), msg() );
        }
        private void sysex( int leng, String mess ) {
            prtime();
            //sw.Write( "SysEx" );
            sb.append( "SysEx" );
            prhex( mess, leng );
        }

        private void arbitrary( int leng, String mess ) {
            prtime();
            //sw.Write( "Arb" );
            sb.append( "Arb" );
            prhex( mess, leng );
        }

        private void metamisc( int type, int leng, String mess ) {
            prtime();
            //sw.Write( "Meta 0x" + Convert.ToString( type, 16 ) );
            sb.append( "Meta 0x" + Convert.ToString( type, 16 ) );
            prhex( mess, leng );
        }

        private void seqnum( int num ) {
            prtime();
            //sw.WriteLine( "SeqNr " + num );
            sb.append( "SeqNr " + num + "\n" );
        }

        private void eot() {
            prtime();
            //sw.WriteLine( "Meta TrkEnd" );
            sb.append( "Meta TrkEnd\n" );
        }

        private void smpte( int hr, int mn, int se, int fr, int ff ) {
            prtime();
            //sw.WriteLine( "SMPTE " + hr + " " + mn + " " + se + " " + fr + " " + ff );
            sb.append( "SMPTE " + hr + " " + mn + " " + se + " " + fr + " " + ff + "\n" );
        }

        private void tempo( long tempo ) {
            prtime();
            //sw.WriteLine( "Tempo " + tempo );
            sb.append( "Tempo " + tempo +"\n" );
        }

        private void timesig( int nn, int dd, int cc, int bb ) {
            int denom = 1;
            while ( dd-- > 0 ) {
                denom *= 2;
            }
            prtime();
            //sw.WriteLine( "TimeSig " + nn + "/" + denom + " " + cc + " " + bb );
            sb.append( "TimeSig " + nn + "/" + denom + " " + cc + " " + bb + "\n" );
            M0 += (int)(Mf_currtime - T0) / (Beat * Measure);
            T0 = Mf_currtime;
            Measure = nn;
            Beat = 4 * Clicks / denom;
        }

        private void keysig( int sf, int mi ) {
            prtime();
            if ( mi != FALSE ) {
                //sw.WriteLine( "KeySig " + sf + " " + "minor" );
                sb.append( "KeySig " + sf + " " + "minor\n" );
            } else {
                //sw.WriteLine( "KeySig " + sf + " " + "major" );
                sb.append( "KeySig " + sf + " " + "major\n" );
            }
        }

        private void sqspecific( int leng, String mess ) {
            prtime();
            //sw.Write( "SeqSpec" );
            sb.append( "SeqSpec" );
            prhex( mess, leng );
        }

        private void text( int type, int leng, String mess ) {
            String[] ttype = {
	            "",
	            "Text",		/* type=0x01 */
	            "Copyright",	/* type=0x02 */
	            "TrkName",
	            "InstrName",	/* ...       */
	            "Lyric",
	            "Marker",
	            "Cue",		/* type=0x07 */
	            "Unrec"
            };

            int unrecognized = ttype.Length - 1;

            prtime();
            if ( type < 1 || type > unrecognized ) {
                //sw.Write( "Meta 0x" + Convert.ToString( type, 16 ) );
                sb.append( "Meta 0x" + Convert.ToString( type, 16 ) );
                prtext( mess, leng );
            } else if ( type == 3 && TrkNr == 1 ) {
                //sw.Write( "Meta SeqName " );
                sb.append( "Meta SeqName " );
                prtext( mess, leng );
            } else {
                //sw.Write( "Meta " + ttype[type] + " " );
                sb.append( "Meta " + ttype[type] + " " );
                prtext( mess, leng );
            }
        }


        // support functions for mf2t

        private void prtime() {
            if ( times != FALSE ) {
                long m = (Mf_currtime - T0) / Beat;
                //sw.Write( "" + (m / Measure + M0) + ":" + (m % Measure) + ":" + ((Mf_currtime - T0) % Beat) + " " );
                sb.append( "" + (m / Measure + M0) + ":" + (m % Measure) + ":" + ((Mf_currtime - T0) % Beat) + " " );
            } else {
                //sw.Write( "" + Mf_currtime + " " );
                sb.append( "" + Mf_currtime + " " );
            }
        }

        private void prtext( String p, int leng ) {
            //Console.Error.WriteLine( "prtext" );
            int n;
            char c;
            int pos = 25;
            //int index = -1;
            //sw.Write( "\"" );
            sb.append( "\"" );
            for ( n = 0; n < leng; n++ ) {
                //index++;
                char[] t = p.substring( n, n + 1 ).toCharArray(); 
                c = t[0];
                if ( fold != FALSE && (pos >= fold) ) {
                    //sw.Write( "\\" + Environment.NewLine + "\t" );//"\\\n\t");
                    sb.append( "\\" + "\n" + "\t" );
                    pos = 13;	/* tab + \xab + \ */
                    if ( c == ' ' || c == '\t' ) {
                        //sw.Write( "\\" );
                        sb.append( "\\" );
                        ++pos;
                    }
                }
                switch ( c ) {
                    case '\\':
                    case '"':
                        //sw.Write( "\\" + c );
                        sb.append( "\\" + c );
                        pos += 2;
                        break;
                    case '\r':
                        //sw.Write( "\\r" );
                        sb.append( "\\r" );
                        pos += 2;
                        break;
                    case '\n':
                        //sw.Write( "\\n" );
                        sb.append( "\\n" );
                        pos += 2;
                        break;
                    case '\0':
                        //sw.Write( "\\-" );
                        sb.append( "\\-" );
                        pos += 2;
                        break;
                    default:
                        if ( isprint( c ) ) {
                            //sw.Write( c.ToString() );
                            sb.append( c.ToString() );
                            ++pos;
                        } else {
                            //sw.Write( "\\x" + Convert.ToString( (int)c, 16 ) );
                            sb.append( "\\x" + Convert.ToString( (int)c, 16 ) );
                            pos += 4;
                        }
                        break;
                }
                //Console.Error.WriteLine( "in for loop" );
            }
            //Console.Error.WriteLine( "buffer=" + buffer );
            //sw.WriteLine( "\"" );
            sb.append( "\"\n" );
        }

        private String mknote( int pitch ) {
            String[] Notes = { "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b" };
            if ( notes != FALSE ) {
                return Notes[pitch % 12] + pitch / 12;
            } else {
                return "" + pitch;
            }
        }

        private int msgleng() {
            return Msgindex;
        }


        private String msg() {
            return Msgbuff;
        }

        private void prhex( String p, int leng ) {
            int n;
            int pos = 25;

            int index = -1;
            for ( n = 0; n < leng; n++, index++ ) {
                if ( fold != FALSE && (pos) >= fold ) {
                    //sw.Write( "\\" + Environment.NewLine + "\t" + Convert.ToString( (int)char.Parse( p.SubString( index, 1 ) ), 16 ) );
                    sb.append( "\\" + "\n" + "\t" + Convert.ToString( (int)char.Parse( p.SubString( index, 1 ) ), 16 ) );
                    pos = 14;	/* tab + ab + " ab" + \ */
                } else {
                    //sw.Write( " " + Convert.ToString( (int)char.Parse( p.SubString( index, 1 ) ), 16 ) );
                    sb.append( " " + Convert.ToString( (int)char.Parse( p.SubString( index, 1 ) ), 16 ) );
                    pos += 3;
                }
            }
            //sw.WriteLine( "" );
            sb.append( "\n" );
        }

        private static boolean isprint( char ch ) {
            if ( 32 <= (int)ch && (int)ch <= 126 ) {
                return true;
            } else {
                return false;
            }
        }

        private void readheader() {
            if ( readmt( "MThd" ) != -1 ) {
                Mf_toberead = read32bit();
                int format = read16bit();
                int ntrks = read16bit();
                int division = read16bit();
                header( format, ntrks, division );
                while ( Mf_toberead > 0L ) {
                    egetc();
                }
            }
        }

        private int readtrack() {
            int[] ttype = { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0 };
            int num2 = 0;
            int num4 = 0;
            int num5 = 0;
            int status = 0;
            if ( readmt( "MTrk" ) == -1 ) {
                return 0;
            }
            Mf_toberead = read32bit();
            Mf_currtime = 0L;
            starttrack();
            while ( Mf_toberead > 0L ) {
                long unrecognized;
                Mf_currtime += readvarinum();
                int c = egetc();
                if ( (num4 != 0) && (c != 0xf7) ) {
                    mferror( "didn't find expected continuation of a sysex" );
                }
                if ( (c & 0x80) == 0 ) {
                    if ( status == 0 ) {
                        mferror( "unexpected running status" );
                    }
                    num5 = 1;
                    num2 = c;
                    c = status;
                } else if ( c < 240 ) {
                    status = c;
                    num5 = 0;
                }
                int num7 = ttype[(c >> 4) & 15];
                if ( num7 != 0 ) {
                    if ( num5 == 0 ) {
                        num2 = egetc();
                    }
                    chanmessage( status, num2, (num7 <= 1) ? 0 : egetc() );
                    continue;
                }
                switch ( c ) {
                    case 0xff: {
                            int type = egetc();
                            unrecognized = (Mf_toberead - readvarinum()) - 1L;
                            msginit();
                            while ( Mf_toberead > unrecognized ) {
                                msgadd( egetc() );
                            }
                            metaevent( type );
                            continue;
                        }
                    case 240: {
                            unrecognized = Mf_toberead - readvarinum();
                            msginit();
                            msgadd( 240 );
                            while ( Mf_toberead > unrecognized ) {
                                msgadd( c = egetc() );
                            }
                            if ( (c != 0xf7) && (Mf_nomerge != 0) ) {
                                break;
                            }
                            sysex();
                            continue;
                        }
                    case 0xf7: {
                            unrecognized = Mf_toberead - readvarinum();
                            if ( num4 == 0 ) {
                                msginit();
                            }
                            while ( Mf_toberead > unrecognized ) {
                                msgadd( c = egetc() );
                            }
                            if ( num4 == 0 ) {
                                arbitrary( msgleng(), msg() );
                            } else if ( c == 0xf7 ) {
                                sysex();
                                num4 = 0;
                            }
                            continue;
                        }
                    default:
                        goto Label_0260;
                }
                num4 = 1;
                continue;
            Label_0260:
                badbyte( c );
            }
            endtrack();
            //Console.Write( buffer );
            return 1;
        }

        private int readmt( String s ) {
            String res = s;
            int num2 = 4;
            int[] e = new int[num2];
            e[0] = getc();
            e[1] = getc();
            e[2] = getc();
            e[3] = getc();
            for ( int i = 0; i < 4; i++ ) {
                if ( e[i] != char.Parse( res.SubString( i, 1 ) ) ) {
                    mferror( "expecting " + s );
                }
            }
            return e[3];
        }

        private void mferror( String s ) {
            error( s );
            //System.Environment.Exit( 1 );
        }

        private int read16bit() {
            int num = egetc();
            int num2 = egetc();
            return to16bit( num, num2 );
        }


        private long read32bit() {
            int num = egetc();
            int num2 = egetc();
            int num3 = egetc();
            int num4 = egetc();
            return to32bit( num, num2, num3, num4 );
        }


        private int egetc() {
            int num = getc();
            if ( num == EOF ) {
                mferror( "premature EOF" );
            }
            Mf_toberead -= 1L;
            return num;
        }

        private long readvarinum() {
            int num2 = egetc();
            long num = num2;
            if ( (num2 & 0x80) != 0 ) {
                num &= 0x7fL;
                do {
                    num2 = egetc();
                    num = (num << 7) + (num2 & 0x7f);
                } while ( (num2 & 0x80) != 0 );
            }
            return num;
        }

        private void chanmessage( int status, int c1, int c2 ) {
            int chan = status & 15;
            switch ( (status & 240) ) {
                case 0x80:
                    off( chan, c1, c2 );
                    break;

                case 0x90:
                    on( chan, c1, c2 );
                    break;

                case 160:
                    pressure( chan, c1, c2 );
                    break;

                case 0xb0:
                    parameter( chan, c1, c2 );
                    break;

                case 0xe0:
                    pitchbend( chan, c1, c2 );
                    break;

                case 0xc0:
                    program( chan, c1 );
                    break;

                case 0xd0:
                    chanpressure( chan, c1 );
                    break;
            }
        }

        private void msginit() {
            Msgindex = 0;
            Msgbuff = "";
        }

        private void msgadd( int c ) {
            Msgbuff = Msgbuff + (char)c;
            Msgindex++;
        }

        private void metaevent( int type ) {
            int leng = msgleng();
            String m = msg();
            switch ( type ) {
                case 0:
                    seqnum( to16bit(
                        (int)char.Parse( m.SubString( 0, 1 ) ),
                        (int)char.Parse( m.SubString( 1, 1 ) ) )
                    );
                    break;
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:
                case 10:
                case 11:
                case 12:
                case 13:
                case 14:
                case 15:
                    text( type, leng, m );
                    break;

                case 0x2f:
                    eot();
                    break;

                case 0x51:
                    tempo( to32bit(
                        0,
                        (int)char.Parse( m.SubString( 0, 1 ) ),
                        (int)char.Parse( m.SubString( 1, 1 ) ),
                        (int)char.Parse( m.SubString( 2, 1 ) ) )
                    );
                    break;

                case 0x54:
                    smpte(
                        (int)char.Parse( m.SubString( 0, 1 ) ),
                        (int)char.Parse( m.SubString( 1, 1 ) ),
                        (int)char.Parse( m.SubString( 2, 1 ) ),
                        (int)char.Parse( m.SubString( 3, 1 ) ),
                        (int)char.Parse( m.SubString( 4, 1 ) )
                    );
                    break;

                case 0x58:
                    timesig(
                        (int)char.Parse( m.SubString( 0, 1 ) ),
                        (int)char.Parse( m.SubString( 1, 1 ) ),
                        (int)char.Parse( m.SubString( 2, 1 ) ),
                        (int)char.Parse( m.SubString( 3, 1 ) )
                    );
                    break;

                case 0x59:
                    keysig(
                        (int)char.Parse( m.SubString( 0, 1 ) ),
                        (int)char.Parse( m.SubString( 1, 1 ) )
                    );
                    break;

                case 0x7f:
                    sqspecific( leng, m );
                    break;

                default:
                    metamisc( type, leng, m );
                    break;
            }
        }

        private void badbyte( int c ) {
            mferror( "unexpected byte: " + c );
        }

        private static int to16bit( int c1, int c2 ) {
            return (((c1 & 0xff) << 8) + (c2 & 0xff));
        }


        private static long to32bit( int c1, int c2, int c3, int c4 ) {
            long num = 0L;
            num = c1 & 0xff;
            num = (num << 8) + (c2 & 0xff);
            num = (num << 8) + (c3 & 0xff);
            return ((num << 8) + (c4 & 0xff));
        }
    }
