Skip to content

Commit dbbaae3

Browse files
committed
use fade out with worklet
1 parent 8fc3b7f commit dbbaae3

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

packages/react/src/lib/useSoundPlayer.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)