6a117a9ba9
Implements the full encoding pipeline for Expert Sleepers ES-5 with ESX-8GT (gate) and ESX-8CV (CV) expanders as a native CLAP plugin. Encoder logic ported from Expert Sleepers' MIT-licensed Pure Data externals (https://github.com/expertsleepersltd/externals). - ES-5 encoder: packs 6 × 8-bit header values into stereo 24-bit audio - ESX-8GT encoder: 8 boolean gates → 1 byte - ESX-8CV encoder: 8 × 12-bit CV values via 64-phase state machine - 54 automatable parameters (6 header types + 48 gate/CV values) - State save/load for preset support - Builds on Linux (tested), macOS, and Windows
77 lines
2.9 KiB
C++
77 lines
2.9 KiB
C++
// ES-5 CLAP Plugin — Bitwig-native encoder for Expert Sleepers ES-5 + ESX expanders
|
||
// Copyright (c) 2026 Sub-Net e.U. — MIT License
|
||
|
||
#pragma once
|
||
|
||
#include <clap/helpers/plugin.hh>
|
||
#include <clap/helpers/plugin.hxx>
|
||
#include <clap/helpers/host-proxy.hh>
|
||
#include <clap/helpers/host-proxy.hxx>
|
||
#include <array>
|
||
#include "encoders.h"
|
||
|
||
// Header type enum (matches parameter stepped values)
|
||
enum class HeaderType : int { Off = 0, GT = 1, CV = 2 };
|
||
|
||
// Parameter ID scheme:
|
||
// 1–6: Header type (Off/GT/CV)
|
||
// 101–108: Header 1 values
|
||
// 201–208: Header 2 values
|
||
// ...
|
||
// 601–608: Header 6 values
|
||
|
||
constexpr uint32_t NUM_HEADERS = 6;
|
||
constexpr uint32_t CHANNELS_PER_HEADER = 8;
|
||
constexpr uint32_t TOTAL_PARAMS = NUM_HEADERS + NUM_HEADERS * CHANNELS_PER_HEADER; // 54
|
||
|
||
constexpr clap_id headerTypeParamId(uint32_t header) { return header + 1; }
|
||
constexpr clap_id headerValueParamId(uint32_t header, uint32_t ch) {
|
||
return (header + 1) * 100 + ch + 1;
|
||
}
|
||
|
||
class ES5Plugin : public clap::helpers::Plugin<
|
||
clap::helpers::MisbehaviourHandler::Terminate,
|
||
clap::helpers::CheckingLevel::Maximal>
|
||
{
|
||
public:
|
||
ES5Plugin(const clap_plugin_descriptor *desc, const clap_host *host);
|
||
|
||
// --- Audio Ports ---
|
||
bool implementsAudioPorts() const noexcept override { return true; }
|
||
uint32_t audioPortsCount(bool isInput) const noexcept override;
|
||
bool audioPortsInfo(uint32_t index, bool isInput,
|
||
clap_audio_port_info *info) const noexcept override;
|
||
|
||
// --- Parameters ---
|
||
bool implementsParams() const noexcept override { return true; }
|
||
uint32_t paramsCount() const noexcept override;
|
||
bool paramsInfo(uint32_t paramIndex, clap_param_info *info) const noexcept override;
|
||
bool paramsValue(clap_id paramId, double *value) noexcept override;
|
||
bool paramsValueToText(clap_id paramId, double value,
|
||
char *display, uint32_t size) noexcept override;
|
||
bool paramsTextToValue(clap_id paramId, const char *display,
|
||
double *value) noexcept override;
|
||
void paramsFlush(const clap_input_events *in,
|
||
const clap_output_events *out) noexcept override;
|
||
|
||
// --- State ---
|
||
bool implementsState() const noexcept override { return true; }
|
||
bool stateSave(const clap_ostream *stream) noexcept override;
|
||
bool stateLoad(const clap_istream *stream) noexcept override;
|
||
|
||
// --- Process ---
|
||
clap_process_status process(const clap_process *process) noexcept override;
|
||
|
||
private:
|
||
void handleEvent(const clap_event_header *hdr);
|
||
|
||
// Parameter storage
|
||
std::array<double, NUM_HEADERS> headerTypes_{}; // 0=off, 1=gt, 2=cv
|
||
std::array<std::array<double, CHANNELS_PER_HEADER>, NUM_HEADERS> values_{}; // 0.0–1.0
|
||
|
||
// Encoder instances
|
||
es5::ES5Encoder es5Encoder_;
|
||
std::array<es5::ESX8GTEncoder, NUM_HEADERS> gtEncoders_;
|
||
std::array<es5::ESX8CVEncoder, NUM_HEADERS> cvEncoders_;
|
||
};
|