Skip to content

Commit 4bc7d27

Browse files
committed
Optimize MIDI import
1 parent 302a892 commit 4bc7d27

6 files changed

Lines changed: 179 additions & 3 deletions

File tree

src/plugins/midiformatconverter/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ diffscope_add_builtin_plugin(${PROJECT_NAME}
3030
uishell
3131
coreplugin
3232
importexportmanager
33+
SVSCraftCore
3334
opendspx::model
3435
opendspx::converter
3536
${_links_icu}
3637
INCLUDE_PRIVATE
3738
internal/**
3839
MACOS_EXCLUDE_SRC *_icu.cpp
39-
)
40+
)

src/plugins/midiformatconverter/internal/MIDIFileImporter.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <CoreApi/runtimeinterface.h>
1212

1313
#include <SVSCraftCore/MusicPitch.h>
14+
#include <SVSCraftCore/MusicMode.h>
1415
#include <SVSCraftQuick/MessageBox.h>
1516

1617
#include <opendspx/model.h>
@@ -49,6 +50,46 @@ namespace MIDIFormatConverter::Internal {
4950
return result;
5051
}
5152

53+
static void postprocessImportedModel(opendspx::Model &model) {
54+
for (auto &track : model.content.tracks) {
55+
for (const auto &clip : track.clips) {
56+
if (!clip || clip->type != opendspx::Clip::Type::Singing || !clip->name.empty())
57+
continue;
58+
59+
const auto singingClip = std::static_pointer_cast<opendspx::SingingClip>(clip);
60+
const auto lyricCount = std::min<std::size_t>(8, singingClip->notes.size());
61+
for (std::size_t i = 0; i < lyricCount; ++i) {
62+
if (i != 0)
63+
clip->name.push_back(' ');
64+
clip->name.append(singingClip->notes[i].lyric);
65+
}
66+
}
67+
68+
if (track.name.empty() && !track.clips.empty() && track.clips.front()) {
69+
track.name = track.clips.front()->name;
70+
}
71+
}
72+
}
73+
74+
static void addDetectedKeySignature(opendspx::Model &model, int mode, int accidentalType, const QList<SVS::MusicPitch> &notes) {
75+
if (notes.isEmpty())
76+
return;
77+
78+
const auto tonality = SVS::MusicMode(mode).detectTonality(notes);
79+
auto &diffscopeWorkspace = model.content.workspace["diffscope"];
80+
if (!diffscopeWorkspace.is_object())
81+
diffscopeWorkspace = nlohmann::json::object();
82+
auto &keySignatures = diffscopeWorkspace["keySignatures"];
83+
if (!keySignatures.is_array())
84+
keySignatures = nlohmann::json::array();
85+
keySignatures.push_back({
86+
{"pos", 0},
87+
{"mode", mode},
88+
{"tonality", static_cast<int>(tonality)},
89+
{"accidentalType", accidentalType},
90+
});
91+
}
92+
5293
bool MIDIFileImporter::execImport(const QString &path, opendspx::Model &model, QWindow *window) {
5394
QFile file(path);
5495
if (!file.open(QIODevice::ReadOnly)) {
@@ -87,10 +128,22 @@ namespace MIDIFormatConverter::Internal {
87128
}
88129
auto codec = dlg.codec();
89130
auto selectedIndexes = dlg.selectedIndexes();
131+
const bool autoDetectKeySignature = dlg.autoDetectKeySignature();
132+
const int musicMode = dlg.musicMode();
133+
const int accidentalType = dlg.accidentalType();
90134
bool ok;
135+
auto sourceTracks = intermediateData.tracks();
91136
std::vector<opendspx::MidiIntermediateData::Track> selectedTracks;
137+
QList<SVS::MusicPitch> selectedNotes;
92138
for (auto index : selectedIndexes) {
93-
selectedTracks.push_back(std::move(intermediateData.tracks().at(index)));
139+
auto &track = sourceTracks.at(index);
140+
if (autoDetectKeySignature) {
141+
selectedNotes.reserve(selectedNotes.size() + static_cast<qsizetype>(track.notes.size()));
142+
for (const auto &note : track.notes) {
143+
selectedNotes.append(SVS::MusicPitch(static_cast<qint8>(note.key)));
144+
}
145+
}
146+
selectedTracks.push_back(std::move(track));
94147
}
95148
intermediateData = {
96149
intermediateData.resolution(),
@@ -107,6 +160,10 @@ namespace MIDIFormatConverter::Internal {
107160
SVS::MessageBox::critical(Core::RuntimeInterface::qmlEngine(), window, tr("Failed to convert MIDI data"), tr("Some meta events in this MIDI document cannot be converted to DSPX. Please try disabling import tempo/time signature."));
108161
return false;
109162
}
163+
postprocessImportedModel(model);
164+
if (autoDetectKeySignature) {
165+
addDetectedKeySignature(model, musicMode, accidentalType, selectedNotes);
166+
}
110167
return true;
111168
}
112169

src/plugins/midiformatconverter/internal/MIDITrackSelectorDialog.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <QTreeView>
1818
#include <QVBoxLayout>
1919

20+
#include <SVSCraftCore/MusicModeInfo.h>
21+
2022
#include <midiformatconverter/internal/MIDITextCodecConverter.h>
2123

2224
namespace MIDIFormatConverter::Internal {
@@ -91,6 +93,27 @@ namespace MIDIFormatConverter::Internal {
9193
importTimeSignatureCheckBox->setChecked(importTimeSignature);
9294
optionsLayout->addWidget(importTimeSignatureCheckBox);
9395

96+
auto *keySignatureLayout = new QHBoxLayout;
97+
autoDetectKeySignatureCheckBox = new QCheckBox(MIDIFormatConverter::Internal::MIDITrackSelectorDialog::tr("Automatically detect key signature"), optionsGroup);
98+
autoDetectKeySignatureCheckBox->setChecked(autoDetectKeySignature);
99+
keySignatureLayout->addWidget(autoDetectKeySignatureCheckBox);
100+
101+
musicModeComboBox = new QComboBox(optionsGroup);
102+
for (const auto &[mode, name] : SVS::MusicModeInfo::getBuiltInMusicModeInfoList()) {
103+
musicModeComboBox->addItem(name, mode.mask());
104+
}
105+
musicModeComboBox->setCurrentIndex(musicModeComboBox->findData(musicMode));
106+
musicModeComboBox->setEnabled(autoDetectKeySignature);
107+
keySignatureLayout->addWidget(musicModeComboBox, 1);
108+
109+
accidentalTypeComboBox = new QComboBox(optionsGroup);
110+
accidentalTypeComboBox->addItem(MIDIFormatConverter::Internal::MIDITrackSelectorDialog::tr("Flat"), 0);
111+
accidentalTypeComboBox->addItem(MIDIFormatConverter::Internal::MIDITrackSelectorDialog::tr("Sharp"), 1);
112+
accidentalTypeComboBox->setCurrentIndex(accidentalTypeComboBox->findData(accidentalType));
113+
accidentalTypeComboBox->setEnabled(autoDetectKeySignature);
114+
keySignatureLayout->addWidget(accidentalTypeComboBox);
115+
optionsLayout->addLayout(keySignatureLayout);
116+
94117
mainLayout->addWidget(optionsGroup);
95118

96119
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, q);
@@ -132,6 +155,25 @@ namespace MIDIFormatConverter::Internal {
132155
Q_Q(MIDITrackSelectorDialog);
133156
q->setImportTimeSignature(checked);
134157
});
158+
159+
QObject::connect(autoDetectKeySignatureCheckBox, &QCheckBox::toggled, q, [this](bool checked) {
160+
Q_Q(MIDITrackSelectorDialog);
161+
q->setAutoDetectKeySignature(checked);
162+
});
163+
164+
QObject::connect(musicModeComboBox, &QComboBox::currentIndexChanged, q, [this](int index) {
165+
if (index < 0)
166+
return;
167+
Q_Q(MIDITrackSelectorDialog);
168+
q->setMusicMode(musicModeComboBox->itemData(index).toInt());
169+
});
170+
171+
QObject::connect(accidentalTypeComboBox, &QComboBox::currentIndexChanged, q, [this](int index) {
172+
if (index < 0)
173+
return;
174+
Q_Q(MIDITrackSelectorDialog);
175+
q->setAccidentalType(accidentalTypeComboBox->itemData(index).toInt());
176+
});
135177
q->resize(640, 480);
136178
}
137179

@@ -431,6 +473,61 @@ namespace MIDIFormatConverter::Internal {
431473
emit importTimeSignatureChanged(enabled);
432474
}
433475

476+
bool MIDITrackSelectorDialog::autoDetectKeySignature() const {
477+
Q_D(const MIDITrackSelectorDialog);
478+
return d->autoDetectKeySignature;
479+
}
480+
481+
void MIDITrackSelectorDialog::setAutoDetectKeySignature(bool enabled) {
482+
Q_D(MIDITrackSelectorDialog);
483+
if (d->autoDetectKeySignature == enabled)
484+
return;
485+
d->autoDetectKeySignature = enabled;
486+
if (d->autoDetectKeySignatureCheckBox) {
487+
QSignalBlocker blocker(d->autoDetectKeySignatureCheckBox);
488+
d->autoDetectKeySignatureCheckBox->setChecked(enabled);
489+
}
490+
if (d->musicModeComboBox)
491+
d->musicModeComboBox->setEnabled(enabled);
492+
if (d->accidentalTypeComboBox)
493+
d->accidentalTypeComboBox->setEnabled(enabled);
494+
emit autoDetectKeySignatureChanged(enabled);
495+
}
496+
497+
int MIDITrackSelectorDialog::musicMode() const {
498+
Q_D(const MIDITrackSelectorDialog);
499+
return d->musicMode;
500+
}
501+
502+
void MIDITrackSelectorDialog::setMusicMode(int mode) {
503+
Q_D(MIDITrackSelectorDialog);
504+
if (d->musicMode == mode)
505+
return;
506+
d->musicMode = mode;
507+
if (d->musicModeComboBox) {
508+
QSignalBlocker blocker(d->musicModeComboBox);
509+
d->musicModeComboBox->setCurrentIndex(d->musicModeComboBox->findData(mode));
510+
}
511+
emit musicModeChanged(mode);
512+
}
513+
514+
int MIDITrackSelectorDialog::accidentalType() const {
515+
Q_D(const MIDITrackSelectorDialog);
516+
return d->accidentalType;
517+
}
518+
519+
void MIDITrackSelectorDialog::setAccidentalType(int accidentalType) {
520+
Q_D(MIDITrackSelectorDialog);
521+
if (d->accidentalType == accidentalType)
522+
return;
523+
d->accidentalType = accidentalType;
524+
if (d->accidentalTypeComboBox) {
525+
QSignalBlocker blocker(d->accidentalTypeComboBox);
526+
d->accidentalTypeComboBox->setCurrentIndex(d->accidentalTypeComboBox->findData(accidentalType));
527+
}
528+
emit accidentalTypeChanged(accidentalType);
529+
}
530+
434531
void MIDITrackSelectorDialog::detectCodec() {
435532
Q_D(MIDITrackSelectorDialog);
436533
const QByteArray combined = d->aggregateLyricsForDetection();

src/plugins/midiformatconverter/internal/MIDITrackSelectorDialog.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ namespace MIDIFormatConverter::Internal {
2020
Q_PROPERTY(bool separateMidiChannels READ separateMidiChannels WRITE setseparateMidiChannels NOTIFY separateMidiChannelsChanged)
2121
Q_PROPERTY(bool importTempo READ importTempo WRITE setImportTempo NOTIFY importTempoChanged)
2222
Q_PROPERTY(bool importTimeSignature READ importTimeSignature WRITE setImportTimeSignature NOTIFY importTimeSignatureChanged)
23+
Q_PROPERTY(bool autoDetectKeySignature READ autoDetectKeySignature WRITE setAutoDetectKeySignature NOTIFY autoDetectKeySignatureChanged)
24+
Q_PROPERTY(int musicMode READ musicMode WRITE setMusicMode NOTIFY musicModeChanged)
25+
Q_PROPERTY(int accidentalType READ accidentalType WRITE setAccidentalType NOTIFY accidentalTypeChanged)
2326

2427
public:
2528
struct TrackInfo {
@@ -51,6 +54,15 @@ namespace MIDIFormatConverter::Internal {
5154
bool importTimeSignature() const;
5255
void setImportTimeSignature(bool enabled);
5356

57+
bool autoDetectKeySignature() const;
58+
void setAutoDetectKeySignature(bool enabled);
59+
60+
int musicMode() const;
61+
void setMusicMode(int mode);
62+
63+
int accidentalType() const;
64+
void setAccidentalType(int accidentalType);
65+
5466
Q_INVOKABLE void detectCodec();
5567

5668
Q_SIGNALS:
@@ -59,6 +71,9 @@ namespace MIDIFormatConverter::Internal {
5971
void separateMidiChannelsChanged(bool enabled);
6072
void importTempoChanged(bool enabled);
6173
void importTimeSignatureChanged(bool enabled);
74+
void autoDetectKeySignatureChanged(bool enabled);
75+
void musicModeChanged(int mode);
76+
void accidentalTypeChanged(int accidentalType);
6277

6378
private:
6479
QScopedPointer<MIDITrackSelectorDialogPrivate> d_ptr;

src/plugins/midiformatconverter/internal/MIDITrackSelectorDialog_p.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ namespace MIDIFormatConverter::Internal {
5959
bool separateMidiChannels = true;
6060
bool importTempo = true;
6161
bool importTimeSignature = true;
62+
bool autoDetectKeySignature = false;
63+
int musicMode = 2741;
64+
int accidentalType = 0;
6265

6366
QComboBox *codecComboBox = nullptr;
6467
QTreeView *trackView = nullptr;
@@ -68,6 +71,9 @@ namespace MIDIFormatConverter::Internal {
6871
QCheckBox *separateMidiChannelsCheckBox = nullptr;
6972
QCheckBox *importTempoCheckBox = nullptr;
7073
QCheckBox *importTimeSignatureCheckBox = nullptr;
74+
QCheckBox *autoDetectKeySignatureCheckBox = nullptr;
75+
QComboBox *musicModeComboBox = nullptr;
76+
QComboBox *accidentalTypeComboBox = nullptr;
7177
};
7278

7379
}

0 commit comments

Comments
 (0)