11import {
2- audioElAtom ,
32 audioPlayingAtom ,
43 currentDurationAtom ,
54 currentTimeAtom ,
6- loadableAudioWaveformAtom ,
75} from "$/states/audio.ts" ;
6+ import { audioEngine } from "$/utils/audio" ;
87import { msToTimestamp } from "$/utils/timestamp.ts" ;
98import { Card } from "@radix-ui/themes" ;
10- import { useAtomValue , useSetAtom } from "jotai" ;
9+ import { useSetAtom } from "jotai" ;
1110import { useCallback , useLayoutEffect , useRef } from "react" ;
1211
1312export const AudioSlider = ( ) => {
1413 const waveformCanvasRef = useRef < HTMLCanvasElement > ( null ) ;
1514 const cachedWaveformRef = useRef < ImageData > ( null ) ;
16- const waveform = useAtomValue ( loadableAudioWaveformAtom ) ;
1715 const mouseSeekPosRef = useRef ( Number . NaN ) ;
18- const audioEl = useAtomValue ( audioElAtom ) ;
16+ const isPressingRef = useRef ( false ) ;
1917
2018 const setCurrentTime = useSetAtom ( currentTimeAtom ) ;
2119 const setCurrentDuration = useSetAtom ( currentDurationAtom ) ;
@@ -28,7 +26,7 @@ export const AudioSlider = () => {
2826 willReadFrequently : true ,
2927 } ) ;
3028 if ( ! ctx ) return ;
31- const p = audioEl . currentTime / audioEl . duration ;
29+ const p = audioEngine . musicCurrentTime / audioEngine . musicDuration ;
3230 const playWidth = canvas . width * p ;
3331 ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
3432 const canvasStyles = getComputedStyle ( canvas ) ;
@@ -49,7 +47,7 @@ export const AudioSlider = () => {
4947 const mouseSeekWidth = mouseSeekPos * canvas . width ;
5048 ctx . globalCompositeOperation = "source-over" ;
5149 const seekTimestamp = msToTimestamp (
52- ( mouseSeekPos * audioEl . duration * 1000 ) | 0 ,
50+ ( mouseSeekPos * audioEngine . musicDuration * 1000 ) | 0 ,
5351 ) ;
5452 const size = ctx . measureText ( seekTimestamp ) ;
5553 ctx . font = `calc(${ fontSize } * ${ devicePixelRatio } ) ${ canvasStyles . fontFamily } ` ;
@@ -72,7 +70,7 @@ export const AudioSlider = () => {
7270 ctx . lineTo ( mouseSeekWidth , canvas . height ) ;
7371 ctx . stroke ( ) ;
7472 }
75- } , [ audioEl ] ) ;
73+ } , [ ] ) ;
7674
7775 const redrawWaveform = useCallback (
7876 ( waveform : Float32Array ) => {
@@ -123,76 +121,92 @@ export const AudioSlider = () => {
123121 useLayoutEffect ( ( ) => {
124122 let frame = 0 ;
125123 const onFrame = ( ) => {
126- if ( audioEl . paused ) {
124+ if ( ! audioEngine . musicPlaying ) {
127125 cancelAnimationFrame ( frame ) ;
128126 frame = 0 ;
129127 return ;
130128 }
131129 redrawCachedWaveform ( ) ;
132- setCurrentTime ( ( audioEl . currentTime * 1000 ) | 0 ) ;
130+ setCurrentTime ( ( audioEngine . musicCurrentTime * 1000 ) | 0 ) ;
133131 frame = requestAnimationFrame ( onFrame ) ;
134132 } ;
135133 const onLoad = ( ) => {
136- console . log ( "music duration" , audioEl . duration ) ;
137- setCurrentDuration ( ( audioEl . duration * 1000 ) | 0 ) ;
134+ setCurrentDuration ( ( audioEngine . musicDuration * 1000 ) | 0 ) ;
138135 } ;
139136 const onPlay = ( ) => {
140137 setAudioPlaying ( true ) ;
141138 onFrame ( ) ;
142139 } ;
143140 const onPause = ( ) => {
144141 setAudioPlaying ( false ) ;
145- setCurrentTime ( ( audioEl . currentTime * 1000 ) | 0 ) ;
142+ setCurrentTime ( ( audioEngine . musicCurrentTime * 1000 ) | 0 ) ;
146143 if ( frame !== 0 ) {
147144 cancelAnimationFrame ( frame ) ;
148145 frame = 0 ;
149146 }
150147 } ;
151148 const onSeeked = ( ) => {
152- setCurrentTime ( ( audioEl . currentTime * 1000 ) | 0 ) ;
149+ setCurrentTime ( ( audioEngine . musicCurrentTime * 1000 ) | 0 ) ;
153150 redrawCachedWaveform ( ) ;
154151 } ;
155- audioEl . addEventListener ( "loadedmetadata" , onLoad ) ;
156- audioEl . addEventListener ( "play" , onPlay ) ;
157- audioEl . addEventListener ( "pause" , onPause ) ;
158- audioEl . addEventListener ( "seeked" , onSeeked ) ;
159- audioEl . addEventListener ( "timeupdate" , onSeeked ) ;
160- setAudioPlaying ( ! audioEl . paused ) ;
152+ audioEngine . addEventListener ( "music-load" , onLoad ) ;
153+ audioEngine . addEventListener ( "music-resume" , onPlay ) ;
154+ audioEngine . addEventListener ( "music-pause" , onPause ) ;
155+ audioEngine . addEventListener ( "music-seeked" , onSeeked ) ;
156+ setAudioPlaying ( audioEngine . musicPlaying ) ;
161157
162158 return ( ) => {
163- audioEl . removeEventListener ( "loadedmetadata" , onLoad ) ;
164- audioEl . removeEventListener ( "play" , onPlay ) ;
165- audioEl . removeEventListener ( "pause" , onPause ) ;
166- audioEl . removeEventListener ( "seeked" , onSeeked ) ;
167- audioEl . removeEventListener ( "timeupdate" , onSeeked ) ;
159+ audioEngine . removeEventListener ( "music-load" , onLoad ) ;
160+ audioEngine . removeEventListener ( "music-resume" , onPlay ) ;
161+ audioEngine . removeEventListener ( "music-pause" , onPause ) ;
162+ audioEngine . removeEventListener ( "music-seeked" , onSeeked ) ;
168163 } ;
169164 } , [
170- audioEl ,
171165 setAudioPlaying ,
172166 setCurrentDuration ,
173167 setCurrentTime ,
174168 redrawCachedWaveform ,
175169 ] ) ;
176170
177171 useLayoutEffect ( ( ) => {
178- if ( waveform . state !== "hasData" ) return ;
179- redrawWaveform ( waveform . data ) ;
180- } , [ redrawWaveform , waveform ] ) ;
172+ const onMusicUnload = ( ) => {
173+ redrawWaveform ( new Float32Array ( 0 ) ) ;
174+ } ;
175+ const onMusicLoad = ( ) => {
176+ redrawWaveform ( audioEngine . musicWaveform ) ;
177+ } ;
178+ audioEngine . addEventListener ( "music-unload" , onMusicUnload ) ;
179+ audioEngine . addEventListener ( "music-load" , onMusicLoad ) ;
180+
181+ return ( ) => {
182+ audioEngine . removeEventListener ( "music-unload" , onMusicUnload ) ;
183+ audioEngine . removeEventListener ( "music-load" , onMusicLoad ) ;
184+ } ;
185+ } , [ redrawWaveform ] ) ;
181186
182187 useLayoutEffect ( ( ) => {
183188 const canvas = waveformCanvasRef . current ;
184189 if ( ! canvas ) return ;
185190 const obs = new ResizeObserver ( ( entries ) => {
186191 canvas . width = entries [ 0 ] . contentRect . width * devicePixelRatio ;
187192 canvas . height = entries [ 0 ] . contentRect . height * devicePixelRatio ;
188- if ( waveform . state !== "hasData" ) return ;
189- redrawWaveform ( waveform . data ) ;
193+ redrawWaveform ( audioEngine . musicWaveform ) ;
190194 } ) ;
191195 obs . observe ( canvas ) ;
192196 return ( ) => {
193197 obs . disconnect ( ) ;
194198 } ;
195- } , [ redrawWaveform , waveform ] ) ;
199+ } , [ redrawWaveform ] ) ;
200+
201+ const onTrySeekMusicWhenDragging = useCallback ( ( ) => {
202+ const mouseSeekPos = mouseSeekPosRef . current ;
203+ if ( ! Number . isNaN ( mouseSeekPos ) ) {
204+ audioEngine . seekMusic (
205+ Math . max ( 0 , mouseSeekPos * audioEngine . musicDuration ) ,
206+ ) ;
207+ redrawCachedWaveform ( ) ;
208+ }
209+ } , [ redrawCachedWaveform ] ) ;
196210
197211 return (
198212 < Card
@@ -203,7 +217,6 @@ export const AudioSlider = () => {
203217 padding : "0" ,
204218 } }
205219 >
206- { /* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */ }
207220 < canvas
208221 style = { {
209222 width : "100%" ,
@@ -214,38 +227,41 @@ export const AudioSlider = () => {
214227 onMouseMove = { ( evt ) => {
215228 const rect = evt . currentTarget . getBoundingClientRect ( ) ;
216229 mouseSeekPosRef . current = ( evt . clientX - rect . left ) / rect . width ;
230+ // if (isPressingRef.current) onTrySeekMusicWhenDragging();
217231 redrawCachedWaveform ( ) ;
218232 } }
219233 onMouseLeave = { ( ) => {
220234 mouseSeekPosRef . current = Number . NaN ;
235+ isPressingRef . current = false ;
221236 redrawCachedWaveform ( ) ;
222237 } }
223238 onTouchStart = { ( evt ) => {
224239 const rect = evt . currentTarget . getBoundingClientRect ( ) ;
225240 mouseSeekPosRef . current =
226241 ( evt . touches [ 0 ] . clientX - rect . left ) / rect . width ;
242+ isPressingRef . current = true ;
243+ onTrySeekMusicWhenDragging ( ) ;
227244 redrawCachedWaveform ( ) ;
228245 } }
229246 onTouchMove = { ( evt ) => {
230247 const rect = evt . currentTarget . getBoundingClientRect ( ) ;
231248 mouseSeekPosRef . current =
232249 ( evt . touches [ 0 ] . clientX - rect . left ) / rect . width ;
250+ // if (isPressingRef.current) onTrySeekMusicWhenDragging();
233251 redrawCachedWaveform ( ) ;
234252 } }
235253 onTouchEnd = { ( ) => {
236- const mouseSeekPos = mouseSeekPosRef . current ;
237- if ( ! Number . isNaN ( mouseSeekPos ) && audioEl ) {
238- audioEl . currentTime = mouseSeekPos * audioEl . duration ;
239- mouseSeekPosRef . current = Number . NaN ;
240- redrawCachedWaveform ( ) ;
241- }
254+ isPressingRef . current = false ;
255+ // onTrySeekMusicWhenDragging();
256+ mouseSeekPosRef . current = Number . NaN ;
257+ } }
258+ onMouseDown = { ( ) => {
259+ isPressingRef . current = true ;
260+ onTrySeekMusicWhenDragging ( ) ;
242261 } }
243- onClick = { ( ) => {
244- const mouseSeekPos = mouseSeekPosRef . current ;
245- if ( ! Number . isNaN ( mouseSeekPos ) && audioEl ) {
246- audioEl . currentTime = mouseSeekPos * audioEl . duration ;
247- redrawCachedWaveform ( ) ;
248- }
262+ onMouseUp = { ( ) => {
263+ isPressingRef . current = false ;
264+ // onTrySeekMusicWhenDragging();
249265 } }
250266 />
251267 </ Card >
0 commit comments