89 lines
3.7 KiB
C++
89 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include <QObject>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include "audio_device.h"
|
|
#include "voice_audio_io.h"
|
|
#include <QTimer>
|
|
#include <QMap>
|
|
#include <array>
|
|
|
|
struct ma_device;
|
|
|
|
/* Desktop-контур рации: GUI владеет capture device и жизненным циклом.
|
|
* Hardware callback копирует PCM в ограниченную очередь VoiceAudioIo.
|
|
* Capture worker выполняет AEC и упорядочивает PCM/PTT/mute; независимый RX worker
|
|
* получает готовый голос из сервисного слоя. SoundManager смешивает его со всеми
|
|
* звуками приложения на том же выходе и возвращает итоговый stereo reference.
|
|
* Stop: close capture → join capture worker → detach output → join RX → release core. */
|
|
class RadioAudioEngine : public QObject {
|
|
Q_OBJECT
|
|
public:
|
|
static RadioAudioEngine* instance();
|
|
|
|
/* Включить прослушивание канала (после toggle рации). 0 = успех. */
|
|
int start(quint64 groupId);
|
|
|
|
/* Выключить прослушивание канала (toggle рации выключен). */
|
|
void stop();
|
|
|
|
/* PTT: зажата / отпущена (только когда радио канала включено). */
|
|
void talkBegin();
|
|
void talkEnd();
|
|
void setHotkeyTalking(bool pressed); // независимый владелец PTT — глобальная клавиатура
|
|
|
|
/* Настройки читаются/применяются в uasync; после подтверждения GUI перезапускает только capture. */
|
|
void loadSettings(const QString& configPath);
|
|
bool applySettings(const QMap<QString, int>& values);
|
|
int setting(const QString& name) const;
|
|
bool settingsReady() const { return m_settingsReady; }
|
|
bool settingsBusy() const { return m_settingsBusy; }
|
|
bool vadEnabled() const { return m_settings[0] != 0; }
|
|
bool vadAvailable() const { return m_vadAvailable; }
|
|
bool captureAvailable() const { return m_captureAvailable; }
|
|
bool isTransmitting() const { return m_transmitting; }
|
|
QString settingsError() const { return m_settingsError; }
|
|
|
|
bool isTalking() const { return m_talking.load(); }
|
|
bool isActive() const { return m_active.load(); }
|
|
quint64 groupId() const { return m_groupId.load(); }
|
|
|
|
/* Hardware capture callback: без DSP и растущих буферов. */
|
|
void captureCallback(const void* in, unsigned int frames);
|
|
|
|
signals:
|
|
void talkingChanged(bool talking); // GUI-поток, суммарное состояние кнопки и хоткея
|
|
void stateChanged(); // фактический TX, доступность capture/VAD и настройки; GUI-поток
|
|
|
|
private:
|
|
RadioAudioEngine();
|
|
void updateTalking();
|
|
bool m_buttonTalking = false, m_hotkeyTalking = false; // GUI-поток
|
|
void pauseDevices();
|
|
void recoverDevices();
|
|
void openCapture();
|
|
void updateState();
|
|
void reconfigureCapture();
|
|
void postSettings(const std::array<int, 6>& values, bool apply);
|
|
std::array<int, 6> m_settings{0, 50, 200, 1, 15, 15};
|
|
QString m_configPath;
|
|
QString m_settingsError;
|
|
bool m_settingsReady = false, m_settingsBusy = false;
|
|
bool m_transmitting = false, m_vadAvailable = false, m_captureAvailable = false;
|
|
|
|
std::atomic<bool> m_active{false};
|
|
std::atomic<bool> m_talking{false};
|
|
std::atomic<uint64_t> m_groupId{0};
|
|
|
|
AudioDevice m_capture;
|
|
VoiceAudioIo m_voice;
|
|
bool m_outputAttached = false;
|
|
QTimer m_recoveryTimer;
|
|
bool m_wantStereo = false;
|
|
QString m_captureId, m_playbackId;
|
|
|
|
int m_captureChannels = 1;
|
|
};
|