diff --git a/lib/audio_compressor.c b/lib/audio_compressor.c index a2d5c50b..68dd2b5c 100644 --- a/lib/audio_compressor.c +++ b/lib/audio_compressor.c @@ -43,7 +43,7 @@ struct audio_compressor { int dbg_counter; }; -/* Начальный gain — середина диапазона [0, max_gain] в дБ (геометрическое среднее). */ +/* Пустой компрессор с теми же настройками для следующей записи. */ struct audio_compressor* audio_compressor_clone_config(const struct audio_compressor* source) { if (!source) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "compressor: cannot clone NULL configuration"); return NULL; } struct audio_compressor* copy = audio_compressor_create(); diff --git a/tools/chatgui/src/voicemessageencoder.cpp b/tools/chatgui/src/voicemessageencoder.cpp index 68339dcb..6885b3ea 100644 --- a/tools/chatgui/src/voicemessageencoder.cpp +++ b/tools/chatgui/src/voicemessageencoder.cpp @@ -1,114 +1,13 @@ #include "voicemessageencoder.h" -extern "C" { -#include "../../../lib/opus_codec.h" -} - #include "../../lib/debug_config.h" - -#include #include -#include -#include -#include - -static const int FRAME_MS = 20; -static const uint32_t OPUS_MAGIC = 0x5355504F; -static const int MAX_PACKET = 4000; static int s_preset = 1; /* 0=Low, 1=Standard, 2=High */ -static const int s_bitrates[] = {16000, 32000, 64000}; -static const int s_complexities[] = {2, 5, 10}; - -void VoiceEncoder::setPreset(int preset) { - if (preset < 0) preset = 0; - if (preset > 2) preset = 2; - s_preset = preset; -} +void VoiceEncoder::setPreset(int preset) { s_preset = qBound(0, preset, 2); } int VoiceEncoder::preset() { return s_preset; } -int VoiceEncoder::encodeToFile(const int16_t* pcm, size_t pcmCount, int sampleRate, int channels, - const QString& filePath, float& outDurationSec) { - if (!pcm || pcmCount == 0 || sampleRate <= 0 || channels <= 0) { - DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoiceEncoder: invalid input (pcm=%zu rate=%d ch=%d)", pcmCount, sampleRate, channels); - return -1; - } - - int frameSamples = sampleRate * FRAME_MS / 1000; - size_t totalSamples = pcmCount / (size_t)channels; - if (totalSamples < (size_t)frameSamples) return -1; - - opus_codec_encoder_t* enc = opus_codec_encoder_create(sampleRate, channels); - if (!enc) { - DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoiceEncoder: encoder create failed"); - return -1; - } - opus_codec_encoder_bitrate_set(enc, s_bitrates[s_preset]); - opus_codec_encoder_complexity_set(enc, s_complexities[s_preset]); - - QFile file(filePath); - if (!file.open(QIODevice::WriteOnly)) { - DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoiceEncoder: cannot open file %s (%s)", qPrintable(filePath), qPrintable(file.errorString())); - opus_codec_encoder_destroy(enc); - return -1; - } - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoiceEncoder::encodeToFile: writing to %s pcm=%zu samples preset=%d", - qPrintable(filePath), pcmCount, s_preset); - - /* write header */ - uint32_t magic = OPUS_MAGIC; - uint32_t sr = (uint32_t)sampleRate; - uint16_t ch = (uint16_t)channels; - uint16_t fs = (uint16_t)frameSamples; - file.write((const char*)&magic, 4); - file.write((const char*)&sr, 4); - file.write((const char*)&ch, 2); - file.write((const char*)&fs, 2); - - int frameCount = 0; - uint8_t packet[MAX_PACKET]; - size_t pcmOff = 0; - - while (pcmOff + (size_t)frameSamples * channels <= totalSamples) { - int len = opus_codec_encode(enc, pcm + pcmOff, frameSamples, packet, MAX_PACKET); - pcmOff += (size_t)frameSamples * channels; - - if (len > 0) { - uint16_t plen = (uint16_t)len; - file.write((const char*)&plen, 2); - file.write((const char*)packet, len); - frameCount++; - } else if (len < 0) { - DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoiceEncoder: encode error at frame %d: %d", frameCount, len); - } - } - - /* end marker */ - uint16_t zero = 0; - file.write((const char*)&zero, 2); - file.close(); - - outDurationSec = (float)frameCount * FRAME_MS / 1000.0f; - - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoiceEncoder: wrote %d frames (%.1fs) to %s", frameCount, outDurationSec, qPrintable(filePath)); - opus_codec_encoder_destroy(enc); - return frameCount; -} - -QString VoiceEncoder::generateFileName() { - QDateTime now = QDateTime::currentDateTime(); - QString ts = now.toString("yyyyMMdd-HHmmss"); - int rnd = rand() & 0xFFFF; - return QString("voice_%1_%2.opus").arg(ts).arg(rnd, 4, 16, QChar('0')); -} - -QString VoiceEncoder::mediaDir(const QString& dbPath, const QString& channelId) { - QDir d(dbPath); - QString mediaPath = d.absoluteFilePath("media/" + channelId); - return mediaPath; -} - void VoiceEncoder::ensureMediaDir(const QString& dir) { QDir d; if (!d.mkpath(dir)) { diff --git a/tools/chatgui/src/voicemessageencoder.h b/tools/chatgui/src/voicemessageencoder.h index 006d4d49..2290f28e 100644 --- a/tools/chatgui/src/voicemessageencoder.h +++ b/tools/chatgui/src/voicemessageencoder.h @@ -1,16 +1,11 @@ #pragma once #include -#include class VoiceEncoder { public: static void setPreset(int preset); static int preset(); - static int encodeToFile(const int16_t* pcm, size_t pcmCount, int sampleRate, int channels, - const QString& filePath, float& outDurationSec); - static QString generateFileName(); - static QString mediaDir(const QString& dbPath, const QString& channelId); static void ensureMediaDir(const QString& dir); };