Update NTK.
[nondaw.git] / sequencer / src / smf.C
blobe79d3fb30ea9c49203abf575a29770eb76ad95bb
2 /*******************************************************************************/
3 /* Copyright (C) 2008 Jonathan Moore Liles                                     */
4 /*                                                                             */
5 /* This program is free software; you can redistribute it and/or modify it     */
6 /* under the terms of the GNU General Public License as published by the       */
7 /* Free Software Foundation; either version 2 of the License, or (at your      */
8 /* option) any later version.                                                  */
9 /*                                                                             */
10 /* This program is distributed in the hope that it will be useful, but WITHOUT */
11 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       */
12 /* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for   */
13 /* more details.                                                               */
14 /*                                                                             */
15 /* You should have received a copy of the GNU General Public License along     */
16 /* with This program; see the file COPYING.  If not,write to the Free Software */
17 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18 /*******************************************************************************/
20 #include "smf.H"
21 #include "phrase.H"
22 #include "pattern.H"
25 smf::smf ( void )
27     _name = NULL;
28     _pos = 0;
30     _fp = NULL;
32     _length = 0;
33     _length_pos = 0;
34     _num_tracks_pos = 0;
35     _tracks = 0;
36     _time = 0;
37     _tally = 0;
38     _cue = 0;
39     _track = 0;
42 smf::~smf ( void )
44     /* fill in the number of tracks */
45     if ( _num_tracks_pos )
46     {
47         fseek( _fp,  _num_tracks_pos, SEEK_SET );
48         write_short( _tracks );
49     }
51     if ( _fp )
52         fclose( _fp );
54     if ( _name )
55         free( _name );
58 int
59 smf::open ( const char *name, int mode )
61     _name = strdup( name );
63     _mode = mode;
65     _fp = fopen( _name, mode == smf::WRITE ? "w" : "r" );
67     return _fp != NULL;
70 /*************************/
71 /* Private bit twiddlers */
72 /*************************/
74 unsigned long
75 smf::read_long ( void )
77     byte_t buf[4];
78     unsigned long ret = 0;
80     read_bytes( buf, 4 );
82     ret += *(buf + 0) << 24;
83     ret += *(buf + 1) << 16;
84     ret += *(buf + 2) << 8;
85     ret += *(buf + 3);
87     return ret;
90 unsigned short
91 smf::read_short ( void )
93     byte_t buf[2];
94     unsigned short ret = 0;
96     read_bytes( buf, 2 );
98     ret += *(buf + 0) << 8;
99     ret += *(buf + 1);
101     return ret;
104 unsigned long
105 smf::read_var ( void )
107     unsigned long ret = 0;
108     unsigned char c;
110     /* while bit #7 is set */
111     while ( ( ( c = read_byte() ) & 0x80 ) != 0x00 )
112     {
114         /* shift ret 7 bits */
115         ret <<= 7;
116         /* add bits 0-6 */
117         ret += c & 0x7F;
118     }
120     /* bit was clear */
121     ret <<= 7;
122     ret += c & 0x7F;
124     return ret;
127 void
128 smf::read_bytes ( void *p, int l )
130     fread( p, l, 1, _fp );
132     _pos += l;
135 byte_t
136 smf::read_byte ( void )
138     byte_t b;
139     read_bytes( &b, 1 );
141     return b;
144 void
145 smf::write_var ( long var )
147     long buffer;
148     buffer = var & 0x7F;
150     /* we shift it right 7, if there is
151        still set bits, encode into buffer
152        in reverse order */
153     while ( ( var >>= 7) )
154     {
155         buffer <<= 8;
156         buffer |= ( var & 0x7F ) | 0x80;
157     }
159     for ( ;; )
160     {
161         write_byte( buffer );
163         if ( buffer & 0x80 )
164             buffer >>= 8;
165         else
166             break;
167     }
171 void
172 smf::write_long ( unsigned long x )
174     byte_t buf[4];
176     buf[0] = ( x & 0xFF000000 ) >> 24;
177     buf[1] = ( x & 0x00FF0000 ) >> 16;
178     buf[2] = ( x & 0x0000FF00 ) >> 8;
179     buf[3] = x & 0x000000FF;
181     write_bytes( buf, 4 );
184 void
185 smf::write_ascii ( const char *buf )
187     if ( strlen( buf ) != 4 )
188         ASSERTION( "invalid MIDI value" );
190     write_bytes( (void *)buf, 4 );
193 void
194 smf::write_short ( unsigned short x )
196     byte_t buf[2];
198     buf[0] = (x & 0xFF00 ) >> 8;
199     buf[1] = x & 0x00FF;
201     write_bytes( buf, 2 );
204 void
205 smf::write_byte ( byte_t b )
207     write_bytes( &b, 1 );
211 void
212 smf::write_bytes ( const void *p, size_t l )
214     fwrite( p, l, 1, _fp );
215     _tally += l;
220 /*************************/
221 /* Read and write tracks */
222 /*************************/
224 /* write event /e/ to the currently open file (should only be used in a track)
225    if /cue/ is true, transform a notes-on/off into cue messages */
226 void
227 smf::write_event ( const midievent *e )
229     tick_t ts = e->timestamp();
230     tick_t delta = ts - _time;
231     _time = ts;
233     write_var( delta );
235     if ( _cue && (e->is_note_off() || e->is_note_on() ) )
236     {
237         /* begin cue message */
238         write_byte( 0xF0 );                                    /* sysex */
240         write_var( 7 );                                        /* length of this message */
242         static byte_t data[] = { 0x7F,                         /* MTC */
243                                  0,                            /* id */
244                                  0x05 };                       /* cue message */
246         write_bytes( data, sizeof( data ) );
248         write_byte( e->opcode() == event::NOTE_ON ? 0x05 : 0x06 );
249         write_short( e->note() );
251         /* terminate */
252         write_byte( 0xF7 );
254         _status = 0;
255     }
256     else
257     {
258         byte_t buf[4];
260         int l = e->size();
262         midievent me = *e;
264         if ( me.opcode() == event::NOTE_OFF )
265         {
266             me.opcode( event::NOTE_ON );
267             me.note_velocity( 0 );
268         }
270         me.raw( buf, l );
272         /* write with running status */
273         if ( buf[0] != _status )
274         {
275             write_bytes( buf, l );
276             _status = buf[0];
277         }
278         else
279             write_bytes( buf + 1, l - 1 );
281     }
284 void
285 smf::write_header ( int fmt )
287     write_ascii( "MThd" );
288     write_long( 6 );                                             /* Always 6 bytes of header */
290     _format = fmt;
292     write_short( fmt );                                          /* format, SMF-0 for 1 track SMF-2 for more */
294     _num_tracks_pos = ftell( _fp );
295     _tracks = 0;
297     write_short( 0xDEAF );
299     write_short( PPQN );
302 /* start a new MIDI 'chunk', /id/ is 4 letters of ASCII */
303 void
304 smf::open_chunk ( const char *id )
306     if ( _length_pos )
307         ASSERTION( "chunks cannot be nested!" );
309     write_ascii( id );
311     /* reset track length counter */
312     _length_pos = ftell( _fp );
314     write_long( 0xBEEFCAFE );                                   /* length, this has to be filled in at track end! */
316     _tally = 0;
317     _time = 0;
320 void
321 smf::close_chunk ( void )
323     /* fill in track length  */
324     long here = ftell( _fp );
326     fseek( _fp, _length_pos, SEEK_SET );
328     write_long( _tally );
330     fseek( _fp, here, SEEK_SET );
332     /* cleanup */
333     _length_pos = 0;
334     _tally = 0;
337 void
338 smf::open_track ( const char *name, int num )
340     open_chunk( "MTrk" );
342     if ( _format == 2 && num >= 0 )
343         write_meta_event ( smf::SEQUENCE, num );
345     if ( name )
346         write_meta_event ( smf::NAME, name );
348     ++_tracks;
350     _status = 0;
352     // FIXME: write time signature here
355 void
356 smf::close_track ( tick_t length )
358     /* end */
359     write_meta_event( smf::END, length ? length - _time : 0 );
361     _cue = 0;
363     close_chunk();
366 void
367 smf::write_meta_event ( byte_t type, int n )
369     write_var( type == smf::END ? n : 0 );                                             /* delta time */
370     write_short( 0xFF00 + type );
372     /* write length bytes */
373     switch ( type )
374     {
375         case smf::TEMPO:
376             write_byte( 3 );
377 // FIXME:
378             break;
379         case smf::SEQUENCE:
380             write_byte( 2 );
381             write_short( n );
382             break;
383         case smf::CHANNEL:
384         case smf::PORT:
385             write_byte( 1 );
386             write_byte( n );
387             break;
388         case smf::END:
389             write_byte( 0x00 );
390             break;
391         case smf::PROPRIETARY:
392             // length
393             write_var( n );
394             break;
395 // FIXME: handle time sig, key sig, proprietary
396     }
398     _status = 0;
401 void
402 smf::write_meta_event ( byte_t type, const char *s )
404     write_var( 0 );
405     write_short( 0xFF00 + type );
407     switch ( type )
408     {
409         case smf::TEXT:
410         case smf::NAME:
411         case smf::INSTRUMENT:
412         case smf::COPYRIGHT:
413         case smf::LYRIC:
414         case smf::MARKER:
415         case smf::CUEPOINT:
416         {
417             int l = strlen( s );
419             write_var( l );
421             write_bytes( s, l );
423             break;
424         }
425         default:
426             ASSERTION( "event type does not take text!" );
427             break;
428     }
431 /** write song gloabl info (only used on playlist track) */
432 void
433 smf::write_song_info ( int mode, int phrases, int patterns, const char *name, const char *notes )
435     write_meta_event( smf::PROPRIETARY, 5 + (4 * 2) /* length */ );
437     write_ascii( "Non!" );
439     write_byte( mode );
440     write_long( phrases );
441     write_long( patterns );
443     if ( name )
444         write_meta_event( smf::NAME, name );
446     write_meta_event( smf::TEXT, ":: Created by the Non-Seqeuncer" );
448     if ( notes )
449         write_meta_event( smf::TEXT, notes );
452 void
453 smf::write_phrase_info ( const phrase *p )
455     if ( p->notes() )
456         write_meta_event( smf::TEXT, p->notes() );
458     char *s = p->viewport.dump();
460     char pat[156];
461     snprintf( pat, sizeof( pat ), "Non: xywh=%s",
462               s );
464     free( s );
466     write_meta_event( smf::PROPRIETARY, strlen( pat ) );
467     write_bytes( pat, strlen( pat ) );
470 /** write proprietary pattern info meta event */
471 void
472 smf::write_pattern_info ( const pattern *p )
474     write_meta_event( smf::PORT, p->port() );
476     char pat[256];
478     snprintf( pat, sizeof( pat ), "%s: %s", p->mapping.type(), p->mapping.name() );
480     write_meta_event( smf::INSTRUMENT, pat );
482     if ( p->notes() )
483         write_meta_event( smf::TEXT, p->notes() );
485     char *s = p->viewport.dump();
487     snprintf( pat, sizeof( pat ), "Non: xywh=%s, ppqn=%d, key=%d, note=%d, mode=%d",
488               s, p->ppqn(), p->mapping.key(), p->note(), p->mode() );
490     free( s );
492     write_meta_event( smf::PROPRIETARY, strlen( pat ) );
493     write_bytes( pat, strlen( pat ) );
496 /* turn on note->cue translation for this track */
497 void
498 smf::cue ( bool b )
500     _cue = b;
504 /**********/
505 /* Reader */
506 /**********/
508 char *
509 smf::read_text ( void )
511     int l = read_var();
513     char *s = (char*) malloc( l + 1 );
515     read_bytes( s, l );
517     s[l] = '\0';
519     return s;
524 smf::read_header ( void )
526     char id[4];
527     read_bytes( id, 4 );
529     if ( strncmp( id, "MThd", 4 ) )
530         return 0;
532     if ( read_long() != 6 )
533         return 0;
535     _format = read_short();
536     _tracks = read_short();
537     _ppqn   = read_short();
539     _pos = 0;
541     return 1;
544 void
545 smf::home ( void )
547     fseek( _fp, 14, SEEK_SET );
549     _track = 0;
550     _pos = 0;
551     _length = 0;
554 void
555 smf::skip ( size_t l )
557     fseek( _fp, l, SEEK_CUR );
558     _pos += l;
561 void
562 smf::backup ( size_t l )
564     skip( 0 - l );
567 char *
568 smf::read_track_name ( void )
570     int status;
571     long where = 0;
572     int num = 0;
574     for ( num = 0; ; ++num )
575     {
576         where = _pos;
578         read_var();                                              /* delta */
579         status = read_byte();
581         /* stop at first non meta-event */
582         if ( status != midievent::META )
583             break;
585         int opcode = read_byte();
587         switch ( opcode )
588         {
589             case smf::NAME:
590                 return read_text();
591             case smf::TEXT:
592                 return read_text();
593             default:
594                 skip( read_var() );
595         }
596     }
598     backup( _pos - where );
600     return NULL;
603 /** read next Cue Point event on track */
604 char *
605 smf::read_cue_point ( void )
607     read_var();                                                 /* delta */
609     int status = read_byte();
611     if ( status != midievent::META )
612         return NULL;
614     int opcode = read_byte();
616     if ( opcode != smf::CUEPOINT )
617         return NULL;
619     return read_text();
622 bool
623 smf::read_song_info ( int * mode, int * phrases, int *patterns, char **name,  char **notes )
625     int status;
626     long where = 0;
627     int num = 0;
628     bool r = false;
629     *notes = NULL;
631     for ( num = 0; ; ++num )
632     {
633         where = _pos;
635         read_var();                                              /* delta */
636         status = read_byte();
638         /* stop at first non meta-event */
639         if ( status != midievent::META )
640             break;
642         int opcode = read_byte();
644         switch ( opcode )
645         {
646             case smf::PROPRIETARY:
647             {
648                 int len = read_var();
650                 if ( len < 5 + (2 * 4) )
651                     return false;
653                 char id[4];
654                 read_bytes( id, 4 );
656                 if ( strncmp( id, "Non!", 4 ) )
657                     return false;
659                 *mode = read_byte();
660                 *phrases = read_long();
661                 *patterns = read_long();
663                 r = true;
665                 break;
666             }
667             case smf::TEXT:
668             {
669                 char *text = read_text();
671                 if ( ! strncmp( text, "::", 2 ) )
672                     free( text );
673                 else
674                     *notes = text;
676                 break;
677             }
678             case smf::NAME:
679                 *name = read_text();
680                 break;
681             case smf::END:
682                 goto done;
683             default:
684                 goto semidone;
685         }
686     }
688 semidone:
690     backup( _pos - where );
692 done:
694     return r;
697 bool
698 smf::read_phrase_info ( phrase *p )
700     int status;
701     long where = 0;
702     int num = 0;
704     for ( num = 0; ; ++num )
705     {
706         where = _pos;
708         read_var();                                              /* delta */
709         status = read_byte();
711         /* stop at first non meta-event */
712         if ( status != midievent::META )
713             break;
715         int opcode = read_byte();
717         switch ( opcode )
718         {
719             case smf::SEQUENCE:
720                 /* currently, this is ignored */
721                 read_var();
722                 read_short();
723                 break;
724             case smf::NAME:
725                 p->name( read_text() );
726                 DMESSAGE( "Track name: %s", p->name() );
727                 break;
728             case smf::INSTRUMENT:
729                 skip( read_var() );
730                 break;
731             case smf::TEXT:
732                 p->notes( read_text() );
733                 break;
734             case smf::PROPRIETARY:
735             {
736                 int l = read_var();
738                 char *data = (char *) alloca( l ) + 1;
740                 read_bytes( data, l );
742                 data[l] = '\0';
744                 char *s;
746                 if ( 1 != sscanf( data, "Non: xywh=%a[0-9:]",
747                                   &s ) )
748                     WARNING( "Invalid phrase info event" );
749                 else
750                 {
751                     p->viewport.read( s );
752                     free( s );
753                 }
754                 break;
755             }
756             case smf::END:
757                 /* Track ends before any non meta-events... */
758                 read_byte();
759                 goto done;
760             default:
761                 int l = read_var();
762                 skip( l );
763                 WARNING( "skipping unrecognized meta event %02X", opcode );
764                 break;
765         }
766     }
768     backup( _pos - where );
770 done:
772     return num ? p : NULL;
775 /** inform pattern /p/ from meta-events at the beginning of the
776     current track */
777 bool
778 smf::read_pattern_info ( pattern *p )
780     int status;
781     long where = 0;
782     int num = 0;
784     bool name_set = false;
786     for ( num = 0; ; ++num )
787     {
788         where = _pos;
790         read_var();                                              /* delta */
791         status = read_byte();
793         /* stop at first non meta-event */
794         if ( status != midievent::META )
795             break;
797         int opcode = read_byte();
799         switch ( opcode )
800         {
801             case smf::SEQUENCE:
802                 /* currently, this is ignored */
803                 read_var();
804                 read_short();
805                 break;
806             case smf::NAME:
807                 p->name( read_text() );
808                 DMESSAGE( "Track name: %s", p->name() );
809                 name_set = true;
810                 break;
811             case smf::INSTRUMENT:
812             {
813                 char *s = read_text();
815                 char pat[256];
817                 if ( 1 == sscanf( s, "Instrument: %[^\n]", pat ) )
818                 {
819                     if ( ! p->mapping.open( Mapping::INSTRUMENT, pat ) )
820                     {
821                         p->mapping.open( Mapping::SCALE, "Chromatic" );
822                         WARNING( "could not find instrument \"%s\"", pat );
823                     }
824                 }
825                 else
826                     if ( 1 == sscanf( s, "Scale: %[^\n]", pat ) )
827                     {
828                         if ( ! p->mapping.open( Mapping::SCALE, pat ) )
829                         {
830                             p->mapping.open( Mapping::SCALE, "Chromatic" );
831                             WARNING( "could not find scale \"%s\"", pat );
832                         }
833                     }
834                 break;
835             }
836             case smf::PORT:
837                 read_byte();
838                 p->port( read_byte() );
839                 break;
840             case smf::TEXT:
841                 if ( ! name_set )
842                 {
843                     /* also accept TEXT event as name if no name was
844                        provided--this is found in a number of older MIDI
845                        files. */
846                     p->name( read_text() );
847                     name_set = true;
848                 }
849                 else
850                     p->notes( read_text() );
851                 break;
852             case smf::PROPRIETARY:
853             {
854                 int l = read_var();
856                 char *data = (char *) alloca( l ) + 1;
858                 read_bytes( data, l );
860                 data[l] = '\0';
862                 int ppqn, key, note, mode;
863                 char *s;
865                 if ( 5 != sscanf( data, "Non: xywh=%a[0-9:], ppqn=%d, key=%d, note=%d, mode=%d",
866                                   &s, &ppqn, &key, &note, &mode ) )
867                     WARNING( "Invalid pattern info event" );
868                 else
869                 {
870                     p->viewport.read( s );
871                     free( s );
873                     p->ppqn( ppqn );
875                     if ( key > 0 )
876                         p->mapping.key( key );
878                     p->note( note );
879                     p->mode( mode );
880                 }
881                 break;
882             }
883             case smf::END:
884                 /* Track ends before any non meta-events... */
885                 read_byte();
886                 goto done;
887             default:
888                 int l = read_var();
889                 skip( l );
890                 WARNING( "skipping unrecognized meta event %02X", opcode );
891                 break;
892         }
893     }
895     backup( _pos - where );
897 done:
899     return num ? p : NULL;
903 smf::next_track ( void )
905     /* first, skip to the end of the track we're on, if any */
906     if ( _length )
907         skip( _length - _pos );
909     while ( ! feof( _fp ) && _track < _tracks )
910     {
911         char id[4];
912         read_bytes( id, 4 );
913         _length = read_long();
915         if ( strncmp( id, "MTrk", 4 ) )
916         {
917             WARNING( "skipping unrecognized chunk \"%s\"", id );
918             /* not a track chunk */
919             skip( _length );
920             continue;
921         }
923         _pos = 0;
924         ++_track;
925         return 1;
926     }
928     return _length = _pos = 0;
932 /** locate track number /n/ */
933 bool
934 smf::seek_track ( int n )
936     home();
938     if ( n >= _tracks )
939         return false;
941     for ( int i = 0; next_track(); ++i )
942         if ( i == n )
943             break;
945     return true;
948 char **
949 smf::track_listing ( void )
951     if ( _pos != 0 )
952         ASSERTION( "attempt to get track listing while in the middle of reading a track." );
954     char **sa = (char**)malloc( sizeof( char* ) * (_tracks + 1) );
955     int i;
957     long where = ftell( _fp );
959     for ( i = 0; next_track(); ++i )
960     {
961         sa[i] = read_track_name();
962         sa[i] = sa[i] ? sa[i] : strdup( "<Unnamed>" );
963     }
965     sa[i] = NULL;
967     /* go back to where we started */
968     fseek( _fp, where, SEEK_SET );
969     _pos = 0;
971     return sa;
974 /* print track list for file /name/ */
975 void
976 smf::print_track_listing ( const char *name )
978     smf f;
980     f.open( name, smf::READ );
982     f.read_header();
984     char **sa = f.track_listing();
986     char *s;
987     for ( int i = 0; (s = sa[i]); ++i )
988         printf( "Track %3d: \"%s\"\n", i, s );
991 /** read all remaining events in current track and return them in a list */
992 list <midievent> *
993 smf::read_track_events ( tick_t *length )
995     list <midievent> *events = new list <midievent>;
996     event e;
998     *length = 0;
1000     byte_t oldstatus = -1;
1001     tick_t time = 0;
1002     tick_t tick = 0;
1003     tick_t delta;
1005     while ( _pos < _length )
1006     {
1007         byte_t data[3];
1009         delta = read_var();
1011         int status = read_byte();
1013         if ( ! (status & 0x80) )
1014         {
1015             backup( 1 );
1016             status = oldstatus;
1017         }
1018         else
1019             oldstatus = status;
1021         time += delta;
1022         tick = (time * PPQN) / _ppqn;
1024         e.timestamp( tick );
1026         int opcode = status & 0xF0;
1028 //        e.status( opcode );
1029         e.status( status );
1031         switch ( opcode )
1032         {
1033             case event::NOTE_OFF:
1034             case event::NOTE_ON:
1035             case event::AFTERTOUCH:
1036             case event::CONTROL_CHANGE:
1037             case event::PITCH_WHEEL:
1039                 read_bytes( data, 2 );
1041                 /* handle note off, vel 0 */
1042                 if ( opcode == event::NOTE_ON && 0 == data[1] )
1043                 {
1044                     e.opcode( event::NOTE_OFF );
1045                     data[1] = 127;
1046                 }
1048                 e.data( data[0], data[1] );
1050                 events->push_back( e );
1052                 /* TODO: set MIDI channel here */
1053                 break;
1054             case event::PROGRAM_CHANGE:
1055             case event::CHANNEL_PRESSURE:
1057                 data[0] = read_byte();
1059                 e.lsb( data[0] );
1061                 events->push_back( e );
1062                 break;
1063             case 0xF0:
1064                 /* TODO: hanlde proprietary events? */
1065                 if ( midievent::META != status )
1066                 {
1067                     if ( 0xF0 == status )
1068                     {
1069                         /* looks like a sysex */
1070                         int l = read_var();
1072                         if ( l < 4 )
1073                             ASSERTION( "unrecognized message" );
1075                         byte_t *data = (byte_t *) alloca( 4 );
1077                         read_bytes( data, 4 );
1079                         l -= 4;
1081                         if ( data[0] == 0x7F &&
1082                              data[2] == 0x05 )
1083                         {
1084                             /* looks like a cue message! */
1086                             switch ( data[3] )
1087                             {
1088                                 case 0x05:
1089                                     /* start */
1090                                     e.status( event::NOTE_ON );
1091                                     e.note( read_short() );
1092                                     events->push_back( e );
1093                                     l -= 2;
1094                                     break;
1095                                 case 0x06:
1096                                     /* stop */
1097                                     e.status( event::NOTE_OFF );
1098                                     e.note( read_short() );
1099                                     events->push_back( e );
1100                                     l -= 2;
1101                                     break;
1102                                 default:
1103                                     ASSERTION( "unrecognized cue message" );
1104                                     break;
1105                             }
1106                         }
1108                         DMESSAGE( "converting MIDI cue to note-on/off n: %d", e.note() );
1110                         /* just in case */
1111                         skip( l );
1112                     }
1113                     else
1114                     {
1115                         WARNING( "unrecognized opcode %02X", status );
1116                         // FIXME: what now?
1117                     }
1118                     break;
1119                 }
1121                 opcode = read_byte();
1123                 switch ( opcode )
1124                 {
1125                     case smf::END:                                  /* track end */
1126                         /* track extends until this event */
1127                         *length = tick;
1129                         if ( read_byte() )
1130                             WARNING( "corrupt MIDI file in track end" );
1131                         goto done;
1132                         break;
1133                     default:
1134                         WARNING( "unhandled meta-event %02X", opcode );
1135                         skip( read_var() );
1136                         break;
1137                 }
1138         }
1139     }
1141 done:
1143     return events;
1147 /**************************/
1148 /* accessors (for reader) */
1149 /**************************/
1152 smf::format ( void ) const
1154     return _format;
1158 smf::tracks ( void ) const
1160     return _tracks;