You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
61 lines
2.7 KiB
61 lines
2.7 KiB
#pragma once |
|
#include "audio_event_queue.h" |
|
#include <atomic> |
|
#include <condition_variable> |
|
#include <mutex> |
|
#include <thread> |
|
#include <cstdint> |
|
|
|
/* Desktop: аппаратные callbacks только копируют PCM, render также выполняет готовое смешивание. |
|
* Capture worker — единственный владелец AEC и порядка PCM/PTT/mute. RX worker независим от VAD. |
|
* Управляющий поток закрывает capture и отсоединяет output до stop/destruction. |
|
* Финальный stereo render приходит с того же выходного устройства после всего микширования. */ |
|
class VoiceAudioIo { |
|
public: |
|
enum class Kind { Call, Radio }; |
|
VoiceAudioIo() = default; |
|
~VoiceAudioIo(); |
|
bool start(Kind kind, uint64_t session); |
|
void stop(); |
|
bool startCapture(int channels, bool aecEnabled); |
|
void stopCapture(); |
|
bool captureRunning() const { return m_capturing.load(); } |
|
void setPtt(bool pressed); |
|
void setMuted(bool muted); |
|
void setAecEnabled(bool enabled); |
|
void resetPlayback(); |
|
/* firstTimeUs — монотонное время первого физического PCM-фрейма (backend либо оценка). */ |
|
void capture(const int16_t* pcm, unsigned frames, int64_t firstTimeUs, uint64_t route = 0, bool missing = false); |
|
void reference(const float* pcm, unsigned frames, int64_t firstTimeUs); |
|
void mix(float* stereo, unsigned frames); |
|
private: |
|
enum class EventKind { Pcm, Ptt, Mute, Aec, Stop }; |
|
struct Event { |
|
EventKind kind = EventKind::Pcm; |
|
unsigned frames = 0; |
|
int64_t time = 0; |
|
int64_t enqueued = 0; |
|
uint64_t sequence = 0; |
|
uint64_t route = 0; |
|
bool value = false; |
|
int16_t pcm[1920]{}; |
|
}; |
|
struct Playback { uint64_t epoch = 0; int16_t pcm[1920]{}; }; |
|
AudioEventQueue<Event, 32> m_captureEvents, m_references; |
|
AudioEventQueue<Playback, 12> m_playback; |
|
std::atomic<bool> m_running{false}, m_capturing{false}; |
|
std::atomic<uint64_t> m_playEpoch{0}, m_captureDropped{0}, m_referenceDropped{0}, m_playbackUnderruns{0}; |
|
uint64_t m_captureSequence = 0; // Только capture callback; reset после его остановки. |
|
Kind m_kind = Kind::Call; |
|
uint64_t m_session = 0; |
|
int m_channels = 1; |
|
std::atomic<bool> m_aecRequested{false}; |
|
std::thread m_captureThread, m_playbackThread; |
|
std::mutex m_waitMutex; |
|
std::condition_variable m_wake; |
|
Playback m_playCurrent{}; |
|
unsigned m_playPosition = 960; |
|
void command(EventKind kind, bool value = false); |
|
void captureLoop(); |
|
void playbackLoop(); |
|
};
|
|
|