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 "musesamplersequencer.h"
24 :
25 : #include <algorithm>
26 :
27 : #include "apitypes.h"
28 :
29 : using namespace muse;
30 : using namespace muse::musesampler;
31 : using namespace muse::mpe;
32 :
33 : static const std::unordered_map<ArticulationType, ms_NoteArticulation> ARTICULATION_TYPES_PART1 {
34 : { ArticulationType::Standard, ms_NoteArticulation_None },
35 : { ArticulationType::Staccato, ms_NoteArticulation_Staccato },
36 : { ArticulationType::Staccatissimo, ms_NoteArticulation_Staccatissimo },
37 : { ArticulationType::Accent, ms_NoteArticulation_Accent },
38 : { ArticulationType::Tenuto, ms_NoteArticulation_Tenuto },
39 : { ArticulationType::Marcato, ms_NoteArticulation_Marcato },
40 : { ArticulationType::Harmonic, ms_NoteArticulation_Harmonics },
41 : { ArticulationType::PalmMute, ms_NoteArticulation_PalmMute },
42 : { ArticulationType::Mute, ms_NoteArticulation_Mute },
43 : { ArticulationType::Legato, ms_NoteArticulation_Slur },
44 : { ArticulationType::Trill, ms_NoteArticulation_Trill },
45 : { ArticulationType::TrillBaroque, ms_NoteArticulation_Trill },
46 :
47 : { ArticulationType::Arpeggio, ms_NoteArticulation_ArpeggioUp },
48 : { ArticulationType::ArpeggioUp, ms_NoteArticulation_ArpeggioUp },
49 : { ArticulationType::ArpeggioDown, ms_NoteArticulation_ArpeggioDown },
50 :
51 : { ArticulationType::Tremolo8th, ms_NoteArticulation_Tremolo1 },
52 : { ArticulationType::Tremolo16th, ms_NoteArticulation_Tremolo2 },
53 : { ArticulationType::Tremolo32nd, ms_NoteArticulation_Tremolo3 },
54 : { ArticulationType::Tremolo64th, ms_NoteArticulation_Tremolo3 },
55 : { ArticulationType::TremoloBuzz, ms_NoteArticulation_BuzzTremolo },
56 :
57 : { ArticulationType::DiscreteGlissando, ms_NoteArticulation_Glissando },
58 : { ArticulationType::ContinuousGlissando, ms_NoteArticulation_Portamento },
59 : { ArticulationType::Slide, ms_NoteArticulation_Portamento },
60 :
61 : { ArticulationType::Scoop, ms_NoteArticulation_Scoop },
62 : { ArticulationType::Plop, ms_NoteArticulation_Plop },
63 : { ArticulationType::Doit, ms_NoteArticulation_Doit },
64 : { ArticulationType::Fall, ms_NoteArticulation_Fall },
65 : { ArticulationType::PreAppoggiatura, ms_NoteArticulation_Appoggiatura },
66 : { ArticulationType::PostAppoggiatura, ms_NoteArticulation_Appoggiatura },
67 : { ArticulationType::Acciaccatura, ms_NoteArticulation_Acciaccatura },
68 :
69 : { ArticulationType::Open, ms_NoteArticulation_Open },
70 :
71 : { ArticulationType::Pizzicato, ms_NoteArticulation_Pizzicato },
72 : { ArticulationType::SnapPizzicato, ms_NoteArticulation_SnapPizzicato },
73 :
74 : { ArticulationType::UpperMordent, ms_NoteArticulation_MordentWhole },
75 : { ArticulationType::UpMordent, ms_NoteArticulation_MordentWhole },
76 : { ArticulationType::LowerMordent, ms_NoteArticulation_MordentInvertedWhole },
77 : { ArticulationType::DownMordent, ms_NoteArticulation_MordentInvertedWhole },
78 : { ArticulationType::Turn, ms_NoteArticulation_TurnWholeWhole },
79 : { ArticulationType::InvertedTurn, ms_NoteArticulation_TurnInvertedWholeWhole },
80 :
81 : { ArticulationType::ColLegno, ms_NoteArticulation_ColLegno },
82 : { ArticulationType::SulTasto, ms_NoteArticulation_SulTasto },
83 : { ArticulationType::SulPont, ms_NoteArticulation_SulPonticello },
84 :
85 : { ArticulationType::MalletBellOnTable, ms_NoteArticulation_MalletBellOnTable },
86 : { ArticulationType::MalletBellSuspended, ms_NoteArticulation_MalletBellSuspended },
87 : { ArticulationType::MalletLift, ms_NoteArticulation_MalletLift },
88 : { ArticulationType::PluckLift, ms_NoteArticulation_PluckLift },
89 : { ArticulationType::Gyro, ms_NoteArticulation_Gyro },
90 : { ArticulationType::Martellato, ms_NoteArticulation_Martellato },
91 : { ArticulationType::MartellatoLift, ms_NoteArticulation_MartellatoLift },
92 : { ArticulationType::HandMartellato, ms_NoteArticulation_HandMartellato },
93 : { ArticulationType::MutedMartellato, ms_NoteArticulation_MutedMartellato },
94 :
95 : { ArticulationType::LaissezVibrer, ms_NoteArticulation_LaissezVibrer },
96 :
97 : { ArticulationType::LeftHandTapping, ms_NoteArticulation_LeftHandTapping },
98 : { ArticulationType::RightHandTapping, ms_NoteArticulation_RightHandTapping },
99 : };
100 :
101 : static const std::unordered_map<ArticulationType, ms_NoteArticulation2> ARTICULATION_TYPES_PART2 {
102 : { ArticulationType::Standard, ms_NoteArticulation2_None },
103 : { ArticulationType::Ring, ms_NoteArticulation2_Ring },
104 : { ArticulationType::ThumbDamp, ms_NoteArticulation2_ThumbDamp },
105 : { ArticulationType::BrushDamp, ms_NoteArticulation2_BrushDamp },
106 : { ArticulationType::RingTouch, ms_NoteArticulation2_RingTouch },
107 : { ArticulationType::Pluck, ms_NoteArticulation2_Pluck },
108 : { ArticulationType::SingingBell, ms_NoteArticulation2_SingingBell },
109 : { ArticulationType::SingingVibrate, ms_NoteArticulation2_SingingVibrate },
110 : { ArticulationType::Swing, ms_NoteArticulation2_HandbellSwing },
111 : { ArticulationType::Echo, ms_NoteArticulation2_Echo },
112 : { ArticulationType::Breath, ms_NoteArticulation2_BreathMark },
113 : };
114 :
115 : static const std::unordered_map<ArticulationType, ms_NoteHead> NOTEHEAD_TYPES {
116 : { ArticulationType::CrossNote, ms_NoteHead_XNote },
117 : { ArticulationType::CrossLargeNote, ms_NoteHead_LargeX },
118 : { ArticulationType::CrossOrnateNote, ms_NoteHead_OrnateXNote },
119 : { ArticulationType::GhostNote, ms_NoteHead_Ghost },
120 : { ArticulationType::CircleNote, ms_NoteHead_Circle },
121 : { ArticulationType::CircleCrossNote, ms_NoteHead_CircleXNote },
122 : { ArticulationType::CircleDotNote, ms_NoteHead_CircleDot },
123 : { ArticulationType::TriangleLeftNote, ms_NoteHead_Triangle },
124 : { ArticulationType::TriangleRightNote, ms_NoteHead_TriangleRight },
125 : { ArticulationType::TriangleUpNote, ms_NoteHead_TriangleUp },
126 : { ArticulationType::TriangleDownNote, ms_NoteHead_TriangleDown },
127 : { ArticulationType::TriangleRoundDownNote, ms_NoteHead_TriangleRoundDown },
128 : { ArticulationType::DiamondNote, ms_NoteHead_Diamond },
129 : { ArticulationType::MoonNote, ms_NoteHead_FlatTop },
130 : { ArticulationType::PlusNote, ms_NoteHead_Plus },
131 : { ArticulationType::SlashNote, ms_NoteHead_Slash },
132 : { ArticulationType::SquareNote, ms_NoteHead_Square },
133 : { ArticulationType::SlashedBackwardsNote, ms_NoteHead_SlashRightFilled },
134 : { ArticulationType::SlashedForwardsNote, ms_NoteHead_SlashLeftFilled },
135 : };
136 :
137 0 : void MuseSamplerSequencer::init(MuseSamplerLibHandlerPtr samplerLib, ms_MuseSampler sampler, IMuseSamplerTracks* tracks,
138 : std::string&& defaultPresetCode)
139 : {
140 0 : m_samplerLib = samplerLib;
141 0 : m_sampler = sampler;
142 0 : m_tracks = tracks;
143 0 : m_defaultPresetCode = std::move(defaultPresetCode);
144 0 : }
145 :
146 0 : void MuseSamplerSequencer::updateMainStreamEvents(const PlaybackEventsMap& events, const DynamicAutomationLayers& dynamics)
147 : {
148 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
149 0 : return;
150 : }
151 :
152 0 : clearAllTracks();
153 :
154 0 : loadEvents(events);
155 0 : loadDynamicEvents(dynamics);
156 :
157 0 : finalizeAllTracks();
158 : }
159 :
160 0 : void MuseSamplerSequencer::updateOffStreamEvents(const PlaybackEventsMap& events)
161 : {
162 0 : m_auditionParamsCache.clear();
163 :
164 0 : for (const auto& pair : events) {
165 0 : for (const auto& event : pair.second) {
166 0 : if (std::holds_alternative<mpe::NoteEvent>(event)) {
167 0 : addAuditionNoteEvent(std::get<mpe::NoteEvent>(event));
168 0 : } else if (std::holds_alternative<mpe::ControllerChangeEvent>(event)) {
169 0 : addAuditionCCEvent(std::get<mpe::ControllerChangeEvent>(event), pair.first);
170 : } else {
171 0 : parseAuditionParams(event, m_auditionParamsCache);
172 : }
173 : }
174 : }
175 :
176 0 : updateOffSequenceIterator();
177 0 : }
178 :
179 0 : void MuseSamplerSequencer::clearAllTracks()
180 : {
181 0 : m_layerIdxToTrackIdx.clear();
182 0 : m_presetChangesByTrack.clear();
183 :
184 0 : for (ms_Track track : allTracks()) {
185 0 : m_samplerLib->clearTrack(m_sampler, track);
186 : }
187 0 : }
188 :
189 0 : void MuseSamplerSequencer::finalizeAllTracks()
190 : {
191 0 : for (ms_Track track : allTracks()) {
192 0 : m_samplerLib->finalizeTrack(m_sampler, track);
193 : }
194 0 : }
195 :
196 0 : ms_Track MuseSamplerSequencer::findOrCreateTrack(layer_idx_t layerIdx)
197 : {
198 0 : const TrackList& tracks = m_tracks->allTracks();
199 0 : auto it = m_layerIdxToTrackIdx.find(layerIdx);
200 :
201 : // A track has already been assigned to this layer
202 0 : if (it != m_layerIdxToTrackIdx.end()) {
203 0 : if (it->second < tracks.size()) {
204 0 : return tracks.at(it->second);
205 : }
206 :
207 0 : ASSERT_X("Invalid track index");
208 : m_layerIdxToTrackIdx.erase(it);
209 : }
210 :
211 : // Try to find a free track
212 0 : std::unordered_set<track_idx_t> assignedTracks(m_layerIdxToTrackIdx.size());
213 0 : for (const auto& pair: m_layerIdxToTrackIdx) {
214 0 : assignedTracks.insert(pair.second);
215 : }
216 :
217 0 : for (track_idx_t trackIdx = 0; trackIdx < tracks.size(); ++trackIdx) {
218 0 : if (!muse::contains(assignedTracks, trackIdx)) {
219 0 : m_layerIdxToTrackIdx.emplace(layerIdx, trackIdx);
220 0 : return tracks.at(trackIdx);
221 : }
222 : }
223 :
224 : // Add a new track
225 0 : ms_Track newTrack = m_tracks->addTrack();
226 0 : if (newTrack) {
227 0 : m_layerIdxToTrackIdx.emplace(layerIdx, tracks.size() - 1);
228 0 : return newTrack;
229 : }
230 :
231 : // Could not add the track. Use 1st track as a fallback
232 0 : if (!tracks.empty()) {
233 0 : m_layerIdxToTrackIdx.emplace(layerIdx, 0);
234 0 : return tracks.front();
235 : }
236 :
237 0 : UNREACHABLE;
238 : return nullptr;
239 0 : }
240 :
241 0 : ms_Track MuseSamplerSequencer::findTrack(layer_idx_t layerIdx) const
242 : {
243 0 : auto it = m_layerIdxToTrackIdx.find(layerIdx);
244 0 : if (it == m_layerIdxToTrackIdx.end()) {
245 0 : return nullptr;
246 : }
247 :
248 0 : const TrackList& tracks = m_tracks->allTracks();
249 0 : IF_ASSERT_FAILED(it->second < tracks.size()) {
250 0 : return nullptr;
251 : }
252 :
253 0 : return tracks.at(it->second);
254 : }
255 :
256 0 : const TrackList& MuseSamplerSequencer::allTracks() const
257 : {
258 0 : IF_ASSERT_FAILED(m_tracks) {
259 0 : static const TrackList dummy;
260 0 : return dummy;
261 : }
262 :
263 0 : return m_tracks->allTracks();
264 : }
265 :
266 0 : void MuseSamplerSequencer::loadEvents(const PlaybackEventsMap& changes)
267 : {
268 0 : for (const auto& pair : changes) {
269 0 : for (const auto& event : pair.second) {
270 0 : if (std::holds_alternative<mpe::NoteEvent>(event)) {
271 0 : addNoteEvent(std::get<mpe::NoteEvent>(event));
272 0 : } else if (std::holds_alternative<mpe::TextArticulationEvent>(event)) {
273 0 : addTextArticulationEvent(std::get<mpe::TextArticulationEvent>(event), pair.first);
274 0 : } else if (std::holds_alternative<mpe::SoundPresetChangeEvent>(event)) {
275 0 : addSoundPresetEvent(std::get<mpe::SoundPresetChangeEvent>(event), pair.first);
276 0 : } else if (std::holds_alternative<mpe::SyllableEvent>(event)) {
277 0 : addSyllableEvent(std::get<mpe::SyllableEvent>(event), pair.first);
278 : }
279 : }
280 : }
281 0 : }
282 :
283 0 : void MuseSamplerSequencer::loadDynamicEvents(const DynamicAutomationLayers& changes)
284 : {
285 0 : constexpr timestamp_t STEP_INTERVAL_US = 30000;
286 :
287 0 : for (const auto& layer : changes) {
288 0 : ms_Track track = findTrack(layer.first);
289 0 : if (!track) {
290 0 : continue;
291 : }
292 :
293 0 : std::optional<double> lastRatio;
294 :
295 0 : resampleCurve(layer.second, STEP_INTERVAL_US, [&](timestamp_t t, real_t normalized) {
296 0 : const double ratio = dynamicLevelRatio(dynamicLevelFromNormalized(normalized));
297 0 : if (lastRatio.has_value() && RealIsEqual(*lastRatio, ratio)) {
298 0 : return;
299 : }
300 0 : lastRatio = ratio;
301 0 : m_samplerLib->addDynamicsEvent(m_sampler, track, DynamicEvent { t, ratio });
302 : });
303 : }
304 0 : }
305 :
306 0 : void MuseSamplerSequencer::addNoteEvent(const mpe::NoteEvent& noteEvent)
307 : {
308 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler && m_tracks) {
309 0 : return;
310 : }
311 :
312 0 : const mpe::ArrangementContext& arrangementCtx = noteEvent.arrangementCtx();
313 0 : const mpe::ArticulationMap& articulations = noteEvent.expressionCtx().articulations;
314 0 : const voice_layer_idx_t voiceIdx = arrangementCtx.voiceLayerIndex;
315 0 : const layer_idx_t layerIdx = makeLayerIdx(arrangementCtx.staffLayerIndex, voiceIdx);
316 :
317 0 : ms_Track track = findOrCreateTrack(layerIdx);
318 0 : IF_ASSERT_FAILED(track) {
319 0 : return;
320 : }
321 :
322 0 : for (const auto& art : articulations) {
323 0 : if (art.first == ArticulationType::Pedal) {
324 0 : addPedalEvent(art.second.meta, track);
325 0 : continue;
326 : }
327 :
328 0 : const ms_NoteArticulation ms_art = muse::value(ARTICULATION_TYPES_PART1, art.first, ms_NoteArticulation_None);
329 0 : if (m_samplerLib->isRangedArticulation(ms_art)) {
330 : // If this starts an articulation range, indicate the start
331 0 : if (art.second.occupiedFrom == 0 && art.second.occupiedTo != HUNDRED_PERCENT) {
332 0 : if (m_samplerLib->addTrackEventRangeStart(m_sampler, track, voiceIdx, ms_art) != ms_Result_OK) {
333 0 : LOGE() << "Unable to add ranged articulation range start";
334 : }
335 : }
336 : }
337 : }
338 :
339 : musesampler::NoteEvent event;
340 0 : event._voice = voiceIdx;
341 0 : event._location_us = arrangementCtx.nominalTimestamp;
342 0 : event._duration_us = arrangementCtx.nominalDuration;
343 0 : event._tempo = arrangementCtx.bps * 60.0; // API expects BPM
344 :
345 0 : pitchAndTuning(noteEvent.pitchCtx().nominalPitchLevel, event._pitch, event._offset_cents);
346 0 : parseArticulations(articulations, event._articulation, event._articulation_2, event._notehead);
347 :
348 0 : long long noteEventId = 0;
349 0 : if (!m_samplerLib->addNoteEvent(m_sampler, track, event, noteEventId)) {
350 0 : LOGE() << "Unable to add event for track, timestamp: " << event._location_us;
351 : }
352 :
353 0 : for (auto& art : articulations) {
354 0 : const ms_NoteArticulation ms_art = muse::value(ARTICULATION_TYPES_PART1, art.first, ms_NoteArticulation_None);
355 0 : if (m_samplerLib->isRangedArticulation(ms_art)) {
356 : // If this ends an articulation range, indicate the end
357 0 : if (art.second.occupiedFrom != 0 && art.second.occupiedTo == HUNDRED_PERCENT) {
358 0 : if (m_samplerLib->addTrackEventRangeEnd(m_sampler, track, voiceIdx, ms_art) != ms_Result_OK) {
359 0 : LOGE() << "Unable to add ranged articulation range end";
360 : }
361 : }
362 : }
363 : }
364 :
365 0 : if (!noteEvent.pitchCtx().pitchCurve.empty() && articulations.contains(ArticulationType::Multibend)) {
366 0 : addPitchBends(noteEvent, noteEventId, track);
367 : }
368 :
369 0 : if (articulations.contains(ArticulationType::Vibrato)) {
370 0 : addVibrato(noteEvent, noteEventId, track);
371 : }
372 : }
373 :
374 0 : void MuseSamplerSequencer::addPedalEvent(const mpe::ArticulationMeta& meta, ms_Track track)
375 : {
376 0 : if (meta.hasStart()) {
377 0 : m_samplerLib->addPedalEvent(m_sampler, track, PedalEvent { meta.timestamp, 1.0 });
378 : }
379 :
380 0 : if (meta.hasEnd()) {
381 0 : m_samplerLib->addPedalEvent(m_sampler, track, PedalEvent { meta.timestamp + meta.overallDuration, 0.0 });
382 : }
383 0 : }
384 :
385 0 : void MuseSamplerSequencer::addTextArticulationEvent(const mpe::TextArticulationEvent& event, long long startUs)
386 : {
387 0 : if (event.text.empty()) {
388 0 : return;
389 : }
390 :
391 0 : ms_Track track = findOrCreateTrack(event.layerIdx);
392 0 : IF_ASSERT_FAILED(track) {
393 0 : return;
394 : }
395 :
396 : // Make sure that the string exists long enough
397 0 : const std::string str = event.text.toStdString();
398 :
399 : ms_TextArticulationEvent evt;
400 0 : evt._start_us = startUs;
401 :
402 0 : if (event.text != ORDINARY_PLAYING_TECHNIQUE_CODE) {
403 0 : evt._articulation = str.c_str();
404 : } else {
405 0 : evt._articulation = ""; // resets the active articulation
406 : }
407 :
408 0 : m_samplerLib->addTextArticulationEvent(m_sampler, track, evt);
409 0 : }
410 :
411 0 : void MuseSamplerSequencer::addSoundPresetEvent(const mpe::SoundPresetChangeEvent& event, long long startUs)
412 : {
413 0 : if (event.code.empty()) {
414 0 : return;
415 : }
416 :
417 0 : ms_Track track = findOrCreateTrack(event.layerIdx);
418 0 : IF_ASSERT_FAILED(track) {
419 0 : return;
420 : }
421 :
422 0 : std::map<long long, ms_PresetChange>& presetChanges = m_presetChangesByTrack[track];
423 0 : ms_PresetChange presetChange = 0;
424 :
425 0 : auto it = presetChanges.find(startUs);
426 0 : if (it == presetChanges.end()) {
427 0 : presetChange = m_samplerLib->createPresetChange(m_sampler, track, startUs);
428 0 : presetChanges[startUs] = presetChange;
429 : } else {
430 0 : presetChange = it->second;
431 : }
432 :
433 : // Make sure that the string exists long enough
434 0 : const std::string code = event.code.toStdString();
435 0 : m_samplerLib->addPreset(m_sampler, track, presetChange, code.c_str());
436 0 : }
437 :
438 0 : void MuseSamplerSequencer::addSyllableEvent(const mpe::SyllableEvent& event, long long positionUs)
439 : {
440 0 : if (event.text.empty()) {
441 0 : return;
442 : }
443 :
444 0 : ms_Track track = findOrCreateTrack(event.layerIdx);
445 0 : IF_ASSERT_FAILED(track) {
446 0 : return;
447 : }
448 :
449 : // Make sure that the string exists long enough
450 0 : const std::string str = event.text.toStdString();
451 :
452 : SyllableEvent evt;
453 0 : evt._position_us = positionUs;
454 0 : evt._text = str.c_str();
455 0 : evt._hyphened_to_next = event.flags.testFlag(mpe::SyllableEvent::HyphenedToNext);
456 :
457 0 : m_samplerLib->addSyllableEvent(m_sampler, track, evt);
458 0 : }
459 :
460 0 : void MuseSamplerSequencer::addPitchBends(const mpe::NoteEvent& noteEvent, long long noteEventId, ms_Track track)
461 : {
462 0 : auto addPitchBendEvent = [this, noteEventId, track](long long startUs, long long durationUs, pitch_level_t pitchOffset) {
463 : ms_PitchBendInfo pitchBend;
464 0 : pitchBend.event_id = noteEventId;
465 0 : pitchBend._start_us = startUs;
466 0 : pitchBend._duration_us = durationUs;
467 0 : const int rawCents = pitchLevelToCents(pitchOffset);
468 0 : pitchBend._offset_cents = std::clamp(rawCents, -MAX_PITCH_BEND_RANGE_CENTS, MAX_PITCH_BEND_RANGE_CENTS);
469 0 : pitchBend._type = PitchBend_Bezier;
470 0 : m_samplerLib->addPitchBend(m_sampler, track, pitchBend);
471 0 : };
472 :
473 0 : const PitchCurve& pitchCurve = noteEvent.pitchCtx().pitchCurve;
474 0 : const duration_t noteDuration = noteEvent.arrangementCtx().actualDuration;
475 0 : const pitch_level_t startPitchOffset = muse::value(pitchCurve, 0, 0);
476 0 : long long startOffsetUs = 0;
477 :
478 0 : if (startPitchOffset != 0) {
479 0 : startOffsetUs = 1000; // 1ms
480 0 : addPitchBendEvent(0, startOffsetUs, startPitchOffset);
481 : }
482 :
483 0 : for (auto it = pitchCurve.begin(); it != pitchCurve.end(); ++it) {
484 0 : auto nextIt = std::next(it);
485 0 : if (nextIt == pitchCurve.end() || nextIt->second == it->second) {
486 0 : continue;
487 : }
488 :
489 0 : const long long currOffsetStartUs = noteDuration * percentageToFactor(it->first);
490 0 : const long long nextOffsetStartUs = noteDuration * percentageToFactor(nextIt->first);
491 0 : const long long startUs = currOffsetStartUs + startOffsetUs;
492 0 : const long long durationUs = nextOffsetStartUs - currOffsetStartUs - startOffsetUs;
493 :
494 0 : addPitchBendEvent(startUs, durationUs, nextIt->second);
495 : }
496 0 : }
497 :
498 0 : void MuseSamplerSequencer::addVibrato(const mpe::NoteEvent& noteEvent, long long noteEventId, ms_Track track)
499 : {
500 0 : const duration_t duration = noteEvent.arrangementCtx().actualDuration;
501 : // stand-in data before actual mpe support
502 0 : constexpr auto MAX_VIBRATO_STARTOFFSET_US = (int64_t)0.1 * 1000000;
503 : // stand-in data before actual mpe support
504 0 : constexpr int VIBRATO_DEPTH_CENTS = 23;
505 :
506 : ms_VibratoInfo vibrato;
507 0 : vibrato.event_id = noteEventId;
508 0 : vibrato._start_us = std::min((int64_t)(duration * 0.1), MAX_VIBRATO_STARTOFFSET_US);
509 0 : vibrato._duration_us = duration;
510 0 : vibrato._depth_cents = VIBRATO_DEPTH_CENTS;
511 :
512 0 : m_samplerLib->addVibrato(m_sampler, track, vibrato);
513 0 : }
514 :
515 0 : void MuseSamplerSequencer::addAuditionNoteEvent(const mpe::NoteEvent& noteEvent)
516 : {
517 0 : const mpe::ArrangementContext& arrangementCtx = noteEvent.arrangementCtx();
518 0 : const mpe::ArticulationMap& articulations = noteEvent.expressionCtx().articulations;
519 :
520 0 : layer_idx_t layerIdx = makeLayerIdx(arrangementCtx.staffLayerIndex, arrangementCtx.voiceLayerIndex);
521 0 : ms_Track track = findOrCreateTrack(layerIdx);
522 0 : IF_ASSERT_FAILED(track) {
523 0 : return;
524 : }
525 :
526 0 : int pitch = 0, offsetCents = 0;
527 0 : pitchAndTuning(noteEvent.pitchCtx().nominalPitchLevel, pitch, offsetCents);
528 :
529 0 : if (arrangementCtx.hasStart()) {
530 0 : AuditionStartNoteEvent noteOn;
531 0 : noteOn.msTrack = track;
532 :
533 0 : auto& msEvent = noteOn.msEvent;
534 0 : msEvent._pitch = pitch;
535 0 : msEvent._offset_cents = offsetCents;
536 :
537 0 : parseArticulations(articulations, msEvent._articulation, msEvent._articulation_2, msEvent._notehead);
538 :
539 0 : if (noteEvent.expressionCtx().velocityOverride.has_value()) {
540 0 : msEvent._dynamics = noteEvent.expressionCtx().velocityOverride.value();
541 : } else {
542 0 : msEvent._dynamics = dynamicLevelRatio(noteEvent.expressionCtx().nominalDynamicLevel);
543 : }
544 :
545 0 : msEvent._active_presets = m_auditionParamsCache.presets.empty() ? m_defaultPresetCode.c_str()
546 0 : : m_auditionParamsCache.presets.c_str();
547 0 : msEvent._active_text_articulation = m_auditionParamsCache.textArticulation.c_str();
548 0 : msEvent._active_syllable = m_auditionParamsCache.syllable.c_str();
549 0 : msEvent._articulation_text_starts_at_note = m_auditionParamsCache.textArticulationStartsAtNote;
550 0 : msEvent._syllable_starts_at_note = m_auditionParamsCache.syllableStartsAtNote;
551 :
552 0 : m_offStreamEvents[arrangementCtx.actualTimestamp].push_back(noteOn);
553 : }
554 :
555 0 : if (arrangementCtx.hasEnd()) {
556 0 : AuditionStopNoteEvent noteOff;
557 0 : noteOff.msEvent = { pitch };
558 0 : noteOff.msTrack = track;
559 :
560 0 : timestamp_t timestampTo = arrangementCtx.actualTimestamp + arrangementCtx.actualDuration;
561 0 : m_offStreamEvents[timestampTo].emplace_back(std::move(noteOff));
562 : }
563 :
564 0 : auto pedalIt = articulations.find(mpe::ArticulationType::Pedal);
565 0 : if (pedalIt != articulations.end()) {
566 0 : addAuditionPedalEvent(pedalIt->second.meta, track);
567 : }
568 : }
569 :
570 0 : void MuseSamplerSequencer::addAuditionPedalEvent(const mpe::ArticulationMeta& meta, ms_Track track)
571 : {
572 0 : AuditionCCEvent event;
573 0 : event.cc = 64;
574 0 : event.msTrack = track;
575 :
576 0 : if (meta.hasStart()) {
577 0 : event.value = 1;
578 0 : m_offStreamEvents[meta.timestamp].push_back(event);
579 : }
580 :
581 0 : if (meta.hasEnd()) {
582 0 : event.value = 0;
583 0 : m_offStreamEvents[meta.timestamp + meta.overallDuration].push_back(event);
584 : }
585 0 : }
586 :
587 0 : void MuseSamplerSequencer::addAuditionCCEvent(const mpe::ControllerChangeEvent& event, long long positionUs)
588 : {
589 : static const std::unordered_map<mpe::ControllerChangeEvent::Type, int> TYPE_TO_CC {
590 : { mpe::ControllerChangeEvent::Modulation, 1 },
591 : { mpe::ControllerChangeEvent::SustainPedalOnOff, 64 },
592 : { mpe::ControllerChangeEvent::PitchBend, 128 },
593 0 : };
594 :
595 0 : auto ccIt = TYPE_TO_CC.find(event.type);
596 0 : if (ccIt == TYPE_TO_CC.end()) {
597 0 : return;
598 : }
599 :
600 0 : AuditionCCEvent ccEvent;
601 0 : ccEvent.cc = ccIt->second;
602 0 : ccEvent.value = event.val;
603 0 : ccEvent.msTrack = findOrCreateTrack(event.layerIdx);
604 0 : IF_ASSERT_FAILED(ccEvent.msTrack) {
605 0 : return;
606 : }
607 :
608 0 : m_offStreamEvents[positionUs].push_back(ccEvent);
609 : }
610 :
611 0 : void MuseSamplerSequencer::pitchAndTuning(const pitch_level_t nominalPitch, int& pitch, int& centsOffset) const
612 : {
613 : // Get midi pitch
614 0 : float stepCount = mpe::ZERO_PITCH_LEVEL_MIDI_EQUIVALENT + nominalPitch / static_cast<float>(mpe::PITCH_LEVEL_STEP);
615 0 : pitch = RealRound(stepCount, 0);
616 :
617 : // Get tuning offset
618 0 : int semitonesCount = pitch - mpe::ZERO_PITCH_LEVEL_MIDI_EQUIVALENT;
619 0 : pitch_level_t tuningPitchLevel = nominalPitch - semitonesCount * mpe::PITCH_LEVEL_STEP;
620 0 : centsOffset = pitchLevelToCents(tuningPitchLevel);
621 0 : }
622 :
623 0 : int MuseSamplerSequencer::pitchLevelToCents(const pitch_level_t pitchLevel) const
624 : {
625 : static constexpr float CONVERT_TO_CENTS = (100.f / static_cast<float>(PITCH_LEVEL_STEP));
626 :
627 0 : return pitchLevel * CONVERT_TO_CENTS;
628 : }
629 :
630 0 : double MuseSamplerSequencer::dynamicLevelRatio(const dynamic_level_t level) const
631 : {
632 : static constexpr dynamic_level_t MIN_SUPPORTED_DYNAMIC_LEVEL = dynamicLevelFromType(DynamicType::ppppppppp);
633 : static constexpr dynamic_level_t MAX_SUPPORTED_DYNAMIC_LEVEL = dynamicLevelFromType(DynamicType::fffffffff);
634 :
635 : // Piecewise linear simple scaling to expected MuseSampler values:
636 : static const std::list<std::pair<dynamic_level_t, double> > conversionMap = {
637 : { MIN_SUPPORTED_DYNAMIC_LEVEL, 0.0 },
638 : { dynamicLevelFromType(DynamicType::ppppp), 1.0 / 127.0 },
639 : { dynamicLevelFromType(DynamicType::pppp), 2.0 / 127.0 },
640 : { dynamicLevelFromType(DynamicType::ppp), 4.0 / 127.0 },
641 : { dynamicLevelFromType(DynamicType::pp), 8.0 / 127.0 },
642 : { dynamicLevelFromType(DynamicType::p), 16.0 / 127.0 },
643 : { dynamicLevelFromType(DynamicType::mp), 32.0 / 127.0 },
644 : { dynamicLevelFromType(DynamicType::mf), 64.0 / 127.0 },
645 : { dynamicLevelFromType(DynamicType::f), 96.0 / 127.0 },
646 : { dynamicLevelFromType(DynamicType::ff), 120.0 / 127.0 },
647 : { dynamicLevelFromType(DynamicType::fff), 127.0 / 127.0 },
648 : { MAX_SUPPORTED_DYNAMIC_LEVEL, 127.0 / 127.0 }
649 0 : };
650 :
651 0 : auto prev_level = conversionMap.begin();
652 0 : auto last_level = conversionMap.end();
653 0 : auto level_it = std::next(prev_level);
654 0 : while (level_it != last_level) {
655 0 : if (level >= prev_level->first && level <= level_it->first) {
656 0 : auto alpha = static_cast<double>(level - prev_level->first) / static_cast<double>(level_it->first - prev_level->first);
657 0 : return alpha * level_it->second + (1.0 - alpha) * prev_level->second;
658 : }
659 0 : prev_level = level_it;
660 0 : level_it = std::next(level_it);
661 : }
662 0 : LOGE() << "Out of range dynamic value found!";
663 0 : return 48.0 / 127.0;
664 : }
665 :
666 0 : void MuseSamplerSequencer::parseArticulations(const ArticulationMap& articulations,
667 : ms_NoteArticulation& articulations1,
668 : ms_NoteArticulation2& articulations2,
669 : ms_NoteHead& notehead) const
670 : {
671 0 : notehead = ms_NoteHead_Normal;
672 0 : uint64_t arts1 = 0;
673 0 : uint64_t arts2 = 0;
674 :
675 0 : for (const auto& pair : articulations) {
676 0 : if (pair.first == mpe::ArticulationType::Standard) {
677 0 : continue;
678 : }
679 :
680 : //! NOTE: skip last note of slur / hopo, sampler requirement
681 0 : if (pair.first == mpe::ArticulationType::Legato) {
682 0 : if (pair.second.occupiedFrom != 0 && pair.second.occupiedTo == HUNDRED_PERCENT) {
683 0 : continue;
684 : }
685 : }
686 :
687 0 : auto art1It = ARTICULATION_TYPES_PART1.find(pair.first);
688 0 : if (art1It != ARTICULATION_TYPES_PART1.cend()) {
689 0 : arts1 |= art1It->second;
690 0 : continue;
691 : }
692 :
693 0 : auto art2It = ARTICULATION_TYPES_PART2.find(pair.first);
694 0 : if (art2It != ARTICULATION_TYPES_PART2.cend()) {
695 0 : arts2 |= art2It->second;
696 0 : continue;
697 : }
698 :
699 0 : if (notehead == ms_NoteHead_Ghost) {
700 0 : continue;
701 : }
702 :
703 0 : auto headIt = NOTEHEAD_TYPES.find(pair.first);
704 0 : if (headIt != NOTEHEAD_TYPES.cend()) {
705 0 : notehead = headIt->second;
706 : }
707 : }
708 :
709 0 : articulations1 = static_cast<ms_NoteArticulation>(arts1);
710 0 : articulations2 = static_cast<ms_NoteArticulation2>(arts2);
711 0 : }
712 :
713 0 : void MuseSamplerSequencer::parseAuditionParams(const mpe::PlaybackEvent& event, AuditionParams& out) const
714 : {
715 0 : if (std::holds_alternative<mpe::TextArticulationEvent>(event)) {
716 0 : const mpe::TextArticulationEvent& artEvent = std::get<mpe::TextArticulationEvent>(event);
717 0 : out.textArticulation = artEvent.text.toStdString();
718 0 : out.textArticulationStartsAtNote = artEvent.flags.testFlag(mpe::TextArticulationEvent::StartsAtPlaybackPosition);
719 0 : } else if (std::holds_alternative<mpe::SoundPresetChangeEvent>(event)) {
720 0 : const mpe::SoundPresetChangeEvent& soundPresetEvent = std::get<mpe::SoundPresetChangeEvent>(event);
721 :
722 0 : if (out.presets.empty()) {
723 0 : out.presets = soundPresetEvent.code.toStdString();
724 : } else {
725 0 : out.presets += "|" + soundPresetEvent.code.toStdString();
726 : }
727 0 : } else if (std::holds_alternative<mpe::SyllableEvent>(event)) {
728 0 : const mpe::SyllableEvent& syllableEvent = std::get<mpe::SyllableEvent>(event);
729 0 : out.syllable = syllableEvent.text.toStdString();
730 0 : out.syllableStartsAtNote = syllableEvent.flags.testFlag(mpe::SyllableEvent::StartsAtPlaybackPosition);
731 : }
732 0 : }
|