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) 2022 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 "vstsequencer.h"
24 :
25 : #include "global/interpolation.h"
26 :
27 : #include <map>
28 :
29 : using namespace muse;
30 : using namespace muse::vst;
31 :
32 : static constexpr ControlIdx MODWHEEL_IDX = static_cast<ControlIdx>(Steinberg::Vst::kCtrlModWheel);
33 : static constexpr ControlIdx SUSTAIN_IDX = static_cast<ControlIdx>(Steinberg::Vst::kCtrlSustainOnOff);
34 : static constexpr ControlIdx SOSTENUTO_IDX = static_cast<ControlIdx>(Steinberg::Vst::kCtrlSustenutoOnOff);
35 : static constexpr ControlIdx PITCH_BEND_IDX = static_cast<ControlIdx>(Steinberg::Vst::kPitchBend);
36 :
37 : static const mpe::ArticulationTypeSet SUSTAIN_PEDAL_CC_SUPPORTED_TYPES {
38 : mpe::ArticulationType::Pedal,
39 : };
40 :
41 : static const mpe::ArticulationTypeSet SOSTENUTO_PEDAL_CC_SUPPORTED_TYPES {
42 : mpe::ArticulationType::LaissezVibrer,
43 : };
44 :
45 : static const mpe::ArticulationTypeSet BEND_SUPPORTED_TYPES {
46 : mpe::ArticulationType::Multibend, mpe::ArticulationType::ContinuousGlissando,
47 : };
48 :
49 0 : void VstSequencer::init(ParamsMapping&& mapping, bool useDynamicEvents)
50 : {
51 0 : m_mapping = std::move(mapping);
52 0 : m_useDynamicEvents = useDynamicEvents;
53 0 : m_inited = true;
54 :
55 0 : updateMainStreamEvents(m_playbackData.originEvents, m_playbackData.dynamics);
56 0 : }
57 :
58 0 : void VstSequencer::updateMainStreamEvents(const mpe::PlaybackEventsMap& events, const mpe::DynamicAutomationLayers& dynamics)
59 : {
60 0 : if (!m_inited) {
61 0 : return;
62 : }
63 :
64 0 : m_mainStreamEvents.clear();
65 :
66 0 : if (m_onMainStreamFlushed) {
67 0 : m_onMainStreamFlushed();
68 : }
69 :
70 0 : addPlaybackEvents(m_mainStreamEvents, events);
71 0 : sortNoteOnEventsByPitch(m_mainStreamEvents);
72 :
73 0 : if (m_useDynamicEvents) {
74 0 : addDynamicEvents(m_mainStreamEvents, dynamics);
75 : }
76 :
77 0 : updateMainSequenceIterator();
78 : }
79 :
80 0 : void VstSequencer::updateOffStreamEvents(const mpe::PlaybackEventsMap& events)
81 : {
82 0 : addPlaybackEvents(m_offStreamEvents, events);
83 0 : updateOffSequenceIterator();
84 0 : }
85 :
86 0 : muse::audio::gain_t VstSequencer::currentGain() const
87 : {
88 0 : if (!m_useDynamicEvents) {
89 0 : return 0.5f;
90 : }
91 :
92 0 : mpe::dynamic_level_t maxDynamicLevel = mpe::MIN_DYNAMIC_LEVEL;
93 0 : bool foundAnyLevel = false;
94 :
95 0 : for (const auto& [_, curve] : m_playbackData.dynamics) {
96 0 : if (curve.empty()) {
97 0 : continue;
98 : }
99 :
100 0 : foundAnyLevel = true;
101 0 : maxDynamicLevel = std::max(maxDynamicLevel, mpe::dynamicLevelFromNormalized(mpe::evaluateCurveAt(curve, m_playbackPosition)));
102 : }
103 :
104 0 : if (!foundAnyLevel) {
105 0 : maxDynamicLevel = mpe::dynamicLevelFromType(mpe::DynamicType::Natural);
106 : }
107 :
108 0 : return expressionLevel(maxDynamicLevel);
109 : }
110 :
111 0 : void VstSequencer::addPlaybackEvents(EventSequenceMap& destination, const mpe::PlaybackEventsMap& events)
112 : {
113 0 : SostenutoTimeAndDurations sostenutoTimeAndDurations;
114 :
115 0 : for (const auto& evPair : events) {
116 0 : for (const mpe::PlaybackEvent& event : evPair.second) {
117 0 : if (std::holds_alternative<mpe::NoteEvent>(event)) {
118 0 : addNoteEvent(destination, std::get<mpe::NoteEvent>(event), sostenutoTimeAndDurations);
119 0 : } else if (std::holds_alternative<mpe::ControllerChangeEvent>(event)) {
120 0 : addControlChangeEvent(destination, evPair.first, std::get<mpe::ControllerChangeEvent>(event));
121 : }
122 : }
123 : }
124 :
125 0 : addSostenutoEvents(destination, sostenutoTimeAndDurations);
126 0 : }
127 :
128 0 : void VstSequencer::addDynamicEvents(EventSequenceMap& destination, const mpe::DynamicAutomationLayers& layers)
129 : {
130 0 : constexpr mpe::timestamp_t STEP_INTERVAL_US = 30000;
131 :
132 : //! NOTE: VST instance has a single gain parameter, so merge layers by tracking each one's last-known level
133 0 : std::map<mpe::timestamp_t, std::vector<std::pair<mpe::layer_idx_t, mpe::dynamic_level_t> > > updatesAt;
134 0 : for (const auto& [layerIdx, curve] : layers) {
135 0 : mpe::resampleCurve(curve, STEP_INTERVAL_US, [&](mpe::timestamp_t t, muse::real_t normalized) {
136 0 : updatesAt[t].emplace_back(layerIdx, mpe::dynamicLevelFromNormalized(normalized));
137 0 : });
138 : }
139 :
140 0 : std::map<mpe::layer_idx_t, mpe::dynamic_level_t> currentLevel;
141 0 : std::optional<float> lastGain;
142 :
143 0 : for (const auto& [t, updates] : updatesAt) {
144 0 : for (const auto& [layerIdx, level] : updates) {
145 0 : currentLevel[layerIdx] = level;
146 : }
147 :
148 0 : mpe::dynamic_level_t maxDynamicLevel = mpe::MIN_DYNAMIC_LEVEL;
149 0 : for (const auto& [_, level] : currentLevel) {
150 0 : maxDynamicLevel = std::max(maxDynamicLevel, level);
151 : }
152 :
153 0 : const float gain = expressionLevel(maxDynamicLevel);
154 0 : if (lastGain.has_value() && muse::RealIsEqual(*lastGain, gain)) {
155 0 : continue;
156 : }
157 0 : lastGain = gain;
158 0 : destination[t].emplace_back(gain);
159 : }
160 0 : }
161 :
162 0 : void VstSequencer::addNoteEvent(EventSequenceMap& destination, const mpe::NoteEvent& noteEvent,
163 : SostenutoTimeAndDurations& sostenutoTimeAndDurations)
164 : {
165 0 : const mpe::ArrangementContext& arrangementCtx = noteEvent.arrangementCtx();
166 0 : const int32_t noteId = noteIndex(noteEvent.pitchCtx().nominalPitchLevel);
167 0 : const float velocityFraction = noteVelocityFraction(noteEvent);
168 0 : const float tuning = noteTuning(noteEvent, noteId);
169 :
170 0 : if (arrangementCtx.hasStart()) {
171 0 : if (m_useDynamicEvents) {
172 0 : destination[arrangementCtx.actualTimestamp].emplace_back(expressionLevel(noteEvent.expressionCtx().nominalDynamicLevel));
173 : }
174 :
175 0 : destination[arrangementCtx.actualTimestamp].emplace_back(buildEvent(VstEvent::kNoteOnEvent, noteId, velocityFraction, tuning));
176 : }
177 :
178 0 : if (arrangementCtx.hasEnd()) {
179 0 : const mpe::timestamp_t timestampTo = arrangementCtx.actualTimestamp + noteEvent.arrangementCtx().actualDuration;
180 0 : destination[timestampTo].emplace_back(buildEvent(VstEvent::kNoteOffEvent, noteId, velocityFraction, tuning));
181 : }
182 :
183 0 : for (const auto& artPair : noteEvent.expressionCtx().articulations) {
184 0 : if (artPair.first == mpe::ArticulationType::Standard) {
185 0 : continue;
186 : }
187 :
188 0 : const mpe::ArticulationMeta& meta = artPair.second.meta;
189 :
190 0 : if (!noteEvent.pitchCtx().pitchCurve.empty() && muse::contains(BEND_SUPPORTED_TYPES, meta.type)) {
191 0 : addPitchCurve(destination, noteEvent, meta);
192 0 : continue;
193 : }
194 :
195 0 : if (muse::contains(SUSTAIN_PEDAL_CC_SUPPORTED_TYPES, meta.type)) {
196 0 : addPedalEvent(destination, meta);
197 0 : continue;
198 : }
199 :
200 0 : if (muse::contains(SOSTENUTO_PEDAL_CC_SUPPORTED_TYPES, meta.type)) {
201 0 : const mpe::timestamp_t timestamp = arrangementCtx.actualTimestamp + noteEvent.arrangementCtx().actualDuration * 0.1; // add offset for Sostenuto to take effect
202 0 : sostenutoTimeAndDurations.push_back(mpe::TimestampAndDuration { timestamp, meta.overallDuration });
203 0 : continue;
204 0 : }
205 : }
206 0 : }
207 :
208 0 : void VstSequencer::addPedalEvent(EventSequenceMap& destination, const mpe::ArticulationMeta& meta)
209 : {
210 0 : if (meta.hasStart()) {
211 0 : addParamChange(destination, meta.timestamp, SUSTAIN_IDX, 1);
212 : }
213 :
214 0 : if (meta.hasEnd()) {
215 0 : addParamChange(destination, meta.timestamp + meta.overallDuration, SUSTAIN_IDX, 0);
216 : }
217 0 : }
218 :
219 0 : void VstSequencer::addControlChangeEvent(EventSequenceMap& destination, const mpe::timestamp_t timestamp,
220 : const mpe::ControllerChangeEvent& event)
221 : {
222 0 : switch (event.type) {
223 0 : case mpe::ControllerChangeEvent::Modulation:
224 0 : addParamChange(destination, timestamp, MODWHEEL_IDX, event.val);
225 0 : break;
226 0 : case mpe::ControllerChangeEvent::SustainPedalOnOff:
227 0 : addParamChange(destination, timestamp, SUSTAIN_IDX, event.val);
228 0 : break;
229 0 : case mpe::ControllerChangeEvent::PitchBend:
230 0 : addParamChange(destination, timestamp, PITCH_BEND_IDX, event.val);
231 0 : break;
232 0 : case mpe::ControllerChangeEvent::Undefined:
233 0 : break;
234 : }
235 0 : }
236 :
237 0 : void VstSequencer::addParamChange(EventSequenceMap& destination, const mpe::timestamp_t timestamp,
238 : const ControlIdx controlIdx, const PluginParamValue value)
239 : {
240 0 : auto controlIt = m_mapping.find(controlIdx);
241 0 : if (controlIt == m_mapping.cend()) {
242 0 : return;
243 : }
244 :
245 0 : const PluginParamId paramId = controlIt->second;
246 0 : EventSequence& events = destination[timestamp];
247 :
248 0 : for (const EventType& e : events) {
249 0 : if (!std::holds_alternative<ParamChangeEvent>(e)) {
250 0 : continue;
251 : }
252 :
253 0 : const ParamChangeEvent& pce = std::get<ParamChangeEvent>(e);
254 0 : if (pce.paramId == paramId && RealIsEqual(pce.value, value)) {
255 0 : return;
256 : }
257 : }
258 :
259 0 : events.emplace_back(ParamChangeEvent { paramId, value });
260 : }
261 :
262 0 : void VstSequencer::addPitchCurve(EventSequenceMap& destination, const mpe::NoteEvent& noteEvent,
263 : const mpe::ArticulationMeta& artMeta)
264 : {
265 0 : auto pitchBendIt = m_mapping.find(PITCH_BEND_IDX);
266 0 : if (pitchBendIt == m_mapping.cend()) {
267 0 : return;
268 : }
269 :
270 0 : const mpe::timestamp_t noteTimestampTo = noteEvent.arrangementCtx().actualTimestamp + noteEvent.arrangementCtx().actualDuration;
271 0 : const mpe::timestamp_t pitchBendTimestampTo = std::min(artMeta.timestamp + artMeta.overallDuration, noteTimestampTo);
272 :
273 0 : ParamChangeEvent event;
274 0 : event.paramId = pitchBendIt->second;
275 0 : event.value = 0.5f;
276 0 : destination[pitchBendTimestampTo].push_back(event);
277 :
278 0 : auto currIt = noteEvent.pitchCtx().pitchCurve.cbegin();
279 0 : auto nextIt = std::next(currIt);
280 0 : auto endIt = noteEvent.pitchCtx().pitchCurve.cend();
281 :
282 0 : float prevBendValue = -1.f;
283 :
284 0 : for (; nextIt != endIt; currIt = nextIt, nextIt = std::next(currIt)) {
285 0 : const float currValue = pitchBendLevel(currIt->second);
286 0 : const float nextValue = pitchBendLevel(nextIt->second);
287 :
288 0 : const mpe::timestamp_t currTime = artMeta.timestamp + artMeta.overallDuration * mpe::percentageToFactor(currIt->first);
289 0 : const mpe::timestamp_t nextTime = artMeta.timestamp + artMeta.overallDuration * mpe::percentageToFactor(nextIt->first);
290 :
291 : using namespace muse::interpolation;
292 0 : const Point currPoint { static_cast<double>(currTime), currValue };
293 0 : const Point nextPoint { static_cast<double>(nextTime), nextValue };
294 :
295 : //! NOTE: Increasing this number results in fewer points being interpolated
296 0 : constexpr mpe::pitch_level_t POINT_WEIGHT = mpe::PITCH_LEVEL_STEP / 25;
297 0 : size_t pointCount = std::abs(nextIt->second - currIt->second) / POINT_WEIGHT;
298 0 : pointCount = std::max(pointCount, size_t(1));
299 :
300 0 : const std::vector<Point> points = lerp(currPoint, nextPoint, pointCount);
301 :
302 0 : for (const Point& point : points) {
303 0 : const mpe::timestamp_t time = static_cast<mpe::timestamp_t>(std::round(point.x));
304 0 : const float bendValue = static_cast<float>(point.y);
305 :
306 0 : if (time < pitchBendTimestampTo && !RealIsEqual(prevBendValue, bendValue)) {
307 0 : event.value = bendValue;
308 0 : destination[time].push_back(event);
309 : }
310 :
311 0 : prevBendValue = bendValue;
312 : }
313 0 : }
314 : }
315 :
316 0 : void VstSequencer::addSostenutoEvents(EventSequenceMap& destination, const SostenutoTimeAndDurations& sostenutoTimeAndDurations)
317 : {
318 0 : for (size_t i = 0; i < sostenutoTimeAndDurations.size(); ++i) {
319 0 : const mpe::TimestampAndDuration& currentTnD = sostenutoTimeAndDurations.at(i);
320 0 : const mpe::timestamp_t timestampTo = currentTnD.timestamp + currentTnD.duration;
321 :
322 0 : addParamChange(destination, currentTnD.timestamp, SOSTENUTO_IDX, 1);
323 :
324 0 : if (i == sostenutoTimeAndDurations.size() - 1) {
325 0 : addParamChange(destination, timestampTo, SOSTENUTO_IDX, 0);
326 0 : continue;
327 : }
328 :
329 0 : const mpe::TimestampAndDuration& nextTnD = sostenutoTimeAndDurations.at(i + 1);
330 0 : if (timestampTo <= nextTnD.timestamp) { // handle potential overlap
331 0 : addParamChange(destination, timestampTo, SOSTENUTO_IDX, 0);
332 : }
333 : }
334 0 : }
335 :
336 : //! Hack to make keyswitches work until we have proper UI support
337 : //! see: https://github.com/musescore/MuseScore/issues/32150
338 0 : void VstSequencer::sortNoteOnEventsByPitch(EventSequenceMap& destination)
339 : {
340 0 : for (auto& [_, seq] : destination) {
341 0 : if (seq.size() <= 1) {
342 0 : continue;
343 : }
344 :
345 0 : std::stable_sort(seq.begin(), seq.end(), [](const EventType& e1, const EventType& e2) {
346 0 : if (!std::holds_alternative<VstEvent>(e1) || !std::holds_alternative<VstEvent>(e2)) {
347 0 : return false;
348 : }
349 :
350 0 : const VstEvent& ve1 = std::get<VstEvent>(e1);
351 0 : const VstEvent& ve2 = std::get<VstEvent>(e2);
352 :
353 0 : if (ve1.type == VstEvent::kNoteOnEvent && ve2.type == VstEvent::kNoteOnEvent) {
354 0 : return ve1.noteOn.pitch < ve2.noteOn.pitch;
355 : }
356 :
357 0 : return false;
358 : });
359 : }
360 0 : }
361 :
362 0 : VstEvent VstSequencer::buildEvent(const VstEvent::EventTypes type, const int32_t noteIdx, const float velocityFraction,
363 : const float tuning) const
364 : {
365 : VstEvent result;
366 :
367 0 : result.busIndex = 0;
368 0 : result.sampleOffset = 0;
369 0 : result.ppqPosition = 0;
370 0 : result.flags = VstEvent::kIsLive;
371 0 : result.type = type;
372 :
373 0 : if (type == VstEvent::kNoteOnEvent) {
374 0 : result.noteOn.noteId = -1;
375 0 : result.noteOn.channel = 0;
376 0 : result.noteOn.pitch = noteIdx;
377 0 : result.noteOn.tuning = tuning;
378 0 : result.noteOn.velocity = velocityFraction;
379 : } else {
380 0 : result.noteOff.noteId = -1;
381 0 : result.noteOff.channel = 0;
382 0 : result.noteOff.pitch = noteIdx;
383 0 : result.noteOff.tuning = tuning;
384 0 : result.noteOff.velocity = velocityFraction;
385 : }
386 :
387 0 : return result;
388 : }
389 :
390 0 : int32_t VstSequencer::noteIndex(const mpe::pitch_level_t pitchLevel) const
391 : {
392 0 : float stepCount = mpe::ZERO_PITCH_LEVEL_MIDI_EQUIVALENT + pitchLevel / static_cast<float>(mpe::PITCH_LEVEL_STEP);
393 :
394 0 : return std::clamp(stepCount, 0.f, 127.f);
395 : }
396 :
397 0 : float VstSequencer::noteTuning(const mpe::NoteEvent& noteEvent, const int noteIdx) const
398 : {
399 0 : int semitonesCount = noteIdx - mpe::ZERO_PITCH_LEVEL_MIDI_EQUIVALENT;
400 :
401 0 : mpe::pitch_level_t tuningPitchLevel = noteEvent.pitchCtx().nominalPitchLevel - semitonesCount * mpe::PITCH_LEVEL_STEP;
402 :
403 0 : return (tuningPitchLevel / static_cast<float>(mpe::PITCH_LEVEL_STEP)) * 100.f;
404 : }
405 :
406 0 : float VstSequencer::noteVelocityFraction(const mpe::NoteEvent& noteEvent) const
407 : {
408 0 : const mpe::ExpressionContext& expressionCtx = noteEvent.expressionCtx();
409 :
410 0 : if (expressionCtx.velocityOverride.has_value()) {
411 0 : return std::clamp(expressionCtx.velocityOverride.value(), 0.f, 1.f);
412 : }
413 :
414 0 : mpe::dynamic_level_t dynamicLevel = expressionCtx.expressionCurve.empty()
415 0 : ? expressionCtx.nominalDynamicLevel : expressionCtx.expressionCurve.maxAmplitudeLevel();
416 0 : return expressionLevel(dynamicLevel);
417 : }
418 :
419 0 : float VstSequencer::expressionLevel(const mpe::dynamic_level_t dynamicLevel) const
420 : {
421 : static constexpr mpe::dynamic_level_t MIN_SUPPORTED_DYNAMIC_LEVEL = mpe::dynamicLevelFromType(mpe::DynamicType::ppp);
422 : static constexpr mpe::dynamic_level_t MAX_SUPPORTED_DYNAMIC_LEVEL = mpe::dynamicLevelFromType(mpe::DynamicType::fff);
423 : static constexpr mpe::dynamic_level_t AVAILABLE_RANGE = MAX_SUPPORTED_DYNAMIC_LEVEL - MIN_SUPPORTED_DYNAMIC_LEVEL;
424 :
425 0 : if (dynamicLevel <= MIN_SUPPORTED_DYNAMIC_LEVEL) {
426 0 : return (0.5f * mpe::ONE_PERCENT) / AVAILABLE_RANGE;
427 : }
428 :
429 0 : if (dynamicLevel >= MAX_SUPPORTED_DYNAMIC_LEVEL) {
430 0 : return 1.f;
431 : }
432 :
433 0 : return RealRound((dynamicLevel - MIN_SUPPORTED_DYNAMIC_LEVEL) / static_cast<float>(AVAILABLE_RANGE), 2);
434 : }
435 :
436 0 : float VstSequencer::pitchBendLevel(const mpe::pitch_level_t pitchLevel) const
437 : {
438 : static constexpr float SEMITONE_RANGE = 2.f;
439 : static constexpr float PITCH_BEND_SEMITONE_STEP = 0.5f / SEMITONE_RANGE;
440 :
441 0 : float pitchLevelSteps = pitchLevel / static_cast<float>(mpe::PITCH_LEVEL_STEP);
442 0 : float offset = pitchLevelSteps * PITCH_BEND_SEMITONE_STEP;
443 :
444 0 : return std::clamp(0.5f + offset, 0.f, 1.f);
445 : }
|