Run the DAC in 24bit mode. (#25)

* Run the DAC in 24bit mode.

* Update comment.

* Remove accidental paste

* Fix distortion.

* Shift up the samples into -1..1, not much different, but we get an extra bit of resolution at the low end.
This commit is contained in:
George Norton 2023-09-14 09:49:56 +01:00 committed by GitHub
parent 1e6896f918
commit 41d4023961
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 47 additions and 45 deletions

View File

@ -41,7 +41,7 @@ static const fix3_28_t fix16_zero = 0x00000000;
static inline fix3_28_t norm_fix3_28_from_s16sample(int16_t); static inline fix3_28_t norm_fix3_28_from_s16sample(int16_t);
static inline int16_t norm_fix3_28_to_s16sample(fix3_28_t); static inline int32_t norm_fix3_28_to_s16sample(fix3_28_t);
static inline fix3_28_t fix3_28_from_flt(float); static inline fix3_28_t fix3_28_from_flt(float);

View File

@ -32,18 +32,18 @@ static inline fix3_28_t norm_fix3_28_from_s16sample(int16_t a) {
/* So, we're using a Q3.28 fixed point system here, and we want the incoming /* So, we're using a Q3.28 fixed point system here, and we want the incoming
audio signal to be represented as a number between -1 and 1. To do this, audio signal to be represented as a number between -1 and 1. To do this,
we need the 16-bit value to map to the 28-bit right-of-decimal field in we need the 16-bit value to map to the 28-bit right-of-decimal field in
our fixed point number. 28-16 = 12, so we shift the incoming value by our fixed point number. 28-16 = 12 + the sign bit = 13, so we shift the
that much to covert it to the desired Q3.28 format and do the normalization incoming value by that much to covert it to the desired Q3.28 format and
all in one go. do the normalization all in one go.
*/ */
return (fix3_28_t)a << 12; return (fix3_28_t)a << 13;
} }
/// @brief Convert fixed point samples into signed integer. Used to convert /// @brief Convert fixed point samples into signed integer. Used to convert
/// calculated sample to one that the DAC can understand. /// calculated sample to one that the DAC can understand.
/// @param a /// @param a
/// @return Signed 16-bit integer. /// @return Signed 16-bit integer.
static inline int16_t norm_fix3_28_to_s16sample(fix3_28_t a) { static inline int32_t norm_fix3_28_to_s16sample(fix3_28_t a) {
// Handle rounding up front, adding one can cause an overflow/underflow // Handle rounding up front, adding one can cause an overflow/underflow
// It's not clear exactly how this works, so we'll disable it for now. // It's not clear exactly how this works, so we'll disable it for now.
@ -56,22 +56,20 @@ static inline int16_t norm_fix3_28_to_s16sample(fix3_28_t a) {
*/ */
// Saturate the value if an overflow has occurred // Saturate the value if an overflow has occurred
uint32_t upper = (a >> 30); uint32_t upper = (a >> 29);
if (a < 0) { if (a < 0) {
if (~upper) if (~upper) {
{ return 0xff800000;
return SHRT_MIN;
} }
} else { } else {
if (upper) if (upper) {
{ return 0x00efffff;
return SHRT_MAX;
} }
} }
/* When we converted the USB audio sample to a fixed point number, we applied /* When we converted the USB audio sample to a fixed point number, we applied
a normalization, or a gain of 1/65536. To convert it back, we can undo that a normalization, or a gain of 1/65536. To convert it back, we can undo that
by shifting it back by the same amount we shifted it in the first place. */ by shifting it but we output 24bts, so the shift is reduced. */
return (a >> 12); return (a >> 6);
} }
static inline fix3_28_t fix3_28_from_flt(float a) { static inline fix3_28_t fix3_28_from_flt(float a) {

View File

@ -140,16 +140,15 @@ static void __no_inline_not_in_flash_func(_as_audio_packet)(struct usb_endpoint
multicore_fifo_push_blocking(CORE0_READY); multicore_fifo_push_blocking(CORE0_READY);
multicore_fifo_push_blocking(samples); multicore_fifo_push_blocking(samples);
for (int j = 0; j < filter_stages; j++) {
// Left channel filter
for (int i = 0; i < samples; i += 2) {
fix3_28_t x_f16 = fix16_mul(norm_fix3_28_from_s16sample((int16_t) out[i]), preprocessing.preamp);
// Left channel filter
for (int i = 0; i < samples; i += 2) {
fix3_28_t x_f16 = fix16_mul(norm_fix3_28_from_s16sample((int16_t) out[i]), preprocessing.preamp);
for (int j = 0; j < filter_stages; j++) {
x_f16 = bqf_transform(x_f16, &bqf_filters_left[j], x_f16 = bqf_transform(x_f16, &bqf_filters_left[j],
&bqf_filters_mem_left[j]); &bqf_filters_mem_left[j]);
out[i] = (int32_t) norm_fix3_28_to_s16sample(x_f16);
} }
out[i] = (int32_t) norm_fix3_28_to_s16sample(x_f16);
} }
// Block until core 1 has finished transforming the data // Block until core 1 has finished transforming the data
@ -187,15 +186,13 @@ void __no_inline_not_in_flash_func(core1_entry)() {
const uint32_t samples = multicore_fifo_pop_blocking(); const uint32_t samples = multicore_fifo_pop_blocking();
for (int j = 0; j < filter_stages; j++) { for (int i = 1; i < samples; i += 2) {
for (int i = 1; i < samples; i += 2) { fix3_28_t x_f16 = fix16_mul(norm_fix3_28_from_s16sample((int16_t) out[i]), preprocessing.preamp);
fix3_28_t x_f16 = fix16_mul(norm_fix3_28_from_s16sample((int16_t) out[i]), preprocessing.preamp); for (int j = 0; j < filter_stages; j++) {
x_f16 = bqf_transform(x_f16, &bqf_filters_right[j], x_f16 = bqf_transform(x_f16, &bqf_filters_right[j],
&bqf_filters_mem_right[j]); &bqf_filters_mem_right[j]);
out[i] = (int16_t) norm_fix3_28_to_s16sample(x_f16);
} }
out[i] = (int32_t) norm_fix3_28_to_s16sample(x_f16);
} }
// Signal to core 0 that the data has all been transformed // Signal to core 0 that the data has all been transformed
@ -273,9 +270,9 @@ void setup() {
// Same here, pal. Hands off. // Same here, pal. Hands off.
sleep_ms(100); sleep_ms(100);
// Set data format to 16 bit right justified, MSB first // Set data format to 24 bit right justified, MSB first
buf[0] = 67; // register addr buf[0] = 67; // register addr
buf[1] = 0x03; // data buf[1] = 0x02; // data
i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false); i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false);
i2s_write_obj.sck_pin = PCM3060_DAC_SCK_PIN; i2s_write_obj.sck_pin = PCM3060_DAC_SCK_PIN;

View File

@ -6,7 +6,6 @@ set(CMAKE_CXX_STANDARD 17)
add_executable(filter_test add_executable(filter_test
filter_test.c filter_test.c
../code/fix16.c
../code/bqf.c ../code/bqf.c
../code/configuration_manager.c ../code/configuration_manager.c
) )

View File

@ -17,7 +17,7 @@ Run `filter_test` to process the PCM samples. The `filter_test` program takes tw
You can listen to the PCM files using ffplay (which is usually included with ffmpeg): You can listen to the PCM files using ffplay (which is usually included with ffmpeg):
``` ```
ffplay -f s16le -ar 48000 -ac 2 output.pcm ffplay -f s24le -ar 48000 -ac 2 output.pcm
``` ```
If there are no obvious problems, go ahead and flash your firmware. If there are no obvious problems, go ahead and flash your firmware.

View File

@ -32,7 +32,7 @@ int main(int argc, char* argv[])
// we dont need to store the whole input and output files in memory. // we dont need to store the whole input and output files in memory.
int samples = input_size / 2; int samples = input_size / 2;
int16_t *in = (int16_t *) calloc(samples, sizeof(int16_t)); int16_t *in = (int16_t *) calloc(samples, sizeof(int16_t));
int16_t *out = (int16_t *) calloc(samples, sizeof(int16_t)); int32_t *out = (int32_t *) calloc(samples, sizeof(int32_t));
fread(in, samples, sizeof(int16_t), input); fread(in, samples, sizeof(int16_t), input);
fclose(input); fclose(input);
@ -54,31 +54,39 @@ int main(int argc, char* argv[])
out[i] = in[i]; out[i] = in[i];
} }
for (int j = 0; j < filter_stages; j++) const fix3_28_t preamp = fix3_28_from_flt(0.92f);
{
for (int i = 0; i < samples; i ++)
{
// Left channel filter
fix16_t x_f16 = fix16_from_s16sample((int16_t) out[i]);
for (int i = 0; i < samples; i ++)
{
// Left channel filter
fix3_28_t x_f16 = fix16_mul(norm_fix3_28_from_s16sample((int16_t) out[i]), preamp);
for (int j = 0; j < filter_stages; j++)
{
x_f16 = bqf_transform(x_f16, &bqf_filters_left[j], x_f16 = bqf_transform(x_f16, &bqf_filters_left[j],
&bqf_filters_mem_left[j]); &bqf_filters_mem_left[j]);
}
out[i] = (int32_t) fix16_to_s16sample(x_f16); out[i] = (int32_t) norm_fix3_28_to_s16sample(x_f16);
// Right channel filter // Right channel filter
i++; i++;
x_f16 = fix16_from_s16sample((int16_t) out[i]); x_f16 = fix16_mul(norm_fix3_28_from_s16sample((int16_t) out[i]), preamp);
for (int j = 0; j < filter_stages; j++)
{
x_f16 = bqf_transform(x_f16, &bqf_filters_right[j], x_f16 = bqf_transform(x_f16, &bqf_filters_right[j],
&bqf_filters_mem_right[j]); &bqf_filters_mem_right[j]);
out[i] = (int16_t) fix16_to_s16sample(x_f16);
} }
out[i] = (int32_t) norm_fix3_28_to_s16sample(x_f16);
//printf("%08x\n", out[i]);
} }
// Write out the processed audio. // Write out the processed audio.
fwrite(out, samples, sizeof(int16_t), output); for (int i=0; i<samples; i++) {
fwrite(&out[i], 3, sizeof(int8_t), output);
}
fclose(output); fclose(output);
free(in); free(in);