Browse Source

Implement CV for internal patchbay mode

Signed-off-by: falkTX <falktx@falktx.com>
tags/v2.1-rc1
falkTX 5 years ago
parent
commit
399fc3b4bc
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
8 changed files with 881 additions and 334 deletions
  1. +0
    -5
      source/backend/engine/CarlaEngine.cpp
  2. +263
    -93
      source/backend/engine/CarlaEngineGraph.cpp
  3. +26
    -0
      source/modules/sfzero/README.md
  4. +6
    -0
      source/modules/water/memory/HeapBlock.h
  5. +66
    -7
      source/modules/water/processors/AudioProcessor.cpp
  6. +25
    -37
      source/modules/water/processors/AudioProcessor.h
  7. +467
    -177
      source/modules/water/processors/AudioProcessorGraph.cpp
  8. +28
    -15
      source/modules/water/processors/AudioProcessorGraph.h

+ 0
- 5
source/backend/engine/CarlaEngine.cpp View File

@@ -596,11 +596,6 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
setLastError("Carla's patchbay mode cannot work with plugins that have multiple MIDI ports, sorry!"); setLastError("Carla's patchbay mode cannot work with plugins that have multiple MIDI ports, sorry!");
canRun = false; canRun = false;
} }
else if (plugin->getCVInCount() > 0 || plugin->getCVInCount() > 0)
{
setLastError("CV ports in patchbay mode is still TODO");
canRun = false;
}
} }


if (! canRun) if (! canRun)


+ 263
- 93
source/backend/engine/CarlaEngineGraph.cpp View File

@@ -1120,37 +1120,55 @@ void RackGraph::processHelper(CarlaEngine::ProtectedData* const data, const floa
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Patchbay Graph stuff // Patchbay Graph stuff


static const uint32_t kAudioInputPortOffset = MAX_PATCHBAY_PLUGINS*1;
static const uint32_t kAudioOutputPortOffset = MAX_PATCHBAY_PLUGINS*2;
static const uint32_t kMidiInputPortOffset = MAX_PATCHBAY_PLUGINS*3;
static const uint32_t kMidiOutputPortOffset = MAX_PATCHBAY_PLUGINS*3+1;

static const uint kMidiChannelIndex = static_cast<uint>(AudioProcessorGraph::midiChannelIndex);
static const uint32_t kMaxPortsPerPlugin = 255;
static const uint32_t kAudioInputPortOffset = kMaxPortsPerPlugin*1;
static const uint32_t kAudioOutputPortOffset = kMaxPortsPerPlugin*2;
static const uint32_t kCVInputPortOffset = kMaxPortsPerPlugin*3;
static const uint32_t kCVOutputPortOffset = kMaxPortsPerPlugin*4;
static const uint32_t kMidiInputPortOffset = kMaxPortsPerPlugin*5;
static const uint32_t kMidiOutputPortOffset = kMaxPortsPerPlugin*6;
static const uint32_t kMaxPortOffset = kMaxPortsPerPlugin*7;


static inline static inline
bool adjustPatchbayPortIdForWater(uint& portId)
bool adjustPatchbayPortIdForWater(AudioProcessor::ChannelType& channelType, uint& portId)
{ {
CARLA_SAFE_ASSERT_RETURN(portId >= kAudioInputPortOffset, false); CARLA_SAFE_ASSERT_RETURN(portId >= kAudioInputPortOffset, false);
CARLA_SAFE_ASSERT_RETURN(portId <= kMidiOutputPortOffset, false);
CARLA_SAFE_ASSERT_RETURN(portId < kMaxPortOffset, false);


if (portId == kMidiInputPortOffset)
if (portId >= kMidiOutputPortOffset)
{
portId -= kMidiOutputPortOffset;
channelType = AudioProcessor::ChannelTypeMIDI;
return true;
}
if (portId >= kMidiInputPortOffset)
{ {
portId = kMidiChannelIndex;
portId -= kMidiInputPortOffset;
channelType = AudioProcessor::ChannelTypeMIDI;
return true; return true;
} }
if (portId == kMidiOutputPortOffset)
if (portId >= kCVOutputPortOffset)
{ {
portId = kMidiChannelIndex;
portId -= kCVOutputPortOffset;
channelType = AudioProcessor::ChannelTypeCV;
return true;
}
if (portId >= kCVInputPortOffset)
{
portId -= kCVInputPortOffset;
channelType = AudioProcessor::ChannelTypeCV;
return true; return true;
} }
if (portId >= kAudioOutputPortOffset) if (portId >= kAudioOutputPortOffset)
{ {
portId -= kAudioOutputPortOffset; portId -= kAudioOutputPortOffset;
channelType = AudioProcessor::ChannelTypeAudio;
return true; return true;
} }
if (portId >= kAudioInputPortOffset) if (portId >= kAudioInputPortOffset)
{ {
portId -= kAudioInputPortOffset; portId -= kAudioInputPortOffset;
channelType = AudioProcessor::ChannelTypeAudio;
return true; return true;
} }


@@ -1162,27 +1180,45 @@ const String getProcessorFullPortName(AudioProcessor* const proc, const uint32_t
{ {
CARLA_SAFE_ASSERT_RETURN(proc != nullptr, String()); CARLA_SAFE_ASSERT_RETURN(proc != nullptr, String());
CARLA_SAFE_ASSERT_RETURN(portId >= kAudioInputPortOffset, String()); CARLA_SAFE_ASSERT_RETURN(portId >= kAudioInputPortOffset, String());
CARLA_SAFE_ASSERT_RETURN(portId <= kMidiOutputPortOffset, String());
CARLA_SAFE_ASSERT_RETURN(portId < kMaxPortOffset, String());


String fullPortName(proc->getName()); String fullPortName(proc->getName());


if (portId == kMidiOutputPortOffset)
/**/ if (portId >= kMidiOutputPortOffset)
{
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeMIDI) > 0, String());
fullPortName += ":" + proc->getOutputChannelName(AudioProcessor::ChannelTypeMIDI,
portId-kMidiOutputPortOffset);
}
else if (portId >= kMidiInputPortOffset)
{
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeMIDI) > 0, String());
fullPortName += ":" + proc->getInputChannelName(AudioProcessor::ChannelTypeMIDI,
portId-kMidiInputPortOffset);
}
else if (portId >= kCVOutputPortOffset)
{ {
fullPortName += ":events-out";
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeCV) > 0, String());
fullPortName += ":" + proc->getOutputChannelName(AudioProcessor::ChannelTypeCV,
portId-kCVOutputPortOffset);
} }
else if (portId == kMidiInputPortOffset)
else if (portId >= kCVInputPortOffset)
{ {
fullPortName += ":events-in";
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeCV) > 0, String());
fullPortName += ":" + proc->getInputChannelName(AudioProcessor::ChannelTypeCV,
portId-kCVInputPortOffset);
} }
else if (portId >= kAudioOutputPortOffset) else if (portId >= kAudioOutputPortOffset)
{ {
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumOutputChannels() > 0, String());
fullPortName += ":" + proc->getOutputChannelName(static_cast<int>(portId-kAudioOutputPortOffset));
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeAudio) > 0, String());
fullPortName += ":" + proc->getOutputChannelName(AudioProcessor::ChannelTypeAudio,
portId-kAudioOutputPortOffset);
} }
else if (portId >= kAudioInputPortOffset) else if (portId >= kAudioInputPortOffset)
{ {
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumInputChannels() > 0, String());
fullPortName += ":" + proc->getInputChannelName(static_cast<int>(portId-kAudioInputPortOffset));
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeAudio) > 0, String());
fullPortName += ":" + proc->getInputChannelName(AudioProcessor::ChannelTypeAudio,
portId-kAudioInputPortOffset);
} }
else else
{ {
@@ -1207,48 +1243,70 @@ void addNodeToPatchbay(const bool sendHost, const bool sendOSC, CarlaEngine* con
0, 0.0f, 0, 0.0f,
proc->getName().toRawUTF8()); proc->getName().toRawUTF8());


for (int i=0, numInputs=proc->getTotalNumInputChannels(); i<numInputs; ++i)
for (uint i=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeAudio); i<numInputs; ++i)
{ {
engine->callback(sendHost, sendOSC, engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, ENGINE_CALLBACK_PATCHBAY_PORT_ADDED,
groupId, groupId,
static_cast<int>(kAudioInputPortOffset)+i,
static_cast<int>(kAudioInputPortOffset+i),
PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT,
0, 0.0f, 0, 0.0f,
proc->getInputChannelName(i).toRawUTF8());
proc->getInputChannelName(AudioProcessor::ChannelTypeAudio, i).toRawUTF8());
} }


for (int i=0, numOutputs=proc->getTotalNumOutputChannels(); i<numOutputs; ++i)
for (uint i=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeAudio); i<numOutputs; ++i)
{ {
engine->callback(sendHost, sendOSC, engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, ENGINE_CALLBACK_PATCHBAY_PORT_ADDED,
groupId, groupId,
static_cast<int>(kAudioOutputPortOffset)+i,
static_cast<int>(kAudioOutputPortOffset+i),
PATCHBAY_PORT_TYPE_AUDIO, PATCHBAY_PORT_TYPE_AUDIO,
0, 0.0f, 0, 0.0f,
proc->getOutputChannelName(i).toRawUTF8());
proc->getOutputChannelName(AudioProcessor::ChannelTypeAudio, i).toRawUTF8());
}

for (uint i=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeCV); i<numInputs; ++i)
{
engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_ADDED,
groupId,
static_cast<int>(kCVInputPortOffset+i),
PATCHBAY_PORT_TYPE_CV|PATCHBAY_PORT_IS_INPUT,
0, 0.0f,
proc->getInputChannelName(AudioProcessor::ChannelTypeCV, i).toRawUTF8());
}

for (uint i=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeCV); i<numOutputs; ++i)
{
engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_ADDED,
groupId,
static_cast<int>(kCVOutputPortOffset+i),
PATCHBAY_PORT_TYPE_CV,
0, 0.0f,
proc->getOutputChannelName(AudioProcessor::ChannelTypeCV, i).toRawUTF8());
} }


if (proc->acceptsMidi())
for (uint i=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeMIDI); i<numInputs; ++i)
{ {
engine->callback(sendHost, sendOSC, engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, ENGINE_CALLBACK_PATCHBAY_PORT_ADDED,
groupId, groupId,
static_cast<int>(kMidiInputPortOffset),
static_cast<int>(kMidiInputPortOffset+i),
PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT,
0, 0.0f, 0, 0.0f,
"events-in");
proc->getInputChannelName(AudioProcessor::ChannelTypeMIDI, i).toRawUTF8());
} }


if (proc->producesMidi())
for (uint i=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeMIDI); i<numOutputs; ++i)
{ {
engine->callback(sendHost, sendOSC, engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, ENGINE_CALLBACK_PATCHBAY_PORT_ADDED,
groupId, groupId,
static_cast<int>(kMidiOutputPortOffset),
static_cast<int>(kMidiOutputPortOffset+i),
PATCHBAY_PORT_TYPE_MIDI, PATCHBAY_PORT_TYPE_MIDI,
0, 0.0f, 0, 0.0f,
"events-out");
proc->getOutputChannelName(AudioProcessor::ChannelTypeMIDI, i).toRawUTF8());
} }
} }


@@ -1259,40 +1317,59 @@ void removeNodeFromPatchbay(const bool sendHost, const bool sendOSC, CarlaEngine
CARLA_SAFE_ASSERT_RETURN(engine != nullptr,); CARLA_SAFE_ASSERT_RETURN(engine != nullptr,);
CARLA_SAFE_ASSERT_RETURN(proc != nullptr,); CARLA_SAFE_ASSERT_RETURN(proc != nullptr,);


for (int i=0, numInputs=proc->getTotalNumInputChannels(); i<numInputs; ++i)
for (uint i=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeAudio); i<numInputs; ++i)
{ {
engine->callback(sendHost, sendOSC, engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED,
groupId, groupId,
static_cast<int>(kAudioInputPortOffset)+i,
static_cast<int>(kAudioInputPortOffset+i),
0, 0, 0.0f, nullptr); 0, 0, 0.0f, nullptr);
} }


for (int i=0, numOutputs=proc->getTotalNumOutputChannels(); i<numOutputs; ++i)
for (uint i=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeAudio); i<numOutputs; ++i)
{ {
engine->callback(sendHost, sendOSC, engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED,
groupId, groupId,
static_cast<int>(kAudioOutputPortOffset)+i,
static_cast<int>(kAudioOutputPortOffset+i),
0, 0, 0.0f, 0, 0, 0.0f,
nullptr); nullptr);
} }


if (proc->acceptsMidi())
for (uint i=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeCV); i<numInputs; ++i)
{ {
engine->callback(sendHost, sendOSC, engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED,
groupId, groupId,
static_cast<int>(kMidiInputPortOffset),
static_cast<int>(kCVInputPortOffset+i),
0, 0, 0.0f, nullptr); 0, 0, 0.0f, nullptr);
} }


if (proc->producesMidi())
for (uint i=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeCV); i<numOutputs; ++i)
{ {
engine->callback(sendHost, sendOSC, engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED,
groupId, groupId,
static_cast<int>(kMidiOutputPortOffset),
static_cast<int>(kCVOutputPortOffset+i),
0, 0, 0.0f,
nullptr);
}

for (uint i=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeMIDI); i<numInputs; ++i)
{
engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED,
groupId,
static_cast<int>(kMidiInputPortOffset+i),
0, 0, 0.0f, nullptr);
}

for (uint i=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeMIDI); i<numOutputs; ++i)
{
engine->callback(sendHost, sendOSC,
ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED,
groupId,
static_cast<int>(kMidiOutputPortOffset+i),
0, 0, 0.0f, nullptr); 0, 0, 0.0f, nullptr);
} }


@@ -1311,8 +1388,12 @@ public:
: kEngine(engine), : kEngine(engine),
fPlugin(plugin) fPlugin(plugin)
{ {
setPlayConfigDetails(static_cast<int>(fPlugin->getAudioInCount()),
static_cast<int>(fPlugin->getAudioOutCount()),
setPlayConfigDetails(fPlugin->getAudioInCount(),
fPlugin->getAudioOutCount(),
fPlugin->getCVInCount(),
fPlugin->getCVOutCount(),
jmax(fPlugin->getMidiInCount(), acceptsMidi() ? 1U : 0U),
jmax(fPlugin->getMidiOutCount(), producesMidi() ? 1U : 0U),
getSampleRate(), getBlockSize()); getSampleRate(), getBlockSize());
} }


@@ -1332,11 +1413,20 @@ public:
return fPlugin->getName(); return fPlugin->getName();
} }


void processBlock(AudioSampleBuffer& audio, MidiBuffer& midi) override
void processBlock(AudioSampleBuffer&, MidiBuffer&) override
{
carla_stderr2("CarlaPluginInstance::processBlock called, this is wrong!");
}

void processBlockWithCV(AudioSampleBuffer& audio,
const AudioSampleBuffer& cvIn,
AudioSampleBuffer& cvOut,
MidiBuffer& midi) override
{ {
if (fPlugin == nullptr || ! fPlugin->isEnabled() || ! fPlugin->tryLock(kEngine->isOffline())) if (fPlugin == nullptr || ! fPlugin->isEnabled() || ! fPlugin->tryLock(kEngine->isOffline()))
{ {
audio.clear(); audio.clear();
cvOut.clear();
midi.clear(); midi.clear();
return; return;
} }
@@ -1354,38 +1444,64 @@ public:


fPlugin->initBuffers(); fPlugin->initBuffers();


// TODO - CV support

const uint32_t numSamples(static_cast<uint32_t>(audio.getNumSamples()));
const uint32_t numSamples = audio.getNumSamples();
const uint32_t numAudioChan = audio.getNumChannels();
const uint32_t numCVInChan = cvIn.getNumChannels();
const uint32_t numCVOutChan = cvOut.getNumChannels();


if (const uint32_t numChan = audio.getNumChannels())
if (numAudioChan+numCVInChan+numCVOutChan == 0)
{
// nothing to process
fPlugin->process(nullptr, nullptr, nullptr, nullptr, numSamples);
}
else if (numAudioChan != 0)
{ {
const uint32_t numChanu = jmin(numChan, 2U);
// processing audio, include code for peaks
const uint32_t numChan2 = jmin(numAudioChan, 2U);


if (fPlugin->getAudioInCount() == 0) if (fPlugin->getAudioInCount() == 0)
audio.clear(); audio.clear();


float* audioBuffers[numChan];
float* audioBuffers[numAudioChan];
float* cvOutBuffers[numCVOutChan];
const float* cvInBuffers[numCVInChan];


for (uint32_t i=0; i<numChan; ++i)
for (uint32_t i=0; i<numAudioChan; ++i)
audioBuffers[i] = audio.getWritePointer(i); audioBuffers[i] = audio.getWritePointer(i);
for (uint32_t i=0; i<numCVOutChan; ++i)
cvOutBuffers[i] = cvOut.getWritePointer(i);
for (uint32_t i=0; i<numCVInChan; ++i)
cvInBuffers[i] = cvIn.getReadPointer(i);


float inPeaks[2] = { 0.0f }; float inPeaks[2] = { 0.0f };
float outPeaks[2] = { 0.0f }; float outPeaks[2] = { 0.0f };


for (uint32_t i=0, count=jmin(fPlugin->getAudioInCount(), numChanu); i<count; ++i)
for (uint32_t i=0, count=jmin(fPlugin->getAudioInCount(), numChan2); i<count; ++i)
inPeaks[i] = carla_findMaxNormalizedFloat(audioBuffers[i], numSamples); inPeaks[i] = carla_findMaxNormalizedFloat(audioBuffers[i], numSamples);


fPlugin->process(const_cast<const float**>(audioBuffers), audioBuffers, nullptr, nullptr, numSamples);
fPlugin->process(const_cast<const float**>(audioBuffers), audioBuffers,
cvInBuffers, cvOutBuffers,
numSamples);


for (uint32_t i=0, count=jmin(fPlugin->getAudioOutCount(), numChanu); i<count; ++i)
for (uint32_t i=0, count=jmin(fPlugin->getAudioOutCount(), numChan2); i<count; ++i)
outPeaks[i] = carla_findMaxNormalizedFloat(audioBuffers[i], numSamples); outPeaks[i] = carla_findMaxNormalizedFloat(audioBuffers[i], numSamples);


kEngine->setPluginPeaksRT(fPlugin->getId(), inPeaks, outPeaks); kEngine->setPluginPeaksRT(fPlugin->getId(), inPeaks, outPeaks);
} }
else else
{ {
fPlugin->process(nullptr, nullptr, nullptr, nullptr, numSamples);
// processing CV only, skip audiopeaks
float* cvOutBuffers[numCVOutChan];
const float* cvInBuffers[numCVInChan];

for (uint32_t i=0; i<numCVOutChan; ++i)
cvOutBuffers[i] = cvOut.getWritePointer(i);
for (uint32_t i=0; i<numCVInChan; ++i)
cvInBuffers[i] = cvIn.getReadPointer(i);

fPlugin->process(nullptr, nullptr,
cvInBuffers, cvOutBuffers,
numSamples);
} }


midi.clear(); midi.clear();
@@ -1402,24 +1518,44 @@ public:
fPlugin->unlock(); fPlugin->unlock();
} }


const String getInputChannelName(int i) const override
const String getInputChannelName(ChannelType t, uint i) const override
{ {
CARLA_SAFE_ASSERT_RETURN(i >= 0, String());
CarlaEngineClient* const client(fPlugin->getEngineClient()); CarlaEngineClient* const client(fPlugin->getEngineClient());
return client->getAudioPortName(true, static_cast<uint>(i));

switch (t)
{
case ChannelTypeAudio:
return client->getAudioPortName(true, i);
case ChannelTypeCV:
return client->getCVPortName(true, i);
case ChannelTypeMIDI:
return client->getEventPortName(true, i);
}

return String();
} }


const String getOutputChannelName(int i) const override
const String getOutputChannelName(ChannelType t, uint i) const override
{ {
CARLA_SAFE_ASSERT_RETURN(i >= 0, String());
CarlaEngineClient* const client(fPlugin->getEngineClient()); CarlaEngineClient* const client(fPlugin->getEngineClient());
return client->getAudioPortName(false, static_cast<uint>(i));

switch (t)
{
case ChannelTypeAudio:
return client->getAudioPortName(false, i);
case ChannelTypeCV:
return client->getCVPortName(false, i);
case ChannelTypeMIDI:
return client->getEventPortName(false, i);
}

return String();
} }


void prepareToPlay(double, int) override {} void prepareToPlay(double, int) override {}
void releaseResources() override {} void releaseResources() override {}


bool acceptsMidi() const override { return fPlugin->getDefaultEventInPort() != nullptr; }
bool acceptsMidi() const override { return fPlugin->getDefaultEventInPort() != nullptr; }
bool producesMidi() const override { return fPlugin->getDefaultEventOutPort() != nullptr; } bool producesMidi() const override { return fPlugin->getDefaultEventOutPort() != nullptr; }


private: private:
@@ -1440,15 +1576,17 @@ public:
inputNames(), inputNames(),
outputNames() {} outputNames() {}


const String getInputChannelName (int index) const override
const String getInputChannelName (ChannelType, uint _index) const override
{ {
const int index = static_cast<int>(_index); // FIXME
if (index < inputNames.size()) if (index < inputNames.size())
return inputNames[index]; return inputNames[index];
return String("Playback ") + String(index+1); return String("Playback ") + String(index+1);
} }


const String getOutputChannelName (int index) const override
const String getOutputChannelName (ChannelType, uint _index) const override
{ {
const int index = static_cast<int>(_index); // FIXME
if (index < outputNames.size()) if (index < outputNames.size())
return outputNames[index]; return outputNames[index];
return String("Capture ") + String(index+1); return String("Capture ") + String(index+1);
@@ -1484,7 +1622,7 @@ PatchbayGraph::PatchbayGraph(CarlaEngine* const engine, const uint32_t ins, cons
const uint32_t bufferSize(engine->getBufferSize()); const uint32_t bufferSize(engine->getBufferSize());
const double sampleRate(engine->getSampleRate()); const double sampleRate(engine->getSampleRate());


graph.setPlayConfigDetails(static_cast<int>(inputs), static_cast<int>(outputs), sampleRate, static_cast<int>(bufferSize));
graph.setPlayConfigDetails(inputs, outputs, 0, 0, 1, 1, sampleRate, static_cast<int>(bufferSize));
graph.prepareToPlay(sampleRate, static_cast<int>(bufferSize)); graph.prepareToPlay(sampleRate, static_cast<int>(bufferSize));


audioBuffer.setSize(jmax(inputs, outputs), bufferSize); audioBuffer.setSize(jmax(inputs, outputs), bufferSize);
@@ -1731,12 +1869,13 @@ bool PatchbayGraph::connect(const bool external,
uint adjustedPortA = portA; uint adjustedPortA = portA;
uint adjustedPortB = portB; uint adjustedPortB = portB;


if (! adjustPatchbayPortIdForWater(adjustedPortA))
AudioProcessor::ChannelType channelType;
if (! adjustPatchbayPortIdForWater(channelType, adjustedPortA))
return false; return false;
if (! adjustPatchbayPortIdForWater(adjustedPortB))
if (! adjustPatchbayPortIdForWater(channelType, adjustedPortB))
return false; return false;


if (! graph.addConnection(groupA, static_cast<int>(adjustedPortA), groupB, static_cast<int>(adjustedPortB)))
if (! graph.addConnection(channelType, groupA, adjustedPortA, groupB, adjustedPortB))
{ {
kEngine->setLastError("Failed from water"); kEngine->setLastError("Failed from water");
return false; return false;
@@ -1777,13 +1916,15 @@ bool PatchbayGraph::disconnect(const bool external, const uint connectionId)
uint adjustedPortA = connectionToId.portA; uint adjustedPortA = connectionToId.portA;
uint adjustedPortB = connectionToId.portB; uint adjustedPortB = connectionToId.portB;


if (! adjustPatchbayPortIdForWater(adjustedPortA))
AudioProcessor::ChannelType channelType;
if (! adjustPatchbayPortIdForWater(channelType, adjustedPortA))
return false; return false;
if (! adjustPatchbayPortIdForWater(adjustedPortB))
if (! adjustPatchbayPortIdForWater(channelType, adjustedPortB))
return false; return false;


if (! graph.removeConnection(connectionToId.groupA, static_cast<int>(adjustedPortA),
connectionToId.groupB, static_cast<int>(adjustedPortB)))
if (! graph.removeConnection(channelType,
connectionToId.groupA, adjustedPortA,
connectionToId.groupB, adjustedPortB))
return false; return false;


kEngine->callback(!usingExternalHost, !usingExternalOSC, kEngine->callback(!usingExternalHost, !usingExternalOSC,
@@ -1816,6 +1957,7 @@ void PatchbayGraph::disconnectInternalGroup(const uint groupId) noexcept
uint adjustedPortA = connectionToId.portA; uint adjustedPortA = connectionToId.portA;
uint adjustedPortB = connectionToId.portB; uint adjustedPortB = connectionToId.portB;


AudioProcessor::ChannelType channelType;
if (! adjustPatchbayPortIdForWater(adjustedPortA)) if (! adjustPatchbayPortIdForWater(adjustedPortA))
return false; return false;
if (! adjustPatchbayPortIdForWater(adjustedPortB)) if (! adjustPatchbayPortIdForWater(adjustedPortB))
@@ -1869,24 +2011,28 @@ void PatchbayGraph::refresh(const bool sendHost, const bool sendOSC, const bool
{ {
const AudioProcessorGraph::Connection* const conn(graph.getConnection(i)); const AudioProcessorGraph::Connection* const conn(graph.getConnection(i));
CARLA_SAFE_ASSERT_CONTINUE(conn != nullptr); CARLA_SAFE_ASSERT_CONTINUE(conn != nullptr);
CARLA_SAFE_ASSERT_CONTINUE(conn->sourceChannelIndex >= 0);
CARLA_SAFE_ASSERT_CONTINUE(conn->destChannelIndex >= 0);


const uint groupA = conn->sourceNodeId; const uint groupA = conn->sourceNodeId;
const uint groupB = conn->destNodeId; const uint groupB = conn->destNodeId;


uint portA = static_cast<uint>(conn->sourceChannelIndex);
uint portB = static_cast<uint>(conn->destChannelIndex);
uint portA = conn->sourceChannelIndex;
uint portB = conn->destChannelIndex;


if (portA == kMidiChannelIndex)
portA = kMidiOutputPortOffset;
else
switch (conn->channelType)
{
case AudioProcessor::ChannelTypeAudio:
portA += kAudioOutputPortOffset; portA += kAudioOutputPortOffset;

if (portB == kMidiChannelIndex)
portB = kMidiInputPortOffset;
else
portB += kAudioInputPortOffset; portB += kAudioInputPortOffset;
break;
case AudioProcessor::ChannelTypeCV:
portA += kCVOutputPortOffset;
portB += kCVInputPortOffset;
break;
case AudioProcessor::ChannelTypeMIDI:
portA += kMidiOutputPortOffset;
portB += kMidiInputPortOffset;
break;
}


ConnectionToId connectionToId; ConnectionToId connectionToId;
connectionToId.setData(++connections.lastId, groupA, portA, groupB, portB); connectionToId.setData(++connections.lastId, groupA, portA, groupB, portB);
@@ -1971,33 +2117,57 @@ bool PatchbayGraph::getGroupAndPortIdFromFullName(const bool external, const cha


groupId = node->nodeId; groupId = node->nodeId;


if (portName == "events-in")
for (uint j=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeAudio); j<numInputs; ++j)
{ {
portId = kMidiInputPortOffset;
if (proc->getInputChannelName(AudioProcessor::ChannelTypeAudio, j) != portName)
continue;

portId = kAudioInputPortOffset+j;
return true; return true;
} }


if (portName == "events-out")
for (uint j=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeAudio); j<numOutputs; ++j)
{ {
portId = kMidiOutputPortOffset;
if (proc->getOutputChannelName(AudioProcessor::ChannelTypeAudio, j) != portName)
continue;

portId = kAudioOutputPortOffset+j;
return true;
}

for (uint j=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeCV); j<numInputs; ++j)
{
if (proc->getInputChannelName(AudioProcessor::ChannelTypeCV, j) != portName)
continue;

portId = kCVInputPortOffset+j;
return true;
}

for (uint j=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeCV); j<numOutputs; ++j)
{
if (proc->getOutputChannelName(AudioProcessor::ChannelTypeCV, j) != portName)
continue;

portId = kCVOutputPortOffset+j;
return true; return true;
} }


for (int j=0, numInputs=proc->getTotalNumInputChannels(); j<numInputs; ++j)
for (uint j=0, numInputs=proc->getTotalNumInputChannels(AudioProcessor::ChannelTypeMIDI); j<numInputs; ++j)
{ {
if (proc->getInputChannelName(j) != portName)
if (proc->getInputChannelName(AudioProcessor::ChannelTypeMIDI, j) != portName)
continue; continue;


portId = kAudioInputPortOffset+static_cast<uint>(j);
portId = kMidiInputPortOffset+j;
return true; return true;
} }


for (int j=0, numOutputs=proc->getTotalNumOutputChannels(); j<numOutputs; ++j)
for (uint j=0, numOutputs=proc->getTotalNumOutputChannels(AudioProcessor::ChannelTypeMIDI); j<numOutputs; ++j)
{ {
if (proc->getOutputChannelName(j) != portName)
if (proc->getOutputChannelName(AudioProcessor::ChannelTypeMIDI, j) != portName)
continue; continue;


portId = kAudioOutputPortOffset+static_cast<uint>(j);
portId = kMidiOutputPortOffset+j;
return true; return true;
} }
} }


+ 26
- 0
source/modules/sfzero/README.md View File

@@ -0,0 +1,26 @@
# SFZero, the Juce module version (module only)

This is a fork of the [original SFZero by Steve Folta](https://github.com/stevefolta/SFZero), with the following changes:

* has been converted to a Juce module, so you can easily consume it from your own projects (you still get the sample player plugin, but it now includes that module)
* requires Juce 4.1 or higher
* supports Juce 4.1 module format
* now also supports new Juce 4.2 module format (thanks to Loki Davison)
* conveniently sits within its own `sfzero::` namespace
* has a tidied-up code base, so it now builds with as few warnings as possible on all platforms and on both 32/64 bit architectures. I also simplified logging, added support for synchronous sample loading, and fixed a few bugs.
* the SFZero Juce module and sample plugin have been separated and the Juce module is now available as a git submodule for easy inclusion in other repositories

For more information, please see also this [blog article](http://www.mucoder.net/blog/2016/03/24/sfzero.html)

Please note that, in order to build, SFZero requires [Juce](http://www.juce.com).

Before building the sample plugin, it's necessary to

* get the sample plugin source code from [https://github.com/altalogix/SFZero](https://github.com/altalogix/SFZero)
* get the module source code from [https://github.com/altalogix/SFZeroModule](https://github.com/altalogix/SFZeroModule)
* copy the SFZeroModule folder as a childfolder to your Juce modules folder.
* load `plugin/SFZero.jucer` into your IntroJucer tool and save the project again. This should regenerate the project build definitions with the proper links to your Juce module location.

If you just want to use the Juce module and not the sample plugin, it suffices to include the contents of [https://github.com/altalogix/SFZeroModule](https://github.com/altalogix/SFZeroModule) within a SFZero child folder of your Juce modules folder.



+ 6
- 0
source/modules/water/memory/HeapBlock.h View File

@@ -121,6 +121,12 @@ public:
*/ */
inline ElementType* getData() const noexcept { return data; } inline ElementType* getData() const noexcept { return data; }
/** Returns a raw pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline const ElementType* getConstData() const noexcept { return data; }
/** Returns a void pointer to the allocated data. /** Returns a void pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method. freed by calling the free() method.


+ 66
- 7
source/modules/water/processors/AudioProcessor.cpp View File

@@ -24,8 +24,12 @@ namespace water {
AudioProcessor::AudioProcessor() AudioProcessor::AudioProcessor()
{ {
cachedTotalIns = 0;
cachedTotalOuts = 0;
numAudioIns = 0;
numAudioOuts = 0;
numCVIns = 0;
numCVOuts = 0;
numMIDIIns = 0;
numMIDIOuts = 0;
currentSampleRate = 0; currentSampleRate = 0;
blockSize = 0; blockSize = 0;
@@ -40,13 +44,21 @@ AudioProcessor::~AudioProcessor()
} }
//============================================================================== //==============================================================================
void AudioProcessor::setPlayConfigDetails (const int newNumIns,
const int newNumOuts,
void AudioProcessor::setPlayConfigDetails (const uint newNumIns,
const uint newNumOuts,
const uint newNumCVIns,
const uint newNumCVOuts,
const uint newNumMIDIIns,
const uint newNumMIDIOuts,
const double newSampleRate, const double newSampleRate,
const int newBlockSize) const int newBlockSize)
{ {
cachedTotalIns = newNumIns;
cachedTotalOuts = newNumOuts;
numAudioIns = newNumIns;
numAudioOuts = newNumOuts;
numCVIns = newNumCVIns;
numCVOuts = newNumCVOuts;
numMIDIIns = newNumMIDIIns;
numMIDIOuts = newNumMIDIOuts;
setRateAndBufferSizeDetails (newSampleRate, newBlockSize); setRateAndBufferSizeDetails (newSampleRate, newBlockSize);
} }
@@ -78,8 +90,55 @@ void AudioProcessor::reset() {}
void AudioProcessor::processBlockBypassed (AudioSampleBuffer& buffer, MidiBuffer&) void AudioProcessor::processBlockBypassed (AudioSampleBuffer& buffer, MidiBuffer&)
{ {
for (int ch = getTotalNumInputChannels(); ch < getTotalNumOutputChannels(); ++ch)
for (uint ch = getTotalNumInputChannels(ChannelTypeAudio); ch < getTotalNumOutputChannels(ChannelTypeAudio); ++ch)
buffer.clear (ch, 0, buffer.getNumSamples()); buffer.clear (ch, 0, buffer.getNumSamples());
} }
void AudioProcessor::processBlockWithCV (AudioSampleBuffer& audioBuffer,
const AudioSampleBuffer&, AudioSampleBuffer&,
MidiBuffer& midiMessages)
{
processBlock (audioBuffer, midiMessages);
}
uint AudioProcessor::getTotalNumInputChannels(ChannelType t) const noexcept
{
switch (t)
{
case ChannelTypeAudio:
return numAudioIns;
case ChannelTypeCV:
return numCVIns;
case ChannelTypeMIDI:
return numMIDIIns;
}
return 0;
}
uint AudioProcessor::getTotalNumOutputChannels(ChannelType t) const noexcept
{
switch (t)
{
case ChannelTypeAudio:
return numAudioOuts;
case ChannelTypeCV:
return numCVOuts;
case ChannelTypeMIDI:
return numMIDIOuts;
}
return 0;
}
const String AudioProcessor::getInputChannelName(ChannelType t, uint) const
{
return t == ChannelTypeMIDI ? "events-in" : "";
}
const String AudioProcessor::getOutputChannelName(ChannelType t, uint) const
{
return t == ChannelTypeMIDI ? "events-out" : "";
}
} }

+ 25
- 37
source/modules/water/processors/AudioProcessor.h View File

@@ -55,6 +55,12 @@ protected:
AudioProcessor(); AudioProcessor();
public: public:
enum ChannelType {
ChannelTypeAudio,
ChannelTypeCV,
ChannelTypeMIDI,
};
//============================================================================== //==============================================================================
/** Destructor. */ /** Destructor. */
virtual ~AudioProcessor(); virtual ~AudioProcessor();
@@ -152,6 +158,11 @@ public:
virtual void processBlock (AudioSampleBuffer& buffer, virtual void processBlock (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages) = 0; MidiBuffer& midiMessages) = 0;
virtual void processBlockWithCV (AudioSampleBuffer& audioBuffer,
const AudioSampleBuffer& cvInBuffer,
AudioSampleBuffer& cvOutBuffer,
MidiBuffer& midiMessages);
/** Renders the next block when the processor is being bypassed. /** Renders the next block when the processor is being bypassed.
The default implementation of this method will pass-through any incoming audio, but The default implementation of this method will pass-through any incoming audio, but
@@ -165,33 +176,11 @@ public:
MidiBuffer& midiMessages); MidiBuffer& midiMessages);
//============================================================================== //==============================================================================
/** Returns the total number of input channels.
This method will return the total number of input channels by accumulating
the number of channels on each input bus. The number of channels of the
buffer passed to your processBlock callback will be equivalent to either
getTotalNumInputChannels or getTotalNumOutputChannels - which ever
is greater.
Note that getTotalNumInputChannels is equivalent to
getMainBusNumInputChannels if your processor does not have any sidechains
or aux buses.
*/
int getTotalNumInputChannels() const noexcept { return cachedTotalIns; }
/** Returns the total number of output channels.
This method will return the total number of output channels by accumulating
the number of channels on each output bus. The number of channels of the
buffer passed to your processBlock callback will be equivalent to either
getTotalNumInputChannels or getTotalNumOutputChannels - which ever
is greater.
Note that getTotalNumOutputChannels is equivalent to
getMainBusNumOutputChannels if your processor does not have any sidechains
or aux buses.
*/
int getTotalNumOutputChannels() const noexcept { return cachedTotalOuts; }
/** Returns the total number of input channels. */
uint getTotalNumInputChannels(ChannelType t) const noexcept;
/** Returns the total number of output channels. */
uint getTotalNumOutputChannels(ChannelType t) const noexcept;
//============================================================================== //==============================================================================
/** Returns the current sample rate. /** Returns the current sample rate.
@@ -238,11 +227,8 @@ public:
/** Returns true if the processor supports MPE. */ /** Returns true if the processor supports MPE. */
virtual bool supportsMPE() const { return false; } virtual bool supportsMPE() const { return false; }
/** Returns true if this is a midi effect plug-in and does no audio processing. */
virtual bool isMidiEffect() const { return false; }
virtual const String getInputChannelName (int) const { return String(); }
virtual const String getOutputChannelName (int) const { return String(); }
virtual const String getInputChannelName (ChannelType, uint) const;
virtual const String getOutputChannelName (ChannelType, uint) const;
//============================================================================== //==============================================================================
/** This returns a critical section that will automatically be locked while the host /** This returns a critical section that will automatically be locked while the host
@@ -322,7 +308,10 @@ public:
/** This is called by the processor to specify its details before being played. Use this /** This is called by the processor to specify its details before being played. Use this
version of the function if you are not interested in any sidechain and/or aux buses version of the function if you are not interested in any sidechain and/or aux buses
and do not care about the layout of channels. Otherwise use setRateAndBufferSizeDetails.*/ and do not care about the layout of channels. Otherwise use setRateAndBufferSizeDetails.*/
void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize);
void setPlayConfigDetails (uint numAudioIns, uint numAudioOuts,
uint numCVIns, uint numCVOuts,
uint numMIDIIns, uint numMIDIOuts,
double sampleRate, int blockSize);
/** This is called by the processor to specify its details before being played. You /** This is called by the processor to specify its details before being played. You
should call this function after having informed the processor about the channel should call this function after having informed the processor about the channel
@@ -339,10 +328,9 @@ private:
bool suspended, nonRealtime; bool suspended, nonRealtime;
CarlaRecursiveMutex callbackLock; CarlaRecursiveMutex callbackLock;
String cachedInputSpeakerArrString;
String cachedOutputSpeakerArrString;
int cachedTotalIns, cachedTotalOuts;
uint numAudioIns, numAudioOuts;
uint numCVIns, numCVOuts;
uint numMIDIIns, numMIDIOuts;
CARLA_DECLARE_NON_COPY_CLASS (AudioProcessor) CARLA_DECLARE_NON_COPY_CLASS (AudioProcessor)
}; };


+ 467
- 177
source/modules/water/processors/AudioProcessorGraph.cpp
File diff suppressed because it is too large
View File


+ 28
- 15
source/modules/water/processors/AudioProcessorGraph.h View File

@@ -109,10 +109,14 @@ public:
{ {
public: public:
//============================================================================== //==============================================================================
Connection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex) noexcept;
Connection (ChannelType channelType,
uint32 sourceNodeId, uint sourceChannelIndex,
uint32 destNodeId, uint destChannelIndex) noexcept;
//============================================================================== //==============================================================================
/** Defines the connection type. */
ChannelType channelType;
/** The ID number of the node which is the input source for this connection. /** The ID number of the node which is the input source for this connection.
@see AudioProcessorGraph::getNodeForId @see AudioProcessorGraph::getNodeForId
*/ */
@@ -125,7 +129,7 @@ public:
it is referring to the source node's midi output. Otherwise, it is the zero-based it is referring to the source node's midi output. Otherwise, it is the zero-based
index of an audio output channel in the source node. index of an audio output channel in the source node.
*/ */
int sourceChannelIndex;
uint sourceChannelIndex;
/** The ID number of the node which is the destination for this connection. /** The ID number of the node which is the destination for this connection.
@see AudioProcessorGraph::getNodeForId @see AudioProcessorGraph::getNodeForId
@@ -139,7 +143,7 @@ public:
it is referring to the destination node's midi input. Otherwise, it is the zero-based it is referring to the destination node's midi input. Otherwise, it is the zero-based
index of an audio input channel in the destination node. index of an audio input channel in the destination node.
*/ */
int destChannelIndex;
uint destChannelIndex;
}; };
//============================================================================== //==============================================================================
@@ -198,10 +202,11 @@ public:
/** Searches for a connection between some specified channels. /** Searches for a connection between some specified channels.
If no such connection is found, this returns nullptr. If no such connection is found, this returns nullptr.
*/ */
const Connection* getConnectionBetween (uint32 sourceNodeId,
int sourceChannelIndex,
const Connection* getConnectionBetween (ChannelType channelType,
uint32 sourceNodeId,
uint sourceChannelIndex,
uint32 destNodeId, uint32 destNodeId,
int destChannelIndex) const;
uint destChannelIndex) const;
/** Returns true if there is a connection between any of the channels of /** Returns true if there is a connection between any of the channels of
two specified nodes. two specified nodes.
@@ -210,16 +215,18 @@ public:
uint32 possibleDestNodeId) const; uint32 possibleDestNodeId) const;
/** Returns true if it would be legal to connect the specified points. */ /** Returns true if it would be legal to connect the specified points. */
bool canConnect (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex) const;
bool canConnect (ChannelType channelType,
uint32 sourceNodeId, uint sourceChannelIndex,
uint32 destNodeId, uint destChannelIndex) const;
/** Attempts to connect two specified channels of two nodes. /** Attempts to connect two specified channels of two nodes.
If this isn't allowed (e.g. because you're trying to connect a midi channel If this isn't allowed (e.g. because you're trying to connect a midi channel
to an audio one or other such nonsense), then it'll return false. to an audio one or other such nonsense), then it'll return false.
*/ */
bool addConnection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex);
bool addConnection (ChannelType channelType,
uint32 sourceNodeId, uint sourceChannelIndex,
uint32 destNodeId, uint destChannelIndex);
/** Deletes the connection with the specified index. */ /** Deletes the connection with the specified index. */
void removeConnection (int index); void removeConnection (int index);
@@ -227,8 +234,9 @@ public:
/** Deletes any connection between two specified points. /** Deletes any connection between two specified points.
Returns true if a connection was actually deleted. Returns true if a connection was actually deleted.
*/ */
bool removeConnection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex);
bool removeConnection (ChannelType channelType,
uint32 sourceNodeId, uint sourceChannelIndex,
uint32 destNodeId, uint destChannelIndex);
/** Removes all connections from the specified node. */ /** Removes all connections from the specified node. */
bool disconnectNode (uint32 nodeId); bool disconnectNode (uint32 nodeId);
@@ -253,7 +261,7 @@ public:
This is used as a channel index value if you want to refer to the midi input This is used as a channel index value if you want to refer to the midi input
or output instead of an audio channel. or output instead of an audio channel.
*/ */
static const int midiChannelIndex;
static const uint midiChannelIndex;
//============================================================================== //==============================================================================
@@ -286,9 +294,10 @@ public:
midiInputNode, /**< In this mode, the processor has a midi output which midiInputNode, /**< In this mode, the processor has a midi output which
delivers the same midi data that is arriving at its delivers the same midi data that is arriving at its
parent graph. */ parent graph. */
midiOutputNode /**< In this mode, the processor has a midi input and
midiOutputNode, /**< In this mode, the processor has a midi input and
any data sent to it will be passed out of the parent any data sent to it will be passed out of the parent
graph. */ graph. */
cvInputNode, cvOutputNode,
}; };
//============================================================================== //==============================================================================
@@ -337,6 +346,10 @@ public:
void prepareToPlay (double, int) override; void prepareToPlay (double, int) override;
void releaseResources() override; void releaseResources() override;
void processBlock (AudioSampleBuffer&, MidiBuffer&) override; void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
void processBlockWithCV (AudioSampleBuffer& buffer,
const AudioSampleBuffer& cvInBuffer,
AudioSampleBuffer& cvOutBuffer,
MidiBuffer& midiMessages) override;
void reset() override; void reset() override;
void setNonRealtime (bool) noexcept override; void setNonRealtime (bool) noexcept override;


Loading…
Cancel
Save