/*************************************************************************** * Copyright (C) 2022 by Alain Carlucci * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see * ***************************************************************************/ #include #include #include #include #include #include #include "interfaces/audio_stream.h" static const char* files[] = {"MIC.raw", "RTX.raw", "MCU.raw"}; #define CHECK(x) \ do \ { \ if (!(x)) \ { \ puts("Failed assertion: " #x "\n"); \ abort(); \ } \ } while (0) void test_linear() { for (int fn = 0; fn < 3; fn++) { // Write a mock audio file FILE* fp = fopen(files[fn], "wb"); CHECK(fp); for (int i = 0; i < 13; i++) { CHECK(fputc(i, fp) == i); CHECK(fputc(0, fp) == 0); } fclose(fp); // Start inputStream using that file as source stream_sample_t tmp[128]; auto id = inputStream_start( static_cast(fn), AudioPriority::PRIO_PROMPT, tmp, sizeof(tmp) / sizeof(tmp[0]), BufMode::BUF_LINEAR, 44100); CHECK(id != -1); // Should fail auto id_2 = inputStream_start( static_cast(fn), AudioPriority::PRIO_BEEP, tmp, sizeof(tmp) / sizeof(tmp[0]), BufMode::BUF_LINEAR, 44100); CHECK(id_2 == -1); auto id_3 = inputStream_start( static_cast(fn), AudioPriority::PRIO_RX, tmp, sizeof(tmp) / sizeof(tmp[0]), BufMode::BUF_LINEAR, 44100); CHECK(id_3 == -1); // This should work, as it has higher priority auto id_4 = inputStream_start( static_cast(fn), AudioPriority::PRIO_TX, tmp, sizeof(tmp) / sizeof(tmp[0]), BufMode::BUF_LINEAR, 44100); CHECK(id_4 != -1); { // id is now invalid (overridden by id_4), getData should fail auto should_fail = inputStream_getData(id); CHECK(should_fail.data == NULL); CHECK(should_fail.len == 0); } // id_4 is now the only valid pointer, should work id = id_4; int c_ptr = 0; // getData multiple times for (int i = 0; i < 20; i++) { using namespace std::chrono; auto t1 = steady_clock::now(); auto db = inputStream_getData(id); auto t2 = steady_clock::now(); const uint64_t delta = duration_cast(t2 - t1).count(); const uint64_t expected = (128 * 1000000 / 44100); // Check that the getData() sleeps the right amount of time CHECK((delta > expected) && delta < (expected + 10000)); // Check the contents CHECK(db.len == 128); for (int i = 0; i < 128; i++) { CHECK(tmp[i] == (c_ptr % 13)); CHECK(db.data[i] == (c_ptr % 13)); c_ptr++; } } // Close inputStream and remove the temporary file inputStream_stop(id); CHECK(remove(files[fn]) == 0); } } void test_ring_buffer(uint64_t n_bytes, uint64_t n_iter, const uint64_t buf_size) { for (int fn = 0; fn < 3; fn++) { FILE* fp = fopen(files[fn], "wb"); CHECK(fp); for (uint64_t i = 0; i < n_bytes; i++) { uint16_t j = i; uint8_t* buf = (uint8_t*)&j; CHECK(fputc(buf[0], fp) == buf[0]); CHECK(fputc(buf[1], fp) == buf[1]); } fclose(fp); std::vector tmp(buf_size); auto id = inputStream_start( static_cast(fn), AudioPriority::PRIO_BEEP, tmp.data(), tmp.size(), BufMode::BUF_CIRC_DOUBLE, 44100); CHECK(id != -1); using namespace std::chrono; time_point t0 = steady_clock::now(); uint64_t ctr = 0; std::vector tmp2(buf_size / 2); for (uint64_t i = 0; i < n_iter; i++) { { auto db = inputStream_getData(id); CHECK(db.len > 0); CHECK(db.data == &tmp[0]); memcpy(tmp2.data(), db.data, db.len * sizeof(stream_sample_t)); for (uint64_t i = 0; i < db.len; i++) { CHECK(uint16_t(tmp2[i]) == uint16_t(ctr % n_bytes)); ctr++; } } { auto db = inputStream_getData(id); CHECK(db.len > 0); CHECK(db.data == &tmp[buf_size / 2]); memcpy(tmp2.data(), db.data, db.len * sizeof(stream_sample_t)); for (uint64_t i = 0; i < db.len; i++) { CHECK(uint16_t(tmp2[i]) == uint16_t(ctr % n_bytes)); ctr++; } } } auto t2 = steady_clock::now(); const uint64_t delta = duration_cast(t2 - t0).count(); const uint64_t expected = (buf_size * n_iter * 1000000lu / 44100); CHECK(delta > expected && delta < expected * 2); inputStream_stop(id); CHECK(remove(files[fn]) == 0); } } int main() { test_linear(); test_ring_buffer(13, 10, 256); test_ring_buffer(128, 10, 256); test_ring_buffer(256, 10, 128); test_ring_buffer(1234, 10, 768); return 0; }