mirror of https://github.com/AxioDL/amuse.git
Initial VST plugin implementation
This commit is contained in:
parent
57bb631f9b
commit
fd0dd8922a
|
@ -0,0 +1,8 @@
|
||||||
|
unset(VST_MAIN_SRC CACHE)
|
||||||
|
find_file(VST_MAIN_SRC vst36/vstplugmain.cpp)
|
||||||
|
if (NOT (${VST_MAIN_SRC} STREQUAL "VST_MAIN_SRC-NOTFOUND"))
|
||||||
|
message(STATUS "${VST_MAIN_SRC} Found VST SDK; building plugin")
|
||||||
|
add_library(amuse-vst SHARED
|
||||||
|
VSTBackend.hpp VSTBackend.cpp
|
||||||
|
VSTEditor.hpp VSTEditor.cpp ${VST_MAIN_SRC})
|
||||||
|
endif()
|
|
@ -0,0 +1,258 @@
|
||||||
|
#include "VSTBackend.hpp"
|
||||||
|
#include "audiodev/AudioVoiceEngine.hpp"
|
||||||
|
#include <logvisor/logvisor.hpp>
|
||||||
|
|
||||||
|
struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
|
||||||
|
{
|
||||||
|
std::vector<float> m_interleavedBuf;
|
||||||
|
float** m_outputData = nullptr;
|
||||||
|
size_t m_renderFrames = 0;
|
||||||
|
|
||||||
|
int m_reqGroup = 0;
|
||||||
|
int m_curGroup = 0;
|
||||||
|
std::shared_ptr<amuse::Sequencer> m_curSeq;
|
||||||
|
|
||||||
|
boo::AudioChannelSet _getAvailableSet()
|
||||||
|
{
|
||||||
|
return boo::AudioChannelSet::Stereo;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
boo::ReceiveFunctor* m_midiReceiver = nullptr;
|
||||||
|
|
||||||
|
struct MIDIIn : public boo::IMIDIIn
|
||||||
|
{
|
||||||
|
MIDIIn(bool virt, boo::ReceiveFunctor&& receiver)
|
||||||
|
: IMIDIIn(virt, std::move(receiver)) {}
|
||||||
|
|
||||||
|
std::string description() const
|
||||||
|
{
|
||||||
|
return "VST MIDI";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver)
|
||||||
|
{
|
||||||
|
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
|
||||||
|
m_midiReceiver = &ret->m_receiver;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<boo::IMIDIOut> newVirtualMIDIOut()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<boo::IMIDIInOut> newVirtualMIDIInOut(boo::ReceiveFunctor&& receiver)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<boo::IMIDIIn> newRealMIDIIn(const char* name, boo::ReceiveFunctor&& receiver)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<boo::IMIDIOut> newRealMIDIOut(const char* name)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<boo::IMIDIInOut> newRealMIDIInOut(const char* name, boo::ReceiveFunctor&& receiver)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useMIDILock() const {return false;}
|
||||||
|
|
||||||
|
VSTVoiceEngine()
|
||||||
|
{
|
||||||
|
m_mixInfo.m_periodFrames = 512;
|
||||||
|
m_mixInfo.m_sampleRate = 96000.0;
|
||||||
|
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
|
||||||
|
m_mixInfo.m_bitsPerSample = 32;
|
||||||
|
_buildAudioRenderClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _buildAudioRenderClient()
|
||||||
|
{
|
||||||
|
m_mixInfo.m_channels = _getAvailableSet();
|
||||||
|
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
|
||||||
|
|
||||||
|
m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
|
||||||
|
|
||||||
|
boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap;
|
||||||
|
chMapOut.m_channelCount = 2;
|
||||||
|
chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft;
|
||||||
|
chMapOut.m_channels[1] = boo::AudioChannel::FrontRight;
|
||||||
|
|
||||||
|
while (chMapOut.m_channelCount < chCount)
|
||||||
|
chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames)
|
||||||
|
{
|
||||||
|
m_mixInfo.m_periodFrames = periodFrames;
|
||||||
|
m_mixInfo.m_sampleRate = sampleRate;
|
||||||
|
_buildAudioRenderClient();
|
||||||
|
|
||||||
|
for (boo::AudioVoice* vox : m_activeVoices)
|
||||||
|
vox->_resetSampleRate(vox->m_sampleRateIn);
|
||||||
|
for (boo::AudioSubmix* smx : m_activeSubmixes)
|
||||||
|
smx->_resetOutputSampleRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void pumpAndMixVoices()
|
||||||
|
{
|
||||||
|
_pumpAndMixVoices(m_renderFrames, m_interleavedBuf.data());
|
||||||
|
|
||||||
|
for (size_t i=0 ; i<2 ; ++i)
|
||||||
|
{
|
||||||
|
float* bufOut = m_outputData[i];
|
||||||
|
for (size_t f=0 ; f<m_renderFrames ; ++f)
|
||||||
|
bufOut[f] = m_interleavedBuf[f*2+i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double getCurrentSampleRate() const {return m_mixInfo.m_sampleRate;}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace amuse
|
||||||
|
{
|
||||||
|
|
||||||
|
#define kBackendID CCONST ('a','m','u','s')
|
||||||
|
|
||||||
|
static logvisor::Module Log("amuse::AudioUnitBackend");
|
||||||
|
|
||||||
|
VSTBackend::VSTBackend(audioMasterCallback cb)
|
||||||
|
: AudioEffectX(cb, 0, 0), m_editor(*this)
|
||||||
|
{
|
||||||
|
setUniqueID(kBackendID);
|
||||||
|
setNumInputs(0);
|
||||||
|
setNumOutputs(2);
|
||||||
|
canProcessReplacing();
|
||||||
|
setEditor(&m_editor);
|
||||||
|
|
||||||
|
m_booBackend = std::make_unique<VSTVoiceEngine>();
|
||||||
|
m_voxAlloc.emplace(*m_booBackend);
|
||||||
|
m_engine.emplace(*m_voxAlloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
AEffEditor* VSTBackend::getEditor()
|
||||||
|
{
|
||||||
|
return &m_editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
VstInt32 VSTBackend::processEvents(VstEvents* events)
|
||||||
|
{
|
||||||
|
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
||||||
|
|
||||||
|
if (engine.m_midiReceiver)
|
||||||
|
{
|
||||||
|
for (VstInt32 i=0 ; i<events->numEvents ; ++i)
|
||||||
|
{
|
||||||
|
VstMidiEvent* evt = reinterpret_cast<VstMidiEvent*>(events->events[i]);
|
||||||
|
if (evt->type == kVstMidiType)
|
||||||
|
{
|
||||||
|
(*engine.m_midiReceiver)(std::vector<uint8_t>(std::cbegin(evt->midiData),
|
||||||
|
std::cbegin(evt->midiData) + evt->byteSize),
|
||||||
|
(m_curFrame + evt->deltaFrames) / sampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTBackend::processReplacing(float**, float** outputs, VstInt32 sampleFrames)
|
||||||
|
{
|
||||||
|
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
||||||
|
|
||||||
|
/* Handle group load request */
|
||||||
|
int reqGroup = engine.m_reqGroup;
|
||||||
|
if (engine.m_curGroup != reqGroup)
|
||||||
|
{
|
||||||
|
engine.m_curGroup = reqGroup;
|
||||||
|
if (engine.m_curSeq)
|
||||||
|
engine.m_curSeq->kill();
|
||||||
|
engine.m_curSeq = m_engine->seqPlay(reqGroup, -1, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output buffers */
|
||||||
|
engine.m_renderFrames = sampleFrames;
|
||||||
|
engine.m_outputData = outputs;
|
||||||
|
m_engine->pumpEngine();
|
||||||
|
|
||||||
|
m_curFrame += sampleFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
VstInt32 VSTBackend::canDo(char* text)
|
||||||
|
{
|
||||||
|
VstInt32 returnCode = 0;
|
||||||
|
|
||||||
|
if (!strcmp(text, "receiveVstEvents"))
|
||||||
|
returnCode = 1;
|
||||||
|
else if (!strcmp(text, "receiveVstMidiEvent"))
|
||||||
|
returnCode = 1;
|
||||||
|
|
||||||
|
return returnCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
VstPlugCategory VSTBackend::getPlugCategory()
|
||||||
|
{
|
||||||
|
return kPlugCategSynth;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSTBackend::getProductString(char* text)
|
||||||
|
{
|
||||||
|
strcpy(text, "Amuse");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSTBackend::getVendorString(char* text)
|
||||||
|
{
|
||||||
|
strcpy(text, "AxioDL");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSTBackend::getOutputProperties(VstInt32 index, VstPinProperties* properties)
|
||||||
|
{
|
||||||
|
bool returnCode = false;
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
strcpy(properties->label, "Amuse Out");
|
||||||
|
properties->flags = kVstPinIsStereo | kVstPinIsActive;
|
||||||
|
returnCode = true;
|
||||||
|
}
|
||||||
|
return returnCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
VstInt32 VSTBackend::getNumMidiInputChannels()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTBackend::setSampleRate(float sampleRate)
|
||||||
|
{
|
||||||
|
AudioEffectX::setSampleRate(sampleRate);
|
||||||
|
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
||||||
|
engine._rebuildAudioRenderClient(sampleRate, engine.mixInfo().m_periodFrames);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTBackend::setBlockSize(VstInt32 blockSize)
|
||||||
|
{
|
||||||
|
AudioEffectX::setBlockSize(blockSize);
|
||||||
|
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
||||||
|
engine._rebuildAudioRenderClient(engine.mixInfo().m_sampleRate, blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioEffect* createEffectInstance(audioMasterCallback audioMaster)
|
||||||
|
{
|
||||||
|
return new VSTBackend(audioMaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef __AMUSE_VSTBACKEND_HPP__
|
||||||
|
#define __AMUSE_VSTBACKEND_HPP__
|
||||||
|
|
||||||
|
#include <vst36/audioeffectx.h>
|
||||||
|
#include "VSTEditor.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include "optional.hpp"
|
||||||
|
|
||||||
|
#include "amuse/BooBackend.hpp"
|
||||||
|
#include "amuse/Engine.hpp"
|
||||||
|
#include "amuse/IBackendVoice.hpp"
|
||||||
|
#include "amuse/IBackendSubmix.hpp"
|
||||||
|
#include "amuse/IBackendVoiceAllocator.hpp"
|
||||||
|
|
||||||
|
namespace amuse
|
||||||
|
{
|
||||||
|
class VSTBackend;
|
||||||
|
|
||||||
|
/** Backend voice allocator implementation for AudioUnit mixer */
|
||||||
|
class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
||||||
|
: BooBackendVoiceAllocator(booEngine) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Actual plugin implementation class */
|
||||||
|
class VSTBackend : public AudioEffectX
|
||||||
|
{
|
||||||
|
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
|
||||||
|
std::experimental::optional<amuse::VSTBackendVoiceAllocator> m_voxAlloc;
|
||||||
|
std::experimental::optional<amuse::Engine> m_engine;
|
||||||
|
size_t m_curFrame = 0;
|
||||||
|
VSTEditor m_editor;
|
||||||
|
public:
|
||||||
|
VSTBackend(audioMasterCallback cb);
|
||||||
|
AEffEditor* getEditor();
|
||||||
|
VstInt32 processEvents(VstEvents* events);
|
||||||
|
void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames);
|
||||||
|
VstInt32 canDo(char* text);
|
||||||
|
VstPlugCategory getPlugCategory();
|
||||||
|
bool getProductString(char* text);
|
||||||
|
bool getVendorString(char* text);
|
||||||
|
bool getOutputProperties(VstInt32 index, VstPinProperties* properties);
|
||||||
|
VstInt32 getNumMidiInputChannels();
|
||||||
|
void setSampleRate(float sampleRate);
|
||||||
|
void setBlockSize(VstInt32 blockSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __AMUSE_VSTBACKEND_HPP__
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "VSTEditor.hpp"
|
||||||
|
#include "VSTBackend.hpp"
|
||||||
|
|
||||||
|
namespace amuse
|
||||||
|
{
|
||||||
|
|
||||||
|
VSTEditor::VSTEditor(VSTBackend& backend)
|
||||||
|
: AEffEditor(&backend), m_backend(backend)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSTEditor::getRect(ERect** rect)
|
||||||
|
{
|
||||||
|
*rect = &m_windowRect;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSTEditor::open(void* ptr)
|
||||||
|
{
|
||||||
|
AEffEditor::open(ptr);
|
||||||
|
#if _WIN32
|
||||||
|
#elif __APPLE__
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef __AMUSE_VSTEDITOR_HPP__
|
||||||
|
#define __AMUSE_VSTEDITOR_HPP__
|
||||||
|
|
||||||
|
#include <vst36/aeffeditor.h>
|
||||||
|
|
||||||
|
namespace amuse
|
||||||
|
{
|
||||||
|
class VSTBackend;
|
||||||
|
|
||||||
|
/** Editor UI class */
|
||||||
|
class VSTEditor : public AEffEditor
|
||||||
|
{
|
||||||
|
VSTBackend& m_backend;
|
||||||
|
ERect m_windowRect = {10, 10, 410, 520};
|
||||||
|
public:
|
||||||
|
VSTEditor(VSTBackend& backend);
|
||||||
|
|
||||||
|
bool getRect(ERect** rect);
|
||||||
|
bool open(void* ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __AMUSE_VSTEDITOR_HPP__
|
Loading…
Reference in New Issue