아이패드 유저에게 '특정 페이지/화면에서만 소리가 재생되지 않아요' voc 전달받음 🤔
센트리에 기록된 에러도 없음 🤔
audioContext.resume() 의 코드 부재 때문인가? → 소리를 재생시킬 때마다 playSound() 메서드를 호출한다. 그 안에서 resume() 를 항시 호출해 주고 있었다. 해당 코드가 없었던 건 아니지만 의심해 볼만 한 상황.running 으로 전환시켜준다. const playSound = async () => {
if (audioContext) {
resume(); // ← 의심되는 코드
await axiosInstance
.get(url, { responseType: 'arraybuffer', adapter: axiosInstance.defaults.adapter })
.then((response) => audioContext.decodeAudioData(response.data))
.then((decodedData) => {
const audioSource = audioContext.createBufferSource();
audioSource.buffer = decodedData;
audioSource.connect(audioContext.destination);
audioSource.start();
})
.catch((error) => {
...
});
}
};
내 애증의 아이패드로 이것저것 테스트 해보다가 재현이 되었는데,
- 해당 프로세스 진행 중 화면에 아이패드를 잠시 sleep 시켜놓고
(= 일반적으로 사이드 버튼 클릭하여 전원 Off라고 일컫는 상태)- 10분 가량 가만히 두다가, sleep 모드를 해제하면 (=전원을 켜서) 다시 해당 화면이 뜬다.
- (아무런 제스쳐를 하지 않고) 해당 화면에서 소리를 재생시키면 진짜 소리가 나지 않았다.
해당 유저님의 패턴도 파악 완.. 아이패드의 sleep 모드를 자주 실행하시는군요 🙂
suspended 가 아니라 interrupted 이었다.suspended 상태만 대응하던 코드였는데, interrupted 의 상태도 추가하여 개선했다.
suspended: 오디오 처리가 일시정지 됨.
→ resume() 이 성공하면 state가running으로 변함
- 새로 만든 audioContext의 default 상태
- 브라우저나 OS가 오디오를 잠시 멈춰야 할 때interrupted: 오디오 출력이 시스템 이벤트 때문에 강제로 중단됨.
→ safari가 강제로 끊어버린 컨텍스트라서 resume()으로는 살아나지 않음.
- 아이폰에서 전화가 오거나 siri가 켜짐
- 알림 소리, 다른 앱에서 오디오 점유
- 에어팟 연결/해제 등
const resume = async () => {
if (audioContext?.state === 'suspended') {
// suspended 상태를 running 상태로 변경하려면 resume() 호출
await audioContext.resume();
}
if (audioContext?.state === 'interrupted') {
// 1. 기존에 연결되었던 audioContext를 종료시키고
await audioContext?.close();
// 2. 새로운 audioContext를 생성하여
const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)();
// 3. audioContext를 관리하는 state에 새 context를 저장
setAudioContext(audioCtx);
}
};
const playSound = async () => {
if (!audioContext) return;
// audioContext.state에 따라 resume 하거나 새 audioContext를 생성
await resume();
// resume이 성공한 상태에서만 실행됨
const response = await axiosInstance.get(url, {
responseType: "arraybuffer",
adapter: axiosInstance.defaults.adapter,
});
const decodedData = await audioContext.decodeAudioData(response.data);
const audioSource = audioContext.createBufferSource();
audioSource.buffer = decodedData;
audioSource.connect(audioContext.destination);
audioSource.start();
} catch (error) {
// 에러 처리
}
};
예상하지 못했던 safari의 interrupted 상태에 따른 처리 부재가 해당 이슈의 원인이었다.
코드 개선 이후, 당장의 이슈 재현은 되지 않지만
동일한 voc가 들어오는지 지켜봐야겠다. 😎
출처: https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/state#interrupted