69 lines
3.3 KiB
C++
69 lines
3.3 KiB
C++
#pragma once
|
|
|
|
#include "miniaudio.h"
|
|
#include "audio_buffer_tuning.h"
|
|
#include <QElapsedTimer>
|
|
#include <atomic>
|
|
#include <functional>
|
|
#include <thread>
|
|
|
|
/* Один владелец в GUI-потоке. Callback публикует только прогресс и уведомления;
|
|
* close() дожидается worker перед освобождением callback и его состояния. */
|
|
class AudioDevice {
|
|
public:
|
|
using Callback = std::function<void(const void*, void*, unsigned int)>;
|
|
AudioDevice();
|
|
~AudioDevice();
|
|
AudioDevice(const AudioDevice&) = delete;
|
|
AudioDevice& operator=(const AudioDevice&) = delete;
|
|
static void requestLowLatency(ma_context* context, ma_device_config& config);
|
|
bool init(ma_context* context, ma_device_config config, const char* role, uint64_t session, Callback callback);
|
|
bool start();
|
|
void pause(); // GUI: завершает текущий callback, сохраняя backend stream и его физический route.
|
|
void close();
|
|
void closeAsync(); // Callback и владелец живут до завершения uninit; close() дожидается worker.
|
|
bool closing() const { return !m_closeDone.load(std::memory_order_acquire); }
|
|
bool captureTimedOut() const; // Секунда без корректного PCM; нулевые сэмплы считаются данными.
|
|
bool needsRecovery();
|
|
bool resizingBuffer() const { return m_resizePending; } // После needsRecovery(): переоткрытие с увеличенным буфером.
|
|
bool retryReady() const;
|
|
void retryLater();
|
|
void retryNow();
|
|
bool initialized() const { return m_device != nullptr; }
|
|
ma_device* device() const { return m_device; }
|
|
int64_t callbackTimeUs() const { return m_callbackTime.load(std::memory_order_relaxed); }
|
|
bool timingMeasured() const { return m_timingMeasured.load(std::memory_order_relaxed); }
|
|
uint64_t route() const { return m_route.load(std::memory_order_relaxed); }
|
|
bool captureMissing() const { return m_captureMissing.load(std::memory_order_relaxed); }
|
|
private:
|
|
static void data(ma_device* device, void* out, const void* in, ma_uint32 frames);
|
|
static void notification(const ma_device_notification* event);
|
|
ma_device* m_device = nullptr;
|
|
Callback m_callback;
|
|
const char* m_role = "audio";
|
|
QElapsedTimer m_clock;
|
|
std::atomic<uint64_t> m_frames{0};
|
|
std::atomic<int64_t> m_callbackTime{0};
|
|
std::atomic<bool> m_timingMeasured{false};
|
|
std::atomic<uint64_t> m_route{0};
|
|
std::atomic<bool> m_captureMissing{false};
|
|
std::atomic<int64_t> m_captureProgress{0};
|
|
std::thread m_closeThread;
|
|
std::atomic<bool> m_closeDone{true};
|
|
uint64_t m_reportedRoute = 0;
|
|
int m_reportedTiming = -1;
|
|
std::atomic<bool> m_suspended{false};
|
|
std::atomic<unsigned> m_bufferGlitches{0};
|
|
uint64_t m_pendingGlitches = 0;
|
|
qint64 m_glitchReportAt = -5000;
|
|
AudioBufferTuning m_bufferTuning;
|
|
ma_device_id m_bufferDeviceId{};
|
|
ma_backend m_bufferBackend = ma_backend_null;
|
|
ma_device_type m_bufferType = ma_device_type_capture;
|
|
uint64_t m_bufferSession = 0;
|
|
bool m_bufferKeyValid = false, m_bufferSelected = false, m_resizePending = false;
|
|
uint64_t m_seenFrames = 0;
|
|
qint64 m_progressAt = 0, m_retryAt = 0;
|
|
int m_retryDelay = 250;
|
|
};
|