@djangocfg/ui-nextjs 2.1.81 → 2.1.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/tools/AudioPlayer/@refactoring3/00-IMPLEMENTATION-ROADMAP.md +1146 -0
- package/src/tools/AudioPlayer/@refactoring3/01-WAVESURFER-STREAMING-ANALYSIS.md +611 -0
- package/src/tools/AudioPlayer/@refactoring3/02-MEDIA-VIEWER-ANALYSIS.md +560 -0
- package/src/tools/AudioPlayer/@refactoring3/03-HYBRID-ARCHITECTURE-PROPOSAL.md +769 -0
- package/src/tools/AudioPlayer/@refactoring3/04-CRACKLING-ISSUE-DIAGNOSIS.md +373 -0
- package/src/tools/AudioPlayer/README.md +177 -205
- package/src/tools/AudioPlayer/components/AudioPlayer.tsx +9 -4
- package/src/tools/AudioPlayer/components/HybridAudioPlayer.tsx +251 -0
- package/src/tools/AudioPlayer/components/HybridSimplePlayer.tsx +291 -0
- package/src/tools/AudioPlayer/components/HybridWaveform.tsx +279 -0
- package/src/tools/AudioPlayer/components/SimpleAudioPlayer.tsx +16 -26
- package/src/tools/AudioPlayer/components/index.ts +6 -1
- package/src/tools/AudioPlayer/context/AudioProvider.tsx +16 -8
- package/src/tools/AudioPlayer/context/HybridAudioProvider.tsx +121 -0
- package/src/tools/AudioPlayer/context/index.ts +14 -2
- package/src/tools/AudioPlayer/hooks/index.ts +11 -0
- package/src/tools/AudioPlayer/hooks/useHybridAudio.ts +387 -0
- package/src/tools/AudioPlayer/hooks/useHybridAudioAnalysis.ts +95 -0
- package/src/tools/AudioPlayer/hooks/useSharedWebAudio.ts +6 -3
- package/src/tools/AudioPlayer/index.ts +31 -0
- package/src/tools/AudioPlayer/progressive/ProgressiveAudioPlayer.tsx +8 -0
- package/src/tools/ImageViewer/hooks/useImageLoading.ts +33 -9
- package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +13 -6
- package/src/tools/VideoPlayer/providers/StreamProvider.tsx +38 -22
- package/src/tools/index.ts +22 -0
- package/src/tools/AudioPlayer/@refactoring/00-PLAN.md +0 -148
- package/src/tools/AudioPlayer/@refactoring/01-TYPES.md +0 -301
- package/src/tools/AudioPlayer/@refactoring/02-HOOKS.md +0 -281
- package/src/tools/AudioPlayer/@refactoring/03-CONTEXT.md +0 -328
- package/src/tools/AudioPlayer/@refactoring/04-COMPONENTS.md +0 -251
- package/src/tools/AudioPlayer/@refactoring/05-EFFECTS.md +0 -427
- package/src/tools/AudioPlayer/@refactoring/06-UTILS-AND-INDEX.md +0 -193
- package/src/tools/AudioPlayer/@refactoring/07-EXECUTION-CHECKLIST.md +0 -146
- package/src/tools/AudioPlayer/@refactoring2/ISSUE_ANALYSIS.md +0 -187
- package/src/tools/AudioPlayer/@refactoring2/PLAN.md +0 -372
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
# Hybrid Audio Player Architecture Proposal
|
|
2
|
+
|
|
3
|
+
**Date:** 2025-12-30
|
|
4
|
+
**Status:** Proposal
|
|
5
|
+
**Goal:** Combine native HTML5 audio playback with Web Audio API visualization and reactive effects
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Executive Summary
|
|
10
|
+
|
|
11
|
+
This proposal outlines a hybrid architecture that:
|
|
12
|
+
1. Uses **native HTML5 `<audio>` element** for playback (eliminates crackling issues)
|
|
13
|
+
2. Connects **Web Audio API AnalyserNode** to the audio element for real-time visualization
|
|
14
|
+
3. Preserves all **reactive cover effects** (glow, orbs, spotlight, mesh)
|
|
15
|
+
4. Supports **chunked/streaming audio** with proper Range request handling
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Problem Statement
|
|
20
|
+
|
|
21
|
+
### Current Issues
|
|
22
|
+
|
|
23
|
+
1. **WaveSurfer.js limitations with streaming:**
|
|
24
|
+
- Requires full file download before seek works properly
|
|
25
|
+
- Seek beyond buffered content (>2 minutes for 2MB chunks) breaks playback
|
|
26
|
+
- After failed seek, player state becomes corrupted
|
|
27
|
+
|
|
28
|
+
2. **Audio crackling in pure Web Audio approach:**
|
|
29
|
+
- Direct AudioBufferSourceNode playback can cause crackling
|
|
30
|
+
- Manual buffer scheduling is complex and error-prone
|
|
31
|
+
- GC pauses during buffer transitions cause audible artifacts
|
|
32
|
+
|
|
33
|
+
3. **Architecture complexity:**
|
|
34
|
+
- Current code has two separate players (WaveSurfer-based and Progressive)
|
|
35
|
+
- Duplication of state management and controls
|
|
36
|
+
- Reactive effects tightly coupled to WaveSurfer implementation
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Architecture Overview
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
HYBRID AUDIO PLAYER ARCHITECTURE
|
|
44
|
+
+===============================================================================+
|
|
45
|
+
| |
|
|
46
|
+
| +-------------------------------------------------------------------------+ |
|
|
47
|
+
| | HybridAudioProvider | |
|
|
48
|
+
| | (React Context - manages all state and coordinates components) | |
|
|
49
|
+
| +-------------------------------------------------------------------------+ |
|
|
50
|
+
| | |
|
|
51
|
+
| +---------------------------+---------------------------+ |
|
|
52
|
+
| | | | |
|
|
53
|
+
| v v v |
|
|
54
|
+
| +---------------+ +----------------+ +------------------+ |
|
|
55
|
+
| | PLAYBACK | | VISUALIZATION | | REACTIVE FX | |
|
|
56
|
+
| | LAYER | | LAYER | | LAYER | |
|
|
57
|
+
| +---------------+ +----------------+ +------------------+ |
|
|
58
|
+
| | | | | | | |
|
|
59
|
+
| | HTML5 <audio> | ---> | MediaElement | ---> | useAudioAnalysis | |
|
|
60
|
+
| | (native) | | SourceNode | | (bass/mid/high) | |
|
|
61
|
+
| | | | | | | | | |
|
|
62
|
+
| | - Range req | | v | | v | |
|
|
63
|
+
| | - Buffering | | AnalyserNode | | AudioReactive | |
|
|
64
|
+
| | - Seek | | | | | Cover | |
|
|
65
|
+
| | - No crackling| | v | | - GlowEffect | |
|
|
66
|
+
| +-------+-------+ | Destination | | - OrbsEffect | |
|
|
67
|
+
| | +-------+--------+ | - SpotlightEffect| |
|
|
68
|
+
| | | | - MeshEffect | |
|
|
69
|
+
| v v +------------------+ |
|
|
70
|
+
| +-------+-------+ +-------+--------+ |
|
|
71
|
+
| | useAudioState | | useSharedWeb | |
|
|
72
|
+
| | - currentTime | | Audio | |
|
|
73
|
+
| | - duration | | (singleton | |
|
|
74
|
+
| | - buffered | | context) | |
|
|
75
|
+
| | - isPlaying | +----------------+ |
|
|
76
|
+
| +---------------+ |
|
|
77
|
+
| |
|
|
78
|
+
+===============================================================================+
|
|
79
|
+
|
|
80
|
+
DATA FLOW DIAGRAM
|
|
81
|
+
|
|
82
|
+
+-----------+ +-----------+ +--------------+ +---------------+
|
|
83
|
+
| Audio | | Browser | | Audio | | React State |
|
|
84
|
+
| Source | --> | Handles | --> | Element | --> | Updates |
|
|
85
|
+
| (URL) | | Range | | Events | | (60fps) |
|
|
86
|
+
+-----------+ | Requests | +--------------+ +---------------+
|
|
87
|
+
+-----------+ |
|
|
88
|
+
v
|
|
89
|
+
+--------------------------------------------------+
|
|
90
|
+
| Web Audio Graph |
|
|
91
|
+
| |
|
|
92
|
+
| [MediaElementSource] --> [AnalyserNode] --> [Destination]
|
|
93
|
+
| | |
|
|
94
|
+
| v |
|
|
95
|
+
| [getByteFrequencyData] |
|
|
96
|
+
| | |
|
|
97
|
+
| v |
|
|
98
|
+
| [AudioLevels State] |
|
|
99
|
+
| { bass, mid, high, overall }
|
|
100
|
+
+--------------------------------------------------+
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Key Components
|
|
106
|
+
|
|
107
|
+
### 1. HybridAudioProvider (Context)
|
|
108
|
+
|
|
109
|
+
**File:** `context/HybridAudioProvider.tsx`
|
|
110
|
+
|
|
111
|
+
**Responsibilities:**
|
|
112
|
+
- Manage audio element lifecycle
|
|
113
|
+
- Create and maintain Web Audio graph
|
|
114
|
+
- Provide unified state to all consumers
|
|
115
|
+
- Handle playback controls
|
|
116
|
+
- Coordinate visualization and effects
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
interface HybridAudioContextState {
|
|
120
|
+
// Core instances
|
|
121
|
+
audioElement: HTMLAudioElement | null;
|
|
122
|
+
audioContext: AudioContext | null;
|
|
123
|
+
sourceNode: MediaElementAudioSourceNode | null;
|
|
124
|
+
analyserNode: AnalyserNode | null;
|
|
125
|
+
|
|
126
|
+
// Playback state
|
|
127
|
+
isReady: boolean;
|
|
128
|
+
isPlaying: boolean;
|
|
129
|
+
currentTime: number;
|
|
130
|
+
duration: number;
|
|
131
|
+
volume: number;
|
|
132
|
+
isMuted: boolean;
|
|
133
|
+
isLooping: boolean;
|
|
134
|
+
buffered: TimeRanges | null;
|
|
135
|
+
|
|
136
|
+
// Audio analysis (for reactive effects)
|
|
137
|
+
audioLevels: AudioLevels;
|
|
138
|
+
|
|
139
|
+
// Actions
|
|
140
|
+
play: () => Promise<void>;
|
|
141
|
+
pause: () => void;
|
|
142
|
+
togglePlay: () => void;
|
|
143
|
+
seek: (time: number) => void;
|
|
144
|
+
seekTo: (progress: number) => void;
|
|
145
|
+
skip: (seconds: number) => void;
|
|
146
|
+
setVolume: (vol: number) => void;
|
|
147
|
+
toggleMute: () => void;
|
|
148
|
+
toggleLoop: () => void;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 2. useHybridAudio (Core Hook)
|
|
153
|
+
|
|
154
|
+
**File:** `hooks/useHybridAudio.ts`
|
|
155
|
+
|
|
156
|
+
**Responsibilities:**
|
|
157
|
+
- Create HTML5 audio element
|
|
158
|
+
- Connect to Web Audio API for analysis only (not playback)
|
|
159
|
+
- Extract frequency data for visualizations
|
|
160
|
+
- Handle streaming/chunked audio sources
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
interface UseHybridAudioOptions {
|
|
164
|
+
src: string;
|
|
165
|
+
autoPlay?: boolean;
|
|
166
|
+
initialVolume?: number;
|
|
167
|
+
loop?: boolean;
|
|
168
|
+
crossOrigin?: 'anonymous' | 'use-credentials';
|
|
169
|
+
|
|
170
|
+
// Callbacks
|
|
171
|
+
onPlay?: () => void;
|
|
172
|
+
onPause?: () => void;
|
|
173
|
+
onEnded?: () => void;
|
|
174
|
+
onTimeUpdate?: (time: number) => void;
|
|
175
|
+
onError?: (error: Error) => void;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface UseHybridAudioReturn {
|
|
179
|
+
// Refs
|
|
180
|
+
audioRef: RefObject<HTMLAudioElement>;
|
|
181
|
+
|
|
182
|
+
// State
|
|
183
|
+
state: AudioState;
|
|
184
|
+
|
|
185
|
+
// Controls
|
|
186
|
+
controls: AudioControls;
|
|
187
|
+
|
|
188
|
+
// Web Audio for visualization
|
|
189
|
+
webAudio: {
|
|
190
|
+
context: AudioContext | null;
|
|
191
|
+
analyser: AnalyserNode | null;
|
|
192
|
+
createAnalyser: (options?: AnalyserOptions) => AnalyserNode | null;
|
|
193
|
+
disconnectAnalyser: (analyser: AnalyserNode) => void;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 3. useAudioAnalysis (Visualization Hook)
|
|
199
|
+
|
|
200
|
+
**File:** `hooks/useAudioAnalysis.ts` (existing, minor updates)
|
|
201
|
+
|
|
202
|
+
**Responsibilities:**
|
|
203
|
+
- Read frequency data from AnalyserNode
|
|
204
|
+
- Calculate bass, mid, high, and overall levels
|
|
205
|
+
- Provide smoothed values for animations
|
|
206
|
+
- Run at 60fps during playback
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// Input: analyser node from useHybridAudio
|
|
210
|
+
// Output: AudioLevels { bass, mid, high, overall }
|
|
211
|
+
|
|
212
|
+
// Frequency bands:
|
|
213
|
+
// - Bass: 0-15% of spectrum (sub-bass to low-mids)
|
|
214
|
+
// - Mids: 15-50% of spectrum (vocals, instruments)
|
|
215
|
+
// - Highs: 50-100% of spectrum (cymbals, harmonics)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 4. WaveformVisualization (Component)
|
|
219
|
+
|
|
220
|
+
**File:** `components/WaveformVisualization.tsx`
|
|
221
|
+
|
|
222
|
+
**Responsibilities:**
|
|
223
|
+
- Real-time waveform rendering using AnalyserNode
|
|
224
|
+
- Progress bar with buffered regions
|
|
225
|
+
- Click-to-seek functionality
|
|
226
|
+
- Responsive canvas rendering
|
|
227
|
+
|
|
228
|
+
**Two Modes:**
|
|
229
|
+
1. **Frequency Bars Mode:** Real-time frequency visualization (like current equalizer)
|
|
230
|
+
2. **Waveform Mode:** Pre-computed peaks (using Progressive player's approach)
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
interface WaveformVisualizationProps {
|
|
234
|
+
mode: 'frequency' | 'waveform';
|
|
235
|
+
|
|
236
|
+
// For waveform mode (pre-computed)
|
|
237
|
+
peaks?: number[];
|
|
238
|
+
loadedPercent?: number;
|
|
239
|
+
|
|
240
|
+
// Shared props
|
|
241
|
+
currentTime: number;
|
|
242
|
+
duration: number;
|
|
243
|
+
buffered: TimeRanges | null;
|
|
244
|
+
onSeek: (time: number) => void;
|
|
245
|
+
|
|
246
|
+
// Styling
|
|
247
|
+
style?: WaveformStyle;
|
|
248
|
+
className?: string;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### 5. AudioReactiveCover (Existing Component)
|
|
253
|
+
|
|
254
|
+
**File:** `components/ReactiveCover/AudioReactiveCover.tsx` (existing)
|
|
255
|
+
|
|
256
|
+
**No changes needed** - already consumes `audioLevels` from context.
|
|
257
|
+
|
|
258
|
+
Effects preserved:
|
|
259
|
+
- **GlowEffect:** Multi-layer glow responding to bass/mid/high
|
|
260
|
+
- **OrbsEffect:** Floating orbs with position/size based on frequencies
|
|
261
|
+
- **SpotlightEffect:** Rotating spotlight with color intensity
|
|
262
|
+
- **MeshEffect:** Morphing mesh gradients
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Web Audio Connection Architecture
|
|
267
|
+
|
|
268
|
+
### The Key Insight
|
|
269
|
+
|
|
270
|
+
HTML5 `<audio>` handles playback natively while Web Audio API provides analysis capabilities:
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
+-------------------+
|
|
274
|
+
| HTML5 <audio> | <-- Browser handles all playback
|
|
275
|
+
| - src loading | - Range requests
|
|
276
|
+
| - buffering | - Decoding
|
|
277
|
+
| - playback | - No crackling
|
|
278
|
+
+--------+----------+
|
|
279
|
+
|
|
|
280
|
+
| createMediaElementSource()
|
|
281
|
+
| (connects once, never disconnect)
|
|
282
|
+
v
|
|
283
|
+
+--------+----------+
|
|
284
|
+
| MediaElementSource| <-- Bridge between HTML5 audio and Web Audio
|
|
285
|
+
+--------+----------+
|
|
286
|
+
|
|
|
287
|
+
| connect()
|
|
288
|
+
v
|
|
289
|
+
+--------+----------+
|
|
290
|
+
| AnalyserNode | <-- For visualization only
|
|
291
|
+
| - fftSize: 256 | - getByteFrequencyData()
|
|
292
|
+
| - smoothing: 0.8| - getByteTimeDomainData()
|
|
293
|
+
+--------+----------+
|
|
294
|
+
|
|
|
295
|
+
| connect()
|
|
296
|
+
v
|
|
297
|
+
+--------+----------+
|
|
298
|
+
| AudioDestination | <-- Audio output (speakers)
|
|
299
|
+
+-------------------+
|
|
300
|
+
|
|
301
|
+
IMPORTANT: Audio flows THROUGH analyser to destination
|
|
302
|
+
This preserves audio output while enabling analysis
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Code Implementation
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
function connectWebAudio(audioElement: HTMLAudioElement): WebAudioGraph {
|
|
309
|
+
// Create or reuse AudioContext
|
|
310
|
+
const AudioContextClass = window.AudioContext ||
|
|
311
|
+
(window as any).webkitAudioContext;
|
|
312
|
+
const audioContext = new AudioContextClass();
|
|
313
|
+
|
|
314
|
+
// CRITICAL: Only create MediaElementSourceNode ONCE per audio element
|
|
315
|
+
// Attempting to create multiple sources throws InvalidStateError
|
|
316
|
+
const sourceNode = audioContext.createMediaElementSource(audioElement);
|
|
317
|
+
|
|
318
|
+
// Create analyser for visualization
|
|
319
|
+
const analyserNode = audioContext.createAnalyser();
|
|
320
|
+
analyserNode.fftSize = 256;
|
|
321
|
+
analyserNode.smoothingTimeConstant = 0.8;
|
|
322
|
+
|
|
323
|
+
// Connect the graph: source -> analyser -> destination
|
|
324
|
+
sourceNode.connect(analyserNode);
|
|
325
|
+
analyserNode.connect(audioContext.destination);
|
|
326
|
+
|
|
327
|
+
return { audioContext, sourceNode, analyserNode };
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Handling AudioContext Suspension
|
|
332
|
+
|
|
333
|
+
Browsers require user interaction before AudioContext can start:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
useEffect(() => {
|
|
337
|
+
const handleUserInteraction = async () => {
|
|
338
|
+
if (audioContext?.state === 'suspended') {
|
|
339
|
+
await audioContext.resume();
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// Resume on first play
|
|
344
|
+
audioElement?.addEventListener('play', handleUserInteraction);
|
|
345
|
+
|
|
346
|
+
return () => {
|
|
347
|
+
audioElement?.removeEventListener('play', handleUserInteraction);
|
|
348
|
+
};
|
|
349
|
+
}, [audioContext, audioElement]);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Streaming Audio Support
|
|
355
|
+
|
|
356
|
+
### How HTML5 Audio Handles Streaming
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
Browser Request: Server Response:
|
|
360
|
+
+------------------+ +------------------+
|
|
361
|
+
| GET /audio.mp3 | | 206 Partial |
|
|
362
|
+
| Range: bytes=0- | --> | Content-Range: |
|
|
363
|
+
+------------------+ | bytes 0-2MB/10MB |
|
|
364
|
+
+------------------+
|
|
365
|
+
|
|
366
|
+
Seek Request: Server Response:
|
|
367
|
+
+------------------+ +------------------+
|
|
368
|
+
| GET /audio.mp3 | | 206 Partial |
|
|
369
|
+
| Range: bytes=5MB-| --> | Content-Range: |
|
|
370
|
+
+------------------+ | bytes 5MB-7MB/ |
|
|
371
|
+
+------------------+
|
|
372
|
+
|
|
373
|
+
The browser automatically:
|
|
374
|
+
1. Requests chunks as needed
|
|
375
|
+
2. Buffers ahead for smooth playback
|
|
376
|
+
3. Handles seek by requesting new ranges
|
|
377
|
+
4. Reports buffered ranges via audio.buffered
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Visualizing Buffered Regions
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
function renderBufferedRegions(
|
|
384
|
+
ctx: CanvasRenderingContext2D,
|
|
385
|
+
buffered: TimeRanges,
|
|
386
|
+
duration: number,
|
|
387
|
+
width: number,
|
|
388
|
+
height: number
|
|
389
|
+
) {
|
|
390
|
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
|
|
391
|
+
|
|
392
|
+
for (let i = 0; i < buffered.length; i++) {
|
|
393
|
+
const start = (buffered.start(i) / duration) * width;
|
|
394
|
+
const end = (buffered.end(i) / duration) * width;
|
|
395
|
+
ctx.fillRect(start, height - 2, end - start, 2);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Implementation Steps
|
|
403
|
+
|
|
404
|
+
### Phase 1: Core Infrastructure
|
|
405
|
+
|
|
406
|
+
**Files to create:**
|
|
407
|
+
- `hooks/useHybridAudio.ts`
|
|
408
|
+
- `context/HybridAudioProvider.tsx`
|
|
409
|
+
|
|
410
|
+
**Tasks:**
|
|
411
|
+
1. Create `useHybridAudio` hook combining HTML5 audio + Web Audio API
|
|
412
|
+
2. Build `HybridAudioProvider` context wrapper
|
|
413
|
+
3. Integrate with existing `useSharedWebAudio` pattern (singleton source)
|
|
414
|
+
4. Add comprehensive event handling (timeupdate, progress, seeking, etc.)
|
|
415
|
+
|
|
416
|
+
### Phase 2: Visualization Integration
|
|
417
|
+
|
|
418
|
+
**Files to modify:**
|
|
419
|
+
- `hooks/useAudioAnalysis.ts` (minor updates)
|
|
420
|
+
- `components/AudioEqualizer.tsx` (connect to new context)
|
|
421
|
+
|
|
422
|
+
**Files to create:**
|
|
423
|
+
- `components/HybridWaveform.tsx`
|
|
424
|
+
|
|
425
|
+
**Tasks:**
|
|
426
|
+
1. Update `useAudioAnalysis` to accept hybrid context
|
|
427
|
+
2. Create `HybridWaveform` component with dual modes
|
|
428
|
+
3. Integrate with existing canvas rendering from Progressive player
|
|
429
|
+
|
|
430
|
+
### Phase 3: Player Component
|
|
431
|
+
|
|
432
|
+
**Files to create:**
|
|
433
|
+
- `components/HybridAudioPlayer.tsx`
|
|
434
|
+
- `components/HybridSimplePlayer.tsx`
|
|
435
|
+
|
|
436
|
+
**Tasks:**
|
|
437
|
+
1. Build main `HybridAudioPlayer` component
|
|
438
|
+
2. Create simplified `HybridSimplePlayer` wrapper
|
|
439
|
+
3. Integrate all UI controls (play, pause, seek, volume, loop)
|
|
440
|
+
4. Add keyboard shortcuts support
|
|
441
|
+
|
|
442
|
+
### Phase 4: Reactive Cover Integration
|
|
443
|
+
|
|
444
|
+
**Files to modify:**
|
|
445
|
+
- `components/ReactiveCover/AudioReactiveCover.tsx` (minimal changes)
|
|
446
|
+
|
|
447
|
+
**Tasks:**
|
|
448
|
+
1. Verify reactive effects work with new context
|
|
449
|
+
2. Test all effect variants (glow, orbs, spotlight, mesh)
|
|
450
|
+
3. Ensure smooth 60fps animations
|
|
451
|
+
|
|
452
|
+
### Phase 5: Migration & Testing
|
|
453
|
+
|
|
454
|
+
**Tasks:**
|
|
455
|
+
1. Create migration guide from WaveSurfer-based player
|
|
456
|
+
2. Update all usages in application
|
|
457
|
+
3. Performance testing with various audio formats/sizes
|
|
458
|
+
4. Cross-browser testing (Chrome, Firefox, Safari, Edge)
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## Component Hierarchy
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
HybridSimplePlayer
|
|
466
|
+
|
|
|
467
|
+
+-- HybridAudioProvider (context)
|
|
468
|
+
|
|
|
469
|
+
+-- AudioReactiveCover (optional)
|
|
470
|
+
| |
|
|
471
|
+
| +-- GlowEffect / OrbsEffect / SpotlightEffect / MeshEffect
|
|
472
|
+
| |
|
|
473
|
+
| +-- Cover Art (children)
|
|
474
|
+
|
|
|
475
|
+
+-- HybridAudioPlayer
|
|
476
|
+
|
|
|
477
|
+
+-- HybridWaveform (canvas visualization)
|
|
478
|
+
|
|
|
479
|
+
+-- Timer (current / duration)
|
|
480
|
+
|
|
|
481
|
+
+-- Controls
|
|
482
|
+
|
|
|
483
|
+
+-- PlayPauseButton
|
|
484
|
+
+-- SkipButtons
|
|
485
|
+
+-- VolumeControl
|
|
486
|
+
+-- LoopToggle
|
|
487
|
+
+-- ShortcutsHelp
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## File Structure
|
|
493
|
+
|
|
494
|
+
```
|
|
495
|
+
AudioPlayer/
|
|
496
|
+
+-- @refactoring3/
|
|
497
|
+
| +-- 03-HYBRID-ARCHITECTURE-PROPOSAL.md (this file)
|
|
498
|
+
|
|
|
499
|
+
+-- context/
|
|
500
|
+
| +-- AudioProvider.tsx (existing - WaveSurfer)
|
|
501
|
+
| +-- HybridAudioProvider.tsx (NEW)
|
|
502
|
+
| +-- index.ts
|
|
503
|
+
|
|
|
504
|
+
+-- hooks/
|
|
505
|
+
| +-- useSharedWebAudio.ts (existing - reuse pattern)
|
|
506
|
+
| +-- useAudioAnalysis.ts (existing - minor updates)
|
|
507
|
+
| +-- useHybridAudio.ts (NEW)
|
|
508
|
+
| +-- useAudioHotkeys.ts (existing - reuse)
|
|
509
|
+
| +-- index.ts
|
|
510
|
+
|
|
|
511
|
+
+-- components/
|
|
512
|
+
| +-- AudioPlayer.tsx (existing - WaveSurfer)
|
|
513
|
+
| +-- SimpleAudioPlayer.tsx (existing - WaveSurfer)
|
|
514
|
+
| +-- HybridAudioPlayer.tsx (NEW)
|
|
515
|
+
| +-- HybridSimplePlayer.tsx (NEW)
|
|
516
|
+
| +-- HybridWaveform.tsx (NEW)
|
|
517
|
+
| +-- AudioEqualizer.tsx (existing)
|
|
518
|
+
| +-- ReactiveCover/ (existing - no changes)
|
|
519
|
+
| +-- index.ts
|
|
520
|
+
|
|
|
521
|
+
+-- progressive/
|
|
522
|
+
| +-- ProgressiveAudioPlayer.tsx (existing - can be deprecated later)
|
|
523
|
+
| +-- useAudioElement.ts (existing - reference for useHybridAudio)
|
|
524
|
+
| +-- WaveformCanvas.tsx (existing - reuse for HybridWaveform)
|
|
525
|
+
| +-- ...
|
|
526
|
+
|
|
|
527
|
+
+-- types/
|
|
528
|
+
| +-- audio.ts (extend with hybrid types)
|
|
529
|
+
| +-- index.ts
|
|
530
|
+
|
|
|
531
|
+
+-- effects/
|
|
532
|
+
| +-- index.ts (existing - no changes)
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## API Design
|
|
538
|
+
|
|
539
|
+
### Basic Usage
|
|
540
|
+
|
|
541
|
+
```tsx
|
|
542
|
+
import { HybridSimplePlayer } from '@djangocfg/ui-nextjs/tools/AudioPlayer';
|
|
543
|
+
|
|
544
|
+
function MyComponent() {
|
|
545
|
+
return (
|
|
546
|
+
<HybridSimplePlayer
|
|
547
|
+
src="https://example.com/stream/audio.mp3"
|
|
548
|
+
title="Track Title"
|
|
549
|
+
artist="Artist Name"
|
|
550
|
+
coverArt="/path/to/cover.jpg"
|
|
551
|
+
reactiveCover
|
|
552
|
+
variant="spotlight"
|
|
553
|
+
/>
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### Advanced Usage with Context
|
|
559
|
+
|
|
560
|
+
```tsx
|
|
561
|
+
import {
|
|
562
|
+
HybridAudioProvider,
|
|
563
|
+
HybridAudioPlayer,
|
|
564
|
+
AudioReactiveCover,
|
|
565
|
+
useHybridAudio
|
|
566
|
+
} from '@djangocfg/ui-nextjs/tools/AudioPlayer';
|
|
567
|
+
|
|
568
|
+
function CustomPlayer({ src }) {
|
|
569
|
+
const containerRef = useRef(null);
|
|
570
|
+
|
|
571
|
+
return (
|
|
572
|
+
<HybridAudioProvider src={src}>
|
|
573
|
+
<div className="flex gap-4">
|
|
574
|
+
<AudioReactiveCover variant="orbs">
|
|
575
|
+
<img src={coverUrl} alt="Cover" />
|
|
576
|
+
</AudioReactiveCover>
|
|
577
|
+
|
|
578
|
+
<HybridAudioPlayer
|
|
579
|
+
ref={containerRef}
|
|
580
|
+
showWaveform
|
|
581
|
+
showEqualizer={false}
|
|
582
|
+
/>
|
|
583
|
+
</div>
|
|
584
|
+
</HybridAudioProvider>
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Using Hooks Directly
|
|
590
|
+
|
|
591
|
+
```tsx
|
|
592
|
+
import { useHybridAudio, useAudioAnalysis } from '@djangocfg/ui-nextjs/tools/AudioPlayer';
|
|
593
|
+
|
|
594
|
+
function CustomVisualization({ src }) {
|
|
595
|
+
const { audioRef, state, controls, webAudio } = useHybridAudio({ src });
|
|
596
|
+
const levels = useAudioAnalysis(webAudio, state.isPlaying);
|
|
597
|
+
|
|
598
|
+
return (
|
|
599
|
+
<div>
|
|
600
|
+
<audio ref={audioRef} />
|
|
601
|
+
<CustomCanvas levels={levels} />
|
|
602
|
+
<button onClick={controls.togglePlay}>
|
|
603
|
+
{state.isPlaying ? 'Pause' : 'Play'}
|
|
604
|
+
</button>
|
|
605
|
+
</div>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
## Migration Path
|
|
613
|
+
|
|
614
|
+
### From WaveSurfer-based Player
|
|
615
|
+
|
|
616
|
+
```tsx
|
|
617
|
+
// Before (WaveSurfer)
|
|
618
|
+
<SimpleAudioPlayer
|
|
619
|
+
src={url}
|
|
620
|
+
showWaveform
|
|
621
|
+
reactiveCover
|
|
622
|
+
variant="spotlight"
|
|
623
|
+
/>
|
|
624
|
+
|
|
625
|
+
// After (Hybrid)
|
|
626
|
+
<HybridSimplePlayer
|
|
627
|
+
src={url}
|
|
628
|
+
showWaveform
|
|
629
|
+
reactiveCover
|
|
630
|
+
variant="spotlight"
|
|
631
|
+
/>
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### From Progressive Player
|
|
635
|
+
|
|
636
|
+
```tsx
|
|
637
|
+
// Before (Progressive)
|
|
638
|
+
<ProgressiveAudioPlayer
|
|
639
|
+
src={streamUrl}
|
|
640
|
+
showWaveform
|
|
641
|
+
/>
|
|
642
|
+
|
|
643
|
+
// After (Hybrid)
|
|
644
|
+
<HybridSimplePlayer
|
|
645
|
+
src={streamUrl}
|
|
646
|
+
showWaveform
|
|
647
|
+
waveformMode="frequency" // or "waveform" for pre-computed peaks
|
|
648
|
+
/>
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## Performance Considerations
|
|
654
|
+
|
|
655
|
+
### Memory Usage
|
|
656
|
+
|
|
657
|
+
| Component | Memory Impact |
|
|
658
|
+
|-----------|--------------|
|
|
659
|
+
| HTML5 Audio | Browser-managed buffering (~5-20MB depending on format) |
|
|
660
|
+
| Web Audio Context | ~2-4KB per context |
|
|
661
|
+
| AnalyserNode | ~1KB + fftSize bytes for data arrays |
|
|
662
|
+
| Canvas | width * height * 4 bytes (RGBA) |
|
|
663
|
+
| Reactive Effects | Minimal (CSS transforms) |
|
|
664
|
+
|
|
665
|
+
### CPU Usage
|
|
666
|
+
|
|
667
|
+
| Operation | Frequency | Cost |
|
|
668
|
+
|-----------|-----------|------|
|
|
669
|
+
| Audio decoding | On-demand | Browser-optimized |
|
|
670
|
+
| getByteFrequencyData | 60fps during playback | ~0.1ms |
|
|
671
|
+
| Canvas render | 60fps during playback | ~1-2ms |
|
|
672
|
+
| React state updates | 60fps during playback | ~0.5ms |
|
|
673
|
+
| Effect calculations | 60fps during playback | ~0.2ms |
|
|
674
|
+
|
|
675
|
+
### Optimization Tips
|
|
676
|
+
|
|
677
|
+
1. **Throttle state updates:** Use `requestAnimationFrame` for visualization updates
|
|
678
|
+
2. **Avoid re-renders:** Memoize effect calculations, use refs for animation data
|
|
679
|
+
3. **Canvas optimization:** Use `willReadFrequently: true` for 2D context
|
|
680
|
+
4. **Web Audio:** Reuse AudioContext (singleton pattern)
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## Browser Compatibility
|
|
685
|
+
|
|
686
|
+
| Feature | Chrome | Firefox | Safari | Edge |
|
|
687
|
+
|---------|--------|---------|--------|------|
|
|
688
|
+
| HTML5 Audio | Yes | Yes | Yes | Yes |
|
|
689
|
+
| Range Requests | Yes | Yes | Yes | Yes |
|
|
690
|
+
| AudioContext | Yes | Yes | Yes (webkit) | Yes |
|
|
691
|
+
| MediaElementSource | Yes | Yes | Yes | Yes |
|
|
692
|
+
| AnalyserNode | Yes | Yes | Yes | Yes |
|
|
693
|
+
| requestAnimationFrame | Yes | Yes | Yes | Yes |
|
|
694
|
+
|
|
695
|
+
**Note:** Safari requires `webkitAudioContext` prefix in older versions.
|
|
696
|
+
|
|
697
|
+
---
|
|
698
|
+
|
|
699
|
+
## Testing Strategy
|
|
700
|
+
|
|
701
|
+
### Unit Tests
|
|
702
|
+
|
|
703
|
+
1. `useHybridAudio` hook behavior
|
|
704
|
+
2. Audio state transitions
|
|
705
|
+
3. Web Audio graph creation
|
|
706
|
+
4. Frequency data extraction
|
|
707
|
+
|
|
708
|
+
### Integration Tests
|
|
709
|
+
|
|
710
|
+
1. Full player component rendering
|
|
711
|
+
2. Play/pause/seek operations
|
|
712
|
+
3. Volume control
|
|
713
|
+
4. Reactive effects updates
|
|
714
|
+
|
|
715
|
+
### E2E Tests
|
|
716
|
+
|
|
717
|
+
1. Streaming audio playback
|
|
718
|
+
2. Seek beyond buffered content
|
|
719
|
+
3. Cross-browser playback
|
|
720
|
+
4. Mobile device support
|
|
721
|
+
|
|
722
|
+
### Manual Testing Scenarios
|
|
723
|
+
|
|
724
|
+
1. Play audio from streaming URL
|
|
725
|
+
2. Seek to unbuffered position
|
|
726
|
+
3. Verify no crackling during playback
|
|
727
|
+
4. Verify reactive effects respond to music
|
|
728
|
+
5. Test with various audio formats (MP3, AAC, OGG)
|
|
729
|
+
6. Test with different file sizes (1MB - 100MB)
|
|
730
|
+
|
|
731
|
+
---
|
|
732
|
+
|
|
733
|
+
## Risks and Mitigations
|
|
734
|
+
|
|
735
|
+
| Risk | Likelihood | Impact | Mitigation |
|
|
736
|
+
|------|------------|--------|------------|
|
|
737
|
+
| Safari AudioContext issues | Medium | High | Fallback to webkit prefix, test thoroughly |
|
|
738
|
+
| CORS issues with streaming | Medium | Medium | Ensure crossOrigin="anonymous" and server headers |
|
|
739
|
+
| Memory leaks with context | Low | Medium | Proper cleanup in useEffect, singleton pattern |
|
|
740
|
+
| Performance on mobile | Medium | Medium | Reduce fftSize, lower frame rate on mobile |
|
|
741
|
+
| Breaking existing integrations | Medium | High | Provide migration guide, keep old components |
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
## Conclusion
|
|
746
|
+
|
|
747
|
+
This hybrid architecture provides the best of both worlds:
|
|
748
|
+
|
|
749
|
+
1. **Native HTML5 Audio for Playback:**
|
|
750
|
+
- No crackling or audio glitches
|
|
751
|
+
- Browser-optimized decoding and buffering
|
|
752
|
+
- Native Range request handling for streaming
|
|
753
|
+
|
|
754
|
+
2. **Web Audio API for Visualization:**
|
|
755
|
+
- Real-time frequency analysis
|
|
756
|
+
- Multiple analyzer nodes for different components
|
|
757
|
+
- Efficient data extraction for animations
|
|
758
|
+
|
|
759
|
+
3. **Preserved Reactive Effects:**
|
|
760
|
+
- All existing effects continue to work
|
|
761
|
+
- Same audio levels data structure
|
|
762
|
+
- No changes needed to effect components
|
|
763
|
+
|
|
764
|
+
4. **Streaming Support:**
|
|
765
|
+
- Works with chunked audio delivery
|
|
766
|
+
- Proper buffered region visualization
|
|
767
|
+
- Seek works even beyond initial buffer
|
|
768
|
+
|
|
769
|
+
The implementation can be done incrementally without breaking existing functionality, and the migration path is straightforward for consumers of the component library.
|