X-Git-Url: https://git.jankratochvil.net/?a=blobdiff_plain;f=common%2Ffiles%2Fmidifile.c;fp=common%2Ffiles%2Fmidifile.c;h=71773739bd6d05372b5852996b830a7f881ae8c6;hb=5d49fe8eea4e11457b4f9904eb0d5db435679841;hp=0000000000000000000000000000000000000000;hpb=a51604573a1dfaaad4304ffbf918e91617acf468;p=gnokii.git diff --git a/common/files/midifile.c b/common/files/midifile.c new file mode 100644 index 0000000..7177373 --- /dev/null +++ b/common/files/midifile.c @@ -0,0 +1,1321 @@ +/* a little modified code from http://iki.fi/too/sw/xring/ */ + +/* embedding modified midifile.h and midifile.c into this file */ + +/***** midifile.h ******/ + +struct MF { +/* definitions for MIDI file parsing code */ + int (*Mf_getc)(struct MF *); + void (*Mf_header)(struct MF *, int, int, int); + void (*Mf_trackstart)(struct MF *); + void (*Mf_trackend)(struct MF *); + void (*Mf_noteon)(struct MF *, int, int, int); + void (*Mf_noteoff)(struct MF *, int, int, int); + void (*Mf_pressure)(struct MF *, int, int, int); + void (*Mf_parameter)(struct MF *, int, int, int); + void (*Mf_pitchbend)(struct MF *, int, int, int); + void (*Mf_program)(struct MF *, int, int); + void (*Mf_chanpressure)(struct MF *, int, int); + void (*Mf_sysex)(struct MF *, int, char *); + void (*Mf_metamisc)(struct MF *, int, int, char * ); + void (*Mf_seqspecific)(struct MF *, int, int, char *); + void (*Mf_seqnum)(struct MF *, int); + void (*Mf_text)(struct MF *, int, int, char *); + void (*Mf_eot)(struct MF *); + void (*Mf_timesig)(struct MF *, int, int, int, int); + void (*Mf_smpte)(struct MF *, int, int, int, int, int); + void (*Mf_tempo)(struct MF *, long); + void (*Mf_keysig)(struct MF *, int, int); + void (*Mf_arbitrary)(struct MF *, int, char *); + void (*Mf_error)(struct MF *, char * ); +#if 0 +/* definitions for MIDI file writing code */ + void (*Mf_putc)(struct MF *); + void (*Mf_writetrack)(struct MF *); + void (*Mf_writetempotrack)(struct MF *); +#endif + /* variables */ + int Mf_nomerge; /* 1 => continue'ed system exclusives are */ + /* not collapsed. */ + long Mf_currtime; /* current time in delta-time units */ + +/* private stuff */ + long Mf_toberead; + long Mf_numbyteswritten; + + char *Msgbuff; /* message buffer */ + int Msgsize; /* Size of currently allocated Msg */ + int Msgindex; /* index of next available location in Msg */ + +}; + +float mf_ticks2sec(unsigned long ticks,int division,unsigned int tempo); +unsigned long mf_sec2ticks(float secs,int division, unsigned int tempo); + +void mferror(struct MF * mf, char * s); + +/*void mfwrite(); */ + + +/* MIDI status commands most significant bit is 1 */ +#define note_off 0x80 +#define note_on 0x90 +#define poly_aftertouch 0xa0 +#define control_change 0xb0 +#define program_chng 0xc0 +#define channel_aftertouch 0xd0 +#define pitch_wheel 0xe0 +#define system_exclusive 0xf0 +#define delay_packet (1111) + +/* 7 bit controllers */ +#define damper_pedal 0x40 +#define portamento 0x41 +#define sostenuto 0x42 +#define soft_pedal 0x43 +#define general_4 0x44 +#define hold_2 0x45 +#define general_5 0x50 +#define general_6 0x51 +#define general_7 0x52 +#define general_8 0x53 +#define tremolo_depth 0x5c +#define chorus_depth 0x5d +#define detune 0x5e +#define phaser_depth 0x5f + +/* parameter values */ +#define data_inc 0x60 +#define data_dec 0x61 + +/* parameter selection */ +#define non_reg_lsb 0x62 +#define non_reg_msb 0x63 +#define reg_lsb 0x64 +#define reg_msb 0x65 + +/* Standard MIDI Files meta event definitions */ +#define meta_event 0xFF +#define sequence_number 0x00 +#define text_event 0x01 +#define copyright_notice 0x02 +#define sequence_name 0x03 +#define instrument_name 0x04 +#define lyric 0x05 +#define marker 0x06 +#define cue_point 0x07 +#define channel_prefix 0x20 +#define end_of_track 0x2f +#define set_tempo 0x51 +#define smpte_offset 0x54 +#define time_signature 0x58 +#define key_signature 0x59 +#define sequencer_specific 0x74 + +/* Manufacturer's ID number */ +#define Seq_Circuits (0x01) /* Sequential Circuits Inc. */ +#define Big_Briar (0x02) /* Big Briar Inc. */ +#define Octave (0x03) /* Octave/Plateau */ +#define Moog (0x04) /* Moog Music */ +#define Passport (0x05) /* Passport Designs */ +#define Lexicon (0x06) /* Lexicon */ +#define Tempi (0x20) /* Bon Tempi */ +#define Siel (0x21) /* S.I.E.L. */ +#define Kawai (0x41) +#define Roland (0x42) +#define Korg (0x42) +#define Yamaha (0x43) + +/* miscellaneous definitions */ +#define MThd 0x4d546864 +#define MTrk 0x4d54726b +#define lowerbyte(x) ((unsigned char)(x & 0xff)) +#define upperbyte(x) ((unsigned char)((x & 0xff00)>>8)) + +/* the midifile interface */ +void midifile(struct MF * mf); + +/***** midifile.c ******/ + +/* + * midifile 1.11 + * + * Read and write a MIDI file. Externally-assigned function pointers are + * called upon recognizing things in the file. + * + * Original release ? + * June 1989 - Added writing capability, M. Czeiszperger. + * + * The file format implemented here is called + * Standard MIDI Files, and is part of the Musical + * instrument Digital Interface specification. + * The spec is avaiable from: + * + * International MIDI Association + * 5316 West 57th Street + * Los Angeles, CA 90056 + * + * An in-depth description of the spec can also be found + * in the article "Introducing Standard MIDI Files", published + * in Electronic Musician magazine, April, 1989. + * + */ + +#include +#include +#include +#include + +#ifdef WIN32 + + #include + +#else + + #include +#endif + +#include "gsm-common.h" +#include "gsm-ringtones.h" + +#define NULLFUNC NULL + +static void readheader(struct MF * mf); +static int readtrack(struct MF * mf); +static void chanmessage(struct MF * mf,int status,int c1,int c2); +static void msginit(struct MF * mf); +static void msgadd(struct MF * mf,int c); +static void metaevent(struct MF * mf, int type); +static void sysex(struct MF * mf); +static int msgleng(struct MF * mf); +static void badbyte(struct MF * mf,int c); +static void biggermsg(struct MF * mf); + + +static long readvarinum(struct MF * mf); +static long read32bit(struct MF * mf); +static long to32bit(int, int, int, int); +static int read16bit(struct MF * mf); +static int to16bit(int, int); +static char * msg(struct MF * mf); + +/* The only non-static function in this file. */ +void mfread(struct MF * mf) +{ + if ( mf->Mf_getc == NULLFUNC ) + mferror(mf, "mfread() called without setting Mf_getc"); + + readheader(mf); + while ( readtrack(mf) ) + ; +} + +/* for backward compatibility with the original lib */ +void midifile(struct MF * mf) +{ + mfread(mf); +} + +/* read through the "MThd" or "MTrk" header string */ +static int readmt(struct MF * mf, char * s) +{ + int n = 0; + char *p = s; + int c=0; + + while ( n++<4 && (c=mf->Mf_getc(mf)) != EOF ) { + if ( c != *p++ ) { + char buff[32]; + (void) strcpy(buff, "expecting "); + (void) strcat(buff, s); + mferror(mf, buff); + } + } + return c; +} + +/* read a single character and abort on EOF */ +static int egetc(struct MF * mf) +{ + int c = mf->Mf_getc(mf); + + if ( c == EOF ) + mferror(mf, "premature EOF"); + mf->Mf_toberead--; + return c; +} + +/* read a header chunk */ +static void readheader(struct MF * mf) +{ + int format, ntrks, division; + + if ( readmt(mf, "MThd") == EOF ) + return; + + mf->Mf_toberead = read32bit(mf); + format = read16bit(mf); + ntrks = read16bit(mf); + division = read16bit(mf); + + if ( mf->Mf_header ) + (*mf->Mf_header)(mf, format,ntrks,division); + + /* flush any extra stuff, in case the length of header is not 6 */ + while ( mf->Mf_toberead > 0 ) + (void) egetc(mf); +} + +static int readtrack(struct MF * mf) /* read a track chunk */ +{ + /* This array is indexed by the high half of a status byte. It's */ + /* value is either the number of bytes needed (1 or 2) for a channel */ + /* message, or 0 (meaning it's not a channel message). */ + static int chantype[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */ + 2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */ + }; + long lookfor; + int c, c1, type; + int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */ + int running = 0; /* 1 when running status used */ + int status = 0; /* status value (e.g. 0x90==note-on) */ + int needed; + + if ( readmt(mf, "MTrk") == EOF ) + return(0); + + mf->Mf_toberead = read32bit(mf); + mf->Mf_currtime = 0; + + if ( mf->Mf_trackstart ) + (*mf->Mf_trackstart)(mf); + + while ( mf->Mf_toberead > 0 ) { + + mf->Mf_currtime += readvarinum(mf); /* delta time */ + + c = egetc(mf); + + if ( sysexcontinue && c != 0xf7 ) + mferror(mf, "didn't find expected continuation of a sysex"); + + if ( (c & 0x80) == 0 ) { /* running status? */ + if ( status == 0 ) + mferror(mf, "unexpected running status"); + running = 1; + } + else { + status = c; + running = 0; + } + + needed = chantype[ (status>>4) & 0xf ]; + + if ( needed ) { /* ie. is it a channel message? */ + + if ( running ) + c1 = c; + else + c1 = egetc(mf); + chanmessage(mf, status, c1, (needed>1)? egetc(mf): 0); + continue;; + } + + switch ( c ) { + + case 0xff: /* meta event */ + + type = egetc(mf); + lookfor = mf->Mf_toberead - readvarinum(mf); + msginit(mf); + + while ( mf->Mf_toberead > lookfor ) + msgadd(mf, egetc(mf)); + + metaevent(mf, type); + break; + + case 0xf0: /* start of system exclusive */ + + lookfor = mf->Mf_toberead - readvarinum(mf); + msginit(mf); + msgadd(mf, 0xf0); + + while ( mf->Mf_toberead > lookfor ) + msgadd(mf, c=egetc(mf)); + + if ( c==0xf7 || mf->Mf_nomerge==0 ) + sysex(mf); + else + sysexcontinue = 1; /* merge into next msg */ + break; + + case 0xf7: /* sysex continuation or arbitrary stuff */ + + lookfor = mf->Mf_toberead - readvarinum(mf); + + if ( ! sysexcontinue ) + msginit(mf); + + while ( mf->Mf_toberead > lookfor ) + msgadd(mf, c=egetc(mf)); + + if ( ! sysexcontinue ) { + if ( mf->Mf_arbitrary ) + (*mf->Mf_arbitrary)(mf, msgleng(mf),msg(mf)); + } + else if ( c == 0xf7 ) { + sysex(mf); + sysexcontinue = 0; + } + break; + default: + badbyte(mf, c); + break; + } + } + if ( mf->Mf_trackend ) + (*mf->Mf_trackend)(mf); + return(1); +} + +static void badbyte(struct MF * mf,int c) +{ + char buff[32]; + + (void) sprintf(buff,"unexpected byte: 0x%02x",c); + mferror(mf, buff); +} + +static void metaevent(struct MF * mf, int type) +{ + int leng = msgleng(mf); + char *m = msg(mf); + + switch ( type ) { + case 0x00: + if ( mf->Mf_seqnum ) + (*mf->Mf_seqnum)(mf, to16bit(m[0],m[1])); + break; + case 0x01: /* Text event */ + case 0x02: /* Copyright notice */ + case 0x03: /* Sequence/Track name */ + case 0x04: /* Instrument name */ + case 0x05: /* Lyric */ + case 0x06: /* Marker */ + case 0x07: /* Cue point */ + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + /* These are all text events */ + if ( mf->Mf_text ) + (*mf->Mf_text)(mf, type,leng,m); + break; + case 0x2f: /* End of Track */ + if ( mf->Mf_eot ) + (*mf->Mf_eot)(mf); + break; + case 0x51: /* Set tempo */ + if ( mf->Mf_tempo ) + (*mf->Mf_tempo)(mf, to32bit(0,m[0],m[1],m[2])); + break; + case 0x54: + if ( mf->Mf_smpte ) + (*mf->Mf_smpte)(mf, m[0],m[1],m[2],m[3],m[4]); + break; + case 0x58: + if ( mf->Mf_timesig ) + (*mf->Mf_timesig)(mf, m[0],m[1],m[2],m[3]); + break; + case 0x59: + if ( mf->Mf_keysig ) + (*mf->Mf_keysig)(mf, m[0],m[1]); + break; + case 0x7f: + if ( mf->Mf_seqspecific ) + (*mf->Mf_seqspecific)(mf, type, leng, m); + break; + default: + if ( mf->Mf_metamisc ) + (*mf->Mf_metamisc)(mf, type,leng,m); + } +} + +static void sysex(struct MF * mf) +{ + if ( mf->Mf_sysex ) + (*mf->Mf_sysex)(mf, msgleng(mf),msg(mf)); +} + +static void chanmessage(struct MF * mf,int status,int c1,int c2) +{ + int chan = status & 0xf; + + switch ( status & 0xf0 ) { + case 0x80: + if ( mf->Mf_noteoff ) + (*mf->Mf_noteoff)(mf, chan,c1,c2); + break; + case 0x90: + if ( mf->Mf_noteon ) + (*mf->Mf_noteon)(mf, chan,c1,c2); + break; + case 0xa0: + if ( mf->Mf_pressure ) + (*mf->Mf_pressure)(mf, chan,c1,c2); + break; + case 0xb0: + if ( mf->Mf_parameter ) + (*mf->Mf_parameter)(mf, chan,c1,c2); + break; + case 0xe0: + if ( mf->Mf_pitchbend ) + (*mf->Mf_pitchbend)(mf, chan,c1,c2); + break; + case 0xc0: + if ( mf->Mf_program ) + (*mf->Mf_program)(mf, chan,c1); + break; + case 0xd0: + if ( mf->Mf_chanpressure ) + (*mf->Mf_chanpressure)(mf, chan,c1); + break; + } +} + +/* readvarinum - read a varying-length number, and return the */ +/* number of characters it took. */ + +static long readvarinum(struct MF * mf) +{ + long value; + int c; + + c = egetc(mf); + value = c; + if ( c & 0x80 ) { + value &= 0x7f; + do { + c = egetc(mf); + value = (value << 7) + (c & 0x7f); + } while (c & 0x80); + } + return (value); +} + +static long to32bit(int c1,int c2,int c3,int c4) +{ + long value = 0L; + + value = (c1 & 0xff); + value = (value<<8) + (c2 & 0xff); + value = (value<<8) + (c3 & 0xff); + value = (value<<8) + (c4 & 0xff); + return (value); +} + +static int to16bit(int c1,int c2) +{ + return ((c1 & 0xff ) << 8) + (c2 & 0xff); +} + +static long read32bit(struct MF * mf) +{ + int c1, c2, c3, c4; + + c1 = egetc(mf); + c2 = egetc(mf); + c3 = egetc(mf); + c4 = egetc(mf); + return to32bit(c1,c2,c3,c4); +} + +static int read16bit(struct MF * mf) +{ + int c1, c2; + c1 = egetc(mf); + c2 = egetc(mf); + return to16bit(c1,c2); +} + +/* static */ +void mferror(struct MF * mf, char * s) +{ + if ( mf->Mf_error ) + (*mf->Mf_error)(mf, s); + exit(1); +} + +/* The code below allows collection of a system exclusive message of */ +/* arbitrary length. The Msgbuff is expanded as necessary. The only */ +/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */ + +#define MSGINCREMENT 128 + +static void msginit(struct MF * mf) +{ + mf->Msgindex = 0; +} + +static char * msg(struct MF * mf) +{ + return(mf->Msgbuff); +} + +static int msgleng(struct MF * mf) +{ + return(mf->Msgindex); +} + +static void msgadd(struct MF * mf,int c) +{ + /* If necessary, allocate larger message buffer. */ + if ( mf->Msgindex >= mf->Msgsize ) + biggermsg(mf); + mf->Msgbuff[mf->Msgindex++] = c; +} + +static void biggermsg(struct MF * mf) +{ + char *newmess; + char *oldmess = mf->Msgbuff; + int oldleng = mf->Msgsize; + + mf->Msgsize += MSGINCREMENT; + newmess = (char *) malloc( (unsigned)(sizeof(char) * mf->Msgsize) ); + + if(newmess == NULL) + mferror(mf, "malloc error!"); + + /* copy old message into larger new one */ + if ( oldmess != NULL ) { + register char *p = newmess; + register char *q = oldmess; + register char *endq = &oldmess[oldleng]; + + for ( ; q!=endq ; p++,q++ ) + *p = *q; + free(oldmess); + } + mf->Msgbuff = newmess; +} + +#if 0 /* saving time not converting write function at this time + */ +/* + * mfwrite() - The only fuction you'll need to call to write out + * a midi file. + * + * format 0 - Single multi-channel track + * 1 - Multiple simultaneous tracks + * 2 - One or more sequentially independent + * single track patterns + * ntracks The number of tracks in the file. + * division This is kind of tricky, it can represent two + * things, depending on whether it is positive or negative + * (bit 15 set or not). If bit 15 of division is zero, + * bits 14 through 0 represent the number of delta-time + * "ticks" which make up a quarter note. If bit 15 of + * division is a one, delta-times in a file correspond to + * subdivisions of a second similiar to SMPTE and MIDI + * time code. In this format bits 14 through 8 contain + * one of four values - 24, -25, -29, or -30, + * corresponding to the four standard SMPTE and MIDI + * time code frame per second formats, where -29 + * represents 30 drop frame. The second byte + * consisting of bits 7 through 0 corresponds the the + * resolution within a frame. Refer the Standard MIDI + * Files 1.0 spec for more details. + * fp This should be the open file pointer to the file you + * want to write. It will have be a global in order + * to work with Mf_putc. + */ +void +mfwrite(format,ntracks,division,fp) +int format,ntracks,division; +FILE *fp; +{ + int i; void mf_write_track_chunk(), mf_write_header_chunk(); + + if ( mf->Mf_putc == NULLFUNC ) + mferror(mf, "mfmf_write() called without setting Mf_putc"); + + if ( mf->Mf_writetrack == NULLFUNC ) + mferror(mf, "mfmf_write() called without setting Mf_mf_writetrack"); + + /* every MIDI file starts with a header */ + mf_write_header_chunk(format,ntracks,division); + + /* In format 1 files, the first track is a tempo map */ + if(format == 1 && ( mf->Mf_writetempotrack )) + { + (*mf->Mf_writetempotrack)(); + } + + /* The rest of the file is a series of tracks */ + for(i = 0; i < ntracks; i++) + mf_write_track_chunk(i,fp); +} + +void +mf_write_track_chunk(which_track,fp) +int which_track; +FILE *fp; +{ + unsigned long 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 = ftell(fp); + +#ifdef DEBUG + printf("offset = %d\n",(int) offset); +#endif + + /* Write the track chunk header */ + write32bit(trkhdr); + write32bit(trklength); + + mf->Mf_numbyteswritten = 0L; /* the header's length doesn't count */ + + if( mf->Mf_writetrack ) + { + (*mf->Mf_writetrack)(which_track); + } + + /* mf_write End of track meta event */ + eputc(mf, 0); + eputc(mf, meta_event); + eputc(mf, end_of_track); + + eputc(mf, 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 = 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); */ + +#ifdef DEBUG +printf("length = %d\n",(int) trklength); +#endif + + if(fseek(fp,offset,0) < 0) + mferror(mf, "error seeking during final stage of write"); + + trklength = mf->Mf_numbyteswritten; + + /* Re-mf_write the track chunk header with right length */ + write32bit(trkhdr); + write32bit(trklength); + + fseek(fp,place_marker,0); +} /* End gen_track_chunk() */ + + +void +mf_write_header_chunk(format,ntracks,division) +int format,ntracks,division; +{ + unsigned long 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() */ + + +#ifdef WHENISTHISNEEDED +/* + * mf_write_midi_event() + * + * Library routine to mf_write a single MIDI track event in the standard MIDI + * file format. The format is: + * + * + * + * In this case, event can be any multi-byte midi message, such as + * "note on", "note off", etc. + * + * delta_time - the time in ticks since the last event. + * type - the type of meta event. + * chan - The midi channel. + * data - A pointer to a block of chars containing the META EVENT, + * data. + * size - The length of the meta-event data. + */ +int +mf_write_midi_event(delta_time, type, chan, data, size) +unsigned long delta_time; +unsigned int chan,type; +unsigned long size; +unsigned char *data; +{ + int i; + void WriteVarLen(); + 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 = type | chan; + + if(chan > 15) + perror("error: MIDI channel greater than 16\n"); + + eputc(mf, c); + + /* write out the data bytes */ + for(i = 0; i < size; i++) + eputc(mf, data[i]); + + return(size); +} /* end mf_write MIDI event */ + +/* + * mf_write_meta_event() + * + * Library routine to mf_write a single meta event in the standard MIDI + * file format. The format of a meta event is: + * + * + * + * delta_time - the time in ticks since the last event. + * type - the type of meta event. + * data - A pointer to a block of chars containing the META EVENT, + * data. + * size - The length of the meta-event data. + */ +int +mf_write_meta_event(delta_time, type, data, size) +unsigned long delta_time; +unsigned char *data,type; +unsigned long size; +{ + int i; + + WriteVarLen(delta_time); + + /* This marks the fact we're writing a meta-event */ + eputc(mf, meta_event); + + /* The type of meta event */ + eputc(mf, type); + + /* The length of the data bytes to follow */ + WriteVarLen(size); + + for(i = 0; i < size; i++) + { + if(eputc(mf, data[i]) != data[i]) + return(-1); + } + return(size); +} /* end mf_write_meta_event */ + +void +mf_write_tempo(tempo) +unsigned long tempo; +{ + /* Write tempo */ + /* all tempos are written as 120 beats/minute, */ + /* expressed in microseconds/quarter note */ + eputc(mf, 0); + eputc(mf, meta_event); + eputc(mf, set_tempo); + + eputc(mf, 3); + eputc(mf, (unsigned)(0xff & (tempo >> 16))); + eputc(mf, (unsigned)(0xff & (tempo >> 8))); + eputc(mf, (unsigned)(0xff & tempo)); +} + +#endif +/* + * Write multi-length bytes to MIDI format files + */ +void +WriteVarLen(value) +unsigned long value; +{ + unsigned long buffer; + + buffer = value & 0x7f; + while((value >>= 7) > 0) + { + buffer <<= 8; + buffer |= 0x80; + buffer += (value & 0x7f); + } + while(1){ + eputc(mf, (unsigned)(buffer & 0xff)); + + if(buffer & 0x80) + buffer >>= 8; + else + return; + } +}/* end of WriteVarLen */ + + +/* + * write32bit() + * write16bit() + * + * These routines are used to make sure that the byte order of + * the various data types remains constant between machines. This + * helps make sure that the code will be portable from one system + * to the next. It is slightly dangerous that it assumes that longs + * have at least 32 bits and ints have at least 16 bits, but this + * has been true at least on PCs, UNIX machines, and Macintosh's. + * + */ +void +write32bit(data) +unsigned long data; +{ + eputc(mf, x(unsigned)((data >> 24) & 0xff)); + eputc(mf, (unsigned)((data >> 16) & 0xff)); + eputc(mf, (unsigned)((data >> 8 ) & 0xff)); + eputc(mf, (unsigned)(data & 0xff)); +} + +void +write16bit(data) +int data; +{ + eputc(mf, (unsigned)((data & 0xff00) >> 8)); + eputc(mf, (unsigned)(data & 0xff)); +} + +/* write a single character and abort on error */ +eputc(mf, c) +unsigned char c; +{ + int return_val; + + if((mf->Mf_putc) == NULLFUNC) + { + mferror(mf, "Mf_putc undefined"); + return(-1); + } + + return_val = (mf->Mf_putc)(mf, c); + + if ( return_val == EOF ) + mferror(mf, "error writing"); + + mf->Mf_numbyteswritten++; + return(return_val); +} + +#endif + +unsigned long mf_sec2ticks(float secs,int division, unsigned int tempo) +{ + return (long)(((secs * 1000.0) / 4.0 * division) / tempo); +} + + +/* + * This routine converts delta times in ticks into seconds. The + * else statement is needed because the formula is different for tracks + * based on notes and tracks based on SMPTE times. + * + */ +float mf_ticks2sec(unsigned long ticks,int division,unsigned int tempo) +{ + float smpte_format, smpte_resolution; + + if(division > 0) + return ((float) (((float)(ticks) * (float)(tempo)) / ((float)(division) * 1000000.0))); + else + { + smpte_format = upperbyte(division); + smpte_resolution = lowerbyte(division); + return (float) ((float) ticks / (smpte_format * smpte_resolution * 1000000.0)); + } +} /* end of ticks2sec() */ + +/* code to utilize the interface */ + +#define TRACE(x, y) do { if (x) printf y; } while (0) + + +typedef unsigned long ulg; +typedef unsigned char uch; +typedef unsigned int ui; + +struct NoteInfo +{ + int beats; + int nrnotes; + struct Notes { + ui note : 4; + ui scale : 4; + ui length : 4; + ui lextra : 4; + } note[1]; +}; + +#define IBUFSIZE 1024 +struct MFX +{ + struct MF mfi; + struct NoteInfo * ni; + int allocated; + + int division; + int trackstate; + + int prevnoteonpitch; /* -1, nothing, 0 pause, 1-x note. */ + ulg prevnoteontime; + + struct { + int fd; + uch buf[IBUFSIZE]; + int len; + int p; + } istrm; +}; + +enum { TRK_NONE, TRK_READING, TRK_FINISHED }; + +#define ALLOCSIZE 256 + +#define NIALLOC(size) (struct NoteInfo *)malloc(sizeof (struct NoteInfo) + ((size) - 1) * sizeof (struct Notes)) + +#define NIREALLOC(ni, size) (struct NoteInfo *)realloc((ni), sizeof (struct NoteInfo) + ((size) - 1) * sizeof (struct Notes)) + + +static void lm_error(struct MF * mf, char * s); + +static int lm_getc(struct MF * mf); +static void lm_header(struct MF * mf, int, int, int); +static void lm_trackstart(struct MF * mf); +static void lm_trackend(struct MF * mf); +static void lm_tempo(struct MF *, long); +static void lm_noteon(struct MF *, int, int, int); +static void lm_noteoff(struct MF *, int, int, int); + + +struct NoteInfo * readmidi(int fd) +{ + struct MFX mfxi = { { 0 } }; + struct MF * mf = (struct MF *)&mfxi; + + mfxi.ni = NIALLOC(ALLOCSIZE); + mfxi.allocated = ALLOCSIZE; + + /* set variables to their initial values */ + mfxi.division = 0; + mfxi.trackstate = TRK_NONE; + mfxi.prevnoteonpitch = -1; + mfxi.ni->nrnotes = 0; + mfxi.ni->beats = 120; + + mfxi.istrm.fd = fd; + mfxi.istrm.p = mfxi.istrm.len = 0; + mf->Mf_getc = lm_getc; + + mf->Mf_header = lm_header; + mf->Mf_tempo = lm_tempo; + mf->Mf_trackstart = lm_trackstart; + mf->Mf_trackend = lm_trackend; + mf->Mf_noteon = lm_noteon; + mf->Mf_noteoff = lm_noteoff; + + mf->Mf_error = lm_error; + + midifile(mf); + + return mfxi.ni; +} + +static void lm_error(struct MF * mf, char * s) +{ + fprintf(stderr, "%s\n", s); +} + +static int lm_getc(struct MF * mf) +{ + struct MFX * mfx = (struct MFX *)mf; + + /* printf("p %d, len %d\n", mfx->istrm.p, mfx->istrm.len); */ + if (mfx->istrm.p == mfx->istrm.len) + { + mfx->istrm.len = read(mfx->istrm.fd, mfx->istrm.buf, IBUFSIZE); + /* printf("readlen %d\n", mfx->istrm.len); */ + if (mfx->istrm.len <= 0) + return -1; + + mfx->istrm.p = 1; + return mfx->istrm.buf[0]; + } + /* else */ + return mfx->istrm.buf[mfx->istrm.p++]; +} + +static void lm_header(struct MF * mf, int format, int ntrks, int division) +{ + struct MFX * mfx = (struct MFX *)mf; + + TRACE(0, ("lm_header(%p, %d, %d, %d)\n", mf, format, ntrks, division)); + + mfx->division = division; +} + +/* this is just a quess */ +static void lm_tempo(struct MF * mf, long tempo) +{ + struct MFX * mfx = (struct MFX *)mf; + + TRACE(0, ("lm_tempo(%p, %ld)\n", mf, tempo)); + + if (mfx->trackstate != TRK_FINISHED) + mfx->ni->beats = 60000000 / tempo; +} + + +static void addnote(struct MFX * mfx, int pitch, int duration, int special) +{ + int nr, p, s; + struct NoteInfo * ni; + + if (mfx->ni->nrnotes == mfx->allocated) + { + mfx->allocated += ALLOCSIZE; + mfx->ni = NIREALLOC(mfx->ni, mfx->allocated); + if (mfx->ni == NULL) + exit(1); + } + ni = mfx->ni; /* mfx->ni pointer value may have changed above */ + nr = ni->nrnotes++; + + + if (pitch == 0) { p = 0; s = 0; } + else { pitch--; p = (pitch % 12) + 1; s = pitch / 12; } + + ni->note[nr].note = p; + ni->note[nr].scale = s; + + ni->note[nr].length = duration; + ni->note[nr].lextra = special; +} + +/* currently supported */ +static /* N 32 32. 16 16. 8 8. 4 4. 2 2. 1 1. */ +int vals[] = { 15, 38, 54, 78, 109, 156, 218, 312, 437, 625, 875, 1250 }; + +static void writenote(struct MFX * mfx, int delta) +{ + ulg millinotetime = delta * 250 / mfx->division; + int i; + int duration; + int special; + + for(i = 0; i < sizeof vals / sizeof vals[0]; i++) + { + if (millinotetime < vals[i]) + break; + } + + if (i == 0) + return; + + i--; + duration = i / 2; + special = i & 1; + + addnote(mfx, mfx->prevnoteonpitch, duration, special); /* XXX think this */ +} + + +static void lm_trackstart(struct MF * mf) +{ + struct MFX * mfx = (struct MFX *)mf; + + TRACE(0, ("lm_trackstart(%p)\n", mf)); + + if (mfx->trackstate == TRK_NONE) + mfx->trackstate = TRK_READING; + + mfx->prevnoteonpitch = -1; +} + +static void lm_trackend(struct MF * mf) +{ + struct MFX * mfx = (struct MFX *)mf; + long time = mf->Mf_currtime; + + TRACE(0, ("lm_trackend(%p)\n", mf)); + + if (mfx->trackstate == TRK_READING && mfx->ni->nrnotes > 0) + mfx->trackstate = TRK_FINISHED; + + if (mfx->prevnoteonpitch >= 0) + writenote(mfx, time - mfx->prevnoteontime); + + mfx->prevnoteonpitch = -1; +} + +static void lm_noteon(struct MF * mf, int chan, int pitch, int vol) +{ + struct MFX * mfx = (struct MFX *)mf; + long time = mf->Mf_currtime; + + TRACE(0, ("lm_noteon(%p, %d, %d, %d)\n", mf, chan, pitch, vol)); + + if (vol == 0) /* kludge? to handle some (format 1? midi files) */ + return; + + if (mfx->trackstate != TRK_READING) + return; + + if (mfx->prevnoteonpitch >= 0) + writenote(mfx, time - mfx->prevnoteontime); + + if (vol == 0) + mfx->prevnoteonpitch = 0; + else + mfx->prevnoteonpitch = pitch + 1; + + mfx->prevnoteontime = time; +} + +static void lm_noteoff(struct MF * mf, int chan, int pitch, int vol) +{ + struct MFX * mfx = (struct MFX *)mf; + long time = mf->Mf_currtime; + + TRACE(0, ("lm_noteoff(%p, %d, %d, %d)\n", mf, chan, pitch, vol)); + + if (mfx->prevnoteonpitch >= 0) + { + writenote(mfx, time - mfx->prevnoteontime); + mfx->prevnoteonpitch = -1; + } + mfx->prevnoteonpitch = 0; + mfx->prevnoteontime = time; +} + +//{ "p", "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "h" }; +char notes[] = + { 255, 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11 , 12 }; + +u8 lengths[] = { 4, 8, 16, 32, 64, 128 }; + +static void countdefaults(struct NoteInfo * ni, int * length_p, int * scale_p) +{ + int lengths[15] = { 0 }; + int scales[15] = { 0 }; + int maxlenval = 0; /* (*) */ + int maxscaleval = 0; + int i; + + for (i = 0; i < ni->nrnotes; i++) + { + struct Notes * note = &ni->note[i]; + + lengths[note->length]++; + scales[note->scale]++; + } + + maxlenval = lengths[0]; /* (*) smart compiler eliminates dead code */ + *length_p = 0; + + for (i = 1; i < 15; i++) /* `p' incremented scales[0], therefore ignored */ + { + TRACE(0, ("%d - len: %d, scale: %d\n", i, lengths[i], scales[i])); + + if (lengths[i] > maxlenval) { + *length_p = i; + maxlenval = lengths[i]; + } + if (scales[i] > maxscaleval) { + *scale_p = i; + maxscaleval = scales[i]; + } + } +} + +GSM_Error loadmid(char *filename, GSM_Ringtone *ringtone) +{ + int fd; + struct NoteInfo * ni; + int i; + int deflen, defscale; + +#ifdef WIN32 + if ((fd = open(filename, O_RDONLY | O_BINARY)) < 0) + { + perror("open"); + return GE_CANTOPENFILE; + } +#else + if ((fd = open(filename, O_RDONLY)) < 0) + { + perror("open"); + return GE_CANTOPENFILE; + } +#endif + + ni = readmidi(fd); + + if (ni == NULL) + return 0; + + countdefaults(ni, &deflen, &defscale); + + strcpy(ringtone->name,"GNOKII"); + + if (ni->nrnotesNrNotes=ni->nrnotes; + else + ringtone->NrNotes=MAX_RINGTONE_NOTES; + + for (i = 0; i < ringtone->NrNotes; i++) + { + struct Notes * note = &ni->note[i]; + + ringtone->notes[i].note=notes[note->note]; + ringtone->notes[i].note=notes[note->note]+(note->scale%4)*14; + + ringtone->notes[i].duration=lengths[note->length]; + if (note->lextra) + ringtone->notes[i].duration=ringtone->notes[i].duration*1.5; + + ringtone->notes[i].tempo=ni->beats; + + } + + ringtone->Loop=15; + + return 0; +}