From bc6904077f3d2e8edc0d25d17d80e1b4d257593f Mon Sep 17 00:00:00 2001 From: "lllucius@gmail.com" Date: Tue, 27 May 2014 02:06:00 +0000 Subject: [PATCH] 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. --- src/effects/lv2/LV2Effect.cpp | 208 +++++++++++++++++++++++++--------- src/effects/lv2/LV2Effect.h | 1 + src/effects/lv2/LoadLV2.cpp | 10 ++ src/effects/lv2/LoadLV2.h | 2 + 4 files changed, 165 insertions(+), 56 deletions(-) diff --git a/src/effects/lv2/LV2Effect.cpp b/src/effects/lv2/LV2Effect.cpp index 7e916b950..23b4ec0b4 100644 --- a/src/effects/lv2/LV2Effect.cpp +++ b/src/effects/lv2/LV2Effect.cpp @@ -58,7 +58,8 @@ LV2Effect::LV2Effect(const LilvPlugin *data, const std::set & categories) : mValid(true), mCategories(categories), - mMidiInput(0) + mMidiInput(0), + mLatencyPortIndex(-1) { // 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 *maximumValues = new float [numPorts]; float *defaultValues = new float [numPorts]; - + // Retrieve the port ranges for all ports (some values may be NaN) lilv_plugin_get_port_ranges_float(mData, minimumValues, maximumValues, defaultValues); @@ -95,10 +96,10 @@ LV2Effect::LV2Effect(const LilvPlugin *data, { const LilvPort *port = lilv_plugin_get_port_by_index(mData, i); LV2Port internalPort; - internalPort.mIndex = lilv_port_get_index(data, port); + internalPort.mIndex = lilv_port_get_index(mData, port); // Get the port name - LilvNode *tmpName = lilv_port_get_name(data, port); + LilvNode *tmpName = lilv_port_get_name(mData, port); internalPort.mName = GetString(tmpName); lilv_node_free(tmpName); @@ -128,7 +129,7 @@ LV2Effect::LV2Effect(const LilvPlugin *data, } else { - // Should happen, but provide something + // Shouldn't happen, but provide something label = uri; } lilv_nodes_free(groups); @@ -178,7 +179,7 @@ LV2Effect::LV2Effect(const LilvPlugin *data, } } 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.mMin = minimumValues[i]; @@ -197,29 +198,47 @@ LV2Effect::LV2Effect(const LilvPlugin *data, internalPort.mControlBuffer = maximumValues[i]; } - if (lilv_port_has_property(data, port, gPortToggled)) + if (lilv_port_has_property(mData, port, gPortToggled)) { internalPort.mToggle = true; } - else if (lilv_port_has_property(data, port, gPortIsInteger)) + else if (lilv_port_has_property(mData, port, gPortIsInteger)) { internalPort.mInteger = true; } - else if (lilv_port_has_property(data, port, gPortIsSampleRate)) + else if (lilv_port_has_property(mData, port, gPortIsSampleRate)) { internalPort.mSampleRate = true; } - else if (lilv_port_has_property(data, port, gPortIsEnumeration)) + else if (lilv_port_has_property(mData, port, gPortIsEnumeration)) { internalPort.mEnumeration = true; } 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) && 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) { mValid = false; @@ -502,7 +521,13 @@ bool LV2Effect::ProcessStereo(int count, lilv_instance_connect_port(handle, mControlOutputs[p].mIndex, &mControlOutputs[p].mControlBuffer); } - + + float latency = 0.0; + if (mLatencyPortIndex >= 0) + { + lilv_instance_connect_port(handle, mLatencyPortIndex, &latency); + } + lilv_instance_activate(handle); // Actually perform the effect here @@ -510,58 +535,129 @@ bool LV2Effect::ProcessStereo(int count, sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; + sampleCount ols = ls; + sampleCount ors = rs; bool noteOver = false; - while (len) + + sampleCount delayed = 0; + sampleCount delay = 0; + bool cleared = false; + + while (len || delayed) { int block = mBlockSize; - if (block > len) - { - block = len; - } - if (left && mAudioInputs.GetCount() > 0) + if (len) { - left->Get((samplePtr)fInBuffer[0], floatSample, ls, block); - } - - 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) + if (block > len) { - 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; + block = len; + } + + if (left && mAudioInputs.GetCount() > 0) + { + 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) { diff --git a/src/effects/lv2/LV2Effect.h b/src/effects/lv2/LV2Effect.h index 3a24428c3..3d6869e84 100644 --- a/src/effects/lv2/LV2Effect.h +++ b/src/effects/lv2/LV2Effect.h @@ -124,6 +124,7 @@ private: LV2PortArray mAudioInputs; LV2PortArray mAudioOutputs; LV2Port *mMidiInput; + int mLatencyPortIndex; sampleCount mNoteLength; unsigned char mNoteVelocity; diff --git a/src/effects/lv2/LoadLV2.cpp b/src/effects/lv2/LoadLV2.cpp index 0cd41e003..5561e9510 100644 --- a/src/effects/lv2/LoadLV2.cpp +++ b/src/effects/lv2/LoadLV2.cpp @@ -90,6 +90,8 @@ LilvNode *gPortToggled; LilvNode *gPortIsInteger; LilvNode *gPortIsSampleRate; LilvNode *gPortIsEnumeration; +LilvNode *gPortIsLatency; +LilvNode *gPortIsOptional; LilvNode *gName; LilvNode *gPortGroup; LilvNode *gSubGroupOf; @@ -123,6 +125,8 @@ void LoadLV2Plugins() gPortIsInteger = lilv_new_uri(gWorld, LV2_CORE__integer); gPortIsSampleRate = lilv_new_uri(gWorld, LV2_CORE__sampleRate); 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); gPortGroup = lilv_new_uri(gWorld, LV2_PORT_GROUPS__group); gSubGroupOf = lilv_new_uri(gWorld, LV2_PORT_GROUPS__subGroupOf); @@ -200,6 +204,12 @@ void UnloadLV2Plugins() lilv_node_free(gPortIsEnumeration); gPortIsEnumeration = NULL; + lilv_node_free(gPortIsLatency); + gPortIsLatency = NULL; + + lilv_node_free(gPortIsOptional); + gPortIsOptional = NULL; + lilv_node_free(gName); gName = NULL; diff --git a/src/effects/lv2/LoadLV2.h b/src/effects/lv2/LoadLV2.h index 21ba61070..3869a00cd 100644 --- a/src/effects/lv2/LoadLV2.h +++ b/src/effects/lv2/LoadLV2.h @@ -28,6 +28,8 @@ extern LilvNode *gPortToggled; extern LilvNode *gPortIsInteger; extern LilvNode *gPortIsSampleRate; extern LilvNode *gPortIsEnumeration; +extern LilvNode *gPortIsLatency; +extern LilvNode *gPortIsOptional; extern LilvNode *gName; extern LilvNode *gPortGroup; extern LilvNode *gSubGroupOf;