보이스와 오디오
Nodaro SDK로 TypeScript에서 보이스를 찾고, 바꾸고, 화자별로 교체하고, 새로 디자인하며, 동영상을 더빙하고, 오디오를 분리, 믹스, 텍스트로 변환합니다.
client.voices는 ElevenLabs 보이스를 다룹니다. 보이스를 나열하고 검색하며, 녹음 속 보이스를 바꾸고, 화자마다 새 보이스를 주고, 새 보이스를 디자인하고, 오디오와 동영상을 다른 언어로 더빙합니다. client.audio는 오디오의 구성 요소를 하나씩 따로 제공합니다. 분리, 음성 추출, 효과, 믹스, 볼륨, 합치기, 음성 텍스트 변환입니다. 모든 생성 메서드는 jobId를 반환하며, 이를 client.jobs.getStatus()로 폴링하세요. 이 메서드들은 보이스와 미디어 REST API를 호출합니다.
메서드
| 메서드 | 기능 |
|---|---|
voices.list() | 기본 제공 보이스 나열 |
voices.searchLibrary(params?) | 커뮤니티 보이스 라이브러리 검색 |
voices.listClones() | 내 기존 복제 보이스 나열 |
voices.deleteClone(id) | 내 복제 보이스 하나 삭제 |
voices.createClone()과 createCloneFromFile() | 종료됨 |
voices.change(input) | 녹음이나 동영상 속 보이스 바꾸기 |
voices.recast(input) | 화자마다 다른 보이스 주기 |
voices.analyze(input) | 보이스 교체 전에 화자 감지 |
voices.exportMix(input) | 믹스한 보이스 스템으로 동영상 렌더링 |
voices.design(input) | 설명으로 새 보이스 만들기 |
voices.remix(input) | 설명한 보이스로 텍스트 읽기 |
voices.dub(input) | 오디오나 동영상을 다른 언어로 더빙 |
voices.textToDialogue(input) | 여러 화자의 스크립트를 오디오 파일 하나로 음성화 |
audio.separate(input) | 트랙을 스템으로 분리 |
audio.isolate(input) | 주 음성만 남기고 노이즈 제거 |
audio.applyFx(input) | 리버브, 에코, 전화기, 메가폰 효과 추가 |
audio.mix(input) | 여러 트랙을 하나로 겹치기 |
audio.adjustVolume(input) | 레벨 변경, 노멀라이즈, 페이드 |
audio.pitchTempo(input) | 피치 변경, 또는 피치를 바꾸지 않고 템포 변경 |
audio.combine(input) | 오디오 세그먼트를 이어 붙이기 |
audio.transcribe(input) | 음성을 단어 타이밍과 함께 텍스트로 변환 |
client.voices: 보이스
voices.list()
기본 제공 ElevenLabs 보이스를 나열합니다(GET /v1/voices). 서버에 ElevenLabs 키가 설정되어 있지 않으면 대신 엄선된 보이스 세트를 반환합니다.
list(): Promise<Voice[]>const voices = await client.voices.list()voices.searchLibrary(params?)
커뮤니티 보이스 라이브러리를 검색합니다(GET /v1/voices/library). 모든 매개변수는 선택 사항이며, 빈 값은 빠지므로 서버 기본값이 적용됩니다. 응답의 hasMore는 다음 페이지가 있는지 알려 줍니다.
searchLibrary(params?: VoiceLibraryParams): Promise<{ voices: Voice[]; hasMore: boolean }>속성
타입
const { voices, hasMore } = await client.voices.searchLibrary({ search: "deep", language: "en" })
const voice = voices[0]
await client.nodes.run("text-to-speech", {
text: "Hello!",
voice: voice.voice_id,
voiceType: "library",
...(voice.recommendedProvider ? { provider: voice.recommendedProvider } : {}),
})각 보이스에는 그 보이스가 검증된 음성 모델에 대한 힌트가 두 가지 담길 수 있습니다.
recommendedProvider는 그 보이스에 가장 잘 맞는 모델입니다. 모델 선택기가 없는 앱은 이 값을 텍스트 음성 변환에provider로 보내야, 보이스가 라이브러리 미리 듣기와 같은 소리를 냅니다.verifiedProviders는 그 보이스가 검증된 모든 모델의 목록입니다. 모델 선택기가 있는 앱은 사용자가 고른 모델이 이 목록에 없을 때만 선택을 바꿔야 합니다.
두 힌트는 ElevenLabs v4, ElevenLabs v3, ElevenLabs v4 Turbo, Turbo v2.5, Multilingual v2 순서를 따릅니다. recommendedProvider는 verifiedProviders의 첫 항목입니다. 모델이 목록에 있는지 확인하고, 고정된 값 집합과 비교하지 마세요. 나중에 추가되는 음성 모델은 출시될 때 목록에 들어갑니다. 두 힌트는 설치에서 제공하는 모델만 가리킵니다. 그곳에서 검증된 모델이 없는 보이스에는 두 필드가 모두 없으므로, 사용자의 현재 모델을 유지하세요.
voices.listClones()
내 복제 보이스를 나열합니다(GET /v1/voice-clones). 복제 기능이 종료되기 전에 만든 복제 보이스는 보이스를 받는 모든 곳에서 여전히 보이스 id로 쓸 수 있습니다.
listClones(): Promise<VoiceClone[]>const clones = await client.voices.listClones()voices.deleteClone(id)
내 복제 보이스 하나를 삭제합니다(DELETE /v1/voice-clones/:id).
deleteClone(id: string): Promise<void>속성
타입
await client.voices.deleteClone(cloneId)voices.createClone()과 createCloneFromFile()
Nodaro는 더 이상 보이스 복제를 제공하지 않습니다. 이 기능은 2026년 9월에 종료되었습니다. 두 메서드는 클라이언트에 남아 있으며 사용 중단으로 표시되어 있습니다. 두 메서드는 code가 voice_cloning_retired이고 상태가 410인 NodaroError로 실패합니다. 새 사용자 지정 보이스가 필요하면 design()을 사용하세요.
client.voices: 보이스 체인저
voices.change(input)
보이스 체인저 노드처럼, 녹음이나 말하는 동영상 전체의 보이스를 다른 보이스로 바꿉니다(POST /v1/voice-changer). videoUrl을 주면 서버가 동영상에서 오디오를 꺼내 보이스를 바꾸고, 새 보이스를 원래 화면에 다시 입힙니다.
change(input: {
voiceId: string
audioUrl?: string
videoUrl?: string
model?: string
stability?: number
similarityBoost?: number
style?: number
useSpeakerBoost?: boolean
seed?: number
removeBackgroundNoise?: boolean
}): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.voices.change({
videoUrl: "https://example.com/talking.mp4",
voiceId: "Aria",
})
// the finished job's output_data has videoUrl and audioUrlvoices.recast(input)
보이스 체인저 Pro 노드처럼, 녹음에서 감지된 화자마다 다른 보이스를 줍니다(POST /v1/voice-changer-pro). Nodaro Cloud에서 실행되고, 크레딧이 들며, 작업으로 실행됩니다.
recast(input: VoiceChangerProInput): Promise<{ jobId: string }>속성
타입
orderedVoices의 각 항목은 다음 중 하나입니다.
- 보이스 id:
"Rachel"같은 기본 제공 보이스 이름이나 ElevenLabs 보이스 id입니다. null: 유지 슬롯입니다. 그 화자는 원래 목소리를 유지하고, 뒤의 화자는 계속 보이스가 바뀝니다. 보이스를 바꾸는 화자만 가격이 매겨지므로 유지 슬롯은 비용이 들지 않습니다.- 객체:
voiceId와 그 화자의 설정을 담습니다. 01 범위의4,294,967,295 범위의stability,similarityBoost,style, 그리고useSpeakerBoost, 0seed입니다.volumeMode는"match"(기본값, 원래 화자의 레벨),"normalize", 또는 퍼센트 단위 0200의1 사이의 어떤 값이든 받고volume을 쓰는"manual"입니다.engine: "v3"는 녹음된 음성을 변환하는 대신, 스크립트를 바탕으로 ElevenLabs v3로[audio tags]를 지원하며 대사를 새로 읽습니다.engine: "v4"는 같은 일을 ElevenLabs v4로 하며,stability로 0similarityBoost를 적용합니다.
화자 0은 orderedVoices[0]을, 화자 1은 orderedVoices[1]을 받는 식입니다. 마지막 항목 뒤의 화자는 원래 목소리를 유지합니다. 보이스와 음악은 항상 먼저 분리됩니다. preserveBackground는 음악을 다시 넣을지만 결정합니다.
voiceFx.preset은 리버브 공간(room, bathroom, car, hall, concert-hall, church, cave, arena, outdoor), telephone, megaphone, echo, custom 중 하나입니다. 리버브 프리셋은 0100 범위의 2,000 범위의 wetDryMix를 씁니다. echo와 custom은 20delayMs와 0~1 범위의 decay를 씁니다.
// Recast speakers 0 and 2, and keep speaker 1's own voice
const { jobId } = await client.voices.recast({
audioUrl: "https://example.com/panel.mp3",
orderedVoices: ["Rachel", null, "Aria"],
})
// Repeatable voices, a hall reverb, and finer separation
const { jobId: tuned } = await client.voices.recast({
audioUrl: "https://example.com/dialogue.mp3",
orderedVoices: [
{ voiceId: "Rachel", seed: 12345, stability: 0.6 },
{ voiceId: "Aria", seed: 67890, volumeMode: "manual", volume: 120 },
],
voiceFx: { preset: "hall", wetDryMix: 35 },
separationQuality: "best",
})동영상 모드에서는 완료된 작업의 output_data에 videoUrl과 audioUrl이 들어 있습니다.
voices.analyze(input)
클립의 화자를 보이스를 바꾸지 않고 감지합니다(POST /v1/voice-changer-pro/analyze). 보이스와 음악을 한 번 분리하고, 누가 언제 말하는지 찾습니다. Nodaro Cloud에서 고정 가격으로, 작업으로 실행됩니다.
analyze(input: {
audioUrl?: string
videoUrl?: string
separationQuality?: "fast" | "best"
suggestTitle?: boolean
}): Promise<{ jobId: string }>속성
타입
완료된 작업의 output_data는 VcpAnalysis입니다. 분리된 vocalsUrl과 backgroundUrl, 감지된 speakers(각각 id, 시간 segments, firstStartSec, wordCount, 텍스트 snippet 포함), languageCode와 languageProbability, 그리고 요청했을 때의 suggestedTitle이 담깁니다. 이후의 모든 recast()에 이것을 analysis로 전달하세요. 그러면 보이스 교체가 분리된 트랙을 재사용하고 감지 비용을 다시 내지 않습니다.
voices.exportMix(input)
믹스한 스템 세트로 최종 동영상을 렌더링합니다(POST /v1/voice-changer-pro/export). recast({ output: "stems" }) 다음의 마지막 단계입니다. Nodaro Cloud에서 고정 가격으로, 작업으로 실행됩니다.
exportMix(input: {
videoUrl: string
tracks: Array<{ url: string; gain: number; muted: boolean; kind?: "voice" | "background" }>
voiceFx?: { preset: AudioFxPreset; wetDryMix?: number; delayMs?: number; decay?: number }
}): Promise<{ jobId: string }>속성
타입
대화형 흐름은 한 번 분석하고, 스템으로 보이스를 바꾼 다음, 믹스하고 렌더링합니다. 작은 폴링 헬퍼를 쓴 예는 다음과 같습니다.
import type { VcpAnalysis } from "@nodaro/sdk"
async function outputOf(jobId: string): Promise<any> {
for (;;) {
const { data } = await client.jobs.getStatus(jobId)
if (data.status === "completed") return data.output_data
if (data.status === "failed" || data.status === "cancelled") throw new Error(data.error_message ?? data.status)
await new Promise((resolve) => setTimeout(resolve, 2_000))
}
}
const { jobId: analyzeJob } = await client.voices.analyze({ videoUrl })
const analysis = (await outputOf(analyzeJob)) as VcpAnalysis
const { jobId: recastJob } = await client.voices.recast({
videoUrl,
orderedVoices: ["Rachel", null, "Aria"],
output: "stems",
analysis,
})
const stems = await outputOf(recastJob)
const { jobId: exportJob } = await client.voices.exportMix({
videoUrl,
tracks: [
{ url: stems.tracks[0].url, gain: 100, muted: false },
{ url: stems.tracks[1].url, gain: 90, muted: false },
{ url: stems.backgroundUrl, gain: 70, muted: false, kind: "background" },
],
voiceFx: { preset: "hall", wetDryMix: 25 },
})
const { videoUrl: finalVideo } = await outputOf(exportJob)게인, 음소거, 효과는 동영상을 렌더링할 때 적용되고, 화면은 그대로 복사됩니다. 따라서 내보낸 결과는 미리보기와 일치하며, 내보내기 전에 믹스를 원하는 만큼 바꿀 수 있습니다. 모든 트랙을 음소거한 믹스는 400으로 거부됩니다.
client.voices: 만들기와 번역
voices.design(input)
보이스 디자인 노드처럼, 텍스트 설명으로 새 합성 보이스를 만듭니다(POST /v1/voice-design). 완료된 작업에는 오디오 미리 듣기와 새 보이스의 id가 담기며, 이 id는 보이스를 받는 모든 곳에서 쓸 수 있습니다.
design(input: {
text: string
voiceDescription: string
model?: string
loudness?: number
guidanceScale?: number
seed?: number
quality?: number
shouldEnhance?: boolean
userPrompt?: string
}): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.voices.design({
text: "Welcome back. Tonight we follow the river north, into the mountains where the story began.",
voiceDescription: "A calm, deep voice of an older male narrator with a slight British accent",
})voices.remix(input)
보이스 리믹스 노드처럼, 보이스를 만들지 않고 평범한 말로 설명한 보이스로 텍스트를 읽습니다(POST /v1/voice-remix).
remix(input: { text: string; voiceDescription: string; userPrompt?: string }): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.voices.remix({
text: "Your order is on its way.",
voiceDescription: "A cheerful young woman, fast and upbeat",
})voices.dub(input)
더빙 노드처럼, 각 화자의 목소리를 유지하면서 오디오나 동영상 전체를 다른 언어로 더빙합니다(POST /v1/dubbing). 동영상 더빙은 더빙된 클립인 output_data.videoUrl과, 더빙된 트랙만 담은 output_data.audioUrl을 반환합니다.
dub(input: DubbingInput): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.voices.dub({
videoUrl: "https://example.com/interview.mp4",
targetLanguage: "es",
numSpeakers: 2,
})가격은 더빙 구간의 분 단위 길이에 따라 정해지며, 최소 1분입니다. 한 구간은 최대 30분이므로, 더 긴 소스에는 startTime과 endTime을 쓰세요.
voices.textToDialogue(input)
텍스트로 대화 만들기 노드가 ElevenLabs Dialogue v3 또는 ElevenLabs Dialogue v4로 하듯이, 여러 화자의 스크립트를 하나의 오디오 파일로 음성화합니다(POST /v1/text-to-dialogue).
textToDialogue(input: {
dialogue: Array<{ text: string; voice: string }>
provider?: "elevenlabs-dialogue" | "elevenlabs-dialogue-v4"
stability?: number
similarityBoost?: number
languageCode?: string
seed?: number
applyTextNormalization?: "auto" | "on" | "off"
}): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.voices.textToDialogue({
dialogue: [
{ text: "Did you hear that?", voice: "Rachel" },
{ text: "[whispers] Stay behind me.", voice: "Callum" },
],
})스크립트는 Dialogue v3에서 최대 5,000자, Dialogue v4에서 최대 10,000자까지 담을 수 있으며, 2,000자 미만일 때 품질이 가장 좋습니다. 서로 다른 보이스는 최대 10개까지 쓸 수 있습니다. 라이브러리 보이스, 디자인한 보이스, 기존 복제 보이스를 원하는 대로 섞어 쓸 수 있습니다. 완료된 작업의 output_data.audioUrl이 그 파일입니다. 두 모델 모두 output_data.transcript에 타이밍이 들어 있습니다. startMs, endMs, speaker가 있는 words[]와 대사마다 하나씩인 segments[] 항목이며, speaker는 그 대사의 voice입니다. transcript.words를 autoTranscribe: false와 함께 addCaptions에 넘기면 다시 받아쓰지 않고 대화에 자막을 넣을 수 있습니다.
client.audio
보이스 체인저 Pro가 내부에서 쓰는 오디오 구성 요소를 하나씩 쓸 수 있습니다. 모든 메서드는 jobId를 반환합니다.
audio.separate(input)
음원 분리 노드처럼, 트랙을 스템으로 나눕니다(POST /v1/audio-separation).
separate(input: { audioUrl: string; mode?: "vocal_instrumental" | "stems"; quality?: "auto" | "fast" | "best" }): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.audio.separate({ audioUrl: songUrl })
// output: vocalUrl and instrumentalUrl, or one URL per stem in stems modeaudio.isolate(input)
음성 추출 노드처럼, 주 음성만 남기고 배경 노이즈를 제거합니다(POST /v1/audio-isolation).
isolate(input: { audioUrl: string }): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.audio.isolate({ audioUrl: interviewUrl })audio.applyFx(input)
오디오 효과 노드처럼, 리버브, 에코, 전화기, 메가폰 효과를 추가합니다(POST /v1/audio-fx). 프리셋은 보이스 체인저의 voiceFx와 같습니다.
applyFx(input: {
audioUrl: string
preset?: AudioFxPreset
mix?: number
delayMs?: number
decay?: number
eqLow?: number
eqHigh?: number
}): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.audio.applyFx({ audioUrl: lineUrl, preset: "telephone" })audio.mix(input)
오디오 믹스 노드처럼, 여러 트랙을 하나로 겹칩니다(POST /v1/mix-audio).
mix(input: {
audioUrls: string[]
trackVolumes?: number[]
duck?: { under: number; amount?: number; thresholdDb?: number; ratio?: number; attackMs?: number; releaseMs?: number }
endAt?: "longest" | "voice"
}): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.audio.mix({ audioUrls: [voiceUrl, musicUrl], trackVolumes: [100, 35] })
// The music dips under the voice (track 0) while it speaks.
const ducked = await client.audio.mix({ audioUrls: [voiceUrl, musicUrl], duck: { under: 0, amount: 80 } })audio.adjustVolume(input)
볼륨 조절 노드처럼, 오디오 파일이나 동영상 오디오의 레벨을 바꿉니다(POST /v1/adjust-volume).
adjustVolume(input: {
audioUrl?: string
videoUrl?: string
volume?: number
normalize?: boolean
fadeIn?: number
fadeOut?: number
}): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.audio.adjustVolume({ audioUrl: musicUrl, volume: 60, fadeOut: 3 })audio.pitchTempo(input)
피치 및 템포 노드처럼, 피치를 옮기거나 피치를 바꾸지 않고 템포를 바꿉니다(POST /v1/pitch-tempo). audioUrl과 videoUrl 중 정확히 하나를 주세요. 동영상은 화면을 그대로 유지하며 템포는 1로 고정됩니다.
pitchTempo(input: {
audioUrl?: string
videoUrl?: string
semitones?: number
tempo?: number
preserveFormants?: boolean
}): Promise<{ jobId: string }>속성
타입
실행 비용은 입력의 시작된 1분마다 22크레딧이며, 최대 30분입니다.
const { jobId } = await client.audio.pitchTempo({ audioUrl: voiceUrl, semitones: -3 })audio.combine(input)
오디오 합치기 노드처럼, 오디오 세그먼트를 이어 붙입니다(POST /v1/combine-audio).
combine(input: { segments: Array<{ url: string; startTime?: number; endTime?: number }> }): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.audio.combine({
segments: [{ url: introUrl }, { url: episodeUrl, startTime: 4 }, { url: outroUrl }],
})audio.transcribe(input)
음성 텍스트 변환 노드처럼, 오디오나 동영상 파일 속 음성을 텍스트로 바꿉니다(POST /v1/transcribe).
transcribe(input: {
audioUrl: string
provider?: "elevenlabs-stt" | "incredibly-fast-whisper" | "whisper"
language?: string
diarize?: boolean
tagAudioEvents?: boolean
wordTimestamps?: boolean
}): Promise<{ jobId: string }>속성
타입
provider | 단어 타이밍 | 참고 |
|---|---|---|
elevenlabs-stt | 항상 | diarize와 tagAudioEvents를 지원하는 유일한 엔진입니다. |
incredibly-fast-whisper | wordTimestamps: true일 때만 | 이 플래그가 없으면 작업은 성공하고 요금이 청구되지만, 구문 세그먼트만 있고 words 목록은 비어 있습니다. |
whisper | 없음 | 구문 세그먼트만 반환합니다. wordTimestamps: true는 크레딧이 쓰이기 전에 400 validation_error로 거부됩니다. |
provider를 생략하면 whisper가 쓰이므로, provider 없이 wordTimestamps: true를 보내도 400을 받습니다. 단어 타이밍이 필요하면 elevenlabs-stt를 지정하거나, incredibly-fast-whisper를 wordTimestamps: true와 함께 지정하세요. 키네틱 자막에는 단어 타이밍이 필요합니다.
const { jobId } = await client.audio.transcribe({ audioUrl: talkUrl, provider: "elevenlabs-stt" })완료된 작업의 output_data는 TranscribeJobOutput입니다.
| 필드 | 단위 | 내용 |
|---|---|---|
text | 스크립트 전체를 하나의 문자열로 담습니다. | |
language | 감지되었거나 요청한 언어 코드입니다. | |
words | 밀리초 | 단어마다 항목 하나로, { text, startMs, endMs, speaker? } 형태입니다. 엔진에 단어 타이밍을 요청하지 않았으면 비어 있습니다. |
json | 밀리초 | 정규화된 스크립트 { version, language, words, segments? }로, client.edit에서 받는 형식입니다. |
segments | 초 | 원본 구문 범위로, 이전 엔진만 반환합니다. elevenlabs-stt는 반환하지 않으므로 words를 읽으세요. |
words는 client.media.addCaptions()가 받는 captions와 형태가 정확히 같으므로, 스크립트를 고친 다음 자막으로 입힐 수 있습니다.
자주 묻는 질문
Nodaro SDK로 동영상 속 보이스를 바꾸려면 어떻게 하나요?
videoUrl과 voiceId를 넣어 client.voices.change를 호출하세요. 서버가 보이스를 바꾸고 원래 동영상에 다시 입힙니다. 작업을 폴링해 output_data.videoUrl을 받으세요.
녹음 속 화자마다 다른 보이스를 주려면 어떻게 하나요?
감지된 화자 순서대로 화자당 항목 하나씩 담은 orderedVoices로 client.voices.recast를 호출하세요. 자기 목소리를 유지할 화자에는 null을 쓰세요. Nodaro Cloud에서 실행됩니다.
SDK로 아직 보이스를 복제할 수 있나요?
아니요. 보이스 복제는 2026년 9월에 종료되었으며, 이제 createClone은 410 voice_cloning_retired로 실패합니다. 기존 복제 보이스는 계속 사용할 수 있습니다. 대신 client.voices.design으로 새 보이스를 디자인하세요.
단어 타이밍을 반환하는 음성 텍스트 변환 엔진은 무엇인가요?
elevenlabs-stt는 항상 단어 타이밍을 반환합니다. incredibly-fast-whisper는 wordTimestamps를 true로 설정했을 때만 반환하고, whisper는 반환하지 않습니다.
관련 문서
미디어 및 업로드
편집
보이스 및 미디어
보이스 체인저 Pro
음성 텍스트 변환
최종 업데이트