2012-05-26 14:49:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
2013-04-15 10:38:16 +02:00
|
|
|
// Music Composition & Notation
|
2012-05-26 14:49:10 +02:00
|
|
|
//
|
2013-04-15 10:38:16 +02:00
|
|
|
// Copyright (C) 2002-2013 Werner Schweer
|
2012-05-26 14:49:10 +02:00
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
2013-04-15 10:38:16 +02:00
|
|
|
// 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
|
|
|
//=============================================================================
|
|
|
|
|
2013-04-15 10:38:16 +02:00
|
|
|
#include "midi/midifile.h"
|
|
|
|
#include "midi/midiinstrument.h"
|
2013-08-20 18:05:49 +02:00
|
|
|
#include "preferences.h"
|
2012-05-26 14:49:10 +02:00
|
|
|
#include "libmscore/score.h"
|
|
|
|
#include "libmscore/key.h"
|
|
|
|
#include "libmscore/clef.h"
|
|
|
|
#include "libmscore/sig.h"
|
|
|
|
#include "libmscore/tempo.h"
|
|
|
|
#include "libmscore/note.h"
|
|
|
|
#include "libmscore/chord.h"
|
|
|
|
#include "libmscore/rest.h"
|
|
|
|
#include "libmscore/segment.h"
|
|
|
|
#include "libmscore/utils.h"
|
|
|
|
#include "libmscore/text.h"
|
|
|
|
#include "libmscore/slur.h"
|
2013-08-22 12:18:14 +02:00
|
|
|
#include "libmscore/tie.h"
|
2012-05-26 14:49:10 +02:00
|
|
|
#include "libmscore/staff.h"
|
|
|
|
#include "libmscore/measure.h"
|
|
|
|
#include "libmscore/style.h"
|
|
|
|
#include "libmscore/part.h"
|
|
|
|
#include "libmscore/timesig.h"
|
|
|
|
#include "libmscore/barline.h"
|
|
|
|
#include "libmscore/pedal.h"
|
|
|
|
#include "libmscore/ottava.h"
|
|
|
|
#include "libmscore/lyrics.h"
|
|
|
|
#include "libmscore/bracket.h"
|
|
|
|
#include "libmscore/drumset.h"
|
|
|
|
#include "libmscore/box.h"
|
|
|
|
#include "libmscore/keysig.h"
|
|
|
|
#include "libmscore/pitchspelling.h"
|
2013-05-26 01:19:09 +02:00
|
|
|
#include "importmidi_meter.h"
|
2013-06-05 21:07:21 +02:00
|
|
|
#include "importmidi_chord.h"
|
|
|
|
#include "importmidi_quant.h"
|
2013-07-04 12:28:51 +02:00
|
|
|
#include "importmidi_tuplet.h"
|
2013-06-27 16:14:39 +02:00
|
|
|
#include "libmscore/tuplet.h"
|
2014-03-12 19:44:13 +01:00
|
|
|
#include "libmscore/articulation.h"
|
2013-08-01 23:52:09 +02:00
|
|
|
#include "importmidi_swing.h"
|
2013-08-17 00:57:42 +02:00
|
|
|
#include "importmidi_fraction.h"
|
2013-08-20 18:05:49 +02:00
|
|
|
#include "importmidi_drum.h"
|
|
|
|
#include "importmidi_inner.h"
|
2013-08-23 20:53:42 +02:00
|
|
|
#include "importmidi_clef.h"
|
2013-08-24 00:11:07 +02:00
|
|
|
#include "importmidi_lrhand.h"
|
2013-09-07 20:57:23 +02:00
|
|
|
#include "importmidi_lyrics.h"
|
2014-03-01 18:00:30 +01:00
|
|
|
#include "importmidi_tie.h"
|
2013-08-23 13:47:36 +02:00
|
|
|
|
2013-09-18 16:31:00 +02:00
|
|
|
#include <set>
|
|
|
|
|
2013-04-28 21:29:12 +02:00
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
namespace Ms {
|
|
|
|
|
2013-05-03 19:44:23 +02:00
|
|
|
extern Preferences preferences;
|
2013-07-27 00:01:30 +02:00
|
|
|
extern void updateNoteLines(Segment*, int track);
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
|
2013-08-19 12:57:46 +02:00
|
|
|
void cleanUpMidiEvents(std::multimap<int, MTrack> &tracks)
|
|
|
|
{
|
|
|
|
auto &opers = preferences.midiImportOperations;
|
|
|
|
|
|
|
|
for (auto &track: tracks) {
|
|
|
|
MTrack &mtrack = track.second;
|
|
|
|
opers.setCurrentTrack(mtrack.indexOfOperation);
|
|
|
|
const auto raster = Quantize::fixedQuantRaster();
|
|
|
|
const bool reduce = opers.currentTrackOperations().quantize.reduceToShorterNotesInBar;
|
|
|
|
|
|
|
|
for (auto chordIt = mtrack.chords.begin(); chordIt != mtrack.chords.end(); ) {
|
|
|
|
MidiChord &ch = chordIt->second;
|
|
|
|
for (auto noteIt = ch.notes.begin(); noteIt != ch.notes.end(); ) {
|
2014-04-01 06:35:21 +02:00
|
|
|
if ((noteIt->offTime - chordIt->first < MChord::minAllowedDuration())
|
|
|
|
|| (!reduce && noteIt->offTime - chordIt->first < raster / 2)) {
|
2013-08-19 12:57:46 +02:00
|
|
|
noteIt = ch.notes.erase(noteIt);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++noteIt;
|
|
|
|
}
|
|
|
|
if (ch.notes.isEmpty()) {
|
|
|
|
chordIt = mtrack.chords.erase(chordIt);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++chordIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-30 12:58:21 +02:00
|
|
|
|
|
|
|
#ifdef QT_DEBUG
|
|
|
|
|
|
|
|
bool doNotesOverlap(const MTrack &track)
|
|
|
|
{
|
|
|
|
const auto &chords = track.chords;
|
|
|
|
for (auto i1 = chords.begin(); i1 != chords.end(); ++i1) {
|
|
|
|
const auto &chord1 = i1->second;
|
|
|
|
for (const auto ¬e1: chord1.notes) {
|
|
|
|
for (auto i2 = std::next(i1); i2 != chords.end(); ++i2) {
|
2014-04-01 06:35:21 +02:00
|
|
|
if (i2->first >= note1.offTime)
|
2014-03-30 12:58:21 +02:00
|
|
|
break;
|
|
|
|
const auto &chord2 = i2->second;
|
|
|
|
if (chord1.voice != chord2.voice)
|
|
|
|
continue;
|
|
|
|
for (const auto ¬e2: chord2.notes) {
|
|
|
|
if (note2.pitch != note1.pitch)
|
|
|
|
continue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool doNotesOverlap(const std::multimap<int, MTrack> &tracks)
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
for (const auto &track: tracks)
|
|
|
|
result = doNotesOverlap(track.second);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool noTooShortNotes(const std::multimap<int, MTrack> &tracks)
|
|
|
|
{
|
|
|
|
for (const auto &track: tracks) {
|
|
|
|
const auto &chords = track.second.chords;
|
|
|
|
for (const auto &chord: chords) {
|
|
|
|
for (const auto ¬e: chord.second.notes) {
|
2014-04-01 06:35:21 +02:00
|
|
|
if (note.offTime - chord.first < MChord::minAllowedDuration())
|
2014-03-30 12:58:21 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
void quantizeAllTracks(std::multimap<int, MTrack> &tracks,
|
|
|
|
TimeSigMap *sigmap,
|
2013-08-17 00:57:42 +02:00
|
|
|
const ReducedFraction &lastTick)
|
2013-05-24 13:47:16 +02:00
|
|
|
{
|
2013-06-05 21:07:21 +02:00
|
|
|
auto &opers = preferences.midiImportOperations;
|
2013-08-20 13:31:10 +02:00
|
|
|
for (auto &track: tracks) {
|
|
|
|
MTrack &mtrack = track.second;
|
|
|
|
// pass current track index through MidiImportOperations
|
|
|
|
// for further usage
|
|
|
|
opers.setCurrentTrack(mtrack.indexOfOperation);
|
2013-10-19 23:05:24 +02:00
|
|
|
opers.adaptForPercussion(mtrack.indexOfOperation, mtrack.mtrack->drumTrack());
|
2013-08-20 15:24:11 +02:00
|
|
|
mtrack.tuplets = MidiTuplet::findAllTuplets(mtrack.chords, sigmap, lastTick);
|
2014-03-30 12:58:21 +02:00
|
|
|
|
|
|
|
Q_ASSERT_X(!doNotesOverlap(track.second),
|
|
|
|
"quantizeAllTracks",
|
|
|
|
"There are overlapping notes of the same voice that is incorrect");
|
|
|
|
|
2013-08-20 15:24:11 +02:00
|
|
|
Quantize::quantizeChords(mtrack.chords, mtrack.tuplets, sigmap);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// processMeta
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-04-15 10:38:16 +02:00
|
|
|
void MTrack::processMeta(int tick, const MidiEvent& mm)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
2013-04-28 21:29:12 +02:00
|
|
|
if (!staff) {
|
|
|
|
qDebug("processMeta: no staff");
|
|
|
|
return;
|
|
|
|
}
|
2013-04-08 10:31:17 +02:00
|
|
|
const uchar* data = (uchar*)mm.edata();
|
2013-08-22 23:26:31 +02:00
|
|
|
Score* cs = staff->score();
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
switch (mm.metaType()) {
|
|
|
|
case META_TEXT:
|
2013-10-19 16:34:30 +02:00
|
|
|
case META_LYRIC:
|
|
|
|
break; // lyric and text are added in importmidi_lyrics.cpp
|
2012-05-26 14:49:10 +02:00
|
|
|
case META_TRACK_NAME:
|
2013-09-26 17:12:44 +02:00
|
|
|
{
|
2014-01-08 18:00:25 +01:00
|
|
|
const std::string text = MidiCharset::fromUchar(data);
|
2013-08-11 21:54:36 +02:00
|
|
|
if (name.isEmpty())
|
2013-09-26 17:12:44 +02:00
|
|
|
name = MidiCharset::convertToCharset(text);
|
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
case META_TEMPO:
|
|
|
|
{
|
2013-08-18 17:22:10 +02:00
|
|
|
const unsigned tempo = data[2] + (data[1] << 8) + (data[0] <<16);
|
|
|
|
const double t = 1000000.0 / double(tempo);
|
2012-05-26 14:49:10 +02:00
|
|
|
cs->setTempo(tick, t);
|
2013-08-22 11:29:20 +02:00
|
|
|
// TODO: create TempoText
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case META_KEY_SIGNATURE:
|
2013-04-28 21:29:12 +02:00
|
|
|
{
|
2013-08-18 17:22:10 +02:00
|
|
|
const int key = ((const char*)data)[0];
|
2013-04-28 21:29:12 +02:00
|
|
|
if (key < -7 || key > 7) {
|
|
|
|
qDebug("ImportMidi: illegal key %d", key);
|
|
|
|
break;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-04-28 21:29:12 +02:00
|
|
|
KeySigEvent ks;
|
|
|
|
ks.setAccidentalType(key);
|
|
|
|
(*staff->keymap())[tick] = ks;
|
|
|
|
hasKey = true;
|
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
case META_COMPOSER: // mscore extension
|
|
|
|
case META_POET:
|
|
|
|
case META_TRANSLATOR:
|
|
|
|
case META_SUBTITLE:
|
|
|
|
case META_TITLE:
|
|
|
|
{
|
|
|
|
Text* text = new Text(cs);
|
|
|
|
switch(mm.metaType()) {
|
|
|
|
case META_COMPOSER:
|
2014-05-30 10:13:29 +02:00
|
|
|
text->setTextStyleType(TextStyleType::COMPOSER);
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
case META_TRANSLATOR:
|
2014-05-30 10:13:29 +02:00
|
|
|
text->setTextStyleType(TextStyleType::TRANSLATOR);
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
case META_POET:
|
2014-05-30 10:13:29 +02:00
|
|
|
text->setTextStyleType(TextStyleType::POET);
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
case META_SUBTITLE:
|
2014-05-30 10:13:29 +02:00
|
|
|
text->setTextStyleType(TextStyleType::SUBTITLE);
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
case META_TITLE:
|
2014-05-30 10:13:29 +02:00
|
|
|
text->setTextStyleType(TextStyleType::TITLE);
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-04-08 10:31:17 +02:00
|
|
|
text->setText((const char*)(mm.edata()));
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
MeasureBase* measure = cs->first();
|
2014-05-22 19:49:51 +02:00
|
|
|
if (measure->type() != Element::ElementType::VBOX) {
|
2012-05-26 14:49:10 +02:00
|
|
|
measure = new VBox(cs);
|
|
|
|
measure->setTick(0);
|
|
|
|
measure->setNext(cs->first());
|
|
|
|
cs->add(measure);
|
|
|
|
}
|
|
|
|
measure->add(text);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case META_COPYRIGHT:
|
2014-04-04 15:46:24 +02:00
|
|
|
cs->setMetaTag("copyright", QString((const char*)(mm.edata())));
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
case META_TIME_SIGNATURE:
|
|
|
|
cs->sigmap()->add(tick, Fraction(data[0], 1 << data[1]));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (MScore::debugMode)
|
|
|
|
qDebug("unknown meta type 0x%02x", mm.metaType());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-17 00:57:42 +02:00
|
|
|
QList<std::pair<ReducedFraction, TDuration> >
|
2013-07-03 23:54:54 +02:00
|
|
|
MTrack::toDurationList(const Measure *measure,
|
|
|
|
int voice,
|
2013-08-17 00:57:42 +02:00
|
|
|
const ReducedFraction &startTick,
|
|
|
|
const ReducedFraction &len,
|
2013-07-03 23:54:54 +02:00
|
|
|
Meter::DurationType durationType)
|
2013-05-17 20:11:36 +02:00
|
|
|
{
|
2013-08-18 17:22:10 +02:00
|
|
|
const bool useDots = preferences.midiImportOperations.currentTrackOperations().useDots;
|
2013-06-27 16:14:39 +02:00
|
|
|
// find tuplets over which duration is go
|
2013-08-20 14:40:57 +02:00
|
|
|
auto barTick = ReducedFraction::fromTicks(measure->tick());
|
|
|
|
auto tupletsData = MidiTuplet::findTupletsInBarForDuration(voice, barTick, startTick,
|
|
|
|
len, tuplets);
|
|
|
|
struct {
|
|
|
|
bool operator()(const MidiTuplet::TupletData &d1,
|
|
|
|
const MidiTuplet::TupletData &d2)
|
|
|
|
{
|
|
|
|
return (d1.len > d2.len);
|
|
|
|
}
|
|
|
|
} comparator;
|
|
|
|
// sort by tuplet length in desc order
|
|
|
|
sort(tupletsData.begin(), tupletsData.end(), comparator);
|
|
|
|
|
|
|
|
const ReducedFraction startTickInBar = startTick - barTick;
|
2013-08-18 17:22:10 +02:00
|
|
|
const ReducedFraction endTickInBar = startTickInBar + len;
|
2013-07-03 23:54:54 +02:00
|
|
|
return Meter::toDurationList(startTickInBar, endTickInBar,
|
2013-08-20 14:40:57 +02:00
|
|
|
ReducedFraction(measure->timesig()), tupletsData,
|
2013-07-03 23:54:54 +02:00
|
|
|
durationType, useDots);
|
2013-06-27 16:14:39 +02:00
|
|
|
}
|
|
|
|
|
2013-08-19 23:26:05 +02:00
|
|
|
ReducedFraction splitDurationOnBarBoundary(const ReducedFraction &len,
|
|
|
|
const ReducedFraction &onTime,
|
2013-08-18 17:22:10 +02:00
|
|
|
const Measure* measure)
|
2013-06-27 16:14:39 +02:00
|
|
|
{
|
2013-08-18 17:22:10 +02:00
|
|
|
const ReducedFraction barLimit = ReducedFraction::fromTicks(measure->tick() + measure->ticks());
|
2013-07-08 20:17:13 +02:00
|
|
|
if (onTime + len > barLimit)
|
|
|
|
return barLimit - onTime;
|
2013-06-27 16:14:39 +02:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fill the gap between successive chords with rests
|
|
|
|
|
2013-08-18 12:21:35 +02:00
|
|
|
void MTrack::fillGapWithRests(Score* score,
|
|
|
|
int voice,
|
2013-08-17 00:57:42 +02:00
|
|
|
const ReducedFraction &startChordTickFrac,
|
2013-08-18 12:21:35 +02:00
|
|
|
const ReducedFraction &restLength,
|
|
|
|
int track)
|
2013-06-27 16:14:39 +02:00
|
|
|
{
|
2013-08-17 00:57:42 +02:00
|
|
|
ReducedFraction startChordTick = startChordTickFrac;
|
|
|
|
ReducedFraction restLen = restLength;
|
2013-08-18 12:21:35 +02:00
|
|
|
while (restLen > ReducedFraction(0, 1)) {
|
2013-08-17 00:57:42 +02:00
|
|
|
ReducedFraction len = restLen;
|
2013-08-22 23:26:31 +02:00
|
|
|
Measure* measure = score->tick2measure(startChordTick.ticks());
|
2013-08-17 00:57:42 +02:00
|
|
|
if (startChordTick >= ReducedFraction::fromTicks(measure->tick() + measure->ticks())) {
|
2013-07-08 20:17:13 +02:00
|
|
|
qDebug("tick2measure: %d end of score?", startChordTick.ticks());
|
2013-06-27 16:14:39 +02:00
|
|
|
startChordTick += restLen;
|
2013-08-23 23:56:04 +02:00
|
|
|
restLen = ReducedFraction(0, 1);
|
2013-05-17 20:11:36 +02:00
|
|
|
break;
|
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
len = splitDurationOnBarBoundary(len, startChordTick, measure);
|
|
|
|
|
2013-08-17 00:57:42 +02:00
|
|
|
if (len >= ReducedFraction::fromTicks(measure->ticks())) {
|
2013-06-27 16:14:39 +02:00
|
|
|
// rest to the whole measure
|
2013-08-17 00:57:42 +02:00
|
|
|
len = ReducedFraction::fromTicks(measure->ticks());
|
2013-07-03 23:54:54 +02:00
|
|
|
if (voice == 0) {
|
2014-05-21 15:43:19 +02:00
|
|
|
TDuration duration(TDuration::DurationType::V_MEASURE);
|
2013-07-03 23:54:54 +02:00
|
|
|
Rest* rest = new Rest(score, duration);
|
|
|
|
rest->setDuration(measure->len());
|
|
|
|
rest->setTrack(track);
|
2013-07-08 20:17:13 +02:00
|
|
|
Segment* s = measure->getSegment(rest, startChordTick.ticks());
|
2013-07-03 23:54:54 +02:00
|
|
|
s->add(rest);
|
2013-07-02 23:37:07 +02:00
|
|
|
}
|
2013-05-17 20:11:36 +02:00
|
|
|
restLen -= len;
|
2013-06-27 16:14:39 +02:00
|
|
|
startChordTick += len;
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
|
|
|
else {
|
2013-08-18 17:22:10 +02:00
|
|
|
const auto dl = toDurationList(measure, voice, startChordTick, len,
|
|
|
|
Meter::DurationType::REST);
|
2013-05-17 20:11:36 +02:00
|
|
|
if (dl.isEmpty()) {
|
2013-07-08 20:17:13 +02:00
|
|
|
qDebug("cannot create duration list for len %d", len.ticks());
|
2013-08-23 23:56:04 +02:00
|
|
|
restLen = ReducedFraction(0, 1); // fake
|
2013-05-17 20:11:36 +02:00
|
|
|
break;
|
|
|
|
}
|
2013-07-03 23:54:54 +02:00
|
|
|
for (const auto &durationPair: dl) {
|
2013-07-08 20:17:13 +02:00
|
|
|
const TDuration &duration = durationPair.second;
|
2013-08-17 00:57:42 +02:00
|
|
|
const ReducedFraction &tupletRatio = durationPair.first;
|
|
|
|
len = ReducedFraction(duration.fraction()) / tupletRatio;
|
2013-08-22 23:26:31 +02:00
|
|
|
Rest* rest = new Rest(score, duration);
|
2013-06-27 16:14:39 +02:00
|
|
|
rest->setDuration(duration.fraction());
|
2013-05-17 20:11:36 +02:00
|
|
|
rest->setTrack(track);
|
2014-06-01 20:24:29 +02:00
|
|
|
Segment* s = measure->getSegment(SegmentType::ChordRest,
|
2013-08-22 23:26:31 +02:00
|
|
|
startChordTick.ticks());
|
2013-05-17 20:11:36 +02:00
|
|
|
s->add(rest);
|
2013-08-20 18:05:49 +02:00
|
|
|
MidiTuplet::addElementToTuplet(voice, startChordTick, len, rest, tuplets);
|
2013-07-05 15:09:23 +02:00
|
|
|
restLen -= len;
|
|
|
|
startChordTick += len;
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
|
|
|
}
|
2013-07-02 23:37:07 +02:00
|
|
|
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-27 16:14:39 +02:00
|
|
|
void setMusicNotesFromMidi(Score *score,
|
|
|
|
const QList<MidiNote> &midiNotes,
|
2013-08-17 00:57:42 +02:00
|
|
|
const ReducedFraction &onTime,
|
|
|
|
const ReducedFraction &len,
|
2013-06-27 16:14:39 +02:00
|
|
|
Chord *chord,
|
2013-08-17 00:57:42 +02:00
|
|
|
const ReducedFraction &tick,
|
2013-06-27 16:14:39 +02:00
|
|
|
const Drumset *drumset,
|
|
|
|
bool useDrumset)
|
|
|
|
{
|
2013-08-18 12:21:35 +02:00
|
|
|
auto actualFraction = ReducedFraction(chord->actualFraction());
|
2013-06-27 16:14:39 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < midiNotes.size(); ++i) {
|
|
|
|
const MidiNote& mn = midiNotes[i];
|
2013-08-22 23:26:31 +02:00
|
|
|
Note* note = new Note(score);
|
2013-06-27 16:14:39 +02:00
|
|
|
|
|
|
|
// TODO - does this need to be key-aware?
|
2014-04-09 09:40:25 +02:00
|
|
|
note->setPitch(mn.pitch);
|
|
|
|
note->setTpcFromPitch();
|
|
|
|
|
2013-06-27 16:14:39 +02:00
|
|
|
chord->add(note);
|
2014-05-07 18:09:01 +02:00
|
|
|
note->setVeloType(ValueType::USER_VAL);
|
2013-06-27 16:14:39 +02:00
|
|
|
note->setVeloOffset(mn.velo);
|
|
|
|
|
|
|
|
NoteEventList el;
|
2013-08-17 00:57:42 +02:00
|
|
|
ReducedFraction f = (onTime - tick) / actualFraction * 1000;
|
2013-08-18 17:22:10 +02:00
|
|
|
const int ron = f.numerator() / f.denominator();
|
2013-07-08 20:17:13 +02:00
|
|
|
f = len / actualFraction * 1000;
|
2013-08-18 17:22:10 +02:00
|
|
|
const int rlen = f.numerator() / f.denominator();
|
2013-07-08 20:17:13 +02:00
|
|
|
|
2013-06-27 16:14:39 +02:00
|
|
|
el.append(NoteEvent(0, ron, rlen));
|
|
|
|
note->setPlayEvents(el);
|
|
|
|
|
|
|
|
if (useDrumset) {
|
|
|
|
if (!drumset->isValid(mn.pitch))
|
|
|
|
qDebug("unmapped drum note 0x%02x %d", mn.pitch, mn.pitch);
|
2013-08-11 17:41:02 +02:00
|
|
|
else {
|
2014-05-07 18:09:01 +02:00
|
|
|
Direction sd = drumset->stemDirection(mn.pitch);
|
2013-08-11 17:41:02 +02:00
|
|
|
chord->setStemDirection(sd);
|
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (midiNotes[i].tie) {
|
|
|
|
midiNotes[i].tie->setEndNote(note);
|
|
|
|
midiNotes[i].tie->setTrack(note->track());
|
|
|
|
note->setTieBack(midiNotes[i].tie);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-08-20 13:31:10 +02:00
|
|
|
void setTies(Chord *chord,
|
|
|
|
Score *score,
|
|
|
|
QList<MidiNote> &midiNotes)
|
2013-06-27 16:14:39 +02:00
|
|
|
{
|
|
|
|
for (int i = 0; i < midiNotes.size(); ++i) {
|
|
|
|
const MidiNote &midiNote = midiNotes[i];
|
2013-08-22 23:26:31 +02:00
|
|
|
Note *note = chord->findNote(midiNote.pitch);
|
2013-06-27 16:14:39 +02:00
|
|
|
midiNotes[i].tie = new Tie(score);
|
|
|
|
midiNotes[i].tie->setStartNote(note);
|
|
|
|
note->setTieFor(midiNotes[i].tie);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-03 23:54:54 +02:00
|
|
|
|
2013-06-27 16:14:39 +02:00
|
|
|
// convert midiChords with the same onTime value to music notation
|
|
|
|
// and fill the remaining empty duration with rests
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
void MTrack::processPendingNotes(QList<MidiChord> &midiChords,
|
|
|
|
int voice,
|
2013-08-17 00:57:42 +02:00
|
|
|
const ReducedFraction &startChordTickFrac,
|
|
|
|
const ReducedFraction &nextChordTick)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
2013-08-22 23:26:31 +02:00
|
|
|
Score* score = staff->score();
|
2013-08-18 17:22:10 +02:00
|
|
|
const int track = staff->idx() * VOICES + voice;
|
2013-08-22 23:26:31 +02:00
|
|
|
Drumset* drumset = staff->part()->instr()->drumset();
|
2013-08-18 17:22:10 +02:00
|
|
|
const bool useDrumset = staff->part()->instr()->useDrumset();
|
|
|
|
|
2013-07-08 12:39:24 +02:00
|
|
|
// all midiChords here should have the same onTime value
|
|
|
|
// and all notes in each midiChord should have the same duration
|
2013-08-17 00:57:42 +02:00
|
|
|
ReducedFraction startChordTick = startChordTickFrac;
|
2013-06-27 16:14:39 +02:00
|
|
|
while (!midiChords.isEmpty()) {
|
2013-08-18 17:22:10 +02:00
|
|
|
const ReducedFraction tick = startChordTick;
|
2013-08-17 00:57:42 +02:00
|
|
|
ReducedFraction len = nextChordTick - tick;
|
2013-08-23 23:56:04 +02:00
|
|
|
if (len <= ReducedFraction(0, 1))
|
2013-04-15 10:38:16 +02:00
|
|
|
break;
|
2014-04-01 06:35:21 +02:00
|
|
|
len = MChord::findMinDuration(tick, midiChords, len);
|
2013-08-22 23:26:31 +02:00
|
|
|
Measure* measure = score->tick2measure(tick.ticks());
|
2013-06-27 16:14:39 +02:00
|
|
|
len = splitDurationOnBarBoundary(len, tick, measure);
|
2013-05-26 01:19:09 +02:00
|
|
|
|
2013-08-18 17:22:10 +02:00
|
|
|
const auto dl = toDurationList(measure, voice, tick, len, Meter::DurationType::NOTE);
|
2013-04-15 10:38:16 +02:00
|
|
|
if (dl.isEmpty())
|
|
|
|
break;
|
2013-07-08 20:17:13 +02:00
|
|
|
const TDuration &d = dl[0].second;
|
2013-08-17 00:57:42 +02:00
|
|
|
const ReducedFraction &tupletRatio = dl[0].first;
|
|
|
|
len = ReducedFraction(d.fraction()) / tupletRatio;
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2013-08-22 23:26:31 +02:00
|
|
|
Chord* chord = new Chord(score);
|
2013-04-15 10:38:16 +02:00
|
|
|
chord->setTrack(track);
|
|
|
|
chord->setDurationType(d);
|
|
|
|
chord->setDuration(d.fraction());
|
2014-03-12 19:44:13 +01:00
|
|
|
if (startChordTick == startChordTickFrac // first chord in tied chord sequence
|
|
|
|
&& midiChords.begin()->isStaccato()) {
|
|
|
|
Articulation* a = new Articulation(chord->score());
|
2014-05-26 21:55:26 +02:00
|
|
|
a->setArticulationType(ArticulationType::Staccato);
|
2014-03-12 19:44:13 +01:00
|
|
|
chord->add(a);
|
|
|
|
}
|
|
|
|
|
2013-08-22 23:26:31 +02:00
|
|
|
Segment* s = measure->getSegment(chord, tick.ticks());
|
2013-04-15 10:38:16 +02:00
|
|
|
s->add(chord);
|
2014-03-28 16:29:55 +01:00
|
|
|
chord->setPlayEventType(PlayEventType::User);
|
2013-08-20 18:05:49 +02:00
|
|
|
MidiTuplet::addElementToTuplet(voice, tick, len, chord, tuplets);
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2013-06-27 16:14:39 +02:00
|
|
|
for (int k = 0; k < midiChords.size(); ++k) {
|
|
|
|
MidiChord& midiChord = midiChords[k];
|
2013-07-08 10:49:05 +02:00
|
|
|
setMusicNotesFromMidi(score, midiChord.notes, startChordTick,
|
2013-07-08 09:28:43 +02:00
|
|
|
len, chord, tick, drumset, useDrumset);
|
2014-04-01 06:35:21 +02:00
|
|
|
if (!midiChord.notes.empty() && midiChord.notes.first().offTime - tick <= len) {
|
2013-06-27 16:14:39 +02:00
|
|
|
midiChords.removeAt(k);
|
2013-04-15 10:38:16 +02:00
|
|
|
--k;
|
|
|
|
continue;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
setTies(chord, score, midiChord.notes);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
startChordTick += len;
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
2013-07-08 20:17:13 +02:00
|
|
|
fillGapWithRests(score, voice, startChordTick,
|
|
|
|
nextChordTick - startChordTick, track);
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-07-27 00:01:30 +02:00
|
|
|
void MTrack::createKeys(int accidentalType)
|
2013-05-11 18:59:13 +02:00
|
|
|
{
|
2013-08-23 13:47:36 +02:00
|
|
|
Score* score = staff->score();
|
|
|
|
const int track = staff->idx() * VOICES;
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2013-08-22 23:26:31 +02:00
|
|
|
KeyList* km = staff->keymap();
|
2013-07-27 00:01:30 +02:00
|
|
|
if (!hasKey && !mtrack->drumTrack()) {
|
|
|
|
KeySigEvent ks;
|
|
|
|
ks.setAccidentalType(accidentalType);
|
|
|
|
(*km)[0] = ks;
|
|
|
|
}
|
|
|
|
for (auto it = km->begin(); it != km->end(); ++it) {
|
2013-08-18 17:22:10 +02:00
|
|
|
const int tick = it->first;
|
|
|
|
const KeySigEvent &key = it->second;
|
2013-08-22 23:26:31 +02:00
|
|
|
KeySig* ks = new KeySig(score);
|
2013-07-27 00:01:30 +02:00
|
|
|
ks->setTrack(track);
|
|
|
|
ks->setGenerated(false);
|
|
|
|
ks->setKeySigEvent(key);
|
|
|
|
ks->setMag(staff->mag());
|
2013-08-22 23:26:31 +02:00
|
|
|
Measure* m = score->tick2measure(tick);
|
|
|
|
Segment* seg = m->getSegment(ks, tick);
|
2013-07-27 00:01:30 +02:00
|
|
|
seg->add(ks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-17 00:57:42 +02:00
|
|
|
void MTrack::convertTrack(const ReducedFraction &lastTick)
|
2013-07-27 00:01:30 +02:00
|
|
|
{
|
2013-07-22 14:03:23 +02:00
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
2013-06-27 16:14:39 +02:00
|
|
|
// startChordTick is onTime value of all simultaneous notes
|
|
|
|
// chords here are consist of notes with equal durations
|
|
|
|
// several chords may have the same onTime value
|
2013-08-17 00:57:42 +02:00
|
|
|
ReducedFraction startChordTick;
|
2013-06-27 16:14:39 +02:00
|
|
|
QList<MidiChord> midiChords;
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2013-06-11 23:14:05 +02:00
|
|
|
for (auto it = chords.begin(); it != chords.end();) {
|
2013-08-17 15:28:15 +02:00
|
|
|
const auto &nextChordTick = it->first;
|
2013-07-02 12:28:49 +02:00
|
|
|
const MidiChord& midiChord = it->second;
|
|
|
|
if (midiChord.voice != voice) {
|
2013-06-11 23:14:05 +02:00
|
|
|
++it;
|
2013-04-15 10:38:16 +02:00
|
|
|
continue;
|
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
processPendingNotes(midiChords, voice, startChordTick, nextChordTick);
|
|
|
|
// now 'midiChords' list is empty
|
|
|
|
// so - fill it:
|
|
|
|
// collect all midiChords on current tick position
|
|
|
|
startChordTick = nextChordTick; // debug
|
2013-08-18 17:22:10 +02:00
|
|
|
for ( ; it != chords.end(); ++it) {
|
2013-07-02 12:28:49 +02:00
|
|
|
const MidiChord& midiChord = it->second;
|
2013-06-27 16:14:39 +02:00
|
|
|
if (it->first != startChordTick)
|
2013-04-15 10:38:16 +02:00
|
|
|
break;
|
2013-07-02 12:28:49 +02:00
|
|
|
if (midiChord.voice != voice)
|
2013-04-15 10:38:16 +02:00
|
|
|
continue;
|
2013-07-02 12:28:49 +02:00
|
|
|
midiChords.append(midiChord);
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
if (midiChords.isEmpty())
|
2013-04-15 10:38:16 +02:00
|
|
|
break;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
// process last chords at the end of the score
|
|
|
|
processPendingNotes(midiChords, voice, startChordTick, lastTick);
|
2013-07-03 23:54:54 +02:00
|
|
|
}
|
2013-07-03 11:14:59 +02:00
|
|
|
|
2013-08-18 17:22:10 +02:00
|
|
|
const int key = 0; // TODO-LIB findKey(mtrack, score->sigmap());
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-08-23 23:32:24 +02:00
|
|
|
MidiTuplet::createTuplets(staff, tuplets);
|
2013-07-27 00:01:30 +02:00
|
|
|
createKeys(key);
|
2013-08-01 23:52:09 +02:00
|
|
|
|
2013-08-18 17:22:10 +02:00
|
|
|
const auto swingType = preferences.midiImportOperations.trackOperations(indexOfOperation).swing;
|
2013-08-01 23:52:09 +02:00
|
|
|
Swing::detectSwing(staff, swingType);
|
2013-08-22 23:15:07 +02:00
|
|
|
|
2014-03-01 18:00:30 +01:00
|
|
|
Q_ASSERT_X(MidiTie::areTiesConsistent(staff), "MTrack::convertTrack", "Ties are inconsistent");
|
|
|
|
|
2013-08-24 20:32:48 +02:00
|
|
|
MidiClef::createClefs(staff, indexOfOperation, mtrack->drumTrack());
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
|
|
|
|
2013-05-17 20:11:36 +02:00
|
|
|
Fraction metaTimeSignature(const MidiEvent& e)
|
2013-04-15 10:38:16 +02:00
|
|
|
{
|
|
|
|
const unsigned char* data = e.edata();
|
2013-08-18 17:22:10 +02:00
|
|
|
const int z = data[0];
|
|
|
|
const int nn = data[1];
|
2013-04-15 10:38:16 +02:00
|
|
|
int n = 1;
|
|
|
|
for (int i = 0; i < nn; ++i)
|
|
|
|
n *= 2;
|
|
|
|
return Fraction(z, n);
|
|
|
|
}
|
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
QList<MTrack> prepareTrackList(const std::multimap<int, MTrack> &tracks)
|
2013-07-12 00:11:06 +02:00
|
|
|
{
|
2013-07-22 14:03:23 +02:00
|
|
|
QList<MTrack> trackList;
|
2013-07-23 13:05:19 +02:00
|
|
|
for (const auto &track: tracks) {
|
2013-07-22 14:03:23 +02:00
|
|
|
trackList.push_back(track.second);
|
2013-07-23 13:05:19 +02:00
|
|
|
}
|
2013-07-22 14:03:23 +02:00
|
|
|
return trackList;
|
2013-07-12 00:11:06 +02:00
|
|
|
}
|
|
|
|
|
2013-08-17 00:57:42 +02:00
|
|
|
std::multimap<int, MTrack> createMTrackList(ReducedFraction &lastTick,
|
2013-07-25 15:11:58 +02:00
|
|
|
TimeSigMap *sigmap,
|
|
|
|
const MidiFile *mf)
|
2013-04-15 10:38:16 +02:00
|
|
|
{
|
|
|
|
sigmap->clear();
|
|
|
|
sigmap->add(0, Fraction(4, 4)); // default time signature
|
|
|
|
|
2013-07-25 15:11:58 +02:00
|
|
|
std::multimap<int, MTrack> tracks; // <track index, track>
|
2013-04-28 21:29:12 +02:00
|
|
|
int trackIndex = -1;
|
2013-07-25 18:32:07 +02:00
|
|
|
for (const auto &t: mf->tracks()) {
|
2013-04-15 10:38:16 +02:00
|
|
|
MTrack track;
|
2013-07-25 18:32:07 +02:00
|
|
|
track.mtrack = &t;
|
2013-08-22 11:29:20 +02:00
|
|
|
track.division = mf->division();
|
2013-04-28 21:29:12 +02:00
|
|
|
int events = 0;
|
2013-06-11 23:14:05 +02:00
|
|
|
// - create time signature list from meta events
|
|
|
|
// - create MidiChord list
|
|
|
|
// - extract some information from track: program, min/max pitch
|
2013-09-07 20:57:23 +02:00
|
|
|
for (const auto &i: t.events()) {
|
2013-04-15 10:38:16 +02:00
|
|
|
const MidiEvent& e = i.second;
|
2013-08-22 11:29:20 +02:00
|
|
|
const auto tick = toMuseScoreTicks(i.first, track.division);
|
2013-06-11 23:14:05 +02:00
|
|
|
// remove time signature events
|
2013-08-22 11:29:20 +02:00
|
|
|
if ((e.type() == ME_META) && (e.metaType() == META_TIME_SIGNATURE)) {
|
2013-07-08 20:17:13 +02:00
|
|
|
sigmap->add(tick.ticks(), metaTimeSignature(e));
|
2013-08-22 11:29:20 +02:00
|
|
|
}
|
2013-04-15 10:38:16 +02:00
|
|
|
else if (e.type() == ME_NOTE) {
|
|
|
|
++events;
|
2013-08-18 17:22:10 +02:00
|
|
|
const int pitch = e.pitch();
|
2014-01-08 16:28:06 +01:00
|
|
|
const auto len = toMuseScoreTicks(e.len(), track.division);
|
2013-07-08 20:17:13 +02:00
|
|
|
if (tick + len > lastTick)
|
|
|
|
lastTick = tick + len;
|
2013-04-15 10:38:16 +02:00
|
|
|
|
|
|
|
MidiNote n;
|
|
|
|
n.pitch = pitch;
|
|
|
|
n.velo = e.velo();
|
2014-04-01 06:35:21 +02:00
|
|
|
n.offTime = tick + len;
|
2013-04-15 10:38:16 +02:00
|
|
|
|
|
|
|
MidiChord c;
|
|
|
|
c.notes.push_back(n);
|
|
|
|
|
2013-05-11 01:35:40 +02:00
|
|
|
track.chords.insert({tick, c});
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
|
|
|
else if (e.type() == ME_PROGRAM)
|
2013-05-11 01:35:40 +02:00
|
|
|
track.program = e.dataB();
|
2013-07-08 20:17:13 +02:00
|
|
|
if (tick > lastTick)
|
|
|
|
lastTick = tick;
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
|
|
|
if (events != 0) {
|
2013-07-23 13:05:19 +02:00
|
|
|
++trackIndex;
|
|
|
|
if (preferences.midiImportOperations.count()) {
|
|
|
|
auto trackOperations
|
|
|
|
= preferences.midiImportOperations.trackOperations(trackIndex);
|
|
|
|
if (trackOperations.doImport) {
|
|
|
|
track.indexOfOperation = trackIndex;
|
|
|
|
tracks.insert({trackOperations.reorderedIndex, track});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // if it is an initial track-list query from MIDI import panel
|
2013-07-22 14:03:23 +02:00
|
|
|
track.indexOfOperation = trackIndex;
|
2013-07-23 13:05:19 +02:00
|
|
|
tracks.insert({trackIndex, track});
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
|
|
|
}
|
2013-07-25 15:11:58 +02:00
|
|
|
|
|
|
|
return tracks;
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-07-27 00:01:30 +02:00
|
|
|
Measure* barFromIndex(const Score *score, int barIndex)
|
|
|
|
{
|
2013-08-18 17:22:10 +02:00
|
|
|
const int tick = score->sigmap()->bar2tick(barIndex, 0);
|
2013-07-27 00:01:30 +02:00
|
|
|
return score->tick2measure(tick);
|
|
|
|
}
|
|
|
|
|
2013-09-18 16:31:00 +02:00
|
|
|
bool isGrandStaff(const MTrack &t1, const MTrack &t2)
|
2013-08-24 20:37:17 +02:00
|
|
|
{
|
2013-09-18 16:31:00 +02:00
|
|
|
const static std::set<int> grandStaffPrograms = {
|
|
|
|
// Piano
|
|
|
|
0, 1, 2, 3, 4, 5, 6, 7
|
|
|
|
// Chromatic Percussion
|
|
|
|
, 8, 10, 11, 12, 13, 15
|
|
|
|
// Organ
|
|
|
|
, 16, 17, 18, 19, 20, 21, 23
|
|
|
|
// Strings
|
|
|
|
, 46
|
|
|
|
// Ensemble
|
|
|
|
, 50, 51, 54
|
|
|
|
// Brass
|
|
|
|
, 62, 63
|
|
|
|
// Synth Lead
|
|
|
|
, 80, 81, 82, 83, 84, 85, 86, 87
|
|
|
|
// Synth Pad
|
|
|
|
, 88, 89, 90, 91, 92, 93, 94, 95
|
|
|
|
// Synth Effects
|
|
|
|
, 96, 97, 98, 99, 100, 101, 102, 103
|
|
|
|
};
|
|
|
|
|
|
|
|
return t1.mtrack->outChannel() == t2.mtrack->outChannel()
|
|
|
|
&& grandStaffPrograms.find(t1.program) != grandStaffPrograms.end();
|
2013-08-24 20:37:17 +02:00
|
|
|
}
|
|
|
|
|
2013-09-05 16:11:26 +02:00
|
|
|
bool isSameChannel(const MTrack &t1, const MTrack &t2)
|
|
|
|
{
|
|
|
|
return (t1.mtrack->outChannel() == t2.mtrack->outChannel());
|
|
|
|
}
|
|
|
|
|
2013-05-14 16:43:21 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// createInstruments
|
|
|
|
// for drum track, if any, set percussion clef
|
|
|
|
// for piano 2 tracks, if any, set G and F clefs
|
|
|
|
// for other track types set G or F clef
|
2014-03-14 14:38:16 +01:00
|
|
|
//
|
|
|
|
// note: after set, clefs also should be created later
|
2013-05-14 16:43:21 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
void createInstruments(Score *score, QList<MTrack> &tracks)
|
2013-04-28 21:29:12 +02:00
|
|
|
{
|
2013-08-18 17:22:10 +02:00
|
|
|
const int ntracks = tracks.size();
|
2013-04-15 10:38:16 +02:00
|
|
|
for (int idx = 0; idx < ntracks; ++idx) {
|
|
|
|
MTrack& track = tracks[idx];
|
2013-08-22 23:26:31 +02:00
|
|
|
Part* part = new Part(score);
|
|
|
|
Staff* s = new Staff(score, part, 0);
|
2013-04-15 10:38:16 +02:00
|
|
|
part->insertStaff(s);
|
|
|
|
score->staves().push_back(s);
|
|
|
|
track.staff = s;
|
|
|
|
|
|
|
|
if (track.mtrack->drumTrack()) {
|
2013-09-05 16:37:49 +02:00
|
|
|
s->setClef(0, ClefType::PERC);
|
2013-04-15 10:38:16 +02:00
|
|
|
part->instr()->setDrumset(smDrumset);
|
2013-08-07 22:04:33 +02:00
|
|
|
part->instr()->setUseDrumset(true);
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-03-14 14:38:16 +01:00
|
|
|
s->setClef(0, ClefType::G); // can be reset later
|
2013-08-24 20:37:17 +02:00
|
|
|
if (idx < (tracks.size() - 1) && idx >= 0
|
2013-09-18 16:31:00 +02:00
|
|
|
&& isGrandStaff(tracks[idx], tracks[idx + 1])) {
|
2013-06-11 23:14:05 +02:00
|
|
|
// assume that the current track and the next track
|
|
|
|
// form a piano part
|
2014-05-26 22:25:34 +02:00
|
|
|
s->setBracket(0, BracketType::BRACE);
|
2013-07-27 00:01:30 +02:00
|
|
|
s->setBracketSpan(0, 2);
|
|
|
|
|
2013-08-22 23:26:31 +02:00
|
|
|
Staff* ss = new Staff(score, part, 1);
|
2013-04-15 10:38:16 +02:00
|
|
|
part->insertStaff(ss);
|
|
|
|
score->staves().push_back(ss);
|
|
|
|
++idx;
|
2014-03-14 14:38:16 +01:00
|
|
|
ss->setClef(0, ClefType::F); // can be reset later
|
2013-04-15 10:38:16 +02:00
|
|
|
tracks[idx].staff = ss;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
score->appendPart(part);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-08-17 00:57:42 +02:00
|
|
|
void createMeasures(ReducedFraction &lastTick, Score *score)
|
2013-04-28 21:29:12 +02:00
|
|
|
{
|
2013-04-15 10:38:16 +02:00
|
|
|
int bars, beat, tick;
|
2013-07-08 20:17:13 +02:00
|
|
|
score->sigmap()->tickValues(lastTick.ticks(), &bars, &beat, &tick);
|
2013-04-15 10:38:16 +02:00
|
|
|
if (beat > 0 || tick > 0)
|
2013-06-11 23:14:05 +02:00
|
|
|
++bars; // convert bar index to number of bars
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2013-08-18 17:22:10 +02:00
|
|
|
const bool pickupMeasure = preferences.midiImportOperations.currentTrackOperations().pickupMeasure;
|
2013-08-11 22:45:48 +02:00
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
for (int i = 0; i < bars; ++i) {
|
2013-08-22 23:26:31 +02:00
|
|
|
Measure* measure = new Measure(score);
|
2013-08-18 17:22:10 +02:00
|
|
|
const int tick = score->sigmap()->bar2tick(i, 0);
|
2012-05-26 14:49:10 +02:00
|
|
|
measure->setTick(tick);
|
2014-03-01 16:23:42 +01:00
|
|
|
measure->setNo(i);
|
2013-08-18 17:22:10 +02:00
|
|
|
const Fraction ts = score->sigmap()->timesig(tick).timesig();
|
2013-07-12 14:20:31 +02:00
|
|
|
Fraction nominalTs = ts;
|
2013-08-11 22:45:48 +02:00
|
|
|
|
|
|
|
if (pickupMeasure && i == 0 && bars > 1) {
|
2013-07-12 14:20:31 +02:00
|
|
|
const int secondBarIndex = 1;
|
2013-08-18 17:22:10 +02:00
|
|
|
const int secondBarTick = score->sigmap()->bar2tick(secondBarIndex, 0);
|
2013-07-12 14:20:31 +02:00
|
|
|
Fraction secondTs(score->sigmap()->timesig(secondBarTick).timesig());
|
2013-08-11 22:45:48 +02:00
|
|
|
if (ts < secondTs) { // the first measure is a pickup measure
|
2013-07-12 14:20:31 +02:00
|
|
|
nominalTs = secondTs;
|
|
|
|
measure->setIrregular(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
measure->setTimesig(nominalTs);
|
2012-05-26 14:49:10 +02:00
|
|
|
measure->setLen(ts);
|
2013-04-28 21:29:12 +02:00
|
|
|
score->add(measure);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-08-22 23:26:31 +02:00
|
|
|
const Measure *m = score->lastMeasure();
|
2013-08-08 15:56:15 +02:00
|
|
|
if (m) {
|
|
|
|
score->fixTicks();
|
2013-08-17 00:57:42 +02:00
|
|
|
lastTick = ReducedFraction::fromTicks(m->endTick());
|
2013-08-08 15:56:15 +02:00
|
|
|
}
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2014-05-30 10:18:50 +02:00
|
|
|
QString instrumentName(MidiType type, int program, bool isDrumTrack)
|
2013-05-14 16:43:21 +02:00
|
|
|
{
|
2013-08-11 13:47:17 +02:00
|
|
|
if (isDrumTrack)
|
|
|
|
return "Percussion";
|
|
|
|
|
2013-05-14 16:43:21 +02:00
|
|
|
int hbank = -1, lbank = -1;
|
|
|
|
if (program == -1)
|
|
|
|
program = 0;
|
|
|
|
else {
|
|
|
|
hbank = (program >> 16);
|
|
|
|
lbank = (program >> 8) & 0xff;
|
|
|
|
program = program & 0xff;
|
|
|
|
}
|
2014-05-30 10:18:50 +02:00
|
|
|
return MidiInstrument::instrName(int(type), hbank, lbank, program);
|
2013-05-14 16:43:21 +02:00
|
|
|
}
|
|
|
|
|
2013-07-25 15:11:58 +02:00
|
|
|
void setTrackInfo(MidiType midiType, MTrack &mt)
|
2013-05-14 16:43:21 +02:00
|
|
|
{
|
|
|
|
if (mt.staff->isTop()) {
|
2013-08-22 23:26:31 +02:00
|
|
|
Part *part = mt.staff->part();
|
2013-05-14 16:43:21 +02:00
|
|
|
if (mt.name.isEmpty()) {
|
2013-08-11 13:47:17 +02:00
|
|
|
QString name = instrumentName(midiType, mt.program, mt.mtrack->drumTrack());
|
2013-09-05 16:11:26 +02:00
|
|
|
if (!name.isEmpty()) {
|
|
|
|
mt.name = name;
|
2013-05-14 16:43:21 +02:00
|
|
|
part->setLongName(name);
|
2013-09-05 16:11:26 +02:00
|
|
|
}
|
2013-05-14 16:43:21 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
part->setLongName(mt.name);
|
2014-02-11 14:27:44 +01:00
|
|
|
part->setPartName(part->longName());
|
2013-05-14 16:43:21 +02:00
|
|
|
part->setMidiChannel(mt.mtrack->outChannel());
|
2013-08-11 13:42:03 +02:00
|
|
|
int bank = 0;
|
|
|
|
if (mt.mtrack->drumTrack())
|
|
|
|
bank = 128;
|
|
|
|
part->setMidiProgram(mt.program & 0x7f, bank); // only GM
|
2013-05-14 16:43:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
void createTimeSignatures(Score *score)
|
2013-05-14 16:43:21 +02:00
|
|
|
{
|
|
|
|
for (auto is = score->sigmap()->begin(); is != score->sigmap()->end(); ++is) {
|
|
|
|
const SigEvent& se = is->second;
|
2013-08-18 17:22:10 +02:00
|
|
|
const int tick = is->first;
|
2013-07-08 20:17:13 +02:00
|
|
|
Measure* m = score->tick2measure(tick);
|
2013-05-14 16:43:21 +02:00
|
|
|
if (!m)
|
|
|
|
continue;
|
2013-07-12 14:20:31 +02:00
|
|
|
Fraction newTimeSig = se.timesig();
|
2013-08-11 22:45:48 +02:00
|
|
|
|
2013-08-18 17:22:10 +02:00
|
|
|
const bool pickupMeasure = preferences.midiImportOperations.currentTrackOperations().pickupMeasure;
|
2013-08-11 22:45:48 +02:00
|
|
|
if (pickupMeasure && is == score->sigmap()->begin()) {
|
2013-12-14 19:07:11 +01:00
|
|
|
auto next = std::next(is);
|
2013-07-12 14:20:31 +02:00
|
|
|
if (next != score->sigmap()->end()) {
|
|
|
|
Measure* mm = score->tick2measure(next->first);
|
|
|
|
if (m && mm && m == barFromIndex(score, 0) && mm == barFromIndex(score, 1)
|
|
|
|
&& m->timesig() == mm->timesig() && newTimeSig != mm->timesig())
|
|
|
|
{
|
|
|
|
// it's a pickup measure - change timesig to nominal value
|
|
|
|
newTimeSig = mm->timesig();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-14 16:43:21 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
|
2013-08-22 23:26:31 +02:00
|
|
|
TimeSig* ts = new TimeSig(score);
|
2013-07-12 14:20:31 +02:00
|
|
|
ts->setSig(newTimeSig);
|
2013-05-14 16:43:21 +02:00
|
|
|
ts->setTrack(staffIdx * VOICES);
|
2013-08-22 23:26:31 +02:00
|
|
|
Segment* seg = m->getSegment(ts, tick);
|
2013-05-14 16:43:21 +02:00
|
|
|
seg->add(ts);
|
|
|
|
}
|
2013-07-12 14:20:31 +02:00
|
|
|
if (newTimeSig != se.timesig()) // was a pickup measure - skip next timesig
|
|
|
|
++is;
|
2013-05-14 16:43:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-22 11:29:20 +02:00
|
|
|
void processMeta(MTrack &mt, bool isLyric)
|
|
|
|
{
|
|
|
|
for (const auto &ie : mt.mtrack->events()) {
|
|
|
|
const MidiEvent &e = ie.second;
|
|
|
|
const auto tick = toMuseScoreTicks(ie.first, mt.division);
|
|
|
|
if ((e.type() == ME_META) && ((e.metaType() == META_LYRIC) == isLyric))
|
|
|
|
mt.processMeta(tick.ticks(), e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-17 00:57:42 +02:00
|
|
|
void createNotes(const ReducedFraction &lastTick, QList<MTrack> &tracks, MidiType midiType)
|
2013-05-14 16:43:21 +02:00
|
|
|
{
|
2013-04-15 10:38:16 +02:00
|
|
|
for (int i = 0; i < tracks.size(); ++i) {
|
2013-07-08 20:17:13 +02:00
|
|
|
MTrack &mt = tracks[i];
|
2013-08-22 11:29:20 +02:00
|
|
|
processMeta(mt, false);
|
2014-05-30 10:18:50 +02:00
|
|
|
if (midiType == MidiType::UNKNOWN)
|
|
|
|
midiType = MidiType::GM;
|
2013-09-18 17:19:25 +02:00
|
|
|
if (i % 2 && isSameChannel(tracks[i - 1], mt)) {
|
2013-09-05 16:11:26 +02:00
|
|
|
mt.program = tracks[i - 1].program;
|
2013-09-18 17:19:25 +02:00
|
|
|
}
|
|
|
|
// if tracks in Grand staff have different names - clear them,
|
|
|
|
// instrument name will be used instead
|
|
|
|
if (i % 2 == 0 && i < tracks.size() - 1
|
|
|
|
&& isGrandStaff(mt, tracks[i + 1])) {
|
|
|
|
if (mt.name != tracks[i + 1].name) {
|
|
|
|
mt.name = "";
|
|
|
|
tracks[i + 1].name = "";
|
|
|
|
}
|
|
|
|
}
|
2013-07-25 15:11:58 +02:00
|
|
|
setTrackInfo(midiType, mt);
|
2013-06-11 23:14:05 +02:00
|
|
|
// pass current track index to the convertTrack function
|
|
|
|
// through MidiImportOperations
|
2013-07-23 13:05:19 +02:00
|
|
|
preferences.midiImportOperations.setCurrentTrack(mt.indexOfOperation);
|
2013-04-15 10:38:16 +02:00
|
|
|
mt.convertTrack(lastTick);
|
2013-08-22 11:29:20 +02:00
|
|
|
processMeta(mt, true);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-05-14 16:43:21 +02:00
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-09-08 22:36:40 +02:00
|
|
|
QList<TrackMeta> getTracksMeta(const QList<MTrack> &tracks,
|
2013-07-22 14:03:23 +02:00
|
|
|
const MidiFile *mf)
|
2013-04-28 21:29:12 +02:00
|
|
|
{
|
2013-05-11 01:35:40 +02:00
|
|
|
QList<TrackMeta> tracksMeta;
|
2013-09-08 22:36:40 +02:00
|
|
|
for (int i = 0; i < tracks.size(); ++i) {
|
|
|
|
const MTrack &mt = tracks[i];
|
2013-09-26 17:12:44 +02:00
|
|
|
std::string trackName;
|
2013-08-24 20:37:17 +02:00
|
|
|
for (const auto &ie: mt.mtrack->events()) {
|
2013-07-22 14:03:23 +02:00
|
|
|
const MidiEvent &e = ie.second;
|
2013-08-22 11:29:20 +02:00
|
|
|
if ((e.type() == ME_META) && (e.metaType() == META_TRACK_NAME)) {
|
2013-07-22 14:03:23 +02:00
|
|
|
trackName = (const char*)e.edata();
|
2013-08-22 11:29:20 +02:00
|
|
|
break;
|
|
|
|
}
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
2013-09-18 16:59:54 +02:00
|
|
|
QString instrName;
|
|
|
|
if (i % 2 && isSameChannel(tracks[i - 1], tracks[i])){
|
|
|
|
TrackMeta lastMeta = tracksMeta.back();
|
|
|
|
instrName = lastMeta.instrumentName;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MidiType midiType = mf->midiType();
|
2014-05-30 10:18:50 +02:00
|
|
|
if (midiType == MidiType::UNKNOWN)
|
|
|
|
midiType = MidiType::GM;
|
2013-09-18 16:59:54 +02:00
|
|
|
instrName = instrumentName(midiType, mt.program,
|
|
|
|
mt.mtrack->drumTrack());
|
|
|
|
}
|
2013-09-08 22:36:40 +02:00
|
|
|
tracksMeta.push_back({trackName,
|
|
|
|
instrName,
|
|
|
|
mt.mtrack->drumTrack(),
|
|
|
|
mt.initLyricTrackIndex
|
|
|
|
});
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
2013-05-11 01:35:40 +02:00
|
|
|
return tracksMeta;
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
|
|
|
|
2013-07-25 15:11:58 +02:00
|
|
|
void convertMidi(Score *score, const MidiFile *mf)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
2013-08-17 00:57:42 +02:00
|
|
|
ReducedFraction lastTick;
|
2013-08-22 23:26:31 +02:00
|
|
|
auto *sigmap = score->sigmap();
|
2013-06-02 01:06:41 +02:00
|
|
|
|
2013-07-25 15:11:58 +02:00
|
|
|
auto tracks = createMTrackList(lastTick, sigmap, mf);
|
2013-08-19 12:57:46 +02:00
|
|
|
cleanUpMidiEvents(tracks);
|
2013-12-08 20:07:56 +01:00
|
|
|
MChord::collectChords(tracks);
|
2013-09-18 16:08:32 +02:00
|
|
|
MChord::removeOverlappingNotes(tracks);
|
2014-01-02 20:42:51 +01:00
|
|
|
|
|
|
|
Q_ASSERT_X(!doNotesOverlap(tracks),
|
2014-03-30 12:58:21 +02:00
|
|
|
"convertMidi", "There are overlapping notes of the same voice that is incorrect");
|
2014-01-02 20:42:51 +01:00
|
|
|
|
2013-06-02 01:06:41 +02:00
|
|
|
quantizeAllTracks(tracks, sigmap, lastTick);
|
2013-12-16 16:23:04 +01:00
|
|
|
|
2014-04-02 08:16:51 +02:00
|
|
|
Q_ASSERT_X(!doNotesOverlap(tracks),
|
|
|
|
"convertMidi", "There are overlapping notes of the same voice that is incorrect");
|
|
|
|
|
2014-02-08 18:43:31 +01:00
|
|
|
Q_ASSERT_X(noTooShortNotes(tracks),
|
2014-03-30 12:58:21 +02:00
|
|
|
"convertMidi", "There are notes of length < min allowed duration");
|
2013-12-16 16:23:04 +01:00
|
|
|
|
2013-12-08 20:07:56 +01:00
|
|
|
MChord::mergeChordsWithEqualOnTimeAndVoice(tracks);
|
2013-08-24 00:11:07 +02:00
|
|
|
LRHand::splitIntoLeftRightHands(tracks);
|
2013-08-20 18:05:49 +02:00
|
|
|
MidiDrum::splitDrumVoices(tracks);
|
|
|
|
MidiDrum::splitDrumTracks(tracks);
|
2013-10-22 22:16:22 +02:00
|
|
|
MidiDrum::removeRests(tracks, sigmap);
|
2013-10-20 01:21:47 +02:00
|
|
|
MChord::splitUnequalChords(tracks);
|
2013-07-22 14:03:23 +02:00
|
|
|
// no more track insertion/reordering/deletion from now
|
|
|
|
QList<MTrack> trackList = prepareTrackList(tracks);
|
|
|
|
createInstruments(score, trackList);
|
2013-08-20 18:05:49 +02:00
|
|
|
MidiDrum::setStaffBracketForDrums(trackList);
|
2013-06-02 01:06:41 +02:00
|
|
|
createMeasures(lastTick, score);
|
2013-07-25 15:11:58 +02:00
|
|
|
createNotes(lastTick, trackList, mf->midiType());
|
2013-06-02 01:06:41 +02:00
|
|
|
createTimeSignatures(score);
|
|
|
|
score->connectTies();
|
2013-10-19 16:01:42 +02:00
|
|
|
MidiLyrics::setLyricsToScore(mf, trackList);
|
2013-06-02 01:06:41 +02:00
|
|
|
}
|
|
|
|
|
2013-07-25 18:32:07 +02:00
|
|
|
void loadMidiData(MidiFile &mf)
|
|
|
|
{
|
|
|
|
mf.separateChannel();
|
2014-05-30 10:18:50 +02:00
|
|
|
MidiType mt = MidiType::UNKNOWN;
|
2013-07-25 18:32:07 +02:00
|
|
|
for (auto &track: mf.tracks())
|
|
|
|
track.mergeNoteOnOffAndFindMidiType(&mt);
|
|
|
|
mf.setMidiType(mt);
|
|
|
|
}
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
QList<TrackMeta> extractMidiTracksMeta(const QString &fileName)
|
2013-04-28 21:29:12 +02:00
|
|
|
{
|
|
|
|
if (fileName.isEmpty())
|
2013-05-11 01:35:40 +02:00
|
|
|
return QList<TrackMeta>();
|
2013-07-25 15:11:58 +02:00
|
|
|
|
|
|
|
auto &midiData = preferences.midiImportOperations.midiData();
|
|
|
|
if (!midiData.midiFile(fileName)) {
|
|
|
|
QFile fp(fileName);
|
|
|
|
if (!fp.open(QIODevice::ReadOnly))
|
|
|
|
return QList<TrackMeta>();
|
|
|
|
MidiFile mf;
|
|
|
|
try {
|
|
|
|
mf.read(&fp);
|
|
|
|
}
|
2013-07-25 18:32:07 +02:00
|
|
|
catch (...) {
|
2013-07-25 15:11:58 +02:00
|
|
|
fp.close();
|
|
|
|
return QList<TrackMeta>();
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
|
|
|
fp.close();
|
2013-07-25 15:11:58 +02:00
|
|
|
|
2013-07-25 18:32:07 +02:00
|
|
|
loadMidiData(mf);
|
2013-07-25 15:11:58 +02:00
|
|
|
midiData.setMidiFile(fileName, mf);
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Score mockScore;
|
2013-08-17 00:57:42 +02:00
|
|
|
ReducedFraction lastTick;
|
2013-09-08 22:36:40 +02:00
|
|
|
const MidiFile *mf = midiData.midiFile(fileName);
|
|
|
|
const auto tracks = createMTrackList(lastTick, mockScore.sigmap(), mf);
|
|
|
|
QList<MTrack> trackList = prepareTrackList(tracks);
|
2013-10-19 15:58:49 +02:00
|
|
|
MidiLyrics::assignLyricsToTracks(trackList);
|
2013-09-08 22:36:40 +02:00
|
|
|
|
|
|
|
return getTracksMeta(trackList, mf);
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
2013-04-21 20:01:44 +02:00
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// importMidi
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
Score::FileError importMidi(Score *score, const QString &name)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
|
|
|
if (name.isEmpty())
|
2014-05-30 10:17:44 +02:00
|
|
|
return Score::FileError::FILE_NOT_FOUND;
|
2013-07-25 15:11:58 +02:00
|
|
|
|
|
|
|
auto &midiData = preferences.midiImportOperations.midiData();
|
|
|
|
if (!midiData.midiFile(name)) {
|
|
|
|
QFile fp(name);
|
|
|
|
if (!fp.open(QIODevice::ReadOnly)) {
|
|
|
|
qDebug("importMidi: file open error <%s>", qPrintable(name));
|
2014-05-30 10:17:44 +02:00
|
|
|
return Score::FileError::FILE_OPEN_ERROR;
|
2013-07-25 15:11:58 +02:00
|
|
|
}
|
|
|
|
MidiFile mf;
|
|
|
|
try {
|
|
|
|
mf.read(&fp);
|
|
|
|
}
|
2013-07-25 18:32:07 +02:00
|
|
|
catch (QString errorText) {
|
2014-02-28 11:14:43 +01:00
|
|
|
if (!MScore::noGui) {
|
2013-07-25 15:11:58 +02:00
|
|
|
QMessageBox::warning(0,
|
2014-02-27 19:57:24 +01:00
|
|
|
QWidget::tr("MuseScore: Load MIDI"),
|
2013-07-25 15:11:58 +02:00
|
|
|
QWidget::tr("Load failed: ") + errorText,
|
|
|
|
QString::null, QWidget::tr("Quit"), QString::null, 0, 1);
|
|
|
|
}
|
|
|
|
fp.close();
|
|
|
|
qDebug("importMidi: bad file format");
|
2014-05-30 10:17:44 +02:00
|
|
|
return Score::FileError::FILE_BAD_FORMAT;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
fp.close();
|
2013-07-25 15:11:58 +02:00
|
|
|
|
2013-07-25 18:32:07 +02:00
|
|
|
loadMidiData(mf);
|
2013-07-25 15:11:58 +02:00
|
|
|
midiData.setMidiFile(name, mf);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
2013-07-25 15:11:58 +02:00
|
|
|
convertMidi(score, midiData.midiFile(name));
|
|
|
|
|
2014-05-30 10:17:44 +02:00
|
|
|
return Score::FileError::FILE_NO_ERROR;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|