portSMF
Loading...
Searching...
No Matches
allegro.h
1// Portsmf (also known as Allegro):
2// music representation system, with
3// extensible in-memory sequence structure
4// upward compatible with MIDI
5// implementations in C++ and Serpent
6// external, text-based representation
7// compatible with Aura
8//
9// SERIALBUFFER CLASS
10//
11// The Serial_buffer class is defined to support serialization and
12// unserialization. A Serial_buffer is just a block of memory with
13// a length and a read/write pointer. When writing, it can expand.
14//
15// SERIALIZATION
16//
17// The Alg_track class has static members:
18// ser_buf -- a Serial_buffer
19// When objects are serialized, they are first written to
20// ser_buf, which is expanded whenever necessary. Then, when
21// the length is known, new memory is allocated and the data
22// is copied to a correctly-sized buffer and returned to caller.
23// The "external" (callable from outside the library)
24// serialization functions are:
25// Alg_track::serialize()
26// Alg_seq::serialize()
27// The "internal" serialization functions to be called from within
28// the library are:
29// Alg_track::serialize_track(bool text)
30// Alg_seq::serialize_seq(bool text)
31// Alg_track::serialize_parameter(
32// Alg_parameter *parm, bool text)
33// These internal serialize functions append data to ser_buf The text
34// flag says to write an ascii representation as opposed to binary.
35//
36// UNSERIALIZATION:
37//
38// The Alg_track class has a static member:
39// unserialize(char *buffer, long len)
40// that will unserialize anything -- an Alg_track or an Alg_seq.
41// No other function should be called from outside the library.
42// Internal unserialize functions are:
43// Alg_seq::unserialize_seq()
44// Alg_track::unserialize_track()
45// Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr)
46// Just as serialization uses ser_buf for output, unserialization uses
47// unser_buf for reading. unser_buf is another static member of Alg_track.
48
49#ifndef ALLEGRO_H
50#define ALLEGRO_H
51#include <cassert>
52#include <cstring>
53#include <istream>
54#include <ostream>
55
56#define ALG_EPS 0.000001 // epsilon
57#define ALG_DEFAULT_BPM 100.0 // default tempo
58
59// are d1 and d2 within epsilon of each other?
60bool within(double d1, double d2, double epsilon);
61
62char *heapify(const char *s); // put a string on the heap
63
64
65// Alg_attribute is an atom in the symbol table
66// with the special addition that the last
67// character is prefixed to the string; thus,
68// the attribute 'tempor' (a real) is stored
69// as 'rtempor'. To get the string name, just
70// use attribute+1.
71typedef const char *Alg_attribute;
72#define alg_attr_name(a) ((a) + 1)
73#define alg_attr_type(a) (*(a))
74
75// Alg_atoms is a symbol table of Alg_attributes and other
76// unique strings
77class Alg_atoms {
78public:
79 Alg_atoms() {
80 maxlen = len = 0;
81 atoms = nullptr;
82 }
83 // Note: the code is possibly more correct and faster without the
84 // following destructor, which will only run after the program takes
85 // a normal exit. Cleaning up after the program exit slows down the exit,
86 // and will cause problems if any other destructor tries to reference an
87 // Alg_atom (which will now be freed). The advantage of this code is
88 // that Alg_atoms will not be reported as memory leaks by automation
89 // that doesn't know better. -RBD
90 virtual ~Alg_atoms() {
91 for (int i = 0; i < len; i++) {
92 delete atoms[i];
93 }
94 if (atoms) {
95 delete[] atoms;
96 }
97 }
98 // insert/lookup an atttribute
99 Alg_attribute insert_attribute(Alg_attribute attr);
100 // insert/lookup attribute by name (without prefixed type)
101 Alg_attribute insert_string(const char *name);
102private:
103 long maxlen;
104 long len;
105 Alg_attribute *atoms;
106
107 // insert an Attriubute not in table after moving attr to heap
108 Alg_attribute insert_new(const char *name, char attr_type);
109 void expand(); // make more space
110};
111
112static Alg_atoms symbol_table;
113
114
115// an attribute/value pair. Since Alg_attribute names imply type,
116// we try to keep attributes and values packaged together as
117// Alg_parameter class
118typedef class Alg_parameter {
119public:
120 // This constructor guarantees that an Alg_parameter can be
121 // deleted safely without further initialization. It does not
122 // do anything useful, so it is expected that the creator will
123 // set attr and store a value in the appropriate union field.
124 Alg_attribute attr;
125 union {
126 double r;// real
127 const char *s; // string
128 long i; // integer
129 bool l; // logical
130 const char *a; // symbol (atom)
131 }; // anonymous union
132
133 Alg_parameter() { attr = "i"; }
134 ~Alg_parameter();
135 void copy(Alg_parameter *); // copy from another parameter
136 char attr_type() { return alg_attr_type(attr); }
137 const char *attr_name() { return alg_attr_name(attr); }
138 void set_attr(Alg_attribute a) { attr = a; }
139 void show();
140} *Alg_parameter_ptr;
141
142
143// a list of attribute/value pairs
144typedef class Alg_parameters {
145public:
146 class Alg_parameters *next;
147 Alg_parameter parm;
148
149 Alg_parameters(Alg_parameters *list) {
150 next = list;
151 }
152
153 //~Alg_parameters() { }
154
155 // each of these routines takes address of pointer to the list
156 // insertion is performed without checking whether or not a
157 // parameter already exists with this attribute. See find() and
158 // remove_key() to assist in checking for and removing existing
159 // parameters.
160 // Note also that these insert_* methods convert name to an
161 // attribute. If you have already done the symbol table lookup/insert
162 // you can do these operations faster (in which case we should add
163 // another set of functions that take attributes as arguments.)
164 static void insert_real(Alg_parameters **list, const char *name, double r);
165 // insert string will copy string to heap
166 static void insert_string(Alg_parameters **list, const char *name,
167 const char *s);
168 static void insert_integer(Alg_parameters **list, const char *name, long i);
169 static void insert_logical(Alg_parameters **list, const char *name, bool l);
170 static void insert_atom(Alg_parameters **list, const char *name,
171 const char *s);
172 static Alg_parameters *remove_key(Alg_parameters **list, const char *name);
173 // find an attribute/value pair
174 Alg_parameter_ptr find(Alg_attribute attr);
175} *Alg_parameters_ptr;
176
177
178// these are type codes associated with certain attributes
179// see Alg_track::find() where these are bit positions in event_type_mask
180#define ALG_NOTE 0 // This is a note, not an update
181#define ALG_GATE 1 // "gate"
182#define ALG_BEND 2 // "bend"
183#define ALG_CONTROL 3 // "control"
184#define ALG_PROGRAM 4 // "program"
185#define ALG_PRESSURE 5 // "pressure"
186#define ALG_KEYSIG 6 // "keysig"
187#define ALG_TIMESIG_NUM 7 // "timesig_num"
188#define ALG_TIMESIG_DEN 8 // "timesig_den"
189#define ALG_OTHER 9 // any other value
190
191// abstract superclass of Alg_note and Alg_update:
192typedef class Alg_event {
193protected:
194 bool selected;
195 char type; // 'e' event, 'n' note, 'u' update
196 long key; // note identifier
197 static const char* description; // static buffer for debugging (in Alg_event)
198public:
199 double time;
200 long chan;
201 virtual void show() = 0;
202 // Note: there is no Alg_event() because Alg_event is an abstract class.
203 bool is_note() { return (type == 'n'); } // tell whether an Alg_event is a note
204 bool is_update() { return (type == 'u'); } // tell whether an Alg_event is a parameter update
205 char get_type() { return type; } // return 'n' for note, 'u' for update
206 int get_type_code(); // 1 = volume change, 2 = pitch bend,
207 // 3 = control change, 4 = program change,
208 // 5 = pressure change, 6 = key signature,
209 // 7 = time sig numerator, 8 = time sig denominator
210 bool get_selected() { return selected; }
211 void set_selected(bool b) { selected = b; }
212 // Note: notes are identified by a (channel, identifier) pair.
213 // For midi, the identifier is the key number (pitch). The identifier
214 // does not have to represent pitch; it's main purpose is to identify
215 // notes so that they can be named by subsequent update events.
216 long get_identifier() { return key; } // get MIDI key or note identifier of note or update
217 void set_identifier(long i) { key = i; } // set the identifier
218 // In all of these set_ methods, strings are owned by the caller and
219 // copied as necessary by the callee. For notes, an attribute/value
220 // pair is added to the parameters list. For updates, the single
221 // attribute/value parameter pair is overwritten. In all cases, the
222 // attribute (first argument) must agree in type with the second arg.
223 // The last letter of the attribute implies the type (see below).
224 void set_parameter(Alg_parameter_ptr new_parameter);
225 void set_string_value(const char *attr, const char *value);
226 void set_real_value(const char *attr, double value);
227 void set_logical_value(const char *attr, bool value);
228 void set_integer_value(const char *attr, long value);
229 void set_atom_value(const char *attr, const char *atom);
230
231 // Some note methods. These fail (via assert()) if this is not a note:
232 //
233 float get_pitch();// get pitch in steps -- use this even for MIDI
234 float get_loud(); // get loudness (MIDI velocity)
235 // times are in seconds or beats, depending upon the units_are_seconds
236 // flag in the containing sequence
237 double get_start_time(); // get start time in seconds or beats
238 double get_end_time(); // get end time in seconds or beats
239 double get_duration(); // get duration in seconds or beats
240 void set_pitch(float);
241 void set_loud(float);
242 void set_duration(double);
243
244 // Notes have lists of attribute values. Attributes are converted
245 // to/from strings in this API to avoid explicit use of Alg_attribute
246 // types. Attribute names end with a type designation: 's', 'r', 'l',
247 // 'i', or 'a'.
248 //
249 bool has_attribute(const char *attr); // test if note has attribute/value pair
250 char get_attribute_type(const char *attr); // get the associated type:
251 // 's' = string,
252 // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long),
253 // 'a' = atom (char *), a unique string stored in Alg_seq
254 // get the string value
255 const char *get_string_value(const char *attr, const char *value = nullptr);
256 // get the real value
257 double get_real_value(const char *attr, double value = 0.0);
258 // get the logical value
259 bool get_logical_value(const char *attr, bool value = false);
260 // get the integer value
261 long get_integer_value(const char *attr, long value = 0);
262 // get the atom value
263 const char *get_atom_value(const char *attr, const char *value = nullptr);
264 void delete_attribute(const char *attr); // delete an attribute/value pair
265 // (ignore if no matching attribute/value pair exists)
266
267 // Some attribute/value methods. These fail if this is not an update.
268 // Attributes are converted to/from strings to avoid explicit use
269 // of Alg_attribute types.
270 //
271 const char *get_attribute(); // get the update's attribute (string)
272 char get_update_type(); // get the update's type: 's' = string,
273 // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long),
274 // 'a' = atom (char *), a unique string stored in Alg_seq
275 const char *get_string_value(); // get the update's string value
276 // Notes: Caller does not own the return value. Do not modify.
277 // Do not use after underlying Alg_seq is modified.
278 double get_real_value(); // get the update's real value
279 bool get_logical_value(); // get the update's logical value
280 long get_integer_value(); // get the update's integer value
281 const char *get_atom_value(); // get the update's atom value
282 // Notes: Caller does not own the return value. Do not modify.
283 // The return value's lifetime is forever.
284
285 // Auxiliary function to aid in editing tracks
286 // Returns true if the event overlaps the given region
287 bool overlap(double t, double len, bool all);
288
289 const char *GetDescription(); // computes a text description of this event
290 // the result is in a static buffer, not thread-safe, just for debugging.
291 Alg_event() { selected = false; }
292 virtual ~Alg_event() {}
293} *Alg_event_ptr;
294
295
296typedef class Alg_note : public Alg_event {
297public:
298 virtual ~Alg_note();
299 Alg_note(Alg_note *); // copy constructor
300 float pitch; // pitch in semitones (69 = A440)
301 float loud; // dynamic corresponding to MIDI velocity
302 double dur; // duration in seconds (normally to release point)
303 Alg_parameters_ptr parameters; // attribute/value pair list
304 Alg_note() { type = 'n'; parameters = nullptr; }
305 void show();
306} *Alg_note_ptr;
307
308
309typedef class Alg_update : public Alg_event {
310public:
311 virtual ~Alg_update() {};
312 Alg_update(Alg_update *); // copy constructor
313 Alg_parameter parameter; // an update contains one attr/value pair
314
315
316 Alg_update() { type = 'u'; }
317 void show();
318} *Alg_update_ptr;
319
320
321// a sequence of Alg_event objects
322typedef class Alg_events {
323private:
324 long maxlen;
325 void expand();
326protected:
327 long len;
328 Alg_event_ptr *events; // events is array of pointers
329public:
330 // sometimes, it is nice to have the time of the last note-off.
331 // In the current implementation,
332 // this field is set by append to indicate the time of the
333 // last note-off in the current unit, so it should be correct after
334 // creating a new track and adding notes to it. It is *not*
335 // updated after uninsert(), so use it with care.
336 double last_note_off;
337 // initially false, in_use can be used to mark "do not delete". If an
338 // Alg_events instance is deleted while "in_use", an assertion will fail.
339 bool in_use;
340 virtual int length() { return len; }
341 Alg_event_ptr &operator[](int i) {
342 assert(i >= 0 && i < len);
343 return events[i];
344 }
345 Alg_events() {
346 maxlen = len = 0;
347 events = nullptr;
348 last_note_off = 0;
349 in_use = false;
350 }
351 // destructor deletes the events array, but not the
352 // events themselves
353 virtual ~Alg_events();
354 void set_events(Alg_event_ptr *e, long l, long m) {
355 if (events) {
356 delete[] events;
357 }
358 events = e; len = l; maxlen = m;
359 }
360 // for use by Alg_track and Alg_seq
361 void insert(Alg_event_ptr event);
362 void append(Alg_event_ptr event);
363 Alg_event_ptr uninsert(long index);
364} *Alg_events_ptr;
365
366class Alg_track;
367
368typedef class Alg_event_list : public Alg_events {
369protected:
370 char type; // 'e' Alg_event_list, 't' Alg_track, 's' Alg_seq
371 static const char *last_error_message;
372 Alg_track *events_owner; // if this is an Alg_event_list,
373 // the events are owned by an Alg_track or an Alg_seq
374 static int sequences; // to keep track of sequence numbers
375 int sequence_number; // this sequence number is incremented
376 // whenever an edit is performed on an Alg_track or Alg_seq.
377 // When an Alg_event_list is created to contain pointers to
378 // a subset of an Alg_track or Alg_seq (the events_owner),
379 // the Alg_event_list gets a copy of the events_owner's
380 // sequence_number. If the events_owner is edited, the pointers
381 // in this Alg_event_list will become invalid. This is detected
382 // (for debugging) as differing sequence_numbers.
383
384 // every event list, track, and seq has a duration.
385 // Usually the duration is set when the list is constructed, e.g.
386 // when you extract from 10 to 15 seconds, the duration is 5 secs.
387 // The duration does not tell you when is the last note-off.
388 // duration is recorded in both beats and seconds:
389 double beat_dur;
390 double real_dur;
391public:
392 // the client should not create one of these, but these are
393 // returned from various track and seq operations. An
394 // Alg_event_list "knows" the Alg_track or Alg_seq that "owns"
395 // the events. All events in an Alg_event_list must belong
396 // to the same Alg_track or Alg_seq structure.
397 // When applied to an Alg_seq, events are enumerated track
398 // by track with increasing indices. This operation is not
399 // particularly fast on an Alg_seq.
400 virtual Alg_event_ptr &operator[](int i);
401 Alg_event_list() { sequence_number = 0;
402 beat_dur = 0.0; real_dur = 0.0; events_owner = nullptr; type = 'e'; }
403 Alg_event_list(Alg_track *owner);
404
405 char get_type() { return type; }
406 Alg_track *get_owner() { return events_owner; }
407
408 // The destructor does not free events because they are owned
409 // by a track or seq structure.
410 virtual ~Alg_event_list();
411
412 // Returns the duration of the sequence in beats or seconds
413 double get_beat_dur() { return beat_dur; }
414 void set_beat_dur(double d) { beat_dur = d; }
415 double get_real_dur() { return real_dur; }
416 void set_real_dur(double d) { real_dur = d; }
417
418 // Events are stored in time order, so when you change the time of
419 // an event, you must adjust the position. When you call set_start_time
420 // on an Alg_event_list, the Alg_event_list is not modified, but the
421 // Alg_track that "owns" the event is modified. If the owner is an
422 // Alg_seq, this may require searching the seq for the track containing
423 // the event. This will mean a logN search of every track in the seq
424 // (but if this turns out to be a problem, we can store each event's
425 // track owner in the Alg_event_list.)
426 virtual void set_start_time(Alg_event *event, double);
427 // get text description of run-time errors detected, clear error
428 const char *get_last_error_message() { return last_error_message; }
429 // Implementation hint: keep a sequence number on each Alg_track that is
430 // incremented anytime there is a structural change. (This behavior is
431 // inherited by Alg_seq as well.) Copy the sequence number to any
432 // Alg_event_list object when it is created. Whenever you access an
433 // Alg_event_list, using operator[], assert that the Alg_event_list sequence
434 // number matches the Alg_seq sequence number. This will guarantee that you
435 // do not try to retain pointers to events beyond the point where the events
436 // may no longer exist.
437} *Alg_event_list_ptr, &Alg_event_list_ref;
438
439
440// Alg_beat is used to contruct a tempo map
441typedef class Alg_beat {
442public:
443 Alg_beat(double t, double b) {
444 time = t; beat = b; }
445 Alg_beat() {};
446 double time;
447 double beat;
448} *Alg_beat_ptr;
449
450
451// Alg_beats is a list of Alg_beat objects used in Alg_seq
452typedef class Alg_beats {
453private:
454 long maxlen;
455 void expand();
456public:
457 long len;
458 Alg_beat_ptr beats;
459 Alg_beat &operator[](int i) {
460 assert(i >= 0 && i < len);
461 return beats[i];
462 }
463 Alg_beats() {
464 maxlen = len = 0;
465 beats = nullptr;
466 expand();
467 beats[0].time = 0;
468 beats[0].beat = 0;
469 len = 1;
470 }
471 ~Alg_beats() {
472 if (beats) {
473 delete[] beats;
474 }
475 }
476 void insert(long i, Alg_beat_ptr beat);
477} *Alg_beats_ptr;
478
479
480typedef class Alg_time_map {
481private:
482 int refcount;
483public:
484 Alg_beats beats; // array of Alg_beat
485 double last_tempo;
486 bool last_tempo_flag;
487 Alg_time_map() {
488 last_tempo = ALG_DEFAULT_BPM / 60.0; // note: this value ignored until
489 // last_tempo_flag is set; nevertheless, the default
490 // tempo is 100.
491 last_tempo_flag = true;
492 refcount = 0;
493 }
494 Alg_time_map(Alg_time_map *map); // copy constructor
495 long length() { return beats.len; }
496 void show();
497 long locate_time(double time);
498 long locate_beat(double beat);
499 double beat_to_time(double beat);
500 double time_to_beat(double time);
501 // Time map manipulations: it is prefered to call the corresponding
502 // methods in Alg_seq. If you manipulate an Alg_time_map directly,
503 // you should take care to convert all tracks that use the time map
504 // to beats or seconds as appropriate: Normally if you insert a beat
505 // you want tracks to be in time units and if you insert a tempo change
506 // you want tracks to be in beat units.
507 void insert_beat(double time, double beat); // add a point to the map
508 bool insert_tempo(double tempo, double beat); // insert a tempo change
509 // get the tempo starting at beat
510 double get_tempo(double beat);
511 // set the tempo over a region
512 bool set_tempo(double tempo, double start_beat, double end_beat);
513 bool stretch_region(double b0, double b1, double dur);
514 void cut(double start, double len, bool units_are_seconds);
515 void trim(double start, double end, bool units_are_seconds);
516 void paste(double start, Alg_track *tr);
517 // insert a span of time. If start is at a tempo change, then
518 // the span of time runs at the changed tempo
519 void insert_time(double start, double len);
520 // insert a span of beats. If start is at a tempo change, the
521 // tempo change takes effect before the inserted beats
522 void insert_beats(double start, double len);
523 void dereference() {
524 --refcount;
525 if (refcount <= 0) {
526 delete this;
527 }
528 }
529 void reference() {
530 refcount++;
531 }
532} *Alg_time_map_ptr;
533
534
535// Serial_buffer is an abstract class with common elements of
536// Serial_read_buffer and Serial_write_buffer
537class Serial_buffer {
538 protected:
539 char *buffer;
540 char *ptr;
541 long len;
542 public:
543 Serial_buffer() {
544 buffer = nullptr;
545 ptr = nullptr;
546 len = 0;
547 }
548 virtual ~Serial_buffer() { }
549
550 long get_posn() { return static_cast<long>(ptr - buffer); }
551 long get_len() { return len; }
552};
553
554
555typedef class Serial_read_buffer : public Serial_buffer {
556public:
557 // note that a Serial_read_buffer is initialized for reading by
558 // setting buffer, but it is not the Serial_read_buffer's responsibility
559 // to delete the buffer (owner might want to reuse it), so the destructor
560 // does nothing.
561 virtual ~Serial_read_buffer() { }
562#if defined(_WIN32)
563#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits
564#pragma warning(disable: 4311) // type cast pointer to long warning
565#endif
566 void get_pad() {
567 while (reinterpret_cast<long>(ptr) & 7) {
568 ptr++;
569 }
570 }
571#if defined(_WIN32)
572#pragma warning(default: 4311 546)
573#endif
574 // Prepare to read n bytes from buf. The caller must manage buf: it is
575 // valid until reading is finished, and it is caller's responsibility
576 // to free buf when it is no longer needed.
577 void init_for_read(void *buf, long n) {
578 buffer = static_cast<char *>(buf);
579 ptr = static_cast<char *>(buf);
580 len = n;
581 }
582 char get_char() { return *ptr++; }
583 void unget_chars(int n) { ptr -= n; } // undo n get_char() calls
584 long get_int32() {
585 long i = *(reinterpret_cast<long *>(ptr));
586 ptr += 4;
587 return i;
588 }
589 float get_float() {
590 float f = *(reinterpret_cast<float *>(ptr));
591 ptr += 4;
592 return f;
593 }
594 double get_double() {
595 double d = *(reinterpret_cast<double *>(ptr));
596 ptr += sizeof(double);
597 return d;
598 }
599 const char *get_string() {
600 char *s = ptr;
601 char *fence = buffer + len;
602 assert(ptr < fence);
603 while (*ptr++) {
604 assert(ptr < fence);
605 }
606 get_pad();
607 return s;
608 }
609 void check_input_buffer(long needed) {
610 assert(get_posn() + needed <= len);
611 }
612} *Serial_read_buffer_ptr;
613
614
615typedef class Serial_write_buffer: public Serial_buffer {
616 public:
617 // Note: allegro.cpp declares one static instance of Serial_buffer to
618 // reduce large memory (re)allocations when serializing tracks for UNDO.
619 // This destructor will only run when the program exits, which will only
620 // add overhead to the exit process, but it will eliminate an incorrect
621 // report of memory leakage from automation that doesn't know better. -RBD
622 virtual ~Serial_write_buffer() {
623 if (buffer) {
624 delete[] buffer;
625 }
626 }
627 void init_for_write() { ptr = buffer; }
628 // store_long writes a long at a given offset
629 void store_long(long offset, long value) {
630 assert(offset <= get_posn() - 4);
631 long *loc = reinterpret_cast<long *>(buffer + offset);
632 *loc = value;
633 }
634 void check_buffer(long needed);
635 void set_string(const char *s) {
636 char *fence = buffer + len;
637 assert(ptr < fence);
638 // two brackets surpress a g++ warning, because this is an
639 // assignment operator inside a test.
640 while ((*ptr++ = *s++)) {
641 assert(ptr < fence);
642 }
643 // 4311 is type cast pointer to long warning
644 // 4312 is type cast long to pointer warning
645#if defined(_WIN32)
646#pragma warning(disable: 4311 4312)
647#endif
648 assert(reinterpret_cast<char *>(reinterpret_cast<long>(ptr + 7) & ~7) <= fence);
649#if defined(_WIN32)
650#pragma warning(default: 4311 4312)
651#endif
652 pad();
653 }
654 void set_int32(long v) {
655 *(reinterpret_cast<long *>(ptr)) = v;
656 ptr += 4;
657 }
658 void set_double(double v) {
659 *(reinterpret_cast<double *>(ptr)) = v;
660 ptr += 8;
661 }
662 void set_float(float v) {
663 *(reinterpret_cast<float *>(ptr)) = v;
664 ptr += 4;
665 }
666 void set_char(char v) {
667 *ptr++ = v;
668 }
669#if defined(_WIN32)
670#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits
671#pragma warning(disable: 4311) // type cast pointer to long warning
672#endif
673 void pad() {
674 while (reinterpret_cast<long>(ptr) & 7) {
675 set_char(0);
676 }
677 }
678#if defined(_WIN32)
679#pragma warning(default: 4311 546)
680#endif
681 void *to_heap(long *len) {
682 *len = get_posn();
683 char *newbuf = new char[*len];
684 memcpy(newbuf, buffer, *len);
685 return newbuf;
686 }
687} *Serial_write_buffer_ptr;
688
689typedef class Alg_seq *Alg_seq_ptr;
690
691typedef class Alg_track : public Alg_event_list {
692protected:
693 Alg_time_map *time_map;
694 bool units_are_seconds;
695 char *get_string(char **p, long *b);
696 long get_int32(char **p, long *b);
697 double get_double(char **p, long *b);
698 float get_float(char **p, long *b);
699 static Serial_read_buffer ser_read_buf;
700 static Serial_write_buffer ser_write_buf;
701 void serialize_parameter(Alg_parameter *parm);
702 // *buffer_ptr points to binary data, bytes_ptr points to how many
703 // bytes have been used so far, len is length of binary data
704 void unserialize_parameter(Alg_parameter_ptr parm_ptr);
705public:
706 void serialize_track();
707 void unserialize_track();
708 virtual Alg_event_ptr &operator[](int i) {
709 assert(i >= 0 && i < len);
710 return events[i];
711 }
712 Alg_track() { units_are_seconds = false; time_map = nullptr;
713 set_time_map(nullptr); type = 't'; }
714 // initialize empty track with a time map
715 Alg_track(Alg_time_map *map, bool seconds);
716
717 Alg_event_ptr copy_event(Alg_event_ptr event); // make a complete copy
718
719 Alg_track(Alg_track &track); // copy constructor, does not copy time_map
720 // copy constructor: event_list is copied, map is installed and referenced
721 Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map,
722 bool units_are_seconds);
723 virtual ~Alg_track() { // note: do not call set_time_map(nullptr)!
724 if (time_map) {
725 time_map->dereference();
726 }
727 time_map = nullptr;
728 }
729
730 // Returns a buffer containing a serialization of the
731 // file. It will be an ASCII representation unless text is true.
732 // *buffer gets a newly allocated buffer pointer. The caller must free it.
733 // *len gets the length of the serialized track
734 virtual void serialize(void **buffer, long *bytes);
735
736 // Try to read from a memory buffer. Automatically guess
737 // whether it's MIDI or text.
738 static Alg_track *unserialize(void *buffer, long len);
739
740 // If the track is really an Alg_seq and you need to access an
741 // Alg_seq method, coerce to an Alg_seq with this function:
742 Alg_seq_ptr to_alg_seq() {
743 return (get_type() == 's' ? (Alg_seq_ptr) this : nullptr); }
744
745 // Are we using beats or seconds?
746 bool get_units_are_seconds() { return units_are_seconds; }
747 // Change units
748 virtual void convert_to_beats();
749 virtual void convert_to_seconds();
750 void set_dur(double dur);
751 double get_dur() { return (units_are_seconds ? real_dur : beat_dur); }
752
753 // Every Alg_track may have an associated time_map. If no map is
754 // specified, or if you set_time_map(nullptr), then the behavior
755 // should be as if there is a constant tempo of 100 beats/minute
756 // (this constant is determined by ALG_DEFAULT_BPM).
757 // Recommendation: create a static global tempo map object. When
758 // any operation that needs a tempo map gets nullptr, use the global
759 // tempo map. (Exception: any operation that would modify the
760 // tempo map should raise an error -- you don't want to change the
761 // default tempo map.)
762 virtual void set_time_map(Alg_time_map *map);
763 Alg_time_map *get_time_map() { return time_map; }
764
765 // Methods to create events. The returned event is owned by the caller.
766 // Use delete to get rid of it unless you call add() -- see below.
767 //
768 Alg_note *create_note(double time, int channel, int identifier,
769 float pitch, float loudness, double duration);
770 // Note: after create_update(), caller should use set_*_value() to
771 // initialize the attribute/value pair:
772 Alg_update *create_update(double time, int channel, int identifier);
773 // Adds a new event - it is automatically inserted into the
774 // correct order in the sequence based on its timestamp.
775 // The ownership passes from the caller to this Alg_seq. The
776 // event is not copied.
777 virtual void add(Alg_event *event) { insert(event); }
778
779 //
780 // Editing regions
781 //
782
783 // Deletes the notes that start within the given region
784 // and returns them in a new sequence. The start times
785 // of the notes in the returned sequence are shifted
786 // by -t. The notes after the region get shifted over
787 // to fill the gap. In an Alg_seq, the tempo track is edited
788 // in a similar way
789 // and the cut tempo information is retained in the new seq.
790 // ONLY NOTES THAT START WITHIN THE REGION ARE CUT unless
791 // "all" is true in which case all notes that intersect
792 // the region are copied. CUT NOTES
793 // MAY EXTEND BEYOND THE DURATION OF THE RESULTING SEQ.
794 // The return type is the same as this (may be Alg_seq).
795 // All times including len are interpreted according to
796 // units_are_seconds in the track.
797 virtual Alg_track *cut(double t, double len, bool all);
798
799 // Like cut() but doesn't remove the notes from the original
800 // sequence. The Alg_events are copied, not shared. ONLY EVENTS
801 // THAT START WITHIN THE REGION ARE COPIED unless "all" is true
802 // in which case all notes that intersect the region are
803 // copied. COPIED NOTES MAY
804 // EXTEND BEYOND THE DURATION OF THE RESULTING SEQ.
805 // The return type is the same as this (may be Alg_seq).
806 virtual Alg_track *copy(double t, double len, bool all);
807
808 // Inserts a sequence in the middle, shifting some notes
809 // over by the duration of the seq, which is first converted
810 // to the same units (seconds or beats) as this. (This makes
811 // a differece because the pasted data may change the tempo,
812 // and notes that overlap the borders will then experience
813 // a tempo change.)
814 // THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE
815 // COPIED, NOT SHARED.
816 // The type of seq must be Alg_seq if seq is an Alg_seq, or
817 // Alg_track if seq is an Alg_track or an Alg_event_list.
818 void paste(double t, Alg_event_list *seq); // Shifts notes
819
820 // Merges two sequences with a certain offset. The offset is
821 // interpreted as either beats or seconds according to the
822 // current units of this, and seq is converted to the same
823 // units as this. Except for a possible conversion to beats
824 // or seconds, the tempo track of seq (if any) is ignored.
825 // (There is no way to merge tempo tracks.)
826 // THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE
827 // COPIED, NOT SHARED.
828 // The type of seq must be Alg_seq if seq is an Alg_seq, or
829 // Alg_track if seq is an Alg_track or an Alg_event_list.
830 virtual void merge(double t, Alg_event_list_ptr seq);
831
832 // Deletes and shifts notes to fill the gap. The tempo track
833 // is also modified accordingly. ONLY EVENTS THAT START WITHIN
834 // THE REGION ARE DELETED unless "all" is true, in which case
835 // all notes that intersect the region are cleared.
836 // NOTES THAT EXTEND FROM BEFORE THE
837 // REGION INTO THE REGION RETAIN THEIR DURATION IN EITHER
838 // BEATS OR SECONDS ACCORDING TO THE CURRENT UNITS OF this.
839 virtual void clear(double t, double len, bool all);
840
841 // Deletes notes but doesn't shift. If the "all" argument
842 // is true, deletes all notes that intersect the range at all,
843 // not just those that start within it. The tempo track is
844 // not affected.
845 virtual void silence(double t, double len, bool all);
846
847 // Simply shifts notes past time t over by len, which is given
848 // in either beats or seconds according to the units of this.
849 // The resulting interveal (t, t+len) may in fact contain notes
850 // that begin before t. The durations of notes are not changed.
851 // If this is an Alg_seq, the tempo track is expanded at t also.
852 virtual void insert_silence(double t, double len);
853
854 //
855 // Accessing for screen display
856 //
857
858 // A useful generic function to retrieve only certain
859 // types of events. The masks should be bit-masks defined
860 // somewhere else. Part of the mask allows us to search for
861 // selected events. If this is an Alg_seq, search all tracks
862 // (otherwise, call track[i].find())
863 // If channel_mask == 0, accept ALL channels
864 virtual Alg_event_list *find(double t, double len, bool all,
865 long channel_mask, long event_type_mask);
866
867 virtual void set_in_use(bool flag) { in_use = flag; }
868 //
869 // MIDI playback
870 //
871 // See Alg_iterator
872} *Alg_track_ptr, &Alg_track_ref;
873
874
875// Alg_time_sig represents a single time signature;
876// although not recommended, time_signatures may have arbitrary
877// floating point values, e.g. 4.5 beats per measure
878typedef class Alg_time_sig {
879public:
880 double beat; // when does this take effect?
881 double num; // what is the "numerator" (top number?)
882 double den; // what is the "denominator" (bottom number?)
883 Alg_time_sig(double b, double n, double d) {
884 beat = b; num = n; den = d;
885 }
886 Alg_time_sig() {
887 beat = 0; num = 0; den = 0;
888 }
889 void beat_to_measure(double beat, double *measure, double *m_beat,
890 double *num, double *den);
891
892} *Alg_time_sig_ptr;
893
894
895// Alg_time_sigs is a dynamic array of time signatures
896//
897// The default (empty) time_sigs has 4/4 time at beat 0.
898// Each time_sig object in time_sigs represents the beginning
899// of a measure. If there is a beat missing, e.g. in the first
900// measure, you can represent this by inserting another
901// time_sig at the next measure beginning. Each time_sig implies
902// an infinite sequence of full measures until the next time_sig.
903// If you insert a time_sig and one already exist near the same
904// beat, the old one is replaced, thus re-barring every measure
905// until the next time_sig.
906class Alg_time_sigs {
907private:
908 long maxlen;
909 void expand(); // make more space
910 long len;
911 Alg_time_sig_ptr time_sigs;
912public:
913 Alg_time_sigs() {
914 maxlen = len = 0;
915 time_sigs = nullptr;
916 }
917 Alg_time_sig &operator[](int i) { // fetch a time signature
918 assert(i >= 0 && i < len);
919 return time_sigs[i];
920 }
921 ~Alg_time_sigs() {
922 if (time_sigs) {
923 delete[] time_sigs;
924 }
925 }
926 void show();
927 long length() { return len; }
928 int find_beat(double beat);
929 // get the number of beats per measure starting at beat
930 double get_bar_len(double beat);
931 void insert(double beat, double num, double den, bool force = false);
932 void cut(double start, double end, double dur); // remove from start to end
933 void trim(double start, double end); // retain just start to end
934 void paste(double start, Alg_seq *seq);
935 void insert_beats(double beat, double len); // insert len beats at beat
936 // find the nearest beat (see Alg_seq::nearest_beat) to beat
937 double nearest_beat(double beat);
938};
939
940
941// a sequence of Alg_events objects
942typedef class Alg_tracks {
943private:
944 long maxlen;
945 void expand();
946 void expand_to(int new_max);
947 long len;
948public:
949 Alg_track_ptr *tracks; // tracks is array of pointers
950 Alg_track &operator[](int i) {
951 assert(i >= 0 && i < len);
952 return *tracks[i];
953 }
954 long length() { return len; }
955 Alg_tracks() {
956 maxlen = len = 0;
957 tracks = nullptr;
958 }
959 ~Alg_tracks();
960 // Append a track to tracks. This Alg_tracks becomes the owner of track.
961 void append(Alg_track_ptr track);
962 void add_track(int track_num, Alg_time_map_ptr time_map, bool seconds);
963 void reset();
964 void set_in_use(bool flag); // handy to set in_use flag on all tracks
965} *Alg_tracks_ptr;
966
967
968typedef enum {
969 alg_no_error = 0, // no error reading Allegro or MIDI file
970 alg_error_open = -800, // could not open Allegro or MIDI file
971 alg_error_syntax // something found in the file that could not be parsed;
972 // generally you should ignore syntax errors or look at the printed error
973 // messages because there are some things in standard midi files that we do
974 // not handle; (maybe we should only set alg_error_syntax when there is a
975 // real problem with the file as opposed to when there is some warning
976 // message for the user)
977} Alg_error;
978
979
980typedef struct Alg_pending_event {
981 void *cookie; // client-provided sequence identifier
982 Alg_events *events; // the array of events
983 long index; // offset of this event
984 bool note_on; // is this a note-on or a note-off (if applicable)?
985 double offset; // time offset for events
986 double time; // time for this event
987} *Alg_pending_event_ptr;
988
989
990typedef class Alg_iterator {
991private:
992 long maxlen;
993 void expand();
994 void expand_to(int new_max);
995 long len;
996 Alg_seq_ptr seq;
997 Alg_pending_event *pending_events;
998 // the next four fields are mainly for request_note_off()
999 Alg_events_ptr events_ptr; // remembers events containing current event
1000 long index; // remembers index of current event
1001 void *cookie; // remembers the cookie associated with next event
1002 double offset;
1003 void show();
1004 bool earlier(int i, int j);
1005 void insert(Alg_events_ptr events, long index, bool note_on,
1006 void *cookie, double offset);
1007 // returns the info on the next pending event in the priority queue
1008 bool remove_next(Alg_events_ptr &events, long &index, bool &note_on,
1009 void *&cookie, double &offset, double &time);
1010public:
1011 bool note_off_flag; // remembers if we are iterating over note-off
1012 // events as well as note-on and update events
1013 long length() { return len; }
1014 Alg_iterator(Alg_seq_ptr s, bool note_off) {
1015 seq = s;
1016 note_off_flag = note_off;
1017 maxlen = len = 0;
1018 pending_events = nullptr;
1019 }
1020 // Normally, iteration is over the events in the one sequence used
1021 // to instatiate the iterator (see above), but with this method, you
1022 // can add more sequences to the iteration. Events are returned in
1023 // time order, so effectively sequence events are merged.
1024 // The optional offset is added to each event time of sequence s
1025 // before merging/sorting. You should call begin_seq() for each
1026 // sequence to be included in the iteration unless you call begin()
1027 // (see below).
1028 void begin_seq(Alg_seq_ptr s, void *cookie = nullptr, double offset = 0.0);
1029 ~Alg_iterator();
1030 // Prepare to enumerate events in order. If note_off_flag is true, then
1031 // iteration_next will merge note-off events into the sequence. If you
1032 // call begin(), you should not normally call begin_seq(). See above.
1033 void begin(void *cookie = nullptr) { begin_seq(seq, cookie); }
1034 // return next event (or nullptr). If iteration_begin was called with
1035 // note_off_flag = true, and if note_on is not nullptr, then *note_on
1036 // is set to true when the result value represents a note-on or update.
1037 // (With note_off_flag, each Alg_note event is returned twice, once
1038 // at the note-on time, with *note_on == true, and once at the note-off
1039 // time, with *note_on == false. If a cookie_ptr is passed, then the
1040 // cookie corresponding to the event is stored at that address
1041 // If end_time is 0, iterate through the entire sequence, but if
1042 // end_time is non_zero, stop iterating at the last event before end_time
1043 Alg_event_ptr next(bool *note_on = nullptr, void **cookie_ptr = nullptr,
1044 double *offset_ptr = nullptr, double end_time = 0);
1045 // Sometimes, the caller wants to receive note-off events for a subset
1046 // of the notes, typically the notes that are played and need to be
1047 // turned off. In this case, when a note is turned on, the client
1048 // should call request_note_off(). This will insert a note-off into
1049 // the queue for the most recent note returned by next().
1050 void request_note_off();
1051 void end(); // clean up after enumerating events
1052} *Alg_iterator_ptr;
1053
1054
1055// An Alg_seq is an array of Alg_events, each a sequence of Alg_event,
1056// with a tempo map and a sequence of time signatures
1057//
1058typedef class Alg_seq : public Alg_track {
1059protected:
1060 Alg_iterator_ptr pending; // iterator used internally by Alg_seq methods
1061 void serialize_seq();
1062 Alg_error error; // error code set by file readers
1063 // an internal function used for writing Allegro track names
1064 Alg_event_ptr write_track_name(std::ostream &file, int n,
1065 Alg_events &events);
1066public:
1067 int channel_offset_per_track; // used to encode track_num into channel
1068 Alg_tracks track_list; // array of Alg_events
1069 Alg_time_sigs time_sig;
1070 int beat_x;
1071 void basic_initialization() {
1072 error = alg_no_error;
1073 units_are_seconds = true; type = 's';
1074 channel_offset_per_track = 0;
1075 add_track(0); // default is one empty track
1076 }
1077 Alg_seq() {
1078 basic_initialization();
1079 }
1080 // copy constructor -- if track is an Alg_seq, make a copy; if
1081 // track is just an Alg_track, the track becomes track 0
1082 Alg_seq(Alg_track_ref track) { seq_from_track(track); }
1083 Alg_seq(Alg_track_ptr track) { seq_from_track(*track); }
1084 void seq_from_track(Alg_track_ref tr);
1085 // create from file:
1086 Alg_seq(std::istream &file, bool smf, double *offset_ptr = nullptr);
1087 // create from filename
1088 Alg_seq(const char *filename, bool smf, double *offset_ptr = nullptr);
1089 virtual ~Alg_seq();
1090 int get_read_error() { return error; }
1091 void serialize(void **buffer, long *bytes);
1092 void copy_time_sigs_to(Alg_seq *dest); // a utility function
1093 void set_time_map(Alg_time_map *map);
1094
1095 // encode sequence structure into contiguous, moveable memory block
1096 // address of newly allocated memory is assigned to *buffer, which must
1097 // be freed by caller; the length of data is assigned to *len
1098 void unserialize_seq();
1099
1100 // write an ascii representation to file
1101 void write(std::ostream &file, bool in_secs, double offset = 0.0);
1102 // returns true on success
1103 bool write(const char *filename, double offset = 0.0);
1104 void smf_write(std::ostream &file);
1105 bool smf_write(const char *filename);
1106
1107 // Returns the number of tracks
1108 int tracks();
1109
1110 // create a track
1111 void add_track(int track_num) {
1112 track_list.add_track(track_num, get_time_map(), units_are_seconds);
1113 }
1114
1115 // Return a particular track. This Alg_seq owns the track, so the
1116 // caller must not delete the result.
1117 Alg_track_ptr track(int);
1118
1119 virtual Alg_event_ptr &operator[](int i);
1120
1121 virtual void convert_to_seconds();
1122 virtual void convert_to_beats();
1123
1124 Alg_track_ptr cut_from_track(int track_num, double start, double dur,
1125 bool all);
1126 Alg_seq *cut(double t, double len, bool all);
1127 void insert_silence_in_track(int track_num, double t, double len);
1128 void insert_silence(double t, double len);
1129 Alg_track_ptr copy_track(int track_num, double t, double len, bool all);
1130 Alg_seq *copy(double start, double len, bool all);
1131 void paste(double start, Alg_seq *seq);
1132 virtual void clear(double t, double len, bool all);
1133 virtual void merge(double t, Alg_event_list_ptr seq);
1134 virtual void silence(double t, double len, bool all);
1135 void clear_track(int track_num, double start, double len, bool all);
1136 void silence_track(int track_num, double start, double len, bool all);
1137 Alg_event_list_ptr find_in_track(int track_num, double t, double len,
1138 bool all, long channel_mask,
1139 long event_type_mask);
1140
1141 // find index of first score event after time
1142 long seek_time(double time, int track_num);
1143 bool insert_beat(double time, double beat);
1144 // return the time of the beat nearest to time, also returns beat
1145 // number through beat. This will correspond to an integer number
1146 // of beats from the nearest previous time signature or 0.0, but
1147 // since time signatures need not be on integer beat boundaries
1148 // the beat location may not be on an integer beat (beat locations
1149 // are measured from the beginning which is beat 0.
1150 double nearest_beat_time(double time, double *beat);
1151 // warning: insert_tempo may change representation from seconds to beats
1152 bool insert_tempo(double bpm, double beat);
1153 // change the duration from b0 to b1 (beats) to dur (seconds) by
1154 // scaling the intervening tempos
1155 bool stretch_region(double b0, double b1, double dur);
1156 // add_event takes a pointer to an event on the heap. The event is not
1157 // copied, and this Alg_seq becomes the owner and freer of the event.
1158 void add_event(Alg_event_ptr event, int track_num);
1159 void add(Alg_event_ptr /*event*/) { assert(false); } // call add_event instead
1160 // get the tempo starting at beat
1161 double get_tempo(double beat);
1162 bool set_tempo(double bpm, double start_beat, double end_beat);
1163
1164 // get the bar length in beats starting at beat
1165 double get_bar_len(double beat);
1166 void set_time_sig(double beat, double num, double den);
1167 void beat_to_measure(double beat, long *measure, double *m_beat,
1168 double *num, double *den);
1169 // void set_events(Alg_event_ptr *events, long len, long max);
1170 void merge_tracks(); // move all track data into one track
1171 void set_in_use(bool flag); // set in_use flag on all tracks
1172} *Alg_seq_ptr, &Alg_seq_ref;
1173
1174
1175// see Alg_seq::Alg_seq() constructors that read from files
1176// the following are for internal library implementation and are
1177// moved to *_internal.h header files.
1178//Alg_seq_ptr alg_read(std::istream &file, Alg_seq_ptr new_seq);
1179//Alg_seq_ptr alg_smf_read(std::istream &file, Alg_seq_ptr new_seq);
1180#endif
Definition allegro.h:77
Definition allegro.h:441
Definition allegro.h:452
Definition allegro.h:192
Definition allegro.h:322
Definition allegro.h:296
Definition allegro.h:118
Definition allegro.h:1058
Definition allegro.h:480
Definition allegro.h:878
Definition allegro.h:906
Definition allegro.h:691
Definition allegro.h:942
Definition allegro.h:309
Definition allegro.h:555
Definition allegro.h:615
Definition allegro.h:980