@@ -101,6 +101,13 @@ export const useSoundPlayer = (props: {
101101
102102 const pcmData = audioBuffer . getChannelData ( 0 ) ;
103103
104+ if ( gainNode . current ) {
105+ const now = audioContext . current . currentTime ;
106+ gainNode . current . gain . cancelScheduledValues ( now ) ;
107+ const targetGain = isAudioMuted ? 0 : volume ;
108+ gainNode . current . gain . setValueAtTime ( targetGain , now ) ;
109+ }
110+
104111 workletNode . current ?. port . postMessage ( { type : 'audio' , data : pcmData } ) ;
105112
106113 setIsPlaying ( true ) ;
@@ -111,7 +118,31 @@ export const useSoundPlayer = (props: {
111118 }
112119 } , [ ] ) ;
113120
114- const stopAll = useCallback ( ( ) => {
121+ const fadeOutAndPostStopMessage = async ( type : 'end' | 'clear' ) => {
122+ const FADE_DURATION = 0.1 ;
123+ if ( ! gainNode . current || ! audioContext . current ) {
124+ workletNode . current ?. port . postMessage ( { type } ) ;
125+ return ;
126+ }
127+
128+ const now = audioContext . current . currentTime ;
129+ const FADE_TARGET = 0.0001 ;
130+
131+ gainNode . current . gain . cancelScheduledValues ( now ) ;
132+ gainNode . current . gain . setValueAtTime ( gainNode . current . gain . value , now ) ;
133+ gainNode . current . gain . exponentialRampToValueAtTime (
134+ FADE_TARGET ,
135+ now + FADE_DURATION ,
136+ ) ;
137+
138+ await new Promise ( ( resolve ) => setTimeout ( resolve , FADE_DURATION * 1000 ) ) ;
139+
140+ workletNode . current ?. port . postMessage ( { type } ) ;
141+
142+ gainNode . current . gain . setValueAtTime ( 1.0 , audioContext . current . currentTime ) ;
143+ } ;
144+
145+ const stopAll = useCallback ( async ( ) => {
115146 isInitialized . current = false ;
116147 isProcessing . current = false ;
117148 setIsPlaying ( false ) ;
@@ -122,6 +153,8 @@ export const useSoundPlayer = (props: {
122153 window . clearInterval ( frequencyDataIntervalId . current ) ;
123154 }
124155
156+ await fadeOutAndPostStopMessage ( 'end' ) ;
157+
125158 if ( analyserNode . current ) {
126159 analyserNode . current . disconnect ( ) ;
127160 analyserNode . current = null ;
@@ -142,7 +175,6 @@ export const useSoundPlayer = (props: {
142175 }
143176
144177 if ( workletNode . current ) {
145- workletNode . current . port . postMessage ( { type : 'end' } ) ;
146178 workletNode . current . port . close ( ) ;
147179 workletNode . current . disconnect ( ) ;
148180 workletNode . current = null ;
@@ -152,7 +184,7 @@ export const useSoundPlayer = (props: {
152184 } , [ ] ) ;
153185
154186 const clearQueue = useCallback ( ( ) => {
155- workletNode . current ?. port . postMessage ( { type : 'clear' } ) ;
187+ void fadeOutAndPostStopMessage ( 'clear' ) ;
156188 isProcessing . current = false ;
157189 setIsPlaying ( false ) ;
158190 setFft ( generateEmptyFft ( ) ) ;
0 commit comments