LCOV - code coverage report
Current view: top level - muse/framework/vst/internal/synth - vstsynthesiser.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 118 0
Test Date: 2026-11-05 04:07:02

            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              : }
        

Generated by: LCOV version 2.0-1