Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2025 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "musesamplerresolver.h"
24 :
25 : #include "musesamplerwrapper.h"
26 :
27 : #include "global/io/fileinfo.h"
28 : #include "global/types/version.h"
29 : #include "global/serialization/json.h"
30 :
31 : #include "log.h"
32 :
33 : using namespace muse;
34 : using namespace muse::async;
35 : using namespace muse::audio;
36 : using namespace muse::audio::synth;
37 : using namespace muse::musesampler;
38 :
39 : static const std::unordered_map<String, ClefType> CLEF_NAME_LOWER_CASE_TO_TYPE {
40 : { u"none", ClefType::None },
41 : { u"treble", ClefType::Treble },
42 : { u"bass", ClefType::Bass },
43 : { u"alto", ClefType::Alto },
44 : { u"tenor", ClefType::Tenor },
45 : { u"percussion", ClefType::Percussion },
46 : { u"higheroctavetreble", ClefType::HigherOctaveTreble },
47 : { u"loweroctavetreble", ClefType::LowerOctaveTreble },
48 : { u"higheroctavebass", ClefType::HigherOctaveBass },
49 : { u"loweroctavebass", ClefType::LowerOctaveBass },
50 : { u"baritone", ClefType::Baritone },
51 : { u"mezzosoprano", ClefType::Mezzosoprano },
52 : { u"soprano", ClefType::Soprano },
53 : { u"frenchviolin", ClefType::FrenchViolin },
54 : };
55 :
56 : static const std::unordered_map<String, StaffType> STAFF_NAME_LOWER_CASE_TO_TYPE {
57 : { u"standard", StaffType::Standard },
58 : { u"grand", StaffType::Grand },
59 : };
60 :
61 0 : InstrumentInfo findInstrument(MuseSamplerLibHandlerPtr libHandler, const AudioResourceMeta& resourceMeta)
62 : {
63 0 : if (!libHandler) {
64 0 : return InstrumentInfo();
65 : }
66 :
67 0 : auto instrumentList = libHandler->getInstrumentList();
68 :
69 0 : while (auto instrument = libHandler->getNextInstrument(instrumentList)) {
70 0 : const int instrumentId = libHandler->getInstrumentId(instrument);
71 :
72 0 : if (resourceMeta.attributeVal(u"museUID") == String::number(instrumentId)) {
73 0 : const bool isOnline = libHandler->isOnlineInstrument(instrument);
74 0 : return { instrumentId, instrument, isOnline };
75 : }
76 0 : }
77 :
78 0 : return InstrumentInfo();
79 : }
80 :
81 0 : void MuseSamplerResolver::init()
82 : {
83 0 : TRACEFUNC;
84 :
85 0 : const io::path_t libraryPath = configuration()->libraryPath();
86 0 : if (io::isAbsolute(libraryPath) && !io::FileInfo::exists(libraryPath)) {
87 0 : LOGI() << "MuseSampler library not found: " << libraryPath;
88 0 : return;
89 : }
90 :
91 0 : m_libHandler = std::make_shared<MuseSamplerLibHandler>();
92 0 : if (!m_libHandler->loadLib(libraryPath)) {
93 0 : LOGE() << "Unable to load MuseSampler library: " << libraryPath;
94 0 : m_libHandler.reset();
95 0 : return;
96 : }
97 :
98 0 : if (!m_libHandler->loadApi(configuration()->minSupportedVersion(), configuration()->useLegacyAudition())) {
99 0 : m_samplerVersion = m_libHandler->version();
100 0 : m_samplerBuildNumber = m_libHandler->buildNumber();
101 0 : m_libHandler.reset();
102 0 : LOGE() << "Incompatible MuseSampler library: " << libraryPath << ", version: " << m_samplerVersion.toString();
103 0 : return;
104 : }
105 :
106 0 : m_samplerVersion = m_libHandler->version();
107 0 : m_samplerBuildNumber = m_libHandler->buildNumber();
108 :
109 0 : if (!m_libHandler->init()) {
110 0 : LOGE() << "Could not init MuseSampler: " << libraryPath << ", version: " << m_samplerVersion.toString();
111 0 : m_libHandler.reset();
112 0 : m_initFailed = true;
113 0 : return;
114 : }
115 :
116 0 : m_libHandler->setLoggingCallback([](ms_ErrorLevel level, const char* msg) {
117 0 : switch (level) {
118 0 : case ms_ErrorLevel_Debug:
119 0 : LOGD() << msg;
120 0 : break;
121 0 : case ms_ErrorLevel_Info:
122 0 : LOGI() << msg;
123 0 : break;
124 0 : case ms_ErrorLevel_Warning:
125 0 : LOGW() << msg;
126 0 : break;
127 0 : case ms_ErrorLevel_Error:
128 0 : LOGE() << msg;
129 0 : break;
130 : }
131 0 : });
132 :
133 0 : LOGI() << "MuseSampler successfully inited: " << libraryPath << ", version: " << m_samplerVersion.toString();
134 0 : }
135 :
136 0 : void MuseSamplerResolver::deinit()
137 : {
138 0 : TRACEFUNC;
139 :
140 0 : if (m_libHandler) {
141 0 : m_libHandler->deinit();
142 : }
143 0 : }
144 :
145 0 : bool MuseSamplerResolver::reloadAllInstruments()
146 : {
147 0 : if (!m_libHandler) {
148 0 : return false;
149 : }
150 :
151 0 : return m_libHandler->reloadAllInstruments() == ms_Result_OK;
152 : }
153 :
154 0 : int MuseSamplerResolver::buildNumber() const
155 : {
156 0 : return m_samplerBuildNumber;
157 : }
158 :
159 0 : ISynthesizerPtr MuseSamplerResolver::resolveSynth(const TrackId /*trackId*/, const AudioInputParams& params,
160 : const audio::OutputSpec&) const
161 : {
162 0 : const InstrumentInfo instrument = findInstrument(m_libHandler, params.resourceMeta);
163 0 : if (instrument.isValid()) {
164 0 : return std::make_shared<MuseSamplerWrapper>(m_libHandler, instrument, params);
165 : }
166 :
167 0 : return nullptr;
168 : }
169 :
170 0 : bool MuseSamplerResolver::hasCompatibleResources(const PlaybackSetupData& setup) const
171 : {
172 : UNUSED(setup);
173 :
174 0 : if (!m_libHandler) {
175 0 : return false;
176 : }
177 :
178 0 : return true;
179 : }
180 :
181 0 : AudioResourceMetaList MuseSamplerResolver::resolveResources() const
182 : {
183 0 : AudioResourceMetaList result;
184 :
185 0 : if (!m_libHandler) {
186 0 : return result;
187 : }
188 :
189 0 : auto instrumentList = m_libHandler->getInstrumentList();
190 0 : while (auto instrument = m_libHandler->getNextInstrument(instrumentList)) {
191 0 : const int instrumentId = m_libHandler->getInstrumentId(instrument);
192 0 : const String internalName = String::fromUtf8(m_libHandler->getInstrumentName(instrument));
193 0 : const String internalCategory = String::fromUtf8(m_libHandler->getInstrumentCategory(instrument));
194 0 : const String instrumentSoundId = String::fromUtf8(m_libHandler->getMpeSoundId(instrument));
195 0 : const String vendorName = String::fromUtf8(m_libHandler->getInstrumentVendorName(instrument));
196 0 : const bool isOnline = m_libHandler->isOnlineInstrument(instrument);
197 :
198 0 : String instrumentPackName = String::fromUtf8(m_libHandler->getInstrumentPackName(instrument));
199 0 : if (instrumentPackName.empty()) {
200 0 : instrumentPackName = internalCategory;
201 : }
202 :
203 0 : AudioResourceMeta meta;
204 0 : meta.id = std::to_string(instrumentId);
205 0 : meta.type = AUDIO_RESOURCE_TYPE_NAME;
206 0 : meta.vendor = "MuseSounds";
207 0 : meta.attributes = {
208 : { u"playbackSetupData", instrumentSoundId },
209 : { u"museCategory", internalCategory },
210 : { u"musePack", instrumentPackName },
211 : { u"museVendorName", vendorName },
212 : { u"museName", internalName },
213 0 : { u"museUID", String::fromStdString(std::to_string(instrumentId)) },
214 0 : };
215 :
216 0 : if (isOnline) {
217 0 : meta.attributes.insert(std::make_pair(u"isOnline", String::number(1)));
218 : }
219 :
220 0 : result.push_back(std::move(meta));
221 0 : }
222 :
223 0 : return result;
224 0 : }
225 :
226 0 : SoundPresetList MuseSamplerResolver::resolveSoundPresets(const AudioResourceMeta& resourceMeta) const
227 : {
228 0 : InstrumentInfo instrument = findInstrument(m_libHandler, resourceMeta);
229 0 : if (!instrument.msInstrument) {
230 0 : return SoundPresetList();
231 : }
232 :
233 0 : ms_PresetList presets = m_libHandler->getPresetList(instrument.msInstrument);
234 0 : SoundPresetList result;
235 :
236 0 : while (const char* presetCode = m_libHandler->getNextPreset(presets)) {
237 0 : SoundPreset soundPreset;
238 0 : soundPreset.code = presetCode;
239 0 : soundPreset.name = presetCode;
240 0 : loadSoundPresetAttributes(soundPreset.attributes, instrument.instrumentId, presetCode);
241 :
242 0 : result.emplace_back(std::move(soundPreset));
243 0 : }
244 :
245 0 : if (result.empty()) {
246 : // All instruments always have at least 1 preset (default)
247 : // If getPresetList returns an empty list, the default preset is implicitly defined as ""
248 0 : static const String DEFAULT_PRESET_CODE(u"Default");
249 :
250 0 : SoundPreset defaultPreset;
251 0 : defaultPreset.code = DEFAULT_PRESET_CODE;
252 0 : loadSoundPresetAttributes(defaultPreset.attributes, instrument.instrumentId, "");
253 :
254 0 : result.emplace_back(std::move(defaultPreset));
255 0 : }
256 :
257 0 : result.front().isDefault = true;
258 :
259 0 : return result;
260 0 : }
261 :
262 0 : void MuseSamplerResolver::refresh()
263 : {
264 0 : }
265 :
266 0 : void MuseSamplerResolver::clearSources()
267 : {
268 0 : }
269 :
270 0 : const Version& MuseSamplerResolver::version() const
271 : {
272 0 : return m_samplerVersion;
273 : }
274 :
275 0 : bool MuseSamplerResolver::isLoaded() const
276 : {
277 0 : if (m_libHandler) {
278 0 : return true;
279 : }
280 :
281 0 : return false;
282 : }
283 :
284 0 : bool MuseSamplerResolver::initFailed() const
285 : {
286 0 : return m_initFailed;
287 : }
288 :
289 0 : float MuseSamplerResolver::defaultReverbLevel(const String& instrumentSoundId) const
290 : {
291 0 : if (!m_libHandler || instrumentSoundId.empty()) {
292 0 : return 0.f;
293 : }
294 :
295 0 : auto instrumentList = m_libHandler->getInstrumentList();
296 0 : while (auto instrument = m_libHandler->getNextInstrument(instrumentList)) {
297 0 : String soundId = String::fromUtf8(m_libHandler->getMpeSoundId(instrument));
298 :
299 0 : if (instrumentSoundId == soundId) {
300 0 : return m_libHandler->getReverbLevel(instrument) / 100.f;
301 : }
302 0 : }
303 :
304 0 : return 0.f;
305 : }
306 :
307 0 : ByteArray MuseSamplerResolver::drumMapping(int instrumentId) const
308 : {
309 0 : if (!m_libHandler) {
310 0 : return ByteArray();
311 : }
312 :
313 0 : const char* mapping_cstr = m_libHandler->getDrumMapping(instrumentId);
314 0 : return mapping_cstr ? ByteArray(mapping_cstr) : ByteArray();
315 : }
316 :
317 0 : std::vector<Instrument> MuseSamplerResolver::instruments() const
318 : {
319 0 : if (!m_libHandler) {
320 0 : return {};
321 : }
322 :
323 0 : std::vector<Instrument> result;
324 :
325 0 : auto instrumentList = m_libHandler->getInstrumentList();
326 0 : while (auto msInstrument = m_libHandler->getNextInstrument(instrumentList)) {
327 0 : const char* json_cstr = m_libHandler->getInstrumentInfoJson(msInstrument);
328 0 : if (!json_cstr) {
329 0 : continue;
330 : }
331 :
332 0 : ByteArray json(json_cstr);
333 0 : if (json.empty()) {
334 0 : continue;
335 : }
336 :
337 0 : std::string err;
338 0 : JsonDocument doc = JsonDocument::fromJson(json, &err);
339 0 : if (!err.empty()) {
340 0 : LOGE() << err;
341 0 : continue;
342 : }
343 :
344 0 : int id = m_libHandler->getInstrumentId(msInstrument);
345 0 : JsonObject obj = doc.rootObject();
346 :
347 0 : Instrument instrument;
348 0 : instrument.id = String::number(id);
349 0 : instrument.soundId = String::fromUtf8(m_libHandler->getMpeSoundId(msInstrument));
350 0 : instrument.musicXmlId = String::fromUtf8(m_libHandler->getMusicXmlSoundId(msInstrument));
351 0 : instrument.name = obj.value("FriendlyName").toString();
352 0 : instrument.abbreviation = obj.value("Abbreviation").toString();
353 0 : instrument.category = obj.value("Category").toString();
354 0 : instrument.vendor = obj.value("Vendor").toString();
355 0 : instrument.staffLines = obj.value("StaffLines", "5").toString().toInt();
356 0 : instrument.staffType = muse::value(STAFF_NAME_LOWER_CASE_TO_TYPE, obj.value(
357 0 : "DefaultStaffType").toString().toLower(), StaffType::Standard);
358 0 : instrument.clefType = muse::value(CLEF_NAME_LOWER_CASE_TO_TYPE, obj.value("DefaultClef").toString().toLower(), ClefType::Treble);
359 :
360 0 : result.emplace_back(std::move(instrument));
361 0 : }
362 :
363 0 : return result;
364 0 : }
365 :
366 0 : void MuseSamplerResolver::loadSoundPresetAttributes(SoundPresetAttributes& attributes, int instrumentId, const char* presetCode) const
367 : {
368 0 : const char* articulations_cstr = m_libHandler->getTextArticulations(instrumentId, presetCode);
369 0 : if (articulations_cstr) {
370 0 : String articulation = String::fromAscii(articulations_cstr);
371 :
372 0 : if (!articulation.empty()) {
373 0 : attributes.emplace(PLAYING_TECHNIQUES_ATTRIBUTE, std::move(articulation));
374 : }
375 0 : }
376 0 : }
|