1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-01 14:43:48 +01:00

Fixing a crash exposed by the kn0ck0ut LV2 plugin.

After reporting the crash to the kn0ck0ut LV2 maintainer, he pointed out that
Audacity wasn't connecting to all required LV2 ports.  In this case it was the
latency port.  This corrects that oversight.

In addition, since I was fiddling with the latency port, I figured I might as
well go ahead and add the necessary code to compensate for it.
This commit is contained in:
lllucius@gmail.com
2014-05-27 02:06:00 +00:00
parent d50f893864
commit bc6904077f
4 changed files with 165 additions and 56 deletions

View File

@@ -58,7 +58,8 @@ LV2Effect::LV2Effect(const LilvPlugin *data,
const std::set<wxString> & categories) const std::set<wxString> & categories)
: mValid(true), : mValid(true),
mCategories(categories), mCategories(categories),
mMidiInput(0) mMidiInput(0),
mLatencyPortIndex(-1)
{ {
// We don't support any features at all, so if the plugin requires // We don't support any features at all, so if the plugin requires
@@ -85,7 +86,7 @@ LV2Effect::LV2Effect(const LilvPlugin *data,
float *minimumValues = new float [numPorts]; float *minimumValues = new float [numPorts];
float *maximumValues = new float [numPorts]; float *maximumValues = new float [numPorts];
float *defaultValues = new float [numPorts]; float *defaultValues = new float [numPorts];
// Retrieve the port ranges for all ports (some values may be NaN) // Retrieve the port ranges for all ports (some values may be NaN)
lilv_plugin_get_port_ranges_float(mData, minimumValues, lilv_plugin_get_port_ranges_float(mData, minimumValues,
maximumValues, defaultValues); maximumValues, defaultValues);
@@ -95,10 +96,10 @@ LV2Effect::LV2Effect(const LilvPlugin *data,
{ {
const LilvPort *port = lilv_plugin_get_port_by_index(mData, i); const LilvPort *port = lilv_plugin_get_port_by_index(mData, i);
LV2Port internalPort; LV2Port internalPort;
internalPort.mIndex = lilv_port_get_index(data, port); internalPort.mIndex = lilv_port_get_index(mData, port);
// Get the port name // Get the port name
LilvNode *tmpName = lilv_port_get_name(data, port); LilvNode *tmpName = lilv_port_get_name(mData, port);
internalPort.mName = GetString(tmpName); internalPort.mName = GetString(tmpName);
lilv_node_free(tmpName); lilv_node_free(tmpName);
@@ -128,7 +129,7 @@ LV2Effect::LV2Effect(const LilvPlugin *data,
} }
else else
{ {
// Should happen, but provide something // Shouldn't happen, but provide something
label = uri; label = uri;
} }
lilv_nodes_free(groups); lilv_nodes_free(groups);
@@ -178,7 +179,7 @@ LV2Effect::LV2Effect(const LilvPlugin *data,
} }
} }
else if (lilv_port_is_a(mData, port, gControlPortClass) && else if (lilv_port_is_a(mData, port, gControlPortClass) &&
lilv_port_is_a(mData, port, gInputPortClass)) lilv_port_is_a(mData, port, gInputPortClass))
{ {
internalPort.mControlBuffer = float(1.0); internalPort.mControlBuffer = float(1.0);
internalPort.mMin = minimumValues[i]; internalPort.mMin = minimumValues[i];
@@ -197,29 +198,47 @@ LV2Effect::LV2Effect(const LilvPlugin *data,
internalPort.mControlBuffer = maximumValues[i]; internalPort.mControlBuffer = maximumValues[i];
} }
if (lilv_port_has_property(data, port, gPortToggled)) if (lilv_port_has_property(mData, port, gPortToggled))
{ {
internalPort.mToggle = true; internalPort.mToggle = true;
} }
else if (lilv_port_has_property(data, port, gPortIsInteger)) else if (lilv_port_has_property(mData, port, gPortIsInteger))
{ {
internalPort.mInteger = true; internalPort.mInteger = true;
} }
else if (lilv_port_has_property(data, port, gPortIsSampleRate)) else if (lilv_port_has_property(mData, port, gPortIsSampleRate))
{ {
internalPort.mSampleRate = true; internalPort.mSampleRate = true;
} }
else if (lilv_port_has_property(data, port, gPortIsEnumeration)) else if (lilv_port_has_property(mData, port, gPortIsEnumeration))
{ {
internalPort.mEnumeration = true; internalPort.mEnumeration = true;
} }
mControlInputs.Add(internalPort); mControlInputs.Add(internalPort);
} }
else if (lilv_port_is_a(mData, port, gControlPortClass) &&
lilv_port_is_a(mData, port, gOutputPortClass))
{
// If there is more than one latency port, the plugin is invalid
if (lilv_port_has_property(mData, port, gPortIsLatency))
{
if (mLatencyPortIndex >= 0)
{
mValid = false;
continue;
}
mLatencyPortIndex = i;
}
else if (!lilv_port_has_property(mData, port, gPortIsOptional))
{
mControlOutputs.Add(internalPort);
}
}
else if (lilv_port_is_a(mData, port, gMidiPortClass) && else if (lilv_port_is_a(mData, port, gMidiPortClass) &&
lilv_port_is_a(mData, port, gInputPortClass)) lilv_port_is_a(mData, port, gInputPortClass))
{ {
// If there are more than one MIDI input ports, the plugin is invalid // If there is more than one MIDI input port, the plugin is invalid
if (mMidiInput) if (mMidiInput)
{ {
mValid = false; mValid = false;
@@ -502,7 +521,13 @@ bool LV2Effect::ProcessStereo(int count,
lilv_instance_connect_port(handle, mControlOutputs[p].mIndex, lilv_instance_connect_port(handle, mControlOutputs[p].mIndex,
&mControlOutputs[p].mControlBuffer); &mControlOutputs[p].mControlBuffer);
} }
float latency = 0.0;
if (mLatencyPortIndex >= 0)
{
lilv_instance_connect_port(handle, mLatencyPortIndex, &latency);
}
lilv_instance_activate(handle); lilv_instance_activate(handle);
// Actually perform the effect here // Actually perform the effect here
@@ -510,58 +535,129 @@ bool LV2Effect::ProcessStereo(int count,
sampleCount originalLen = len; sampleCount originalLen = len;
sampleCount ls = lstart; sampleCount ls = lstart;
sampleCount rs = rstart; sampleCount rs = rstart;
sampleCount ols = ls;
sampleCount ors = rs;
bool noteOver = false; bool noteOver = false;
while (len)
sampleCount delayed = 0;
sampleCount delay = 0;
bool cleared = false;
while (len || delayed)
{ {
int block = mBlockSize; int block = mBlockSize;
if (block > len)
{
block = len;
}
if (left && mAudioInputs.GetCount() > 0) if (len)
{ {
left->Get((samplePtr)fInBuffer[0], floatSample, ls, block); if (block > len)
}
if (right && mAudioInputs.GetCount() > 1)
{
right->Get((samplePtr)fInBuffer[1], floatSample, rs, block);
}
lilv_instance_run(handle, block);
if (left && mAudioOutputs.GetCount() > 0)
{
left->Set((samplePtr)fOutBuffer[0], floatSample, ls, block);
}
if (right && mAudioOutputs.GetCount() > 1)
{
right->Set((samplePtr)fOutBuffer[1], floatSample, rs, block);
}
len -= block;
noteOffTime -= block;
ls += block;
rs += block;
// Clear the event buffer and add the note off event if needed
if (mMidiInput)
{
lv2_event_buffer_reset(midiBuffer, 1,
(uint8_t *)midiBuffer +
sizeof(LV2_Event_Buffer));
if (!noteOver && noteOffTime < len && noteOffTime < block)
{ {
LV2_Event_Iterator iter; block = len;
lv2_event_begin(&iter, midiBuffer); }
uint8_t noteOff[] = { 0x80, mNoteKey, 64 };
lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); if (left && mAudioInputs.GetCount() > 0)
noteOver = true; {
left->Get((samplePtr)fInBuffer[0], floatSample, ls, block);
}
if (right && mAudioInputs.GetCount() > 1)
{
right->Get((samplePtr)fInBuffer[1], floatSample, rs, block);
} }
} }
else if (delayed)
{
// At the end if we don't have enough left for a whole block
if (block > delayed)
{
block = delayed;
}
// Clear the input buffer so that we only pass zeros to the effect.
if (!cleared)
{
for (int i = 0; i < mBlockSize; i++)
{
fInBuffer[0][i] = 0.0;
}
if (right)
{
memcpy(fInBuffer[1], fOutBuffer[0], mBlockSize);
}
cleared = true;
}
}
lilv_instance_run(handle, block);
if (delayed == 0 && latency != 0)
{
delayed = delay = latency;
}
if (delay >= block)
{
delay -= block;
}
else if (delay > 0)
{
sampleCount oblock = block - delay;
if (left && mAudioOutputs.GetCount() > 0)
{
left->Set((samplePtr)(fOutBuffer[0] + delay), floatSample, ols, oblock);
}
if (right && mAudioOutputs.GetCount() > 1)
{
right->Set((samplePtr)(fOutBuffer[1] + delay), floatSample, ors, oblock);
}
ols += oblock;
ors += oblock;
delay = 0;
}
else
{
if (left && mAudioOutputs.GetCount() > 0)
{
left->Set((samplePtr)fOutBuffer[0], floatSample, ols, block);
}
if (right && mAudioOutputs.GetCount() > 1)
{
right->Set((samplePtr)fOutBuffer[1], floatSample, ors, block);
}
ols += block;
ors += block;
}
if (len)
{
len -= block;
noteOffTime -= block;
// Clear the event buffer and add the note off event if needed
if (mMidiInput)
{
lv2_event_buffer_reset(midiBuffer, 1,
(uint8_t *)midiBuffer +
sizeof(LV2_Event_Buffer));
if (!noteOver && noteOffTime < len && noteOffTime < block)
{
LV2_Event_Iterator iter;
lv2_event_begin(&iter, midiBuffer);
uint8_t noteOff[] = { 0x80, mNoteKey, 64 };
lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff);
noteOver = true;
}
}
}
else if (delayed)
{
delayed -= block;
}
ls += block;
rs += block;
if (mAudioInputs.GetCount() > 1) if (mAudioInputs.GetCount() > 1)
{ {

View File

@@ -124,6 +124,7 @@ private:
LV2PortArray mAudioInputs; LV2PortArray mAudioInputs;
LV2PortArray mAudioOutputs; LV2PortArray mAudioOutputs;
LV2Port *mMidiInput; LV2Port *mMidiInput;
int mLatencyPortIndex;
sampleCount mNoteLength; sampleCount mNoteLength;
unsigned char mNoteVelocity; unsigned char mNoteVelocity;

View File

@@ -90,6 +90,8 @@ LilvNode *gPortToggled;
LilvNode *gPortIsInteger; LilvNode *gPortIsInteger;
LilvNode *gPortIsSampleRate; LilvNode *gPortIsSampleRate;
LilvNode *gPortIsEnumeration; LilvNode *gPortIsEnumeration;
LilvNode *gPortIsLatency;
LilvNode *gPortIsOptional;
LilvNode *gName; LilvNode *gName;
LilvNode *gPortGroup; LilvNode *gPortGroup;
LilvNode *gSubGroupOf; LilvNode *gSubGroupOf;
@@ -123,6 +125,8 @@ void LoadLV2Plugins()
gPortIsInteger = lilv_new_uri(gWorld, LV2_CORE__integer); gPortIsInteger = lilv_new_uri(gWorld, LV2_CORE__integer);
gPortIsSampleRate = lilv_new_uri(gWorld, LV2_CORE__sampleRate); gPortIsSampleRate = lilv_new_uri(gWorld, LV2_CORE__sampleRate);
gPortIsEnumeration = lilv_new_uri(gWorld, LV2_CORE__enumeration); gPortIsEnumeration = lilv_new_uri(gWorld, LV2_CORE__enumeration);
gPortIsLatency = lilv_new_uri(gWorld, LV2_CORE__reportsLatency);
gPortIsOptional = lilv_new_uri(gWorld, LV2_CORE__connectionOptional);
gName = lilv_new_uri(gWorld, LV2_CORE__name); gName = lilv_new_uri(gWorld, LV2_CORE__name);
gPortGroup = lilv_new_uri(gWorld, LV2_PORT_GROUPS__group); gPortGroup = lilv_new_uri(gWorld, LV2_PORT_GROUPS__group);
gSubGroupOf = lilv_new_uri(gWorld, LV2_PORT_GROUPS__subGroupOf); gSubGroupOf = lilv_new_uri(gWorld, LV2_PORT_GROUPS__subGroupOf);
@@ -200,6 +204,12 @@ void UnloadLV2Plugins()
lilv_node_free(gPortIsEnumeration); lilv_node_free(gPortIsEnumeration);
gPortIsEnumeration = NULL; gPortIsEnumeration = NULL;
lilv_node_free(gPortIsLatency);
gPortIsLatency = NULL;
lilv_node_free(gPortIsOptional);
gPortIsOptional = NULL;
lilv_node_free(gName); lilv_node_free(gName);
gName = NULL; gName = NULL;

View File

@@ -28,6 +28,8 @@ extern LilvNode *gPortToggled;
extern LilvNode *gPortIsInteger; extern LilvNode *gPortIsInteger;
extern LilvNode *gPortIsSampleRate; extern LilvNode *gPortIsSampleRate;
extern LilvNode *gPortIsEnumeration; extern LilvNode *gPortIsEnumeration;
extern LilvNode *gPortIsLatency;
extern LilvNode *gPortIsOptional;
extern LilvNode *gName; extern LilvNode *gName;
extern LilvNode *gPortGroup; extern LilvNode *gPortGroup;
extern LilvNode *gSubGroupOf; extern LilvNode *gSubGroupOf;