--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#ifdef WIN32\r
+\r
+ #include <windows.h>\r
+\r
+#else\r
+\r
+ #include <unistd.h>\r
+#endif\r
+
+#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:
+ *
+ * <delta-time><event>
+ *
+ * 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><FF><type><length><bytes>
+ *
+ * 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->nrnotes<MAX_RINGTONE_NOTES)
+ ringtone->NrNotes=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;
+}