Взлом
Уважаемые гости! При посещении нашего сайта просим вас ознакомиться с разделами форума, прежде чем оставлять ваши объявления и т.п., а также при обращении за помощью просим быть внимательными: на сайте есть как проверенные специалисты, так и непроверенные. Если вы обратились к специалисту, который проверку НЕ проходил, рекомендуем воспользоваться услугой гарант-сервиса. Спасибо, что посетили XakerPlus!

После комментариев на Хабре мы развернули тестовую ветку БОЛТУНа: Electron, WinUI 3 или Tauri 2?

📌 Как поднять тему без нарушений

Не допускаются бессодержательные комментарии: «Ап»,«Up»,«Актуально»«Работаю» и прочие однотипные сообщения.

✅ Как поднять/обновить тему правильно: Напишите что изменилось в услугах, какие новости, почему сейчас стоит обратиться или приобрести товар — конкретно и по делу.

⚠️ За систематический спам — ограничения вплоть до блокировки и удаления тем

🔒 Предупреждение отображается только для автора темы

P

PAPADAN

Гость
article-b9310bcca3f7305c.png


В первой статье мы рассказали, как впятером делали голосовую связь и несколько недель обвиняли Opus в провалах, которые на самом деле создавал наш логгер.

После публикации обсуждение довольно быстро вышло за пределы кодека. Спасибо всем, кто прочитал статью и поделился ценными мыслями. Благодаря вам мы внимательнее посмотрели на Windows-клиент и задумались, как отделить работающий звуковой тракт от интерфейса и попробовать более современную оболочку.

Самый простой ответ звучал бы так: «Win32 работает — не трогайте». И это не шутка. Через Win32 у нас уже работали глобальный push-to-talk, трей, прозрачный click-through оверлей поверх игры, выбор устройств и жизненный цикл окон. Голосовой тракт на C++ использовал WASAPI, Opus, RNNoise и SpeexDSP. Переписывать всё ради скруглённых кнопок было бы плохим обменом.

И эти мысли попали в больное место. К этому моменту один только SetupWindow.cpp вырос более чем до 6000 строк. В нём жили создание контролов, раскладка, GDI/GDI+ рисование, анимация человечка, комнаты, аватары, ввод PIN, настройки микрофона и обработка сообщений Windows. Каждая новая панель означала ещё набор координат, состояний, перерисовок и ручных проверок DPI.

Так появилась не идея «переписать БОЛТУН на модный фреймворк», а более узкий вопрос:

На каком стеке развивать следующий Desktop-интерфейс, если real-time голос, PTT, сеть и оверлей должны остаться в уже работающем нативном ядре?

Мы не стали переделывать стабильный клиент на глазах у пользователей. Сначала развернули отдельную тестовую ветку БОЛТУНа. На ней изучили три варианта для нового интерфейсного стека — Electron, WinUI 3 и Tauri 2, — а затем собрали вертикальный прототип на Tauri. Эта статья — промежуточный отчёт: выбор для эксперимента уже сделан, но решение для рабочего клиента ещё предстоит подтвердить интеграцией и замерами.

Почему Win32 вообще был разумным выбором

Первая версия начиналась как небольшая говорилка для пяти человек, а не как продукт с дизайн-системой. Win32 давал всё, что требовалось рядом с игрой:

  • обычный нативный EXE без отдельной GUI-среды выполнения в комплекте;
  • прямой доступ к циклу сообщений, устройствам и системным событиям;
  • глобальные клавиши через RegisterHotKey и GetAsyncKeyState;
  • прозрачный оверлей с WS_EX_LAYERED, WS_EX_TOPMOST, WS_EX_NOACTIVATE и WS_EX_TRANSPARENT;
  • трей и несколько небольших служебных окон;
  • отсутствие ещё одного IPC-слоя между интерфейсом и кодом на C++.

Для первой версии это было быстрее и предсказуемее любого большого архитектурного проекта.

Проблема появилась не потому, что Win32 внезапно испортился. Изменился сам БОЛТУН. К лобби добавились полноценные комнаты, персональная громкость, обработка звука, уведомления, обновления, радио, интерактивный человечек и новый визуальный язык. Интерфейс перестал быть тонкой оболочкой над пятью кнопками.

Win32 по-прежнему прекрасно решал системные задачи, но всё дороже решал задачу «нарисовать и поддерживать современное приложение».

Сначала развернули отдельную тестовую ветку

Тестовая ветка — не новое название продукта и не отдельный голосовой сервис. Это тот же БОЛТУН, но с собственным закрытым контуром для изменений, которые пока рано отдавать всем.

Мы развернули для неё отдельный сайт, отдельный процесс Go-координатора, собственный Hub с комнатами и отдельный каталог данных. Тестовые Windows- и Android-сборки жёстко направлены на тестовый WebSocket. Их комнаты не смешиваются с комнатами стабильной версии. Страница закрыта паролем и помечена noindex, поэтому эксперимент не выглядит как публичный релиз.

При этом контуры пока живут на одном сервере. Изоляция защищает данные, комнаты и версии, но тяжёлый тест всё ещё способен конкурировать со стабильным процессом за CPU и сеть. Поэтому тестовая ветка — страховка от функциональных ошибок, а не лицензия бездумно нагружать машину.

Именно туда мы сначала добавили новый интерфейс, двухэтапную автокалибровку и экспериментальную Desktop-оболочку. Стабильный БОЛТУН продолжил работать на прежнем коде.

Три кандидата: Electron, WinUI 3 и Tauri 2

Мы не начинали сравнение с вопроса «какой фреймворк моднее». У голосового клиента уже были ограничения, которые нельзя было проигнорировать:

  • существующее ядро на C++ со звуком, сетью и DSP нельзя переписывать вместе с интерфейсом;
  • глобальный PTT, трей и игровой оверлей должны сохранить нативное поведение;
  • PCM-кадры не должны ходить через UI-IPC;
  • хотелось повторно использовать TypeScript, CSS, SVG и часть логики веб-клиента;
  • приложение постоянно висит рядом с игрой, поэтому цену среды выполнения нужно не угадывать, а измерять;
  • переход должен быть поэтапным: сначала тестовая ветка и узкий мост к ядру, затем решение по рабочему клиенту.

С этими критериями у каждого кандидата обнаружился свой сильный сценарий.

Первое сравнение фреймворков мы сделали с помощью ИИ. Описали ему устройство БОЛТУНа, существующее ядро на C++ и наши требования, а он помог быстро разложить Electron, WinUI 3 и Tauri 2 по плюсам, минусам и возможным проблемам. После этого мы перепроверили важные пункты по официальной документации и перешли к прототипу. ИИ заметно ускорил исследование, но окончательный выбор мы делали уже под свой проект и свои ограничения.

Electron: наиболее прямой путь для веб-команды

Electron выглядел самым знакомым вариантом. Интерфейс можно собирать привычными веб-инструментами, поведение Chromium одинаково на поддерживаемых системах, а вокруг фреймворка есть зрелая экосистема. Для быстрого переноса веб-интерфейса это серьёзные плюсы.

Но Electron — это не просто окно с HTML. В его официальной модели процессов главный процесс работает в среде Node.js, а окна получают отдельные renderer-процессы Chromium. В рекомендациях по безопасности отдельно подчёркиваются обновление Electron вместе с Chromium и Node.js, sandbox, context isolation, CSP и проверка IPC.

Для БОЛТУНа это означало бы поставлять собственную браузерную среду и аккуратно защищать ещё одну границу между интерфейсом и системой. Мы не стали заранее объявлять Electron «тяжёлым»: время запуска, RAM и CPU всё равно нужно сравнивать на одной машине. Но для небольшой постоянно работающей утилиты собственный Chromium оказался архитектурной ценой, которую наш прототип пока не оправдывал.

WinUI 3: современный нативный Windows-вариант

WinUI 3 был не кандидатом для галочки, а самой сильной нативной альтернативой. Microsoft рекомендует его для новых Windows-приложений: это C++ или C# с XAML, современными контролами и поддержкой Windows 10 версии 1809 и новее. Windows App SDK можно подключать и к существующим Win32-приложениям, а XAML Islands позволяют внедрять новый интерфейс постепенно.

Для нашего ядра на C++ это естественное соседство: меньше концептуальных слоёв между UI и Windows API, понятный путь к системным функциям и нативный визуальный стек. Если бы единственным критерием была интеграция с текущим Windows-клиентом, WinUI 3 мог бы оказаться первым выбором.

Цена — новый XAML-слой, особенности упаковки и развёртывания Windows App SDK и привязка нового интерфейса к Windows. Уже написанные TypeScript, CSS, SVG и браузерная логика не переезжают в WinUI напрямую. Для нашего эксперимента это означало сначала заново собрать UI, а уже потом проверять архитектурную границу с ядром.

Tauri 2: системный WebView и узкий Rust-bridge

Tauri занял промежуточную позицию. Его frontend остаётся веб-интерфейсом, backend пишется на Rust, а обмен между ними строится через команды и события. На Windows фреймворк использует установленный Microsoft Edge WebView2, то есть не кладёт ещё один Chromium в каждый дистрибутив. Общая схема с системным WebView описана в архитектуре Tauri.

Это позволяло повторно использовать нашу работу с TypeScript, CSS, SVG и motion JSON, а границу с ядром на C++ оформить небольшим Rust-адаптером. Для frontend можно явно ограничить доступ через capabilities и permissions и дополнительно зажать источники контента через CSP.

У Tauri тоже нет бесплатных чудес. Нужно учитывать наличие и версию WebView2, различия системных WebView на разных ОС, освоить Rust и самостоятельно спроектировать bridge к C++. Глобальный PTT, click-through overlay и высокочастотный звук от смены оболочки проще не становятся. А экономию памяти и размера ещё предстоит доказать замерами, а не логотипом фреймворка.

Если свести предварительный выбор к короткой таблице, получилось так:

КритерийElectronWinUI 3Tauri 2
Повторное использование нашего веб-интерфейсамаксимальноеминимальноемаксимальное
Соседство с существующим C++/Win32-кодомчерез нативный мостнаиболее прямоечерез мост Rust/C++
Среда интерфейсаChromium и Node.js в поставкеWindows App SDKсистемный WebView2
Другие Desktop-ОС в перспективеданетда, с учётом различий WebView
Главный риск для насцена runtime и поверхность IPCповторная реализация UI и Windows-onlyнезавершённый native bridge и необходимость бенчмарков

Мы выбрали Tauri 2 не потому, что он победил остальные варианты по всем пунктам. Для тестовой ветки он дал наиболее полезный компромисс: повторно использовать новый веб-интерфейс, не переносить аудиоядро, не поставлять собственный Chromium и проверить строгую границу WebView -> Rust -> C++. WinUI 3 остался запасным нативным вариантом, а Electron — понятной точкой сравнения для будущих замеров.

Почему для прототипа выбрали Tauri 2

Для проверки выбранной схемы мы создали ещё один изолированный проект внутри тестовой работы. Он не заменяет текущий Windows-клиент и не меняет стабильную сборку.

Целевая граница выглядит так:

Код:
┌────────────────────────────────────────────┐
│         Tauri WebView: представление       │
│                                            │
│  комнаты · настройки · радио · SVG UI      │
│  аватары · анимация · состояния кнопок     │
└──────────────────────┬─────────────────────┘
                       │ команды и события
┌──────────────────────▼─────────────────────┐
│             Tauri 2 / Rust                 │
│                                            │
│ validation · lifecycle · capabilities      │
│ адаптер к существующему native core        │
└──────────────────────┬─────────────────────┘
                       │ узкий интерфейс
┌──────────────────────▼─────────────────────┐
│       Существующее ядро C++ / Win32        │
│                                            │
│ WASAPI · Opus · DSP · сеть · PTT · overlay │
└────────────────────────────────────────────┘

Главное правило: высокочастотное аудио не проходит через JSON IPC. WebView не должен получать PCM-буфер каждые 20 мс, рисовать из него бизнес-логику или решать, отправлять ли голос в комнату.

Интерфейс может сказать:

Код:
начать автокалибровку
выбрать микрофон
включить push-to-talk
установить громкость участника

Нативное ядро может вернуть:

Код:
калибровка завершена
шумовой фон: -52,4 dBFS
рекомендуемый gain: 1,18
VAD threshold: 0,014

Но сами аудиокадры остаются в C++.

Из чего собрали прототип

В эксперименте используются:

  • Tauri CLI 2.11.4;
  • Rust 1.97.1 GNU;
  • TypeScript 7.0.2;
  • Vite 8.2.1;
  • установленный в Windows Microsoft Edge WebView2.

React мы не добавляли. Веб-клиент БОЛТУНа и так построен без тяжёлого UI-фреймворка, а для прототипа хватило TypeScript, CSS и Vite. В production-зависимостях интерфейса остался только @tauri-apps/api.

Tauri-часть тоже пока минимальна. Rust создаёт окно с отдельным каталогом данных WebView2 и предоставляет одну команду-заглушку get_runtime_status. Она доказывает работу первоначального IPC-моста, но ещё не подключает настоящее голосовое ядро на C++.

Это важное ограничение. Красивый экран комнат не доказывает, что Tauri-клиент умеет разговаривать.

Что уже получилось

Прототип воспроизвёл новый интерфейс тестовой ветки БОЛТУНа в отдельном frameless-окне: профиль, комнаты, участники, создание комнаты, настройки звука и панель радио.

Ещё в новом интерфейсе появился маленький анимированный человечек. К голосовой связи он напрямую не относится — это скорее небольшая живая деталь, чтобы окно не выглядело совсем статичным. В Tauri мы собрали его из SVG-частей, поэтому он остаётся чётким при разном масштабе. Человечек умеет ходить, целиться, уклоняться, получать попадания и падать.

Мы начинали с простой анимации, а в итоге неожиданно для себя получили первый опыт создания маленькой игры внутри БОЛТУНа. Но это уже отдельная история для следующей статьи.

Сборка Tauri в режиме release запустилась в нативном WebView2-окне. SVG и motion JSON загружаются под CSP, окно корректно сворачивается, разворачивается, восстанавливается и закрывается. Прототип остался полностью изолирован от рабочего клиента.

Для доступа frontend мы выдали только минимальные capabilities:

Код:
{
  "permissions": [
    "core:default",
    "core:window:allow-minimize",
    "core:window:allow-toggle-maximize",
    "core:window:allow-close"
  ]
}

В CSP нет разрешения на произвольные внешние скрипты, shell или файловую систему. Когда появятся настоящие команды ядра, каждую придётся добавлять явно и проверять её входные данные на Rust/native-стороне.

Автокалибровка как проверка архитектурной границы

В интерфейсе тестовой ветки БОЛТУНа есть кнопка «Автокалибровка · 3 сек». В Tauri-прототипе она пока демонстрирует состояние UI. В тестовой версии настоящего клиента за ней уже работает нативный алгоритм на Windows и аналогичная реализация на Android.

Эта функция хорошо показывает, почему мы не хотим переносить весь код в WebView.

Пользователь не должен знать, что такое VAD threshold, dBFS и входной gain. Он нажимает кнопку и три секунды говорит обычным голосом. Приложение должно подобрать чувствительность так, чтобы не отправлять шум комнаты и не отрезать тихие начала слов.

Никакой нейросети и облачного распознавания здесь нет. Калибровка выполняется локально на PCM:

Код:
WASAPI / AudioRecord
        ↓
20-миллисекундные PCM-кадры
        ↓
RMS каждого кадра + общий peak
        ↓
оценка шумового фона и голосовых кадров
        ↓
рекомендованные input gain и VAD threshold

Содержание речи не распознаётся и на сервер не отправляется. В настройках сохраняются только имя устройства, уровни в децибелах и подобранные коэффициенты.

Три секунды быстро, минута осторожно

Одного трёхсекундного окна мало. Пользователь мог нажать кнопку и не успеть заговорить. Поэтому в тестовой версии появилась двухэтапная схема:

  1. Быстрая фаза собирает три секунды и сразу даёт пригодную настройку.
  2. Фоновая фаза продолжает анализ ещё 57 секунд и уточняет профиль по первой минуте разговора.

Быстрый результат применяется сразу. Финальный — плавно, чтобы порог VAD не перескочил посреди слова. На каждом цикле ядро приближает текущие значения к рассчитанным на 3,5% разницы:

Код:
appliedGain += (targetGain - appliedGain) * 0.035f;
appliedVad  += (targetVad  - appliedVad)  * 0.035f;

При push-to-talk в голосовую выборку попадают только кадры, снятые во время удержания назначенной кнопки. Иначе минута честного молчания могла бы убедить калибровщик, что голос у пользователя отсутствует.

После смены микрофона сохранённый профиль сбрасывается. Калибровка встроенного микрофона ноутбука не должна автоматически применяться к USB-гарнитуре.

Почему не стоит писать третий калибровщик на TypeScript

На Windows алгоритм реализован на C++, на Android — на Java рядом с AudioRecord. Формулы близки, но уже имеют небольшие платформенные различия. Например, Android оценивает рекомендуемый VAD из raw RMS и текущего gain, а Windows использует уровень, который видит интерфейс.

Если перенести логику в Tauri frontend, появится третья реализация. Она будет зависеть от частоты обновления UI, WebView lifecycle и сериализации. Со временем три клиента начнут по-разному понимать одну и ту же кнопку «Автокалибровка».

Поэтому для Tauri мы хотим оставить простой контракт:

Код:
UI -> begin_microphone_calibration(device_id)

native -> calibration_progress(seconds)
native -> calibration_finished({
  noiseFloorDb,
  voiceRmsDb,
  peakDb,
  inputGain,
  vadThreshold,
  voiceDetected,
  clippingDetected
})

PCM, процентили и изменение gain остаются в нативном аудиопути. WebView только показывает процесс и результат.

К этому моменту прототип уже ответил на главный вопрос первого этапа: новый интерфейс можно собрать в Tauri, запустить в WebView2 и изолировать от стабильного клиента. Этого было достаточно, чтобы перейти от выбора фреймворка к следующей задаче — аккуратно соединить новый UI с работающим нативным ядром.

Поэтому дальнейшую работу мы разложили на несколько этапов, не пытаясь заменить весь Windows-клиент одним большим обновлением.

Как будет выглядеть миграция, если тест пройдёт

Мы не планируем одним коммитом заменить всё приложение.

Первый этап — подключить Tauri UI к существующему ядру на C++ через узкий адаптер. Rust отвечает за проверку команд, жизненный цикл приложения и разрешения. C++ продолжает захватывать и воспроизводить звук, кодировать Opus, работать с сетью и глобальными клавишами.

Второй этап — перенести в Rust только те системные сервисы, для которых появится измеримая польза. Не потому, что Rust «правильнее», а потому, что конкретный модуль станет безопаснее или проще сопровождать.

Третий этап — отказаться от старого Win32 GUI только после функционального паритета, сравнительных замеров и полевого теста с игрой.

Оверлей может вообще остаться отдельным нативным окном. Его текущие свойства — topmost, click-through и отсутствие фокуса — важнее единообразия технологии.

Что мы вынесли из обсуждения

Обсуждение не доказало, что Win32 был ошибкой. Первая версия благодаря ему вообще появилась и получила рабочий PTT, оверлей и звук без многомесячного фреймворк-проекта.

Но обсуждение помогло правильно сформулировать накопившуюся проблему. Нам не обязательно выбирать между двумя крайностями:

Код:
оставить весь интерфейс на Win32 навсегда
                или
переписать всё приложение вместе со звуком

Можно заменить представление, сохранив real-time ядро. Tauri 2 здесь не новая религия, а проверяемая гипотеза.

Сейчас результат такой: тестовая ветка уже доказала, что новый интерфейс БОЛТУНа можно собрать в Tauri/WebView2, сохранить векторную графику и изолировать эксперимент от стабильной версии. Она ещё не доказала, что Tauri готов заменить рабочий Win32-клиент в игре.

Именно поэтому следующий шаг — не дорисовывать ещё одну красивую панель, а подключить настоящий мост к ядру на C++, вызвать через него ту же автокалибровку и измерить, замечает ли аудиопоток существование WebView.

Если не замечает — у эксперимента есть будущее.

Проект: БОЛТУН

Отдельное спасибо Alexme — именно он настоял на внедрении нового фреймворка и подтолкнул нас к этому эксперименту.

Команда: PAPA DAN — разработчик; Alexme — главный тестировщик; Mihanz, San4o и A1ex — участники командных проверок и обратной связи.

Источник: habr.com
 


Сверху