wfview/audioplugin.cpp

666 wiersze
20 KiB
C++

#include "audioplugin.h"
//#include <lilv-0/lilv/lilv.h>
//#include <lv2.h>
#include "logcategories.h"
audioPlugin::audioPlugin(int maxBufferSizeNeeded, pluginPathType path,
unsigned char chainPosition, QObject *parent) : QObject(parent)
{
// Note: the size is for "interlaced" 2-channel 8-bit data, as is used outside of this plugin.
// Please do not set this to an odd number.
// We need each of our 16-bit one-channel buffers to be four times smaller in size.
if(maxBufferSizeNeeded==0)
{
bufferSize = 2728/2; // seems like an acceptable default value
} else {
bufferSize = maxBufferSizeNeeded/4;
}
this->audioChain = path;
this->chainPosition = chainPosition;
instanceId = (uint64_t)this;
instanceString = QString("0x%1").arg(instanceId, 16, 16, QChar('0'));
inBypassMode = false;
info.chainPosition = chainPosition;
info.instanceID = instanceId;
info.path = audioChain;
pluginURIAsQString = QString("unset");
externalSourceBuffer = NULL;
externalSinkBuffer = NULL;
sampleRate = 48000;
makeByteTable();
allocateBuffers();
initPluginWorld();
}
audioPlugin::~audioPlugin()
{
// free plugin helpers
unloadPlugin();
// TODO: consider locking the runPlugin carefully
// free buffers
deallocateBuffers();
}
void audioPlugin::setInputBuffer(char *stereoInterlacedInputBuffer)
{
if(stereoInterlacedInputBuffer == NULL)
{
emit pluginErrorMessage("Could not use NULL input buffer.");
haveExternalSourceBuffer = false;
externalSourceBuffer = NULL;
} else {
this->externalSourceBuffer = (int16_t*)stereoInterlacedInputBuffer;
haveExternalSourceBuffer = true;
}
}
void audioPlugin::setOutputBuffer(char *stereoInterlacedOutputBuffer)
{
if(stereoInterlacedOutputBuffer == NULL)
{
emit pluginErrorMessage("Could not use NULL output buffer.");
haveExternalSinkBuffer = false;
externalSinkBuffer = NULL;
} else {
this->externalSinkBuffer = (int16_t*)stereoInterlacedOutputBuffer;
haveExternalSinkBuffer = true;
}
}
int16_t* audioPlugin::getInputBuffer()
{
return externalSourceBuffer;
}
int16_t* audioPlugin::getOutputBuffer()
{
return externalSinkBuffer;
}
void audioPlugin::selectPluginByID(int pluginId)
{
(void)pluginId;
}
void audioPlugin::selectPluginByLabel(QString pluginLabel)
{
(void)pluginLabel;
}
void audioPlugin::selectPluginByName(QString pluginName)
{
(void)pluginName;
}
void audioPlugin::allocateBuffers()
{
// Internal use float buffers for audio
// These buffers may be larger than required, but must not be smaller.
// They will be accessed based on the size of the audio samples passing through.
if(haveAllocatedInternalBuffers)
{
// realloc?
emit pluginErrorMessage("WARNING: asked to reallocate buffer memory! This feature has not been implemented yet.");
} else {
inputBuffer[0]= (float*)malloc(sizeof(float) * bufferSize);
inputBuffer[1]= (float*)malloc(sizeof(float) * bufferSize);
outputBuffer[0]= (float*)malloc(sizeof(float) * bufferSize);
outputBuffer[1]= (float*)malloc(sizeof(float) * bufferSize);
haveAllocatedInternalBuffers = true;
}
}
void audioPlugin::deallocateBuffers()
{
if(haveAllocatedInternalBuffers)
{
haveAllocatedInternalBuffers = false;
free(inputBuffer);
free(outputBuffer);
} else {
emit pluginErrorMessage("WARNING: asked to deallocate memory that has not been allocated!");
}
}
void audioPlugin::initPluginWorld()
{
// Setup the "world" for the plugin functions:
world = lilv_world_new();
lilv_world_load_all(world);
plugins = lilv_world_get_all_plugins(world);
gControlPortClass = lilv_new_uri(world, LV2_CORE__ControlPort);
// TODO: if thing == NULL... emit fail
}
bool audioPlugin::activatePlugin()
{
// TODO: unactivate and unload reset bools
if(haveLoadedPlugin && !haveActivatedPlugin)
{
lilv_instance_activate(instance);
haveActivatedPlugin = true;
emit pluginStatusMessage(QString("[%1] activated plugin.").arg(instanceString));
return true;
} else {
emit pluginErrorMessage(QString("Cannot activate plugin that has not been loaded."));
return false;
}
}
void audioPlugin::setupPlugin(char *pluginURI)
{
// This function loads exactly the URI it is told to load.
// Future versions might include other, easier methods.
bool loadedOk = false;
bool activatedOk = false;
this->pluginURIAsQString = QString("%1").arg(pluginURI);
loadedOk = loadPlugin(pluginURI);
haveLoadedPlugin = loadedOk;
activatedOk = activatePlugin();
info.isLoaded = loadedOk && activatedOk;
info.pluginName = pluginNameAsQString;
info.plugin = this;
info.pluginURI = this->pluginURIAsQString;
emit pluginLoaded(info);
}
bool audioPlugin::loadPlugin(char *pluginURI)
{
bool success = false;
lilv_plugins_get_by_uri(plugins, uri);
uri = lilv_new_uri(world, pluginURI);
plugin = lilv_plugins_get_by_uri(plugins, uri);
if(plugin == NULL)
{
success = false;
this->info.isLoaded = false;
emit pluginLoaded(this->info);
emit pluginErrorMessage(QString("Could not find plugin by URI [%1]").arg(pluginURI));
pluginNameAsQString = "";
haveLoadedPlugin = false;
return success;
}
pluginName = lilv_plugin_get_name(plugin);
pluginNameAsQString = QString("%1").arg(lilv_node_as_string(pluginName));
emit pluginStatusMessage(QString("Loaded plugin named [%1] from URI [%2]").arg(lilv_node_as_string(pluginName)).arg(pluginURI));
success = true;
success = createPorts();
if(n_audio_in == 0 || n_audio_in > 2)
{
success = false;
this->info.isLoaded = false;
emit pluginLoaded(this->info);
emit pluginErrorMessage(QString("Number of audio input ports (%1) for plugin [%2] is not compatible.")\
.arg(n_audio_in).arg(lilv_node_as_string(pluginName)));
return success;
}
const uint32_t n_portsLoop = lilv_plugin_get_num_ports(plugin);
;
feature_uri_map_data = { this, audioPlugin::urid_map };
feature_uri_map = { LV2_URID__map, &feature_uri_map_data };
feature_uri_unmap_data = { this, audioPlugin::urid_unmap };
feature_uri_unmap = { LV2_URID__unmap, &feature_uri_unmap_data };
feature_worker = { LV2_WORKER__schedule, (LV2_Worker_Schedule*)malloc(sizeof(LV2_Worker_Schedule)) };
features[0] = &feature_uri_map;
features[1] = &feature_uri_unmap;
features[2] = &feature_worker;
instance = lilv_plugin_instantiate(plugin, sampleRate, features);
// The NULL is "features" ??
if(instance == NULL)
{
success = false;
emit pluginErrorMessage(QString("Plugin instance was NULL for plugin with name [%1]").arg(pluginNameAsQString));
return success;
}
// This is where the controls, input buffer, and output buffer, are connected
// into the plugin instance using pointers.
for (uint32_t p = 0, i = 0, o = 0; p < n_portsLoop; ++p) {
if (ports[p].type == TYPE_CONTROL) {
lilv_instance_connect_port(instance, p, &ports[p].value);
} else if (ports[p].type == TYPE_AUDIO) {
if (ports[p].is_input) {
lilv_instance_connect_port(instance, p, inputBuffer[ i++ ]);
} else {
lilv_instance_connect_port(instance, p, outputBuffer[ o++ ]);
}
} else {
lilv_instance_connect_port(instance, p, NULL);
}
}
success = true;
return success;
}
bool audioPlugin::createPorts()
{
bool success = false;
n_ports = lilv_plugin_get_num_ports(plugin);
n_audio_in = 0;
n_audio_out = 0;
emit pluginStatusMessage(QString("Creating ports, number of ports returned: %1").arg(n_ports));
ports = (Port*)calloc(n_ports, sizeof(Port));
float *values = (float*)calloc(n_ports, sizeof(float)); // default values actually
float *minValues = (float*)calloc(n_ports, sizeof(float));
float *maxValues = (float*)calloc(n_ports, sizeof(float));
controlsType singleControl;
controls.clear();
unsigned int controlNumber = 0;
lilv_plugin_get_port_ranges_float(plugin, minValues, maxValues, values);
// The two NULL above contain pointers to float arrays holding max and min values for the controls.
LilvNode* lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort);
LilvNode* lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort);
LilvNode* lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort);
LilvNode* lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort);
LilvNode* lv2_EventPort = lilv_new_uri(world, LV2_EVENT__EventPort);
LilvNode* lv2_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort);
LilvNode* lv2_connectionOptional = lilv_new_uri(world, LV2_CORE__connectionOptional);
LilvNode* lv2_urid_map = lilv_new_uri(world, LV2_URID__map);
LilvNode* lv2_urid_unmap = lilv_new_uri(world, LV2_URID__unmap);
LilvNode* lv2_worker_schedule = lilv_new_uri(world, LV2_WORKER__schedule);
for (uint32_t i = 0; i < n_ports; ++i) {
Port *port = &ports[i];
const LilvPort *lport = lilv_plugin_get_port_by_index(plugin, i);
port->lilv_port = lport;
port->index = i;
port->value = isnan(values[i]) ? 0.0f : values[i];
port->optional = lilv_port_has_property(plugin, lport, lv2_connectionOptional);
// Check if input or output:
if (lilv_port_is_a(plugin, lport, lv2_InputPort)) {
port->is_input = true;
} else if (!lilv_port_is_a(plugin, lport, lv2_OutputPort) && !port->optional)
{
success = false;
emit pluginErrorMessage(QString("Port %1 is neither input nor output").arg(i));
return success;
}
// Check if port is audio or control:
if (lilv_port_is_a(plugin, lport, lv2_ControlPort)) {
port->type = TYPE_CONTROL;
singleControl.instanceId = instanceId;
singleControl.portIndex = i;
singleControl.controlsIndex = controlNumber++;
singleControl.def = isnan(values[i]) ? 0.0f : values[i];
singleControl.min = isnan(minValues[i]) ? 0.0f : minValues[i];
singleControl.max = isnan(maxValues[i]) ? 0.0f : maxValues[i];
singleControl.value = singleControl.def; // start at default value
singleControl.label = QString("%1").arg(lilv_node_as_string(lilv_port_get_name(plugin, lport)));
controls.push_back(singleControl);
}
else if (lilv_port_is_a(plugin, lport, lv2_AudioPort)) {
port->type = TYPE_AUDIO;
if (port->is_input) {
++n_audio_in;
}
else {
++n_audio_out;
}
}
else if (lilv_port_is_a(plugin, lport, lv2_EventPort)) {
emit pluginStatusMessage(QString("[%1] is an event port").arg(i));
port->type = TYPE_EVENT;
}
else if (lilv_port_is_a(plugin, lport, lv2_AtomPort)) {
emit pluginStatusMessage(QString("[%1] is an atom port").arg(i));
port->type = TYPE_ATOM;
}
else if (!port->optional)
{
success = false;
emit pluginErrorMessage(QString("Port %1 has an unsupported type.").arg(i));
return success;
}
}
if (lilv_plugin_has_feature(plugin, lv2_urid_map)) {
emit pluginStatusMessage(QString("Plugin is requesting urid_map feature"));
}
if (lilv_plugin_has_feature(plugin, lv2_urid_unmap)) {
emit pluginStatusMessage(QString("Plugin is requesting urid_unmap feature"));
}
if (lilv_plugin_has_feature(plugin, lv2_worker_schedule)) {
emit pluginStatusMessage(QString("Plugin is requesting worker_schedule feature"));
}
// At this point, the plugin is "discovered" and the temporary variables can be free'd:
lilv_node_free(lv2_connectionOptional);
lilv_node_free(lv2_ControlPort);
lilv_node_free(lv2_AudioPort);
lilv_node_free(lv2_OutputPort);
lilv_node_free(lv2_InputPort);
lilv_node_free(lv2_AtomPort);
free(values);
free(minValues);
free(maxValues);
// What persists is the "ports" holder, which is now populated,
// as well as the counters for input and output ports.
success = true;
return success;
}
void audioPlugin::unloadPlugin()
{
haveActivatedPlugin = false;
haveLoadedPlugin = false;
lilv_instance_deactivate(instance);
lilv_world_free(world);
emit pluginStatusMessage(QString("[%1] unloaded plugin [%2]")\
.arg(instanceString)\
.arg(pluginNameAsQString));
}
void audioPlugin::runPlugin(int samples)
{
// samples is the number of samples *at 8-8 bit stereo interlaced*
// The interface here is designed so that one can call it with the QByteArray.length() number.
if(inBypassMode)
return;
if(haveActivatedPlugin && haveExternalSinkBuffer && haveExternalSourceBuffer)
{
// Let's make these calculations only at one location in the code:
externalSampleCount = samples;
sourceBufferSampleCount = samples / 2; // 16-bit is half as many
inputBufferSampleCount = samples / 4; // 16-bit per-channel is 1/4 as many per channel.
// You can safely comment out any of these functions to determine where the crash is:
convertInputBuffer(); // seems ok
copyControlsToPluginInterface(); // seems ok
lilv_instance_run(instance, inputBufferSampleCount);
convertOutputBuffer(); // seems ok
} else {
emit pluginErrorMessage(QString("[%1] Tried to run without complete plugin activation.").arg(instanceString));
emit pluginErrorMessage(QString("[%1] has the following data: activated: %2, have sink: %3, have source: %4")\
.arg(instanceString)\
.arg(haveActivatedPlugin)\
.arg(haveExternalSinkBuffer)\
.arg(haveExternalSourceBuffer));
}
}
void audioPlugin::getPluginControlPorts(pluginInstanceInfoType info)
{
// For this, we should only be returning control ports, not audio or other ports,
// and we should be sending a copy of the port data, not pointers.
// We need to do this with an array of ports...
uint64_t instanceRequested = info.instanceID;
if(instanceRequested != this->instanceId)
{
emit pluginErrorMessage(QString("Plugin Controls Query received for instance 0x%1 but this is instance 0x%2.")\
.arg(instanceRequested, 16, 16, QChar('0'))\
.arg(instanceId, 16, 16, QChar('0')));
return;
}
if(haveActivatedPlugin)
{
emit haveControlPorts(controls);
} else {
emit pluginErrorMessage(QString("Cannot return plugin ports of unactivated plugin"));
}
}
void audioPlugin::handleAdjustedPluginControls(controlsType control)
{
// Access the local vector of controls, called "controls".
// Alter the control->controlsIndex member to have equal value.
if(control.controlsIndex <= (unsigned int)controls.size())
{
controls[control.controlsIndex].value = control.value;
} else {
emit pluginErrorMessage(QString("Error in plugin [%1][%4]: Asked to adjust control at index %2 when the max index is %3.")\
.arg(pluginNameAsQString)\
.arg(control.controlsIndex)\
.arg(controls.size())\
.arg(instanceString));
}
}
void audioPlugin::setBypass(bool bypass)
{
this->inBypassMode = bypass;
}
void audioPlugin::copyControlsToPluginInterface()
{
// Haven't figured out exactly how to do this one yet. Something like this:
// Loop across all ports, modify if matching index.
// Should I have used the n_params variable here instead of portIndex?
// I have assumed that "value" is the control value.
controlsType c;
for(int i=0; i < controls.size(); i++)
{
c = controls.at(i);
this->ports[ c.portIndex ].value = c.value;
}
}
void audioPlugin::convertInputBuffer()
{
// Copy from the interleaved uint16 buffer
// into the float buffer for the plugin to process
// Includes amplitude scaling into -1 to +1
// externalSourceBuffer --> scale --> inputBuffer
float max = 65535.0f / 2.0f;
//float mid = max / 2.0f;
float scalingFactor = 1.0f / max;
// Note, we have already cast the 8MSB, 8LSB type data into a 16-bit holder
// So accessing one member should result in a full 16-bit number.
for (int n = 0; n < sourceBufferSampleCount; n += 2) {
inputBuffer[0][n/2] = ( externalSourceBuffer[n]) * scalingFactor;
inputBuffer[1][n/2] = ( externalSourceBuffer[n+1]) * scalingFactor;
// inputBuffer[0][n/2] = (externalSourceBuffer[n] - mid) * scalingFactor;
// inputBuffer[1][n/2] = (externalSourceBuffer[n+1] - mid) * scalingFactor;
}
}
void audioPlugin::convertOutputBuffer()
{
// Copy from the plugin float output
// to the interleaved uint16 buffer for the audioHandler.
// Includes amplitude scaling back to uint16 levels.
// Also converts to "dual mono" if so requested, copying
// the channel 0 data over to channel 1.
// Useful for mono plugins that do not create a second output stream.
// outputBuffer --> scale --> externalSinkBuffer
float max = 65535.0f;
float mid = max / 2.0f; // set to zero for muted testing
// copy through test:
// Copy input to output:
// for (int n = 0; n+1 < sourceBufferSampleCount; n += 1) {
// externalSinkBuffer[n] = externalSourceBuffer[n];
// }
// Copy float of input to output:
// for (int n = 0; n+1 < sourceBufferSampleCount; n += 2) {
// externalSinkBuffer[n] = (inputBuffer[0][n/2] + 1) * mid; // L
// externalSinkBuffer[n+1] = (inputBuffer[0][n/2] + 1) * mid; // Copy L
// }
if(forceOutputMono)
{
for (int n = 0; n+1 < sourceBufferSampleCount; n += 2) {
externalSinkBuffer[n] = (outputBuffer[0][n/2]) * mid; // L
externalSinkBuffer[n+1] = (outputBuffer[0][n/2]) * mid; // Copy L
}
} else {
for (int n = 0; n+1 < sourceBufferSampleCount; n += 2) {
externalSinkBuffer[n] = (outputBuffer[0][n/2]) * mid; // L
externalSinkBuffer[n+1] = (outputBuffer[1][n/2]) * mid; // R
}
}
}
void audioPlugin::makeByteTable()
{
for(unsigned int i=0; i < 65535; i++)
{
byteTable[i] = ((i&0x00ff) << 8) | ( (i&0xff00) >> 8 ) ;
}
}
QString audioPlugin::getPLuginControlInfo()
{
QString info = QString("Plugin controls information for plugin named [%1] on instance [%2]: \n")\
.arg(pluginNameAsQString)\
.arg(instanceString);
controlsType c;
for(int i = 0; i < controls.size(); i ++)
{
c = controls.at(i);
info.append(QString("Control %1: port index: %7, min: %2, max: %3, default: %4, current value: %5, name: [%6] \n")\
.arg(c.controlsIndex)
.arg(c.min)\
.arg(c.max)\
.arg(c.def)\
.arg(c.value)\
.arg(c.label)\
.arg(c.portIndex));
}
return info;
}
LV2_URID audioPlugin::urid_map(LV2_URID_Map_Handle handle, const char* uri)
{
audioPlugin* lv2 = (audioPlugin*)handle;
LV2_URID urid;
std::string key = uri;
if (lv2->urid_map_data.find(key) == lv2->urid_map_data.end()) {
urid = lv2->current_urid++;
lv2->urid_map_data[key] = urid;
}
else {
urid = lv2->urid_map_data[key];
}
return urid;
}
const char* audioPlugin::urid_unmap(void* handle, LV2_URID urid)
{
audioPlugin* lv2 = (audioPlugin*)handle;
const char* value = nullptr;
for (auto const& p : lv2->urid_map_data) {
if (p.second == urid) {
value = p.first.c_str();
}
}
return value;
}
void audioPlugin::debugThis()
{
qDebug() << __PRETTY_FUNCTION__ << "Reached plugin debug function for instance" << instanceString;
emit pluginStatusMessage(getPLuginControlInfo());
}