You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
6.6 KiB
117 lines
6.6 KiB
/* Реальный stereo Opus/SoundTouch: высота тонов, баланс каналов и отображение услышанных кадров в исходный PTS. */ |
|
#include <cmath> |
|
#include <cstdio> |
|
#include <cstdlib> |
|
#include <vector> |
|
|
|
extern "C" { |
|
#include "../../../lib/debug_config.h" |
|
#include "../../../lib/mem.h" |
|
#include "../../../lib/opus_codec.h" |
|
#include "../../../src/stream/stream_playback_audio.h" |
|
} |
|
|
|
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "stream audio test line=%d: %s", __LINE__, #x); exit(1); } } while (0) |
|
|
|
static void test_stereo_tempo() { |
|
auto* encoder = opus_codec_encoder_create(48000, 2); |
|
CHECK(encoder && opus_codec_encoder_application_set(encoder, OPUS_CODEC_APP_AUDIO) == 0); |
|
CHECK(opus_codec_encoder_bitrate_set(encoder, 128000) == 0); |
|
auto* audio = stream_playback_audio_create(0, 40); CHECK(audio); |
|
stream_playback_audio_set_tempo(audio, 1100); |
|
std::vector<int16_t> decoded; |
|
int16_t pcm[1920], output[1920]; uint8_t packet[1275]; |
|
for (unsigned frame = 0; frame < 200; frame++) { |
|
for (unsigned i = 0; i < 960; i++) { |
|
double t = (frame * 960 + i) / 48000.0; |
|
pcm[2 * i] = (int16_t)std::lrint(10000 * std::sin(2 * 3.141592653589793 * 1000 * t)); |
|
pcm[2 * i + 1] = (int16_t)std::lrint(4000 * std::sin(2 * 3.141592653589793 * 500 * t)); |
|
} |
|
int length = opus_codec_encode(encoder, pcm, 960, packet, sizeof(packet)); CHECK(length > 0); |
|
CHECK(stream_playback_audio_feed(audio, frame * 20000, packet, (size_t)length) == 0); |
|
for (;;) { |
|
int count = stream_playback_audio_pull(audio, output, 480, INT64_MAX); CHECK(count >= 0); |
|
if (!count) break; |
|
decoded.insert(decoded.end(), output, output + count * 2); |
|
} |
|
} |
|
stream_playback_audio_finish(audio); |
|
for (unsigned i = 0; !stream_playback_audio_empty(audio) && i < 100; i++) { |
|
int count = stream_playback_audio_pull(audio, output, 480, INT64_MAX); CHECK(count > 0); |
|
decoded.insert(decoded.end(), output, output + count * 2); |
|
} |
|
CHECK(stream_playback_audio_empty(audio)); |
|
uint64_t written = stream_playback_audio_written(audio); |
|
CHECK(written == decoded.size() / 2 && std::abs((double)written - 192000 / 1.1) < 2); |
|
unsigned crossings[2] = {0, 0}; double energy[2] = {0, 0}; |
|
unsigned first = 9600, last = (unsigned)written - 9600; |
|
for (unsigned i = first + 1; i < last; i++) for (unsigned ch = 0; ch < 2; ch++) { |
|
if (decoded[(i - 1) * 2 + ch] < 0 && decoded[i * 2 + ch] >= 0) crossings[ch]++; |
|
energy[ch] += (double)decoded[i * 2 + ch] * decoded[i * 2 + ch]; |
|
} |
|
double seconds = (last - first) / 48000.0; |
|
CHECK(std::abs(crossings[0] / seconds - 1000) < 5 && std::abs(crossings[1] / seconds - 500) < 5); |
|
CHECK(std::sqrt(energy[0] / energy[1]) > 2.0 && std::sqrt(energy[0] / energy[1]) < 3.0); |
|
int64_t half = stream_playback_audio_position(audio, written / 2); |
|
CHECK(std::abs(half - (int64_t)(written / 2 * 1100000.0 / 48000)) < 100); |
|
int64_t gain = stream_playback_audio_pending_gain(audio, written / 2); |
|
CHECK(gain > 170000 && gain < 190000); |
|
stream_playback_audio_destroy(audio); opus_codec_encoder_destroy(encoder); |
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] stereo x1.1: unchanged tone pitch/balance, exact duration and heard PTS mapping"); |
|
} |
|
|
|
static void test_silence_clock() { |
|
auto* encoder = opus_codec_encoder_create(48000, 2); auto* audio = stream_playback_audio_create(1000000, 40); |
|
CHECK(encoder && audio); |
|
int16_t pcm[1920] = {0}, out[1920]; uint8_t packet[1275]; |
|
for (unsigned i = 0; i < 4; i++) { |
|
int length = opus_codec_encode(encoder, pcm, 960, packet, sizeof(packet)); CHECK(length > 0); |
|
CHECK(stream_playback_audio_feed(audio, 1000000 + i * 20000, packet, (size_t)length) == 0); |
|
} |
|
int length = opus_codec_encode(encoder, pcm, 960, packet, sizeof(packet)); CHECK(length > 0); |
|
CHECK(stream_playback_audio_feed(audio, INT64_MAX - 20000, packet, (size_t)length) == -2); |
|
CHECK(stream_playback_audio_feed(audio, 1080000, packet, (size_t)length) == 0); |
|
int got = stream_playback_audio_pull(audio, out, 960, INT64_MAX); CHECK(got > 0); |
|
uint64_t before = stream_playback_audio_written(audio); |
|
int64_t pts = stream_playback_audio_position(audio, before); |
|
stream_playback_audio_silence(audio, 9600); |
|
CHECK(stream_playback_audio_position(audio, before + 4800) == pts); |
|
CHECK(stream_playback_audio_starved(audio, before + 4800)); |
|
CHECK(stream_playback_audio_pending_gain(audio, before + 4800) == 0); |
|
stream_playback_audio_destroy(audio); opus_codec_encoder_destroy(encoder); |
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] device silence freezes source time and preserves stereo decoder state"); |
|
} |
|
|
|
/* Готовые пакеты ждут worker: больший запас покрывает паузу его планирования между callback устройства. */ |
|
static void test_pcm_buffer() { |
|
CHECK(!stream_playback_audio_create(0, 0)); |
|
CHECK(!stream_playback_audio_create(0, 1001)); |
|
auto* encoder = opus_codec_encoder_create(48000, 2); CHECK(encoder); |
|
int16_t pcm[1920] = {0}, output[960]; uint8_t packet[1275]; |
|
int length = opus_codec_encode(encoder, pcm, 960, packet, sizeof(packet)); CHECK(length > 0); |
|
for (unsigned bufferMs : {20u, 40u}) { |
|
auto* audio = stream_playback_audio_create(0, bufferMs); CHECK(audio); |
|
unsigned packets = 0; |
|
while (stream_playback_audio_needs_input(audio) && packets < 10) { |
|
CHECK(stream_playback_audio_feed(audio, packets * 20000, packet, (size_t)length) == 0); ++packets; |
|
} |
|
CHECK(!stream_playback_audio_needs_input(audio)); |
|
unsigned played = 0; |
|
for (unsigned callback = 0; callback < 4; callback++) { |
|
int count = stream_playback_audio_pull(audio, output, 480, INT64_MAX); CHECK(count >= 0); |
|
played += (unsigned)count; |
|
} |
|
CHECK(bufferMs == 40 ? played == 1920 : played < 1920); |
|
CHECK(stream_playback_audio_needs_input(audio)); |
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] PCM buffer=%ums worker_gap=40ms played=%u/1920 frames", bufferMs, played); |
|
stream_playback_audio_destroy(audio); |
|
} |
|
opus_codec_encoder_destroy(encoder); |
|
} |
|
|
|
int main() { |
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_INFO); |
|
size_t allocations = u_get_allocated_count(); |
|
test_stereo_tempo(); test_silence_clock(); test_pcm_buffer(); CHECK(u_get_allocated_count() == allocations); |
|
return 0; |
|
}
|
|
|