#include "audiorecorder.h" #include "miniaudio.h" #include "sound_manager.h" extern "C" { #include "../../lib/audio_compressor.h" } #include "../../lib/debug_config.h" #include #include #include static AudioRecorder* g_recorder = nullptr; void audioCaptureCallback(ma_device* pDevice, void* pOutput, const void* pInput, unsigned int frameCount) { (void)pOutput; AudioRecorder* self = g_recorder; if (!self || !self->m_recording) return; if (!pInput) return; const int16_t* src = (const int16_t*)pInput; size_t add = (size_t)frameCount * (size_t)self->m_channels; if (self->m_compressor && self->m_compressorEnabled) { audio_compressor_push(self->m_compressor, src, add); } else { size_t cur = self->m_pcmBuffer.size(); self->m_pcmBuffer.resize(cur + add); std::memcpy(self->m_pcmBuffer.data() + cur, src, add * sizeof(int16_t)); } float sumSq = 0.0f; for (unsigned int i = 0; i < frameCount * (unsigned int)self->m_channels; i++) { float v = (float)src[i] / 32768.0f; sumSq += v * v; } float rms = sqrtf(sumSq / (float)(frameCount * self->m_channels)); self->m_peakLevel = rms; self->m_recentRms[self->m_rmsIdx % 5] = rms; self->m_rmsIdx++; if (self->m_rmsIdx >= 5) { float avg = 0.0f; for (int i = 0; i < 5; i++) avg += self->m_recentRms[i]; avg /= 5.0f; self->m_waveformLevels.push_back(avg); } } AudioRecorder::AudioRecorder(QObject* parent) : QObject(parent) { g_recorder = this; m_compressor = audio_compressor_create(); if (m_compressor) { audio_compressor_config_t cfg = {0}; cfg.sample_rate = m_sampleRate; cfg.channels = m_channels; audio_compressor_configure(m_compressor, &cfg); } m_durationTimer.setInterval(100); connect(&m_durationTimer, &QTimer::timeout, this, [this]() { if (m_recording) { m_elapsedMs += 100; emit durationChanged(m_elapsedMs); } }); } AudioRecorder::~AudioRecorder() { shutdown(); audio_compressor_destroy(m_compressor); m_compressor = nullptr; if (g_recorder == this) g_recorder = nullptr; } bool AudioRecorder::init() { if (m_initialized) return true; m_device = new ma_device; std::memset(m_device, 0, sizeof(*m_device)); m_initialized = true; DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "initialized (sampleRate=%d, channels=%d)", m_sampleRate, m_channels); return true; } void AudioRecorder::shutdown() { if (!m_initialized) return; if (m_recording) stopRecording(); if (m_deviceStarted) { m_deviceStarted = false; if (m_device) { ma_device_stop(m_device); ma_device_uninit(m_device); } } if (m_device) { delete m_device; m_device = nullptr; } m_initialized = false; DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "shutdown"); } void AudioRecorder::startRecording() { if (m_recording) return; if (!m_initialized) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "not initialized"); return; } m_pcmBuffer.clear(); m_waveformLevels.clear(); m_rmsIdx = 0; m_elapsedMs = 0; if (m_compressor) audio_compressor_reset(m_compressor); ma_context* ctx = SoundManager::instance()->context(); if (!ctx) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "no audio context"); return; } ma_device_config config = ma_device_config_init(ma_device_type_capture); config.capture.format = ma_format_s16; config.capture.channels = (ma_uint32)m_channels; config.sampleRate = (ma_uint32)m_sampleRate; config.periodSizeInFrames = 480; config.dataCallback = audioCaptureCallback; config.pUserData = this; if (m_captureDeviceIndex >= 0) { ma_device_info* pCaptureDevices = nullptr; ma_uint32 captureCount = 0; ma_result r = ma_context_get_devices(ctx, nullptr, nullptr, &pCaptureDevices, &captureCount); if (r == MA_SUCCESS && (ma_uint32)m_captureDeviceIndex < captureCount) { config.capture.pDeviceID = &pCaptureDevices[m_captureDeviceIndex].id; DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "using capture device[%d]: %s", m_captureDeviceIndex, pCaptureDevices[m_captureDeviceIndex].name); } } ma_result r = ma_device_init(ctx, &config, m_device); if (r != MA_SUCCESS) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "device init failed (%d), trying default", r); config.capture.pDeviceID = nullptr; r = ma_device_init(ctx, &config, m_device); if (r != MA_SUCCESS) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "default device init also failed (%d)", r); return; } } r = ma_device_start(m_device); if (r != MA_SUCCESS) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "device start failed (%d)", r); ma_device_uninit(m_device); return; } m_recording = true; m_deviceStarted = true; m_durationTimer.start(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "recording started"); emit recordingStarted(); } void AudioRecorder::stopRecording() { if (!m_recording) return; m_durationTimer.stop(); if (m_deviceStarted) { ma_device_stop(m_device); ma_device_uninit(m_device); m_deviceStarted = false; } m_recording = false; if (m_compressor && m_compressorEnabled) audio_compressor_flush(m_compressor); int duration = m_elapsedMs; size_t bufSize = (m_compressor && m_compressorEnabled) ? audio_compressor_output_size(m_compressor) : m_pcmBuffer.size(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "recording stopped, duration=%dms, pcmSamples=%zu, compressor=%d", duration, bufSize, m_compressorEnabled ? 1 : 0); emit recordingStopped(duration); emit durationChanged(0); } int AudioRecorder::durationMs() const { return m_elapsedMs; } float AudioRecorder::peakLevel() const { float v = m_peakLevel; m_peakLevel = 0.0f; return v; } const int16_t* AudioRecorder::bufferData() const { if (m_compressor && m_compressorEnabled) return audio_compressor_output(m_compressor); return m_pcmBuffer.data(); } size_t AudioRecorder::bufferSize() const { if (m_compressor && m_compressorEnabled) return audio_compressor_output_size(m_compressor); return m_pcmBuffer.size(); } void AudioRecorder::setCompressorEnabled(bool enabled) { m_compressorEnabled = enabled; if (m_compressor) audio_compressor_set_enabled(m_compressor, enabled); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "compressor %s", enabled ? "enabled" : "disabled"); }