ref: 8b4d3fcc3e82eaed55c00038ff7bf718868e13aa
dir: /src/dither.c/
/* Effect: dither/noise-shape Copyright (c) 2008-9 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 */ #ifdef NDEBUG /* Enable assert always. */ #undef NDEBUG /* Must undef above assert.h or other that might include it. */ #endif #include "sox_i.h" #include "getopt.h" #include <assert.h> #undef RANQD1 #define RANQD1 ranqd1(p->ranqd1) typedef enum { /* Collection of various filters from the net */ Shape_none, Shape_lipshitz, Shape_f_weighted, Shape_modified_e_weighted, Shape_improved_e_weighted, Shape_gesemann, Shape_shibata, Shape_low_shibata, Shape_high_shibata } filter_name_t; static lsx_enum_item const filter_names[] = { 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}, LSX_ENUM_ITEM(Shape_,gesemann) LSX_ENUM_ITEM(Shape_,shibata) {"low-shibata", Shape_low_shibata}, {"high-shibata", Shape_high_shibata}, {0, 0}}; typedef struct { sox_rate_t rate; enum {fir, iir} type; size_t len; int gain_cB; /* Chosen so clips are few if any, but not guaranteed none. */ double const * coefs; filter_name_t name; } filter_t; static double const lip44[] = {2.033, -2.165, 1.959, -1.590, .6149}; static double const fwe44[] = { 2.412, -3.370, 3.937, -4.174, 3.353, -2.205, 1.281, -.569, .0847}; static double const mew44[] = { 1.662, -1.263, .4827, -.2913, .1268, -.1124, .03252, -.01265, -.03524}; static double const iew44[] = { 2.847, -4.685, 6.214, -7.184, 6.639, -5.032, 3.263, -1.632, .4191}; static double const ges44[] = { 2.2061, -.4706, -.2534, -.6214, 1.0587, .0676, -.6054, -.2738}; static double const ges48[] = { 2.2374, -.7339, -.1251, -.6033, .903, .0116, -.5853, -.2571}; static double const shi48[] = { 2.8720729351043701172, -5.0413231849670410156, 6.2442994117736816406, -5.8483986854553222656, 3.7067542076110839844, -1.0495119094848632812, -1.1830236911773681641, 2.1126792430877685547, -1.9094531536102294922, 0.99913084506988525391, -0.17090806365013122559, -0.32615602016448974609, 0.39127644896507263184, -0.26876461505889892578, 0.097676105797290802002, -0.023473845794796943665, }; static double const shi44[] = { 2.6773197650909423828, -4.8308925628662109375, 6.570110321044921875, -7.4572014808654785156, 6.7263274192810058594, -4.8481650352478027344, 2.0412089824676513672, 0.7006359100341796875, -2.9537565708160400391, 4.0800385475158691406, -4.1845216751098632812, 3.3311812877655029297, -2.1179926395416259766, 0.879302978515625, -0.031759146600961685181, -0.42382788658142089844, 0.47882103919982910156, -0.35490813851356506348, 0.17496839165687561035, -0.060908168554306030273, }; static double const shi38[] = { 1.6335992813110351562, -2.2615492343902587891, 2.4077029228210449219, -2.6341717243194580078, 2.1440362930297851562, -1.8153258562088012695, 1.0816224813461303711, -0.70302653312683105469, 0.15991993248462677002, 0.041549518704414367676, -0.29416576027870178223, 0.2518316805362701416, -0.27766478061676025391, 0.15785403549671173096, -0.10165894031524658203, 0.016833892092108726501, }; static double const shl48[] = { 2.3925774097442626953, -3.4350297451019287109, 3.1853709220886230469, -1.8117271661758422852, -0.20124770700931549072, 1.4759907722473144531, -1.7210904359817504883, 0.97746700048446655273, -0.13790138065814971924, -0.38185903429985046387, 0.27421241998672485352, 0.066584214568138122559, -0.35223302245140075684, 0.37672343850135803223, -0.23964276909828186035, 0.068674825131893157959, }; static double const shl44[] = { 2.0833916664123535156, -3.0418450832366943359, 3.2047898769378662109, -2.7571926116943359375, 1.4978630542755126953, -0.3427594602108001709, -0.71733748912811279297, 1.0737057924270629883, -1.0225815773010253906, 0.56649994850158691406, -0.20968692004680633545, -0.065378531813621520996, 0.10322438180446624756, -0.067442022264003753662, -0.00495197344571352005, }; static double const shh44[] = { 3.0259189605712890625, -6.0268716812133789062, 9.195003509521484375, -11.824929237365722656, 12.767142295837402344, -11.917946815490722656, 9.1739168167114257812, -5.3712320327758789062, 1.1393624544143676758, 2.4484779834747314453, -4.9719839096069335938, 6.0392003059387207031, -5.9359521865844726562, 4.903278350830078125, -3.5527443885803222656, 2.1909697055816650391, -1.1672389507293701172, 0.4903914332389831543, -0.16519790887832641602, 0.023217858746647834778, }; static const filter_t filters[] = { {44100, fir, 5, 210, lip44, Shape_lipshitz}, {46000, fir, 9, 271, fwe44, Shape_f_weighted}, {46000, fir, 9, 159, mew44, Shape_modified_e_weighted}, {46000, fir, 9, 320, iew44, Shape_improved_e_weighted}, {48000, iir, 4, 220, ges48, Shape_gesemann}, {44100, iir, 4, 229, ges44, Shape_gesemann}, {48000, fir, 16, 300, shi48, Shape_shibata}, {44100, fir, 20, 330, shi44, Shape_shibata}, {37800, fir, 16, 240, shi38, Shape_shibata}, {48000, fir, 16, 249, shl48, Shape_low_shibata}, {44100, fir, 15, 247, shl44, Shape_low_shibata}, {44100, fir, 20, 382, shh44, Shape_high_shibata}, { 0, fir, 0, 0, NULL, Shape_none}, }; #define MAX_N 20 typedef struct { filter_name_t filter_name; sox_bool auto_detect, alt_tpdf; double dummy; double previous_errors[MAX_N * 2]; double previous_outputs[MAX_N * 2]; size_t pos, prec, num_output; int32_t history, ranqd1, r; double const * coefs; sox_bool dither_off; int (*flow)(sox_effect_t *, const sox_sample_t *, sox_sample_t *, size_t *, size_t *); } priv_t; #define CONVOLVE _ _ _ _ #define NAME flow_iir_4 #define IIR #define N 4 #include "dither.h" #undef IIR #define CONVOLVE _ _ _ _ _ #define NAME flow_fir_5 #define N 5 #include "dither.h" #define CONVOLVE _ _ _ _ _ _ _ _ _ #define NAME flow_fir_9 #define N 9 #include "dither.h" #define CONVOLVE _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ #define NAME flow_fir_15 #define N 15 #include "dither.h" #define CONVOLVE _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ #define NAME flow_fir_16 #define N 16 #include "dither.h" #define CONVOLVE _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ #define NAME flow_fir_20 #define N 20 #include "dither.h" static int flow_no_shape(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); while (len--) { if (!p->auto_detect || (p->history = ((p->history << 1) + !!(*ibuf & (-1u >> p->prec))))) { int32_t r = RANQD1 >> p->prec; double d = ((double)*ibuf++ + r + (p->alt_tpdf? -p->r : (RANQD1 >> p->prec))) / (1 << (32 - p->prec)); int i = d < 0? d - .5 : d + .5; p->r = r; if (i <= (-1 << (p->prec-1))) ++effp->clips, *obuf = SOX_SAMPLE_MIN; else if (i > (int)SOX_INT_MAX(p->prec)) ++effp->clips, *obuf = SOX_INT_MAX(p->prec) << (32 - p->prec); else *obuf = i << (32 - p->prec); ++obuf; if (p->dither_off) lsx_debug("flow %u: on @ %u", (unsigned)effp->flow, (unsigned)p->num_output); p->dither_off = sox_false; } else { *obuf++ = *ibuf++; if (!p->dither_off) lsx_debug("flow %u: off @ %u", (unsigned)effp->flow, (unsigned)p->num_output); p->dither_off = sox_true; } ++p->num_output; } return SOX_SUCCESS; } static int getopts(sox_effect_t * effp, int argc, char * * argv) { priv_t * p = (priv_t *)effp->priv; int c; while ((c = getopt(argc, argv, "+aSsf:""rt")) != -1) switch (c) { case 'a': p->auto_detect = sox_true; break; case 'S': p->alt_tpdf = sox_true; break; case 'r': case 't': break; /* No longer in use */ case 's': p->filter_name = Shape_shibata; break; case 'f': p->filter_name = lsx_enum_option(c, filter_names); if (p->filter_name == INT_MAX) return SOX_EOF; break; default: lsx_fail("invalid option `-%c'", optopt); return lsx_usage(effp); } argc -= optind, argv += optind; do {NUMERIC_PARAMETER(dummy, 0.5, 1)} while (0); /* No longer in use */ return argc? lsx_usage(effp) : SOX_SUCCESS; } static int start(sox_effect_t * effp) { priv_t * p = (priv_t *)effp->priv; double mult = 1; /* Amount the noise shaping multiplies up the TPDF (+/-1) */ p->prec = effp->out_signal.precision; if (effp->in_signal.precision <= p->prec || p->prec > 24) return SOX_EFF_NULL; /* Dithering not needed at this resolution */ effp->out_signal.precision = effp->in_signal.precision; p->flow = flow_no_shape; if (p->filter_name) { filter_t const * f; for (f = filters; f->len && (f->name != p->filter_name || fabs(effp->in_signal.rate - f->rate) / f->rate > .05); ++f); /* 5% leeway on frequency */ if (!f->len) { p->alt_tpdf |= effp->in_signal.rate >= 22050; lsx_warn("no `%s' filter is available for rate %g; using %s TPDF", lsx_find_enum_value(p->filter_name, filter_names)->text, effp->in_signal.rate, p->alt_tpdf? "sloped" : "plain"); } else { assert(f->len <= MAX_N); if (f->type == fir) switch(f->len) { case 5: p->flow = flow_fir_5 ; break; case 9: p->flow = flow_fir_9 ; break; case 15: p->flow = flow_fir_15; break; case 16: p->flow = flow_fir_16; break; case 20: p->flow = flow_fir_20; break; default: assert(sox_false); } else switch(f->len) { case 4: p->flow = flow_iir_4 ; break; default: assert(sox_false); } p->coefs = f->coefs; mult = dB_to_linear(f->gain_cB / 10); } } p->ranqd1 = ranqd1(sox_globals.ranqd1) + effp->flow; if (effp->in_signal.mult) /* (Takes account of ostart mult (sox.c). */ *effp->in_signal.mult *= (SOX_SAMPLE_MAX - (1 << (31 - p->prec)) * (2 * mult + 1)) / (SOX_SAMPLE_MAX - (1 << (31 - p->prec))); 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; return p->flow(effp, ibuf, obuf, isamp, osamp); } sox_effect_handler_t const * lsx_dither_effect_fn(void) { static sox_effect_handler_t handler = { "dither", "[-a] [-S|-s|-f filter]" "\n (none) Use TPDF" "\n -a Automatically turn on & off dithering as needed (use with caution!)" "\n -S Use sloped TPDF (without noise shaping)" "\n -s Shape noise (with shibata filter)" "\n -f name Set shaping filter to one of: lipshitz, f-weighted," "\n modified-e-weighted, improved-e-weighted, gesemann," "\n shibata, low-shibata, high-shibata.", SOX_EFF_PREC, getopts, start, flow, 0, 0, 0, sizeof(priv_t) }; return &handler; }