ParameterSource - Multi-Source Parameter System
Parameter aus verschiedenen Quellen zusammenführen: Tweakpane, MIDI, OSC, JSON, Sockets, Node Editor.
Das Problem
In komplexen Setups kommen Parameter aus verschiedenen Quellen:
- Tweakpane UI - Zum Entwickeln und Testen
- JSON Files - Presets und gespeicherte Snapshots
- MIDI Controller - Live Performance mit Hardware
- OSC - TouchOSC, Lemur und andere Apps
- Sockets - Studio-Steuerung und Collaboration
- Node Editor - Visuelle Programmierung
Was passiert wenn mehrere Quellen den gleichen Parameter setzen wollen?
Die Lösung: ParameterComposer
Der ParameterComposer führt alle Quellen intelligent zusammen:
Tweakpane (50) ──┐
JSON (10) ───────┼──► ParameterComposer ──► parameter.brush.size
MIDI (30) ───────┤
Socket (90) ─────┘Jede Quelle hat eine Priorität. Höhere Priorität gewinnt bei Konflikten.
Schnellstart
import {
createParameterSystem,
createSchema,
numParam,
colorParam,
boolParam,
} from '@carstennichte/cc-toolbox';
// 1. Parameter-Schema definieren
const schema = createSchema([
numParam('brush.size', 10, 1, 100, { step: 1, label: 'Größe' }),
numParam('brush.opacity', 1.0, 0, 1, { step: 0.01, label: 'Deckkraft' }),
colorParam('brush.color', '#ff0000', { label: 'Farbe' }),
boolParam('brush.active', true, { label: 'Aktiv' }),
]);
// 2. System mit Quellen erstellen
const system = createParameterSystem({
parameter: myParameter,
schema,
tweakpane: { manager: myTweakpaneManager },
midi: {
ccMappings: {
1: 'brush.size', // Mod Wheel → Größe
7: 'brush.opacity', // Volume → Deckkraft
}
},
});
// 3. Verbinden
await system.connect();Verfügbare Quellen
Tweakpane (Priorität: 50)
Die bekannte GUI zum Entwickeln. Zeigt aktuelle Werte und erlaubt Bearbeitung.
const system = createParameterSystem({
parameter,
schema,
tweakpane: {
manager: myTweakpaneManager,
syncFromComposer: true, // Zeigt merged values
},
});JSON (Priorität: 10)
Für Presets und gespeicherte Einstellungen.
const system = createParameterSystem({
parameter,
schema,
json: {
source: 'url',
url: '/presets/default.json',
},
});
// Oder aus localStorage
const system = createParameterSystem({
parameter,
schema,
json: {
source: 'storage',
storageKey: 'my-artwork-preset',
},
});MIDI (Priorität: 30)
Hardware Controller für Live Performance.
const system = createParameterSystem({
parameter,
schema,
midi: {
deviceFilter: 'Arturia', // Optional
channel: 1, // Optional
ccMappings: {
1: 'brush.size', // CC 1 = Mod Wheel
7: 'brush.opacity', // CC 7 = Volume
74: 'effect.cutoff', // CC 74 = Filter
},
},
});
// Learn Mode für unbekannte Controller
system.sources.midi.startLearn((cc, channel) => {
console.log(`CC ${cc} auf Kanal ${channel}`);
// Hier Mapping speichern...
system.sources.midi.stopLearn();
});Standard CC-Nummern:
| CC | Name | Typische Verwendung |
|---|---|---|
| 1 | Mod Wheel | Intensität |
| 7 | Volume | Deckkraft |
| 10 | Pan | Position X |
| 74 | Filter Cutoff | Effekt-Stärke |
OSC (Priorität: 30)
Für TouchOSC, Lemur und ähnliche Apps.
const system = createParameterSystem({
parameter,
schema,
osc: {
port: 9000,
addressMappings: {
'/page1/fader1': 'brush.size',
'/page1/fader2': 'brush.opacity',
'/page1/xy1/x': 'position.x',
'/page1/xy1/y': 'position.y',
},
},
});Hinweis: OSC im Browser benötigt eine WebSocket-Bridge (z.B. osc-web-bridge).
Sockets (Priorität: 90)
Höchste Priorität - für Studio-Steuerung und Collaboration.
const system = createParameterSystem({
parameter,
schema,
socket: {
url: 'http://localhost:3090',
room: 'artwork:my-artwork',
artworkId: 'my-artwork',
syncToServer: true,
},
});Node Editor (Priorität: 70)
Visuelle Programmierung im Art.Works! Studio.
const system = createParameterSystem({
parameter,
schema,
nodeEditor: {
socketUrl: 'http://localhost:3090',
artworkId: 'my-artwork',
},
});
// Prüfen ob Parameter von Node kontrolliert wird
if (system.sources.nodeEditor.isControlled('brush.size')) {
// Tweakpane-Control deaktivieren
}Merge-Strategien
Was passiert wenn mehrere Quellen den gleichen Parameter setzen?
const system = createParameterSystem({
parameter,
schema,
mergeStrategies: {
'brush.size': 'override', // Höchste Priorität gewinnt (default)
'brush.opacity': 'multiply', // Werte multiplizieren
'effect.intensity': 'max', // Höchster Wert
'position.x': 'blend', // Gewichtetes Mittel nach Priorität
},
});Verfügbare Strategien:
| Strategie | Beschreibung |
|---|---|
override |
Höchste Priorität gewinnt (Standard) |
blend |
Gewichtetes Mittel nach Priorität |
add |
Werte addieren |
multiply |
Werte multiplizieren |
max |
Höchster Wert |
min |
Niedrigster Wert |
first |
Erste Quelle die einen Wert hat |
last |
Letzte Änderung gewinnt |
Auf Änderungen reagieren
// Alle Änderungen
system.composer.onChange(event => {
console.log(`${event.path}: ${event.oldValue} → ${event.value}`);
console.log(`Quelle: ${event.sourceId}`);
});
// Nur bestimmte Parameter
system.composer.onChange(event => {
if (event.path.startsWith('brush.')) {
updateBrush(event.path, event.value);
}
});Prioritäten-Übersicht
| Quelle | Priorität | Typischer Einsatz |
|---|---|---|
| JSON | 10 | Basis-Presets, Defaults |
| MIDI | 30 | Hardware Controller |
| OSC | 30 | Mobile Apps |
| Tweakpane | 50 | Entwicklung, Fine-Tuning |
| Node Editor | 70 | Visuelle Programmierung |
| Sockets. | 90 | Studio-Override, Collaboration |
Debugging
const system = createParameterSystem({
// ...
debug: true, // Aktiviert Logging
});
// Manuell Quellen-Status prüfen
system.sources.forEach((source, id) => {
console.log(`${id}: ${source.isConnected() ? '✅' : '❌'}`);
});
// Aktuellen Wert mit Quelle abrufen
const { value, sourceId } = system.composer.getValueWithSource('brush.size');
console.log(`brush.size = ${value} (von ${sourceId})`);Zusammenfassung
- Schema definieren - Welche Parameter gibt es?
- Quellen konfigurieren - Woher kommen die Werte?
- Merge-Strategien wählen - Was passiert bei Konflikten?
- Verbinden und nutzen -
await system.connect()
Das ParameterSource Pattern macht komplexe Multi-Input Setups einfach und vorhersagbar.