
AI에게 DJ를 시켜보니, 똑똑함보다 '시간을 지키는 설계'가 중요했다
요약
AI 코딩 에이전트를 활용해 알고리즘 DJ 앱인 DeckGhost를 개발한 사례를 소개합니다. AI의 판단 엔진과 실시간 DSP(디지털 신호 처리)를 분리하여 설계함으로써 실시간 처리의 안정성을 확보하는 방법을 다룹니다.
핵심 포인트
- AI는 연주 계획을 세우고, DSP는 결정론적으로 연주하는 설계 원칙 적용
- AI 코딩 에이전트를 활용해 5일 만에 핵심 기능을 구현
- 실시간 오디오 처리 시 AI의 판단 오류에 대비한 안전한 회피 설계 중요
- Web Audio API와 Node.js를 활용한 Local-first 아키텍처 구성
DeckGhost로 배우는, 판단 엔진과 실시간 처리의 분리 방법
개인 개발로, DeckGhost라는 알고리즘 DJ 앱을 만들고 있습니다.
이 앱은 초기 구현부터 AUTO MIX, 곡 해석, DJ 기법 데이터베이스, MIDI, 녹음·리플레이, 박자 위치 보정 등의 주요 기능까지를, AI 코딩 에이전트 (AI Coding Agent)를 사용하며 첫 5일 동안 집중적으로 개발했습니다.
그 후에도 곡 구성의 SSM 해석, 국소 템포 (Local Tempo), 코드열 (Chord Sequence), 실제로 겹치는 구간의 조성 평가, 자동 테스트를 추가하고 있습니다. 5일 만에 완성했다기보다, 5일 만에 움직이는 핵심을 만들고, 관측된 실패를 재료 삼아 계속 키워나가고 있습니다.
다만, 이 5일은 정연한 프로젝트 관리가 성공한 결과는 아닙니다.
별도의 장기 개발 프로젝트에서는 AI 개발을 위해 태스크의 정본 (Source of Truth), READY 조건, WIP 제한, 완료 증적, 문서 동기화 등의 메커니즘을 먼저 만들어 두었습니다. 그럼에도 불구하고 DeckGhost에서는 어떤 사정으로 인해 그 규칙을 거의 무시하고 구현을 시작했습니다.
이 뒷이야기는 제10장에서 다루겠습니다.
실제로 동작하는 공개 버전은 이쪽입니다.
DeckGhost
곡을 순서대로 재생하기만 하는 오토 DJ가 아닙니다. 곡을 해석하여 진입점이 되는 CUE 포인트를 만들고, 궁합이 좋은 곡과 DJ 기법을 선택하며, 2대의 데크 (Deck), 믹서, 이펙트를 자동으로 조작합니다.
처음에 생각했던 것은 상당히 소박한 아이디어였습니다.
곡이 끝날 때 페이드 아웃(Fade-out)할 뿐만 아니라, 곡 중간에서도 DJ답게 자연스럽게 MIX할 수 없을까.
그런데 실제로 만들기 시작하니 어려웠던 것은 'AI에게 어떤 곡을 고르게 할 것인가'뿐만이 아니었습니다.
- 음악의 템포를 어떻게 측정할 것인가
- 어느 위치에서 다음 곡을 넣을 것인가
- 저음끼리의 충돌을 어떻게 피할 것인가
- 브라우저가 잠시 멈춰도 소리가 깨지지 않게 하는 방법
- AI의 판단이 틀렸을 때 어떻게 안전하게 회피할 것인가
- 동일한 연주를 나중에 재현할 수 있는가
만들어 나가는 과정에서 가장 중요한 설계 원칙이 보였습니다.
AI는 연주 계획을 세운다. DSP는 결정론적(Deterministic)으로 연주한다.
이 기사에서는 DeckGhost 개발 중에 실제로 일어난 실패를 소개하면서, 실시간 처리(Real-time processing)에 AI를 결합할 때의 설계를 설명합니다.
DJ나 음성 처리를 접해본 적이 없더라도 읽어나갈 수 있도록 전문 용어는 그 자리에서 보충하겠습니다.
🔰 간단히 말하자면 2대의 음악 플레이어와 믹서를 브라우저 상에 만들고, 곡의 해석, 선곡, MIX 계획, 연주, 평가까지를 자동화하는 앱입니다.
DeckGhost는 Node.js와 브라우저의 Web Audio API로 동작합니다.
Node.js 측은 화면과 곡 라이브러리, 기법 데이터를 전달합니다. 실제 음성 해석과 재생은 브라우저 내에서 수행하는 Local-first 구성입니다. 불러온 음성 파일을 해석을 위해 서버로 전송하지 않습니다.
현재의 주요 기능은 다음과 같습니다.
- 2데크 재생, CUE, 루프 (Loop), 템포 변경, SYNC
- 3밴드 EQ, 크로스페이더 (Crossfader), Limiter, Safety Gain
- ECHO, REVERB, FILTER, ROLL, GATE, DISTORTION, NOISE, BACKSPIN 등 12종의 BEAT FX
- BPM, 박자 위치, 키 (Key), 스펙트럼, 라우드니스 (Loudness), 무음 구간, 국소 템포 추정
- SSM과 복수의 특징량에 의한 Intro, Verse, Build, Chorus, Drop, Break, Outro 구성 해석
- 마디 단위의 코드열과 실제로 겹치는 구간의 조성 평가
- 전체 파형에 대한 섹션 표시
- 역할이 지정된 CUE의 자동 생성
- 67개 기법의 지식 베이스와 60개 기법의 AUTO MIX 후보
- AUTO MIX를 통한 결정론적인 자동 연주
- 연주 이력 기록, 리플레이, Mix Critic에 의한 평가
- MIDI 컨트롤러 입출력
- Node.js 표준 테스트를 통한 해석·판단 로직 검증
그림은 스마트폰에서도 글자가 뭉개지지 않도록 원칙적으로 위에서 아래로 읽습니다. 복잡한 접속도는 여러 장으로 나누었습니다.
이 기사에서는 음악 이론 자체를 자세히 외울 필요는 없습니다.
최소한 다음의 흐름만 이해하면 읽어나갈 수 있습니다.
- Deck A에서 현재 곡을 재생한다
- Deck B로 다음 곡을 불러온다
- 헤드폰으로만 Deck B를 사전 확인한다
- 두 곡의 속도와 박자 위치를 맞춘다
- Deck B를 조금씩 메인 출력으로 내보낸다
- 저음이나 음량의 주도권을 Deck A에서 Deck B로 넘긴다
- Deck A를 없애고 다음 곡으로 넘어간다
DeckGhost가 자동화하고 있는 것은 바로 이 일련의 판단과 조작입니다.
음악 용어가 등장하면, 이를 「시간의 눈금」, 「소리의 대역」, 「현재 곡과 다음 곡의 전환」 중 하나로 읽는다고 생각하면 대체로 길을 잃지 않습니다.
| 용어 | 음악·DJ에서의 의미 | 엔지니어 관점에서의 의미 |
|---|---|---|
| 박자 (Beat) | 메트로놈의 「탁」 소리 한 번에 해당하는 시간 단위 | 최소 스케줄링 (Scheduling) 단위 |
| ... | ... | ... |
음악 용어가 많은 장이라도, 다음 세 가지로 치환해서 읽으면 괜찮습니다.
| 기사에서 다루는 것 | 치환된 의미 |
|---|---|
| BPM, 박자, 프레이즈 (Phrase) | 시간축과 스케줄링 (Scheduling) |
| ... | ... |
음악 이론의 정답을 외우는 기사가 아니라, 불확실한 분석 결과로부터 시간 제약이 있는 처리를 안전하게 실행하는 설계에 관한 기사로 읽어주세요.
🔰 간단히 말하자면 「어떻게 연주할지 생각하는 일」과 「소리를 정해진 시간에 맞춰 움직이는 일」은 별개의 문제입니다.
| 용어 | 이 기사에서의 의미 | 엔지니어 관점에서의 의미 |
|---|---|---|
| 실시간 처리 (Real-time processing) | 박자나 음성을 멈추지 않고 기한 내에 계속 처리함 | 응답 기한을 어기면 결과 자체가 무효가 되는 처리 |
| ... | ... | ... |
자동 DJ라고 하면, AI가 실시간으로 소리를 들으면서 페이더(Fader)나 이펙트(Effect)를 자유자재로 움직이는 모습을 상상할지도 모릅니다.
하지만 LLM이나 복잡한 판단 처리에는 응답 시간의 편차가 있습니다.
100밀리초(ms) 만에 답이 올 때도 있고, 몇 초가 걸릴 때도 있습니다. 네트워크 연결이 필요한 AI라면 통신 장애도 발생할 수 있습니다.
반면, 음악의 박자는 기다려주지 않습니다.
128BPM의 곡에서는 메트로놈의 「탁」 소리 한 번에 해당하는 1박이 약 469밀리초입니다. 16분 음표라면 약 117밀리초밖에 되지 않습니다. 판단이 조금만 늦어져도 박자의 기준이 되기 쉬운 낮은 타격음인 킥(Kick)끼리 어긋나거나, 페이더 조작이 8·16마디 처리 블록에 해당하는 프레이즈(Phrase)에서 벗어나게 됩니다.
그래서 DeckGhost를 두 개의 층(Layer)으로 나누었습니다.
Brain
- 다음에 사용할 곡
- 시작점과 종료점의 CUE
- 사용할 DJ 기법
- MIX의 길이
- 조성(Key)이나 스펙트럼(Spectrum)의 리스크
- 박자 동기화(Sync) 실패 시의 대체 수단
DSP
- 지정된 시각부터 재생
- 템포(Tempo)와 위상(Phase) 조정
- LOW, MID, HIGH 조작
- 채널 페이더와 크로스 페이더(Crossfader)
- ECHO나 REVERB 적용
- 종료 시의 상태 복원
Brain은 "16마디에 걸쳐 저음을 교체하고, 마지막에는 페이더로 빼낸다"라고 계획합니다.
Planner는 그 계획을 시간축상의 조작으로 변환합니다.
DSP는 계획된 조작을 동일한 입력이라면 동일하게 실행합니다.
인프라 엔지니어에게는 제어 평면(Control Plane)과 데이터 평면(Data Plane)의 분리에 가까운 구조입니다.
- Brain은 제어 평면
- DSP는 데이터 평면
제어 평면은 방침을 세우거나 경로를 결정합니다. 시간이 다소 걸려도 상관없습니다.
데이터 평면은 실제 패킷이나 음성을 멈추지 않고 처리합니다. 매번 다시 생각해서는 시간을 맞출 수 없습니다.
DJ 앱을 만들면서 설계 측면에서 인프라 분야에 익숙한 사고방식이 상당히 도움이 되었습니다.
실시간 시스템에 AI를 도입할 때, AI를 시간 제약이 엄격한 처리 경로에서 제외하는 것이 첫 번째 안전책이 됩니다.
🔰 간단히 말하자면 BPM이나 키(Key)는 자동 분석할 수 있지만, 항상 정확한 것은 아닙니다. 추정치에는 신뢰도와 수동 수정 기능을 마련합니다.
| 용어 | 이 기사에서의 의미 | 엔지니어 관점에서의 의미 |
|---|---|---|
| 휴리스틱 (Heuristic) | 소리의 특징으로부터 현실적인 후보를 찾는 경험칙 | 엄밀해(Exact solution)보다 속도와 실용성을 우선시한 근사 알고리즘 |
| ... | ... | ... |
DeckGhost는 곡을 로드하면 다음 정보를 분석합니다.
- 곡 전체의 BPM과 비트 그리드 (Beat Grid)
- 8초 윈도우(Window)로 추적하는 국소 템포와 템포 안정도
- 곡의 도입부, 종결부, 곡 중간의 무음 구간
- 키(Key)와 Camelot 표기
- 마디 단위의 코드열
- LOW, MID, HIGH의 비율
- 라우드니스(Loudness), 에너지, 스펙트럼 무게 중심
- 타악기 중심인지, 지속음 중심인지를 나타내는 특징
- Intro, Verse, Build, Chorus, Drop, Break, Outro 등의 곡 구성
- 섹션별 추정 신뢰도
- 8, 16, 32마디 주기와 SSM으로부터 얻은 프레이즈 경계
여기서 중요한 것은 분석 결과를 '정답'이 아니라 후보로 취급하는 것입니다.
BPM 검출에서는 곡의 음량 변화로부터 주기를 찾습니다.
초기 구현에서는 특정 주기뿐만 아니라, 그 2배, 3배, 4배 위치에도 유사한 피크(Peak)가 있는지 확인하여 가산점을 주었습니다. 이론적으로는 많은 주기에서 일치하는 후보를 선택하면 정밀도가 올라갈 것으로 보였습니다.
하지만 실제 음원을 넣었더니 약 96BPM인 곡이 193BPM으로 판정되었습니다. 거의 정확히 2배입니다.
원인은 짝수 배의 피크(Peak)에 가산점을 준 탓에, 빠른 템포 후보가 느린 템포의 강도까지 가로채고 있었기 때문이었습니다.
그래서 짝수 배를 평가에서 제외하고, 경합하기 어려운 홀수 배만 사용하도록 수정했습니다.
이 경험을 통해 다음과 같이 설계를 변경했습니다.
자동 해석값
+ 해석 원천
+ 신뢰도
...
예를 들어 BPM이라면, 값뿐만 아니라 다음과 같은 정보를 갖게 합니다.
{
bpm: 128.0,
bpmSource: "estimated",
...
사용자가 수동으로 수정했을 경우에는 자동 해석보다 우선합니다.
- BPM을 절반 또는 2배로 보정
- BPM을 직접 입력
- TAP으로 측정
- 비트 그리드(Beat Grid)를 1ms, 10ms, 50ms 단위로 이동
- 현재 위치를 첫 박자로 설정
초기의 곡 구성 해석은 8마디 단위의 평균 에너지를 보고 intro, groove, break, drop 등으로 분류했습니다.
이 방법은 가볍지만, 경계가 8마디 단위로 반올림됩니다. 음색이나 코드, 타악기의 변화가 마디 중간에 일어나더라도 실제 변화 위치를 포착할 수 없습니다.
현재는 다음과 같은 흐름으로 업데이트하고 있습니다.
구조 해석의 흐름
병행하여 취득하는 특징
SSM(Similarity Matrix)은 곡의 각 마디가 다른 마디와 어느 정도 유사한지를 행렬로 나타낸 것입니다. 로그 구간끼리의 유사도를 전수 조사하여 나열하고, 반복 패턴과 변화점을 찾는 이미지에 가깝습니다. 반복되는 사비(Chorus)나 비슷한 음색의 Verse를 찾기 쉬워집니다.
HPSS(Harmonic-Percussive Source Separation)는 여기서 보컬이나 드럼 음원을 추출하는 Stem 분리가 아닙니다. 일정 시간 지속되는 성분과 순간적으로 튀어나오는 성분을 특징 추출을 위해 나누어 보는 전처리입니다. 시간 방향과 주파수 방향의 중앙값을 사용하여, 해석 특징을 지속음 계열과 타악기 계열로 나누는 마스크(Mask)로 사용하고 있습니다.
브라우저에서 곡 전체의 거대한 스펙트로그램(Spectrogram)을 유지하면 무겁기 때문에, 마디 중앙 부근의 특징으로 집약하고 있습니다. 정밀도뿐만 아니라 실행 환경의 계산량도 설계 조건입니다.
자동 테스트에서는 같은 음명이라도 220Hz에서 880Hz로 음색이 변하는 합성 파형을 만들어, 그 실제 변화 위치에 프레이즈 경계를 둘 수 있는지 확인하고 있습니다.
머신러닝이나 휴리스틱(Heuristic)의 출력은 값만 반환하면 후속 단계에서 사실로 확정되어 버립니다.
다음 정보들을 함께 제공하면 안전하게 다루기 쉬워집니다.
- 어떤 방법으로 얻었는가
- 어느 정도 신뢰할 수 있는가
- 인간이 수정했는가
- 수정 전의 값
- 어떤 처리에 의존하고 있는가
추정치를 사용하는 시스템에서는 "올바른 값을 내는 것"만큼이나 "틀렸을 가능성을 표현하는 것"이 중요합니다.
🔰 간단히 말하자면, 평가식이 그럴싸해 보여도 가중치(Weight)를 잘못 설정하면 모든 상황에서 똑같은 판단을 내리는 시스템이 됩니다.
| 용어 | 이 기사에서의 의미 | 엔지니어용 표현 |
|---|---|---|
| 평가 함수 | 곡 쌍이나 기법에 점수를 매기는 계산 | 후보 선택용 스코어링 함수 (Scoring Function) |
| ... |
DeckGhost는 두 곡을 겹쳤을 때의 음역대 충돌을 평가합니다.
처음에는 LOW끼리의 중첩을 가장 위험하다고 판단하여, 저역대에 큰 가중치를 두었습니다.
LOW의 충돌: 매우 무거움
MID의 충돌: 중간
HIGH의 충돌: 가벼움
언뜻 보기에는 맞는 것 같습니다. 킥(Kick)과 베이스(Bass)가 두 곡 분량만큼 겹치면 확실히 소리는 탁해집니다.
실제 음원 5곡으로부터 총 20개의 쌍을 평가했더니, 거의 모든 쌍이 "블렌딩 금지, 짧게 전환해야 함"이라고 판정되었습니다.
어떤 곡을 넣어도 비슷한 하드 컷(Hard Cut)이나 슬램(Slam) 계열의 MIX가 됩니다.
원인은 일반적인 댄스 뮤직끼리라면 LOW 값이 높은 것이 보통이기 때문입니다. LOW끼리 곱하여 크게 가산하면 거의 모든 쌍이 상한선에 달라붙게 됩니다.
하지만 실제 DJ는 저음의 중첩을 EQ의 LOW Kill이나 Bass Swap으로 처리할 수 있습니다.
오히려 처리하기 어려운 것은 보컬이나 멜로디가 모이는 MID의 충돌입니다.
그래서 평가 축을 다음과 같이 변경했습니다.
- LOW: 기법으로 처리할 수 있으므로 가중치를 낮춤
- MID: 혼잡해지면 피하기 어렵기 때문에 가중치를 높임
- HIGH: 보조적으로 평가
- 기법 측에 LOW를 한쪽만 남기는 제약을 추가
곡 전체에 부여된 호환성 라벨인 키(Key)가 같더라도, 실제로 MIX하는 16마디 동안 울리고 있는 국소적 상태인 코드(Chord)까지 같으리라는 보장은 없습니다.
그래서 현재의 Brain은 곡 전체의 Camelot 호환성뿐만 아니라, 후보 CUE로부터 실제로 겹치는 최대 16마디의 코드열을 비교합니다.
곡 A의 전체 키: 8B
곡 B의 전체 키: 8B
↓
...
코드 추정의 확신도가 낮은 마디는 N.C.로 취급합니다. 모르는 것을 억지로 코드명으로 결정하지 않고, 판단 재료에서 약화시키기 위해서입니다.
자동 테스트에서도, 전체 키가 일치하는 두 곡에 대하여 겹치는 구간에만 충돌하는 코드열을 부여하고, 조성 스코어(tonality score)와는 별개로 겹치는 구간의 점수가 저하되는 것을 확인하고 있습니다.
평가 함수를 만들 때, 대상의 특징량(feature)만 봐서는 부족합니다.
후속 제어에서 무엇을 해결할 수 있는가까지 포함하여 가중치를 결정해야 합니다.
이는 음성 이외에도 마찬가지입니다.
예를 들어 장애 리스크 평가에서, 복구 수단이 있는 고장과 데이터를 잃는 고장을 같은 가중치로 다루어서는 안 됩니다.
입력의 위험도
- 후단에서 흡수할 수 있는 범위
= 실제로 판단에 사용해야 할 리스크
평가식은 현실의 운용이나 제어 수단을 모르면, 수학적으로 정교하더라도 사용할 수 없습니다.
🔰 간단히 말하자면, 좋은 CUE 포인트를 선택하더라도 거기서 MIX할 시간이 남아 있지 않다면 좋은 연주가 되지 않습니다.
| 용어 | 이 기사에서의 의미 | 엔지니어용 표현 |
|---|---|---|
| CUE | MIX를 시작·종료하는 후보 위치 | 처리 시작점, 체크포인트 |
| ... |
DeckGhost는 프레이즈 경계나 곡 구성으로부터 CUE 후보를 만듭니다.
초기 평가에서는 출구로서 음악적으로 깔끔한 위치를 높게 평가했습니다.
그런데 곡의 상당히 후반부에 있는 사비(chorus) 끝부분이 선택되는 경우가 있었습니다.
Brain은 16마디의 블렌딩(blending)을 계획하고 있었습니다.
하지만 선택된 출구로부터 곡 끝까지 남은 시간이 짧아, 실행 시에는 1~2마디로 압축되었습니다. 다음 곡을 넣은 직후 바로 전환되는 부자연스러운 MIX가 됩니다.
CUE 단독으로는 '깔끔한 출구'였지만, 플랜(plan) 전체로는 사용할 수 없었습니다.
그래서 CUE 평가에 **runway(활주로)**라는 개념을 추가했습니다.
- 계획한 마디 수를 실행할 수 있는 남은 시간
- 프리롤(pre-roll)에 필요한 시간
- 비트 동기화(beat sync)를 안정시키는 시간
- 페이드아웃(fade-out) 이후의 여백
국소적 평가뿐만 아니라, 후속 처리가 끝까지 완주할 수 있는지를 확인합니다.
좋은 시작 위치
+ 필요한 실행 시간
+ 안전한 종료 여백
...
현재의 표준 AUTO MIX는 단순히 크로스페이더(crossfader)를 왼쪽에서 오른쪽으로 움직이는 처리가 아닙니다.
Brain이 이 흐름을 선택하고, Planner가 마디와 비트로 전개하며, DSP가 정해진 시간에 실행합니다.
통상적인 MIX를 명시적인 절차로 만듦으로써, 화려한 기법을 늘리더라도 기본 동작으로 돌아갈 수 있는 지점이 생겼습니다. 고도의 기법이 성립하지 않는 경우에는 짧은 컷(cut)이나 세이프 페이드(safe fade)로 축퇴(degrade)합니다.
하나의 함수가 올바른 값을 반환하더라도, 시스템 전체가 올바르다는 보장은 없습니다.
- API 단독으로는 올바름
- 데이터 형식도 올바름
- 테스트도 통과함
- 하지만 후공정의 시간이나 용량이 부족함
이러한 문제는 로컬한 단위 테스트만으로는 찾아내기 어렵습니다.
후보의 품질뿐만 아니라, 그 후보를 선택한 후에 시스템이 완주할 수 있는지를 평가합니다.
🔰 간단히 말하자면 음성에서는 신호 경로(signal path)나 타이밍의 작은 실수가 음량의 급증, 탁함, 어긋남으로 즉시 표면화됩니다.
| 용어 | 이 기사에서의 의미 | 엔지니어용 표현 |
|---|---|---|
| Audio Node | 음원, EQ, FX, 출력을 나타내는 Web Audio API의 부품 | 연결 가능한 처리 노드 |
| ... |
Web Audio API에서는 소리를 노드(node)로서 연결합니다.
가로로 너무 길어지지 않도록 연결을 세 부분으로 나누어 표시합니다.
1채널 분량의 처리
Deck A와 Deck B는 각각 동일한 경로를 가집니다.
2채널의 합류와 메인 출력
헤드폰용 CUE 경로
PFL은 채널 페이더의 영향을 받기 전에 분기됩니다.
현재는 ECHO, DELAY, REVERB, FLANGER, PHASER, FILTER, CRUSH, ROLL, GATE, DISTORTION, NOISE, BACKSPIN의 12종을 다룹니다.
FX를 추가할 경우에는 종류를 늘리기 전에 원음과 이펙트음의 섞는 방식을 설계해야 합니다.
ECHO, DELAY, REVERB에서 원음을 100퍼센트 남겨둔 채 이펙트음도 더하고 있었습니다.
결과적으로 FX를 ON으로 하면 소리가 두꺼워지는 것이 아니라 단순히 음량이 커졌습니다.
사용자 입장에서는 'CUE 모니터 소리까지 섞여 있는 것 아닌가'라고 의심하게 되는 증상입니다. 하지만 원인은 dry와 wet의 경로 설계였습니다.
그래서 equal-power 방식의 dry/wet 크로스페이드(crossfade)로 변경했습니다.
- dry를 낮추면서 wet를 올림
- send 계열 FX에서는 최소한의 dry를 남김
- insert 계열 FX는 깊은 설정으로 원음을 대체
- 최종 단계에 Limiter와 Safety Gain을 배치
DeckGhost는 이상적인 MIX뿐만 아니라, 실패했을 때의 퇴로(escape route)도 계획합니다.
- 박자 락(Beat lock)을 할 수 없는 경우에는 세이프 페이드(Safe fade)
- 들어오는 곡을 무음으로 선행 재생하는 프리롤(Pre-roll)
- 킥(Kick)의 위치를 추정하여 시작 위치를 미세 조정
- 신뢰도가 낮으면 세밀한 보정을 포기하고 그리드 동기화(Grid sync)로 축퇴(Degradation)
- 곡 끝의 남은 시간이 부족한 경우에만 프리롤을 단축
- 지속적인 피크(Peak)를 감지했을 경우에는 자동으로 게인(Gain)을 낮춤
정상계(Normal case)의 정밀도를 높이는 것뿐만 아니라, 실패 시의 소리를 '사고'에서 '자연스러운 페이드(Fade)'로 바꿉니다.
이는 실시간 시스템의 graceful degradation(우아한 성능 저하), 즉 축퇴 운전에 가까운 사고방식입니다.
완벽하게 동기화할 수 없을 때, 무리하게 고도의 기법을 계속하기보다, 투박하더라도 파탄 나지 않는 기법으로 물러납니다.
🔰쉽게 말하면, 매 프레임(frame) 호출되는 처리 과정에 크기 변경이나 초기화와 같은 무거운 부작용(side effect)을 섞으면, 화면이 자기 증식할 수 있습니다.
| 용어 | 이 기사에서의 의미 | 엔지니어에게 말하자면 |
|---|---|---|
| Canvas | 파형, 조그(Jog), VU 미터를 그리는 영역 | 저수준(Low-level) 드로잉 버퍼 |
| ... |
음성과는 별개로, UI에서도 인상적인 결함이 있었습니다.
DeckGhost는 파형, 조그, VU 미터를 Canvas에 그립니다. 고해상도 디스플레이에 대응하기 위해 devicePixelRatio에 맞춰 Canvas의 내부 해상도를 조정하고 있었습니다.
매 프레임 다음과 같은 조정을 하고 있었습니다.
canvas.width = clientWidth × devicePixelRatio
그런데 일부 Canvas에서는 CSS상의 표시 크기가 고정되어 있지 않았습니다.
내부 크기를 크게 함
→ clientWidth도 커짐
→ 다음 프레임에서 더욱 크게 함
이 루프가 발생하여, 조작하지 않는데도 화면이 계속 확대되었습니다.
대책은 화려하지 않습니다.
- CSS로 표시 크기를 고정
- 내부 해상도와 표시 크기를 분리
- 제로 사이즈(Zero size) 가드
- 크기가 같다면 재할당하지 않음
- 초기화와 매 프레임 드로잉을 분리
requestAnimationFrame이나 감시 루프에 무엇이든 두는 것은 위험합니다.
루프 내에서는 가급적 다음을 피합니다.
- DOM 구조의 재구성
- Canvas의 재할당
- 네트워크 액세스
- 오디오 노드(Audio node)의 재연결
- 큰 배열의 생성
- 상태의 초기화
빈번하게 호출되는 처리일수록 읽기와 드로잉에 집중하고, 부작용을 작게 만듭니다.
🔰쉽게 말하면 AI가 이름을 알고 있는 기법과, 현재의 프로그램이 안전하게 실행할 수 있는 기법은 별개입니다.
| 용어 | 이 기사에서의 의미 | 엔지니어에게 말하자면 |
|---|---|---|
| 지식 베이스 (Knowledge base) | 기법명, 적용 조건, 주의점을 저장한 데이터 | 정책·규칙·카탈로그 |
| ... |
DeckGhost에는 DJ 기법의 지식 베이스가 있습니다.
Phrase Blend, Bass Swap, Echo Out, Double Drop, Loop Exit, Wordplay 등, 67가지 기법을 데이터로 수록하고 있습니다.
한편, 현재의 DSP와 데크 제어로 AUTO MIX 후보로서 다룰 수 있는 것은 60가지 기법입니다.
새롭게 Mid Swap, High Swap, V-Curve Fade, Constant Power Fade, Ghost Drop, Noise Sweep, Gate Transition, Distortion Fade, Snare Drop, Break Drop, Intro to Outro, Chorus to Chorus, Half / Double BPM, Semitone Shift 등의 실연 커브(Performance curve)도 추가되었습니다.
Stem 분리, 가사 타임코드, 음계 MIDI, 샘플러 등, 아직 존재하지 않는 입력이나 기능이 필요한 기법은 지식 베이스에는 남겨두되 실행 후보에서는 제외하고 있습니다.
지식 베이스
67가지 기법
↓ 실행 조건을 충족하는가
...
이는 기능 부족을 숨기기 위함이 아닙니다.
지식과 능력을 분리하면 다음과 같은 이점이 있습니다.
- 향후 구현할 기법의 설계를 미리 정리
- 현재 할 수 없는 것을 명시
- Brain(두뇌)이 존재하지 않는 능력을 선택하지 않음
- DSP 구현 후에 실행 가능 플래그를 전환
- 기법의 선택 이유를 인간에게 설명
LLM은 구현되지 않은 조작에 대해서도 자연스럽게 설명할 수 있습니다.
따라서 도구 사용형 AI(Tool-use AI)에서는 지식과 실행 권한을 명확히 분리해야 합니다.
알고 있다
≠
실행할 수 있다
...
DeckGhost에서는 기법 데이터에 실행 가능성과 가드 조건(Guard condition)을 부여하여, 현재 DSP가 보장할 수 있는 조작만을 Planner에게 전달합니다.
AI에게 능력을 설명하게 만드는 것이 아니라, 기계적 판정이 가능한 능력 목록을 시스템 측에서 보유합니다.
🔰 간단히 말하자면, "어딘가 이상했던 연주"를 수정하기 위해서는 그때 무엇을 판단했고 어떤 조작을 했는지 재현할 수 있어야 합니다.
| 용어 | 이 기사에서의 의미 | 엔지니어 관점에서의 의미 |
|---|---|---|
| 가관측성 (Observability) | 판단, 연주, 실패 이유를 외부에서 확인할 수 있는 성질 | 로그(Log), 메트릭(Metric), 트레이스(Trace)를 통해 내부 상태를 추측할 수 있는 것 |
| ... |
실시간 연주의 버그는 재현하기 어려운 법입니다.
- 곡의 조합
- 시작 위치
- BPM 분석 결과
- 선택한 기법
- 페이더의 진행
- FX 타이밍
- 브라우저의 부하
조건이 조금만 바뀌어도 동일한 증상이 나타나지 않습니다.
그래서 DeckGhost에는 관측과 재현을 위한 메커니즘을 넣었습니다.
Brain이 만든 계획을 인간이 읽을 수 있는 형태로 표시합니다.
- 어떤 CUE를 사용할지
- 몇 마디를 MIX할지
- 어디서 LOW를 교체할지
- 어떤 FX를 사용할지
- 어떤 조건을 리스크로 판단했는지
250밀리초(ms) 간격으로 다음을 기록합니다.
- 데크(Deck)의 재생 위치
- 재생 상태
- EQ
- 채널 페이더
- 크로스페이더
- Planner 이벤트
동일한 곡 라이브러리 상에서 조작 이력을 재실행할 수 있습니다.
연주 후에 다음을 평가합니다.
- 동일 기법의 과도한 사용
- 키(Key) 호환성
- FX 과다
- 계획의 신뢰도
- 리스크 및 개선안
여기서 중요한 것은 Critic을 "정답을 결정하는 AI"로 만들지 않는 것입니다.
연주를 관측하고, 다음 개선 재료를 만드는 역할에 머물게 합니다.
계획
↓
결정론적인 연주
...
이는 메트릭, 로그, 트레이스를 사용하여 운영을 개선하는 흐름과 같습니다.
리플레이(Replay)는 실제 연주 전체를 재현하는 메커니즘입니다.
이와는 별도로, 분석이나 Brain의 작은 부품들을 위해 Node.js 표준 테스트 러너(Test runner)를 추가했습니다.
npm test
현재는 다음과 같은 조건들을 합성 데이터(Synthetic data)로 확인하고 있습니다.
- 곡의 도입부, 곡의 끝, 곡 중간의 무음 구간
- 일정 120BPM의 국소 템포 안정성
- 120BPM에서 132BPM으로 변화하는 곡의 검출
- 무음을 "안정적인 템포"로 오판단하지 않을 것
- FFT 스펙트럼 무게 중심
- HPSS 마스크를 통한 지속음과 순간적인 타격음의 분리
- SSM을 통한 실제 음색 변화 위치의 검출
- 메이저 코드와
N.C.의 식별 - 실제 중첩 구간에서의 코드 호환성
테스트 음원은 리포지토리에 큰 음악 파일을 두지 않고, 사인파(Sine wave), 클릭(Click), 크로마 벡터(Chroma vector) 등을 코드로 합성합니다.
이를 통해 실제 음원을 통한 청감 확인과는 별개로, 분석 알고리즘의 전제가 깨지지 않았는지 단시간에 확인할 수 있습니다.
실시간 처리에서는 똑똑한 판단을 만들기 전에, 무엇이 일어났는지를 재현할 수 있는 상태를 만듭니다.
🔰 간단히 말하자면, 수중에 음원이 없더라도 개발자나 새로운 멤버가 즉시 테스트할 수 있도록 앱 내에서 테스트용 곡을 생성합니다.
| 용어 | 이 기사에서의 의미 | 엔지니어 관점에서의 의미 |
|---|---|---|
| 데모 데이터 | 음원 없이도 기능을 시험할 수 있는 인공적인 5곡 | 자기 완결적인 Fixture |
| ... |
DeckGhost는 브라우저 내에서 5곡의 데모 트랙을 합성할 수 있습니다.
구현에서는 OfflineAudioContext 상에서 8마디의 intro, full, outro 패턴을 먼저 생성하고, 이를 나열하여 56~64마디의 곡으로 조립합니다. 킥(Kick)은 OscillatorNode의 주파수를 짧은 시간 동안 150Hz에서 44Hz로 낮추고, 하이햇이나 클랩(Clap)은 화이트 노이즈를 필터에 통과시켜 합성합니다.
const kick = ctx.createOscillator();
kick.frequency.setValueAtTime(150, t);
kick.frequency.exponentialRampToValueAtTime(44, t + 0.11);
코드는 기사 작성을 위해 변수명을 간략화했습니다. 실제 구현에서는 베이스, 코드, 아르페지오, 노이즈 스윕(Noise sweep)도 조합하여 사용합니다.
데모 곡에는 BPM, 키(Key), 곡 구성, 에너지 변화를 의도적으로 부여했습니다.
이를 통해 저작권이 있는 곡을 리포지토리(Repository)에 넣지 않고도 다음 사항들을 확인할 수 있습니다.
- 곡 분석
- Camelot 호환 곡 선정
- CUE 생성
- AUTO MIX
- FX
- 녹음
- 세션 리플레이 (Session replay)
데모 데이터가 있으면 새로운 개발자도 npm install과 npm start 이후 즉시 동작을 확인할 수 있습니다.
이는 프로젝트를 처음 접하는 사람이 테스트하기 위한 환경으로서도 중요합니다.
입력 데이터를 각자 준비해야만 작동하는 프로젝트는 환경 구축 단계에서 학습이 중단되기 쉽습니다.
샘플 데이터는 단순한 설명 자료가 아니라, 최소한의 통합 테스트 (Integration test) 환경입니다.
🔰 간단히 말하자면 DeckGhost는 잘 관리된 AI 개발의 성공 사례가 아닙니다. 이미 가지고 있던 관리 규칙을 시간에 대한 조급함 때문에 의도적으로 배제하고, 구현을 먼저 실행시킨 단기 스파이크 (Spike)입니다.
| 용어 | 이 기사에서의 의미 | 엔지니어식 표현 |
|---|---|---|
| 스파이크 (Spike) | 정식 구현 전에 단기간에 가능성을 확인하는 시제품 | 기술 검증용 타임박스 (Time-box) 개발 |
| ... |
DeckGhost를 만들기 전, 다른 장기 개발 프로젝트에서는 AI 에이전트 (AI Agent)를 지속적으로 운용하기 위한 관리 구조를 구현하고 있었습니다.
예를 들어 다음과 같은 메커니즘입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기