sfizz/plugins/vst/SfizzVstProcessor.cpp

795 lines
26 KiB
C++
Raw Normal View History

2020-03-05 11:16:47 +01:00
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SfizzVstProcessor.h"
#include "SfizzVstController.h"
#include "SfizzVstState.h"
2021-02-04 02:48:49 +01:00
#include "SfizzVstParameters.h"
2020-10-29 10:24:00 +01:00
#include "SfizzFileScan.h"
2021-03-07 17:40:05 +01:00
#include "plugin/ForeignInstrument.h"
2020-03-05 11:16:47 +01:00
#include "base/source/fstreamer.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
2020-10-29 10:24:00 +01:00
#include <ghc/fs_std.hpp>
2020-03-05 11:16:47 +01:00
#include <cstring>
2020-03-12 23:43:57 +01:00
template<class T>
constexpr int fastRound(T x)
{
return static_cast<int>(x + T{ 0.5 }); // NOLINT
}
2020-03-05 11:16:47 +01:00
2020-06-20 16:29:50 +02:00
static const char defaultSfzText[] =
"<region>sample=*sine" "\n"
"ampeg_attack=0.02 ampeg_release=0.1" "\n";
2020-10-19 02:58:01 +02:00
enum {
kMidiEventMaximumSize = 4,
kOscTempSize = 8192,
};
static const char* kRingIdMidi = "Mid";
static const char* kRingIdOsc = "Osc";
2020-10-14 20:04:59 +02:00
2021-03-01 06:17:58 +01:00
static const char* kMsgIdSetNumVoices = "SetNumVoices";
static const char* kMsgIdSetOversampling = "SetOversampling";
static const char* kMsgIdSetPreloadSize = "SetPreloadSize";
static const char* kMsgIdCheckShouldReload = "CheckShouldReload";
static const char* kMsgIdNotifyPlayState = "NotifyPlayState";
static const char* kMsgIdReceiveMessage = "ReceiveMessage";
static const char* kMsgIdNoteEvents = "NoteEvents";
2020-03-05 11:16:47 +01:00
SfizzVstProcessor::SfizzVstProcessor()
2020-10-19 02:58:01 +02:00
: _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024),
_oscTemp(new uint8_t[kOscTempSize])
2020-03-05 11:16:47 +01:00
{
setControllerClass(SfizzVstController::cid);
2020-10-29 10:24:00 +01:00
// ensure the SFZ path exists:
// the one specified in the configuration, otherwise the fallback
absl::optional<fs::path> configDefaultPath = SfizzPaths::getSfzConfigDefaultPath();
if (configDefaultPath) {
std::error_code ec;
fs::create_directory(*configDefaultPath, ec);
}
else {
fs::path fallbackDefaultPath = SfizzPaths::getSfzFallbackDefaultPath();
std::error_code ec;
fs::create_directory(fallbackDefaultPath, ec);
}
2020-03-05 11:16:47 +01:00
}
SfizzVstProcessor::~SfizzVstProcessor()
{
2020-03-12 23:43:57 +01:00
try {
stopBackgroundWork();
} catch (const std::exception& e) {
fprintf(stderr, "Caught exception: %s\n", e.what());
}
2020-03-05 11:16:47 +01:00
}
tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
{
tresult result = AudioEffect::initialize(context);
if (result != kResultTrue)
return result;
addAudioOutput(STR16("Audio Output"), Vst::SpeakerArr::kStereo);
addEventInput(STR16("Event Input"), 1);
2020-03-05 18:21:46 +01:00
_state = SfizzVstState();
fprintf(stderr, "[sfizz] new synth\n");
_synth.reset(new sfz::Sfizz);
2020-10-19 02:58:01 +02:00
auto onMessage = +[](void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
auto *self = reinterpret_cast<SfizzVstProcessor*>(data);
self->receiveMessage(delay, path, sig, args);
};
_client = _synth->createClient(this);
_synth->setReceiveCallback(*_client, onMessage);
_synth->setBroadcastCallback(onMessage, this);
2020-06-20 05:49:00 +02:00
_currentStretchedTuning = 0.0;
2020-06-20 13:01:38 +02:00
loadSfzFileOrDefault(*_synth, {});
2020-08-11 15:31:23 +02:00
_synth->tempo(0, 0.5);
_timeSigNumerator = 4;
_timeSigDenominator = 4;
_synth->timeSignature(0, _timeSigNumerator, _timeSigDenominator);
_synth->timePosition(0, 0, 0);
_synth->playbackState(0, 0);
2021-02-23 09:27:20 +01:00
_noteEventsCurrentCycle.fill(-1.0f);
2020-03-05 11:16:47 +01:00
return result;
}
tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts)
{
bool isStereo = numIns == 0 && numOuts == 1 && outputs[0] == Vst::SpeakerArr::kStereo;
if (!isStereo)
return kResultFalse;
return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts);
}
2020-03-05 18:21:46 +01:00
tresult PLUGIN_API SfizzVstProcessor::setState(IBStream* stream)
2020-03-05 11:16:47 +01:00
{
SfizzVstState s;
2020-03-05 18:21:46 +01:00
tresult r = s.load(stream);
2020-03-05 11:16:47 +01:00
if (r != kResultTrue)
return r;
2020-10-29 10:24:00 +01:00
// check the files to really exist, otherwise search them
for (std::string* statePath : { &s.sfzFile, &s.scalaFile }) {
if (statePath->empty())
continue;
fs::path pathOrig = fs::u8path(*statePath);
std::error_code ec;
if (fs::is_regular_file(pathOrig, ec))
continue;
fprintf(stderr, "[Sfizz] searching for missing file: %s\n", pathOrig.filename().u8string().c_str());
SfzFileScan& fileScan = SfzFileScan::getInstance();
fs::path pathFound;
if (!fileScan.locateRealFile(pathOrig, pathFound))
fprintf(stderr, "[Sfizz] file not found: %s\n", pathOrig.filename().u8string().c_str());
else {
2020-12-09 17:08:27 +01:00
fprintf(stderr, "[Sfizz] file found: %s\n", pathFound.u8string().c_str());
2020-10-29 10:24:00 +01:00
*statePath = pathFound.u8string();
}
}
//
2021-02-01 22:51:24 +01:00
std::lock_guard<SpinMutex> lock(_processMutex);
2020-03-05 18:21:46 +01:00
_state = s;
syncStateToSynth();
2020-03-05 11:16:47 +01:00
return r;
}
2020-03-05 18:21:46 +01:00
tresult PLUGIN_API SfizzVstProcessor::getState(IBStream* stream)
2020-03-05 11:16:47 +01:00
{
2021-02-01 22:51:24 +01:00
std::lock_guard<SpinMutex> lock(_processMutex);
2020-03-05 18:21:46 +01:00
return _state.store(stream);
}
2020-03-05 11:16:47 +01:00
2020-03-05 18:21:46 +01:00
void SfizzVstProcessor::syncStateToSynth()
{
sfz::Sfizz* synth = _synth.get();
if (!synth)
return;
2020-06-20 13:01:38 +02:00
loadSfzFileOrDefault(*synth, _state.sfzFile);
2020-03-05 18:21:46 +01:00
synth->setVolume(_state.volume);
2020-03-06 11:46:49 +01:00
synth->setNumVoices(_state.numVoices);
2020-03-06 13:15:32 +01:00
synth->setOversamplingFactor(1 << _state.oversamplingLog2);
synth->setPreloadSize(_state.preloadSize);
2020-06-20 05:49:00 +02:00
synth->loadScalaFile(_state.scalaFile);
synth->setScalaRootKey(_state.scalaRootKey);
synth->setTuningFrequency(_state.tuningFrequency);
synth->loadStretchTuningByRatio(_state.stretchedTuning);
2020-03-05 11:16:47 +01:00
}
tresult PLUGIN_API SfizzVstProcessor::canProcessSampleSize(int32 symbolicSampleSize)
{
if (symbolicSampleSize != Vst::kSample32)
return kResultFalse;
return kResultTrue;
}
tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
{
sfz::Sfizz* synth = _synth.get();
2020-03-05 11:16:47 +01:00
if (bool(state) == _isActive)
return kResultTrue;
2020-03-15 14:04:22 +01:00
if (!synth)
return kResultFalse;
if (state) {
2020-03-15 23:24:07 +01:00
synth->setSampleRate(processSetup.sampleRate);
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
2020-03-05 11:16:47 +01:00
2020-08-12 19:23:50 +02:00
_fileChangePeriod = static_cast<uint32>(1.0 * processSetup.sampleRate);
_playStateChangePeriod = static_cast<uint32>(50e-3 * processSetup.sampleRate);
startBackgroundWork();
} else {
stopBackgroundWork();
synth->allSoundOff();
2020-03-05 11:16:47 +01:00
}
_isActive = bool(state);
2020-03-05 11:16:47 +01:00
return kResultTrue;
}
tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
{
sfz::Sfizz& synth = *_synth;
2020-08-11 15:31:23 +02:00
if (data.processContext)
updateTimeInfo(*data.processContext);
if (Vst::IParameterChanges* pc = data.inputParameterChanges)
2020-03-05 18:21:46 +01:00
processParameterChanges(*pc);
2020-03-05 11:16:47 +01:00
if (data.numOutputs < 1) // flush mode
return kResultTrue;
uint32 numFrames = data.numSamples;
constexpr uint32 numChannels = 2;
float* outputs[numChannels];
assert(numChannels == data.outputs[0].numChannels);
for (unsigned c = 0; c < numChannels; ++c)
outputs[c] = data.outputs[0].channelBuffers32[c];
2021-02-01 22:51:24 +01:00
std::unique_lock<SpinMutex> lock(_processMutex, std::try_to_lock);
2020-03-05 18:21:46 +01:00
2020-03-05 11:16:47 +01:00
if (!lock.owns_lock()) {
for (unsigned c = 0; c < numChannels; ++c)
std::memset(outputs[c], 0, numFrames * sizeof(float));
data.outputs[0].silenceFlags = 3;
return kResultTrue;
}
2020-03-06 13:37:12 +01:00
if (data.processMode == Vst::kOffline)
synth.enableFreeWheeling();
else
synth.disableFreeWheeling();
2020-10-19 02:58:01 +02:00
processMessagesFromUi();
2020-10-14 20:04:59 +02:00
2020-03-05 18:21:46 +01:00
if (Vst::IParameterChanges* pc = data.inputParameterChanges)
processControllerChanges(*pc);
2020-03-05 11:16:47 +01:00
2020-03-05 18:21:46 +01:00
if (Vst::IEventList* events = data.inputEvents)
processEvents(*events);
synth.setVolume(_state.volume);
2020-06-20 05:49:00 +02:00
synth.setScalaRootKey(_state.scalaRootKey);
synth.setTuningFrequency(_state.tuningFrequency);
if (_currentStretchedTuning != _state.stretchedTuning) {
synth.loadStretchTuningByRatio(_state.stretchedTuning);
_currentStretchedTuning = _state.stretchedTuning;
}
2020-03-05 18:21:46 +01:00
synth.renderBlock(outputs, numFrames, numChannels);
_fileChangeCounter += numFrames;
if (_fileChangeCounter > _fileChangePeriod) {
_fileChangeCounter %= _fileChangePeriod;
2021-03-01 06:17:58 +01:00
if (writeWorkerMessage(kMsgIdCheckShouldReload, nullptr, 0))
_semaToWorker.post();
}
2020-08-12 19:23:50 +02:00
_playStateChangeCounter += numFrames;
if (_playStateChangeCounter > _playStateChangePeriod) {
_playStateChangeCounter %= _playStateChangePeriod;
SfizzPlayState playState {};
2020-08-12 19:23:50 +02:00
playState.curves = synth.getNumCurves();
playState.masters = synth.getNumMasters();
playState.groups = synth.getNumGroups();
playState.regions = synth.getNumRegions();
playState.preloadedSamples = synth.getNumPreloadedSamples();
playState.activeVoices = synth.getNumActiveVoices();
2021-03-01 06:17:58 +01:00
if (writeWorkerMessage(kMsgIdNotifyPlayState, &playState, sizeof(playState)))
2020-08-12 19:23:50 +02:00
_semaToWorker.post();
}
2021-02-23 09:27:20 +01:00
//
std::pair<uint32, float> noteEvents[128];
size_t numNoteEvents = 0;
for (uint32 key = 0; key < 128; ++key) {
float value = _noteEventsCurrentCycle[key];
if (value < 0.0f)
continue;
noteEvents[numNoteEvents++] = std::make_pair(key, value);
_noteEventsCurrentCycle[key] = -1.0f;
}
if (numNoteEvents > 0) {
2021-03-01 06:17:58 +01:00
if (writeWorkerMessage(kMsgIdNoteEvents, noteEvents, numNoteEvents * sizeof(noteEvents[0])))
2021-02-23 09:27:20 +01:00
_semaToWorker.post();
}
2020-03-05 18:21:46 +01:00
return kResultTrue;
}
2020-08-11 15:31:23 +02:00
void SfizzVstProcessor::updateTimeInfo(const Vst::ProcessContext& context)
{
sfz::Sfizz& synth = *_synth;
if (context.state & context.kTempoValid)
synth.tempo(0, 60.0f / context.tempo);
if (context.state & context.kTimeSigValid) {
_timeSigNumerator = context.timeSigNumerator;
_timeSigDenominator = context.timeSigDenominator;
synth.timeSignature(0, _timeSigNumerator, _timeSigDenominator);
}
if (context.state & context.kProjectTimeMusicValid) {
double beats = context.projectTimeMusic * 0.25 * _timeSigDenominator;
double bars = beats / _timeSigNumerator;
beats -= int(bars) * _timeSigNumerator;
2020-08-14 04:18:58 +02:00
synth.timePosition(0, int(bars), beats);
2020-08-11 15:31:23 +02:00
}
2020-08-11 16:20:55 +02:00
synth.playbackState(0, (context.state & context.kPlaying) != 0);
2020-08-11 15:31:23 +02:00
}
2020-03-05 18:21:46 +01:00
void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc)
{
uint32 paramCount = pc.getParameterCount();
for (uint32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
Vst::IParamValueQueue* vq = pc.getParameterData(paramIndex);
if (!vq)
continue;
2021-02-04 02:48:49 +01:00
const Vst::ParamID id = vq->getParameterId();
const SfizzRange range = SfizzRange::getForParameter(id);
2020-03-05 18:21:46 +01:00
uint32 pointCount = vq->getPointCount();
int32 sampleOffset;
Vst::ParamValue value;
switch (id) {
case kPidVolume:
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
2021-02-04 02:48:49 +01:00
_state.volume = range.denormalize(value);
2020-03-05 18:21:46 +01:00
break;
2020-03-06 11:46:49 +01:00
case kPidNumVoices:
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
2021-02-04 02:48:49 +01:00
int32 data = static_cast<int32>(range.denormalize(value));
2020-03-28 17:00:07 +01:00
_state.numVoices = data;
2021-03-01 06:17:58 +01:00
if (writeWorkerMessage(kMsgIdSetNumVoices, &data, sizeof(data)))
2020-03-28 17:00:07 +01:00
_semaToWorker.post();
2020-03-06 11:46:49 +01:00
}
break;
2020-03-06 11:55:52 +01:00
case kPidOversampling:
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
2021-02-04 02:48:49 +01:00
int32 data = static_cast<int32>(range.denormalize(value));
2020-03-28 17:00:07 +01:00
_state.oversamplingLog2 = data;
2021-03-01 06:17:58 +01:00
if (writeWorkerMessage(kMsgIdSetOversampling, &data, sizeof(data)))
2020-03-28 17:00:07 +01:00
_semaToWorker.post();
2020-03-06 11:55:52 +01:00
}
break;
2020-03-06 13:15:32 +01:00
case kPidPreloadSize:
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
2021-02-04 02:48:49 +01:00
int32 data = static_cast<int32>(range.denormalize(value));
2020-03-28 17:00:07 +01:00
_state.preloadSize = data;
2021-03-01 06:17:58 +01:00
if (writeWorkerMessage(kMsgIdSetPreloadSize, &data, sizeof(data)))
2020-03-28 17:00:07 +01:00
_semaToWorker.post();
2020-03-06 13:15:32 +01:00
}
break;
2020-06-20 05:49:00 +02:00
case kPidScalaRootKey:
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
2021-02-04 02:48:49 +01:00
_state.scalaRootKey = static_cast<int32>(range.denormalize(value));
2020-06-20 05:49:00 +02:00
break;
case kPidTuningFrequency:
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
2021-02-04 02:48:49 +01:00
_state.tuningFrequency = range.denormalize(value);
2020-06-20 05:49:00 +02:00
break;
case kPidStretchedTuning:
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
2021-02-04 02:48:49 +01:00
_state.stretchedTuning = range.denormalize(value);
2020-06-20 05:49:00 +02:00
break;
2020-03-05 18:21:46 +01:00
}
}
}
2020-03-05 11:16:47 +01:00
2020-03-05 18:21:46 +01:00
void SfizzVstProcessor::processControllerChanges(Vst::IParameterChanges& pc)
{
sfz::Sfizz& synth = *_synth;
uint32 paramCount = pc.getParameterCount();
for (uint32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
Vst::IParamValueQueue* vq = pc.getParameterData(paramIndex);
if (!vq)
continue;
Vst::ParamID id = vq->getParameterId();
uint32 pointCount = vq->getPointCount();
int32 sampleOffset;
Vst::ParamValue value;
switch (id) {
default:
2021-02-08 20:14:07 +01:00
if (id >= kPidCC0 && id <= kPidCCLast) {
auto ccNumber = static_cast<int>(id - kPidCC0);
2020-03-05 18:21:46 +01:00
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
2020-03-05 11:16:47 +01:00
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
2021-02-08 20:15:48 +01:00
synth.hdcc(sampleOffset, ccNumber, value);
2020-03-05 11:16:47 +01:00
}
}
2020-03-05 18:21:46 +01:00
break;
2021-02-08 20:14:07 +01:00
case kPidAftertouch:
2020-03-05 18:21:46 +01:00
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
2020-03-12 23:43:57 +01:00
synth.aftertouch(sampleOffset, fastRound(value * 127.0));
2020-03-05 18:21:46 +01:00
}
break;
2021-02-08 20:14:07 +01:00
case kPidPitchBend:
2020-03-05 18:21:46 +01:00
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
2020-03-12 23:43:57 +01:00
synth.pitchWheel(sampleOffset, fastRound(value * 16383) - 8192);
2020-03-05 18:21:46 +01:00
}
break;
2020-03-05 11:16:47 +01:00
}
}
2020-03-05 18:21:46 +01:00
}
2020-03-05 11:16:47 +01:00
2020-03-05 18:21:46 +01:00
void SfizzVstProcessor::processEvents(Vst::IEventList& events)
{
sfz::Sfizz& synth = *_synth;
uint32 numEvents = events.getEventCount();
for (uint32 i = 0; i < numEvents; i++) {
Vst::Event e;
if (events.getEvent(i, e) != kResultTrue)
continue;
switch (e.type) {
2021-02-23 09:27:20 +01:00
case Vst::Event::kNoteOnEvent: {
int pitch = e.noteOn.pitch;
if (pitch < 0 || pitch >= 128)
break;
if (e.noteOn.velocity <= 0.0f) {
synth.noteOff(e.sampleOffset, pitch, 0);
_noteEventsCurrentCycle[pitch] = 0.0f;
}
else {
synth.noteOn(e.sampleOffset, pitch, convertVelocityFromFloat(e.noteOn.velocity));
_noteEventsCurrentCycle[pitch] = e.noteOn.velocity;
}
2020-03-05 18:21:46 +01:00
break;
2021-02-23 09:27:20 +01:00
}
case Vst::Event::kNoteOffEvent: {
int pitch = e.noteOn.pitch;
if (pitch < 0 || pitch >= 128)
break;
synth.noteOff(e.sampleOffset, pitch, convertVelocityFromFloat(e.noteOff.velocity));
_noteEventsCurrentCycle[pitch] = 0.0f;
2020-03-05 18:21:46 +01:00
break;
2021-02-23 09:27:20 +01:00
}
2020-03-05 18:21:46 +01:00
// case Vst::Event::kPolyPressureEvent:
// synth.aftertouch(e.sampleOffset, convertVelocityFromFloat(e.polyPressure.pressure));
// break;
2020-03-05 11:16:47 +01:00
}
}
2020-03-05 18:21:46 +01:00
}
2020-03-05 11:16:47 +01:00
2020-10-19 02:58:01 +02:00
void SfizzVstProcessor::processMessagesFromUi()
2020-10-14 20:04:59 +02:00
{
sfz::Sfizz& synth = *_synth;
2020-10-19 02:58:01 +02:00
sfz::Client& client = *_client;
Ring_Buffer& fifo = _fifoMessageFromUi;
RTMessage header;
2020-10-14 20:04:59 +02:00
2020-10-19 02:58:01 +02:00
while (fifo.peek(header) && fifo.size_used() >= sizeof(header) + header.size) {
fifo.discard(sizeof(header));
2020-10-14 20:04:59 +02:00
2020-10-19 02:58:01 +02:00
if (header.type == kRingIdMidi) {
if (header.size > kMidiEventMaximumSize) {
fifo.discard(header.size);
continue;
}
uint8_t data[kMidiEventMaximumSize] = {};
fifo.get(data, header.size);
// interpret the MIDI message
switch (data[0] & 0xf0) {
case 0x80:
synth.noteOff(0, data[1] & 0x7f, data[2] & 0x7f);
break;
case 0x90:
synth.noteOn(0, data[1] & 0x7f, data[2] & 0x7f);
break;
case 0xb0:
synth.cc(0, data[1] & 0x7f, data[2] & 0x7f);
break;
case 0xe0:
synth.pitchWheel(0, (data[2] << 7) + data[1] - 8192);
break;
}
2020-10-14 20:04:59 +02:00
}
2020-10-19 02:58:01 +02:00
else if (header.type == kRingIdOsc) {
uint8_t* oscTemp = _oscTemp.get();
2020-10-14 20:04:59 +02:00
2020-10-19 02:58:01 +02:00
if (header.size > kOscTempSize) {
fifo.discard(header.size);
continue;
}
2020-10-14 20:04:59 +02:00
2020-10-19 02:58:01 +02:00
fifo.get(oscTemp, header.size);
const char* path;
const char* sig;
const sfizz_arg_t* args;
uint8_t buffer[1024];
if (sfizz_extract_message(oscTemp, header.size, buffer, sizeof(buffer), &path, &sig, &args) > 0)
synth.sendMessage(client, 0, path, sig, args);
}
else {
assert(false);
return;
2020-10-14 20:04:59 +02:00
}
}
}
2020-03-05 18:21:46 +01:00
int SfizzVstProcessor::convertVelocityFromFloat(float x)
{
return std::min(127, std::max(0, (int)(x * 127.0f)));
2020-03-05 11:16:47 +01:00
}
tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
{
2020-03-28 17:00:07 +01:00
// Note(jpc) this notification is not necessarily handled by the RT thread
2020-03-05 11:16:47 +01:00
tresult result = AudioEffect::notify(message);
if (result != kResultFalse)
return result;
2020-03-28 17:00:07 +01:00
const char* id = message->getMessageID();
Vst::IAttributeList* attr = message->getAttributes();
2020-03-05 11:16:47 +01:00
2020-03-28 17:00:07 +01:00
if (!std::strcmp(id, "LoadSfz")) {
const void* data = nullptr;
uint32 size = 0;
result = attr->getBinary("File", data, size);
2020-03-05 11:16:47 +01:00
2020-03-28 17:00:07 +01:00
if (result != kResultTrue)
return result;
2021-02-01 22:51:24 +01:00
std::unique_lock<SpinMutex> lock(_processMutex);
2020-03-28 17:00:07 +01:00
_state.sfzFile.assign(static_cast<const char *>(data), size);
2020-06-20 13:01:38 +02:00
loadSfzFileOrDefault(*_synth, _state.sfzFile);
2020-06-20 05:49:00 +02:00
lock.unlock();
Steinberg::OPtr<Vst::IMessage> reply { allocateMessage() };
reply->setMessageID("LoadedSfz");
reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size());
sendMessage(reply);
}
else if (!std::strcmp(id, "LoadScala")) {
const void* data = nullptr;
uint32 size = 0;
result = attr->getBinary("File", data, size);
if (result != kResultTrue)
return result;
2021-02-01 22:51:24 +01:00
std::unique_lock<SpinMutex> lock(_processMutex);
2020-06-20 05:49:00 +02:00
_state.scalaFile.assign(static_cast<const char *>(data), size);
_synth->loadScalaFile(_state.scalaFile);
lock.unlock();
Steinberg::OPtr<Vst::IMessage> reply { allocateMessage() };
reply->setMessageID("LoadedScala");
reply->getAttributes()->setBinary("File", _state.scalaFile.data(), _state.scalaFile.size());
sendMessage(reply);
2020-03-28 17:00:07 +01:00
}
2020-10-14 20:04:59 +02:00
else if (!std::strcmp(id, "MidiMessage")) {
const void* data = nullptr;
uint32 size = 0;
result = attr->getBinary("Data", data, size);
2020-10-19 02:58:01 +02:00
if (size < kMidiEventMaximumSize)
writeMessage(_fifoMessageFromUi, kRingIdMidi, data, size);
}
else if (!std::strcmp(id, "OscMessage")) {
const void* data = nullptr;
uint32 size = 0;
result = attr->getBinary("Data", data, size);
writeMessage(_fifoMessageFromUi, kRingIdOsc, data, size);
2020-10-14 20:04:59 +02:00
}
2020-03-28 17:00:07 +01:00
return result;
2020-03-05 11:16:47 +01:00
}
FUnknown* SfizzVstProcessor::createInstance(void*)
{
return static_cast<Vst::IAudioProcessor*>(new SfizzVstProcessor);
}
2020-10-19 02:58:01 +02:00
void SfizzVstProcessor::receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
uint8_t* oscTemp = _oscTemp.get();
2021-02-23 09:27:20 +01:00
uint32 oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args);
if (oscSize <= kOscTempSize) {
2021-03-01 06:17:58 +01:00
if (writeWorkerMessage(kMsgIdReceiveMessage, oscTemp, oscSize))
_semaToWorker.post();
}
2020-10-19 02:58:01 +02:00
}
2020-06-20 13:01:38 +02:00
void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath)
{
2021-03-07 17:40:05 +01:00
if (!filePath.empty()) {
const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance();
const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(filePath);
if (!format)
synth.loadSfzFile(filePath);
else {
auto importer = format->createImporter();
std::string virtualPath = filePath + ".sfz";
std::string sfzText = importer->convertToSfz(filePath);
synth.loadSfzString(virtualPath, sfzText);
}
}
2020-06-20 13:01:38 +02:00
else
2020-06-20 16:29:50 +02:00
synth.loadSfzString("default.sfz", defaultSfzText);
2020-06-20 13:01:38 +02:00
}
2020-03-05 11:16:47 +01:00
void SfizzVstProcessor::doBackgroundWork()
{
for (;;) {
_semaToWorker.wait();
if (!_workRunning)
break;
2020-03-28 17:00:07 +01:00
RTMessagePtr msg = readWorkerMessage();
if (!msg) {
2020-03-05 11:16:47 +01:00
fprintf(stderr, "[Sfizz] message synchronization error in worker\n");
std::abort();
}
2020-03-28 17:00:07 +01:00
const char* id = msg->type;
2020-03-05 11:16:47 +01:00
2021-03-01 06:17:58 +01:00
if (id == kMsgIdSetNumVoices) {
2020-03-28 17:00:07 +01:00
int32 value = *msg->payload<int32>();
2021-02-01 22:51:24 +01:00
std::lock_guard<SpinMutex> lock(_processMutex);
2020-03-28 17:00:07 +01:00
_synth->setNumVoices(value);
2020-03-06 11:46:49 +01:00
}
2021-03-01 06:17:58 +01:00
else if (id == kMsgIdSetOversampling) {
2020-03-28 17:00:07 +01:00
int32 value = *msg->payload<int32>();
2021-02-01 22:51:24 +01:00
std::lock_guard<SpinMutex> lock(_processMutex);
2020-03-28 17:00:07 +01:00
_synth->setOversamplingFactor(1 << value);
2020-03-06 13:15:32 +01:00
}
2021-03-01 06:17:58 +01:00
else if (id == kMsgIdSetPreloadSize) {
2020-03-28 17:00:07 +01:00
int32 value = *msg->payload<int32>();
2021-02-01 22:51:24 +01:00
std::lock_guard<SpinMutex> lock(_processMutex);
2020-03-28 17:00:07 +01:00
_synth->setPreloadSize(value);
2020-03-06 11:55:52 +01:00
}
2021-03-01 06:17:58 +01:00
else if (id == kMsgIdCheckShouldReload) {
if (_synth->shouldReloadFile()) {
2020-06-20 05:49:00 +02:00
fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n");
2021-02-01 22:51:24 +01:00
std::lock_guard<SpinMutex> lock(_processMutex);
2020-06-20 13:01:38 +02:00
loadSfzFileOrDefault(*_synth, _state.sfzFile);
Steinberg::OPtr<Vst::IMessage> reply { allocateMessage() };
reply->setMessageID("LoadedSfz");
reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size());
sendMessage(reply);
}
2020-06-20 05:49:00 +02:00
else if (_synth->shouldReloadScala()) {
fprintf(stderr, "[Sfizz] scala file has changed, reloading\n");
2021-02-01 22:51:24 +01:00
std::lock_guard<SpinMutex> lock(_processMutex);
2020-06-20 05:49:00 +02:00
_synth->loadScalaFile(_state.scalaFile);
}
}
2021-03-01 06:17:58 +01:00
else if (id == kMsgIdNotifyPlayState) {
2020-08-12 19:23:50 +02:00
SfizzPlayState playState = *msg->payload<SfizzPlayState>();
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
notification->setMessageID("NotifiedPlayState");
notification->getAttributes()->setBinary("PlayState", &playState, sizeof(playState));
sendMessage(notification);
}
2021-03-01 06:17:58 +01:00
else if (id == kMsgIdReceiveMessage) {
2020-10-19 02:58:01 +02:00
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
notification->setMessageID("ReceivedMessage");
notification->getAttributes()->setBinary("Message", msg->payload<uint8_t>(), msg->size);
sendMessage(notification);
}
2021-03-01 06:17:58 +01:00
else if (id == kMsgIdNoteEvents) {
2021-02-23 09:27:20 +01:00
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
notification->setMessageID("NoteEvents");
notification->getAttributes()->setBinary("Events", msg->payload<uint8_t>(), msg->size);
sendMessage(notification);
}
2020-03-05 11:16:47 +01:00
}
}
void SfizzVstProcessor::startBackgroundWork()
{
if (_workRunning)
return;
_workRunning = true;
_worker = std::thread([this]() { doBackgroundWork(); });
}
2020-03-05 11:16:47 +01:00
void SfizzVstProcessor::stopBackgroundWork()
{
if (!_workRunning)
return;
_workRunning = false;
_semaToWorker.post();
_worker.join();
while (_semaToWorker.try_wait()) {
2020-03-28 17:00:07 +01:00
if (!discardWorkerMessage()) {
2020-03-05 11:16:47 +01:00
fprintf(stderr, "[Sfizz] message synchronization error in processor\n");
std::abort();
}
}
}
2020-03-28 17:00:07 +01:00
bool SfizzVstProcessor::writeWorkerMessage(const char* type, const void* data, uintptr_t size)
{
2020-10-19 02:58:01 +02:00
return writeMessage(_fifoToWorker, type, data, size);
2020-03-28 17:00:07 +01:00
}
SfizzVstProcessor::RTMessagePtr SfizzVstProcessor::readWorkerMessage()
{
RTMessage header;
if (!_fifoToWorker.peek(header))
return nullptr;
if (_fifoToWorker.size_used() < sizeof(header) + header.size)
return nullptr;
RTMessagePtr msg { reinterpret_cast<RTMessage*>(std::malloc(sizeof(header) + header.size)) };
if (!msg)
throw std::bad_alloc();
msg->type = header.type;
msg->size = header.size;
_fifoToWorker.discard(sizeof(header));
_fifoToWorker.get(const_cast<char*>(msg->payload<char>()), header.size);
return msg;
}
bool SfizzVstProcessor::discardWorkerMessage()
{
RTMessage header;
if (!_fifoToWorker.peek(header))
return false;
if (_fifoToWorker.size_used() < sizeof(header) + header.size)
return false;
_fifoToWorker.discard(sizeof(header) + header.size);
return true;
}
2020-10-19 02:58:01 +02:00
bool SfizzVstProcessor::writeMessage(Ring_Buffer& fifo, const char* type, const void* data, uintptr_t size)
{
RTMessage header;
header.type = type;
header.size = size;
if (fifo.size_free() < sizeof(header) + size)
return false;
fifo.put(header);
fifo.put(static_cast<const uint8*>(data), size);
return true;
}
2020-03-05 11:16:47 +01:00
/*
Note(jpc) Generated at random with uuidgen.
Can't find docs on it... maybe it's to register somewhere?
*/
FUID SfizzVstProcessor::cid(0xe8fab718, 0x15ed46e3, 0x8b598310, 0x1e12993f);