- JavaScript 84.7%
- CSS 11.7%
- HTML 2.7%
- Python 0.6%
- Shell 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Uncommittete Änderungen gesichert (Sammel-Commit), damit der Stand auf Forgejo liegt — ggf. noch halbfertige Arbeit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nis4Pzds5rZXdAQ8VBxKqP |
||
| deploy | ||
| public | ||
| server | ||
| tools | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SPEC-V2.md | ||
| SPEC-V3.md | ||
| SPEC-V4.md | ||
| SPEC-V5.md | ||
| SPEC-V6.md | ||
| SPEC.md | ||
Dawtastic — Mini-DAW für den Musikunterricht
Eine kleine Web-DAW für Schul-iPads und Desktop-Browser. Schüler legen ein Projekt an und
bekommen eine eindeutige Projekt-URL (/p/<token>) — das ist ihr einziger „Login“.
Alles wird automatisch auf dem Server gespeichert.
Setup
Voraussetzung: Node.js ≥ 22 (wegen des eingebauten node:sqlite).
cd /var/www/dawtastic
npm install
npm start
Der Server läuft danach auf Port 3007 (änderbar über die Umgebungsvariable PORT):
PORT=8080 npm start
Beim ersten Start wird das Verzeichnis data/ automatisch angelegt, darin:
data/dawtastic.sqlite— Datenbank (Projekte + Audio-Metadaten)data/audio/<token>/…— hochgeladene Audiodateiendata/config.json— hier steht das generierte Admin-Passwort
Admin-Panel
Unter /admin erreichbar. Das Passwort findest du in data/config.json
(Feld adminPassword, wird beim ersten Start automatisch generiert).
Im Admin-Panel kannst du:
- alle Projekte sehen (Name, URL, Größe, letzte Änderung)
- Projekt-URLs sperren/entsperren (gesperrte Schüler sehen eine Hinweisseite)
- Projekte löschen (inkl. aller Audiodateien)
Betrieb hinter Nginx
Eine Beispiel-Konfiguration liegt in deploy/nginx.example.conf
(Reverse Proxy auf 127.0.0.1:3007, client_max_body_size 60m für Audio-Uploads).
Kopiere sie nach /etc/nginx/sites-available/, passe Domain und Zertifikate an
und aktiviere sie. Nicht unverändert übernehmen.
Module
- Steps — Step-Sequencer: bis zu 8 Spuren, 1/2/4 Takte, Swing, Built-in-Drumsounds oder eigene Aufnahmen pro Spur.
- Sampler — 8 Pads zum Antippen: eigene Aufnahmen oder Dateien importieren, trimmen, pitchen, Lautstärke einstellen.
- Recorder — Audiospuren aufnehmen und mit Offset/Lautstärke zu einem Mini-Arrangement zusammensetzen.
Neue Module lassen sich leicht ergänzen: eine Datei in public/js/modules/ mit dem
Standard-Interface ({ id, title, mount(rootEl, ctx), unmount() }) anlegen und in
public/js/shell.js in die modules-Liste eintragen.
Built-in-Sounds neu erzeugen
node tools/gen-sounds.mjs
Erzeugt die 8 Drumsounds (Kick, Snare, Clap, Hi-Hat, Open-Hat, Tom, Rim, Shaker)
als WAV-Dateien in public/sounds/.
Diagnose-Werkzeuge (Audio)
Nicht verlinkt, für Schüler unsichtbar — bei Audio-Problemen aber Gold wert:
/audiotest.html— spielt dreimal denselben Ton über den neutralen Signalweg (Master → Limiter) und misst die Spitzenpegel im Graphen. Zeigt außerdem, wie langeresume()braucht und wann die Audio-Uhr wirklich losläuft./chaintest.html— baut jeden Knotentyp einzeln in einem eigenen frischen AudioContext und vergleicht den ersten Ton mit den folgenden. Damit wurde gefunden, dass ein frischerDynamicsCompressordas erste Signal, das je durch ihn läuft, auf 44 % dämpft (auch im Bypass) — seitdem liegt er infxchain.jsparallel und stumm, solange er aus ist.?audiodbg=1an der Projekt-URL — Overlay in der laufenden App: je Note die benutzten Werte (Pegel = Anschlag × Verstärkung, Attack, Rate) und die gemessenen Pegel an Quelle, Ketten-Eingang, Spur-Ausgang und Master.
Timing und Latenz (Bluetooth-Kopfhörer)
Kopfhörer geben den Ton verzögert aus — per Bluetooth 100–300 ms. Wer „auf das Gehörte" spielt, landet dann systematisch hinter dem Raster. Dagegen gibt es zwei Stufen:
- Automatisch:
engine.outputLatencySec()liestAudioContext.outputLatency(Rückfall:baseLatency) bei jedem Zugriff frisch — der Wert springt, sobald ein Bluetooth-Gerät verbunden wird. Er verzögert Playhead, Steps-Lauflicht und Video-Sync auf den Hör-Zeitpunkt. - Gemessen (Tap-Test): Der Knopf „Timing" in der Klaviatur-Leiste öffnet
latcal.js: Metronom auf der Audio-Uhr, Schüler tippt mit, Median aus ≥ 8 Treffern = Ausgabe- + Touch-Latenz.engine.recordAheadSec()zieht diesen Wert von jedem Einspiel-Stempel ab. Für Bluetooth ist das der einzig verlässliche Weg — ob iOS-Safari die echte BT-Latenz inoutputLatencydurchreicht, ist offen, und ein Mikrofon-Loopback hört Kopfhörer nicht.
Gespeichert wird pro Gerät in localStorage (dawtastic-lat-play): Der Versatz
gehört zu iPad + Kopfhörer, nicht zum Schüler. Ein Wechsel des Ausgabegeräts
(devicechange oder ein Sprung im API-Wert) markiert die Messung als veraltet.
Session-Aufnahmen brauchen keine Messung: captureStart() legt einen unhörbaren
1-kHz-Marker in den Aufnahmezweig, der beim Ablegen wiedergefunden wird
(findSyncMarkerSec) — das verortet den Take samplegenau und liefert nebenbei
die MediaRecorder-Anlaufzeit des Geräts.
/audiotest.html protokolliert outputLatency live mit — zum Prüfen einfach
während des Tests einen Bluetooth-Kopfhörer verbinden und trennen.
Sicherung
deploy/backup.sh macht einen konsistenten SQLite-Online-Snapshot und spiegelt
data/ per rsync; deploy/dawtastic-backup.timer startet das täglich um 02:30.
Standardziel ist /var/backups/dawtastic — das liegt auf derselben Platte
und schützt damit nur gegen versehentliches Löschen und DB-Schäden. Gegen einen
Plattenausfall hilft nur ein anderes Ziel:
sudo DEST_DIR=/mnt/nas/dawtastic bash deploy/backup.sh (bzw. DEST_DIR in der
Service-Unit setzen).
Technik
- Kein Framework, kein Build-Schritt, keine CDNs — Vanilla-ES-Module.
- Backend: Express +
node:sqlite(einzige npm-Dependency:express). - Audio: Web Audio API, Aufnahme über
MediaRecorder(Safari:audio/mp4, Chrome/Firefox:audio/webm).