# Vozes e áudio

> Navegue pelas vozes, troque e substitua vozes, crie novas vozes, duble vídeos e separe, mixe e transcreva áudio em TypeScript com o SDK do Nodaro.

Source: https://nodaro.ai/pt-BR/docs/developers/sdk/voices-and-audio

**`client.voices`** trabalha com vozes da ElevenLabs: lista e pesquisa vozes, troca a voz de uma gravação, dá uma nova voz a cada falante, cria novas vozes e dubla áudio e vídeo para outros idiomas. **`client.audio`** oferece os blocos de áudio separadamente: separação, isolamento de voz, efeitos, mixagem, volume, junção e transcrição. Todo método de geração retorna um `jobId`; consulte-o periodicamente com [`client.jobs.getStatus()`](https://nodaro.ai/docs/developers/sdk/jobs-and-executions). Os métodos chamam a [API REST de voz e mídia](https://nodaro.ai/docs/developers/api/voice-and-media).

## Métodos
| Método | O que faz |
| --- | --- |
| [`voices.list()`](#voiceslist) | Lista as vozes prontas |
| [`voices.searchLibrary(params?)`](#voicessearchlibraryparams) | Pesquisa a Biblioteca de vozes da comunidade |
| [`voices.listClones()`](#voiceslistclones) | Lista seus clones de voz existentes |
| [`voices.deleteClone(id)`](#voicesdeletecloneid) | Exclui um dos seus clones de voz |
| [`voices.createClone()` e `createCloneFromFile()`](#voicescreateclone-and-createclonefromfile) | Descontinuados |
| [`voices.change(input)`](#voiceschangeinput) | Substitui a voz em uma gravação ou em um vídeo |
| [`voices.recast(input)`](#voicesrecastinput) | Dá uma voz diferente a cada falante |
| [`voices.analyze(input)`](#voicesanalyzeinput) | Detecta os falantes antes de uma troca de vozes |
| [`voices.exportMix(input)`](#voicesexportmixinput) | Renderiza um vídeo a partir de stems de voz mixados |
| [`voices.design(input)`](#voicesdesigninput) | Cria uma nova voz a partir de uma descrição |
| [`voices.remix(input)`](#voicesremixinput) | Fala um texto em uma voz que você descreve |
| [`voices.dub(input)`](#voicesdubinput) | Dubla áudio ou vídeo para outro idioma |
| [`voices.textToDialogue(input)`](#voicestexttodialogueinput) | Dá voz a um roteiro com vários falantes em um único arquivo de áudio |
| [`audio.separate(input)`](#audioseparateinput) | Divide uma faixa em stems |
| [`audio.isolate(input)`](#audioisolateinput) | Mantém a voz principal e remove o ruído |
| [`audio.applyFx(input)`](#audioapplyfxinput) | Adiciona reverberação, eco, telefone ou megafone |
| [`audio.mix(input)`](#audiomixinput) | Sobrepõe várias faixas em uma só |
| [`audio.adjustVolume(input)`](#audioadjustvolumeinput) | Muda o nível, normaliza ou aplica fade |
| [`audio.combine(input)`](#audiocombineinput) | Junta segmentos de áudio em sequência |
| [`audio.transcribe(input)`](#audiotranscribeinput) | Transforma fala em texto, com os tempos das palavras |

## client.voices: vozes
### voices.list()
Lista as vozes prontas da ElevenLabs (`GET /v1/voices`). Quando o servidor não tem uma chave da ElevenLabs configurada, ele retorna, em vez disso, um conjunto selecionado.

```ts
list(): Promise<Voice[]>
```

```ts
const voices = await client.voices.list()
```

### voices.searchLibrary(params?)
Pesquisa a Biblioteca de vozes da comunidade (`GET /v1/voices/library`). Todos os parâmetros são opcionais, e valores vazios são omitidos para que os padrões do servidor se apliquem. `hasMore`, na resposta, indica se existe outra página.

```ts
searchLibrary(params?: VoiceLibraryParams): Promise<{ voices: Voice[]; hasMore: boolean }>
```

<TypeTable
type={{
search: { type: 'string', description: "Palavras a pesquisar." },
gender: { type: 'string', description: "Filtra por gênero." },
age: { type: 'string', description: "Filtra por idade." },
accent: { type: 'string', description: "Filtra por sotaque." },
language: { type: 'string', description: "Filtra pelo código do idioma, como en." },
category: { type: 'string', description: "Filtra por categoria." },
use_cases: { type: 'string', description: "Filtra por caso de uso." },
descriptives: { type: 'string', description: "Filtra por tags descritivas." },
featured: { type: 'boolean', description: "Só vozes em destaque." },
sort: { type: 'string', description: "A ordem, como trending." },
page: { type: 'number', default: '0', description: "A página, contada a partir de 0." },
page_size: { type: 'number', default: '30', description: "Vozes por página, de 1 a 100." },
}}
/>

```ts
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 } : {}),
})
```

Cada voz pode trazer duas indicações sobre os modelos de fala em que foi verificada:

- **`recommendedProvider`** é o melhor modelo para a voz. Apps sem um seletor de modelo devem enviá-lo como `provider` para o [**Texto para fala** (Text to Speech)](https://nodaro.ai/docs/nodes/audio/text-to-speech), para que a voz soe como a prévia dela na biblioteca.
- **`verifiedProviders`** lista todos os modelos em que a voz foi verificada. Apps com um seletor de modelo devem substituir a escolha do usuário só quando ela não estiver nessa lista.

### voices.listClones()
Lista seus clones de voz (`GET /v1/voice-clones`). Os clones feitos antes de a clonagem ser descontinuada continuam funcionando como IDs de voz em qualquer lugar que aceite uma voz.

```ts
listClones(): Promise<VoiceClone[]>
```

```ts
const clones = await client.voices.listClones()
```

### voices.deleteClone(id)
Exclui um dos seus clones de voz (`DELETE /v1/voice-clones/:id`).

```ts
deleteClone(id: string): Promise<void>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do clone." },
}}
/>

```ts
await client.voices.deleteClone(cloneId)
```

### voices.createClone() e createCloneFromFile()
A clonagem de voz não é mais oferecida no Nodaro. Ela foi descontinuada em setembro de 2026. Os dois métodos continuam no cliente e estão marcados como obsoletos. Eles falham com um `NodaroError` cujo `code` é `voice_cloning_retired`, com status 410. Para uma nova voz personalizada, use [`design()`](#voicesdesigninput).

## client.voices: modificador de voz
### voices.change(input)
Substitui a voz de uma gravação, ou de um vídeo inteiro com fala, por outra voz (`POST /v1/voice-changer`), como faz o nó [**Modificador de voz** (Voice Changer)](https://nodaro.ai/docs/nodes/audio/voice-changer). Com `videoUrl`, o servidor extrai o áudio do vídeo, troca a voz e coloca a nova voz de volta na imagem original.

```ts
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 }>
```

<TypeTable
type={{
voiceId: { type: 'string', required: true, description: "A nova voz: o nome de uma voz pronta ou um ID de voz da ElevenLabs." },
audioUrl: { type: 'string', description: "A gravação a modificar. Informe audioUrl ou videoUrl. Quando os dois são enviados, o vídeo prevalece." },
videoUrl: { type: 'string', description: "Um vídeo cuja voz será trocada." },
model: { type: 'string', description: "Um modelo de fala para fala que substitui o padrão." },
stability: { type: 'number', description: "A estabilidade da voz, de 0 a 1." },
similarityBoost: { type: 'number', description: "O quanto se aproximar da voz de destino, de 0 a 1." },
style: { type: 'number', default: '0', description: "O exagero de estilo, de 0 a 1. Acima de 0, reforça a interpretação, com alguma perda de velocidade e de estabilidade." },
useSpeakerBoost: { type: 'boolean', description: "Aumenta a semelhança com a voz de destino, um pouco mais devagar." },
seed: { type: 'number', description: "Um número inteiro que torna o resultado reproduzível." },
removeBackgroundNoise: { type: 'boolean', description: "true retorna só a voz limpa. false mantém a música e os efeitos sob a nova voz." },
}}
/>

```ts
const { jobId } = await client.voices.change({
videoUrl: "https://example.com/talking.mp4",
voiceId: "Aria",
})
// the finished job's output_data has videoUrl and audioUrl
```

### voices.recast(input)
Dá uma voz diferente a cada falante detectado em uma gravação (`POST /v1/voice-changer-pro`), como faz o nó [**Modificador de voz Pro** (Voice Changer Pro)](https://nodaro.ai/docs/nodes/audio/voice-changer-pro). O método roda no Nodaro Cloud, custa créditos e é executado como um job.

```ts
recast(input: VoiceChangerProInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
orderedVoices: { type: 'Array<VoiceChangerProVoice | null>', required: true, description: "De 1 a 8 entradas, uma por falante detectado, em ordem. Cada uma é um ID de voz, um objeto com configurações por voz ou null para manter a voz original desse falante. Pelo menos uma entrada deve ser diferente de null." },
audioUrl: { type: 'string', description: "A gravação. Informe audioUrl ou videoUrl." },
videoUrl: { type: 'string', description: "Um vídeo cujos falantes terão a voz trocada. As novas vozes são colocadas de volta na imagem original." },
model: { type: 'string', description: "Um modelo de fala para fala que substitui o padrão." },
preserveBackground: { type: 'boolean', default: 'true', description: "Mixa a música e os efeitos de volta sob as novas vozes. false retorna só as vozes." },
separationQuality: { type: '"fast" | "best"', default: '"fast"', description: "fast é mais rápido e preserva mais da voz. best separa a voz e a música com mais precisão." },
removeBackgroundNoise: { type: 'boolean', description: "Também remove o ruído do resultado." },
musicVolumeMode: { type: '"match" | "normalize" | "manual"', default: '"match"', description: "O nível do fundo mantido: o nível original, normalizado ou musicVolume." },
musicVolume: { type: 'number', description: "O nível do fundo em porcentagem, de 0 a 200, com musicVolumeMode manual." },
voiceFx: { type: '{ preset, wetDryMix?, delayMs?, decay? }', description: "Um efeito em todas as novas vozes, aplicado antes de o fundo voltar." },
output: { type: '"video" | "stems"', default: '"video"', description: "video renderiza o resultado final. stems retorna faixas separadas, sem mixagem, para você mixar e renderizar com exportMix()." },
analysis: { type: 'VcpAnalysis', description: "O resultado de um job anterior de analyze(), para pular a detecção de falantes." },
}}
/>

Cada entrada de `orderedVoices` é uma destas opções:

- **Um ID de voz**: o nome de uma voz pronta, como `"Rachel"`, ou um ID de voz da ElevenLabs.
- **`null`**: uma posição que mantém a voz original. Esse falante mantém a própria voz, enquanto os falantes seguintes continuam tendo a voz trocada. As posições que mantêm a voz original não custam nada, porque só os falantes com voz trocada são cobrados.
- **Um objeto** com `voiceId` e as configurações desse falante: `stability`, `similarityBoost` e `style`, de 0 a 1, `useSpeakerBoost` e um `seed` de 0 a 4.294.967.295. `volumeMode` é `"match"` (o padrão, o nível do falante original), `"normalize"` ou `"manual"`, com `volume` em porcentagem de 0 a 200. `engine: "v3"` fala a linha de novo a partir da transcrição, com suporte a `[audio tags]`, em vez de converter a fala gravada.

O falante 0 recebe `orderedVoices[0]`, o falante 1 recebe `orderedVoices[1]`, e assim por diante. Os falantes depois da última entrada mantêm a própria voz. A voz e a música são sempre separadas primeiro. `preserveBackground` só decide se a música volta.

`voiceFx.preset` é um ambiente de reverberação (`room`, `bathroom`, `car`, `hall`, `concert-hall`, `church`, `cave`, `arena` ou `outdoor`), `telephone`, `megaphone`, `echo` ou `custom`. As predefinições de reverberação usam `wetDryMix` de 0 a 100. `echo` e `custom` usam `delayMs` de 20 a 2.000 e `decay` de 0 a 1.

```ts
// 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",
})
```

No modo de vídeo, o `output_data` do job concluído tem `videoUrl` e `audioUrl`.

### voices.analyze(input)
Detecta os falantes de um clipe **sem trocar as vozes** (`POST /v1/voice-changer-pro/analyze`). O método separa a voz da música uma vez e descobre quem fala quando. Ele roda no Nodaro Cloud, com preço fixo, como um job.

```ts
analyze(input: {
audioUrl?: string
videoUrl?: string
separationQuality?: "fast" | "best"
suggestTitle?: boolean
}): Promise<{ jobId: string }>
```

<TypeTable
type={{
audioUrl: { type: 'string', description: "A gravação. Informe exatamente um entre audioUrl e videoUrl." },
videoUrl: { type: 'string', description: "Um vídeo a analisar." },
separationQuality: { type: '"fast" | "best"', description: "A qualidade da separação, como em recast()." },
suggestTitle: { type: 'boolean', description: "Também sugere um título para o clipe." },
}}
/>

O `output_data` do job concluído é um `VcpAnalysis`: os `vocalsUrl` e `backgroundUrl` separados, os `speakers` detectados (cada um com `id`, os `segments` de tempo, `firstStartSec`, `wordCount` e um `snippet` de texto), `languageCode` com `languageProbability` e `suggestedTitle`, quando pedido. Passe-o como `analysis` para cada `recast()` seguinte, para que a troca de vozes reaproveite as faixas separadas e não pague de novo pela detecção.

### voices.exportMix(input)
Renderiza o vídeo final a partir de um conjunto mixado de stems (`POST /v1/voice-changer-pro/export`). É a última etapa depois de `recast({ output: "stems" })`. O método roda no Nodaro Cloud, com preço fixo, como um job.

```ts
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 }>
```

<TypeTable
type={{
videoUrl: { type: 'string', required: true, description: "O vídeo. A imagem dele é copiada sem recodificação." },
tracks: { type: 'VcpExportTrack[]', required: true, description: "Até 16 faixas, com pelo menos uma fora do mudo. Cada uma tem uma url, um gain de 0 a 200, muted e um kind opcional." },
voiceFx: { type: '{ preset, wetDryMix?, delayMs?, decay? }', description: "Um efeito só nas faixas de voz, nunca em uma faixa de fundo." },
}}
/>

O fluxo interativo analisa uma vez, troca as vozes gerando stems e depois mixa e renderiza. Veja o fluxo com uma pequena função auxiliar de consulta periódica:

```ts

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)
```

Os ganhos, os mudos e o efeito são aplicados quando o vídeo é renderizado, e a imagem é copiada como está. Por isso, a exportação corresponde à sua prévia, e você pode mudar a mixagem quantas vezes quiser antes de exportar. Uma mixagem com todas as faixas no mudo é recusada com um 400.

## client.voices: criar e traduzir
### voices.design(input)
Cria uma nova voz sintética a partir de uma descrição em texto (`POST /v1/voice-design`), como faz o nó [**Design de voz** (Voice Design)](https://nodaro.ai/docs/nodes/audio/voice-design). O job concluído traz uma prévia em áudio e o ID da nova voz, que você pode usar em qualquer lugar que aceite uma voz.

```ts
design(input: {
text: string
voiceDescription: string
model?: string
loudness?: number
guidanceScale?: number
seed?: number
quality?: number
shouldEnhance?: boolean
userPrompt?: string
}): Promise<{ jobId: string }>
```

<TypeTable
type={{
text: { type: 'string', required: true, description: "A fala que a prévia diz, de 100 a 1.000 caracteres." },
voiceDescription: { type: 'string', required: true, description: "A voz que você quer, como a voz grave e calorosa de um narrador mais velho." },
model: { type: 'string', description: "Um modelo de design de voz que substitui o padrão." },
loudness: { type: 'number', description: "A intensidade sonora, de -1 a 1." },
guidanceScale: { type: 'number', description: "O quanto seguir a descrição, de 0 a 100." },
seed: { type: 'number', description: "Um seed para um resultado reproduzível." },
quality: { type: 'number', description: "A configuração de qualidade." },
shouldEnhance: { type: 'boolean', description: "Deixa o modelo expandir sua descrição." },
userPrompt: { type: 'string', description: "Seu pedido original, guardado com o job." },
}}
/>

```ts
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)
Fala um texto em uma voz descrita em palavras simples, sem criar uma voz (`POST /v1/voice-remix`), como faz o nó [**Remix de voz** (Voice Remix)](https://nodaro.ai/docs/nodes/audio/voice-remix).

```ts
remix(input: { text: string; voiceDescription: string; userPrompt?: string }): Promise<{ jobId: string }>
```

<TypeTable
type={{
text: { type: 'string', required: true, description: "O texto a falar, de 1 a 5.000 caracteres." },
voiceDescription: { type: 'string', required: true, description: "A voz a usar, descrita em palavras." },
userPrompt: { type: 'string', description: "Seu pedido original, guardado com o job." },
}}
/>

```ts
const { jobId } = await client.voices.remix({
text: "Your order is on its way.",
voiceDescription: "A cheerful young woman, fast and upbeat",
})
```

### voices.dub(input)
Dubla um áudio, ou um vídeo inteiro, para outro idioma, mantendo a voz de cada falante (`POST /v1/dubbing`), como faz o nó [**Dublagem** (Dubbing)](https://nodaro.ai/docs/nodes/audio/dubbing). A dublagem de um vídeo retorna `output_data.videoUrl`, o clipe dublado, e `output_data.audioUrl`, só a faixa dublada.

```ts
dub(input: DubbingInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
targetLanguage: { type: 'string', required: true, description: "O idioma da dublagem, como um código, por exemplo es ou pt-BR." },
audioUrl: { type: 'string', description: "Uma fonte de áudio. Informe exatamente um entre audioUrl, videoUrl e sourceUrl." },
videoUrl: { type: 'string', description: "Uma fonte de vídeo. O resultado é um vídeo dublado." },
sourceUrl: { type: 'string', description: "Um link público do YouTube, do TikTok ou direto, baixado para você." },
sourceLanguage: { type: 'string', description: "O idioma falado. É detectado quando omitido." },
numSpeakers: { type: 'number', default: '0', description: "O número de falantes, de 1 a 20, ou 0 para detectá-lo. Um número conhecido melhora a separação." },
disableVoiceCloning: { type: 'boolean', description: "Usa vozes padrão em vez da voz de cada falante." },
dropBackgroundAudio: { type: 'boolean', description: "Deixa de fora a música e os efeitos." },
startTime: { type: 'number', description: "Dubla só a partir deste ponto, em segundos." },
endTime: { type: 'number', description: "Dubla só até este ponto, em segundos." },
highestResolution: { type: 'boolean', description: "Mantém a resolução da fonte na dublagem de um vídeo." },
useProfanityFilter: { type: 'boolean', description: "Filtra palavrões." },
targetAccent: { type: 'string', description: "Um sotaque para a dublagem. Experimental." },
watermark: { type: 'boolean', description: "Adiciona a marca d’água do fabricante do modelo à dublagem de um vídeo." },
}}
/>

```ts
const { jobId } = await client.voices.dub({
videoUrl: "https://example.com/interview.mp4",
targetLanguage: "es",
numSpeakers: 2,
})
```

O preço depende dos minutos do trecho dublado, com um mínimo de 1 minuto. Um trecho tem no máximo 30 minutos; por isso, use `startTime` e `endTime` em fontes mais longas.

### voices.textToDialogue(input)
Dá voz a um roteiro com vários falantes em **um único** arquivo de áudio (`POST /v1/text-to-dialogue`), como faz o nó [**Texto para diálogo** (Text to Dialogue)](https://nodaro.ai/docs/nodes/audio/text-to-dialogue) com o [ElevenLabs Dialogue v3](https://nodaro.ai/docs/models/audio/elevenlabs-dialogue-v3).

```ts
textToDialogue(input: {
dialogue: Array<{ text: string; voice: string }>
stability?: 0 | 0.5 | 1
languageCode?: string
seed?: number
applyTextNormalization?: "auto" | "on" | "off"
}): Promise<{ jobId: string }>
```

<TypeTable
type={{
dialogue: { type: 'Array<{ text: string; voice: string }>', required: true, description: "As falas na ordem em que são ditas. voice é o nome de uma voz pronta ou um ID de voz da ElevenLabs. O texto pode trazer tags de áudio como [laughs]." },
stability: { type: '0 | 0.5 | 1', description: "A estabilidade da interpretação." },
languageCode: { type: 'string', description: "Uma indicação de idioma ISO 639-1. É detectado quando omitido." },
seed: { type: 'number', description: "De 0 a 4.294.967.295 para um resultado reproduzível. Omita-o para um resultado aleatório." },
applyTextNormalization: { type: '"auto" | "on" | "off"', description: "Se números e abreviações devem ser escritos por extenso." },
}}
/>

```ts
const { jobId } = await client.voices.textToDialogue({
dialogue: [
{ text: "Did you hear that?", voice: "Rachel" },
{ text: "[whispers] Stay behind me.", voice: "Callum" },
],
})
```

Um roteiro pode ter no máximo 5.000 caracteres, e menos de 2.000 dão a melhor qualidade. Ele pode usar no máximo 10 vozes diferentes. Vozes da biblioteca, vozes criadas com design de voz e clones existentes funcionam, combinados como você quiser. O `output_data.audioUrl` do job concluído é o arquivo.

## client.audio
Os blocos de áudio que o Modificador de voz Pro usa internamente, disponíveis um a um. Todo método retorna um `jobId`.

### audio.separate(input)
Divide uma faixa em stems (`POST /v1/audio-separation`), como faz o nó [**Separação de áudio** (Audio Separation)](https://nodaro.ai/docs/nodes/audio/audio-separation).

```ts
separate(input: { audioUrl: string; mode?: "vocal_instrumental" | "stems"; quality?: "auto" | "fast" | "best" }): Promise<{ jobId: string }>
```

<TypeTable
type={{
audioUrl: { type: 'string', required: true, description: "A faixa a dividir." },
mode: { type: '"vocal_instrumental" | "stems"', default: '"vocal_instrumental"', description: "vocal_instrumental separa a voz da música e dos efeitos. stems retorna bateria, baixo e as outras partes." },
quality: { type: '"auto" | "fast" | "best"', description: "A qualidade da separação." },
}}
/>

```ts
const { jobId } = await client.audio.separate({ audioUrl: songUrl })
// output: vocalUrl and instrumentalUrl, or one URL per stem in stems mode
```

### audio.isolate(input)
Mantém a voz principal e remove o ruído de fundo (`POST /v1/audio-isolation`), como faz o nó [**Extrator de voz** (Voice Extractor)](https://nodaro.ai/docs/nodes/audio/voice-extractor).

```ts
isolate(input: { audioUrl: string }): Promise<{ jobId: string }>
```

<TypeTable
type={{
audioUrl: { type: 'string', required: true, description: "A gravação a limpar." },
}}
/>

```ts
const { jobId } = await client.audio.isolate({ audioUrl: interviewUrl })
```

### audio.applyFx(input)
Adiciona um efeito de reverberação, eco, telefone ou megafone (`POST /v1/audio-fx`), como faz o nó [**Efeitos de áudio** (Audio FX)](https://nodaro.ai/docs/nodes/audio/audio-fx). As predefinições são as mesmas do `voiceFx` da troca de vozes.

```ts
applyFx(input: {
audioUrl: string
preset?: AudioFxPreset
mix?: number
delayMs?: number
decay?: number
eqLow?: number
eqHigh?: number
}): Promise<{ jobId: string }>
```

<TypeTable
type={{
audioUrl: { type: 'string', required: true, description: "A faixa a processar." },
preset: { type: 'AudioFxPreset', description: "Um ambiente de reverberação, como room, hall ou church, ou telephone, megaphone, echo ou custom." },
mix: { type: 'number', description: "O equilíbrio wet/dry da reverberação, de 0 a 100." },
delayMs: { type: 'number', description: "O atraso do eco, para echo e custom." },
decay: { type: 'number', description: "O decaimento do eco, para echo e custom." },
eqLow: { type: 'number', description: "O corte ou reforço de graves em dB, para telephone e megaphone." },
eqHigh: { type: 'number', description: "O corte ou reforço de agudos em dB, para telephone e megaphone." },
}}
/>

```ts
const { jobId } = await client.audio.applyFx({ audioUrl: lineUrl, preset: "telephone" })
```

### audio.mix(input)
Sobrepõe várias faixas em uma só (`POST /v1/mix-audio`), como faz o nó [**Mixar áudio** (Mix Audio)](https://nodaro.ai/docs/nodes/audio/mix-audio).

```ts
mix(input: { audioUrls: string[]; trackVolumes?: number[] }): Promise<{ jobId: string }>
```

<TypeTable
type={{
audioUrls: { type: 'string[]', required: true, description: "De 2 a 20 faixas, tocadas juntas." },
trackVolumes: { type: 'number[]', description: "Um nível por faixa em porcentagem, de 0 a 200, na mesma ordem." },
}}
/>

```ts
const { jobId } = await client.audio.mix({ audioUrls: [voiceUrl, musicUrl], trackVolumes: [100, 35] })
```

### audio.adjustVolume(input)
Muda o nível de um arquivo de áudio, ou do áudio de um vídeo (`POST /v1/adjust-volume`), como faz o nó [**Ajustar volume** (Adjust Volume)](https://nodaro.ai/docs/nodes/audio/adjust-volume).

```ts
adjustVolume(input: {
audioUrl?: string
videoUrl?: string
volume?: number
normalize?: boolean
fadeIn?: number
fadeOut?: number
}): Promise<{ jobId: string }>
```

<TypeTable
type={{
audioUrl: { type: 'string', description: "Um arquivo de áudio. Informe audioUrl ou videoUrl." },
videoUrl: { type: 'string', description: "Um vídeo cujo áudio será alterado." },
volume: { type: 'number', default: '100', description: "O nível em porcentagem." },
normalize: { type: 'boolean', description: "Leva a intensidade sonora a um nível padrão." },
fadeIn: { type: 'number', description: "Um fade-in, em segundos." },
fadeOut: { type: 'number', description: "Um fade-out, em segundos." },
}}
/>

```ts
const { jobId } = await client.audio.adjustVolume({ audioUrl: musicUrl, volume: 60, fadeOut: 3 })
```

### audio.combine(input)
Junta segmentos de áudio em sequência (`POST /v1/combine-audio`), como faz o nó [**Combinar áudio** (Combine Audio)](https://nodaro.ai/docs/nodes/audio/combine-audio).

```ts
combine(input: { segments: Array<{ url: string; startTime?: number; endTime?: number }> }): Promise<{ jobId: string }>
```

<TypeTable
type={{
segments: { type: 'Array<{ url: string; startTime?: number; endTime?: number }>', required: true, description: "Os segmentos em ordem. Cada um pode usar só parte do arquivo dele, de startTime a endTime, em segundos." },
}}
/>

```ts
const { jobId } = await client.audio.combine({
segments: [{ url: introUrl }, { url: episodeUrl, startTime: 4 }, { url: outroUrl }],
})
```

### audio.transcribe(input)
Transforma em texto a fala de um arquivo de áudio ou de vídeo (`POST /v1/transcribe`), como faz o nó [**Transcrever** (Transcribe)](https://nodaro.ai/docs/nodes/audio/transcribe).

```ts
transcribe(input: {
audioUrl: string
provider?: "elevenlabs-stt" | "incredibly-fast-whisper" | "whisper"
language?: string
diarize?: boolean
tagAudioEvents?: boolean
wordTimestamps?: boolean
}): Promise<{ jobId: string }>
```

<TypeTable
type={{
audioUrl: { type: 'string', required: true, description: "O arquivo de áudio ou de vídeo." },
provider: { type: '"elevenlabs-stt" | "incredibly-fast-whisper" | "whisper"', default: '"whisper"', description: "O mecanismo. Veja a tabela abaixo." },
language: { type: 'string', description: "Força um idioma. Omita-o para detectar o idioma." },
diarize: { type: 'boolean', description: "Indica quem fala cada palavra. Só elevenlabs-stt." },
tagAudioEvents: { type: 'boolean', description: "Marca risadas, aplausos e outros sons. Só elevenlabs-stt." },
wordTimestamps: { type: 'boolean', description: "Pede os tempos de cada palavra." },
}}
/>

| `provider` | Tempos das palavras | Observações |
| --- | --- | --- |
| [`elevenlabs-stt`](https://nodaro.ai/docs/models/audio/elevenlabs-stt) | Sempre | O único mecanismo que respeita `diarize` e `tagAudioEvents`. |
| [`incredibly-fast-whisper`](https://nodaro.ai/docs/models/audio/incredibly-fast-whisper) | Só com `wordTimestamps: true` | Sem a opção, o job é concluído e cobrado, com segmentos de frase e uma lista `words` vazia. |
| [`whisper`](https://nodaro.ai/docs/models/audio/whisper) | Nunca | Só segmentos de frase. `wordTimestamps: true` é recusado com `400 validation_error` antes de qualquer gasto de créditos. |

Omitir `provider` usa `whisper`; por isso, `wordTimestamps: true` sem um `provider` também recebe o 400. Para obter os tempos das palavras, indique `elevenlabs-stt`, ou `incredibly-fast-whisper` com `wordTimestamps: true`. As legendas cinéticas precisam deles.

```ts
const { jobId } = await client.audio.transcribe({ audioUrl: talkUrl, provider: "elevenlabs-stt" })
```

O `output_data` do job concluído é um `TranscribeJobOutput`:

| Campo | Unidade | Conteúdo |
| --- | --- | --- |
| `text` | | A transcrição inteira em uma única string. |
| `language` | | O código do idioma detectado ou pedido. |
| `words` | milissegundos | Uma entrada por palavra: `{ text, startMs, endMs, speaker? }`. Fica vazia quando os tempos das palavras não foram pedidos ao mecanismo. |
| `json` | milissegundos | A transcrição normalizada, `{ version, language, words, segments? }`, que [`client.edit`](https://nodaro.ai/docs/developers/sdk/editing) aceita. |
| `segments` | **segundos** | Os intervalos brutos das frases, só dos mecanismos mais antigos. `elevenlabs-stt` não retorna nenhum; por isso, leia `words`. |

`words` tem exatamente o formato das `captions` que [`client.media.addCaptions()`](https://nodaro.ai/docs/developers/sdk/media-and-uploads#mediaaddcaptionsinput) aceita; assim, você pode corrigir uma transcrição e gravá-la como legendas no vídeo.

## Frequently asked questions

### Como troco a voz de um vídeo com o SDK do Nodaro?

Chame client.voices.change com o videoUrl e um voiceId. O servidor substitui a voz e a coloca de volta no vídeo original. Consulte o job periodicamente até obter output_data.videoUrl.

### Como dou uma voz diferente a cada falante de uma gravação?

Chame client.voices.recast com orderedVoices, uma entrada por falante detectado, em ordem. Use null para um falante que mantém a própria voz. O método roda no Nodaro Cloud.

### Ainda posso clonar uma voz com o SDK?

Não. A clonagem de voz foi descontinuada em setembro de 2026, e createClone agora falha com 410 voice_cloning_retired. Os clones existentes continuam funcionando. Em vez disso, crie uma nova voz com client.voices.design.

### Qual mecanismo de transcrição retorna os tempos das palavras?

elevenlabs-stt sempre retorna os tempos das palavras. incredibly-fast-whisper só os retorna com wordTimestamps definido como true, e whisper nunca os retorna.
