diff --git a/public/index.html b/public/index.html
index 266e94c..2af028d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -320,6 +320,7 @@
현재 호출 번호
-
대기 중 0명
+
@@ -329,6 +330,49 @@ const socket = io();
let currentPin = '';
let currentRole = '';
let myTicketNumber = null;
+let lastCalledCount = 0;
+
+// ── 소리 ──────────────────────────────────────
+let audioCtx = null;
+let soundMuted = false;
+
+function initAudio() {
+ if (audioCtx) return;
+ audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+}
+
+function playDingDong() {
+ if (soundMuted || !audioCtx) return;
+ if (audioCtx.state === 'suspended') audioCtx.resume();
+ const t = audioCtx.currentTime;
+
+ function bell(freq, start, dur, vol) {
+ const osc = audioCtx.createOscillator();
+ const gain = audioCtx.createGain();
+ osc.connect(gain);
+ gain.connect(audioCtx.destination);
+ osc.type = 'sine';
+ osc.frequency.setValueAtTime(freq, start);
+ gain.gain.setValueAtTime(0, start);
+ gain.gain.linearRampToValueAtTime(vol, start + 0.012);
+ gain.gain.exponentialRampToValueAtTime(0.0001, start + dur);
+ osc.start(start);
+ osc.stop(start + dur + 0.05);
+ }
+
+ // 띵 (E5 + 옥타브 배음)
+ bell(659, t, 1.1, 0.40);
+ bell(1318, t, 1.1, 0.12);
+ // 동 (C5 + 옥타브 배음) — 0.45초 후
+ bell(523, t + 0.45, 1.4, 0.40);
+ bell(1046, t + 0.45, 1.4, 0.12);
+}
+
+document.getElementById('display-mute-btn').addEventListener('click', () => {
+ soundMuted = !soundMuted;
+ document.getElementById('display-mute-btn').textContent =
+ soundMuted ? '🔇 소리 꺼짐' : '🔔 소리 켜짐';
+});
// ── 유틸 ──────────────────────────────────────
function showScreen(id) {
@@ -376,7 +420,9 @@ document.getElementById('role-issue').addEventListener('click', () => {
showScreen('screen-issue');
});
document.getElementById('role-display').addEventListener('click', () => {
- currentRole = 'display'; showScreen('screen-display');
+ currentRole = 'display';
+ initAudio(); // 사용자 인터랙션 직후 AudioContext 생성 (autoplay 정책)
+ showScreen('screen-display');
});
// 역할 변경
@@ -424,8 +470,12 @@ function applyState(s) {
displayEl.classList.remove('flash');
void displayEl.offsetWidth; // reflow
displayEl.textContent = newVal;
- if (s.calledCount > 0) displayEl.classList.add('flash');
+ if (s.calledCount > 0) {
+ displayEl.classList.add('flash');
+ if (currentRole === 'display') playDingDong();
+ }
}
+ lastCalledCount = s.calledCount;
document.getElementById('display-sub').textContent = `대기 중 ${s.waitingCount}명`;
}