원문 캡처 · github.com
원문 캡처 · github.com

Auricle은 음성 활동 감지와 대화 차례 관리, 상대방이 말할 때 에이전트 음성을 낮추거나 멈추는 기능을 브라우저에서 제공하는 오픈소스 라이브러리다. 제작자는 Rust 프로그램 하나를 WebAssembly로 컴파일해 구현했으며, OpenAI 호환 음성 인식·채팅·음성 합성 API와 연결할 수 있다고 설명한다.

브라우저에서 작동하는 음성 에이전트 도구

Auricle은 추론 런타임이나 별도 모델 파일을 복사할 필요 없이 사용할 수 있으며, @ricky0123/vad-web에서 한 줄의 import를 바꾸는 방식으로 전환할 수 있다고 제작자는 소개한다. npm 패키지는 auricle과 React 훅을 제공하는 auricle-react 두 가지다. 제작자가 Chromium 145와 WebKit 26에서 측정한 결과는 다음과 같다.

측정 항목Auricle v1 탑재 버전vad-web의 Silero v6 가중치를 사용한 Auricle 엔진vad-web
32ms 프레임당 처리 시간0.035~0.038ms0.050~0.052ms0.18ms
모델 준비까지 걸리는 시작 시간4~10ms13~15ms175~215ms
다운로드 크기(gzip)350KB948KB5,520KB
같은 가중치를 썼을 때 onnxruntime 대비 출력 차이7.8e-7같은 모델—
사용자 발화가 끝난 뒤 발화 종료까지 걸리는 시간(라벨링 데이터, 재현율 100%)324ms—1,432ms
끼어들기 때 음성 낮추기·즉시 중단, 실제로 들은 텍스트 기록, 백엔드 어댑터지원—미지원

제작자가 npm 패키지에 포함한 감지기는 직접 학습한 Auricle v1이다. 제작자의 비교에서는 다른 사람이 같은 방에서 말하는 상황에서 Auricle v1의 전경 음성 F1 점수는 94.2%, 배경 음성을 잘못 감지하는 비율은 35%였다. Silero v6는 각각 89.4%, 82%였다. 사람의 단일 화자 음성을 라벨링한 데이터에서는 두 모델의 정확도가 같았으며, 7개 언어를 대상으로 시험했다고 설명한다. 수치와 재현 명령은 비교 문서에, 모델 정보는 모델 카드에 공개돼 있다.

데모에서는 API 키 없이 음성 구간을 녹음할 수 있다. Groq 키를 입력하면 대화 데모를 사용해 에이전트가 말하는 도중 끼어들 수 있다.

설치와 사용 단계

기본 패키지와 React 훅 패키지는 다음과 같이 설치한다.

sh
npm install auricle # core
npm install auricle-react # React hooks (peer: auricle, react >= 18)

번들러 없이 스크립트 태그로 사용할 수도 있다.

html
<script src="https://cdn.jsdelivr.net/npm/auricle@0.1.0/dist/auricle.iife.js"></script>
<!-- or https://unpkg.com/auricle@0.1.0/dist/auricle.iife.js; defines window.Auricle. The script build carries both detectors (1.4 MB gzipped); the ESM build loads only the one you ask for. -->

제작자는 세 가지 사용 수준을 제시한다. 첫 번째는 음성 감지기다. 몇 줄의 코드로 vad-web을 대체하며, 발화 후보와 발화 확정, 구간 종료, 오감지, 32ms마다 발생하는 프레임 이벤트 등을 처리한다.

js
import { createVad } from 'auricle';

const vad = await createVad({
 onSpeechStart: ({ tMs }) => ui.listening(), // candidate: 3 voiced frames, reversible actions
 onSpeechConfirmed: () => ui.speaking(), // 6 of the last 10 frames: irreversible actions
 onSegment: (s) => upload(s.wav()), // finished segment: audio, startMs, endMs, wav()
 onMisfire: () => ui.listening(),
 onFrame: (p) => meter.set(p), // every 32 ms
 thresholds: { hangoverMs: 240 }, // vad-web waits 1,400 by default
});
await vad.start(); // from a click: it asks for the microphone
vad.pause(); vad.resume(); vad.setThresholds({ minSpeechMs: 200 });
console.log(vad.stats.sttShare); // share of the session that was sent to speech-to-text

제작자는 공개 메서드와 옵션, 이벤트, 타입마다 JSDoc 주석을 달아 편집기나 언어 모델이 문서를 열지 않고도 기능을 파악할 수 있다고 설명한다.

두 번째는 vad-web에서 옮겨오는 호환 계층이다. 기존 API를 유지하면서 import를 바꾸는 방식이다.

js
import { MicVAD, utils } from 'auricle/compat'; // was '@ricky0123/vad-web'
const vad = await MicVAD.new({ onSpeechEnd: (audio) => upload(utils.encodeWAV(audio)) });
vad.start();

이 방식으로 옮길 때 baseAssetPath, onnxWASMBasePath와 복사해 둔 파일을 제거할 수 있다고 한다. 매핑과 차이점은 마이그레이션 안내에 정리돼 있다.

세 번째는 대화 전체를 연결하는 방식이다. OpenAI 호환 엔드포인트를 지정하면 발화 후보 단계에서 에이전트 음량을 낮추고, 발화가 확인되면 멈춘다. 사용자에게 실제로 들린 부분까지만 에이전트 대화 기록에 남긴다.

js
import { Auricle, OpenAICompat } from 'auricle';

const auricle = await Auricle.create({
 backend: new OpenAICompat({
 baseURL: 'https://api.groq.com/openai/v1',
 apiKey,
 models: { stt: 'whisper-large-v3-turbo', llm: 'openai/gpt-oss-20b', tts: 'canopylabs/orpheus-v1-english' },
 voice: 'autumn',
 }),
 profile: 'speakers', // or 'headphones': different thresholds while the agent talks
});
auricle.on('state', (s) => ui.set(s)); // listening | user_speaking | thinking | agent_speaking
auricle.on('turn', (t) => log(t.clean, t.verbatim)); // clean text for the model, verbatim for debugging
auricle.on('interrupted', (e) => log(e.heardText, e.stopLatencyMs));
auricle.on('metrics', (m) => log(m.endToFirstAudioMs));
await auricle.start();
console.log(formatStats(auricle.stats)); // session 2:14 · sent to STT 0:31 (23%) · TTS chars 812, avoided 1,040 · ...

auricle.stats와 vad.stats, useVad().stats는 세션에서 실제로 브라우저 밖으로 전송된 양을 센다. 음성 인식으로 보낸 오디오의 길이와 전체 세션 길이, 사용자가 끼어들어 듣지 못한 합성 음성의 글자 수와 모델이 작성한 글자 수, 끼어들기 후 오디오를 멈추는 데 걸린 시간의 중앙값을 기록한다. 제작자는 이를 추정치가 아니라 해당 세션에서 발생한 이벤트를 센 값이라고 설명한다. 비용은 이용자가 각 서비스의 분당·글자당 요금을 적용해 계산할 수 있으며, 요금이 매달 바뀌므로 Auricle은 달러 기준 비용을 공개하지 않는다고 덧붙였다.

React에서는 useVad, useAuricle, useMicVAD 훅을 사용할 수 있다.

js
import { useVad, useAuricle, useMicVAD } from 'auricle-react';

const { listening, speaking, probability, segments, stats, toggle } = useVad({ onSegment: (s) => upload(s.wav()) });
const { state, turns, agentText, heardText, stats, start, interrupt } = useAuricle({ backend });
const mic = useMicVAD({ onSpeechEnd: upload }); // the @ricky0123/vad-react shape

전체 옵션과 이벤트 설명은 web/src/auricle.ts, web/src/vad.ts, web/src/compat.ts와 react/src의 TypeScript 타입에 있다.

구성 요소와 처리 방식

저장소는 Rust 기반 오디오 처리·감지·대화 차례 관리 코드와 브라우저용 패키지, 데모 및 벤치마크 도구로 구성돼 있다.

경로·이름종류역할
crates/auricle-dspRust다상 리샘플러, 링 버퍼, 프레임 분할기. 처리 경로에서 메모리를 할당하지 않는다.
crates/auricle-vadRustAuricle v1을 직접 작성한 순전파 방식으로 Vad 트레이트 뒤에 둔다. PyTorch와 동등성 시험을 하며 런타임은 쓰지 않는다. Silero VAD v6는 엔진 벤치마크를 위해 선택형 Cargo 기능으로 제공하고, onnxruntime과 비교한다.
crates/auricle-turnRustspeech_candidate, speech_confirmed, silence_candidate, speech_end, misfire 이벤트와 음량 낮추기·복구·중단을 결정하는 끼어들기 중재기를 제공한다. 임계값은 프로필별로 설정한다.
crates/auricle-textRust하위 수열 검증기를 이용해 말버릇, 반복, 더듬음을 삭제 방식으로 정리하고 발언권 유지 단서를 처리한다.
crates/auricle-workletRust, C-ABI WASM캡처 작업자는 오디오를 16kHz로 리샘플링하고 512샘플 프레임으로 나눈다. 재생 작업자는 링 버퍼를 사용해 30ms 동안 음량을 낮추고, 즉시 중단할 때 15ms 페이드아웃을 적용하며 재생 샘플을 센다.
crates/auricle-brainRust, wasm-bindgen음성 감지기, 차례 처리기, 텍스트 정리를 실행하는 작업자 진입점이다.
crates/auricle-evalRust CLIWAV 파일을 같은 크레이트로 재생해 이벤트와 프레임별 처리 비용을 출력한다. --silero를 지정하면 동등성 벤치마크용 Silero 빌드로 전환한다.
web/npm auriclecreateVad, Auricle, auricle/compat, 오디오 그래프, Chromium AEC 루프백, OpenAI 호환 백엔드, curl 파서, 실제로 들은 텍스트에 맞춰 기록을 자르는 기능을 제공한다.
react/npm auricle-reactuseVad, useAuricle, useMicVAD를 제공한다.
demo/Vite 사이트GitHub Pages의 데모, 벤치마크, 모델 카드, 블로그다.
bench/Node, Python, Playwright@ricky0123/vad-web과 엔진 속도, 라벨링 데이터 정확도, TEN 데이터셋, 브라우저 지원, 지연 시간을 비교한다.
tools/Python, HTMLSilero ONNX 가중치 내보내기, Auricle v1 동등성 테스트 자료, 브랜드·차트 렌더러, Open Graph 이미지 도구를 포함한다.

마이크 입력은 Rust 캡처 작업자에서 리샘플링과 프레임 분할을 거쳐 Rust 작업자의 음성 감지기와 차례 처리기로 전달된다. 재생 경로에서는 Rust 작업자가 TTS 오디오를 재생하고 음량을 낮추거나 페이드아웃한다. 메인 스레드는 STT와 채팅(SSE), TTS를 fetch로 처리하고, 사용자가 실제로 들은 내용에 맞춰 대화 기록을 자른다.

음량을 낮추거나 재생을 멈추는 결정은 메인 스레드나 네트워크를 거치지 않는다. 제작자 설명에 따르면 감지기는 작업자에서 실행되고 오디오는 작업자에서 처리된다.

Auricle v1의 학습과 비교 결과

npm 패키지에는 Auricle v1 감지기 하나가 포함된다. 이 모델은 83,393개 매개변수와 334KB의 가중치로 이뤄졌으며 MIT 라이선스를 적용했다. 모델은 Hugging Face의 auricle-voice/auricle-v1에서 공개됐다. 16ms 단위로 점수를 계산하며, 일반적인 감지기가 다루지 못한다고 제작자가 설명한 같은 방의 다른 사람이나 텔레비전 소리를 구별하는 데 초점을 뒀다.

Auricle v1은 공개 오디오를 사용해 처음부터 학습했다. 깨끗한 음성의 프레임 라벨은 Silero VAD로 초기 생성했다. 제작자는 배경 화자의 음성을 무시하도록 데이터 구성 방법을 설계했다고 설명한다.

  • LibriSpeech의 낭독 음성 961시간에 16ms마다 음성 또는 무음 라벨을 붙였다. 라벨은 모델에 입력하지 않을 깨끗한 녹음에서 정했으며, 에너지 기준은 -60 dBFS였다.
  • 학습에 입력하는 5초 클립은 한 화자의 발화 1~3개와 현실적인 간격으로 구성했다. 클립의 50%에는 RIRS_NOISES에서 측정한 실내 임펄스 응답을 적용했다. 85%에는 MUSAN의 소음이나 음악을 신호 대 잡음비 -5~25dB로 섞었다.
  • 클립의 45%에는 사용자보다 3~25dB 작은 다른 화자의 음성을 넣고 이를 음성이 아닌 것으로 라벨링했다. 이 가운데 4분의 1에서는 한 사람의 목소리 대신 MUSAN 군중 소음을 넣었다. 20%에는 VocalSound의 웃음·기침·한숨·코 훌쩍임을 사용자와 같은 수준으로 발화 간격에 넣고 음성이 아닌 것으로 라벨링했다.
  • 전화 대역 제한은 20%, 클리핑은 10%의 클립에 적용했다. 음량은 -30~0dB 사이에서 무작위로 조정했다. 클립의 12%에는 전경 음성을 아예 넣지 않아 잡음 속 무음을 학습하도록 했다.
  • 오디오를 변형해도 깨끗한 녹음에서 만든 라벨은 바꾸지 않았다. 배경 화자만 말하는 프레임은 무음으로 표시했다. 제작자는 이 방식으로 전경 화자와 다른 소리를 구분하는 능력을 학습시켰다고 설명한다.
  • 모델은 40밴드 로그 멜 특징, 커널 5·채널 64인 인과 합성곱 두 층, 유닛 64개인 GRU 두 층, 시그모이드 하나로 구성했다. 미래 오디오를 미리 보지 않도록 만들어 브라우저가 이후 오디오를 기다리지 않는다. 매개변수는 83,393개, 가중치는 334KB다.
  • 학습은 CPU 한 대에서 진행했다. 128개 클립을 묶어 20,000회 학습했고, AdamW 학습률은 2e-3, 코사인 감쇠와 1e-2 가중치 감쇠를 사용했다. 가중치의 지수이동평균을 적용하고 라벨이 바뀌는 지점 주변 두 프레임은 손실 가중치를 절반으로 뒀다. 순환 상태가 안정되는 동안의 첫 0.5초는 제외했다. 6코어 노트북 CPU에서 9.7시간이 걸렸으며 GPU는 쓰지 않았다.
  • 정확도는 라벨 생성기와 비교하는 대신 사람의 라벨과 비교했다. TEN VAD의 사람이 주석을 단 30개 녹음에서 정확도를 측정했고, 두 사람이 말하는 상황은 학습에 쓰지 않은 화자의 음성을 모델이 보지 못한 방의 조건에서 평가했다. 7개 언어는 FLEURS에서 측정했다. Rust 구현은 같은 클립을 사용해 PyTorch와 1e-4 이내로 일치하는지 확인했다.
  • 학습 후 두 가지 시도는 결과를 바꾸지 않아 채택하지 않았다. 하나는 더 어려운 두 화자 혼합음을 추가하는 방식이었고, 다른 하나는 6개 언어의 오디오를 각각 10시간 추가하는 방식이었다. 자세한 내용은 연구 기록에 있다.

제작자가 같은 오디오에서 비교한 수치는 다음과 같다.

평가 항목Auricle v1Silero VAD v6
TEN VAD 사람 라벨 데이터의 F192.7%93.6%
다른 화자가 말할 때 전경 음성 F194.2%89.4%
다른 화자의 음성을 음성으로 판정한 프레임35%82%

제작자는 같은 방에서 사용자의 음성보다 3~15dB 작은 두 번째 화자가 말하는 조건에서 모든 임계값을 살펴보면, 사용자 음성의 보존 비율이 같을 때 Auricle v1이 상대방 음성을 3~4배 적게 통과시킨다고 설명한다. 한 사람만 말하는 조건에서는 Silero v6의 점수가 약 1점 높았다. 자세한 수치는 정밀도-재현율 곡선 비교에서 확인할 수 있다.

Silero VAD v6는 npm 패키지에 들어 있지 않다. Rust 크레이트에서 선택형 silero Cargo 기능으로 제공해, 같은 가중치를 사용한 vad-web 엔진 비교를 재현할 수 있게 했다. 학습 코드는 VAD-train에 공개했다. 학습 방법과 언어, 부정적 결과 및 주의점은 모델 카드와 연구 기록에 있다.

데모 이용 조건과 한계

데모 사이트는 키를 입력하지 않으면 음성 감지 녹음기로 작동한다. 감지된 각 구간은 재생 가능한 클립으로 저장된다. Play pretend agent를 누르면 API 없이 음량 낮추기와 끼어들기를 시험할 수 있다. Groq 키를 입력하면 대화 모드가 활성화되고, 에이전트에게 끼어들면 대화 말풍선이 실제로 들은 단어까지만 잘린다.

화면의 오디오 추적 아래에는 음성 인식으로 전송한 오디오와 전체 세션 길이, 합성한 글자 수와 끼어들기로 피한 글자 수, 중단 지연 시간의 중앙값이 표시된다. 내보내기 기능은 이벤트와 대화 차례, 오디오 구간을 담은 JSON 묶음을 로컬에 저장한다. 입력한 엔드포인트 외에는 어디에도 업로드하지 않는다고 제작자는 설명한다.

웹페이지에 입력한 키는 해당 페이지의 코드에서 볼 수 있으므로, 제작자는 범위가 제한되거나 수명이 짧은 키 또는 프록시를 사용할 것을 안내한다. 브라우저에서 호출하려면 엔드포인트가 CORS 헤더를 보내야 한다. Groq는 이를 지원하며, 자체 호스팅 서버에는 보통 별도 설정이 필요하다. 실제 키와 CORS를 추가하는 10줄짜리 Cloudflare Worker 예제는 마이그레이션 안내의 인접 자료에 있다고 설명한다. 데모의 Advanced 패널은 오류를 쉬운 말로 안내한다.

Chromium 기반 브라우저에서는 localhost를 제외한 일반 http:// 엔드포인트가 HTTPS 페이지에서 차단된다.

제작자가 명시한 미검증 범위도 있다. 단일 화자 음성 감지 정확도는 Silero v6보다 높지 않으며, Auricle v1은 사람이 라벨링한 녹음에서 F1 점수가 약 1점 낮다. 다른 화자가 말하는 상황의 수치는 회의 데이터셋 벤치마크가 공개되기 전까지 생성한 클립을 대상으로 한 결과다. Firefox와 모바일 측정값, 실제 환경에서의 잘못된 끼어들기 비율, 실제 운영 사용자는 아직 없다고 한다. 제작자는 vad-web에는 이런 현장 데이터가 3년간 쌓여 있으며, 데모의 내보내기 기능은 해당 데이터를 모으기 위해 마련했다고 밝혔다.

소스 빌드와 문서

소스에서 빌드하려면 Rust stable과 wasm32-unknown-unknown 대상, 크레이트와 버전을 맞춘 wasm-bindgen-cli 0.2.129, binaryen의 wasm-opt, Node 22가 필요하다.

sh
cargo test # 16 Rust tests, including both parity tests
./scripts/build-wasm.sh # worklet + brain WASM into web/src/generated
npm install
npm test # vitest for web and react
npm run build # web (ESM, compat, IIFE, types), react, demo
npm run dev # the demo on a local port, library from source

벤치마크는 다음 명령으로 실행한다.

sh
cd bench && npm install && npm run bench
npm run accuracy
npm run browsers
npm run ten
OPENAI_API_KEY=... npm run latency

각 스크립트의 출력이 docs/COMPARISON.md에 인용한 결과라고 제작자는 설명한다. vX.Y.Z 태그를 만들면 릴리스 워크플로가 테스트와 빌드를 실행하고 provenance를 포함해 두 패키지를 npm에 게시하며, 라이브러리 파일을 GitHub 릴리스에 첨부한다. Pages는 main에 푸시할 때마다 배포된다.

저장소에는 비교 문서, 모델 카드, vad-web 마이그레이션 안내, 연구 기록, 로드맵, 사이트 문서, 변경 기록이 있다. 라이선스는 MIT다.