✏️ Übung: Web Audio API

Praktische Anwendung der Web Audio API

Lernziele

In dieser Übung wirst du:

Voraussetzungen

Theoretische Grundlagen

Web Audio API Konzepte

Die Web Audio API basiert auf folgenden zentralen Konzepten:

Typische Anwendungsbereiche

Aufgabenstellung

Aufgabe 1: Einfacher Oszillator

Erstelle einen einfachen Oszillator, der einen Ton abspielt:

  1. Erstelle einen AudioContext
  2. Erstelle einen Oszillator-Node
  3. Setze die Frequenz auf 440 Hz (A4)
  4. Verbinde den Oszillator mit dem destination-Node
  5. Starte den Oszillator
  6. Stoppe den Oszillator nach 2 Sekunden
  7. Erweitere das Beispiel um eine Frequenzänderung

Aufgabe 2: Audio-Datei Wiedergabe

Lade und spiele eine Audiodatei ab:

  1. Erstelle einen AudioContext
  2. Lade eine Audiodatei (z.B. über fetch API)
  3. Decodiere die Audiodatei zu einem AudioBuffer
  4. Erstelle einen BufferSource-Node
  5. Verbinde den BufferSource mit dem destination-Node
  6. Starte die Wiedergabe
  7. Implementiere Steuerelemente (Play, Pause, Stop)

Aufgabe 3: Effekt-Hinzufügung

Füge einen Effekt zur Audio-Wiedergabe hinzu:

  1. Erstelle einen Gain-Node für Lautstärkekontrolle
  2. Erstelle einen Filter-Node (z.B. Tiefpassfilter)
  3. Erstelle einen Delay-Node
  4. Verbinde die Nodes in der richtigen Reihenfolge
  5. Implementiere Steuerelemente für die Effekt-Parameter
  6. Teste verschiedene Effekt-Kombinationen

Praktische Anwendung

1. Erstellung eines einfachen Synthesizers

Implementiere einen einfachen Web-Synthesizer:

// HTML
<div id="synth">
  <label>Frequenz: <input type="range" id="freqControl" min="50" max="1000" value="440"></label>
  <label>Lautstärke: <input type="range" id="volControl" min="0" max="1" step="0.01" value="0.5"></label>
  <button id="playBtn">Play</button>
  <button id="stopBtn">Stop</button>
</div>

// JavaScript
class SimpleSynth {
  constructor() {
    this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    this.oscillator = null;
    this.gainNode = null;
    
    this.setupControls();
  }
  
  setupControls() {
    const freqControl = document.getElementById('freqControl');
    const volControl = document.getElementById('volControl');
    const playBtn = document.getElementById('playBtn');
    const stopBtn = document.getElementById('stopBtn');
    
    freqControl.addEventListener('input', (e) => {
      if (this.oscillator) {
        this.oscillator.frequency.value = parseFloat(e.target.value);
      }
    });
    
    volControl.addEventListener('input', (e) => {
      if (this.gainNode) {
        this.gainNode.gain.value = parseFloat(e.target.value);
      }
    });
    
    playBtn.addEventListener('click', () => this.play());
    stopBtn.addEventListener('click', () => this.stop());
  }
  
  play() {
    if (this.audioCtx.state === 'suspended') {
      this.audioCtx.resume();
    }
    
    if (this.oscillator) {
      this.oscillator.stop();
    }
    
    this.oscillator = this.audioCtx.createOscillator();
    this.gainNode = this.audioCtx.createGain();
    
    const freqControl = document.getElementById('freqControl');
    const volControl = document.getElementById('volControl');
    
    this.oscillator.type = 'sine';
    this.oscillator.frequency.value = parseFloat(freqControl.value);
    this.gainNode.gain.value = parseFloat(volControl.value);
    
    this.oscillator.connect(this.gainNode);
    this.gainNode.connect(this.audioCtx.destination);
    
    this.oscillator.start();
  }
  
  stop() {
    if (this.oscillator) {
      this.oscillator.stop();
      this.oscillator = null;
    }
  }
}

// Initialisierung
document.addEventListener('DOMContentLoaded', () => {
  new SimpleSynth();
});

2. Audio-Visualisierung

Erstelle eine einfache Frequenz-Visualisierung:

// Erweitere die SimpleSynth Klasse
initVisualizer() {
  this.analyser = this.audioCtx.createAnalyser();
  this.analyser.fftSize = 2048;
  
  // Verbinde nach dem Gain-Node
  this.gainNode.connect(this.analyser);
  this.analyser.connect(this.audioCtx.destination);
  
  // Canvas für Visualisierung
  this.canvas = document.createElement('canvas');
  this.canvas.width = 600;
  this.canvas.height = 200;
  document.body.appendChild(this.canvas);
  this.canvasCtx = this.canvas.getContext('2d');
  
  this.drawVisualizer();
}

drawVisualizer() {
  if (!this.analyser) return;
  
  const bufferLength = this.analyser.frequencyBinCount;
  const dataArray = new Uint8Array(bufferLength);
  
  const draw = () => {
    requestAnimationFrame(draw);
    
    this.analyser.getByteTimeDomainData(dataArray);
    
    this.canvasCtx.fillStyle = 'rgb(0, 0, 0)';
    this.canvasCtx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    
    this.canvasCtx.lineWidth = 2;
    this.canvasCtx.strokeStyle = '#00bcd4';
    this.canvasCtx.beginPath();
    
    const sliceWidth = this.canvas.width * 1.0 / bufferLength;
    let x = 0;
    
    for (let i = 0; i < bufferLength; i++) {
      const v = dataArray[i] / 128.0;
      const y = v * this.canvas.height / 2;
      
      if (i === 0) {
        this.canvasCtx.moveTo(x, y);
      } else {
        this.canvasCtx.lineTo(x, y);
      }
      
      x += sliceWidth;
    }
    
    this.canvasCtx.lineTo(this.canvas.width, this.canvas.height / 2);
    this.canvasCtx.stroke();
  };
  
  draw();
}

Erweiterte Techniken

1. Effekt-Rack

Erstelle eine modulare Effekt-Verarbeitung:

class EffectChain {
  constructor(audioCtx) {
    this.audioCtx = audioCtx;
    this.nodes = [];
    this.input = null;
    this.output = null;
  }
  
  addEffect(effectType, params) {
    let effectNode;
    
    switch(effectType) {
      case 'filter':
        effectNode = this.audioCtx.createBiquadFilter();
        effectNode.type = params.type || 'lowpass';
        effectNode.frequency.value = params.frequency || 1000;
        break;
        
      case 'delay':
        effectNode = this.audioCtx.createDelay(params.maxDelay || 1.0);
        effectNode.delayTime.value = params.delayTime || 0.5;
        break;
        
      case 'gain':
        effectNode = this.audioCtx.createGain();
        effectNode.gain.value = params.gain || 1.0;
        break;
        
      default:
        throw new Error('Unbekannter Effekt-Typ');
    }
    
    this.nodes.push(effectNode);
    return effectNode;
  }
  
  connect(input, output) {
    let currentNode = input;
    
    for (const node of this.nodes) {
      currentNode.connect(node);
      currentNode = node;
    }
    
    currentNode.connect(output);
  }
}

2. Sample-Genauigkeit

Nutze die sample-genauen Funktionen der Web Audio API:

// Starte Audio zu einem exakten Zeitpunkt
const startTime = audioCtx.currentTime + 0.1; // Starte in 100ms
oscillator.start(startTime);

// Stoppe zu einem exakten Zeitpunkt
const stopTime = startTime + 2.0; // Stoppe nach 2 Sekunden
oscillator.stop(stopTime);

// Automatisiere Parameter zu exakten Zeitpunkten
gainNode.gain.setValueAtTime(0.5, startTime);
gainNode.gain.linearRampToValueAtTime(1.0, startTime + 0.5);
gainNode.gain.exponentialRampToValueAtTime(0.1, startTime + 2.0);

Best Practices

Performance

Browser-Kompatibilität

// Sichere Initialisierung des AudioContext
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioCtx = new AudioContext();

// Resumiere Context falls nötig
if (audioCtx.state === 'suspended') {
  await audioCtx.resume();
}

Sicherheit

Problembehandlung

Häufige Probleme

Debugging

// Debugging-Hilfe
console.log('AudioContext State:', audioCtx.state);
console.log('Sample Rate:', audioCtx.sampleRate);

// Node-Status überprüfen
if (oscillator) {
  console.log('Oscillator State:', oscillator.playbackState);
}

// Verbindungen visualisieren
function logConnections(node, prefix = '') {
  console.log(`${prefix}${node.constructor.name}`);
  // Beachte: Es gibt keine direkte Methode, um Verbindungen zu überprüfen
  // Du musst deine eigene Verwaltung implementieren
}

Auswertung und Dokumentation

Zu dokumentierende Ergebnisse

Reflexion

Beantworte folgende Fragen in deiner Dokumentation:

  1. Wie intuitiv war die Web Audio API für dich?
  2. Welche Konzepte waren besonders schwierig zu verstehen?
  3. Wie unterscheidet sich die Web Audio API von Desktop-Audio-Software?
  4. Welche Anwendungsmöglichkeiten siehst du für deine Projekte?
  5. Wie könntest du die Web Audio API in zukünftigen Web-Anwendungen einsetzen?

Vertiefung

Erweiterungsaufgaben

Du hast die Web Audio API erfolgreich in der Praxis angewandt!

→ Zurück zum Hauptmenü