Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ get-pip.py
# Audio test samples
/audio_test_samples/

# Local QA screenshots/reports generated by browser and native harnesses
/qa/

# Logs
*.log
npm-debug.log*
Expand Down
15 changes: 15 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,21 @@ python build.py prod

**No feature flags** — all features (ASIO, WASAPI, DirectSound, VST3 hosting, WebView2) are always enabled via hardcoded `target_compile_definitions` in CMakeLists.txt. The `build.py dev` mode uses Debug config; `build.py prod` uses Release.

### Manual Testing Handoff Requirement

When handing work to the user for manual testing, assume the user will run exactly:

```bash
python build.py dev --run
```

Before asking for manual testing:
- Make sure the latest frontend code is built into `frontend/dist` when packaged fallback could be used.
- Run `cmake --build build --config Debug` after frontend or C++ changes so the Debug app and copied `webui` assets are current.
- Do not require the user to pre-run Vite, npm, or any other server. `python build.py dev --run` must start what it needs.
- Stop any Codex-started dev servers, harness browsers, or background Vite/npm processes before handing off. Verify port `5173` is not left occupied by a Codex-started process.
- In the handoff, state that the CMake Debug build was completed and that no pre-running server is required.

## Key Technical Details

### State Management
Expand Down
3,673 changes: 3,423 additions & 250 deletions Source/AudioEngine.cpp

Large diffs are not rendered by default.

26 changes: 23 additions & 3 deletions Source/AudioEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class AudioEngine : public juce::AudioIODeviceCallback,
juce::AudioDeviceManager& getDeviceManager() { return deviceManager; }

// Messaging
juce::String addTrack(const juce::String& explicitId = juce::String()); // Returns track ID, optional explicit ID for restore
juce::String addTrack(const juce::String& explicitId = juce::String(),
const juce::String& initialType = juce::String()); // Returns track ID, optional explicit ID/type for restore
bool removeTrack(const juce::String& trackId);
bool reorderTrack(const juce::String& trackId, int newPosition);
int getTrackIndex(const juce::String& trackId) const; // For lookups
Expand Down Expand Up @@ -204,7 +205,14 @@ class AudioEngine : public juce::AudioIODeviceCallback,
void setTrackMIDIInput(const juce::String& trackId, const juce::String& deviceName, int channel);
void setTrackMIDIClips(const juce::String& trackId, const juce::String& clipsJSON);
bool sendMidiNote(const juce::String& trackId, int note, int velocity, bool isNoteOn);
juce::var getTrackMIDINoteActivity(const juce::String& trackId, int maxAgeMs = 1200) const;
bool panicMIDI();
bool loadInstrument(const juce::String& trackId, const juce::String& vstPath);
bool removeInstrument(const juce::String& trackId);
bool setTrackSamplerSample(const juce::String& trackId, const juce::String& samplePath, int rootNote);
bool clearTrackSamplerSample(const juce::String& trackId);
juce::String getInstrumentState(const juce::String& trackId);
bool setInstrumentState(const juce::String& trackId, const juce::String& base64State);
void setProcessingPrecision(const juce::String& precisionMode);
juce::String getProcessingPrecision() const;
bool setTrackPluginPrecisionOverride(const juce::String& trackId, int fxIndex, bool isInputFX, const juce::String& mode);
Expand All @@ -218,6 +226,13 @@ class AudioEngine : public juce::AudioIODeviceCallback,
juce::var getTrackInputFX(const juce::String& trackId);
juce::var getTrackFX(const juce::String& trackId);
juce::var getPluginParameters(const juce::String& trackId, int fxIndex, bool isInputFX);
bool setPluginParameter(const juce::String& trackId, int fxIndex, bool isInputFX, int paramIndex, float value);
juce::var getBuiltInPluginSchema(const juce::String& trackId, const juce::String& chainType, int fxIndex);
juce::var getBuiltInPluginState(const juce::String& trackId, const juce::String& chainType, int fxIndex);
bool setBuiltInPluginParam(const juce::String& trackId, const juce::String& chainType, int fxIndex,
const juce::String& paramId, float value);
bool setBuiltInPluginState(const juce::String& trackId, const juce::String& chainType, int fxIndex,
const juce::String& stateJSON);
void removeTrackInputFX(const juce::String& trackId, int fxIndex);
void removeTrackFX(const juce::String& trackId, int fxIndex);
void bypassTrackInputFX(const juce::String& trackId, int fxIndex, bool bypassed);
Expand Down Expand Up @@ -346,6 +361,9 @@ class AudioEngine : public juce::AudioIODeviceCallback,
// Set all automation points for a track parameter (bulk sync from frontend)
void setAutomationPoints(const juce::String& trackId, const juce::String& parameterId,
const juce::String& pointsJSON);
void replaceAutomationPointsInRange(const juce::String& trackId, const juce::String& parameterId,
double startTimeSeconds, double endTimeSeconds,
const juce::String& pointsJSON);
// Set automation mode for a track parameter
void setAutomationMode(const juce::String& trackId, const juce::String& parameterId,
const juce::String& modeStr);
Expand Down Expand Up @@ -414,6 +432,7 @@ class AudioEngine : public juce::AudioIODeviceCallback,

// MIDI Import/Export (Phase 19.9)
juce::var importMIDIFile(const juce::String& filePath);
bool exportProjectMIDI(const juce::String& outputPath, const juce::var& midiTracks, double bpm = 120.0);
bool exportMIDIFile(const juce::String& trackId, const juce::String& clipId,
const juce::String& eventsJSON, const juce::String& outputPath, double clipTempo);

Expand Down Expand Up @@ -498,6 +517,7 @@ class AudioEngine : public juce::AudioIODeviceCallback,
juce::var getAiToolsStatus();
juce::var refreshAiToolsStatus();
juce::var installAiTools(bool userConfirmedDownload);
juce::var installAiTools(const juce::String& optionsJSON);
juce::var resetAiTools();
juce::var separateStemsAsync(const juce::String& trackId, const juce::String& clipId, const juce::String& optionsJSON);
juce::var getStemSeparationProgress();
Expand Down Expand Up @@ -590,8 +610,8 @@ class AudioEngine : public juce::AudioIODeviceCallback,

juce::MidiBuffer buildTrackMidiBlock(const juce::String& trackId, double blockStartTimeSeconds,
int numSamples, double sampleRate, bool playing);
void queueAllNotesOffForTrack(TrackProcessor& track);
void queueAllNotesOffForAllTracks();
void queueAllNotesOffForTrack(TrackProcessor& track, bool requestChase = true);
void queueAllNotesOffForAllTracks(bool requestChase = true);
void applyProcessingPrecisionToTrack(TrackProcessor& track);
void rebuildRealtimeProcessingSnapshots();
std::unique_ptr<juce::AudioProcessor> createProcessorForStageSlot(const DesiredFXStageSlot& slot,
Expand Down
Loading
Loading