Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions components/KeysingyouGameRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { TimerCircle } from "./ui/TimerCircle";
import { Progress } from "./ui/Progress";
import AudioVisualizer from "./ui/AudioVisualizer";
import { getSharedAudioCtx } from "@/lib/sharedAudioCtx";
import { attachEcho } from "@/lib/applyEcho";

interface GameRoomProps {
user: any;
Expand Down Expand Up @@ -154,17 +155,24 @@ const KeysingyouGameRoom = ({ user, room, onBack }: GameRoomProps) => {


useEffect(() => {
if (phase === "listen") {
setAnalysisStep("분석중");
const timer1 = setTimeout(() => setAnalysisStep("대조중"), 4000);
const timer2 = setTimeout(() => setAnalysisStep("추출중"), 7000);
return () => {
clearTimeout(timer1);
clearTimeout(timer2);
};
}
if (phase !== "listen") return;

setAnalysisStep("분석중");
const timer1 = setTimeout(() => setAnalysisStep("대조중"), 4000);
const timer2 = setTimeout(() => setAnalysisStep("추출중"), 7000);
return () => {
clearTimeout(timer1);
clearTimeout(timer2);
};
}, [phase, audioSrc]);

useEffect(() => {
if (phase !== "listen" || !audioRef.current) return;

getSharedAudioCtx().resume().catch(() => { });
attachEcho(audioRef.current);
}, [phase]);

// 2. 키워드 phase 진입 시 슬롯머신 애니메이션
useEffect(() => {
if (phase === "keyword" && keyword) {
Expand Down
1 change: 1 addition & 0 deletions components/ui/AudioVisualizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const AudioVisualizer: React.FC<Props> = ({ audioRef }) => {
if (!sourceRef.current) {
try {
sourceRef.current = audioCtx.createMediaElementSource(audioEl);
audioEl._srcNode = sourceRef.current;
sourceRef.current.connect(analyser);
analyser.connect(audioCtx.destination);
} catch (error) {
Expand Down
40 changes: 40 additions & 0 deletions lib/applyEcho.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { getSharedAudioCtx } from "@/lib/sharedAudioCtx";

declare global {
interface HTMLAudioElement {
_srcNode?: MediaElementAudioSourceNode;
_echoAttached?: boolean;
}
}

export function attachEcho(audioEl: HTMLAudioElement) {
const ctx = getSharedAudioCtx();
if (audioEl._echoAttached) return; // 이미 체인 붙었으면 종료

/** ✦ 핵심: SourceNode 재사용 또는 신규 생성 */
const src =
audioEl._srcNode ?? (audioEl._srcNode = ctx.createMediaElementSource(audioEl));

// ───── echo chain once ─────
const delay = ctx.createDelay(5);
delay.delayTime.value = 0.33;

const feedback = ctx.createGain();
feedback.gain.value = 0.4;

const lpf = ctx.createBiquadFilter();
lpf.type = "lowpass";
lpf.frequency.value = 3500;

const wet = ctx.createGain();
wet.gain.value = 0.6;

const dry = ctx.createGain();
dry.gain.value = 1;

delay.connect(feedback).connect(lpf).connect(delay);
src.connect(dry).connect(ctx.destination); // dry
src.connect(delay).connect(wet).connect(ctx.destination); // wet

audioEl._echoAttached = true; // 플래그 마킹
}