developer-guide.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Developer Guide - BrowserEQ V2</title> 8 <link rel="stylesheet" href="styles.css"> 9 </head> 10 11 <body> 12 <header> 13 <div class="wave-decoration"></div> 14 <div class="language-toggle"> 15 <button onclick="setLanguage('en')" class="active" id="en-btn">English</button> 16 <button onclick="setLanguage('de')" id="de-btn">Deutsch</button> 17 </div> 18 <div class="container"> 19 <h1>BrowserEQ V2</h1> 20 <p class="en">Developer Guide</p> 21 <p class="de">Entwicklerhandbuch</p> 22 <div class="github-buttons"> 23 <a href="index.html">Home</a> 24 <a href="https://github.com/berraknil/BrowserEQ-V2" target="_blank">View on GitHub</a> 25 </div> 26 </div> 27 </header> 28 29 <nav> 30 <ul> 31 <li><a href="#architecture">Architecture</a></li> 32 <li><a href="#data-flow">Data Flow</a></li> 33 <li><a href="#audio-processing">Audio Processing</a></li> 34 <li><a href="#presets">Presets</a></li> 35 <li><a href="#adding-features">Adding Features</a></li> 36 </ul> 37 </nav> 38 39 <main class="container"> 40 <section id="introduction"> 41 <div class="en"> 42 <p>This guide provides detailed information for developers who want to understand, modify, or contribute 43 to the BrowserEQ project.</p> 44 </div> 45 <div class="de"> 46 <p>Dieses Handbuch bietet detaillierte Informationen für Entwickler, die das BrowserEQ-Projekt 47 verstehen, modifizieren oder dazu beitragen möchten.</p> 48 </div> 49 </section> 50 51 <section id="architecture"> 52 <h2 class="en">Architecture Overview</h2> 53 <h2 class="de">Architekturübersicht</h2> 54 55 <div class="en"> 56 <p>BrowserEQ uses a Vue 3 frontend with the Composition API, combined with Web Audio API for audio 57 processing. The extension follows a component-based architecture:</p> 58 <div class="code-block"> 59 <pre>UI Components (Vue) → Audio Logic (TypeScript) → Web Audio API → Browser Audio</pre> 60 </div> 61 62 <h3>Key Components</h3> 63 <ol> 64 <li><strong>Popup.vue</strong>: The main component that renders the user interface and orchestrates 65 the other components.</li> 66 <li><strong>PresetManager.vue</strong>: Handles preset selection and management.</li> 67 <li><strong>Strip.vue</strong>: Represents a single filter strip with its controls.</li> 68 <li><strong>Slider.vue & Toggle.vue</strong>: Reusable UI controls.</li> 69 </ol> 70 71 <h3>Core Logic Modules</h3> 72 <ol> 73 <li><strong>audioProcessing.ts</strong>: Contains the Web Audio API implementation.</li> 74 <li><strong>EQValues.ts</strong>: Defines the default presets.</li> 75 <li><strong>presetStorage.ts</strong>: Manages storing preferences in localStorage.</li> 76 <li><strong>recordingService.ts</strong>: Handles audio recording functionality.</li> 77 </ol> 78 </div> 79 80 <div class="de"> 81 <p>BrowserEQ verwendet ein Vue 3 Frontend mit der Composition API, kombiniert mit der Web Audio API für 82 die Audioverarbeitung. Die Erweiterung folgt einer komponentenbasierten Architektur:</p> 83 <div class="code-block"> 84 <pre>UI-Komponenten (Vue) → Audio-Logik (TypeScript) → Web Audio API → Browser-Audio</pre> 85 </div> 86 87 <h3>Hauptkomponenten</h3> 88 <ol> 89 <li><strong>Popup.vue</strong>: Die Hauptkomponente, die die Benutzeroberfläche rendert und die 90 anderen Komponenten orchestriert.</li> 91 <li><strong>PresetManager.vue</strong>: Verwaltet die Voreinstellungsauswahl und -verwaltung.</li> 92 <li><strong>Strip.vue</strong>: Repräsentiert einen einzelnen Filterstreifen mit seinen 93 Steuerelementen.</li> 94 <li><strong>Slider.vue & Toggle.vue</strong>: Wiederverwendbare UI-Steuerelemente.</li> 95 </ol> 96 97 <h3>Kern-Logikmodule</h3> 98 <ol> 99 <li><strong>audioProcessing.ts</strong>: Enthält die Web Audio API-Implementierung.</li> 100 <li><strong>EQValues.ts</strong>: Definiert die Standardvoreinstellungen.</li> 101 <li><strong>presetStorage.ts</strong>: Verwaltet die Speicherung von Einstellungen im localStorage. 102 </li> 103 <li><strong>recordingService.ts</strong>: Handhabt die Audio-Aufnahmefunktionalität.</li> 104 </ol> 105 </div> 106 </section> 107 108 <section id="data-flow"> 109 <h2 class="en">Data Flow</h2> 110 <h2 class="de">Datenfluss</h2> 111 112 <div class="en"> 113 <ol> 114 <li>User interacts with UI components (sliders, toggles, buttons)</li> 115 <li>Component emits events to parent (Popup.vue)</li> 116 <li>Popup.vue updates the current preset state</li> 117 <li>Audio processing logic applies changes to Web Audio nodes</li> 118 <li>Preferences can be stored in localStorage</li> 119 </ol> 120 121 <h3>Key Interfaces</h3> 122 <p>The core data structures are defined in several places:</p> 123 <div class="code-block"> 124 <pre>// EQ Preset structure 125 interface EQPreset { 126 mainOut: { 127 gain: number; 128 muted: boolean; 129 }; 130 filters: { 131 [key: string]: EQFilter; 132 }; 133 } 134 135 // Individual filter structure 136 interface EQFilter { 137 name: string; 138 type: BiquadFilterType; // Web Audio API filter type 139 enabled: boolean; 140 frequency: { 141 value: number; 142 }; 143 Q?: { 144 name: string; 145 value: number; 146 }; 147 gain?: { 148 value: number; 149 }; 150 }</pre> 151 </div> 152 </div> 153 154 <div class="de"> 155 <ol> 156 <li>Benutzer interagiert mit UI-Komponenten (Schieberegler, Umschalter, Buttons)</li> 157 <li>Komponente sendet Ereignisse an übergeordnetes Element (Popup.vue)</li> 158 <li>Popup.vue aktualisiert den aktuellen Voreinstellungsstatus</li> 159 <li>Audio-Verarbeitungslogik wendet Änderungen auf Web Audio-Knoten an</li> 160 <li>Einstellungen können im localStorage gespeichert werden</li> 161 </ol> 162 163 <h3>Wichtige Schnittstellen</h3> 164 <p>Die Kerndatenstrukturen sind an mehreren Stellen definiert:</p> 165 <div class="code-block"> 166 <pre>// EQ-Voreinstellungsstruktur 167 interface EQPreset { 168 mainOut: { 169 gain: number; 170 muted: boolean; 171 }; 172 filters: { 173 [key: string]: EQFilter; 174 }; 175 } 176 177 // Individuelle Filterstruktur 178 interface EQFilter { 179 name: string; 180 type: BiquadFilterType; // Web Audio API Filtertyp 181 enabled: boolean; 182 frequency: { 183 value: number; 184 }; 185 Q?: { 186 name: string; 187 value: number; 188 }; 189 gain?: { 190 value: number; 191 }; 192 }</pre> 193 </div> 194 </div> 195 </section> 196 197 <section id="audio-processing"> 198 <h2 class="en">Audio Processing Pipeline</h2> 199 <h2 class="de">Audio-Verarbeitungspipeline</h2> 200 201 <div class="en"> 202 <ol> 203 <li><strong>Capture</strong>: The extension captures tab audio using <code>chrome.tabCapture</code>. 204 </li> 205 <li><strong>Source Creation</strong>: A MediaStreamAudioSourceNode is created from the capture.</li> 206 <li><strong>Filter Chain</strong>: Audio is routed through various BiquadFilterNode instances based 207 on user settings.</li> 208 <li><strong>Gain Node</strong>: Final processing includes volume adjustment through a GainNode.</li> 209 <li><strong>Output</strong>: Processed audio is sent to the AudioContext destination.</li> 210 </ol> 211 212 <div class="code-block"> 213 <pre>MediaStreamSource → [Enabled Filters] → GainNode → AudioContext.destination</pre> 214 </div> 215 216 <h3>Filter Connectivity Logic</h3> 217 <p>The system dynamically connects and disconnects filters when users toggle them:</p> 218 <ol> 219 <li>When enabling a filter, it's inserted into the chain at the appropriate point.</li> 220 <li>When disabling a filter, it's bypassed, and the chain is reconnected.</li> 221 <li>The <code>lastFilter</code> reference always points to the last active filter in the chain.</li> 222 </ol> 223 </div> 224 225 <div class="de"> 226 <ol> 227 <li><strong>Erfassung</strong>: Die Erweiterung erfasst Tab-Audio mit 228 <code>chrome.tabCapture</code>. 229 </li> 230 <li><strong>Quellenerstellung</strong>: Ein MediaStreamAudioSourceNode wird aus der Erfassung 231 erstellt.</li> 232 <li><strong>Filterkette</strong>: Audio wird durch verschiedene BiquadFilterNode-Instanzen basierend 233 auf Benutzereinstellungen geleitet.</li> 234 <li><strong>Verstärkungsknoten</strong>: Abschließende Verarbeitung umfasst Lautstärkeanpassung 235 durch einen GainNode.</li> 236 <li><strong>Ausgabe</strong>: Verarbeitetes Audio wird an das AudioContext-Ziel gesendet.</li> 237 </ol> 238 239 <div class="code-block"> 240 <pre>MediaStreamSource → [Aktivierte Filter] → GainNode → AudioContext.destination</pre> 241 </div> 242 243 <h3>Filterverbindungslogik</h3> 244 <p>Das System verbindet und trennt Filter dynamisch, wenn Benutzer sie umschalten:</p> 245 <ol> 246 <li>Beim Aktivieren eines Filters wird er an der entsprechenden Stelle in die Kette eingefügt.</li> 247 <li>Beim Deaktivieren eines Filters wird er umgangen, und die Kette wird neu verbunden.</li> 248 <li>Die <code>lastFilter</code>-Referenz zeigt immer auf den letzten aktiven Filter in der Kette. 249 </li> 250 </ol> 251 </div> 252 </section> 253 254 <section id="presets"> 255 <h2 class="en">Preset Management</h2> 256 <h2 class="de">Voreinstellungsverwaltung</h2> 257 258 <div class="en"> 259 <p>The preset system works as follows:</p> 260 <ol> 261 <li>Default presets are defined in <code>EQValues.ts</code> and serve as the source of truth.</li> 262 <li>User preferences may be stored in localStorage for persistence between sessions.</li> 263 </ol> 264 265 <h3>Deep Copy Implementation</h3> 266 <p>To avoid reference issues, presets are deep-copied using a custom function:</p> 267 <div class="code-block"> 268 <pre>function deepCopyPreset(preset: EQPreset): EQPreset { 269 // Creates a new object with all nested properties copied 270 // This prevents issues with shared references 271 }</pre> 272 </div> 273 274 <h3>Reactive State Management</h3> 275 <p>The extension uses Vue 3's reactivity system:</p> 276 <ol> 277 <li><code>ref</code> and <code>computed</code> are used for reactive state.</li> 278 <li>Watchers (<code>watch</code>) observe changes to critical values like sliders.</li> 279 <li>Two-way binding with <code>v-model</code> connects UI elements to state.</li> 280 </ol> 281 282 <h3>LocalStorage Schema</h3> 283 <p>Preferences are stored in localStorage for persistence:</p> 284 <div class="code-block"> 285 <pre>// Key format example 286 { 287 "selectedPreset": "Neutral", 288 // other settings... 289 }</pre> 290 </div> 291 </div> 292 293 <div class="de"> 294 <p>Das Voreinstellungssystem funktioniert wie folgt:</p> 295 <ol> 296 <li>Standardvoreinstellungen sind in <code>EQValues.ts</code> definiert und dienen als Quelle der 297 Wahrheit.</li> 298 <li>Benutzereinstellungen können im localStorage für die Persistenz zwischen Sitzungen gespeichert 299 werden.</li> 300 </ol> 301 302 <h3>Deep-Copy-Implementierung</h3> 303 <p>Um Referenzprobleme zu vermeiden, werden Voreinstellungen mit einer benutzerdefinierten Funktion tief 304 kopiert:</p> 305 <div class="code-block"> 306 <pre>function deepCopyPreset(preset: EQPreset): EQPreset { 307 // Erstellt ein neues Objekt mit allen verschachtelten Eigenschaften kopiert 308 // Dies verhindert Probleme mit gemeinsam genutzten Referenzen 309 }</pre> 310 </div> 311 312 <h3>Reaktive Zustandsverwaltung</h3> 313 <p>Die Erweiterung verwendet das Reaktivitätssystem von Vue 3:</p> 314 <ol> 315 <li><code>ref</code> und <code>computed</code> werden für den reaktiven Zustand verwendet.</li> 316 <li>Beobachter (<code>watch</code>) überwachen Änderungen an kritischen Werten wie Schiebereglern. 317 </li> 318 <li>Zwei-Wege-Bindung mit <code>v-model</code> verbindet UI-Elemente mit dem Zustand.</li> 319 </ol> 320 321 <h3>LocalStorage-Schema</h3> 322 <p>Einstellungen werden im localStorage für die Persistenz gespeichert:</p> 323 <div class="code-block"> 324 <pre>// Schlüsselformat-Beispiel 325 { 326 "selectedPreset": "Neutral", 327 // andere Einstellungen... 328 }</pre> 329 </div> 330 </div> 331 </section> 332 333 <section id="adding-features"> 334 <h2 class="en">Adding New Features</h2> 335 <h2 class="de">Neue Funktionen hinzufügen</h2> 336 337 <div class="en"> 338 <h3>Adding a New Preset</h3> 339 <ol> 340 <li>Add the preset definition to <code>EQValues.ts</code>: 341 <div class="code-block"> 342 <pre>NewPreset: { 343 mainOut: { gain: 1.0, muted: false }, 344 filters: { 345 // Define filter settings 346 } 347 }</pre> 348 </div> 349 </li> 350 <li>The UI will automatically include the new preset in the dropdown menu.</li> 351 </ol> 352 353 <h3>Adding a New Filter Type</h3> 354 <ol> 355 <li>Add the filter to the base preset in <code>EQValues.ts</code>.</li> 356 <li>Create UI elements in <code>Popup.vue</code> for the new filter.</li> 357 <li>Update <code>audioProcessing.ts</code> to handle the new filter type.</li> 358 <li>Update related logic as needed.</li> 359 </ol> 360 361 <h3>Common Pitfalls</h3> 362 <ol> 363 <li><strong>Reference Issues</strong>: Always use deep copies when working with preset objects to 364 avoid unintended changes.</li> 365 <li><strong>Web Audio API Timing</strong>: Be aware that some audio operations must be carefully 366 timed to avoid clicks and pops.</li> 367 <li><strong>Chrome Extension Context</strong>: Remember that the extension runs in an isolated 368 context with security restrictions.</li> 369 </ol> 370 </div> 371 372 <div class="de"> 373 <h3>Hinzufügen einer neuen Voreinstellung</h3> 374 <ol> 375 <li>Fügen Sie die Voreinstellungsdefinition zu <code>EQValues.ts</code> hinzu: 376 <div class="code-block"> 377 <pre>NewPreset: { 378 mainOut: { gain: 1.0, muted: false }, 379 filters: { 380 // Filtereinstellungen definieren 381 } 382 }</pre> 383 </div> 384 </li> 385 <li>Die Benutzeroberfläche wird die neue Voreinstellung automatisch im Dropdown-Menü aufnehmen.</li> 386 </ol> 387 388 <h3>Hinzufügen eines neuen Filtertyps</h3> 389 <ol> 390 <li>Füge den Filter zur Basisvoreinstellung in <code>EQValues.ts</code> hinzu.</li> 391 <li>Erstelle UI-Elemente in <code>Popup.vue</code> für den neuen Filter.</li> 392 <li>Aktualisiere <code>audioProcessing.ts</code>, um den neuen Filtertyp zu handhaben.</li> 393 <li>Aktualisiere die zugehörige Logik nach Bedarf.</li> 394 </ol> 395 396 <h3>Häufige Fallstricke</h3> 397 <ol> 398 <li><strong>Referenzprobleme</strong>: Verwenden Sie immer tiefe Kopien, wenn Sie mit 399 Voreinstellungsobjekten arbeiten, um unbeabsichtigte Änderungen zu vermeiden.</li> 400 <li><strong>Web Audio API Timing</strong>: Beachten Sie, dass einige Audiooperationen sorgfältig 401 zeitlich abgestimmt werden müssen, um Klicks und Knackgeräusche zu vermeiden.</li> 402 <li><strong>Chrome-Erweiterungskontext</strong>: Denken Sie daran, dass die Erweiterung in einem 403 isolierten Kontext mit Sicherheitsbeschränkungen ausgeführt wird.</li> 404 </ol> 405 </div> 406 </section> 407 408 <section id="build-and-test"> 409 <h2 class="en">Build Process</h2> 410 <h2 class="de">Build-Prozess</h2> 411 412 <div class="en"> 413 <p>The project uses Vite for building:</p> 414 <ol> 415 <li><strong>Development</strong>: <code>npm run dev</code> - Builds in development mode with hot 416 reloading.</li> 417 <li><strong>Production</strong>: <code>npm run build</code> - Creates a production version in the 418 <code>extension</code> folder. 419 </li> 420 <li><strong>Testing</strong>: Load the <code>extension</code> folder as an unpacked extension in 421 Chrome.</li> 422 </ol> 423 424 <h3>Testing</h3> 425 <p>We recommend testing your changes in these scenarios:</p> 426 <ol> 427 <li><strong>Different Content Types</strong>: Test with music, voice calls, videos, etc.</li> 428 <li><strong>Edge Cases</strong>: Test with extreme filter settings and combinations.</li> 429 <li><strong>Performance</strong>: Ensure changes don't cause audio glitches or high CPU usage.</li> 430 </ol> 431 </div> 432 433 <div class="de"> 434 <p>Das Projekt verwendet Vite zum Erstellen:</p> 435 <ol> 436 <li><strong>Entwicklung</strong>: <code>npm run dev</code> - Erstellt im Entwicklungsmodus mit Hot 437 Reloading.</li> 438 <li><strong>Produktion</strong>: <code>npm run build</code> - Erstellt eine Produktionsversion im 439 Ordner <code>extension</code>.</li> 440 <li><strong>Tests</strong>: Laden Sie den Ordner <code>extension</code> als entpackte Erweiterung in 441 Chrome.</li> 442 </ol> 443 444 <h3>Testen</h3> 445 <p>Wir empfehlen, Ihre Änderungen in diesen Szenarien zu testen:</p> 446 <ol> 447 <li><strong>Verschiedene Inhaltstypen</strong>: Testen Sie mit Musik, Sprachanrufen, Videos usw. 448 </li> 449 <li><strong>Randfälle</strong>: Testen Sie mit extremen Filtereinstellungen und Kombinationen.</li> 450 <li><strong>Leistung</strong>: Stellen Sie sicher, dass Änderungen keine Audioprobleme oder hohe 451 CPU-Auslastung verursachen.</li> 452 </ol> 453 </div> 454 </section> 455 456 <section id="resources"> 457 <h2 class="en">Resources</h2> 458 <h2 class="de">Ressourcen</h2> 459 460 <div class="en"> 461 <ul> 462 <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API" target="_blank">Web 463 Audio API Documentation</a></li> 464 <li><a href="https://v3.vuejs.org/" target="_blank">Vue 3 Documentation</a></li> 465 <li><a href="https://developer.chrome.com/docs/extensions/reference/" target="_blank">Chrome 466 Extension API</a></li> 467 </ul> 468 </div> 469 470 <div class="de"> 471 <ul> 472 <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API" target="_blank">Web 473 Audio API Dokumentation</a></li> 474 <li><a href="https://v3.vuejs.org/" target="_blank">Vue 3 Dokumentation</a></li> 475 <li><a href="https://developer.chrome.com/docs/extensions/reference/" target="_blank">Chrome 476 Extension API</a></li> 477 </ul> 478 </div> 479 </section> 480 </main> 481 482 <footer> 483 <div class="container"> 484 <div class="en"> 485 <p>BrowserEQ V2 - <a href="https://github.com/berraknil/BrowserEQ-V2">GitHub Repository</a> | <a 486 href="index.html">Home</a></p> 487 </div> 488 <div class="de"> 489 <p>BrowserEQ V2 - <a href="https://github.com/berraknil/BrowserEQ-V2">GitHub-Repository</a> | <a 490 href="index.html">Startseite</a></p> 491 </div> 492 </div> 493 </footer> 494 495 <script> 496 function setLanguage(lang) { 497 if (lang === 'de') { 498 document.body.classList.add('german'); 499 document.getElementById('de-btn').classList.add('active'); 500 document.getElementById('en-btn').classList.remove('active'); 501 } else { 502 document.body.classList.remove('german'); 503 document.getElementById('en-btn').classList.add('active'); 504 document.getElementById('de-btn').classList.remove('active'); 505 } 506 } 507 </script> 508 </body> 509 510 </html>