Skip to content

Commit

Permalink
VST3 Programs / Program Changes (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
defiantnerd authored Feb 25, 2024
1 parent 817bcd4 commit cb96ae5
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 13 deletions.
8 changes: 5 additions & 3 deletions src/detail/vst3/parameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,16 @@ Vst3Parameter* Vst3Parameter::create(uint8_t bus, uint8_t channel, uint8_t cc, V
str8ToStr16(v.title, fullname.c_str(), str16BufferSize(v.title));
// TODO: string shrink algorithm shortening the string a bit
str8ToStr16(v.shortTitle, name, str16BufferSize(v.shortTitle));
v.units[0] = 0; // unfortunately, CLAP has no unit for parameter values
v.unitId = channel + 1;

v.units[0] = 0; // nothing in the "unit" field
// the unit will not be set here, but outside
// v.unitId = channel + 1;

v.defaultNormalizedValue = 0;
v.flags = Vst::ParameterInfo::kNoFlags;
if (cc == Vst::ControllerNumbers::kCtrlProgramChange)
{
v.flags |= Vst::ParameterInfo::kIsProgramChange;
v.flags |= Vst::ParameterInfo::kIsProgramChange | Vst::ParameterInfo::kCanAutomate;
v.stepCount = 128;
}

Expand Down
12 changes: 11 additions & 1 deletion src/detail/vst3/parameter.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,20 @@ class Vst3Parameter : public Steinberg::Vst::Parameter

inline double asClapValue(double vst3value) const
{
return vst3value * (max_value - min_value) + min_value;
auto& info = this->getInfo();
if (info.stepCount > 0)
{
return (vst3value * float(info.stepCount)) + min_value;
}
return (vst3value * (max_value - min_value)) + min_value;
}
inline double asVst3Value(double clapvalue) const
{
auto& info = this->getInfo();
if (info.stepCount > 0)
{
return (clapvalue - min_value) / float(info.stepCount);
}
return (clapvalue - min_value) / (max_value - min_value);
}
static Vst3Parameter* create(const clap_param_info_t* info,
Expand Down
8 changes: 8 additions & 0 deletions src/detail/vst3/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,14 @@ void ProcessAdapter::process(Steinberg::Vst::ProcessData& data)
n.midi.data[2] = (val >> 7) & 0x7F; // MSB
}
break;
case Vst::ControllerNumbers::kCtrlProgramChange:
{
auto val = (uint16_t)param->asClapValue(value);
n.midi.data[0] = 0xC0 | param->channel; // $Cc
n.midi.data[1] = (val & 0x7F); // only one byte
n.midi.data[2] = 0;
}
break;
default:
n.midi.data[0] = 0xB0 | param->channel;
n.midi.data[1] = param->controller;
Expand Down
86 changes: 78 additions & 8 deletions src/wrapasvst3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,14 @@ tresult PLUGIN_API ClapAsVst3::getParamStringByValue(Vst::ParamID id, Vst::Param
auto param = (Vst3Parameter*)this->getParameterObject(id);
auto val = param->asClapValue(valueNormalized);

if (param->getInfo().flags & Vst::ParameterInfo::kIsProgramChange)
{
UString wrapper(&string[0], str16BufferSize(Steinberg::Vst::String128));

wrapper.assign("Program", 8);
return kResultOk;
}

char outbuf[128];
memset(outbuf, 0, sizeof(outbuf));
if (this->_plugin->_ext._params->value_to_text(_plugin->_plugin, param->id, val, outbuf, 127))
Expand Down Expand Up @@ -282,8 +290,11 @@ tresult PLUGIN_API ClapAsVst3::getMidiControllerAssignment(int32 busIndex, int16
// for my first Event bus and for MIDI channel 0 and for MIDI CC Volume only
if (busIndex == 0) // && channel == 0) // && midiControllerNumber == Vst::kCtrlVolume)
{
id = _IMidiMappingIDs[channel][midiControllerNumber];
return kResultTrue;
if (midiControllerNumber < Vst::kCountCtrlNumber) // with program change
{
id = _IMidiMappingIDs[channel][midiControllerNumber];
return kResultTrue;
}
}
return kResultFalse;
}
Expand Down Expand Up @@ -331,12 +342,22 @@ tresult ClapAsVst3::getNoteExpressionValueByString(int32 /*busIndex*/, int16 /*c

#endif

////-----------------------------------------------------------------------------
//tresult PLUGIN_API ClapAsVst3::queryInterface(const TUID iid, void** obj)
//{
// DEF_INTERFACE(IMidiMapping)
// return SingleComponentEffect::queryInterface(iid, obj);
//}
tresult ClapAsVst3::getUnitByBus(Vst::MediaType type, Vst::BusDirection dir, int32 busIndex,
int32 channel, Vst::UnitID& unitId /*out*/)
{
if (type == Vst::MediaTypes::kEvent && dir == Vst::BusDirections::kInput)
{
if (busIndex == 0)
{
if ((channel >= 0) && (channel < (Steinberg::int32)_MIDIUnits.size()))
{
unitId = _MIDIUnits[channel];
return kResultTrue;
}
}
}
return kResultFalse;
}

static Vst::SpeakerArrangement speakerArrFromPortType(const char* port_type)
{
Expand Down Expand Up @@ -614,9 +635,21 @@ void ClapAsVst3::setupParameters(const clap_plugin_t* plugin, const clap_plugin_
// find free tags for IMidiMapping
Vst::ParamID x = 0xb00000;
_IMidiMappingEasy = true;
_MIDIUnits.clear();

for (uint8_t channel = 0; channel < _numMidiChannels; channel++)
{
// the unit for that channel
Vst::UnitInfo midiUnitInfo;

midiUnitInfo.id = (decltype(midiUnitInfo.id))units.size();
midiUnitInfo.parentUnitId = 0; // parented in the root unit
midiUnitInfo.programListId = Vst::kNoProgramListId;

auto name = fmt::format("MIDI Channel {}", channel + 1);

VST3::StringConvert::convert(name, midiUnitInfo.name);

for (int i = 0; i < Vst::ControllerNumbers::kCountCtrlNumber; ++i)
{
while (parameters.getParameter(x))
Expand All @@ -627,9 +660,46 @@ void ClapAsVst3::setupParameters(const clap_plugin_t* plugin, const clap_plugin_
x++;
}
auto p = Vst3Parameter::create(0, channel, i, x);
p->setUnitID(midiUnitInfo.id);
parameters.addParameter(p);
_IMidiMappingIDs[channel][i] = x++;
}
// if (false)
{
// program change parameter
while (parameters.getParameter(x))
{
// if this happens there is a index clash between the parameter ids
// and the ones reserved for the IMidiMapping
_IMidiMappingEasy = false;
x++;
}
auto p = Vst3Parameter::create(0, channel, Vst::ControllerNumbers::kCtrlProgramChange, x);

p->setUnitID(midiUnitInfo.id);
_MIDIUnits.emplace_back(midiUnitInfo.id);

parameters.addParameter(p);

auto programlist = new Steinberg::Vst::ProgramList(STR16("Program Changes"), x, midiUnitInfo.id);
for (int pc = 0; pc < 128; ++pc)
{
auto programname = fmt::format("Program {}", pc + 1);

programlist->addProgram(VST3::StringConvert::convert(programname).c_str());
}
this->addProgramList(programlist);

auto newUnit = new Vst::Unit(midiUnitInfo);

addUnit(newUnit);

// the programlist ID is actually the parameter ID
newUnit->setProgramListID(x);

//_IMidiMappingIDs[channel][Vst::ControllerNumbers::kCtrlProgramChange] = x++;
x++;
}
}
}

Expand Down
24 changes: 23 additions & 1 deletion src/wrapasvst3.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,25 @@ class ClapAsVst3 : public Steinberg::Vst::SingleComponentEffect,
int32 busIndex, int16 channel, Vst::NoteExpressionTypeID id, const Vst::TChar* string /*in*/,
Vst::NoteExpressionValue& valueNormalized /*out*/) override;

//---IUnitInfo--------------------------------------------------------------------------

tresult PLUGIN_API getUnitByBus(Vst::MediaType /*type*/, Vst::BusDirection /*dir*/, int32 /*busIndex*/,
int32 /*channel*/, Vst::UnitID& /*unitId*/ /*out*/) SMTG_OVERRIDE;

#if 0
// units selection --------------------
Vst::UnitID PLUGIN_API getSelectedUnit () SMTG_OVERRIDE { return selectedUnit; }
tresult PLUGIN_API selectUnit (Vst::UnitID unitId) SMTG_OVERRIDE
{
selectedUnit = unitId;
return kResultTrue;
}
#endif

//---Interface--------------------------------------------------------------------------
OBJ_METHODS(ClapAsVst3, SingleComponentEffect)
DEFINE_INTERFACES
// since the macro above opens a local function, this code is being executed during QueryInterface() :)
// dynamic interfaces
if (::Steinberg::FUnknownPrivate::iidEqual(iid, IMidiMapping::iid))
{
// when queried for the IMididMapping interface, check if the CLAP supports MIDI dialect on the MIDI Input busses and only return IMidiMapping then
Expand All @@ -176,6 +191,12 @@ class ClapAsVst3 : public Steinberg::Vst::SingleComponentEffect,
DEF_INTERFACE(IMidiMapping)
}
}
// add any other interfaces here:
//if (::Steinberg::FUnknownPrivate::iidEqual(iid, IExampleSomething::iid))
//{
// DEF_INTERFACE(IExampleSomething)
//}

DEF_INTERFACE(INoteExpressionController)
// tresult PLUGIN_API queryInterface(const TUID iid, void** obj) override;
END_DEFINE_INTERFACES(SingleComponentEffect)
Expand Down Expand Up @@ -323,4 +344,5 @@ class ClapAsVst3 : public Steinberg::Vst::SingleComponentEffect,
#else
clap_supported_note_expressions::AS_VST3_NOTE_EXPRESSION_PRESSURE;
#endif
std::vector<Vst::UnitID> _MIDIUnits;
};

0 comments on commit cb96ae5

Please sign in to comment.