1 /* libSoX earwax - makes listening to headphones easier     November 9, 2000
2  *
3  * Copyright (c) 2000 Edward Beingessner And Sundry Contributors.
4  * This source code is freely redistributable and may be used for any purpose.
5  * This copyright notice must be maintained.  Edward Beingessner And Sundry
6  * Contributors are not responsible for the consequences of using this
7  * software.
8  *
9  * This effect takes a 44.1kHz stereo (CD format) signal that is meant to be
10  * listened to on headphones, and adds audio cues to move the soundstage from
11  * inside your head (standard for headphones) to outside and in front of the
12  * listener (standard for speakers). This makes the sound much easier to listen
13  * to on headphones.
14  */
15 
16 #include "sox_i.h"
17 #include <string.h>
18 
19 static const sox_sample_t filt[32 * 2] = {
20 /* 30°  330° */
21     4,   -6,     /* 32 tap stereo FIR filter. */
22     4,  -11,     /* One side filters as if the */
23    -1,   -5,     /* signal was from 30 degrees */
24     3,    3,     /* from the ear, the other as */
25    -2,    5,     /* if 330 degrees. */
26    -5,    0,
27     9,    1,
28     6,    3,     /*                         Input                         */
29    -4,   -1,     /*                   Left         Right                  */
30    -5,   -3,     /*                __________   __________                */
31    -2,   -5,     /*               |          | |          |               */
32    -7,    1,     /*           .---|  Hh,0(f) | |  Hh,0(f) |---.           */
33     6,   -7,     /*          /    |__________| |__________|    \          */
34    30,  -29,     /*         /                \ /                \         */
35    12,   -3,     /*        /                  X                  \        */
36   -11,    4,     /*       /                  / \                  \       */
37    -3,    7,     /*  ____V_____   __________V   V__________   _____V____  */
38   -20,   23,     /* |          | |          |   |          | |          | */
39     2,    0,     /* | Hh,30(f) | | Hh,330(f)|   | Hh,330(f)| | Hh,30(f) | */
40     1,   -6,     /* |__________| |__________|   |__________| |__________| */
41   -14,   -5,     /*      \     ___      /           \      ___     /      */
42    15,  -18,     /*       \   /   \    /    _____    \    /   \   /       */
43     6,    7,     /*        `->| + |<--'    /     \    `-->| + |<-'        */
44    15,  -10,     /*           \___/      _/       \_      \___/           */
45   -14,   22,     /*               \     / \       / \     /               */
46    -7,   -2,     /*                `--->| |       | |<---'                */
47    -4,    9,     /*                     \_/       \_/                     */
48     6,  -12,     /*                                                       */
49     6,   -6,     /*                       Headphones                      */
50     0,  -11,
51     0,   -5,
52     4,    0};
53 
54 #define NUMTAPS array_length(filt)
55 typedef struct {sox_sample_t tap[NUMTAPS];} priv_t; /* FIR filter z^-1 delays */
56 
start(sox_effect_t * effp)57 static int start(sox_effect_t * effp)
58 {
59   priv_t * p = (priv_t *)effp->priv;
60   if (effp->in_signal.rate != 44100 || effp->in_signal.channels != 2) {
61     lsx_fail("works only with stereo audio sampled at 44100Hz (i.e. CDDA)");
62     return SOX_EOF;
63   }
64   memset(p->tap, 0, NUMTAPS * sizeof(*p->tap)); /* zero tap memory */
65   if (effp->in_signal.mult)
66     *effp->in_signal.mult *= dB_to_linear(-4.4);
67   return SOX_SUCCESS;
68 }
69 
flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)70 static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
71                 sox_sample_t * obuf, size_t * isamp, size_t * osamp)
72 {
73   priv_t * p = (priv_t *)effp->priv;
74   size_t i, len = *isamp = *osamp = min(*isamp, *osamp);
75 
76   while (len--) {       /* update taps and calculate output */
77     double output = 0;
78 
79     for (i = NUMTAPS - 1; i; --i) {
80       p->tap[i] = p->tap[i - 1];
81       output += p->tap[i] * filt[i];
82     }
83     p->tap[0] = *ibuf++ / 64; /* scale output */
84     output += p->tap[0] * filt[0];
85     *obuf++ = SOX_ROUND_CLIP_COUNT(output, effp->clips);
86   }
87   return SOX_SUCCESS;
88 }
89 
90 /* No drain: preserve audio file length; it's only 32 samples anyway. */
91 
lsx_earwax_effect_fn(void)92 sox_effect_handler_t const *lsx_earwax_effect_fn(void)
93 {
94   static sox_effect_handler_t handler = {"earwax", NULL, SOX_EFF_MCHAN,
95     NULL, start, flow, NULL, NULL, NULL, sizeof(priv_t)};
96   return &handler;
97 }
98