feat: 표시판 호출 시 띵동 효과음 추가
Web Audio API로 E5→C5 두음 벨소리 합성 (외부 파일 불필요). 표시판 화면에 소리 켜짐/꺼짐 토글 버튼 추가. AudioContext는 역할 선택 클릭 시 초기화 (브라우저 autoplay 정책 대응). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -320,6 +320,7 @@
|
|||||||
<div class="display-label">현재 호출 번호</div>
|
<div class="display-label">현재 호출 번호</div>
|
||||||
<div class="display-num" id="display-num">-</div>
|
<div class="display-num" id="display-num">-</div>
|
||||||
<div class="display-sub" id="display-sub">대기 중 0명</div>
|
<div class="display-sub" id="display-sub">대기 중 0명</div>
|
||||||
|
<button class="btn" id="display-mute-btn" style="bottom:60px">🔔 소리 켜짐</button>
|
||||||
<button class="btn" id="display-back-btn">역할 변경</button>
|
<button class="btn" id="display-back-btn">역할 변경</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -329,6 +330,49 @@ const socket = io();
|
|||||||
let currentPin = '';
|
let currentPin = '';
|
||||||
let currentRole = '';
|
let currentRole = '';
|
||||||
let myTicketNumber = null;
|
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) {
|
function showScreen(id) {
|
||||||
@@ -376,7 +420,9 @@ document.getElementById('role-issue').addEventListener('click', () => {
|
|||||||
showScreen('screen-issue');
|
showScreen('screen-issue');
|
||||||
});
|
});
|
||||||
document.getElementById('role-display').addEventListener('click', () => {
|
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');
|
displayEl.classList.remove('flash');
|
||||||
void displayEl.offsetWidth; // reflow
|
void displayEl.offsetWidth; // reflow
|
||||||
displayEl.textContent = newVal;
|
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}명`;
|
document.getElementById('display-sub').textContent = `대기 중 ${s.waitingCount}명`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user