Implemented Vsti synthesiser
This commit is contained in:
parent
769bcb1750
commit
5a508b4cc5
8 changed files with 485 additions and 7 deletions
|
@ -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
|
||||
|
|
153
src/framework/vst/internal/synth/vstaudioclient.cpp
Normal file
153
src/framework/vst/internal/synth/vstaudioclient.cpp
Normal 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;
|
||||
}
|
48
src/framework/vst/internal/synth/vstaudioclient.h
Normal file
48
src/framework/vst/internal/synth/vstaudioclient.h
Normal 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
|
163
src/framework/vst/internal/synth/vstsynthesiser.cpp
Normal file
163
src/framework/vst/internal/synth/vstsynthesiser.cpp
Normal 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);
|
||||
}
|
84
src/framework/vst/internal/synth/vstsynthesiser.h
Normal file
84
src/framework/vst/internal/synth/vstsynthesiser.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue