allegrosmfrd.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. // midifile reader
  2. #include "stdlib.h"
  3. #include "stdio.h"
  4. #include "string.h"
  5. #include "debug.h"
  6. #include <string>
  7. #include <fstream>
  8. #include "allegro.h"
  9. #include "algsmfrd_internal.h"
  10. #include "mfmidi.h"
  11. #include "trace.h"
  12. using namespace std;
  13. typedef class Alg_pending {
  14. public:
  15. Alg_note_ptr note;
  16. class Alg_pending *next;
  17. Alg_pending(Alg_note_ptr n, class Alg_pending *list) {
  18. note = n; next = list; }
  19. } *Alg_pending_ptr;
  20. class Alg_midifile_reader: public Midifile_reader {
  21. public:
  22. istream *file;
  23. Alg_seq_ptr seq;
  24. int divisions;
  25. Alg_pending_ptr pending;
  26. Alg_track_ptr track;
  27. int track_number; // the number of the (current) track
  28. // chan is actual_channel + channel_offset_per_track * track_num +
  29. // channel_offset_per_track * port
  30. long channel_offset_per_track; // used to encode track number into channel
  31. // default is 0, set this to 0 to merge all tracks to 16 channels
  32. long channel_offset_per_port; // used to encode port number into channel
  33. // default is 16, set to 0 to ignore port prefix meta events
  34. // while reading, this is channel_offset_per_track * track_num
  35. int channel_offset;
  36. Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) {
  37. file = &f;
  38. pending = NULL;
  39. seq = new_seq;
  40. channel_offset_per_track = 0;
  41. channel_offset_per_port = 16;
  42. track_number = -1; // no tracks started yet, 1st will be #0
  43. meta_channel = -1;
  44. port = 0;
  45. }
  46. // delete destroys the seq member as well, so set it to NULL if you
  47. // copied the pointer elsewhere
  48. ~Alg_midifile_reader();
  49. // the following is used to load the Alg_seq from the file:
  50. bool parse();
  51. void set_nomerge(bool flag) { Mf_nomerge = flag; }
  52. void set_skipinit(bool flag) { Mf_skipinit = flag; }
  53. long get_currtime() { return Mf_currtime; }
  54. protected:
  55. int meta_channel; // the channel for meta events, set by MIDI chan prefix
  56. int port; // value from the portprefix meta event
  57. double get_time();
  58. void update(int chan, int key, Alg_parameter_ptr param);
  59. void *Mf_malloc(size_t size) { return malloc(size); }
  60. void Mf_free(void *obj, size_t size) { free(obj); }
  61. /* Methods to be called while processing the MIDI file. */
  62. void Mf_starttrack();
  63. void Mf_endtrack();
  64. int Mf_getc();
  65. void Mf_chanprefix(int chan);
  66. void Mf_portprefix(int port);
  67. void Mf_eot();
  68. void Mf_error(const char *);
  69. void Mf_header(int,int,int);
  70. void Mf_on(int,int,int);
  71. void Mf_off(int,int,int);
  72. void Mf_pressure(int,int,int);
  73. void Mf_controller(int,int,int);
  74. void Mf_pitchbend(int,int,int);
  75. void Mf_program(int,int);
  76. void Mf_chanpressure(int,int);
  77. void binary_msg(int len, char *msg, const char *attr_string);
  78. void Mf_sysex(int,char*);
  79. void Mf_arbitrary(int,char*);
  80. void Mf_metamisc(int,int,char*);
  81. void Mf_seqnum(int);
  82. void Mf_smpte(int,int,int,int,int);
  83. void Mf_timesig(int,int,int,int);
  84. void Mf_tempo(int);
  85. void Mf_keysig(int,int);
  86. void Mf_sqspecific(int,char*);
  87. void Mf_text(int,int,char*);
  88. };
  89. Alg_midifile_reader::~Alg_midifile_reader()
  90. {
  91. while (pending) {
  92. Alg_pending_ptr to_be_freed = pending;
  93. pending = pending->next;
  94. delete to_be_freed;
  95. }
  96. finalize(); // free Mf reader memory
  97. }
  98. bool Alg_midifile_reader::parse()
  99. {
  100. channel_offset = 0;
  101. seq->convert_to_beats();
  102. midifile();
  103. seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur()));
  104. return midifile_error != 0;
  105. }
  106. void Alg_midifile_reader::Mf_starttrack()
  107. {
  108. // printf("starting new track\n");
  109. // create a new track that will share the sequence time map
  110. // since time is in beats, the seconds parameter is false
  111. track_number++;
  112. seq->add_track(track_number); // make sure track exists
  113. track = seq->track(track_number); // keep pointer to current track
  114. meta_channel = -1;
  115. port = 0;
  116. }
  117. void Alg_midifile_reader::Mf_endtrack()
  118. {
  119. // note: track is already part of seq, so do not add it here
  120. // printf("finished track, length %d number %d\n", track->len, track_num / 100);
  121. channel_offset += seq->channel_offset_per_track;
  122. track = NULL;
  123. double now = get_time();
  124. if (seq->get_beat_dur() < now) seq->set_beat_dur(now);
  125. meta_channel = -1;
  126. port = 0;
  127. }
  128. int Alg_midifile_reader::Mf_getc()
  129. {
  130. return file->get();
  131. }
  132. void Alg_midifile_reader::Mf_chanprefix(int chan)
  133. {
  134. meta_channel = chan;
  135. }
  136. void Alg_midifile_reader::Mf_portprefix(int p)
  137. {
  138. port = p;
  139. }
  140. void Alg_midifile_reader::Mf_eot()
  141. {
  142. meta_channel = -1;
  143. port = 0;
  144. }
  145. void Alg_midifile_reader::Mf_error(const char *msg)
  146. {
  147. fprintf(stdout, "Midifile reader error: %s\n", msg);
  148. }
  149. void Alg_midifile_reader::Mf_header(int format, int ntrks, int division)
  150. {
  151. if (format > 1) {
  152. char msg[80];
  153. sprintf(msg, "file format %d not implemented", format);
  154. Mf_error(msg);
  155. }
  156. divisions = division;
  157. }
  158. double Alg_midifile_reader::get_time()
  159. {
  160. double beat = ((double) get_currtime()) / divisions;
  161. return beat;
  162. }
  163. void Alg_midifile_reader::Mf_on(int chan, int key, int vel)
  164. {
  165. assert(!seq->get_units_are_seconds());
  166. if (vel == 0) {
  167. Mf_off(chan, key, vel);
  168. return;
  169. }
  170. Alg_note_ptr note = new Alg_note();
  171. pending = new Alg_pending(note, pending);
  172. /* trace("on: %d at %g\n", key, get_time()); */
  173. note->time = get_time();
  174. note->chan = chan + channel_offset + port * channel_offset_per_port;
  175. note->dur = 0;
  176. note->set_identifier(key);
  177. note->pitch = (float) key;
  178. note->loud = (float) vel;
  179. track->append(note);
  180. meta_channel = -1;
  181. }
  182. void Alg_midifile_reader::Mf_off(int chan, int key, int vel)
  183. {
  184. double time = get_time();
  185. Alg_pending_ptr *p = &pending;
  186. while (*p) {
  187. if ((*p)->note->get_identifier() == key &&
  188. (*p)->note->chan ==
  189. chan + channel_offset + port * channel_offset_per_port) {
  190. (*p)->note->dur = time - (*p)->note->time;
  191. // trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur);
  192. Alg_pending_ptr to_be_freed = *p;
  193. *p = to_be_freed->next;
  194. delete to_be_freed;
  195. } else {
  196. p = &((*p)->next);
  197. }
  198. }
  199. meta_channel = -1;
  200. }
  201. void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param)
  202. {
  203. Alg_update_ptr update = new Alg_update;
  204. update->time = get_time();
  205. update->chan = chan;
  206. if (chan != -1) {
  207. update->chan = chan + channel_offset + port * channel_offset_per_port;
  208. }
  209. update->set_identifier(key);
  210. update->parameter = *param;
  211. // prevent the destructor from destroying the string twice!
  212. // the new Update takes the string from param
  213. if (param->attr_type() == 's') param->s = NULL;
  214. track->append(update);
  215. }
  216. void Alg_midifile_reader::Mf_pressure(int chan, int key, int val)
  217. {
  218. Alg_parameter parameter;
  219. parameter.set_attr(symbol_table.insert_string("pressurer"));
  220. parameter.r = val / 127.0;
  221. update(chan, key, &parameter);
  222. meta_channel = -1;
  223. }
  224. void Alg_midifile_reader::Mf_controller(int chan, int control, int val)
  225. {
  226. Alg_parameter parameter;
  227. char name[32];
  228. sprintf(name, "control%dr", control);
  229. parameter.set_attr(symbol_table.insert_string(name));
  230. parameter.r = val / 127.0;
  231. update(chan, -1, &parameter);
  232. meta_channel = -1;
  233. }
  234. void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2)
  235. {
  236. Alg_parameter parameter;
  237. parameter.set_attr(symbol_table.insert_string("bendr"));
  238. parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0;
  239. update(chan, -1, &parameter);
  240. meta_channel = -1;
  241. }
  242. void Alg_midifile_reader::Mf_program(int chan, int program)
  243. {
  244. Alg_parameter parameter;
  245. parameter.set_attr(symbol_table.insert_string("programi"));
  246. parameter.i = program;
  247. update(chan, -1, &parameter);
  248. meta_channel = -1;
  249. }
  250. void Alg_midifile_reader::Mf_chanpressure(int chan, int val)
  251. {
  252. Alg_parameter parameter;
  253. parameter.set_attr(symbol_table.insert_string("pressurer"));
  254. parameter.r = val / 127.0;
  255. update(chan, -1, &parameter);
  256. meta_channel = -1;
  257. }
  258. void Alg_midifile_reader::binary_msg(int len, char *msg,
  259. const char *attr_string)
  260. {
  261. Alg_parameter parameter;
  262. char *hexstr = new char[len * 2 + 1];
  263. for (int i = 0; i < len; i++) {
  264. sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i]));
  265. }
  266. parameter.s = hexstr;
  267. parameter.set_attr(symbol_table.insert_string(attr_string));
  268. update(meta_channel, -1, &parameter);
  269. }
  270. void Alg_midifile_reader::Mf_sysex(int len, char *msg)
  271. {
  272. // sysex messages become updates with attribute sysexs and a hex string
  273. binary_msg(len, msg, "sysexs");
  274. }
  275. void Alg_midifile_reader::Mf_arbitrary(int len, char *msg)
  276. {
  277. Mf_error("arbitrary data ignored");
  278. }
  279. void Alg_midifile_reader::Mf_metamisc(int type, int len, char *msg)
  280. {
  281. char text[128];
  282. sprintf(text, "metamsic data, type 0x%x, ignored", type);
  283. Mf_error(text);
  284. }
  285. void Alg_midifile_reader::Mf_seqnum(int n)
  286. {
  287. Mf_error("seqnum data ignored");
  288. }
  289. static const char *fpsstr[4] = {"24", "25", "29.97", "30"};
  290. void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs,
  291. int frames, int subframes)
  292. {
  293. // string will look like "24fps:01h:27m:07s:19.00f"
  294. // 30fps (drop frame) is notated as "29.97fps"
  295. char text[32];
  296. int fps = (hours >> 6) & 3;
  297. hours &= 0x1F;
  298. sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df",
  299. fpsstr[fps], hours, mins, secs, frames, subframes);
  300. Alg_parameter smpteoffset;
  301. smpteoffset.s = heapify(text);
  302. smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets"));
  303. update(meta_channel, -1, &smpteoffset);
  304. // Mf_error("SMPTE data ignored");
  305. }
  306. void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4)
  307. {
  308. seq->set_time_sig(get_currtime() / divisions, i1, 1 << i2);
  309. }
  310. void Alg_midifile_reader::Mf_tempo(int tempo)
  311. {
  312. double beat = get_currtime();
  313. beat = beat / divisions; // convert to quarters
  314. // 6000000 us/min / n us/beat => beat / min
  315. double bpm = 60000000.0 / tempo;
  316. seq->insert_tempo(bpm, beat);
  317. }
  318. void Alg_midifile_reader::Mf_keysig(int key, int mode)
  319. {
  320. Alg_parameter key_parm;
  321. key_parm.set_attr(symbol_table.insert_string("keysigi"));
  322. // use 0 for C major, 1 for G, -1 for F, etc., that is,
  323. // the number of sharps, where flats are negative sharps
  324. key_parm.i = key; //<<<---- fix this
  325. // use -1 to mean "all channels"
  326. update(meta_channel, -1, &key_parm);
  327. Alg_parameter mode_parm;
  328. mode_parm.set_attr(symbol_table.insert_string("modea"));
  329. mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") :
  330. symbol_table.insert_string("minor"));
  331. update(meta_channel, -1, &mode_parm);
  332. }
  333. void Alg_midifile_reader::Mf_sqspecific(int len, char *msg)
  334. {
  335. // sequencer specific messages become updates with attribute sqspecifics
  336. // and a hex string for the value
  337. binary_msg(len, msg, "sqspecifics");
  338. }
  339. char *heapify2(int len, char *s)
  340. {
  341. char *h = new char[len + 1];
  342. memcpy(h, s, len);
  343. h[len] = 0;
  344. return h;
  345. }
  346. void Alg_midifile_reader::Mf_text(int type, int len, char *msg)
  347. {
  348. Alg_parameter text;
  349. text.s = heapify2(len, msg);
  350. const char *attr = "miscs";
  351. if (type == 1) attr = "texts";
  352. else if (type == 2) attr = "copyrights";
  353. else if (type == 3)
  354. attr = (track_number == 0 ? "seqnames" : "tracknames");
  355. else if (type == 4) attr = "instruments";
  356. else if (type == 5) attr = "lyrics";
  357. else if (type == 6) attr = "markers";
  358. else if (type == 7) attr = "cues";
  359. text.set_attr(symbol_table.insert_string(attr));
  360. update(meta_channel, -1, &text);
  361. }
  362. // parse file into a seq.
  363. Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq)
  364. {
  365. assert(new_seq);
  366. Alg_midifile_reader ar(file, new_seq);
  367. bool err = ar.parse();
  368. ar.seq->set_real_dur(ar.seq->get_time_map()->
  369. beat_to_time(ar.seq->get_beat_dur()));
  370. return (err ? alg_error_syntax : alg_no_error);
  371. }