--- phase: 04-polish-audio plan: 01 type: execute wave: 1 depends_on: [] files_modified: - src/audio/sounds.ts - src/audio/speech.ts - src/types.ts - index.html - src/styles/main.css - src/main.ts autonomous: true requirements: [AUDI-01, AUDI-02, AUDI-03, AUDI-04, AUDI-05] must_haves: truths: - "playCorrectSound() erzeugt 800Hz Sinus-Ton fuer 0.1s bei audioEnabled=true" - "playRewardSound() erzeugt aufsteigende Tonfolge fuer 0.8s" - "playForestElementSound() erzeugt gefiltertes weisses Rauschen fuer 0.5s" - "Mute-Button im DOM sichtbar, toggle aendert audioEnabled in Settings und persistiert" - "speakText() liest deutschen Text via Web Speech API vor wenn speechEnabled=true" - "AudioContext wird lazy bei erster User-Interaktion erstellt (Autoplay Policy)" artifacts: - path: "src/audio/sounds.ts" provides: "Web Audio API sound effects" exports: ["playCorrectSound", "playRewardSound", "playForestElementSound", "initAudioOnInteraction"] - path: "src/audio/speech.ts" provides: "Web Speech API text-to-speech" exports: ["speakText"] - path: "index.html" provides: "Mute button element outside screens" contains: "mute-btn" key_links: - from: "src/audio/sounds.ts" to: "src/storage/db.ts" via: "getSettings() check for audioEnabled" pattern: "getSettings.*audioEnabled" - from: "src/audio/speech.ts" to: "src/storage/db.ts" via: "getSettings() check for speechEnabled" pattern: "getSettings.*speechEnabled" --- Audio-System fuer Zauberwald: Web Audio API Sound-Effekte (3 Sounds), Mute-Button, optionale Sprachausgabe via Web Speech API. Purpose: Auditives Feedback macht das Tipperlebnis lebendig und belohnend -- per D-01 bis D-05 aus CONTEXT.md. Output: Zwei neue Module (sounds.ts, speech.ts), Mute-Button in index.html, speechEnabled Feld in Settings. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/04-polish-audio/04-CONTEXT.md @src/types.ts @src/storage/db.ts @src/main.ts @index.html @src/styles/main.css Task 1: Audio module + Speech module + Settings extension src/audio/sounds.ts, src/audio/speech.ts, src/types.ts src/types.ts, src/storage/db.ts, .planning/phases/04-polish-audio/04-CONTEXT.md 1. In `src/types.ts`: Add `speechEnabled: boolean` to the `Settings` interface (per D-05). Keep existing fields unchanged. 2. Create `src/audio/sounds.ts` (per D-01, D-02): - Module-level `let audioCtx: AudioContext | null = null` - `initAudioOnInteraction()`: Creates/resumes AudioContext on first call. Intended to be called from a user-interaction event handler. Export this. - Helper `isAudioEnabled()`: async, calls `getSettings(db)` from db.ts (needs db reference -- use a module-level `let db: IDBDatabase | null` set via an `setAudioDB(d)` function, or import `getDB()` from app.ts). Check `settings?.audioEnabled !== false`. Return boolean. - `playCorrectSound()`: async. Check `isAudioEnabled()`. If false, return. Ensure audioCtx exists (call initAudioOnInteraction if needed). Create OscillatorNode: type sine, frequency 800Hz, connect to gain (0.15), start, stop after 0.1s. Ramp gain to 0 over last 0.02s for click-free cutoff. - `playRewardSound()`: async. Check enabled. Ascending tone sequence: play 3-4 notes (e.g. C5=523, E5=659, G5=784, C6=1047) each ~0.2s apart, each 0.15s duration, sine wave, gain 0.12. Total ~0.8s. - `playForestElementSound()`: async. Check enabled. Create white noise buffer (AudioBuffer, fill with Math.random()*2-1), play through BiquadFilterNode (bandpass, frequency 3000Hz, Q 0.5), gain 0.1, duration 0.5s with gain ramp-down. - All functions: wrap in try/catch (never throw to caller). 3. Create `src/audio/speech.ts` (per D-04, D-05): - `speakText(text: string)`: async. Check `speechEnabled` from Settings via getSettings. If not enabled, return. Check `window.speechSynthesis` exists. Create `SpeechSynthesisUtterance(text)`, set `lang='de-DE'`, `rate=0.9`, `pitch=1.1`. Try to find a German voice (`getVoices().find(v => v.lang.startsWith('de'))`), set if found. Call `speechSynthesis.speak(utterance)`. Wrap in try/catch, silent fallback. - Handle voice loading: voices may load async. Use `speechSynthesis.onvoiceschanged` if `getVoices()` returns empty initially. cd /home/dev/workspace/zauberwald && npx tsc --noEmit 2>&1 | head -20 - src/audio/sounds.ts exports playCorrectSound, playRewardSound, playForestElementSound, initAudioOnInteraction - src/audio/speech.ts exports speakText - Settings interface in types.ts includes speechEnabled: boolean - All three sound functions check audioEnabled before playing - speakText checks speechEnabled before speaking - TypeScript compiles without errors Three sound functions and speakText function exist, all check Settings before executing, TypeScript compiles clean Task 2: Mute button in HTML + CSS + wiring in main.ts index.html, src/styles/main.css, src/main.ts index.html, src/main.ts, src/styles/main.css, src/storage/db.ts 1. In `index.html` (per D-03): Add a mute button BEFORE the `