Refresh both AGENTS guides and clarify module purpose, usage, ownership, threading, return values and wire formats in headers. Remove the unused timeout_heap freed_count field and unimplemented getter.
86 lines
2.7 KiB
C++
86 lines
2.7 KiB
C++
/* Запись голосового сообщения: capture 48 kHz mono → очередь → worker PCM/компрессора.
|
|
* GUI управляет init/start/stop/shutdown; meterOnly измеряет уровень без накопления записи.
|
|
* takeRecording передаёт остановленные PCM/компрессор в attachment_send_req для фоновой подготовки. */
|
|
#pragma once
|
|
|
|
#include <QObject>
|
|
#include <QString>
|
|
#include <QTimer>
|
|
#include <vector>
|
|
#include <cstdint>
|
|
#include "audio_device.h"
|
|
#include "audio_event_queue.h"
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
extern "C" {
|
|
#include "audio_compressor.h"
|
|
}
|
|
|
|
struct ma_device;
|
|
struct audio_compressor;
|
|
struct attachment_send_req;
|
|
|
|
class AudioRecorder : public QObject {
|
|
Q_OBJECT
|
|
public:
|
|
explicit AudioRecorder(QObject* parent = nullptr);
|
|
~AudioRecorder() override;
|
|
|
|
bool init();
|
|
void shutdown();
|
|
bool isInitialized() const { return m_initialized; }
|
|
|
|
void setCaptureDevice(const QString& id) { m_captureId = id; m_customDevice = true; }
|
|
|
|
void startRecording(bool meterOnly = false);
|
|
void stopRecording();
|
|
bool isRecording() const { return m_recording; }
|
|
|
|
float peakLevel() const;
|
|
|
|
int durationMs() const;
|
|
bool takeRecording(struct attachment_send_req* request); /* Передать остановленную запись без копии PCM. */
|
|
|
|
void configureCompressor(const audio_compressor_config_t& config);
|
|
void setCompressorEnabled(bool enabled);
|
|
bool isCompressorEnabled() const { return m_compressorEnabled; }
|
|
|
|
signals:
|
|
void recordingStarted();
|
|
void recordingStopped(int durationMs);
|
|
void durationChanged(int ms);
|
|
|
|
private:
|
|
AudioDevice m_capture;
|
|
QTimer m_recoveryTimer;
|
|
void recoverCapture();
|
|
void captureCallback(const void* input, unsigned int frames);
|
|
void recordLoop();
|
|
struct CaptureBlock { unsigned frames = 0; int16_t pcm[1920]{}; };
|
|
AudioEventQueue<CaptureBlock, 32> m_captureQueue;
|
|
std::thread m_recordWorker;
|
|
std::mutex m_waitMutex;
|
|
std::condition_variable m_wake;
|
|
std::atomic<bool> m_workerRunning{false}, m_captureFailed{false};
|
|
bool m_meterOnly = false;
|
|
bool m_initialized = false;
|
|
std::atomic<bool> m_recording{false};
|
|
int m_sampleRate = 48000;
|
|
int m_channels = 1;
|
|
QString m_captureId;
|
|
bool m_customDevice = false;
|
|
|
|
std::vector<int16_t> m_pcmBuffer;
|
|
mutable std::atomic<float> m_peakLevel{0.0f};
|
|
std::atomic<uint64_t> m_recordedFrames{0};
|
|
QTimer m_durationTimer;
|
|
int m_elapsedMs = 0;
|
|
|
|
struct audio_compressor* m_compressor = nullptr;
|
|
bool m_compressorEnabled = false;
|
|
bool m_captureCompressorEnabled = false;
|
|
audio_compressor_config_t m_compressorConfig{};
|
|
|
|
};
|