이 레포가 무엇을 하는 물건인가.
README 첫 문장이 정체성을 정확히 말한다. Suno 같은 웹 도구가 "텍스트로 완성곡을 뽑는" 것이라면, gary4juce는 내가 방금 친 기타 리프에 AI가 '반응'해 이어 연주하게 만든다. 작곡 도구 안에서 AI와 주고받는 게 핵심.
gary4juce는 DAW(디지털 오디오 워크스테이션) 플러그인이다. 즉 Ableton Live·FL Studio·Reaper 같은 음악 제작 프로그램에 꽂아 쓰는 부품이다. 이 플러그인 하나 안에 7개의 최신 AI 음악 모델이 탭으로 들어 있어, 사용자는 세션에서 오디오를 녹음하고 → AI에게 이어 만들기/변형을 시키고 → 결과를 타임라인에 드래그해 곡으로 엮는다.
중요한 설계 결정: 무거운 AI 계산은 플러그인이 직접 하지 않는다. 모델 추론은 외부 백엔드(개발자가 운영하는 무료 원격 서버, 또는 사용자가 자기 GPU에 띄우는 localhost 서비스)가 맡고, 플러그인은 오디오를 보내고 결과를 받아 재생·드래그하는 클라이언트 역할만 한다. 덕분에 플러그인 자체는 가볍고, GPU가 없어도 원격 백엔드로 바로 써볼 수 있다.
트렌딩 이유 · 경쟁 도구 대비 장점.
화제의 핵심은 "웹 AI 음악툴과 정반대 포지션"이다. 대부분의 AI 음악 서비스는 브라우저에서 완성곡을 뽑아주는 데 그치지만, gary4juce는 음악가가 이미 만들고 있는 작업물에 AI가 끼어들게 한다. 게다가 7개의 SOTA(최신) 모델을 한 UI에 통합한 것은 2026년 기준 사실상 최대 규모다.
| 항목 | 웹 AI 음악툴(예: Suno) | gary4juce |
|---|---|---|
| 작업 위치 | 브라우저(별도 공간) | DAW 세션 안(작업 흐름 유지) |
| 입력 | 주로 텍스트 프롬프트 | 내 녹음 오디오 + 텍스트 + BPM/키 |
| 모델 수 | 보통 1종(자체) | 7종(MusicGen·SA3·ACE-Step·Magenta…) |
| 실행 위치 | 제공사 서버 고정 | 원격(무료) 또는 내 GPU(localhost) 선택 |
| 공개성 | 비공개 | 오픈소스(AGPL-3.0) + 소스/라이선스 투명 |
각 모델에는 사람 이름 코드네임이 붙어 있어 외우기 쉽다 — gary(MusicGen, 오디오 이어붙이기), jerry(stable-audio-open-small, 빠른 짧은 루프), sa3(Stable Audio 3, v4의 메인 신기능: 최대 300초 생성·변형·LoRA 블렌딩), carey(ACE-Step, 50개 언어 보컬·stem 추출), terry(MelodyFlow, 스타일 변환), darius(Magenta RT, 48kHz 고품질 이어붙이기), rc-jerry(Foundation-1, BPM·키 인식 루프). 그리고 코드 곳곳에 실전 견고함의 흔적(stall 감지, cold-start 처리, 에디터 파괴/재생성 생존)이 있어 단순 토이가 아님이 드러난다.
웹에서 곡을 뽑으면 다시 다운로드해 DAW로 가져와야 하고, 내가 지금 만들고 있는 트랙의 BPM·키·분위기와 맞물리지 않는다. AI가 '완성품'을 던져줄 뿐, 음악가가 만드는 과정에 함께 들어오지 못한다.
플러그인이 DAW의 재생·BPM을 읽어 내 연주를 녹음하고, 그 오디오를 조건으로 AI가 이어/변형해 만든 결과를 바로 타임라인에 드래그해 넣는다. 무거운 추론은 백엔드에 맡겨 플러그인은 가볍게 유지하면서, AI를 작곡 과정의 '연주 파트너'로 만든다.
플러그인(클라이언트) · 네트워킹 · AI 백엔드.
구조는 명확히 둘로 갈린다 — 이 저장소에 담긴 C++ 플러그인(클라이언트)과, 별도 저장소에 있는 Python AI 백엔드(서버). 플러그인은 별도 네트워킹 라이브러리 없이 순수 JUCE만으로 HTTP 통신을 한다.
| 레이어 | 기술 / 버전 | 역할 |
|---|---|---|
| 언어 | C++ (모던 — std::atomic·shared_ptr·람다) | 플러그인 전체 로직(~3.3만 줄). |
| 프레임워크 | JUCE 8.0.8 (모듈 12종, DSP 모듈 없음) | 오디오·플러그인 포맷·UI·네트워킹. |
| 플러그인 포맷 | VST3 / AU / Standalone | Ableton·FL·Reaper·Logic 등에서 동작. |
| 프로젝트 정의 | Projucer .jucer (XML) | 모듈·포맷·빌드 설정의 단일 진실원. |
| 빌드 | Windows VS2022 / macOS Xcode | Projucer가 프로젝트 파일 생성. |
| 네트워킹 | juce::URL + InputStream (별도 lib 없음) | HTTP POST/GET. 오디오는 base64로 JSON에 실음. |
| JSON / 인코딩 | juce::JSON · DynamicObject · Base64 | 요청 직렬화 · 오디오 base64 변환. |
| 비동기 | juce::Thread::launch → MessageManager::callAsync | 네트워크 I/O를 워커 스레드로, UI 복귀는 메시지 스레드. |
| AI 백엔드(별도 레포) | Python + PyTorch (Audiocraft·stable-audio-tools·ACE-Step…) | HTTP API로 모델 추론 서빙. |
| 모델 | MusicGen·Stable Audio 3/Open·ACE-Step·MelodyFlow·Magenta RT | 7종. 각기 다른 라이선스(MIT·CC-BY-NC·Stability Community…). |
.jucer XML에 적어두면 Projucer가 실제 프로젝트 파일을 만들어 준다. "설계도 → 공장 세팅"의 설계도에 해당.내 연주가 AI를 거쳐 타임라인으로 돌아오기까지.
먼저 독수리 시점. 플러그인 내부는 크게 둘로 나뉜다 — 오디오를 실시간으로 다루는 Gary4juceAudioProcessor(프로세서)와, UI + 모든 네트워킹을 맡는 Gary4juceAudioProcessorEditor(에디터). 그 바깥에 HTTP로 연결된 AI 백엔드가 있다.
이제 대표 흐름 한 줄기를 따라가자 — "녹음한 오디오로 AI에게 이어 만들기를 시킨다". 폴링(주기적으로 물어보기) 기반의 긴 작업 패턴이 핵심이다.
여기서 가장 배울 점은 오디오 스레드를 절대 멈추지 않는 설계다. 실시간 오디오 콜백(processBlock)에서 락(잠금)을 걸고 기다리면 소리가 끊긴다(글리치). 그래서 이 코드는 "논블로킹 시도 + 폴백 버퍼" 패턴을 쓴다 — 락을 시도만 하고, 못 잡으면 입력을 짧은 임시 버퍼에 잠깐 담아뒀다 다음 블록에서 합류시킨다.
# PluginProcessor.cpp — 오디오 스레드는 절대 블로킹하지 않는다
if (bufferLock.tryEnter()) { // 잠금을 '시도만'
flushDeferredRecordingBlocks(...); // 임시버퍼에 쌓인 것 합류
copyInputBlockToRecordingBuffer(...); // 정상 녹음
bufferLock.exit();
} else {
appendDeferredRecordingBlock(...); // 락 실패 → 임시버퍼로 (안 멈춤)
}
또 하나의 백미는 비동기 콜백 안전장치 3종 세트다. 네트워크 응답이 늦게 도착했을 때 UI가 이미 닫혔거나 사용자가 탭을 바꿨을 수 있다. 이를 위해 ① SafePointer(에디터가 파괴됐는지 검사), ② weak_ptr<atomic<bool>>(살아있는지 플래그), ③ 단조 증가 토큰(GenerationAsyncToken — 옛 작업의 늦은 응답을 무시)을 함께 쓴다. "늦게 온 답이 엉뚱한 곳에 꽂히는" use-after-free·stale response 사고를 막는 정석이다.
이 프로젝트 특유의 관례: 모든 오디오 교환을 파일로 한다(~/Documents/gary4juce/). 녹음은 myBuffer.wav, 결과는 myOutput.wav로 저장하고, 백엔드 출력 샘플레이트가 호스트와 다르면 LagrangeInterpolator로 리샘플해 맞춘다. 타임라인 드래그도 임시 파일을 만들어 OS 네이티브 드래그로 DAW에 넘긴다.
어디부터 열어봐야 하나.
핵심은 Source/ 안에 있다. 거대한 에디터 클래스를 기능별 .cpp로 쪼개 두었고(같은 클래스, 여러 파일), 모델별 UI는 별도 컴포넌트로 떼어냈다. 처음엔 PluginProcessor(오디오)와 PluginEditor(UI·네트워킹) 두 축만 잡으면 된다.
| 위치 | 역할 |
|---|---|
gary4juce.jucer | 빌드의 출발점. 어떤 JUCE 모듈·플러그인 포맷·빌드 도구를 쓰는지 다 여기. Projucer로 열어 프로젝트를 생성. |
PluginProcessor.cpp | 오디오의 심장. processBlock(실시간 콜백), 녹음/재생 버퍼, 호스트 BPM 읽기, 상태 영속화. 스레딩 학습의 핵심 파일. |
PluginEditor.cpp | UI 골격 + 모든 네트워킹의 허브(7,465줄). 제출·폴링·콜백 안전장치가 여기. 가장 큰 파일. |
PluginEditor.<모델>.cpp | 같은 에디터 클래스를 모델별 .cpp로 분할. 각 모델의 요청 본문(JSON) 작성 로직. |
Components/<모델>/ | 모델별 UI 패널을 별도 컴포넌트로 분리. unique_ptr로 보유, 탭 enum으로 전환. |
Utils/BarTrim.h | 생성 오디오를 마디 경계로 자르는 유틸. 파일 쓰기 완료 대기·무음 감지 같은 실전 디테일. |
이 레포에서 배울 만한 것 + 어느 파일을 펴면 되는지.
AudioProcessor(오디오·상태)와 AudioProcessorEditor(UI)를 분리하고, 오래 유지할 상태는 Processor에 두는 설계를 본다. getStateInformation으로 XML 상태를 DAW 프로젝트에 영속화하고 버전 마이그레이션까지 처리. 빈 JUCE 플러그인에 "녹음→WAV 저장"만 붙여보는 게 좋은 첫 실습.
processBlock에서 mutex를 쓰지 않고 atomic + tryEnter() + 폴백 버퍼로 글리치를 막는다. std::memory_order_acquire/release의 실제 용례, shared_ptr의 원자적 교체로 새 데이터를 오디오 스레드에 안전히 넘기는 패턴을 코드로 익힌다.
Thread::launch 워커 → MessageManager::callAsync 복귀의 정석과, SafePointer+weak_ptr+증가 토큰의 안전장치 3종을 본다. "생성 중 탭을 바꾸면 결과가 엉뚱한 탭에 꽂히는" 문제를 토큰 검사로 막는 방식이 백미. 일부러 토큰 검사를 빼고 버그를 재현해보면 이해가 빠르다.
모델 추론을 플러그인에서 떼어내 별도 프로세스(HTTP)로 두는 설계의 장단점, 오디오를 base64 JSON으로 실어 보내는 방식의 한계, 다중 모델을 포트로 구분해 운영하는 법을 본다. warmup(cold-start)·stall·queue 같은 실제 서빙의 너저분한 현실을 코드가 어떻게 받아내는지가 압권.
7,465줄짜리 에디터를 같은 클래스의 여러 .cpp로 분할하고 모델별 UI를 컴포넌트로 떼는 구조를 본다. 또 백엔드 출력(예 48kHz)과 호스트(예 44.1kHz)의 샘플레이트가 다를 때 LagrangeInterpolator로 리샘플하는 실전 처리를 익힌다.
예컨대 SA3 생성 요청은 이런 JSON으로 나간다 — 프롬프트에 BPM·키를 자동으로 붙이고, 켜진 LoRA만 배열로 싣는다:
// PluginEditor.Jerry.cpp — SA3 /generate 요청 본문(요약)
prompt = userPrompt + ", 120 bpm" // BPM/키 자동 첨부
steps = 8 // 추론 스텝(품질↔속도)
cfg_scale= 1.0 // 프롬프트 충실도
seed = -1 // -1=랜덤, 응답에 쓰인 seed 반환(재현용)
loras = [{ name, strength(0~2) }] // 강도 0 초과만 실음
플러그인은 가볍고, 백엔드(GPU)는 무겁다.
플러그인 자체는 매우 가볍다 — VST3/AU를 지원하는 DAW와 인터넷(원격 백엔드 사용 시)이면 충분하다. 무게는 전부 AI 백엔드 쪽에 있고, 그건 원격(무료)으로 우회할 수 있다.
| 항목 | 요구사항 |
|---|---|
| 플러그인 | VST3/AU 지원 DAW. 가벼움. 코드사이닝 없음 → Windows Defender 경고 가능. |
| OS / 빌드 | Windows(VST3·VS2022) / macOS(AU+VST3·Xcode). LMMS 미지원. |
| 호스트 DAW | AU: GarageBand·Logic / VST3: Ableton·FL·Reaper·Cubase·Bitwig. |
| 백엔드(원격) | g4l.thecollabagepatch.com 사용 시 내 GPU 불필요(단, spot VM이라 가끔 죽음). |
| 백엔드(localhost) | 최소 10GB+ VRAM, 권장 16GB+. Darius(Magenta RT)는 24GB+ 권장. |
| JUCE 소스 | 레포에 미포함. JUCE 8.0.8을 별도로 받아 Projucer로 프로젝트 재생성 필요. |
gary4juce는 '요리를 외주 주는 주방장'이다. 플러그인(주방장)은 재료를 다듬고(녹음) 주문서를 쓰고(요청) 완성된 요리를 차린다(재생)만 한다. 실제 조리(AI 추론)는 멀리 있는 거대한 주방(GPU 백엔드)이 맡는다. 그래서 내 컴퓨터(주방장)는 가벼워도 되지만, 주방(GPU)은 따로 있어야 — 또는 남의 공용 주방(원격)을 빌려야 — 한다.
읽기만 하지 말고 손에 익히는 단계.
레포를 받아 Projucer로 .jucer를 열고 VS2022/Xcode로 Release 빌드 → DAW에 올린 뒤, 원격 백엔드로 Jerry 루프를 하나 만들어 타임라인에 드래그한다. JUCE 빌드 파이프라인을 끝까지 한 번 통과하는 게 목표.
processBlock의 녹음 길이(180초)와 폴백 버퍼(2초) 상수를 바꿔보고, 작은 호스트 버퍼에서 "deferred 버퍼 오버플로" 로그가 뜨는 조건을 만들어 관찰한다. 실시간 오디오의 제약을 몸으로 느끼는 단계.
Python(FastAPI)으로 /health, /api/juce/process_audio(즉시 session_id 반환), /poll_status/{id}(진행률 올리다 테스트 WAV를 base64로 반환) 세 개만 만들어 localhost(8000)에 띄운다. 플러그인을 붙여 제출→폴링→재생 전 과정을 GPU 없이 재현.
SA3 요청 본문에 있지만 UI엔 없는 값(예: per-LoRA interval)을 슬라이더로 추가하고, JSON에 실어 보내도록 PluginEditor.Jerry.cpp를 확장한다. "UI 컨트롤 → 요청 본문 필드"의 배선을 직접 깔아보는 연습.
탭 enum 추가 → 새 XxxUI 컴포넌트 → ActiveOp 항목 → getServiceUrl 라우팅 → 제출/폴링 함수 → 상태 영속화(getStateInformation)까지, 한 모델을 끝에서 끝까지 연결한다. 이 플러그인의 전체 구조를 손으로 꿰는 종합 실습.
GenerationAsyncToken 검사를 일부러 빼고, 생성 도중 탭을 빠르게 전환해 "늦게 온 응답이 엉뚱한 탭에 꽂히는" stale response 버그를 재현한 뒤 다시 복구한다. 비동기 안전장치가 왜 필요한지 가장 확실히 배우는 방법.
한 주씩 따라가는 6주 계획.
| 주차 | 주제 | 학습 자료 |
|---|---|---|
| 1주차 | JUCE 기초 + 빈 플러그인 빌드 | JUCE 튜토리얼 · Projucer · gary4juce.jucer + 실습 1 |
| 2주차 | AudioProcessor·processBlock·상태 영속화 | PluginProcessor.cpp + 실습 2 |
| 3주차 | 실시간 오디오 스레딩 — atomic·lock-free·메모리 순서 | PluginProcessor.cpp 녹음 로직 · C++ 동시성 자료 |
| 4주차 | 비동기 네트워킹 + 콜백 안전장치 | PluginEditor.cpp 제출/폴링 · 실습 3 |
| 5주차 | AI 음악 모델 이해(MusicGen·Stable Audio) | Audiocraft · stable-audio-tools 문서 + 실습 4 |
| 6주차 | 구조 확장 — 새 모델 탭·요청/응답 스키마 설계 | PluginEditor.<모델>.cpp · Components/ + 실습 5·6 |
본문에 나온 용어 빠른 참조.
| 용어 | 의미 |
|---|---|
| DAW | 디지털 오디오 워크스테이션. 음악을 녹음·편집·믹싱하는 프로그램(Ableton·FL·Logic). |
| VST3 / AU / Standalone | 플러그인 규격(스타인버그/애플)과 독립 실행 형태. 하나의 JUCE 프로젝트가 여러 형태로 빌드. |
| JUCE | 오디오 앱·플러그인용 C++ 프레임워크. 오디오·포맷·UI·네트워킹 제공(여기 8.0.8). |
| Projucer / .jucer | JUCE 프로젝트 생성기와 그 설정 파일(모듈·포맷·빌드 익스포트). |
| processBlock | 플러그인의 실시간 오디오 콜백. 멈춤·메모리 할당 금지 구역. |
| recording / deferred buffer | 입력을 모으는 메인 버퍼(180초)와, 락 경합 시 임시로 받는 폴백 버퍼(2초). |
| 샘플레이트 / 리샘플 | 초당 샘플 수. 백엔드 출력과 호스트 SR이 다르면 LagrangeInterpolator로 변환. |
| base64 audio | 바이너리 WAV를 텍스트로 인코딩해 JSON에 실어 HTTP 전송. |
| session_id / polling | 비동기 추론 작업 식별자. 제출 후 /poll_status/{id}를 3초마다 GET해 결과 수신. |
| warmup / cold start | 모델·서버가 처음 깨어나며 가중치를 로딩하는 구간(최대 8분 유예). |
| MusicGen (gary) | Meta Audiocraft의 음악 생성/이어붙이기 모델. |
| Stable Audio 3 / Open (sa3 / jerry) | Stability AI의 text-to-audio 모델. SA3는 변형·이어붙이기·LoRA·seed recall 지원. |
| LoRA | 작은 보조 가중치로 특정 스타일/음색을 주입(강도 0~2, 여러 개 동시). |
| seed recall | 랜덤 생성 후 사용된 seed를 응답으로 돌려줘 같은 결과를 재현. |
| cfg_scale / steps | 프롬프트 충실도 / 추론 스텝 수. 생성 품질·성격을 조절. |
| ACE-Step (carey) | 가사·다국어 보컬 생성 + stem 추출/cover 모델. |
| SafePointer / weak_ptr / 토큰 | 늦게 온 비동기 응답이 파괴된 UI·옛 작업에 꽂히는 사고를 막는 안전장치 3종. |