123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- /*
- This file is part of QTau
- Copyright (C) 2013-2018 Tobias "Tomoko" Platen <tplaten@posteo.de>
- Copyright (C) 2013 digited <https://github.com/digited>
- Copyright (C) 2010-2013 HAL@ShurabaP <https://github.com/haruneko>
- QTau is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: GPL-3.0+
- */
- #include "midifile.h"
- #include <smf.h>
- #include <QJsonArray>
- #include <QJsonObject>
- #include "math.h"
- #include "ustjkeys.h"
- #include "tempomap.h"
- static void importTempomap(const smf_t *smf,TempoMap* tmap)
- {
- int i;
- smf_tempo_t *tempo;
- int current_measure = 0;
- int last_time = 0;
- int numerator = -1;
- int denominator = -1;
- int microseconds_per_quarter_note = 0;
- for (i = 0;; i++) {
- tempo = smf_get_tempo_by_number(smf, i);
- if (tempo == NULL)
- break;
- printf("------\n");
- //compute deltas
- int delta = tempo->time_pulses-last_time;
- last_time = tempo->time_pulses;
- int t = delta/tempo->numerator/smf->ppqn*tempo->denominator/4;
- //integrate
- current_measure += t;
- //output measure
- //output change events
- if(microseconds_per_quarter_note != tempo->microseconds_per_quarter_note)
- {
- float bpm = 60 * 1000 * 1000 / tempo->microseconds_per_quarter_note;
- printf("bpm %f\n",bpm);
- tmap->addTempo(current_measure,bpm);
- }
- if(numerator != tempo->numerator || denominator != tempo->denominator)
- {
- tmap->addTimeSignature(current_measure,tempo->numerator,tempo->denominator);
- }
- microseconds_per_quarter_note = tempo->microseconds_per_quarter_note;
- numerator = tempo->numerator;
- denominator = tempo->denominator;
- }
- }
- MidiFile::MidiFile() {}
- int MidiFile::saveMidi(const QJsonArray &ust, QString fileName) {
- // FIXME: ustJson(ust);
- smf_t *smf;
- smf_track_t *track;
- smf_event_t *event;
- smf = smf_new();
- track = smf_track_new();
- smf_add_track(smf, track);
- int bpm = 60;
- // int bpm = getTempo();
- track = smf_track_new();
- smf_add_track(smf, track);
- char temposig[6];
- int tempo = 60 * 1000 * 1000 / bpm;
- temposig[0] = 0xFF;
- temposig[1] = 0x51;
- temposig[2] = 0x03;
- temposig[3] = (tempo >> 16) & 0xFF;
- temposig[4] = (tempo >> 8) & 0xFF;
- temposig[5] = (tempo)&0xFF;
- event = smf_event_new_from_pointer(temposig, 6);
- smf_track_add_event_pulses(track, event, 0);
- char timesig[7];
- timesig[0] = 0xFF;
- timesig[1] = 0x58;
- timesig[2] = 0x04;
- // QJsonArray a = getTimeSignature();
- // int nn=a[0].toInt();
- // int dd=a[1].toInt();
- int nn = 4;
- int dd = 4;
- dd = log(dd) / log(2);
- timesig[3] = nn;
- timesig[4] = dd;
- timesig[5] = 0;
- timesig[6] = 8;
- event = smf_event_new_from_pointer(timesig, 7);
- smf_track_add_event_pulses(track, event, 0);
- for (int i = 0; i < ust.count(); ++i) {
- auto o = ust[i];
- double ts = 0.25;
- unsigned char midi[3];
- midi[0] = 0x90;
- midi[1] = o.toObject()[NOTE_KEY_NUMBER].toInt();
- midi[2] = 100; // FIXME do not hardcode
- int noteOffset = o.toObject()[NOTE_PULSE_OFFSET].toInt();
- int noteLength = o.toObject()[NOTE_PULSE_LENGTH].toInt();
- if (noteLength > 0) {
- event = smf_event_new_from_pointer(midi, 3);
- smf_track_add_event_pulses(track, event, ts * noteOffset);
- // http://www.ccarh.org/courses/253/handout/smf/
- QString lyric = o.toObject()[NOTE_LYRIC].toString();
- event = smf_event_new_textual(0x05, lyric.toUtf8());
- smf_track_add_event_pulses(track, event, ts * noteOffset);
- midi[0] = 0x80;
- event = smf_event_new_from_pointer(midi, 3);
- smf_track_add_event_pulses(track, event, ts * (noteOffset + noteLength));
- }
- }
- return smf_save(smf, fileName.toUtf8());
- }
- void MidiFile::loadMidi(QString fileName, QJsonArray &ustRef) {
- smf_t *smf;
- smf_event_t *event;
- smf = smf_load(fileName.toUtf8());
- if (smf == NULL) return;
- TempoMap tmap;
- importTempomap(smf,&tmap);
- QJsonObject setup;
- QJsonArray json;
- tmap.toJson(json);
- setup[TEMPOMAP] = json;
- setup[USER_AGENT] = "QTAU::MidiFile";
- ustRef.push_back(setup);
- smf_track_t *track = smf_get_track_by_number(smf, smf->number_of_tracks);
- int activeNote = -1;
- int notePos = 0;
- QString text;
- for (int i = 1; i < track->number_of_events + 1; i++) {
- event = smf_track_get_event_by_number(track, i);
- if (smf_event_is_textual(event)) {
- char *txt = smf_event_extract_text(event);
- text = txt;
- free(txt);
- } else {
- if (event->midi_buffer_length == 3) {
- unsigned char status = event->midi_buffer[0];
- unsigned char statusb = 0xF0 & status;
- unsigned char notenum = event->midi_buffer[1];
- unsigned char velocity = event->midi_buffer[2];
- float factor = 1;
- if (statusb == 0x80 || (statusb == 0x90 && velocity == 0)) {
- int length = (event->time_pulses - notePos);
- QJsonObject note;
- note[NOTE_PULSE_OFFSET] = notePos * factor;
- note[NOTE_PULSE_LENGTH] = length * factor;
- note[NOTE_KEY_NUMBER] = activeNote;
- note[NOTE_VELOCITY] = velocity;
- if (text.length() == 0) text = "a";
- note[NOTE_LYRIC] = text;
- ustRef.append(note);
- activeNote = -1;
- } else if (statusb == 0x90) {
- if (activeNote != -1) {
- smf_delete(smf);
- return;
- }
- activeNote = notenum;
- notePos = event->time_pulses;
- }
- }
- }
- }
- smf_delete(smf);
- }
|