본문 바로가기
C.W.K.
Stream
Lesson 03 of 05 · published

anchored 클럭 하나로 scheduler drift를 막아

~13 min · shared-clock, web-audio, scheduler

Level 0식은 재
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"JavaScript 타이머는 다음 일을 깨우고, 오디오 클럭은 실제로 언제 울릴지 정해."

증상: scheduler drift

playback 엔진에서 solo, 코드, 메트로놈은 같은 음악적 순간에 맞물려야 해. 각 voice가 자기 시작 시각과 JavaScript 타이머만으로 다음 이벤트를 정하면 callback 지연이 서로 다른 오차로 쌓일 수 있어. 메트로놈은 조금 앞서고 코드는 늦는 식으로 그루브가 헐거워져. 연습 도구에서는 작은 어긋남도 손이 믿고 따라갈 기준을 흔들어.

수정: anchored 오디오 클럭 하나

anchor 하나에서 소스 시간과 AudioContext.currentTime을 대응시키고, 모든 voice의 이벤트 시각을 같은 함수로 계산해. lookahead scheduler는 setTimeout이나 setInterval로 자주 깨어나도 돼. 중요한 건 JavaScript timer가 실제 발음 시각을 결정하지 않는다는 점이야. 다음 window의 이벤트를 미리 찾아 Web Audio의 오디오 클럭 시각에 예약해야 해.

공유 timebase는 독립 scheduler가 쌓는 drift를 막아 주지만, 모든 소리가 자동으로 sample-perfect가 된다는 뜻은 아냐. asset 시작점, decode 지연, 신호 경로, 출력 장치 latency 때문에 고정 offset이 남을 수 있어. voice별 calibration과 실제 장치에서의 측정이 여전히 필요해. 클럭은 공통 기준을 주고, 보정은 각 경로의 차이를 맞춰.

one-model 아이디어를 시간에 적용해

Track 3의 단일 진실원이 다른 모자를 쓴 셈이야. 거기서는 음악 모델 하나에서 여러 view가 파생되고, 여기서는 timebase 하나에서 여러 voice의 시각이 파생돼. 다만 단일 소스는 관측 가능한 차이를 없애는 마법이 아니라, 차이를 설명하고 보정할 공통 좌표계야. 표류를 보면 먼저 권위 있는 시간원이 하나인지 확인하고, 그다음 경로별 latency를 측정해.

Code

JavaScript timer로 깨우고, 오디오 클럭에 예약해·javascript
function audioTimeOf(srcTime, voice) {
  const shared = anchorAudio + (srcTime - anchorSrc) / playbackRate;
  return shared + voiceOffset[voice]; // 측정한 경로별 고정 보정
}

const SCHEDULE_AHEAD = 0.1; // 초
const LOOKAHEAD_MS = 25;

function scheduler() {
  while (next.srcTime < currentSrcTime() + SCHEDULE_AHEAD) {
    // 실제 발음 시각은 AudioContext timebase에 예약해.
    scheduleVoiceAt(audioTimeOf(next.srcTime, next.voice), next);
    next = advance();
  }
  // 이 timer는 scheduler를 깨울 뿐, 발음 시각 그 자체가 아냐.
  setTimeout(scheduler, LOOKAHEAD_MS);
}

External links

Exercise

동기화돼야 하는 두 출력이 있는 시스템을 골라. 각자 시간을 누적하는지, 공유 클럭에서 현재 위치를 계산하는지 확인해. 'anchor 하나 + 공통 timebase + 경로별 offset'으로 수정을 스케치하고, scheduler drift와 고정 latency를 따로 측정해 봐.
Hint
setTimeout이나 setInterval은 lookahead scheduler를 깨우는 데 쓸 수 있어. 실제 이벤트 시각까지 JavaScript callback 도착 시각에 맡기는 게 문제야. 공통 클럭과 경로별 보정을 한 문제로 뭉개지 마.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 피파warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.