123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- // midifile reader
- #include "stdlib.h"
- #include "stdio.h"
- #include "string.h"
- #include "assert.h"
- #include <string>
- #include <fstream>
- #include "allegro.h"
- #include "algsmfrd_internal.h"
- #include "mfmidi.h"
- #include "trace.h"
- using namespace std;
- typedef class Alg_note_list {
- public:
- Alg_note_ptr note;
- class Alg_note_list *next;
- Alg_note_list(Alg_note_ptr n, class Alg_note_list *list) {
- note = n; next = list; }
- } *Alg_note_list_ptr;
- class Alg_midifile_reader: public Midifile_reader {
- public:
- istream *file;
- Alg_seq_ptr seq;
- int divisions;
- Alg_note_list_ptr note_list;
- Alg_track_ptr track;
- int track_number; // the number of the (current) track
- // chan is actual_channel + channel_offset_per_track * track_num +
- // channel_offset_per_track * port
- long channel_offset_per_track; // used to encode track number into channel
- // default is 0, set this to 0 to merge all tracks to 16 channels
- long channel_offset_per_port; // used to encode port number into channel
- // default is 16, set to 0 to ignore port prefix meta events
- // while reading, this is channel_offset_per_track * track_num
- int channel_offset;
- Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) {
- file = &f;
- note_list = nullptr;
- seq = new_seq;
- channel_offset_per_track = 0;
- channel_offset_per_port = 16;
- track_number = -1; // no tracks started yet, 1st will be #0
- meta_channel = -1;
- port = 0;
- }
- // delete destroys the seq member as well, so set it to NULL if you
- // copied the pointer elsewhere
- ~Alg_midifile_reader();
- // the following is used to load the Alg_seq from the file:
- bool parse();
- void set_nomerge(bool flag) { Mf_nomerge = flag; }
- void set_skipinit(bool flag) { Mf_skipinit = flag; }
- long get_currtime() { return Mf_currtime; }
- protected:
- int meta_channel; // the channel for meta events, set by MIDI chan prefix
- int port; // value from the portprefix meta event
- double get_time();
- void update(int chan, int key, Alg_parameter_ptr param);
- void *Mf_malloc(size_t size) { return malloc(size); }
- void Mf_free(void *obj, size_t size) { free(obj); }
- /* Methods to be called while processing the MIDI file. */
- void Mf_starttrack();
- void Mf_endtrack();
- int Mf_getc();
- void Mf_chanprefix(int chan);
- void Mf_portprefix(int port);
- void Mf_eot();
- void Mf_error(char *);
- void Mf_error(const char *);
- void Mf_header(int,int,int);
- void Mf_on(int,int,int);
- void Mf_off(int,int,int);
- void Mf_pressure(int,int,int);
- void Mf_controller(int,int,int);
- void Mf_pitchbend(int,int,int);
- void Mf_program(int,int);
- void Mf_chanpressure(int,int);
- void binary_msg(int len, unsigned char *msg, const char *attr_string);
- void Mf_sysex(int,unsigned char*);
- void Mf_arbitrary(int,unsigned char*);
- void Mf_metamisc(int,int,unsigned char*);
- void Mf_seqnum(int);
- void Mf_smpte(int,int,int,int,int);
- void Mf_timesig(int,int,int,int);
- void Mf_tempo(int);
- void Mf_keysig(int,int);
- void Mf_sqspecific(int,unsigned char*);
- void Mf_text(int,int,unsigned char*);
- };
- Alg_midifile_reader::~Alg_midifile_reader()
- {
- while (note_list) {
- Alg_note_list_ptr to_be_freed = note_list;
- note_list = note_list->next;
- delete to_be_freed;
- }
- finalize(); // free Mf reader memory
- }
- bool Alg_midifile_reader::parse()
- {
- channel_offset = 0;
- seq->convert_to_beats();
- midifile();
- seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur()));
- return midifile_error != 0;
- }
- void Alg_midifile_reader::Mf_starttrack()
- {
- // printf("starting new track\n");
- // create a new track that will share the sequence time map
- // since time is in beats, the seconds parameter is false
- track_number++;
- seq->add_track(track_number); // make sure track exists
- track = seq->track(track_number); // keep pointer to current track
- meta_channel = -1;
- port = 0;
- }
- void Alg_midifile_reader::Mf_endtrack()
- {
- // note: track is already part of seq, so do not add it here
- // printf("finished track, length %d number %d\n", track->len, track_num / 100);
- channel_offset += seq->channel_offset_per_track;
- track = nullptr;
- double now = get_time();
- if (seq->get_beat_dur() < now) seq->set_beat_dur(now);
- meta_channel = -1;
- port = 0;
- }
- int Alg_midifile_reader::Mf_getc()
- {
- return file->get();
- }
- void Alg_midifile_reader::Mf_chanprefix(int chan)
- {
- meta_channel = chan;
- }
- void Alg_midifile_reader::Mf_portprefix(int p)
- {
- port = p;
- }
- void Alg_midifile_reader::Mf_eot()
- {
- meta_channel = -1;
- port = 0;
- }
- void Alg_midifile_reader::Mf_error(char *msg)
- {
- fprintf(stdout, "Midifile reader error: %s\n", msg);
- }
- void Alg_midifile_reader::Mf_error(const char *msg)
- {
- Mf_error(const_cast<char*>(msg));
- }
- void Alg_midifile_reader::Mf_header(int format, int ntrks, int division)
- {
- if (format > 1) {
- char msg[80];
- //#pragma warning(disable: 4996) // msg is long enough
- sprintf(msg, "file format %d not implemented", format);
- //#pragma warning(default: 4996)
- Mf_error(msg);
- }
- divisions = division;
- }
- double Alg_midifile_reader::get_time()
- {
- double beat = ((double) get_currtime()) / divisions;
- return beat;
- }
- void Alg_midifile_reader::Mf_on(int chan, int key, int vel)
- {
- assert(!seq->get_units_are_seconds());
- if (vel == 0) {
- Mf_off(chan, key, vel);
- return;
- }
- Alg_note_ptr note = new Alg_note();
- note_list = new Alg_note_list(note, note_list);
- /* trace("on: %d at %g\n", key, get_time()); */
- note->time = get_time();
- note->chan = chan + channel_offset + port * channel_offset_per_port;
- note->dur = 0;
- note->set_identifier(key);
- note->pitch = (float) key;
- note->loud = (float) vel;
- track->append(note);
- meta_channel = -1;
- }
- void Alg_midifile_reader::Mf_off(int chan, int key, int vel)
- {
- double time = get_time();
- Alg_note_list_ptr *p = ¬e_list;
- while (*p) {
- if ((*p)->note->get_identifier() == key &&
- (*p)->note->chan ==
- chan + channel_offset + port * channel_offset_per_port) {
- (*p)->note->dur = time - (*p)->note->time;
- // trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur);
- Alg_note_list_ptr to_be_freed = *p;
- *p = to_be_freed->next;
- delete to_be_freed;
- } else {
- p = &((*p)->next);
- }
- }
- meta_channel = -1;
- }
- void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param)
- {
- Alg_update_ptr update = new Alg_update;
- update->time = get_time();
- update->chan = chan;
- if (chan != -1) {
- update->chan = chan + channel_offset + port * channel_offset_per_port;
- }
- update->set_identifier(key);
- update->parameter = *param;
- // prevent the destructor from destroying the string twice!
- // the new Update takes the string from param
- if (param->attr_type() == 's') param->s = nullptr;
- track->append(update);
- }
- void Alg_midifile_reader::Mf_pressure(int chan, int key, int val)
- {
- Alg_parameter parameter;
- parameter.set_attr(symbol_table.insert_string("pressurer"));
- parameter.r = val / 127.0;
- update(chan, key, ¶meter);
- meta_channel = -1;
- }
- void Alg_midifile_reader::Mf_controller(int chan, int control, int val)
- {
- Alg_parameter parameter;
- char name[32];
- //#pragma warning(disable: 4996) // name is long enough
- sprintf(name, "control%dr", control);
- //#pragma warning(default: 4996)
- parameter.set_attr(symbol_table.insert_string(name));
- parameter.r = val / 127.0;
- update(chan, -1, ¶meter);
- meta_channel = -1;
- }
- void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2)
- {
- Alg_parameter parameter;
- parameter.set_attr(symbol_table.insert_string("bendr"));
- parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0;
- update(chan, -1, ¶meter);
- meta_channel = -1;
- }
- void Alg_midifile_reader::Mf_program(int chan, int program)
- {
- Alg_parameter parameter;
- parameter.set_attr(symbol_table.insert_string("programi"));
- parameter.i = program;
- update(chan, -1, ¶meter);
- meta_channel = -1;
- }
- void Alg_midifile_reader::Mf_chanpressure(int chan, int val)
- {
- Alg_parameter parameter;
- parameter.set_attr(symbol_table.insert_string("pressurer"));
- parameter.r = val / 127.0;
- update(chan, -1, ¶meter);
- meta_channel = -1;
- }
- void Alg_midifile_reader::binary_msg(int len, unsigned char *msg,
- const char *attr_string)
- {
- Alg_parameter parameter;
- char *hexstr = new char[len * 2 + 1];
- for (int i = 0; i < len; i++) {
- //#pragma warning(disable: 4996) // hexstr is long enough
- sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i]));
- //#pragma warning(default: 4996)
- }
- parameter.s = hexstr;
- parameter.set_attr(symbol_table.insert_string(attr_string));
- update(meta_channel, -1, ¶meter);
- }
- void Alg_midifile_reader::Mf_sysex(int len, unsigned char *msg)
- {
- // sysex messages become updates with attribute sysexs and a hex string
- binary_msg(len, msg, "sysexs");
- }
- void Alg_midifile_reader::Mf_arbitrary(int len, unsigned char *msg)
- {
- Mf_error("arbitrary data ignored");
- }
- void Alg_midifile_reader::Mf_metamisc(int type, int len, unsigned char *msg)
- {
- char text[128];
- //#pragma warning(disable: 4996) // text is long enough
- sprintf(text, "metamsic data, type 0x%x, ignored", type);
- //#pragma warning(default: 4996)
- Mf_error(text);
- }
- void Alg_midifile_reader::Mf_seqnum(int n)
- {
- Mf_error("seqnum data ignored");
- }
- static const char *fpsstr[4] = {"24", "25", "29.97", "30"};
- void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs,
- int frames, int subframes)
- {
- // string will look like "24fps:01h:27m:07s:19.00f"
- // 30fps (drop frame) is notated as "29.97fps"
- char text[32];
- int fps = (hours >> 6) & 3;
- hours &= 0x1F;
- //#pragma warning(disable: 4996) // text is long enough
- sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df",
- fpsstr[fps], hours, mins, secs, frames, subframes);
- //#pragma warning(default: 4996)
- Alg_parameter smpteoffset;
- smpteoffset.s = heapify(text);
- smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets"));
- update(meta_channel, -1, &smpteoffset);
- // Mf_error("SMPTE data ignored");
- }
- void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4)
- {
- seq->set_time_sig(double(get_currtime()) / divisions, i1, 1 << i2);
- }
- void Alg_midifile_reader::Mf_tempo(int tempo)
- {
- double beat = get_currtime();
- beat = beat / divisions; // convert to quarters
- // 6000000 us/min / n us/beat => beat / min
- double bpm = 60000000.0 / tempo;
- seq->insert_tempo(bpm, beat);
- }
- void Alg_midifile_reader::Mf_keysig(int key, int mode)
- {
- Alg_parameter key_parm;
- key_parm.set_attr(symbol_table.insert_string("keysigi"));
- // use 0 for C major, 1 for G, -1 for F, etc., that is,
- // the number of sharps, where flats are negative sharps
- key_parm.i = key; //<<<---- fix this
- // use -1 to mean "all channels"
- update(meta_channel, -1, &key_parm);
- Alg_parameter mode_parm;
- mode_parm.set_attr(symbol_table.insert_string("modea"));
- mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") :
- symbol_table.insert_string("minor"));
- update(meta_channel, -1, &mode_parm);
- }
- void Alg_midifile_reader::Mf_sqspecific(int len, unsigned char *msg)
- {
- // sequencer specific messages become updates with attribute sqspecifics
- // and a hex string for the value
- binary_msg(len, msg, "sqspecifics");
- }
- char *heapify2(int len, unsigned char *s)
- {
- char *h = new char[len + 1];
- memcpy(h, s, len);
- h[len] = 0;
- return h;
- }
- void Alg_midifile_reader::Mf_text(int type, int len, unsigned char *msg)
- {
- Alg_parameter text;
- text.s = heapify2(len, msg);
- const char *attr = "miscs";
- if (type == 1) attr = "texts";
- else if (type == 2) attr = "copyrights";
- else if (type == 3)
- attr = (track_number == 0 ? "seqnames" : "tracknames");
- else if (type == 4) attr = "instruments";
- else if (type == 5) attr = "lyrics";
- else if (type == 6) attr = "markers";
- else if (type == 7) attr = "cues";
- text.set_attr(symbol_table.insert_string(attr));
- update(meta_channel, -1, &text);
- }
- // parse file into a seq.
- Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq)
- {
- assert(new_seq);
- Alg_midifile_reader ar(file, new_seq);
- bool err = ar.parse();
- ar.seq->set_real_dur(ar.seq->get_time_map()->
- beat_to_time(ar.seq->get_beat_dur()));
- return (err ? alg_error_syntax : alg_no_error);
- }
|