mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-01 22:53:50 +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:
@@ -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
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
@@ -503,6 +522,12 @@ bool LV2Effect::ProcessStereo(int count,
|
|||||||
&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,10 +535,20 @@ 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 (len)
|
||||||
|
{
|
||||||
if (block > len)
|
if (block > len)
|
||||||
{
|
{
|
||||||
block = len;
|
block = len;
|
||||||
@@ -528,23 +563,77 @@ bool LV2Effect::ProcessStereo(int count,
|
|||||||
{
|
{
|
||||||
right->Get((samplePtr)fInBuffer[1], floatSample, rs, block);
|
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);
|
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)
|
if (left && mAudioOutputs.GetCount() > 0)
|
||||||
{
|
{
|
||||||
left->Set((samplePtr)fOutBuffer[0], floatSample, ls, block);
|
left->Set((samplePtr)(fOutBuffer[0] + delay), floatSample, ols, oblock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (right && mAudioOutputs.GetCount() > 1)
|
if (right && mAudioOutputs.GetCount() > 1)
|
||||||
{
|
{
|
||||||
right->Set((samplePtr)fOutBuffer[1], floatSample, rs, block);
|
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;
|
len -= block;
|
||||||
noteOffTime -= block;
|
noteOffTime -= block;
|
||||||
ls += block;
|
|
||||||
rs += block;
|
|
||||||
|
|
||||||
// Clear the event buffer and add the note off event if needed
|
// Clear the event buffer and add the note off event if needed
|
||||||
if (mMidiInput)
|
if (mMidiInput)
|
||||||
@@ -562,6 +651,13 @@ bool LV2Effect::ProcessStereo(int count,
|
|||||||
noteOver = true;
|
noteOver = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (delayed)
|
||||||
|
{
|
||||||
|
delayed -= block;
|
||||||
|
}
|
||||||
|
ls += block;
|
||||||
|
rs += block;
|
||||||
|
|
||||||
if (mAudioInputs.GetCount() > 1)
|
if (mAudioInputs.GetCount() > 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user