96 lines
3.4 KiB
C++
96 lines
3.4 KiB
C++
/**
|
|
* @file resample-performance.ino
|
|
* @brief Measures the raw CPU cost of ResampleStream's interpolation math in
|
|
* isolation, with NO adaptive control loop (fixed step_size) and no USB/I2S
|
|
* involved at all - just a static in-memory buffer feeding the exact same
|
|
* read path AdaptiveResamplingBuffer uses (TransformationReader pumping
|
|
* ResampleStream::write()/writeFixed()).
|
|
*
|
|
* The source is a cheap precomputed static buffer (no floating-point sine
|
|
* generation per sample) so the baseline reflects the real USB sketch,
|
|
* where the source is just bytes already sitting in a ring buffer.
|
|
*
|
|
* For each (sample_rate, step_size) combination, it reads exactly 1 second
|
|
* worth of resampled output as fast as possible and reports how long that
|
|
* took. step_size = 1.0 is a no-op passthrough (baseline: pipe/plumbing
|
|
* overhead only). step_size = 1.001 is representative of what the adaptive
|
|
* resampler actually applies most of the time (small clock-drift
|
|
* correction) - the delta between the two isolates the interpolation
|
|
* math's true added cost.
|
|
*/
|
|
#include "AudioTools.h"
|
|
|
|
AudioInfo info(48000, 2, 16);
|
|
uint8_t src_buf[8192];
|
|
MemoryStream mem(src_buf, sizeof(src_buf), true);
|
|
ResampleStream resample(mem);
|
|
uint8_t out_buf[1024];
|
|
|
|
void runTest(int sample_rate, float step_size) {
|
|
info.sample_rate = sample_rate;
|
|
mem.begin(info);
|
|
|
|
auto rcfg = resample.defaultConfig();
|
|
rcfg.copyFrom(info);
|
|
rcfg.step_size = step_size;
|
|
resample.begin(rcfg);
|
|
// Loop the small static source indefinitely instead of latching EOF once
|
|
// it's drained once - same setting AdaptiveResamplingBuffer uses for a
|
|
// live producer.
|
|
resample.transformationReader().setEofOnZeroReads(false);
|
|
resample.transformationReader().setZeroReadDelay(0);
|
|
|
|
size_t bytes_per_sec =
|
|
(size_t)sample_rate * info.channels * (info.bits_per_sample / 8);
|
|
|
|
size_t total = 0;
|
|
uint32_t start = micros();
|
|
while (total < bytes_per_sec) {
|
|
// Keep enough slack ahead of the internal chunk size so a read never
|
|
// straddles the end of the buffer mid-chunk.
|
|
if (mem.available() < 2048) mem.rewind();
|
|
size_t to_read = min(sizeof(out_buf), bytes_per_sec - total);
|
|
size_t got = resample.readBytes(out_buf, to_read);
|
|
if (got == 0) break;
|
|
total += got;
|
|
}
|
|
uint32_t elapsed_us = micros() - start;
|
|
float cpu_percent = elapsed_us / 10000.0f; // elapsed_us / 1e6 * 100
|
|
|
|
Serial.print("sample_rate=");
|
|
Serial.print(sample_rate);
|
|
Serial.print(" step_size=");
|
|
Serial.print(step_size, 4);
|
|
Serial.print(" bytes=");
|
|
Serial.print((unsigned long)total);
|
|
Serial.print(" elapsed_us=");
|
|
Serial.print(elapsed_us);
|
|
Serial.print(" cpu_percent=");
|
|
Serial.println(cpu_percent, 2);
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
delay(3000);
|
|
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
|
|
|
// Cheap, non-constant, integer-only pattern - avoids floating point
|
|
// entirely so we measure ONLY the resampling/plumbing cost, not source
|
|
// generation cost.
|
|
for (size_t i = 0; i < sizeof(src_buf); i += 2) {
|
|
int16_t v = (int16_t)((i >> 2) & 0xFFF) - 2048;
|
|
src_buf[i] = (uint8_t)(v & 0xFF);
|
|
src_buf[i + 1] = (uint8_t)((v >> 8) & 0xFF);
|
|
}
|
|
|
|
Serial.println("--- ResampleStream performance test (static buffer, no sine) ---");
|
|
runTest(32000, 1.0f);
|
|
runTest(32000, 1.001f);
|
|
runTest(44100, 1.0f);
|
|
runTest(44100, 1.001f);
|
|
runTest(48000, 1.0f);
|
|
runTest(48000, 1.001f);
|
|
Serial.println("--- done ---");
|
|
}
|
|
|
|
void loop() {}
|