브라우저에서 완전히 실행되는 AI 음악 생성: ONNX, WebGPU, 샘플러(Samplers) 및 긴 오디오 스티칭(Long Audio
요약
브라우저 환경에서 WebGPU와 ONNX를 활용하여 Stable Audio 3 Small 모델을 실행하는 기술적 방법론을 다룹니다. 모델 샤딩, 4비트 양자화, WebGPU 메모리 관리 및 직렬 초기화 전략을 통해 로컬 브라우저 기반의 AI 음악 생성 파이프라인 구축 과정을 상세히 설명합니다.
핵심 포인트
- WebGPU 메모리 제한을 극복하기 위한 4비트 양자화 및 모델 샤딩 적용
- 네트워크 I/O는 병렬화하되, WebGPU 세션 초기화는 메모리 스파이크 방지를 위해 반드시 직렬화 수행
- Stable Audio 3 Small 모델을 활용한 로컬 브라우저 기반 AI 음악 생성 파이프라인 구축
- 모델 크기 감소와 런타임 메모리 최적화를 통한 브라우저 환경 최적화
많은 개발자들은 추론(Inference) 성능 때문에 브라우저에서 AI 모델을 실행하는 것이 어렵다고 생각합니다.
우리의 오픈 소스 비디오 에디터인 Timeline Studio에 로컬 브라우저 AI 음악 생성 기능을 탑재한 후, 저는 다음과 같은 사실을 확인할 수 있었습니다:
모델 추론(Model inference)은 쉬운 부분입니다.
진정한 어려움은 모델 크기, WebGPU 메모리 제한, 캐시 할당량(Cache quotas), 샘플러(Sampler)의 정확성, 긴 오디오 생성, 그리고 안정적인 에디터 통합과 같은 브라우저 특유의 제약 사항을 해결하는 데 있습니다.
이 글에서는 모든 함정과 해결책을 포함하여, 우리가 구축한 완전한 프로덕션급 브라우저 AI 음악 파이프라인을 상세히 분석합니다.
1. Stable Audio 3 Small (Q4 ONNX)을 선택한 이유
우리는 Stable Audio 3 Small Music Q4 ONNX 모델을 사용합니다.
총 크기: 683MB
이 모델은 네 가지 핵심 구성 요소로 이루어져 있습니다:
| 모듈 | 목적 | 크기 |
|---|---|---|
| Text Encoder | 프롬프트를 컨디셔닝 벡터(Conditioning vectors)로 인코딩 | 213MB |
| ... |
브라우저 특화 최적화
-
모델 샤딩 (Model sharding)
브라우저 네트워크 타임아웃과 파싱 실패를 방지하기 위해 모델 가중치(Weights)를 100MB 미만의 청크(Chunks)로 분할합니다. -
4비트 양자화 (4-bit quantization)
-
MatMul / Linear→MatMulNBits -
Embedding→GatherBlockQuantized -
기타 파라미터는 FP32로 유지
Q4 방식은 다운로드 크기와 런타임 메모리 사용량을 획기적으로 줄여줍니다.
트레이드오프(Tradeoff): 고주파 영역과 리버브(Reverb) 잔향에서 미세한 입자감(Grainy artifacts)이 발생하지만, 완전히 로컬인 브라우저 생성 환경과 16GB M1 기기에서 실행 가능하다는 점을 고려하면 수용 가능한 수준입니다.
2. 병렬 다운로드 + 직렬 WebGPU 초기화
병렬 네트워크 페치 (Parallel network fetch)
첫 실행 시의 지연 시간(Latency)을 줄이기 위해 모델 파일들을 병렬로 다운로드합니다:
const responses = await Promise.all(
paths.map(path => fetchModelFile(path))
);
직렬 GPU 초기화 (중요한 수정 사항)
네트워크 요청은 병렬화가 가능하지만, WebGPU 세션 생성은 병렬화할 수 없습니다.
여러 개의 대규모 ONNX 세션을 병렬로 초기화하면, 특히 Apple Silicon에서 즉각적인 통합 메모리(Unified Memory) 스파이크가 발생하며, 이는 종종 WebGPU 할당 실패나 탭 크래시(Tab crash)로 이어집니다.
우리는 엄격한 순차적 파이프라인(Sequential pipeline)을 강제합니다:
Text Encoder → Number Conditioner → DiT → Audio Decoder
핵심 엔지니어링 규칙 (Core engineering rule)
네트워크 I/O는 병렬화할 수 있지만, WebGPU 리소스 초기화는 반드시 직렬화(Serialized)되어야 합니다.
Web Worker에서 한 번 초기화되면, 모든 GPU 세션은 이후의 생성 작업에 재사용되어 재로드 오버헤드를 방지합니다.
3. 다국어 프롬프트 파이프라인 (Multilingual Prompt Pipeline) (자동 번역 + 구조화된 프롬프트)
Stable Audio 모델은 기본적으로 영어만 이해합니다.
전 세계 사용자를 지원하기 위해, 우리는 완전 자동화된 다국어 프롬프트 파이프라인을 구축했습니다:
사용자 모국어 프롬프트
→ 언어 감지 (Language detection)
→ Chrome 내장 번역
...
또한 스타일(Style), 무드(Mood), 악기(Instrument), BPM, 그리고 보컬 제외(No-vocal) 제약 조건과 같은 표준화된 메타데이터를 자동으로 추가합니다.
변환 예시:
원문 중국어 입력
melancholic piano in a rainy café
최종 구조화된 프롬프트
melancholic jazz piano in a rainy café,
cinematic soundtrack,
dreamy,
...
번역을 지원하지 않는 브라우저의 경우, 조용히 생성에 실패하는 것을 방지하기 위해 우아하게 폴백(Fallback)하여 사용자에게 영어 입력을 요청합니다.
4. 대부분의 오디오 품질 문제는 양자화(Quantization) 때문이 아닙니다
초기에 생성된 오디오는 다음과 같은 문제를 겪었습니다:
- 거친 고주파 노이즈 (Grainy high-frequency noise)
- 흐릿한 악기 질감 (Blurry instrument texture)
- 불안정한 리버브 잔향 (Unstable reverb tails)
- 느슨한 리듬 구조 (Loose rhythm structure)
우리는 처음에 Q4 양자화(Quantization)로 인한 품질 저하를 의심했습니다.
심층 조사 결과, 실제 근본 원인은 잘못된 샘플러 스케줄(Sampler schedule) 구현이었습니다.
교정된 Rectified Flow 샘플링 로직
denoised = x - tCurrent * velocity;
xNext = (1 - tNext) * denoised + tNext * randomNoise;
시간 파라미터 t는 반드시 1에서 0으로 엄격하게 감쇠(Decay)해야 합니다.
우리의 기존 스케줄은 약 0.27까지만 감쇠되었으며, 이로 인해 디코딩 전 최종 잠재 공간(Latent)에 **27%의 잔류 노이즈(Residual noise)**가 남게 되었습니다. 이것이 오디오 품질을 파괴했습니다.
수정된 LogSNR 스케줄 (Fixed LogSNR schedule)
const logSnr = 2 - t * 8.2;
const sigma = 1 / (1 + Math.exp(logSnr));
...
이 단 한 번의 수정이 샘플링 단계(Sampling steps)를 늘리는 것보다 훨씬 더 오디오 품질을 개선했습니다.
핵심 요약 (Key takeaway)
로컬 모델의 생성 품질 저하는 대개 모델 양자화 (Quantization) 때문이 아니라, 전처리(Preprocessing) 또는 스케줄러(Scheduler)의 버그로 인해 발생합니다.
5. 잠재 출력에서 최종 WAV까지 (From Latent Output to Final WAV)
잠재 공간(Latent)의 길이는 목표 오디오 길이에 따라 동적으로 계산됩니다:
latentLength =
Math.ceil((seconds + 6) * 44100 / 8192) * 2;
DiT는 잠재 텐서(Latent tensors)를 출력하며, 디코더(Decoder)는 다음을 생성합니다:
- 형태 (Shape):
[1, 2, audioFrames] - 형식 (Format): Float32
- 범위 (Range): -1 ~ 1
- 샘플링 레이트 (Sample rate): 44100Hz
우리는 부동 소수점(Float) 샘플을 표준 16비트 PCM WAV로 변환합니다:
sample< 0 ? sample * 32768 : sample * 32767;
최종적으로 오디오 에셋(Audio asset)은 자동으로 다음과 같이 처리됩니다:
- WAV Blob으로 변환
- 정확한 길이를 위한 디코딩
- 파형 분석 (Waveform-analyzed)
- 프롬프트/모델 메타데이터와 함께 저장
- 에디터 에셋 라이브러리로 임포트
6. 긴 오디오 생성: 직접적인 120초 추론을 피하는 이유 (Long Audio Generation: Why We Avoid Direct 120s Inference)
브라우저에서 직접적인 장문 오디오 추론(90초/120초)을 수행하면 다음과 같은 문제가 발생합니다:
- WebGPU 버퍼 할당 실패
- 과도한 메모리 사용
- 탭 프리징(Freezing) 또는 시스템 프로세스 강제 종료
- 디코더 메모리 압박(Memory pressure) 피크 발생
이를 해결하기 위해, 우리는 **세그먼트 생성 + 지능형 루핑 전략 (Segmented generation + intelligent looping strategy)**을 사용합니다:
- 90초 목표 → 45초 세그먼트 생성
- 120초 목표 → 60초 세그먼트 생성
최종 긴 오디오는 고품질의 짧은 세그먼트들을 스티칭(Stitched)하여 만들어지며, 이를 통해 VRAM과 추론 시간을 거의 절반으로 줄일 수 있습니다.
7. 매끄러운 오디오 스티칭 (Seamless Audio Stitching: 클릭음이나 끊김 없음)
단순한 루프 이어붙이기는 다음과 같은 명확한 아티팩트(Artifacts)를 생성합니다:
- 볼륨 점프 (Volume jumps)
- 드럼 히트의 불일치 (Misaligned drum hits)
- 위상 불연속성 (Phase discontinuity)
- 들리는 클릭음/일시 정지 (Audible clicks/pauses)
우리는 소스 세그먼트의 마지막 5초를 분석하고 다음 항목들을 사용하여 후보 컷 지점(Cut points)의 점수를 매깁니다:
- RMS 에너지 (RMS energy)
- 진폭 점프 차이 (Amplitude jump difference)
- 파형 기울기 점프 (Waveform slope jump)
- 단축 비율 페널티 (Shorten ratio penalty)
통합 점수 공식 (Unified scoring formula):
score =
rms * 0.7
+ amplitudeJump * 0.8
...
점수가 낮을수록 더 나은 루프 경계(loop boundary)를 의미합니다.
또한, 국소적인 오디오 에너지(local audio energy)를 기반으로 동적 페이드 지속 시간 (dynamic fade duration) (0.25s ~ 1.5s)을 사용합니다:
fadeSeconds = clamp(
0.25 + localRms * 4,
0.25,
...
에너지가 높은 영역에는 더 긴 페이드(fade)를 적용하고, 조용한 영역에는 자연스러운 루핑(looping)을 위해 짧은 전환(transition)을 사용합니다.
8. 모델 캐싱 (Model Caching): 추론보다 어려운 작업
683MB 크기의 모델을 지속적으로 캐싱하기 위해 **서비스 워커 (Service Worker) + 캐시 스토리지 (Cache Storage)**를 사용합니다.
우리는 프로덕션 환경에서 치명적인 두 가지 브라우저 캐시 문제를 해결했습니다.
문제 1: 캐시 히트(Cache hit) 오판
커스텀 HTTP 헤더는 서비스 워커 프록시(Service Worker proxying) 이후 교차 출처(cross-origin) 환경에서 신뢰성 있게 노출될 수 없습니다.
이로 인해 모델이 로컬 캐시에서 로드되었음에도 불구하고 UI에 "다운로드 중"이라고 표시되는 문제가 발생했습니다.
해결책: 응답 헤더(response headers)에 의존하는 대신, AI 워커 (AI worker)에서 캐시 스토리지 (Cache Storage)를 직접 쿼리합니다.
문제 2: QuotaExceededError (할당량 초과 오류)
AI 워커와 서비스 워커 양쪽에서 발생하는 이중 쓰기(Dual-write)로 인해 일시적으로 모델 점유 공간이 중복되어 브라우저 저장소 할당량(storage quota)을 초과하는 문제가 발생했습니다.
최종 캐시 아키텍처:
- 서비스 워커 (Service Worker)만 캐시에 기록함
- AI 워커 (AI Worker)는 캐시를 읽고 검증만 수행함
- 캐시 실패가 생성(generation) 과정을 차단하지 않음
- 오래된 모델 캐시 버전은 자동으로 정리함
대규모 모델 캐싱에는 쓰기 작업을 위한 단일 진실 공급원(single source of truth)이 필요합니다. 캐시 로직이 핵심 추론(inference)을 절대 방해해서는 안 됩니다.
9. 모델 미러링 (Model Mirroring) 및 불변 버전 잠금 (Immutable Version Locking)
공개된 Hugging Face 저장소는 프로덕션 용도로 사용하기에 불안정합니다. 작성자가 가중치(weights)를 삭제, 덮어쓰기 또는 업데이트할 수 있기 때문입니다.
우리는 모델을 완전히 미러링하고 고정(frozen)했습니다:
- 미러 저장소 (Mirror repo):
haixin/stable-audio-3-small-music-onnx - 고정된 불변 커밋 해시 (Fixed immutable commit hash):
0b8a05e0bc3511e674b4cb3413d3ef6c48880cdb
우리는 다음 사항들을 검증했습니다:
- 26개의 모델 파일
- 683MB 전체 바이트 무결성
- SHA256 해시
- ONNX 그래프 및 가중치 샤드 (weight shards)
- 라이선스 및 고지 파일
이전 캐시 항목들과 호환되므로, 기존 사용자는 다시 다운로드할 필요가 없습니다.
10. 실제 경계 및 브라우저 네이티브 AI 음악의 가치
한계점
- Q4 양자화 (quantization)는 서버 측의 FP16/FP32 품질을 따라갈 수 없음
- 긴 오디오 (Long audio)는 전체 추론 (inference) 대신 지능적인 루핑 (looping)에 의존함
- WebGPU 지원은 장치 및 브라우저 구현에 따라 달라짐
독보적인 강점
- 서버 측 추론 비용 제로
- 100% 로컬 처리 (프롬프트 및 오디오가 절대 업로드되지 않음)
- 1회 모델 다운로드 + 지속적인 캐시 (cache)
- 빠른 후속 생성을 위한 GPU 세션 재사용 (Hot GPU session reuse)
- 비디오 편집 워크플로우에 네이티브하게 통합
- 정적 호스팅 (static hosting)을 통해 배포 가능
결론
브라우저에서 프로덕션 수준의 AI 음악을 실행하는 것은 단순히 ONNX 추론을 작동시키는 것만이 아닙니다.
이는 다음과 같은 세심한 시스템 수준의 엔지니어링을 요구합니다:
- 정확한 샘플러 (sampler) 스케줄링
- WebGPU 메모리 및 동시성 (concurrency) 제어
- 지능적인 긴 오디오 스티칭 (long audio stitching)
- 견고한 캐싱 (caching) 전략
- 불변의 모델 공급망 (Immutable model supply chain)
이러한 해결책들을 통해, 683MB의 양자화된 음악 모델은 사용 가능하고 안정적인 프로덕션급 브라우저 AI 기능이 될 수 있습니다.
오픈 소스 프로젝트
브라우저 측 AI, WebGPU 추론 또는 로컬 미디어 생성에 관심이 있다면:
GitHub: martindelophy/ai-video-editor
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기