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"
|
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-04-15 10:38:16 +02:00
|
|
|
#include "preferences.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"
|
2013-08-01 23:52:09 +02:00
|
|
|
#include "importmidi_swing.h"
|
2013-06-05 21:07:21 +02:00
|
|
|
|
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-04-15 10:38:16 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// MTrack
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
class MTrack {
|
|
|
|
public:
|
2013-07-25 18:32:07 +02:00
|
|
|
int program = 0;
|
|
|
|
Staff* staff = nullptr;
|
|
|
|
const MidiTrack* mtrack = nullptr;
|
2013-04-15 10:38:16 +02:00
|
|
|
QString name;
|
|
|
|
bool hasKey = false;
|
2013-07-22 14:03:23 +02:00
|
|
|
int indexOfOperation = 0;
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
std::multimap<Fraction, MidiChord> chords;
|
|
|
|
std::multimap<Fraction, MidiTuplet::TupletData> tuplets; // <tupletOnTime, ...>
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
void convertTrack(const Fraction &lastTick);
|
2013-07-07 21:34:43 +02:00
|
|
|
void processPendingNotes(QList<MidiChord>& midiChords, int voice,
|
2013-07-08 20:17:13 +02:00
|
|
|
const Fraction &startChordTickFrac, const Fraction &nextChordTick);
|
2013-04-15 10:38:16 +02:00
|
|
|
void processMeta(int tick, const MidiEvent& mm);
|
2013-07-08 20:17:13 +02:00
|
|
|
void fillGapWithRests(Score *score, int voice, const Fraction &startChordTickFrac,
|
|
|
|
const Fraction &restLength, int track);
|
2013-07-07 21:34:43 +02:00
|
|
|
QList<std::pair<Fraction, TDuration> >
|
2013-07-08 20:17:13 +02:00
|
|
|
toDurationList(const Measure *measure, int voice, const Fraction &startTick,
|
|
|
|
const Fraction &len, Meter::DurationType durationType);
|
|
|
|
void addElementToTuplet(int voice, const Fraction &onTime,
|
|
|
|
const Fraction &len, DurationElement *el);
|
2013-07-27 00:01:30 +02:00
|
|
|
void createTuplets();
|
|
|
|
void createKeys(int accidentalType);
|
|
|
|
void createClefs();
|
2013-08-11 17:41:02 +02:00
|
|
|
std::multimap<Fraction, MidiTuplet::TupletData>::iterator
|
|
|
|
findTuplet(int voice, const Fraction &onTime, const Fraction &len);
|
2013-06-22 23:39:48 +02:00
|
|
|
};
|
2013-05-24 13:47:16 +02:00
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-06-02 01:06:41 +02:00
|
|
|
// remove overlapping notes with the same pitch
|
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
void removeOverlappingNotes(std::multimap<int, MTrack> &tracks)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
|
|
|
for (auto &track: tracks) {
|
2013-07-22 14:03:23 +02:00
|
|
|
auto &chords = track.second.chords;
|
2013-06-11 23:14:05 +02:00
|
|
|
for (auto it = chords.begin(); it != chords.end(); ++it) {
|
|
|
|
auto &firstChord = it->second;
|
2013-07-08 20:17:13 +02:00
|
|
|
const auto &firstOnTime = it->first;
|
2013-06-02 01:06:41 +02:00
|
|
|
for (auto ¬e1: firstChord.notes) {
|
2013-06-11 23:14:05 +02:00
|
|
|
auto ii = it;
|
2013-06-02 01:06:41 +02:00
|
|
|
++ii;
|
|
|
|
bool overlapFound = false;
|
|
|
|
for (; ii != chords.end(); ++ii) {
|
|
|
|
auto &secondChord = ii->second;
|
2013-07-08 20:17:13 +02:00
|
|
|
const auto &secondOnTime = ii->first;
|
2013-06-02 01:06:41 +02:00
|
|
|
for (auto ¬e2: secondChord.notes) {
|
|
|
|
if (note2.pitch != note1.pitch)
|
|
|
|
continue;
|
2013-07-08 10:49:05 +02:00
|
|
|
if (secondOnTime >= (firstOnTime + note1.len))
|
2013-06-02 01:06:41 +02:00
|
|
|
continue;
|
|
|
|
qDebug("Midi import: overlapping events: %d+%d %d+%d",
|
2013-07-08 20:17:13 +02:00
|
|
|
firstOnTime.ticks(), note1.len.ticks(),
|
|
|
|
secondOnTime.ticks(), note2.len.ticks());
|
2013-07-08 10:49:05 +02:00
|
|
|
note1.len = secondOnTime - firstOnTime;
|
2013-06-02 01:06:41 +02:00
|
|
|
overlapFound = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (overlapFound)
|
|
|
|
break;
|
|
|
|
}
|
2013-07-08 20:17:13 +02:00
|
|
|
if (note1.len <= Fraction(0)) {
|
|
|
|
qDebug("Midi import: duration <= 0: drop note at %d",
|
|
|
|
firstOnTime.ticks());
|
2013-06-02 01:06:41 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // for note1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// based on quickthresh algorithm
|
2013-07-09 09:42:21 +02:00
|
|
|
//
|
|
|
|
// http://www.cycling74.com/docs/max5/refpages/max-ref/quickthresh.html
|
2013-07-11 16:11:49 +02:00
|
|
|
// (link date 9 July 2013)
|
2013-07-09 09:42:21 +02:00
|
|
|
//
|
2013-06-02 01:06:41 +02:00
|
|
|
// here are default values for audio, in milliseconds
|
|
|
|
// for midi there will be another values, in ticks
|
|
|
|
|
|
|
|
// all notes received in the left inlet within this time period are collected into a chord
|
|
|
|
// threshTime = 40 ms
|
|
|
|
|
|
|
|
// if there are any incoming values within this amount of time
|
|
|
|
// at the end of the base thresh time,
|
|
|
|
// the threshold is extended to allow more notes to be added to the chord
|
|
|
|
// fudgeTime = 10 ms
|
|
|
|
|
|
|
|
// this is an extension value of the base thresh time, which is used if notes arrive
|
|
|
|
// in the object's inlet in the "fudge" time zone
|
|
|
|
// threshExtTime = 20 ms
|
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
void collectChords(std::multimap<int, MTrack> &tracks, const Fraction &minNoteDuration)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
|
|
|
for (auto &track: tracks) {
|
2013-07-22 14:03:23 +02:00
|
|
|
auto &chords = track.second.chords;
|
2013-06-02 01:06:41 +02:00
|
|
|
if (chords.empty())
|
|
|
|
continue;
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction threshTime = minNoteDuration / 2;
|
|
|
|
Fraction fudgeTime = threshTime / 4;
|
|
|
|
Fraction threshExtTime = threshTime / 2;
|
2013-06-02 01:06:41 +02:00
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction startTime(-1, 1); // invalid
|
|
|
|
Fraction curThreshTime(-1, 1);
|
2013-06-11 23:14:05 +02:00
|
|
|
// if intersection of note durations is less than min(minNoteDuration, threshTime)
|
|
|
|
// then this is not a chord
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction tol(-1, 1); // invalid
|
|
|
|
Fraction beg(-1, 1);
|
|
|
|
Fraction end(-1, 1);
|
2013-06-11 23:14:05 +02:00
|
|
|
// chords here consist of a single note
|
|
|
|
// because notes are not united into chords yet
|
|
|
|
for (auto it = chords.begin(); it != chords.end(); ) {
|
|
|
|
const auto ¬e = it->second.notes[0];
|
2013-07-08 20:17:13 +02:00
|
|
|
// this should not be executed when it == chords.begin()
|
2013-07-08 10:49:05 +02:00
|
|
|
if (it->first <= startTime + curThreshTime) {
|
2013-08-11 17:41:02 +02:00
|
|
|
if (it->first > beg)
|
|
|
|
beg = it->first;
|
|
|
|
if (it->first + note.len < end)
|
|
|
|
end = it->first + note.len;
|
|
|
|
if (note.len < tol)
|
|
|
|
tol = note.len;
|
|
|
|
if (end - beg >= tol) {
|
|
|
|
// add current note to the previous chord
|
|
|
|
auto prev = it;
|
|
|
|
--prev;
|
|
|
|
prev->second.notes.push_back(note);
|
|
|
|
if (it->first >= startTime + curThreshTime - fudgeTime)
|
|
|
|
curThreshTime += threshExtTime;
|
|
|
|
it = chords.erase(it);
|
|
|
|
continue;
|
2013-06-02 01:06:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2013-07-08 10:49:05 +02:00
|
|
|
startTime = it->first;
|
2013-07-08 09:28:43 +02:00
|
|
|
beg = startTime;
|
|
|
|
end = startTime + note.len;
|
2013-06-02 01:06:41 +02:00
|
|
|
tol = threshTime;
|
|
|
|
if (curThreshTime != threshTime)
|
|
|
|
curThreshTime = threshTime;
|
|
|
|
}
|
2013-06-11 23:14:05 +02:00
|
|
|
++it;
|
2013-06-02 01:06:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
void sortNotesByPitch(std::multimap<Fraction, MidiChord> &chords)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
|
|
|
struct {
|
|
|
|
bool operator()(const MidiNote ¬e1, const MidiNote ¬e2)
|
|
|
|
{
|
|
|
|
return note1.pitch < note2.pitch;
|
|
|
|
}
|
|
|
|
} pitchSort;
|
|
|
|
|
|
|
|
for (auto &chordEvent: chords) {
|
2013-06-11 23:14:05 +02:00
|
|
|
// in each chord sort notes by pitches
|
2013-06-02 01:06:41 +02:00
|
|
|
auto ¬es = chordEvent.second.notes;
|
|
|
|
qSort(notes.begin(), notes.end(), pitchSort);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
void sortNotesByLength(std::multimap<Fraction, MidiChord> &chords)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
|
|
|
struct {
|
|
|
|
bool operator()(const MidiNote ¬e1, const MidiNote ¬e2)
|
|
|
|
{
|
|
|
|
return note1.len < note2.len;
|
|
|
|
}
|
|
|
|
} lenSort;
|
|
|
|
|
|
|
|
for (auto &chordEvent: chords) {
|
2013-06-11 23:14:05 +02:00
|
|
|
// in each chord sort notes by pitches
|
2013-06-02 01:06:41 +02:00
|
|
|
auto ¬es = chordEvent.second.notes;
|
|
|
|
qSort(notes.begin(), notes.end(), lenSort);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// find notes of each chord that have different durations
|
|
|
|
// and separate them into different chords
|
2013-07-19 16:47:11 +02:00
|
|
|
// so all notes inside every chord will have equal lengths
|
2013-06-02 01:06:41 +02:00
|
|
|
|
2013-08-11 21:54:36 +02:00
|
|
|
void splitUnequalChords(std::multimap<int, MTrack> &tracks)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
|
|
|
for (auto &track: tracks) {
|
2013-07-08 20:17:13 +02:00
|
|
|
std::vector<std::pair<Fraction, MidiChord>> newChordEvents;
|
2013-08-11 21:54:36 +02:00
|
|
|
auto &chords = track.second.chords;
|
2013-06-02 01:06:41 +02:00
|
|
|
sortNotesByLength(chords);
|
|
|
|
for (auto &chordEvent: chords) {
|
|
|
|
auto &chord = chordEvent.second;
|
|
|
|
auto ¬es = chord.notes;
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction len;
|
2013-06-11 23:14:05 +02:00
|
|
|
for (auto it = notes.begin(); it != notes.end(); ) {
|
|
|
|
if (it == notes.begin())
|
|
|
|
len = it->len;
|
2013-06-02 01:06:41 +02:00
|
|
|
else {
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction newLen = it->len;
|
2013-06-02 01:06:41 +02:00
|
|
|
if (newLen != len) {
|
2013-08-11 17:41:02 +02:00
|
|
|
MidiChord newChord;
|
|
|
|
newChord.voice = chord.voice;
|
2013-06-11 23:14:05 +02:00
|
|
|
for (int j = it - notes.begin(); j > 0; --j)
|
2013-06-02 01:06:41 +02:00
|
|
|
newChord.notes.push_back(notes[j - 1]);
|
2013-07-08 10:49:05 +02:00
|
|
|
newChordEvents.push_back({chordEvent.first, newChord});
|
2013-06-11 23:14:05 +02:00
|
|
|
it = notes.erase(notes.begin(), it);
|
2013-06-02 01:06:41 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2013-06-11 23:14:05 +02:00
|
|
|
++it;
|
2013-06-02 01:06:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto &event: newChordEvents)
|
|
|
|
chords.insert(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-11 17:41:02 +02:00
|
|
|
void removeEmptyTuplets(MTrack &track)
|
|
|
|
{
|
|
|
|
if (track.tuplets.empty())
|
|
|
|
return;
|
|
|
|
for (auto it = track.tuplets.begin(); it != track.tuplets.end(); ) {
|
|
|
|
const auto &tupletData = it->second;
|
|
|
|
bool containsChord = false;
|
|
|
|
for (const auto &chord: track.chords) {
|
|
|
|
if (tupletData.voice != chord.second.voice)
|
|
|
|
continue;
|
|
|
|
const Fraction &onTime = chord.first;
|
|
|
|
Fraction len = maxNoteLen(chord.second.notes);
|
|
|
|
if (onTime + len > tupletData.onTime
|
|
|
|
&& onTime + len <= tupletData.onTime + tupletData.len) {
|
|
|
|
// tuplet contains at least one chord
|
|
|
|
containsChord = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!containsChord) {
|
|
|
|
it = track.tuplets.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-11 21:54:36 +02:00
|
|
|
void splitDrumVoices(std::multimap<int, MTrack> &tracks)
|
2013-08-11 17:41:02 +02:00
|
|
|
{
|
2013-08-11 21:54:36 +02:00
|
|
|
for (auto &trackItem: tracks) {
|
|
|
|
MTrack &track = trackItem.second;
|
2013-08-11 17:41:02 +02:00
|
|
|
std::vector<std::pair<Fraction, MidiChord>> newChordEvents;
|
|
|
|
auto &chords = track.chords;
|
|
|
|
Drumset* drumset = track.mtrack->drumTrack() ? smDrumset : 0;
|
|
|
|
if (!drumset)
|
|
|
|
continue;
|
|
|
|
// all chords of drum track have voice == 0
|
|
|
|
// because useMultipleVoices == false (see MidiImportOperations)
|
|
|
|
for (auto chordIt = chords.begin(); chordIt != chords.end(); ) {
|
|
|
|
auto &chord = chordIt->second;
|
|
|
|
auto ¬es = chord.notes;
|
|
|
|
MidiChord newChord;
|
|
|
|
for (auto it = notes.begin(); it != notes.end(); ) {
|
|
|
|
if (drumset->isValid(it->pitch) && drumset->voice(it->pitch) != 0) {
|
|
|
|
newChord.voice = drumset->voice(it->pitch);
|
|
|
|
newChord.notes.push_back(*it);
|
|
|
|
|
|
|
|
it = notes.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
if (!newChord.notes.isEmpty()) {
|
|
|
|
newChordEvents.push_back({chordIt->first, newChord});
|
|
|
|
|
|
|
|
auto tupletIt = track.findTuplet(chordIt->second.voice, chordIt->first,
|
|
|
|
maxNoteLen(newChord.notes));
|
2013-08-11 20:08:12 +02:00
|
|
|
auto newTupletIt = track.findTuplet(newChord.voice, chordIt->first,
|
|
|
|
maxNoteLen(newChord.notes));
|
|
|
|
if (tupletIt != track.tuplets.end()
|
|
|
|
&& newTupletIt == track.tuplets.end()) {
|
2013-08-11 17:41:02 +02:00
|
|
|
MidiTuplet::TupletData newTupletData = tupletIt->second;
|
|
|
|
newTupletData.voice = newChord.voice;
|
|
|
|
track.tuplets.insert({tupletIt->first, newTupletData});
|
|
|
|
}
|
|
|
|
if (notes.isEmpty()) {
|
|
|
|
removeEmptyTuplets(track);
|
|
|
|
|
|
|
|
chordIt = chords.erase(chordIt);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++chordIt;
|
|
|
|
}
|
|
|
|
for (const auto &event: newChordEvents)
|
|
|
|
chords.insert(event);
|
|
|
|
}
|
|
|
|
}
|
2013-05-24 13:47:16 +02:00
|
|
|
|
2013-08-11 21:54:36 +02:00
|
|
|
std::map<int, MTrack> splitDrumTrack(MTrack &drumTrack)
|
|
|
|
{
|
|
|
|
std::map<int, MTrack> newTracks; // <percussion note pitch, track>
|
|
|
|
if (drumTrack.chords.empty())
|
|
|
|
return newTracks;
|
|
|
|
|
|
|
|
while (!drumTrack.chords.empty()) {
|
|
|
|
int pitch = -1;
|
|
|
|
MTrack *curTrack = nullptr;
|
|
|
|
for (auto it = drumTrack.chords.begin(); it != drumTrack.chords.end(); ) {
|
|
|
|
MidiChord &chord = it->second;
|
|
|
|
for (auto noteIt = chord.notes.begin(); noteIt != chord.notes.end(); ) {
|
|
|
|
if (pitch == -1) {
|
|
|
|
pitch = noteIt->pitch;
|
|
|
|
MTrack newTrack = drumTrack;
|
|
|
|
newTrack.chords.clear();
|
|
|
|
newTrack.tuplets.clear();
|
|
|
|
newTrack.name = smDrumset->name(pitch);
|
|
|
|
newTracks.insert({pitch, newTrack});
|
|
|
|
curTrack = &newTracks.find(pitch)->second;
|
|
|
|
}
|
|
|
|
if (noteIt->pitch == pitch) {
|
|
|
|
MidiChord newChord;
|
|
|
|
newChord.voice = chord.voice;
|
|
|
|
newChord.notes.push_back(*noteIt);
|
|
|
|
curTrack->chords.insert({it->first, newChord});
|
|
|
|
|
|
|
|
auto tupletIt = drumTrack.findTuplet(chord.voice, it->first,
|
|
|
|
noteIt->len);
|
|
|
|
if (tupletIt != drumTrack.tuplets.end()) {
|
|
|
|
auto newTupletIt = curTrack->findTuplet(newChord.voice, it->first,
|
|
|
|
noteIt->len);
|
|
|
|
if (newTupletIt == curTrack->tuplets.end()) {
|
|
|
|
MidiTuplet::TupletData newTupletData = tupletIt->second;
|
|
|
|
newTupletData.voice = newChord.voice;
|
|
|
|
curTrack->tuplets.insert({tupletIt->first, newTupletData});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
noteIt = chord.notes.erase(noteIt);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++noteIt;
|
|
|
|
}
|
|
|
|
if (chord.notes.isEmpty()) {
|
|
|
|
it = drumTrack.chords.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return newTracks;
|
|
|
|
}
|
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
void quantizeAllTracks(std::multimap<int, MTrack> &tracks,
|
|
|
|
TimeSigMap *sigmap,
|
|
|
|
const Fraction &lastTick)
|
2013-05-24 13:47:16 +02:00
|
|
|
{
|
2013-06-05 21:07:21 +02:00
|
|
|
auto &opers = preferences.midiImportOperations;
|
2013-07-22 14:03:23 +02:00
|
|
|
if (opers.count() == 1 && opers.trackOperations(0).quantize.humanPerformance) {
|
2013-06-05 21:07:21 +02:00
|
|
|
opers.setCurrentTrack(0);
|
2013-07-22 14:03:23 +02:00
|
|
|
Quantize::applyAdaptiveQuant(tracks.begin()->second.chords, sigmap, lastTick);
|
|
|
|
Quantize::applyGridQuant(tracks.begin()->second.chords, sigmap, lastTick);
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
2013-06-02 01:06:41 +02:00
|
|
|
else {
|
2013-07-22 14:03:23 +02:00
|
|
|
for (auto &track: tracks) {
|
2013-06-11 23:14:05 +02:00
|
|
|
// pass current track index through MidiImportOperations
|
|
|
|
// for further usage
|
2013-08-11 17:41:02 +02:00
|
|
|
MTrack &mtrack = track.second;
|
|
|
|
opers.setCurrentTrack(mtrack.indexOfOperation);
|
|
|
|
if (mtrack.mtrack->drumTrack())
|
|
|
|
opers.adaptForPercussion(mtrack.indexOfOperation);
|
|
|
|
Quantize::quantizeChordsAndTuplets(mtrack.tuplets, mtrack.chords,
|
2013-07-05 10:46:06 +02:00
|
|
|
sigmap, lastTick);
|
2013-06-02 01:06:41 +02:00
|
|
|
}
|
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-04-15 10:38:16 +02:00
|
|
|
int staffIdx = staff->idx();
|
|
|
|
Score* cs = staff->score();
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
switch (mm.metaType()) {
|
|
|
|
case META_TEXT:
|
2013-04-15 10:38:16 +02:00
|
|
|
case META_LYRIC: {
|
|
|
|
QString s((char*)data);
|
|
|
|
cs->addLyrics(tick, staffIdx, s);
|
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case META_TRACK_NAME:
|
2013-08-11 21:54:36 +02:00
|
|
|
if (name.isEmpty())
|
|
|
|
name = (const char*)data;
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case META_TEMPO:
|
|
|
|
{
|
|
|
|
unsigned tempo = data[2] + (data[1] << 8) + (data[0] <<16);
|
|
|
|
double t = 1000000.0 / double(tempo);
|
|
|
|
cs->setTempo(tick, t);
|
|
|
|
// TODO: create TempoText
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case META_KEY_SIGNATURE:
|
2013-04-28 21:29:12 +02:00
|
|
|
{
|
|
|
|
int key = ((const char*)data)[0];
|
|
|
|
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:
|
|
|
|
text->setTextStyleType(TEXT_STYLE_COMPOSER);
|
|
|
|
break;
|
|
|
|
case META_TRANSLATOR:
|
|
|
|
text->setTextStyleType(TEXT_STYLE_TRANSLATOR);
|
|
|
|
break;
|
|
|
|
case META_POET:
|
|
|
|
text->setTextStyleType(TEXT_STYLE_POET);
|
|
|
|
break;
|
|
|
|
case META_SUBTITLE:
|
|
|
|
text->setTextStyleType(TEXT_STYLE_SUBTITLE);
|
|
|
|
break;
|
|
|
|
case META_TITLE:
|
|
|
|
text->setTextStyleType(TEXT_STYLE_TITLE);
|
|
|
|
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();
|
2012-09-13 18:01:34 +02:00
|
|
|
if (measure->type() != Element::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:
|
2013-04-08 10:31:17 +02:00
|
|
|
cs->setMetaTag("Copyright", QString((const char*)(mm.edata())));
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case META_TIME_SIGNATURE:
|
2013-04-15 10:38:16 +02:00
|
|
|
qDebug("midi: meta timesig: %d, division %d", tick, MScore::division);
|
2012-05-26 14:49:10 +02:00
|
|
|
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-07-03 23:54:54 +02:00
|
|
|
QList<std::pair<Fraction, TDuration> >
|
|
|
|
MTrack::toDurationList(const Measure *measure,
|
|
|
|
int voice,
|
2013-07-08 20:17:13 +02:00
|
|
|
const Fraction &startTick,
|
|
|
|
const Fraction &len,
|
2013-07-03 23:54:54 +02:00
|
|
|
Meter::DurationType durationType)
|
2013-05-17 20:11:36 +02:00
|
|
|
{
|
|
|
|
bool useDots = preferences.midiImportOperations.currentTrackOperations().useDots;
|
2013-06-27 16:14:39 +02:00
|
|
|
// find tuplets over which duration is go
|
2013-07-07 21:34:43 +02:00
|
|
|
std::vector<MidiTuplet::TupletData> tupletData
|
2013-07-08 20:17:13 +02:00
|
|
|
= MidiTuplet::findTupletsForDuration(voice, Fraction::fromTicks(measure->tick()),
|
2013-07-07 21:34:43 +02:00
|
|
|
startTick, len, tuplets);
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction startTickInBar = startTick - Fraction::fromTicks(measure->tick());
|
|
|
|
Fraction endTickInBar = startTickInBar + len;
|
2013-07-03 23:54:54 +02:00
|
|
|
return Meter::toDurationList(startTickInBar, endTickInBar,
|
|
|
|
measure->timesig(), tupletData,
|
|
|
|
durationType, useDots);
|
2013-06-27 16:14:39 +02:00
|
|
|
}
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction splitDurationOnBarBoundary(const Fraction &len, const Fraction &onTime,
|
|
|
|
const Measure* measure)
|
2013-06-27 16:14:39 +02:00
|
|
|
{
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction barLimit = Fraction::fromTicks(measure->tick() + measure->ticks());
|
|
|
|
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-07-08 20:17:13 +02:00
|
|
|
void MTrack::fillGapWithRests(Score* score, int voice,
|
|
|
|
const Fraction &startChordTickFrac,
|
|
|
|
const Fraction &restLength, int track)
|
2013-06-27 16:14:39 +02:00
|
|
|
{
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction startChordTick = startChordTickFrac;
|
|
|
|
Fraction restLen = restLength;
|
2013-05-17 20:11:36 +02:00
|
|
|
while (restLen > 0) {
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction len = restLen;
|
|
|
|
Measure* measure = score->tick2measure(startChordTick.ticks());
|
|
|
|
if (startChordTick >= Fraction::fromTicks(measure->tick() + measure->ticks())) {
|
|
|
|
qDebug("tick2measure: %d end of score?", startChordTick.ticks());
|
2013-06-27 16:14:39 +02:00
|
|
|
startChordTick += restLen;
|
2013-07-08 20:17:13 +02:00
|
|
|
restLen = Fraction(0);
|
2013-05-17 20:11:36 +02:00
|
|
|
break;
|
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
len = splitDurationOnBarBoundary(len, startChordTick, measure);
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
if (len >= Fraction::fromTicks(measure->ticks())) {
|
2013-06-27 16:14:39 +02:00
|
|
|
// rest to the whole measure
|
2013-07-08 20:17:13 +02:00
|
|
|
len = Fraction::fromTicks(measure->ticks());
|
2013-07-03 23:54:54 +02:00
|
|
|
if (voice == 0) {
|
|
|
|
TDuration duration(TDuration::V_MEASURE);
|
|
|
|
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-07-03 23:54:54 +02:00
|
|
|
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());
|
|
|
|
restLen = Fraction(0); // 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-07-05 15:09:23 +02:00
|
|
|
const Fraction &tupletRatio = durationPair.first;
|
2013-07-08 20:17:13 +02:00
|
|
|
len = duration.fraction() / tupletRatio;
|
2013-06-27 16:14:39 +02:00
|
|
|
Rest* rest = new Rest(score, duration);
|
|
|
|
rest->setDuration(duration.fraction());
|
2013-05-17 20:11:36 +02:00
|
|
|
rest->setTrack(track);
|
2013-07-08 20:17:13 +02:00
|
|
|
Segment* s = measure->getSegment(Segment::SegChordRest,
|
|
|
|
startChordTick.ticks());
|
2013-05-17 20:11:36 +02:00
|
|
|
s->add(rest);
|
2013-07-03 23:54:54 +02:00
|
|
|
addElementToTuplet(voice, startChordTick, len, rest);
|
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-07-08 20:17:13 +02:00
|
|
|
const Fraction &onTime,
|
|
|
|
const Fraction &len,
|
2013-06-27 16:14:39 +02:00
|
|
|
Chord *chord,
|
2013-07-08 20:17:13 +02:00
|
|
|
const Fraction &tick,
|
2013-06-27 16:14:39 +02:00
|
|
|
const Drumset *drumset,
|
|
|
|
bool useDrumset)
|
|
|
|
{
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction actualFraction = chord->actualFraction();
|
2013-06-27 16:14:39 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < midiNotes.size(); ++i) {
|
|
|
|
const MidiNote& mn = midiNotes[i];
|
|
|
|
Note* note = new Note(score);
|
|
|
|
|
|
|
|
// TODO - does this need to be key-aware?
|
|
|
|
note->setPitch(mn.pitch, pitch2tpc(mn.pitch, KEY_C, PREFER_NEAREST));
|
|
|
|
chord->add(note);
|
|
|
|
note->setVeloType(MScore::USER_VAL);
|
|
|
|
note->setVeloOffset(mn.velo);
|
|
|
|
|
|
|
|
NoteEventList el;
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction f = (onTime - tick) / actualFraction * 1000;
|
|
|
|
int ron = f.numerator() / f.denominator();
|
|
|
|
f = len / actualFraction * 1000;
|
|
|
|
int rlen = f.numerator() / f.denominator();
|
|
|
|
|
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 {
|
|
|
|
MScore::Direction sd = drumset->stemDirection(mn.pitch);
|
|
|
|
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-07-08 20:17:13 +02:00
|
|
|
Fraction findMinDuration(const QList<MidiChord> &midiChords, const Fraction &length)
|
2013-06-27 16:14:39 +02:00
|
|
|
{
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction len = length;
|
2013-06-27 16:14:39 +02:00
|
|
|
for (const auto &chord: midiChords) {
|
2013-07-08 12:39:24 +02:00
|
|
|
for (const auto ¬e: chord.notes) {
|
|
|
|
if ((note.len < len) && (note.len != 0))
|
|
|
|
len = note.len;
|
|
|
|
}
|
2013-06-27 16:14:39 +02:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setTies(Chord *chord, Score *score, QList<MidiNote> &midiNotes)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < midiNotes.size(); ++i) {
|
|
|
|
const MidiNote &midiNote = midiNotes[i];
|
|
|
|
Note *note = chord->findNote(midiNote.pitch);
|
|
|
|
midiNotes[i].tie = new Tie(score);
|
|
|
|
midiNotes[i].tie->setStartNote(note);
|
|
|
|
note->setTieFor(midiNotes[i].tie);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-11 17:41:02 +02:00
|
|
|
std::multimap<Fraction, MidiTuplet::TupletData>::iterator
|
|
|
|
MTrack::findTuplet(int voice, const Fraction &onTime, const Fraction &len)
|
2013-07-03 23:54:54 +02:00
|
|
|
{
|
2013-07-05 10:46:06 +02:00
|
|
|
if (tuplets.empty())
|
2013-08-11 17:41:02 +02:00
|
|
|
return tuplets.end();
|
|
|
|
|
2013-07-03 23:54:54 +02:00
|
|
|
auto it = tuplets.lower_bound(onTime);
|
2013-07-05 10:46:06 +02:00
|
|
|
if (it == tuplets.end())
|
|
|
|
it = tuplets.begin();
|
2013-07-03 23:54:54 +02:00
|
|
|
if (it != tuplets.begin())
|
|
|
|
--it;
|
|
|
|
for ( ; it != tuplets.end(); ++it) {
|
|
|
|
auto &tupletData = it->second;
|
|
|
|
if (tupletData.voice != voice)
|
|
|
|
continue;
|
|
|
|
if (onTime >= tupletData.onTime
|
2013-07-08 20:17:13 +02:00
|
|
|
&& onTime + len <= tupletData.onTime + tupletData.len) {
|
2013-08-11 17:41:02 +02:00
|
|
|
return it;
|
2013-07-03 23:54:54 +02:00
|
|
|
}
|
|
|
|
}
|
2013-08-11 17:41:02 +02:00
|
|
|
return tuplets.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MTrack::addElementToTuplet(int voice, const Fraction &onTime,
|
|
|
|
const Fraction &len, DurationElement *el)
|
|
|
|
{
|
|
|
|
auto it = findTuplet(voice, onTime, len);
|
|
|
|
if (it != tuplets.end())
|
|
|
|
it->second.elements.push_back(el); // add chord/rest to the tuplet
|
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,
|
|
|
|
const Fraction &startChordTickFrac,
|
|
|
|
const Fraction &nextChordTick)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
2013-04-15 10:38:16 +02:00
|
|
|
Score* score = staff->score();
|
|
|
|
int track = staff->idx() * VOICES + voice;
|
|
|
|
Drumset* drumset = staff->part()->instr()->drumset();
|
|
|
|
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-07-08 20:17:13 +02:00
|
|
|
Fraction startChordTick = startChordTickFrac;
|
2013-06-27 16:14:39 +02:00
|
|
|
while (!midiChords.isEmpty()) {
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction tick = startChordTick;
|
|
|
|
Fraction len = nextChordTick - tick;
|
|
|
|
if (len <= Fraction(0))
|
2013-04-15 10:38:16 +02:00
|
|
|
break;
|
2013-06-27 16:14:39 +02:00
|
|
|
len = findMinDuration(midiChords, len);
|
2013-07-08 20:17:13 +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-07-03 23:54:54 +02:00
|
|
|
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-07-03 23:54:54 +02:00
|
|
|
const Fraction &tupletRatio = dl[0].first;
|
2013-07-08 20:17:13 +02:00
|
|
|
len = d.fraction() / tupletRatio;
|
2013-04-15 10:38:16 +02:00
|
|
|
|
|
|
|
Chord* chord = new Chord(score);
|
|
|
|
chord->setTrack(track);
|
|
|
|
chord->setDurationType(d);
|
|
|
|
chord->setDuration(d.fraction());
|
2013-07-08 20:17:13 +02:00
|
|
|
Segment* s = measure->getSegment(chord, tick.ticks());
|
2013-04-15 10:38:16 +02:00
|
|
|
s->add(chord);
|
|
|
|
chord->setUserPlayEvents(true);
|
2013-07-05 10:46:06 +02:00
|
|
|
addElementToTuplet(voice, tick, len, chord);
|
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);
|
2013-07-08 12:39:24 +02:00
|
|
|
if (!midiChord.notes.empty() && midiChord.notes.first().len <= 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);
|
2013-07-08 09:28:43 +02:00
|
|
|
for (auto &midiNote: midiChord.notes)
|
2013-07-08 12:39:24 +02:00
|
|
|
midiNote.len -= len;
|
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::createTuplets()
|
2013-07-05 10:46:06 +02:00
|
|
|
{
|
2013-07-27 00:01:30 +02:00
|
|
|
Score* score = staff->score();
|
|
|
|
int track = staff->idx() * VOICES;
|
|
|
|
|
2013-07-04 12:28:51 +02:00
|
|
|
for (const auto &tupletEvent: tuplets) {
|
|
|
|
const auto &tupletData = tupletEvent.second;
|
2013-07-05 10:46:06 +02:00
|
|
|
if (tupletData.elements.empty())
|
|
|
|
continue;
|
|
|
|
|
2013-07-04 12:28:51 +02:00
|
|
|
Tuplet* tuplet = new Tuplet(score);
|
|
|
|
auto ratioIt = MidiTuplet::tupletRatios().find(tupletData.tupletNumber);
|
|
|
|
Fraction tupletRatio = (ratioIt != MidiTuplet::tupletRatios().end())
|
|
|
|
? ratioIt->second : Fraction(2, 2);
|
2013-07-09 15:10:43 +02:00
|
|
|
if (ratioIt == MidiTuplet::tupletRatios().end())
|
|
|
|
qDebug("Tuplet ratio not found for tuplet number: %d", tupletData.tupletNumber);
|
2013-07-04 12:28:51 +02:00
|
|
|
tuplet->setRatio(tupletRatio);
|
|
|
|
|
|
|
|
tuplet->setDuration(tupletData.len);
|
2013-07-07 21:14:02 +02:00
|
|
|
TDuration baseLen(tupletData.len / tupletRatio.denominator());
|
2013-07-04 12:28:51 +02:00
|
|
|
tuplet->setBaseLen(baseLen);
|
|
|
|
|
|
|
|
tuplet->setTrack(track);
|
2013-07-08 20:17:13 +02:00
|
|
|
tuplet->setTick(tupletData.onTime.ticks());
|
|
|
|
Measure* measure = score->tick2measure(tupletData.onTime.ticks());
|
2013-07-04 12:28:51 +02:00
|
|
|
tuplet->setParent(measure);
|
|
|
|
|
|
|
|
for (DurationElement *el: tupletData.elements) {
|
|
|
|
tuplet->add(el);
|
|
|
|
el->setTuplet(tuplet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-27 00:01:30 +02:00
|
|
|
void MTrack::createKeys(int accidentalType)
|
2013-05-11 18:59:13 +02:00
|
|
|
{
|
2013-04-15 10:38:16 +02:00
|
|
|
Score* score = staff->score();
|
|
|
|
int track = staff->idx() * VOICES;
|
|
|
|
|
2013-07-27 00:01:30 +02:00
|
|
|
KeyList* km = staff->keymap();
|
|
|
|
if (!hasKey && !mtrack->drumTrack()) {
|
|
|
|
KeySigEvent ks;
|
|
|
|
ks.setAccidentalType(accidentalType);
|
|
|
|
(*km)[0] = ks;
|
|
|
|
}
|
|
|
|
for (auto it = km->begin(); it != km->end(); ++it) {
|
|
|
|
int tick = it->first;
|
|
|
|
KeySigEvent key = it->second;
|
|
|
|
KeySig* ks = new KeySig(score);
|
|
|
|
ks->setTrack(track);
|
|
|
|
ks->setGenerated(false);
|
|
|
|
ks->setKeySigEvent(key);
|
|
|
|
ks->setMag(staff->mag());
|
|
|
|
Measure* m = score->tick2measure(tick);
|
|
|
|
Segment* seg = m->getSegment(ks, tick);
|
|
|
|
seg->add(ks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ClefType clefTypeFromAveragePitch(int averagePitch)
|
|
|
|
{
|
|
|
|
return averagePitch < 60 ? CLEF_F : CLEF_G;
|
|
|
|
}
|
|
|
|
|
|
|
|
void createClef(ClefType clefType, Staff* staff, int tick, bool isSmall = false)
|
|
|
|
{
|
|
|
|
Clef* clef = new Clef(staff->score());
|
|
|
|
clef->setClefType(clefType);
|
|
|
|
int track = staff->idx() * VOICES;
|
|
|
|
clef->setTrack(track);
|
|
|
|
clef->setGenerated(false);
|
|
|
|
clef->setMag(staff->mag());
|
|
|
|
clef->setSmall(isSmall);
|
|
|
|
Measure* m = staff->score()->tick2measure(tick);
|
|
|
|
Segment* seg = m->getSegment(clef, tick);
|
|
|
|
seg->add(clef);
|
|
|
|
}
|
|
|
|
|
2013-07-28 21:23:23 +02:00
|
|
|
void createSmallClef(ClefType clefType, int tick, Staff *staff)
|
|
|
|
{
|
|
|
|
bool isSmallClef = true;
|
|
|
|
createClef(clefType, staff, tick, isSmallClef);
|
|
|
|
}
|
|
|
|
|
|
|
|
void resetIfNotChanged(int &counter, int &oldCounter)
|
|
|
|
{
|
|
|
|
if (counter != 0 && counter == oldCounter) {
|
|
|
|
counter = 0;
|
|
|
|
oldCounter = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isTiedBack(const std::multimap<Fraction, MidiChord>::const_iterator &it,
|
|
|
|
const std::multimap<Fraction, MidiChord> &chords)
|
|
|
|
{
|
|
|
|
if (it == chords.begin())
|
|
|
|
return false;
|
|
|
|
auto i = it;
|
|
|
|
--i;
|
|
|
|
for (;;) {
|
|
|
|
if (i->first + maxNoteLen(i->second.notes) > it->first)
|
|
|
|
return true;
|
|
|
|
if (i == chords.begin())
|
|
|
|
break;
|
|
|
|
--i;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-07-27 00:01:30 +02:00
|
|
|
void MTrack::createClefs()
|
|
|
|
{
|
2013-07-28 21:23:23 +02:00
|
|
|
ClefType currentClef = staff->initialClef()._concertClef;
|
|
|
|
createClef(currentClef, staff, 0);
|
2013-07-27 00:01:30 +02:00
|
|
|
|
|
|
|
auto trackOpers = preferences.midiImportOperations.trackOperations(indexOfOperation);
|
|
|
|
if (trackOpers.changeClef) {
|
2013-07-28 21:23:23 +02:00
|
|
|
const int HIGH_PITCH = 62; // all notes upper - in treble clef
|
|
|
|
const int MED_PITCH = 60;
|
|
|
|
const int LOW_PITCH = 57; // all notes lower - in bass clef
|
|
|
|
|
|
|
|
int oldTrebleCounter = 0;
|
|
|
|
int trebleCounter = 0;
|
|
|
|
int oldBassCounter = 0;
|
|
|
|
int bassCounter = 0;
|
|
|
|
|
|
|
|
const int COUNTER_LIMIT = 3;
|
|
|
|
// N^2 / 2 checks of tied chords in the worst case but fast enough in practice
|
|
|
|
for (auto chordIt = chords.begin(); chordIt != chords.end(); ++chordIt) {
|
|
|
|
if (isTiedBack(chordIt, chords))
|
|
|
|
continue;
|
|
|
|
int tick = chordIt->first.ticks();
|
|
|
|
int avgPitch = findAveragePitch(chordIt->second.notes);
|
|
|
|
if (currentClef == CLEF_G && avgPitch < LOW_PITCH) {
|
|
|
|
currentClef = CLEF_F;
|
|
|
|
createSmallClef(currentClef, tick, staff);
|
|
|
|
}
|
|
|
|
else if (currentClef == CLEF_F && avgPitch > HIGH_PITCH) {
|
|
|
|
currentClef = CLEF_G;
|
|
|
|
createSmallClef(currentClef, tick, staff);
|
|
|
|
}
|
|
|
|
else if (currentClef == CLEF_G && avgPitch >= LOW_PITCH && avgPitch < MED_PITCH) {
|
|
|
|
if (trebleCounter < COUNTER_LIMIT)
|
|
|
|
++trebleCounter;
|
|
|
|
else {
|
|
|
|
currentClef = CLEF_F;
|
|
|
|
createSmallClef(currentClef, tick, staff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (currentClef == CLEF_F && avgPitch <= HIGH_PITCH && avgPitch >= MED_PITCH) {
|
|
|
|
if (bassCounter < COUNTER_LIMIT)
|
|
|
|
++bassCounter;
|
|
|
|
else {
|
|
|
|
currentClef = CLEF_G;
|
|
|
|
createSmallClef(currentClef, tick, staff);
|
|
|
|
}
|
2013-07-27 00:01:30 +02:00
|
|
|
}
|
2013-07-28 21:23:23 +02:00
|
|
|
resetIfNotChanged(bassCounter, oldBassCounter);
|
|
|
|
resetIfNotChanged(trebleCounter, oldTrebleCounter);
|
2013-07-27 00:01:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MTrack::convertTrack(const Fraction &lastTick)
|
|
|
|
{
|
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-07-08 20:17:13 +02:00
|
|
|
Fraction 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-07-08 20:17:13 +02:00
|
|
|
const Fraction &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-06-11 23:14:05 +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-07-27 00:01:30 +02:00
|
|
|
int key = 0; // TODO-LIB findKey(mtrack, score->sigmap());
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-07-27 00:01:30 +02:00
|
|
|
createTuplets();
|
|
|
|
createKeys(key);
|
|
|
|
createClefs();
|
2013-08-01 23:52:09 +02:00
|
|
|
|
|
|
|
auto swingType = preferences.midiImportOperations.trackOperations(indexOfOperation).swing;
|
|
|
|
Swing::detectSwing(staff, swingType);
|
2013-04-15 10:38:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
2012-05-26 14:49:10 +02:00
|
|
|
//---------------------------------------------------
|
|
|
|
// remove empty measures at beginning
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
|
|
|
int startBar, endBar, beat, tick;
|
|
|
|
score->sigmap()->tickValues(lastTick, &endBar, &beat, &tick);
|
|
|
|
if (beat || tick)
|
|
|
|
++endBar;
|
|
|
|
|
|
|
|
for (startBar = 0; startBar < endBar; ++startBar) {
|
|
|
|
int tick1 = score->sigmap()->bar2tick(startBar, 0);
|
|
|
|
int tick2 = score->sigmap()->bar2tick(startBar + 1, 0);
|
|
|
|
int events = 0;
|
|
|
|
foreach (MidiTrack* midiTrack, *tracks) {
|
|
|
|
if (midiTrack->staffIdx() == -1)
|
|
|
|
continue;
|
|
|
|
foreach(const Event ev, midiTrack->events()) {
|
|
|
|
int t = ev.ontime();
|
|
|
|
if (t >= tick2)
|
|
|
|
break;
|
|
|
|
if (t < tick1)
|
|
|
|
continue;
|
|
|
|
if (ev.type() == ME_NOTE) {
|
|
|
|
++events;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (events)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tick = score->sigmap()->bar2tick(startBar, 0);
|
|
|
|
if (tick)
|
|
|
|
qDebug("remove empty measures %d ticks, startBar %d", tick, startBar);
|
|
|
|
mf->move(-tick);
|
2013-04-15 10:38:16 +02:00
|
|
|
#endif
|
|
|
|
|
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();
|
|
|
|
int z = data[0];
|
|
|
|
int nn = data[1];
|
|
|
|
int n = 1;
|
|
|
|
for (int i = 0; i < nn; ++i)
|
|
|
|
n *= 2;
|
|
|
|
return Fraction(z, n);
|
|
|
|
}
|
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
void insertNewLeftHandTrack(std::multimap<int, MTrack> &tracks,
|
|
|
|
std::multimap<int, MTrack>::iterator &it,
|
2013-07-08 20:17:13 +02:00
|
|
|
const std::multimap<Fraction, MidiChord> &leftHandChords)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
2013-07-22 14:03:23 +02:00
|
|
|
auto leftHandTrack = it->second;
|
2013-06-02 01:06:41 +02:00
|
|
|
leftHandTrack.chords = leftHandChords;
|
2013-07-25 00:24:54 +02:00
|
|
|
removeEmptyTuplets(leftHandTrack);
|
2013-07-22 14:03:23 +02:00
|
|
|
it = tracks.insert({it->first, leftHandTrack});
|
2013-06-02 01:06:41 +02:00
|
|
|
}
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
void addNewLeftHandChord(std::multimap<Fraction, MidiChord> &leftHandChords,
|
2013-06-02 01:06:41 +02:00
|
|
|
const QList<MidiNote> &leftHandNotes,
|
2013-07-22 14:03:23 +02:00
|
|
|
const std::multimap<Fraction, MidiChord>::iterator &it)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
2013-07-22 14:03:23 +02:00
|
|
|
MidiChord leftHandChord = it->second;
|
2013-06-02 01:06:41 +02:00
|
|
|
leftHandChord.notes = leftHandNotes;
|
2013-07-22 14:03:23 +02:00
|
|
|
leftHandChords.insert({it->first, leftHandChord});
|
2013-06-02 01:06:41 +02:00
|
|
|
}
|
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
void splitIntoLRHands_FixedPitch(std::multimap<int, MTrack> &tracks,
|
|
|
|
std::multimap<int, MTrack>::iterator &it)
|
2013-05-17 20:11:36 +02:00
|
|
|
{
|
2013-07-22 14:03:23 +02:00
|
|
|
auto &srcTrack = it->second;
|
|
|
|
auto trackOpers = preferences.midiImportOperations.trackOperations(srcTrack.indexOfOperation);
|
2013-06-02 01:06:41 +02:00
|
|
|
int splitPitch = 12 * (int)trackOpers.LHRH.splitPitchOctave
|
|
|
|
+ (int)trackOpers.LHRH.splitPitchNote;
|
2013-07-08 20:17:13 +02:00
|
|
|
std::multimap<Fraction, MidiChord> leftHandChords;
|
2013-06-02 01:06:41 +02:00
|
|
|
|
|
|
|
for (auto i = srcTrack.chords.begin(); i != srcTrack.chords.end(); ++i) {
|
|
|
|
auto ¬es = i->second.notes;
|
|
|
|
QList<MidiNote> leftHandNotes;
|
|
|
|
for (auto j = notes.begin(); j != notes.end(); ) {
|
|
|
|
auto ¬e = *j;
|
|
|
|
if (note.pitch < splitPitch) {
|
|
|
|
leftHandNotes.push_back(note);
|
|
|
|
j = notes.erase(j);
|
2013-05-17 20:11:36 +02:00
|
|
|
continue;
|
|
|
|
}
|
2013-06-02 01:06:41 +02:00
|
|
|
++j;
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
2013-06-02 01:06:41 +02:00
|
|
|
if (!leftHandNotes.empty())
|
|
|
|
addNewLeftHandChord(leftHandChords, leftHandNotes, i);
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
2013-06-02 01:06:41 +02:00
|
|
|
if (!leftHandChords.empty())
|
2013-07-22 14:03:23 +02:00
|
|
|
insertNewLeftHandTrack(tracks, it, leftHandChords);
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
void splitIntoLRHands_HandWidth(std::multimap<int, MTrack> &tracks,
|
|
|
|
std::multimap<int, MTrack>::iterator &it)
|
2013-05-17 20:11:36 +02:00
|
|
|
{
|
2013-07-22 14:03:23 +02:00
|
|
|
auto &srcTrack = it->second;
|
2013-06-02 01:06:41 +02:00
|
|
|
sortNotesByPitch(srcTrack.chords);
|
2013-05-17 20:11:36 +02:00
|
|
|
const int OCTAVE = 12;
|
2013-07-08 20:17:13 +02:00
|
|
|
std::multimap<Fraction, MidiChord> leftHandChords;
|
2013-06-11 23:14:05 +02:00
|
|
|
// chords after MIDI import are sorted by onTime values
|
2013-05-17 20:11:36 +02:00
|
|
|
for (auto i = srcTrack.chords.begin(); i != srcTrack.chords.end(); ++i) {
|
2013-06-02 01:06:41 +02:00
|
|
|
auto ¬es = i->second.notes;
|
|
|
|
QList<MidiNote> leftHandNotes;
|
|
|
|
int minPitch = notes.front().pitch;
|
|
|
|
int maxPitch = notes.back().pitch;
|
|
|
|
if (maxPitch - minPitch > OCTAVE) {
|
2013-06-11 23:14:05 +02:00
|
|
|
// need both hands
|
2013-07-22 14:03:23 +02:00
|
|
|
// assign all chords in range [minPitch .. minPitch + OCTAVE]
|
|
|
|
// to left hand and all other chords - to right hand
|
2013-06-02 01:06:41 +02:00
|
|
|
for (auto j = notes.begin(); j != notes.end(); ) {
|
2013-07-25 00:24:54 +02:00
|
|
|
const auto ¬e = *j;
|
2013-06-02 01:06:41 +02:00
|
|
|
if (note.pitch <= minPitch + OCTAVE) {
|
|
|
|
leftHandNotes.push_back(note);
|
|
|
|
j = notes.erase(j);
|
|
|
|
continue;
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
2013-06-02 01:06:41 +02:00
|
|
|
++j;
|
2013-07-22 14:03:23 +02:00
|
|
|
// maybe todo later: if range of right-hand chords > OCTAVE
|
|
|
|
// => assign all bottom right-hand chords to another, third track
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
2013-06-02 01:06:41 +02:00
|
|
|
}
|
2013-06-11 23:14:05 +02:00
|
|
|
else { // check - use two hands or one hand will be enough (right or left?)
|
|
|
|
// assign top chord for right hand, all the rest - to left hand
|
2013-06-02 01:06:41 +02:00
|
|
|
while (notes.size() > 1) {
|
|
|
|
leftHandNotes.push_back(notes.front());
|
|
|
|
notes.erase(notes.begin());
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
|
|
|
}
|
2013-06-02 01:06:41 +02:00
|
|
|
if (!leftHandNotes.empty())
|
|
|
|
addNewLeftHandChord(leftHandChords, leftHandNotes, i);
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
2013-06-02 01:06:41 +02:00
|
|
|
if (!leftHandChords.empty())
|
2013-07-22 14:03:23 +02:00
|
|
|
insertNewLeftHandTrack(tracks, it, leftHandChords);
|
2013-05-17 20:11:36 +02:00
|
|
|
}
|
2013-04-21 20:01:44 +02:00
|
|
|
|
2013-07-22 14:03:23 +02:00
|
|
|
void splitIntoLeftRightHands(std::multimap<int, MTrack> &tracks)
|
2013-04-22 01:13:14 +02:00
|
|
|
{
|
2013-07-22 14:03:23 +02:00
|
|
|
for (auto it = tracks.begin(); it != tracks.end(); ++it) {
|
2013-08-11 17:41:02 +02:00
|
|
|
if (it->second.mtrack->drumTrack())
|
|
|
|
continue;
|
2013-07-22 14:03:23 +02:00
|
|
|
auto operations = preferences.midiImportOperations.trackOperations(
|
|
|
|
it->second.indexOfOperation);
|
2013-05-17 20:11:36 +02:00
|
|
|
if (!operations.LHRH.doIt)
|
2013-04-28 21:29:12 +02:00
|
|
|
continue;
|
2013-07-22 14:03:23 +02:00
|
|
|
// iterator 'it' will change after track split to ++it
|
|
|
|
// C++11 guarantees that newely inserted item with equal key will go after:
|
|
|
|
// "The relative ordering of elements with equivalent keys is preserved,
|
|
|
|
// and newly inserted elements follow those with equivalent keys
|
|
|
|
// already in the container"
|
2013-05-17 20:11:36 +02:00
|
|
|
switch (operations.LHRH.method) {
|
2013-05-30 16:04:02 +02:00
|
|
|
case MidiOperation::LHRHMethod::HAND_WIDTH:
|
2013-07-22 14:03:23 +02:00
|
|
|
splitIntoLRHands_HandWidth(tracks, it);
|
2013-05-17 20:11:36 +02:00
|
|
|
break;
|
2013-07-13 14:53:09 +02:00
|
|
|
case MidiOperation::LHRHMethod::SPECIFIED_PITCH:
|
2013-07-22 14:03:23 +02:00
|
|
|
splitIntoLRHands_FixedPitch(tracks, it);
|
2013-05-17 20:11:36 +02:00
|
|
|
break;
|
2013-04-22 01:13:14 +02:00
|
|
|
}
|
2013-04-21 20:01:44 +02:00
|
|
|
}
|
2013-04-22 01:13:14 +02:00
|
|
|
}
|
2013-04-21 20:01:44 +02:00
|
|
|
|
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-07-25 15:11:58 +02:00
|
|
|
std::multimap<int, MTrack> createMTrackList(Fraction &lastTick,
|
|
|
|
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-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-07-25 18:32:07 +02:00
|
|
|
for (auto i : t.events()) {
|
2013-04-15 10:38:16 +02:00
|
|
|
const MidiEvent& e = i.second;
|
2013-06-11 23:14:05 +02:00
|
|
|
// change division to MScore::division
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction tick = Fraction::fromTicks((i.first * MScore::division + mf->division()/2)
|
|
|
|
/ mf->division());
|
2013-06-11 23:14:05 +02:00
|
|
|
// remove time signature events
|
2013-04-15 10:38:16 +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-04-15 10:38:16 +02:00
|
|
|
else if (e.type() == ME_NOTE) {
|
|
|
|
++events;
|
|
|
|
int pitch = e.pitch();
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction len = Fraction::fromTicks((e.len() * MScore::division + mf->division()/2)
|
|
|
|
/ mf->division());
|
|
|
|
if (tick + len > lastTick)
|
|
|
|
lastTick = tick + len;
|
2013-04-15 10:38:16 +02:00
|
|
|
|
|
|
|
MidiNote n;
|
|
|
|
n.pitch = pitch;
|
|
|
|
n.velo = e.velo();
|
2013-04-16 10:26:59 +02:00
|
|
|
n.len = 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)
|
|
|
|
{
|
|
|
|
int tick = score->sigmap()->bar2tick(barIndex, 0);
|
|
|
|
return score->tick2measure(tick);
|
|
|
|
}
|
|
|
|
|
|
|
|
int findAveragePitch(const std::map<Fraction, MidiChord>::const_iterator &startChordIt,
|
|
|
|
const std::map<Fraction, MidiChord>::const_iterator &endChordIt)
|
|
|
|
{
|
|
|
|
int avgPitch = 0;
|
|
|
|
int counter = 0;
|
|
|
|
for (auto it = startChordIt; it != endChordIt; ++it) {
|
|
|
|
avgPitch += findAveragePitch(it->second.notes);
|
|
|
|
++counter;
|
|
|
|
}
|
|
|
|
if (counter)
|
|
|
|
avgPitch /= counter;
|
|
|
|
return avgPitch;
|
|
|
|
}
|
|
|
|
|
2013-08-12 09:47:34 +02:00
|
|
|
void setBracket(Staff *&staff, int &counter)
|
|
|
|
{
|
|
|
|
if (staff && counter > 1) {
|
|
|
|
staff->setBracket(0, BRACKET_NORMAL);
|
|
|
|
staff->setBracketSpan(0, counter);
|
|
|
|
}
|
|
|
|
if (counter)
|
|
|
|
counter = 0;
|
|
|
|
if (staff)
|
|
|
|
staff = nullptr;
|
|
|
|
}
|
|
|
|
|
2013-08-11 22:21:07 +02:00
|
|
|
void setStaffBracketForDrums(QList<MTrack> &tracks)
|
|
|
|
{
|
|
|
|
int counter = 0;
|
|
|
|
Staff *firstDrumStaff = nullptr;
|
2013-08-12 09:47:34 +02:00
|
|
|
int opIndex = -1;
|
|
|
|
const auto &opers = preferences.midiImportOperations;
|
|
|
|
|
2013-08-11 22:21:07 +02:00
|
|
|
for (const MTrack &track: tracks) {
|
|
|
|
if (track.mtrack->drumTrack()) {
|
2013-08-12 09:47:34 +02:00
|
|
|
if (opIndex != track.indexOfOperation) {
|
|
|
|
setBracket(firstDrumStaff, counter);
|
|
|
|
if (opers.trackOperations(track.indexOfOperation).drums.showStaffBracket) {
|
|
|
|
opIndex = track.indexOfOperation;
|
|
|
|
firstDrumStaff = track.staff;
|
|
|
|
}
|
|
|
|
}
|
2013-08-11 22:21:07 +02:00
|
|
|
++counter;
|
|
|
|
}
|
2013-08-12 09:47:34 +02:00
|
|
|
else
|
|
|
|
setBracket(firstDrumStaff, counter);
|
2013-08-11 22:21:07 +02:00
|
|
|
}
|
2013-08-12 09:47:34 +02:00
|
|
|
setBracket(firstDrumStaff, counter);
|
2013-08-11 22:21:07 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-07-08 20:17:13 +02:00
|
|
|
void createInstruments(Score *score, QList<MTrack> &tracks)
|
2013-04-28 21:29:12 +02:00
|
|
|
{
|
2013-04-15 10:38:16 +02:00
|
|
|
int ntracks = tracks.size();
|
|
|
|
for (int idx = 0; idx < ntracks; ++idx) {
|
|
|
|
MTrack& track = tracks[idx];
|
|
|
|
Part* part = new Part(score);
|
|
|
|
Staff* s = new Staff(score, part, 0);
|
|
|
|
part->insertStaff(s);
|
|
|
|
score->staves().push_back(s);
|
|
|
|
track.staff = s;
|
|
|
|
|
|
|
|
if (track.mtrack->drumTrack()) {
|
2013-06-11 23:14:05 +02:00
|
|
|
// drum track
|
2013-04-15 10:38:16 +02:00
|
|
|
s->setInitialClef(CLEF_PERC);
|
|
|
|
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 {
|
2013-07-27 00:01:30 +02:00
|
|
|
int avgPitch = findAveragePitch(track.chords.begin(), track.chords.end());
|
|
|
|
s->setInitialClef(clefTypeFromAveragePitch(avgPitch));
|
2013-05-14 16:43:21 +02:00
|
|
|
if ((idx < (ntracks-1))
|
|
|
|
&& (tracks.at(idx+1).mtrack->outChannel() == track.mtrack->outChannel())
|
2013-04-28 21:29:12 +02:00
|
|
|
&& (track.program == 0)) {
|
2013-06-11 23:14:05 +02:00
|
|
|
// assume that the current track and the next track
|
|
|
|
// form a piano part
|
2013-07-27 00:01:30 +02:00
|
|
|
s->setBracket(0, BRACKET_BRACE);
|
|
|
|
s->setBracketSpan(0, 2);
|
|
|
|
|
2013-04-15 10:38:16 +02:00
|
|
|
Staff* ss = new Staff(score, part, 1);
|
|
|
|
part->insertStaff(ss);
|
|
|
|
score->staves().push_back(ss);
|
|
|
|
++idx;
|
2013-07-27 00:01:30 +02:00
|
|
|
avgPitch = findAveragePitch(tracks[idx].chords.begin(), tracks[idx].chords.end());
|
|
|
|
ss->setInitialClef(clefTypeFromAveragePitch(avgPitch));
|
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-07-08 20:17:13 +02:00
|
|
|
void createMeasures(Fraction &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-11 22:45:48 +02:00
|
|
|
bool pickupMeasure = preferences.midiImportOperations.currentTrackOperations().pickupMeasure;
|
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
for (int i = 0; i < bars; ++i) {
|
|
|
|
Measure* measure = new Measure(score);
|
|
|
|
int tick = score->sigmap()->bar2tick(i, 0);
|
|
|
|
measure->setTick(tick);
|
|
|
|
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;
|
|
|
|
int secondBarTick = score->sigmap()->bar2tick(secondBarIndex, 0);
|
|
|
|
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-08 15:56:15 +02:00
|
|
|
Measure *m = score->lastMeasure();
|
|
|
|
if (m) {
|
|
|
|
score->fixTicks();
|
|
|
|
lastTick = Fraction::fromTicks(m->endTick());
|
|
|
|
}
|
2013-04-28 21:29:12 +02:00
|
|
|
}
|
2013-04-15 10:38:16 +02:00
|
|
|
|
2013-08-11 13:47:17 +02:00
|
|
|
QString instrumentName(int 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;
|
|
|
|
}
|
|
|
|
return MidiInstrument::instrName(type, hbank, lbank, program);
|
|
|
|
}
|
|
|
|
|
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-07-08 20:17:13 +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-05-14 16:43:21 +02:00
|
|
|
if (!name.isEmpty())
|
|
|
|
part->setLongName(name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
part->setLongName(mt.name);
|
|
|
|
part->setPartName(part->longName().toPlainText());
|
|
|
|
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-07-08 20:17:13 +02:00
|
|
|
int tick = is->first;
|
|
|
|
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
|
|
|
|
|
|
|
bool pickupMeasure = preferences.midiImportOperations.currentTrackOperations().pickupMeasure;
|
|
|
|
if (pickupMeasure && is == score->sigmap()->begin()) {
|
2013-07-12 14:20:31 +02:00
|
|
|
auto next = is;
|
|
|
|
++next;
|
|
|
|
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) {
|
|
|
|
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);
|
|
|
|
Segment* seg = m->getSegment(ts, tick);
|
|
|
|
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-07-25 15:11:58 +02:00
|
|
|
void createNotes(const Fraction &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-04-15 10:38:16 +02:00
|
|
|
|
2013-05-14 16:43:21 +02:00
|
|
|
for (auto ie : mt.mtrack->events()) {
|
2013-07-08 20:17:13 +02:00
|
|
|
const MidiEvent &e = ie.second;
|
2012-05-26 14:49:10 +02:00
|
|
|
if ((e.type() == ME_META) && (e.metaType() != META_LYRIC))
|
2013-04-15 10:38:16 +02:00
|
|
|
mt.processMeta(ie.first, e);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-08-07 15:24:29 +02:00
|
|
|
if (midiType == MT_UNKNOWN)
|
|
|
|
midiType = MT_GM;
|
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);
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-05-14 16:43:21 +02:00
|
|
|
for (auto ie : mt.mtrack->events()) {
|
2013-07-08 20:17:13 +02:00
|
|
|
const MidiEvent &e = ie.second;
|
2012-05-26 14:49:10 +02:00
|
|
|
if ((e.type() == ME_META) && (e.metaType() == META_LYRIC))
|
2013-04-15 10:38:16 +02:00
|
|
|
mt.processMeta(ie.first, e);
|
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-07-22 14:03:23 +02:00
|
|
|
QList<TrackMeta> getTracksMeta(const std::multimap<int, MTrack> &tracks,
|
|
|
|
const MidiFile *mf)
|
2013-04-28 21:29:12 +02:00
|
|
|
{
|
2013-05-11 01:35:40 +02:00
|
|
|
QList<TrackMeta> tracksMeta;
|
2013-07-22 14:03:23 +02:00
|
|
|
for (const auto &track: tracks) {
|
|
|
|
const MTrack &mt = track.second;
|
|
|
|
const MidiTrack *midiTrack = mt.mtrack;
|
|
|
|
QString trackName;
|
|
|
|
for (const auto &ie: midiTrack->events()) {
|
|
|
|
const MidiEvent &e = ie.second;
|
2013-05-11 01:35:40 +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-04-28 21:29:12 +02:00
|
|
|
}
|
2013-05-11 01:35:40 +02:00
|
|
|
MidiType midiType = mf->midiType();
|
|
|
|
if (midiType == MT_UNKNOWN)
|
|
|
|
midiType = MT_GM;
|
2013-08-11 13:47:17 +02:00
|
|
|
QString instrName = instrumentName(midiType, mt.program, mt.mtrack->drumTrack());
|
2013-08-11 17:41:02 +02:00
|
|
|
bool isDrumTrack = midiTrack->drumTrack();
|
|
|
|
tracksMeta.push_back({trackName, instrName, isDrumTrack});
|
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-08-11 21:54:36 +02:00
|
|
|
void splitDrumTracks(std::multimap<int, MTrack> &tracks)
|
|
|
|
{
|
|
|
|
for (auto it = tracks.begin(); it != tracks.end(); ++it) {
|
|
|
|
if (!it->second.mtrack->drumTrack())
|
|
|
|
continue;
|
|
|
|
auto operations = preferences.midiImportOperations.trackOperations(
|
|
|
|
it->second.indexOfOperation);
|
2013-08-12 09:47:34 +02:00
|
|
|
if (!operations.drums.doSplit)
|
2013-08-11 21:54:36 +02:00
|
|
|
continue;
|
|
|
|
auto newTracks = splitDrumTrack(it->second);
|
|
|
|
int trackIndex = it->first;
|
|
|
|
it = tracks.erase(it);
|
|
|
|
for (const auto &newTrack: newTracks)
|
|
|
|
it = tracks.insert({trackIndex, newTrack.second});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-25 15:11:58 +02:00
|
|
|
void convertMidi(Score *score, const MidiFile *mf)
|
2013-06-02 01:06:41 +02:00
|
|
|
{
|
2013-07-08 20:17:13 +02:00
|
|
|
Fraction lastTick;
|
2013-06-02 01:06:41 +02:00
|
|
|
auto sigmap = score->sigmap();
|
|
|
|
|
2013-07-25 15:11:58 +02:00
|
|
|
auto tracks = createMTrackList(lastTick, sigmap, mf);
|
2013-08-11 19:21:25 +02:00
|
|
|
Fraction minNoteDuration = Fraction::fromTicks(MScore::division) / 32;
|
|
|
|
collectChords(tracks, minNoteDuration);
|
2013-06-02 01:06:41 +02:00
|
|
|
quantizeAllTracks(tracks, sigmap, lastTick);
|
|
|
|
removeOverlappingNotes(tracks);
|
|
|
|
splitIntoLeftRightHands(tracks);
|
2013-08-11 21:54:36 +02:00
|
|
|
splitUnequalChords(tracks);
|
|
|
|
splitDrumVoices(tracks);
|
|
|
|
splitDrumTracks(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-11 22:21:07 +02:00
|
|
|
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-07-25 18:32:07 +02:00
|
|
|
void loadMidiData(MidiFile &mf)
|
|
|
|
{
|
|
|
|
mf.separateChannel();
|
|
|
|
MidiType mt = MT_UNKNOWN;
|
|
|
|
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-07-08 20:17:13 +02:00
|
|
|
Fraction lastTick;
|
2013-07-25 15:11:58 +02:00
|
|
|
auto tracks = createMTrackList(lastTick, mockScore.sigmap(),
|
|
|
|
midiData.midiFile(fileName));
|
|
|
|
return getTracksMeta(tracks, midiData.midiFile(fileName));
|
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())
|
2012-10-07 19:53:58 +02:00
|
|
|
return Score::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));
|
|
|
|
return Score::FILE_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
MidiFile mf;
|
|
|
|
try {
|
|
|
|
mf.read(&fp);
|
|
|
|
}
|
2013-07-25 18:32:07 +02:00
|
|
|
catch (QString errorText) {
|
2013-07-25 15:11:58 +02:00
|
|
|
if (!noGui) {
|
|
|
|
QMessageBox::warning(0,
|
|
|
|
QWidget::tr("MuseScore: load midi"),
|
|
|
|
QWidget::tr("Load failed: ") + errorText,
|
|
|
|
QString::null, QWidget::tr("Quit"), QString::null, 0, 1);
|
|
|
|
}
|
|
|
|
fp.close();
|
|
|
|
qDebug("importMidi: bad file format");
|
|
|
|
return Score::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));
|
|
|
|
|
2012-10-07 19:53:58 +02:00
|
|
|
return Score::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
|
|
|
|