Skip to content

Commit

Permalink
WIP: MIDI system improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
timothyschoen committed Nov 28, 2024
1 parent d22d66b commit d0f5160
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 48 deletions.
4 changes: 1 addition & 3 deletions Source/Pd/Patch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,8 @@ Patch::~Patch()
// Otherwise, this is a subpatcher and it will get cleaned up by Pd
// when the object is deleted
if (closePatchOnDelete && instance) {
instance->setThis();
instance->clearObjectImplementationsForPatch(this); // Make sure that there are no object implementations running in the background!

if (auto patch = ptr.get<void>()) {
instance->clearObjectImplementationsForPatch(this); // Make sure that there are no object implementations running in the background!
libpd_closefile(patch.get());
}
}
Expand Down
58 changes: 29 additions & 29 deletions Source/PluginProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,8 @@ void PluginProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
variableBlockSize = !ProjectInfo::isStandalone || samplesPerBlock < pdBlockSize || samplesPerBlock % pdBlockSize != 0;

if (variableBlockSize) {
inputFifo = std::make_unique<AudioFifo>(maxChannels, std::max<int>(pdBlockSize, samplesPerBlock) * 3);
outputFifo = std::make_unique<AudioFifo>(maxChannels, std::max<int>(pdBlockSize, samplesPerBlock) * 3);
inputFifo = std::make_unique<AudioMidiFifo>(maxChannels, std::max<int>(pdBlockSize, samplesPerBlock) * 3);
outputFifo = std::make_unique<AudioMidiFifo>(maxChannels, std::max<int>(pdBlockSize, samplesPerBlock) * 3);
outputFifo->writeSilence(Instance::getBlockSize());
}

Expand Down Expand Up @@ -625,13 +625,14 @@ void PluginProcessor::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiB
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();

if (!ProjectInfo::isStandalone && !midiBuffer.isEmpty()) {
midiDeviceManager.enqueueMidiInput(0, midiBuffer);
}

setThis();
sendPlayhead();
sendParameters();

midiDeviceManager.dequeueMidiInput(buffer.getNumSamples(), [this](int port, int blockSize, MidiBuffer& buffer) {
midiInputHistory.addEvents(buffer, 0, blockSize, 0);
sendMidiBuffer(port, buffer);
});

for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i) {
buffer.clear(i, 0, buffer.getNumSamples());
Expand All @@ -645,7 +646,8 @@ void PluginProcessor::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiB
if (variableBlockSize) {
processVariable(blockOut, midiBuffer);
} else {
processConstant(blockOut, midiBuffer);
midiBuffer.clear();
processConstant(blockOut);
}

if (oversampling > 0) {
Expand Down Expand Up @@ -692,10 +694,6 @@ void PluginProcessor::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiB
midiInputHistory.clear();
midiOutputHistory.clear();

midiBuffer.clear();
if (!ProjectInfo::isStandalone) {
midiDeviceManager.dequeueMidiOutput(0, midiBuffer, buffer.getNumSamples());
}
// If the internalSynth is enabled and loaded, let it process the midi
if (internalSynthPort >= 0 && internalSynth->isReady()) {
midiBufferInternalSynth.clear();
Expand All @@ -708,8 +706,6 @@ void PluginProcessor::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiB
}
midiBufferInternalSynth.clear();

midiDeviceManager.sendMidiOutput();

if (protectedMode && buffer.getNumChannels() > 0) {
// Take out inf and NaN values
auto* const* writePtr = buffer.getArrayOfWritePointers();
Expand All @@ -726,7 +722,8 @@ void PluginProcessor::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiB
}
}

void PluginProcessor::processConstant(dsp::AudioBlock<float> buffer, MidiBuffer& midiBuffer)
// only used for standalone, and if blocksize if a multiple of 64
void PluginProcessor::processConstant(dsp::AudioBlock<float> buffer)
{
int pdBlockSize = Instance::getBlockSize();
int numBlocks = buffer.getNumSamples() / pdBlockSize;
Expand All @@ -750,11 +747,6 @@ void PluginProcessor::processConstant(dsp::AudioBlock<float> buffer, MidiBuffer&

setThis();

midiDeviceManager.dequeueMidiInput(pdBlockSize, [this](int port, int blockSize, MidiBuffer& buffer) {
midiInputHistory.addEvents(buffer, 0, blockSize, 0);
sendMidiBuffer(port, buffer);
});

// Process audio
performDSP(audioVectorIn.data(), audioVectorOut.data());

Expand All @@ -780,17 +772,20 @@ void PluginProcessor::processVariable(dsp::AudioBlock<float> buffer, MidiBuffer&
auto const pdBlockSize = Instance::getBlockSize();
auto const numChannels = buffer.getNumChannels();

inputFifo->writeAudio(buffer);

audioAdvancement = 0; // Always has to be 0 if we use the AudioFifo!
inputFifo->writeAudioAndMidi(buffer, midiBuffer);
midiInputHistory.addEvents(midiBuffer, 0, buffer.getNumSamples(), 0);

audioAdvancement = 0; // Always has to be 0 if we use the AudioMidiFifo!

while (inputFifo->getNumSamplesAvailable() >= pdBlockSize) {
inputFifo->readAudio(audioBufferIn);
blockMidiBuffer.clear();

inputFifo->readAudioAndMidi(audioBufferIn, blockMidiBuffer);

midiDeviceManager.dequeueMidiInput(pdBlockSize, [this](int port, int blockSize, MidiBuffer& buffer) {
midiInputHistory.addEvents(buffer, 0, blockSize, 0);
sendMidiBuffer(port, buffer);
});
if(!ProjectInfo::isStandalone)
{
sendMidiBuffer(1, blockMidiBuffer);
}

for (int channel = 0; channel < audioBufferIn.getNumChannels(); channel++) {
// Copy the channel data into the vector
Expand Down Expand Up @@ -825,10 +820,15 @@ void PluginProcessor::processVariable(dsp::AudioBlock<float> buffer, MidiBuffer&
pdBlockSize);
}

outputFifo->writeAudio(audioBufferOut);
blockMidiBuffer.clear();
if(!ProjectInfo::isStandalone) {
midiDeviceManager.dequeueMidiOutput(1, blockMidiBuffer, pdBlockSize);
outputFifo->writeAudioAndMidi(audioBufferOut, blockMidiBuffer);
}
}

outputFifo->readAudio(buffer);
midiBuffer.clear();
outputFifo->readAudioAndMidi(buffer, midiBuffer);
}

void PluginProcessor::sendPlayhead()
Expand Down
10 changes: 5 additions & 5 deletions Source/PluginProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "Utility/Config.h"
#include "Utility/Limiter.h"
#include "Utility/SettingsFile.h"
#include "Utility/AudioFifo.h"
#include "Utility/AudioMidiFifo.h"
#include "Utility/SeqLock.h"
#include "Utility/MidiDeviceManager.h"

Expand Down Expand Up @@ -99,7 +99,7 @@ class PluginProcessor final : public AudioProcessor

void reloadAbstractions(File changedPatch, t_glist* except) override;

void processConstant(dsp::AudioBlock<float>, MidiBuffer& midiBuffer);
void processConstant(dsp::AudioBlock<float>);
void processVariable(dsp::AudioBlock<float>, MidiBuffer& midiBuffer);

MidiDeviceManager& getMidiDeviceManager();
Expand Down Expand Up @@ -199,10 +199,10 @@ class PluginProcessor final : public AudioProcessor
HeapArray<float> audioVectorIn;
HeapArray<float> audioVectorOut;

std::unique_ptr<AudioFifo> inputFifo;
std::unique_ptr<AudioFifo> outputFifo;
std::unique_ptr<AudioMidiFifo> inputFifo;
std::unique_ptr<AudioMidiFifo> outputFifo;

MidiBuffer midiInputHistory, midiOutputHistory;
MidiBuffer blockMidiBuffer, midiInputHistory, midiOutputHistory;
MidiBuffer midiBufferInternalSynth;

MidiDeviceManager midiDeviceManager;
Expand Down
34 changes: 27 additions & 7 deletions Source/Utility/AudioMidiFifo.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
*/

class AudioFifo {
class AudioMidiFifo {
public:
AudioFifo(int channels, int maxSize)
AudioMidiFifo(int channels, int maxSize)
{
setSize(channels, maxSize);
}
Expand All @@ -27,16 +27,19 @@ class AudioFifo {
{
fifo.reset();
audioBuffer.clear();
midiBuffer.clear();
}

int getNumSamplesAvailable() { return fifo.getNumReady(); }
int getNumSamplesFree() { return fifo.getFreeSpace(); }

void writeAudio(dsp::AudioBlock<float> const& audioSrc)
void writeAudioAndMidi(dsp::AudioBlock<float> const& audioSrc, MidiBuffer const& midiSrc)
{
jassert(getNumSamplesFree() >= audioSrc.getNumSamples());
jassert(audioSrc.getNumChannels() == audioBuffer.getNumChannels());

midiBuffer.addEvents(midiSrc, 0, audioSrc.getNumSamples(), fifo.getNumReady());

int start1, size1, start2, size2;
fifo.prepareToWrite(audioSrc.getNumSamples(), start1, size1, start2, size2);

Expand All @@ -48,11 +51,18 @@ class AudioFifo {
fifo.finishedWrite(size1 + size2);
}

void readAudio(dsp::AudioBlock<float>& audioDst)
void readAudioAndMidi(dsp::AudioBlock<float>& audioDst, MidiBuffer& midiDst)
{
jassert(getNumSamplesAvailable() >= audioDst.getNumSamples());
jassert(audioDst.getNumChannels() == audioBuffer.getNumChannels());

midiDst.addEvents(midiBuffer, 0, audioDst.getNumSamples(), 0);

// Move all the remaining midi events forward by the number of samples removed
MidiBuffer temp;
temp.addEvents(midiBuffer, audioDst.getNumSamples(), fifo.getNumReady(), -audioDst.getNumSamples());
midiBuffer = temp;

int start1, size1, start2, size2;
fifo.prepareToRead(audioDst.getNumSamples(), start1, size1, start2, size2);

Expand All @@ -79,11 +89,13 @@ class AudioFifo {
fifo.finishedWrite(size1 + size2);
}

void writeAudio(juce::AudioBuffer<float> const& audioSrc)
void writeAudioAndMidi(juce::AudioBuffer<float> const& audioSrc, juce::MidiBuffer const& midiSrc)
{
jassert(getNumSamplesFree() >= audioSrc.getNumSamples());
jassert(audioSrc.getNumChannels() == audioBuffer.getNumChannels());

midiBuffer.addEvents(midiSrc, 0, audioSrc.getNumSamples(), fifo.getNumReady());

int start1, size1, start2, size2;
fifo.prepareToWrite(audioSrc.getNumSamples(), start1, size1, start2, size2);

Expand All @@ -98,11 +110,18 @@ class AudioFifo {
fifo.finishedWrite(size1 + size2);
}

void readAudio(juce::AudioBuffer<float>& audioDst)
void readAudioAndMidi(juce::AudioBuffer<float>& audioDst, juce::MidiBuffer& midiDst)
{
jassert(getNumSamplesAvailable() >= audioDst.getNumSamples());
jassert(audioDst.getNumChannels() == audioBuffer.getNumChannels());

midiDst.addEvents(midiBuffer, 0, audioDst.getNumSamples(), 0);

// Move all the remaining midi events forward by the number of samples removed
juce::MidiBuffer temp;
temp.addEvents(midiBuffer, audioDst.getNumSamples(), fifo.getNumReady(), -audioDst.getNumSamples());
midiBuffer = temp;

int start1, size1, start2, size2;
fifo.prepareToRead(audioDst.getNumSamples(), start1, size1, start2, size2);

Expand All @@ -120,6 +139,7 @@ class AudioFifo {
private:
AbstractFifo fifo { 1 };
AudioBuffer<float> audioBuffer;
MidiBuffer midiBuffer;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioFifo)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioMidiFifo)
};
16 changes: 12 additions & 4 deletions Source/Utility/MidiDeviceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

class MidiDeviceManager : public ChangeListener
, public AsyncUpdater
, public MidiInputCallback {
, public MidiInputCallback
, public Timer
{

public:
MidiDeviceManager()
Expand All @@ -29,6 +31,7 @@ class MidiDeviceManager : public ChangeListener

updateMidiDevices();
midiBufferIn.ensureSize(2048);
startTimer(2);
}

~MidiDeviceManager()
Expand Down Expand Up @@ -261,11 +264,10 @@ class MidiDeviceManager : public ChangeListener
}
}

// Send all MIDI output to target devices
void sendMidiOutput()
{
for (auto& port : outputPorts) {
if(!port.enabled) continue;
if(!port.enabled || port.queue.isEmpty()) continue;
for (auto* device : port.devices) {
device->sendBlockOfMessages(port.queue, Time::getMillisecondCounterHiRes(), currentSampleRate);
}
Expand Down Expand Up @@ -346,6 +348,12 @@ class MidiDeviceManager : public ChangeListener
}

private:

void timerCallback() override
{
sendMidiOutput();
}

void handleIncomingMidiMessage(MidiInput* input, MidiMessage const& message) override
{
auto port = [this, input]() -> int {
Expand Down Expand Up @@ -375,7 +383,7 @@ class MidiDeviceManager : public ChangeListener
}

float currentSampleRate = 44100.f;
float lastCallbackTime = 0.0f;
std::atomic<float> lastCallbackTime = 0.0f;

struct MidiInputPort
{
Expand Down

0 comments on commit d0f5160

Please sign in to comment.