Browse Source

AU params

Signed-off-by: falkTX <falktx@falktx.com>
pull/1898/head
falkTX 6 months ago
parent
commit
bbfbf7c75d
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
2 changed files with 404 additions and 11 deletions
  1. +403
    -10
      source/backend/plugin/CarlaPluginAU.cpp
  2. +1
    -1
      source/backend/plugin/CarlaPluginNative.cpp

+ 403
- 10
source/backend/plugin/CarlaPluginAU.cpp View File

@@ -22,6 +22,12 @@ typedef OSStatus (*InitializeFn)(void*);
typedef OSStatus (*UninitializeFn)(void*);
typedef OSStatus (*GetPropertyInfoFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, UInt32*, Boolean*);
typedef OSStatus (*GetPropertyFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, void*, UInt32*);
typedef OSStatus (*SetPropertyFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, const void*, UInt32);
typedef OSStatus (*GetParameterFn)(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, AudioUnitParameterValue*);
typedef OSStatus (*SetParameterFn)(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, AudioUnitParameterValue, UInt32);
typedef OSStatus (*ScheduleParametersFn)(void*, const AudioUnitParameterEvent*, UInt32);
typedef OSStatus (*ResetFn)(void*, AudioUnitScope, AudioUnitElement);
typedef OSStatus (*RenderFn)(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*);
typedef OSStatus (*MIDIEventFn)(void*, UInt32, UInt32, UInt32, UInt32);

static constexpr FourCharCode getFourCharCodeFromString(const char str[4])
@@ -103,6 +109,25 @@ public:
// -------------------------------------------------------------------
// Information (per-plugin data)

uint getOptionsAvailable() const noexcept override
{
// TODO
return 0x0;
}

float getParameterValue(const uint32_t parameterId) const noexcept override
{
CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr, 0.f);
CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, 0.f);

const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
AudioUnitParameterValue value = 0.f;
if (fFunctions.getParameter(fInterface, paramId, kAudioUnitScope_Global, 0, &value) == noErr)
return value;

return 0.f;
}

bool getLabel(char* const strBuf) const noexcept override
{
std::strncpy(strBuf, fLabel.buffer(), STR_MAX);
@@ -121,6 +146,76 @@ public:
return true;
}

bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
{
CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr, false);
CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);

const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
AudioUnitParameterInfo info = {};
UInt32 outDataSize = sizeof(AudioUnitParameterInfo);

if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramId, &info, &outDataSize) == noErr)
{
if (info.flags & kAudioUnitParameterFlag_HasCFNameString)
{
*strBuf = '\0';
if (! CFStringGetCString(info.cfNameString, strBuf, std::min<int>(STR_MAX, CFStringGetLength(info.cfNameString) + 1), kCFStringEncodingUTF8))
{
carla_stdout("CFStringGetCString fail '%s'", info.name);
std::strncpy(strBuf, info.name, STR_MAX);
}
else
{
carla_stdout("CFStringGetCString ok '%s' '%s'", info.name, strBuf);
std::strncpy(strBuf, info.name, STR_MAX);
}

if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
CFRelease(info.cfNameString);

return true;
}

carla_stdout("CFStringGetCString not used '%s'", info.name);
std::strncpy(strBuf, info.name, STR_MAX);
return true;
}

carla_safe_assert("fFunctions.getProperty(...)", __FILE__, __LINE__);
return CarlaPlugin::getParameterName(parameterId, strBuf);
}

// -------------------------------------------------------------------
// Set data (plugin-specific stuff)

void setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept override
{
CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);

const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
const float fixedValue = pData->param.getFixedValue(parameterId, value);

fFunctions.setParameter(fInterface, paramId, kAudioUnitScope_Global, 0, value, 0);

CarlaPlugin::setParameterValue(parameterId, fixedValue, sendGui, sendOsc, sendCallback);
}

void setParameterValueRT(const uint32_t parameterId, const float value, const uint32_t frameOffset, const bool sendCallbackLater) noexcept override
{
CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);

const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
const float fixedValue = pData->param.getFixedValue(parameterId, value);

// TODO use scheduled events
fFunctions.setParameter(fInterface, paramId, kAudioUnitScope_Global, 0, value, 0);

CarlaPlugin::setParameterValueRT(parameterId, fixedValue, frameOffset, sendCallbackLater);
}

// -------------------------------------------------------------------
// Plugin state

@@ -130,6 +225,8 @@ public:
CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
carla_debug("CarlaPluginAU::reload() - start");

const EngineProcessMode processMode = pData->engine->getProccessMode();

// Safely disable plugin for reload
const ScopedDisabler sd(this);

@@ -138,6 +235,259 @@ public:

clearBuffers();

uint32_t audioIns, audioOuts, parametersIns, parametersOuts;
audioIns = audioOuts = parametersIns = parametersOuts = 0;

bool needsCtrlIn, needsCtrlOut, hasMidiIn, hasMidiOut;
needsCtrlIn = needsCtrlOut = hasMidiIn = hasMidiOut = false;

CarlaString portName;
const uint portNameSize(pData->engine->getMaxPortNameSize());

UInt32 outDataSize;
Boolean outWritable = false;

// audio ports
outDataSize = 0;
if (fFunctions.getPropertyInfo(fInterface, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, &outDataSize, &outWritable) == noErr && outDataSize != 0 && outDataSize % sizeof(AUChannelInfo) == 0)
{
const uint32_t numChannels = outDataSize / sizeof(AUChannelInfo);
AUChannelInfo* const channelInfo = new AUChannelInfo[numChannels];

if (fFunctions.getProperty(fInterface, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, channelInfo, &outDataSize) == noErr && outDataSize == numChannels * sizeof(AUChannelInfo))
{
AUChannelInfo* highestInfo = &channelInfo[0];

for (uint32_t i=1; i<numChannels; ++i)
{
if (channelInfo[i].inChannels > highestInfo->inChannels && channelInfo[i].outChannels > highestInfo->outChannels)
highestInfo = &channelInfo[i];
}

audioIns = highestInfo->inChannels;
audioOuts = highestInfo->outChannels;
}
}

if (audioIns > 0)
{
pData->audioIn.createNew(audioIns);
}

if (audioOuts > 0)
{
pData->audioOut.createNew(audioOuts);
needsCtrlIn = true;
}

// Audio Ins
for (uint32_t i=0; i < audioIns; ++i)
{
portName.clear();

if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
{
portName = pData->name;
portName += ":";
}

if (audioIns > 1)
{
portName += "input_";
portName += CarlaString(i+1);
}
else
portName += "input";

portName.truncate(portNameSize);

pData->audioIn.ports[i].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true, i);
pData->audioIn.ports[i].rindex = i;
}

// Audio Outs
for (uint32_t i=0; i < audioOuts; ++i)
{
portName.clear();

if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
{
portName = pData->name;
portName += ":";
}

if (audioOuts > 1)
{
portName += "output_";
portName += CarlaString(i+1);
}
else
portName += "output";

portName.truncate(portNameSize);

pData->audioOut.ports[i].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, i);
pData->audioOut.ports[i].rindex = i;
}

// parameters
outDataSize = 0;
if (fFunctions.getPropertyInfo(fInterface, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, &outDataSize, &outWritable) == noErr && outDataSize != 0 && outDataSize % sizeof(AudioUnitParameterID) == 0)
{
const uint32_t numParams = outDataSize / sizeof(AudioUnitParameterID);
AudioUnitParameterID* const paramIds = new AudioUnitParameterID[numParams];

if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, paramIds, &outDataSize) == noErr && outDataSize == numParams * sizeof(AudioUnitParameterID))
{
pData->param.createNew(numParams, false);

AudioUnitParameterInfo info;
float min, max, def, step, stepSmall, stepLarge;

for (uint32_t i=0; i<numParams; ++i)
{
carla_zeroStruct(info);

outDataSize = 0;
if (fFunctions.getPropertyInfo(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &outDataSize, &outWritable) != noErr)
break;
if (outDataSize != sizeof(AudioUnitParameterInfo))
break;
if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &info, &outDataSize) != noErr)
break;

if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
CFRelease(info.cfNameString);

pData->param.data[i].index = static_cast<int32_t>(i);
pData->param.data[i].rindex = static_cast<int32_t>(paramIds[i]);

if (info.flags & kAudioUnitParameterFlag_IsWritable)
{
pData->param.data[i].type = PARAMETER_INPUT;
needsCtrlIn = true;
}
else if (info.flags & (kAudioUnitParameterFlag_IsReadable|kAudioUnitParameterFlag_MeterReadOnly))
{
pData->param.data[i].type = PARAMETER_OUTPUT;
needsCtrlOut = true;
}
else
{
pData->param.data[i].type = PARAMETER_UNKNOWN;
continue;
}

min = info.minValue;
max = info.maxValue;
def = info.defaultValue;

if (min > max)
max = min;

if (carla_isEqual(min, max))
{
carla_stderr2("WARNING - Broken plugin parameter '%s': max == min", info.name);
max = min + 0.1f;
}

if (def < min)
def = min;
else if (def > max)
def = max;

pData->param.data[i].hints |= PARAMETER_IS_ENABLED;

if ((info.flags & kAudioUnitParameterFlag_NonRealTime) == 0)
{
pData->param.data[i].hints |= PARAMETER_IS_AUTOMATABLE;
pData->param.data[i].hints |= PARAMETER_CAN_BE_CV_CONTROLLED;
}

if (info.unit == kAudioUnitParameterUnit_Boolean)
{
step = max - min;
stepSmall = step;
stepLarge = step;
pData->param.data[i].hints |= PARAMETER_IS_BOOLEAN;
}
else if (info.unit == kAudioUnitParameterUnit_Indexed)
{
step = 1.0f;
stepSmall = 1.0f;
stepLarge = 10.0f;
pData->param.data[i].hints |= PARAMETER_IS_INTEGER;
}
else
{
float range = max - min;
step = range/100.0f;
stepSmall = range/1000.0f;
stepLarge = range/10.0f;
}

pData->param.ranges[i].min = min;
pData->param.ranges[i].max = max;
pData->param.ranges[i].def = def;
pData->param.ranges[i].step = step;
pData->param.ranges[i].stepSmall = stepSmall;
pData->param.ranges[i].stepLarge = stepLarge;
}
}

delete[] paramIds;
}

if (needsCtrlIn || hasMidiIn)
{
portName.clear();

if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
{
portName = pData->name;
portName += ":";
}

portName += "events-in";
portName.truncate(portNameSize);

pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0);
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->event.cvSourcePorts = pData->client->createCVSourcePorts();
#endif
}

if (needsCtrlOut || hasMidiOut)
{
portName.clear();

if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
{
portName = pData->name;
portName += ":";
}

portName += "events-out";
portName.truncate(portNameSize);

pData->event.portOut = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, false, 0);
}

// plugin hints
pData->hints = 0x0;

if (audioOuts > 0 && (audioIns == audioOuts || audioIns == 1))
pData->hints |= PLUGIN_CAN_DRYWET;

if (audioOuts > 0)
pData->hints |= PLUGIN_CAN_VOLUME;

if (audioOuts >= 2 && audioOuts % 2 == 0)
pData->hints |= PLUGIN_CAN_BALANCE;

// extra plugin hints
pData->extraHints = 0x0;

bufferSizeChanged(pData->engine->getBufferSize());
reloadPrograms(true);

@@ -314,16 +664,7 @@ public:
return false;
}

const InitializeFn auInitialize = (InitializeFn)fInterface->Lookup(kAudioUnitInitializeSelect);
const UninitializeFn auUninitialize = (UninitializeFn)fInterface->Lookup(kAudioUnitUninitializeSelect);
const GetPropertyInfoFn auGetPropertyInfo = (GetPropertyInfoFn)fInterface->Lookup(kAudioUnitGetPropertyInfoSelect);
const GetPropertyFn auGetProperty = (GetPropertyFn)fInterface->Lookup(kAudioUnitGetPropertySelect);
const MIDIEventFn auMIDIEvent = (MIDIEventFn)fInterface->Lookup(kMusicDeviceMIDIEventSelect);

if (auInitialize == nullptr ||
auUninitialize == nullptr ||
auGetPropertyInfo == nullptr ||
auGetProperty == nullptr)
if (! fFunctions.init(fInterface))
{
pData->engine->setLastError("Component does not provide all necessary functions");
fInterface = nullptr;
@@ -390,6 +731,58 @@ private:
CarlaString fName;
CarlaString fLabel;
CarlaString fMaker;

struct Functions {
InitializeFn initialize;
UninitializeFn uninitialize;
GetPropertyInfoFn getPropertyInfo;
GetPropertyFn getProperty;
SetPropertyFn setProperty;
GetParameterFn getParameter;
SetParameterFn setParameter;
ScheduleParametersFn scheduleParameters;
ResetFn reset;
RenderFn render;
MIDIEventFn midiEvent;

Functions()
: initialize(nullptr),
uninitialize(nullptr),
getPropertyInfo(nullptr),
getProperty(nullptr),
setProperty(nullptr),
getParameter(nullptr),
setParameter(nullptr),
scheduleParameters(nullptr),
reset(nullptr),
render(nullptr),
midiEvent(nullptr) {}

bool init(AudioComponentPlugInInterface* const interface)
{
initialize = (InitializeFn)interface->Lookup(kAudioUnitInitializeSelect);
uninitialize = (UninitializeFn)interface->Lookup(kAudioUnitUninitializeSelect);
getPropertyInfo = (GetPropertyInfoFn)interface->Lookup(kAudioUnitGetPropertyInfoSelect);
getProperty = (GetPropertyFn)interface->Lookup(kAudioUnitGetPropertySelect);
setProperty = (SetPropertyFn)interface->Lookup(kAudioUnitSetPropertySelect);
getParameter = (GetParameterFn)interface->Lookup(kAudioUnitGetParameterSelect);
setParameter = (SetParameterFn)interface->Lookup(kAudioUnitSetParameterSelect);
scheduleParameters = (ScheduleParametersFn)interface->Lookup(kAudioUnitScheduleParametersSelect);
reset = (ResetFn)interface->Lookup(kAudioUnitResetSelect);
render = (RenderFn)interface->Lookup(kAudioUnitRenderSelect);
midiEvent = (MIDIEventFn)interface->Lookup(kMusicDeviceMIDIEventSelect);

return initialize != nullptr
&& uninitialize != nullptr
&& getPropertyInfo != nullptr
&& getProperty != nullptr
&& setProperty != nullptr
&& getParameter != nullptr
&& setParameter != nullptr
&& scheduleParameters != nullptr
&& render != nullptr;
}
} fFunctions;
};
#endif



+ 1
- 1
source/backend/plugin/CarlaPluginNative.cpp View File

@@ -1068,7 +1068,7 @@ public:
CARLA_SAFE_ASSERT_RETURN(fHandle != nullptr,);
carla_debug("CarlaPluginNative::reload() - start");

const EngineProcessMode processMode(pData->engine->getProccessMode());
const EngineProcessMode processMode = pData->engine->getProccessMode();

// Safely disable plugin for reload
const ScopedDisabler sd(this);


Loading…
Cancel
Save