Files
rehearshalhub/STATIC_PLAYER_DEBUG_ANALYSIS.md
2026-04-08 08:12:05 +00:00

5.5 KiB

Static Player Debug Analysis

Issue Identified

  • Player button appears in UI
  • Playback stops when changing views
  • State handling errors suspected

Architecture Review

Current Flow Analysis

1. State Initialization

  • useWaveform.ts creates WaveSurfer instance
  • Global store initialized with default values
  • State sync happens in useEffect

2. Playback State Issues

  • WaveSurfer instance destroyed when component unmounts
  • Global state may not be properly restored
  • Audio context issues when switching routes

3. Potential Weak Points

Weak Point 1: Waveform Destruction

Location: useWaveform.ts cleanup function

return () => {
  ws.destroy();  // This destroys the audio context
  wsRef.current = null;
};

Issue: When navigating away, the WaveSurfer instance is destroyed, stopping playback completely.

Weak Point 2: State Restoration Logic

Location: useWaveform.ts ready event handler

// Only restores if same song AND same band AND was playing
if (options.songId && options.bandId && 
    currentSongId === options.songId && 
    globalBandId === options.bandId && 
    globalIsPlaying) {
  ws.play();  // This may not work if audio context is suspended
}

Issue: Audio context may be suspended after route change, requiring user interaction to resume.

Weak Point 3: Global State Sync Timing

Location: State updates in audioprocess event

ws.on("audioprocess", (time) => {
  setCurrentTime(time);
  setGlobalCurrentTime(time);
  options.onTimeUpdate?.(time);
});

Issue: Local state updates may not properly sync with global state during route transitions.

Weak Point 4: Component Lifecycle

Issue: SongPage component unmounts → waveform destroyed → state lost → new component mounts with fresh state.

Root Cause Analysis

Primary Issue: Audio Context Lifecycle

  1. WaveSurfer creates an AudioContext
  2. When component unmounts, AudioContext is destroyed
  3. New component creates new AudioContext
  4. Browser requires user interaction to resume suspended audio contexts
  5. Even if we restore state, audio won't play without user interaction

Secondary Issue: State Restoration Timing

  1. Global state may be updated after component unmounts
  2. New component may mount before global state is fully updated
  3. Race condition in state restoration

Solution Architecture

  • Move WaveSurfer instance outside React component lifecycle
  • Create singleton audio service
  • Maintain audio context across route changes
  • Use global state only for UI synchronization

Option 2: Audio Context Recovery

  • Handle suspended audio context states
  • Add user interaction requirement handling
  • Implement graceful degradation

Option 3: Hybrid Approach

  • Keep minimal global state for navigation
  • Create persistent audio manager
  • Sync between audio manager and React components

Implementation Plan for Fix

Step 1: Create Audio Service (New File)

// web/src/services/audioService.ts
class AudioService {
  private static instance: AudioService;
  private wavesurfer: WaveSurfer | null = null;
  private audioContext: AudioContext | null = null;
  
  private constructor() {}
  
  public static getInstance() {
    if (!this.instance) {
      this.instance = new AudioService();
    }
    return this.instance;
  }
  
  public initialize(container: HTMLElement, url: string) {
    // Create wavesurfer with persistent audio context
  }
  
  public play() {
    // Handle suspended audio context
    if (this.audioContext?.state === 'suspended') {
      this.audioContext.resume();
    }
    this.wavesurfer?.play();
  }
  
  public cleanup() {
    // Don't destroy audio context, just disconnect nodes
  }
}

Step 2: Modify Waveform Hook

  • Use audio service instead of local WaveSurfer instance
  • Sync service state with global store
  • Handle component mount/unmount gracefully

Step 3: Update Global State Management

  • Separate audio state from UI state
  • Add audio context status tracking
  • Implement proper error handling

Step 4: Add User Interaction Handling

  • Detect suspended audio context
  • Provide UI feedback
  • Handle resume on user interaction

Debugging Steps

1. Verify Current Behavior

# Check browser console for audio context errors
# Look for "play() failed because the user didn't interact with the document first"

2. Add Debug Logging

// Add to useWaveform.ts
console.log('Waveform ready, attempting to restore state:', {
  currentSongId,
  globalBandId,
  globalIsPlaying,
  globalCurrentTime
});

// Add audio context state logging
console.log('Audio context state:', ws.backend.getAudioContext().state);

3. Test State Restoration

  • Start playback
  • Navigate away
  • Check global store state in Redux devtools
  • Navigate back
  • Verify state is restored correctly

Short-term Fix (Quick Implementation)

  1. Modify useWaveform.ts to handle suspended audio context
  2. Add user interaction requirement detection
  3. Implement graceful fallback when audio context is suspended

Long-term Fix (Robust Solution)

  1. Create persistent audio service
  2. Separate audio management from React components
  3. Implement proper audio context lifecycle management
  4. Add comprehensive error handling

Next Steps

  1. Add debug logging to identify exact failure point
  2. Implement suspended audio context handling
  3. Test state restoration with debug logs
  4. Implement persistent audio service if needed