-
Notifications
You must be signed in to change notification settings - Fork 448
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Precalculate common PWMAudio dividers, avoid noise
Calculating the DMA clock divider for PWMAudio can take a very long time because there are 65K floating point divisions required. But most audio will be one of a dozen sample rates, so it is possible to precalculate the rates on the host and use a small lookup table to avoid any math at all. Removes occasional scratching in PWMAudio when the BackgroundAudio library changes sample rates.
- Loading branch information
1 parent
5fb5e16
commit 79596ec
Showing
3 changed files
with
132 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Generated by tools/makepacer.cpp, do not edit | ||
typedef struct { | ||
uint32_t freq; | ||
uint16_t n; | ||
uint16_t d; | ||
} PWMPacerPrecalc; | ||
#if F_CPU == 50000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 6250, 1}, {11025, 31746, 7}, {16000, 3125, 1}, {22050, 15873, 7}, {32000, 3125, 2}, {44100, 53288, 47}, {48000, 3125, 3}, {88200, 42517, 75}, {96000, 3125, 6}, {176400, 55839, 197}, {192000, 3125, 12}}; | ||
#elif F_CPU == 100000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 12500, 1}, {11025, 63492, 7}, {16000, 6250, 1}, {22050, 31746, 7}, {32000, 3125, 1}, {44100, 15873, 7}, {48000, 6250, 3}, {88200, 53288, 47}, {96000, 3125, 3}, {176400, 42517, 75}, {192000, 3125, 6}}; | ||
#elif F_CPU == 120000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 15000, 1}, {11025, 32653, 3}, {16000, 7500, 1}, {22050, 59864, 11}, {32000, 3750, 1}, {44100, 62585, 23}, {48000, 2500, 1}, {88200, 62585, 46}, {96000, 1250, 1}, {176400, 62585, 92}, {192000, 625, 1}}; | ||
#elif F_CPU == 125000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 15625, 1}, {11025, 56689, 5}, {16000, 15625, 2}, {22050, 62358, 11}, {32000, 15625, 4}, {44100, 42517, 15}, {48000, 15625, 6}, {88200, 42517, 30}, {96000, 15625, 12}, {176400, 42517, 60}, {192000, 15625, 24}}; | ||
#elif F_CPU == 128000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 16000, 1}, {11025, 11610, 1}, {16000, 8000, 1}, {22050, 5805, 1}, {32000, 4000, 1}, {44100, 5805, 2}, {48000, 8000, 3}, {88200, 5805, 4}, {96000, 4000, 3}, {176400, 61678, 85}, {192000, 2000, 3}}; | ||
#elif F_CPU == 133000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 16625, 1}, {11025, 24127, 2}, {16000, 16625, 2}, {22050, 24127, 4}, {32000, 16625, 4}, {44100, 24127, 8}, {48000, 16625, 6}, {88200, 24127, 16}, {96000, 16625, 12}, {176400, 47500, 63}, {192000, 16625, 24}}; | ||
#elif F_CPU == 150000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 18750, 1}, {11025, 27211, 2}, {16000, 9375, 1}, {22050, 47619, 7}, {32000, 9375, 2}, {44100, 37415, 11}, {48000, 3125, 1}, {88200, 42517, 25}, {96000, 3125, 2}, {176400, 42517, 50}, {192000, 3125, 4}}; | ||
#elif F_CPU == 175000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 21875, 1}, {11025, 15873, 1}, {16000, 21875, 2}, {22050, 15873, 2}, {32000, 21875, 4}, {44100, 15873, 4}, {48000, 21875, 6}, {88200, 15873, 8}, {96000, 21875, 12}, {176400, 62500, 63}, {192000, 21875, 24}}; | ||
#elif F_CPU == 200000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 25000, 1}, {11025, 54422, 3}, {16000, 12500, 1}, {22050, 63492, 7}, {32000, 6250, 1}, {44100, 31746, 7}, {48000, 12500, 3}, {88200, 15873, 7}, {96000, 6250, 3}, {176400, 53288, 47}, {192000, 3125, 3}}; | ||
#elif F_CPU == 225000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 28125, 1}, {11025, 20408, 1}, {16000, 28125, 2}, {22050, 10204, 1}, {32000, 28125, 4}, {44100, 5102, 1}, {48000, 9375, 2}, {88200, 63776, 25}, {96000, 9375, 4}, {176400, 62500, 49}, {192000, 9375, 8}}; | ||
#elif F_CPU == 240000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 30000, 1}, {11025, 65306, 3}, {16000, 15000, 1}, {22050, 32653, 3}, {32000, 7500, 1}, {44100, 59864, 11}, {48000, 5000, 1}, {88200, 62585, 23}, {96000, 2500, 1}, {176400, 62585, 46}, {192000, 1250, 1}}; | ||
#elif F_CPU == 250000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 31250, 1}, {11025, 45351, 2}, {16000, 15625, 1}, {22050, 56689, 5}, {32000, 15625, 2}, {44100, 62358, 11}, {48000, 15625, 3}, {88200, 42517, 15}, {96000, 15625, 6}, {176400, 42517, 30}, {192000, 15625, 12}}; | ||
#elif F_CPU == 275000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 34375, 1}, {11025, 49887, 2}, {16000, 34375, 2}, {22050, 37415, 3}, {32000, 34375, 4}, {44100, 37415, 6}, {48000, 34375, 6}, {88200, 37415, 12}, {96000, 34375, 12}, {176400, 35856, 23}, {192000, 34375, 24}}; | ||
#elif F_CPU == 300000000 | ||
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 37500, 1}, {11025, 27211, 1}, {16000, 18750, 1}, {22050, 27211, 2}, {32000, 9375, 1}, {44100, 47619, 7}, {48000, 6250, 1}, {88200, 37415, 11}, {96000, 3125, 1}, {176400, 42517, 25}, {192000, 3125, 2}}; | ||
#else | ||
const PWMPacerPrecalc __PWMAudio_pacer[] = {{1, 1, 1}}; // Invalid, should never match | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// Generates a header with precalculated best dividers for PWMAudio at | ||
// standard clock frequencies and audio rates. | ||
// | ||
|
||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <math.h> | ||
|
||
void find_pacer_fraction(int F_CPU, int target, uint16_t *numerator, uint16_t *denominator) { | ||
const uint16_t max = 0xFFFF; | ||
|
||
/*Cache last results so we dont have to recalculate*/ | ||
static int last_target; | ||
static uint16_t bestNum; | ||
static uint16_t bestDenom; | ||
/*Check if we can load the previous values*/ | ||
if (target == last_target) { | ||
*numerator = bestNum; | ||
*denominator = bestDenom; | ||
return; | ||
} | ||
|
||
float targetRatio = (float)F_CPU / target; | ||
float lowestError = 10000000; | ||
|
||
for (uint16_t denom = 1; denom < max; denom++) { | ||
uint16_t num = (int)((targetRatio * denom) + 0.5f); /*Calculate numerator, rounding to nearest integer*/ | ||
|
||
/*Check if numerator is within bounds*/ | ||
if (num > 0 && num < max) { | ||
float actualRatio = (float)num / denom; | ||
float error = fabsf(actualRatio - targetRatio); | ||
|
||
if (error < lowestError) { | ||
bestNum = num; | ||
bestDenom = denom; | ||
lowestError = error; | ||
if (error == 0) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
last_target = target; | ||
*numerator = bestNum; | ||
*denominator = bestDenom; | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
(void) argc; | ||
(void) argv; | ||
int M = 1000000; | ||
int fsys[] = {50*M, 100*M, 120*M, 125*M, 128*M, 133*M, 150*M, 175*M, 200*M, 225*M, 240*M, 250*M, 275*M, 300*M}; | ||
int freq[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000}; | ||
FILE *f = fopen("../libraries/PWMAudio/src/PWMAudioPrecalc.h", "w"); | ||
fprintf(f, "// Generated by tools/makepacer.cpp, do not edit\n"); | ||
fprintf(f, "typedef struct {\n"); | ||
fprintf(f, " uint32_t freq;\n"); | ||
fprintf(f, " uint16_t n;\n"); | ||
fprintf(f, " uint16_t d;\n"); | ||
fprintf(f, "} PWMPacerPrecalc;\n"); | ||
for (int i = 0; i < sizeof(fsys)/sizeof(fsys[0]); i++) { | ||
fprintf(f, "#%s F_CPU == %d\n", i == 0 ? "if" : "elif", fsys[i]); | ||
fprintf(f, "static const PWMPacerPrecalc __PWMAudio_pacer[] = {"); | ||
for (int j = 0; j < sizeof(freq)/sizeof(freq[0]); j++) { | ||
uint16_t n, d; | ||
find_pacer_fraction(fsys[i], freq[j], &n, &d); | ||
fprintf(f, "{%d, %d, %d}", freq[j], n, d); | ||
if (j < sizeof(freq)/sizeof(freq[0]) - 1) { | ||
fprintf(f, ", "); | ||
} | ||
} | ||
fprintf(f, "};\n"); | ||
} | ||
fprintf(f, "#else\n"); | ||
fprintf(f, "const PWMPacerPrecalc __PWMAudio_pacer[] = {{1, 1, 1}}; // Invalid, should never match\n"); | ||
fprintf(f, "#endif\n"); | ||
fclose(f); | ||
} | ||
|