Implemented Vsti synthesiser

This commit is contained in:
vpereverzev 2021-04-12 20:19:48 +02:00 committed by Igor Korsukov
parent 769bcb1750
commit 5a508b4cc5
8 changed files with 485 additions and 7 deletions

View file

@ -41,6 +41,10 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/internal/vstconfiguration.h
${CMAKE_CURRENT_LIST_DIR}/internal/vstplugin.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/vstplugin.h
${CMAKE_CURRENT_LIST_DIR}/internal/synth/vstaudioclient.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/synth/vstaudioclient.h
${CMAKE_CURRENT_LIST_DIR}/internal/synth/vstsynthesiser.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/synth/vstsynthesiser.h
${CMAKE_CURRENT_LIST_DIR}/devtools/vstpluginlistmodelexample.cpp
${CMAKE_CURRENT_LIST_DIR}/devtools/vstpluginlistmodelexample.h
${CMAKE_CURRENT_LIST_DIR}/view/vstplugineditorview.cpp

View file

@ -0,0 +1,153 @@
#include "vstaudioclient.h"
#include "audio/synthtypes.h"
using namespace mu;
using namespace mu::vst;
VstAudioClient::~VstAudioClient()
{
IAudioProcessorPtr processor = m_pluginComponent;
if (!processor) {
return;
}
processor->setProcessing(false);
m_pluginComponent->setActive(false);
}
void VstAudioClient::init(PluginComponentPtr component)
{
if (!component) {
return;
}
m_pluginComponent = component;
}
bool VstAudioClient::handleEvent(const mu::midi::Event& e)
{
if (m_eventList.addEvent(vstFromMidi(e)) == Steinberg::kResultTrue) {
return true;
}
return false;
}
void VstAudioClient::process(float* output, unsigned int samples)
{
IAudioProcessorPtr processor = m_pluginComponent;
if (!processor || !output) {
return;
}
m_processData.numSamples = samples;
if (processor->process(m_processData) != Steinberg::kResultOk) {
return;
}
m_eventList.clear();
fillOutputBuffer(samples, output);
}
void VstAudioClient::setBlockSize(unsigned int samples)
{
m_samplesInfo.samplesPerBlock = samples;
updateProcessSetup();
}
void VstAudioClient::setSampleRate(unsigned int sampleRate)
{
m_samplesInfo.sampleRate = sampleRate;
updateProcessSetup();
}
void VstAudioClient::setUpProcessData()
{
m_processContext.sampleRate = m_samplesInfo.sampleRate;
m_processData.inputEvents = &m_eventList;
m_processData.processContext = &m_processContext;
m_processData.prepare(*m_pluginComponent, m_samplesInfo.samplesPerBlock, Steinberg::Vst::kSample32);
}
void VstAudioClient::updateProcessSetup()
{
if (!m_samplesInfo.isValid()) {
return;
}
IAudioProcessorPtr processor = m_pluginComponent;
if (!processor) {
return;
}
VstProcessSetup setup;
setup.processMode = Steinberg::Vst::kRealtime;
setup.symbolicSampleSize = Steinberg::Vst::kSample32;
setup.maxSamplesPerBlock = m_samplesInfo.samplesPerBlock;
setup.sampleRate = m_samplesInfo.sampleRate;
if (processor->setupProcessing(setup) != Steinberg::kResultOk) {
return;
}
processor->setProcessing(true);
m_pluginComponent->setActive(true);
setUpProcessData();
}
void VstAudioClient::fillOutputBuffer(unsigned int samples, float* output)
{
for (unsigned int i = 0; i < samples; ++i) {
for (unsigned int s = 0; s < audio::synth::AUDIO_CHANNELS; ++s) {
auto getFromChannel = std::min<unsigned int>(s, m_processData.outputs[0].numChannels - 1);
output[i * audio::synth::AUDIO_CHANNELS + s] = m_processData.outputs[0].channelBuffers32[getFromChannel][i];
}
}
}
VstEvent VstAudioClient::vstFromMidi(const mu::midi::Event& event)
{
VstEvent result;
if (!event.isValid()) {
return result;
}
result.busIndex = event.group();
result.sampleOffset = 0;
result.ppqPosition = 0;
result.flags = VstEvent::kIsLive;
switch (event.opcode()) {
case midi::Event::Opcode::NoteOn:
result.type = VstEvent::kNoteOnEvent;
result.noteOn.noteId = event.note();
result.noteOn.channel = event.channel();
result.noteOn.pitch = event.pitchNote();
result.noteOn.tuning = event.pitchTuningCents();
result.noteOn.velocity = event.velocityFraction();
break;
case midi::Event::Opcode::NoteOff:
result.type = VstEvent::kNoteOffEvent;
result.noteOff.noteId = event.note();
result.noteOff.channel = event.channel();
result.noteOff.pitch = event.pitchNote();
result.noteOff.tuning = event.pitchTuningCents();
result.noteOff.velocity = event.velocityFraction();
break;
default:
break;
}
return result;
}

View file

@ -0,0 +1,48 @@
#ifndef VSTAUDIOCLIENT_H
#define VSTAUDIOCLIENT_H
#include "vsttypes.h"
namespace mu::vst {
class VstAudioClient
{
public:
VstAudioClient() = default;
~VstAudioClient();
void init(PluginComponentPtr component);
bool handleEvent(const midi::Event& e);
void process(float* output, unsigned int samples);
void setBlockSize(unsigned int samples);
void setSampleRate(unsigned int sampleRate);
private:
struct SamplesInfo {
unsigned int sampleRate = 0;
unsigned int samplesPerBlock = 0;
bool isValid() {
return sampleRate > 0 && samplesPerBlock > 0;
}
};
void setUpProcessData();
void updateProcessSetup();
void fillOutputBuffer(unsigned int samples, float* output);
VstEvent vstFromMidi(const mu::midi::Event& event);
PluginComponentPtr m_pluginComponent = nullptr;
SamplesInfo m_samplesInfo;
VstEventList m_eventList;
VstProcessData m_processData;
VstProcessContext m_processContext;
};
}
#endif // VSTAUDIOCLIENT_H

View file

@ -0,0 +1,163 @@
#include "vstsynthesiser.h"
#include "log.h"
#include "internal/vstplugin.h"
using namespace mu;
using namespace mu::vst;
VstSynthesiser::VstSynthesiser(const VstPluginMeta& meta)
: m_pluginMeta(meta), m_vstAudioClient(std::make_unique<VstAudioClient>())
{
}
Ret VstSynthesiser::init()
{
RetVal plugin = repository()->findPluginById(m_pluginMeta.id);
if (!plugin.ret) {
return plugin.ret;
}
m_pluginPtr = plugin.val;
m_vstAudioClient->init(m_pluginPtr->component());
return make_ret(Ret::Code::Ok);
}
bool VstSynthesiser::isValid() const
{
if (!m_pluginPtr) {
return false;
}
return m_pluginPtr->isValid();
}
bool VstSynthesiser::isActive() const
{
return m_isActive;
}
void VstSynthesiser::setIsActive(bool arg)
{
m_isActive = arg;
}
std::string VstSynthesiser::name() const
{
return m_pluginMeta.name;
}
audio::synth::SoundFontFormats VstSynthesiser::soundFontFormats() const
{
NOT_SUPPORTED;
return { audio::synth::SoundFontFormat::Undefined };
}
Ret VstSynthesiser::addSoundFonts(const std::vector<io::path>& /*sfonts*/)
{
NOT_SUPPORTED;
return Ret(Ret::Code::NotSupported);
}
Ret VstSynthesiser::removeSoundFonts()
{
NOT_SUPPORTED;
return Ret(Ret::Code::NotSupported);
}
bool VstSynthesiser::handleEvent(const midi::Event& e)
{
if (!m_vstAudioClient) {
return false;
}
return m_vstAudioClient->handleEvent(e);
}
void VstSynthesiser::writeBuf(float* stream, unsigned int samples)
{
m_vstAudioClient->process(stream, samples);
}
void VstSynthesiser::allSoundsOff()
{
NOT_IMPLEMENTED;
}
void VstSynthesiser::flushSound()
{
NOT_IMPLEMENTED;
}
Ret VstSynthesiser::setupChannels(const std::vector<midi::Event>& /*events*/)
{
NOT_IMPLEMENTED;
return Ret(Ret::Code::Ok);
}
void VstSynthesiser::channelSoundsOff(midi::channel_t /*chan*/)
{
NOT_IMPLEMENTED;
}
bool VstSynthesiser::channelVolume(midi::channel_t /*chan*/, float /*val*/)
{
NOT_IMPLEMENTED;
return true;
}
bool VstSynthesiser::channelBalance(midi::channel_t /*chan*/, float /*val*/)
{
NOT_IMPLEMENTED;
return true;
}
bool VstSynthesiser::channelPitch(midi::channel_t /*chan*/, int16_t /*val*/)
{
NOT_IMPLEMENTED;
return true;
}
void VstSynthesiser::setSampleRate(unsigned int sampleRate)
{
m_vstAudioClient->setSampleRate(sampleRate);
}
unsigned int VstSynthesiser::streamCount() const
{
return audio::synth::AUDIO_CHANNELS;
}
async::Channel<unsigned int> VstSynthesiser::streamsCountChanged() const
{
return m_streamsCountChanged;
}
void VstSynthesiser::forward(unsigned int sampleCount)
{
writeBuf(m_buffer.data(), sampleCount);
}
const float* VstSynthesiser::data() const
{
return m_buffer.data();
}
void VstSynthesiser::setBufferSize(unsigned int samples)
{
LOGI() << "SET BUFFER SIZE = " << samples;
unsigned int newSize = samples * streamCount();
if (newSize == 0 || m_buffer.size() == newSize) {
return;
}
m_buffer.resize(newSize);
m_vstAudioClient->setBlockSize(samples);
}

View file

@ -0,0 +1,84 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2021 MuseScore BVBA 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 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_VST_VSTSYNTHESISER_H
#define MU_VST_VSTSYNTHESISER_H
#include "audio/isynthesizer.h"
#include "modularity/ioc.h"
#include "vsttypes.h"
#include "ivstpluginrepository.h"
#include "vstaudioclient.h"
namespace mu::vst {
class VstSynthesiser : public audio::synth::ISynthesizer
{
INJECT(vst, IVstPluginRepository, repository)
public:
explicit VstSynthesiser(const VstPluginMeta& meta);
Ret init() override;
bool isValid() const override;
bool isActive() const override;
void setIsActive(bool arg) override;
std::string name() const override;
audio::synth::SoundFontFormats soundFontFormats() const override;
Ret addSoundFonts(const std::vector<io::path>& sfonts) override;
Ret removeSoundFonts() override;
bool handleEvent(const midi::Event& e) override;
void writeBuf(float* stream, unsigned int samples) override;
void allSoundsOff() override;
void flushSound() override;
Ret setupChannels(const std::vector<midi::Event>& events) override;
void channelSoundsOff(midi::channel_t chan) override;
bool channelVolume(midi::channel_t chan, float val) override;
bool channelBalance(midi::channel_t chan, float val) override;
bool channelPitch(midi::channel_t chan, int16_t val) override;
// IAudioSource
void setSampleRate(unsigned int sampleRate) override;
unsigned int streamCount() const override;
async::Channel<unsigned int> streamsCountChanged() const override;
void forward(unsigned int sampleCount) override;
const float* data() const override;
void setBufferSize(unsigned int samples) override;
private:
VstPluginMeta m_pluginMeta;
VstPluginPtr m_pluginPtr = nullptr;
std::unique_ptr<VstAudioClient> m_vstAudioClient = nullptr;
bool m_isActive = false;
std::vector<float> m_buffer;
async::Channel<unsigned int> m_streamsCountChanged;
};
}
#endif // MU_VST_VSTSYNTHESISER_H

View file

@ -101,7 +101,7 @@ VstPluginMeta VstPlugin::meta() const
return result;
}
PluginView VstPlugin::view()
PluginViewPtr VstPlugin::view()
{
if (m_pluginView) {
return m_pluginView;
@ -112,6 +112,17 @@ PluginView VstPlugin::view()
return m_pluginView;
}
PluginComponentPtr VstPlugin::component()
{
if (m_pluginComponent) {
return m_pluginComponent;
}
m_pluginComponent = m_pluginProvider->getComponent();
return m_pluginComponent;
}
bool VstPlugin::isValid() const
{
if (!m_module

View file

@ -35,7 +35,8 @@ public:
PluginId id() const;
VstPluginMeta meta() const;
PluginView view();
PluginViewPtr view();
PluginComponentPtr component();
bool isValid() const;
@ -44,8 +45,9 @@ private:
PluginModulePtr m_module = nullptr;
PluginFactory m_factory;
PluginProviderPtr m_pluginProvider = nullptr;
PluginController m_pluginController = nullptr;
PluginView m_pluginView = nullptr;
PluginControllerPtr m_pluginController = nullptr;
PluginComponentPtr m_pluginComponent = nullptr;
PluginViewPtr m_pluginView = nullptr;
PluginContext m_pluginContext;
};

View file

@ -23,10 +23,14 @@
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "pluginterfaces/gui/iplugview.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "framework/midi/miditypes.h"
namespace mu::vst {
class VstPlugin;
using VstPluginPtr = std::shared_ptr<VstPlugin>;
@ -40,10 +44,11 @@ using PluginContextFactory = Steinberg::Vst::PluginContextFactory;
using PluginContext = Steinberg::Vst::HostApplication;
using PluginProviderPtr = Steinberg::IPtr<Steinberg::Vst::PlugProvider>;
using PluginProvider = Steinberg::Vst::PlugProvider;
using PluginController = Steinberg::IPtr<Steinberg::Vst::IEditController>;
using PluginView = Steinberg::IPtr<Steinberg::IPlugView>;
using PluginControllerPtr = Steinberg::IPtr<Steinberg::Vst::IEditController>;
using PluginComponentPtr = Steinberg::IPtr<Steinberg::Vst::IComponent>;
using PluginViewPtr = Steinberg::IPtr<Steinberg::IPlugView>;
using IAudioProcessorPtr = Steinberg::FUnknownPtr<Steinberg::Vst::IAudioProcessor>;
using FIDString = Steinberg::FIDString;
namespace PluginEditorViewType = Steinberg::Vst::ViewType;
struct VstPluginMeta {
PluginId id;
@ -52,5 +57,13 @@ struct VstPluginMeta {
};
using VstPluginMetaList = std::vector<VstPluginMeta>;
using VstEventList = Steinberg::Vst::EventList;
using VstEvent = Steinberg::Vst::Event;
using VstProcessData = Steinberg::Vst::HostProcessData;
using VstProcessContext = Steinberg::Vst::ProcessContext;
using VstProcessSetup = Steinberg::Vst::ProcessSetup;
namespace PluginEditorViewType = Steinberg::Vst::ViewType;
}
#endif // MU_VST_VSTTYPES_H