876 lines
30 KiB
Markdown
876 lines
30 KiB
Markdown
# Project Brief: A Modern, Lightweight, energyXT-Inspired DAW
|
|
|
|
**Document purpose:** This file is the authoritative project brief and
|
|
implementation guide for an AI coding agent working inside an IDE.
|
|
|
|
**Working project name:** `xtc` (temporary; keep the project name configurable
|
|
and do not hard-code it into public APIs).
|
|
|
|
**Primary goal:** Build a modern, reliable, lightweight digital audio
|
|
workstation inspired by the workflow and modular philosophy of energyXT. This is
|
|
a new implementation, not a clone of energyXT's proprietary code or assets.
|
|
|
|
**Architecture decision:** **B — Modular engine first.** The sequencer,
|
|
instruments, effects, mixers, audio inputs, and outputs are components in one
|
|
unified processing graph. The default user experience should still be a simple
|
|
sequencer-first workspace; users can open the modular view when they need to
|
|
inspect or edit signal flow.
|
|
|
|
**Language decision:**
|
|
|
|
- **100% C for the audio engine and core domain logic.**
|
|
- **FLTK with C++ for the graphical user interface only.**
|
|
- The GUI talks to the core through a small, documented C API.
|
|
- Do not introduce C++ into the audio engine or core library.
|
|
- Keep dependencies small, prefer open-source components, and avoid unnecessary
|
|
frameworks.
|
|
|
|
---
|
|
|
|
## 1. Instructions to the AI Coding Agent
|
|
|
|
Treat this document as the project contract. Follow it in every implementation
|
|
task.
|
|
|
|
1. Read this file and the existing project before making changes.
|
|
2. Inspect the current build system, source tree, and tests. Do not assume the
|
|
repository is empty.
|
|
3. Implement work in small, buildable increments. Each increment must compile
|
|
and have relevant tests before proceeding.
|
|
4. Prefer complete, coherent file updates over partial snippets. Do not silently
|
|
remove existing functionality.
|
|
5. Before editing, state briefly what you intend to change and which files are
|
|
affected. Then make the changes.
|
|
6. Do not invent APIs, FLTK features, audio backend capabilities, or dependency
|
|
behavior. Check installed headers and official documentation when uncertain.
|
|
7. Keep C source compatible with **C99** initially. Use C11 features only when
|
|
explicitly justified and supported by the selected compiler/toolchain.
|
|
8. Use C snake_case identifiers. Use clear names; avoid cryptic one-letter names
|
|
except conventional loop counters.
|
|
9. Validate external input, lengths, indexes, counts, enum values, and
|
|
allocation sizes. Handle errors explicitly.
|
|
10. Do not hide failures, silently clamp invalid data, or continue with
|
|
partially initialized objects.
|
|
11. Avoid unnecessary dependencies, generated-code systems, global mutable
|
|
state, and elaborate abstractions.
|
|
12. Never perform audio-thread-unsafe operations from the real-time callback.
|
|
See the real-time rules below.
|
|
13. Keep the project usable on Linux first. Do not make Linux-specific details
|
|
leak into the public core API.
|
|
14. Add or update tests and documentation with each feature.
|
|
15. At the end of a task, report: files changed, behavior added, build/test
|
|
commands run, results, known limitations, and the next sensible step.
|
|
16. If a requirement is ambiguous, choose the smallest safe implementation
|
|
consistent with this document and explain the assumption. Ask a question
|
|
only if the ambiguity blocks safe progress.
|
|
17. Do not claim that a feature works unless it has been implemented and tested.
|
|
Clearly distinguish a prototype, stub, and finished feature.
|
|
18. Never copy energyXT source code, proprietary resources, UI artwork, or
|
|
undocumented internal implementation. Use energyXT only as inspiration for
|
|
high-level workflow ideas.
|
|
|
|
### Definition of done for every task
|
|
|
|
A task is complete only when:
|
|
|
|
- the code builds with warnings enabled;
|
|
- relevant tests pass;
|
|
- error paths are considered;
|
|
- resource ownership and cleanup are clear;
|
|
- documentation is updated where behavior or APIs changed;
|
|
- no unrelated functionality was removed;
|
|
- the agent reports any untested behavior honestly.
|
|
|
|
---
|
|
|
|
## 2. Product Principles
|
|
|
|
The application should feel like a small, responsive music instrument rather
|
|
than a general-purpose content-creation suite.
|
|
|
|
### Core principles
|
|
|
|
- **Simple by default, powerful when opened up.**
|
|
- A single unified component graph underlies the application.
|
|
- The sequencer, mixer, instruments, effects, audio I/O, and utility processors
|
|
use a common node model.
|
|
- Common operations should require few clicks and avoid modal dialogs when
|
|
practical.
|
|
- The modular graph should expose real signal flow, not be a decorative diagram.
|
|
- The project must remain understandable to a human developer.
|
|
- Audio reliability is more important than visual effects or feature count.
|
|
- Favor predictable behavior over clever implicit behavior.
|
|
- Use non-destructive editing where practical.
|
|
- Avoid feature bloat. Every feature must justify its complexity.
|
|
- Save files should be versioned and recoverable.
|
|
- The application should remain useful at every development milestone.
|
|
|
|
### Explicit non-goals for the first release
|
|
|
|
Do not initially implement:
|
|
|
|
- video, notation, collaboration, cloud accounts, or online services;
|
|
- an integrated marketplace;
|
|
- scripting as a requirement for basic operation;
|
|
- dozens of bundled instruments or effects;
|
|
- advanced surround/spatial audio;
|
|
- distributed processing;
|
|
- a complicated theme system;
|
|
- an elaborate plugin sandbox;
|
|
- automatic online update services;
|
|
- a proprietary project format that cannot be inspected or migrated.
|
|
|
|
These may be considered later, but they must not delay the first reliable
|
|
recording and playback workflow.
|
|
|
|
---
|
|
|
|
## 3. High-Level Architecture
|
|
|
|
Use distinct layers with narrow interfaces.
|
|
|
|
```text
|
|
+------------------------------------------------------------+
|
|
| FLTK / C++ GUI |
|
|
| Main window, transport, sequencer, mixer, modular editor, |
|
|
| dialogs, custom drawing, keyboard and mouse interaction |
|
|
+----------------------------+-------------------------------+
|
|
|
|
|
documented C API
|
|
|
|
|
+----------------------------v-------------------------------+
|
|
| C application core |
|
|
| Project model, commands/undo, transport state, persistence, |
|
|
| parameter model, asset references, validation |
|
|
+----------------------------+-------------------------------+
|
|
|
|
|
real-time-safe bridge
|
|
|
|
|
+----------------------------v-------------------------------+
|
|
| C audio engine |
|
|
| Graph compilation, node processing, scheduling, buffers, |
|
|
| transport sample position, MIDI/event delivery, metering |
|
|
+----------------------------+-------------------------------+
|
|
|
|
|
backend abstraction
|
|
|
|
|
+----------------------------v-------------------------------+
|
|
| Audio/MIDI backend(s): start with JACK-compatible audio |
|
|
| and ALSA MIDI on Linux; add backends only when justified |
|
|
+------------------------------------------------------------+
|
|
```
|
|
|
|
The C core and audio engine may be separate libraries or clearly separated
|
|
modules within one C library. Keep their interfaces explicit even if they
|
|
initially compile into one library.
|
|
|
|
### Suggested repository layout
|
|
|
|
Adapt this to an existing repository instead of replacing it blindly.
|
|
|
|
```text
|
|
xtc/
|
|
├── README.md
|
|
├── LICENSE
|
|
├── Makefile
|
|
├── docs/
|
|
│ ├── ARCHITECTURE.md
|
|
│ ├── AUDIO_ENGINE.md
|
|
│ ├── PROJECT_FORMAT.md
|
|
│ ├── GUI.md
|
|
│ ├── ROADMAP.md
|
|
│ └── decisions/
|
|
│ ├── 0001-modular-engine-first.md
|
|
│ ├── 0002-c-core-fltk-gui.md
|
|
│ └── 0003-realtime-thread-rules.md
|
|
├── include/
|
|
│ └── xtc/
|
|
│ ├── xtc.h
|
|
│ ├── xtc_types.h
|
|
│ ├── xtc_project.h
|
|
│ ├── xtc_engine.h
|
|
│ └── xtc_version.h
|
|
├── src/
|
|
│ ├── core/
|
|
│ ├── project/
|
|
│ ├── graph/
|
|
│ ├── engine/
|
|
│ ├── backend/
|
|
│ ├── midi/
|
|
│ ├── dsp/
|
|
│ ├── persistence/
|
|
│ ├── util/
|
|
│ └── gui/
|
|
│ ├── main.cpp
|
|
│ ├── main_window.cpp
|
|
│ ├── main_window.hpp
|
|
│ ├── transport_widget.cpp
|
|
│ ├── sequencer_widget.cpp
|
|
│ ├── mixer_widget.cpp
|
|
│ └── graph_widget.cpp
|
|
├── tests/
|
|
│ ├── unit/
|
|
│ ├── integration/
|
|
│ └── fixtures/
|
|
├── examples/
|
|
└── build/ # generated; do not commit
|
|
```
|
|
|
|
Keep C and C++ files clearly separated. C headers exposed to C++ must use
|
|
`extern "C"` guards.
|
|
|
|
---
|
|
|
|
## 4. Language and API Boundary
|
|
|
|
### C Code owns
|
|
|
|
- project and track data;
|
|
- graph nodes, ports, and connections;
|
|
- audio processing and DSP;
|
|
- transport and sample-position calculations;
|
|
- MIDI event representation and scheduling;
|
|
- parameter values and automation evaluation;
|
|
- project serialization and validation;
|
|
- undoable core commands where they affect domain state;
|
|
- backend-independent engine state;
|
|
- error codes and diagnostic messages.
|
|
|
|
### C++/FLTK Code owns
|
|
|
|
- windows and widgets;
|
|
- layout and drawing;
|
|
- mouse and keyboard interaction;
|
|
- selection and view state;
|
|
- GUI-only drag previews;
|
|
- user-facing dialogs and menus;
|
|
- conversion between user gestures and C API commands.
|
|
|
|
### Boundary rules
|
|
|
|
- Public C headers must compile from both C and C++.
|
|
- Do not expose C++ classes, STL containers, exceptions, or FLTK types through
|
|
the C API.
|
|
- Prefer opaque handles for objects whose internal layout must remain private.
|
|
- Use explicit integer error codes and documented output parameters.
|
|
- Define ownership for every pointer: borrowed, caller-owned, or transferred.
|
|
- Do not return pointers into mutable engine memory unless their lifetime is
|
|
guaranteed.
|
|
- Do not let GUI widgets mutate internal engine structures directly.
|
|
- Avoid a public API that mirrors every internal struct field. Provide stable
|
|
operations instead.
|
|
|
|
Example API shape (illustrative only; refine after reviewing the actual
|
|
codebase):
|
|
|
|
```c
|
|
#ifndef XTC_H
|
|
#define XTC_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
typedef struct xtc_app xtc_app;
|
|
|
|
typedef enum {
|
|
XTC_OK = 0,
|
|
XTC_ERR_INVALID_ARGUMENT,
|
|
XTC_ERR_OUT_OF_MEMORY,
|
|
XTC_ERR_NOT_FOUND,
|
|
XTC_ERR_INVALID_STATE,
|
|
XTC_ERR_IO,
|
|
XTC_ERR_UNSUPPORTED
|
|
} xtc_result;
|
|
|
|
xtc_result xtc_app_create(xtc_app **out_app);
|
|
void xtc_app_destroy(xtc_app *app);
|
|
|
|
xtc_result xtc_transport_play(xtc_app *app);
|
|
xtc_result xtc_transport_stop(xtc_app *app);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
```
|
|
|
|
Do not implement this exact API blindly if the existing design calls for a
|
|
better interface. Keep it small and consistent.
|
|
|
|
---
|
|
|
|
## 5. Modular Processing Graph — Primary Design Decision
|
|
|
|
**Decision B: Modular engine first.** The graph is the common representation of
|
|
signal processing and routing. The sequencer is a source of time-based events
|
|
and clips, not a separate audio system disconnected from the graph.
|
|
|
|
### Node categories
|
|
|
|
Start with a small set:
|
|
|
|
- audio input;
|
|
- audio output;
|
|
- audio clip player;
|
|
- MIDI input;
|
|
- MIDI clip/event player;
|
|
- instrument;
|
|
- audio effect;
|
|
- gain/pan;
|
|
- mixer;
|
|
- metering/analyzer;
|
|
- utility nodes such as channel mapping.
|
|
|
|
Do not build every node at once. First prove the graph with a test tone, gain
|
|
node, and output node.
|
|
|
|
### Ports and connections
|
|
|
|
Each node has a stable identifier, a node type, and declared ports. A port has:
|
|
|
|
- a unique identifier within its node;
|
|
- a direction (input or output);
|
|
- a signal/data type (audio, MIDI/event, or control);
|
|
- a channel layout or event format where applicable;
|
|
- a documented sample/block processing contract.
|
|
|
|
Validate connections before accepting them:
|
|
|
|
- source and destination exist;
|
|
- source port is an output and destination is an input;
|
|
- signal types are compatible;
|
|
- channel layouts can be connected or explicitly converted;
|
|
- the connection does not violate graph rules;
|
|
- identifiers and connection counts are valid.
|
|
|
|
### Cycles and feedback
|
|
|
|
For the first working engine, reject cycles in the audio-processing graph. A
|
|
directed acyclic graph is easier to schedule safely and predictably. Report a
|
|
useful error to the user. Do not silently break a cycle.
|
|
|
|
Feedback support can be considered later using explicit delay/feedback nodes and
|
|
a defined block-boundary model. Never introduce arbitrary graph cycles without a
|
|
correct scheduling design.
|
|
|
|
### Graph compilation
|
|
|
|
Compile the editable graph into an execution plan outside the real-time thread:
|
|
|
|
1. validate nodes, ports, and edges;
|
|
2. identify source nodes and reachable output nodes;
|
|
3. detect cycles;
|
|
4. topologically sort processing nodes;
|
|
5. determine channel layouts and required buffers;
|
|
6. allocate buffers and prepare node state;
|
|
7. publish an immutable, ready-to-run plan.
|
|
|
|
The audio callback processes the prepared plan. It must not perform graph
|
|
discovery, sorting, dynamic allocation, file loading, or plugin creation.
|
|
|
|
### Graph replacement during playback
|
|
|
|
The editor must not modify the graph currently being traversed by the audio
|
|
thread. Prepare a new plan off-thread and publish it through a bounded
|
|
real-time-safe mechanism. Define how the old plan is retired only after the
|
|
audio thread can no longer access it. Do not use a naive pointer swap followed
|
|
by immediate free.
|
|
|
|
Possible approaches include a carefully designed single-producer/single-consumer
|
|
handoff, epochs, or another proven reclamation scheme. Choose one, document it,
|
|
and test it under stress. Keep the initial design as simple as safety allows.
|
|
|
|
### Sequencer integration
|
|
|
|
The sequencer stores timeline content and emits events to the graph at the
|
|
correct sample positions. A clip player or instrument node consumes those
|
|
events. The GUI timeline is a view/editor for project data; it is not the audio
|
|
scheduler.
|
|
|
|
---
|
|
|
|
## 6. Real-Time Audio Requirements
|
|
|
|
These rules are mandatory.
|
|
|
|
### Audio callback must not
|
|
|
|
- allocate or free memory;
|
|
- acquire blocking mutexes or wait on condition variables;
|
|
- perform file, network, or console I/O;
|
|
- log using potentially blocking facilities;
|
|
- create or destroy GUI objects;
|
|
- load plugins, sample files, or project files;
|
|
- resize containers or buffers;
|
|
- perform unbounded work based on arbitrary user input;
|
|
- wait for the GUI or another thread.
|
|
|
|
### Audio callback should
|
|
|
|
- process a bounded number of frames and events;
|
|
- use preallocated buffers;
|
|
- use predictable algorithms;
|
|
- avoid unnecessary copying;
|
|
- detect invalid runtime states and recover safely;
|
|
- update meters through a bounded, non-blocking communication path;
|
|
- process silence safely when a source is unavailable;
|
|
- handle backend buffer sizes and channel counts within documented limits.
|
|
|
|
Do not assume all backends use a fixed block size unless the backend contract
|
|
guarantees it. Establish maximum supported frame/event counts and define what
|
|
happens when limits are exceeded.
|
|
|
|
### Thread ownership
|
|
|
|
Document which thread owns each mutable resource:
|
|
|
|
- GUI thread: widgets and GUI state;
|
|
- control/project thread: editable project state, file operations, graph
|
|
compilation;
|
|
- audio thread: active processing plan and real-time DSP state;
|
|
- backend callbacks: follow the backend's documented threading model.
|
|
|
|
Avoid sharing mutable structs across threads without an explicit synchronization
|
|
strategy. Atomics are not automatically safe for every use: verify that the
|
|
selected atomic operations are lock-free where required and use correct memory
|
|
ordering.
|
|
|
|
### Timing
|
|
|
|
Use audio sample position as the authoritative playback clock. Represent
|
|
timeline positions with an integer or rational scheme that avoids accumulated
|
|
floating-point drift. Convert musical time (bars/beats/ticks) to sample
|
|
positions using tempo and meter maps. Define behavior at tempo changes before
|
|
implementing advanced tempo automation.
|
|
|
|
---
|
|
|
|
## 7. Audio Backend Strategy
|
|
|
|
Start on Linux and keep the backend replaceable.
|
|
|
|
1. Define a small backend interface in C.
|
|
2. Implement one backend first, selected after checking the target system and
|
|
installed development packages. JACK-compatible audio is a reasonable first
|
|
target on Linux.
|
|
3. Test device enumeration, start/stop, sample rate, buffer size, channel count,
|
|
disconnects, and shutdown.
|
|
4. Keep backend-specific headers out of the public API.
|
|
5. Add another backend only after the engine works and tests pass.
|
|
|
|
Do not assume PipeWire-native, JACK compatibility, ALSA PCM, and desktop audio
|
|
routing are interchangeable. Document the chosen backend's setup and
|
|
limitations.
|
|
|
|
The engine should also have an offline/test mode that processes buffers without
|
|
physical audio hardware. This makes unit and integration testing repeatable.
|
|
|
|
---
|
|
|
|
## 8. MIDI, Parameters, Automation, and DSP
|
|
|
|
### MIDI/Events
|
|
|
|
- Use a compact, well-defined event representation.
|
|
- Store event time relative to a project timeline and convert it to sample
|
|
offsets for a processing block.
|
|
- Validate event sizes and event counts.
|
|
- Handle note-on, note-off, velocity, channel, controller, pitch bend, and
|
|
tempo/transport events as needed.
|
|
- Ensure all held notes can be released on stop, panic, seek, device loss, or
|
|
project teardown.
|
|
- Avoid unbounded event queues.
|
|
|
|
### Parameters
|
|
|
|
Every automatable control should have:
|
|
|
|
- a stable parameter ID;
|
|
- a type and valid range;
|
|
- a default value;
|
|
- a documented unit and mapping;
|
|
- a safe update mechanism;
|
|
- optional automation and MIDI mapping.
|
|
|
|
Do not silently clamp invalid serialized values. Validate project data and
|
|
return an error or apply a clearly documented migration rule. UI controls may
|
|
constrain user input, but core validation must remain authoritative.
|
|
|
|
### Automation
|
|
|
|
Implement automation only after transport, graph processing, and basic parameter
|
|
updates work. Define interpolation types explicitly (initially step and linear
|
|
are enough). Automation evaluation must be deterministic and bounded.
|
|
|
|
### DSP
|
|
|
|
Start with a few well-tested processors:
|
|
|
|
- gain;
|
|
- pan;
|
|
- simple meter/peak detector;
|
|
- test oscillator;
|
|
- basic delay only after buffer/lifetime handling is sound.
|
|
|
|
Keep DSP algorithms in C and independently testable. Use appropriate numerical
|
|
safeguards, including finite-value checks where needed. Avoid premature SIMD
|
|
optimization until profiling shows a real bottleneck.
|
|
|
|
---
|
|
|
|
## 9. FLTK/C++ GUI Guidelines
|
|
|
|
The GUI is a thin client of the C API, not the owner of the engine.
|
|
|
|
### Main views
|
|
|
|
1. **Sequencer:** tracks, audio/MIDI clips, timeline ruler, playhead, selection,
|
|
zoom and horizontal scrolling.
|
|
2. **Mixer:** channels, faders, pan, mute/solo, inserts/sends when supported,
|
|
and meters.
|
|
3. **Modular graph:** nodes, ports, connections, zoom/pan, selection, and
|
|
connection validation feedback.
|
|
4. **Transport:** play, stop, record, tempo, meter, loop range, and current
|
|
position.
|
|
5. **Project/file dialogs:** create, open, save, import audio, and manage
|
|
missing assets.
|
|
|
|
The default workspace should emphasize the sequencer and transport. The graph
|
|
view is an advanced but first-class view of the same underlying project.
|
|
|
|
### GUI Implementation
|
|
|
|
- Use FLTK for conventional widgets and application windows.
|
|
- Use custom-drawn widgets for the timeline, piano roll, mixer strips, waveform
|
|
display, and graph canvas where needed.
|
|
- Keep drawing code separate from project mutation.
|
|
- Use GUI timers or the toolkit's appropriate event mechanism for refreshing
|
|
meters; never poll from the audio callback or block the GUI waiting for audio.
|
|
- Avoid redrawing the entire application when only a small region changes.
|
|
- Support keyboard shortcuts and predictable focus behavior.
|
|
- Make selection, dragging, snapping, zooming, and scrolling consistent across
|
|
editors.
|
|
- Never call FLTK APIs from the audio thread.
|
|
- Do not hold GUI locks while calling potentially blocking engine operations.
|
|
- Keep C++ simple: use RAII for GUI resources, clear ownership, and small
|
|
classes. Do not force the C core into C++ abstractions.
|
|
|
|
### Visual direction
|
|
|
|
Aim for a compact, dark, high-contrast interface with restrained decoration.
|
|
Prioritize legible waveforms, clip boundaries, transport state, and meter
|
|
levels. Avoid excessive gradients, animations, nested panels, and modal
|
|
configuration windows.
|
|
|
|
Do not copy energyXT's artwork, exact visual assets, or proprietary UI
|
|
resources. Recreate the general principles of compactness, direct manipulation,
|
|
and modular routing in an original design.
|
|
|
|
---
|
|
|
|
## 10. Project Format and Persistence
|
|
|
|
The project format must be versioned from its first release.
|
|
|
|
### Requirements
|
|
|
|
- Include a format version and application version.
|
|
- Store stable IDs for tracks, nodes, ports where necessary, clips, parameters,
|
|
and connections.
|
|
- Save relative asset paths when possible.
|
|
- Define behavior for missing audio files.
|
|
- Validate every field before allocating large amounts of memory.
|
|
- Handle truncated, malformed, unsupported, or corrupted files gracefully.
|
|
- Never execute code while loading a project.
|
|
- Save atomically: write a temporary file, flush as appropriate, and rename it
|
|
into place when safe.
|
|
- Preserve unknown optional fields when practical, or explicitly document when
|
|
they are discarded.
|
|
- Include migration functions when the format changes.
|
|
- Keep project serialization outside the real-time thread.
|
|
|
|
Choose a simple, well-supported format after considering the dependency and
|
|
robustness trade-offs. A small, documented text format can help inspect
|
|
projects, but large binary audio data should generally live in separate assets
|
|
rather than be embedded in a text document. Do not add a serialization
|
|
dependency without a clear reason.
|
|
|
|
---
|
|
|
|
## 11. Undo/Redo and Editing Commands
|
|
|
|
Use explicit commands for user-visible edits, such as:
|
|
|
|
- create/delete/move/resize clip;
|
|
- create/delete/rename track;
|
|
- change parameter;
|
|
- add/remove node;
|
|
- connect/disconnect ports;
|
|
- change tempo or meter.
|
|
|
|
A command should validate its inputs and have a well-defined inverse or stored
|
|
previous state. Avoid designing a generic event-sourcing framework before it is
|
|
needed.
|
|
|
|
The initial implementation may begin without a complete undo stack, but design
|
|
edit operations so undo/redo can be added without rewriting all editors. Never
|
|
make an edit partially visible if it fails validation.
|
|
|
|
---
|
|
|
|
## 12. Build, Coding Standards, and Dependencies
|
|
|
|
### Build system
|
|
|
|
Use a straightforward Makefile unless the existing repository has a sound reason
|
|
to use another build system.
|
|
|
|
The build must:
|
|
|
|
- compile C with the selected C standard and warnings enabled;
|
|
- compile C++ GUI sources with the standard supported by the chosen FLTK
|
|
version;
|
|
- link the GUI with FLTK and the C core;
|
|
- link the required audio backend;
|
|
- provide `all`, `clean`, `test`, and a debug build target;
|
|
- allow compiler and installation-prefix overrides;
|
|
- avoid embedding machine-specific paths;
|
|
- separate generated objects and binaries into a build directory.
|
|
|
|
Use `pkg-config` where supported by the installed dependencies. Check actual
|
|
package names on the target distribution rather than hard-coding unverified
|
|
names.
|
|
|
|
### C Style
|
|
|
|
- Use C99 initially.
|
|
- Use `snake_case`.
|
|
- Prefer explicit structs and functions over clever macros.
|
|
- Keep functions focused.
|
|
- Validate pointers and sizes at API boundaries.
|
|
- Check allocation and I/O results.
|
|
- Avoid variable-length arrays for untrusted sizes.
|
|
- Make ownership and cleanup obvious.
|
|
- Use `const` where appropriate.
|
|
- Do not suppress warnings merely to get a clean build.
|
|
|
|
### C++ GUI Style
|
|
|
|
- Keep C++ confined to the GUI and any unavoidable C++-only third-party wrapper.
|
|
- Prefer small classes with explicit ownership.
|
|
- Use RAII for GUI-side resources.
|
|
- Avoid exceptions crossing the C API boundary.
|
|
- Catch exceptions at the GUI's top-level boundary if any dependency can throw.
|
|
- Never pass STL types across the C API.
|
|
|
|
### Dependencies
|
|
|
|
Before adding a dependency:
|
|
|
|
1. identify the exact feature it provides;
|
|
2. check license and maintenance status;
|
|
3. determine whether the existing code can provide the functionality simply and
|
|
safely;
|
|
4. document the dependency and its version constraints;
|
|
5. keep it optional if the feature is optional.
|
|
|
|
Prefer open-source dependencies with clear licensing. Do not claim the whole
|
|
application is license-compatible until all direct and bundled dependencies have
|
|
been reviewed.
|
|
|
|
---
|
|
|
|
## 13. Testing and Diagnostics
|
|
|
|
### Unit tests
|
|
|
|
Test at least:
|
|
|
|
- graph validation and topological sorting;
|
|
- invalid ports and incompatible connections;
|
|
- cycle detection;
|
|
- parameter validation;
|
|
- sample-position and tempo conversion;
|
|
- project serialization/deserialization;
|
|
- malformed project files and missing assets;
|
|
- node lifecycle and buffer bounds;
|
|
- DSP output for known inputs;
|
|
- command execution and undo/redo when implemented.
|
|
|
|
### Integration tests
|
|
|
|
Test:
|
|
|
|
- test oscillator → gain → output processing;
|
|
- offline rendering with deterministic output;
|
|
- graph replacement during active processing;
|
|
- start/stop and repeated initialization/cleanup;
|
|
- project save/load round trips;
|
|
- missing audio assets;
|
|
- MIDI note start/stop and panic;
|
|
- backend disconnect/reconnect where supported.
|
|
|
|
### Tools
|
|
|
|
Where available, use:
|
|
|
|
- compiler warnings;
|
|
- AddressSanitizer and UndefinedBehaviorSanitizer in non-real-time test builds;
|
|
- static analysis;
|
|
- a leak checker;
|
|
- stress tests for graph edits and shutdown;
|
|
- audio loopback or offline render comparisons.
|
|
|
|
Sanitizers and diagnostic logging should not be enabled in production real-time
|
|
callbacks unless their real-time implications have been specifically evaluated.
|
|
|
|
---
|
|
|
|
## 14. Development Roadmap and Acceptance Criteria
|
|
|
|
Implement these milestones in order. Do not jump ahead merely because a later
|
|
feature is exciting.
|
|
|
|
### Repository and design baseline
|
|
|
|
- Inspect the repository and available toolchain.
|
|
- Create or update architecture documentation and the Makefile.
|
|
- Verify C compiler, C++ compiler, FLTK, audio development headers, and test
|
|
tools.
|
|
- Record dependency and licensing decisions.
|
|
- Add a minimal application that starts and exits cleanly.
|
|
|
|
**Acceptance:** clean build on the target Linux environment; no audio engine
|
|
assumptions hidden in GUI code.
|
|
|
|
### C Core and offline processing
|
|
|
|
- Create the C library/API.
|
|
- Define node, port, connection, and graph data structures.
|
|
- Implement graph validation and topological sorting.
|
|
- Implement preallocated audio buffers.
|
|
- Add oscillator, gain, meter, and output test nodes.
|
|
- Implement deterministic offline block processing.
|
|
|
|
**Acceptance:** automated test proves oscillator → gain → meter/output produces
|
|
expected samples without hardware.
|
|
|
|
### Real-time backend
|
|
|
|
- Add the first Linux audio backend.
|
|
- Implement start/stop, device configuration, and error handling.
|
|
- Ensure callback rules are followed.
|
|
- Add safe control-to-audio communication.
|
|
- Stress-test shutdown and graph replacement.
|
|
|
|
**Acceptance:** stable playback for a sustained test; no known callback
|
|
allocations or blocking calls; failures are reported clearly.
|
|
|
|
### FLTK Shell
|
|
|
|
- Create main window, menus, transport, status area, and view switching.
|
|
- Call the C API through the public header.
|
|
- Add an audio engine status indicator.
|
|
- Implement clean startup and shutdown.
|
|
|
|
**Acceptance:** GUI can start/stop the engine, report backend errors, and close
|
|
without leaks or hangs.
|
|
|
|
### Modular graph editor
|
|
|
|
- Draw nodes and ports.
|
|
- Add/select/move nodes.
|
|
- Create/delete connections.
|
|
- Display invalid connection feedback.
|
|
- Submit graph edits through validated C API commands.
|
|
|
|
**Acceptance:** graph shown by the GUI matches the graph used by the engine;
|
|
invalid connections are rejected without corrupting active playback.
|
|
|
|
### Project and sequencer foundation
|
|
|
|
- Add project persistence and stable IDs.
|
|
- Add tracks and clip objects.
|
|
- Implement timeline rendering, selection, moving, trimming, zoom, and
|
|
scrolling.
|
|
- Add transport/sample-position synchronization.
|
|
|
|
**Acceptance:** save/load a project with clips and reproduce the same
|
|
arrangement.
|
|
|
|
### Audio clips and recording
|
|
|
|
- Add audio file import and clip playback.
|
|
- Add recording through a safe producer/consumer buffer path.
|
|
- Keep file I/O outside the audio callback.
|
|
- Add missing-asset reporting.
|
|
|
|
**Acceptance:** record, save, reopen, and play a basic audio project without
|
|
callback file I/O.
|
|
|
|
### MIDI And a simple instrument
|
|
|
|
- Add MIDI input and clip events.
|
|
- Add a basic internal synthesizer or test instrument.
|
|
- Handle note-off, panic, seeking, and stop correctly.
|
|
- Add a minimal piano-roll editor only after event scheduling works.
|
|
|
|
**Acceptance:** record or draw MIDI notes, play them in time, save the project,
|
|
and reopen it.
|
|
|
|
### Mixer, automation, and editing polish
|
|
|
|
- Add mixer channels, gain/pan, meters, mute/solo.
|
|
- Add automation lanes and interpolation.
|
|
- Add a bounded undo/redo system for common edits.
|
|
- Improve keyboard interaction and workflow.
|
|
|
|
**Acceptance:** basic composition and mixing can be completed without editing
|
|
project files manually.
|
|
|
|
### Extensibility and release preparation
|
|
|
|
- Define and document the native node/instrument/effect API.
|
|
- Evaluate external plugin hosting only after core lifecycle handling is robust.
|
|
- Review project format migration, crash behavior, performance, and licensing.
|
|
- Package the application and document Linux setup.
|
|
|
|
**Acceptance:** reproducible release build, tested install/uninstall, clear
|
|
limitations, and complete user/developer documentation.
|
|
|
|
---
|
|
|
|
## 15. Initial Implementation Task for the Agent
|
|
|
|
Start with the **repository and design baseline** only.
|
|
|
|
1. Inspect the current repository and report its state.
|
|
2. Identify the available C/C++ compilers, FLTK version, audio backend
|
|
development packages, `pkg-config`, and test tools.
|
|
3. Do not install packages or modify system configuration without permission.
|
|
4. Propose the smallest initial source tree compatible with this brief.
|
|
5. Add or update the Makefile and documentation only as needed.
|
|
6. Create a minimal program that builds and exits cleanly; do not yet implement
|
|
a full audio engine or GUI.
|
|
7. Build it and run its tests.
|
|
8. Report exact commands, results, assumptions, and any missing dependencies.
|
|
|
|
After the baseline is reviewed, continue with the C core and offline processing.
|
|
Keep each milestone independently buildable and testable.
|
|
|
|
---
|
|
|
|
## 16. Long-Term Quality Bar
|
|
|
|
The project succeeds if a musician can open it quickly, connect components
|
|
without fighting the interface, record and arrange audio/MIDI, and make music
|
|
without needing to understand every detail of the engine.
|
|
|
|
The implementation succeeds if a developer can read the C code, understand
|
|
ownership and thread boundaries, test components independently, and add
|
|
functionality without turning the application into an unmaintainable framework.
|
|
|
|
**Guiding rule:** build a small, coherent system whose components compose well.
|
|
Do not build a large DAW and then attempt to simplify it afterward.
|