@@ -266,6 +266,9 @@ | |||
<addaction name="act_canvas_zoom_out"/> | |||
<addaction name="act_canvas_zoom_100"/> | |||
</widget> | |||
<addaction name="act_canvas_show_internal"/> | |||
<addaction name="act_canvas_show_external"/> | |||
<addaction name="separator"/> | |||
<addaction name="act_canvas_arrange"/> | |||
<addaction name="act_canvas_refresh"/> | |||
<addaction name="menu_Canvas_Zoom"/> | |||
@@ -797,6 +797,11 @@ | |||
<string>Continuous Rack</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>Patchbay</string> | |||
</property> | |||
</item> | |||
</widget> | |||
</item> | |||
</layout> | |||
@@ -816,6 +821,11 @@ | |||
<string>Continuous Rack</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>Patchbay</string> | |||
</property> | |||
</item> | |||
</widget> | |||
</item> | |||
</layout> | |||
@@ -25,6 +25,7 @@ | |||
#include "CarlaMIDI.h" | |||
using water::jmax; | |||
using water::jmin; | |||
using water::AudioProcessor; | |||
using water::MidiBuffer; | |||
using water::String; | |||
@@ -1152,14 +1153,7 @@ public: | |||
void processBlock(AudioSampleBuffer& audio, MidiBuffer& midi) override | |||
{ | |||
if (fPlugin == nullptr || ! fPlugin->isEnabled()) | |||
{ | |||
audio.clear(); | |||
midi.clear(); | |||
return; | |||
} | |||
if (! fPlugin->tryLock(kEngine->isOffline())) | |||
if (fPlugin == nullptr || ! fPlugin->isEnabled() || ! fPlugin->tryLock(kEngine->isOffline())) | |||
{ | |||
audio.clear(); | |||
midi.clear(); | |||
@@ -1181,7 +1175,7 @@ public: | |||
// TODO - CV support | |||
const int numSamples(audio.getNumSamples()); | |||
const uint32_t numSamples(static_cast<uint32_t>(audio.getNumSamples())); | |||
if (const int numChan = audio.getNumChannels()) | |||
{ | |||
@@ -1193,33 +1187,22 @@ public: | |||
for (int i=0; i<numChan; ++i) | |||
audioBuffers[i] = audio.getWritePointer(i); | |||
#if 0 | |||
float inPeaks[2] = { 0.0f }; | |||
float outPeaks[2] = { 0.0f }; | |||
juce::Range<float> range; | |||
for (int i=static_cast<int>(jmin(fPlugin->getAudioInCount(), 2U)); --i>=0;) | |||
{ | |||
range = FloatVectorOperations::findMinAndMax(audioBuffers[i], numSamples); | |||
inPeaks[i] = carla_maxLimited<float>(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f); | |||
} | |||
#endif | |||
for (uint32_t i=0, count=jmin(fPlugin->getAudioInCount(), 2U); i<count; ++i) | |||
inPeaks[i] = carla_findMaxNormalizedFloat(audioBuffers[i], numSamples); | |||
fPlugin->process(const_cast<const float**>(audioBuffers), audioBuffers, nullptr, nullptr, static_cast<uint32_t>(numSamples)); | |||
fPlugin->process(const_cast<const float**>(audioBuffers), audioBuffers, nullptr, nullptr, numSamples); | |||
#if 0 | |||
for (int i=static_cast<int>(jmin(fPlugin->getAudioOutCount(), 2U)); --i>=0;) | |||
{ | |||
range = FloatVectorOperations::findMinAndMax(audioBuffers[i], numSamples); | |||
outPeaks[i] = carla_maxLimited<float>(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f); | |||
} | |||
for (uint32_t i=0, count=jmin(fPlugin->getAudioOutCount(), 2U); i<count; ++i) | |||
outPeaks[i] = carla_findMaxNormalizedFloat(audioBuffers[i], numSamples); | |||
kEngine->setPluginPeaks(fPlugin->getId(), inPeaks, outPeaks); | |||
#endif | |||
} | |||
else | |||
{ | |||
fPlugin->process(nullptr, nullptr, nullptr, nullptr, static_cast<uint32_t>(numSamples)); | |||
fPlugin->process(nullptr, nullptr, nullptr, nullptr, numSamples); | |||
} | |||
midi.clear(); | |||
@@ -1302,7 +1285,8 @@ private: | |||
}; | |||
PatchbayGraph::PatchbayGraph(CarlaEngine* const engine, const uint32_t ins, const uint32_t outs) | |||
: connections(), | |||
: CarlaThread("PatchbayReorderThread"), | |||
connections(), | |||
graph(), | |||
audioBuffer(), | |||
midiBuffer(), | |||
@@ -1390,10 +1374,14 @@ PatchbayGraph::PatchbayGraph(CarlaEngine* const engine, const uint32_t ins, cons | |||
node->properties.set("isMIDI", true); | |||
node->properties.set("isOSC", false); | |||
} | |||
startThread(); | |||
} | |||
PatchbayGraph::~PatchbayGraph() | |||
{ | |||
stopThread(-1); | |||
connections.clear(); | |||
extGraph.clear(); | |||
@@ -1852,6 +1840,15 @@ void PatchbayGraph::process(CarlaEngine::ProtectedData* const data, const float* | |||
} | |||
} | |||
void PatchbayGraph::run() | |||
{ | |||
while (! shouldThreadExit()) | |||
{ | |||
carla_msleep(100); | |||
graph.reorderNowIfNeeded(); | |||
} | |||
} | |||
// ----------------------------------------------------------------------- | |||
// InternalGraph | |||
@@ -22,6 +22,7 @@ | |||
#include "CarlaMutex.hpp" | |||
#include "CarlaPatchbayUtils.hpp" | |||
#include "CarlaStringList.hpp" | |||
#include "CarlaThread.hpp" | |||
#include "water/water.h" | |||
@@ -145,7 +146,8 @@ struct RackGraph { | |||
// ----------------------------------------------------------------------- | |||
// PatchbayGraph | |||
struct PatchbayGraph { | |||
class PatchbayGraph : private CarlaThread { | |||
public: | |||
PatchbayConnectionList connections; | |||
AudioProcessorGraph graph; | |||
AudioSampleBuffer audioBuffer; | |||
@@ -180,6 +182,9 @@ struct PatchbayGraph { | |||
void process(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const int frames); | |||
private: | |||
void run() override; | |||
CarlaEngine* const kEngine; | |||
CARLA_DECLARE_NON_COPY_CLASS(PatchbayGraph) | |||
}; | |||
@@ -202,9 +202,9 @@ class CarlaSettingsW(QDialog): | |||
PATH_INDEX_DSSI = 1 | |||
PATH_INDEX_LV2 = 2 | |||
PATH_INDEX_VST2 = 3 | |||
PATH_INDEX_GIG = 5 | |||
PATH_INDEX_SF2 = 6 | |||
PATH_INDEX_SFZ = 7 | |||
PATH_INDEX_GIG = 4 | |||
PATH_INDEX_SF2 = 5 | |||
PATH_INDEX_SFZ = 6 | |||
# Single and Multiple client mode is only for JACK, | |||
# but we still want to match QComboBox index to backend defines, | |||
@@ -64,7 +64,6 @@ public: | |||
virtual void cleanUp (ValueUnion&) const noexcept {} | |||
virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; } | |||
virtual bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept = 0; | |||
virtual void writeToStream (const ValueUnion& data, OutputStream& output) const = 0; | |||
}; | |||
//============================================================================== | |||
@@ -76,7 +75,6 @@ public: | |||
bool isVoid() const noexcept override { return true; } | |||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); } | |||
}; | |||
//============================================================================== | |||
@@ -89,12 +87,6 @@ public: | |||
bool isUndefined() const noexcept override { return true; } | |||
String toString (const ValueUnion&) const override { return "undefined"; } | |||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (1); | |||
output.writeByte (varMarker_Undefined); | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -118,13 +110,6 @@ public: | |||
return otherType.toInt (otherData) == data.intValue; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (5); | |||
output.writeByte (varMarker_Int); | |||
output.writeInt (data.intValue); | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -148,13 +133,6 @@ public: | |||
return otherType.toInt64 (otherData) == data.int64Value; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (9); | |||
output.writeByte (varMarker_Int64); | |||
output.writeInt64 (data.int64Value); | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -175,13 +153,6 @@ public: | |||
{ | |||
return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits<double>::epsilon(); | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (9); | |||
output.writeByte (varMarker_Double); | |||
output.writeDouble (data.doubleValue); | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -202,12 +173,6 @@ public: | |||
{ | |||
return otherType.toBool (otherData) == data.boolValue; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (1); | |||
output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse); | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -234,17 +199,6 @@ public: | |||
return otherType.toString (otherData) == *getString (data); | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
const String* const s = getString (data); | |||
const size_t len = s->getNumBytesAsUTF8() + 1; | |||
HeapBlock<char> temp (len); | |||
s->copyToUTF8 (temp, len); | |||
output.writeCompressedInt ((int) (len + 1)); | |||
output.writeByte (varMarker_String); | |||
output.write (temp, len); | |||
} | |||
private: | |||
static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast<const String*> (data.stringValue); } | |||
static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast<String*> (data.stringValue); } | |||