mirror of
https://github.com/jessicanataliagta/PSPRecomp
synced 2026-09-26 16:49:34 -04:00
37e5469cbd
initial release
107 lines
4.0 KiB
C++
107 lines
4.0 KiB
C++
#include "audio_resampler.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
using Stereo = std::pair<std::int16_t, std::int16_t>;
|
|
|
|
void require(bool condition, const char *message) {
|
|
if (!condition) throw std::runtime_error(message);
|
|
}
|
|
|
|
std::vector<std::int16_t> make_signal(std::size_t frames, bool stereo) {
|
|
std::vector<std::int16_t> pcm(frames * (stereo ? 2u : 1u));
|
|
for (std::size_t i = 0; i < frames; ++i) {
|
|
const std::int16_t left = static_cast<std::int16_t>((static_cast<int>(i * 977u) % 50000) - 25000);
|
|
const std::int16_t right = static_cast<std::int16_t>((static_cast<int>(i * 619u + 1234u) % 48000) - 24000);
|
|
if (stereo) {
|
|
pcm[i * 2u] = left;
|
|
pcm[i * 2u + 1u] = right;
|
|
} else {
|
|
pcm[i] = left;
|
|
}
|
|
}
|
|
return pcm;
|
|
}
|
|
|
|
std::vector<Stereo> run_one(const std::vector<std::int16_t> &pcm, std::uint32_t frames,
|
|
bool stereo, std::uint32_t rate) {
|
|
vcs::StreamingLinearResampler resampler;
|
|
resampler.reset(rate, stereo);
|
|
std::vector<Stereo> output;
|
|
resampler.process(pcm, frames, stereo, rate,
|
|
[&](std::int16_t l, std::int16_t r) { output.emplace_back(l, r); });
|
|
return output;
|
|
}
|
|
|
|
std::vector<Stereo> run_chunked(const std::vector<std::int16_t> &pcm, std::uint32_t frames,
|
|
bool stereo, std::uint32_t rate,
|
|
const std::vector<std::uint32_t> &chunks) {
|
|
vcs::StreamingLinearResampler resampler;
|
|
resampler.reset(rate, stereo);
|
|
std::vector<Stereo> output;
|
|
const std::size_t channels = stereo ? 2u : 1u;
|
|
std::uint32_t offset_frames = 0u;
|
|
std::size_t chunk_index = 0u;
|
|
while (offset_frames < frames) {
|
|
const std::uint32_t requested = chunks[chunk_index++ % chunks.size()];
|
|
const std::uint32_t count = std::min(requested, frames - offset_frames);
|
|
const std::span<const std::int16_t> view(
|
|
pcm.data() + static_cast<std::size_t>(offset_frames) * channels,
|
|
static_cast<std::size_t>(count) * channels);
|
|
resampler.process(view, count, stereo, rate,
|
|
[&](std::int16_t l, std::int16_t r) { output.emplace_back(l, r); });
|
|
offset_frames += count;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
void test_chunk_invariance(std::uint32_t rate, bool stereo) {
|
|
constexpr std::uint32_t frames = 4096u;
|
|
const auto input = make_signal(frames, stereo);
|
|
const auto whole = run_one(input, frames, stereo, rate);
|
|
const auto chunked = run_chunked(input, frames, stereo, rate, {17u, 63u, 256u, 511u, 37u});
|
|
require(whole == chunked, "streaming resampler changed output at a PSP buffer boundary");
|
|
}
|
|
|
|
void test_unity_rate_exact() {
|
|
constexpr std::uint32_t frames = 1024u;
|
|
const auto input = make_signal(frames, true);
|
|
const auto output = run_chunked(input, frames, true, 44100u, {64u, 128u, 257u});
|
|
require(output.size() == frames, "44100 Hz resampler changed frame count");
|
|
for (std::size_t i = 0; i < output.size(); ++i) {
|
|
require(output[i].first == input[i * 2u] && output[i].second == input[i * 2u + 1u],
|
|
"44100 Hz resampler was not bit-exact");
|
|
}
|
|
}
|
|
|
|
void test_mono_duplication() {
|
|
const auto input = make_signal(1024u, false);
|
|
const auto output = run_chunked(input, 1024u, false, 32000u, {127u, 129u});
|
|
require(!output.empty(), "mono resampler produced no data");
|
|
for (const auto &sample : output)
|
|
require(sample.first == sample.second, "mono source was not duplicated to stereo");
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
try {
|
|
test_unity_rate_exact();
|
|
test_chunk_invariance(32000u, true);
|
|
test_chunk_invariance(48000u, true);
|
|
test_chunk_invariance(22050u, true);
|
|
test_chunk_invariance(24000u, false);
|
|
test_mono_duplication();
|
|
std::cout << "audio_resampler_tests: PASS\n";
|
|
return 0;
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "audio_resampler_tests: FAIL: " << e.what() << "\n";
|
|
return 1;
|
|
}
|
|
}
|