723 lines
30 KiB
Markdown
723 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 phases in order. Do not jump ahead merely because a later feature is exciting.
|
|
|
|
### Phase 0 — 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.
|
|
|
|
### Phase 1 — 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.
|
|
|
|
### Phase 2 — 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.
|
|
|
|
### Phase 3 — 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.
|
|
|
|
### Phase 4 — 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.
|
|
|
|
### Phase 5 — 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.
|
|
|
|
### Phase 6 — 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.
|
|
|
|
### Phase 7 — 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.
|
|
|
|
### Phase 8 — 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.
|
|
|
|
### Phase 9 — 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 **Phase 0 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 Phase 0 is reviewed, continue with Phase 1. Keep each phase 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.
|