From aad64a9b7d7515a4d26c2ccec9381389b5680934 Mon Sep 17 00:00:00 2001 From: George Norton Date: Fri, 31 Mar 2023 16:48:17 +0100 Subject: [PATCH] Add a simple test application for running the filtering stages on a PC. --- firmware/test/CMakeLists.txt | 25 ++++++++++ firmware/test/README.md | 23 +++++++++ firmware/test/filter_test.c | 91 ++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 firmware/test/CMakeLists.txt create mode 100644 firmware/test/README.md create mode 100644 firmware/test/filter_test.c diff --git a/firmware/test/CMakeLists.txt b/firmware/test/CMakeLists.txt new file mode 100644 index 0000000..9125749 --- /dev/null +++ b/firmware/test/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.13) + +project(ploopy_headphones_project C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +add_executable(filter_test + filter_test.c + ../code/fix16.c + ../code/bqf.c + ../code/user.c +) + +target_include_directories(filter_test PRIVATE ${CMAKE_SOURCE_DIR}/../code) + +# TODO: user.c includes run.h to get the definition for SAMPLING_FREQ, but this +# pulls in the whole pico sdk as a dependency. A little refactoring would fix it all. +target_compile_definitions(filter_test PRIVATE + SAMPLING_FREQ=48000 + RUN_H +) + +target_link_libraries(filter_test + m +) diff --git a/firmware/test/README.md b/firmware/test/README.md new file mode 100644 index 0000000..3a2323b --- /dev/null +++ b/firmware/test/README.md @@ -0,0 +1,23 @@ +## filter_test +This is a basic utility for testing the Ploopy headphones filtering routines on a PC. + +### Usage +Find a source file and use ffmpeg to convert it to PCM data: + +``` +ffmpeg -i +#include +#include "bqf.h" +#include "fix16.h" +#include "user.h" + +bqf_coeff_t bqf_filters_left[FILTER_STAGES]; +bqf_coeff_t bqf_filters_right[FILTER_STAGES]; +bqf_mem_t bqf_filters_mem_left[FILTER_STAGES]; +bqf_mem_t bqf_filters_mem_right[FILTER_STAGES]; + +const char* usage = "Usage: %s INFILE OUTFILE\n\n" + "Reads 16bit stereo PCM data from INFILE, runs it through the Ploopy headphones\n" + "filters then writes it out to OUTFILE.\n"; + +int main(int argc, char* argv[]) +{ + if (argc != 3) + { + fprintf(stdout, usage, argv[0]); + exit(1); + } + + // Load the input data into a buffer + FILE* input = fopen(argv[1], "rb"); + if (!input) { + fprintf(stderr, "Cannot open input file '%s'\n", argv[1]); + exit(1); + } + + // Get the file size + fseek(input , 0L , SEEK_END); + size_t input_size = ftell(input); + rewind(input); + + // Allocate our input and output buffers. This could be optimized + // we dont need to store the whole input and output files in memory. + int samples = input_size / 2; + int16_t *in = (int16_t *) calloc(samples, sizeof(int16_t)); + int16_t *out = (int16_t *) calloc(samples, sizeof(int16_t)); + + fread(in, samples, sizeof(int16_t), input); + fclose(input); + + // Open the output file. + FILE* output = fopen(argv[2], "wb"); + if (!output) + { + fprintf(stderr, "Cannot open output file '%s'\n", argv[2]); + exit(1); + } + + // The smaple proccesing code, essentially the same as the + // code in the firmware's run.c file. + define_filters(); + + for (int i = 0; i < samples; i++) + { + out[i] = in[i]; + } + + for (int j = 0; j < FILTER_STAGES; j++) + { + for (int i = 0; i < samples; i ++) + { + // Left channel filter + fix16_t x_f16 = fix16_from_int((int16_t) out[i]); + + x_f16 = bqf_transform(x_f16, &bqf_filters_left[j], + &bqf_filters_mem_left[j]); + + out[i] = (int32_t) fix16_to_int(x_f16); + + // Right channel filter + i++; + x_f16 = fix16_from_int((int16_t) out[i]); + + x_f16 = bqf_transform(x_f16, &bqf_filters_right[j], + &bqf_filters_mem_right[j]); + + out[i] = (int16_t) fix16_to_int(x_f16); + } + } + + // Write out the processed audio. + fwrite(out, samples, sizeof(int16_t), output); + fclose(output); + + free(in); + free(out); +}