From 67aa5da5d2d90013ea5ae622716f5b965a506e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Lavi=C3=A9ville?= Date: Fri, 17 Jul 2026 19:40:39 +0200 Subject: [PATCH] feat: migrating instrument factory for maximal compatibility with future tier plugin --- webapp/src/components/app/CLAUDE.md | 82 +++-- .../app/instruments/InstrumentParamField.vue | 125 +++++++ .../app/instruments/InstrumentParamsModal.vue | 105 ++++++ .../app/instruments/InstrumentSettings.vue | 333 ++---------------- .../app/timeline/AddTrackButton.vue | 36 +- webapp/src/composables/useOnboardingTour.ts | 26 +- .../engines/audio-clip/AudioClipEngine.ts | 4 +- .../audio/engines/audio-clip/definition.ts | 17 + .../src/lib/audio/engines/audio-clip/index.ts | 1 + .../engines/basic-synth/BasicSynthEngine.ts | 8 +- .../audio/engines/basic-synth/definition.ts | 34 ++ .../lib/audio/engines/basic-synth/index.ts | 1 + .../lib/audio/engines/smplr/SmplrEngine.ts | 9 +- .../src/lib/audio/engines/smplr/definition.ts | 30 ++ webapp/src/lib/audio/engines/smplr/index.ts | 1 + webapp/src/lib/audio/engines/types.ts | 14 +- .../engines/undertale/UndertaleEngine.ts | 23 +- .../lib/audio/engines/undertale/definition.ts | 70 ++++ .../src/lib/audio/engines/undertale/index.ts | 1 + webapp/src/lib/audio/instrumentFactory.ts | 106 ++---- webapp/src/lib/audio/instruments/index.ts | 29 ++ webapp/src/lib/audio/instruments/registry.ts | 51 +++ webapp/src/lib/audio/instruments/types.ts | 38 ++ webapp/src/lib/onboarding/CLAUDE.md | 6 +- webapp/src/lib/onboarding/steps.ts | 23 +- webapp/src/lib/utils/types.ts | 40 ++- webapp/src/stores/timelineStore.ts | 4 +- 27 files changed, 730 insertions(+), 487 deletions(-) create mode 100644 webapp/src/components/app/instruments/InstrumentParamField.vue create mode 100644 webapp/src/components/app/instruments/InstrumentParamsModal.vue create mode 100644 webapp/src/lib/audio/engines/audio-clip/definition.ts create mode 100644 webapp/src/lib/audio/engines/basic-synth/definition.ts create mode 100644 webapp/src/lib/audio/engines/smplr/definition.ts create mode 100644 webapp/src/lib/audio/engines/undertale/definition.ts create mode 100644 webapp/src/lib/audio/instruments/index.ts create mode 100644 webapp/src/lib/audio/instruments/registry.ts create mode 100644 webapp/src/lib/audio/instruments/types.ts diff --git a/webapp/src/components/app/CLAUDE.md b/webapp/src/components/app/CLAUDE.md index 009f1d8..8aeb2f4 100644 --- a/webapp/src/components/app/CLAUDE.md +++ b/webapp/src/components/app/CLAUDE.md @@ -74,18 +74,28 @@ setAutomationPoints(trackId, laneId, points): boolean ### Types clés (`lib/utils/types.ts`) ```typescript -InstrumentType = "basicSynth" | "elementarySynth" | "smplr" | "undertale" | "audioTrack" - -// Discriminated unions pour type safety +// InstrumentType = clé du registre d'instruments (lib/audio/instruments/registry.ts), +// forme ouverte (string) — plus une union fermée. Un nouvel instrument +// s'ajoute en s'enregistrant, sans modifier ce fichier. +InstrumentType = string + +// InstrumentConfig = forme ouverte, à l'image d'EffectInstanceConfig : le +// `type` est une clé du registre, les champs spécifiques (oscillatorType, +// soundfont, attack...) vivent à plat pour rester compatibles avec les +// projets déjà sauvegardés. +InstrumentConfig { type: string, gain?: number, [key: string]: unknown } + +// Types internes documentaires par engine (utilisés par chaque `definition.ts` +// et le constructeur de l'engine concerné, ne composent plus l'union +// InstrumentConfig) : BasicSynthConfig { type: "basicSynth", oscillatorType: OscillatorType, gain? } SmplrConfig { type: "smplr", soundfont: string, gain? } -ElementarySynthConfig { type: "elementarySynth", preset?, gain? } +ElementarySynthConfig { type: "elementarySynth", preset?, gain? } // legacy, voir plus bas UndertaleConfig { type: "undertale", instrument: string, gain?, attack?, decay?, sustain?, release? } AudioTrackConfig { type: "audioTrack", gain? } -InstrumentConfig = BasicSynthConfig | SmplrConfig | ElementarySynthConfig | UndertaleConfig | AudioTrackConfig -// Pour les updates partiels (sans discriminant) -InstrumentConfigUpdate { oscillatorType?, soundfont?, preset?, gain? } +// Pour les updates partiels : Partial (plus d'interface +// InstrumentConfigUpdate séparée à faire grossir à chaque nouveau type) EQBand { id: string // "sub", "bass", "mid", "presence", "brilliance" @@ -169,29 +179,53 @@ existantes. Les anciens projets (positions en "colonnes", 1 colonne = 1 double-croche) sont migrés automatiquement au chargement (`timelineStore.ts`, `migrateLegacyProject`). +### Registre d'instruments (`lib/audio/instruments/`) + +Registre extensible façon plugin, sur le modèle du [système d'effets](#système-deffets-libaudioeffects) (même esprit "proche de WAM dans la forme") — remplace l'ancien switch fermé. Contrairement aux effets (paramètres strictement numériques), les instruments ont aussi des réglages à choix fermé (waveform, soundfont, preset), d'où un discriminant `kind` sur les métadonnées : + +``` +lib/audio/instruments/ + types.ts # InstrumentParamMeta = NumericInstrumentParamMeta ("number") | EnumInstrumentParamMeta ("enum") + # EnumInstrumentParamMeta.options: Option[] | "dynamic" (liste connue seulement après + # chargement d'une ressource par l'engine, ex: presets Undertale — résolue via + # InstrumentEngine.getParamOptions(paramId)) + registry.ts # InstrumentDefinition { type, label, icon?, params, createDefaultConfig(), engine } + # registerInstrument/getInstrumentDefinition/listInstrumentDefinitions/createInstrumentEngine + index.ts # Enregistre les 4 instruments intégrés au chargement du module +``` + +Chaque `InstrumentDefinition` concrète est colocalisée avec son engine (`engines/basic-synth/definition.ts`, etc.) plutôt que regroupée ici — le registre reste générique, les détails (bornes ADSR, liste de soundfonts) sont propres à chaque engine. + +`lib/audio/instrumentFactory.ts` reste le point d'entrée utilisé par les stores (signature stable) : adaptateur fin qui délègue au registre pour tout type connu, et garde en dur le seul cas legacy `elementarySynth → BasicSynthEngine` (jamais eu de vrai moteur, plus proposé à la création — voir plus bas). + ### Engines Audio (`lib/audio/engines/`) Architecture modulaire avec classe de base abstraite : ``` engines/ - types.ts # InstrumentEngine, EngineState, EngineStateCallback + types.ts # InstrumentEngine, EngineState, EngineStateCallback, getParamOptions?() BaseEngine.ts # Classe abstraite avec state management noteUtils.ts # noteNameToFrequency() partagé index.ts # Re-exports publics basic-synth/ BasicSynthEngine.ts # Oscillateurs Web Audio (toujours ready) + definition.ts # InstrumentDefinition (enum oscillatorType) smplr/ SmplrEngine.ts # Soundfonts via `smplr` (128+ instruments) soundfonts.ts # SOUNDFONT_LIST + SoundfontName + definition.ts # InstrumentDefinition (enum soundfont, options dérivées de SOUNDFONT_LIST) undertale/ - UndertaleEngine.ts # Soundfont custom Undertale avec ADSR -``` + UndertaleEngine.ts # Soundfont custom Undertale avec ADSR, getParamOptions("instrument") + definition.ts # InstrumentDefinition (enum instrument "dynamic" + 4 params ADSR numériques) -Factory : `lib/audio/instrumentFactory.ts` - Crée les instances d'engines selon le type. + audio-clip/ + AudioClipEngine.ts # Lecture de clips audio (pas de synthèse) + definition.ts # InstrumentDefinition (params: []) +``` ### Bibliothèque de samples (Audiothèque) @@ -318,10 +352,12 @@ Le playback engine, l'export et l'enregistrement voix dépendent l'un de l'autre #### Instruments (`components/app/instruments/`) -| Composant | Rôle | -| ------------------------ | ------------------------------------------------------- | -| `InstrumentSettings.vue` | Panneau latéral : volume, pile d'effets (`EffectRack`), options instrument | -| `TrackEqualizer.vue` | Canvas EQ 5 bandes interactif (drag points), réutilisé par `EffectSlot.vue` | +| Composant | Rôle | +| ----------------------------- | ------------------------------------------------------- | +| `InstrumentSettings.vue` | Panneau latéral : volume, pile d'effets (`EffectRack`), bouton d'ouverture des réglages instrument | +| `InstrumentParamsModal.vue` | Modale (`BaseModal`) : rendu générique des `params` de l'`InstrumentDefinition` (via `InstrumentParamField`) | +| `InstrumentParamField.vue` | Un champ de réglage instrument, `RangeSlider` (number) ou `