Browse Source

Check SoundTouch source time against identifiable audio samples

master
evgeny 4 days ago
parent
commit
05d9e3f22e
  1. 3
      src/call/call_video_rate.c
  2. 86
      tools/chatgui/tests/test_soundtouch_clock.cpp

3
src/call/call_video_rate.c

@ -44,7 +44,8 @@ int call_video_rate_step(struct call_video_rate *rate, const struct call_video_r
rate->next_change_us = s->now_us + interval;
}
/* Запас RTT относится к inflight, два кадра — к пакетному выходу encoder. */
uint64_t normal = result.delivery_bps * ((uint64_t)s->baseline_rtt_us + 50000) / 8000000;
uint64_t bounded_delivery = result.delivery_bps > MAX_BITRATE ? MAX_BITRATE : result.delivery_bps;
uint64_t normal = bounded_delivery * ((uint64_t)s->baseline_rtt_us + 50000) / 8000000;
normal += (uint64_t)s->applied_bps * s->frame_interval_us * 2 / 8000000;
result.excess_bytes = s->debt_bytes > normal ? s->debt_bytes - normal : 0;
if (!result.excess_bytes || !s->valid || s->locally_held || s->probing) rate->high = 0;

86
tools/chatgui/tests/test_soundtouch_clock.cpp

@ -0,0 +1,86 @@
/* Проверяем позицию по самому сигналу: каждый четырёхсэмпловый фрагмент уникален.
* Сравниваем номинальный feed/tempo с фактическим потреблением входа SoundTouch. */
#include "soundtouch/SoundTouch.h"
#include "../../../lib/debug_config.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <unordered_map>
#include <vector>
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "clock test line=%d: %s", __LINE__, #x); exit(1); } } while (0)
struct Span { double first, last, sourceFirst, sourceLast; };
static double position(const std::vector<Span>& spans, size_t& index, double output) {
while (index + 1 < spans.size() && spans[index].last <= output) index++;
const auto& s = spans[index];
return s.sourceFirst + (s.sourceLast - s.sourceFirst) * (output - s.first) / (s.last - s.first);
}
static void check_clock(unsigned mode) {
constexpr unsigned frames = 3000, block = 960;
std::vector<float> source(frames * block), output;
std::unordered_multimap<uint32_t, unsigned> locations;
uint32_t seed = 123456789;
for (unsigned i = 0; i < source.size(); i++) {
seed ^= seed << 13; seed ^= seed >> 17; seed ^= seed << 5;
source[i] = (int32_t)(seed & 0xffffff) / 16777216.0f - 0.5f;
uint32_t bits; std::memcpy(&bits, &source[i], sizeof(bits)); locations.emplace(bits, i);
}
soundtouch::SoundTouch st;
st.setSampleRate(48000); st.setChannels(1);
st.setSetting(SETTING_USE_QUICKSEEK, 1); st.setSetting(SETTING_SEQUENCE_MS, 20);
st.setSetting(SETTING_SEEKWINDOW_MS, 10); st.setSetting(SETTING_OVERLAP_MS, 4);
st.setSetting(SETTING_USE_AA_FILTER, 0);
std::vector<Span> nominal, consumed;
double expected = 0, mappedEnd = 0;
unsigned produced = 0;
for (unsigned i = 0; i < frames; i++) {
double triangle = std::abs((int)(i % 100) - 50) / 50.0;
double tempo = mode == 0 ? 1.6 : mode == 1 ? 1.0 + 0.6 * triangle : (i % 2 ? 1.6 : 1.0);
st.setTempo(tempo);
nominal.push_back({expected, expected + block / tempo, (double)i * block, (double)(i + 1) * block});
expected += block / tempo;
unsigned before = st.numSamples();
st.putSamples(source.data() + i * block, block);
unsigned added = st.numSamples() - before;
if (added) {
// Середина seek-window компенсирует стартовый сдвиг WSOLA; звук не изменяем.
double end = (i + 1) * block - st.numUnprocessedSamples() + 240.0;
consumed.push_back({(double)produced, (double)produced + added, mappedEnd, end});
mappedEnd = end; produced += added;
}
float pcm[480];
for (;;) {
unsigned count = st.receiveSamples(pcm, 480);
if (!count) break;
output.insert(output.end(), pcm, pcm + count);
}
}
size_t nominalIndex = 0, consumedIndex = 0;
unsigned matches = 0;
double maxNominal = 0, maxConsumed = 0;
for (unsigned i = 0; i + 4 < output.size(); i += 97) {
uint32_t bits; std::memcpy(&bits, &output[i], sizeof(bits));
auto found = locations.equal_range(bits);
for (auto it = found.first; it != found.second; ++it) {
unsigned at = it->second;
if (at + 4 > source.size() || std::memcmp(&source[at], &output[i], 4 * sizeof(float))) continue;
maxNominal = std::max(maxNominal, std::abs(position(nominal, nominalIndex, i) - at) / 48.0);
maxConsumed = std::max(maxConsumed, std::abs(position(consumed, consumedIndex, i) - at) / 48.0);
matches++; break;
}
}
DEBUG_INFO(DEBUG_CATEGORY_CALL, "SoundTouch clock mode=%u matches=%u nominal_error_ms=%.3f consumed_error_ms=%.3f",
mode, matches, maxNominal, maxConsumed);
CHECK(matches > 10000);
CHECK(maxConsumed < 20.0);
}
int main() {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
check_clock(0); check_clock(1); check_clock(2);
return 0;
}
Loading…
Cancel
Save