mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-30 15:49:41 +02:00
182 lines
5.9 KiB
C++
182 lines
5.9 KiB
C++
// 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 = 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++) {
|
|
e = events[i];
|
|
if (e->time > 0) break;
|
|
if (e->is_update()) {
|
|
Alg_update_ptr u = (Alg_update_ptr) e;
|
|
if (u->parameter.attr == attr) {
|
|
file << " " << u->parameter.s;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
file << endl;
|
|
return e;
|
|
}
|
|
|
|
|
|
void Alg_seq::write(ostream &file, bool in_secs)
|
|
{
|
|
int i, j;
|
|
if (in_secs) convert_to_seconds();
|
|
else convert_to_beats();
|
|
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)
|
|
{
|
|
ofstream file(filename);
|
|
if (file.fail()) return false;
|
|
write(file, units_are_seconds);
|
|
file.close();
|
|
return true;
|
|
}
|