radio: add activation controls, live VAD meters and signal diagnostics

This commit is contained in:
evgeny committed 2026-10-09 16:11:11 +02:00
1 parent fddbba217d
commit 7f3247bfcc
23 files changed
+818 -182

No files matched your search

+34 -4
View File
@@ -1,7 +1,6 @@
/* Настоящие Qt-кнопки, null audio backend и общий radio_audio; подменяем только вероятность VAD и сетевой выход. */
#include "../src/radiopanel.h"
#include "../src/radiosettingspage.h"
#include <QComboBox>
#include <QSpinBox>
#include "../src/radio_audio_engine.h"
#include "../src/sound_manager.h"
@@ -124,12 +123,42 @@ int main(int argc, char** argv) {
REQUIRE(engine->settingsBusy() && !vad->isEnabled());
await([&]() { return !engine->settingsBusy() && engine->autoAvailable(); });
REQUIRE(vad->isChecked() && outputs == oldOutputs && inputs == oldInputs + 1);
// Удержание открывает редактор без переключения автоматики и перезапуска захвата.
oldInputs = inputs;
QTest::mousePress(vad, Qt::LeftButton); pump(550);
auto* popup = panel.findChild<QMenu*>("radioActivationPopup");
REQUIRE(popup && popup->isVisible() && engine->autoEnabled() && inputs == oldInputs);
QTest::mouseRelease(vad, Qt::LeftButton);
REQUIRE(popup->isVisible() && engine->autoEnabled() && !engine->settingsBusy());
auto* quickVad = popup->findChild<QPushButton*>("radioModeVad");
auto* quickLevel = popup->findChild<QPushButton*>("radioModeLevel");
auto* quickThreshold = popup->findChild<QSpinBox*>("radioVadThreshold");
auto* quickOn = popup->findChild<QSpinBox*>("radioLevelOn");
auto* quickApply = popup->findChild<QPushButton*>("radioActivationApply");
auto* quickProbability = popup->findChild<QLabel*>("radioVadText");
auto* quickGain = popup->findChild<QLabel*>("radioVadGainText");
REQUIRE(quickVad && quickLevel && quickThreshold && quickOn && quickApply && quickProbability && quickGain);
quickLevel->click(); REQUIRE(quickOn->isEnabled() && quickThreshold->isEnabled() && engine->autoMode() == 1);
quickVad->click(); quickThreshold->setValue(60); REQUIRE(quickApply->isEnabled());
quickApply->click(); await([&] { return !engine->settingsBusy() && engine->vadValid(); });
REQUIRE(engine->setting("radio_vad_threshold") == 60 && engine->autoEnabled() && !quickApply->isEnabled());
probability = 0.25f;
await([&] { return quickProbability->text().contains("25.0%") && quickGain->text().contains("dB"); });
REQUIRE(engine->vadCompressorEnabled() && engine->vadGain() > 0);
REQUIRE(popup->grab().save("/tmp/desktop-radio-activation-popup.png"));
quickThreshold->setValue(70);
QTest::keyClick(popup, Qt::Key_Escape); REQUIRE(!popup->isVisible());
QMetaObject::invokeMethod(vad, "customContextMenuRequested", Q_ARG(QPoint, QPoint()));
REQUIRE(popup->isVisible() && quickThreshold->value() == 60);
popup->hide(); probability = 0;
qInfo("radio panel: hold/context popup, no toggle, direct modes, both thresholds, apply/cancel, live probability/gain PASS");
pump(750); // Полное кольцо prefetch до начала речи.
int previousPackets = packets;
probability = 0.9f;
await([&]() { return engine->isTransmitting() && begins == 1 && packets >= previousPackets + 30; });
REQUIRE(!engine->isTalking() && ptt->property("transmitting").toBool() && begins == 1);
REQUIRE(selfSpeaking()); // VAD включает собственный динамик без нажатой PTT.
REQUIRE(panel.grab().save("/tmp/desktop-radio-vad-meter.png"));
REQUIRE(packets >= previousPackets + 30);
int beforeManual = begins;
QTest::mousePress(ptt, Qt::LeftButton);
@@ -210,17 +239,18 @@ int main(int argc, char** argv) {
RadioSettingsPage settings(nullptr);
settings.resize(620, 780); settings.show();
auto* mode = settings.findChild<QComboBox*>("radioAutoMode");
auto* mode = settings.findChild<QPushButton*>("radioModeLevel");
auto* on = settings.findChild<QSpinBox*>("radioLevelOn");
auto* off = settings.findChild<QSpinBox*>("radioLevelOff");
auto* levelText = settings.findChild<QLabel*>("radioLevelText");
REQUIRE(mode && on && off && levelText);
mode->setCurrentIndex(1); on->setValue(-50); off->setValue(-40);
mode->click(); on->setValue(-50); off->setValue(-40);
REQUIRE(!settings.apply() && !engine->settingsBusy() && engine->autoMode() == 1);
off->setValue(-60);
REQUIRE(settings.apply());
await([&]() { return !engine->settingsBusy() && engine->autoAvailable() && engine->inputLevelValid(); });
REQUIRE(engine->autoMode() == 2 && vad->text() == "Level" && radio_audio_auto_mode() == RADIO_AUTO_LEVEL);
REQUIRE(engine->autoMode() == 2 && !vad->icon().isNull() && vad->text().isEmpty() && radio_audio_auto_mode() == RADIO_AUTO_LEVEL);
REQUIRE(settings.findChild<QSpinBox*>("radioVadThreshold")->isEnabled() && on->isEnabled());
REQUIRE(!engine->isTransmitting() && status->text() == "Waiting for signal");
REQUIRE(levelText->text().contains("dBFS") && engine->inputLevel() == -120);
// Silero недоступен; уровень работает и сохраняется вместе с парой порогов.