Личная библиотека для книг, статей, подкастов и видео: транскрипция, синтез, перевод и смысловой поиск считаются на самом телефоне.
В прошлых статьях я рассказывал про локальный переводчик с синтезом голоса на кабардинском и про то, как собрать полный NLP-стек для «финального босса лингвистики» из старых словарей. За прошедшие месяцы весь этот стек – лемматизация, POS, NER, тональность, эмбеддинги, синтез – переехал с Python/Flask на десктопе в Swift-приложение на iOS и заодно перерос сам себя: начиналось всё как синтезатор речи для одного языка, а выросло в персональную библиотеку для любого контента – книги, статьи, подкасты, видео, музыка, файлами или ссылками. Полгода на голом Swift 6, вечерами и выходными.
Приложение уже можно ставить и ломать – оно в бете, включая рабочий CarPlay. Под катом: что оно умеет, как устроено внутри и какие цифры получились на реальном железе.
Коротко: что это и как попробовать
Личная библиотека для любого контента с озвучкой, переводом и смысловым поиском. «Офлайн» здесь не про то, что приложение не ходит в сеть – ссылки, подписки и загрузка контента работают как обычно, – а про то, где происходит обработка: распознавание речи, синтез, перевод и весь смысловой анализ считаются на самом телефоне.
Источником может быть файл, ссылка на статью, выпуск подкаста или видео – или целая подписка: RSS новостного сайта, лента подкаста, YouTube-канал. Статья приходит уже очищенной от рекламы и баннеров, одним текстом; и текст, и транскрипт остаются в вашей библиотеке навсегда, даже если издатель удалит оригинал или пропадёт интернет.
Требования. iOS 18.6+, iPhone 12 и новее. Apple Intelligence требует iPhone 15 Pro и новее – ограничение Apple; на старых моделях работает языковой пакет перевода и чат с цитатами, но без перефразирования локальной моделью.
Вес. Приложение – около 50 МБ. Остальное по требованию: Silero-синтез для языков СНГ – 100 МБ, ударения для славянских – 50 МБ, латышский голос – 60 МБ, пунктуация для русского – 80 МБ.
Цена. На время беты всё бесплатно; после релиза один объект с полным ИИ-анализом останется бесплатным навсегда, дальше – поштучно или подписка.
Ссылка: https://sayfable.com/ – тестирование анонимное, отдельный аккаунт не нужен, только TestFlight.
Почему такая библиотека стала возможна именно сейчас
Раньше на телефоне каждый тип контента жил в своём приложении: музыка в одном, подкасты в другом, YouTube в третьем, статьи – если повезёт, в read-it-later вроде закрывшегося Pocket. У аудио нет текста, у текста нет озвучки: нужен транскрипт подкаста – платите за API, хотите спросить у модели, о чём книга, – грузите её в чат-бот и тратьте сотни тысяч токенов, причём результат разбора нигде не сохранится.
С появлением в iOS 26 доступа к локальной языковой модели на уровне системы мы смогли соединить все типы контента в одном приложении и выполнять всю обработку медиа локально на устройстве. Контекстное окно у FM-модели Apple небольшое, но в связке с собственной разметкой этого хватает, чтобы отвечать на вопросы по личной библиотеке без галлюцинаций и со ссылкой на источник. SayFable обогащает любое медиа текстом (транскрипцией или субтитрами), локально размечает и режет на сцены – дальше с ним можно работать как с индексированным текстом: выбрать только интересные сцены из подкаста, собрать из десятка статей абзацы по своей теме, слушать книгу плейлистом сцен с одним персонажем.
Что можно делать в приложении уже сейчас
Импорт книг почти в любом формате. EPUB, FB2, DOCX/DOC, RTF, TXT, PDF – файл разбирается на главы по оглавлению, а внутри главы текст режется на сцены по смыслу.
Карточка книги, которую можно листать во время прослушивания. Не останавливая воспроизведение, видно оглавление: сколько в главе сцен и как они называются – по названиям обычно уже понятно, о чём глава. Рядом «Атмосфера» со статистикой: объём, доли частей речи, соотношение действия и описания, средняя важность и эмоциональность, доля диалогов, типы сцен и эмоциональная кривая по главам. И «Сущности» – персонажи, места и организации из текста; там же персонажу назначается свой голос (по умолчанию вся книга читается одним). Единственное исключение – YouTube: его встроенный плеер останавливается, как только уходишь с экрана видео, и это политика Google, а не наша недоработка.
Слева направо: карточка книги с главами и сценами, «Атмосфера» со статистикой разбора и редактор сущностей – 52 персонажа с числом упоминаний, где каждому назначается голос.
Своя подборка по любой теме. Объект в библиотеке – не обязательно одна книга. Можно начать с одной понравившейся статьи и собирать вокруг неё свою тему – робототехника, спорт, что угодно: кнопка «добавить по ссылке» кладёт каждую новую статью, выпуск подкаста или видео с субтитрами отдельной главой. Дальше по всей подборке работают поиск, слайдер важности, озвучка и чат.
Текст к любому аудио – и обратно. Подкаст, видео, свою запись можно распознать в текст и слушать с подсветкой слов. А заменив голос подкаста на любого доступного спикера, вы слушаете речь, синтезированную уже из распознанного текста, – интернет при переслушивании не нужен.
Перевод и дубляж на лету. Вместо оригинала – перевод, вплоть до синтезированной озвучки поверх оригинальной дорожки: закадровый перевод, собранный на телефоне без платного API.
YouTube без рекламы. Видео играет внутри приложения, ниже – субтитры, которые можно переводить и озвучивать. Плеер Google останавливается при блокировке экрана, поэтому есть кнопка «Не блокировать экран»: автоблокировка выключается, яркость гаснет до минимума – телефон можно убрать в карман.
Показать в статье все режимы не выйдет – она и так на пределе размера. Ниже только два кадра, а разборы остального, с видео и скриншотами по каждому экрану, лежат на sayfable.com: кому интересно, там видно куда больше, чем можно втиснуть в текст.
Как это считается на телефоне: таблица вместо векторного чёрного ящика
Чтобы навигация по сценам, плейлисты и чат работали быстро и офлайн, обычного RAG поверх эмбеддингов не хватило. Стандартный подход – нарезать книгу на чанки, заэмбеддить, искать по косинусу – наследует две проблемы: вектор одной модели ничего не значит для другой, а голое косинусное сходство у контекстных эмбеддингов регулярно промахивается мимо смысла (анизотропия – «всё похоже на всё» с косинусом около 0.9, пока вектор не отцентрировать).
SayFable вместо этого один раз при импорте строит из книги колоночную таблицу – плоские массивы «одна колонка на признак», по которым можно сканировать без разбора JSON. Уровней три, различаются они гранулярностью: слово → предложение и абзац → сцена.
Слово. Лемма, часть речи, номера предложения / абзаца / сцены, позиция ударной гласной и salience – частота слова внутри своей сцены, умноженная на обратную частоту по сценам книги: «насколько слово необычно вот здесь», а не «насколько оно редкое вообще». Отдельной колонкой – эмоциональная валентность из словаря Уорринера (для трёх десятков языков перенесена на леммы через глоссы тем же мостом, что описан во второй статье).
Предложение и абзац. Реплика это или авторская речь, вопрос, восклицание, кто говорит, доля диалога, плотности частей речи. И здесь же – важность, которую крутит слайдер:
-
важность предложения = сумма salience его знаменательных слов с фиксированным весом части речи (существительное 1.0, глагол 0.9, прилагательное 0.7), умноженная на центральность предложения к центроиду своей сцены – центрированный косинус его эмбеддинга. Именно умножение: набор редких слов в предложении, к теме сцены отношения не имеющем, важности не даёт;
-
важность абзаца = среднее по предложениям. Красивая гипотеза «взять топ-K лучших и пик» проиграла бенчмарку на всех трёх тестовых книгах, так что в коде осталось скучное среднее;
-
дальше значение ранжируется внутри главы в перцентиль и подгоняется под эталонный профиль. Без этого абсолютные пороги («важность > 0.75» для интонации кульминации) значили бы в разных книгах разное: на одной срабатывали бы на каждом абзаце, на другой – никогда.
Целиком закон важности выглядит скучнее своего описания – и это, пожалуй, лучшее, что можно про него сказать:
static func posWeight(_ p: POSCode) -> Double {
switch p {
case .noun, .propn: return 1.0
case .verb: return 0.9
case .adj: return 0.7
case .num: return 0.5
default: return 0.2
}
}
/// Масса значимости предложения: Σ salience × вес части речи.
static func salienceMass(_ lemmas: [WeightedLemma]) -> Double {
lemmas.reduce(0) { $0 + $1.salience * $1.posWeight }
}
/// Центральность к центроиду сцены. Нет вектора — нейтральная 1.0.
static func centrality(_ v: [Float]?, _ centroid: [Float]?) -> Double {
guard let v, let centroid else { return 1.0 }
return max(0, cosine(v, centroid))
}
static func sentenceScore(salienceMass: Double, centrality: Double) -> Double {
salienceMass * centrality
}
Ни одной обучаемой величины, ни одного вызова модели – чистая арифметика по колонкам, поэтому она одинаково считается и на телефоне, и в Mac-утилите, которая готовит .say.
Сцена. Границы считаются по сдвигу плотностей частей речи и по семантическому разрыву эмбеддингов – не по длине абзаца и не по одному сигналу. Дальше сцена получает свёрнутые сверху метрики: средняя и пиковая важность, эмоция и её размах, доля диалога, доминирующий говорящий, тип сцены. Арифметика по готовым колонкам, никакой модели.
Где нужна модель побольше. Локальная Foundation Model от Apple подключается точечно и только на верхнем уровне: назвать сцену заголовком в шесть слов и разметить её паспорт – точка зрения, место, время, ключевые события. Принципиальный момент: модель не пишет прозу, которая потом хранится и переигрывается, – она возвращает ссылки на предложения самой книги. Поэтому чат отвечает цитатами из текста: галлюцинировать нечем, максимум промахнуться ссылкой. Честно про потолок: разметка сущностей и говорящих на телефоне пока эвристическая, без валидации большой моделью – точность 80–90%, список сущностей иногда приходится править руками.
На эту же таблицу опираются слайдер важности от 5% до 100% (прокрутить книгу, оставив только несущий сюжет), чат с цитатами вместо пересказа и синтез: колонки «говорящий» и «эмоция» отдаются озвучке, поэтому у персонажа свой голос, а темп и высота меняются вместе с тем, что происходит в предложении. Как эта таблица выглядит глазами пользователя – на скриншоте иммерсивного режима ниже: там у каждого слова видны ровно эти колонки.
Что это значит в цифрах
Всё мерилось на iPhone 15 Pro Max, на релизной сборке и на реальных книгах — цифры ниже это время стены из логов, а не оценка.
|
Операция |
Замер |
В пересчёте |
|---|---|---|
|
Полный анализ книги – разметка, эмбеддинги, сцены |
2069 абзацев, 32 главы – 2,5 минуты |
около 85% этого времени уходит на фазу эмбеддингов |
|
Транскрипция аудиокниги |
11 ч 24 мин звука – 50 минут |
≈14× быстрее реального времени |
|
Перевод на языковом пакете Apple |
6,5 абзаца в секунду |
книга в 3400 абзацев – минут за девять |
|
Синтез речи (самый тяжёлый процесс) |
9,7–10,3× быстрее реального времени |
Apple и Silero на одном тексте практически поровну |
Ждать всё это целиком не надо: и транскрипция, и синтез идут по главам, так что слушать можно, пока готовятся следующие. А вот анализ стоит первым в очереди не случайно – он дешевле всего остального на порядок и при этом обязателен: из его колонок синтез берёт, где ускориться, где понизить тон и где поставить паузу. Без него озвучка получается ровной и плоской, каким бы хорошим ни был голос.
Эти цифры держатся на сознательном ограничении: всё построено на инфраструктуре самой Apple. Распознавание речи, перевод, эмбеддинги, языковая модель, аудиосессия, CarPlay – системные фреймворки, а не сторонние библиотеки. Извне мы тащим только данные: словари для разметки и веса моделей синтеза. Единственная внешняя зависимость с кодом – ONNX Runtime, нативный бинарник с предсказуемым поведением, который мы вызываем в своём потоке. Держать приложение, часами синтезирующее речь в фоне и при этом не падающее, сложно и так – каждая лишняя зависимость делает это на порядок сложнее.
Отсюда же честный ответ про Android: там нет системной локальной модели такого уровня, и половине стека просто не на чем стоять. Портировать урезанную копию мы думаем, но опыт может выйти хуже, чем отсутствие приложения.
Два железных ограничения – и обходные манёвры
Перевод через Apple Intelligence. Когда он включён, система сама перехватывает перевод и гонит его через генеративную модель – на уровне ОС, вне контроля приложения. Замер на книге в 3431 абзац: с включённым Apple Intelligence – 0.32–0.56 абзаца в секунду и тепловая пауза каждые 50–100 абзацев; с выключенным, через обычный языковой пакет – 5.9–6.5 абзаца в секунду и одна пауза на всю книгу. Разница больше чем десятикратная, и скачанный языковой пакет при включённом Apple Intelligence просто игнорируется. Обходной путь нашёлся не в коде, а в настройках телефона: на время большого перевода Apple Intelligence отключается целиком.
CarPlay и живой синтез. Прямое воспроизведение синтезированной речи в машине заикалось: CarPlay не готов к живому потоку TTS, а перезапуск плеера на границе каждого абзаца слышно растягивал паузы. Решение двухэтажное: аудио сначала синтезируется в файл и играет из буфера, а готовые файлы соседних абзацев одной сцены склеиваются в бесшовный трек. Цена – задержка 2–3 секунды на старте ещё не подготовленной главы.
В машине это отдельная поверхность с недавним, библиотекой, подписками и плейлистами, а не один экран «сейчас играет».
Малые языки: то, ради чего всё начиналось
Здесь сходятся все три статьи. Всё, что было в первых двух на примере кабардинского – синтез Silero, словарная лемматизация и POS-разметка, перенос тональности через языковой мост, — оказалось не разовым случаем, а методом: тем же путём полный словарный стек доведён до покрытия у 19 из 20 языков с Silero-озвучкой. Раньше это жило Python-сервисом на Mac mini, теперь работает на телефоне.
Ядро – портированная на iPhone Silero base под лицензией MIT: 43 голоса на 20 языков, полностью офлайн. По группам: славянские (русский, украинский, белорусский – с ударениями), тюркские (восемь языков), финно-угорские (три), кавказские (кабардинский, грузинский, армянский), плюс таджикский и калмыцкий. Ударения ставит отдельная портированная модель – русский без правильных ударений это не отдых, а наказание для слушателя. Ещё одна закрывает похожую дыру в распознавании: Apple прекрасно распознаёт русскую речь, но не расставляет знаки препинания (замер модели пунктуации против авторских субтитров подкаста: совпадение знаков 88.5%, капитализации 93.6%). Нативную лемматизацию, POS и NER от Apple из этих 20 языков получает только русский; для остальных 18 всё собрано словарным методом из второй статьи.
Масштаб в цифрах: не только Silero
Silero – только один слой. Синтез, распознавание речи, перевод и NLP-разметка считаются отдельно, и покрытие у них разное:
|
Функция |
Языков и вариантов |
Основные группы |
|---|---|---|
|
Синтез речи (Apple + Silero + Piper) |
68 |
европейские, славянские, тюркские, финно-угорские, кавказские, азиатские |
|
Распознавание речи |
47 |
европейские, славянские, азиатские |
|
Машинный перевод |
22 |
европейские, славянские, азиатские |
|
Полный NLP-стек (лемма + POS + NER + тональность) |
52 |
германские, романские, славянские, тюркские, финно-угорские, кавказские |
Полный список кодов – на сайте проекта. Отдельная история – эмбеддинги для смыслового поиска: у Apple они привязаны к письменности, а не к языку, поэтому латиница и кириллица считаются напрямую, а для белорусского, греческого, армянского, грузинского и кабардинского не работает ни та, ни другая модель. Там включается мост через глоссы: предложение слово за словом переводится в русские или английские глоссы, и эмбеддинг считается уже по ним. Каждый такой маршрут включался после замера с контрольным языком, а не «на всякий случай». Словари и модели для «обделённых» языков выложены отдельно, с открытыми лицензиями: github.com/sayfable-models.
Иммерсивный режим: побочный эффект полезнее исходной идеи
Планировался он как окно в то, как модель видит текст: у каждого слова лемма, часть речи, сущности, вес важности, валентность и настройки синтеза. Просто показать двигатель изнутри.
Но стоило подставить в то же окно глоссу – тот самый мост из предыдущего раздела, – как получился режим чтения на другом языке: под каждой леммой не перевод всей фразы, а её собственный аналог.
«Остров сокровищ» с кабардинскими глоссами: у текущего слова – часть речи, глосса ар, езы, рассчитанные rate / pitch / volume / pause и голос говорящего, ниже – важность, эмоция и сцена абзаца. Это и есть та самая таблица, показанная человеку.
Насколько это педагогически полезно – вопрос открытый: я читаю английские слова с параллельными кабардинскими глоссами, и они запоминаются заметно легче. Возможно, это особенность моего восприятия, а не свойство метода, – интересно, работает ли это у кого-то ещё.
Формат .say: разбор, который не приходится пересчитывать
.say – книга вместе с уже посчитанной таблицей: весами слов и валентностью, настройками синтеза, транскрипцией, сущностями, эмбеддингами предложений, переводом. Разобрали один раз – переслали файл на другой телефон, и там ничего не пересчитывается: книга открывается со сценами и персонажами сразу. 600 страниц весят около 15 МБ вместе с исходным текстом – в 50 раз меньше средней аудиокниги. Открытую спецификацию опубликуем на GitHub после тестов.
Отсюда же план, который снимает оговорку про эвристику выше: издавать книги из общественного достояния сразу в .say, с разметкой, прогнанной на большой модели. Тогда не придётся ни ждать анализа, ни править сущности руками: у каждой реплики проставлен правильный персонаж, а границы сцен подтверждены сменой времени, места и состава действующих лиц, а не только статистикой.
Как помочь с тестированием
Движок, кажется, получился. А удобное приложение рождается только из того, как им пользуются живые люди, – как любому родителю, мне сложно увидеть недостатки своего детища. Что интереснее всего проверить:
-
Заменяет ли SayFable у вас несколько отдельных приложений – или удобнее держать их порознь.
-
Синтез и подбор голоса под персонажа; для славянских – качество ударений и помогает ли личный словарь исправлений.
-
Насколько осмысленно собираются плейлисты по важности, эмоции и персонажам на вашей книге.
-
Ловит ли чат себя за руку там, где обычный чат-бот тихо бы что-то придумал: совпадают ли цитаты с текстом.
-
Как ведёт себя CarPlay – есть ли заикания на стыках абзацев на ещё не подготовленных главах.
-
Если вы носитель малоресурсного языка – как звучит синтез и насколько адекватна разметка.
Ссылка: https://sayfable.com/ – тестирование анонимное. Буду рад любым отзывам, восторженным и не очень.
Автор: Kubataba
- Запись добавлена: 06.08.2026 в 05:36
- Оставлено в


