Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MIDI Modulator processor #313

Merged
merged 5 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
- Added "Crying Child" module.
- Added "Solo-Vibe" module.
- Added "Ladder Filter" module.
- Added "MIDI Modulator" module.
- Added "netlist view" to allow for customization of some circuit-modelled modules.
- Added AVX support (for PCs that support it) for neural network-based modules.
- Added mouse interactions for selecting and moving/deleting multiple modules at once.
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ juce_add_plugin(BYOD
FORMATS ${JUCE_FORMATS}
ProductName "BYOD"
ICON_BIG res/logo.png
NEEDS_MIDI_INPUT True

VST2_CATEGORY kPlugCategEffect
VST3_CATEGORIES Fx Distortion
Expand Down
5 changes: 3 additions & 2 deletions src/BYOD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,17 @@ void BYOD::prepareToPlay (double sampleRate, int samplesPerBlock)
bypassScratchBuffer.setSize (2, samplesPerBlock);
}

void BYOD::processAudioBlock (AudioBuffer<float>& buffer)
void BYOD::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi)
{
const juce::ScopedNoDenormals noDenormals {};
AudioProcessLoadMeasurer::ScopedTimer loadTimer { loadMeasurer, buffer.getNumSamples() };

// push samples into bypass delay
bypassScratchBuffer.makeCopyOf (buffer, true);
processBypassDelay (bypassScratchBuffer);

// real processing here!
procs->processAudio (buffer);
procs->processAudio (buffer, midi);

chowdsp::BufferMath::sanitizeBuffer<AudioBuffer<float>, float> (buffer);
}
Expand Down
4 changes: 3 additions & 1 deletion src/BYOD.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ class BYOD : public chowdsp::PluginBase<BYOD>
static void addParameters (Parameters& params);
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override {}
void processAudioBlock (AudioBuffer<float>& buffer) override;
bool acceptsMidi() const override { return true; }
void processAudioBlock (AudioBuffer<float>&) override {}
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override;
void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override;

AudioProcessorEditor* createEditor() override;
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ target_sources(BYOD PRIVATE
processors/modulation/Rotary.cpp
processors/modulation/Tremolo.cpp
processors/modulation/Flanger.cpp
processors/modulation/MIDIModulator.cpp
processors/modulation/phaser/Phaser4.cpp
processors/modulation/phaser/Phaser8.cpp
processors/modulation/scanner_vibrato/ScannerVibrato.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/headless/tests/PreBufferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ class PreBufferTest : public UnitTest
{
proc->prepareProcessing (testSampleRate, testBlockSize);

MidiBuffer midi;
AudioBuffer<float> buffer (1, testBlockSize);
buffer.clear();
proc->midiBuffer = &midi;
proc->processAudioBlock (buffer);

const auto steadyStateMax = [] (const auto& name)
Expand Down
20 changes: 12 additions & 8 deletions src/headless/tests/StereoTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ class StereoTest : public UnitTest
plugin.getVTS().getParameter ("mono_mode")->setValueNotifyingHost (0.0f);
MessageManager::getInstance()->runDispatchLoopUntil (100);

MidiBuffer midi;
AudioBuffer<float> buffer (2, blockSize);
buffer.clear();
chain.processAudio (buffer);
chain.processAudio (buffer, midi);

FloatVectorOperations::fill (buffer.getWritePointer (0), 1.0f, blockSize);
FloatVectorOperations::fill (buffer.getWritePointer (1), 1.0f, blockSize);
chain.processAudio (buffer);
chain.processAudio (buffer, midi);

auto leftMinMax = FloatVectorOperations::findMinAndMax (buffer.getReadPointer (0) + blockSize / 2, blockSize / 2);
auto rightMinMax = FloatVectorOperations::findMinAndMax (buffer.getReadPointer (1) + blockSize / 2, blockSize / 2);
Expand Down Expand Up @@ -99,13 +100,14 @@ class StereoTest : public UnitTest
plugin.getVTS().getParameter ("mono_mode")->setValueNotifyingHost (0.35f);
MessageManager::getInstance()->runDispatchLoopUntil (100);

MidiBuffer midi;
AudioBuffer<float> buffer (2, blockSize);
buffer.clear();
chain.processAudio (buffer);
chain.processAudio (buffer, midi);

FloatVectorOperations::fill (buffer.getWritePointer (0), 1.0f, blockSize);
FloatVectorOperations::fill (buffer.getWritePointer (1), -1.0f, blockSize);
chain.processAudio (buffer);
chain.processAudio (buffer, midi);

auto leftMinMax = FloatVectorOperations::findMinAndMax (buffer.getReadPointer (0) + blockSize / 2, blockSize / 2);
auto rightMinMax = FloatVectorOperations::findMinAndMax (buffer.getReadPointer (1) + blockSize / 2, blockSize / 2);
Expand Down Expand Up @@ -138,13 +140,14 @@ class StereoTest : public UnitTest
plugin.getVTS().getParameter ("mono_mode")->setValueNotifyingHost (0.35f);
MessageManager::getInstance()->runDispatchLoopUntil (100);

MidiBuffer midi;
AudioBuffer<float> buffer (2, blockSize);
buffer.clear();
chain.processAudio (buffer);
chain.processAudio (buffer, midi);

FloatVectorOperations::fill (buffer.getWritePointer (0), 1.0f, blockSize);
FloatVectorOperations::fill (buffer.getWritePointer (1), -1.0f, blockSize);
chain.processAudio (buffer);
chain.processAudio (buffer, midi);

auto leftMinMax = FloatVectorOperations::findMinAndMax (buffer.getReadPointer (0) + blockSize / 2, blockSize / 2);
auto rightMinMax = FloatVectorOperations::findMinAndMax (buffer.getReadPointer (1) + blockSize / 2, blockSize / 2);
Expand Down Expand Up @@ -177,13 +180,14 @@ class StereoTest : public UnitTest
plugin.getVTS().getParameter ("mono_mode")->setValueNotifyingHost (0.35f);
MessageManager::getInstance()->runDispatchLoopUntil (100);

MidiBuffer midi;
AudioBuffer<float> buffer (2, blockSize);
buffer.clear();
chain.processAudio (buffer);
chain.processAudio (buffer, midi);

FloatVectorOperations::fill (buffer.getWritePointer (0), 1.0f, blockSize);
FloatVectorOperations::fill (buffer.getWritePointer (1), -1.0f, blockSize);
chain.processAudio (buffer);
chain.processAudio (buffer, midi);

auto leftMinMax = FloatVectorOperations::findMinAndMax (buffer.getReadPointer (0) + blockSize / 2, blockSize / 2);
auto rightMinMax = FloatVectorOperations::findMinAndMax (buffer.getReadPointer (1) + blockSize / 2, blockSize / 2);
Expand Down
8 changes: 7 additions & 1 deletion src/processors/BaseProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,15 @@ class BaseProcessor : private JuceProcWrapper
const auto& getParameters() const { return AudioProcessor::getParameters(); }

bool isOutputModulationPortConnected();

const std::vector<String>* getParametersToDisableWhenInputIsConnected (int portIndex) const noexcept;

/**
* MIDI buffer that the processor may access during processAudio() or processAudioBypassed().
* While in those methods, the processor chain will ensure that the MIDI buffer is non-null.
* At all other times this will be null.
*/
const MidiBuffer* midiBuffer = nullptr;

protected:
virtual void prepare (double sampleRate, int samplesPerBlock) = 0;
virtual void releaseMemory() {}
Expand Down
2 changes: 2 additions & 0 deletions src/processors/ProcessorStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

#include "modulation/Chorus.h"
#include "modulation/Flanger.h"
#include "modulation/MIDIModulator.h"
#include "modulation/Panner.h"
#include "modulation/Rotary.h"
#include "modulation/Tremolo.h"
Expand Down Expand Up @@ -118,6 +119,7 @@ ProcessorStore::StoreMap ProcessorStore::store = {

{ "Chorus", &processorFactory<Chorus> },
{ "Flanger", &processorFactory<Flanger> },
{ "MIDI Modulator", &processorFactory<MidiModulator> },
{ "Panner", &processorFactory<Panner> },
{ "Phaser4", &processorFactory<Phaser4> },
{ "Phaser8", &processorFactory<Phaser8> },
Expand Down
27 changes: 25 additions & 2 deletions src/processors/chain/ProcessorChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ namespace
std::cout << "Level for channel: " << ch << ": " << level << std::endl;
}
}

const MidiBuffer& getMidiBufferToUse (const MidiBuffer& hostMidiBuffer, MidiBuffer& internalMidiBuffer, int osFactor)
{
if (hostMidiBuffer.isEmpty() || osFactor == 1)
return hostMidiBuffer;

internalMidiBuffer.clear();
for (const auto& midiEvent : hostMidiBuffer)
internalMidiBuffer.addEvent (midiEvent.getMessage(), midiEvent.samplePosition * osFactor);

return internalMidiBuffer;
}
} // namespace

ProcessorChain::ProcessorChain (ProcessorStore& store,
Expand Down Expand Up @@ -70,6 +82,9 @@ void ProcessorChain::prepare (double sampleRate, int samplesPerBlock)

ioProcessor.prepare (sampleRate, samplesPerBlock);

internalMidiBuffer.clear();
internalMidiBuffer.ensureSize (256);

SpinLock::ScopedLockType scopedProcessingLock (processingLock);
initializeProcessors();
}
Expand Down Expand Up @@ -151,7 +166,7 @@ void ProcessorChain::runProcessor (BaseProcessor* proc, AudioBuffer<float>& buff
}
}

void ProcessorChain::processAudio (AudioBuffer<float>& buffer)
void ProcessorChain::processAudio (AudioBuffer<float>& buffer, const MidiBuffer& hostMidiBuffer)
{
SpinLock::ScopedTryLockType tryProcessingLock (processingLock);
if (! tryProcessingLock.isLocked())
Expand All @@ -178,10 +193,15 @@ void ProcessorChain::processAudio (AudioBuffer<float>& buffer)
inputBuffer.copyFrom (ch, 0, osBlock.getChannelPointer ((size_t) ch), osNumSamples);
}

// process standalone modulation ports
bool outProcessed = false;
const auto& processMidiBuffer = getMidiBufferToUse (hostMidiBuffer, internalMidiBuffer, ioProcessor.getOversamplingFactor());

for (auto* processor : procs)
{
// set up MIDI buffer
processor->midiBuffer = &processMidiBuffer;

// process standalone modulation ports
auto noInputsConnected = processor->getNumInputConnections() == 0;
auto modOutputConnected = processor->isOutputModulationPortConnected();
if (noInputsConnected && modOutputConnected)
Expand All @@ -192,7 +212,10 @@ void ProcessorChain::processAudio (AudioBuffer<float>& buffer)
runProcessor (&inputProcessor, inputBuffer, outProcessed);

for (auto* processor : procs)
{
processor->midiBuffer = nullptr;
processor->clearNumInputsReady();
}

if (! outProcessed)
{
Expand Down
4 changes: 3 additions & 1 deletion src/processors/chain/ProcessorChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class ProcessorChain : private AudioProcessorValueTreeState::Listener

static void createParameters (Parameters& params);
void prepare (double sampleRate, int samplesPerBlock);
void processAudio (AudioBuffer<float>& buffer);
void processAudio (AudioBuffer<float>& buffer, const MidiBuffer& hostMidiBuffer);

auto& getProcessors() { return procs; }
const auto& getProcessors() const { return procs; }
Expand Down Expand Up @@ -77,5 +77,7 @@ class ProcessorChain : private AudioProcessorValueTreeState::Listener
chowdsp::DeferredAction mainThreadAction;
std::unique_ptr<ParamForwardManager>& paramForwardManager;

MidiBuffer internalMidiBuffer;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorChain)
};
Loading