Skip to content

feat: add conditioning modes to the VST3 workflow#913

Closed
ChuxiJ wants to merge 1 commit intocodex/v2-906-workflow-modelfrom
codex/v2-907-conditioning
Closed

feat: add conditioning modes to the VST3 workflow#913
ChuxiJ wants to merge 1 commit intocodex/v2-906-workflow-modelfrom
codex/v2-907-conditioning

Conversation

@ChuxiJ
Copy link
Contributor

@ChuxiJ ChuxiJ commented Mar 20, 2026

Summary

  • add VST3 workflow modes for text, reference, cover/remix, and custom conditioning
  • expand the current MVP editor so users can choose source/reference audio paths, enter audio semantic codes, and tune conditioning strength
  • route payloads by mode while keeping the existing generate, poll, preview, and reveal workflow intact

Validation

  • cmake --build build/acestep_vst3 --config Release
  • synced the rebuilt bundle to ~/Library/Audio/Plug-Ins/VST3/ACE-Step VST3 Shell.vst3 for Reaper smoke testing

Closes #907
Refs #903

Summary by CodeRabbit

  • New Features

    • Workflow selector with Text, Reference, Cover/Remix, and Custom Conditioning modes
    • Reference/source audio pickers, preview controls, and cover-strength slider
    • New transport/status strip, result and preview decks, and central synth/transport panels
    • Persisted session/result tracking with per-take metadata and compare slots
    • New visual theme and updated UI layout, buttons and controls
  • Bug Fixes / Validation

    • Mode-specific input validation and clearer transport state reporting

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

This PR implements workflow-mode-aware generation and a V2 UI redesign for the ACE-Step VST3 plugin: introduces WorkflowMode and TransportState enums, persists mode/session/result metadata, adds new V2 look/ chrome/ UI components (status strip, synth panel, transport, result/preview decks), wires mode-specific validation in the processor, and builds mode-aware backend request payloads.

Changes

Cohort / File(s) Summary
Enums & Config
plugins/acestep_vst3/src/PluginEnums.h, plugins/acestep_vst3/src/PluginEnums.cpp, plugins/acestep_vst3/src/PluginConfig.h
Add WorkflowMode and TransportState enums with toString/fromString helpers; bump state version to 2.
State & Persistence
plugins/acestep_vst3/src/PluginState.h, plugins/acestep_vst3/src/PluginState.cpp
Extend PluginState with workflow, transport, audio-conditioning fields, session struct, and per-take PluginResultTake; serialize/deserialize new fields and sync legacy arrays.
Processor Logic
plugins/acestep_vst3/src/PluginProcessor.cpp
Add mode-specific validation in requestGeneration(), maintain transportState transitions, populate per-slot result metadata, manage compare/last-completed session fields, and clear/reset logic.
Backend Client
plugins/acestep_vst3/src/PluginBackendClient.cpp
Construct backend payloads conditionally by workflowMode (prompt omission for non-text modes, task_type"cover" for coverRemix, add reference_audio_path, src_audio_path, audio_code_string, audio_cover_strength as appropriate).
Editor: API & State Wiring
plugins/acestep_vst3/src/PluginEditor.h, plugins/acestep_vst3/src/PluginEditorState.cpp, plugins/acestep_vst3/src/PluginEditor.cpp, plugins/acestep_vst3/src/PluginEditorPreview.cpp
Replace many fine-grained widgets with composite components; add mode selector, reference/source file choosers and clear actions, conditioning codes and cover-strength UI; persist/sync new fields and update UI wiring.
New UI Components
.../StatusStripComponent.*, .../SynthPanelComponent.*, .../TapeTransportComponent.*, .../ResultDeckComponent.*, .../PreviewDeckComponent.*
Add V2 UI component implementations and headers (status strip, synth panel, tape transport, result deck, preview deck) exposing public accessors used by editor.
Look & Chrome
plugins/acestep_vst3/src/V2LookAndFeel.*, plugins/acestep_vst3/src/V2Chrome.*, plugins/acestep_vst3/src/V2Chrome.h
Introduce V2 look-and-feel and drawing helpers (chassis, modules, lamps, tape reel) and theme colour constants.
Build
plugins/acestep_vst3/CMakeLists.txt
Add new source files for the V2 UI components and look-and-feel to the target sources.

Sequence Diagram

sequenceDiagram
    participant User
    participant Editor
    participant Processor
    participant Backend

    User->>Editor: Select WorkflowMode / populate inputs
    Editor->>Editor: update UI fields & persist state

    User->>Editor: Click Render / Generate
    Editor->>Processor: requestGeneration()

    Processor->>Processor: validate by WorkflowMode
    alt Validation fails
        Processor->>Editor: set jobStatus/transportState = failed, set errorMessage
    else Validation succeeds
        Processor->>Processor: clear previous results, set transportState=submitting
        Processor->>Backend: startGeneration(payload based on WorkflowMode)
        Backend->>Backend: build payload (prompt, task_type, reference/src paths, codes, cover strength)
        Backend-->>Processor: submit / poll / final result callbacks
        Processor->>Editor: update state (resultTakes, transportState, session fields)
    end

    Editor->>User: update UI/status/preview/result deck
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • Epic: Tape Synth V2 for ACE-Step VST3 #903 — Implements the V2 tape-synth UI components, workflow/transport enums, and multi-take session/result support referenced by the epic; this PR appears to implement those objectives.

Poem

🐰 I hopped in with a tiny drum, four modes to choose—now here they come.
Text, reference, remix, and codes, the UI blooms in softer modes.
State remembers every take, sessions hum like carrot cake.
Buttons, lamps, and tape reels spin—generation's garden grows within. 🎶

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.80% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add conditioning modes to the VST3 workflow' accurately captures the main objective of the PR, which is to add support for multiple workflow modes (text, reference, cover/remix, custom conditioning) to the VST3 plugin.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #907: adds WorkflowMode enum with text/reference/coverRemix/customConditioning, implements mode-specific validation in requestGeneration(), routes backend payloads conditionally by mode, updates UI to expose mode selection and mode-specific controls (reference/source audio paths, conditioning codes, cover strength), persists new workflow state, and defines transport state transitions.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing conditioning modes per #907: backend payload routing, mode-specific UI controls, state serialization, transport state management, and V2 look-and-feel refactoring to support the new UI layout. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/v2-907-conditioning

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/acestep_vst3/src/PluginEditor.cpp (1)

57-57: ⚠️ Potential issue | 🟡 Minor

Remove redundant no-op line.

This line sets promptEditor_ bounds to its own current bounds, which has no effect. This appears to be leftover from editing.

🧹 Proposed fix
     lyricsLabel_.setBounds(left.removeFromTop(18));
-    promptEditor_.setBounds(promptEditor_.getBounds());
     lyricsEditor_.setBounds(left.removeFromTop(44));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/acestep_vst3/src/PluginEditor.cpp` at line 57, Remove the redundant
no-op call to promptEditor_.setBounds(promptEditor_.getBounds()); in
PluginEditor.cpp — simply delete that line where promptEditor_ is being set to
its own bounds (occurrence using promptEditor_.setBounds and
promptEditor_.getBounds) so the code no longer contains the ineffective
self-assignment.
🧹 Nitpick comments (1)
plugins/acestep_vst3/src/PluginEditorState.cpp (1)

151-208: Add a round-trip regression test for the new mode sync.

syncFromProcessor() and persistTextFields() now have to keep combo ordering, WorkflowMode, and persisted state aligned for four modes plus audioCoverStrength. A focused test that exercises all four modes through both directions would catch silent session-restore regressions early. Based on learnings, AI-Agent Workflow: Add/update focused tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/acestep_vst3/src/PluginEditorState.cpp` around lines 151 - 208, Add a
round-trip unit test that verifies syncFromProcessor() and persistTextFields()
keep combo ordering and WorkflowMode in sync for all four modes and
audioCoverStrength: for each WorkflowMode value set processor_.getMutableState()
fields (workflowMode, audioCoverStrength, modelPreset, qualityMode,
durationSeconds, seed, backendBaseUrl, prompt, lyrics, referenceAudioPath,
sourceAudioPath, customConditioningCodes), call
ACEStepVST3AudioProcessorEditor::syncFromProcessor(), then immediately call
persistTextFields() and assert the processor state equals the original
(including workflowMode and audioCoverStrength); also add the inverse case where
you set GUI controls (modeBox_.setSelectedId / coverStrengthSlider_.setValue
etc.), call persistTextFields(), construct a fresh processor state from that
persisted state and call syncFromProcessor() and assert UI reflects the same
values—use the same helper symbols (syncFromProcessor, persistTextFields,
modeBox_, workflowMode, coverStrengthSlider_) and the project test harness for
setup/teardown.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/acestep_vst3/src/PluginEditorPreview.cpp`:
- Around line 9-18: The shared helper launchAudioChooser currently allows
multiple audio formats but conditioning/reference pickers must be WAV-only;
modify launchAudioChooser to accept an allowedExtensions parameter (or add a new
launchWavChooser wrapper) and use "*.wav" for conditioning/reference pickers,
then update the calls that create conditioning/source file choosers (the
occurrences referenced in PluginEditorPreview.cpp) to pass/ call the
WAV-restricted variant so only .wav files are selectable.

In `@plugins/acestep_vst3/src/PluginEditorState.cpp`:
- Around line 191-193: The code is currently persisting machine-local absolute
paths by assigning state.referenceAudioPath =
referenceAudioEditor_.getText().trim() and state.sourceAudioPath =
sourceAudioEditor_.getText().trim(); instead, convert these absolute file paths
into project-relative/imported asset references or avoid serializing them:
detect if the chosen file is inside the project/asset folder and store a
relative path or asset ID (e.g., transform referenceAudioEditor_.getText() into
a project-relative path or lookup an AssetRegistry entry), and if not, leave
state.referenceAudioPath/state.sourceAudioPath empty or mark them session-only
so they are not written to the saved plugin XML; keep
state.customConditioningCodes as-is if intended to be serializable.

---

Outside diff comments:
In `@plugins/acestep_vst3/src/PluginEditor.cpp`:
- Line 57: Remove the redundant no-op call to
promptEditor_.setBounds(promptEditor_.getBounds()); in PluginEditor.cpp — simply
delete that line where promptEditor_ is being set to its own bounds (occurrence
using promptEditor_.setBounds and promptEditor_.getBounds) so the code no longer
contains the ineffective self-assignment.

---

Nitpick comments:
In `@plugins/acestep_vst3/src/PluginEditorState.cpp`:
- Around line 151-208: Add a round-trip unit test that verifies
syncFromProcessor() and persistTextFields() keep combo ordering and WorkflowMode
in sync for all four modes and audioCoverStrength: for each WorkflowMode value
set processor_.getMutableState() fields (workflowMode, audioCoverStrength,
modelPreset, qualityMode, durationSeconds, seed, backendBaseUrl, prompt, lyrics,
referenceAudioPath, sourceAudioPath, customConditioningCodes), call
ACEStepVST3AudioProcessorEditor::syncFromProcessor(), then immediately call
persistTextFields() and assert the processor state equals the original
(including workflowMode and audioCoverStrength); also add the inverse case where
you set GUI controls (modeBox_.setSelectedId / coverStrengthSlider_.setValue
etc.), call persistTextFields(), construct a fresh processor state from that
persisted state and call syncFromProcessor() and assert UI reflects the same
values—use the same helper symbols (syncFromProcessor, persistTextFields,
modeBox_, workflowMode, coverStrengthSlider_) and the project test harness for
setup/teardown.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c874025f-e29c-4f7a-8fce-c959ffa763c0

📥 Commits

Reviewing files that changed from the base of the PR and between 00f514a and d62dea7.

📒 Files selected for processing (10)
  • plugins/acestep_vst3/src/PluginBackendClient.cpp
  • plugins/acestep_vst3/src/PluginEditor.cpp
  • plugins/acestep_vst3/src/PluginEditor.h
  • plugins/acestep_vst3/src/PluginEditorPreview.cpp
  • plugins/acestep_vst3/src/PluginEditorState.cpp
  • plugins/acestep_vst3/src/PluginEnums.cpp
  • plugins/acestep_vst3/src/PluginEnums.h
  • plugins/acestep_vst3/src/PluginProcessor.cpp
  • plugins/acestep_vst3/src/PluginState.cpp
  • plugins/acestep_vst3/src/PluginState.h

Comment on lines +9 to +18
void launchAudioChooser(std::unique_ptr<juce::FileChooser>& chooser,
const juce::String& title,
std::function<void(const juce::FileChooser&)> onComplete)
{
chooser = std::make_unique<juce::FileChooser>(
title, juce::File(), "*.wav;*.aiff;*.flac;*.ogg;*.mp3;*.m4a");
chooser->launchAsync(juce::FileBrowserComponent::openMode
| juce::FileBrowserComponent::canSelectFiles,
std::move(onComplete));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restrict conditioning pickers to WAV.

Issue #907 scopes the conditioning modes as raw WAV inputs, but this shared helper now exposes AIFF/FLAC/OGG/MP3/M4A for the reference/source pickers too. That widens the UI contract beyond what these workflows are supposed to accept.

🎯 Proposed split between preview and conditioning filters
 void launchAudioChooser(std::unique_ptr<juce::FileChooser>& chooser,
                         const juce::String& title,
+                        const juce::String& wildcard,
                         std::function<void(const juce::FileChooser&)> onComplete)
 {
-    chooser = std::make_unique<juce::FileChooser>(
-        title, juce::File(), "*.wav;*.aiff;*.flac;*.ogg;*.mp3;*.m4a");
+    chooser = std::make_unique<juce::FileChooser>(title, juce::File(), wildcard);
     chooser->launchAsync(juce::FileBrowserComponent::openMode
                              | juce::FileBrowserComponent::canSelectFiles,
                          std::move(onComplete));
 }
 ...
     launchAudioChooser(previewChooser_,
                        "Select preview audio file",
+                       "*.wav;*.aiff;*.flac;*.ogg;*.mp3;*.m4a",
                        [this](const juce::FileChooser& chooser) {
 ...
     launchAudioChooser(conditioningChooser_,
                        "Select reference audio file",
+                       "*.wav",
                        [this](const juce::FileChooser& chooser) {
 ...
     launchAudioChooser(conditioningChooser_,
                        "Select source audio file",
+                       "*.wav",
                        [this](const juce::FileChooser& chooser) {

Also applies to: 23-24, 66-67, 92-93

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/acestep_vst3/src/PluginEditorPreview.cpp` around lines 9 - 18, The
shared helper launchAudioChooser currently allows multiple audio formats but
conditioning/reference pickers must be WAV-only; modify launchAudioChooser to
accept an allowedExtensions parameter (or add a new launchWavChooser wrapper)
and use "*.wav" for conditioning/reference pickers, then update the calls that
create conditioning/source file choosers (the occurrences referenced in
PluginEditorPreview.cpp) to pass/ call the WAV-restricted variant so only .wav
files are selectable.

Comment on lines +191 to +193
state.referenceAudioPath = referenceAudioEditor_.getText().trim();
state.sourceAudioPath = sourceAudioEditor_.getText().trim();
state.customConditioningCodes = conditioningCodesEditor_.getText();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't persist machine-local conditioning paths verbatim.

These assignments feed PluginState.cpp, which now serializes the strings into the saved plugin XML. For a DAW recall path, that makes the new reference/source workflows brittle across machines and embeds user-local directory names in project/preset files. Please persist a project-relative/imported asset reference instead, or keep these fields session-local.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/acestep_vst3/src/PluginEditorState.cpp` around lines 191 - 193, The
code is currently persisting machine-local absolute paths by assigning
state.referenceAudioPath = referenceAudioEditor_.getText().trim() and
state.sourceAudioPath = sourceAudioEditor_.getText().trim(); instead, convert
these absolute file paths into project-relative/imported asset references or
avoid serializing them: detect if the chosen file is inside the project/asset
folder and store a relative path or asset ID (e.g., transform
referenceAudioEditor_.getText() into a project-relative path or lookup an
AssetRegistry entry), and if not, leave
state.referenceAudioPath/state.sourceAudioPath empty or mark them session-only
so they are not written to the saved plugin XML; keep
state.customConditioningCodes as-is if intended to be serializable.

@ChuxiJ ChuxiJ mentioned this pull request Mar 20, 2026
6 tasks
@ChuxiJ
Copy link
Contributor Author

ChuxiJ commented Mar 21, 2026

Superseded by consolidated V2 implementation PR #919, which now carries this stack forward against . Closing this split PR to keep the review surface clean.

@ChuxiJ ChuxiJ closed this Mar 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tape Synth V2: Conditioning and generation modes

1 participant