/* * * gmext.c * * * * gm.cat MIDI sequence extractor * * */ /* * Originally created by cirdan * Published on: http://www.tt-forums.net/viewtopic.php?f=31&t=31293&hilit=music+gm * * Modified by Lck for MSVC 2008 compatibility */ //#include #include #include typedef int uint32_t; #define RUNNING_MODE /*inline*/ long read_delta (const unsigned char* *p) { long delta = **p & 0x7F; while ( *(*p)++ & 0x80 ) { delta <<= 7; delta += **p & 0x7F; } return delta; } void write_delta (FILE *f, long delta) { unsigned char c [4] = { 0, 0x80, 0x80, 0x80 }; int i = 0; while ( delta > 0x7F ) { c[i++] |= delta & 0x7F; delta >>= 7; } c[i++] |= delta; while ( i ) fwrite (c + --i, 1, 1, f); } struct seq { uint32_t size; unsigned char *data; }; unsigned char volume [0x80] = { 100,100,100,100,100, 90, 100,100,100,100,100, 90, 100,100,100,100,100,100, 85, 100,100,100,100,100,100,100,100,100, 90,90, 110, 80, 100,100,100, 90, 70, 100,100,100,100,100,100,100,100,100,100,100,100,100, 90, 100,100,100,100,100,100, 120, 100,100,100, 120, 100, 127, 100,100, 90, 100,100,100,100,100,100, 95, 100,100,100,100,100,100,100,100,100,100,100,100,100, 115, 100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100 }; long tseq (FILE *f, unsigned nsubs, const struct seq *subs, char channel, const struct seq *seq, unsigned char *patch, long delta0) { long delta = delta0; unsigned char output, command = 0x80; unsigned char data1, data2; const unsigned char *data = seq->data; const unsigned char *dataend = seq->data + seq->size; while ( data < dataend ) { delta += read_delta (&data); if ( *data & 0x80 ) command = *data++; switch ( command ) { case 0xFF: case 0xFD: return delta; case 0xFE: /* insert subsequence */ if ( *data >= nsubs ) { puts ("invalid subsequence"); return 0; } delta = tseq (f, nsubs, subs, channel, &subs[*data++], patch, delta); continue; } command &= 0xF0; data1 = *data++; switch ( command ) { case 0x80: case 0x90: data2 = *data++; write_delta (f, delta); delta = 0; #ifdef RUNNING_MODE if ( output != (command|channel) ) #endif { output = command | channel; fwrite (&output, 1, 1, f); } fwrite (&data1, 1, 1, f); if ( data2 ) data2 = (unsigned) data2 * (channel==9 ? 80 : volume[*patch]) >> 7; fwrite (&data2, 1, 1, f); break; case 0xC0: if ( data1 == 0x7E ) return delta; *patch = data1; write_delta (f, delta); delta = 0; #ifdef RUNNING_MODE if ( output != (0xC0|channel) ) #endif { output = 0xC0 | channel; fwrite (&output, 1, 1, f); } if ( (data1==0x57) || (data1==0x3F) ) data1 = 0x3E; fwrite (&data1, 1, 1, f); break; case 0xB0: data2 = *data++; if ( (data1==0x7E) || (!data1 && !data2) ) continue; write_delta (f, delta); delta = 0; if ( !data1 ) { uint32_t x; char xdata [3]; output = 0xFF; fwrite (&output, 1, 1, f); data1 = 0x51; fwrite (&data1, 1, 1, f); data1 = 3; fwrite (&data1, 1, 1, f); x = (740*163840) * data2; xdata[0] = x>>16; xdata[1] = x>>8; xdata[2] = x; fwrite (&xdata, 1, 3, f); } #ifdef RUNNING_MODE if ( output != (0xB0|channel) ) #endif { output = 0xB0 | channel; fwrite (&output, 1, 1, f); } fwrite (&data1, 1, 1, f); if ( data1 == 0x5B ) data2 = 0x1E; fwrite (&data2, 1, 1, f); break; case 0xE0: data2 = *data++; write_delta (f, delta); delta = 0; #ifdef RUNNING_MODE if ( output != (0xE0|channel) ) #endif { output = 0xE0 | channel; fwrite (&output, 1, 1, f); } fwrite (&data1, 1, 1, f); fwrite (&data2, 1, 1, f); break; } } return 0; } void midi_header (FILE *f, unsigned ntracks) { static const char header [10] = { 'M','T','h','d',0,0,0,6,0,1 }; static const char delta [2] = { 0,24 }; char ntr [2]; fwrite (header, 1, 10, f); ntr[0] = ntracks>>8; ntr[1] = ntracks; fwrite (ntr, 1, 2, f); fwrite (delta, 1, 2, f); } static const char midi_track_start [8] = { 'M','T','r','k',0,0,0,0 }; static const char midi_track_end [4] = { 0, 0xFF, 0x2F, 0 }; void midi_track0 (FILE *f, unsigned tempo, unsigned namelen, const char *name) { static const char set_tempo [4] = { 0, 0xFF, 0x51, 3 }; static const char text_event [3] = { 0, 0xFF, 1 }; char data [4]; unsigned tsize; fwrite (midi_track_start, 1, 4, f); tsize = namelen + 15; data[0] = tsize>>24; data[1] = tsize>>16; data[2] = tsize>>8; data[3] = tsize; fwrite (data, 1, 4, f); fwrite (set_tempo, 1, 4, f); tempo = (370*163840) / tempo; data[0] = tempo>>16; data[1] = tempo>>8; data[2] = tempo; fwrite (data, 1, 3, f); fwrite (text_event, 1, 3, f); fwrite (&namelen, 1, 1, f); fwrite (name, 1, namelen, f); fwrite (midi_track_end, 1, 4, f); } void midi_track (FILE *f, unsigned nsubs, const struct seq *subs, char channel, const struct seq *seq) { static char midi_track_begin [10] = { 0, 0xB0, 0x78, 0, 0, 0x79, 0, 0, 0x7B, 0 }; long foffset, length; char data [4]; unsigned char patch = 0; fwrite (midi_track_start, 1, 8, f); foffset = ftell (f); midi_track_begin[1] = 0xB0 | channel; fwrite (midi_track_begin, 1, 10, f); write_delta (f, tseq (f, nsubs, subs, channel, seq, &patch, 0)); fwrite (midi_track_end+1, 1, 3, f); length = ftell(f) - foffset; fseek (f, foffset-4, SEEK_SET); data[0] = length>>24; data[1] = length>>16; data[2] = length>>8; data[3] = length; fwrite (data, 1, 4, f); fseek (f, 0, SEEK_END); } int gmext_song (FILE *f, uint32_t n) { uint32_t offset, length; unsigned char namelength, tempo, nsubs, ntracks; char name [256]; struct seq subs [256]; char channels [256]; struct seq tracks [256]; unsigned namelen, i; char out [256]; FILE *fout; /* read in offset and length of song */ if ( fseek (f, 8*n, SEEK_SET) == -1 ) return -1; if ( fread (&offset, 4, 1, f) != 1 ) return -1; if ( fread (&length, 4, 1, f) != 1 ) return -1; if ( fseek (f, offset, SEEK_SET) == -1 ) return -1; /* read in song name */ if ( fread (&namelength, 1, 1, f) != 1 ) return -1; if ( fread (name, 1, namelength, f) != namelength ) return -1; if ( namelength && !name[namelength-1] ) namelen = namelength-1; else name[namelen=namelength] = 0; printf ("Extracting song #%u %s\n", n, name); /* read in tempo */ if ( fread (&tempo, 1, 1, f) != 1 ) return -1; /* read in subs */ if ( fread (&nsubs, 1, 1, f) != 1 ) return -1; for (i=0; i1 ? args[1] : "gm.cat"); }