allegrosmfrd.cpp 13 KB

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