-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathallegrosmfrd.cpp
455 lines (381 loc) · 12.6 KB
/
allegrosmfrd.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
// 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
int 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
int 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 = NULL;
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; }
int64 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(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 = NULL;
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(const char *msg)
{
fprintf(stdout, "Midifile reader error: %s\n", 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 = NULL;
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);
}