allegrowr.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // allegrowr.cpp -- write sequence to an Allegro file (text)
  2. #include "assert.h"
  3. #include "stdlib.h"
  4. #include <iostream>
  5. #include <fstream>
  6. #include <iomanip>
  7. #include <errno.h>
  8. #include <string>
  9. #include "memory.h"
  10. using namespace std;
  11. #include "strparse.h"
  12. #include "allegro.h"
  13. // Note about precision: %g prints 6 significant digits. For 1ms precision,
  14. // the maximum magnitude is 999.999, i.e. 1000s < 17minutes. For anything
  15. // over 1000s, time in seconds will be printed with 10ms precision, which
  16. // is not good. Therefore, times and durations are printed as %.4d, which
  17. // gives 100us precision.
  18. // The following define allows you to change this decision:
  19. /* #define TIMFMT "%.4d" */
  20. #define TIMPREC 4
  21. #define TIMFMT fixed << setprecision(TIMPREC)
  22. #define GFMT resetiosflags(ios::floatfield) << setprecision(6)
  23. void parameter_print(ostream &file, Alg_parameter_ptr p)
  24. {
  25. file << " -" << p->attr_name() << ":";
  26. switch (p->attr_type()) {
  27. case 'a':
  28. file << "'" << alg_attr_name(p->a) << "'";
  29. break;
  30. case 'i':
  31. file << p->i;
  32. break;
  33. case 'l':
  34. file << (p->l ? "true" : "false");
  35. break;
  36. case 'r':
  37. file << p->r;
  38. break;
  39. case 's': {
  40. string str;
  41. string_escape(str, p->s, "\"");
  42. file << str;
  43. break;
  44. }
  45. }
  46. }
  47. Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n,
  48. Alg_events &events)
  49. // write #track <n> <trackname-or-sequencename>
  50. // if we write the name on the "#track" line, then we do *not* want
  51. // to write again as an update: "-seqnames:"Jordu", so if we do
  52. // find a name and write it, return a pointer to it so the track
  53. // writer knows what update (if any) to skip
  54. {
  55. Alg_event_ptr e = nullptr; // e is the result, default is NULL
  56. file << "#track " << n;
  57. const char *attr = symbol_table.insert_string(
  58. n == 0 ? "seqnames" : "tracknames");
  59. // search for name in events with timestamp of 0
  60. for (int i = 0; i < events.length(); i++) {
  61. Alg_event_ptr ue = events[i];
  62. if (ue->time > 0) break;
  63. if (ue->is_update()) {
  64. Alg_update_ptr u = (Alg_update_ptr) ue;
  65. if (u->parameter.attr == attr) {
  66. file << " " << u->parameter.s;
  67. e = ue; // return the update event we found
  68. break;
  69. }
  70. }
  71. }
  72. file << endl; // end of line containing #track [<name>]
  73. return e; // return parameter event with name if one was found
  74. }
  75. void Alg_seq::write(ostream &file, bool in_secs, double offset)
  76. {
  77. int i, j;
  78. if (in_secs) convert_to_seconds();
  79. else convert_to_beats();
  80. file << "#offset " << offset << endl;
  81. Alg_event_ptr update_to_skip = write_track_name(file, 0, track_list[0]);
  82. Alg_beats &beats = time_map->beats;
  83. for (i = 0; i < beats.len - 1; i++) {
  84. Alg_beat_ptr b = &(beats[i]);
  85. if (in_secs) {
  86. file << "T" << TIMFMT << b->time;
  87. } else {
  88. file << "TW" << TIMFMT << b->beat / 4;
  89. }
  90. double tempo = (beats[i + 1].beat - b->beat) /
  91. (beats[i + 1].time - beats[i].time);
  92. file << " -tempor:" << GFMT << tempo * 60 << "\n";
  93. }
  94. if (time_map->last_tempo_flag) { // we have final tempo:
  95. Alg_beat_ptr b = &(beats[beats.len - 1]);
  96. if (in_secs) {
  97. file << "T" << TIMFMT << b->time;
  98. } else {
  99. file << "TW" << TIMFMT << b->beat / 4;
  100. }
  101. file << " -tempor:" << GFMT << time_map->last_tempo * 60.0 << "\n";
  102. }
  103. // write the time signatures
  104. for (i = 0; i < time_sig.length(); i++) {
  105. Alg_time_sig &ts = time_sig[i];
  106. double time = ts.beat;
  107. if (in_secs) {
  108. file << "T" << TIMFMT << time << " V- -timesig_numr:" <<
  109. GFMT << ts.num << "\n";
  110. file << "T" << TIMFMT << time << " V- -timesig_denr:" <<
  111. GFMT << ts.den << "\n";
  112. } else {
  113. double wholes = ts.beat / 4;
  114. file << "TW" << TIMFMT << wholes << " V- -timesig_numr:" <<
  115. GFMT << ts.num << "\n";
  116. file << "TW" << TIMFMT << wholes << " V- -timesig_denr:" <<
  117. GFMT << ts.den << "\n";
  118. }
  119. }
  120. for (j = 0; j < track_list.length(); j++) {
  121. Alg_events &notes = track_list[j];
  122. if (j != 0) update_to_skip = write_track_name(file, j, notes);
  123. // now write the notes at beat positions
  124. for (i = 0; i < notes.length(); i++) {
  125. Alg_event_ptr e = notes[i];
  126. // if we already wrote this event as a track or sequence name,
  127. // do not write it again
  128. if (e == update_to_skip) continue;
  129. double start = e->time;
  130. if (in_secs) {
  131. file << "T" << TIMFMT << start;
  132. } else {
  133. file << "TW" << TIMFMT << start / 4;
  134. }
  135. // write the channel as Vn or V-
  136. if (e->chan == -1) file << " V-";
  137. else file << " V" << e->chan;
  138. // write the note or update data
  139. if (e->is_note()) {
  140. Alg_note_ptr n = (Alg_note_ptr) e;
  141. double dur = n->dur;
  142. file << " K" << n->get_identifier() <<
  143. " P" << GFMT << n->pitch;
  144. if (in_secs) {
  145. file << " U" << TIMFMT << dur;
  146. } else {
  147. file << " Q" << TIMFMT << dur;
  148. }
  149. file << " L" << GFMT << n->loud;
  150. Alg_parameters_ptr p = n->parameters;
  151. while (p) {
  152. parameter_print(file, &(p->parm));
  153. p = p->next;
  154. }
  155. } else { // an update
  156. assert(e->is_update());
  157. Alg_update_ptr u = (Alg_update_ptr) e;
  158. if (u->get_identifier() != -1) {
  159. file << " K" << u->get_identifier();
  160. }
  161. parameter_print(file, &(u->parameter));
  162. }
  163. file << "\n";
  164. }
  165. }
  166. }
  167. bool Alg_seq::write(const char *filename, double offset)
  168. {
  169. ofstream file(filename);
  170. if (file.fail()) return false;
  171. write(file, units_are_seconds, offset);
  172. file.close();
  173. return true;
  174. }