]> git.sesse.net Git - nageru/commitdiff
Add a correctness test to the audio benchmark.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 26 Aug 2016 18:46:51 +0000 (20:46 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 19 Oct 2016 22:55:44 +0000 (00:55 +0200)
benchmark_audio_mixer.cpp
ref.raw [new file with mode: 0644]

index f100920e0c1b2f1bbc4f4acc06c07f1dbd8d2bb5..8fe4710cbc8e834b4f84f6ca44a67309db7c28a1 100644 (file)
@@ -7,11 +7,13 @@
 #include <vector>
 #include <chrono>
 #include "audio_mixer.h"
+#include "db.h"
 #include "timebase.h"
 
 #define NUM_BENCHMARK_CARDS 4
 #define NUM_WARMUP_FRAMES 100
 #define NUM_BENCHMARK_FRAMES 1000
+#define NUM_TEST_FRAMES 10
 #define NUM_CHANNELS 8
 #define NUM_SAMPLES 1024
 
@@ -24,6 +26,21 @@ uint8_t samples16[(NUM_SAMPLES * NUM_CHANNELS + 1024) * sizeof(uint16_t)];
 // 24-bit samples, white noise at low volume (-48 dB).
 uint8_t samples24[(NUM_SAMPLES * NUM_CHANNELS + 1024) * 3];
 
+static uint32_t seed = 1234;
+
+// We use our own instead of rand() to get deterministic behavior.
+// Quality doesn't really matter much.
+uint32_t lcgrand()
+{
+       seed = seed * 1103515245u + 12345u;
+       return seed;
+}
+
+void reset_lcgrand()
+{
+       seed = 1234;
+}
+
 void callback(float level_lufs, float peak_db,
               std::vector<AudioMixer::BusLevel> bus_levels,
              float global_level_lufs, float range_low_lufs, float range_high_lufs,
@@ -33,19 +50,27 @@ void callback(float level_lufs, float peak_db,
        // Empty.
 }
 
-int main(void)
+vector<float> process_frame(unsigned frame_num, AudioMixer *mixer)
 {
-       for (unsigned i = 0; i < NUM_SAMPLES * NUM_CHANNELS + 1024; ++i) {
-               samples16[i * 2] = rand() & 0xff;
-               samples16[i * 2 + 1] = rand() & 0xff;
-
-               samples24[i * 3] = rand() & 0xff;
-               samples24[i * 3 + 1] = rand() & 0xff;
-               samples24[i * 3 + 2] = 0;
+       // Feed the inputs.
+       for (unsigned card_index = 0; card_index < NUM_BENCHMARK_CARDS; ++card_index) {
+               bmusb::AudioFormat audio_format;
+               audio_format.bits_per_sample = card_index == 3 ? 24 : 16;
+               audio_format.num_channels = NUM_CHANNELS;
+               
+               unsigned num_samples = NUM_SAMPLES + (lcgrand() % 9) - 5;
+               bool ok = mixer->add_audio(DeviceSpec{InputSourceType::CAPTURE_CARD, card_index},
+                       card_index == 3 ? samples24 : samples16, num_samples, audio_format,
+                       NUM_SAMPLES * TIMEBASE / OUTPUT_FREQUENCY);
+               assert(ok);
        }
-       AudioMixer mixer(NUM_BENCHMARK_CARDS);
-       mixer.set_audio_level_callback(callback);
 
+       double pts = double(frame_num) * NUM_SAMPLES / OUTPUT_FREQUENCY;
+       return mixer->get_output(pts, NUM_SAMPLES, ResamplingQueue::ADJUST_RATE);
+}
+
+void init_mapping(AudioMixer *mixer)
+{
        InputMapping mapping;
 
        InputMapping::Bus bus1;
@@ -60,30 +85,64 @@ int main(void)
        bus2.source_channel[1] = 4;
        mapping.buses.push_back(bus2);
 
-       mixer.set_input_mapping(mapping);
+       mixer->set_input_mapping(mapping);
+}
+
+void do_test(const char *filename)
+{
+       AudioMixer mixer(NUM_BENCHMARK_CARDS);
+       mixer.set_audio_level_callback(callback);
+       init_mapping(&mixer);
+
+       reset_lcgrand();
+
+       vector<float> output;
+       for (unsigned i = 0; i < NUM_TEST_FRAMES; ++i) {
+               vector<float> frame_output = process_frame(i, &mixer);
+               output.insert(output.end(), frame_output.begin(), frame_output.end());
+       }
+
+       FILE *fp = fopen(filename, "rb");
+       if (fp == nullptr) {
+               fprintf(stderr, "%s not found, writing new reference.\n", filename);
+               fp = fopen(filename, "wb");
+               fwrite(&output[0], output.size() * sizeof(float), 1, fp);
+               fclose(fp);
+               return;
+       }
+
+       vector<float> ref;
+       ref.resize(output.size());
+       fread(&ref[0], output.size() * sizeof(float), 1, fp);
+       fclose(fp);
+
+       float max_err = 0.0f, sum_sq_err = 0.0f;
+       for (unsigned i = 0; i < output.size(); ++i) {
+               float err = output[i] - ref[i];
+               max_err = max(max_err, fabs(err));
+               sum_sq_err += err * err;
+       }
+
+       printf("Largest error: %.6f (%+.1f dB)\n", max_err, to_db(max_err));
+       printf("RMS error:     %+.1f dB\n", to_db(sqrt(sum_sq_err) / output.size()));
+}
+
+void do_benchmark()
+{
+       AudioMixer mixer(NUM_BENCHMARK_CARDS);
+       mixer.set_audio_level_callback(callback);
+       init_mapping(&mixer);
 
        size_t out_samples = 0;
 
+       reset_lcgrand();
+
        steady_clock::time_point start, end;
        for (unsigned i = 0; i < NUM_WARMUP_FRAMES + NUM_BENCHMARK_FRAMES; ++i) {
                if (i == NUM_WARMUP_FRAMES) {
                        start = steady_clock::now();
                }
-               // Feed the inputs.
-               for (unsigned card_index = 0; card_index < NUM_BENCHMARK_CARDS; ++card_index) {
-                       bmusb::AudioFormat audio_format;
-                       audio_format.bits_per_sample = card_index == 3 ? 24 : 16;
-                       audio_format.num_channels = NUM_CHANNELS;
-                       
-                       unsigned num_samples = NUM_SAMPLES + (rand() % 9) - 5;
-                       bool ok = mixer.add_audio(DeviceSpec{InputSourceType::CAPTURE_CARD, card_index},
-                               card_index == 3 ? samples24 : samples16, num_samples, audio_format,
-                               NUM_SAMPLES * TIMEBASE / OUTPUT_FREQUENCY);
-                       assert(ok);
-               }
-
-               double pts = double(i) * NUM_SAMPLES / OUTPUT_FREQUENCY;
-               vector<float> output = mixer.get_output(pts, NUM_SAMPLES, ResamplingQueue::ADJUST_RATE);
+               vector<float> output = process_frame(i, &mixer);
                if (i >= NUM_WARMUP_FRAMES) {
                        out_samples += output.size();
                }
@@ -95,3 +154,21 @@ int main(void)
        printf("%ld samples produced in %.1f ms (%.1f%% CPU, %.1fx realtime).\n",
                out_samples, elapsed * 1e3, 100.0 * elapsed / simulated, simulated / elapsed);
 }
+
+int main(int argc, char **argv)
+{
+       for (unsigned i = 0; i < NUM_SAMPLES * NUM_CHANNELS + 1024; ++i) {
+               samples16[i * 2] = lcgrand() & 0xff;
+               samples16[i * 2 + 1] = lcgrand() & 0xff;
+
+               samples24[i * 3] = lcgrand() & 0xff;
+               samples24[i * 3 + 1] = lcgrand() & 0xff;
+               samples24[i * 3 + 2] = 0;
+       }
+
+       if (argc == 2) {
+               do_test(argv[1]);
+       }
+       do_benchmark();
+}
+
diff --git a/ref.raw b/ref.raw
new file mode 100644 (file)
index 0000000..210b8c3
Binary files /dev/null and b/ref.raw differ