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.
85 lines
2.7 KiB
85 lines
2.7 KiB
/* Запись голосового сообщения: 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{}; |
|
|
|
};
|
|
|