initial scaffold: JUCE VST3/AU plugin (Path 2)
Cross-platform DAW plugin that uploads audio to registry.houseofmixtape.com and imports stems back into the host DAW. Skeleton — does not build yet. To build: 1. Clone JUCE next to this repo: git clone https://github.com/juce-framework/JUCE 2. cmake -B build -DJUCE_DIR=../JUCE 3. cmake --build build --target HouseOfMixtape_VST3 Files: - CMakeLists.txt — juce_add_plugin VST3+AU+Standalone - Source/PluginProcessor.{h,cpp} — passthrough AudioProcessor - Source/PluginEditor.{h,cpp} — drag-drop + Upload btn + progress (Paper&Ink dark) - Source/HomApiClient.{h,cpp} — HTTP skeleton for /api/jobs/* (real wiring deferred) - README.md — full build instructions + roadmap This commit is the scaffold checkpoint. Real HTTP wiring + DAW file-import API integration land in subsequent commits.
This commit is contained in:
commit
f466c5cdad
|
|
@ -0,0 +1,61 @@
|
||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
|
project(HouseOfMixtape VERSION 0.1.0 LANGUAGES C CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# JUCE is expected as a sibling directory by default. Override with
|
||||||
|
# -DJUCE_DIR=/path/to/JUCE if you keep it elsewhere.
|
||||||
|
if(NOT DEFINED JUCE_DIR)
|
||||||
|
set(JUCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../JUCE")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT EXISTS "${JUCE_DIR}/CMakeLists.txt")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"JUCE not found at ${JUCE_DIR}. "
|
||||||
|
"Clone https://github.com/juce-framework/JUCE next to this dir, "
|
||||||
|
"or pass -DJUCE_DIR=/path/to/JUCE."
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(${JUCE_DIR} JUCE)
|
||||||
|
|
||||||
|
juce_add_plugin(HouseOfMixtape
|
||||||
|
COMPANY_NAME "House of Mixtape"
|
||||||
|
BUNDLE_ID "com.houseofmixtape.daw"
|
||||||
|
PLUGIN_MANUFACTURER_CODE HoMx
|
||||||
|
PLUGIN_CODE HoM1
|
||||||
|
FORMATS VST3 AU Standalone
|
||||||
|
PRODUCT_NAME "House of Mixtape"
|
||||||
|
NEEDS_MIDI_INPUT FALSE
|
||||||
|
NEEDS_MIDI_OUTPUT FALSE
|
||||||
|
IS_SYNTH FALSE
|
||||||
|
EDITOR_WANTS_KEYBOARD_FOCUS TRUE
|
||||||
|
COPY_PLUGIN_AFTER_BUILD TRUE
|
||||||
|
VST3_CATEGORIES "Fx" "Tools"
|
||||||
|
AU_MAIN_TYPE "kAudioUnitType_Effect"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(HouseOfMixtape PRIVATE
|
||||||
|
Source/PluginProcessor.cpp
|
||||||
|
Source/PluginEditor.cpp
|
||||||
|
Source/HomApiClient.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(HouseOfMixtape PRIVATE
|
||||||
|
JUCE_WEB_BROWSER=0
|
||||||
|
JUCE_USE_CURL=1
|
||||||
|
JUCE_VST3_CAN_REPLACE_VST2=0
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(HouseOfMixtape PRIVATE
|
||||||
|
juce::juce_audio_utils
|
||||||
|
juce::juce_dsp
|
||||||
|
juce::juce_gui_extra
|
||||||
|
juce::juce_audio_formats
|
||||||
|
juce::juce_audio_devices
|
||||||
|
juce::juce_recommended_config_flags
|
||||||
|
juce::juce_recommended_lto_flags
|
||||||
|
juce::juce_recommended_warning_flags
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
# House of Mixtape — DAW Plugin (Path 2)
|
||||||
|
|
||||||
|
Cross-platform VST3/AU plugin that lets the user upload audio from inside
|
||||||
|
their DAW and receive Phoenix-extracted stems + MIDI directly into the
|
||||||
|
arrangement. Targets Win/Mac/Linux. AAX (Pro Tools) is a v2 ambition.
|
||||||
|
|
||||||
|
## Status: scaffolding (2026-05-03)
|
||||||
|
|
||||||
|
This directory holds the JUCE project skeleton + build instructions. The
|
||||||
|
actual plugin is **not yet compiled** — that's a separate sprint that
|
||||||
|
needs:
|
||||||
|
|
||||||
|
1. JUCE installed (clone https://github.com/juce-framework/JUCE)
|
||||||
|
2. CMake 3.22+
|
||||||
|
3. Per-OS toolchain (MSVC on Win, Xcode on macOS, gcc/clang on Linux)
|
||||||
|
|
||||||
|
The skeleton is enough to verify the build pipeline. Once a developer
|
||||||
|
has JUCE + CMake set up locally, `cmake -B build && cmake --build build`
|
||||||
|
should produce a working empty plugin that loads in Ableton/FL/Logic.
|
||||||
|
|
||||||
|
## Why JUCE (vs alternatives)
|
||||||
|
|
||||||
|
- **JUCE** : industry standard for VST3/AU plugins. Free for open-source,
|
||||||
|
paid licence ($800/yr indie) for commercial. Cross-platform out of the
|
||||||
|
box. Used by everyone (Pro-Q, Serum, Massive, ToneBoosters…).
|
||||||
|
- iPlug2 : MIT-licenced alternative. Smaller community. Considered.
|
||||||
|
- DPF : niche.
|
||||||
|
- Faust : DSP-only, not for HTTP-talking plugins.
|
||||||
|
|
||||||
|
JUCE wins on docs + support + the fact that we'll integrate HTTP +
|
||||||
|
file-import in C++, which is well-trod ground there.
|
||||||
|
|
||||||
|
## What the plugin does
|
||||||
|
|
||||||
|
```
|
||||||
|
┌───────────── DAW (Ableton/FL/Logic/Reaper) ─────────────┐
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────┐ │
|
||||||
|
│ │ House of Mixtape (VST3 plugin) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 1. User drag-drops audio │ │
|
||||||
|
│ │ 2. POST registry.../jobs/upload │ │
|
||||||
|
│ │ 3. POST .../jobs/submit │ │
|
||||||
|
│ │ 4. Poll /status (progress bar) │ │
|
||||||
|
│ │ 5. GET /output → stems + MIDI │ │
|
||||||
|
│ │ 6. Drop stems into DAW tracks │ │
|
||||||
|
│ │ (via VST3 host import API) │ │
|
||||||
|
│ └──────────────────────────────────┘ │
|
||||||
|
└──────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| Path | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `CMakeLists.txt` | JUCE+CMake project. References JUCE submodule. |
|
||||||
|
| `Source/PluginProcessor.{h,cpp}` | AudioProcessor — does no DSP yet, just hosts the editor. |
|
||||||
|
| `Source/PluginEditor.{h,cpp}` | The plugin UI. Skeleton: Upload button + status text. |
|
||||||
|
| `Source/HomApiClient.{h,cpp}` | HTTP client for `registry.houseofmixtape.com/api/jobs/*`. Skeleton stubs. |
|
||||||
|
|
||||||
|
## Build (when ready)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone JUCE next to this dir
|
||||||
|
cd ..
|
||||||
|
git clone --depth 1 https://github.com/juce-framework/JUCE.git
|
||||||
|
cd daw-plugin
|
||||||
|
|
||||||
|
# Configure + build (Linux example)
|
||||||
|
cmake -B build -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DJUCE_BUILD_TESTS=OFF \
|
||||||
|
-DJUCE_DIR=$(pwd)/../JUCE
|
||||||
|
cmake --build build --target HouseOfMixtape_VST3 -j
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# build/HouseOfMixtape_artefacts/Release/VST3/HouseOfMixtape.vst3/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
- [x] Scaffold (this commit)
|
||||||
|
- [ ] HelloWorld build verified on at least one OS
|
||||||
|
- [ ] HTTP client wired to `registry.houseofmixtape.com`
|
||||||
|
- [ ] Bearer auth flow
|
||||||
|
- [ ] Audio drag-drop in plugin window
|
||||||
|
- [ ] Status polling + progress bar
|
||||||
|
- [ ] Stems import to DAW tracks (VST3 host file-drop API)
|
||||||
|
- [ ] AU build (macOS)
|
||||||
|
- [ ] Code signing + notarization (macOS)
|
||||||
|
- [ ] Win installer
|
||||||
|
- [ ] Distribution (Gumroad? Free? Subscription?)
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include "HomApiClient.h"
|
||||||
|
|
||||||
|
HomApiClient::HomApiClient (juce::String registryBaseUrl)
|
||||||
|
: baseUrl (std::move (registryBaseUrl))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomApiClient::submitJobAsync (const juce::File& audio,
|
||||||
|
ProgressCallback onProgress,
|
||||||
|
DoneCallback onDone,
|
||||||
|
ErrorCallback onError)
|
||||||
|
{
|
||||||
|
juce::ignoreUnused (audio);
|
||||||
|
|
||||||
|
// Skeleton — fire a fake progress sweep then signal "not implemented".
|
||||||
|
// Real implementation:
|
||||||
|
// 1) POST baseUrl + "/jobs/upload" with multipart audio file
|
||||||
|
// 2) POST baseUrl + "/jobs/submit" with the returned key
|
||||||
|
// 3) Poll baseUrl + "/jobs/{id}/status" every ~3s
|
||||||
|
// 4) GET baseUrl + "/jobs/{id}/output" → stems URLs
|
||||||
|
// 5) Download each stem to a temp dir
|
||||||
|
// 6) Trigger the host DAW to import them as new tracks
|
||||||
|
// (FileDragAndDropHandling on the host's edit window)
|
||||||
|
|
||||||
|
juce::Thread::launch ([onProgress, onError]
|
||||||
|
{
|
||||||
|
for (double p : { 0.05, 0.20, 0.50, 0.80 })
|
||||||
|
{
|
||||||
|
juce::Thread::sleep (300);
|
||||||
|
if (onProgress) onProgress (p);
|
||||||
|
}
|
||||||
|
if (onError)
|
||||||
|
onError ("API client skeleton — wire HTTP in next sprint.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <juce_core/juce_core.h>
|
||||||
|
#include <juce_audio_basics/juce_audio_basics.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* House of Mixtape API client (Path 2 plugin → registry.houseofmixtape.com).
|
||||||
|
* Skeleton — submit/poll/output endpoints are stubbed; the actual
|
||||||
|
* HTTP/JUCE URL wiring lands in a follow-up commit.
|
||||||
|
*/
|
||||||
|
class HomApiClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ProgressCallback = std::function<void (double)>;
|
||||||
|
using DoneCallback = std::function<void (juce::var)>;
|
||||||
|
using ErrorCallback = std::function<void (const juce::String&)>;
|
||||||
|
|
||||||
|
explicit HomApiClient (juce::String registryBaseUrl);
|
||||||
|
|
||||||
|
// Async: upload + submit + poll until done|error.
|
||||||
|
void submitJobAsync (const juce::File& audio,
|
||||||
|
ProgressCallback onProgress,
|
||||||
|
DoneCallback onDone,
|
||||||
|
ErrorCallback onError);
|
||||||
|
|
||||||
|
void setBearerToken (const juce::String& token) { bearer = token; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
juce::String baseUrl;
|
||||||
|
juce::String bearer;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
#include "PluginEditor.h"
|
||||||
|
|
||||||
|
HomAudioProcessorEditor::HomAudioProcessorEditor (HomAudioProcessor& p)
|
||||||
|
: AudioProcessorEditor (&p), processor (p),
|
||||||
|
progressBar (progressValue)
|
||||||
|
{
|
||||||
|
setSize (440, 240);
|
||||||
|
setResizable (true, true);
|
||||||
|
|
||||||
|
addAndMakeVisible (uploadBtn);
|
||||||
|
uploadBtn.onClick = [this] { onUploadClicked(); };
|
||||||
|
|
||||||
|
addAndMakeVisible (statusLbl);
|
||||||
|
statusLbl.setText ("Drop an audio file or click the button.",
|
||||||
|
juce::dontSendNotification);
|
||||||
|
statusLbl.setJustificationType (juce::Justification::centred);
|
||||||
|
|
||||||
|
addAndMakeVisible (progressBar);
|
||||||
|
progressBar.setVisible (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomAudioProcessorEditor::paint (juce::Graphics& g)
|
||||||
|
{
|
||||||
|
// Paper&Ink dark
|
||||||
|
g.fillAll (juce::Colour (0xff1A1715));
|
||||||
|
g.setColour (juce::Colour (0xffD4A574));
|
||||||
|
g.setFont (16.0f);
|
||||||
|
g.drawFittedText ("House of Mixtape",
|
||||||
|
getLocalBounds().removeFromTop (40),
|
||||||
|
juce::Justification::centred, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomAudioProcessorEditor::resized()
|
||||||
|
{
|
||||||
|
auto r = getLocalBounds().reduced (16);
|
||||||
|
r.removeFromTop (40); // brand header
|
||||||
|
|
||||||
|
statusLbl.setBounds (r.removeFromTop (32));
|
||||||
|
r.removeFromTop (12);
|
||||||
|
uploadBtn.setBounds (r.removeFromTop (44).reduced (40, 0));
|
||||||
|
r.removeFromTop (16);
|
||||||
|
progressBar.setBounds (r.removeFromTop (24));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HomAudioProcessorEditor::isInterestedInFileDrag (const juce::StringArray& files)
|
||||||
|
{
|
||||||
|
for (auto& f : files)
|
||||||
|
if (f.endsWithIgnoreCase (".wav") || f.endsWithIgnoreCase (".mp3")
|
||||||
|
|| f.endsWithIgnoreCase (".flac") || f.endsWithIgnoreCase (".m4a"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomAudioProcessorEditor::filesDropped (const juce::StringArray& files, int, int)
|
||||||
|
{
|
||||||
|
if (files.isEmpty()) return;
|
||||||
|
setStatus ("Uploading " + juce::File (files[0]).getFileName() + "…");
|
||||||
|
progressBar.setVisible (true);
|
||||||
|
progressValue = 0.05;
|
||||||
|
|
||||||
|
// Hand off to the API client (skeleton — actual HTTP wiring lands in
|
||||||
|
// a subsequent commit once Path 2 sprint resumes).
|
||||||
|
processor.getApiClient().submitJobAsync (
|
||||||
|
juce::File (files[0]),
|
||||||
|
/* onProgress */ [this] (double p) { progressValue = p; },
|
||||||
|
/* onDone */ [this] (juce::var output) {
|
||||||
|
juce::ignoreUnused (output);
|
||||||
|
progressBar.setVisible (false);
|
||||||
|
setStatus ("Done — stems imported into the project.");
|
||||||
|
},
|
||||||
|
/* onError */ [this] (const juce::String& msg) {
|
||||||
|
progressBar.setVisible (false);
|
||||||
|
setStatus ("Error: " + msg);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomAudioProcessorEditor::onUploadClicked()
|
||||||
|
{
|
||||||
|
auto chooser = std::make_shared<juce::FileChooser> (
|
||||||
|
"Choose an audio file",
|
||||||
|
juce::File::getSpecialLocation (juce::File::userMusicDirectory),
|
||||||
|
"*.wav;*.mp3;*.flac;*.m4a"
|
||||||
|
);
|
||||||
|
auto flags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectFiles;
|
||||||
|
chooser->launchAsync (flags, [this, chooser] (const juce::FileChooser& fc) {
|
||||||
|
auto files = fc.getResults();
|
||||||
|
if (files.isEmpty()) return;
|
||||||
|
juce::StringArray paths;
|
||||||
|
paths.add (files[0].getFullPathName());
|
||||||
|
filesDropped (paths, 0, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomAudioProcessorEditor::setStatus (const juce::String& msg)
|
||||||
|
{
|
||||||
|
statusLbl.setText (msg, juce::dontSendNotification);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <juce_audio_processors/juce_audio_processors.h>
|
||||||
|
#include "PluginProcessor.h"
|
||||||
|
|
||||||
|
class HomAudioProcessorEditor : public juce::AudioProcessorEditor,
|
||||||
|
public juce::FileDragAndDropTarget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit HomAudioProcessorEditor (HomAudioProcessor&);
|
||||||
|
~HomAudioProcessorEditor() override = default;
|
||||||
|
|
||||||
|
void paint (juce::Graphics&) override;
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
// FileDragAndDropTarget
|
||||||
|
bool isInterestedInFileDrag (const juce::StringArray& files) override;
|
||||||
|
void filesDropped (const juce::StringArray& files, int x, int y) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
HomAudioProcessor& processor;
|
||||||
|
|
||||||
|
juce::TextButton uploadBtn { "Drop / browse audio" };
|
||||||
|
juce::Label statusLbl;
|
||||||
|
juce::ProgressBar progressBar;
|
||||||
|
double progressValue { 0.0 };
|
||||||
|
|
||||||
|
void onUploadClicked();
|
||||||
|
void setStatus (const juce::String& msg);
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HomAudioProcessorEditor)
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
#include "PluginProcessor.h"
|
||||||
|
#include "PluginEditor.h"
|
||||||
|
|
||||||
|
HomAudioProcessor::HomAudioProcessor()
|
||||||
|
: AudioProcessor (BusesProperties()
|
||||||
|
.withInput ("Input", juce::AudioChannelSet::stereo(), true)
|
||||||
|
.withOutput ("Output", juce::AudioChannelSet::stereo(), true)),
|
||||||
|
apiClient ("https://registry.houseofmixtape.com/api")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomAudioProcessor::prepareToPlay (double, int) {}
|
||||||
|
void HomAudioProcessor::releaseResources() {}
|
||||||
|
|
||||||
|
bool HomAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
|
||||||
|
{
|
||||||
|
// Mono or stereo only (passthrough — we don't process audio yet)
|
||||||
|
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
|
||||||
|
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
|
||||||
|
return false;
|
||||||
|
return layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer&)
|
||||||
|
{
|
||||||
|
// Passthrough — the plugin's value is in the GUI (upload + receive stems),
|
||||||
|
// not in real-time DSP.
|
||||||
|
juce::ignoreUnused (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::AudioProcessorEditor* HomAudioProcessor::createEditor()
|
||||||
|
{
|
||||||
|
return new HomAudioProcessorEditor (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HomAudioProcessor::getStateInformation (juce::MemoryBlock&) {}
|
||||||
|
void HomAudioProcessor::setStateInformation (const void*, int) {}
|
||||||
|
|
||||||
|
// JUCE plugin entry point
|
||||||
|
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
||||||
|
{
|
||||||
|
return new HomAudioProcessor();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// House of Mixtape — DAW Plugin (Path 2)
|
||||||
|
// Skeleton AudioProcessor. Does no DSP yet; lives only to host the editor.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <juce_audio_processors/juce_audio_processors.h>
|
||||||
|
#include "HomApiClient.h"
|
||||||
|
|
||||||
|
class HomAudioProcessor : public juce::AudioProcessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HomAudioProcessor();
|
||||||
|
~HomAudioProcessor() override = default;
|
||||||
|
|
||||||
|
// ── AudioProcessor lifecycle ──────────────────────────────────────
|
||||||
|
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
|
||||||
|
void releaseResources() override;
|
||||||
|
|
||||||
|
bool isBusesLayoutSupported (const BusesLayout&) const override;
|
||||||
|
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
|
||||||
|
|
||||||
|
// ── Boilerplate JUCE host queries ─────────────────────────────────
|
||||||
|
juce::AudioProcessorEditor* createEditor() override;
|
||||||
|
bool hasEditor() const override { return true; }
|
||||||
|
|
||||||
|
const juce::String getName() const override { return "House of Mixtape"; }
|
||||||
|
|
||||||
|
bool acceptsMidi() const override { return false; }
|
||||||
|
bool producesMidi() const override { return false; }
|
||||||
|
bool isMidiEffect() const override { return false; }
|
||||||
|
double getTailLengthSeconds() const override { return 0.0; }
|
||||||
|
|
||||||
|
int getNumPrograms() override { return 1; }
|
||||||
|
int getCurrentProgram() override { return 0; }
|
||||||
|
void setCurrentProgram (int) override {}
|
||||||
|
const juce::String getProgramName (int) override { return {}; }
|
||||||
|
void changeProgramName (int, const juce::String&) override {}
|
||||||
|
|
||||||
|
void getStateInformation (juce::MemoryBlock&) override;
|
||||||
|
void setStateInformation (const void*, int) override;
|
||||||
|
|
||||||
|
// ── House of Mixtape — exposed to the editor ──────────────────────
|
||||||
|
HomApiClient& getApiClient() { return apiClient; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
HomApiClient apiClient;
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HomAudioProcessor)
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue