Движок речи, который не привязан к одной модели
vox запускает Qwen3-TTS и F5-TTS из одного файла .tmod: на процессоре, на видеокарте через Vulkan и прямо в браузере через WebGPU. Один seed — один звук.
- Подключается как библиотека (C ABI), как CLI, как Python-пакет и как один скрипт
vox.js - F5-TTS в тестовом прогоне отличается от официального
f5_ttsменьше чем на 1e-6 от амплитуды сигнала - Шум не зависит от числа потоков, видеокарты и размера чанка
Модель — это файл, а движок — всегда один
Веса лежат в одном .tmod, голос — в .tvoice. Новая модель подключается как «семейство» поверх того же рантайма, поэтому планировщик ресурсов, стриминг и тесты общие.
Ресурсы под контролем
Выбор устройства, лимиты RAM и VRAM, число потоков и режим работы. План размещения можно посмотреть, ничего не загружая: vox plan.
Стриминг без швов
Текст подаётся кусками, звук забирается по мере готовности. У F5-TTS куски склеиваются кроссфейдом 0,15 с, как в официальном инференсе.
Проверка против эталона
Каждое семейство сверяется с официальным пакетом на тестовой модели со случайными весами. Что не проверено — написано в README и ниже на этой странице.
Какие модели уже работают
Один движок, разные семейства. Следующие модели добавляются по одной за итерацию, каждая со своими тестами.
| Модель | Процессор | Vulkan | Браузер (WebGPU) | Что проверено |
|---|---|---|---|---|
| Qwen3-TTS 12HzBase, CustomVoice, VoiceDesign; 0,6B и 1,7B | да | да | да | Ядро сверено с официальным qwen_tts, в том числе на настоящей 1,7B-Base; замеры скорости на RTX 3050 есть в README. В браузере: загрузка по ссылке HF и синтез сверены с ядром на крошечной модели; на настоящих весах и на реальной видеокарте не измерялось. Голос из записи в браузере пока не создаётся: нужен готовый .tvoice, диктор или описание |
| F5-TTSDiT + flow matching + Vocos, клонирование по образцу | да | линейные слои | да | Сверена с f5_tts и vocos на тестовой модели, в браузере тоже. На настоящих весах и на реальной видеокарте скорость не измерялась |
| XTTS, CosyVoice, Chatterbox | в очереди | Будут подключены по одной модели за итерацию | ||
Путь от текста до звука
Все клиенты ходят через один и тот же контракт. Различается только то, где считается модель.
Конвертер
Веса из Hugging Face превращаются в .tmod: f32, f16, bf16, q8 или q4.
python -m vox buildpython -m vox.build_f5
Семейство
Описывает стадии модели и умеет клонировать голос. Авторегрессионные модели идут через кодеки, остальные — через «прямой» режим.
Устройство
Планировщик раскладывает стадии по железу.
- процессор: AVX2, aarch64
- Vulkan: NVIDIA, AMD, Intel
- WebGPU: браузер (
vox.js)
Клиент
Звук — float32, mono. Забирайте его одним куском или потоком.
- C ABI, C++, Rust, Go
- Python, Node.js
- HTTP-сервер, браузер
Как скачать
Репозиторий: github.com/dariumi/Vox. Лицензия Apache-2.0. Веса моделей в репозиторий не входят: у каждой модели своя лицензия.
Сборка из исходников
git clone https://github.com/dariumi/Vox.git cd Vox cmake -S . -B build -DVOX_NATIVE=ON cmake --build build -j
Получатся build/vox (CLI) и build/libvox.so (библиотека; на macOS и Windows — .dylib и .dll). Vulkan включится сам, если найдёт заголовки.
Python-пакет
pip install .
Собирает нативный модуль (нужны CMake и компилятор). Дальше: import vox. Сервер: python -m vox serve.
Для браузера: один файл
<script src="vox.js"></script>
Без сборки и зависимостей, около 75 КБ. Лучше всего с WebGPU (иначе считает процессор, в разы медленнее): Chrome и Edge 113+ (Windows, macOS; на Linux WebGPU пока включается флагами), Firefox 141+ (Windows), Safari 26. Страница должна открываться по HTTPS или с localhost.
Файл модели .tmod
# Qwen3-TTS
python -m vox build --model ./Qwen3-TTS-12Hz-1.7B-Base \
--dtype bf16 --quant q8 -o model.q8.tmod
# F5-TTS
python -m vox.build_f5 --ckpt model_1250000.safetensors \
--vocab vocab.txt --vocos ./vocos-mel-24khz \
--dtype bf16 -o f5.bf16.tmod
Для браузера берите bf16 или q8: файл F5 в f32 весит больше 1 ГБ.
Как использовать из разных языков
Все привязки идут через C ABI. Примеры из репозитория проверены автоматически: на тестовой модели каждый выдаёт ровно те же отсчёты, что и vox say с тем же seed.
# голос из записи (лучше WAV 24 кГц, 5–15 секунд чистой речи)
vox voice create ref.wav --model model.tmod --text "Текст, который сказан в записи" -o me.tvoice
# синтез в файл
vox say "Привет! Это проверка." --model model.tmod --voice me.tvoice -o out.wav
# потоком в плеер
vox say --stdin --stream --model model.tmod --voice me.tvoice | ffplay -f f32le -ar 24000 -i -
# F5-TTS: параметры называются как в официальном коде
vox say "Привет!" --model f5.tmod --voice me.tvoice --nfe 16 --cfg 2 --sway -1 --speed 1 -o out.wavvox devices, vox plan --model model.tmod.import vox
eng = vox.Engine("model.tmod", device="auto") # "cpu", "vulkan:0", "auto"
voice = eng.clone_voice("ref.wav", "Текст, который сказан в записи", name="me", seed=42)
voice.save("me.tvoice")
audio = eng.synthesize("Привет!", voice, seed=42) # numpy float32, mono, eng.sample_rate
# текст приходит по кускам, звук забирается по мере готовности
with eng.session(voice, seed=42) as s:
s.feed("Первая фраза. ")
chunks = list(s.ready())
s.end()
chunks += list(s)pip install . из корня репозитория. Ошибки — vox.VoxError, нехватка памяти — vox.OutOfMemory./* Минимальный пример C ABI на чистом C: синтез фразы в сырой float32 PCM.
* cc examples/minimal.c -Iinclude -Lbuild -lvox -o minimal
* ./minimal model.tmod voice.tvoice "Привет!" > out.f32
*/
#include <stdio.h>
#include "vox.h"
#define CHECK(x) do { vox_status s_ = (x); if (s_ < 0) { fprintf(stderr, "%s: %s\n", #x, vox_last_error()); return 1; } } while (0)
int main(int argc, char **argv) {
if (argc < 4) { fprintf(stderr, "usage: %s model.tmod voice.tvoice text\n", argv[0]); return 1; }
vox_engine *e; vox_voice *v; vox_session *s;
CHECK(vox_engine_open(argv[1], NULL, &e));
CHECK(vox_voice_load(e, argv[2], &v));
vox_gen_params p;
vox_gen_params_default(&p);
p.seed = 42;
CHECK(vox_session_create(e, v, &p, &s));
CHECK(vox_session_feed_text(s, argv[3]));
CHECK(vox_session_end_text(s));
float buf[4096];
size_t n, total = 0;
vox_status st;
while ((st = vox_session_pull(s, buf, 4096, &n)) == VOX_OK) {
fwrite(buf, sizeof(float), n, stdout);
total += n;
}
CHECK(st);
fprintf(stderr, "%zu samples @ %u Hz\n", total, vox_engine_sample_rate(e));
vox_session_free(s);
vox_voice_free(v);
vox_engine_close(e);
return 0;
}cc examples/minimal.c -Iinclude -Lbuild -lvox -o minimal. Контракт — include/vox.h.// C++ поверх C ABI: RAII-обёртки и синтез в stdout (сырой float32 PCM, mono).
// c++ -std=c++17 examples/minimal.cpp -Iinclude -Lbuild -lvox -o minimal_cpp
// LD_LIBRARY_PATH=build ./minimal_cpp model.tmod voice.tvoice "Привет!" > out.f32
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include "vox.h"
static void check(vox_status s, const char *what) {
if (s < 0) throw std::runtime_error(std::string(what) + ": " + vox_last_error());
}
template <class T, void (*F)(T *)> struct Deleter { void operator()(T *p) const { F(p); } };
using Engine = std::unique_ptr<vox_engine, Deleter<vox_engine, vox_engine_close>>;
using Voice = std::unique_ptr<vox_voice, Deleter<vox_voice, vox_voice_free>>;
using Session = std::unique_ptr<vox_session, Deleter<vox_session, vox_session_free>>;
int main(int argc, char **argv) {
if (argc < 4) { std::fprintf(stderr, "usage: %s model.tmod voice.tvoice text\n", argv[0]); return 1; }
try {
vox_engine *e = nullptr;
check(vox_engine_open(argv[1], nullptr, &e), "open");
Engine engine(e);
vox_voice *v = nullptr;
check(vox_voice_load(e, argv[2], &v), "voice");
Voice voice(v);
vox_gen_params p;
vox_gen_params_default(&p);
p.seed = 42;
vox_session *s = nullptr;
check(vox_session_create(e, v, &p, &s), "session");
Session session(s);
check(vox_session_feed_text(s, argv[3]), "feed");
check(vox_session_end_text(s), "end");
std::vector<float> buf(4096);
size_t n = 0, total = 0;
vox_status st;
while ((st = vox_session_pull(s, buf.data(), buf.size(), &n)) == VOX_OK) {
std::fwrite(buf.data(), sizeof(float), n, stdout);
total += n;
}
check(st, "pull");
std::fprintf(stderr, "%zu samples @ %u Hz\n", total, vox_engine_sample_rate(e));
} catch (const std::exception &ex) {
std::fprintf(stderr, "%s\n", ex.what());
return 1;
}
return 0;
}//! Rust через C ABI (без зависимостей): синтез в stdout — сырой float32 PCM.
//! cd examples/rust && cargo build --release && LD_LIBRARY_PATH=../../build ./target/release/vox-example model.tmod voice.tvoice "Привет!" > out.f32
use std::ffi::{c_char, c_void, CStr, CString};
use std::io::Write;
use std::ptr;
#[repr(C)]
struct GenParams {
struct_size: u32,
temperature: f32,
top_p: f32,
repetition_penalty: f32,
top_k: i32,
sub_temperature: f32,
sub_top_p: f32,
sub_top_k: i32,
seed: u64,
chunk_frames: u32,
max_frames: u32,
language: *const c_char,
decoder_context_frames: u32,
decoder_mode: u32,
extra_json: *const c_char,
}
const VOX_OK: i32 = 0;
extern "C" {
fn vox_last_error() -> *const c_char;
fn vox_gen_params_default(p: *mut GenParams);
fn vox_engine_open(path: *const c_char, opts: *const c_void, out: *mut *mut c_void) -> i32;
fn vox_engine_close(e: *mut c_void);
fn vox_engine_sample_rate(e: *const c_void) -> u32;
fn vox_voice_load(e: *mut c_void, path: *const c_char, out: *mut *mut c_void) -> i32;
fn vox_voice_free(v: *mut c_void);
fn vox_session_create(e: *mut c_void, v: *const c_void, p: *const GenParams, out: *mut *mut c_void) -> i32;
fn vox_session_feed_text(s: *mut c_void, utf8: *const c_char) -> i32;
fn vox_session_end_text(s: *mut c_void) -> i32;
fn vox_session_pull(s: *mut c_void, pcm: *mut f32, cap: usize, written: *mut usize) -> i32;
fn vox_session_free(s: *mut c_void);
}
fn check(status: i32, what: &str) -> Result<(), String> {
if status < 0 {
let msg = unsafe { CStr::from_ptr(vox_last_error()) }.to_string_lossy().into_owned();
return Err(format!("{what}: {msg}"));
}
Ok(())
}
fn run() -> Result<(), String> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 4 {
return Err(format!("usage: {} model.tmod voice.tvoice text", args[0]));
}
let (model, voice_path, text) = (CString::new(args[1].as_str()).unwrap(), CString::new(args[2].as_str()).unwrap(), CString::new(args[3].as_str()).unwrap());
unsafe {
let (mut e, mut v, mut s) = (ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
check(vox_engine_open(model.as_ptr(), ptr::null(), &mut e), "open")?;
check(vox_voice_load(e, voice_path.as_ptr(), &mut v), "voice")?;
let mut p: GenParams = std::mem::zeroed();
vox_gen_params_default(&mut p);
p.seed = 42;
check(vox_session_create(e, v, &p, &mut s), "session")?;
check(vox_session_feed_text(s, text.as_ptr()), "feed")?;
check(vox_session_end_text(s), "end")?;
let mut buf = vec![0f32; 4096];
let (mut total, mut out) = (0usize, std::io::stdout().lock());
loop {
let mut n = 0usize;
let st = vox_session_pull(s, buf.as_mut_ptr(), buf.len(), &mut n);
if st != VOX_OK {
check(st, "pull")?;
break; // VOX_DONE (2) или VOX_AGAIN (1) для синхронного режима здесь означает конец
}
let bytes = std::slice::from_raw_parts(buf.as_ptr() as *const u8, n * 4);
out.write_all(bytes).map_err(|e| e.to_string())?;
total += n;
}
eprintln!("{total} samples @ {} Hz", vox_engine_sample_rate(e));
vox_session_free(s);
vox_voice_free(v);
vox_engine_close(e);
}
Ok(())
}
fn main() {
if let Err(e) = run() {
eprintln!("{e}");
std::process::exit(1);
}
}VOX_LIB_DIR при сборке, при запуске нужен LD_LIBRARY_PATH.// Go через cgo (C ABI): синтез в stdout — сырой float32 PCM.
//
// cd examples/go && go build -o vox-go . && LD_LIBRARY_PATH=../../build ./vox-go model.tmod voice.tvoice "Привет!" > out.f32
package main
/*
#cgo CFLAGS: -I../../include
#cgo LDFLAGS: -L../../build -lvox
#include <stdlib.h>
#include "vox.h"
*/
import "C"
import (
"fmt"
"os"
"unsafe"
)
func check(st C.vox_status, what string) {
if st < 0 {
fmt.Fprintf(os.Stderr, "%s: %s\n", what, C.GoString(C.vox_last_error()))
os.Exit(1)
}
}
func main() {
if len(os.Args) < 4 {
fmt.Fprintf(os.Stderr, "usage: %s model.tmod voice.tvoice text\n", os.Args[0])
os.Exit(1)
}
model, voicePath, text := C.CString(os.Args[1]), C.CString(os.Args[2]), C.CString(os.Args[3])
defer C.free(unsafe.Pointer(model))
defer C.free(unsafe.Pointer(voicePath))
defer C.free(unsafe.Pointer(text))
var e *C.vox_engine
var v *C.vox_voice
var s *C.vox_session
check(C.vox_engine_open(model, nil, &e), "open")
defer C.vox_engine_close(e)
check(C.vox_voice_load(e, voicePath, &v), "voice")
defer C.vox_voice_free(v)
var p C.vox_gen_params
C.vox_gen_params_default(&p)
p.seed = 42
check(C.vox_session_create(e, v, &p, &s), "session")
defer C.vox_session_free(s)
check(C.vox_session_feed_text(s, text), "feed")
check(C.vox_session_end_text(s), "end")
buf := make([]float32, 4096)
total := 0
for {
var n C.size_t
st := C.vox_session_pull(s, (*C.float)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)), &n)
if st != C.VOX_OK {
check(st, "pull")
break
}
os.Stdout.Write(unsafe.Slice((*byte)(unsafe.Pointer(&buf[0])), int(n)*4))
total += int(n)
}
fmt.Fprintf(os.Stderr, "%d samples @ %d Hz\n", total, uint32(C.vox_engine_sample_rate(e)))
}// Node.js через koffi (FFI к C ABI): синтез в stdout — сырой float32 PCM.
// cd examples/node && npm install && node vox.mjs model.tmod voice.tvoice "Привет!" > out.f32
// Путь к библиотеке: VOX_LIB (по умолчанию ../../build/libvox.so; macOS — libvox.dylib, Windows — vox.dll).
import koffi from "koffi";
const lib = koffi.load(process.env.VOX_LIB || new URL("../../build/libvox.so", import.meta.url).pathname);
const vox_last_error = lib.func("const char *vox_last_error()");
const vox_gen_params_default = lib.func("void vox_gen_params_default(void *p)");
const vox_engine_open = lib.func("int vox_engine_open(const char *path, void *opts, _Out_ void **out)");
const vox_engine_close = lib.func("void vox_engine_close(void *e)");
const vox_engine_sample_rate = lib.func("uint32_t vox_engine_sample_rate(void *e)");
const vox_voice_load = lib.func("int vox_voice_load(void *e, const char *path, _Out_ void **out)");
const vox_voice_free = lib.func("void vox_voice_free(void *v)");
const vox_session_create = lib.func("int vox_session_create(void *e, void *v, void *p, _Out_ void **out)");
const vox_session_feed_text = lib.func("int vox_session_feed_text(void *s, const char *utf8)");
const vox_session_end_text = lib.func("int vox_session_end_text(void *s)");
const vox_session_pull = lib.func("int vox_session_pull(void *s, float *pcm, size_t cap, _Out_ size_t *written)");
const vox_session_free = lib.func("void vox_session_free(void *s)");
const check = (st, what) => { if (st < 0) throw new Error(`${what}: ${vox_last_error()}`); };
const [model, voicePath, text] = process.argv.slice(2);
if (!text) { console.error("usage: node vox.mjs model.tmod voice.tvoice text"); process.exit(1); }
const out = [null], voice = [null], sess = [null];
check(vox_engine_open(model, null, out), "open");
const engine = out[0];
check(vox_voice_load(engine, voicePath, voice), "voice");
// vox_gen_params: struct_size заполняет vox_gen_params_default; seed — uint64 по смещению 32
const params = Buffer.alloc(72);
vox_gen_params_default(params);
params.writeBigUInt64LE(42n, 32);
check(vox_session_create(engine, voice[0], params, sess), "session");
check(vox_session_feed_text(sess[0], text), "feed");
check(vox_session_end_text(sess[0]), "end");
const buf = new Float32Array(4096), written = [0n];
let total = 0;
for (;;) {
const st = vox_session_pull(sess[0], buf, buf.length, written);
if (st !== 0) { check(st, "pull"); break; }
const n = Number(written[0]);
process.stdout.write(Buffer.from(buf.buffer, 0, n * 4));
total += n;
}
console.error(`${total} samples @ ${vox_engine_sample_rate(engine)} Hz`);
vox_session_free(sess[0]); vox_voice_free(voice[0]); vox_engine_close(engine);koffi (npm install в папке примера)."""Чистый ctypes поверх C ABI (без установки пакета vox): синтез в stdout — сырой float32 PCM.
python examples/python/ctypes_example.py model.tmod voice.tvoice "Привет!" > out.f32
Путь к библиотеке: VOX_LIB (по умолчанию build/libvox.so; macOS — libvox.dylib, Windows — vox.dll).
Обычно удобнее готовый пакет: `import vox; vox.Engine(...)` (см. README)."""
import ctypes as C
import os
import struct
import sys
here = os.path.dirname(os.path.abspath(__file__))
lib = C.CDLL(os.environ.get("VOX_LIB") or os.path.join(here, "..", "..", "build", "libvox.so"))
lib.vox_last_error.restype = C.c_char_p
lib.vox_engine_sample_rate.restype = C.c_uint32
lib.vox_engine_sample_rate.argtypes = [C.c_void_p]
for name in ("vox_engine_close", "vox_voice_free", "vox_session_free"):
getattr(lib, name).argtypes = [C.c_void_p]
def check(st, what):
if st < 0:
raise RuntimeError(f"{what}: {lib.vox_last_error().decode()}")
return st
def main(model, voice_path, text):
e, v, s = C.c_void_p(), C.c_void_p(), C.c_void_p()
check(lib.vox_engine_open(model.encode(), None, C.byref(e)), "open")
check(lib.vox_voice_load(e, voice_path.encode(), C.byref(v)), "voice")
params = C.create_string_buffer(72) # vox_gen_params; struct_size проставит vox_gen_params_default
lib.vox_gen_params_default(params)
struct.pack_into("<Q", params, 32, 42) # seed: uint64 по смещению 32
check(lib.vox_session_create(e, v, params, C.byref(s)), "session")
check(lib.vox_session_feed_text(s, text.encode()), "feed")
check(lib.vox_session_end_text(s), "end")
buf = (C.c_float * 4096)()
n, total = C.c_size_t(), 0
while True:
st = lib.vox_session_pull(s, buf, 4096, C.byref(n))
if st != 0:
check(st, "pull")
break
sys.stdout.buffer.write(bytes(buf)[: n.value * 4])
total += n.value
print(f"{total} samples @ {lib.vox_engine_sample_rate(e)} Hz", file=sys.stderr)
lib.vox_session_free(s)
lib.vox_voice_free(v)
lib.vox_engine_close(e)
if __name__ == "__main__":
if len(sys.argv) < 4:
sys.exit("usage: ctypes_example.py model.tmod voice.tvoice text")
main(*sys.argv[1:4])ctypes поверх той же библиотеки.<script src="vox.js"></script>
<script type="module">
// 1. модель: что угодно — репозиторий HF, прямая ссылка, .tmod, File/Blob. Чекпойнт (safetensors) качается и
// конвертируется в браузере сам; Qwen3-TTS читается по тензорам (HTTP Range), целиком в память не попадает.
const model = await Vox.load("hf:Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice", {
dtype: "bf16", quant: "", // "q8" — вчетверо меньше f32
onProgress: (p) => console.log(Math.round(p * 100) + "%"), onStatus: console.log,
});
// F5-TTS так же: Vox.load("hf:SWivid/F5-TTS") или Vox.load("https://…/model_1250000.safetensors", { vocab: "https://…/vocab.txt" })
// 2. голос. F5-TTS: из записи (File, Blob, URL или Float32Array + sampleRate) и текста записи.
// Qwen3-TTS: готовый диктор / описание голоса или .tvoice (создаётся командой vox voice create)
const voice = model.family === "qwen3-tts"
? model.speakerVoice("aiden", "Speak slowly and calmly", { language: "english" })
: await model.cloneVoice(refFile, "Текст, который сказан в записи");
// 3. синтез целиком или потоком
const { pcm, sampleRate } = await model.synthesize("Привет, мир!", { voice, seed: 1 });
Vox.play(pcm, sampleRate); // или Vox.wav(pcm, sampleRate) → Blob для скачивания
// 4. двусторонний стриминг: текст подаётся на лету, звук забирается на лету
const player = new Vox.StreamPlayer(model.sampleRate);
const d = model.duplex({ voice, commit: "auto", idle_ms: 600 }); // auto | sentence | manual
input.addEventListener("input", () => d.setText(input.value)); // поле ввода; можно и d.write(токен из LLM)
(async () => { for await (const piece of d.audio()) player.push(piece); })();
// d.flush() — озвучить накопленное сейчас; d.end() — текст закончился; d.cancel() — оборвать
</script>d.caps показывает, как модель принимает текст). Параметры F5 называются как в официальном коде: nfe_step, cfg_strength, sway_sampling_coef, speed. Отмена — signal (AbortSignal).python -m vox serve --model model.tmod --voices ./voices --port 8080 --device vulkan
curl localhost:8080/v1/voices
curl -X POST localhost:8080/v1/tts -d '{"text":"Привет!","voice":"me","seed":42}' | ffplay -i -
curl -X POST localhost:8080/v1/voices -d '{"name":"me","audio_b64":"<WAV в base64>","ref_text":"Текст записи"}'wav, pcm16, f32.Демо: F5-TTS и Qwen3-TTS на WebGPU
Всё считается у вас в браузере: на видеокарте (WebGPU), а если её нет — на процессоре. Ничего не отправляется на сервер: модель и запись голоса остаются на странице.
Загрузите модель, чтобы начать.
Скорость зависит от видеокарты и числа шагов. F5-TTS тяжёлая: на слабых встроенных видеокартах берите «Быстро». Замеров на реальных видеокартах у автора страницы пока нет.
Живой ввод: печатаете — звучит
Двусторонний стриминг. Текст подаётся на лету, звук идёт сразу, пока модель читает дальше. Модели разные: Qwen3-TTS читает текст по словам и начинает говорить на полуслове, F5-TTS озвучивает целыми кусками (по предложению или по паузе в наборе). Движок сам подбирает, как кормить выбранную модель. Нужна загруженная выше модель и голос.
Ввод остановлено
Загрузите модель и выберите голос, потом нажмите «Начать».
Режим manual собирает всё набранное в одну запись: нажмите «Закончить» и дождитесь звука. Правка уже озвученного не переозвучивается.