Real-time composition loop #
The elected bed is a musical genome: chord progression, topline, rhythm, and rate. A live piece may develop that genome measure by measure, but inference never enters the audio callback.
Three clocks #
- Sample clock: the C engine renders every audio period. It is sovereign.
- Measure clock: a fixed-size
MeasurePlanbecomes immutable at the bar boundary. The engine always holds a committed next measure and a local fallback after it. - Context clock: an agent and data adapters update future plans. Time may update each minute; weather no faster than its source, currently five minutes in the existing AC weather piece.
At 120 BPM, a 4/4 measure lasts two seconds. While measure N plays, N+1 is already committed. An agent normally proposes N+2, giving it about two seconds; a network model should work four to eight measures ahead. A missed deadline causes no silence: C advances the elected motif through a deterministic local variation.
Native boundary #
fedac/native/src/audio.c already renders voices period by period. HDA uses a
roughly 1 ms period and 4 ms buffer; SOF uses 20 ms and 80 ms to avoid XRUNs.
Add a transport above those voices:
typedef struct {
uint64_t bar;
float bpm;
uint8_t chord_count, note_count, pulse_count;
ACChord chords[4];
ACNote notes[32];
ACPulse pulses[32];
uint64_t context_hash;
} ACMeasurePlan;
A control thread parses plans and writes a double buffer. At a measure boundary the audio thread atomically swaps one pointer. It never parses JSON, waits for a process, allocates memory, fetches data, or calls an agent.
Agent observation and action #
The agent receives the elected genome, the last eight measure summaries, the next committed measure, listener actions, and a minimized context vector. It may choose only bounded musical operations: retain, transpose a motif degree, change chord inversion or extension, alter note density, switch an elected rhythm family, or move rate inside the elected range. Every decision is logged with its input vector and can be replayed without the model.
Time and weather are slow compositional forces:
| input | bounded musical control |
|---|---|
| UTC measure number | shared seed and globally aligned form |
| local hour / sunrise | mode brightness and register |
| cloud cover | chord extensions and note density |
| wind | syncopation and rate within ±4% |
| precipitation | articulation and ornament probability |
| temperature | voicing width, not raw pitch |
Inputs must not directly trigger notes. They bias future-plan choices, are quantized at measure or section boundaries, and retain the previous valid value when unavailable.
Latency experiment #
- Render the elected genome locally with no agent and verify indefinite, sample-continuous measure swaps.
- Feed synthetic context changes and record decision-to-audible latency.
- Add a deterministic local planner for N+1 and a remote agent for N+4.
- Compare HDA and SOF using
fedac/native/scripts/latency.mjs; the success condition is zero added XRUNs and zero missed-measure silence. - Only then connect live time, Open-Meteo, or listener data.
Audible prototype #
pop/realtime/c/measure-engine.c implements the fixed plan queue, atomic
measure-boundary swap, local fallback, and CoreAudio callback. The callback
does not allocate, parse, fetch, lock, or infer. plan-agent.mjs currently
stands in for an agent: it reads Los Angeles weather, combines it with a UTC
minute seed, and keeps the engine planned four measures ahead.
make -C pop/realtime
node pop/realtime/plan-agent.mjs --seconds 180