Developer Guide
Welcome to the Arps Euclidya codebase! The project uses C++17, JUCE, and CMake.
Architectural Overview
The heart of the application is an instantaneous topological di-graph (Directed Acyclic Graph). Unlike DSP audio block routing (where every component processes audio every frame), our system uses a "Dirty Flag" calculation methodology.
Whenever a parameter is changed, a patch cable is dragged, or a MIDI key is struck, the Engine marks the source node as "Dirty". It then uses Kahn's Topological Sort to recalculate the sequence flow for every node down the chain instantaneously.
Engine Subsystems (src/)
GraphEngine.cpp/h: The primary data orchestrator. It holds references to allGraphNodesubclasses. It performs the AABB grid occupancy checks (isAreaOccupied) for drag-and-drop mechanics, explicitly managesGraphNode::Connectionmaps, and implementsrecalculate()andtopologicalSort().GraphNode.cpp/h: The abstract base class from which all 25+ modules inherit. Each provides grid positioning variables (gridX,gridY) and aprocess()hook for the topological sort.MidiHandler.cpp/h: Maintains the global state of MPE channel mapping. It absorbs raw JUCE MidiMessages from the DAW, interprets MPE (Pitchbend, CC74, Pressure), converts them into continuous internal bounds, and feeds theMidiInNode.ClockManager.cpp/h: Syncs heavily modified states involvinggetCumulativePpq()(Pulses Per Quarter note) for seamless sub-tick synchronization and playhead monitoring.NodeBlock.cpp/h: The JUCE visual representation of the node. Inherits from JUCEComponentbut relies entirely onGraphNodefor backend mathematics.
Core Data Model
The single form of data passed across all cables is the NoteSequence, found in src/DataModel.h.
struct HeldNote {
int noteNumber = 0;
int channel = 1;
float velocity = 0.0f;
float mpeX, mpeY, mpeZ; // Pitch Bend, Timbre, Pressure
// ...
};
using NoteSequence = std::vector<std::vector<HeldNote>>;
It represents a 2D matrix of "Time Steps", where each individual step contains a vector of HeldNotes (to natively represent Chords occurring simultaneously on a single clock step).
Creating New Nodes
If you want to create a new module, you must:
- Inherit from
GraphNode.hand implementprocess(). - Determine input ports and output ports by overriding
getNumInputPorts(). - Add it to
NodeFactory.h(createNodeinstantiation, name list, andgetPreviewMetadata()). - Recompile via the unified CMake structure.