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 "SfizzVstController.h"
|
|
|
|
|
#include "SfizzVstEditor.h"
|
2021-02-04 02:48:49 +01:00
|
|
|
#include "SfizzVstParameters.h"
|
2020-03-05 11:16:47 +01:00
|
|
|
#include "base/source/fstreamer.h"
|
2021-02-04 02:48:49 +01:00
|
|
|
#include "base/source/updatehandler.h"
|
2020-03-05 11:16:47 +01:00
|
|
|
|
|
|
|
|
tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|
|
|
|
{
|
|
|
|
|
tresult result = EditController::initialize(context);
|
|
|
|
|
if (result != kResultTrue)
|
|
|
|
|
return result;
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
// initialize the update handler
|
|
|
|
|
Steinberg::UpdateHandler::instance();
|
|
|
|
|
|
|
|
|
|
// create update objects
|
|
|
|
|
oscUpdate_ = Steinberg::owned(new OSCUpdate);
|
2021-02-23 09:27:20 +01:00
|
|
|
noteUpdate_ = Steinberg::owned(new NoteUpdate);
|
2021-02-04 02:48:49 +01:00
|
|
|
sfzPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateSfz));
|
|
|
|
|
scalaPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateScala));
|
|
|
|
|
processorStateUpdate_ = Steinberg::owned(new ProcessorStateUpdate);
|
|
|
|
|
playStateUpdate_ = Steinberg::owned(new PlayStateUpdate);
|
|
|
|
|
|
|
|
|
|
// Parameters
|
2020-03-05 11:16:47 +01:00
|
|
|
Vst::ParamID pid = 0;
|
|
|
|
|
|
2020-03-05 18:21:46 +01:00
|
|
|
// Ordinary parameters
|
|
|
|
|
parameters.addParameter(
|
2021-02-04 02:48:49 +01:00
|
|
|
SfizzRange::getForParameter(kPidVolume).createParameter(
|
2020-03-05 18:21:46 +01:00
|
|
|
Steinberg::String("Volume"), pid++, Steinberg::String("dB"),
|
|
|
|
|
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
2020-03-06 11:46:49 +01:00
|
|
|
parameters.addParameter(
|
2021-02-04 02:48:49 +01:00
|
|
|
SfizzRange::getForParameter(kPidNumVoices).createParameter(
|
2020-03-06 11:46:49 +01:00
|
|
|
Steinberg::String("Polyphony"), pid++, nullptr,
|
2020-05-16 21:30:58 +02:00
|
|
|
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
2020-03-06 11:55:52 +01:00
|
|
|
parameters.addParameter(
|
2021-02-04 02:48:49 +01:00
|
|
|
SfizzRange::getForParameter(kPidOversampling).createParameter(
|
2020-03-06 11:55:52 +01:00
|
|
|
Steinberg::String("Oversampling"), pid++, nullptr,
|
2020-05-16 21:30:58 +02:00
|
|
|
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
2020-03-06 13:15:32 +01:00
|
|
|
parameters.addParameter(
|
2021-02-04 02:48:49 +01:00
|
|
|
SfizzRange::getForParameter(kPidPreloadSize).createParameter(
|
2020-03-06 13:15:32 +01:00
|
|
|
Steinberg::String("Preload size"), pid++, nullptr,
|
2020-05-16 21:30:58 +02:00
|
|
|
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
2020-06-20 05:49:00 +02:00
|
|
|
parameters.addParameter(
|
2021-02-04 02:48:49 +01:00
|
|
|
SfizzRange::getForParameter(kPidScalaRootKey).createParameter(
|
2020-06-20 05:49:00 +02:00
|
|
|
Steinberg::String("Scala root key"), pid++, nullptr,
|
|
|
|
|
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
|
|
|
|
parameters.addParameter(
|
2021-02-04 02:48:49 +01:00
|
|
|
SfizzRange::getForParameter(kPidTuningFrequency).createParameter(
|
2020-06-20 05:49:00 +02:00
|
|
|
Steinberg::String("Tuning frequency"), pid++, Steinberg::String("Hz"),
|
|
|
|
|
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
|
|
|
|
parameters.addParameter(
|
2021-02-04 02:48:49 +01:00
|
|
|
SfizzRange::getForParameter(kPidStretchedTuning).createParameter(
|
2020-06-20 05:49:00 +02:00
|
|
|
Steinberg::String("Stretched tuning"), pid++, nullptr,
|
|
|
|
|
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
2020-03-05 18:21:46 +01:00
|
|
|
|
2020-03-06 13:32:41 +01:00
|
|
|
// MIDI special controllers
|
|
|
|
|
parameters.addParameter(Steinberg::String("Aftertouch"), nullptr, 0, 0.5, 0, pid++, Vst::kRootUnitId);
|
2020-06-20 05:49:00 +02:00
|
|
|
parameters.addParameter(Steinberg::String("Pitch bend"), nullptr, 0, 0.5, 0, pid++, Vst::kRootUnitId);
|
2020-03-06 13:32:41 +01:00
|
|
|
|
2020-03-05 11:16:47 +01:00
|
|
|
// MIDI controllers
|
2021-02-08 20:12:48 +01:00
|
|
|
for (unsigned i = 0; i < sfz::config::numCCs; ++i) {
|
2020-03-05 11:16:47 +01:00
|
|
|
Steinberg::String title;
|
|
|
|
|
Steinberg::String shortTitle;
|
|
|
|
|
title.printf("Controller %u", i);
|
|
|
|
|
shortTitle.printf("CC%u", i);
|
|
|
|
|
|
|
|
|
|
parameters.addParameter(
|
2020-05-16 21:30:58 +02:00
|
|
|
title, nullptr, 0, 0, Vst::ParameterInfo::kNoFlags,
|
2020-03-05 11:16:47 +01:00
|
|
|
pid++, Vst::kRootUnitId, shortTitle);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-08 20:12:48 +01:00
|
|
|
// Initial MIDI mapping
|
|
|
|
|
for (int32 i = 0; i < Vst::kCountCtrlNumber; ++i) {
|
|
|
|
|
Vst::ParamID id = Vst::kNoParamId;
|
|
|
|
|
switch (i) {
|
|
|
|
|
case Vst::kAfterTouch:
|
2021-02-08 20:14:07 +01:00
|
|
|
id = kPidAftertouch;
|
2021-02-08 20:12:48 +01:00
|
|
|
break;
|
|
|
|
|
case Vst::kPitchBend:
|
2021-02-08 20:14:07 +01:00
|
|
|
id = kPidPitchBend;
|
2021-02-08 20:12:48 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
if (i < 128)
|
2021-02-08 20:14:07 +01:00
|
|
|
id = kPidCC0 + i;
|
2021-02-08 20:12:48 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
midiMapping_[i] = id;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-05 11:16:47 +01:00
|
|
|
return kResultTrue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tresult PLUGIN_API SfizzVstControllerNoUi::terminate()
|
|
|
|
|
{
|
|
|
|
|
return EditController::terminate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tresult PLUGIN_API SfizzVstControllerNoUi::getMidiControllerAssignment(int32 busIndex, int16 channel, Vst::CtrlNumber midiControllerNumber, Vst::ParamID& id)
|
|
|
|
|
{
|
2021-02-08 20:12:48 +01:00
|
|
|
if (midiControllerNumber < 0 || midiControllerNumber >= Vst::kCountCtrlNumber) {
|
|
|
|
|
id = Vst::kNoParamId;
|
|
|
|
|
return kResultFalse;
|
|
|
|
|
}
|
2020-03-05 11:16:47 +01:00
|
|
|
|
2021-02-08 20:12:48 +01:00
|
|
|
id = midiMapping_[midiControllerNumber];
|
|
|
|
|
if (id == Vst::kNoParamId)
|
|
|
|
|
return kResultFalse;
|
2020-03-05 11:16:47 +01:00
|
|
|
|
2021-02-08 20:12:48 +01:00
|
|
|
return kResultTrue;
|
2020-03-05 11:16:47 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-06 11:55:52 +01:00
|
|
|
tresult PLUGIN_API SfizzVstControllerNoUi::getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string)
|
|
|
|
|
{
|
|
|
|
|
switch (tag) {
|
|
|
|
|
case kPidOversampling:
|
|
|
|
|
{
|
2021-02-04 02:48:49 +01:00
|
|
|
const SfizzRange range = SfizzRange::getForParameter(tag);
|
|
|
|
|
const int factorLog2 = static_cast<int>(range.denormalize(valueNormalized));
|
2020-03-06 11:55:52 +01:00
|
|
|
Steinberg::String buf;
|
2020-03-06 13:15:32 +01:00
|
|
|
buf.printf("%dX", 1 << factorLog2);
|
2020-03-06 11:55:52 +01:00
|
|
|
buf.copyTo(string);
|
|
|
|
|
return kResultTrue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return EditController::getParamStringByValue(tag, valueNormalized, string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tresult PLUGIN_API SfizzVstControllerNoUi::getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized)
|
|
|
|
|
{
|
|
|
|
|
switch (tag) {
|
|
|
|
|
case kPidOversampling:
|
|
|
|
|
{
|
2020-03-06 17:51:06 +01:00
|
|
|
int32 factor;
|
2021-02-04 02:48:49 +01:00
|
|
|
if (!Steinberg::String::scanInt32(string, factor, false))
|
2020-03-06 11:55:52 +01:00
|
|
|
factor = 1;
|
2020-03-06 13:15:32 +01:00
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
const SfizzRange range = SfizzRange::getForParameter(tag);
|
|
|
|
|
valueNormalized = range.normalize(integerLog2(factor));
|
2020-03-06 11:55:52 +01:00
|
|
|
return kResultTrue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return EditController::getParamValueByString(tag, string, valueNormalized);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
tresult SfizzVstControllerNoUi::setParam(Vst::ParamID tag, float value)
|
2020-03-05 11:16:47 +01:00
|
|
|
{
|
2021-02-04 02:48:49 +01:00
|
|
|
const SfizzRange range = SfizzRange::getForParameter(tag);
|
|
|
|
|
return setParamNormalized(tag, range.normalize(value));
|
2020-03-05 11:16:47 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
tresult PLUGIN_API SfizzVstControllerNoUi::setParamNormalized(Vst::ParamID tag, Vst::ParamValue normValue)
|
2020-03-05 11:16:47 +01:00
|
|
|
{
|
2021-02-04 02:48:49 +01:00
|
|
|
tresult r = EditController::setParamNormalized(tag, normValue);
|
2020-03-05 11:16:47 +01:00
|
|
|
if (r != kResultTrue)
|
|
|
|
|
return r;
|
|
|
|
|
|
2021-02-04 08:29:57 +01:00
|
|
|
processorStateUpdate_->access([tag, normValue](SfizzVstState& state) {
|
|
|
|
|
const SfizzRange range = SfizzRange::getForParameter(tag);
|
|
|
|
|
switch (tag) {
|
|
|
|
|
case kPidVolume:
|
|
|
|
|
state.volume = range.denormalize(normValue);
|
|
|
|
|
break;
|
|
|
|
|
case kPidNumVoices:
|
|
|
|
|
state.numVoices = (int32)range.denormalize(normValue);
|
|
|
|
|
break;
|
|
|
|
|
case kPidOversampling:
|
|
|
|
|
state.oversamplingLog2 = (int32)range.denormalize(normValue);
|
|
|
|
|
break;
|
|
|
|
|
case kPidPreloadSize:
|
|
|
|
|
state.preloadSize = (int32)range.denormalize(normValue);
|
|
|
|
|
break;
|
|
|
|
|
case kPidScalaRootKey:
|
|
|
|
|
state.scalaRootKey = (int32)range.denormalize(normValue);
|
|
|
|
|
break;
|
|
|
|
|
case kPidTuningFrequency:
|
|
|
|
|
state.tuningFrequency = (float)range.denormalize(normValue);
|
|
|
|
|
break;
|
|
|
|
|
case kPidStretchedTuning:
|
|
|
|
|
state.stretchedTuning = range.denormalize(normValue);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
});
|
2020-03-05 18:21:46 +01:00
|
|
|
|
2020-03-05 11:16:47 +01:00
|
|
|
return kResultTrue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream)
|
2020-03-05 11:16:47 +01:00
|
|
|
{
|
|
|
|
|
SfizzVstState s;
|
|
|
|
|
|
2020-12-07 09:41:36 +01:00
|
|
|
tresult r = s.load(stream);
|
2020-03-05 11:16:47 +01:00
|
|
|
if (r != kResultTrue)
|
|
|
|
|
return r;
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
processorStateUpdate_->setState(s);
|
|
|
|
|
|
|
|
|
|
setParam(kPidVolume, s.volume);
|
|
|
|
|
setParam(kPidNumVoices, s.numVoices);
|
|
|
|
|
setParam(kPidOversampling, s.oversamplingLog2);
|
|
|
|
|
setParam(kPidPreloadSize, s.preloadSize);
|
|
|
|
|
setParam(kPidScalaRootKey, s.scalaRootKey);
|
|
|
|
|
setParam(kPidTuningFrequency, s.tuningFrequency);
|
|
|
|
|
setParam(kPidStretchedTuning, s.stretchedTuning);
|
2020-03-05 18:21:46 +01:00
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
sfzPathUpdate_->setPath(s.sfzFile);
|
|
|
|
|
sfzPathUpdate_->deferUpdate();
|
|
|
|
|
scalaPathUpdate_->setPath(s.scalaFile);
|
|
|
|
|
scalaPathUpdate_->deferUpdate();
|
2020-03-05 11:16:47 +01:00
|
|
|
|
|
|
|
|
return kResultTrue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
|
2020-06-20 05:49:00 +02:00
|
|
|
{
|
2020-12-07 09:41:36 +01:00
|
|
|
// Note: may be called from any thread (Reaper)
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
tresult result = EditController::notify(message);
|
2020-06-20 05:49:00 +02:00
|
|
|
if (result != kResultFalse)
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
const char* id = message->getMessageID();
|
|
|
|
|
Vst::IAttributeList* attr = message->getAttributes();
|
|
|
|
|
|
|
|
|
|
if (!strcmp(id, "LoadedSfz")) {
|
|
|
|
|
const void* data = nullptr;
|
|
|
|
|
uint32 size = 0;
|
|
|
|
|
result = attr->getBinary("File", data, size);
|
2020-06-20 11:26:10 +02:00
|
|
|
|
|
|
|
|
if (result != kResultTrue)
|
|
|
|
|
return result;
|
|
|
|
|
|
2021-02-04 08:29:57 +01:00
|
|
|
std::string sfzFile(static_cast<const char *>(data), size);
|
|
|
|
|
processorStateUpdate_->access([&sfzFile](SfizzVstState& state) {
|
|
|
|
|
state.sfzFile = sfzFile;
|
|
|
|
|
});
|
|
|
|
|
sfzPathUpdate_->setPath(std::move(sfzFile));
|
2021-02-04 02:48:49 +01:00
|
|
|
sfzPathUpdate_->deferUpdate();
|
2020-06-20 05:49:00 +02:00
|
|
|
}
|
|
|
|
|
else if (!strcmp(id, "LoadedScala")) {
|
|
|
|
|
const void* data = nullptr;
|
|
|
|
|
uint32 size = 0;
|
|
|
|
|
result = attr->getBinary("File", data, size);
|
2020-06-20 11:26:10 +02:00
|
|
|
|
|
|
|
|
if (result != kResultTrue)
|
|
|
|
|
return result;
|
|
|
|
|
|
2021-02-04 08:29:57 +01:00
|
|
|
std::string scalaFile(static_cast<const char *>(data), size);
|
|
|
|
|
processorStateUpdate_->access([&scalaFile](SfizzVstState& state) {
|
|
|
|
|
state.scalaFile = scalaFile;
|
|
|
|
|
});
|
|
|
|
|
scalaPathUpdate_->setPath(std::move(scalaFile));
|
2021-02-04 02:48:49 +01:00
|
|
|
scalaPathUpdate_->deferUpdate();
|
2020-06-20 05:49:00 +02:00
|
|
|
}
|
2020-08-12 19:23:50 +02:00
|
|
|
else if (!strcmp(id, "NotifiedPlayState")) {
|
|
|
|
|
const void* data = nullptr;
|
|
|
|
|
uint32 size = 0;
|
|
|
|
|
result = attr->getBinary("PlayState", data, size);
|
|
|
|
|
|
|
|
|
|
if (result != kResultTrue)
|
|
|
|
|
return result;
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
playStateUpdate_->setState(*static_cast<const SfizzPlayState*>(data));
|
|
|
|
|
playStateUpdate_->deferUpdate();
|
2020-08-12 19:23:50 +02:00
|
|
|
}
|
2020-10-19 02:58:01 +02:00
|
|
|
else if (!strcmp(id, "ReceivedMessage")) {
|
|
|
|
|
const void* data = nullptr;
|
|
|
|
|
uint32 size = 0;
|
|
|
|
|
result = attr->getBinary("Message", data, size);
|
|
|
|
|
|
|
|
|
|
if (result != kResultTrue)
|
|
|
|
|
return result;
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
// this is a synchronous send, because the update object gets reused
|
|
|
|
|
oscUpdate_->setMessage(data, size, false);
|
|
|
|
|
oscUpdate_->changed();
|
|
|
|
|
oscUpdate_->clear();
|
2020-10-19 02:58:01 +02:00
|
|
|
}
|
2021-02-23 09:27:20 +01:00
|
|
|
else if (!strcmp(id, "NoteEvents")) {
|
|
|
|
|
const void* data = nullptr;
|
|
|
|
|
uint32 size = 0;
|
|
|
|
|
result = attr->getBinary("Events", data, size);
|
|
|
|
|
|
|
|
|
|
const auto* events = reinterpret_cast<
|
|
|
|
|
const std::pair<uint32_t, float>*>(data);
|
|
|
|
|
uint32 numEvents = size / sizeof(events[0]);
|
|
|
|
|
|
|
|
|
|
// this is a synchronous send, because the update object gets reused
|
|
|
|
|
noteUpdate_->setEvents(events, numEvents, false);
|
|
|
|
|
noteUpdate_->changed();
|
|
|
|
|
noteUpdate_->clear();
|
|
|
|
|
}
|
2020-06-20 05:49:00 +02:00
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 02:48:49 +01:00
|
|
|
// --- Controller with UI --- //
|
|
|
|
|
|
|
|
|
|
IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name)
|
|
|
|
|
{
|
|
|
|
|
ConstString name(_name);
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "[sfizz] about to create view: %s\n", _name);
|
|
|
|
|
|
|
|
|
|
if (name != Vst::ViewType::kEditor)
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
std::vector<FObject*> continuousUpdates;
|
|
|
|
|
continuousUpdates.push_back(sfzPathUpdate_);
|
|
|
|
|
continuousUpdates.push_back(scalaPathUpdate_);
|
|
|
|
|
continuousUpdates.push_back(playStateUpdate_);
|
|
|
|
|
for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i)
|
|
|
|
|
continuousUpdates.push_back(parameters.getParameterByIndex(i));
|
|
|
|
|
|
|
|
|
|
std::vector<FObject*> triggerUpdates;
|
|
|
|
|
triggerUpdates.push_back(oscUpdate_);
|
2021-02-23 09:27:20 +01:00
|
|
|
triggerUpdates.push_back(noteUpdate_);
|
2021-02-04 02:48:49 +01:00
|
|
|
|
|
|
|
|
IPtr<SfizzVstEditor> editor = Steinberg::owned(
|
|
|
|
|
new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates)));
|
|
|
|
|
|
|
|
|
|
editor->remember();
|
|
|
|
|
return editor;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-05 11:16:47 +01:00
|
|
|
FUnknown* SfizzVstController::createInstance(void*)
|
|
|
|
|
{
|
|
|
|
|
return static_cast<Vst::IEditController*>(new SfizzVstController);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Note(jpc) Generated at random with uuidgen.
|
|
|
|
|
Can't find docs on it... maybe it's to register somewhere?
|
|
|
|
|
*/
|
|
|
|
|
FUID SfizzVstController::cid(0x7129736c, 0xbc784134, 0xbb899d56, 0x2ebafe4f);
|