123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- // allegrowr.cpp -- write sequence to an Allegro file (text)
- #include "assert.h"
- #include "stdlib.h"
- #include <iostream>
- #include <fstream>
- #include <iomanip>
- #include <errno.h>
- #include <string>
- #include "memory.h"
- using namespace std;
- #include "strparse.h"
- #include "allegro.h"
- // Note about precision: %g prints 6 significant digits. For 1ms precision,
- // the maximum magnitude is 999.999, i.e. 1000s < 17minutes. For anything
- // over 1000s, time in seconds will be printed with 10ms precision, which
- // is not good. Therefore, times and durations are printed as %.4d, which
- // gives 100us precision.
- // The following define allows you to change this decision:
- /* #define TIMFMT "%.4d" */
- #define TIMPREC 4
- #define TIMFMT fixed << setprecision(TIMPREC)
- #define GFMT resetiosflags(ios::floatfield) << setprecision(6)
- void parameter_print(ostream &file, Alg_parameter_ptr p)
- {
- file << " -" << p->attr_name() << ":";
- switch (p->attr_type()) {
- case 'a':
- file << "'" << alg_attr_name(p->a) << "'";
- break;
- case 'i':
- file << p->i;
- break;
- case 'l':
- file << (p->l ? "true" : "false");
- break;
- case 'r':
- file << p->r;
- break;
- case 's': {
- string str;
- string_escape(str, p->s, "\"");
- file << str;
- break;
- }
- }
- }
- Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n,
- Alg_events &events)
- // write #track <n> <trackname-or-sequencename>
- // if we write the name on the "#track" line, then we do *not* want
- // to write again as an update: "-seqnames:"Jordu", so if we do
- // find a name and write it, return a pointer to it so the track
- // writer knows what update (if any) to skip
- {
- Alg_event_ptr e = nullptr; // e is the result, default is NULL
- file << "#track " << n;
- const char *attr = symbol_table.insert_string(
- n == 0 ? "seqnames" : "tracknames");
- // search for name in events with timestamp of 0
- for (int i = 0; i < events.length(); i++) {
- Alg_event_ptr ue = events[i];
- if (ue->time > 0) break;
- if (ue->is_update()) {
- Alg_update_ptr u = (Alg_update_ptr) ue;
- if (u->parameter.attr == attr) {
- file << " " << u->parameter.s;
- e = ue; // return the update event we found
- break;
- }
- }
- }
- file << endl; // end of line containing #track [<name>]
- return e; // return parameter event with name if one was found
- }
- void Alg_seq::write(ostream &file, bool in_secs, double offset)
- {
- int i, j;
- if (in_secs) convert_to_seconds();
- else convert_to_beats();
- file << "#offset " << offset << endl;
- Alg_event_ptr update_to_skip = write_track_name(file, 0, track_list[0]);
- Alg_beats &beats = time_map->beats;
- for (i = 0; i < beats.len - 1; i++) {
- Alg_beat_ptr b = &(beats[i]);
- if (in_secs) {
- file << "T" << TIMFMT << b->time;
- } else {
- file << "TW" << TIMFMT << b->beat / 4;
- }
- double tempo = (beats[i + 1].beat - b->beat) /
- (beats[i + 1].time - beats[i].time);
- file << " -tempor:" << GFMT << tempo * 60 << "\n";
- }
- if (time_map->last_tempo_flag) { // we have final tempo:
- Alg_beat_ptr b = &(beats[beats.len - 1]);
- if (in_secs) {
- file << "T" << TIMFMT << b->time;
- } else {
- file << "TW" << TIMFMT << b->beat / 4;
- }
- file << " -tempor:" << GFMT << time_map->last_tempo * 60.0 << "\n";
- }
- // write the time signatures
- for (i = 0; i < time_sig.length(); i++) {
- Alg_time_sig &ts = time_sig[i];
- double time = ts.beat;
- if (in_secs) {
- file << "T" << TIMFMT << time << " V- -timesig_numr:" <<
- GFMT << ts.num << "\n";
- file << "T" << TIMFMT << time << " V- -timesig_denr:" <<
- GFMT << ts.den << "\n";
- } else {
- double wholes = ts.beat / 4;
- file << "TW" << TIMFMT << wholes << " V- -timesig_numr:" <<
- GFMT << ts.num << "\n";
- file << "TW" << TIMFMT << wholes << " V- -timesig_denr:" <<
- GFMT << ts.den << "\n";
- }
- }
- for (j = 0; j < track_list.length(); j++) {
- Alg_events ¬es = track_list[j];
- if (j != 0) update_to_skip = write_track_name(file, j, notes);
- // now write the notes at beat positions
- for (i = 0; i < notes.length(); i++) {
- Alg_event_ptr e = notes[i];
- // if we already wrote this event as a track or sequence name,
- // do not write it again
- if (e == update_to_skip) continue;
- double start = e->time;
- if (in_secs) {
- file << "T" << TIMFMT << start;
- } else {
- file << "TW" << TIMFMT << start / 4;
- }
- // write the channel as Vn or V-
- if (e->chan == -1) file << " V-";
- else file << " V" << e->chan;
- // write the note or update data
- if (e->is_note()) {
- Alg_note_ptr n = (Alg_note_ptr) e;
- double dur = n->dur;
- file << " K" << n->get_identifier() <<
- " P" << GFMT << n->pitch;
- if (in_secs) {
- file << " U" << TIMFMT << dur;
- } else {
- file << " Q" << TIMFMT << dur;
- }
- file << " L" << GFMT << n->loud;
- Alg_parameters_ptr p = n->parameters;
- while (p) {
- parameter_print(file, &(p->parm));
- p = p->next;
- }
- } else { // an update
- assert(e->is_update());
- Alg_update_ptr u = (Alg_update_ptr) e;
- if (u->get_identifier() != -1) {
- file << " K" << u->get_identifier();
- }
- parameter_print(file, &(u->parameter));
- }
- file << "\n";
- }
- }
- }
- bool Alg_seq::write(const char *filename, double offset)
- {
- ofstream file(filename);
- if (file.fail()) return false;
- write(file, units_are_seconds, offset);
- file.close();
- return true;
- }
|