|
16 | 16 | #include <QVariant> |
17 | 17 |
|
18 | 18 | #include <CoreApi/runtimeinterface.h> |
| 19 | +#include <opendspx/mixedsinger.h> |
| 20 | +#include <opendspx/singlesinger.h> |
| 21 | +#include <SVSCraftQuick/MessageBox.h> |
19 | 22 |
|
20 | 23 | #include <dspxmodelORM/DynamicMixingAnchor.h> |
21 | 24 | #include <dspxmodelORM/DynamicMixingAnchorSequence.h> |
22 | 25 | #include <dspxmodelORM/Model.h> |
| 26 | +#include <dspxmodelORM/Note.h> |
| 27 | +#include <dspxmodelORM/NoteSequence.h> |
23 | 28 | #include <dspxmodelORM/Singer.h> |
24 | 29 | #include <dspxmodelORM/SingerList.h> |
25 | 30 | #include <dspxmodelORM/SingingClip.h> |
26 | 31 | #include <dspxmodelORM/Sources.h> |
27 | 32 |
|
| 33 | +#include <coreplugin/CoreInterface.h> |
28 | 34 | #include <coreplugin/DspxDocument.h> |
| 35 | +#include <coreplugin/SingerInfo.h> |
| 36 | +#include <coreplugin/SingerRegistry.h> |
29 | 37 | #include <coreplugin/SourcesPickerModel.h> |
30 | 38 |
|
31 | 39 | #include <transactional/TransactionController.h> |
@@ -95,6 +103,24 @@ namespace Core { |
95 | 103 | return result; |
96 | 104 | } |
97 | 105 |
|
| 106 | + QString firstSingerId(const std::vector<opendspx::SingerRef> &singers) { |
| 107 | + for (const auto &singer : singers) { |
| 108 | + if (!singer) |
| 109 | + continue; |
| 110 | + if (singer->type == opendspx::Singer::Type::Single) { |
| 111 | + const auto &single = static_cast<const opendspx::SingleSinger &>(*singer); |
| 112 | + if (!single.id.empty()) |
| 113 | + return QString::fromStdString(single.id); |
| 114 | + } else if (singer->type == opendspx::Singer::Type::Mixed) { |
| 115 | + const auto &mixed = static_cast<const opendspx::MixedSinger &>(*singer); |
| 116 | + const auto id = firstSingerId(mixed.singers); |
| 117 | + if (!id.isEmpty()) |
| 118 | + return id; |
| 119 | + } |
| 120 | + } |
| 121 | + return {}; |
| 122 | + } |
| 123 | + |
98 | 124 | QList<QPointer<dspx::SingingClip>> guardedClips(const QList<dspx::SingingClip *> &clips) { |
99 | 125 | QList<QPointer<dspx::SingingClip>> result; |
100 | 126 | result.reserve(clips.size()); |
@@ -171,19 +197,35 @@ namespace Core { |
171 | 197 |
|
172 | 198 | bool EditSourcesScenarioPrivate::applySourcesImpl( |
173 | 199 | SourcesPickerModel *model, const QList<QPointer<dspx::SingingClip>> &clips) const { |
| 200 | + Q_Q(const EditSourcesScenario); |
174 | 201 | if (!model) |
175 | 202 | return false; |
176 | 203 |
|
177 | 204 | const QString architectureId = model->architectureId(); |
178 | 205 | const auto singers = toSingerVector(model->singers()); |
| 206 | + const auto *registry = CoreInterface::singerRegistry(); |
| 207 | + const auto languages = registry ? registry->singerInfo(architectureId, firstSingerId(singers)).languages() : SingerInfo::LanguageMap{}; |
| 208 | + bool hasUnsupportedLanguage = false; |
179 | 209 | bool applied = false; |
180 | 210 | for (const auto &guardedClip : clips) { |
181 | 211 | auto *clip = guardedClip.data(); |
182 | 212 | if (!clip) |
183 | 213 | continue; |
184 | 214 | updateSources(clip, architectureId, singers); |
| 215 | + if (!languages.isEmpty() && !hasUnsupportedLanguage) { |
| 216 | + hasUnsupportedLanguage = std::ranges::any_of(clip->notes()->asRange(), [&languages](const dspx::Note *note) { |
| 217 | + return !languages.contains(note->language()); |
| 218 | + }); |
| 219 | + } |
185 | 220 | applied = true; |
186 | 221 | } |
| 222 | + if (hasUnsupportedLanguage) { |
| 223 | + SVS::MessageBox::warning( |
| 224 | + RuntimeInterface::qmlEngine(), window, |
| 225 | + q->tr("Unsupported Note Languages"), |
| 226 | + q->tr("Some notes use languages that the selected singer does not support. Synthesis may fail. Change the note languages to languages supported by the singer.") |
| 227 | + ); |
| 228 | + } |
187 | 229 | return applied; |
188 | 230 | } |
189 | 231 |
|
|
0 commit comments