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

#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();
};