forked from thesofproject/sof
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Math: Add functions to convert decibels to linear
This patch adds two functions: db2lin_fixed() and exp_fixed(). The first can convert fractional format dB values to linear within range -100..+66 dB. The latter is a more generic fractional e^x function. See the code comments for used Qx.y fractional formats and limitations. The implementation for exp(x) core function is based on first 11 terms of Taylor series approximation for |x| < 2. The larger values of x are computed with help of rule exp(x) = exp(x/2) * exp(x/2). It achieves sufficient accuracy for volume control type of applications. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
- Loading branch information
Showing
3 changed files
with
141 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
|
||
/* SPDX-License-Identifier: BSD-3-Clause | ||
* | ||
* Copyright(c) 2019 Intel Corporation. All rights reserved. | ||
* | ||
* Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> | ||
*/ | ||
|
||
#ifndef DECIBELS_H | ||
#define DECIBELS_H | ||
|
||
#include <stdint.h> | ||
|
||
#define EXP_FIXED_INPUT_QY 27 | ||
#define EXP_FIXED_OUTPUT_QY 20 | ||
#define DB2LIN_FIXED_INPUT_QY 24 | ||
#define DB2LIN_FIXED_OUTPUT_QY 20 | ||
|
||
int32_t exp_fixed(int32_t x); /* Input is Q5.27, output is Q12.20 */ | ||
int32_t db2lin_fixed(int32_t x); /* Input is Q8.24, output is Q12.20 */ | ||
|
||
#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 |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
|
||
add_local_sources(sof numbers.c trig.c) | ||
add_local_sources(sof numbers.c trig.c decibels.c) |
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,118 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
// | ||
// Copyright(c) 2019 Intel Corporation. All rights reserved. | ||
// | ||
// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> | ||
|
||
#include <sof/audio/format.h> | ||
#include <sof/math/decibels.h> | ||
|
||
#include <stdint.h> | ||
|
||
#define ONE_Q20 Q_CONVERT_FLOAT(1.0, 20) /* Use Q12.20 */ | ||
#define ONE_Q23 Q_CONVERT_FLOAT(1.0, 23) /* Use Q9.23 */ | ||
#define TWO_Q27 Q_CONVERT_FLOAT(2.0, 27) /* Use Q5.27 */ | ||
#define MINUS_TWO_Q27 Q_CONVERT_FLOAT(-2.0, 27) /* Use Q5.27 */ | ||
#define LOG10_DIV20_Q27 Q_CONVERT_FLOAT(0.1151292546, 27) /* Use Q5.27 */ | ||
|
||
/* Exponent function for small values of x. This function calculates | ||
* fairly accurately exponent for x in range -2.0 .. +2.0. The iteration | ||
* uses first 11 terms of Taylor series approximation for exponent | ||
* function. With the current scaling the numerator just remains under | ||
* 64 bits with the 11 terms. | ||
* | ||
* See https://en.wikipedia.org/wiki/Exponential_function#Computation | ||
* | ||
* The input is Q3.29 | ||
* The output is Q9.23 | ||
*/ | ||
|
||
static int32_t exp_small_fixed(int32_t x) | ||
{ | ||
int64_t p; | ||
int64_t num = Q_SHIFT_RND(x, 29, 23); | ||
int32_t y0 = (int32_t)num; | ||
int32_t den = 1; | ||
int32_t inc; | ||
int k; | ||
|
||
/* Numerator is x^k, denominator is k! */ | ||
for (k = 2; k < 12; k++) { | ||
p = num * x; /* Q9.23 x Q3.29 -> Q12.52 */ | ||
num = Q_SHIFT_RND(p, 52, 23); | ||
den = den * k; | ||
inc = (int32_t)(num / den); | ||
y0 += inc; | ||
} | ||
|
||
return y0 + ONE_Q23; | ||
} | ||
|
||
/* Decibels to linear conversion: The function uses exp() to calculate | ||
* the linear value. The argument is multiplied by log(10)/20 to | ||
* calculate equivalent of 10^(db/20). | ||
* | ||
* The error in conversion is less than 0.1 dB for -89..+66 dB range. Do not | ||
* use the code for argument less than -100 dB. The code simply returns zero | ||
* as linear value for such very small value. | ||
* | ||
* Input is Q8.24 (max 128.0) | ||
* output is Q12.20 (max 2048.0) | ||
*/ | ||
|
||
int32_t db2lin_fixed(int32_t db) | ||
{ | ||
int32_t arg; | ||
|
||
if (db < Q_CONVERT_FLOAT(-100.0, 24)) | ||
return 0; | ||
|
||
/* Q8.24 x Q5.27, result needs to be Q5.27 */ | ||
arg = (int32_t)Q_MULTSR_32X32((int64_t)db, LOG10_DIV20_Q27, 24, 27, 27); | ||
return exp_fixed(arg); | ||
} | ||
|
||
/* Fixed point exponent function for approximate range -11.5 .. 7.6 | ||
* that corresponds to decibels range -100 .. +66 dB. | ||
* | ||
* The functions uses rule exp(x) = exp(x/2) * exp(x/2) to reduce | ||
* the input argument for private small value exp() function that is | ||
* accurate with input range -2.0 .. +2.0. The number of possible | ||
* divisions by 2 is computed into variable n. The returned value is | ||
* exp()^(2^n). | ||
* | ||
* Input is Q5.27, -16.0 .. +16.0, but note the input range limitation | ||
* Output is Q12.20, 0.0 .. +2048.0 | ||
*/ | ||
|
||
int32_t exp_fixed(int32_t x) | ||
{ | ||
int32_t xs; | ||
int32_t y; | ||
int32_t y0; | ||
int i; | ||
int n = 0; | ||
|
||
if (x < Q_CONVERT_FLOAT(-11.5, 27)) | ||
return 0; | ||
|
||
if (x > Q_CONVERT_FLOAT(7.6245, 27)) | ||
return INT32_MAX; | ||
|
||
/* x is Q5.27 */ | ||
xs = x; | ||
while (xs >= TWO_Q27 || xs <= MINUS_TWO_Q27) { | ||
xs >>= 1; | ||
n++; | ||
} | ||
|
||
/* exp_small_fixed() input is Q3.29, while x1 is Q5.27 | ||
* exp_small_fixed() output is Q9.23, while y0 is Q12.20 | ||
*/ | ||
y0 = Q_SHIFT_RND(exp_small_fixed(Q_SHIFT_LEFT(xs, 27, 29)), 23, 20); | ||
y = ONE_Q20; | ||
for (i = 0; i < (1 << n); i++) | ||
y = (int32_t)Q_MULTSR_32X32((int64_t)y, y0, 20, 20, 20); | ||
|
||
return y; | ||
} |