PageRenderTime 333ms CodeModel.GetById 40ms app.highlight 248ms RepoModel.GetById 13ms app.codeStats 2ms

/frontends/CsoundAC/allegro.cpp

https://github.com/tim81cortes/csound
C++ | 3516 lines | 2491 code | 387 blank | 638 comment | 558 complexity | f781d11ec5458eac6955eee0a82cddf9 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// Allegro: music representation system, with
   2//      extensible in-memory sequence structure
   3//      upward compatible with MIDI
   4//      implementations in C++ and Serpent
   5//      external, text-based representation
   6//      compatible with Aura
   7//
   8/* CHANGE LOG:
   904 apr 03 -- fixed bug in add_track that caused infinite loop
  10*/
  11
  12#include "assert.h"
  13#include "stdlib.h"
  14#include "stdio.h"
  15#include "string.h"
  16#include "memory.h"
  17#include <iostream>
  18#include <fstream>
  19using namespace std;
  20#include "allegro.h"
  21#include "algrd_internal.h"
  22#include "algsmfrd_internal.h"
  23// #include "trace.h" -- only needed for debugging
  24#include "math.h"
  25
  26#define STREQL(x, y) (strcmp(x, y) == 0)
  27#define MAX(x, y) ((x) > (y) ? (x) : (y))
  28#define ROUND(x) ((int) ((x) + 0.5))
  29
  30// 4311 is type cast ponter to long warning
  31// 4996 is warning against strcpy
  32// 4267 is size_t to long warning
  33#pragma warning(disable: 4311 4996 4267)
  34Alg_atoms symbol_table;
  35Serial_read_buffer Alg_track::ser_read_buf; // declare the static variables
  36Serial_write_buffer Alg_track::ser_write_buf;
  37
  38bool within(double d1, double d2, double epsilon)
  39{
  40    d1 -= d2;
  41    return d1 < epsilon && d1 > -epsilon;
  42}
  43
  44
  45char *heapify(const char *s)
  46{
  47    char *h = new char[strlen(s) + 1];
  48    strcpy(h, s);
  49    return h;
  50}
  51
  52
  53void Alg_atoms::expand()
  54{
  55    maxlen = (maxlen + 5);   // extra growth for small sizes
  56    maxlen += (maxlen >> 2); // add 25%
  57    Alg_attribute *new_atoms = new Alg_attribute[maxlen];
  58    // now do copy
  59    memcpy(new_atoms, atoms, len * sizeof(Alg_attribute));
  60    if (atoms) delete[] atoms;
  61    atoms = new_atoms;
  62}
  63
  64
  65// insert_new -- insert an attribute name and type
  66//
  67// attributes are stored as a string consisting of the type
  68// (a char) followed by the attribute name. This makes it
  69// easy to retrieve the type or the name or both.
  70//
  71Alg_attribute Alg_atoms::insert_new(const char *name, char attr_type)
  72{
  73    if (len == maxlen) expand();
  74    char *h = new char[strlen(name) + 2];
  75    strcpy(h + 1, name);
  76    *h = attr_type;
  77    atoms[len++] = h;
  78    return h;
  79}
  80
  81
  82Alg_attribute Alg_atoms::insert_attribute(Alg_attribute attr)
  83{
  84    // should use hash algorithm
  85    for (int i = 0; i < len; i++) {
  86        if (STREQL(attr, atoms[i])) {
  87            return atoms[i];
  88        }
  89    }
  90    return insert_new(attr + 1, attr[0]);
  91}
  92
  93
  94Alg_attribute Alg_atoms::insert_string(const char *name)
  95{
  96    char attr_type = name[strlen(name) - 1];
  97    for (int i = 0; i < len; i++) {
  98        if (attr_type == atoms[i][0] &&
  99            STREQL(name, atoms[i] + 1)) {
 100            return atoms[i];
 101        }
 102    }
 103    return insert_new(name, attr_type);
 104}
 105
 106
 107void Alg_parameter::copy(Alg_parameter_ptr parm)
 108{
 109    *this = *parm; // copy all fields
 110    // if the value is a string, copy the string
 111    if (attr_type() == 's') {
 112        s = heapify(s);
 113    }
 114}
 115
 116
 117void Alg_parameter::show()
 118{
 119    switch (attr[0]) {
 120    case 'r':
 121        printf("%s:%g", attr_name(), r);
 122        break;
 123    case 's':
 124        printf("%s:%s", attr_name(), s);
 125        break;
 126    case 'i':
 127        printf("%s:%ld", attr_name(), i);
 128        break;
 129    case 'l':
 130        printf("%s:%s", attr_name(), (l ? "t" : "f"));
 131        break;
 132    case 'a':
 133        printf("%s:%s", attr_name(), a);
 134        break;
 135    }
 136}
 137
 138
 139Alg_parameter::~Alg_parameter()
 140{
 141    if (attr_type() == 's' && s) {
 142        delete[] s;
 143    }
 144}
 145
 146
 147void Alg_parameters::insert_real(Alg_parameters **list, const char *name,
 148                                 double r)
 149{
 150    Alg_parameters_ptr a = new Alg_parameters(*list);
 151    *list = a;
 152    a->parm.set_attr(symbol_table.insert_string(name));
 153    a->parm.r = r;
 154    assert(a->parm.attr_type() == 'r');
 155}
 156
 157
 158void Alg_parameters::insert_string(Alg_parameters **list, const char *name,
 159                                   const char *s)
 160{
 161    Alg_parameters_ptr a = new Alg_parameters(*list);
 162    *list = a;
 163    a->parm.set_attr(symbol_table.insert_string(name));
 164    // string is deleted when parameter is deleted
 165    a->parm.s = heapify(s);
 166    assert(a->parm.attr_type() == 's');
 167}
 168
 169
 170void Alg_parameters::insert_integer(Alg_parameters **list, const char *name,
 171                                    long i)
 172{
 173    Alg_parameters_ptr a = new Alg_parameters(*list);
 174    *list = a;
 175    a->parm.set_attr(symbol_table.insert_string(name));
 176    a->parm.i = i;
 177    assert(a->parm.attr_type() == 'i');
 178}
 179
 180
 181void Alg_parameters::insert_logical(Alg_parameters **list, const char *name,
 182                                    bool l)
 183{
 184    Alg_parameters_ptr a = new Alg_parameters(*list);
 185    *list = a;
 186    a->parm.set_attr(symbol_table.insert_string(name));
 187    a->parm.l = l;
 188    assert(a->parm.attr_type() == 'l');
 189}
 190
 191
 192void Alg_parameters::insert_atom(Alg_parameters **list, const char *name,
 193                                 const char *s)
 194{
 195    Alg_parameters_ptr a = new Alg_parameters(*list);
 196    *list = a;
 197    a->parm.set_attr(symbol_table.insert_string(name));
 198    a->parm.a = symbol_table.insert_string(s);
 199    assert(a->parm.attr_type() == 'a');
 200}
 201
 202
 203Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list,
 204                                           const char *name)
 205{
 206    while (*list) {
 207        if (STREQL((*list)->parm.attr_name(), name)) {
 208            Alg_parameters_ptr p = *list;
 209            *list = p->next;
 210            p->next = NULL;
 211            return p; // caller should free this pointer
 212        }
 213        list = &((*list)->next);
 214    }
 215    return NULL;
 216}
 217
 218
 219Alg_parameter_ptr Alg_parameters::find(Alg_attribute attr)
 220{
 221    assert(attr);
 222    Alg_parameters_ptr temp = this;
 223    while (temp) {
 224        if (temp->parm.attr == attr) {
 225            return &(temp->parm);
 226        }
 227    }
 228    return NULL;
 229}
 230
 231
 232int Alg_event::get_type_code()
 233{
 234    if (!is_note()) {
 235        const char* attr = get_attribute();
 236        if (STREQL(attr, "gater"))         // volume change
 237            return ALG_GATE;
 238        if (STREQL(attr, "bendr"))         // pitch bend
 239            return ALG_BEND;
 240        if (strncmp(attr, "control", 7) == 0)      // control change
 241            // note that midi control changes have attributes of the form
 242            // "control<n>" where n is the decimal number (as a character string)
 243            // of the midi controller, e.g. control2 is the breath controller.
 244            // We don't check for decimal numbers in the range 0-127, so any
 245            // attribute that begins with "control" is an ALG_CONTROL:
 246            return ALG_CONTROL;
 247        if (STREQL(attr, "programi"))      // program change
 248            return ALG_PROGRAM;
 249        if (STREQL(attr, "pressurer"))    // pressure change
 250            return ALG_PRESSURE;
 251        if (STREQL(attr, "keysigi"))       // key signature
 252            return ALG_KEYSIG;
 253        if (STREQL(attr, "timesig_numi"))  // time signature numerator
 254            return ALG_TIMESIG_NUM;
 255        if (STREQL(attr, "timesig_deni"))  // time signature denominator
 256            return ALG_TIMESIG_DEN;
 257        return ALG_OTHER;
 258    }
 259    return ALG_NOTE; // it is a note
 260}
 261
 262
 263void Alg_event::set_parameter(Alg_parameter_ptr new_parameter)
 264{
 265    Alg_parameter_ptr parm;
 266    if (is_note()) {
 267        Alg_note_ptr note = (Alg_note_ptr) this;
 268        parm = note->parameters->find(new_parameter->attr);
 269        if (!parm) {
 270            note->parameters = new Alg_parameters(note->parameters);
 271            parm = &(note->parameters->parm);
 272        }
 273    } else { // update
 274        Alg_update_ptr update = (Alg_update_ptr) this;
 275        parm = &(update->parameter);
 276    }
 277    parm->copy(new_parameter); // copy entire parameter
 278}
 279
 280
 281void Alg_event::set_string_value(const char *a, const char *value)
 282{
 283    assert(a); // must be non-null
 284    Alg_attribute attr = symbol_table.insert_string(a);
 285    assert(attr[0] == 's');
 286    Alg_parameter parm;
 287    parm.set_attr(attr);
 288    parm.s = value;
 289    set_parameter(&parm);
 290    parm.s = NULL; // do this to prevent string from being freed
 291}
 292
 293
 294void Alg_event::set_real_value(const char *a, double value)
 295{
 296    assert(a); // must be non-null
 297    // attr is like a, but it has the type code prefixed for
 298    // fast lookup, and it is a unique string in symbol_table
 299    // e.g. a="attackr" -> attr="rattackr"
 300    Alg_attribute attr = symbol_table.insert_string(a);
 301    assert(attr[0] == 'r');
 302    Alg_parameter parm;
 303    parm.set_attr(attr);
 304    parm.r = value;
 305    set_parameter(&parm);
 306    // since type is 'r' we don't have to NULL the string
 307}
 308
 309
 310void Alg_event::set_logical_value(const char *a, bool value)
 311{
 312    assert(a); // must be non-null
 313    Alg_attribute attr = symbol_table.insert_string(a);
 314    assert(attr[0] == 'l');
 315    Alg_parameter parm;
 316    parm.set_attr(attr);
 317    parm.l = value;
 318    set_parameter(&parm);
 319    // since type is 'l' we don't have to NULL the string
 320}
 321
 322
 323void Alg_event::set_integer_value(const char *a, long value)
 324{
 325    assert(a); // must be non-null
 326    Alg_attribute attr = symbol_table.insert_string(a);
 327    assert(attr[0] == 'i');
 328    Alg_parameter parm;
 329    parm.set_attr(attr);
 330    parm.i = value;
 331    set_parameter(&parm);
 332    // since tpye is 'i' we don't have to NULL the string
 333}
 334
 335
 336void Alg_event::set_atom_value(const char *a, const char *value)
 337{
 338    assert(a); // must be non-null
 339    Alg_attribute attr = symbol_table.insert_string(a);
 340    assert(attr[0] == 'a');
 341    Alg_parameter parm;
 342    parm.set_attr(attr);
 343    parm.a = value;
 344    set_parameter(&parm);
 345    /* since type is 'a' we don't have to null the string */
 346}
 347
 348
 349float Alg_event::get_pitch()
 350{
 351    assert(is_note());
 352    Alg_note* note = (Alg_note *) this;
 353    return note->pitch;
 354}
 355
 356
 357float Alg_event::get_loud()
 358{
 359    assert(is_note());
 360    Alg_note* note = (Alg_note *) this;
 361    return note->loud;
 362}
 363
 364
 365double Alg_event::get_start_time()
 366{
 367    assert(is_note());
 368    Alg_note* note = (Alg_note *) this;
 369    return note->time;
 370}
 371
 372
 373double Alg_event::get_end_time()
 374{
 375    assert(is_note());
 376    Alg_note* note = (Alg_note *) this;
 377    return note->time + note->dur;
 378}
 379
 380
 381double Alg_event::get_duration()
 382{
 383    assert(is_note());
 384    Alg_note* note = (Alg_note *) this;
 385    return note->dur;
 386}
 387
 388
 389void Alg_event::set_pitch(float p)
 390{
 391    assert(is_note());
 392    Alg_note* note = (Alg_note *) this;
 393    note->pitch = p;
 394}
 395
 396void Alg_event::set_loud(float l)
 397{
 398    assert(is_note());
 399    Alg_note *note = (Alg_note *) this;
 400    note->loud = l;
 401}
 402
 403
 404void Alg_event::set_duration(double d)
 405{
 406    assert(is_note());
 407    Alg_note* note = (Alg_note *) this;
 408    note->dur = d;
 409}
 410
 411
 412bool Alg_event::has_attribute(const char *a)
 413{
 414    assert(is_note());
 415    assert(a); // must be non-null
 416    Alg_note* note = (Alg_note *) this;
 417    Alg_attribute attr = symbol_table.insert_string(a);
 418    Alg_parameter_ptr parm = note->parameters->find(attr);
 419    return parm != NULL;
 420}
 421
 422
 423char Alg_event::get_attribute_type(const char *a)
 424{
 425    assert(is_note());
 426    assert(a);
 427    return a[strlen(a) - 1];
 428}
 429
 430
 431const char *Alg_event::get_string_value(const char *a, const char *value)
 432{
 433    assert(is_note());
 434    assert(a); // must be non-null
 435    Alg_note* note = (Alg_note *) this;
 436    Alg_attribute attr = symbol_table.insert_string(a);
 437    assert(a[0] == 's'); // must be of type string
 438    Alg_parameter_ptr parm = note->parameters->find(attr);
 439    if (parm) return parm->s;
 440    return value;
 441}
 442
 443
 444double Alg_event::get_real_value(const char *a, double value)
 445{
 446    assert(is_note());
 447    assert(a);
 448    Alg_note* note = (Alg_note *) this;
 449    Alg_attribute attr = symbol_table.insert_string(a);
 450    assert(a[0] == 'r'); // must be of type real
 451    Alg_parameter_ptr parm = note->parameters->find(attr);
 452    if (parm) return parm->r;
 453    return value;
 454}
 455
 456
 457bool Alg_event::get_logical_value(const char *a, bool value)
 458{
 459    assert(is_note());
 460    assert(a);
 461    Alg_note* note = (Alg_note *) this;
 462    Alg_attribute attr = symbol_table.insert_string(a);
 463    assert(a[0] == 'l'); // must be of type logical
 464    Alg_parameter_ptr parm = note->parameters->find(attr);
 465    if (parm) return parm->l;
 466    return value;
 467}
 468
 469
 470long Alg_event::get_integer_value(const char *a, long value)
 471{
 472    assert(is_note());
 473    assert(a);
 474    Alg_note* note = (Alg_note *) this;
 475    Alg_attribute attr = symbol_table.insert_string(a);
 476    assert(a[0] == 'i'); // must be of type integer
 477    Alg_parameter_ptr parm = note->parameters->find(attr);
 478    if (parm) return parm->i;
 479    return value;
 480}
 481
 482
 483const char *Alg_event::get_atom_value(const char *a, const char *value)
 484{
 485    assert(is_note());
 486    assert(a);
 487    Alg_note* note = (Alg_note *) this;
 488    Alg_attribute attr = symbol_table.insert_string(a);
 489    assert(a[0] == 'a'); // must be of type atom
 490    Alg_parameter_ptr parm = note->parameters->find(attr);
 491    if (parm) return parm->a;
 492    // if default is a string, convert to an atom (unique
 493    // string in symbol table) and return it
 494    return (value == NULL ? NULL :
 495              symbol_table.insert_string(value));
 496}
 497
 498
 499void Alg_event::delete_attribute(const char *a)
 500{
 501    assert(is_note());
 502    Alg_note* note = (Alg_note *) this;
 503    Alg_parameters::remove_key(&(note->parameters), a);
 504}
 505
 506
 507const char *Alg_event::get_attribute()
 508// Note: this returns a string, not an Alg_attribute
 509{
 510    assert(is_update());
 511    Alg_update* update = (Alg_update *) this;
 512    return update->parameter.attr_name();
 513}
 514
 515
 516char Alg_event::get_update_type()
 517{
 518    assert(is_update());
 519    Alg_update* update = (Alg_update *) this;
 520    return update->parameter.attr_type();
 521}
 522
 523
 524const char *Alg_event::get_string_value()
 525{
 526    assert(is_update());
 527    Alg_update* update = (Alg_update *) this;
 528    assert(get_update_type() == 's');
 529    return update->parameter.s;
 530}
 531
 532
 533double Alg_event::get_real_value()
 534{
 535    assert(is_update());
 536    Alg_update* update = (Alg_update *) this;
 537    assert(get_update_type() == 'r');
 538    return update->parameter.r;
 539}
 540
 541
 542bool Alg_event::get_logical_value()
 543{
 544    assert(is_update());
 545    Alg_update* update = (Alg_update *) this;
 546    assert(get_update_type() == 'l');
 547    return update->parameter.l;
 548}
 549
 550
 551long Alg_event::get_integer_value()
 552{
 553    assert(is_update());
 554    Alg_update* update = (Alg_update *) this;
 555    assert(get_update_type() == 'i');
 556    return update->parameter.i;
 557}
 558
 559
 560const char *Alg_event::get_atom_value()
 561{
 562    assert(is_update());
 563    Alg_update* update = (Alg_update *) this;
 564    assert(get_update_type() == 'a');
 565    return update->parameter.a;
 566}
 567
 568
 569bool Alg_event::overlap(double t, double len, bool all)
 570{
 571    // event starts within region
 572    if (time >= t && time <= t + len - ALG_EPS)
 573        return true;
 574    if (all && is_note()) {
 575        double dur = ((Alg_note_ptr) this)->dur;
 576        // note overlaps with region
 577        if (time < t && time + dur - ALG_EPS > t)
 578            return true;
 579    }
 580    // does not overlap
 581    return false;
 582}
 583
 584
 585Alg_note::Alg_note(Alg_note_ptr note)
 586{
 587    *this = *note; // copy all fields
 588    // parameters is now a shared pointer. We need to copy the
 589    // parameters
 590    Alg_parameters_ptr next_param_ptr = parameters;
 591    while (next_param_ptr) {
 592        Alg_parameters_ptr new_params = new Alg_parameters(next_param_ptr->next);
 593        new_params->parm.copy(&(next_param_ptr->parm)); // copy the attribute and value
 594        next_param_ptr = new_params->next;
 595    }
 596}
 597
 598
 599Alg_note::~Alg_note()
 600{
 601    while (parameters) {
 602        Alg_parameters_ptr to_delete = parameters;
 603        parameters = parameters->next;
 604        delete to_delete;
 605    }
 606}
 607
 608
 609void Alg_note::show()
 610{
 611    printf("Alg_note: time %g, chan %ld, dur %g, key %ld, "
 612           "pitch %g, loud %g, attributes ",
 613           time, chan, dur, key, pitch, loud);
 614    Alg_parameters_ptr parms = parameters;
 615    while (parms) {
 616        parms->parm.show();
 617        printf(" ");
 618        parms = parms->next;
 619    }
 620    printf("\n");
 621}
 622
 623
 624Alg_update::Alg_update(Alg_update_ptr update)
 625{
 626    *this = *update; // copy all fields
 627    // parameter requires careful copy to possibly duplicate string value:
 628    this->parameter.copy(&(update->parameter));
 629}
 630
 631
 632void Alg_update::show()
 633{
 634    printf("Alg_update: ");
 635    parameter.show();
 636    printf("\n");
 637}
 638
 639
 640void Alg_events::expand()
 641{
 642    maxlen = (maxlen + 5);   // extra growth for small sizes
 643    maxlen += (maxlen >> 2); // add 25%
 644    Alg_event_ptr *new_events = new Alg_event_ptr[maxlen];
 645    // now do copy
 646    memcpy(new_events, events, len * sizeof(Alg_event_ptr));
 647    if (events) delete[] events;
 648    events = new_events;
 649}
 650
 651
 652void Alg_events::insert(Alg_event_ptr event)
 653{
 654    if (maxlen <= len) {
 655        expand();
 656    }
 657    // Note: if the new event is the last one, the assignment
 658    // events[i] = event; (below) will never execute, so just
 659    // in case, we do the assignment here. events[len] will
 660    // be replaced during the memmove() operation below if
 661    // this is not the last event.
 662    events[len] = event;
 663    len++;
 664    // find insertion point: (this could be a binary search)
 665    for (int i = 0; i < len; i++) {
 666        if (events[i]->time > event->time) {
 667            // insert event at i
 668            memmove(&events[i + 1], &events[i],
 669                    sizeof(Alg_event_ptr) * (len - i - 1));
 670            events[i] = event;
 671            return;
 672        }
 673    }
 674}
 675
 676Alg_event_ptr Alg_events::uninsert(long index)
 677{
 678    assert(0 <= index && index < len);
 679    Alg_event_ptr event = events[index];
 680    //printf("memmove: %x from %x (%d)\n", events + index, events + index + 1,
 681    //        sizeof(Alg_event_ptr) * (len - index - 1));
 682    memmove(events + index, events + index + 1,
 683            sizeof(Alg_event_ptr) * (len - index - 1));
 684    len--;
 685    return event;
 686}
 687
 688
 689void Alg_events::append(Alg_event_ptr event)
 690{
 691    if (maxlen <= len) {
 692        expand();
 693    }
 694    events[len++] = event;
 695    // keep track of last note_off time
 696    if (event->is_note()) {
 697        Alg_note_ptr note = (Alg_note_ptr) event;
 698        double note_off = note->time + note->dur;
 699        if (note_off > last_note_off)
 700            last_note_off = note_off;
 701    }
 702}
 703
 704
 705Alg_events::~Alg_events()
 706{
 707    assert(!in_use);
 708    // individual events are not deleted, only the array
 709    if (events) {
 710        delete[] events;
 711    }
 712}
 713
 714
 715Alg_event_list::Alg_event_list(Alg_track *owner)
 716{
 717        events_owner = owner;
 718        sequence_number = owner->sequence_number;
 719        beat_dur = 0.0; real_dur = 0.0; type = 'e';
 720}
 721
 722
 723Alg_event_ptr &Alg_event_list::operator [](int i)
 724{
 725    assert(i >= 0 && i < len);
 726    return events[i];
 727}
 728
 729
 730Alg_event_list::~Alg_event_list()
 731{
 732    // note that the events contained in the list are not destroyed
 733}
 734
 735
 736void Alg_event_list::set_start_time(Alg_event *event, double t)
 737{
 738    // For Alg_event_list, find the owner and do the update there
 739    // For Alg_track, change the time and move the event to the right place
 740    // For Alg_seq, find the track and do the update there
 741
 742    long index, i;
 743    Alg_track_ptr track_ptr;
 744    if (type == 'e') { // this is an Alg_event_list
 745        // make sure the owner has not changed its event set
 746        assert(events_owner &&
 747               sequence_number == events_owner->sequence_number);
 748        // do the update on the owner
 749        events_owner->set_start_time(event, t);
 750        return;
 751    } else if (type == 't') { // this is an Alg_track
 752        // find the event in the track
 753        track_ptr = (Alg_track_ptr) this;
 754        // this should be a binary search since events are in time order
 755        // probably there should be member function to do the search
 756        for (index = 0; index < length(); index++) {
 757            if ((*track_ptr)[index] == event) goto found_event;
 758        }
 759    } else { // type == 's', an Alg_seq
 760        Alg_seq_ptr seq = (Alg_seq_ptr) this;
 761        for (i = 0; i < seq->tracks(); i++) {
 762            track_ptr = seq->track(i);
 763            // if you implemented binary search, you could call it
 764            // instead of this loop too.
 765            for (index = 0; index < track_ptr->length(); index++) {
 766                if ((*track_ptr)[index] == event) goto found_event;
 767            }
 768        }
 769    }
 770    assert(false); // event not found seq or track!
 771  found_event:
 772    // at this point, track[index] == event
 773    // we could be clever and figure out exactly what notes to move
 774    // but it is simpler to just remove the event and reinsert it:
 775    track_ptr->uninsert(index);
 776    event->time = t;
 777    track_ptr->insert(event);
 778}
 779
 780
 781void Alg_beats::expand()
 782{
 783    maxlen = (maxlen + 5);   // extra growth for small sizes
 784    maxlen += (maxlen >> 2); // add 25%
 785    Alg_beat_ptr new_beats = new Alg_beat[maxlen];
 786    // now do copy
 787    memcpy(new_beats, beats, len * sizeof(Alg_beat));
 788    if (beats) delete[] beats;
 789    beats = new_beats;
 790}
 791
 792
 793void Alg_beats::insert(long i, Alg_beat_ptr beat)
 794{
 795    assert(i >= 0 && i <= len);
 796    if (maxlen <= len) {
 797        expand();
 798    }
 799    memmove(&beats[i + 1], &beats[i], sizeof(Alg_beat) * (len - i));
 800    memcpy(&beats[i], beat, sizeof(Alg_beat));
 801    len++;
 802}
 803
 804
 805Alg_time_map::Alg_time_map(Alg_time_map *map)
 806{
 807    refcount = 0;
 808    assert(map->beats[0].beat == 0 && map->beats[0].time == 0);
 809    assert(map->beats.len > 0);
 810    // new_beats[0] = map->beats[0];
 811       // this is commented because
 812       // both new_beats[0] and map->beats[0] should be (0, 0)
 813    for (int i = 1; i < map->beats.len; i++) {
 814        beats.insert(i, &map->beats[i]);
 815    }
 816    last_tempo = map->last_tempo;
 817    last_tempo_flag = map->last_tempo_flag;
 818}
 819
 820
 821void Alg_time_map::show()
 822{
 823    printf("Alg_time_map: ");
 824    for (int i = 0; i < beats.len; i++) {
 825        Alg_beat &b = beats[i];
 826        printf("(%g, %g) ", b.time, b.beat);
 827    }
 828    printf("last tempo: %g\n", last_tempo);
 829}
 830
 831
 832long Alg_time_map::locate_time(double time)
 833{
 834    int i = 0;
 835    while ((i < beats.len) && (time > beats[i].time)) {
 836        i++;
 837    }
 838    return i;
 839}
 840
 841
 842long Alg_time_map::locate_beat(double beat)
 843{
 844    int i = 0;
 845    while ((i < beats.len) && (beat > beats[i].beat)) {
 846        i++;
 847    }
 848    return i;
 849}
 850
 851
 852double Alg_time_map::beat_to_time(double beat)
 853{
 854    Alg_beat_ptr mbi;
 855    Alg_beat_ptr mbi1;
 856    if (beat <= 0) {
 857        return beat;
 858    }
 859    int i = locate_beat(beat);
 860    // case 1: beat is between two time/beat pairs
 861    if (0 < i && i < beats.len) {
 862        mbi = &beats[i - 1];
 863        mbi1 = &beats[i];
 864    // case 2: beat is beyond last time/beat pair
 865    } else if (i == beats.len) {
 866        if (last_tempo_flag) {
 867            return beats[i - 1].time +
 868                   (beat - beats[i - 1].beat) / last_tempo;
 869        } else if (i == 1) {
 870            return beat * 60.0 / ALG_DEFAULT_BPM;
 871                // so we use that as default allegro tempo too
 872        } else {
 873            mbi = &beats[i - 2];
 874            mbi1 = &beats[i - 1];
 875        }
 876    // case 3: beat is at time 0
 877    } else /* if (i == 0) */ {
 878        return beats[0].time;
 879    }
 880    // whether we extrapolate or interpolate, the math is the same
 881    double time_dif = mbi1->time - mbi->time;
 882    double beat_dif = mbi1->beat - mbi->beat;
 883    return mbi->time + (beat - mbi->beat) * time_dif / beat_dif;
 884}
 885
 886
 887double Alg_time_map::time_to_beat(double time)
 888{
 889    Alg_beat_ptr mbi;
 890    Alg_beat_ptr mbi1;
 891    if (time <= 0.0) return time;
 892    int i = locate_time(time);
 893    if (i == beats.len) {
 894        if (last_tempo_flag) {
 895            return beats[i - 1].beat +
 896                   (time - beats[i - 1].time) * last_tempo;
 897        } else if (i == 1) {
 898            return time * (ALG_DEFAULT_BPM / 60.0);
 899        } else {
 900            mbi = &beats[i - 2];
 901            mbi1 = &beats[i - 1];
 902        }
 903    } else {
 904        mbi = &beats[i - 1];
 905        mbi1 = & beats[i];
 906    }
 907    double time_dif = mbi1->time - mbi->time;
 908    double beat_dif = mbi1->beat - mbi->beat;
 909    return mbi->beat + (time - mbi->time) * beat_dif / time_dif;
 910}
 911
 912
 913void Alg_time_map::insert_beat(double time, double beat)
 914{
 915    int i = locate_time(time); // i is insertion point
 916    if (i < beats.len && within(beats[i].time, time, 0.000001)) {
 917        // replace beat if time is already in the map
 918        beats[i].beat = beat;
 919    } else {
 920        Alg_beat point;
 921        point.beat = beat;
 922        point.time = time;
 923        beats.insert(i, &point);
 924    }
 925    // beats[i] contains new beat
 926    // make sure we didn't generate a zero tempo.
 927    // if so, space beats by one microbeat as necessary
 928    long j = i;
 929    if (j == 0) j = 1; // do not adjust beats[0]
 930    while (j < beats.len &&
 931        beats[j - 1].beat + 0.000001 >= beats[j].beat) {
 932        beats[j].beat = beats[j - 1].beat + 0.000001;
 933        j++;
 934    }
 935}
 936
 937
 938bool Alg_time_map::insert_tempo(double tempo, double beat)
 939{
 940    tempo = tempo / 60.0; // convert to beats per second
 941    // change the tempo at the given beat until the next beat event
 942    if (beat < 0) return false;
 943    double time = beat_to_time(beat);
 944    long i = locate_time(time);
 945    if (i >= beats.len || !within(beats[i].time, time, 0.000001)) {
 946        insert_beat(time, beat);
 947    }
 948    // now i is index of beat where tempo will change
 949    if (i == beats.len - 1) {
 950        last_tempo = tempo;
 951        // printf("last_tempo to %g\n", last_tempo);
 952        last_tempo_flag = true;
 953    } else { // adjust all future beats
 954        // compute the difference in beats
 955        double diff = beats[i + 1].beat - beats[i].beat;
 956        // convert beat difference to seconds at new tempo
 957        diff = diff / tempo;
 958        // figure out old time difference:
 959        double old_diff = beats[i + 1].time - time;
 960        // compute difference too
 961        diff = diff - old_diff;
 962        // apply new_diff to score and beats
 963        i++;
 964        while (i < beats.len) {
 965            beats[i].time = beats[i].time + diff;
 966            i++;
 967        }
 968    }
 969    return true;
 970}
 971
 972
 973double Alg_time_map::get_tempo(double beat)
 974{
 975    Alg_beat_ptr mbi;
 976    Alg_beat_ptr mbi1;
 977    // if beat < 0, there is probably an error; return something nice anyway
 978    if (beat < 0) return ALG_DEFAULT_BPM / 60.0;
 979    long i = locate_beat(beat);
 980    // this code is similar to beat_to_time() so far, but we want to get
 981    // beyond beat if possible because we want the tempo FOLLOWING beat
 982    // (Consider the case beat == 0.0)
 983    if (i < beats.len && beat >= beats[i].beat) i++;
 984    // case 1: beat is between two time/beat pairs
 985    if (i < beats.len) {
 986        mbi = &beats[i - 1];
 987        mbi1 = &beats[i];
 988    // case 2: beat is beyond last time/beat pair
 989    } else /* if (i == beats.len) */ {
 990        if (last_tempo_flag) {
 991            return last_tempo;
 992        } else if (i == 1) {
 993            return ALG_DEFAULT_BPM / 60.0;
 994        } else {
 995            mbi = &beats[i - 2];
 996            mbi1 = &beats[i - 1];
 997        }
 998    }
 999    double time_dif = mbi1->time - mbi->time;
1000    double beat_dif = mbi1->beat - mbi->beat;
1001    return beat_dif / time_dif;
1002}
1003
1004
1005bool Alg_time_map::set_tempo(double tempo, double start_beat, double end_beat)
1006{
1007    if (start_beat >= end_beat) return false;
1008    // algorithm: insert a beat event if necessary at start_beat
1009    // and at end_beat
1010    // delete intervening map elements
1011    // change the tempo
1012    insert_beat(beat_to_time(start_beat), start_beat);
1013    insert_beat(beat_to_time(end_beat), end_beat);
1014    long start_x = locate_beat(start_beat) + 1;
1015    long stop_x = locate_beat(end_beat);
1016    while (stop_x < beats.len) {
1017        beats[start_x] = beats[stop_x];
1018        start_x++;
1019        stop_x++;
1020    }
1021    beats.len = start_x; // truncate the map to new length
1022    return insert_tempo(tempo, start_beat);
1023}
1024
1025
1026bool Alg_time_map::stretch_region(double b0, double b1, double dur)
1027{
1028    // find current duration
1029    double t0 = beat_to_time(b0);
1030    double t1 = beat_to_time(b1);
1031    double old_dur = t1 - t0;
1032    if (old_dur <= 0 || dur <= 0) return false;
1033    double scale = dur / old_dur; // larger scale => slower
1034    // insert a beat if necessary at b0 and b1
1035    insert_beat(t0, b0);
1036    insert_beat(t1, b1);
1037    long start_x = locate_beat(b0);
1038    long stop_x = locate_beat(b1);
1039    double orig_time = beats[start_x].time;
1040    double prev_time = orig_time;
1041    for (int i = start_x + 1; i < beats.len; i++) {
1042        double delta = beats[i].time - orig_time;
1043        if (i <= stop_x) { // change tempo to next Alg_beat
1044            delta *= scale;
1045        }
1046        orig_time = beats[i].time;
1047        prev_time += delta;
1048        beats[i].time = prev_time;
1049    }
1050    return true;
1051}
1052
1053
1054void Alg_time_map::trim(double start, double end, bool units_are_seconds)
1055{
1056    // extract the time map from start to end and shift to time zero
1057    // start and end are time in seconds if units_are_seconds is true
1058    int i = 0; // index into beats
1059    int start_index; // index of first breakpoint after start
1060    int count = 1;
1061    double initial_beat = start;
1062    double final_beat = end;
1063    if (units_are_seconds) {
1064        initial_beat = time_to_beat(start);
1065        final_beat = time_to_beat(end);
1066    } else {
1067        start = beat_to_time(initial_beat);
1068        end = beat_to_time(final_beat);
1069    }
1070    while (i < length() && beats[i].time < start) i++;
1071    // now i is index into beats of the first breakpoint after start
1072    // beats[0] is (0,0) and remains that way
1073    // copy beats[start_index] to beats[1], etc.
1074    // skip any beats at or near (start,initial_beat), using count
1075    // to keep track of how many entries there are
1076    start_index = i;
1077    while (i < length() && beats[i].time < end) {
1078        if (beats[i].time - start > ALG_EPS &&
1079            beats[i].beat - initial_beat > ALG_EPS) {
1080            beats[i].time = beats[i].time - start;
1081            beats[i].beat = beats[i].beat - initial_beat;
1082            beats[i - start_index + 1] = beats[i];
1083            count = count + 1;
1084        } else {
1085            start_index = start_index + 1;
1086        }
1087        i = i + 1;
1088    }
1089    // set last tempo data
1090    // we last examined beats[i-1] and copied it to
1091    //   beats[i - start_index]. Next tempo should come
1092    //   from beats[i] and store in beats[i - start_index + 1]
1093    // case 1: there is at least one breakpoint beyond end
1094    //         => interpolate to put a breakpoint at end
1095    // case 2: no more breakpoints => set last tempo data
1096    if (i < length()) {
1097        // we know beats[i].time >= end, so case 1 applies
1098        beats[i - start_index + 1].time = end - start;
1099        beats[i - start_index + 1].beat = final_beat - initial_beat;
1100        count = count + 1;
1101    }
1102    // else we'll just use stored last tempo (if any)
1103    beats.len = count;
1104}
1105
1106
1107void Alg_time_map::cut(double start, double len, bool units_are_seconds)
1108{
1109    // remove portion of time map from start to start + len,
1110    // shifting the tail left by len. start and len are in whatever
1111    // units the score is in. If you cut the time_map as well as cut
1112    // the tracks of the sequence, then sequences will preserve the
1113    // association between tempo changes and events
1114    double end = start + len;
1115    double initial_beat = start;
1116    double final_beat = end;
1117    int i = 0;
1118
1119    if (units_are_seconds) {
1120        initial_beat = time_to_beat(start);
1121        final_beat = time_to_beat(end);
1122    } else {
1123        start = beat_to_time(initial_beat);
1124        end = beat_to_time(final_beat);
1125        len = end - start;
1126    }
1127    double beat_len = final_beat - initial_beat;
1128
1129    while (i < length() && beats[i].time < start - ALG_EPS) {
1130        i = i + 1;
1131    }
1132
1133    // if no beats exist at or after start, just return; nothing to cut
1134    if (i == length()) return;
1135
1136    // now i is index into beats of the first breakpoint on or
1137    // after start, insert (start, initial_beat) in map
1138    if (i < length() && within(beats[i].time, start, ALG_EPS)) {
1139        // perterb time map slightly (within alg_eps) to place
1140        // break point exactly at the start time
1141        beats[i].time = start;
1142        beats[i].beat = initial_beat;
1143    } else {
1144        Alg_beat point(start, initial_beat);
1145        beats.insert(i, &point);
1146    }
1147    // now, we're correct up to beats[i] and beats[i] happens at start.
1148    // find first beat after end so we can start shifting from there
1149    i = i + 1;
1150    int start_index = i;
1151    while (i < length() && beats[i].time < end + ALG_EPS) i++;
1152    // now beats[i] is the next point to be included in beats
1153    // but from i onward, we must shift by (-len, -beat_len)
1154    while (i < length()) {
1155        Alg_beat &b = beats[i];
1156        b.time = b.time - len;
1157        b.beat = b.beat - beat_len;
1158        beats[start_index] = b;
1159        i = i + 1;
1160        start_index = start_index + 1;
1161    }
1162    beats.len = start_index;
1163}
1164
1165
1166void Alg_time_map::paste(double beat, Alg_track *tr)
1167{
1168    // insert a given time map at a given time and dur (in beats)
1169    Alg_time_map_ptr from_map = tr->get_time_map();
1170    // printf("time map paste\nfrom map\n");
1171    // from_map->show();
1172    // printf("to map\n");
1173    // show();
1174    Alg_beats &from = from_map->beats;
1175    double time = beat_to_time(beat);
1176    // Locate the point at which dur occurs
1177    double dur = tr->get_beat_dur();
1178    double tr_end_time = from_map->beat_to_time(dur);
1179    // add offset to make room for insert
1180    int i = locate_beat(beat);
1181    while (i < length()) {
1182        beats[i].beat += dur;
1183        beats[i].time += tr_end_time;
1184        i++;
1185    }
1186    // printf("after opening up\n");
1187    // show();
1188    // insert point at beginning and end of paste
1189    insert_beat(time, beat);
1190    // printf("after beginning point insert\n");
1191    // show();
1192    // insert_beat(time + tr_end_time, beat + dur);
1193    // printf("after ending point insert\n");
1194    // show();
1195    int j = from_map->locate_beat(dur);
1196    for (i = 0; i < j; i++) {
1197        insert_beat(from[i].time + time,  // shift by time
1198                    from[i].beat + beat); // and beat
1199    }
1200    // printf("after inserts\n");
1201    show();
1202}
1203
1204
1205void Alg_time_map::insert_time(double start, double len)
1206{
1207    // find time,beat pair that determines tempo at start
1208    // compute beat offset = (delta beat / delta time) * len
1209    // add len,beat offset to each following Alg_beat
1210    // show();
1211    int i = locate_time(start); // start <= beats[i].time
1212    if (beats[i].time == start) i++; // start < beats[i].time
1213    // case 1: between beats
1214    if (i > 0 && i < length()) {
1215        double beat_offset = len * (beats[i].beat - beats[i-1].beat) /
1216                                   (beats[i].time - beats[i-1].time);
1217        while (i < length()) {
1218            beats[i].beat += beat_offset;
1219            beats[i].time += len;
1220            i++;
1221        }
1222    } // otherwise, last tempo is in effect; nothing to do
1223    // printf("time_map AFTER INSERT\n");
1224    // show();
1225}
1226
1227
1228void Alg_time_map::insert_beats(double start, double len)
1229{
1230    int i = locate_beat(start); // start <= beats[i].beat
1231    if (beats[i].beat == start) i++;
1232    if (i > 0 && i < length()) {
1233        double time_offset = len * (beats[i].time - beats[i-1].time) /
1234                                   (beats[i].beat - beats[i-1].beat);
1235        while (i < length()) {
1236            beats[i].time += time_offset;
1237            beats[i].beat += len;
1238            i++;
1239        }
1240    } // otherwise, last tempo is in effect; nothing to do
1241    // printf("time_map AFTER INSERT\n");
1242    // show();
1243}
1244
1245
1246Alg_track::Alg_track(Alg_time_map *map, bool seconds)
1247{
1248    type = 't';
1249    time_map = NULL;
1250    units_are_seconds = seconds;
1251    set_time_map(map);
1252}
1253
1254
1255Alg_event_ptr Alg_track::copy_event(Alg_event_ptr event)
1256{
1257    Alg_event *new_event;
1258    if (event->is_note()) {
1259        new_event = new Alg_note((Alg_note_ptr) event);
1260    } else { // update
1261        new_event = new Alg_update((Alg_update_ptr) event);
1262    }
1263    return new_event;
1264}
1265
1266
1267Alg_track::Alg_track(Alg_track &track)
1268{
1269    type = 't';
1270    time_map = NULL;
1271    for (int i = 0; i < track.length(); i++) {
1272      append(copy_event(track.events[i]));
1273    }
1274    set_time_map(track.time_map);
1275    units_are_seconds = track.units_are_seconds;
1276}
1277
1278
1279Alg_track::Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map,
1280                     bool units_are_seconds)
1281{
1282    type = 't';
1283    time_map = NULL;
1284    for (int i = 0; i < event_list.length(); i++) {
1285        append(copy_event(event_list[i]));
1286    }
1287    set_time_map(map);
1288    this->units_are_seconds = units_are_seconds;
1289}
1290
1291
1292void Alg_track::serialize(void **buffer, long *bytes)
1293{
1294    // first determine whether this is a seq or a track.
1295    // if it is a seq, then we will write the time map and a set of tracks
1296    // if it is a track, we just write the track data and not the time map
1297    //
1298    // The code will align doubles on ALIGN boundaries, and longs and
1299    // floats are aligned to multiples of 4 bytes.
1300    //
1301    // The format for a seq is:
1302    //   'ALGS' -- indicates that this is a sequence
1303    //   long length of all seq data in bytes starting with 'ALGS'
1304    //   long channel_offset_per_track
1305    //   long units_are_seconds
1306    //   time_map:
1307    //      double last_tempo
1308    //      long last_tempo_flag
1309    //      long len -- number of tempo changes
1310    //      for each tempo change (Alg_beat):
1311    //         double time
1312    //         double beat
1313    //   time_sigs:
1314    //      long len -- number of time_sigs
1315    //      long pad
1316    //      for each time signature:
1317    //         double beat
1318    //         double num
1319    //         double den
1320    //   tracks:
1321    //      long len -- number of tracks
1322    //      long pad
1323    //      for each track:
1324    //         'ALGT' -- indicates this is a track
1325    //         long length of all track data in bytes starting with 'ALGT'
1326    //         long units_are_seconds
1327    //         double beat_dur
1328    //         double real_dur
1329    //         long len -- number of events
1330    //         for each event:
1331    //            long selected
1332    //            long type
1333    //            long key
1334    //            long channel
1335    //            double time
1336    //            if this is a note:
1337    //               double pitch
1338    //               double dur
1339    //               double loud
1340    //               long len -- number of parameters
1341    //               for each parameter:
1342    //                  char attribute[] with zero pad to ALIGN
1343    //                  one of the following, depending on type:
1344    //                     double r
1345    //                     char s[] terminated by zero
1346    //                     long i
1347    //                     long l
1348    //                     char a[] terminated by zero
1349    //               zero pad to ALIGN
1350    //            else if this is an update
1351    //               (same representation as parameter above)
1352    //               zero pad to ALIGN
1353    //
1354    // The format for a track is given within the Seq format above
1355    assert(get_type() == 't');
1356    ser_write_buf.init_for_write();
1357    serialize_track();
1358    *buffer = ser_write_buf.to_heap(bytes);
1359}
1360
1361
1362void Alg_seq::serialize(void **buffer, long *bytes)
1363{
1364    assert(get_type() == 's');
1365    ser_write_buf.init_for_write();
1366    serialize_seq();
1367    *buffer = ser_write_buf.to_heap(bytes);
1368}
1369
1370
1371void Serial_write_buffer::check_buffer(long needed)
1372{
1373    if (len < (ptr - buffer) + needed) { // do we need more space?
1374        long new_len = len * 2; // exponential growth is important
1375        // initially, length is zero, so bump new_len to a starting value
1376        if (new_len == 0) new_len = 1024;
1377         // make sure new_len is as big as needed
1378        if (needed > new_len) new_len = needed;
1379        char *new_buffer = new char[new_len]; // allocate space
1380        ptr = new_buffer + (ptr - buffer); // relocate ptr to new buffer
1381        if (len > 0) { // we had a buffer already
1382            memcpy(new_buffer, buffer, len); // copy from old buffer
1383            delete buffer; // free old buffer
1384        }
1385        buffer = new_buffer; // update buffer information
1386        len = new_len;
1387    }
1388}
1389
1390
1391void Alg_seq::serialize_seq()
1392{
1393    int i; // loop counters
1394    // we can easily compute how much buffer space we need until we
1395    // get to tracks, so expand at least that much
1396    long needed = 64 + 16 * time_map->beats.len + 24 * time_sig.length();
1397    ser_write_buf.check_buffer(needed);
1398    ser_write_buf.set_char('A');
1399    ser_write_buf.set_char('L');
1400    ser_write_buf.set_char('G');
1401    ser_write_buf.set_char('S');
1402    long length_offset = ser_write_buf.get_posn();
1403    ser_write_buf.set_int32(0); // leave room to come back and write length
1404    ser_write_buf.set_int32(channel_offset_per_track);
1405    ser_write_buf.set_int32(units_are_seconds);
1406    ser_write_buf.set_double(beat_dur);
1407    ser_write_buf.set_double(real_dur);
1408    ser_write_buf.set_double(time_map->last_tempo);
1409    ser_write_buf.set_int32(time_map->last_tempo_flag);
1410    ser_write_buf.set_int32(time_map->beats.len);
1411    for (i = 0; i < time_map->beats.len; i++) {
1412        ser_write_buf.set_double(time_map->beats[i].time);
1413        ser_write_buf.set_double(time_map->beats[i].beat);
1414    }
1415    ser_write_buf.set_int32(time_sig.length());
1416    ser_write_buf.pad();
1417    for (i = 0; i < time_sig.length(); i++) {
1418        ser_write_buf.set_double(time_sig[i].beat);
1419        ser_write_buf.set_double(time_sig[i].num);
1420        ser_write_buf.set_double(time_sig[i].den);
1421    }
1422    ser_write_buf.set_int32(tracks());
1423    ser_write_buf.pad();
1424    for (i = 0; i < tracks(); i++) {
1425        track(i)->serialize_track();
1426    }
1427    // do not include ALGS, include padding at end
1428    ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset);
1429}
1430
1431
1432void Alg_track::serialize_track()
1433{
1434    // to simplify the code, copy from parameter addresses to locals
1435    int j;
1436    ser_write_buf.check_buffer(32);
1437    ser_write_buf.set_char('A');
1438    ser_write_buf.set_char('L');
1439    ser_write_buf.set_char('G');
1440    ser_write_buf.set_char('T');
1441    long length_offset = ser_write_buf.get_posn(); // save location for track length
1442    ser_write_buf.set_int32(0); // room to write track length
1443    ser_write_buf.set_int32(units_are_seconds);
1444    ser_write_buf.set_double(beat_dur);
1445    ser_write_buf.set_double(real_dur);
1446    ser_write_buf.set_int32(len);
1447    for (j = 0; j < len; j++) {
1448        ser_write_buf.check_buffer(24);
1449        Alg_event *event = (*this)[j];
1450        ser_write_buf.set_int32(event->get_selected());
1451        ser_write_buf.set_int32(event->get_type());
1452        ser_write_buf.set_int32(event->get_identifier());
1453        ser_write_buf.set_int32(event->chan);
1454        ser_write_buf.set_double(event->time);
1455        if (event->is_note()) {
1456            ser_write_buf.check_buffer(20);
1457            Alg_note *note = (Alg_note *) event;
1458            ser_write_buf.set_float(note->pitch);
1459            ser_write_buf.set_float(note->loud);
1460            ser_write_buf.set_double(note->dur);
1461            long parm_num_offset = ser_write_buf.get_posn();
1462            long parm_num = 0;
1463            ser_write_buf.set_int32(0); // placeholder for no. parameters
1464            Alg_parameters_ptr parms = note->parameters;
1465            while (parms) {
1466                serialize_parameter(&(parms->parm));
1467                parms = parms->next;
1468                parm_num++;
1469            }
1470            ser_write_buf.store_long(parm_num_offset, parm_num);
1471        } else {
1472            assert(event->is_update());
1473            Alg_update *update = (Alg_update *) event;
1474            serialize_parameter(&(update->parameter));
1475        }
1476        ser_write_buf.check_buffer(7); // maximum padding possible
1477        ser_write_buf.pad();
1478    }
1479    // write length, not including ALGT, including padding at end
1480    ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset);
1481}
1482
1483
1484void Alg_track::serialize_parameter(Alg_parameter *parm)
1485{
1486    // add eight to account for name + zero end-of-string and the
1487    // possibility of adding 7 padding bytes
1488    long len = strlen(parm->attr_name()) + 8;
1489    ser_write_buf.check_buffer(len);
1490    ser_write_buf.set_string(parm->attr_name());
1491    ser_write_buf.pad();
1492    switch (parm->attr_type()) {
1493    case 'r':
1494        ser_write_buf.check_buffer(8);
1495        ser_write_buf.set_double(parm->r);
1496        break;
1497    case 's':
1498        ser_write_buf.check_buffer(strlen(parm->s) + 1);
1499        ser_write_buf.set_string(parm->s);
1500        break;
1501    case 'i':
1502        ser_write_buf.check_buffer(4);
1503        ser_write_buf.set_int32(parm->i);
1504        break;
1505    case 'l':
1506        ser_write_buf.check_buffer(4);
1507        ser_write_buf.set_int32(parm->l);
1508        break;
1509    case 'a':
1510        ser_write_buf.check_buffer(strlen(parm->a) + 1);
1511        ser_write_buf.set_string(parm->a);
1512        break;
1513    }
1514}
1515
1516
1517
1518Alg_track *Alg_track::unserialize(void *buffer, long len)
1519{
1520    assert(len > 8);
1521    ser_read_buf.init_for_read(buffer, len);
1522    bool alg = ser_read_buf.get_char() == 'A' &&
1523               ser_read_buf.get_char() == 'L' &&
1524               ser_read_buf.get_char() == 'G';
1525    assert(alg);
1526    char c = ser_read_buf.get_char();
1527    if (c == 'S') {
1528        Alg_seq *seq = new Alg_seq;
1529        ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,S
1530        seq->unserialize_seq();
1531        return seq;
1532    } else {
1533        assert(c == 'T');
1534        Alg_track *track = new Alg_track;
1535        ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,T
1536        track->unserialize_track();
1537        return track;
1538    }
1539}
1540
1541
1542#pragma warning(disable: 4800) // long to bool performance warning
1543
1544/* Note: this Alg_seq must have a default initialized Alg_time_map.
1545 * It will be filled in with data from the ser_read_buf buffer.
1546 */
1547void Alg_seq::unserialize_seq()
1548{
1549    ser_read_buf.check_input_buffer(48);
1550    bool algs = (ser_read_buf.get_char() == 'A') &&
1551                (ser_read_buf.get_char() == 'L') &&
1552                (ser_read_buf.get_char() == 'G') &&
1553                (ser_read_buf.get_char() == 'S');
1554    assert(algs);
1555    long len = ser_read_buf.get_int32();
1556    assert(ser_read_buf.get_len() >= len);
1557    channel_offset_per_track = ser_read_buf.get_int32();
1558    units_are_seconds = ser_read_buf.get_int32() != 0;
1559    beat_dur = ser_read_buf.get_double();
1560    real_dur = ser_read_buf.get_double();
1561    // no need to allocate an Alg_time_map since it's done during initialization
1562    time_map->last_tempo = ser_read_buf.get_double();
1563    time_map->last_tempo_flag = ser_read_buf.get_int32() != 0;
1564    long beats = ser_read_buf.get_int32();
1565    ser_read_buf.check_input_buffer(beats * 16 + 4);
1566    int i;
1567    for (i = 0; i < beats; i++) {
1568        double time = ser_read_buf.get_double();
1569        double beat = ser_read_buf.get_double();
1570        time_map->insert_beat(time, beat);
1571        // printf("time_map: %g, %g\n", time, beat);
1572    }
1573    long time_sig_len = ser_read_buf.get_int32();
1574    ser_read_buf.get_pad();
1575    ser_read_buf.check_input_buffer(time_sig_len * 24 + 8);
1576    for (i = 0; i < time_sig_len; i++) {
1577        double beat = ser_read_buf.get_double();
1578        double num = ser_read_buf.get_double();
1579        double den = ser_read_buf.get_double();
1580        time_sig.insert(beat, num, den);
1581    }
1582    long tracks_num = ser_read_buf.get_int32();
1583    ser_read_buf.get_pad();
1584    add_track(tracks_num - 1); // create tracks_num tracks
1585    for (i = 0; i < tracks_num; i++) {
1586        track(i)->unserialize_track();
1587    }
1588    // assume seq started at beginning of buffer. len measures
1589    // bytes after 'ALGS' header, so add 4 bytes and compare to
1590    // current buffer position -- they should agree
1591    assert(ser_read_buf.get_posn() == len + 4);
1592}
1593
1594
1595void Alg_track::unserialize_track()
1596{
1597    ser_read_buf.check_input_buffer(32);
1598    bool algt = (ser_read_buf.get_char() == 'A') &&
1599                (ser_read_buf.get_char() == 'L') &&
1600                (ser_read_buf.get_char() == 'G') &&
1601                (ser_read_buf.get_char() == 'T');
1602    assert(algt);
1603    long offset = ser_read_buf.get_posn(); // stored length does not include 'ALGT'
1604    long bytes = ser_read_buf.get_int32();
1605    assert(bytes <= ser_read_buf.get_len() - offset);
1606    units_are_seconds = (bool) ser_read_buf.get_int32();
1607    beat_dur = ser_read_buf.get_double();
1608    real_dur = ser_read_buf.get_double();
1609    int event_count = ser_read_buf.get_int32();
1610    for (int i = 0; i < event_count; i++) {
1611        ser_read_buf.check_input_buffer(24);
1612        long selected = ser_read_buf.get_int32();
1613        char type = (char) ser_read_buf.get_int32();
1614        long key = ser_read_buf.get_int32();
1615        long channel = ser_read_buf.get_int32();
1616        double time = ser_read_buf.get_double();
1617        if (type == 'n') {
1618            ser_read_buf.check_input_buffer(20);
1619            float pitch = ser_read_buf.get_float();
1620            float loud = ser_read_buf.get_float();
1621            double dur = ser_read_buf.get_double();
1622            Alg_note *note =
1623                    create_note(time, channel, key, pitch, loud, dur);
1624            note->set_selected(selected != 0);
1625            long param_num = ser_read_buf.get_int32();
1626            int j;
1627            // this builds a list of parameters in the correct order
1628            // (although order shouldn't matter)
1629            Alg_parameters_ptr *list = &note->parameters;
1630            for (j = 0; j < param_num; j++) {
1631                *list = new Alg_parameters(NULL);
1632                unserialize_parameter(&((*list)->parm));
1633                list = &((*list)->next);
1634            }
1635            append(note);
1636        } else {
1637            assert(type == 'u');
1638            Alg_update *update = create_update(time, channel, key);
1639            update->set_selected(selected != 0);
1640            unserialize_parameter(&(update->parameter));
1641            append(update);
1642        }
1643        ser_read_buf.get_pad();
1644    }
1645    assert(offset + bytes == ser_read_buf.get_posn());
1646}
1647
1648
1649void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr)
1650{
1651    Alg_attribute attr = ser_read_buf.get_string();
1652    parm_ptr->attr = symbol_table.insert_string(attr);
1653    switch (parm_ptr->attr_type()) {
1654    case 'r':
1655        ser_read_buf.check_input_buffer(8);
1656        parm_ptr->r = ser_read_buf.get_double();
1657        break;
1658    case 's':
1659        parm_ptr->s = heapify(ser_read_buf.get_string());
1660        break;
1661    case 'i':
1662        ser_read_buf.check_input_buffer(4);
1663        parm_ptr->i = ser_read_buf.get_int32();
1664        break;
1665    case

Large files files are truncated, but you can click here to view the full file