ref: 074940656f422d0bc21e70be87de06d37a1da21e
dir: /src/dither.c/
/* Effect: dither/noise-shape Copyright (c) 2008 robs@users.sourceforge.net
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "sox_i.h"
#include "getopt.h"
#define N 9
#define CONVOLVE _ _ _ _ _ _ _ _ _
static double coefs[][N] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{2.033, -2.165, 1.959, -1.590, .6149, 0, 0, 0, 0},
{2.412, -3.370, 3.937, -4.174, 3.353, -2.205, 1.281, -.569, .0847},
{1.662, -1.263, .4827, -.2913, .1268, -.1124, .03252, -.01265, -.03524},
{2.847, -4.685, 6.214, -7.184, 6.639, -5.032, 3.263, -1.632, .4191},
};
#define PREC effp->out_signal.precision
#define RAND_CONST 140359821
static int32_t rand_ = ~RAND_CONST;
#define RAND_ (rand_ = rand_ * RAND_CONST + 1)
typedef enum {Pdf_rectangular, Pdf_triangular, Pdf_gaussian} pdf_type_t;
static lsx_enum_item const pdf_types[] = {
LSX_ENUM_ITEM(Pdf_,rectangular)
LSX_ENUM_ITEM(Pdf_,triangular)
LSX_ENUM_ITEM(Pdf_,gaussian)
{0, 0}};
typedef enum {Shape_none, Shape_lipshitz, Shape_f_weighted,
Shape_modified_e_weighted, Shape_improved_e_weighted} filter_type_t;
static lsx_enum_item const filter_types[] = {
LSX_ENUM_ITEM(Shape_,none)
LSX_ENUM_ITEM(Shape_,lipshitz)
{"f-weighted", Shape_f_weighted},
{"modified-e-weighted", Shape_modified_e_weighted},
{"improved-e-weighted", Shape_improved_e_weighted},
{0, 0}};
typedef struct {
pdf_type_t pdf;
filter_type_t filter;
double am0, am1, depth;
double previous_errors[N * 2];
size_t pos;
} priv_t;
static int getopts(sox_effect_t * effp, int argc, char * * argv)
{
priv_t * p = (priv_t *)effp->priv;
int c;
p->pdf = Pdf_triangular;
while ((c = getopt(argc, argv, "+rtsf:")) != -1) switch (c) {
case 'r': p->pdf = Pdf_rectangular; break;
case 't': p->pdf = Pdf_triangular ; break;
case 's': p->filter = Shape_improved_e_weighted; break;
case 'f': p->filter = lsx_enum_option(c, filter_types); break;
default: lsx_fail("invalid option `-%c'", optopt); return lsx_usage(effp);
}
argc -= optind, argv += optind;
p->depth = 1;
do {NUMERIC_PARAMETER(depth, .5, 4)} while (0);
return argc? lsx_usage(effp) : SOX_SUCCESS;
}
static int start(sox_effect_t * effp)
{
priv_t * p = (priv_t *)effp->priv;
if (PREC >= 24)
return SOX_EFF_NULL; /* Dithering not needed at this resolution */
if (p->filter &&
(effp->in_signal.rate < 44100 || effp->in_signal.rate > 48000)) {
lsx_fail("noise shaping not supported with this sample rate");
return SOX_EOF;
}
p->am1 = p->depth / (1 << PREC);
p->am0 = (p->pdf == Pdf_triangular) * p->am1;
lsx_report("pdf=%s filter=%s depth=%g", lsx_find_enum_value(p->pdf, pdf_types)->text, lsx_find_enum_value(p->filter, filter_types)->text, p->depth);
return SOX_SUCCESS;
}
static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
sox_sample_t * obuf, size_t * isamp, size_t * osamp)
{
priv_t * p = (priv_t *)effp->priv;
size_t len = *isamp = *osamp = min(*isamp, *osamp);
int dummy = 0;
if (p->filter) while (len--) {
#define _ d += coefs[p->filter][j] * p->previous_errors[p->pos + j], ++j;
double r = p->am0 * RAND_ + p->am1 * RAND_;
double error, d = *ibuf++;
int j = 0;
CONVOLVE
*obuf = SOX_ROUND_CLIP_COUNT(d + r, dummy);
error = d - ((((*obuf++^(1<<31))+(1<<(31-PREC)))&(-1<<(32-PREC)))^(1<<31));
p->pos = p->pos? p->pos - 1 : p->pos - 1 + N;
p->previous_errors[p->pos + N] = p->previous_errors[p->pos] = error;
}
else while (len--) {
double d = *ibuf++ + p->am0 * RAND_ + p->am1 * RAND_;
*obuf++ = SOX_ROUND_CLIP_COUNT(d, dummy);
}
return SOX_SUCCESS;
}
sox_effect_handler_t const * sox_dither_effect_fn(void)
{
static sox_effect_handler_t handler = {
"dither", "[-r|-t] [-s] [depth]",
SOX_EFF_GETOPT | SOX_EFF_PREC, getopts, start, flow, 0, 0, 0, sizeof(priv_t)
};
return &handler;
}