DAW SynchronizationFrom PluginProcessor.cpp:1055-1128, playhead information is synchronized:
void PluginProcessor::sendPlayhead(){ AudioPlayHead const* playhead = getPlayHead(); if (!playhead) return; auto infos = playhead->getPosition(); lockAudioThread(); setThis(); if (infos.hasValue()) { // Playing state atoms_playhead[0] = infos->getIsPlaying(); sendMessage("__playhead", "playing", atoms_playhead); // Recording state atoms_playhead[0] = infos->getIsRecording(); sendMessage("__playhead", "recording", atoms_playhead); // Loop points atoms_playhead[0] = infos->getIsLooping(); auto loopPoints = infos->getLoopPoints(); if (loopPoints.hasValue()) { atoms_playhead.emplace_back(loopPoints->ppqStart); atoms_playhead.emplace_back(loopPoints->ppqEnd); } sendMessage("__playhead", "looping", atoms_playhead); // Tempo if (infos->getBpm().hasValue()) { atoms_playhead[0] = static_cast<float>(*infos->getBpm()); sendMessage("__playhead", "bpm", atoms_playhead); } // Position (PPQ, samples, seconds) auto ppq = infos->getPpqPosition(); auto samplesTime = infos->getTimeInSamples(); auto secondsTime = infos->getTimeInSeconds(); atoms_playhead.resize(3); atoms_playhead[0] = ppq.hasValue() ? *ppq : 0.0f; atoms_playhead[1] = samplesTime.hasValue() ? *samplesTime : 0.0f; atoms_playhead[2] = secondsTime.hasValue() ? *secondsTime : 0.0f; sendMessage("__playhead", "position", atoms_playhead); } unlockAudioThread();}
Pd patches can receive:
Playing/recording/looping state
Tempo (BPM)
Time signature
Transport position (PPQ, samples, seconds)
Loop points
Frame rate
Access via:
[r __playhead playing] ← 0 or 1[r __playhead bpm] ← Current tempo[r __playhead position] ← PPQ, samples, seconds (list)
AutomationFrom PluginProcessor.cpp:120-131, parameters are exposed:
// Volume parameter (built-in)auto* volumeParameter = new PlugDataParameter(this, "volume", 0.8f, true, 0, 0.0f, 1.0f);addParameter(volumeParameter);// General purpose automation (param1-paramN)for (int n = 0; n < numParameters; n++) { auto* parameter = new PlugDataParameter(this, "param" + String(n + 1), 0.0f, false, n + 1, 0.0f, 1.0f); addParameter(parameter);}
Automation parameters:
param1 through param64 (default)
Mapped to Pd receivers: [r param1], [r param2], etc.
Fully automatable in DAW
Saved with DAW project
From PluginProcessor.cpp:1149-1159, parameter updates:
void PluginProcessor::sendParameters(){ ScopedLock lock(audioLock); for (auto* param : enabledParameters) { if (EXPECT_UNLIKELY(param->wasChanged())) { auto title = param->getTitle(); sendFloat(title.data(), param->getUnscaledValue()); param->setUnchanged(); } }}
State ManagementPlugin state is saved with DAW projects:
Patch content (embedded)
Parameter values
Editor size/position
Connection paths (segmented)
GUI states
From PluginProcessor.cpp:1224-1308:
void PluginProcessor::getStateInformation(MemoryBlock& destData){ setThis(); MemoryOutputStream ostream(destData, false); ostream.writeInt(patches.size()); // Save each patch lockAudioThread(); for (auto const& patch : patches) { auto content = patch->getCanvasContent(); auto patchFile = patch->getCurrentFile().getFullPathName(); // Store content and location ostream.writeString(content); ostream.writeString(patchFile); } unlockAudioThread(); // Save settings ostream.writeInt(getLatencySamples() - Instance::getBlockSize()); ostream.writeInt(oversampling); ostream.writeFloat(getValue<float>(tailLength)); // XML format for extensibility auto xml = XmlElement("plugdata_save"); xml.setAttribute("Version", PLUGDATA_VERSION); xml.setAttribute("Oversampling", oversampling); xml.setAttribute("Width", editor->getWidth()); xml.setAttribute("Height", editor->getHeight()); PlugDataParameter::saveStateInformation(xml, getParameters());}
MIDI RoutingPlugin mode MIDI handling from PluginProcessor.cpp:1166-1201:
void PluginProcessor::sendMidiBuffer(int const device, MidiBuffer const& buffer){ if (acceptsMidi()) { for (auto event : buffer) { auto message = event.getMessage(); auto const channel = message.getChannel() + (device << 4); if (message.isNoteOn()) { sendNoteOn(channel, message.getNoteNumber(), message.getVelocity()); } else if (message.isNoteOff()) { sendNoteOn(channel, message.getNoteNumber(), 0); } else if (message.isController()) { sendControlChange(channel, message.getControllerNumber(), message.getControllerValue()); } else if (message.isPitchWheel()) { sendPitchBend(channel, message.getPitchWheelValue() - 8192); } // ... additional MIDI message types } }}
✅ Prototyping new ideas quickly
✅ Learning Pure Data/DSP
✅ Live performance without DAW
✅ Testing with different audio interfaces
✅ Working with multiple patches simultaneously
✅ Need direct file system access
✅ Developing standalone instruments/effects
✅ Integrating with DAW workflow
✅ Recording automation
✅ Tempo sync required
✅ Multiple instances needed
✅ Mixing with other plugins
✅ Session recall important
✅ Distributing to users with DAWs