feat: voice compressor with RMS+peak leveling, sliding window envelope, rate-limited gain

This commit is contained in:
Evgeny committed 2026-07-27 11:24:47 +03:00
1 parent 7004b110ae
commit f1b599f0c0
8 files changed
+406 -9

No files matched your search

+35 -5
View File
@@ -1,4 +1,5 @@
#include "audiorecorder.h"
#include "audiocompressor.h"
#include "miniaudio.h"
#include "sound_manager.h"
@@ -16,10 +17,15 @@ void audioCaptureCallback(ma_device* pDevice, void* pOutput, const void* pInput,
if (!pInput) return;
const int16_t* src = (const int16_t*)pInput;
size_t cur = self->m_pcmBuffer.size();
size_t add = (size_t)frameCount * (size_t)self->m_channels;
self->m_pcmBuffer.resize(cur + add);
std::memcpy(self->m_pcmBuffer.data() + cur, src, add * sizeof(int16_t));
if (self->m_compressor && self->m_compressorEnabled) {
self->m_compressor->push(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++) {
@@ -42,6 +48,11 @@ void audioCaptureCallback(ma_device* pDevice, void* pOutput, const void* pInput,
AudioRecorder::AudioRecorder(QObject* parent)
: QObject(parent) {
g_recorder = this;
m_compressor = new AudioCompressor;
AudioCompressor::Config cfg;
cfg.sampleRate = m_sampleRate;
cfg.channels = m_channels;
m_compressor->configure(cfg);
m_durationTimer.setInterval(100);
connect(&m_durationTimer, &QTimer::timeout, this, [this]() {
if (m_recording) {
@@ -53,6 +64,8 @@ AudioRecorder::AudioRecorder(QObject* parent)
AudioRecorder::~AudioRecorder() {
shutdown();
delete m_compressor;
m_compressor = nullptr;
if (g_recorder == this) g_recorder = nullptr;
}
@@ -86,6 +99,8 @@ void AudioRecorder::startRecording() {
m_rmsIdx = 0;
m_elapsedMs = 0;
if (m_compressor) m_compressor->reset();
ma_context* ctx = SoundManager::instance()->context();
if (!ctx) { qWarning("AudioRecorder: no audio context"); return; }
@@ -142,10 +157,14 @@ void AudioRecorder::stopRecording() {
ma_device_uninit(m_device);
m_deviceStarted = false;
}
m_recording = false;
if (m_compressor && m_compressorEnabled) m_compressor->flush();
int duration = m_elapsedMs;
qDebug("AudioRecorder: recording stopped, duration=%dms, pcmSamples=%zu", duration, m_pcmBuffer.size());
size_t bufSize = (m_compressor && m_compressorEnabled) ? m_compressor->outputBuffer().size() : m_pcmBuffer.size();
qDebug("AudioRecorder: recording stopped, duration=%dms, pcmSamples=%zu, compressor=%d",
duration, bufSize, m_compressorEnabled ? 1 : 0);
emit recordingStopped(duration);
emit durationChanged(0);
}
@@ -159,3 +178,14 @@ float AudioRecorder::peakLevel() const {
m_peakLevel = 0.0f;
return v;
}
const std::vector<int16_t>& AudioRecorder::buffer() const {
if (m_compressor && m_compressorEnabled) return m_compressor->outputBuffer();
return m_pcmBuffer;
}
void AudioRecorder::setCompressorEnabled(bool enabled) {
m_compressorEnabled = enabled;
if (m_compressor) m_compressor->setEnabled(enabled);
qDebug("AudioRecorder: compressor %s", enabled ? "enabled" : "disabled");
}