Browse Source

unify compressor: remove C++ duplicate audiocompressor, use lib/audio_compressor.c in chatgui

topo_upd
evgeny 2 months ago
parent
commit
1dac0b8c81
  1. 4
      lib/audio_compressor.c
  2. 1
      tools/chatgui/CMakeLists.txt
  3. 146
      tools/chatgui/src/audiocompressor.cpp
  4. 59
      tools/chatgui/src/audiocompressor.h
  5. 21
      tools/chatgui/src/audiodevicesettingspage.cpp
  6. 1
      tools/chatgui/src/audiodevicesettingspage.h
  7. 40
      tools/chatgui/src/audiorecorder.cpp
  8. 9
      tools/chatgui/src/audiorecorder.h
  9. 7
      tools/chatgui/src/inputbar.cpp
  10. 19
      tools/chatgui/src/mainwindow.cpp
  11. 13
      tools/chatgui/src/voicemessageencoder.cpp
  12. 3
      tools/chatgui/src/voicemessageencoder.h

4
lib/audio_compressor.c

@ -1,6 +1,6 @@
#include "audio_compressor.h" #include "audio_compressor.h"
#include "../../../lib/debug_config.h" #include "debug_config.h"
#include "../../../lib/mem.h" #include "mem.h"
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>

1
tools/chatgui/CMakeLists.txt

@ -115,7 +115,6 @@ add_executable(chatgui
src/soundsettingspage.cpp src/soundsettingspage.cpp
src/sound_manager.cpp src/sound_manager.cpp
src/audiorecorder.cpp src/audiorecorder.cpp
src/audiocompressor.cpp
src/voicemessageencoder.cpp src/voicemessageencoder.cpp
src/voiceplayback.cpp src/voiceplayback.cpp
src/media_blocks.cpp src/media_blocks.cpp

146
tools/chatgui/src/audiocompressor.cpp

@ -1,146 +0,0 @@
#include "audiocompressor.h"
#include "../../lib/debug_config.h"
#include <cmath>
#include <algorithm>
#include <cstring>
void AudioCompressor::configure(const Config& cfg) {
m_cfg = cfg;
m_blockSamples = m_cfg.sampleRate * m_cfg.blockDurationMs / 1000 * m_cfg.channels;
m_lookbackBlocks = m_cfg.lookbackMs / m_cfg.blockDurationMs;
m_lookaheadBlocks = m_cfg.lookaheadMs / m_cfg.blockDurationMs;
m_riseFactorPerBlock = powf(m_cfg.riseRatePer500ms, (float)m_cfg.blockDurationMs / 500.0f);
m_maxGain = powf(10.0f, m_cfg.maxGainDb / 20.0f);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "config rate=%d ch=%d block=%dms window=%dms+%dms maxGain=%.0fdB riseRate=%.1fx/500ms target=%.0fdBFS",
m_cfg.sampleRate, m_cfg.channels, m_cfg.blockDurationMs,
m_cfg.lookbackMs, m_cfg.lookaheadMs, m_cfg.maxGainDb,
m_cfg.riseRatePer500ms, 20.0f * log10f(m_cfg.targetLevel));
}
void AudioCompressor::reset() {
m_blockLevels.clear();
m_pending.clear();
m_accumulator.clear();
m_output.clear();
m_gainSmoothed = 1.0f;
m_dbgCounter = 0;
}
void AudioCompressor::push(const int16_t* samples, size_t count) {
if (!m_enabled) {
m_output.insert(m_output.end(), samples, samples + count);
return;
}
size_t remaining = count;
const int16_t* src = samples;
while (remaining > 0) {
size_t need = (size_t)m_blockSamples - m_accumulator.size();
size_t take = std::min(remaining, need);
m_accumulator.insert(m_accumulator.end(), src, src + take);
src += take;
remaining -= take;
if (m_accumulator.size() == (size_t)m_blockSamples) {
float level = computeBlockLevel(m_accumulator.data(), m_accumulator.size());
BlockLevel bl;
bl.level = level;
m_blockLevels.push_back(bl);
PendingBlock pb;
pb.samples = std::move(m_accumulator);
pb.levelIndex = m_blockLevels.size() - 1;
m_pending.push_back(std::move(pb));
m_accumulator.clear();
}
}
while (m_pending.size() > (size_t)m_lookaheadBlocks) {
processPendingBlock();
}
}
float AudioCompressor::computeBlockLevel(const int16_t* samples, size_t count) const {
float sumSq = 0.0f;
float peak = 0.0f;
for (size_t i = 0; i < count; i++) {
float v = (float)samples[i] / 32768.0f;
sumSq += v * v;
float av = fabsf(v);
if (av > peak) peak = av;
}
float rms = sqrtf(sumSq / (float)count);
return (rms * 2.0f + peak) / 2.0f;
}
void AudioCompressor::processPendingBlock() {
PendingBlock& block = m_pending.front();
size_t idx = block.levelIndex;
size_t winStart = idx >= (size_t)m_lookbackBlocks ? idx - (size_t)m_lookbackBlocks : 0;
size_t winEnd = std::min(idx + (size_t)m_lookaheadBlocks, m_blockLevels.size() - 1);
float envelope = 0.0f;
for (size_t i = winStart; i <= winEnd; i++) {
if (m_blockLevels[i].level > envelope) envelope = m_blockLevels[i].level;
}
if (envelope < 0.0001f) envelope = 0.0001f;
float G_raw = m_cfg.targetLevel / envelope;
float G_prev = m_gainSmoothed;
if (G_raw > m_gainSmoothed) {
float maxRise = m_gainSmoothed * m_riseFactorPerBlock;
m_gainSmoothed = std::min(G_raw, maxRise);
} else {
m_gainSmoothed = G_raw;
}
m_gainSmoothed = std::min(m_gainSmoothed, m_maxGain);
float gainDb = 20.0f * log10f(m_gainSmoothed);
float prevDb = 20.0f * log10f(G_prev > 0.0001f ? G_prev : 0.0001f);
if (m_dbgCounter % 25 == 0 || fabsf(gainDb - prevDb) > 3.0f) {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "block %zu level=%.4f envelope=%.4f gainRaw=%.1fdB gain=%.1fdB",
idx, m_blockLevels[idx].level, envelope, 20.0f * log10f(G_raw), gainDb);
}
m_dbgCounter++;
for (size_t i = 0; i < block.samples.size(); i++) {
float v = (float)block.samples[i] * m_gainSmoothed;
if (v > 32767.0f) v = 32767.0f;
if (v < -32768.0f) v = -32768.0f;
m_output.push_back((int16_t)(int)v);
}
m_pending.pop_front();
}
void AudioCompressor::flush() {
if (!m_enabled) return;
if (!m_accumulator.empty()) {
float level = computeBlockLevel(m_accumulator.data(), m_accumulator.size());
BlockLevel bl;
bl.level = level;
m_blockLevels.push_back(bl);
PendingBlock pb;
pb.samples = std::move(m_accumulator);
pb.levelIndex = m_blockLevels.size() - 1;
m_pending.push_back(std::move(pb));
m_accumulator.clear();
}
int remain = (int)m_pending.size();
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "flush %d pending blocks (gainSmoothed=%.1fdB)", remain,
20.0f * log10f(m_gainSmoothed));
while (!m_pending.empty()) {
processPendingBlock();
}
}

59
tools/chatgui/src/audiocompressor.h

@ -1,59 +0,0 @@
#pragma once
#include <vector>
#include <deque>
#include <cstdint>
class AudioCompressor {
public:
struct Config {
int sampleRate = 48000;
int channels = 1;
int blockDurationMs = 20;
int lookbackMs = 200;
int lookaheadMs = 100;
float maxGainDb = 30.0f;
float riseRatePer500ms = 2.0f;
float targetLevel = 0.25f;
};
void configure(const Config& cfg);
void reset();
void setEnabled(bool enabled) { m_enabled = enabled; }
bool isEnabled() const { return m_enabled; }
void push(const int16_t* samples, size_t count);
const std::vector<int16_t>& outputBuffer() const { return m_output; }
bool hasPending() const { return !m_pending.empty() || !m_accumulator.empty(); }
void flush();
private:
Config m_cfg;
bool m_enabled = true;
struct BlockLevel {
float level;
};
std::deque<BlockLevel> m_blockLevels;
struct PendingBlock {
std::vector<int16_t> samples;
size_t levelIndex;
};
std::deque<PendingBlock> m_pending;
std::vector<int16_t> m_accumulator;
std::vector<int16_t> m_output;
int m_blockSamples = 960;
int m_lookbackBlocks = 10;
int m_lookaheadBlocks = 5;
float m_riseFactorPerBlock = 1.0f;
float m_maxGain = 31.62f;
float m_gainSmoothed = 1.0f;
int m_dbgCounter = 0;
float computeBlockLevel(const int16_t* samples, size_t count) const;
void processPendingBlock();
};

21
tools/chatgui/src/audiodevicesettingspage.cpp

@ -1,8 +1,11 @@
#include "audiodevicesettingspage.h" #include "audiodevicesettingspage.h"
#include "sound_manager.h" #include "sound_manager.h"
#include "audiorecorder.h" #include "audiorecorder.h"
#include "audiocompressor.h"
#include "voicemessageencoder.h" #include "voicemessageencoder.h"
extern "C" {
#include "../../lib/audio_compressor.h"
}
#include "../../lib/debug_config.h" #include "../../lib/debug_config.h"
#include "../db/db_manager.h" #include "../db/db_manager.h"
#include "../transport/gui_bridge.h" #include "../transport/gui_bridge.h"
@ -236,15 +239,15 @@ void AudioDeviceSettingsPage::applyAndSave() {
if (m_recorder) { if (m_recorder) {
m_recorder->setCompressorEnabled(compEnabled != 0); m_recorder->setCompressorEnabled(compEnabled != 0);
if (m_recorder->compressor() && compEnabled) { if (m_recorder->compressor() && compEnabled) {
AudioCompressor::Config compCfg; audio_compressor_config_t compCfg = {0};
compCfg.sampleRate = 48000; compCfg.sample_rate = 48000;
compCfg.channels = 1; compCfg.channels = 1;
compCfg.maxGainDb = (float)compMaxGain; compCfg.max_gain_db = (float)compMaxGain;
compCfg.lookbackMs = compLookback; compCfg.lookback_ms = compLookback;
compCfg.lookaheadMs = compLookahead; compCfg.lookahead_ms = compLookahead;
compCfg.riseRatePer500ms = (float)compRiseRateTenths / 10.0f; compCfg.rise_rate_per_500ms = (float)compRiseRateTenths / 10.0f;
compCfg.targetLevel = powf(10.0f, (float)compTargetLevel / 20.0f); compCfg.target_level = powf(10.0f, (float)compTargetLevel / 20.0f);
m_recorder->compressor()->configure(compCfg); audio_compressor_configure(m_recorder->compressor(), &compCfg);
} }
} }
} }

1
tools/chatgui/src/audiodevicesettingspage.h

@ -9,7 +9,6 @@
#include <QTimer> #include <QTimer>
class DbManager; class DbManager;
class AudioRecorder; class AudioRecorder;
class AudioCompressor;
class AudioDeviceSettingsPage : public QWidget { class AudioDeviceSettingsPage : public QWidget {
Q_OBJECT Q_OBJECT

40
tools/chatgui/src/audiorecorder.cpp

@ -1,8 +1,11 @@
#include "audiorecorder.h" #include "audiorecorder.h"
#include "audiocompressor.h"
#include "miniaudio.h" #include "miniaudio.h"
#include "sound_manager.h" #include "sound_manager.h"
extern "C" {
#include "../../lib/audio_compressor.h"
}
#include "../../lib/debug_config.h" #include "../../lib/debug_config.h"
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
@ -19,7 +22,7 @@ void audioCaptureCallback(ma_device* pDevice, void* pOutput, const void* pInput,
size_t add = (size_t)frameCount * (size_t)self->m_channels; size_t add = (size_t)frameCount * (size_t)self->m_channels;
if (self->m_compressor && self->m_compressorEnabled) { if (self->m_compressor && self->m_compressorEnabled) {
self->m_compressor->push(src, add); audio_compressor_push(self->m_compressor, src, add);
} else { } else {
size_t cur = self->m_pcmBuffer.size(); size_t cur = self->m_pcmBuffer.size();
self->m_pcmBuffer.resize(cur + add); self->m_pcmBuffer.resize(cur + add);
@ -47,11 +50,13 @@ void audioCaptureCallback(ma_device* pDevice, void* pOutput, const void* pInput,
AudioRecorder::AudioRecorder(QObject* parent) AudioRecorder::AudioRecorder(QObject* parent)
: QObject(parent) { : QObject(parent) {
g_recorder = this; g_recorder = this;
m_compressor = new AudioCompressor; m_compressor = audio_compressor_create();
AudioCompressor::Config cfg; if (m_compressor) {
cfg.sampleRate = m_sampleRate; audio_compressor_config_t cfg = {0};
cfg.channels = m_channels; cfg.sample_rate = m_sampleRate;
m_compressor->configure(cfg); cfg.channels = m_channels;
audio_compressor_configure(m_compressor, &cfg);
}
m_durationTimer.setInterval(100); m_durationTimer.setInterval(100);
connect(&m_durationTimer, &QTimer::timeout, this, [this]() { connect(&m_durationTimer, &QTimer::timeout, this, [this]() {
if (m_recording) { if (m_recording) {
@ -63,7 +68,7 @@ AudioRecorder::AudioRecorder(QObject* parent)
AudioRecorder::~AudioRecorder() { AudioRecorder::~AudioRecorder() {
shutdown(); shutdown();
delete m_compressor; audio_compressor_destroy(m_compressor);
m_compressor = nullptr; m_compressor = nullptr;
if (g_recorder == this) g_recorder = nullptr; if (g_recorder == this) g_recorder = nullptr;
} }
@ -98,7 +103,7 @@ void AudioRecorder::startRecording() {
m_rmsIdx = 0; m_rmsIdx = 0;
m_elapsedMs = 0; m_elapsedMs = 0;
if (m_compressor) m_compressor->reset(); if (m_compressor) audio_compressor_reset(m_compressor);
ma_context* ctx = SoundManager::instance()->context(); ma_context* ctx = SoundManager::instance()->context();
if (!ctx) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "no audio context"); return; } if (!ctx) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "no audio context"); return; }
@ -158,10 +163,10 @@ void AudioRecorder::stopRecording() {
} }
m_recording = false; m_recording = false;
if (m_compressor && m_compressorEnabled) m_compressor->flush(); if (m_compressor && m_compressorEnabled) audio_compressor_flush(m_compressor);
int duration = m_elapsedMs; int duration = m_elapsedMs;
size_t bufSize = (m_compressor && m_compressorEnabled) ? m_compressor->outputBuffer().size() : m_pcmBuffer.size(); 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", DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "recording stopped, duration=%dms, pcmSamples=%zu, compressor=%d",
duration, bufSize, m_compressorEnabled ? 1 : 0); duration, bufSize, m_compressorEnabled ? 1 : 0);
emit recordingStopped(duration); emit recordingStopped(duration);
@ -178,13 +183,18 @@ float AudioRecorder::peakLevel() const {
return v; return v;
} }
const std::vector<int16_t>& AudioRecorder::buffer() const { const int16_t* AudioRecorder::bufferData() const {
if (m_compressor && m_compressorEnabled) return m_compressor->outputBuffer(); if (m_compressor && m_compressorEnabled) return audio_compressor_output(m_compressor);
return m_pcmBuffer; 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) { void AudioRecorder::setCompressorEnabled(bool enabled) {
m_compressorEnabled = enabled; m_compressorEnabled = enabled;
if (m_compressor) m_compressor->setEnabled(enabled); if (m_compressor) audio_compressor_set_enabled(m_compressor, enabled);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "compressor %s", enabled ? "enabled" : "disabled"); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "compressor %s", enabled ? "enabled" : "disabled");
} }

9
tools/chatgui/src/audiorecorder.h

@ -7,7 +7,7 @@
#include <cstdint> #include <cstdint>
struct ma_device; struct ma_device;
class AudioCompressor; struct audio_compressor;
class AudioRecorder : public QObject { class AudioRecorder : public QObject {
Q_OBJECT Q_OBJECT
@ -28,11 +28,12 @@ public:
float peakLevel() const; float peakLevel() const;
const std::vector<int16_t>& buffer() const; const int16_t* bufferData() const;
size_t bufferSize() const;
const std::vector<float>& waveformLevels() const { return m_waveformLevels; } const std::vector<float>& waveformLevels() const { return m_waveformLevels; }
int durationMs() const; int durationMs() const;
AudioCompressor* compressor() const { return m_compressor; } struct audio_compressor* compressor() const { return m_compressor; }
void setCompressorEnabled(bool enabled); void setCompressorEnabled(bool enabled);
bool isCompressorEnabled() const { return m_compressorEnabled; } bool isCompressorEnabled() const { return m_compressorEnabled; }
@ -58,7 +59,7 @@ private:
QTimer m_durationTimer; QTimer m_durationTimer;
int m_elapsedMs = 0; int m_elapsedMs = 0;
AudioCompressor* m_compressor = nullptr; struct audio_compressor* m_compressor = nullptr;
bool m_compressorEnabled = false; bool m_compressorEnabled = false;
friend void audioCaptureCallback(ma_device* pDevice, void* pOutput, const void* pInput, unsigned int frameCount); friend void audioCaptureCallback(ma_device* pDevice, void* pOutput, const void* pInput, unsigned int frameCount);

7
tools/chatgui/src/inputbar.cpp

@ -220,8 +220,9 @@ void InputBar::onPttReleased() {
return; return;
} }
const std::vector<int16_t>& pcm = m_recorder->buffer(); const int16_t* pcm = m_recorder->bufferData();
if (pcm.empty()) return; size_t pcmSize = m_recorder->bufferSize();
if (pcmSize == 0) return;
QString mediaPath = m_mediaDirBase + "/" + m_channelIdForRecord; QString mediaPath = m_mediaDirBase + "/" + m_channelIdForRecord;
VoiceEncoder::ensureMediaDir(mediaPath); VoiceEncoder::ensureMediaDir(mediaPath);
@ -231,7 +232,7 @@ void InputBar::onPttReleased() {
QString fileName = VoiceEncoder::generateFileName(); QString fileName = VoiceEncoder::generateFileName();
QString tempPath = mediaPath + "/" + fileName; QString tempPath = mediaPath + "/" + fileName;
float durationSec = 0; float durationSec = 0;
int frames = VoiceEncoder::encodeToFile(pcm, 48000, 1, tempPath, durationSec); int frames = VoiceEncoder::encodeToFile(pcm, pcmSize, 48000, 1, tempPath, durationSec);
if (frames <= 0) { if (frames <= 0) {
QFile::remove(tempPath); QFile::remove(tempPath);
return; return;

19
tools/chatgui/src/mainwindow.cpp

@ -9,8 +9,11 @@
#include "invite_link.h" #include "invite_link.h"
#include "sound_manager.h" #include "sound_manager.h"
#include "audiorecorder.h" #include "audiorecorder.h"
#include "audiocompressor.h"
#include "voicemessageencoder.h" #include "voicemessageencoder.h"
extern "C" {
#include "../../lib/audio_compressor.h"
}
#include "animtimer.h" #include "animtimer.h"
#include "../db/db_manager.h" #include "../db/db_manager.h"
#include "../transport/utun_node.h" #include "../transport/utun_node.h"
@ -213,13 +216,13 @@ void MainWindow::setupSoundFromConfig() {
int compEnabled = m_db->getUiStateInt("compressor_enabled", 0); int compEnabled = m_db->getUiStateInt("compressor_enabled", 0);
m_recorder->setCompressorEnabled(compEnabled != 0); m_recorder->setCompressorEnabled(compEnabled != 0);
if (m_recorder->compressor()) { if (m_recorder->compressor()) {
AudioCompressor::Config compCfg; audio_compressor_config_t compCfg = {0};
compCfg.maxGainDb = (float)m_db->getUiStateInt("compressor_max_gain_db", 30); compCfg.max_gain_db = (float)m_db->getUiStateInt("compressor_max_gain_db", 30);
compCfg.lookbackMs = m_db->getUiStateInt("compressor_lookback_ms", 200); compCfg.lookback_ms = m_db->getUiStateInt("compressor_lookback_ms", 200);
compCfg.lookaheadMs = m_db->getUiStateInt("compressor_lookahead_ms", 100); compCfg.lookahead_ms = m_db->getUiStateInt("compressor_lookahead_ms", 100);
compCfg.riseRatePer500ms = (float)m_db->getUiStateInt("compressor_rise_rate_tenths", 20) / 10.0f; compCfg.rise_rate_per_500ms = (float)m_db->getUiStateInt("compressor_rise_rate_tenths", 20) / 10.0f;
compCfg.targetLevel = powf(10.0f, (float)m_db->getUiStateInt("compressor_target_level_db", -12) / 20.0f); compCfg.target_level = powf(10.0f, (float)m_db->getUiStateInt("compressor_target_level_db", -12) / 20.0f);
m_recorder->compressor()->configure(compCfg); audio_compressor_configure(m_recorder->compressor(), &compCfg);
} }
} }
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "AudioRecorder initialized (captureDevice=%d, opusPreset=%d, compressor=%d)", DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "AudioRecorder initialized (captureDevice=%d, opusPreset=%d, compressor=%d)",

13
tools/chatgui/src/voicemessageencoder.cpp

@ -28,15 +28,15 @@ void VoiceEncoder::setPreset(int preset) {
int VoiceEncoder::preset() { return s_preset; } int VoiceEncoder::preset() { return s_preset; }
int VoiceEncoder::encodeToFile(const std::vector<int16_t>& pcm, int sampleRate, int channels, int VoiceEncoder::encodeToFile(const int16_t* pcm, size_t pcmCount, int sampleRate, int channels,
const QString& filePath, float& outDurationSec) { const QString& filePath, float& outDurationSec) {
if (pcm.empty() || sampleRate <= 0 || channels <= 0) { if (!pcm || pcmCount == 0 || sampleRate <= 0 || channels <= 0) {
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoiceEncoder: invalid input (pcm=%zu rate=%d ch=%d)", pcm.size(), sampleRate, channels); DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoiceEncoder: invalid input (pcm=%zu rate=%d ch=%d)", pcmCount, sampleRate, channels);
return -1; return -1;
} }
int frameSamples = sampleRate * FRAME_MS / 1000; int frameSamples = sampleRate * FRAME_MS / 1000;
size_t totalSamples = pcm.size() / channels; size_t totalSamples = pcmCount / (size_t)channels;
if (totalSamples < (size_t)frameSamples) return -1; if (totalSamples < (size_t)frameSamples) return -1;
opus_codec_encoder_t* enc = opus_codec_encoder_create(sampleRate, channels); opus_codec_encoder_t* enc = opus_codec_encoder_create(sampleRate, channels);
@ -54,7 +54,7 @@ int VoiceEncoder::encodeToFile(const std::vector<int16_t>& pcm, int sampleRate,
return -1; return -1;
} }
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoiceEncoder::encodeToFile: writing to %s pcm=%zu samples preset=%d", DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoiceEncoder::encodeToFile: writing to %s pcm=%zu samples preset=%d",
qPrintable(filePath), pcm.size(), s_preset); qPrintable(filePath), pcmCount, s_preset);
/* write header */ /* write header */
uint32_t magic = OPUS_MAGIC; uint32_t magic = OPUS_MAGIC;
@ -68,11 +68,10 @@ int VoiceEncoder::encodeToFile(const std::vector<int16_t>& pcm, int sampleRate,
int frameCount = 0; int frameCount = 0;
uint8_t packet[MAX_PACKET]; uint8_t packet[MAX_PACKET];
const int16_t* pcmData = pcm.data();
size_t pcmOff = 0; size_t pcmOff = 0;
while (pcmOff + (size_t)frameSamples * channels <= totalSamples) { while (pcmOff + (size_t)frameSamples * channels <= totalSamples) {
int len = opus_codec_encode(enc, pcmData + pcmOff, frameSamples, packet, MAX_PACKET); int len = opus_codec_encode(enc, pcm + pcmOff, frameSamples, packet, MAX_PACKET);
pcmOff += (size_t)frameSamples * channels; pcmOff += (size_t)frameSamples * channels;
if (len > 0) { if (len > 0) {

3
tools/chatgui/src/voicemessageencoder.h

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <vector>
#include <cstdint> #include <cstdint>
class VoiceEncoder { class VoiceEncoder {
@ -9,7 +8,7 @@ public:
static void setPreset(int preset); static void setPreset(int preset);
static int preset(); static int preset();
static int encodeToFile(const std::vector<int16_t>& pcm, int sampleRate, int channels, static int encodeToFile(const int16_t* pcm, size_t pcmCount, int sampleRate, int channels,
const QString& filePath, float& outDurationSec); const QString& filePath, float& outDurationSec);
static QString generateFileName(); static QString generateFileName();
static QString mediaDir(const QString& dbPath, const QString& channelId); static QString mediaDir(const QString& dbPath, const QString& channelId);

Loading…
Cancel
Save