Fixed point math tweaks. Treat samples as normalized (-1..1), this doesnt change much, except the filter coefficients get way more bits of precision.
This commit is contained in:
parent
55ee01c349
commit
66943e9c84
|
@ -22,9 +22,9 @@
|
|||
#include "configuration_types.h"
|
||||
#include "bqf.h"
|
||||
#include "run.h"
|
||||
#ifndef TEST_TARGET
|
||||
#include "version.h"
|
||||
#include "pico_base/pico/version.h"
|
||||
#ifndef TEST_TARGET
|
||||
#include "pico/multicore.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/usb_device.h"
|
||||
|
@ -276,6 +276,7 @@ bool apply_configuration(tlv_header *config) {
|
|||
case FILTER_CONFIGURATION:
|
||||
apply_filter_configuration((filter_configuration_tlv*) tlv);
|
||||
break;
|
||||
#ifndef TEST_TARGET
|
||||
case PREPROCESSING_CONFIGURATION: {
|
||||
preprocessing_configuration_tlv* preprocessing_config = (preprocessing_configuration_tlv*) tlv;
|
||||
preprocessing.preamp = fix16_from_dbl(1.0 + preprocessing_config->preamp);
|
||||
|
@ -290,6 +291,7 @@ bool apply_configuration(tlv_header *config) {
|
|||
audio_state.de_emphasis = pcm3060_config->de_emphasis;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -25,13 +25,52 @@
|
|||
#include <limits.h>
|
||||
#include "fix16.h"
|
||||
|
||||
fix16_t fix16_from_int(int16_t a) {
|
||||
return a * fix16_one;
|
||||
#ifdef USE_DOUBLE
|
||||
fix16_t fix16_from_s16sample(int16_t a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
int16_t fix16_to_int(fix16_t a) {
|
||||
int16_t fix16_to_s16sample(fix16_t a) {
|
||||
// Handle rounding up front, adding one can cause an overflow/underflow
|
||||
a+=(fix16_one >> 1);
|
||||
if (a < 0) {
|
||||
a -= 0.5;
|
||||
} else {
|
||||
a += 0.5;
|
||||
}
|
||||
|
||||
// Saturate the value if an overflow has occurred
|
||||
if (a < SHRT_MIN) {
|
||||
return SHRT_MIN;
|
||||
}
|
||||
if (a < SHRT_MAX) {
|
||||
return SHRT_MAX;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
fix16_t fix16_from_dbl(double a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
double fix16_to_dbl(fix16_t a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) {
|
||||
return inArg0 * inArg1;
|
||||
}
|
||||
#else
|
||||
fix16_t fix16_from_s16sample(int16_t a) {
|
||||
return a * fix16_one_normalized;
|
||||
}
|
||||
|
||||
int16_t fix16_to_s16sample(fix16_t a) {
|
||||
// Handle rounding up front, adding one can cause an overflow/underflow
|
||||
if (a < 0) {
|
||||
a -= (fix16_one_normalized >> 1);
|
||||
} else {
|
||||
a += (fix16_one_normalized >> 1);
|
||||
}
|
||||
|
||||
// Saturate the value if an overflow has occurred
|
||||
uint32_t upper = (a >> 30);
|
||||
|
@ -59,12 +98,24 @@ double fix16_to_dbl(fix16_t a) {
|
|||
return (double)a / fix16_one;
|
||||
}
|
||||
|
||||
// hic sunt dracones
|
||||
// We work in 64bits then shift the result to get
|
||||
// the bit representing 1 back into the correct position.
|
||||
fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) {
|
||||
int64_t product = (int64_t)inArg0 * inArg1;
|
||||
|
||||
fix16_t result = product >> 15;
|
||||
result += (product & 0x4000) >> 14;
|
||||
|
||||
const int64_t product = (int64_t)inArg0 * inArg1;
|
||||
fix16_t result = product >> 29;
|
||||
// Handle rounding where we are choppping off low order bits
|
||||
// Disabled for now, too much load. We get crackling when adjusting
|
||||
// the volume.
|
||||
#if 0
|
||||
if (product & 0x4000) {
|
||||
if (result >= 0) {
|
||||
result++;
|
||||
}
|
||||
else {
|
||||
result--;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
#endif
|
|
@ -22,16 +22,31 @@
|
|||
#ifndef FIX16_H
|
||||
#define FIX16_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
// During development, it can be useful to run with real double values for reference.
|
||||
//#define USE_DOUBLE
|
||||
#ifdef USE_DOUBLE
|
||||
typedef double fix16_t;
|
||||
static const fix16_t fix16_zero = 0;
|
||||
#else
|
||||
// We normalize all values into the range -1..1 with 1 extra bit for overflows
|
||||
// and one bit for the sign. We allow fixed point values to overflow, but they
|
||||
// are clipped at the point they are written back to a s16sample.
|
||||
//
|
||||
// The reason for normalizing the samples is because the filter coefficients are
|
||||
// small (usually in the range -1..1), by normalizing everything the coefficients
|
||||
// get lots of additional bits of precision.
|
||||
typedef int32_t fix16_t;
|
||||
|
||||
static const fix16_t fix16_overflow = 0x80000000;
|
||||
static const fix16_t fix16_one = 0x00008000;
|
||||
static const fix16_t fix16_one_normalized = 0x00008000;
|
||||
static const fix16_t fix16_one = 0x20000000;
|
||||
static const fix16_t fix16_zero = 0x00000000;
|
||||
#endif
|
||||
|
||||
fix16_t fix16_from_int(int16_t);
|
||||
int16_t fix16_to_int(fix16_t);
|
||||
fix16_t fix16_from_s16sample(int16_t);
|
||||
int16_t fix16_to_s16sample(fix16_t);
|
||||
fix16_t fix16_from_dbl(double);
|
||||
double fix16_to_dbl(fix16_t);
|
||||
|
||||
|
|
|
@ -143,12 +143,12 @@ static void _as_audio_packet(struct usb_endpoint *ep) {
|
|||
for (int j = 0; j < filter_stages; j++) {
|
||||
// Left channel filter
|
||||
for (int i = 0; i < samples; i += 2) {
|
||||
fix16_t x_f16 = fix16_mul(fix16_from_int((int16_t) out[i]), preprocessing.preamp);
|
||||
fix16_t x_f16 = fix16_mul(fix16_from_s16sample((int16_t) out[i]), preprocessing.preamp);
|
||||
|
||||
x_f16 = bqf_transform(x_f16, &bqf_filters_left[j],
|
||||
&bqf_filters_mem_left[j]);
|
||||
|
||||
out[i] = (int32_t) fix16_to_int(x_f16);
|
||||
out[i] = (int32_t) fix16_to_s16sample(x_f16);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,12 +189,12 @@ void core1_entry() {
|
|||
|
||||
for (int j = 0; j < filter_stages; j++) {
|
||||
for (int i = 1; i < samples; i += 2) {
|
||||
fix16_t x_f16 = fix16_mul(fix16_from_int((int16_t) out[i]), preprocessing.preamp);
|
||||
fix16_t x_f16 = fix16_mul(fix16_from_s16sample((int16_t) out[i]), preprocessing.preamp);
|
||||
|
||||
x_f16 = bqf_transform(x_f16, &bqf_filters_right[j],
|
||||
&bqf_filters_mem_right[j]);
|
||||
|
||||
out[i] = (int16_t) fix16_to_int(x_f16);
|
||||
out[i] = (int16_t) fix16_to_s16sample(x_f16);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,21 +59,21 @@ int main(int argc, char* argv[])
|
|||
for (int i = 0; i < samples; i ++)
|
||||
{
|
||||
// Left channel filter
|
||||
fix16_t x_f16 = fix16_from_int((int16_t) out[i]);
|
||||
fix16_t x_f16 = fix16_from_s16sample((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);
|
||||
out[i] = (int32_t) fix16_to_s16sample(x_f16);
|
||||
|
||||
// Right channel filter
|
||||
i++;
|
||||
x_f16 = fix16_from_int((int16_t) out[i]);
|
||||
x_f16 = fix16_from_s16sample((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);
|
||||
out[i] = (int16_t) fix16_to_s16sample(x_f16);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue