Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/framework/vst/internal/fx/vstfxprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore Limited and others
* Copyright (C) 2025 MuseScore Limited and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
Expand Down Expand Up @@ -46,8 +46,7 @@ void VstFxProcessor::init(const audio::OutputSpec& spec)

auto onPluginLoaded = [this]() {
m_pluginPtr->updatePluginConfig(m_params.configuration);
m_vstAudioClient->setSampleRate(m_outputSpec.sampleRate);
m_vstAudioClient->setMaxSamplesPerBlock(m_outputSpec.samplesPerChannel);
m_vstAudioClient->setOutputSpec(m_outputSpec);
m_inited = true;
};

Expand Down Expand Up @@ -85,7 +84,7 @@ async::Channel<AudioFxParams> VstFxProcessor::paramsChanged() const
void VstFxProcessor::setOutputSpec(const audio::OutputSpec& spec)
{
m_outputSpec = spec;
m_vstAudioClient->setSampleRate(spec.sampleRate);
m_vstAudioClient->setOutputSpec(spec);
}

bool VstFxProcessor::active() const
Expand Down
8 changes: 3 additions & 5 deletions src/framework/vst/internal/fx/vstfxprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore Limited and others
* Copyright (C) 2025 MuseScore Limited and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
Expand All @@ -19,8 +19,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MUSE_VST_VSTFXPROCESSOR_H
#define MUSE_VST_VSTFXPROCESSOR_H

#pragma once

#include <memory>

Expand Down Expand Up @@ -61,5 +61,3 @@ class VstFxProcessor : public muse::audio::IFxProcessor, public async::Asyncable

using VstFxPtr = std::shared_ptr<VstFxProcessor>;
}

#endif // MUSE_VST_VSTFXPROCESSOR_H
22 changes: 13 additions & 9 deletions src/framework/vst/internal/synth/vstsynthesiser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore Limited and others
* Copyright (C) 2025 MuseScore Limited and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
Expand Down Expand Up @@ -60,12 +60,11 @@ void VstSynthesiser::init(const OutputSpec& spec)

m_pluginPtr = instancesRegister()->makeAndRegisterInstrPlugin(m_params.resourceMeta.id, m_trackId);

m_vstAudioClient->init(AudioPluginType::Instrument, m_pluginPtr, m_outputSpec.audioChannelCount);
m_vstAudioClient->init(AudioPluginType::Instrument, m_pluginPtr);

auto onPluginLoaded = [this]() {
m_pluginPtr->updatePluginConfig(m_params.configuration);
m_vstAudioClient->setSampleRate(m_outputSpec.sampleRate);
m_vstAudioClient->setMaxSamplesPerBlock(m_outputSpec.samplesPerChannel);
m_vstAudioClient->setOutputSpec(m_outputSpec);
m_vstAudioClient->loadSupportedParams();
m_sequencer.init(m_vstAudioClient->paramsMapping(SUPPORTED_CONTROLLERS), m_useDynamicEvents);
};
Expand All @@ -90,6 +89,15 @@ void VstSynthesiser::init(const OutputSpec& spec)
});
}

void VstSynthesiser::updateRenderingMode(const RenderMode mode)
{
if (mode == RenderMode::OfflineMode) {
m_vstAudioClient->setProcessMode(VstProcessMode::kOffline);
} else {
m_vstAudioClient->setProcessMode(VstProcessMode::kRealtime);
}
}

void VstSynthesiser::toggleVolumeGain(const bool isActive)
{
static constexpr muse::audio::gain_t NON_ACTIVE_GAIN = 0.5f;
Expand Down Expand Up @@ -174,7 +182,7 @@ void VstSynthesiser::setPlaybackPosition(const muse::audio::msecs_t newPosition)
void VstSynthesiser::setOutputSpec(const audio::OutputSpec& spec)
{
m_outputSpec = spec;
m_vstAudioClient->setSampleRate(spec.sampleRate);
m_vstAudioClient->setOutputSpec(spec);
}

unsigned int VstSynthesiser::audioChannelsCount() const
Expand All @@ -193,10 +201,6 @@ samples_t VstSynthesiser::process(float* buffer, samples_t samplesPerChannel)
return 0;
}

if (samplesPerChannel > m_vstAudioClient->maxSamplesPerBlock()) {
m_vstAudioClient->setMaxSamplesPerBlock(samplesPerChannel);
}

const msecs_t nextMsecs = samplesToMsecs(samplesPerChannel, m_outputSpec.sampleRate);
const VstSequencer::EventSequenceMap sequences = m_sequencer.movePlaybackForward(nextMsecs);

Expand Down
4 changes: 3 additions & 1 deletion src/framework/vst/internal/synth/vstsynthesiser.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore Limited and others
* Copyright (C) 2025 MuseScore Limited and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
Expand Down Expand Up @@ -71,6 +71,8 @@ class VstSynthesiser : public muse::audio::synth::AbstractSynthesizer
muse::audio::samples_t process(float* buffer, muse::audio::samples_t samplesPerChannel) override;

private:
void updateRenderingMode(const audio::RenderMode mode) override;

void toggleVolumeGain(const bool isActive);
audio::samples_t processSequence(const VstSequencer::EventSequence& sequence, const audio::samples_t samples, float* buffer);

Expand Down
162 changes: 79 additions & 83 deletions src/framework/vst/internal/vstaudioclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore Limited and others
* Copyright (C) 2025 MuseScore Limited and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
Expand Down Expand Up @@ -46,15 +46,14 @@ VstAudioClient::~VstAudioClient()
m_pluginComponent->terminate();
}

void VstAudioClient::init(AudioPluginType type, IVstPluginInstancePtr instance, audioch_t audioChannelsCount)
void VstAudioClient::init(AudioPluginType type, IVstPluginInstancePtr instance)
{
IF_ASSERT_FAILED(instance && type != AudioPluginType::Undefined) {
return;
}

m_type = type;
m_pluginPtr = std::move(instance);
m_audioChannelsCount = audioChannelsCount;
}

void VstAudioClient::loadSupportedParams()
Expand Down Expand Up @@ -90,6 +89,36 @@ void VstAudioClient::setIsActive(const bool isActive)
}
}

void VstAudioClient::setOutputSpec(const audio::OutputSpec& spec)
{
if (m_outputSpec == spec) {
return;
}

m_processData.numSamples = static_cast<Steinberg::int32>(spec.samplesPerChannel);
m_outputSpec = spec;
m_needUnprepareProcessData = true;

updateProcessSetup();
}

void VstAudioClient::setProcessMode(VstProcessMode mode)
{
if (m_processMode == mode) {
return;
}

m_processMode = mode;
m_needUnprepareProcessData = true;

updateProcessSetup();
}

void VstAudioClient::setVolumeGain(const muse::audio::gain_t newVolumeGain)
{
m_volumeGain = newVolumeGain;
}

bool VstAudioClient::handleEvent(const VstEvent& event)
{
ensureActivity();
Expand Down Expand Up @@ -119,58 +148,6 @@ bool VstAudioClient::handleParamChange(const ParamChangeEvent& param)
return true;
}

void VstAudioClient::setVolumeGain(const muse::audio::gain_t newVolumeGain)
{
m_volumeGain = newVolumeGain;
}

muse::audio::samples_t VstAudioClient::process(float* output, muse::audio::samples_t samplesPerChannel,
muse::audio::msecs_t playbackPosition)
{
IAudioProcessorPtr processor = pluginProcessor();
if (!processor || !output) {
return 0;
}

if (!m_isActive) {
return 0;
}

//! NOTE: From the VST3 documentation:
//!
//! Note that the ProcessData->numSamples
//! which indicates how many samples are used in a process call can change from call to call,
//! but never bigger than the maxSamplesPerBlock
m_processData.numSamples = samplesPerChannel;

m_processContext.projectTimeSamples = (playbackPosition / 1000000.f) * m_samplesInfo.sampleRate;

if (samplesPerChannel > m_samplesInfo.maxSamplesPerBlock) {
setMaxSamplesPerBlock(samplesPerChannel);
}

if (m_type == AudioPluginType::Fx) {
extractInputSamples(samplesPerChannel, output);
}

if (processor->process(m_processData) != Steinberg::kResultOk) {
return 0;
}

if (m_type == AudioPluginType::Instrument) {
m_eventList.clear();
m_paramChanges.clearQueue();

if (!fillOutputBufferInstrument(samplesPerChannel, output)) {
return 0;
}
} else if (!fillOutputBufferFx(samplesPerChannel, output)) {
return 0;
}

return samplesPerChannel;
}

void VstAudioClient::flushSound()
{
if (m_playingNotes.empty() && m_playingParams.empty()) {
Expand Down Expand Up @@ -217,33 +194,53 @@ void VstAudioClient::flushSound()
m_playingParams.clear();
}

samples_t VstAudioClient::maxSamplesPerBlock() const
muse::audio::samples_t VstAudioClient::process(float* output, muse::audio::samples_t samplesPerChannel,
muse::audio::msecs_t playbackPosition)
{
return m_samplesInfo.maxSamplesPerBlock;
}
IAudioProcessorPtr processor = pluginProcessor();
if (!processor || !output) {
return 0;
}

void VstAudioClient::setMaxSamplesPerBlock(samples_t samples)
{
if (m_samplesInfo.maxSamplesPerBlock == samples) {
return;
if (!m_isActive) {
return 0;
}

m_processData.numSamples = static_cast<Steinberg::int32>(samples);
m_samplesInfo.maxSamplesPerBlock = samples;
m_needUnprepareProcessData = true;
//! NOTE: From the VST3 documentation:
//!
//! Note that the ProcessData->numSamples
//! which indicates how many samples are used in a process call can change from call to call,
//! but never bigger than the maxSamplesPerBlock
m_processData.numSamples = samplesPerChannel;

updateProcessSetup();
}
m_processContext.projectTimeSamples = (playbackPosition / 1000000.f) * m_outputSpec.sampleRate;

void VstAudioClient::setSampleRate(unsigned int sampleRate)
{
if (m_samplesInfo.sampleRate == sampleRate) {
return;
if (samplesPerChannel > m_outputSpec.samplesPerChannel) {
OutputSpec newSpec = m_outputSpec;
newSpec.samplesPerChannel = samplesPerChannel;
setOutputSpec(newSpec);
}

m_samplesInfo.sampleRate = sampleRate;
if (m_type == AudioPluginType::Fx) {
extractInputSamples(samplesPerChannel, output);
}

updateProcessSetup();
if (processor->process(m_processData) != Steinberg::kResultOk) {
return 0;
}

if (m_type == AudioPluginType::Instrument) {
m_eventList.clear();
m_paramChanges.clearQueue();

if (!fillOutputBufferInstrument(samplesPerChannel, output)) {
return 0;
}
} else if (!fillOutputBufferFx(samplesPerChannel, output)) {
return 0;
}

return samplesPerChannel;
}

ParamsMapping VstAudioClient::paramsMapping(const std::set<Steinberg::Vst::CtrlNumber>& controllers) const
Expand Down Expand Up @@ -299,8 +296,7 @@ void VstAudioClient::setUpProcessData()
return;
}

m_processContext.sampleRate = m_samplesInfo.sampleRate;

m_processContext.sampleRate = m_outputSpec.sampleRate;
m_processData.inputEvents = &m_eventList;
m_processData.inputParameterChanges = &m_paramChanges;
m_processData.processContext = &m_processContext;
Expand All @@ -311,7 +307,7 @@ void VstAudioClient::setUpProcessData()
}

if (!m_processData.outputs || !m_processData.inputs) {
m_processData.prepare(*component, m_samplesInfo.maxSamplesPerBlock, Steinberg::Vst::kSample32);
m_processData.prepare(*component, m_outputSpec.samplesPerChannel, Steinberg::Vst::kSample32);
}

if (!m_activeOutputBusses.empty() && !m_activeInputBusses.empty()) {
Expand Down Expand Up @@ -367,7 +363,7 @@ void VstAudioClient::setUpProcessData()

void VstAudioClient::updateProcessSetup()
{
if (!m_samplesInfo.isValid()) {
if (!m_outputSpec.isValid()) {
return;
}

Expand All @@ -379,10 +375,10 @@ void VstAudioClient::updateProcessSetup()
disableActivity();

VstProcessSetup setup;
setup.processMode = Steinberg::Vst::kRealtime;
setup.processMode = m_processMode;
setup.symbolicSampleSize = Steinberg::Vst::kSample32;
setup.maxSamplesPerBlock = m_samplesInfo.maxSamplesPerBlock;
setup.sampleRate = m_samplesInfo.sampleRate;
setup.maxSamplesPerBlock = m_outputSpec.samplesPerChannel;
setup.sampleRate = m_outputSpec.sampleRate;

if (processor->setupProcessing(setup) != Steinberg::kResultOk) {
return;
Expand All @@ -403,7 +399,7 @@ void VstAudioClient::extractInputSamples(samples_t sampleCount, const float* sou
Steinberg::Vst::AudioBusBuffers& bus = m_processData.inputs[0];

for (samples_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
size_t offset = sampleIndex * m_audioChannelsCount;
size_t offset = sampleIndex * m_outputSpec.audioChannelCount;

for (audioch_t audioChannelIndex = 0; audioChannelIndex < bus.numChannels; ++audioChannelIndex) {
bus.channelBuffers32[audioChannelIndex][sampleIndex] = sourceBuffer[offset + audioChannelIndex];
Expand All @@ -423,7 +419,7 @@ bool VstAudioClient::fillOutputBufferInstrument(samples_t sampleCount, float* ou
Steinberg::Vst::AudioBusBuffers bus = m_processData.outputs[busIndex];

for (samples_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
size_t offset = sampleIndex * m_audioChannelsCount;
size_t offset = sampleIndex * m_outputSpec.audioChannelCount;

for (audioch_t audioChannelIndex = 0; audioChannelIndex < bus.numChannels; ++audioChannelIndex) {
float sample = bus.channelBuffers32[audioChannelIndex][sampleIndex];
Expand Down Expand Up @@ -451,7 +447,7 @@ bool VstAudioClient::fillOutputBufferFx(samples_t sampleCount, float* output)
Steinberg::Vst::AudioBusBuffers bus = m_processData.outputs[busIndex];

for (samples_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
size_t offset = sampleIndex * m_audioChannelsCount;
size_t offset = sampleIndex * m_outputSpec.audioChannelCount;

for (audioch_t audioChannelIndex = 0; audioChannelIndex < bus.numChannels; ++audioChannelIndex) {
float sample = bus.channelBuffers32[audioChannelIndex][sampleIndex];
Expand Down
Loading
Loading