WIP Working on player
This commit is contained in:
191
STATIC_PLAYER_DEBUG_ANALYSIS.md
Normal file
191
STATIC_PLAYER_DEBUG_ANALYSIS.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# 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
|
||||
```typescript
|
||||
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
|
||||
```typescript
|
||||
// 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
|
||||
```typescript
|
||||
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
|
||||
|
||||
### Option 1: Persistent Audio Context (Recommended)
|
||||
- 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)
|
||||
```typescript
|
||||
// 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
|
||||
```bash
|
||||
# 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
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
## Recommended Fix Strategy
|
||||
|
||||
### 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
|
||||
Reference in New Issue
Block a user