MuseScore/mscore/exportmidi.cpp

285 lines
11 KiB
C++
Raw Normal View History

2012-05-26 14:49:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
2012-05-26 14:49:10 +02:00
//
// Copyright (C) 2002-2011 Werner Schweer
2012-05-26 14:49:10 +02:00
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
2012-05-26 14:49:10 +02:00
//=============================================================================
#include "exportmidi.h"
2013-04-15 10:38:16 +02:00
#include "midi/midifile.h"
#include "libmscore/score.h"
#include "libmscore/part.h"
#include "libmscore/staff.h"
#include "libmscore/tempo.h"
#include "synthesizer/event.h"
2013-04-15 10:38:16 +02:00
#include "libmscore/sig.h"
#include "libmscore/key.h"
#include "libmscore/text.h"
#include "libmscore/measure.h"
#include "libmscore/repeatlist.h"
2012-05-26 14:49:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// writeHeader
//---------------------------------------------------------
void ExportMidi::writeHeader()
{
2013-04-15 10:38:16 +02:00
if (mf.tracks().isEmpty())
2012-05-26 14:49:10 +02:00
return;
MidiTrack &track = mf.tracks().front();
#if 0 // TODO
2012-05-26 14:49:10 +02:00
MeasureBase* measure = cs->first();
foreach (const Element* e, *measure->el()) {
2013-01-11 18:10:18 +01:00
if (e->type() == Element::TEXT) {
2012-05-26 14:49:10 +02:00
const Text* text = (const Text*)(e);
QString str = text->getText();
int len = str.length() + 1;
unsigned char* data = new unsigned char[len];
strcpy((char*)(data), str.toLatin1().data());
Event ev(ME_META);
ev.setOntime(0);
ev.setData(data);
ev.setLen(len);
switch (text->subtype()) {
case TEXT_TITLE:
ev.setMetaType(META_TITLE);
track.insert(ev);
2012-05-26 14:49:10 +02:00
break;
case TEXT_SUBTITLE:
ev.setMetaType(META_SUBTITLE);
track.insert(ev);
2012-05-26 14:49:10 +02:00
break;
case TEXT_COMPOSER:
ev.setMetaType(META_COMPOSER);
track.insert(ev);
2012-05-26 14:49:10 +02:00
break;
case TEXT_TRANSLATOR:
ev.setMetaType(META_TRANSLATOR);
track.insert(ev);
2012-05-26 14:49:10 +02:00
break;
case TEXT_POET:
ev.setMetaType(META_POET);
track.insert(ev);
2012-05-26 14:49:10 +02:00
break;
default:
break;
}
}
}
#endif
2013-01-11 18:10:18 +01:00
2012-05-26 14:49:10 +02:00
//--------------------------------------------
// write time signature
//--------------------------------------------
TimeSigMap* sigmap = cs->sigmap();
foreach(const RepeatSegment* rs, *cs->repeatList()) {
int startTick = rs->tick;
int endTick = startTick + rs->len;
int tickOffset = rs->utick - rs->tick;
2013-03-13 18:15:36 +01:00
auto bs = sigmap->lower_bound(startTick);
auto es = sigmap->lower_bound(endTick);
2012-05-26 14:49:10 +02:00
2013-03-13 18:15:36 +01:00
for (auto is = bs; is != es; ++is) {
2012-05-26 14:49:10 +02:00
SigEvent se = is->second;
unsigned char* data = new unsigned char[4];
Fraction ts(se.timesig());
data[0] = ts.numerator();
int n;
switch (ts.denominator()) {
case 1: n = 0; break;
case 2: n = 1; break;
case 4: n = 2; break;
case 8: n = 3; break;
case 16: n = 4; break;
case 32: n = 5; break;
default:
n = 2;
qDebug("ExportMidi: unknown time signature %s\n",
qPrintable(ts.print()));
break;
}
data[1] = n;
data[2] = 24;
data[3] = 8;
2013-04-15 10:38:16 +02:00
MidiEvent ev;
ev.setType(ME_META);
2012-05-26 14:49:10 +02:00
ev.setMetaType(META_TIME_SIGNATURE);
2013-04-08 10:31:17 +02:00
ev.setEData(data);
2012-05-26 14:49:10 +02:00
ev.setLen(4);
track.insert(is->first + tickOffset, ev);
2012-05-26 14:49:10 +02:00
}
}
//---------------------------------------------------
// write key signatures
// assume every staff corresponds to a midi track
//---------------------------------------------------
2013-04-15 10:38:16 +02:00
int staffIdx = 0;
for (auto &track: mf.tracks()) {
2013-04-15 10:38:16 +02:00
Staff* staff = cs->staff(staffIdx);
2012-05-26 14:49:10 +02:00
KeyList* keymap = staff->keymap();
foreach(const RepeatSegment* rs, *cs->repeatList()) {
int startTick = rs->tick;
int endTick = startTick + rs->len;
int tickOffset = rs->utick - rs->tick;
2013-03-15 11:01:26 +01:00
auto sk = keymap->lower_bound(startTick);
auto ek = keymap->lower_bound(endTick);
2012-05-26 14:49:10 +02:00
bool keysigFound = false;
2013-03-15 11:01:26 +01:00
for (auto ik = sk; ik != ek; ++ik) {
2012-05-26 14:49:10 +02:00
keysigFound = true;
2013-04-15 10:38:16 +02:00
MidiEvent ev;
ev.setType(ME_META);
2012-05-26 14:49:10 +02:00
int key = ik->second.accidentalType(); // -7 -- +7
ev.setMetaType(META_KEY_SIGNATURE);
ev.setLen(2);
unsigned char* data = new unsigned char[2];
data[0] = key;
data[1] = 0; // major
2013-04-08 10:31:17 +02:00
ev.setEData(data);
track.insert(ik->first + tickOffset, ev);
2012-05-26 14:49:10 +02:00
}
if (!keysigFound) {
2013-04-15 10:38:16 +02:00
MidiEvent ev;
ev.setType(ME_META);
2012-05-26 14:49:10 +02:00
int key = 0;
ev.setMetaType(META_KEY_SIGNATURE);
ev.setLen(2);
unsigned char* data = new unsigned char[2];
data[0] = key;
data[1] = 0; // major
2013-04-08 10:31:17 +02:00
ev.setEData(data);
track.insert(0, ev);
2012-05-26 14:49:10 +02:00
}
}
2013-04-15 10:38:16 +02:00
++staffIdx;
2012-05-26 14:49:10 +02:00
}
//--------------------------------------------
// write tempo changes
//--------------------------------------------
TempoMap* tempomap = cs->tempomap();
2013-01-11 18:10:18 +01:00
int relTempo = tempomap->relTempo();
2012-05-26 14:49:10 +02:00
foreach(const RepeatSegment* rs, *cs->repeatList()) {
int startTick = rs->tick;
int endTick = startTick + rs->len;
int tickOffset = rs->utick - rs->tick;
2013-03-15 11:01:26 +01:00
auto se = tempomap->lower_bound(startTick);
auto ee = tempomap->lower_bound(endTick);
for (auto it = se; it != ee; ++it) {
2013-04-15 10:38:16 +02:00
MidiEvent ev;
ev.setType(ME_META);
2012-05-26 14:49:10 +02:00
//
// compute midi tempo: microseconds / quarter note
//
int tempo = lrint((1.0 / (it->second.tempo * relTempo)) * 1000000.0);
2012-05-26 14:49:10 +02:00
ev.setMetaType(META_TEMPO);
ev.setLen(3);
unsigned char* data = new unsigned char[3];
data[0] = tempo >> 16;
data[1] = tempo >> 8;
data[2] = tempo;
2013-04-08 10:31:17 +02:00
ev.setEData(data);
track.insert(it->first + tickOffset, ev);
2012-05-26 14:49:10 +02:00
}
}
}
//---------------------------------------------------------
// write
// export midi file
// return false on error
//---------------------------------------------------------
bool ExportMidi::write(const QString& name, bool midiExpandRepeats)
2012-05-26 14:49:10 +02:00
{
f.setFileName(name);
if (!f.open(QIODevice::WriteOnly))
return false;
mf.setDivision(MScore::division);
mf.setFormat(1);
QList<MidiTrack>& tracks = mf.tracks();
2013-04-15 10:38:16 +02:00
for (int i = 0; i < cs->nstaves(); ++i)
tracks.append(MidiTrack());
2012-05-26 14:49:10 +02:00
cs->updateRepeatList(midiExpandRepeats);
2012-05-26 14:49:10 +02:00
writeHeader();
2013-04-15 10:38:16 +02:00
int staffIdx = 0;
for (auto &track: tracks) {
2013-04-15 10:38:16 +02:00
Staff* staff = cs->staff(staffIdx);
2012-05-26 14:49:10 +02:00
Part* part = staff->part();
int channel = part->midiChannel();
track.setOutPort(0);
track.setOutChannel(channel);
2012-05-26 14:49:10 +02:00
if (staff->isTop()) {
// set pitch bend sensitivity to 12 semitones:
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_LRPN, 0));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_HRPN, 0));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_HDATA, 12));
2012-05-26 14:49:10 +02:00
// reset fine tuning
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_LRPN, 1));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_HRPN, 0));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_HDATA, 64));
2012-05-26 14:49:10 +02:00
// deactivate rpn
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_LRPN, 127));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_HRPN, 127));
2012-05-26 14:49:10 +02:00
if (part->midiProgram() != -1)
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_PROGRAM, part->midiProgram()));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_VOLUME, part->volume()));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_PANPOT, part->pan()));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_REVERB_SEND, part->reverb()));
track.insert(0, MidiEvent(ME_CONTROLLER, channel, CTRL_CHORUS_SEND, part->chorus()));
2012-05-26 14:49:10 +02:00
}
EventMap events;
2013-04-08 17:48:37 +02:00
cs->renderStaff(&events, staff);
2012-05-26 14:49:10 +02:00
for (auto i = events.begin(); i != events.end(); ++i) {
NPlayEvent event(i->second);
2012-05-26 14:49:10 +02:00
if (event.channel() != channel)
continue;
if (event.type() == ME_NOTEON) {
track.insert(i->first, MidiEvent(ME_NOTEON, event.channel(),
2013-04-15 10:38:16 +02:00
event.pitch(), event.velo()));
2012-05-26 14:49:10 +02:00
}
else if (event.type() == ME_CONTROLLER) {
track.insert(i->first, MidiEvent(ME_CONTROLLER, event.channel(),
2013-04-15 10:38:16 +02:00
event.controller(), event.value()));
2012-05-26 14:49:10 +02:00
}
else {
qDebug("writeMidi: unknown midi event 0x%02x\n", event.type());
}
}
2013-04-15 10:38:16 +02:00
++staffIdx;
2012-05-26 14:49:10 +02:00
}
return !mf.write(&f);
}
2013-05-13 18:49:17 +02:00
}
2013-04-15 10:38:16 +02:00