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 : #include "vstsynthesiser.h"
23 :
24 : #include "log.h"
25 :
26 : using namespace muse;
27 : using namespace muse::vst;
28 : using namespace muse::audio::synth;
29 : using namespace muse::audio;
30 : using namespace muse::audioplugins;
31 :
32 : static const std::set<Steinberg::Vst::CtrlNumber> SUPPORTED_CONTROLLERS = {
33 : Steinberg::Vst::kCtrlVolume,
34 : Steinberg::Vst::kCtrlExpression,
35 : Steinberg::Vst::kCtrlSustainOnOff,
36 : Steinberg::Vst::kCtrlSustenutoOnOff,
37 : Steinberg::Vst::kPitchBend,
38 : };
39 :
40 0 : VstSynthesiser::VstSynthesiser(const TrackId trackId, const muse::audio::AudioInputParams& params)
41 : : AbstractSynthesizer(params),
42 0 : m_vstAudioClient(std::make_unique<VstAudioClient>()),
43 0 : m_trackId(trackId)
44 : {
45 0 : }
46 :
47 0 : VstSynthesiser::~VstSynthesiser()
48 : {
49 0 : instancesRegister()->unregisterInstrPlugin(m_params.resourceMeta.id, m_trackId);
50 0 : }
51 :
52 0 : void VstSynthesiser::init(const OutputSpec& spec)
53 : {
54 0 : IF_ASSERT_FAILED(spec.isValid()) {
55 0 : return;
56 : }
57 :
58 0 : m_outputSpec = spec;
59 :
60 0 : m_pluginPtr = instancesRegister()->makeAndRegisterInstrPlugin(m_params.resourceMeta.id, m_trackId);
61 :
62 0 : m_vstAudioClient->init(PluginType::Instrument, m_pluginPtr);
63 :
64 0 : auto onPluginLoaded = [this]() {
65 0 : m_pluginPtr->updatePluginConfig(m_params.configuration);
66 0 : m_vstAudioClient->setOutputSpec(m_outputSpec);
67 0 : m_vstAudioClient->loadSupportedParams();
68 0 : m_sequencer.init(m_vstAudioClient->paramsMapping(SUPPORTED_CONTROLLERS), m_useDynamicEvents);
69 0 : m_inited = true;
70 0 : };
71 :
72 0 : if (m_pluginPtr->isLoaded()) {
73 0 : onPluginLoaded();
74 : } else {
75 0 : m_pluginPtr->loadingCompleted().onNotify(this, onPluginLoaded);
76 : }
77 :
78 0 : m_pluginPtr->pluginSettingsChanged().onReceive(this, [this](const muse::audio::AudioUnitConfig& newConfig) {
79 0 : if (m_params.configuration == newConfig) {
80 0 : return;
81 : }
82 :
83 0 : m_params.configuration = newConfig;
84 0 : m_paramsChanges.send(m_params);
85 : });
86 :
87 0 : m_sequencer.setOnOffStreamFlushed([this]() {
88 0 : m_vstAudioClient->flushSound();
89 0 : });
90 : }
91 :
92 0 : void VstSynthesiser::toggleVolumeGain(const bool isActive)
93 : {
94 : static constexpr muse::audio::gain_t NON_ACTIVE_GAIN = 0.5f;
95 :
96 0 : if (isActive) {
97 0 : m_vstAudioClient->setVolumeGain(m_sequencer.currentGain());
98 : } else {
99 0 : m_vstAudioClient->setVolumeGain(NON_ACTIVE_GAIN);
100 : }
101 0 : }
102 :
103 0 : bool VstSynthesiser::isValid() const
104 : {
105 0 : if (!m_pluginPtr) {
106 0 : return false;
107 : }
108 :
109 0 : return m_pluginPtr->isLoaded();
110 : }
111 :
112 0 : muse::audio::AudioSourceType VstSynthesiser::type() const
113 : {
114 0 : return m_params.type();
115 : }
116 :
117 0 : std::string VstSynthesiser::name() const
118 : {
119 0 : if (!m_pluginPtr) {
120 0 : return std::string();
121 : }
122 :
123 0 : return m_pluginPtr->name();
124 : }
125 :
126 0 : void VstSynthesiser::flushSound()
127 : {
128 0 : m_sequencer.flushOffstream();
129 0 : m_vstAudioClient->flushSound();
130 0 : }
131 :
132 0 : void VstSynthesiser::setupSound(const mpe::PlaybackSetupData& setupData)
133 : {
134 0 : m_useDynamicEvents = setupData.supportsSingleNoteDynamics;
135 0 : }
136 :
137 0 : void VstSynthesiser::setupEvents(const mpe::PlaybackData& playbackData)
138 : {
139 0 : m_sequencer.load(playbackData);
140 0 : }
141 :
142 0 : const mpe::PlaybackData& VstSynthesiser::playbackData() const
143 : {
144 0 : return m_sequencer.playbackData();
145 : }
146 :
147 0 : void VstSynthesiser::setMode(const muse::audio::ProcessMode mode)
148 : {
149 0 : if (m_mode == mode) {
150 0 : return;
151 : }
152 :
153 0 : AbstractSynthesizer::setMode(mode);
154 :
155 0 : bool isActive = isModePlaying(mode);
156 0 : m_sequencer.setActive(isActive);
157 0 : toggleVolumeGain(isActive);
158 0 : m_vstAudioClient->setIsPlaying(isActive);
159 0 : m_vstAudioClient->setIsActive(isActive);
160 :
161 0 : if (mode == ProcessMode::PlayingOffline) {
162 0 : m_vstAudioClient->setProcessMode(VstProcessMode::kOffline);
163 : } else {
164 0 : m_vstAudioClient->setProcessMode(VstProcessMode::kRealtime);
165 : }
166 : }
167 :
168 0 : muse::audio::TimePosition VstSynthesiser::playbackPosition() const
169 : {
170 0 : return m_currentPosition;
171 : }
172 :
173 0 : void VstSynthesiser::setPlaybackPosition(const muse::audio::TimePosition& position)
174 : {
175 0 : IF_ASSERT_FAILED(position.isValid()) {
176 0 : return;
177 : }
178 :
179 : //! NOTE Don't trust that msecs_t is used everywhere here,
180 : // in fact, usecs_t (microseconds) is stored there.
181 0 : const usecs_t usecs = muse::secs_to_usecs(position.time());
182 0 : m_sequencer.setPlaybackPosition(msecs_t(usecs.raw()));
183 :
184 0 : m_currentPosition = position;
185 :
186 0 : if (m_sequencer.isActive()) {
187 0 : m_vstAudioClient->setVolumeGain(m_sequencer.currentGain());
188 : }
189 : }
190 :
191 0 : void VstSynthesiser::setOutputSpec(const audio::OutputSpec& spec)
192 : {
193 0 : m_outputSpec = spec;
194 0 : if (m_inited) {
195 0 : m_vstAudioClient->setOutputSpec(spec);
196 : }
197 0 : }
198 :
199 0 : samples_t VstSynthesiser::process(float* buffer, samples_t samplesPerChannel)
200 : {
201 0 : if (!buffer) {
202 0 : return 0;
203 : }
204 :
205 0 : const msecs_t nextMsecs = samplesToMsecs(samplesPerChannel, m_outputSpec.sampleRate);
206 0 : const VstSequencer::EventSequenceMap sequences = m_sequencer.movePlaybackForward(nextMsecs);
207 0 : const bool active = m_sequencer.isActive();
208 :
209 0 : samples_t sampleOffset = 0;
210 0 : samples_t processedSamples = 0;
211 :
212 0 : for (auto it = sequences.cbegin(); it != sequences.cend(); ++it) {
213 0 : samples_t durationInSamples = samplesPerChannel - sampleOffset;
214 :
215 0 : auto nextIt = std::next(it);
216 0 : if (nextIt != sequences.cend()) {
217 0 : msecs_t duration = nextIt->first - it->first;
218 0 : durationInSamples = microSecsToSamples(duration, m_outputSpec.sampleRate);
219 : }
220 :
221 0 : IF_ASSERT_FAILED(sampleOffset + durationInSamples <= samplesPerChannel) {
222 0 : break;
223 : }
224 :
225 0 : processedSamples += processSequence(it->second, durationInSamples, buffer + sampleOffset * m_outputSpec.audioChannelCount);
226 0 : sampleOffset += durationInSamples;
227 :
228 0 : if (active) {
229 0 : m_currentPosition.forward(durationInSamples);
230 : }
231 : }
232 :
233 0 : return processedSamples;
234 0 : }
235 :
236 0 : samples_t VstSynthesiser::processSequence(const VstSequencer::EventSequence& sequence, const samples_t samples, float* buffer)
237 : {
238 0 : for (const VstSequencer::EventType& event : sequence) {
239 0 : if (std::holds_alternative<VstEvent>(event)) {
240 0 : m_vstAudioClient->handleEvent(std::get<VstEvent>(event));
241 0 : } else if (std::holds_alternative<ParamChangeEvent>(event)) {
242 0 : m_vstAudioClient->handleParamChange(std::get<ParamChangeEvent>(event));
243 : } else {
244 0 : muse::audio::gain_t newGain = std::get<muse::audio::gain_t>(event);
245 0 : m_vstAudioClient->setVolumeGain(newGain);
246 : }
247 : }
248 :
249 0 : if (samples == 0) {
250 0 : return 0;
251 : }
252 :
253 0 : return m_vstAudioClient->process(buffer, samples, m_currentPosition.samples());
254 : }
|