midifile.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. This file is part of QTau
  3. Copyright (C) 2013-2018 Tobias "Tomoko" Platen <tplaten@posteo.de>
  4. Copyright (C) 2013 digited <https://github.com/digited>
  5. Copyright (C) 2010-2013 HAL@ShurabaP <https://github.com/haruneko>
  6. QTau is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. SPDX-License-Identifier: GPL-3.0+
  17. */
  18. #include "midifile.h"
  19. #include <smf.h>
  20. #include <QJsonArray>
  21. #include <QJsonObject>
  22. #include "math.h"
  23. #include "ustjkeys.h"
  24. #include "tempomap.h"
  25. static void importTempomap(const smf_t *smf,TempoMap* tmap)
  26. {
  27. int i;
  28. smf_tempo_t *tempo;
  29. int current_measure = 0;
  30. int last_time = 0;
  31. int numerator = -1;
  32. int denominator = -1;
  33. int microseconds_per_quarter_note = 0;
  34. for (i = 0;; i++) {
  35. tempo = smf_get_tempo_by_number(smf, i);
  36. if (tempo == NULL)
  37. break;
  38. printf("------\n");
  39. //compute deltas
  40. int delta = tempo->time_pulses-last_time;
  41. last_time = tempo->time_pulses;
  42. int t = delta/tempo->numerator/smf->ppqn*tempo->denominator/4;
  43. //integrate
  44. current_measure += t;
  45. //output measure
  46. //output change events
  47. if(microseconds_per_quarter_note != tempo->microseconds_per_quarter_note)
  48. {
  49. float bpm = 60 * 1000 * 1000 / tempo->microseconds_per_quarter_note;
  50. printf("bpm %f\n",bpm);
  51. tmap->addTempo(current_measure,bpm);
  52. }
  53. if(numerator != tempo->numerator || denominator != tempo->denominator)
  54. {
  55. tmap->addTimeSignature(current_measure,tempo->numerator,tempo->denominator);
  56. }
  57. microseconds_per_quarter_note = tempo->microseconds_per_quarter_note;
  58. numerator = tempo->numerator;
  59. denominator = tempo->denominator;
  60. }
  61. }
  62. MidiFile::MidiFile() {}
  63. int MidiFile::saveMidi(const QJsonArray &ust, QString fileName) {
  64. // FIXME: ustJson(ust);
  65. smf_t *smf;
  66. smf_track_t *track;
  67. smf_event_t *event;
  68. smf = smf_new();
  69. track = smf_track_new();
  70. smf_add_track(smf, track);
  71. int bpm = 60;
  72. // int bpm = getTempo();
  73. track = smf_track_new();
  74. smf_add_track(smf, track);
  75. char temposig[6];
  76. int tempo = 60 * 1000 * 1000 / bpm;
  77. temposig[0] = 0xFF;
  78. temposig[1] = 0x51;
  79. temposig[2] = 0x03;
  80. temposig[3] = (tempo >> 16) & 0xFF;
  81. temposig[4] = (tempo >> 8) & 0xFF;
  82. temposig[5] = (tempo)&0xFF;
  83. event = smf_event_new_from_pointer(temposig, 6);
  84. smf_track_add_event_pulses(track, event, 0);
  85. char timesig[7];
  86. timesig[0] = 0xFF;
  87. timesig[1] = 0x58;
  88. timesig[2] = 0x04;
  89. // QJsonArray a = getTimeSignature();
  90. // int nn=a[0].toInt();
  91. // int dd=a[1].toInt();
  92. int nn = 4;
  93. int dd = 4;
  94. dd = log(dd) / log(2);
  95. timesig[3] = nn;
  96. timesig[4] = dd;
  97. timesig[5] = 0;
  98. timesig[6] = 8;
  99. event = smf_event_new_from_pointer(timesig, 7);
  100. smf_track_add_event_pulses(track, event, 0);
  101. for (int i = 0; i < ust.count(); ++i) {
  102. auto o = ust[i];
  103. double ts = 0.25;
  104. unsigned char midi[3];
  105. midi[0] = 0x90;
  106. midi[1] = o.toObject()[NOTE_KEY_NUMBER].toInt();
  107. midi[2] = 100; // FIXME do not hardcode
  108. int noteOffset = o.toObject()[NOTE_PULSE_OFFSET].toInt();
  109. int noteLength = o.toObject()[NOTE_PULSE_LENGTH].toInt();
  110. if (noteLength > 0) {
  111. event = smf_event_new_from_pointer(midi, 3);
  112. smf_track_add_event_pulses(track, event, ts * noteOffset);
  113. // http://www.ccarh.org/courses/253/handout/smf/
  114. QString lyric = o.toObject()[NOTE_LYRIC].toString();
  115. event = smf_event_new_textual(0x05, lyric.toUtf8());
  116. smf_track_add_event_pulses(track, event, ts * noteOffset);
  117. midi[0] = 0x80;
  118. event = smf_event_new_from_pointer(midi, 3);
  119. smf_track_add_event_pulses(track, event, ts * (noteOffset + noteLength));
  120. }
  121. }
  122. return smf_save(smf, fileName.toUtf8());
  123. }
  124. void MidiFile::loadMidi(QString fileName, QJsonArray &ustRef) {
  125. smf_t *smf;
  126. smf_event_t *event;
  127. smf = smf_load(fileName.toUtf8());
  128. if (smf == NULL) return;
  129. TempoMap tmap;
  130. importTempomap(smf,&tmap);
  131. QJsonObject setup;
  132. QJsonArray json;
  133. tmap.toJson(json);
  134. setup[TEMPOMAP] = json;
  135. setup[USER_AGENT] = "QTAU::MidiFile";
  136. ustRef.push_back(setup);
  137. smf_track_t *track = smf_get_track_by_number(smf, smf->number_of_tracks);
  138. int activeNote = -1;
  139. int notePos = 0;
  140. QString text;
  141. for (int i = 1; i < track->number_of_events + 1; i++) {
  142. event = smf_track_get_event_by_number(track, i);
  143. if (smf_event_is_textual(event)) {
  144. char *txt = smf_event_extract_text(event);
  145. text = txt;
  146. free(txt);
  147. } else {
  148. if (event->midi_buffer_length == 3) {
  149. unsigned char status = event->midi_buffer[0];
  150. unsigned char statusb = 0xF0 & status;
  151. unsigned char notenum = event->midi_buffer[1];
  152. unsigned char velocity = event->midi_buffer[2];
  153. float factor = 1;
  154. if (statusb == 0x80 || (statusb == 0x90 && velocity == 0)) {
  155. int length = (event->time_pulses - notePos);
  156. QJsonObject note;
  157. note[NOTE_PULSE_OFFSET] = notePos * factor;
  158. note[NOTE_PULSE_LENGTH] = length * factor;
  159. note[NOTE_KEY_NUMBER] = activeNote;
  160. note[NOTE_VELOCITY] = velocity;
  161. if (text.length() == 0) text = "a";
  162. note[NOTE_LYRIC] = text;
  163. ustRef.append(note);
  164. activeNote = -1;
  165. } else if (statusb == 0x90) {
  166. if (activeNote != -1) {
  167. smf_delete(smf);
  168. return;
  169. }
  170. activeNote = notenum;
  171. notePos = event->time_pulses;
  172. }
  173. }
  174. }
  175. }
  176. smf_delete(smf);
  177. }