Redesign MIDI import operations architecture
This commit is contained in:
parent
b0d760e0bd
commit
3de0b15547
48 changed files with 1837 additions and 3150 deletions
|
@ -150,14 +150,13 @@ QT4_WRAP_CPP (mocs
|
|||
inspector/inspectorJump.h inspector/inspectorMarker.h inspector/inspectorGlissando.h
|
||||
inspector/inspectorNote.h inspector/inspectorAmbitus.h
|
||||
paletteBoxButton.h pathlistdialog.h exampleview.h noteGroups.h inspector/inspectorTextLine.h
|
||||
importmidi_panel.h importmidi_operations.h
|
||||
importmidi_opmodel.h importmidi_trmodel.h importmidi_opdelegate.h
|
||||
importmidi_panel.h importmidi_operations.h importmidi_model.h importmidi_delegate.h
|
||||
importmidi_operation.h importmidi_quant.h importmidi_chord.h
|
||||
importmidi_inner.h importmidi_data.h importmidi_swing.h
|
||||
importmidi_inner.h importmidi_swing.h
|
||||
importmidi_tuplet_detect.h importmidi_tuplet_filter.h importmidi_tuplet_tonotes.h
|
||||
importmidi_tuplet_voice.h importmidi_beat.h importmidi_simplify.h
|
||||
debugger/debugger.h importmidi_fraction.h importmidi_drum.h
|
||||
importmidi_lrhand.h importmidi_lyrics.h importmidi_trview.h importmidi_tie.h importmidi_voice.h
|
||||
debugger/debugger.h importmidi_fraction.h importmidi_drum.h importmidi_view.h
|
||||
importmidi_lrhand.h importmidi_lyrics.h importmidi_tie.h importmidi_voice.h
|
||||
resourceManager.h downloadUtils.h
|
||||
${OMR_MOCS}
|
||||
${SCRIPT_MOCS}
|
||||
|
@ -269,13 +268,13 @@ add_executable ( ${ExecutableName}
|
|||
paletteBoxButton.cpp driver.cpp exportmidi.cpp noteGroups.cpp
|
||||
pathlistdialog.cpp exampleview.cpp inspector/inspectorTextLine.cpp
|
||||
importmidi_panel.cpp importmidi_operations.cpp miconengine.cpp
|
||||
importmidi_opmodel.cpp importmidi_trmodel.cpp importmidi_opdelegate.cpp
|
||||
importmidi_model.cpp importmidi_delegate.cpp
|
||||
importmidi_meter.cpp importmidi_quant.cpp importmidi_tuplet.cpp importmidi_chord.cpp
|
||||
importmidi_data.cpp importmidi_swing.cpp importmidi_fraction.cpp importmidi_drum.cpp
|
||||
importmidi_clef.cpp importmidi_lrhand.cpp importmidi_lyrics.cpp importmidi_trview.cpp
|
||||
importmidi_swing.cpp importmidi_fraction.cpp importmidi_drum.cpp
|
||||
importmidi_clef.cpp importmidi_lrhand.cpp importmidi_lyrics.cpp
|
||||
importmidi_inner.cpp importmidi_tie.cpp importmidi_tuplet_voice.cpp importmidi_beat.cpp
|
||||
importmidi_tuplet_detect.cpp importmidi_tuplet_filter.cpp importmidi_tuplet_tonotes.cpp
|
||||
importmidi_simplify.cpp importmidi_voice.cpp
|
||||
importmidi_simplify.cpp importmidi_voice.cpp importmidi_view.cpp
|
||||
resourceManager.cpp downloadUtils.cpp
|
||||
textcursor.cpp
|
||||
continuouspanel.cpp
|
||||
|
|
|
@ -313,7 +313,7 @@ Score* MuseScore::readScore(const QString& name)
|
|||
return 0;
|
||||
|
||||
Score* score = new Score(MScore::baseStyle()); // start with built-in style
|
||||
setMidiPrefOperations(name);
|
||||
setReopenInProgress(name);
|
||||
Score::FileError rv = Ms::readScore(score, name, false);
|
||||
if (rv == Score::FileError::FILE_TOO_OLD || rv == Score::FileError::FILE_TOO_NEW) {
|
||||
if (readScoreError(name, rv, true))
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "importmidi_beat.h"
|
||||
#include "importmidi_simplify.h"
|
||||
#include "importmidi_voice.h"
|
||||
#include "importmidi_operations.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
|
@ -70,11 +71,8 @@ extern void updateNoteLines(Segment*, int track);
|
|||
|
||||
void cleanUpMidiEvents(std::multimap<int, MTrack> &tracks)
|
||||
{
|
||||
auto &opers = preferences.midiImportOperations;
|
||||
|
||||
for (auto &track: tracks) {
|
||||
MTrack &mtrack = track.second;
|
||||
opers.setCurrentTrack(mtrack.indexOfOperation);
|
||||
|
||||
for (auto chordIt = mtrack.chords.begin(); chordIt != mtrack.chords.end(); ) {
|
||||
MidiChord &ch = chordIt->second;
|
||||
|
@ -155,10 +153,18 @@ void quantizeAllTracks(std::multimap<int, MTrack> &tracks,
|
|||
MTrack &mtrack = track.second;
|
||||
// pass current track index through MidiImportOperations
|
||||
// for further usage
|
||||
opers.setCurrentTrack(mtrack.indexOfOperation);
|
||||
opers.adaptForPercussion(mtrack.indexOfOperation, mtrack.mtrack->drumTrack());
|
||||
MidiOperations::CurrentTrackSetter setCurrentTrack{opers, mtrack.indexOfOperation};
|
||||
|
||||
if (opers.data()->isNewlyOpened) {
|
||||
opers.data()->trackOpers.isDrumTrack.setValue(
|
||||
opers.currentTrack(), mtrack.mtrack->drumTrack());
|
||||
if (mtrack.mtrack->drumTrack()) {
|
||||
opers.data()->trackOpers.maxVoiceCount.setValue(
|
||||
opers.currentTrack(), MidiOperations::VoiceCount::V_1);
|
||||
}
|
||||
}
|
||||
const auto basicQuant = Quantize::userQuantNoteToFraction(
|
||||
opers.currentTrackOperations().quantize.value);
|
||||
opers.data()->trackOpers.quantValue.defaultValue());
|
||||
|
||||
MidiTuplet::findAllTuplets(mtrack.tuplets, mtrack.chords, sigmap, lastTick, basicQuant);
|
||||
|
||||
|
@ -270,7 +276,6 @@ MTrack::toDurationList(const Measure *measure,
|
|||
const ReducedFraction &len,
|
||||
Meter::DurationType durationType)
|
||||
{
|
||||
const bool useDots = preferences.midiImportOperations.currentTrackOperations().useDots;
|
||||
// find tuplets over which duration goes
|
||||
auto barTick = ReducedFraction::fromTicks(measure->tick());
|
||||
auto tupletsData = MidiTuplet::findTupletsInBarForDuration(
|
||||
|
@ -287,6 +292,9 @@ MTrack::toDurationList(const Measure *measure,
|
|||
|
||||
const ReducedFraction startTickInBar = startTick - barTick;
|
||||
const ReducedFraction endTickInBar = startTickInBar + len;
|
||||
|
||||
const auto &opers = preferences.midiImportOperations;
|
||||
const bool useDots = opers.data()->trackOpers.useDots.value(indexOfOperation);
|
||||
return Meter::toDurationList(startTickInBar, endTickInBar,
|
||||
ReducedFraction(measure->timesig()), tupletsData,
|
||||
durationType, useDots);
|
||||
|
@ -439,7 +447,9 @@ void MTrack::processPendingNotes(QList<MidiChord> &midiChords,
|
|||
const int track = staff->idx() * VOICES + voice;
|
||||
Drumset* drumset = staff->part()->instr()->drumset();
|
||||
const bool useDrumset = staff->part()->instr()->useDrumset();
|
||||
const auto opers = preferences.midiImportOperations.currentTrackOperations();
|
||||
|
||||
const auto& opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
|
||||
// all midiChords here should have the same onTime value
|
||||
// and all notes in each midiChord should have the same duration
|
||||
|
@ -465,7 +475,7 @@ void MTrack::processPendingNotes(QList<MidiChord> &midiChords,
|
|||
chord->setDurationType(d);
|
||||
chord->setDuration(d.fraction());
|
||||
|
||||
if (opers.showStaccato
|
||||
if (opers.showStaccato.value(currentTrack)
|
||||
&& startChordTick == startChordTickFrac // first chord in tied chord sequence
|
||||
&& midiChords.begin()->isStaccato()) {
|
||||
Articulation* a = new Articulation(chord->score());
|
||||
|
@ -559,7 +569,8 @@ void MTrack::convertTrack(const ReducedFraction &lastTick)
|
|||
MidiTuplet::createTuplets(staff, tuplets);
|
||||
createKeys(key);
|
||||
|
||||
const auto swingType = preferences.midiImportOperations.trackOperations(indexOfOperation).swing;
|
||||
const auto& opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const auto swingType = opers.swing.value(indexOfOperation);
|
||||
Swing::detectSwing(staff, swingType);
|
||||
|
||||
Q_ASSERT_X(MidiTie::areTiesConsistent(staff), "MTrack::convertTrack", "Ties are inconsistent");
|
||||
|
@ -645,12 +656,12 @@ std::multimap<int, MTrack> createMTrackList(ReducedFraction &lastTick,
|
|||
}
|
||||
if (events != 0) {
|
||||
++trackIndex;
|
||||
if (preferences.midiImportOperations.count()) {
|
||||
auto trackOperations
|
||||
= preferences.midiImportOperations.trackOperations(trackIndex);
|
||||
if (trackOperations.doImport) {
|
||||
const auto *data = preferences.midiImportOperations.data();
|
||||
if (!data->isNewlyOpened) {
|
||||
if (data->trackOpers.doImport.value(trackIndex)) {
|
||||
track.indexOfOperation = trackIndex;
|
||||
tracks.insert({trackOperations.reorderedIndex, track});
|
||||
tracks.insert({data->trackOpers.trackIndexAfterShuffle.value(trackIndex),
|
||||
track});
|
||||
}
|
||||
}
|
||||
else { // if it is an initial track-list query from MIDI import panel
|
||||
|
@ -733,7 +744,8 @@ void createMeasures(ReducedFraction &lastTick, Score *score)
|
|||
if (beat > 0 || tick > 0)
|
||||
++bars; // convert bar index to number of bars
|
||||
|
||||
const bool pickupMeasure = preferences.midiImportOperations.currentTrackOperations().pickupMeasure;
|
||||
const auto& opers = preferences.midiImportOperations;
|
||||
const bool pickupMeasure = opers.data()->trackOpers.searchPickupMeasure;
|
||||
|
||||
for (int i = 0; i < bars; ++i) {
|
||||
Measure* measure = new Measure(score);
|
||||
|
@ -811,7 +823,9 @@ void createTimeSignatures(Score *score)
|
|||
continue;
|
||||
Fraction newTimeSig = se.timesig();
|
||||
|
||||
const bool pickupMeasure = preferences.midiImportOperations.currentTrackOperations().pickupMeasure;
|
||||
const auto& opers = preferences.midiImportOperations;
|
||||
const bool pickupMeasure = opers.data()->trackOpers.searchPickupMeasure;
|
||||
|
||||
if (pickupMeasure && is == score->sigmap()->begin()) {
|
||||
auto next = std::next(is);
|
||||
if (next != score->sigmap()->end()) {
|
||||
|
@ -868,46 +882,13 @@ void createNotes(const ReducedFraction &lastTick, QList<MTrack> &tracks, MidiTyp
|
|||
setTrackInfo(midiType, mt);
|
||||
// pass current track index to the convertTrack function
|
||||
// through MidiImportOperations
|
||||
preferences.midiImportOperations.setCurrentTrack(mt.indexOfOperation);
|
||||
auto &opers = preferences.midiImportOperations;
|
||||
MidiOperations::CurrentTrackSetter setCurrentTrack(opers, mt.indexOfOperation);
|
||||
mt.convertTrack(lastTick);
|
||||
processMeta(mt, true);
|
||||
}
|
||||
}
|
||||
|
||||
QList<TrackMeta> getTracksMeta(const QList<MTrack> &tracks,
|
||||
const MidiFile *mf)
|
||||
{
|
||||
QList<TrackMeta> tracksMeta;
|
||||
for (int i = 0; i < tracks.size(); ++i) {
|
||||
const MTrack &mt = tracks[i];
|
||||
std::string trackName;
|
||||
for (const auto &ie: mt.mtrack->events()) {
|
||||
const MidiEvent &e = ie.second;
|
||||
if ((e.type() == ME_META) && (e.metaType() == META_TRACK_NAME)) {
|
||||
trackName = (const char*)e.edata();
|
||||
break;
|
||||
}
|
||||
}
|
||||
QString instrName;
|
||||
if (i % 2 && isSameChannel(tracks[i - 1], tracks[i])){
|
||||
TrackMeta lastMeta = tracksMeta.back();
|
||||
instrName = lastMeta.instrumentName;
|
||||
}
|
||||
else {
|
||||
MidiType midiType = mf->midiType();
|
||||
if (midiType == MidiType::UNKNOWN)
|
||||
midiType = MidiType::GM;
|
||||
instrName = instrumentName(midiType, mt.program,
|
||||
mt.mtrack->drumTrack());
|
||||
}
|
||||
tracksMeta.push_back({trackName,
|
||||
instrName,
|
||||
mt.mtrack->drumTrack(),
|
||||
mt.initLyricTrackIndex
|
||||
});
|
||||
}
|
||||
return tracksMeta;
|
||||
}
|
||||
|
||||
void setLeftRightHandSplit(const std::multimap<int, MTrack> &tracks)
|
||||
{
|
||||
|
@ -917,7 +898,8 @@ void setLeftRightHandSplit(const std::multimap<int, MTrack> &tracks)
|
|||
bool needToSplit = false;
|
||||
if (LRHand::needToSplit(mtrack.chords, mtrack.program))
|
||||
needToSplit = true;
|
||||
preferences.midiImportOperations.setNeedToSplit(trackIndex, needToSplit);
|
||||
preferences.midiImportOperations.data()->trackOpers.doStaffSplit.setValue(
|
||||
trackIndex, needToSplit);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -930,18 +912,21 @@ void convertMidi(Score *score, const MidiFile *mf)
|
|||
cleanUpMidiEvents(tracks);
|
||||
auto &opers = preferences.midiImportOperations;
|
||||
|
||||
if (opers.count() == 0) // for newly opened MIDI file
|
||||
if (opers.data()->isNewlyOpened) { // for newly opened MIDI file
|
||||
opers.data()->trackCount = tracks.size();
|
||||
MidiLyrics::extractLyricsToMidiData(mf);
|
||||
|
||||
}
|
||||
// for newly opened MIDI file - detect if it is a human performance
|
||||
// if so - detect beats and set initial time signature
|
||||
if (opers.count() == 0 && opers.defaultOperations().canRedefineDefaultsLater)
|
||||
if (opers.data()->isNewlyOpened && opers.data()->canRedefineDefaultsLater)
|
||||
Quantize::setIfHumanPerformance(tracks, sigmap);
|
||||
else // user value
|
||||
MidiBeat::setTimeSignature(sigmap);
|
||||
|
||||
Q_ASSERT_X(preferences.midiImportOperations.isHumanPerformance()
|
||||
? opers.timeSignature() != ReducedFraction(0, 1) : true,
|
||||
Q_ASSERT_X((opers.data()->trackOpers.isHumanPerformance)
|
||||
? Meter::userTimeSigToFraction(opers.data()->trackOpers.timeSigNumerator,
|
||||
opers.data()->trackOpers.timeSigDenominator)
|
||||
!= ReducedFraction(0, 1) : true,
|
||||
"convertMidi", "Null time signature for human-performed MIDI file");
|
||||
|
||||
MChord::collectChords(tracks);
|
||||
|
@ -949,7 +934,7 @@ void convertMidi(Score *score, const MidiFile *mf)
|
|||
MChord::mergeChordsWithEqualOnTimeAndVoice(tracks);
|
||||
|
||||
// for newly opened MIDI file
|
||||
if (opers.count() == 0 && opers.defaultOperations().canRedefineDefaultsLater)
|
||||
if (opers.data()->isNewlyOpened && opers.data()->canRedefineDefaultsLater)
|
||||
setLeftRightHandSplit(tracks);
|
||||
|
||||
MChord::removeOverlappingNotes(tracks);
|
||||
|
@ -985,10 +970,7 @@ void convertMidi(Score *score, const MidiFile *mf)
|
|||
score->connectTies();
|
||||
MidiLyrics::setLyricsToScore(trackList);
|
||||
|
||||
if (opers.count() == 0) {
|
||||
// clear defaults - they can be set during opening of this new MIDI file
|
||||
opers.clear();
|
||||
}
|
||||
opers.data()->isNewlyOpened = false;
|
||||
}
|
||||
|
||||
void loadMidiData(MidiFile &mf)
|
||||
|
@ -1000,51 +982,23 @@ void loadMidiData(MidiFile &mf)
|
|||
mf.setMidiType(mt);
|
||||
}
|
||||
|
||||
// for new MIDI file called AFTER importMidi
|
||||
|
||||
QList<TrackMeta> extractMidiTracksMeta(const QString &fileName)
|
||||
{
|
||||
if (fileName.isEmpty())
|
||||
return QList<TrackMeta>();
|
||||
|
||||
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);
|
||||
}
|
||||
catch (...) {
|
||||
fp.close();
|
||||
return QList<TrackMeta>();
|
||||
}
|
||||
fp.close();
|
||||
|
||||
loadMidiData(mf);
|
||||
midiData.setMidiFile(fileName, mf);
|
||||
}
|
||||
|
||||
Score mockScore;
|
||||
ReducedFraction lastTick;
|
||||
const MidiFile *mf = midiData.midiFile(fileName);
|
||||
const auto tracks = createMTrackList(lastTick, mockScore.sigmap(), mf);
|
||||
QList<MTrack> trackList = prepareTrackList(tracks);
|
||||
MidiLyrics::assignLyricsToTracks(trackList);
|
||||
|
||||
return getTracksMeta(trackList, mf);
|
||||
}
|
||||
|
||||
// for new MIDI file called BEFORE extractMidiTracksMeta
|
||||
|
||||
Score::FileError importMidi(Score *score, const QString &name)
|
||||
{
|
||||
if (name.isEmpty())
|
||||
return Score::FileError::FILE_NOT_FOUND;
|
||||
|
||||
auto &midiData = preferences.midiImportOperations.midiData();
|
||||
if (!midiData.midiFile(name)) {
|
||||
auto &opers = preferences.midiImportOperations;
|
||||
|
||||
if (!opers.hasFile(name)) {
|
||||
opers.addNewFile(name);
|
||||
opers.data()->isNewlyOpened = true;
|
||||
}
|
||||
else {
|
||||
opers.setCurrentMidiFile(name);
|
||||
}
|
||||
|
||||
if (opers.data()->isNewlyOpened) {
|
||||
|
||||
QFile fp(name);
|
||||
if (!fp.open(QIODevice::ReadOnly)) {
|
||||
qDebug("importMidi: file open error <%s>", qPrintable(name));
|
||||
|
@ -1068,10 +1022,10 @@ Score::FileError importMidi(Score *score, const QString &name)
|
|||
fp.close();
|
||||
|
||||
loadMidiData(mf);
|
||||
midiData.setMidiFile(name, mf);
|
||||
opers.setMidiFileData(name, mf);
|
||||
}
|
||||
|
||||
convertMidi(score, midiData.midiFile(name));
|
||||
convertMidi(score, opers.midiFile(name));
|
||||
|
||||
return Score::FileError::FILE_NO_ERROR;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "importmidi_inner.h"
|
||||
#include "importmidi_quant.h"
|
||||
#include "importmidi_meter.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "thirdparty/beatroot/BeatTracker.h"
|
||||
#include "preferences.h"
|
||||
#include "libmscore/mscore.h"
|
||||
|
@ -160,13 +161,13 @@ void addLastBeats(
|
|||
}
|
||||
}
|
||||
|
||||
HumanBeatData prepareHumanBeatData(
|
||||
MidiOperations::HumanBeatData prepareHumanBeatData(
|
||||
const std::vector<double> &beatTimes,
|
||||
const std::multimap<ReducedFraction, MidiChord> &chords,
|
||||
double ticksPerSec,
|
||||
int beatsInBar)
|
||||
{
|
||||
HumanBeatData beatData;
|
||||
MidiOperations::HumanBeatData beatData;
|
||||
if (chords.empty())
|
||||
return beatData;
|
||||
|
||||
|
@ -284,7 +285,7 @@ void findBeatLocations(
|
|||
salienceFuncs = {findChordSalience1, findChordSalience2};
|
||||
|
||||
// <match rank, beat data, comparator>
|
||||
std::map<double, HumanBeatData, std::greater<double>> beatResults;
|
||||
std::map<double, MidiOperations::HumanBeatData, std::greater<double>> beatResults;
|
||||
|
||||
for (const auto &func: salienceFuncs) {
|
||||
const auto events = prepareChordEvents(allChords, func, ticksPerSec);
|
||||
|
@ -305,8 +306,8 @@ void findBeatLocations(
|
|||
"MidiBeat::findBeatLocations", "Wrong count of bar levels");
|
||||
|
||||
// beat set - first case
|
||||
HumanBeatData beatData = prepareHumanBeatData(beatTimes, allChords,
|
||||
ticksPerSec, beatsInBar);
|
||||
MidiOperations::HumanBeatData beatData = prepareHumanBeatData(
|
||||
beatTimes, allChords, ticksPerSec, beatsInBar);
|
||||
beatData.timeSig = barFraction;
|
||||
double matchRank = findMatchRank(beatData.beatSet, events,
|
||||
levels, beatsInBar, ticksPerSec);
|
||||
|
@ -319,14 +320,19 @@ void findBeatLocations(
|
|||
beatResults.insert({matchRank, beatData});
|
||||
}
|
||||
}
|
||||
|
||||
auto *data = preferences.midiImportOperations.data();
|
||||
if (!beatResults.empty()) {
|
||||
const HumanBeatData &beatData = beatResults.begin()->second;
|
||||
const MidiOperations::HumanBeatData &beatData = beatResults.begin()->second;
|
||||
setTimeSig(sigmap, beatData.timeSig);
|
||||
preferences.midiImportOperations.setHumanBeats(beatData);
|
||||
data->humanBeatData = beatData;
|
||||
}
|
||||
else {
|
||||
const auto currentTimeSig = ReducedFraction(sigmap->timesig(0).timesig());
|
||||
preferences.midiImportOperations.setTimeSignature(currentTimeSig);
|
||||
data->trackOpers.timeSigNumerator = Meter::fractionNumeratorToUserValue(
|
||||
currentTimeSig.numerator());
|
||||
data->trackOpers.timeSigDenominator = Meter::fractionDenominatorToUserValue(
|
||||
currentTimeSig.denominator());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,13 +381,13 @@ void adjustChordsToBeats(std::multimap<int, MTrack> &tracks,
|
|||
ReducedFraction &lastTick)
|
||||
{
|
||||
const auto &opers = preferences.midiImportOperations;
|
||||
const std::set<ReducedFraction> *beats = opers.getHumanBeats();
|
||||
if (!beats || beats->empty())
|
||||
const std::set<ReducedFraction> &beats = opers.data()->humanBeatData.beatSet;
|
||||
if (beats.empty())
|
||||
return;
|
||||
|
||||
if (opers.trackOperations(0).quantize.humanPerformance) {
|
||||
if (opers.data()->trackOpers.isHumanPerformance) {
|
||||
|
||||
Q_ASSERT_X(beats->size() > 1, "MidiBeat::adjustChordsToBeats", "Human beat count < 2");
|
||||
Q_ASSERT_X(beats.size() > 1, "MidiBeat::adjustChordsToBeats", "Human beat count < 2");
|
||||
|
||||
const auto newBeatLen = ReducedFraction::fromTicks(MScore::division);
|
||||
for (auto trackIt = tracks.begin(); trackIt != tracks.end(); ++trackIt) {
|
||||
|
@ -393,11 +399,11 @@ void adjustChordsToBeats(std::multimap<int, MTrack> &tracks,
|
|||
lastTick = {0, 1};
|
||||
|
||||
auto chordIt = chords.begin();
|
||||
auto it = beats->begin();
|
||||
auto it = beats.begin();
|
||||
auto beatStart = *it;
|
||||
auto newBeatStart = ReducedFraction(0, 1);
|
||||
|
||||
for (++it; it != beats->end(); ++it) {
|
||||
for (++it; it != beats.end(); ++it) {
|
||||
const auto &beatEnd = *it;
|
||||
|
||||
Q_ASSERT_X(beatEnd > beatStart, "MidiBeat::adjustChordsToBeats",
|
||||
|
@ -410,7 +416,7 @@ void adjustChordsToBeats(std::multimap<int, MTrack> &tracks,
|
|||
// quantize to prevent ReducedFraction overflow
|
||||
newOnTimeInBeat = Quantize::quantizeValue(
|
||||
newOnTimeInBeat, MChord::minAllowedDuration());
|
||||
scaleOffTimes(chordIt->second.notes, *beats, it,
|
||||
scaleOffTimes(chordIt->second.notes, beats, it,
|
||||
newBeatStart, newBeatLen, lastTick);
|
||||
const auto newOnTime = newBeatStart + newOnTimeInBeat;
|
||||
newChords.insert({newOnTime, chordIt->second});
|
||||
|
@ -430,20 +436,13 @@ void adjustChordsToBeats(std::multimap<int, MTrack> &tracks,
|
|||
|
||||
void setTimeSignature(TimeSigMap *sigmap)
|
||||
{
|
||||
auto &opers = preferences.midiImportOperations;
|
||||
const auto currentTimeSig = opers.timeSignature();
|
||||
// when opened MIDI file was not detected as human-performed
|
||||
if (currentTimeSig == ReducedFraction(0, 1))
|
||||
return;
|
||||
|
||||
const auto newTimeSig = Meter::userTimeSigToFraction(opers.currentTrackOperations().timeSig);
|
||||
setTimeSig(sigmap, newTimeSig);
|
||||
|
||||
const auto *beats = opers.getHumanBeats();
|
||||
if (!beats || beats->empty() || newTimeSig == currentTimeSig)
|
||||
return;
|
||||
|
||||
opers.setTimeSignature(newTimeSig);
|
||||
const auto *data = preferences.midiImportOperations.data();
|
||||
const std::set<ReducedFraction> &beats = data->humanBeatData.beatSet;
|
||||
if (beats.empty())
|
||||
return; // don't set time sig for non-human performed MIDI files
|
||||
const auto timeSig = Meter::userTimeSigToFraction(data->trackOpers.timeSigNumerator,
|
||||
data->trackOpers.timeSigDenominator);
|
||||
setTimeSig(sigmap, timeSig);
|
||||
}
|
||||
|
||||
} // namespace MidiBeat
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "importmidi_inner.h"
|
||||
#include "importmidi_chord.h"
|
||||
#include "importmidi_clef.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "libmscore/mscore.h"
|
||||
#include "preferences.h"
|
||||
|
||||
|
@ -196,11 +197,10 @@ void collectChords(std::multimap<int, MTrack> &tracks)
|
|||
if (chords.empty())
|
||||
continue;
|
||||
|
||||
const int trackIndex = track.second.indexOfOperation;
|
||||
const auto opers = preferences.midiImportOperations.trackOperations(trackIndex);
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const auto minAllowedDur = minAllowedDuration();
|
||||
|
||||
const auto threshTime = (opers.quantize.humanPerformance)
|
||||
const auto threshTime = (opers.isHumanPerformance)
|
||||
? minAllowedDur * 2 : minAllowedDur / 2;
|
||||
const auto fudgeTime = threshTime / 4;
|
||||
const auto threshExtTime = threshTime / 2;
|
||||
|
|
|
@ -21,8 +21,11 @@
|
|||
#include "libmscore/note.h"
|
||||
#include "libmscore/slur.h"
|
||||
#include "libmscore/element.h"
|
||||
#include "libmscore/sig.h"
|
||||
#include "importmidi_tie.h"
|
||||
#include "importmidi_meter.h"
|
||||
#include "importmidi_fraction.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#include <set>
|
||||
|
@ -398,9 +401,9 @@ void createClefs(Staff *staff, int indexOfOperation, bool isDrumTrack)
|
|||
|
||||
ClefType mainClef = staff->clefTypeList(0)._concertClef;
|
||||
const int strack = staff->idx() * VOICES;
|
||||
const auto trackOpers = preferences.midiImportOperations.trackOperations(indexOfOperation);
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
|
||||
if (trackOpers.changeClef) {
|
||||
if (opers.changeClef.value(indexOfOperation)) {
|
||||
MidiTie::TieStateMachine tieTracker;
|
||||
|
||||
// find optimal clef changes via dynamic programming
|
||||
|
|
|
@ -1,234 +0,0 @@
|
|||
#include "importmidi_data.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "importmidi_meter.h"
|
||||
#include "importmidi_inner.h"
|
||||
#include "importmidi_beat.h"
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
void MidiData::setTracksData(const QString &fileName, const QList<TrackData> &tracksData)
|
||||
{
|
||||
data[fileName].tracksData = tracksData;
|
||||
}
|
||||
|
||||
void MidiData::saveHHeaderState(const QString &fileName, const QByteArray &headerData)
|
||||
{
|
||||
data[fileName].HHeaderData = headerData;
|
||||
}
|
||||
|
||||
void MidiData::saveVHeaderState(const QString &fileName, const QByteArray &headerData)
|
||||
{
|
||||
data[fileName].VHeaderData = headerData;
|
||||
}
|
||||
|
||||
void MidiData::excludeFile(const QString &fileName)
|
||||
{
|
||||
data.remove(fileName);
|
||||
}
|
||||
|
||||
QList<TrackData> MidiData::tracksData(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return QList<TrackData>();
|
||||
return it.value().tracksData;
|
||||
}
|
||||
|
||||
QByteArray MidiData::HHeaderData(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return QByteArray();
|
||||
return it.value().HHeaderData;
|
||||
}
|
||||
|
||||
QByteArray MidiData::VHeaderData(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return QByteArray();
|
||||
return it.value().VHeaderData;
|
||||
}
|
||||
|
||||
int MidiData::selectedRow(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return 0;
|
||||
return it.value().selectedRow;
|
||||
}
|
||||
|
||||
void MidiData::setSelectedRow(const QString &fileName, int row)
|
||||
{
|
||||
data[fileName].selectedRow = row;
|
||||
}
|
||||
|
||||
void MidiData::setMidiFile(const QString &fileName, const MidiFile &midiFile)
|
||||
{
|
||||
data[fileName].midiFile = midiFile;
|
||||
}
|
||||
|
||||
const MidiFile* MidiData::midiFile(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return nullptr;
|
||||
return &(it.value().midiFile);
|
||||
}
|
||||
|
||||
void MidiData::addTrackLyrics(const QString &fileName,
|
||||
const std::multimap<ReducedFraction, std::string> &trackLyrics)
|
||||
{
|
||||
data[fileName].lyricTracks.push_back(trackLyrics);
|
||||
}
|
||||
|
||||
const QList<std::multimap<ReducedFraction, std::string> >*
|
||||
MidiData::getLyrics(const QString &fileName)
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return nullptr;
|
||||
return &it.value().lyricTracks;
|
||||
}
|
||||
|
||||
QString MidiData::charset(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return "";
|
||||
return it.value().charset;
|
||||
}
|
||||
|
||||
void MidiData::setCharset(const QString &fileName, const QString &charset)
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return;
|
||||
it.value().charset = charset;
|
||||
}
|
||||
|
||||
bool MidiData::isHumanPerformance(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return false;
|
||||
return it.value().isHumanPerformance;
|
||||
}
|
||||
|
||||
void MidiData::setHumanPerformance(const QString &fileName, bool value)
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return;
|
||||
it.value().isHumanPerformance = value;
|
||||
}
|
||||
|
||||
MidiOperation::QuantValue MidiData::quantValue(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return Quantization().value;
|
||||
return it.value().quantValue;
|
||||
}
|
||||
|
||||
void MidiData::setQuantValue(const QString &fileName, MidiOperation::QuantValue value)
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return;
|
||||
it.value().quantValue = value;
|
||||
}
|
||||
|
||||
bool MidiData::needToSplit(const QString &fileName, int trackIndex) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return false;
|
||||
const auto fit = it.value().needLRhandSplit.find(trackIndex);
|
||||
if (fit == it.value().needLRhandSplit.end())
|
||||
return false;
|
||||
return fit->second;
|
||||
}
|
||||
|
||||
void MidiData::setNeedToSplit(const QString &fileName, int trackIndex, bool value)
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return;
|
||||
it.value().needLRhandSplit[trackIndex] = value;
|
||||
}
|
||||
|
||||
MidiOperation::QuantValue MidiData::defaultQuantValue()
|
||||
{
|
||||
return Quantization().value;
|
||||
}
|
||||
|
||||
const std::set<ReducedFraction>*
|
||||
MidiData::getHumanBeats(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return nullptr;
|
||||
return &it.value().humanBeatData.beatSet;
|
||||
}
|
||||
|
||||
void MidiData::setHumanBeats(const QString &fileName, const HumanBeatData &beatData)
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return;
|
||||
it.value().humanBeatData = beatData;
|
||||
}
|
||||
|
||||
ReducedFraction MidiData::timeSignature(const QString &fileName) const
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
// if data was not found - return zero value;
|
||||
// it means that time sig should be taken from MIDI file meta event
|
||||
// or, if no such event, it will be set to default 4/4 later;
|
||||
// so, don't return default time sig here
|
||||
if (it == data.end())
|
||||
return ReducedFraction(0, 1);
|
||||
return it.value().humanBeatData.timeSig;
|
||||
}
|
||||
|
||||
void MidiData::setTimeSignature(const QString &fileName, const ReducedFraction &timeSig)
|
||||
{
|
||||
const auto it = data.find(fileName);
|
||||
if (it == data.end())
|
||||
return;
|
||||
|
||||
HumanBeatData &beatData = it.value().humanBeatData;
|
||||
if (beatData.timeSig == timeSig)
|
||||
return;
|
||||
|
||||
beatData.timeSig = timeSig;
|
||||
|
||||
auto &beatSet = beatData.beatSet;
|
||||
if (beatSet.empty())
|
||||
return;
|
||||
|
||||
for (int i = 0; i != beatData.addedFirstBeats; ++i) {
|
||||
|
||||
Q_ASSERT_X(!beatSet.empty(), "MidiData::setTimeSignature",
|
||||
"Empty beat set after deletion first beats");
|
||||
|
||||
beatSet.erase(beatSet.begin());
|
||||
}
|
||||
for (int i = 0; i != beatData.addedLastBeats; ++i) {
|
||||
|
||||
Q_ASSERT_X(!beatSet.empty(), "MidiData::setTimeSignature",
|
||||
"Empty beat set after deletion last beats");
|
||||
|
||||
beatSet.erase(std::prev(beatSet.end()));
|
||||
}
|
||||
|
||||
const int beatsInBar = MidiBeat::beatsInBar(timeSig);
|
||||
MidiBeat::addFirstBeats(beatSet, beatData.firstChordTick,
|
||||
beatsInBar, beatData.addedFirstBeats);
|
||||
MidiBeat::addLastBeats(beatSet, beatData.lastChordTick,
|
||||
beatsInBar, beatData.addedLastBeats);
|
||||
}
|
||||
|
||||
} // namespace Ms
|
|
@ -1,90 +0,0 @@
|
|||
#ifndef IMPORTMIDI_DATA_H
|
||||
#define IMPORTMIDI_DATA_H
|
||||
|
||||
#include "midi/midifile.h"
|
||||
#include "importmidi_fraction.h"
|
||||
#include "importmidi_operation.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
namespace MidiCharset {
|
||||
QString defaultCharset();
|
||||
}
|
||||
|
||||
struct TrackData;
|
||||
|
||||
class MidiData
|
||||
{
|
||||
public:
|
||||
void setTracksData(const QString &fileName, const QList<TrackData> &tracksData);
|
||||
void saveHHeaderState(const QString &fileName, const QByteArray &headerData);
|
||||
void saveVHeaderState(const QString &fileName, const QByteArray &headerData);
|
||||
void excludeFile(const QString &fileName);
|
||||
QList<TrackData> tracksData(const QString &fileName) const;
|
||||
QByteArray HHeaderData(const QString &fileName) const;
|
||||
QByteArray VHeaderData(const QString &fileName) const;
|
||||
int selectedRow(const QString &fileName) const;
|
||||
void setSelectedRow(const QString &fileName, int row);
|
||||
void setMidiFile(const QString &fileName, const MidiFile &midiFile);
|
||||
const MidiFile *midiFile(const QString &fileName) const;
|
||||
|
||||
// lyrics
|
||||
void addTrackLyrics(const QString &fileName,
|
||||
const std::multimap<ReducedFraction, std::string> &trackLyrics);
|
||||
const QList<std::multimap<ReducedFraction, std::string> >*
|
||||
getLyrics(const QString &fileName);
|
||||
QString charset(const QString &fileName) const;
|
||||
void setCharset(const QString &fileName, const QString &charset);
|
||||
|
||||
// human performance: is MIDI unaligned
|
||||
bool isHumanPerformance(const QString &fileName) const;
|
||||
void setHumanPerformance(const QString &fileName, bool value);
|
||||
|
||||
const std::set<ReducedFraction>* getHumanBeats(const QString &fileName) const;
|
||||
void setHumanBeats(const QString &fileName, const HumanBeatData &beatData);
|
||||
// time sig can be zero (not specified) if, when opened,
|
||||
// MIDI file was not detected as human-performed
|
||||
ReducedFraction timeSignature(const QString &fileName) const;
|
||||
void setTimeSignature(const QString &fileName, const ReducedFraction &value);
|
||||
|
||||
// quantization
|
||||
MidiOperation::QuantValue quantValue(const QString &fileName) const;
|
||||
void setQuantValue(const QString &fileName, MidiOperation::QuantValue value);
|
||||
|
||||
// left/right hand separation
|
||||
bool needToSplit(const QString &fileName, int trackIndex) const;
|
||||
void setNeedToSplit(const QString &fileName, int trackIndex, bool value);
|
||||
|
||||
private:
|
||||
static MidiOperation::QuantValue defaultQuantValue();
|
||||
|
||||
struct MidiDataStore
|
||||
{
|
||||
QByteArray HHeaderData;
|
||||
QByteArray VHeaderData;
|
||||
QList<TrackData> tracksData;
|
||||
// tracks of <tick, lyric fragment> from karaoke files
|
||||
// QList of lyric tracks - there can be multiple lyric tracks,
|
||||
// lyric track count != MIDI chord track count in general
|
||||
QList<std::multimap<ReducedFraction, std::string>> lyricTracks;
|
||||
// default values - when MIDI is opened
|
||||
int selectedRow = 0;
|
||||
MidiFile midiFile;
|
||||
QString charset = MidiCharset::defaultCharset();
|
||||
bool isHumanPerformance = false;
|
||||
std::map<int, bool> needLRhandSplit; // <track index, value = false by default>
|
||||
MidiOperation::QuantValue quantValue = defaultQuantValue();
|
||||
// human performance
|
||||
HumanBeatData humanBeatData;
|
||||
};
|
||||
|
||||
QMap<QString, MidiDataStore> data; // <file name, tracks data>
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_DATA_H
|
|
@ -1,5 +1,4 @@
|
|||
#include "importmidi_opdelegate.h"
|
||||
#include "importmidi_opmodel.h"
|
||||
#include "importmidi_delegate.h"
|
||||
|
||||
|
||||
namespace Ms {
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef IMPORTMIDI_OPDELEGATE_H
|
||||
#define IMPORTMIDI_OPDELEGATE_H
|
||||
#ifndef IMPORTMIDI_DELEGATE_H
|
||||
#define IMPORTMIDI_DELEGATE_H
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
@ -31,4 +31,4 @@ class OperationsDelegate : public QStyledItemDelegate
|
|||
} // namespace Ms
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_OPDELEGATE_H
|
||||
#endif // IMPORTMIDI_DELEGATE_H
|
|
@ -5,7 +5,9 @@
|
|||
#include "libmscore/drumset.h"
|
||||
#include "importmidi_chord.h"
|
||||
#include "importmidi_tuplet.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "libmscore/score.h"
|
||||
#include "midi/midifile.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
|
@ -156,9 +158,8 @@ void splitDrumTracks(std::multimap<int, MTrack> &tracks)
|
|||
for (auto it = tracks.begin(); it != tracks.end(); ++it) {
|
||||
if (!it->second.mtrack->drumTrack())
|
||||
continue;
|
||||
const auto operations = preferences.midiImportOperations.trackOperations(
|
||||
it->second.indexOfOperation);
|
||||
if (!operations.splitDrums.doSplit)
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
if (!opers.doStaffSplit.value(it->second.indexOfOperation))
|
||||
continue;
|
||||
const auto newTracks = splitDrumTrack(it->second);
|
||||
const int trackIndex = it->first;
|
||||
|
@ -185,16 +186,13 @@ void setStaffBracketForDrums(QList<MTrack> &tracks)
|
|||
int counter = 0;
|
||||
Staff *firstDrumStaff = nullptr;
|
||||
int opIndex = -1;
|
||||
const auto &opers = preferences.midiImportOperations;
|
||||
|
||||
for (const MTrack &track: tracks) {
|
||||
if (track.mtrack->drumTrack()) {
|
||||
if (opIndex != track.indexOfOperation) {
|
||||
opIndex = track.indexOfOperation;
|
||||
setBracket(firstDrumStaff, counter);
|
||||
if (opers.trackOperations(track.indexOfOperation).splitDrums.showStaffBracket) {
|
||||
opIndex = track.indexOfOperation;
|
||||
firstDrumStaff = track.staff;
|
||||
}
|
||||
firstDrumStaff = track.staff;
|
||||
}
|
||||
++counter;
|
||||
}
|
||||
|
@ -236,13 +234,13 @@ ReducedFraction findOptimalNoteOffTime(
|
|||
|
||||
void removeRests(std::multimap<int, MTrack> &tracks, const TimeSigMap *sigmap)
|
||||
{
|
||||
const auto &opers = preferences.midiImportOperations;
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
|
||||
for (auto &trackItem: tracks) {
|
||||
MTrack &track = trackItem.second;
|
||||
if (!track.mtrack->drumTrack())
|
||||
continue;
|
||||
if (!opers.trackOperations(track.indexOfOperation).removeDrumRests)
|
||||
if (!opers.simplifyDurations.value(track.indexOfOperation))
|
||||
continue;
|
||||
bool changed = false;
|
||||
// all chords here with the same voice should have different onTime values
|
||||
|
|
|
@ -1,65 +1,69 @@
|
|||
#include "importmidi_inner.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "preferences.h"
|
||||
#include "libmscore/durationtype.h"
|
||||
#include "midi/midifile.h"
|
||||
|
||||
|
||||
namespace Ms {
|
||||
namespace Meter {
|
||||
|
||||
ReducedFraction userTimeSigToFraction(const MidiTimeSig &timeSig)
|
||||
ReducedFraction userTimeSigToFraction(
|
||||
MidiOperations::TimeSigNumerator timeSigNumerator,
|
||||
MidiOperations::TimeSigDenominator timeSigDenominator)
|
||||
{
|
||||
int numerator = 4;
|
||||
int denominator = 4;
|
||||
|
||||
switch (timeSig.numerator) {
|
||||
case MidiOperation::TimeSigNumerator::_2:
|
||||
switch (timeSigNumerator) {
|
||||
case MidiOperations::TimeSigNumerator::_2:
|
||||
numerator = 2;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_3:
|
||||
case MidiOperations::TimeSigNumerator::_3:
|
||||
numerator = 3;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_4:
|
||||
case MidiOperations::TimeSigNumerator::_4:
|
||||
numerator = 4;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_5:
|
||||
case MidiOperations::TimeSigNumerator::_5:
|
||||
numerator = 5;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_6:
|
||||
case MidiOperations::TimeSigNumerator::_6:
|
||||
numerator = 6;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_7:
|
||||
case MidiOperations::TimeSigNumerator::_7:
|
||||
numerator = 7;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_9:
|
||||
case MidiOperations::TimeSigNumerator::_9:
|
||||
numerator = 9;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_12:
|
||||
case MidiOperations::TimeSigNumerator::_12:
|
||||
numerator = 12;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_15:
|
||||
case MidiOperations::TimeSigNumerator::_15:
|
||||
numerator = 15;
|
||||
break;
|
||||
case MidiOperation::TimeSigNumerator::_21:
|
||||
case MidiOperations::TimeSigNumerator::_21:
|
||||
numerator = 21;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (timeSig.denominator) {
|
||||
case MidiOperation::TimeSigDenominator::_2:
|
||||
switch (timeSigDenominator) {
|
||||
case MidiOperations::TimeSigDenominator::_2:
|
||||
denominator = 2;
|
||||
break;
|
||||
case MidiOperation::TimeSigDenominator::_4:
|
||||
case MidiOperations::TimeSigDenominator::_4:
|
||||
denominator = 4;
|
||||
break;
|
||||
case MidiOperation::TimeSigDenominator::_8:
|
||||
case MidiOperations::TimeSigDenominator::_8:
|
||||
denominator = 8;
|
||||
break;
|
||||
case MidiOperation::TimeSigDenominator::_16:
|
||||
case MidiOperations::TimeSigDenominator::_16:
|
||||
denominator = 16;
|
||||
break;
|
||||
case MidiOperation::TimeSigDenominator::_32:
|
||||
case MidiOperations::TimeSigDenominator::_32:
|
||||
denominator = 32;
|
||||
break;
|
||||
default:
|
||||
|
@ -69,50 +73,50 @@ ReducedFraction userTimeSigToFraction(const MidiTimeSig &timeSig)
|
|||
return ReducedFraction(numerator, denominator);
|
||||
}
|
||||
|
||||
MidiOperation::TimeSigNumerator fractionNumeratorToUserValue(int n)
|
||||
MidiOperations::TimeSigNumerator fractionNumeratorToUserValue(int n)
|
||||
{
|
||||
MidiOperation::TimeSigNumerator numerator = MidiOperation::TimeSigNumerator::_4;
|
||||
MidiOperations::TimeSigNumerator numerator = MidiOperations::TimeSigNumerator::_4;
|
||||
|
||||
if (n == 2)
|
||||
numerator = MidiOperation::TimeSigNumerator::_2;
|
||||
numerator = MidiOperations::TimeSigNumerator::_2;
|
||||
else if (n == 3)
|
||||
numerator = MidiOperation::TimeSigNumerator::_3;
|
||||
numerator = MidiOperations::TimeSigNumerator::_3;
|
||||
else if (n == 4)
|
||||
numerator = MidiOperation::TimeSigNumerator::_4;
|
||||
numerator = MidiOperations::TimeSigNumerator::_4;
|
||||
else if (n == 5)
|
||||
numerator = MidiOperation::TimeSigNumerator::_5;
|
||||
numerator = MidiOperations::TimeSigNumerator::_5;
|
||||
else if (n == 6)
|
||||
numerator = MidiOperation::TimeSigNumerator::_6;
|
||||
numerator = MidiOperations::TimeSigNumerator::_6;
|
||||
else if (n == 7)
|
||||
numerator = MidiOperation::TimeSigNumerator::_7;
|
||||
numerator = MidiOperations::TimeSigNumerator::_7;
|
||||
else if (n == 9)
|
||||
numerator = MidiOperation::TimeSigNumerator::_9;
|
||||
numerator = MidiOperations::TimeSigNumerator::_9;
|
||||
else if (n == 12)
|
||||
numerator = MidiOperation::TimeSigNumerator::_12;
|
||||
numerator = MidiOperations::TimeSigNumerator::_12;
|
||||
else if (n == 15)
|
||||
numerator = MidiOperation::TimeSigNumerator::_15;
|
||||
numerator = MidiOperations::TimeSigNumerator::_15;
|
||||
else if (n == 21)
|
||||
numerator = MidiOperation::TimeSigNumerator::_21;
|
||||
numerator = MidiOperations::TimeSigNumerator::_21;
|
||||
else
|
||||
Q_ASSERT_X(false, "Meter::fractionNumeratorToUserValue", "Unknown numerator");
|
||||
|
||||
return numerator;
|
||||
}
|
||||
|
||||
MidiOperation::TimeSigDenominator fractionDenominatorToUserValue(int z)
|
||||
MidiOperations::TimeSigDenominator fractionDenominatorToUserValue(int z)
|
||||
{
|
||||
MidiOperation::TimeSigDenominator denominator = MidiOperation::TimeSigDenominator::_4;
|
||||
MidiOperations::TimeSigDenominator denominator = MidiOperations::TimeSigDenominator::_4;
|
||||
|
||||
if (z == 2)
|
||||
denominator = MidiOperation::TimeSigDenominator::_2;
|
||||
denominator = MidiOperations::TimeSigDenominator::_2;
|
||||
else if (z == 4)
|
||||
denominator = MidiOperation::TimeSigDenominator::_4;
|
||||
denominator = MidiOperations::TimeSigDenominator::_4;
|
||||
else if (z == 8)
|
||||
denominator = MidiOperation::TimeSigDenominator::_8;
|
||||
denominator = MidiOperations::TimeSigDenominator::_8;
|
||||
else if (z == 16)
|
||||
denominator = MidiOperation::TimeSigDenominator::_16;
|
||||
denominator = MidiOperations::TimeSigDenominator::_16;
|
||||
else if (z == 32)
|
||||
denominator = MidiOperation::TimeSigDenominator::_32;
|
||||
denominator = MidiOperations::TimeSigDenominator::_32;
|
||||
else
|
||||
Q_ASSERT_X(false, "Meter::fractionDenominatorToUserValue", "Unknown denominator");
|
||||
|
||||
|
@ -147,7 +151,7 @@ namespace MidiCharset {
|
|||
QString convertToCharset(const std::string &text)
|
||||
{
|
||||
// charset for the current MIDI file
|
||||
QString charset = preferences.midiImportOperations.charset();
|
||||
QString charset = preferences.midiImportOperations.data()->charset;
|
||||
auto *codec = QTextCodec::codecForName(charset.toLatin1());
|
||||
if (codec)
|
||||
return codec->toUnicode(text.c_str());
|
||||
|
|
|
@ -49,9 +49,11 @@ struct DivisionInfo
|
|||
|
||||
enum class DurationType : char;
|
||||
|
||||
ReducedFraction userTimeSigToFraction(const MidiTimeSig &timeSig);
|
||||
MidiOperation::TimeSigNumerator fractionNumeratorToUserValue(int n);
|
||||
MidiOperation::TimeSigDenominator fractionDenominatorToUserValue(int z);
|
||||
ReducedFraction userTimeSigToFraction(
|
||||
MidiOperations::TimeSigNumerator timeSigNumerator,
|
||||
MidiOperations::TimeSigDenominator timeSigDenominator);
|
||||
MidiOperations::TimeSigNumerator fractionNumeratorToUserValue(int n);
|
||||
MidiOperations::TimeSigDenominator fractionDenominatorToUserValue(int z);
|
||||
|
||||
} // namespace Meter
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "importmidi_inner.h"
|
||||
#include "importmidi_fraction.h"
|
||||
#include "importmidi_chord.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "preferences.h"
|
||||
|
||||
|
||||
|
@ -386,9 +387,9 @@ void splitByFixedPitch(std::multimap<int, MTrack> &tracks,
|
|||
std::multimap<int, MTrack>::iterator &it)
|
||||
{
|
||||
auto &srcTrack = it->second;
|
||||
const auto trackOpers = preferences.midiImportOperations.trackOperations(srcTrack.indexOfOperation);
|
||||
const int splitPitch = 12 * (int)trackOpers.LHRH.splitPitchOctave
|
||||
+ (int)trackOpers.LHRH.splitPitchNote;
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int splitPitch = 12 * (int)opers.staffSplitOctave.value(srcTrack.indexOfOperation)
|
||||
+ (int)opers.staffSplitNote.value(srcTrack.indexOfOperation);
|
||||
std::multimap<ReducedFraction, MidiChord> leftHandChords;
|
||||
|
||||
for (auto i = srcTrack.chords.begin(); i != srcTrack.chords.end(); ) {
|
||||
|
@ -420,20 +421,19 @@ void splitIntoLeftRightHands(std::multimap<int, MTrack> &tracks)
|
|||
for (auto it = tracks.begin(); it != tracks.end(); ++it) {
|
||||
if (it->second.mtrack->drumTrack())
|
||||
continue;
|
||||
const auto operations = preferences.midiImportOperations.trackOperations(
|
||||
it->second.indexOfOperation);
|
||||
if (!operations.LHRH.doIt)
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
if (!opers.doStaffSplit.value(it->second.indexOfOperation))
|
||||
continue;
|
||||
// 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"
|
||||
switch (operations.LHRH.method) {
|
||||
case MidiOperation::LHRHMethod::HAND_WIDTH:
|
||||
switch (opers.staffSplitMethod.value(it->second.indexOfOperation)) {
|
||||
case MidiOperations::StaffSplitMethod::HAND_WIDTH:
|
||||
splitByHandWidth(tracks, it);
|
||||
break;
|
||||
case MidiOperation::LHRHMethod::SPECIFIED_PITCH:
|
||||
case MidiOperations::StaffSplitMethod::SPECIFIED_PITCH:
|
||||
splitByFixedPitch(tracks, it);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "importmidi_inner.h"
|
||||
#include "importmidi_fraction.h"
|
||||
#include "importmidi_chord.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "libmscore/box.h"
|
||||
#include "libmscore/element.h"
|
||||
#include "libmscore/measurebase.h"
|
||||
|
@ -171,19 +172,19 @@ void extractLyricsToMidiData(const MidiFile *mf)
|
|||
for (const auto &t: mf->tracks()) {
|
||||
const auto lyrics = extractLyricsFromTrack(t, mf->division());
|
||||
if (!lyrics.empty())
|
||||
preferences.midiImportOperations.addTrackLyrics(lyrics);
|
||||
preferences.midiImportOperations.data()->lyricTracks.push_back(lyrics);
|
||||
}
|
||||
}
|
||||
|
||||
void assignLyricsToTracks(QList<MTrack> &tracks)
|
||||
{
|
||||
std::set<int> usedTracks;
|
||||
const auto *lyricTracks = preferences.midiImportOperations.getLyrics();
|
||||
if (!lyricTracks)
|
||||
const auto &lyricTracks = preferences.midiImportOperations.data()->lyricTracks;
|
||||
if (lyricTracks.isEmpty())
|
||||
return;
|
||||
|
||||
for (int i = 0; i != lyricTracks->size(); ++i) {
|
||||
const BestTrack bestTrack = findBestTrack(tracks, (*lyricTracks)[i], usedTracks);
|
||||
for (int i = 0; i != lyricTracks.size(); ++i) {
|
||||
const BestTrack bestTrack = findBestTrack(tracks, lyricTracks[i], usedTracks);
|
||||
if (bestTrack.index >= 0) {
|
||||
usedTracks.insert(bestTrack.index);
|
||||
tracks[bestTrack.index].initLyricTrackIndex = i;
|
||||
|
@ -194,11 +195,11 @@ void assignLyricsToTracks(QList<MTrack> &tracks)
|
|||
void setInitialLyricsFromMidiData(const QList<MTrack> &tracks)
|
||||
{
|
||||
std::set<int> usedTracks;
|
||||
const auto *lyricTracks = preferences.midiImportOperations.getLyrics();
|
||||
if (!lyricTracks)
|
||||
const auto &lyricTracks = preferences.midiImportOperations.data()->lyricTracks;
|
||||
if (lyricTracks.isEmpty())
|
||||
return;
|
||||
|
||||
for (const auto &lyricTrack: *lyricTracks) {
|
||||
for (const auto &lyricTrack: lyricTracks) {
|
||||
const BestTrack bestTrack = findBestTrack(tracks, lyricTrack, usedTracks);
|
||||
if (bestTrack.index >= 0) {
|
||||
usedTracks.insert(bestTrack.index);
|
||||
|
@ -228,14 +229,14 @@ std::vector<std::pair<ReducedFraction, ReducedFraction> > findMatchedLyricTimes(
|
|||
|
||||
void setLyricsFromOperations(const QList<MTrack> &tracks)
|
||||
{
|
||||
const auto *lyricTracks = preferences.midiImportOperations.getLyrics();
|
||||
if (!lyricTracks)
|
||||
const auto &lyricTracks = preferences.midiImportOperations.data()->lyricTracks;
|
||||
if (lyricTracks.isEmpty())
|
||||
return;
|
||||
for (const auto &track: tracks) {
|
||||
const auto opers = preferences.midiImportOperations.trackOperations(
|
||||
track.indexOfOperation);
|
||||
if (opers.lyricTrackIndex >= 0 && opers.lyricTrackIndex < lyricTracks->size()) {
|
||||
const auto &lyricTrack = (*lyricTracks)[opers.lyricTrackIndex];
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int lyricTrackIndex = opers.lyricTrackIndex.value(track.indexOfOperation);
|
||||
if (lyricTrackIndex >= 0 && lyricTrackIndex < lyricTracks.size()) {
|
||||
const auto &lyricTrack = lyricTracks[lyricTrackIndex];
|
||||
const auto matchedLyricTimes = findMatchedLyricTimes(track.chords, lyricTrack);
|
||||
|
||||
addLyricsToScore(lyricTrack, matchedLyricTimes, track.staff);
|
||||
|
@ -245,7 +246,8 @@ void setLyricsFromOperations(const QList<MTrack> &tracks)
|
|||
|
||||
void setLyricsToScore(const QList<MTrack> &tracks)
|
||||
{
|
||||
if (preferences.midiImportOperations.count() == 0)
|
||||
const auto *data = preferences.midiImportOperations.data();
|
||||
if (data->isNewlyOpened)
|
||||
setInitialLyricsFromMidiData(tracks);
|
||||
else
|
||||
setLyricsFromOperations(tracks);
|
||||
|
@ -254,12 +256,13 @@ void setLyricsToScore(const QList<MTrack> &tracks)
|
|||
QList<std::string> makeLyricsListForUI()
|
||||
{
|
||||
QList<std::string> list;
|
||||
const auto *lyrics = preferences.midiImportOperations.getLyrics();
|
||||
if (!lyrics)
|
||||
const auto &lyrics = preferences.midiImportOperations.data()->lyricTracks;
|
||||
if (lyrics.isEmpty())
|
||||
return list;
|
||||
|
||||
const unsigned int symbolLimit = 16;
|
||||
|
||||
for (const auto &trackLyric: *lyrics) {
|
||||
for (const auto &trackLyric: lyrics) {
|
||||
std::string lyricText;
|
||||
|
||||
for (const auto &lyric: trackLyric) {
|
||||
|
|
403
mscore/importmidi_model.cpp
Normal file
403
mscore/importmidi_model.cpp
Normal file
|
@ -0,0 +1,403 @@
|
|||
#include "importmidi_model.h"
|
||||
|
||||
|
||||
//struct TrackCol {
|
||||
// enum {
|
||||
// DO_IMPORT = 0,
|
||||
// STAFF_NAME,
|
||||
// INSTRUMENT,
|
||||
// LYRICS,
|
||||
// QUANTIZATION,
|
||||
// HUMAN,
|
||||
// VOICE_COUNT,
|
||||
// TUPLETS,
|
||||
// SPLIT_STAFF,
|
||||
// CHANGE_CLEF,
|
||||
// SIMPLIFY,
|
||||
// STACCATO,
|
||||
// USE_DOTS,
|
||||
// TIME_SIG,
|
||||
// PICKUP_BAR,
|
||||
// SWING,
|
||||
// COL_COUNT
|
||||
// };
|
||||
// };
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class TracksModel::Column
|
||||
{
|
||||
public:
|
||||
Column(MidiOperations::Opers &opers)
|
||||
: _opers(opers), _isEditable(true), _hasCharset(false)
|
||||
{}
|
||||
virtual ~Column() {}
|
||||
|
||||
virtual QVariant value(int trackIndex) const = 0;
|
||||
virtual void setValue(const QVariant &value, int trackIndex) = 0;
|
||||
virtual QString headerName() const = 0;
|
||||
virtual QStringList valueList() const { return _values; }
|
||||
bool isEditable() const { return _isEditable; }
|
||||
bool hasCharset() const { return _hasCharset; }
|
||||
|
||||
protected:
|
||||
MidiOperations::Opers &_opers;
|
||||
QStringList _values;
|
||||
bool _isEditable;
|
||||
bool _hasCharset;
|
||||
};
|
||||
|
||||
|
||||
TracksModel::TracksModel()
|
||||
: _trackCount(0)
|
||||
, _frozenColCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
TracksModel::~TracksModel()
|
||||
{
|
||||
}
|
||||
|
||||
void TracksModel::reset(const MidiOperations::Opers &opers,
|
||||
const QList<std::string> &lyricsList,
|
||||
int trackCount)
|
||||
{
|
||||
_trackOpers = opers;
|
||||
_columns.clear();
|
||||
_trackCount = trackCount;
|
||||
_frozenColCount = 0;
|
||||
if (trackCount == 0)
|
||||
return;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
struct Import : Column {
|
||||
Import(MidiOperations::Opers &opers) : Column(opers) {}
|
||||
|
||||
QString headerName() const { return "Import"; }
|
||||
QVariant value(int trackIndex) const
|
||||
{
|
||||
return _opers.doImport.value(trackIndex);
|
||||
}
|
||||
void setValue(const QVariant &value, int trackIndex)
|
||||
{
|
||||
_opers.doImport.setValue(trackIndex, value.toBool());
|
||||
}
|
||||
};
|
||||
++_frozenColCount;
|
||||
_columns.push_back(std::unique_ptr<Column>(new Import(_trackOpers)));
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
struct InstrumentName : Column {
|
||||
InstrumentName(MidiOperations::Opers &opers) : Column(opers)
|
||||
{
|
||||
_isEditable = false;
|
||||
}
|
||||
QString headerName() const { return "Sound"; }
|
||||
QVariant value(int trackIndex) const
|
||||
{
|
||||
return _opers.instrumentName.value(trackIndex);
|
||||
}
|
||||
void setValue(const QVariant &/*value*/, int /*trackIndex*/) {}
|
||||
};
|
||||
++_frozenColCount;
|
||||
_columns.push_back(std::unique_ptr<Column>(new InstrumentName(_trackOpers)));
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
bool hasStaffName = false;
|
||||
for (int i = 0; i != _trackCount; ++i) {
|
||||
if (_trackOpers.staffName.value(i) != "") {
|
||||
hasStaffName = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasStaffName) {
|
||||
struct StaffName : Column {
|
||||
StaffName(MidiOperations::Opers &opers) : Column(opers)
|
||||
{
|
||||
_isEditable = false;
|
||||
_hasCharset = true;
|
||||
}
|
||||
QString headerName() const { return "Staff name"; }
|
||||
QVariant value(int trackIndex) const
|
||||
{
|
||||
return MidiCharset::convertToCharset(_opers.staffName.value(trackIndex));
|
||||
}
|
||||
void setValue(const QVariant &/*value*/, int /*trackIndex*/) {}
|
||||
};
|
||||
++_frozenColCount;
|
||||
_columns.push_back(std::unique_ptr<Column>(new StaffName(_trackOpers)));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
if (!lyricsList.isEmpty()) {
|
||||
struct Lyrics : Column {
|
||||
Lyrics(MidiOperations::Opers &opers, const QList<std::string> &lyricsList)
|
||||
: Column(opers), _lyricsList(lyricsList)
|
||||
{
|
||||
_hasCharset = true;
|
||||
}
|
||||
QString headerName() const { return "Lyrics"; }
|
||||
QVariant value(int trackIndex) const
|
||||
{
|
||||
int index = _opers.lyricTrackIndex.value(trackIndex);
|
||||
if (index >= 0)
|
||||
return MidiCharset::convertToCharset(_lyricsList[index]);
|
||||
return "";
|
||||
}
|
||||
void setValue(const QVariant &value, int trackIndex)
|
||||
{
|
||||
_opers.lyricTrackIndex.setValue(trackIndex, value.toInt());
|
||||
}
|
||||
QStringList valueList() const
|
||||
{
|
||||
auto list = QStringList("");
|
||||
for (const auto &lyric: _lyricsList)
|
||||
list.append(MidiCharset::convertToCharset(lyric));
|
||||
return list;
|
||||
}
|
||||
private:
|
||||
QList<std::string> _lyricsList;
|
||||
};
|
||||
_columns.push_back(std::unique_ptr<Column>(new Lyrics(_trackOpers, lyricsList)));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
struct QuantValue : Column {
|
||||
QuantValue(MidiOperations::Opers &opers) : Column(opers)
|
||||
{
|
||||
_values.push_back("Default");
|
||||
_values.push_back("Quarter");
|
||||
_values.push_back("Eighth");
|
||||
_values.push_back("16th");
|
||||
_values.push_back("32nd");
|
||||
_values.push_back("64th");
|
||||
_values.push_back("128th");
|
||||
}
|
||||
QString headerName() const { return "Quantization"; }
|
||||
QVariant value(int trackIndex) const
|
||||
{
|
||||
return _values[(int)_opers.quantValue.value(trackIndex)];
|
||||
}
|
||||
void setValue(const QVariant &value, int trackIndex)
|
||||
{
|
||||
_opers.quantValue.setValue(trackIndex, (MidiOperations::QuantValue)value.toInt());
|
||||
}
|
||||
};
|
||||
_columns.push_back(std::unique_ptr<Column>(new QuantValue(_trackOpers)));
|
||||
}
|
||||
|
||||
void TracksModel::clear()
|
||||
{
|
||||
beginResetModel();
|
||||
_trackCount = 0;
|
||||
_trackOpers = MidiOperations::Opers();
|
||||
_columns.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
const MidiOperations::Opers& TracksModel::trackOpers() const
|
||||
{
|
||||
return _trackOpers;
|
||||
}
|
||||
|
||||
void TracksModel::setTrackShuffleIndex(int trackIndex, int newIndex)
|
||||
{
|
||||
if (!isTrackIndexValid(trackIndex))
|
||||
return;
|
||||
_trackOpers.trackIndexAfterShuffle.setValue(trackIndex, newIndex);
|
||||
}
|
||||
|
||||
void TracksModel::updateCharset()
|
||||
{
|
||||
for (int i = 0; i != (int)_columns.size(); ++i) {
|
||||
if (_columns[i]->hasCharset())
|
||||
forceColumnDataChanged(i);
|
||||
}
|
||||
}
|
||||
|
||||
int TracksModel::rowFromTrackIndex(int trackIndex) const
|
||||
{
|
||||
// first row reserved for all tracks if track count > 1
|
||||
return (_trackCount > 1) ? trackIndex + 1 : trackIndex;
|
||||
}
|
||||
|
||||
int TracksModel::trackIndexFromRow(int row) const
|
||||
{
|
||||
// first row reserved for all tracks if track count > 1
|
||||
// return -1 if row is all tracks row
|
||||
return (_trackCount > 1) ? row - 1 : row;
|
||||
}
|
||||
|
||||
int TracksModel::trackCountForImport() const
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i != _trackCount; ++i) {
|
||||
if (_trackOpers.doImport.value(i))
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int TracksModel::frozenRowCount() const
|
||||
{
|
||||
if (_trackCount > 1)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TracksModel::frozenColCount() const
|
||||
{
|
||||
return _frozenColCount;
|
||||
}
|
||||
|
||||
int TracksModel::rowCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return (_trackCount > 1) ? _trackCount + 1 : _trackCount;
|
||||
}
|
||||
|
||||
int TracksModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return _columns.size();
|
||||
}
|
||||
|
||||
QVariant TracksModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
const int trackIndex = trackIndexFromRow(index.row());
|
||||
if (!isTrackIndexValid(trackIndex) || !isColumnValid(index.column()))
|
||||
return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
if (trackIndex == -1) { // all tracks
|
||||
QVariant value = _columns[index.column()]->value(0);
|
||||
if (value.type() == QVariant::String) {
|
||||
for (int i = 1; i < _trackCount; ++i) {
|
||||
if (_columns[index.column()]->value(i).toString() != value.toString())
|
||||
return "...";
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
else {
|
||||
QVariant value = _columns[index.column()]->value(trackIndex);
|
||||
if (value.type() == QVariant::String)
|
||||
return value.toString();
|
||||
}
|
||||
break;
|
||||
case Qt::EditRole:
|
||||
if (_columns[index.column()]->isEditable()) {
|
||||
QVariant value = _columns[index.column()]->value(trackIndex);
|
||||
if (value.type() == QVariant::StringList)
|
||||
return _columns[index.column()]->valueList();
|
||||
}
|
||||
break;
|
||||
case Qt::CheckStateRole:
|
||||
if (trackIndex == -1) {
|
||||
QVariant value = _columns[index.column()]->value(0);
|
||||
if (value.type() == QVariant::Bool) {
|
||||
for (int i = 1; i < _trackCount; ++i) {
|
||||
if (_columns[index.column()]->value(i).toBool() != value.toBool())
|
||||
return Qt::PartiallyChecked;
|
||||
}
|
||||
return (value.toBool()) ? Qt::Checked : Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
else {
|
||||
QVariant value = _columns[index.column()]->value(trackIndex);
|
||||
if (value.type() == QVariant::Bool)
|
||||
return (value.toBool()) ? Qt::Checked : Qt::Unchecked;
|
||||
}
|
||||
break;
|
||||
case Qt::TextAlignmentRole:
|
||||
// if (!columns[index.column()]->valuesList.empty())
|
||||
// return Qt::AlignLeft + Qt::AlignVCenter;
|
||||
return Qt::AlignCenter;
|
||||
break;
|
||||
case Qt::ToolTipRole:
|
||||
if (trackIndex != -1) {
|
||||
QVariant value = _columns[index.column()]->value(trackIndex);
|
||||
if (value.type() == QVariant::String
|
||||
&& _columns[index.column()]->valueList().empty()) {
|
||||
return MidiCharset::convertToCharset(value.toString().toStdString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags TracksModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
Qt::ItemFlags flags = Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
if (_columns[index.column()]->value(0).type() == QVariant::Bool)
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
if (_columns[index.column()]->isEditable())
|
||||
flags |= Qt::ItemIsEditable;
|
||||
return flags;
|
||||
}
|
||||
|
||||
void TracksModel::forceRowDataChanged(int row)
|
||||
{
|
||||
const auto begIndex = this->index(row, 0);
|
||||
const auto endIndex = this->index(row, columnCount(QModelIndex()));
|
||||
emit dataChanged(begIndex, endIndex);
|
||||
}
|
||||
|
||||
void TracksModel::forceColumnDataChanged(int col)
|
||||
{
|
||||
const auto begIndex = this->index(0, col);
|
||||
const auto endIndex = this->index(rowCount(QModelIndex()), col);
|
||||
emit dataChanged(begIndex, endIndex);
|
||||
}
|
||||
|
||||
bool TracksModel::setData(const QModelIndex &index, const QVariant &value, int /*role*/)
|
||||
{
|
||||
const int trackIndex = trackIndexFromRow(index.row());
|
||||
if (!isTrackIndexValid(trackIndex) || !isColumnValid(index.column()))
|
||||
return false;
|
||||
|
||||
if (trackIndex == -1) { // all tracks row
|
||||
for (int i = 0; i != _trackCount; ++i)
|
||||
_columns[index.column()]->setValue(value, i);
|
||||
forceColumnDataChanged(index.column());
|
||||
}
|
||||
else {
|
||||
_columns[index.column()]->setValue(value, index.row());
|
||||
emit dataChanged(index, index);
|
||||
if (_trackCount > 1) // update 'all tracks' row
|
||||
forceRowDataChanged(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant TracksModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
return QCoreApplication::translate("MIDI import track list",
|
||||
_columns[section]->headerName().toStdString().c_str());
|
||||
}
|
||||
else if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
|
||||
if (_trackCount > 1 && section == 0)
|
||||
return QCoreApplication::translate("MIDI import operations", "All");
|
||||
return section + 1;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool TracksModel::isTrackIndexValid(int trackIndex) const
|
||||
{
|
||||
return trackIndex >= 0 && trackIndex < _trackCount;
|
||||
}
|
||||
|
||||
bool TracksModel::isColumnValid(int column) const
|
||||
{
|
||||
return (column >= 0 && column < (int)_columns.size());
|
||||
}
|
||||
|
||||
} // namespace Ms
|
55
mscore/importmidi_model.h
Normal file
55
mscore/importmidi_model.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef IMPORTMIDI_MODEL_H
|
||||
#define IMPORTMIDI_MODEL_H
|
||||
|
||||
#include "importmidi_operations.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class TracksModel : public QAbstractTableModel
|
||||
{
|
||||
public:
|
||||
TracksModel();
|
||||
~TracksModel();
|
||||
|
||||
void reset(const MidiOperations::Opers &opers,
|
||||
const QList<std::string> &lyricsList,
|
||||
int trackCount);
|
||||
void clear();
|
||||
void setTrackShuffleIndex(int trackIndex, int newIndex);
|
||||
void updateCharset();
|
||||
|
||||
const MidiOperations::Opers& trackOpers() const;
|
||||
int trackCount() const { return _trackCount; }
|
||||
int trackCountForImport() const;
|
||||
int frozenRowCount() const;
|
||||
int frozenColCount() const;
|
||||
|
||||
int rowCount(const QModelIndex &/*parent*/) const;
|
||||
int columnCount(const QModelIndex &/*parent*/) const;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||
|
||||
private:
|
||||
bool isTrackIndexValid(int trackIndex) const;
|
||||
bool isColumnValid(int column) const;
|
||||
int rowFromTrackIndex(int trackIndex) const;
|
||||
int trackIndexFromRow(int row) const;
|
||||
void forceRowDataChanged(int row);
|
||||
void forceColumnDataChanged(int col);
|
||||
|
||||
MidiOperations::Opers _trackOpers;
|
||||
int _trackCount;
|
||||
int _frozenColCount;
|
||||
class Column;
|
||||
std::vector<std::unique_ptr<Column>> _columns;
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_MODEL_H
|
|
@ -9,150 +9,90 @@
|
|||
namespace Ms {
|
||||
|
||||
// all enums below should have default indexes like 0, 1, 2...
|
||||
// text names for enum items are in OperationsModel class
|
||||
// text names for enum items are in TracksModel class
|
||||
|
||||
struct MidiOperation
|
||||
{
|
||||
enum class Type {
|
||||
DO_IMPORT = 0,
|
||||
namespace MidiOperations {
|
||||
|
||||
QUANT_VALUE,
|
||||
QUANT_HUMAN,
|
||||
|
||||
TIME_SIG_NUMERATOR,
|
||||
TIME_SIG_DENOMINATOR,
|
||||
|
||||
DO_LHRH_SEPARATION,
|
||||
LHRH_METHOD,
|
||||
LHRH_SPLIT_OCTAVE,
|
||||
LHRH_SPLIT_NOTE,
|
||||
|
||||
USE_DOTS,
|
||||
|
||||
SIMPLIFY_DURATIONS,
|
||||
SHOW_STACCATO,
|
||||
SEPARATE_VOICES,
|
||||
|
||||
ALLOWED_VOICES,
|
||||
|
||||
TUPLET_SEARCH,
|
||||
TUPLET_2,
|
||||
TUPLET_3,
|
||||
TUPLET_4,
|
||||
TUPLET_5,
|
||||
TUPLET_7,
|
||||
TUPLET_9,
|
||||
|
||||
CHANGE_CLEF,
|
||||
|
||||
SPLIT_DRUMS,
|
||||
SHOW_STAFF_BRACKET,
|
||||
|
||||
REMOVE_DRUM_RESTS,
|
||||
|
||||
PICKUP_MEASURE,
|
||||
|
||||
SWING,
|
||||
|
||||
LYRIC_TRACK_INDEX
|
||||
} type;
|
||||
|
||||
QVariant value;
|
||||
|
||||
enum class QuantValue {
|
||||
FROM_PREFERENCES = 0,
|
||||
N_4,
|
||||
N_8,
|
||||
N_16,
|
||||
N_32,
|
||||
N_64,
|
||||
N_128
|
||||
};
|
||||
|
||||
enum class TimeSigNumerator {
|
||||
_2 = 0,
|
||||
_3,
|
||||
_4,
|
||||
_5,
|
||||
_6,
|
||||
_7,
|
||||
_9,
|
||||
_12,
|
||||
_15,
|
||||
_21
|
||||
};
|
||||
|
||||
enum class TimeSigDenominator {
|
||||
_2 = 0,
|
||||
_4,
|
||||
_8,
|
||||
_16,
|
||||
_32
|
||||
};
|
||||
|
||||
enum class AllowedVoices {
|
||||
V_1 = 0,
|
||||
V_2,
|
||||
V_3,
|
||||
V_4
|
||||
};
|
||||
|
||||
enum class Swing {
|
||||
NONE = 0,
|
||||
SWING,
|
||||
SHUFFLE
|
||||
};
|
||||
|
||||
enum class LHRHMethod {
|
||||
HAND_WIDTH = 0,
|
||||
SPECIFIED_PITCH
|
||||
};
|
||||
|
||||
enum class Octave {
|
||||
C_1 = 0,
|
||||
C0,
|
||||
C1,
|
||||
C2,
|
||||
C3,
|
||||
C4,
|
||||
C5,
|
||||
C6,
|
||||
C7,
|
||||
C8,
|
||||
C9
|
||||
};
|
||||
|
||||
enum class Note {
|
||||
C = 0,
|
||||
Cis,
|
||||
D,
|
||||
Dis,
|
||||
E,
|
||||
F,
|
||||
Fis,
|
||||
G,
|
||||
Gis,
|
||||
A,
|
||||
Ais,
|
||||
H
|
||||
};
|
||||
enum class QuantValue {
|
||||
FROM_PREFERENCES = 0,
|
||||
Q_4,
|
||||
Q_8,
|
||||
Q_16,
|
||||
Q_32,
|
||||
Q_64,
|
||||
Q_128
|
||||
};
|
||||
|
||||
struct HumanBeatData
|
||||
{
|
||||
std::set<ReducedFraction> beatSet;
|
||||
// to adapt human beats to a different time sig, if necessary
|
||||
int addedFirstBeats;
|
||||
int addedLastBeats;
|
||||
ReducedFraction firstChordTick;
|
||||
ReducedFraction lastChordTick;
|
||||
ReducedFraction timeSig;
|
||||
enum class StaffSplitMethod {
|
||||
HAND_WIDTH = 0,
|
||||
SPECIFIED_PITCH
|
||||
};
|
||||
|
||||
enum class StaffSplitOctave {
|
||||
C_1 = 0,
|
||||
C0,
|
||||
C1,
|
||||
C2,
|
||||
C3,
|
||||
C4,
|
||||
C5,
|
||||
C6,
|
||||
C7,
|
||||
C8,
|
||||
C9
|
||||
};
|
||||
|
||||
enum class StaffSplitNote {
|
||||
C = 0,
|
||||
Cis,
|
||||
D,
|
||||
Dis,
|
||||
E,
|
||||
F,
|
||||
Fis,
|
||||
G,
|
||||
Gis,
|
||||
A,
|
||||
Ais,
|
||||
H
|
||||
};
|
||||
|
||||
enum class VoiceCount {
|
||||
V_1 = 0,
|
||||
V_2,
|
||||
V_3,
|
||||
V_4
|
||||
};
|
||||
|
||||
enum class Swing {
|
||||
NONE = 0,
|
||||
SWING,
|
||||
SHUFFLE
|
||||
};
|
||||
|
||||
enum class TimeSigNumerator {
|
||||
_2 = 0,
|
||||
_3,
|
||||
_4,
|
||||
_5,
|
||||
_6,
|
||||
_7,
|
||||
_9,
|
||||
_12,
|
||||
_15,
|
||||
_21
|
||||
};
|
||||
|
||||
enum class TimeSigDenominator {
|
||||
_2 = 0,
|
||||
_4,
|
||||
_8,
|
||||
_16,
|
||||
_32
|
||||
};
|
||||
|
||||
} // namespace MidiOperations
|
||||
} // namespace Ms
|
||||
|
||||
|
||||
Q_DECLARE_METATYPE(Ms::MidiOperation);
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_OPERATION_H
|
||||
|
|
|
@ -2,158 +2,67 @@
|
|||
|
||||
|
||||
namespace Ms {
|
||||
namespace MidiOperations {
|
||||
|
||||
bool MidiImportOperations::isValidIndex(int index) const
|
||||
FileData* Data::data()
|
||||
{
|
||||
return index >= 0 && index < operations_.size();
|
||||
const auto it = _data.find(_currentMidiFile);
|
||||
if (it != _data.end())
|
||||
return &it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TrackOperations& MidiImportOperations::defaultOperations(int trackIndex)
|
||||
const FileData* Data::data() const
|
||||
{
|
||||
const auto it = defaultOpersMap.find(trackIndex);
|
||||
if (it != defaultOpersMap.end())
|
||||
return it->second;
|
||||
return defaultOpers;
|
||||
const auto it = _data.find(_currentMidiFile);
|
||||
if (it != _data.end())
|
||||
return &it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const TrackOperations& MidiImportOperations::defaultOperations(int trackIndex) const
|
||||
void Data::addNewFile(const QString &fileName)
|
||||
{
|
||||
const auto it = defaultOpersMap.find(trackIndex);
|
||||
if (it != defaultOpersMap.end())
|
||||
return it->second;
|
||||
return defaultOpers;
|
||||
_data.insert({fileName, FileData()});
|
||||
_currentMidiFile = fileName;
|
||||
}
|
||||
|
||||
void MidiImportOperations::appendTrackOperations(const TrackOperations &operations)
|
||||
const MidiFile* Data::midiFile(const QString &fileName)
|
||||
{
|
||||
operations_.push_back(operations);
|
||||
if (operations_.size() == 1)
|
||||
currentTrack_ = 0;
|
||||
const auto it = _data.find(fileName);
|
||||
if (it != _data.end())
|
||||
return &it->second.midiFile;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MidiImportOperations::clear()
|
||||
void Data::setMidiFileData(const QString &fileName, const MidiFile &midiFile)
|
||||
{
|
||||
operations_.clear();
|
||||
currentTrack_ = -1;
|
||||
defaultOpers = TrackOperations();
|
||||
defaultOpersMap.clear();
|
||||
_data[fileName].midiFile = midiFile;
|
||||
}
|
||||
|
||||
void MidiImportOperations::resetDefaults(const TrackOperations &operations)
|
||||
void Data::setCurrentMidiFile(const QString &fileName)
|
||||
{
|
||||
defaultOpers = operations;
|
||||
defaultOpersMap.clear();
|
||||
_currentMidiFile = fileName;
|
||||
}
|
||||
|
||||
void MidiImportOperations::setCurrentTrack(int trackIndex)
|
||||
void Data::excludeFile(const QString &fileName)
|
||||
{
|
||||
if (!isValidIndex(trackIndex))
|
||||
return;
|
||||
currentTrack_ = trackIndex;
|
||||
_data.erase(fileName);
|
||||
}
|
||||
|
||||
void MidiImportOperations::setCurrentMidiFile(const QString &fileName)
|
||||
bool Data::hasFile(const QString &fileName)
|
||||
{
|
||||
currentMidiFile_ = fileName;
|
||||
return _data.find(fileName) != _data.end();
|
||||
}
|
||||
|
||||
TrackOperations MidiImportOperations::currentTrackOperations() const
|
||||
int Data::currentTrack() const
|
||||
{
|
||||
if (!isValidIndex(currentTrack_))
|
||||
return defaultOperations(currentTrack_);
|
||||
return operations_[currentTrack_];
|
||||
}
|
||||
|
||||
TrackOperations MidiImportOperations::trackOperations(int trackIndex) const
|
||||
{
|
||||
if (!isValidIndex(trackIndex))
|
||||
return defaultOperations(trackIndex);
|
||||
return operations_[trackIndex];
|
||||
}
|
||||
|
||||
QString MidiImportOperations::charset() const
|
||||
{
|
||||
return midiData_.charset(currentMidiFile_);
|
||||
}
|
||||
|
||||
void MidiImportOperations::adaptForPercussion(int trackIndex, bool isDrumTrack)
|
||||
{
|
||||
// small hack: don't use multiple voices for tuplets in percussion tracks
|
||||
if (isValidIndex(trackIndex)) {
|
||||
if (isDrumTrack)
|
||||
operations_[trackIndex].allowedVoices = MidiOperation::AllowedVoices::V_1;
|
||||
}
|
||||
else {
|
||||
auto &def = defaultOperations(trackIndex);
|
||||
def.allowedVoices = (isDrumTrack)
|
||||
? MidiOperation::AllowedVoices::V_1 : TrackOperations().allowedVoices;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiImportOperations::addTrackLyrics(
|
||||
const std::multimap<ReducedFraction, std::string> &trackLyrics)
|
||||
{
|
||||
midiData_.addTrackLyrics(currentMidiFile_, trackLyrics);
|
||||
}
|
||||
|
||||
const QList<std::multimap<ReducedFraction, std::string> >*
|
||||
MidiImportOperations::getLyrics()
|
||||
{
|
||||
return midiData_.getLyrics(currentMidiFile_);
|
||||
}
|
||||
|
||||
bool MidiImportOperations::isHumanPerformance() const
|
||||
{
|
||||
return midiData_.isHumanPerformance(currentMidiFile_);
|
||||
}
|
||||
|
||||
void MidiImportOperations::setHumanPerformance(bool value)
|
||||
{
|
||||
midiData_.setHumanPerformance(currentMidiFile_, value);
|
||||
defaultOperations(currentTrack_).quantize.humanPerformance = value;
|
||||
}
|
||||
|
||||
MidiOperation::QuantValue MidiImportOperations::quantValue() const
|
||||
{
|
||||
return midiData_.quantValue(currentMidiFile_);
|
||||
}
|
||||
|
||||
void MidiImportOperations::setQuantValue(MidiOperation::QuantValue value)
|
||||
{
|
||||
midiData_.setQuantValue(currentMidiFile_, value);
|
||||
defaultOperations(currentTrack_).quantize.value = value;
|
||||
}
|
||||
|
||||
bool MidiImportOperations::needToSplit(int trackIndex) const
|
||||
{
|
||||
return midiData_.needToSplit(currentMidiFile_, trackIndex);
|
||||
}
|
||||
|
||||
void MidiImportOperations::setNeedToSplit(int trackIndex, bool value)
|
||||
{
|
||||
midiData_.setNeedToSplit(currentMidiFile_, trackIndex, value);
|
||||
defaultOperations(currentTrack_).LHRH.doIt = value;
|
||||
}
|
||||
|
||||
const std::set<ReducedFraction>* MidiImportOperations::getHumanBeats() const
|
||||
{
|
||||
return midiData_.getHumanBeats(currentMidiFile_);
|
||||
}
|
||||
|
||||
void MidiImportOperations::setHumanBeats(const HumanBeatData &beatData)
|
||||
{
|
||||
return midiData_.setHumanBeats(currentMidiFile_, beatData);
|
||||
}
|
||||
|
||||
ReducedFraction MidiImportOperations::timeSignature() const
|
||||
{
|
||||
return midiData_.timeSignature(currentMidiFile_);
|
||||
}
|
||||
|
||||
void MidiImportOperations::setTimeSignature(const ReducedFraction &value)
|
||||
{
|
||||
return midiData_.setTimeSignature(currentMidiFile_, value);
|
||||
|
||||
Q_ASSERT_X(_currentTrack >= 0,
|
||||
"Data::currentTrack", "Invalid current track index");
|
||||
|
||||
return _currentTrack;
|
||||
}
|
||||
|
||||
} // namespace MidiOperations
|
||||
} // namespace Ms
|
||||
|
||||
|
|
|
@ -2,11 +2,16 @@
|
|||
#define IMPORTMIDI_OPERATIONS_H
|
||||
|
||||
#include "importmidi_operation.h"
|
||||
#include "importmidi_data.h"
|
||||
#include "midi/midifile.h"
|
||||
#include "importmidi_inner.h"
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class ReducedFraction;
|
||||
|
||||
namespace MidiOperations {
|
||||
|
||||
// operation types are in importmidi_operation.h
|
||||
|
||||
// to add an operation one need to add code also to:
|
||||
|
@ -15,147 +20,176 @@ namespace Ms {
|
|||
// - importmidi_trmodel.cpp (2 places),
|
||||
// and - other importmidi files where algorithm requires it
|
||||
|
||||
struct SearchTuplets
|
||||
{
|
||||
bool doSearch = true;
|
||||
bool duplets = false;
|
||||
bool triplets = true;
|
||||
bool quadruplets = true;
|
||||
bool quintuplets = true;
|
||||
bool septuplets = true;
|
||||
bool nonuplets = true;
|
||||
};
|
||||
|
||||
struct Quantization
|
||||
{
|
||||
MidiOperation::QuantValue value = MidiOperation::QuantValue::FROM_PREFERENCES;
|
||||
bool humanPerformance = false;
|
||||
};
|
||||
|
||||
struct LHRHSeparation
|
||||
{
|
||||
bool doIt = false;
|
||||
MidiOperation::LHRHMethod method = MidiOperation::LHRHMethod::HAND_WIDTH;
|
||||
MidiOperation::Octave splitPitchOctave = MidiOperation::Octave::C4;
|
||||
MidiOperation::Note splitPitchNote = MidiOperation::Note::E;
|
||||
};
|
||||
|
||||
struct SplitDrums
|
||||
{
|
||||
bool doSplit = false;
|
||||
bool showStaffBracket = true;
|
||||
};
|
||||
|
||||
struct MidiTimeSig
|
||||
{
|
||||
MidiOperation::TimeSigNumerator numerator = MidiOperation::TimeSigNumerator::_4;
|
||||
MidiOperation::TimeSigDenominator denominator = MidiOperation::TimeSigDenominator::_4;
|
||||
};
|
||||
|
||||
// bool and enum-like elementary operations (itself and inside structs) are allowed
|
||||
struct TrackOperations
|
||||
{
|
||||
bool canRedefineDefaultsLater = true; // can try to adapt defaults to the imported score
|
||||
int reorderedIndex = 0;
|
||||
bool doImport = true;
|
||||
Quantization quantize;
|
||||
bool useDots = true;
|
||||
bool simplifyDurations = true;
|
||||
bool showStaccato = true;
|
||||
bool separateVoices = true;
|
||||
LHRHSeparation LHRH;
|
||||
SearchTuplets tuplets;
|
||||
MidiOperation::AllowedVoices allowedVoices = MidiOperation::AllowedVoices::V_4;
|
||||
bool changeClef = true;
|
||||
MidiOperation::Swing swing = MidiOperation::Swing::NONE;
|
||||
SplitDrums splitDrums;
|
||||
bool removeDrumRests = true;
|
||||
bool pickupMeasure = true;
|
||||
int lyricTrackIndex = -1; // empty lyric
|
||||
MidiTimeSig timeSig;
|
||||
};
|
||||
|
||||
struct TrackMeta
|
||||
{
|
||||
std::string staffName; // will be converted to unicode later
|
||||
QString instrumentName;
|
||||
bool isDrumTrack;
|
||||
int initLyricTrackIndex;
|
||||
};
|
||||
|
||||
struct TrackData
|
||||
{
|
||||
TrackMeta meta;
|
||||
TrackOperations opers;
|
||||
};
|
||||
|
||||
struct DefinedTrackOperations
|
||||
{
|
||||
QSet<int> undefinedOpers;
|
||||
bool isDrumTrack;
|
||||
bool allTracksSelected;
|
||||
TrackOperations opers;
|
||||
};
|
||||
|
||||
class ReducedFraction;
|
||||
|
||||
class MidiImportOperations
|
||||
template<typename T>
|
||||
class TrackOp
|
||||
{
|
||||
public:
|
||||
void appendTrackOperations(const TrackOperations& operations);
|
||||
void clear();
|
||||
void resetDefaults(const TrackOperations& operations);
|
||||
void setCurrentTrack(int trackIndex);
|
||||
void setCurrentMidiFile(const QString &fileName);
|
||||
int currentTrack() const { return currentTrack_; }
|
||||
TrackOperations defaultOperations() const { return defaultOpers; }
|
||||
TrackOperations currentTrackOperations() const;
|
||||
TrackOperations trackOperations(int trackIndex) const;
|
||||
int count() const { return operations_.size(); }
|
||||
MidiData& midiData() { return midiData_; }
|
||||
QString charset() const;
|
||||
void adaptForPercussion(int trackIndex, bool isDrumTrack);
|
||||
TrackOp(T defaultValue)
|
||||
: _operation{{-1, defaultValue}}
|
||||
{}
|
||||
|
||||
// lyrics
|
||||
void addTrackLyrics(const std::multimap<ReducedFraction, std::string> &trackLyrics);
|
||||
const QList<std::multimap<ReducedFraction, std::string> > *getLyrics();
|
||||
TrackOp<T>& operator=(const TrackOp<T> &op)
|
||||
{
|
||||
if (this == &op)
|
||||
return *this;
|
||||
for (const auto &pair: op._operation) {
|
||||
if (pair.second != value(pair.first) && pair.second != defaultValue()) {
|
||||
_operation[pair.first] = pair.second;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// human performance: is MIDI unaligned
|
||||
bool isHumanPerformance() const;
|
||||
void setHumanPerformance(bool value);
|
||||
const std::set<ReducedFraction>* getHumanBeats() const;
|
||||
void setHumanBeats(const HumanBeatData &beatData);
|
||||
// time sig can be zero (not specified) if, when opened,
|
||||
// MIDI file was not detected as human-performed
|
||||
ReducedFraction timeSignature() const;
|
||||
void setTimeSignature(const ReducedFraction &value);
|
||||
T value(int trackIndex) const
|
||||
{
|
||||
const auto it = _operation.find(trackIndex);
|
||||
if (it == _operation.end())
|
||||
return _operation.find(-1)->second;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// quantization
|
||||
MidiOperation::QuantValue quantValue() const;
|
||||
void setQuantValue(MidiOperation::QuantValue value);
|
||||
void setValue(int trackIndex, T value)
|
||||
{
|
||||
Q_ASSERT_X(trackIndex >= 0, "TrackOperation", "Invalid track index");
|
||||
|
||||
// left/right hand split
|
||||
bool needToSplit(int trackIndex) const;
|
||||
void setNeedToSplit(int trackIndex, bool value);
|
||||
if (value != defaultValue())
|
||||
_operation[trackIndex] = value;
|
||||
}
|
||||
|
||||
T defaultValue() const
|
||||
{
|
||||
return _operation.find(-1)->second;
|
||||
}
|
||||
|
||||
void setDefaultValue(T value)
|
||||
{
|
||||
_operation[-1] = value;
|
||||
}
|
||||
private:
|
||||
TrackOperations& defaultOperations(int trackIndex);
|
||||
const TrackOperations& defaultOperations(int trackIndex) const;
|
||||
|
||||
QList<TrackOperations> operations_;
|
||||
std::map<int, TrackOperations> defaultOpersMap; // <track index, operations>
|
||||
TrackOperations defaultOpers;
|
||||
int currentTrack_ = -1;
|
||||
QString currentMidiFile_;
|
||||
MidiData midiData_;
|
||||
|
||||
bool isValidIndex(int index) const;
|
||||
// <track index, operation value>
|
||||
// if track index == -1 then it's default value (for all tracks)
|
||||
std::map<int, T> _operation;
|
||||
};
|
||||
|
||||
// values that can be changed
|
||||
|
||||
struct Opers
|
||||
{
|
||||
// data that cannot be changed by the user
|
||||
TrackOp<std::string> staffName = std::string(); // will be converted to unicode later
|
||||
TrackOp<QString> instrumentName = QString();
|
||||
TrackOp<bool> isDrumTrack = false;
|
||||
|
||||
// operations for all tracks
|
||||
bool isHumanPerformance = false;
|
||||
bool searchPickupMeasure = true;
|
||||
TimeSigNumerator timeSigNumerator = TimeSigNumerator::_4;
|
||||
TimeSigDenominator timeSigDenominator = TimeSigDenominator::_4;
|
||||
|
||||
// operations for individual tracks
|
||||
TrackOp<int> trackIndexAfterShuffle = 0;
|
||||
TrackOp<bool> doImport = true;
|
||||
TrackOp<QuantValue> quantValue = QuantValue::FROM_PREFERENCES;
|
||||
TrackOp<bool> searchTuplets = true;
|
||||
TrackOp<bool> search2plets = false;
|
||||
TrackOp<bool> search3plets = true;
|
||||
TrackOp<bool> search4plets = true;
|
||||
TrackOp<bool> search5plets = true;
|
||||
TrackOp<bool> search7plets = true;
|
||||
TrackOp<bool> search9plets = true;
|
||||
TrackOp<bool> useDots = true;
|
||||
TrackOp<bool> simplifyDurations = true; // for drum tracks - remove rests and ties
|
||||
TrackOp<bool> showStaccato = true;
|
||||
TrackOp<bool> doStaffSplit = false; // for drum tracks - split by voices
|
||||
TrackOp<StaffSplitMethod> staffSplitMethod = StaffSplitMethod::HAND_WIDTH;
|
||||
TrackOp<StaffSplitOctave> staffSplitOctave = StaffSplitOctave::C4;
|
||||
TrackOp<StaffSplitNote> staffSplitNote = StaffSplitNote::E;
|
||||
TrackOp<VoiceCount> maxVoiceCount = VoiceCount::V_4;
|
||||
TrackOp<bool> changeClef = true;
|
||||
TrackOp<Swing> swing = Swing::NONE;
|
||||
TrackOp<bool> removeDrumRests = true;
|
||||
TrackOp<int> lyricTrackIndex = -1; // empty lyric
|
||||
};
|
||||
|
||||
struct HumanBeatData
|
||||
{
|
||||
std::set<ReducedFraction> beatSet;
|
||||
// to adapt human beats to a different time sig, if necessary
|
||||
int addedFirstBeats;
|
||||
int addedLastBeats;
|
||||
ReducedFraction firstChordTick;
|
||||
ReducedFraction lastChordTick;
|
||||
ReducedFraction timeSig;
|
||||
};
|
||||
|
||||
struct FileData
|
||||
{
|
||||
MidiFile midiFile;
|
||||
bool isNewlyOpened = true;
|
||||
bool canRedefineDefaultsLater = true;
|
||||
QByteArray HHeaderData;
|
||||
QByteArray VHeaderData;
|
||||
int selectedRow = 0;
|
||||
int trackCount = 0;
|
||||
MidiOperations::Opers trackOpers;
|
||||
QString charset = MidiCharset::defaultCharset();
|
||||
// after the user apply MIDI import operations
|
||||
// this value should be set to false
|
||||
// tracks of <tick, lyric fragment> from karaoke files
|
||||
// QList of lyric tracks - there can be multiple lyric tracks,
|
||||
// lyric track count != MIDI track count in general
|
||||
QList<std::multimap<ReducedFraction, std::string>> lyricTracks;
|
||||
HumanBeatData humanBeatData;
|
||||
};
|
||||
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
FileData* data();
|
||||
const FileData* data() const;
|
||||
|
||||
void addNewFile(const QString &fileName);
|
||||
int currentTrack() const;
|
||||
void setMidiFileData(const QString &fileName, const MidiFile &midiFile);
|
||||
void setCurrentMidiFile(const QString &fileName);
|
||||
void excludeFile(const QString &fileName);
|
||||
bool hasFile(const QString &fileName);
|
||||
const MidiFile* midiFile(const QString &fileName);
|
||||
|
||||
private:
|
||||
friend class CurrentTrackSetter;
|
||||
|
||||
QString _currentMidiFile;
|
||||
int _currentTrack = -1;
|
||||
|
||||
std::map<QString, FileData> _data; // <file name, tracks data>
|
||||
};
|
||||
|
||||
// scoped setter of current track
|
||||
class CurrentTrackSetter
|
||||
{
|
||||
public:
|
||||
CurrentTrackSetter(Data &opers, int track)
|
||||
: _opers(opers)
|
||||
{
|
||||
_opers._currentTrack = track;
|
||||
}
|
||||
|
||||
~CurrentTrackSetter()
|
||||
{
|
||||
_opers._currentTrack = -1;
|
||||
}
|
||||
private:
|
||||
Data &_opers;
|
||||
// disallow heap allocation - for stack-only usage
|
||||
void* operator new(size_t); // standard new
|
||||
void* operator new(size_t, void*); // placement new
|
||||
void* operator new[](size_t); // array new
|
||||
void* operator new[](size_t, void*); // placement array new
|
||||
};
|
||||
|
||||
} // namespace MidiOperations
|
||||
} // namespace Ms
|
||||
|
||||
|
||||
Q_DECLARE_METATYPE(Ms::TrackData);
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_OPERATIONS_H
|
||||
|
|
|
@ -1,772 +0,0 @@
|
|||
#include "importmidi_opmodel.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "preferences.h"
|
||||
#include "libmscore/mscore.h"
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
extern Preferences preferences;
|
||||
|
||||
struct Node {
|
||||
QString name;
|
||||
MidiOperation oper;
|
||||
QStringList values;
|
||||
bool visible = true;
|
||||
Node *parent = nullptr;
|
||||
std::vector<std::unique_ptr<Node> > children;
|
||||
|
||||
void setParent(Node *par)
|
||||
{
|
||||
parent = par;
|
||||
par->children.push_back(std::unique_ptr<Node>(this));
|
||||
}
|
||||
};
|
||||
|
||||
struct Controller {
|
||||
Node *LHRHdoIt = nullptr;
|
||||
Node *LHRHMethod = nullptr;
|
||||
Node *LHRHPitchOctave = nullptr;
|
||||
Node *LHRHPitchNote = nullptr;
|
||||
Node *quantValue = nullptr;
|
||||
Node *quantHuman = nullptr;
|
||||
Node *searchTuplets = nullptr;
|
||||
Node *duplets = nullptr;
|
||||
Node *triplets = nullptr;
|
||||
Node *quadruplets = nullptr;
|
||||
Node *quintuplets = nullptr;
|
||||
Node *septuplets = nullptr;
|
||||
Node *nonuplets = nullptr;
|
||||
Node *allowedVoices = nullptr;
|
||||
Node *separateVoices = nullptr;
|
||||
Node *splitDrums = nullptr;
|
||||
Node *showStaffBracket = nullptr;
|
||||
Node *pickupMeasure = nullptr;
|
||||
Node *clef = nullptr;
|
||||
Node *removeDrumRests = nullptr;
|
||||
Node *simplifyDurations = nullptr;
|
||||
Node *showStaccato = nullptr;
|
||||
Node *timeSig = nullptr;
|
||||
|
||||
bool isDrumTrack = false;
|
||||
bool allTracksSelected = true;
|
||||
bool timeSigVisible = false;
|
||||
|
||||
|
||||
bool updateNodeDependencies(Node *node, bool forceUpdate);
|
||||
};
|
||||
|
||||
OperationsModel::OperationsModel()
|
||||
: root(std::unique_ptr<Node>(new Node()))
|
||||
, controller(std::unique_ptr<Controller>(new Controller()))
|
||||
, updateQuantTimer(new QTimer)
|
||||
{
|
||||
connect(updateQuantTimer, SIGNAL(timeout()), this, SLOT(updateQuantValue()));
|
||||
updateQuantTimer->start(100);
|
||||
|
||||
beginResetModel();
|
||||
// - initialize opeations with their default values
|
||||
// - string lists below should match Operation enum values
|
||||
Node *quantValue = new Node;
|
||||
quantValue->name = QCoreApplication::translate("MIDI import operations", "Max quantization value");
|
||||
quantValue->oper.type = MidiOperation::Type::QUANT_VALUE;
|
||||
quantValue->oper.value = (int)TrackOperations().quantize.value;
|
||||
quantValue->values.push_back(QCoreApplication::translate("MIDI import operations", "Value from preferences"));
|
||||
quantValue->values.push_back(QCoreApplication::translate("MIDI import operations", "Quarter"));
|
||||
quantValue->values.push_back(QCoreApplication::translate("MIDI import operations", "Eighth"));
|
||||
quantValue->values.push_back(QCoreApplication::translate("MIDI import operations", "16th"));
|
||||
quantValue->values.push_back(QCoreApplication::translate("MIDI import operations", "32nd"));
|
||||
quantValue->values.push_back(QCoreApplication::translate("MIDI import operations", "64th"));
|
||||
quantValue->values.push_back(QCoreApplication::translate("MIDI import operations", "128th"));
|
||||
quantValue->setParent(root.get());
|
||||
controller->quantValue = quantValue;
|
||||
|
||||
|
||||
Node *humanPerformance = new Node;
|
||||
humanPerformance->name = QCoreApplication::translate("MIDI import operations", "Human performance");
|
||||
humanPerformance->oper.type = MidiOperation::Type::QUANT_HUMAN;
|
||||
humanPerformance->oper.value = Quantization().humanPerformance;
|
||||
humanPerformance->setParent(quantValue);
|
||||
controller->quantHuman = humanPerformance;
|
||||
|
||||
|
||||
Node *timeSigNumerator = new Node;
|
||||
timeSigNumerator->name = QCoreApplication::translate("MIDI import operations", "Time signature");
|
||||
timeSigNumerator->oper.type = MidiOperation::Type::TIME_SIG_NUMERATOR;
|
||||
timeSigNumerator->oper.value = (int)MidiTimeSig().numerator;
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "2"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "3"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "4"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "5"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "6"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "7"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "9"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "12"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "15"));
|
||||
timeSigNumerator->values.push_back(QCoreApplication::translate("MIDI import operations", "21"));
|
||||
timeSigNumerator->setParent(root.get());
|
||||
controller->timeSig = timeSigNumerator;
|
||||
|
||||
|
||||
Node *timeSigDenominator = new Node;
|
||||
timeSigDenominator->name = QCoreApplication::translate("MIDI import operations", "/");
|
||||
timeSigDenominator->oper.type = MidiOperation::Type::TIME_SIG_DENOMINATOR;
|
||||
timeSigDenominator->oper.value = (int)MidiTimeSig().denominator;
|
||||
timeSigDenominator->values.push_back(QCoreApplication::translate("MIDI import operations", "2"));
|
||||
timeSigDenominator->values.push_back(QCoreApplication::translate("MIDI import operations", "4"));
|
||||
timeSigDenominator->values.push_back(QCoreApplication::translate("MIDI import operations", "8"));
|
||||
timeSigDenominator->values.push_back(QCoreApplication::translate("MIDI import operations", "16"));
|
||||
timeSigDenominator->values.push_back(QCoreApplication::translate("MIDI import operations", "32"));
|
||||
timeSigDenominator->setParent(timeSigNumerator);
|
||||
|
||||
|
||||
Node *useDots = new Node;
|
||||
useDots->name = QCoreApplication::translate("MIDI import operations", "Use dots");
|
||||
useDots->oper.type = MidiOperation::Type::USE_DOTS;
|
||||
useDots->oper.value = TrackOperations().useDots;
|
||||
useDots->setParent(root.get());
|
||||
|
||||
|
||||
Node *simplifyDurations = new Node;
|
||||
simplifyDurations->name = QCoreApplication::translate("MIDI import operations", "Simplify durations");
|
||||
simplifyDurations->oper.type = MidiOperation::Type::SIMPLIFY_DURATIONS;
|
||||
simplifyDurations->oper.value = TrackOperations().simplifyDurations;
|
||||
simplifyDurations->setParent(root.get());
|
||||
controller->simplifyDurations = simplifyDurations;
|
||||
|
||||
|
||||
Node *showStaccato = new Node;
|
||||
showStaccato->name = QCoreApplication::translate("MIDI import operations", "Show staccato");
|
||||
showStaccato->oper.type = MidiOperation::Type::SHOW_STACCATO;
|
||||
showStaccato->oper.value = TrackOperations().showStaccato;
|
||||
showStaccato->setParent(simplifyDurations);
|
||||
controller->showStaccato = showStaccato;
|
||||
|
||||
|
||||
Node *allowedVoices = new Node;
|
||||
allowedVoices->name = QCoreApplication::translate("MIDI import operations", "Max allowed voices");
|
||||
allowedVoices->oper.type = MidiOperation::Type::ALLOWED_VOICES;
|
||||
allowedVoices->oper.value = (int)TrackOperations().allowedVoices;
|
||||
allowedVoices->values.push_back(QCoreApplication::translate("MIDI import operations", "1"));
|
||||
allowedVoices->values.push_back(QCoreApplication::translate("MIDI import operations", "2"));
|
||||
allowedVoices->values.push_back(QCoreApplication::translate("MIDI import operations", "3"));
|
||||
allowedVoices->values.push_back(QCoreApplication::translate("MIDI import operations", "4"));
|
||||
allowedVoices->setParent(root.get());
|
||||
controller->allowedVoices = allowedVoices;
|
||||
|
||||
|
||||
Node *separateVoices = new Node;
|
||||
separateVoices->name = QCoreApplication::translate("MIDI import operations", "Separate voices to remove ties");
|
||||
separateVoices->oper.type = MidiOperation::Type::SEPARATE_VOICES;
|
||||
separateVoices->oper.value = TrackOperations().separateVoices;
|
||||
separateVoices->setParent(allowedVoices);
|
||||
controller->separateVoices = separateVoices;
|
||||
|
||||
// ------------- staff separation --------------
|
||||
|
||||
Node *doLHRH = new Node;
|
||||
doLHRH->name = QCoreApplication::translate("MIDI import operations", "Staff separation");
|
||||
doLHRH->oper.type = MidiOperation::Type::DO_LHRH_SEPARATION;
|
||||
doLHRH->oper.value = LHRHSeparation().doIt;
|
||||
doLHRH->setParent(root.get());
|
||||
controller->LHRHdoIt = doLHRH;
|
||||
|
||||
|
||||
Node *LHRHMethod = new Node;
|
||||
LHRHMethod->name = QCoreApplication::translate("MIDI import operations", "Separation method");
|
||||
LHRHMethod->oper.type = MidiOperation::Type::LHRH_METHOD;
|
||||
LHRHMethod->oper.value = (int)LHRHSeparation().method;
|
||||
LHRHMethod->values.push_back(QCoreApplication::translate("MIDI import operations", "Hand width"));
|
||||
LHRHMethod->values.push_back(QCoreApplication::translate("MIDI import operations", "Fixed pitch"));
|
||||
LHRHMethod->setParent(doLHRH);
|
||||
controller->LHRHMethod = LHRHMethod;
|
||||
|
||||
|
||||
Node *LHRHPitchOctave = new Node;
|
||||
LHRHPitchOctave->name = QCoreApplication::translate("MIDI import operations", "Split pitch octave");
|
||||
LHRHPitchOctave->oper.type = MidiOperation::Type::LHRH_SPLIT_OCTAVE;
|
||||
LHRHPitchOctave->oper.value = (int)LHRHSeparation().splitPitchOctave;
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C-1"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C0"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C1"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C2"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C3"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C4"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C5"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C6"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C7"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C8"));
|
||||
LHRHPitchOctave->values.push_back(QCoreApplication::translate("MIDI import operations", "C9"));
|
||||
LHRHPitchOctave->setParent(LHRHMethod);
|
||||
controller->LHRHPitchOctave = LHRHPitchOctave;
|
||||
|
||||
|
||||
Node *LHRHPitchNote = new Node;
|
||||
LHRHPitchNote->name = QCoreApplication::translate("MIDI import operations", "Split pitch note");
|
||||
LHRHPitchNote->oper.type = MidiOperation::Type::LHRH_SPLIT_NOTE;
|
||||
LHRHPitchNote->oper.value = (int)LHRHSeparation().splitPitchNote;
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "C"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "C#"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "D"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "D#"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "E"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "F"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "F#"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "G"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "G#"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "A"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "A#"));
|
||||
LHRHPitchNote->values.push_back(QCoreApplication::translate("MIDI import operations", "B"));
|
||||
LHRHPitchNote->setParent(LHRHMethod);
|
||||
controller->LHRHPitchNote = LHRHPitchNote;
|
||||
|
||||
// ------------- tuplets --------------
|
||||
|
||||
Node *searchTuplets = new Node;
|
||||
searchTuplets->name = QCoreApplication::translate("MIDI import operations", "Search tuplets");
|
||||
searchTuplets->oper.type = MidiOperation::Type::TUPLET_SEARCH;
|
||||
searchTuplets->oper.value = TrackOperations().tuplets.doSearch;
|
||||
searchTuplets->setParent(root.get());
|
||||
controller->searchTuplets = searchTuplets;
|
||||
|
||||
|
||||
Node *duplets = new Node;
|
||||
duplets->name = QCoreApplication::translate("MIDI import operations", "Duplets (2)");
|
||||
duplets->oper.type = MidiOperation::Type::TUPLET_2;
|
||||
duplets->oper.value = TrackOperations().tuplets.duplets;
|
||||
duplets->setParent(searchTuplets);
|
||||
controller->duplets = duplets;
|
||||
|
||||
|
||||
Node *triplets = new Node;
|
||||
triplets->name = QCoreApplication::translate("MIDI import operations", "Triplets (3)");
|
||||
triplets->oper.type = MidiOperation::Type::TUPLET_3;
|
||||
triplets->oper.value = TrackOperations().tuplets.triplets;
|
||||
triplets->setParent(searchTuplets);
|
||||
controller->triplets = triplets;
|
||||
|
||||
|
||||
Node *quadruplets = new Node;
|
||||
quadruplets->name = QCoreApplication::translate("MIDI import operations", "Quadruplets (4)");
|
||||
quadruplets->oper.type = MidiOperation::Type::TUPLET_4;
|
||||
quadruplets->oper.value = TrackOperations().tuplets.quadruplets;
|
||||
quadruplets->setParent(searchTuplets);
|
||||
controller->quadruplets = quadruplets;
|
||||
|
||||
|
||||
Node *quintuplets = new Node;
|
||||
quintuplets->name = QCoreApplication::translate("MIDI import operations", "Quintuplets (5)");
|
||||
quintuplets->oper.type = MidiOperation::Type::TUPLET_5;
|
||||
quintuplets->oper.value = TrackOperations().tuplets.quintuplets;
|
||||
quintuplets->setParent(searchTuplets);
|
||||
controller->quintuplets = quintuplets;
|
||||
|
||||
|
||||
Node *septuplets = new Node;
|
||||
septuplets->name = QCoreApplication::translate("MIDI import operations", "Septuplets (7)");
|
||||
septuplets->oper.type = MidiOperation::Type::TUPLET_7;
|
||||
septuplets->oper.value = TrackOperations().tuplets.septuplets;
|
||||
septuplets->setParent(searchTuplets);
|
||||
controller->septuplets = septuplets;
|
||||
|
||||
|
||||
Node *nonuplets = new Node;
|
||||
nonuplets->name = QCoreApplication::translate("MIDI import operations", "Nonuplets (9)");
|
||||
nonuplets->oper.type = MidiOperation::Type::TUPLET_9;
|
||||
nonuplets->oper.value = TrackOperations().tuplets.nonuplets;
|
||||
nonuplets->setParent(searchTuplets);
|
||||
controller->nonuplets = nonuplets;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
Node *pickupMeasure = new Node;
|
||||
pickupMeasure->name = QCoreApplication::translate("MIDI import operations", "Recognize pickup measure");
|
||||
pickupMeasure->oper.type = MidiOperation::Type::PICKUP_MEASURE;
|
||||
pickupMeasure->oper.value = TrackOperations().pickupMeasure;
|
||||
pickupMeasure->setParent(root.get());
|
||||
controller->pickupMeasure = pickupMeasure;
|
||||
|
||||
|
||||
Node *swing = new Node;
|
||||
swing->name = QCoreApplication::translate("MIDI import operations", "Detect swing");
|
||||
swing->oper.type = MidiOperation::Type::SWING;
|
||||
swing->oper.value = (int)TrackOperations().swing;
|
||||
swing->values.push_back(QCoreApplication::translate("MIDI import operations", "None (1:1)"));
|
||||
swing->values.push_back(QCoreApplication::translate("MIDI import operations", "Swing (2:1)"));
|
||||
swing->values.push_back(QCoreApplication::translate("MIDI import operations", "Shuffle (3:1)"));
|
||||
swing->setParent(root.get());
|
||||
|
||||
|
||||
Node *changeClef = new Node;
|
||||
changeClef->name = QCoreApplication::translate("MIDI import operations", "Allow clef changes within a staff");
|
||||
changeClef->oper.type = MidiOperation::Type::CHANGE_CLEF;
|
||||
changeClef->oper.value = TrackOperations().changeClef;
|
||||
changeClef->setParent(root.get());
|
||||
controller->clef = changeClef;
|
||||
|
||||
|
||||
Node *removeDrumRests = new Node;
|
||||
removeDrumRests->name = QCoreApplication::translate("MIDI import operations", "Remove rests and ties between notes");
|
||||
removeDrumRests->oper.type = MidiOperation::Type::REMOVE_DRUM_RESTS;
|
||||
removeDrumRests->oper.value = TrackOperations().removeDrumRests;
|
||||
removeDrumRests->setParent(root.get());
|
||||
controller->removeDrumRests = removeDrumRests;
|
||||
|
||||
|
||||
Node *splitDrums = new Node;
|
||||
splitDrums->name = QCoreApplication::translate("MIDI import operations", "Split drum set");
|
||||
splitDrums->oper.type = MidiOperation::Type::SPLIT_DRUMS;
|
||||
splitDrums->oper.value = TrackOperations().splitDrums.doSplit;;
|
||||
splitDrums->setParent(root.get());
|
||||
controller->splitDrums = splitDrums;
|
||||
|
||||
|
||||
Node *showStaffBracket = new Node;
|
||||
showStaffBracket->name = QCoreApplication::translate("MIDI import operations", "Show staff bracket");
|
||||
showStaffBracket->oper.type = MidiOperation::Type::SHOW_STAFF_BRACKET;
|
||||
showStaffBracket->oper.value = TrackOperations().splitDrums.showStaffBracket;
|
||||
showStaffBracket->setParent(splitDrums);
|
||||
controller->showStaffBracket = showStaffBracket;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
connect(this,
|
||||
SIGNAL(dataChanged(QModelIndex,QModelIndex)),
|
||||
SLOT(onDataChanged(QModelIndex)));
|
||||
controller->updateNodeDependencies(nullptr, true);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
OperationsModel::~OperationsModel()
|
||||
{
|
||||
}
|
||||
|
||||
void OperationsModel::setTimeSigVisibility(bool value)
|
||||
{
|
||||
controller->timeSigVisible = value;
|
||||
}
|
||||
|
||||
QModelIndex OperationsModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!root || row < 0 || column < 0 || column >= OperationCol::OCOL_COUNT)
|
||||
return QModelIndex();
|
||||
const Node *parentNode = nodeFromIndex(parent);
|
||||
if (!parentNode)
|
||||
return QModelIndex();
|
||||
if (parentNode->children.empty() || row >= (int)parentNode->children.size())
|
||||
return QModelIndex();
|
||||
// find new row in connection with invisible items
|
||||
int shift = 0;
|
||||
for (int i = 0; i <= row + shift; ++i) {
|
||||
if (i >= (int)parentNode->children.size())
|
||||
return QModelIndex();
|
||||
if (!parentNode->children.at(i)->visible)
|
||||
++shift;
|
||||
}
|
||||
Node *childNode = parentNode->children.at(row + shift).get();
|
||||
if (!childNode || !childNode->visible)
|
||||
return QModelIndex();
|
||||
return createIndex(row, column, childNode);
|
||||
}
|
||||
|
||||
QModelIndex OperationsModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
const Node *node = nodeFromIndex(child);
|
||||
if (!node)
|
||||
return QModelIndex();
|
||||
Node *parentNode = node->parent;
|
||||
if (!parentNode)
|
||||
return QModelIndex();
|
||||
const Node *grandparentNode = parentNode->parent;
|
||||
if (!grandparentNode)
|
||||
return QModelIndex();
|
||||
const auto &children = grandparentNode->children;
|
||||
const auto iter = std::find_if(children.begin(), children.end(),
|
||||
[parentNode](const std::unique_ptr<Node> &el){ return el.get() == parentNode; });
|
||||
const int row = (iter == children.end()) ? -1 : iter - children.begin();
|
||||
return createIndex(row, 0, parentNode);
|
||||
}
|
||||
|
||||
int OperationsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.column() >= OperationCol::OCOL_COUNT)
|
||||
return 0;
|
||||
const Node *parentNode = nodeFromIndex(parent);
|
||||
if (!parentNode)
|
||||
return 0;
|
||||
// take only visible nodes into account
|
||||
size_t counter = 0;
|
||||
for (const auto &p: parentNode->children)
|
||||
if (p->visible)
|
||||
++counter;
|
||||
return counter;
|
||||
}
|
||||
|
||||
int OperationsModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return OperationCol::OCOL_COUNT;
|
||||
}
|
||||
|
||||
// All nodes can have either bool value or list of possible values
|
||||
// also node value can be undefined (QVariant()), for example grayed checkbox
|
||||
|
||||
QVariant OperationsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
const Node *node = nodeFromIndex(index);
|
||||
if (!node)
|
||||
return QVariant();
|
||||
switch (role) {
|
||||
case DataRole:
|
||||
if (node->values.empty() && !node->values.empty()) // checkbox
|
||||
return node->oper.value.toBool();
|
||||
else
|
||||
return node->oper.value.toInt();
|
||||
break;
|
||||
case Qt::DisplayRole:
|
||||
switch (index.column()) {
|
||||
case OperationCol::OPER_NAME:
|
||||
return node->name;
|
||||
case OperationCol::VALUE:
|
||||
if (!node->values.empty()) {
|
||||
if (!node->oper.value.isValid()) // undefined operation value
|
||||
return " . . . ";
|
||||
// list contains names of possible string values
|
||||
// like {"1/4", "1/8"}
|
||||
// valid node value is one of enum items
|
||||
// -> use enum item as index
|
||||
int indexOfValue = node->oper.value.toInt();
|
||||
if (indexOfValue < node->values.size() && indexOfValue >= 0)
|
||||
return node->values.at(indexOfValue);
|
||||
}
|
||||
// otherwise return nothing because it's a checkbox
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::EditRole:
|
||||
if (index.column() == OperationCol::VALUE && !node->values.empty())
|
||||
return node->values;
|
||||
break;
|
||||
case Qt::CheckStateRole:
|
||||
if (index.column() == OperationCol::VALUE && node->values.empty()) {
|
||||
if (!node->oper.value.isValid())
|
||||
return Qt::PartiallyChecked;
|
||||
return (node->oper.value.toBool())
|
||||
? Qt::Checked : Qt::Unchecked;
|
||||
}
|
||||
break;
|
||||
case Qt::SizeHintRole:
|
||||
{
|
||||
QSize sz;
|
||||
sz.setHeight(22);
|
||||
return sz;
|
||||
}
|
||||
case OperationTypeRole:
|
||||
return (int)node->oper.type;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant OperationsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
{
|
||||
switch (section) {
|
||||
case OperationCol::OPER_NAME:
|
||||
return QCoreApplication::translate("MIDI import operations",
|
||||
"Selected track [%1] operations").arg(trackLabel);
|
||||
case OperationCol::VALUE:
|
||||
return QCoreApplication::translate("MIDI import operations", "Value");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags OperationsModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
const Node *node = nodeFromIndex(index);
|
||||
if (!node)
|
||||
return Qt::ItemFlags();
|
||||
Qt::ItemFlags flags = Qt::ItemFlags(Qt::ItemIsEnabled);
|
||||
if (index.column() == OperationCol::VALUE) {
|
||||
if (node->values.empty()) // node value is bool - a checkbox
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
else // node has list of values
|
||||
flags |= Qt::ItemIsEditable;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
bool OperationsModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
Node *node = nodeFromIndex(index);
|
||||
if (!node)
|
||||
return false;
|
||||
bool result = false;
|
||||
if (index.column() == OperationCol::VALUE) {
|
||||
switch (role) {
|
||||
case Qt::CheckStateRole:
|
||||
node->oper.value = value.toBool();
|
||||
result = true;
|
||||
break;
|
||||
case Qt::EditRole:
|
||||
// set enum value from value == list index
|
||||
node->oper.value = value.toInt();
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
emit dataChanged(index, index);
|
||||
return result;
|
||||
}
|
||||
|
||||
void setNodeOperations(Node *node, const DefinedTrackOperations &opers)
|
||||
{
|
||||
if (opers.undefinedOpers.contains((int)node->oper.type))
|
||||
node->oper.value = QVariant();
|
||||
else {
|
||||
switch (node->oper.type) {
|
||||
case MidiOperation::Type::DO_IMPORT:
|
||||
case MidiOperation::Type::LYRIC_TRACK_INDEX:
|
||||
break;
|
||||
|
||||
case MidiOperation::Type::QUANT_VALUE:
|
||||
node->oper.value = (int)opers.opers.quantize.value; break;
|
||||
case MidiOperation::Type::QUANT_HUMAN:
|
||||
node->oper.value = opers.opers.quantize.humanPerformance; break;
|
||||
|
||||
case MidiOperation::Type::TIME_SIG_NUMERATOR:
|
||||
node->oper.value = (int)opers.opers.timeSig.numerator; break;
|
||||
case MidiOperation::Type::TIME_SIG_DENOMINATOR:
|
||||
node->oper.value = (int)opers.opers.timeSig.denominator; break;
|
||||
|
||||
case MidiOperation::Type::DO_LHRH_SEPARATION:
|
||||
node->oper.value = opers.opers.LHRH.doIt; break;
|
||||
case MidiOperation::Type::LHRH_METHOD:
|
||||
node->oper.value = (int)opers.opers.LHRH.method; break;
|
||||
case MidiOperation::Type::LHRH_SPLIT_OCTAVE:
|
||||
node->oper.value = (int)opers.opers.LHRH.splitPitchOctave; break;
|
||||
case MidiOperation::Type::LHRH_SPLIT_NOTE:
|
||||
node->oper.value = (int)opers.opers.LHRH.splitPitchNote; break;
|
||||
|
||||
case MidiOperation::Type::USE_DOTS:
|
||||
node->oper.value = opers.opers.useDots; break;
|
||||
|
||||
case MidiOperation::Type::SIMPLIFY_DURATIONS:
|
||||
node->oper.value = opers.opers.simplifyDurations; break;
|
||||
case MidiOperation::Type::SHOW_STACCATO:
|
||||
node->oper.value = opers.opers.showStaccato; break;
|
||||
|
||||
case MidiOperation::Type::SEPARATE_VOICES:
|
||||
node->oper.value = opers.opers.separateVoices; break;
|
||||
|
||||
case MidiOperation::Type::SWING:
|
||||
node->oper.value = (int)opers.opers.swing; break;
|
||||
|
||||
case MidiOperation::Type::ALLOWED_VOICES:
|
||||
node->oper.value = (int)opers.opers.allowedVoices; break;
|
||||
|
||||
case MidiOperation::Type::TUPLET_SEARCH:
|
||||
node->oper.value = opers.opers.tuplets.doSearch; break;
|
||||
case MidiOperation::Type::TUPLET_2:
|
||||
node->oper.value = opers.opers.tuplets.duplets; break;
|
||||
case MidiOperation::Type::TUPLET_3:
|
||||
node->oper.value = opers.opers.tuplets.triplets; break;
|
||||
case MidiOperation::Type::TUPLET_4:
|
||||
node->oper.value = opers.opers.tuplets.quadruplets; break;
|
||||
case MidiOperation::Type::TUPLET_5:
|
||||
node->oper.value = opers.opers.tuplets.quintuplets; break;
|
||||
case MidiOperation::Type::TUPLET_7:
|
||||
node->oper.value = opers.opers.tuplets.septuplets; break;
|
||||
case MidiOperation::Type::TUPLET_9:
|
||||
node->oper.value = opers.opers.tuplets.nonuplets; break;
|
||||
|
||||
case MidiOperation::Type::CHANGE_CLEF:
|
||||
node->oper.value = opers.opers.changeClef; break;
|
||||
|
||||
case MidiOperation::Type::PICKUP_MEASURE:
|
||||
node->oper.value = opers.opers.pickupMeasure; break;
|
||||
|
||||
case MidiOperation::Type::SPLIT_DRUMS:
|
||||
node->oper.value = opers.opers.splitDrums.doSplit; break;
|
||||
case MidiOperation::Type::SHOW_STAFF_BRACKET:
|
||||
node->oper.value = opers.opers.splitDrums.showStaffBracket; break;
|
||||
case MidiOperation::Type::REMOVE_DRUM_RESTS:
|
||||
node->oper.value = opers.opers.removeDrumRests; break;
|
||||
}
|
||||
}
|
||||
for (const auto &nodePtr: node->children)
|
||||
setNodeOperations(nodePtr.get(), opers);
|
||||
}
|
||||
|
||||
void OperationsModel::setTrackData(const QString &trackLabel,
|
||||
const DefinedTrackOperations &opers)
|
||||
{
|
||||
this->trackLabel = trackLabel;
|
||||
controller->isDrumTrack = opers.isDrumTrack;
|
||||
controller->allTracksSelected = opers.allTracksSelected;
|
||||
// set new operations values
|
||||
beginResetModel();
|
||||
for (const auto &nodePtr: root->children)
|
||||
setNodeOperations(nodePtr.get(), opers);
|
||||
controller->updateNodeDependencies(nullptr, true);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void OperationsModel::onDataChanged(const QModelIndex &index)
|
||||
{
|
||||
Node *node = nodeFromIndex(index);
|
||||
if (!node)
|
||||
return;
|
||||
if (controller->updateNodeDependencies(node, false))
|
||||
layoutChanged();
|
||||
}
|
||||
|
||||
void OperationsModel::updateQuantValue()
|
||||
{
|
||||
const auto newPrefQuantValue = ReducedFraction::fromTicks(preferences.shortestNote);
|
||||
if (newPrefQuantValue == prefQuantValue)
|
||||
return;
|
||||
|
||||
prefQuantValue = newPrefQuantValue;
|
||||
const auto division = ReducedFraction::fromTicks(MScore::division);
|
||||
QString value;
|
||||
|
||||
if (prefQuantValue == division)
|
||||
value += "Quarter";
|
||||
else if (prefQuantValue == division / 2)
|
||||
value += "Eighth";
|
||||
else if (prefQuantValue == division / 4)
|
||||
value += "16th";
|
||||
else if (prefQuantValue == division / 8)
|
||||
value += "32nd";
|
||||
else if (prefQuantValue == division / 16)
|
||||
value += "64th";
|
||||
else if (prefQuantValue == division / 32)
|
||||
value += "128th";
|
||||
else
|
||||
Q_ASSERT_X(false, "OperationsModel::updateQuantValue", "Unknown quantization value");
|
||||
|
||||
controller->quantValue->values[0] = QCoreApplication::translate(
|
||||
"MIDI import operations", "Value from preferences (%1)").arg(value);
|
||||
emit dataChanged(QModelIndex(), QModelIndex());
|
||||
}
|
||||
|
||||
Node* OperationsModel::nodeFromIndex(const QModelIndex &index) const
|
||||
{
|
||||
if (index.isValid())
|
||||
return static_cast<Node *>(index.internalPointer());
|
||||
else
|
||||
return root.get();
|
||||
}
|
||||
|
||||
// Different controller actions, i.e. conditional visibility of node
|
||||
|
||||
bool Controller::updateNodeDependencies(Node *node, bool forceUpdate)
|
||||
{
|
||||
bool result = false;
|
||||
if (!node && !forceUpdate)
|
||||
return result;
|
||||
if (LHRHMethod && (forceUpdate || node == LHRHMethod)) {
|
||||
const auto value = (MidiOperation::LHRHMethod)LHRHMethod->oper.value.toInt();
|
||||
switch (value) {
|
||||
case MidiOperation::LHRHMethod::HAND_WIDTH:
|
||||
if (LHRHPitchOctave)
|
||||
LHRHPitchOctave->visible = false;
|
||||
if (LHRHPitchNote)
|
||||
LHRHPitchNote->visible = false;
|
||||
result = true;
|
||||
break;
|
||||
case MidiOperation::LHRHMethod::SPECIFIED_PITCH:
|
||||
if (LHRHPitchOctave)
|
||||
LHRHPitchOctave->visible = true;
|
||||
if (LHRHPitchNote)
|
||||
LHRHPitchNote->visible = true;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (LHRHdoIt && (forceUpdate || node == LHRHdoIt)) {
|
||||
const auto value = LHRHdoIt->oper.value.toBool();
|
||||
if (LHRHMethod)
|
||||
LHRHMethod->visible = value;
|
||||
result = true;
|
||||
}
|
||||
if (simplifyDurations && (forceUpdate || node == simplifyDurations)) {
|
||||
const auto value = simplifyDurations->oper.value.toBool();
|
||||
if (showStaccato)
|
||||
showStaccato->visible = value;
|
||||
result = true;
|
||||
}
|
||||
if (allowedVoices && (forceUpdate || node == allowedVoices)) {
|
||||
const auto value = (MidiOperation::AllowedVoices)allowedVoices->oper.value.toInt();
|
||||
switch (value) {
|
||||
case MidiOperation::AllowedVoices::V_1:
|
||||
if (separateVoices)
|
||||
separateVoices->visible = false;
|
||||
result = true;
|
||||
break;
|
||||
case MidiOperation::AllowedVoices::V_2:
|
||||
case MidiOperation::AllowedVoices::V_3:
|
||||
case MidiOperation::AllowedVoices::V_4:
|
||||
if (separateVoices)
|
||||
separateVoices->visible = true;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (searchTuplets && (forceUpdate || node == searchTuplets)) {
|
||||
const auto value = searchTuplets->oper.value.toBool();
|
||||
if (duplets)
|
||||
duplets->visible = value;
|
||||
if (triplets)
|
||||
triplets->visible = value;
|
||||
if (quadruplets)
|
||||
quadruplets->visible = value;
|
||||
if (quintuplets)
|
||||
quintuplets->visible = value;
|
||||
if (septuplets)
|
||||
septuplets->visible = value;
|
||||
if (nonuplets)
|
||||
nonuplets->visible = value;
|
||||
result = true;
|
||||
}
|
||||
if (splitDrums && (forceUpdate || node == splitDrums)) {
|
||||
const auto value = splitDrums->oper.value.toBool();
|
||||
if (showStaffBracket)
|
||||
showStaffBracket->visible = value;
|
||||
result = true;
|
||||
}
|
||||
if (forceUpdate) {
|
||||
if (LHRHdoIt)
|
||||
LHRHdoIt->visible = !isDrumTrack;
|
||||
if (splitDrums)
|
||||
splitDrums->visible = allTracksSelected || isDrumTrack;
|
||||
if (removeDrumRests)
|
||||
removeDrumRests->visible = allTracksSelected || isDrumTrack;
|
||||
if (allowedVoices)
|
||||
allowedVoices->visible = !isDrumTrack;
|
||||
if (separateVoices)
|
||||
separateVoices->visible = !isDrumTrack;
|
||||
if (clef)
|
||||
clef->visible = !isDrumTrack;
|
||||
if (pickupMeasure)
|
||||
pickupMeasure->visible = allTracksSelected;
|
||||
if (quantHuman)
|
||||
quantHuman->visible = allTracksSelected;
|
||||
if (timeSig)
|
||||
timeSig->visible = allTracksSelected && timeSigVisible;
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Ms
|
|
@ -1,65 +0,0 @@
|
|||
#ifndef IMPORTMIDI_OPMODEL_H
|
||||
#define IMPORTMIDI_OPMODEL_H
|
||||
|
||||
#include "importmidi_operation.h"
|
||||
#include "importmidi_fraction.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
enum {
|
||||
OperationTypeRole = 100,
|
||||
DataRole
|
||||
};
|
||||
|
||||
struct Node;
|
||||
struct Controller;
|
||||
struct TrackOperations;
|
||||
struct DefinedTrackOperations;
|
||||
|
||||
enum OperationCol {
|
||||
OPER_NAME,
|
||||
VALUE,
|
||||
OCOL_COUNT
|
||||
};
|
||||
|
||||
class OperationsModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OperationsModel();
|
||||
~OperationsModel();
|
||||
|
||||
void setTimeSigVisibility(bool value);
|
||||
void setTrackData(const QString &trackLabel, const Ms::DefinedTrackOperations &opers);
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const;
|
||||
QModelIndex parent(const QModelIndex &child) const;
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
int columnCount(const QModelIndex &parent) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||
|
||||
private slots:
|
||||
void onDataChanged(const QModelIndex &index);
|
||||
void updateQuantValue();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Node> root;
|
||||
std::unique_ptr<Controller> controller;
|
||||
QString trackLabel;
|
||||
QTimer *updateQuantTimer;
|
||||
ReducedFraction prefQuantValue;
|
||||
|
||||
Node* nodeFromIndex(const QModelIndex &index) const;
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_OPMODEL_H
|
|
@ -1,225 +1,92 @@
|
|||
#include "importmidi_panel.h"
|
||||
#include "ui_importmidi_panel.h"
|
||||
#include "musescore.h"
|
||||
#include "libmscore/score.h"
|
||||
#include "preferences.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "importmidi_trmodel.h"
|
||||
#include "importmidi_opmodel.h"
|
||||
#include "importmidi_opdelegate.h"
|
||||
#include "importmidi_data.h"
|
||||
#include "importmidi_model.h"
|
||||
#include "importmidi_lyrics.h"
|
||||
#include "importmidi_inner.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "importmidi_delegate.h"
|
||||
#include "preferences.h"
|
||||
#include "musescore.h"
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
extern Score::FileError importMidi(Score*, const QString&);
|
||||
extern QList<TrackMeta> extractMidiTracksMeta(const QString&);
|
||||
extern MuseScore* mscore;
|
||||
extern Preferences preferences;
|
||||
|
||||
|
||||
ImportMidiPanel::ImportMidiPanel(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::ImportMidiPanel)
|
||||
, updateUiTimer(new QTimer)
|
||||
, importInProgress(false)
|
||||
, prefferedVisible_(false)
|
||||
, reopenInProgress(false)
|
||||
, _ui(new Ui::ImportMidiPanel)
|
||||
, _updateUiTimer(new QTimer)
|
||||
, _prefferedVisible(false)
|
||||
, _importInProgress(false)
|
||||
, _reopenInProgress(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
tracksModel = new TracksModel();
|
||||
operationsModel = new OperationsModel();
|
||||
operationsDelegate = new OperationsDelegate(parentWidget(), false);
|
||||
tracksDelegate = new OperationsDelegate(parentWidget(), true); // same class
|
||||
ui->treeViewOperations->setModel(operationsModel);
|
||||
ui->treeViewOperations->setItemDelegate(operationsDelegate);
|
||||
ui->tableViewTracks->setModel(tracksModel);
|
||||
ui->tableViewTracks->setItemDelegate(tracksDelegate);
|
||||
tweakUi();
|
||||
_ui->setupUi(this);
|
||||
|
||||
_model = new TracksModel();
|
||||
_delegate = new OperationsDelegate(parentWidget(), false);
|
||||
_ui->tracksView->setModel(_model);
|
||||
_ui->tracksView->setItemDelegate(_delegate);
|
||||
|
||||
setupUi();
|
||||
}
|
||||
|
||||
ImportMidiPanel::~ImportMidiPanel()
|
||||
{
|
||||
delete ui;
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void ImportMidiPanel::onCurrentTrackChanged(const QModelIndex ¤tIndex)
|
||||
void ImportMidiPanel::setMidiFile(const QString &fileName)
|
||||
{
|
||||
if (currentIndex.isValid()) {
|
||||
const int row = currentIndex.row();
|
||||
QString trackLabel = tracksModel->data(
|
||||
tracksModel->index(row, TrackCol::TRACK_NUMBER)).toString();
|
||||
operationsModel->setTrackData(trackLabel, tracksModel->trackOperations(row));
|
||||
ui->treeViewOperations->expandAll();
|
||||
preferences.midiImportOperations.midiData().setSelectedRow(midiFile, row);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportMidiPanel::onOperationChanged(const QModelIndex &index)
|
||||
{
|
||||
const MidiOperation::Type operType = (MidiOperation::Type)index.data(OperationTypeRole).toInt();
|
||||
const QModelIndex ¤tIndex = ui->tableViewTracks->currentIndex();
|
||||
tracksModel->setOperation(currentIndex.row(), operType, index.data(DataRole));
|
||||
ui->treeViewOperations->expandAll();
|
||||
// select first column to clear focus of current item
|
||||
QModelIndex firstColIndex = operationsModel->index(index.row(), index.column() - 1,
|
||||
index.parent());
|
||||
ui->treeViewOperations->setCurrentIndex(firstColIndex);
|
||||
}
|
||||
|
||||
// Сlass to add an extra width to specific columns
|
||||
|
||||
class CustomHorizHeaderView : public QHeaderView
|
||||
{
|
||||
public:
|
||||
CustomHorizHeaderView() : QHeaderView(Qt::Horizontal) {}
|
||||
|
||||
protected:
|
||||
QSize sectionSizeFromContents(int logicalIndex) const
|
||||
{
|
||||
auto sz = QHeaderView::sectionSizeFromContents(logicalIndex);
|
||||
const int EXTRA_SPACE = 35;
|
||||
if (logicalIndex == TrackCol::STAFF_NAME || logicalIndex == TrackCol::INSTRUMENT)
|
||||
return QSize(sz.width() + EXTRA_SPACE, sz.height());
|
||||
else
|
||||
return sz;
|
||||
}
|
||||
};
|
||||
|
||||
void ImportMidiPanel::tweakUi()
|
||||
{
|
||||
connect(updateUiTimer, SIGNAL(timeout()), this, SLOT(updateUi()));
|
||||
connect(ui->pushButtonImport, SIGNAL(clicked()), SLOT(doMidiImport()));
|
||||
connect(ui->pushButtonUp, SIGNAL(clicked()), SLOT(moveTrackUp()));
|
||||
connect(ui->pushButtonDown, SIGNAL(clicked()), SLOT(moveTrackDown()));
|
||||
connect(ui->toolButtonHideMidiPanel, SIGNAL(clicked()), SLOT(hidePanel()));
|
||||
|
||||
const QItemSelectionModel *sm = ui->tableViewTracks->selectionModel();
|
||||
connect(sm, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
|
||||
SLOT(onCurrentTrackChanged(QModelIndex)));
|
||||
connect(ui->treeViewOperations->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
|
||||
SLOT(onOperationChanged(QModelIndex)));
|
||||
|
||||
updateUiTimer->start(100);
|
||||
updateUi();
|
||||
|
||||
ui->tableViewTracks->verticalHeader()->setDefaultSectionSize(22);
|
||||
ui->tableViewTracks->setHorizontalHeader(new CustomHorizHeaderView());
|
||||
ui->tableViewTracks->horizontalHeader()->setResizeMode(TrackCol::TRACK_NUMBER,
|
||||
QHeaderView::ResizeToContents);
|
||||
ui->tableViewTracks->horizontalHeader()->setResizeMode(TrackCol::DO_IMPORT,
|
||||
QHeaderView::ResizeToContents);
|
||||
ui->tableViewTracks->horizontalHeader()->setResizeMode(TrackCol::LYRICS,
|
||||
QHeaderView::Stretch);
|
||||
ui->tableViewTracks->horizontalHeader()->setResizeMode(TrackCol::STAFF_NAME,
|
||||
QHeaderView::Stretch);
|
||||
ui->tableViewTracks->horizontalHeader()->setResizeMode(TrackCol::INSTRUMENT,
|
||||
QHeaderView::Stretch);
|
||||
ui->treeViewOperations->header()->resizeSection(0, 300);
|
||||
ui->treeViewOperations->setAllColumnsShowFocus(true);
|
||||
ui->comboBoxCharset->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
|
||||
|
||||
fillCharsetList();
|
||||
}
|
||||
|
||||
bool ImportMidiPanel::canImportMidi() const
|
||||
{
|
||||
return isMidiFileExists() && tracksModel->numberOfTracksForImport();
|
||||
}
|
||||
|
||||
void ImportMidiPanel::hidePanel()
|
||||
{
|
||||
if (isVisible()) {
|
||||
setVisible(false);
|
||||
emit closeClicked();
|
||||
prefferedVisible_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ImportMidiPanel::canMoveTrackUp(int visualIndex)
|
||||
{
|
||||
return tracksModel->trackCount() > 1 && visualIndex > 1;
|
||||
}
|
||||
|
||||
bool ImportMidiPanel::canMoveTrackDown(int visualIndex)
|
||||
{
|
||||
return tracksModel->trackCount() > 1
|
||||
&& visualIndex < tracksModel->trackCount() && visualIndex > 0;
|
||||
}
|
||||
|
||||
int ImportMidiPanel::currentVisualIndex()
|
||||
{
|
||||
const auto selectedRows = ui->tableViewTracks->selectionModel()->selectedRows();
|
||||
int curRow = -1;
|
||||
if (!selectedRows.isEmpty())
|
||||
curRow = ui->tableViewTracks->selectionModel()->selectedRows()[0].row();
|
||||
const int visIndex = ui->tableViewTracks->verticalHeader()->visualIndex(curRow);
|
||||
|
||||
return visIndex;
|
||||
}
|
||||
|
||||
void ImportMidiPanel::moveTrackUp()
|
||||
{
|
||||
int visIndex = currentVisualIndex();
|
||||
if (canMoveTrackUp(visIndex))
|
||||
ui->tableViewTracks->verticalHeader()->moveSection(visIndex, visIndex - 1);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::moveTrackDown()
|
||||
{
|
||||
const int visIndex = currentVisualIndex();
|
||||
if (canMoveTrackDown(visIndex))
|
||||
ui->tableViewTracks->verticalHeader()->moveSection(visIndex, visIndex + 1);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::updateUi()
|
||||
{
|
||||
ui->pushButtonImport->setEnabled(canImportMidi());
|
||||
|
||||
const int visualIndex = currentVisualIndex();
|
||||
ui->pushButtonUp->setEnabled(canMoveTrackUp(visualIndex));
|
||||
ui->pushButtonDown->setEnabled(canMoveTrackDown(visualIndex));
|
||||
}
|
||||
|
||||
QList<int> ImportMidiPanel::findReorderedIndexes()
|
||||
{
|
||||
QList<int> reorderedIndexes;
|
||||
for (int i = 0; i != tracksModel->trackCount(); ++i) {
|
||||
const int trackRow = tracksModel->rowFromTrackIndex(i);
|
||||
const int reorderedRow = ui->tableViewTracks->verticalHeader()->logicalIndex(trackRow);
|
||||
const int reorderedIndex = tracksModel->trackIndexFromRow(reorderedRow);
|
||||
reorderedIndexes.push_back(reorderedIndex);
|
||||
}
|
||||
return reorderedIndexes;
|
||||
}
|
||||
|
||||
void ImportMidiPanel::doMidiImport()
|
||||
{
|
||||
if (!canImportMidi())
|
||||
if (_reopenInProgress)
|
||||
_reopenInProgress = false;
|
||||
if (_midiFile == fileName || _importInProgress)
|
||||
return;
|
||||
|
||||
importInProgress = true;
|
||||
const QList<int> reorderedIndexes = findReorderedIndexes();
|
||||
QList<TrackData> trackData;
|
||||
for (int i = 0; i != tracksModel->trackCount(); ++i) {
|
||||
tracksModel->setTrackReorderedIndex(i, reorderedIndexes.indexOf(i));
|
||||
trackData.push_back(tracksModel->trackData(i));
|
||||
_midiFile = fileName;
|
||||
updateUi();
|
||||
if (!QFile(_midiFile).exists())
|
||||
return;
|
||||
|
||||
MidiOperations::Data &opers = preferences.midiImportOperations;
|
||||
opers.setCurrentMidiFile(_midiFile);
|
||||
|
||||
_model->reset(opers.data()->trackOpers,
|
||||
MidiLyrics::makeLyricsListForUI(),
|
||||
opers.data()->trackCount);
|
||||
|
||||
if (opers.data()->isNewlyOpened) { // open new MIDI file
|
||||
resetTableViewState();
|
||||
saveTableViewState();
|
||||
}
|
||||
else { // switch to already opened MIDI file
|
||||
restoreTableViewState();
|
||||
}
|
||||
|
||||
setMidiPrefOperations(trackData);
|
||||
// update charset
|
||||
preferences.midiImportOperations.midiData().setCharset(
|
||||
midiFile, ui->comboBoxCharset->currentText());
|
||||
tracksModel->forceColumnDataChanged(TrackCol::STAFF_NAME);
|
||||
tracksModel->forceColumnDataChanged(TrackCol::LYRICS);
|
||||
_ui->tracksView->setFrozenRowCount(_model->frozenRowCount());
|
||||
_ui->tracksView->setFrozenColCount(_model->frozenColCount());
|
||||
_ui->comboBoxCharset->setCurrentText(preferences.midiImportOperations.data()->charset);
|
||||
_ui->tracksView->selectRow(opers.data()->selectedRow);
|
||||
_ui->tracksView->selectColumn(0);
|
||||
}
|
||||
|
||||
mscore->openScore(midiFile);
|
||||
clearMidiPrefOperations();
|
||||
preferences.midiImportOperations.midiData().setTracksData(midiFile, trackData);
|
||||
saveTableViewState(midiFile);
|
||||
importInProgress = false;
|
||||
void ImportMidiPanel::saveTableViewState()
|
||||
{
|
||||
const QByteArray hData = _ui->tracksView->horizontalHeader()->saveState();
|
||||
const QByteArray vData = _ui->tracksView->verticalHeader()->saveState();
|
||||
preferences.midiImportOperations.data()->HHeaderData = hData;
|
||||
preferences.midiImportOperations.data()->VHeaderData = vData;
|
||||
}
|
||||
|
||||
void ImportMidiPanel::restoreTableViewState()
|
||||
{
|
||||
const QByteArray hData = preferences.midiImportOperations.data()->HHeaderData;
|
||||
const QByteArray vData = preferences.midiImportOperations.data()->VHeaderData;
|
||||
_ui->tracksView->horizontalHeader()->restoreState(hData);
|
||||
_ui->tracksView->verticalHeader()->restoreState(vData);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::resetTableViewState()
|
||||
{
|
||||
_model->clear();
|
||||
_ui->tracksView->verticalHeader()->reset();
|
||||
}
|
||||
|
||||
bool ImportMidiPanel::isMidiFile(const QString &fileName)
|
||||
|
@ -228,199 +95,148 @@ bool ImportMidiPanel::isMidiFile(const QString &fileName)
|
|||
return (extension == "mid" || extension == "midi" || extension == "kar");
|
||||
}
|
||||
|
||||
void ImportMidiPanel::saveTableViewState(const QString &fileName)
|
||||
void ImportMidiPanel::setupUi()
|
||||
{
|
||||
const QByteArray hData = ui->tableViewTracks->horizontalHeader()->saveState();
|
||||
const QByteArray vData = ui->tableViewTracks->verticalHeader()->saveState();
|
||||
preferences.midiImportOperations.midiData().saveHHeaderState(fileName, hData);
|
||||
preferences.midiImportOperations.midiData().saveVHeaderState(fileName, vData);
|
||||
connect(_updateUiTimer, SIGNAL(timeout()), this, SLOT(updateUi()));
|
||||
connect(_ui->pushButtonApply, SIGNAL(clicked()), SLOT(applyMidiImport()));
|
||||
connect(_ui->pushButtonUp, SIGNAL(clicked()), SLOT(moveTrackUp()));
|
||||
connect(_ui->pushButtonDown, SIGNAL(clicked()), SLOT(moveTrackDown()));
|
||||
connect(_ui->toolButtonHideMidiPanel, SIGNAL(clicked()), SLOT(hidePanel()));
|
||||
|
||||
const QItemSelectionModel *sm = _ui->tracksView->selectionModel();
|
||||
connect(sm, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
|
||||
SLOT(onCurrentTrackChanged(QModelIndex)));
|
||||
|
||||
_updateUiTimer->start(100);
|
||||
updateUi();
|
||||
// tracks view
|
||||
_ui->tracksView->verticalHeader()->setDefaultSectionSize(24);
|
||||
// _ui->tracksView->horizontalHeader()->setResizeMode(TrackCol::DO_IMPORT,
|
||||
// QHeaderView::ResizeToContents);
|
||||
// _ui->tracksView->horizontalHeader()->setResizeMode(TrackCol::STAFF_NAME,
|
||||
// QHeaderView::Stretch);
|
||||
// _ui->tracksView->horizontalHeader()->setResizeMode(TrackCol::INSTRUMENT,
|
||||
// QHeaderView::Stretch);
|
||||
// charset
|
||||
_ui->comboBoxCharset->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
|
||||
fillCharsetList();
|
||||
}
|
||||
|
||||
void ImportMidiPanel::restoreTableViewState(const QString &fileName)
|
||||
void ImportMidiPanel::onCurrentTrackChanged(const QModelIndex ¤tIndex)
|
||||
{
|
||||
const QByteArray hData = preferences.midiImportOperations.midiData().HHeaderData(fileName);
|
||||
const QByteArray vData = preferences.midiImportOperations.midiData().VHeaderData(fileName);
|
||||
ui->tableViewTracks->horizontalHeader()->restoreState(hData);
|
||||
ui->tableViewTracks->verticalHeader()->restoreState(vData);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::setMidiPrefOperations(const QList<TrackData> &trackData)
|
||||
{
|
||||
clearMidiPrefOperations();
|
||||
for (const auto &data: trackData)
|
||||
preferences.midiImportOperations.appendTrackOperations(data.opers);
|
||||
preferences.midiImportOperations.setCurrentMidiFile(midiFile);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::resetTableViewState()
|
||||
{
|
||||
tracksModel->clear();
|
||||
ui->tableViewTracks->verticalHeader()->reset();
|
||||
}
|
||||
|
||||
void ImportMidiPanel::clearMidiPrefOperations()
|
||||
{
|
||||
preferences.midiImportOperations.clear();
|
||||
preferences.midiImportOperations.setCurrentMidiFile(midiFile);
|
||||
}
|
||||
|
||||
bool ImportMidiPanel::isMidiFileExists() const
|
||||
{
|
||||
return preferences.midiImportOperations.midiData().midiFile(midiFile);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::setMidiPrefOperations(const QString &fileName)
|
||||
{
|
||||
if (importInProgress)
|
||||
return;
|
||||
reopenInProgress = true;
|
||||
QList<TrackData> trackData
|
||||
= preferences.midiImportOperations.midiData().tracksData(fileName);
|
||||
setMidiPrefOperations(trackData);
|
||||
preferences.midiImportOperations.setCurrentMidiFile(fileName);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::showOrHideStaffNameCol(const QList<TrackMeta> &tracksMeta)
|
||||
{
|
||||
bool emptyName = true;
|
||||
for (const auto &meta: tracksMeta) {
|
||||
if (!meta.staffName.empty()) {
|
||||
emptyName = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (emptyName)
|
||||
ui->tableViewTracks->horizontalHeader()->hideSection(TrackCol::STAFF_NAME);
|
||||
else
|
||||
ui->tableViewTracks->horizontalHeader()->showSection(TrackCol::STAFF_NAME);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::showOrHideLyricsCol(const QList<TrackData> &tracksData)
|
||||
{
|
||||
bool hasLyricsTrack = false;
|
||||
for (const auto &data: tracksData) {
|
||||
if (data.opers.lyricTrackIndex >= 0) {
|
||||
hasLyricsTrack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasLyricsTrack)
|
||||
ui->tableViewTracks->horizontalHeader()->showSection(TrackCol::LYRICS);
|
||||
else
|
||||
ui->tableViewTracks->horizontalHeader()->hideSection(TrackCol::LYRICS);
|
||||
if (currentIndex.isValid())
|
||||
preferences.midiImportOperations.data()->selectedRow = currentIndex.row();
|
||||
}
|
||||
|
||||
void ImportMidiPanel::fillCharsetList()
|
||||
{
|
||||
QFontMetrics fm(ui->comboBoxCharset->font());
|
||||
QFontMetrics fm(_ui->comboBoxCharset->font());
|
||||
|
||||
ui->comboBoxCharset->clear();
|
||||
_ui->comboBoxCharset->clear();
|
||||
QList<QByteArray> charsets = QTextCodec::availableCodecs();
|
||||
qSort(charsets.begin(), charsets.end());
|
||||
int idx = 0;
|
||||
int maxWidth = 0;
|
||||
for (const auto &charset: charsets) {
|
||||
ui->comboBoxCharset->addItem(charset);
|
||||
_ui->comboBoxCharset->addItem(charset);
|
||||
if (charset == MidiCharset::defaultCharset())
|
||||
ui->comboBoxCharset->setCurrentIndex(idx);
|
||||
_ui->comboBoxCharset->setCurrentIndex(idx);
|
||||
int newWidth = fm.width(charset);
|
||||
if (newWidth > maxWidth)
|
||||
maxWidth = newWidth;
|
||||
++idx;
|
||||
}
|
||||
ui->comboBoxCharset->view()->setMinimumWidth(maxWidth);
|
||||
_ui->comboBoxCharset->view()->setMinimumWidth(maxWidth);
|
||||
}
|
||||
|
||||
void ImportMidiPanel::setMidiFile(const QString &fileName)
|
||||
void ImportMidiPanel::updateUi()
|
||||
{
|
||||
if (reopenInProgress)
|
||||
reopenInProgress = false;
|
||||
if (midiFile == fileName || importInProgress)
|
||||
return;
|
||||
midiFile = fileName;
|
||||
updateUi();
|
||||
_ui->pushButtonApply->setEnabled(canImportMidi());
|
||||
|
||||
if (isMidiFileExists()) {
|
||||
auto &midiData = preferences.midiImportOperations.midiData();
|
||||
QList<TrackData> trackData = midiData.tracksData(fileName);
|
||||
if (trackData.isEmpty()) { // open new MIDI file
|
||||
resetTableViewState();
|
||||
clearMidiPrefOperations();
|
||||
const QList<TrackMeta> tracksMeta = extractMidiTracksMeta(fileName);
|
||||
tracksModel->reset(tracksMeta);
|
||||
tracksModel->setLyricsList(MidiLyrics::makeLyricsListForUI());
|
||||
// assign initial values of computed options to GUI model
|
||||
const int row = 0; // for all tracks
|
||||
const bool isHumanPerformance = midiData.isHumanPerformance(fileName);
|
||||
tracksModel->setOperation(row, MidiOperation::Type::QUANT_HUMAN,
|
||||
QVariant(isHumanPerformance));
|
||||
const auto quantValue = midiData.quantValue(fileName);
|
||||
tracksModel->setOperation(row, MidiOperation::Type::QUANT_VALUE,
|
||||
QVariant((int)quantValue));
|
||||
if (isHumanPerformance) {
|
||||
const auto timeSig = midiData.timeSignature(fileName);
|
||||
const int visualIndex = currentVisualIndex();
|
||||
_ui->pushButtonUp->setEnabled(canMoveTrackUp(visualIndex));
|
||||
_ui->pushButtonDown->setEnabled(canMoveTrackDown(visualIndex));
|
||||
}
|
||||
|
||||
Q_ASSERT_X(timeSig != ReducedFraction(0, 1),
|
||||
"ImportMidiPanel::setMidiFile", "Zero time signature");
|
||||
|
||||
const auto numerator = Meter::fractionNumeratorToUserValue(timeSig.numerator());
|
||||
const auto denominator = Meter::fractionDenominatorToUserValue(timeSig.denominator());
|
||||
tracksModel->setOperation(row, MidiOperation::Type::TIME_SIG_NUMERATOR,
|
||||
QVariant((int)numerator));
|
||||
tracksModel->setOperation(row, MidiOperation::Type::TIME_SIG_DENOMINATOR,
|
||||
QVariant((int)denominator));
|
||||
}
|
||||
operationsModel->setTimeSigVisibility(isHumanPerformance);
|
||||
|
||||
for (int i = 0; i != tracksModel->trackCount(); ++i) {
|
||||
const bool needSplit = midiData.needToSplit(fileName, i);
|
||||
tracksModel->setOperation(
|
||||
tracksModel->rowFromTrackIndex(i),
|
||||
MidiOperation::Type::DO_LHRH_SEPARATION,
|
||||
QVariant(needSplit));
|
||||
}
|
||||
|
||||
showOrHideStaffNameCol(tracksMeta);
|
||||
for (int i = 0; i != tracksModel->trackCount(); ++i)
|
||||
trackData.push_back(tracksModel->trackData(i));
|
||||
midiData.setTracksData(fileName, trackData);
|
||||
showOrHideLyricsCol(trackData);
|
||||
saveTableViewState(fileName);
|
||||
}
|
||||
else { // load previously saved data (tracks, operations) for this MIDI file
|
||||
preferences.midiImportOperations.setCurrentMidiFile(midiFile);
|
||||
tracksModel->reset(trackData);
|
||||
tracksModel->setLyricsList(MidiLyrics::makeLyricsListForUI());
|
||||
// time sig option is shown only for files
|
||||
// that were initially detected as human-performed
|
||||
const bool isHumanPerformance = midiData.isHumanPerformance(fileName);
|
||||
operationsModel->setTimeSigVisibility(isHumanPerformance);
|
||||
restoreTableViewState(fileName);
|
||||
}
|
||||
ui->comboBoxCharset->setCurrentText(preferences.midiImportOperations.charset());
|
||||
ui->tableViewTracks->selectRow(
|
||||
preferences.midiImportOperations.midiData().selectedRow(midiFile));
|
||||
void ImportMidiPanel::hidePanel()
|
||||
{
|
||||
if (isVisible()) {
|
||||
setVisible(false);
|
||||
emit closeClicked();
|
||||
_prefferedVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ImportMidiPanel::applyMidiImport()
|
||||
{
|
||||
if (!canImportMidi())
|
||||
return;
|
||||
|
||||
_importInProgress = true;
|
||||
MidiOperations::FileData *midiData = preferences.midiImportOperations.data();
|
||||
// update charset
|
||||
if (midiData->charset != _ui->comboBoxCharset->currentText()) {
|
||||
midiData->charset = _ui->comboBoxCharset->currentText();
|
||||
// need to update model because of charset change
|
||||
_model->updateCharset();
|
||||
}
|
||||
mscore->openScore(_midiFile);
|
||||
midiData->trackOpers = _model->trackOpers();
|
||||
saveTableViewState();
|
||||
_importInProgress = false;
|
||||
}
|
||||
|
||||
bool ImportMidiPanel::canImportMidi() const
|
||||
{
|
||||
return QFile(_midiFile).exists() && _model->trackCountForImport() > 0;
|
||||
}
|
||||
|
||||
bool ImportMidiPanel::canMoveTrackUp(int visualIndex) const
|
||||
{
|
||||
return _model->trackCount() > 1 && visualIndex > 1;
|
||||
}
|
||||
|
||||
bool ImportMidiPanel::canMoveTrackDown(int visualIndex) const
|
||||
{
|
||||
return _model->trackCount() > 1
|
||||
&& visualIndex < _model->trackCount() && visualIndex > 0;
|
||||
}
|
||||
|
||||
int ImportMidiPanel::currentVisualIndex() const
|
||||
{
|
||||
const auto selectedRows = _ui->tracksView->selectionModel()->selectedRows();
|
||||
int curRow = -1;
|
||||
if (!selectedRows.isEmpty())
|
||||
curRow = _ui->tracksView->selectionModel()->selectedRows()[0].row();
|
||||
const int visIndex = _ui->tracksView->verticalHeader()->visualIndex(curRow);
|
||||
|
||||
return visIndex;
|
||||
}
|
||||
|
||||
void ImportMidiPanel::excludeMidiFile(const QString &fileName)
|
||||
{
|
||||
// because button "Apply" of MIDI import operations
|
||||
// causes reopen of the current score
|
||||
// we need to prevent MIDI import panel from closing at that moment
|
||||
if (importInProgress || reopenInProgress)
|
||||
if (_importInProgress || _reopenInProgress)
|
||||
return;
|
||||
|
||||
preferences.midiImportOperations.midiData().excludeFile(fileName);
|
||||
if (fileName == midiFile) {
|
||||
preferences.midiImportOperations.setCurrentMidiFile("");
|
||||
midiFile = "";
|
||||
auto &opers = preferences.midiImportOperations;
|
||||
opers.excludeFile(fileName);
|
||||
if (fileName == _midiFile) {
|
||||
opers.setCurrentMidiFile("");
|
||||
_midiFile = "";
|
||||
}
|
||||
}
|
||||
|
||||
void ImportMidiPanel::setPrefferedVisible(bool visible)
|
||||
{
|
||||
prefferedVisible_ = visible;
|
||||
_prefferedVisible = visible;
|
||||
}
|
||||
|
||||
void ImportMidiPanel::setReopenInProgress()
|
||||
{
|
||||
_reopenInProgress = true;
|
||||
}
|
||||
|
||||
} // namespace Ms
|
||||
|
|
|
@ -6,16 +6,11 @@ namespace Ui {
|
|||
class ImportMidiPanel;
|
||||
}
|
||||
|
||||
class QModelIndex;
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class TracksModel;
|
||||
class OperationsModel;
|
||||
class OperationsDelegate;
|
||||
struct TrackData;
|
||||
struct TrackMeta;
|
||||
|
||||
class ImportMidiPanel : public QWidget
|
||||
{
|
||||
|
@ -24,52 +19,44 @@ class ImportMidiPanel : public QWidget
|
|||
public:
|
||||
explicit ImportMidiPanel(QWidget *parent = 0);
|
||||
~ImportMidiPanel();
|
||||
static bool isMidiFile(const QString &fileName);
|
||||
|
||||
void setMidiFile(const QString &fileName);
|
||||
void excludeMidiFile(const QString &fileName);
|
||||
bool prefferedVisible() const { return prefferedVisible_; }
|
||||
bool isPrefferedVisible() const { return _prefferedVisible; }
|
||||
void setPrefferedVisible(bool visible);
|
||||
void setMidiPrefOperations(const QString &fileName);
|
||||
void setReopenInProgress();
|
||||
|
||||
static bool isMidiFile(const QString &fileName);
|
||||
|
||||
signals:
|
||||
void closeClicked();
|
||||
|
||||
private slots:
|
||||
void updateUi();
|
||||
void onCurrentTrackChanged(const QModelIndex ¤tIndex);
|
||||
void onOperationChanged(const QModelIndex &index);
|
||||
void doMidiImport();
|
||||
void hidePanel();
|
||||
void moveTrackUp();
|
||||
void moveTrackDown();
|
||||
bool canMoveTrackUp(int visualIndex);
|
||||
bool canMoveTrackDown(int visualIndex);
|
||||
void applyMidiImport();
|
||||
void onCurrentTrackChanged(const QModelIndex &);
|
||||
|
||||
private:
|
||||
void tweakUi();
|
||||
void setupUi();
|
||||
bool canImportMidi() const;
|
||||
QList<int> findReorderedIndexes();
|
||||
void saveTableViewState(const QString &fileName);
|
||||
void restoreTableViewState(const QString &fileName);
|
||||
bool canMoveTrackUp(int visualIndex) const;
|
||||
bool canMoveTrackDown(int visualIndex) const;
|
||||
int currentVisualIndex() const;
|
||||
void saveTableViewState();
|
||||
void restoreTableViewState();
|
||||
void resetTableViewState();
|
||||
int currentVisualIndex();
|
||||
void setMidiPrefOperations(const QList<TrackData> &trackData);
|
||||
void clearMidiPrefOperations();
|
||||
bool isMidiFileExists() const;
|
||||
void showOrHideStaffNameCol(const QList<TrackMeta> &tracksMeta);
|
||||
void showOrHideLyricsCol(const QList<TrackData> &tracksData);
|
||||
void fillCharsetList();
|
||||
|
||||
Ui::ImportMidiPanel *ui;
|
||||
QTimer *updateUiTimer;
|
||||
QString midiFile;
|
||||
TracksModel *tracksModel;
|
||||
OperationsModel *operationsModel;
|
||||
OperationsDelegate *operationsDelegate;
|
||||
OperationsDelegate *tracksDelegate;
|
||||
bool importInProgress;
|
||||
bool prefferedVisible_;
|
||||
bool reopenInProgress;
|
||||
Ui::ImportMidiPanel *_ui;
|
||||
QTimer *_updateUiTimer;
|
||||
|
||||
TracksModel *_model;
|
||||
OperationsDelegate *_delegate;
|
||||
bool _prefferedVisible;
|
||||
bool _importInProgress;
|
||||
bool _reopenInProgress;
|
||||
QString _midiFile;
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
|
|
|
@ -200,7 +200,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonImport">
|
||||
<widget class="QPushButton" name="pushButtonApply">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
|
@ -249,59 +249,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="handleWidth">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="TracksView" name="tableViewTracks">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::CurrentChanged|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTreeView" name="treeViewOperations">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::CurrentChanged|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
|
||||
</property>
|
||||
<property name="tabKeyNavigation">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="TracksView" name="tracksView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -309,7 +257,7 @@
|
|||
<customwidget>
|
||||
<class>TracksView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>mscore/importmidi_trview.h</header>
|
||||
<header>mscore/importmidi_view.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "importmidi_inner.h"
|
||||
#include "importmidi_beat.h"
|
||||
#include "importmidi_voice.h"
|
||||
#include "importmidi_operations.h"
|
||||
|
||||
#include <set>
|
||||
#include <deque>
|
||||
|
@ -20,31 +21,31 @@ extern Preferences preferences;
|
|||
|
||||
namespace Quantize {
|
||||
|
||||
ReducedFraction userQuantNoteToFraction(MidiOperation::QuantValue quantNote)
|
||||
ReducedFraction userQuantNoteToFraction(MidiOperations::QuantValue quantNote)
|
||||
{
|
||||
const auto division = ReducedFraction::fromTicks(MScore::division);
|
||||
auto userQuantValue = ReducedFraction::fromTicks(preferences.shortestNote);
|
||||
// specified quantization value
|
||||
switch (quantNote) {
|
||||
case MidiOperation::QuantValue::N_4:
|
||||
case MidiOperations::QuantValue::Q_4:
|
||||
userQuantValue = division;
|
||||
break;
|
||||
case MidiOperation::QuantValue::N_8:
|
||||
case MidiOperations::QuantValue::Q_8:
|
||||
userQuantValue = division / 2;
|
||||
break;
|
||||
case MidiOperation::QuantValue::N_16:
|
||||
case MidiOperations::QuantValue::Q_16:
|
||||
userQuantValue = division / 4;
|
||||
break;
|
||||
case MidiOperation::QuantValue::N_32:
|
||||
case MidiOperations::QuantValue::Q_32:
|
||||
userQuantValue = division / 8;
|
||||
break;
|
||||
case MidiOperation::QuantValue::N_64:
|
||||
case MidiOperations::QuantValue::Q_64:
|
||||
userQuantValue = division / 16;
|
||||
break;
|
||||
case MidiOperation::QuantValue::N_128:
|
||||
case MidiOperations::QuantValue::Q_128:
|
||||
userQuantValue = division / 32;
|
||||
break;
|
||||
case MidiOperation::QuantValue::FROM_PREFERENCES:
|
||||
case MidiOperations::QuantValue::FROM_PREFERENCES:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -366,9 +367,11 @@ void setIfHumanPerformance(
|
|||
if (allChords.empty())
|
||||
return;
|
||||
const bool isHuman = isHumanPerformance(allChords, sigmap);
|
||||
preferences.midiImportOperations.setHumanPerformance(isHuman);
|
||||
auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
opers.isHumanPerformance = isHuman;
|
||||
|
||||
if (isHuman) {
|
||||
preferences.midiImportOperations.setQuantValue(MidiOperation::QuantValue::N_8);
|
||||
opers.quantValue.setDefaultValue(MidiOperations::QuantValue::Q_8);
|
||||
const double ticksPerSec = MidiTempo::findBasicTempo(tracks) * MScore::division;
|
||||
MidiBeat::findBeatLocations(allChords, sigmap, ticksPerSec); // and set time sig
|
||||
}
|
||||
|
@ -992,8 +995,8 @@ double findTempoPenalty(
|
|||
|
||||
void applyDynamicProgramming(std::vector<QuantData> &quantData)
|
||||
{
|
||||
const auto &opers = preferences.midiImportOperations.currentTrackOperations();
|
||||
const bool isHuman = opers.quantize.humanPerformance;
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const bool isHuman = opers.isHumanPerformance;
|
||||
const double MERGE_PENALTY_COEFF = 5.0;
|
||||
|
||||
for (int chordIndex = 0; chordIndex != (int)quantData.size(); ++chordIndex) {
|
||||
|
|
|
@ -13,7 +13,7 @@ class ReducedFraction;
|
|||
|
||||
namespace Quantize {
|
||||
|
||||
ReducedFraction userQuantNoteToFraction(MidiOperation::QuantValue quantNote);
|
||||
ReducedFraction userQuantNoteToFraction(MidiOperations::QuantValue quantNote);
|
||||
|
||||
ReducedFraction findQuantForRange(
|
||||
const std::multimap<ReducedFraction, MidiChord>::const_iterator &beg,
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
#include "importmidi_tuplet.h"
|
||||
#include "importmidi_quant.h"
|
||||
#include "importmidi_voice.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "preferences.h"
|
||||
#include "libmscore/sig.h"
|
||||
#include "libmscore/durationtype.h"
|
||||
#include "importmidi_tuplet_voice.h"
|
||||
#include "midi/midifile.h"
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
@ -57,8 +59,10 @@ void lengthenNote(
|
|||
if (endTime <= note.offTime)
|
||||
return;
|
||||
|
||||
const auto &opers = preferences.midiImportOperations.currentTrackOperations();
|
||||
const bool useDots = opers.useDots;
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
|
||||
const bool useDots = opers.useDots.value(currentTrack);
|
||||
const auto tupletsForDuration = MidiTuplet::findTupletsInBarForDuration(
|
||||
voice, barStart, note.offTime, endTime - note.offTime, tuplets);
|
||||
|
||||
|
@ -111,7 +115,7 @@ void lengthenNote(
|
|||
|
||||
if (noteDurationCount + restDurationCount
|
||||
< minNoteDurationCount + minRestDurationCount) {
|
||||
if (opers.quantize.humanPerformance || noteDurationCount <= 1.5) {
|
||||
if (opers.isHumanPerformance || noteDurationCount <= 1.5) {
|
||||
minNoteDurationCount = noteDurationCount;
|
||||
minRestDurationCount = restDurationCount;
|
||||
bestOffTime = offTime;
|
||||
|
@ -142,7 +146,7 @@ void lengthenNote(
|
|||
// discard change because it silently reduces duration accuracy
|
||||
// without significant improvement of readability
|
||||
|
||||
if (!opers.quantize.humanPerformance
|
||||
if (!opers.isHumanPerformance
|
||||
&& (origNoteDurations.size() + origRestDurations.size())
|
||||
- (minNoteDurationCount + minRestDurationCount) <= 1
|
||||
&& !hasComplexBeamedDurations(origNoteDurations)
|
||||
|
@ -220,11 +224,9 @@ void simplifyDurations(std::multimap<int, MTrack> &tracks, const TimeSigMap *sig
|
|||
auto &chords = track.second.chords;
|
||||
if (chords.empty())
|
||||
continue;
|
||||
// pass current track index through MidiImportOperations
|
||||
// for further usage
|
||||
opers.setCurrentTrack(mtrack.indexOfOperation);
|
||||
|
||||
if (opers.currentTrackOperations().simplifyDurations) {
|
||||
if (opers.data()->trackOpers.simplifyDurations.value(mtrack.indexOfOperation)) {
|
||||
MidiOperations::CurrentTrackSetter setCurrentTrack{opers, mtrack.indexOfOperation};
|
||||
minimizeNumberOfRests(chords, sigmap, mtrack.tuplets);
|
||||
// empty tuplets may appear after simplification
|
||||
MidiTuplet::removeEmptyTuplets(mtrack);
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Swing {
|
|||
class SwingDetector
|
||||
{
|
||||
public:
|
||||
SwingDetector(MidiOperation::Swing st);
|
||||
SwingDetector(MidiOperations::Swing st);
|
||||
|
||||
void add(ChordRest *cr);
|
||||
bool wasSwingApplied() const { return swingApplied; }
|
||||
|
@ -25,7 +25,7 @@ class SwingDetector
|
|||
std::vector<ChordRest *> elements;
|
||||
ReducedFraction sumLen;
|
||||
const ReducedFraction FULL_LEN = {1, 4};
|
||||
MidiOperation::Swing swingType;
|
||||
MidiOperations::Swing swingType;
|
||||
bool swingApplied = false;
|
||||
|
||||
void reset();
|
||||
|
@ -38,7 +38,7 @@ class SwingDetector
|
|||
};
|
||||
|
||||
|
||||
SwingDetector::SwingDetector(MidiOperation::Swing st)
|
||||
SwingDetector::SwingDetector(MidiOperations::Swing st)
|
||||
: swingType(st)
|
||||
{
|
||||
}
|
||||
|
@ -61,10 +61,10 @@ void SwingDetector::add(ChordRest *cr)
|
|||
if (sumLen == FULL_LEN) {
|
||||
// check for swing patterns
|
||||
switch (swingType) {
|
||||
case MidiOperation::Swing::SWING:
|
||||
case MidiOperations::Swing::SWING:
|
||||
checkNormalSwing();
|
||||
break;
|
||||
case MidiOperation::Swing::SHUFFLE:
|
||||
case MidiOperations::Swing::SHUFFLE:
|
||||
checkShuffle();
|
||||
break;
|
||||
default:
|
||||
|
@ -192,23 +192,23 @@ bool SwingDetector::areAllNonTuplets() const
|
|||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
QString swingCaption(MidiOperation::Swing swingType)
|
||||
QString swingCaption(MidiOperations::Swing swingType)
|
||||
{
|
||||
QString caption;
|
||||
switch (swingType) {
|
||||
case MidiOperation::Swing::SWING:
|
||||
case MidiOperations::Swing::SWING:
|
||||
caption = "Swing";
|
||||
break;
|
||||
case MidiOperation::Swing::SHUFFLE:
|
||||
case MidiOperations::Swing::SHUFFLE:
|
||||
caption = "Shuffle";
|
||||
break;
|
||||
case MidiOperation::Swing::NONE:
|
||||
case MidiOperations::Swing::NONE:
|
||||
break;
|
||||
}
|
||||
return caption;
|
||||
}
|
||||
|
||||
void detectSwing(Staff *staff, MidiOperation::Swing swingType)
|
||||
void detectSwing(Staff *staff, MidiOperations::Swing swingType)
|
||||
{
|
||||
Score *score = staff->score();
|
||||
const int strack = staff->idx() * VOICES;
|
||||
|
|
|
@ -10,7 +10,7 @@ class Staff;
|
|||
|
||||
namespace Swing {
|
||||
|
||||
void detectSwing(Staff *staff, MidiOperation::Swing swingType);
|
||||
void detectSwing(Staff *staff, MidiOperations::Swing swingType);
|
||||
|
||||
} // namespace Swing
|
||||
} // namespace Ms
|
||||
|
|
|
@ -1,644 +0,0 @@
|
|||
#include "importmidi_trmodel.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "importmidi_inner.h"
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
TracksModel::TracksModel()
|
||||
: trackCount_(0)
|
||||
, colCount_(TrackCol::TCOL_COUNT)
|
||||
{
|
||||
}
|
||||
|
||||
void TracksModel::reset(const QList<TrackMeta> &tracksMeta)
|
||||
{
|
||||
beginResetModel();
|
||||
trackCount_ = tracksMeta.size();
|
||||
tracksData_.clear();
|
||||
int i = 0;
|
||||
for (const auto &meta: tracksMeta) {
|
||||
TrackOperations ops; // initialized by default values - see ctor
|
||||
ops.reorderedIndex = i++;
|
||||
ops.lyricTrackIndex = meta.initLyricTrackIndex;
|
||||
tracksData_.push_back({meta, ops});
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TracksModel::reset(const QList<TrackData> &tracksData)
|
||||
{
|
||||
beginResetModel();
|
||||
trackCount_ = tracksData.size();
|
||||
tracksData_ = tracksData;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TracksModel::clear()
|
||||
{
|
||||
beginResetModel();
|
||||
trackCount_ = 0;
|
||||
tracksData_.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TracksModel::setOperation(int row, MidiOperation::Type operType, const QVariant &operValue)
|
||||
{
|
||||
const int trackIndex = trackIndexFromRow(row);
|
||||
if (trackIndex == -1)
|
||||
setOperationForAllTracks(operType, operValue);
|
||||
else if (isTrackIndexValid(trackIndex))
|
||||
setTrackOperation(trackIndex, operType, operValue);
|
||||
}
|
||||
|
||||
void TracksModel::setTrackReorderedIndex(int trackIndex, int reorderIndex)
|
||||
{
|
||||
if (!isTrackIndexValid(trackIndex))
|
||||
return;
|
||||
tracksData_[trackIndex].opers.reorderedIndex = reorderIndex;
|
||||
}
|
||||
|
||||
void TracksModel::setLyricsList(const QList<std::string> &list)
|
||||
{
|
||||
lyricsList_ = list;
|
||||
}
|
||||
|
||||
void TracksModel::setOperationForAllTracks(MidiOperation::Type operType,
|
||||
const QVariant &operValue)
|
||||
{
|
||||
for (int i = 0; i != trackCount_; ++i)
|
||||
setTrackOperation(i, operType, operValue);
|
||||
}
|
||||
|
||||
void TracksModel::setTrackOperation(int trackIndex, MidiOperation::Type operType,
|
||||
const QVariant &operValue)
|
||||
{
|
||||
if (!operValue.isValid() || !isTrackIndexValid(trackIndex))
|
||||
return;
|
||||
TrackData &trackData = tracksData_[trackIndex];
|
||||
|
||||
switch (operType) {
|
||||
case MidiOperation::Type::QUANT_VALUE:
|
||||
trackData.opers.quantize.value = (MidiOperation::QuantValue)operValue.toInt();
|
||||
break;
|
||||
case MidiOperation::Type::QUANT_HUMAN:
|
||||
trackData.opers.quantize.humanPerformance = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::TIME_SIG_NUMERATOR:
|
||||
trackData.opers.timeSig.numerator = (MidiOperation::TimeSigNumerator)operValue.toInt();
|
||||
break;
|
||||
case MidiOperation::Type::TIME_SIG_DENOMINATOR:
|
||||
trackData.opers.timeSig.denominator = (MidiOperation::TimeSigDenominator)operValue.toInt();
|
||||
break;
|
||||
case MidiOperation::Type::DO_LHRH_SEPARATION:
|
||||
trackData.opers.LHRH.doIt = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::SPLIT_DRUMS:
|
||||
trackData.opers.splitDrums.doSplit = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::SHOW_STAFF_BRACKET:
|
||||
trackData.opers.splitDrums.showStaffBracket = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::REMOVE_DRUM_RESTS:
|
||||
trackData.opers.removeDrumRests = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::LHRH_METHOD:
|
||||
trackData.opers.LHRH.method = (MidiOperation::LHRHMethod)operValue.toInt();
|
||||
break;
|
||||
case MidiOperation::Type::LHRH_SPLIT_OCTAVE:
|
||||
trackData.opers.LHRH.splitPitchOctave
|
||||
= (MidiOperation::Octave)operValue.toInt();
|
||||
break;
|
||||
case MidiOperation::Type::LHRH_SPLIT_NOTE:
|
||||
trackData.opers.LHRH.splitPitchNote = (MidiOperation::Note)operValue.toInt();
|
||||
break;
|
||||
case MidiOperation::Type::USE_DOTS:
|
||||
trackData.opers.useDots = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::SIMPLIFY_DURATIONS:
|
||||
trackData.opers.simplifyDurations = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::SHOW_STACCATO:
|
||||
trackData.opers.showStaccato = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::SEPARATE_VOICES:
|
||||
trackData.opers.separateVoices = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::SWING:
|
||||
trackData.opers.swing = (MidiOperation::Swing)operValue.toInt();
|
||||
break;
|
||||
case MidiOperation::Type::ALLOWED_VOICES:
|
||||
trackData.opers.allowedVoices = (MidiOperation::AllowedVoices)operValue.toInt();
|
||||
break;
|
||||
case MidiOperation::Type::TUPLET_SEARCH:
|
||||
trackData.opers.tuplets.doSearch = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::TUPLET_2:
|
||||
trackData.opers.tuplets.duplets = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::TUPLET_3:
|
||||
trackData.opers.tuplets.triplets = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::TUPLET_4:
|
||||
trackData.opers.tuplets.quadruplets = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::TUPLET_5:
|
||||
trackData.opers.tuplets.quintuplets = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::TUPLET_7:
|
||||
trackData.opers.tuplets.septuplets = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::TUPLET_9:
|
||||
trackData.opers.tuplets.nonuplets = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::CHANGE_CLEF:
|
||||
trackData.opers.changeClef = operValue.toBool();
|
||||
break;
|
||||
case MidiOperation::Type::PICKUP_MEASURE:
|
||||
trackData.opers.pickupMeasure = operValue.toBool();
|
||||
break;
|
||||
|
||||
case MidiOperation::Type::DO_IMPORT:
|
||||
case MidiOperation::Type::LYRIC_TRACK_INDEX:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DefinedTrackOperations TracksModel::trackOperations(int row) const
|
||||
{
|
||||
DefinedTrackOperations opers;
|
||||
const int trackIndex = trackIndexFromRow(row);
|
||||
opers.allTracksSelected = (trackIndex == -1 || trackCount_ == 1);
|
||||
|
||||
if (trackIndex == -1) {
|
||||
// all tracks row case
|
||||
// find tracks that operation values are different
|
||||
// and mark them as undefined
|
||||
|
||||
opers.opers = tracksData_.front().opers;
|
||||
opers.isDrumTrack = false;
|
||||
|
||||
// MidiOperation::Type::QUANT_VALUE
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.quantize.value != opers.opers.quantize.value) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::QUANT_VALUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::QUANT_HUMAN
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.quantize.humanPerformance
|
||||
!= opers.opers.quantize.humanPerformance) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::QUANT_HUMAN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::TIME_SIG_NUMERATOR
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.timeSig.numerator != opers.opers.timeSig.numerator) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TIME_SIG_NUMERATOR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::TIME_SIG_DENOMINATOR
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.timeSig.denominator != opers.opers.timeSig.denominator) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TIME_SIG_DENOMINATOR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::DO_LHRH_SEPARATION
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.LHRH.doIt != opers.opers.LHRH.doIt) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::DO_LHRH_SEPARATION);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::SPLIT_DRUMS
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.splitDrums.doSplit != opers.opers.splitDrums.doSplit) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::SPLIT_DRUMS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::SHOW_STAFF_BRACKET
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.splitDrums.showStaffBracket != opers.opers.splitDrums.showStaffBracket) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::SHOW_STAFF_BRACKET);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::REMOVE_DRUM_RESTS
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.removeDrumRests != opers.opers.removeDrumRests) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::REMOVE_DRUM_RESTS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::LHRH_METHOD
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.LHRH.method != opers.opers.LHRH.method) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::LHRH_METHOD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::LHRH_SPLIT_OCTAVE
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.LHRH.splitPitchOctave
|
||||
!= opers.opers.LHRH.splitPitchOctave) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::LHRH_SPLIT_OCTAVE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::LHRH_SPLIT_NOTE
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.LHRH.splitPitchNote
|
||||
!= opers.opers.LHRH.splitPitchNote) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::LHRH_SPLIT_NOTE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::USE_DOTS
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.useDots != opers.opers.useDots) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::USE_DOTS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::SIMPLIFY_DURATIONS
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.simplifyDurations != opers.opers.simplifyDurations) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::SIMPLIFY_DURATIONS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::SHOW_STACCATO
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.simplifyDurations != opers.opers.showStaccato) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::SHOW_STACCATO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::SEPARATE_VOICES
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.separateVoices != opers.opers.separateVoices) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::SEPARATE_VOICES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::SWING
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.swing != opers.opers.swing) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::SWING);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::ALLOWED_VOICES
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.allowedVoices != opers.opers.allowedVoices) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::ALLOWED_VOICES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::TUPLET_SEARCH
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.tuplets.doSearch != opers.opers.tuplets.doSearch) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TUPLET_SEARCH);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::TUPLET_2
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.tuplets.duplets != opers.opers.tuplets.duplets) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TUPLET_2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::TUPLET_3
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.tuplets.triplets != opers.opers.tuplets.triplets) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TUPLET_3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// MidiOperation::Type::TUPLET_4
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.tuplets.quadruplets != opers.opers.tuplets.quadruplets) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TUPLET_4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::TUPLET_5
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.tuplets.quintuplets != opers.opers.tuplets.quintuplets) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TUPLET_5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::TUPLET_7
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.tuplets.septuplets != opers.opers.tuplets.septuplets) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TUPLET_7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::TUPLET_9
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.tuplets.nonuplets != opers.opers.tuplets.nonuplets) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::TUPLET_9);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::CHANGE_CLEF
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.changeClef != opers.opers.changeClef) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::CHANGE_CLEF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MidiOperation::Type::PICKUP_MEASURE
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.pickupMeasure != opers.opers.pickupMeasure) {
|
||||
opers.undefinedOpers.insert((int)MidiOperation::Type::PICKUP_MEASURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
opers.opers = tracksData_[trackIndex].opers;
|
||||
opers.isDrumTrack = tracksData_[trackIndex].meta.isDrumTrack;
|
||||
}
|
||||
|
||||
return opers;
|
||||
}
|
||||
|
||||
int TracksModel::rowFromTrackIndex(int trackIndex) const
|
||||
{
|
||||
// first row reserved for all tracks if track count > 1
|
||||
return (trackCount_ > 1) ? trackIndex + 1 : trackIndex;
|
||||
}
|
||||
|
||||
int TracksModel::trackIndexFromRow(int row) const
|
||||
{
|
||||
// first row reserved for all tracks if track count > 1
|
||||
// return -1 if row is all tracks row
|
||||
return (trackCount_ > 1) ? row - 1 : row;
|
||||
}
|
||||
|
||||
int TracksModel::numberOfTracksForImport() const
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.doImport)
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int TracksModel::rowCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return (trackCount_ > 1) ? trackCount_ + 1 : trackCount_;
|
||||
}
|
||||
|
||||
int TracksModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return colCount_;
|
||||
}
|
||||
|
||||
Qt::CheckState TracksModel::areAllTracksForImport() const
|
||||
{
|
||||
if (trackCount_ == 0)
|
||||
return Qt::Unchecked;
|
||||
const bool doFirstTrackImport = tracksData_[0].opers.doImport;
|
||||
for (int i = 1; i != trackCount_; ++i) {
|
||||
if (tracksData_[i].opers.doImport != doFirstTrackImport)
|
||||
return Qt::PartiallyChecked;
|
||||
}
|
||||
return (doFirstTrackImport) ? Qt::Checked : Qt::Unchecked;
|
||||
}
|
||||
|
||||
QVariant TracksModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
const int trackIndex = trackIndexFromRow(index.row());
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
switch (index.column()) {
|
||||
case TrackCol::TRACK_NUMBER:
|
||||
if (trackIndex == -1)
|
||||
return QCoreApplication::translate("MIDI import operations", "All");
|
||||
return trackIndex + 1;
|
||||
case TrackCol::LYRICS:
|
||||
{
|
||||
if (trackIndex == -1)
|
||||
return "";
|
||||
int lyricTrack = tracksData_[trackIndex].opers.lyricTrackIndex;
|
||||
if (lyricTrack == -1)
|
||||
return "";
|
||||
if (lyricTrack >= 0 && lyricTrack < lyricsList_.size())
|
||||
return MidiCharset::convertToCharset(lyricsList_[lyricTrack]);
|
||||
}
|
||||
break;
|
||||
case TrackCol::STAFF_NAME:
|
||||
if (trackIndex == -1)
|
||||
return "";
|
||||
return MidiCharset::convertToCharset(
|
||||
tracksData_[trackIndex].meta.staffName);
|
||||
case TrackCol::INSTRUMENT:
|
||||
if (trackIndex == -1)
|
||||
return "";
|
||||
return tracksData_[trackIndex].meta.instrumentName;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::EditRole:
|
||||
if (index.column() == TrackCol::LYRICS && trackIndex != -1) {
|
||||
if (!lyricsList_.isEmpty()) {
|
||||
auto list = QStringList("");
|
||||
for (const auto &lyric: lyricsList_)
|
||||
list.append(MidiCharset::convertToCharset(lyric));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::CheckStateRole:
|
||||
switch (index.column()) {
|
||||
case TrackCol::DO_IMPORT:
|
||||
if (trackIndex == -1)
|
||||
return areAllTracksForImport();
|
||||
return (tracksData_[trackIndex].opers.doImport)
|
||||
? Qt::Checked : Qt::Unchecked;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::TextAlignmentRole:
|
||||
if (index.column() == TrackCol::LYRICS)
|
||||
return Qt::AlignLeft + Qt::AlignVCenter;
|
||||
else
|
||||
return Qt::AlignCenter;
|
||||
break;
|
||||
case Qt::ToolTipRole:
|
||||
if (trackIndex != -1) {
|
||||
switch (index.column()) {
|
||||
case TrackCol::STAFF_NAME:
|
||||
return MidiCharset::convertToCharset(
|
||||
tracksData_[trackIndex].meta.staffName);
|
||||
case TrackCol::INSTRUMENT:
|
||||
return tracksData_[trackIndex].meta.instrumentName;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags TracksModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
Qt::ItemFlags flags = Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
if (index.column() == TrackCol::DO_IMPORT)
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
if (index.column() == TrackCol::LYRICS
|
||||
&& !lyricsList_.isEmpty()
|
||||
&& trackIndexFromRow(index.row()) != -1) {
|
||||
flags |= Qt::ItemIsEditable;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
void TracksModel::forceColumnDataChanged(int col)
|
||||
{
|
||||
const auto begIndex = this->index(0, col);
|
||||
const auto endIndex = this->index(rowCount(QModelIndex()), col);
|
||||
emit dataChanged(begIndex, endIndex);
|
||||
}
|
||||
|
||||
bool TracksModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
bool result = false;
|
||||
const int trackIndex = trackIndexFromRow(index.row());
|
||||
|
||||
if (trackIndex == -1) { // all tracks row
|
||||
if (index.column() == TrackCol::DO_IMPORT && role == Qt::CheckStateRole) {
|
||||
for (auto &trackData: tracksData_)
|
||||
trackData.opers.doImport = value.toBool();
|
||||
result = true;
|
||||
}
|
||||
if (result) {
|
||||
// update checkboxes of all tracks
|
||||
// because we've changed option for all tracks simultaneously
|
||||
forceColumnDataChanged(TrackCol::DO_IMPORT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
TrackData *trackData = trackDataFromIndex(index);
|
||||
if (!trackData)
|
||||
return false;
|
||||
if (index.column() == TrackCol::DO_IMPORT && role == Qt::CheckStateRole) {
|
||||
trackData->opers.doImport = value.toBool();
|
||||
result = true;
|
||||
}
|
||||
else if (index.column() == TrackCol::LYRICS && role == Qt::EditRole) {
|
||||
trackData->opers.lyricTrackIndex = value.toInt() - 1;
|
||||
result = true;
|
||||
}
|
||||
if (result) {
|
||||
// update checkbox of current track row
|
||||
emit dataChanged(index, index);
|
||||
// update checkbox of <all tracks> row
|
||||
const auto allIndex = this->index(0, TrackCol::DO_IMPORT);
|
||||
emit dataChanged(allIndex, allIndex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariant TracksModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case TrackCol::DO_IMPORT:
|
||||
return QCoreApplication::translate("MIDI import track list", "Import");
|
||||
case TrackCol::TRACK_NUMBER:
|
||||
return QCoreApplication::translate("MIDI import track list", "Track");
|
||||
case TrackCol::LYRICS:
|
||||
return QCoreApplication::translate("MIDI import track list", "Lyrics");
|
||||
case TrackCol::STAFF_NAME:
|
||||
return QCoreApplication::translate("MIDI import track list", "Staff Name");
|
||||
case TrackCol::INSTRUMENT:
|
||||
return QCoreApplication::translate("MIDI import track list", "Sound");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
TrackData TracksModel::trackData(int trackIndex) const
|
||||
{
|
||||
if (isTrackIndexValid(trackIndex))
|
||||
return tracksData_[trackIndex];
|
||||
return TrackData();
|
||||
}
|
||||
|
||||
TrackData* TracksModel::trackDataFromIndex(const QModelIndex &index)
|
||||
{
|
||||
if (index.isValid()) {
|
||||
if (!isMappingRowToTrackValid(index.row()) || !isColumnValid(index.column()))
|
||||
return nullptr;
|
||||
return &tracksData_[trackIndexFromRow(index.row())];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TracksModel::isMappingRowToTrackValid(int row) const
|
||||
{
|
||||
if (trackCount_ > 1) // first row is reserved for all tracks
|
||||
return (row > 0 && row <= trackCount_);
|
||||
return row >= 0 && row < trackCount_;
|
||||
}
|
||||
|
||||
bool TracksModel::isColumnValid(int column) const
|
||||
{
|
||||
return (column >= 0 && column < colCount_);
|
||||
}
|
||||
|
||||
bool TracksModel::isTrackIndexValid(int trackIndex) const
|
||||
{
|
||||
return trackIndex >= 0 && trackIndex < trackCount_;
|
||||
}
|
||||
|
||||
} // namespace Ms
|
|
@ -1,68 +0,0 @@
|
|||
#ifndef IMPORTMIDI_TRMODEL_H
|
||||
#define IMPORTMIDI_TRMODEL_H
|
||||
|
||||
#include "importmidi_operation.h"
|
||||
|
||||
|
||||
namespace Ms {
|
||||
|
||||
struct TrackMeta;
|
||||
struct TrackData;
|
||||
struct TrackOperations;
|
||||
struct DefinedTrackOperations;
|
||||
|
||||
enum TrackCol : char {
|
||||
DO_IMPORT = 0,
|
||||
TRACK_NUMBER,
|
||||
STAFF_NAME,
|
||||
INSTRUMENT,
|
||||
LYRICS,
|
||||
TCOL_COUNT
|
||||
};
|
||||
|
||||
class TracksModel : public QAbstractTableModel
|
||||
{
|
||||
public:
|
||||
TracksModel();
|
||||
|
||||
void reset(const QList<TrackMeta> &tracksMeta);
|
||||
void reset(const QList<TrackData> &tracksData);
|
||||
void clear();
|
||||
void setOperation(int row, MidiOperation::Type operType, const QVariant &operValue);
|
||||
void setTrackReorderedIndex(int trackIndex, int reorderIndex);
|
||||
void setLyricsList(const QList<std::string> &list);
|
||||
|
||||
TrackData trackData(int trackIndex) const;
|
||||
DefinedTrackOperations trackOperations(int row) const;
|
||||
int trackCount() const { return trackCount_; }
|
||||
int rowFromTrackIndex(int trackIndex) const;
|
||||
int trackIndexFromRow(int row) const;
|
||||
int numberOfTracksForImport() const;
|
||||
|
||||
int rowCount(const QModelIndex &/*parent*/) const;
|
||||
int columnCount(const QModelIndex &/*parent*/) const;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||
void forceColumnDataChanged(int col);
|
||||
|
||||
private:
|
||||
QList<TrackData> tracksData_;
|
||||
int trackCount_;
|
||||
int colCount_;
|
||||
QList<std::string> lyricsList_;
|
||||
|
||||
void setTrackOperation(int trackIndex, MidiOperation::Type operType, const QVariant &operValue);
|
||||
void setOperationForAllTracks(MidiOperation::Type operType, const QVariant &operValue);
|
||||
Qt::CheckState areAllTracksForImport() const;
|
||||
TrackData* trackDataFromIndex(const QModelIndex &index);
|
||||
bool isMappingRowToTrackValid(int row) const;
|
||||
bool isColumnValid(int column) const;
|
||||
bool isTrackIndexValid(int trackIndex) const;
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_TRMODEL_H
|
|
@ -1,28 +0,0 @@
|
|||
#include "importmidi_trview.h"
|
||||
|
||||
|
||||
TracksView::TracksView(QWidget *parent)
|
||||
: QTableView(parent)
|
||||
{
|
||||
}
|
||||
|
||||
// show tooltip if the text is wider than the table cell
|
||||
|
||||
bool TracksView::viewportEvent(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::ToolTip) {
|
||||
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
|
||||
QModelIndex index = indexAt(helpEvent->pos());
|
||||
if (index.isValid()) {
|
||||
QSize sizeHint = itemDelegate(index)->sizeHint(viewOptions(), index);
|
||||
QRect rItem(0, 0, sizeHint.width(), sizeHint.height());
|
||||
QRect rVisual = visualRect(index);
|
||||
if (rItem.width() <= rVisual.width()) {
|
||||
QToolTip::hideText();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QTableView::viewportEvent(event);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
#ifndef IMPORTMIDI_TRVIEW_H
|
||||
#define IMPORTMIDI_TRVIEW_H
|
||||
|
||||
#include <QTableView>
|
||||
|
||||
|
||||
class TracksView : public QTableView
|
||||
{
|
||||
public:
|
||||
explicit TracksView(QWidget *parent);
|
||||
|
||||
protected:
|
||||
bool viewportEvent(QEvent *event);
|
||||
};
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_TRVIEW_H
|
|
@ -5,6 +5,8 @@
|
|||
#include "importmidi_chord.h"
|
||||
#include "importmidi_quant.h"
|
||||
#include "importmidi_inner.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "libmscore/sig.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#include <set>
|
||||
|
@ -1021,8 +1023,9 @@ void findTuplets(
|
|||
{
|
||||
if (chords.empty() || startBarTick >= endBarTick) // invalid cases
|
||||
return;
|
||||
const auto operations = preferences.midiImportOperations.currentTrackOperations();
|
||||
if (!operations.tuplets.doSearch)
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
if (!opers.searchTuplets.value(currentTrack))
|
||||
return;
|
||||
|
||||
const auto tol = basicQuant / 2;
|
||||
|
@ -1041,7 +1044,7 @@ void findTuplets(
|
|||
|
||||
// later notes will be sorted and their indexes become invalid
|
||||
// so assign staccato information to notes now
|
||||
if (operations.simplifyDurations)
|
||||
if (opers.simplifyDurations.value(currentTrack))
|
||||
markStaccatoTupletNotes(tuplets);
|
||||
|
||||
// because of tol for non-tuplets we should use only chords with onTime >= bar start
|
||||
|
@ -1066,7 +1069,7 @@ void findTuplets(
|
|||
minimizeOffTimeError(tuplets, chords, nonTuplets, startBarTick, basicQuant, barIndex);
|
||||
}
|
||||
|
||||
if (operations.simplifyDurations)
|
||||
if (opers.simplifyDurations.value(currentTrack))
|
||||
cleanStaccatoOfNonTuplets(nonTuplets);
|
||||
|
||||
Q_ASSERT_X(!doTupletsHaveCommonChords(tuplets),
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "importmidi_chord.h"
|
||||
#include "importmidi_quant.h"
|
||||
#include "importmidi_inner.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#include <set>
|
||||
|
@ -34,9 +35,8 @@ bool isTupletAllowed(const TupletInfo &tupletInfo)
|
|||
}
|
||||
}
|
||||
// for all tuplets
|
||||
const bool isHumanPerformance = preferences.midiImportOperations
|
||||
.currentTrackOperations().quantize.humanPerformance;
|
||||
const int minAllowedNoteCount = (isHumanPerformance)
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int minAllowedNoteCount = (opers.isHumanPerformance)
|
||||
? tupletLimits(tupletInfo.tupletNumber).minNoteCountHuman
|
||||
: tupletLimits(tupletInfo.tupletNumber).minNoteCount;
|
||||
if ((int)tupletInfo.chords.size() < minAllowedNoteCount)
|
||||
|
@ -65,23 +65,24 @@ bool isTupletAllowed(const TupletInfo &tupletInfo)
|
|||
std::vector<int> findTupletNumbers(const ReducedFraction &divLen,
|
||||
const ReducedFraction &barFraction)
|
||||
{
|
||||
const auto operations = preferences.midiImportOperations.currentTrackOperations();
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
std::vector<int> tupletNumbers;
|
||||
|
||||
if (Meter::isCompound(barFraction) && divLen == Meter::beatLength(barFraction)) {
|
||||
if (operations.tuplets.duplets)
|
||||
if (opers.search2plets.value(currentTrack))
|
||||
tupletNumbers.push_back(2);
|
||||
if (operations.tuplets.quadruplets)
|
||||
if (opers.search4plets.value(currentTrack))
|
||||
tupletNumbers.push_back(4);
|
||||
}
|
||||
else {
|
||||
if (operations.tuplets.triplets)
|
||||
if (opers.search3plets.value(currentTrack))
|
||||
tupletNumbers.push_back(3);
|
||||
if (operations.tuplets.quintuplets)
|
||||
if (opers.search5plets.value(currentTrack))
|
||||
tupletNumbers.push_back(5);
|
||||
if (operations.tuplets.septuplets)
|
||||
if (opers.search7plets.value(currentTrack))
|
||||
tupletNumbers.push_back(7);
|
||||
if (operations.tuplets.nonuplets)
|
||||
if (opers.search9plets.value(currentTrack))
|
||||
tupletNumbers.push_back(9);
|
||||
}
|
||||
|
||||
|
@ -99,10 +100,12 @@ ReducedFraction findSumLengthOfRests(
|
|||
const auto tupletEndTime = tupletInfo.onTime + tupletInfo.len;
|
||||
const auto tupletNoteLen = tupletInfo.len / tupletInfo.tupletNumber;
|
||||
ReducedFraction sumLen = {0, 1};
|
||||
const auto opers = preferences.midiImportOperations.currentTrackOperations();
|
||||
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
|
||||
for (const auto &chord: tupletInfo.chords) {
|
||||
const auto staccatoIt = (opers.simplifyDurations)
|
||||
const auto staccatoIt = (opers.simplifyDurations.value(currentTrack))
|
||||
? tupletInfo.staccatoChords.find(chord.first)
|
||||
: tupletInfo.staccatoChords.end();
|
||||
|
||||
|
@ -320,8 +323,10 @@ std::vector<TupletInfo> detectTuplets(
|
|||
auto tupletInfo = findTupletApproximation(divLen, tupletNumber,
|
||||
basicQuant, startDivTime, startDivChordIt, endDivChordIt);
|
||||
|
||||
const auto opers = preferences.midiImportOperations.currentTrackOperations();
|
||||
if (opers.simplifyDurations) {
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
|
||||
if (opers.simplifyDurations.value(currentTrack)) {
|
||||
if (!haveChordsInTheMiddleBetweenTupletChords(
|
||||
startDivChordIt, endDivChordIt, tupletInfo)) {
|
||||
detectStaccato(tupletInfo);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "importmidi_quant.h"
|
||||
#include "importmidi_inner.h"
|
||||
#include "importmidi_voice.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "libmscore/mscore.h"
|
||||
#include "preferences.h"
|
||||
|
||||
|
@ -15,8 +16,9 @@ namespace MidiTuplet {
|
|||
|
||||
int tupletVoiceLimit()
|
||||
{
|
||||
const auto operations = preferences.midiImportOperations.currentTrackOperations();
|
||||
const int allowedVoices = MidiVoice::toIntVoices(operations.allowedVoices);
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
const int allowedVoices = MidiVoice::toIntVoiceCount(opers.maxVoiceCount.value(currentTrack));
|
||||
|
||||
Q_ASSERT_X(allowedVoices <= VOICES,
|
||||
"MidiTuplet::tupletVoiceLimit",
|
||||
|
|
449
mscore/importmidi_view.cpp
Normal file
449
mscore/importmidi_view.cpp
Normal file
|
@ -0,0 +1,449 @@
|
|||
#include "importmidi_view.h"
|
||||
|
||||
|
||||
class SeparatorDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
SeparatorDelegate(QObject *parent = 0)
|
||||
: QStyledItemDelegate(parent)
|
||||
, _frozenRowIndex(0)
|
||||
, _frozenColIndex(0)
|
||||
{}
|
||||
|
||||
void setFrozenRowIndex(int index)
|
||||
{
|
||||
_frozenRowIndex = index;
|
||||
}
|
||||
|
||||
void setFrozenColIndex(int index)
|
||||
{
|
||||
_frozenColIndex = index;
|
||||
}
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
{
|
||||
this->QStyledItemDelegate::paint(painter, option, index);
|
||||
|
||||
if (index.row() == _frozenRowIndex) {
|
||||
painter->save();
|
||||
painter->setPen(option.palette.foreground().color());
|
||||
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
|
||||
painter->restore();
|
||||
}
|
||||
if (index.column() == _frozenColIndex) {
|
||||
painter->save();
|
||||
painter->setPen(option.palette.foreground().color());
|
||||
painter->drawLine(option.rect.topRight(), option.rect.bottomRight());
|
||||
painter->restore();
|
||||
}
|
||||
}
|
||||
private:
|
||||
int _frozenRowIndex;
|
||||
int _frozenColIndex;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
TracksView::TracksView(QWidget *parent)
|
||||
: QTableView(parent)
|
||||
, _frozenRowCount(0)
|
||||
, _frozenColCount(0)
|
||||
{
|
||||
_frozenHTableView = new QTableView(this);
|
||||
_frozenVTableView = new QTableView(this);
|
||||
_frozenCornerTableView = new QTableView(this);
|
||||
_delegate = new SeparatorDelegate(this);
|
||||
|
||||
_delegate->setFrozenRowIndex(_frozenRowCount - 1);
|
||||
_delegate->setFrozenColIndex(_frozenColCount - 1);
|
||||
|
||||
initHorizontalView();
|
||||
initVerticalView();
|
||||
initCornerView();
|
||||
initMainView();
|
||||
initConnections();
|
||||
}
|
||||
|
||||
TracksView::~TracksView()
|
||||
{
|
||||
}
|
||||
|
||||
void TracksView::initHorizontalView()
|
||||
{
|
||||
_frozenHTableView->horizontalHeader()->hide();
|
||||
_frozenHTableView->setSelectionBehavior(SelectItems);
|
||||
_frozenHTableView->setSelectionMode(SingleSelection);
|
||||
_frozenHTableView->setAutoScroll(false);
|
||||
_frozenHTableView->setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
_frozenHTableView->setStyleSheet("QTableView { border: none; }");
|
||||
_frozenHTableView->setItemDelegate(_delegate);
|
||||
|
||||
_frozenHTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
_frozenHTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
_frozenHTableView->show();
|
||||
|
||||
_frozenHTableView->setHorizontalScrollMode(ScrollPerPixel);
|
||||
}
|
||||
|
||||
void TracksView::initVerticalView()
|
||||
{
|
||||
_frozenVTableView->verticalHeader()->hide();
|
||||
_frozenVTableView->setSelectionBehavior(SelectItems);
|
||||
_frozenVTableView->setSelectionMode(SingleSelection);
|
||||
_frozenVTableView->setAutoScroll(false);
|
||||
_frozenVTableView->setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
_frozenVTableView->setStyleSheet("QTableView { border: none; }");
|
||||
_frozenVTableView->setItemDelegate(_delegate);
|
||||
|
||||
_frozenVTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
_frozenVTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
_frozenVTableView->show();
|
||||
|
||||
_frozenVTableView->setVerticalScrollMode(ScrollPerPixel);
|
||||
|
||||
_frozenHTableView->stackUnder(_frozenVTableView);
|
||||
}
|
||||
|
||||
void TracksView::initCornerView()
|
||||
{
|
||||
_frozenCornerTableView->horizontalHeader()->hide();
|
||||
_frozenCornerTableView->verticalHeader()->hide();
|
||||
_frozenCornerTableView->setAutoScroll(false);
|
||||
_frozenCornerTableView->setSelectionBehavior(SelectItems);
|
||||
_frozenCornerTableView->setSelectionMode(SingleSelection);
|
||||
_frozenCornerTableView->setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
_frozenCornerTableView->setStyleSheet("QTableView { border: none; }");
|
||||
_frozenCornerTableView->setItemDelegate(_delegate);
|
||||
|
||||
_frozenCornerTableView->horizontalScrollBar()->setDisabled(true);
|
||||
_frozenCornerTableView->verticalScrollBar()->setDisabled(true);
|
||||
_frozenCornerTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
_frozenCornerTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
_frozenCornerTableView->show();
|
||||
|
||||
_frozenVTableView->stackUnder(_frozenCornerTableView);
|
||||
}
|
||||
|
||||
void TracksView::initMainView()
|
||||
{
|
||||
horizontalHeader()->setSectionsMovable(true);
|
||||
verticalHeader()->setSectionsMovable(true);
|
||||
|
||||
setHorizontalScrollMode(ScrollPerPixel);
|
||||
setVerticalScrollMode(ScrollPerPixel);
|
||||
setSelectionBehavior(SelectItems);
|
||||
setSelectionMode(SingleSelection);
|
||||
setAutoScroll(false);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
horizontalScrollBar()->setFocusPolicy(Qt::NoFocus);
|
||||
verticalScrollBar()->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
viewport()->stackUnder(_frozenHTableView);
|
||||
}
|
||||
|
||||
void TracksView::initConnections()
|
||||
{
|
||||
connect(horizontalHeader(),SIGNAL(sectionResized(int,int,int)),
|
||||
this, SLOT(updateMainViewSectionWidth(int,int,int)));
|
||||
connect(verticalHeader(),SIGNAL(sectionResized(int,int,int)),
|
||||
this, SLOT(updateMainViewSectionHeight(int,int,int)));
|
||||
|
||||
connect(verticalHeader(), SIGNAL(sectionMoved(int,int,int)),
|
||||
SLOT(onVSectionMove(int,int,int)));
|
||||
connect(horizontalHeader(), SIGNAL(sectionMoved(int,int,int)),
|
||||
SLOT(onHSectionMove(int,int,int)));
|
||||
|
||||
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
|
||||
_frozenHTableView->horizontalScrollBar(), SLOT(setValue(int)));
|
||||
connect(_frozenHTableView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
|
||||
horizontalScrollBar(), SLOT(setValue(int)));
|
||||
|
||||
connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
|
||||
_frozenVTableView->verticalScrollBar(), SLOT(setValue(int)));
|
||||
connect(_frozenVTableView->verticalScrollBar(), SIGNAL(valueChanged(int)),
|
||||
verticalScrollBar(), SLOT(setValue(int)));
|
||||
}
|
||||
|
||||
void TracksView::setModel(QAbstractItemModel *model)
|
||||
{
|
||||
QTableView::setModel(model);
|
||||
|
||||
_frozenHTableView->setModel(model);
|
||||
_frozenVTableView->setModel(model);
|
||||
_frozenCornerTableView->setModel(model);
|
||||
|
||||
_frozenHTableView->setSelectionModel(selectionModel());
|
||||
_frozenVTableView->setSelectionModel(selectionModel());
|
||||
_frozenCornerTableView->setSelectionModel(selectionModel());
|
||||
|
||||
connect(_frozenVTableView->selectionModel(),
|
||||
SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
|
||||
SLOT(currentChanged(const QModelIndex &, const QModelIndex &)));
|
||||
connect(_frozenHTableView->selectionModel(),
|
||||
SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
|
||||
SLOT(currentChanged(const QModelIndex &, const QModelIndex &)));
|
||||
connect(_frozenCornerTableView->selectionModel(),
|
||||
SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
|
||||
SLOT(currentChanged(const QModelIndex &, const QModelIndex &)));
|
||||
|
||||
setFrozenRowCount(_frozenRowCount);
|
||||
setFrozenColCount(_frozenColCount);
|
||||
|
||||
connect(_frozenVTableView->horizontalHeader(),SIGNAL(sectionResized(int,int,int)),
|
||||
this, SLOT(updateFrozenSectionWidth(int,int,int)), Qt::UniqueConnection);
|
||||
connect(_frozenHTableView->verticalHeader(),SIGNAL(sectionResized(int,int,int)),
|
||||
this, SLOT(updateFrozenSectionHeight(int,int,int)), Qt::UniqueConnection);
|
||||
|
||||
updateFrozenTableGeometry();
|
||||
}
|
||||
|
||||
void TracksView::setFrozenRowCount(int count)
|
||||
{
|
||||
if (model()->rowCount() == 0)
|
||||
return;
|
||||
|
||||
Q_ASSERT_X(count >= 0 && count < model()->rowCount(),
|
||||
"TracksView::setFrozenRowCount", "Invalid frozen row count");
|
||||
|
||||
_frozenRowCount = count;
|
||||
_delegate->setFrozenRowIndex(_frozenRowCount - 1);
|
||||
|
||||
for (int row = 0; row != count; ++row) {
|
||||
_frozenHTableView->setRowHidden(row, false);
|
||||
_frozenHTableView->setRowHeight(row, rowHeight(row));
|
||||
}
|
||||
|
||||
for (int row = count; row < model()->rowCount(); ++row) {
|
||||
_frozenHTableView->setRowHidden(row, true);
|
||||
}
|
||||
}
|
||||
|
||||
void TracksView::setFrozenColCount(int count)
|
||||
{
|
||||
if (model()->columnCount() == 0)
|
||||
return;
|
||||
|
||||
Q_ASSERT_X(count >= 0 && count < model()->columnCount(),
|
||||
"TracksView::setFrozenColCount", "Invalid frozen column count");
|
||||
|
||||
_frozenColCount = count;
|
||||
_delegate->setFrozenColIndex(_frozenColCount - 1);
|
||||
|
||||
for (int col = 0; col != count; ++col) {
|
||||
_frozenVTableView->setColumnHidden(col, false);
|
||||
_frozenVTableView->setColumnWidth(col, columnWidth(col));
|
||||
}
|
||||
|
||||
for (int col = count; col < model()->columnCount(); ++col) {
|
||||
_frozenVTableView->setColumnHidden(col, true);
|
||||
}
|
||||
}
|
||||
|
||||
void TracksView::updateMainViewSectionWidth(int logicalIndex, int /*oldSize*/, int newSize)
|
||||
{
|
||||
bool changed = false;
|
||||
for (int col = 0; col != _frozenColCount; ++col) {
|
||||
if (logicalIndex == col) {
|
||||
changed = true;
|
||||
_frozenVTableView->setColumnWidth(col, newSize);
|
||||
_frozenCornerTableView->setColumnWidth(col, newSize);
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
updateFrozenTableGeometry();
|
||||
|
||||
_frozenHTableView->setColumnWidth(logicalIndex, newSize);
|
||||
}
|
||||
|
||||
void TracksView::updateMainViewSectionHeight(int logicalIndex, int /*oldSize*/, int newSize)
|
||||
{
|
||||
bool changed = false;
|
||||
for (int row = 0; row != _frozenRowCount; ++row) {
|
||||
if (logicalIndex == row) {
|
||||
changed = true;
|
||||
_frozenHTableView->setRowHeight(row, newSize);
|
||||
_frozenCornerTableView->setRowHeight(row, newSize);
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
updateFrozenTableGeometry();
|
||||
|
||||
_frozenVTableView->setRowHeight(logicalIndex, newSize);
|
||||
}
|
||||
|
||||
void TracksView::updateFrozenSectionWidth(int logicalIndex, int /*oldSize*/, int newSize)
|
||||
{
|
||||
bool changed = false;
|
||||
for (int col = 0; col != _frozenColCount; ++col) {
|
||||
if (logicalIndex == col) {
|
||||
changed = true;
|
||||
setColumnWidth(col, newSize);
|
||||
_frozenCornerTableView->setColumnWidth(col, newSize);
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
updateFrozenTableGeometry();
|
||||
|
||||
_frozenHTableView->setColumnWidth(logicalIndex, newSize);
|
||||
}
|
||||
|
||||
void TracksView::updateFrozenSectionHeight(int logicalIndex, int /*oldSize*/, int newSize)
|
||||
{
|
||||
bool changed = false;
|
||||
for (int row = 0; row != _frozenRowCount; ++row) {
|
||||
if (logicalIndex == row) {
|
||||
changed = true;
|
||||
setRowHeight(row, newSize);
|
||||
_frozenCornerTableView->setRowHeight(row, newSize);
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
updateFrozenTableGeometry();
|
||||
|
||||
_frozenVTableView->setRowHeight(logicalIndex, newSize);
|
||||
}
|
||||
|
||||
void TracksView::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QTableView::resizeEvent(event);
|
||||
updateFrozenTableGeometry();
|
||||
}
|
||||
|
||||
void TracksView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
{
|
||||
updateFocus(current.row(), current.column());
|
||||
keepVisible(previous, current);
|
||||
}
|
||||
|
||||
void TracksView::onHSectionMove(int /*logicalIndex*/, int oldVisualIndex, int newVisualIndex)
|
||||
{
|
||||
if (newVisualIndex < _frozenColCount) { // disallow move
|
||||
horizontalHeader()->moveSection(newVisualIndex, oldVisualIndex); // move back
|
||||
}
|
||||
else if (oldVisualIndex >= _frozenColCount) {
|
||||
// move only if this slot was not called after previous move back
|
||||
_frozenHTableView->horizontalHeader()->moveSection(oldVisualIndex, newVisualIndex);
|
||||
_frozenVTableView->horizontalHeader()->moveSection(oldVisualIndex, newVisualIndex);
|
||||
_frozenCornerTableView->horizontalHeader()->moveSection(oldVisualIndex, newVisualIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void TracksView::onVSectionMove(int /*logicalIndex*/, int oldVisualIndex, int newVisualIndex)
|
||||
{
|
||||
if (newVisualIndex < _frozenRowCount) { // disallow move
|
||||
verticalHeader()->moveSection(newVisualIndex, oldVisualIndex); // move back
|
||||
}
|
||||
else if (oldVisualIndex >= _frozenRowCount) {
|
||||
// move only if this slot was not called after previous move back
|
||||
_frozenHTableView->verticalHeader()->moveSection(oldVisualIndex, newVisualIndex);
|
||||
_frozenVTableView->verticalHeader()->moveSection(oldVisualIndex, newVisualIndex);
|
||||
_frozenCornerTableView->verticalHeader()->moveSection(oldVisualIndex, newVisualIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void TracksView::updateFocus(int currentRow, int currentColumn)
|
||||
{
|
||||
if (currentRow < _frozenRowCount) {
|
||||
if (currentColumn < _frozenColCount)
|
||||
_frozenCornerTableView->setFocus();
|
||||
else
|
||||
_frozenHTableView->setFocus();
|
||||
}
|
||||
else {
|
||||
if (currentColumn < _frozenColCount)
|
||||
_frozenVTableView->setFocus();
|
||||
else
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void TracksView::keepVisible(const QModelIndex &previous, const QModelIndex ¤t)
|
||||
{
|
||||
// update scroll bars to show the cell hidden by the frozen table view if it gets focus
|
||||
const int hInvisibleGap = visualRect(current).topLeft().x() - frozenVTableWidth();
|
||||
const int vInvisibleGap = visualRect(current).topLeft().y() - frozenHTableHeight();
|
||||
|
||||
if (current.column() != previous.column() && current.column() >= _frozenColCount
|
||||
&& hInvisibleGap < 0) {
|
||||
const int newValue = horizontalScrollBar()->value() + hInvisibleGap;
|
||||
horizontalScrollBar()->setValue(newValue);
|
||||
}
|
||||
if (current.row() != previous.row() && current.row() >= _frozenRowCount
|
||||
&& vInvisibleGap < 0) {
|
||||
const int newValue = verticalScrollBar()->value() + vInvisibleGap;
|
||||
verticalScrollBar()->setValue(newValue);
|
||||
}
|
||||
|
||||
// update scroll bars to show the cell hidden by the borders of main table view
|
||||
const int hInvisibleGap2 = width() - verticalHeader()->width() - 4 * frameWidth()
|
||||
- ((verticalScrollBar()->isHidden()) ? 0 : verticalScrollBar()->width())
|
||||
- visualRect(current).bottomRight().x();
|
||||
const int vInvisibleGap2 = height() - horizontalHeader()->height() - 4 * frameWidth()
|
||||
- ((horizontalScrollBar()->isHidden()) ? 0 : horizontalScrollBar()->height())
|
||||
- visualRect(current).bottomRight().y();
|
||||
|
||||
if (current.column() != previous.column() && hInvisibleGap2 < 0) {
|
||||
const int newValue = horizontalScrollBar()->value() - hInvisibleGap2;
|
||||
horizontalScrollBar()->setValue(newValue);
|
||||
}
|
||||
if (current.row() != previous.row() && vInvisibleGap2 < 0) {
|
||||
const int newValue = verticalScrollBar()->value() - vInvisibleGap2;
|
||||
verticalScrollBar()->setValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
int TracksView::frozenRowHeight()
|
||||
{
|
||||
int height = 0;
|
||||
for (int row = 0; row != _frozenRowCount; ++row)
|
||||
height += rowHeight(row);
|
||||
return height;
|
||||
}
|
||||
|
||||
int TracksView::frozenColWidth()
|
||||
{
|
||||
int width = 0;
|
||||
for (int col = 0; col != _frozenColCount; ++col)
|
||||
width += columnWidth(col);
|
||||
return width;
|
||||
}
|
||||
|
||||
int TracksView::frozenVTableWidth()
|
||||
{
|
||||
int width = 0;
|
||||
for (int col = 0; col != _frozenColCount; ++col)
|
||||
width += _frozenVTableView->columnWidth(col);
|
||||
return width;
|
||||
}
|
||||
|
||||
int TracksView::frozenHTableHeight()
|
||||
{
|
||||
int height = 0;
|
||||
for (int row = 0; row != _frozenRowCount; ++row)
|
||||
height += _frozenHTableView->rowHeight(row);
|
||||
return height;
|
||||
}
|
||||
|
||||
void TracksView::updateFrozenTableGeometry()
|
||||
{
|
||||
_frozenHTableView->setGeometry(
|
||||
frameWidth(),
|
||||
horizontalHeader()->height() + frameWidth(),
|
||||
viewport()->width() + verticalHeader()->width(),
|
||||
frozenRowHeight());
|
||||
|
||||
_frozenVTableView->setGeometry(
|
||||
verticalHeader()->width() + frameWidth(),
|
||||
frameWidth(),
|
||||
frozenColWidth(),
|
||||
viewport()->height() + horizontalHeader()->height());
|
||||
|
||||
_frozenCornerTableView->setGeometry(
|
||||
verticalHeader()->width() + frameWidth(),
|
||||
horizontalHeader()->height() + frameWidth(),
|
||||
frozenColWidth(),
|
||||
frozenRowHeight());
|
||||
}
|
62
mscore/importmidi_view.h
Normal file
62
mscore/importmidi_view.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
#ifndef IMPORTMIDI_VIEW_H
|
||||
#define IMPORTMIDI_VIEW_H
|
||||
|
||||
#include <QTableView>
|
||||
|
||||
|
||||
class SeparatorDelegate;
|
||||
|
||||
class TracksView : public QTableView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TracksView(QWidget *parent);
|
||||
~TracksView();
|
||||
|
||||
void setModel(QAbstractItemModel *model);
|
||||
|
||||
void setFrozenRowCount(int count);
|
||||
void setFrozenColCount(int count);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
private slots:
|
||||
void currentChanged(const QModelIndex &, const QModelIndex &);
|
||||
|
||||
void updateMainViewSectionWidth(int,int,int);
|
||||
void updateMainViewSectionHeight(int,int,int);
|
||||
void updateFrozenSectionWidth(int,int,int);
|
||||
void updateFrozenSectionHeight(int,int,int);
|
||||
void onHSectionMove(int,int,int);
|
||||
void onVSectionMove(int,int,int);
|
||||
|
||||
private:
|
||||
void initHorizontalView();
|
||||
void initVerticalView();
|
||||
void initCornerView();
|
||||
void initMainView();
|
||||
void initConnections();
|
||||
|
||||
void keepVisible(const QModelIndex &previous, const QModelIndex ¤t);
|
||||
|
||||
int frozenRowHeight();
|
||||
int frozenColWidth();
|
||||
|
||||
int frozenVTableWidth();
|
||||
int frozenHTableHeight();
|
||||
|
||||
void updateFrozenTableGeometry();
|
||||
void updateFocus(int currentRow, int currentColumn);
|
||||
|
||||
QTableView *_frozenHTableView;
|
||||
QTableView *_frozenVTableView;
|
||||
QTableView *_frozenCornerTableView;
|
||||
SeparatorDelegate *_delegate;
|
||||
int _frozenRowCount;
|
||||
int _frozenColCount;
|
||||
};
|
||||
|
||||
|
||||
#endif // IMPORTMIDI_VIEW_H
|
|
@ -4,6 +4,7 @@
|
|||
#include "importmidi_inner.h"
|
||||
#include "importmidi_chord.h"
|
||||
#include "importmidi_meter.h"
|
||||
#include "importmidi_operations.h"
|
||||
#include "libmscore/sig.h"
|
||||
#include "libmscore/mscore.h"
|
||||
#include "preferences.h"
|
||||
|
@ -15,16 +16,16 @@ namespace MidiVoice {
|
|||
|
||||
// no more than VOICES
|
||||
|
||||
int toIntVoices(MidiOperation::AllowedVoices value)
|
||||
int toIntVoiceCount(MidiOperations::VoiceCount value)
|
||||
{
|
||||
switch (value) {
|
||||
case MidiOperation::AllowedVoices::V_1:
|
||||
case MidiOperations::VoiceCount::V_1:
|
||||
return 1;
|
||||
case MidiOperation::AllowedVoices::V_2:
|
||||
case MidiOperations::VoiceCount::V_2:
|
||||
return 2;
|
||||
case MidiOperation::AllowedVoices::V_3:
|
||||
case MidiOperations::VoiceCount::V_3:
|
||||
return 3;
|
||||
case MidiOperation::AllowedVoices::V_4:
|
||||
case MidiOperations::VoiceCount::V_4:
|
||||
return 4;
|
||||
}
|
||||
return VOICES;
|
||||
|
@ -32,14 +33,15 @@ int toIntVoices(MidiOperation::AllowedVoices value)
|
|||
|
||||
int voiceLimit()
|
||||
{
|
||||
const auto operations = preferences.midiImportOperations.currentTrackOperations();
|
||||
const int allowedVoices = toIntVoices(operations.allowedVoices);
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
const int allowedVoiceCount = toIntVoiceCount(opers.maxVoiceCount.value(currentTrack));
|
||||
|
||||
Q_ASSERT_X(allowedVoices <= VOICES,
|
||||
Q_ASSERT_X(allowedVoiceCount <= VOICES,
|
||||
"MidiVoice::voiceLimit",
|
||||
"Allowed voice count exceeds MuseScore voice limit");
|
||||
|
||||
return allowedVoices;
|
||||
return allowedVoiceCount;
|
||||
}
|
||||
|
||||
|
||||
|
@ -120,8 +122,9 @@ int findDurationCountInGroup(
|
|||
"MidiVoice::findDurationCountInGroup",
|
||||
"Notes are not sorted by off time in ascending order");
|
||||
|
||||
const auto &opers = preferences.midiImportOperations.currentTrackOperations();
|
||||
const bool useDots = opers.useDots;
|
||||
const auto &opers = preferences.midiImportOperations.data()->trackOpers;
|
||||
const int currentTrack = preferences.midiImportOperations.currentTrack();
|
||||
const bool useDots = opers.useDots.value(currentTrack);
|
||||
|
||||
int count = 0;
|
||||
auto onTime = chordOnTime;
|
||||
|
@ -790,7 +793,7 @@ void sortVoices(
|
|||
const auto &chord = it->second;
|
||||
|
||||
// some notes: if chord off time belongs to tuplet
|
||||
// then this tuplet belongs to the same bar as the chord off time
|
||||
// then this tuplet belongs to the same bar as the chord off time;
|
||||
// same is for chord on time
|
||||
|
||||
if (chord.barIndex <= maxBarIndex) {
|
||||
|
@ -823,11 +826,13 @@ bool separateVoices(std::multimap<int, MTrack> &tracks, const TimeSigMap *sigmap
|
|||
auto &chords = track.second.chords;
|
||||
if (chords.empty())
|
||||
continue;
|
||||
const int userVoiceCount = toIntVoiceCount(
|
||||
opers.data()->trackOpers.maxVoiceCount.value(mtrack.indexOfOperation));
|
||||
// pass current track index through MidiImportOperations
|
||||
// for further usage
|
||||
opers.setCurrentTrack(mtrack.indexOfOperation);
|
||||
MidiOperations::CurrentTrackSetter setCurrentTrack{opers, mtrack.indexOfOperation};
|
||||
|
||||
if (opers.currentTrackOperations().separateVoices && voiceLimit() > 1) {
|
||||
if (userVoiceCount > 1 && userVoiceCount <= voiceLimit()) {
|
||||
|
||||
Q_ASSERT_X(MidiTuplet::areAllTupletsReferenced(mtrack.chords, mtrack.tuplets),
|
||||
"MidiVoice::separateVoices",
|
||||
|
|
|
@ -11,7 +11,7 @@ class TimeSigMap;
|
|||
|
||||
namespace MidiVoice {
|
||||
|
||||
int toIntVoices(MidiOperation::AllowedVoices value);
|
||||
int toIntVoiceCount(MidiOperations::VoiceCount value);
|
||||
int voiceLimit();
|
||||
bool separateVoices(std::multimap<int, MTrack> &tracks, const TimeSigMap *sigmap);
|
||||
|
||||
|
|
|
@ -1603,12 +1603,12 @@ void MuseScore::midiPanelOnSwitchToFile(const QString &file)
|
|||
bool isMidiFile = ImportMidiPanel::isMidiFile(file);
|
||||
if (isMidiFile) {
|
||||
importmidiPanel->setMidiFile(file);
|
||||
if (importmidiPanel->prefferedVisible())
|
||||
if (importmidiPanel->isPrefferedVisible())
|
||||
importmidiPanel->setVisible(true);
|
||||
}
|
||||
else
|
||||
importmidiPanel->setVisible(false);
|
||||
importmidiShowPanel->setVisible(!importmidiPanel->prefferedVisible() && isMidiFile);
|
||||
importmidiShowPanel->setVisible(!importmidiPanel->isPrefferedVisible() && isMidiFile);
|
||||
}
|
||||
|
||||
void MuseScore::midiPanelOnCloseFile(const QString &file)
|
||||
|
@ -1623,10 +1623,10 @@ void MuseScore::allowShowMidiPanel(const QString &file)
|
|||
importmidiPanel->setPrefferedVisible(true);
|
||||
}
|
||||
|
||||
void MuseScore::setMidiPrefOperations(const QString &file)
|
||||
void MuseScore::setReopenInProgress(const QString &file)
|
||||
{
|
||||
if (ImportMidiPanel::isMidiFile(file))
|
||||
importmidiPanel->setMidiPrefOperations(file);
|
||||
importmidiPanel->setReopenInProgress();
|
||||
}
|
||||
|
||||
void MuseScore::showMidiImportPanel()
|
||||
|
|
|
@ -620,7 +620,7 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
void midiPanelOnSwitchToFile(const QString &file);
|
||||
void midiPanelOnCloseFile(const QString &file);
|
||||
void allowShowMidiPanel(const QString &file);
|
||||
void setMidiPrefOperations(const QString &file);
|
||||
void setReopenInProgress(const QString &file);
|
||||
|
||||
static Palette* newTempoPalette();
|
||||
static Palette* newTextPalette();
|
||||
|
|
|
@ -147,7 +147,7 @@ struct Preferences {
|
|||
QString importCharsetGP;
|
||||
QString importStyleFile;
|
||||
int shortestNote; // for midi input
|
||||
MidiImportOperations midiImportOperations;
|
||||
MidiOperations::Data midiImportOperations;
|
||||
|
||||
bool useOsc;
|
||||
int oscPort;
|
||||
|
|
|
@ -51,7 +51,6 @@ add_library(
|
|||
${PROJECT_SOURCE_DIR}/mscore/importmidi_tuplet_voice.cpp
|
||||
${PROJECT_SOURCE_DIR}/mscore/importmidi_tuplet_tonotes.cpp
|
||||
${PROJECT_SOURCE_DIR}/mscore/importmidi_chord.cpp
|
||||
${PROJECT_SOURCE_DIR}/mscore/importmidi_data.cpp
|
||||
${PROJECT_SOURCE_DIR}/mscore/importmidi_fraction.cpp
|
||||
${PROJECT_SOURCE_DIR}/mscore/importmidi_swing.cpp
|
||||
${PROJECT_SOURCE_DIR}/mscore/importmidi_drum.cpp
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "mscore/importmidi_inner.h"
|
||||
#include "mscore/importmidi_quant.h"
|
||||
#include "mscore/importmidi_fraction.h"
|
||||
#include "mscore/importmidi_operations.h"
|
||||
#include "mscore/preferences.h"
|
||||
|
||||
|
||||
|
@ -50,52 +51,54 @@ class TestImportMidi : public QObject, public MTest
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
void mf(const char* name);
|
||||
QString midiFilePath(const QString &fileName) const;
|
||||
QString midiFilePath(const char* fileName) const;
|
||||
void mf(const char* name) const;
|
||||
|
||||
// functions that modify default settings
|
||||
void dontSimplify(const char *file)
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.canRedefineDefaultsLater = false;
|
||||
opers.simplifyDurations = false;
|
||||
opers.separateVoices = false;
|
||||
opers.LHRH.doIt = false;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(file));
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.canRedefineDefaultsLater = false;
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(false);
|
||||
data.trackOpers.maxVoiceCount.setDefaultValue(MidiOperations::VoiceCount::V_1);
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(false);
|
||||
mf(file);
|
||||
preferences.midiImportOperations.clear();
|
||||
}
|
||||
void voiceSeparation(const char *file, bool simplify = false)
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.canRedefineDefaultsLater = false;
|
||||
opers.LHRH.doIt = false;
|
||||
opers.simplifyDurations = simplify;
|
||||
opers.separateVoices = true;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(file));
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.canRedefineDefaultsLater = false;
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(false);
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(simplify);
|
||||
data.trackOpers.maxVoiceCount.setDefaultValue(MidiOperations::VoiceCount::V_4);
|
||||
mf(file);
|
||||
preferences.midiImportOperations.clear();
|
||||
}
|
||||
void simplification(const char *file)
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.canRedefineDefaultsLater = false;
|
||||
opers.LHRH.doIt = false;
|
||||
opers.simplifyDurations = true;
|
||||
opers.separateVoices = false;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(file));
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.canRedefineDefaultsLater = false;
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(false);
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(true);
|
||||
data.trackOpers.maxVoiceCount.setDefaultValue(MidiOperations::VoiceCount::V_1);
|
||||
mf(file);
|
||||
preferences.midiImportOperations.clear();
|
||||
}
|
||||
void lrhand(const char *file)
|
||||
void staffSplit(const char *file)
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.canRedefineDefaultsLater = false;
|
||||
opers.LHRH.doIt = true;
|
||||
opers.simplifyDurations = false;
|
||||
opers.separateVoices = false;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(file));
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.canRedefineDefaultsLater = false;
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(true);
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(false);
|
||||
data.trackOpers.maxVoiceCount.setDefaultValue(MidiOperations::VoiceCount::V_1);
|
||||
mf(file);
|
||||
preferences.midiImportOperations.clear();
|
||||
}
|
||||
|
||||
private slots:
|
||||
|
@ -119,13 +122,15 @@ class TestImportMidi : public QObject, public MTest
|
|||
// human-performed (unaligned) files
|
||||
void human4_4()
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.simplifyDurations = false;
|
||||
opers.separateVoices = false;
|
||||
opers.LHRH.doIt = false;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
mf("human_4-4");
|
||||
preferences.midiImportOperations.clear();
|
||||
QString midiFile("human_4-4");
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(midiFile));
|
||||
preferences.midiImportOperations.addNewFile(midiFile);
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(false);
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(false);
|
||||
data.trackOpers.maxVoiceCount.setDefaultValue(MidiOperations::VoiceCount::V_1);
|
||||
mf(midiFile.toStdString().c_str());
|
||||
}
|
||||
|
||||
// chord detection
|
||||
|
@ -179,15 +184,15 @@ class TestImportMidi : public QObject, public MTest
|
|||
void tuplet2VoicesTupletNon() { mf("tuplet_2_voices_tuplet_non"); }
|
||||
void tuplet3_5_7tuplets()
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.canRedefineDefaultsLater = false;
|
||||
opers.changeClef = false;
|
||||
opers.simplifyDurations = false;
|
||||
opers.separateVoices = false;
|
||||
opers.LHRH.doIt = false;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
mf("tuplet_3_5_7_tuplets");
|
||||
preferences.midiImportOperations.clear();
|
||||
QString midiFile("tuplet_3_5_7_tuplets");
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(midiFile));
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.canRedefineDefaultsLater = false;
|
||||
data.trackOpers.changeClef.setDefaultValue(false);
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(false);
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(false);
|
||||
mf(midiFile.toStdString().c_str());
|
||||
}
|
||||
void tuplet5_5TupletsRests() { dontSimplify("tuplet_5_5_tuplets_rests"); }
|
||||
void tuplet3_4() { dontSimplify("tuplet_3-4"); }
|
||||
|
@ -237,54 +242,57 @@ class TestImportMidi : public QObject, public MTest
|
|||
void pickupMeasure() { dontSimplify("pickup"); }
|
||||
|
||||
// LH/RH separation
|
||||
void LHRH_Nontuplet() { lrhand("split_nontuplet"); }
|
||||
void LHRH_Acid() { lrhand("split_acid"); }
|
||||
void LHRH_Tuplet() { lrhand("split_tuplet"); }
|
||||
void LHRH_2melodies() { lrhand("split_2_melodies"); }
|
||||
void LHRH_octave() { lrhand("split_octave"); }
|
||||
void LHRH_Nontuplet() { staffSplit("split_nontuplet"); }
|
||||
void LHRH_Acid() { staffSplit("split_acid"); }
|
||||
void LHRH_Tuplet() { staffSplit("split_tuplet"); }
|
||||
void LHRH_2melodies() { staffSplit("split_2_melodies"); }
|
||||
void LHRH_octave() { staffSplit("split_octave"); }
|
||||
|
||||
// swing
|
||||
void swingTriplets()
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.canRedefineDefaultsLater = false;
|
||||
opers.swing = MidiOperation::Swing::SWING;
|
||||
opers.simplifyDurations = false;
|
||||
opers.separateVoices = false;
|
||||
opers.LHRH.doIt = false;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
mf("swing_triplets");
|
||||
preferences.midiImportOperations.clear();
|
||||
QString midiFile("swing_triplets");
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(midiFile));
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.canRedefineDefaultsLater = false;
|
||||
data.trackOpers.swing.setDefaultValue(MidiOperations::Swing::SWING);
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(false);
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(false);
|
||||
data.trackOpers.maxVoiceCount.setDefaultValue(MidiOperations::VoiceCount::V_1);
|
||||
mf(midiFile.toStdString().c_str());
|
||||
}
|
||||
void swingShuffle()
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.canRedefineDefaultsLater = false;
|
||||
opers.swing = MidiOperation::Swing::SHUFFLE;
|
||||
opers.simplifyDurations = false;
|
||||
opers.separateVoices = false;
|
||||
opers.LHRH.doIt = false;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
mf("swing_shuffle");
|
||||
preferences.midiImportOperations.clear();
|
||||
QString midiFile("swing_shuffle");
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(midiFile));
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.canRedefineDefaultsLater = false;
|
||||
data.trackOpers.swing.setDefaultValue(MidiOperations::Swing::SHUFFLE);
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(false);
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(false);
|
||||
data.trackOpers.maxVoiceCount.setDefaultValue(MidiOperations::VoiceCount::V_1);
|
||||
mf(midiFile.toStdString().c_str());
|
||||
}
|
||||
void swingClef()
|
||||
{
|
||||
TrackOperations opers;
|
||||
opers.canRedefineDefaultsLater = false;
|
||||
opers.swing = MidiOperation::Swing::SWING;
|
||||
opers.changeClef = true;
|
||||
opers.simplifyDurations = false;
|
||||
opers.separateVoices = false;
|
||||
opers.LHRH.doIt = false;
|
||||
preferences.midiImportOperations.resetDefaults(opers);
|
||||
mf("swing_clef");
|
||||
preferences.midiImportOperations.clear();
|
||||
QString midiFile("swing_clef");
|
||||
preferences.midiImportOperations.addNewFile(midiFilePath(midiFile));
|
||||
auto &data = *preferences.midiImportOperations.data();
|
||||
|
||||
data.canRedefineDefaultsLater = false;
|
||||
data.trackOpers.swing.setDefaultValue(MidiOperations::Swing::SWING);
|
||||
data.trackOpers.changeClef.setDefaultValue(true);
|
||||
data.trackOpers.doStaffSplit.setDefaultValue(false);
|
||||
data.trackOpers.simplifyDurations.setDefaultValue(false);
|
||||
data.trackOpers.maxVoiceCount.setDefaultValue(MidiOperations::VoiceCount::V_1);
|
||||
mf(midiFile.toStdString().c_str());
|
||||
}
|
||||
|
||||
// percussion
|
||||
void percDrums() { dontSimplify("perc_drums"); }
|
||||
void percRemoveTies() { dontSimplify("perc_remove_ties"); }
|
||||
void percDrums() { mf("perc_drums"); }
|
||||
void percRemoveTies() { mf("perc_remove_ties"); }
|
||||
|
||||
// clef changes along the score
|
||||
void clefTied() { dontSimplify("clef_tied"); }
|
||||
|
@ -319,17 +327,27 @@ void TestImportMidi::initTestCase()
|
|||
// midifile
|
||||
//---------------------------------------------------------
|
||||
|
||||
void TestImportMidi::mf(const char* name)
|
||||
void TestImportMidi::mf(const char* name) const
|
||||
{
|
||||
Score* score = new Score(mscore->baseStyle());
|
||||
score->setName(name);
|
||||
const QString midiname = QString(name) + ".mid";
|
||||
const QString mscorename = QString(name) + ".mscx";
|
||||
QCOMPARE(importMidi(score, TESTROOT "/mtest/" + DIR + midiname), Score::FileError::FILE_NO_ERROR);
|
||||
QCOMPARE(importMidi(score, midiFilePath(name)), Score::FileError::FILE_NO_ERROR);;
|
||||
QVERIFY(saveCompareScore(score, mscorename, DIR + mscorename));
|
||||
delete score;
|
||||
}
|
||||
|
||||
QString TestImportMidi::midiFilePath(const QString &fileName) const
|
||||
{
|
||||
const QString nameWithExtention = fileName + ".mid";
|
||||
return QString(TESTROOT "/mtest/" + DIR + nameWithExtention);
|
||||
}
|
||||
|
||||
QString TestImportMidi::midiFilePath(const char* fileName) const
|
||||
{
|
||||
return midiFilePath(QString(fileName));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// tuplet recognition fuctions
|
||||
//---------------------------------------------------------
|
||||
|
@ -532,6 +550,9 @@ void TestImportMidi::isTupletAllowed()
|
|||
|
||||
void TestImportMidi::findTupletNumbers()
|
||||
{
|
||||
auto &opers = preferences.midiImportOperations;
|
||||
opers.addNewFile("");
|
||||
MidiOperations::CurrentTrackSetter setCurrentTrack{opers, 0};
|
||||
{
|
||||
const ReducedFraction barFraction(4, 4);
|
||||
const ReducedFraction divLen = barFraction / 4;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "synthesizer/msynthesizer.h"
|
||||
#include "mscore/musescoreCore.h"
|
||||
#include "mscore/shortcut.h"
|
||||
#include "mscore/importmidi_operations.h"
|
||||
#include "libmscore/xml.h"
|
||||
#include "libmscore/excerpt.h"
|
||||
|
||||
|
@ -160,7 +161,7 @@ Score* MTest::readCreatedScore(const QString& name)
|
|||
// saveScore
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool MTest::saveScore(Score* score, const QString& name)
|
||||
bool MTest::saveScore(Score* score, const QString& name) const
|
||||
{
|
||||
QFileInfo fi(name);
|
||||
// MScore::testMode = true;
|
||||
|
@ -171,7 +172,7 @@ bool MTest::saveScore(Score* score, const QString& name)
|
|||
// compareFiles
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool MTest::compareFiles(const QString& saveName, const QString& compareWith)
|
||||
bool MTest::compareFiles(const QString& saveName, const QString& compareWith) const
|
||||
{
|
||||
QString cmd = "diff";
|
||||
QStringList args;
|
||||
|
@ -196,7 +197,7 @@ bool MTest::compareFiles(const QString& saveName, const QString& compareWith)
|
|||
// saveCompareScore
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool MTest::saveCompareScore(Score* score, const QString& saveName, const QString& compareWith)
|
||||
bool MTest::saveCompareScore(Score* score, const QString& saveName, const QString& compareWith) const
|
||||
{
|
||||
saveScore(score, saveName);
|
||||
return compareFiles(saveName, compareWith);
|
||||
|
|
|
@ -33,11 +33,11 @@ class MTest {
|
|||
MTest();
|
||||
Ms::Score* readScore(const QString& name);
|
||||
Ms::Score* readCreatedScore(const QString& name);
|
||||
bool saveScore(Ms::Score*, const QString& name);
|
||||
bool saveScore(Ms::Score*, const QString& name) const;
|
||||
bool savePdf(Ms::Score*, const QString& name);
|
||||
bool saveMusicXml(Ms::Score*, const QString& name);
|
||||
bool compareFiles(const QString& saveName, const QString& compareWith);
|
||||
bool saveCompareScore(Ms::Score*, const QString& saveName, const QString& compareWith);
|
||||
bool compareFiles(const QString& saveName, const QString& compareWith) const;
|
||||
bool saveCompareScore(Ms::Score*, const QString& saveName, const QString& compareWith) const;
|
||||
bool saveCompareMusicXmlScore(Ms::Score*, const QString& saveName, const QString& compareWith);
|
||||
Ms::Element* writeReadElement(Ms::Element* element);
|
||||
void initMTest();
|
||||
|
|
Loading…
Reference in a new issue