/* libSoX earwax - makes listening to headphones easier     November 9, 2000
 *
 * Copyright (c) 2000 Edward Beingessner And Sundry Contributors.
 * This source code is freely redistributable and may be used for any purpose.
 * This copyright notice must be maintained.  Edward Beingessner And Sundry
 * Contributors are not responsible for the consequences of using this
 * software.
 *
 * This effect takes a 44.1kHz stereo (CD format) signal that is meant to be
 * listened to on headphones, and adds audio cues to move the soundstage from
 * inside your head (standard for headphones) to outside and in front of the
 * listener (standard for speakers). This makes the sound much easier to listen
 * to on headphones.
 */

#include "sox_i.h"

static const sox_sample_t filt[32 * 2] = {
/* 30°  330° */
    4,   -6,     /* 32 tap stereo FIR filter. */
    4,  -11,     /* One side filters as if the */
   -1,   -5,     /* signal was from 30 degrees */
    3,    3,     /* from the ear, the other as */
   -2,    5,     /* if 330 degrees. */
   -5,    0,
    9,    1,
    6,    3,     /*                         Input                         */
   -4,   -1,     /*                   Left         Right                  */
   -5,   -3,     /*                __________   __________                */
   -2,   -5,     /*               |          | |          |               */
   -7,    1,     /*           .---|  Hh,0(f) | |  Hh,0(f) |---.           */
    6,   -7,     /*          /    |__________| |__________|    \          */
   30,  -29,     /*         /                \ /                \         */
   12,   -3,     /*        /                  X                  \        */
  -11,    4,     /*       /                  / \                  \       */
   -3,    7,     /*  ____V_____   __________V   V__________   _____V____  */
  -20,   23,     /* |          | |          |   |          | |          | */
    2,    0,     /* | Hh,30(f) | | Hh,330(f)|   | Hh,330(f)| | Hh,30(f) | */
    1,   -6,     /* |__________| |__________|   |__________| |__________| */
  -14,   -5,     /*      \     ___      /           \      ___     /      */
   15,  -18,     /*       \   /   \    /    _____    \    /   \   /       */
    6,    7,     /*        `->| + |<--'    /     \    `-->| + |<-'        */
   15,  -10,     /*           \___/      _/       \_      \___/           */
  -14,   22,     /*               \     / \       / \     /               */
   -7,   -2,     /*                `--->| |       | |<---'                */
   -4,    9,     /*                     \_/       \_/                     */
    6,  -12,     /*                                                       */
    6,   -6,     /*                       Headphones                      */
    0,  -11,
    0,   -5,
    4,    0};

#define NUMTAPS array_length(filt)
typedef struct {sox_sample_t tap[NUMTAPS];} priv_t; /* FIR filter z^-1 delays */

static int start_earwav(sox_effect_t * effp)
{
  priv_t * p = (priv_t *)effp->priv;
  if (effp->in_signal.rate != 44100 || effp->in_signal.channels != 2) {
    lsx_fail("works only with stereo audio sampled at 44100Hz (i.e. CDDA)");
    return SOX_EOF;
  }
  memset(p->tap, 0, NUMTAPS * sizeof(*p->tap)); /* zero tap memory */
  if (effp->in_signal.mult)
    *effp->in_signal.mult *= dB_to_linear(-4.4);
  return SOX_SUCCESS;
}

static int flow_earwav(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 i, len = *isamp = *osamp = min(*isamp, *osamp);

  while (len--) {       /* update taps and calculate output */
    double output = 0;

    for (i = NUMTAPS - 1; i; --i) {
      p->tap[i] = p->tap[i - 1];
      output += p->tap[i] * filt[i];
    }
    p->tap[0] = *ibuf++ / 64; /* scale output */
    output += p->tap[0] * filt[0];
    *obuf++ = SOX_ROUND_CLIP_COUNT(output, effp->clips);
  }
  return SOX_SUCCESS;
}

/* No drain: preserve audio file length; it's only 32 samples anyway. */

static char const * extra_usage[] = {
"                         Input",
"                   Left         Right",
"                __________   __________",
"               |          | |          |",
"           .---|  Hh,0(f) | |  Hh,0(f) |---.",
"          /    |__________| |__________|    \\",
"         /                \\ /                \\",
"        /                  X                  \\",
"       /                  / \\                  \\",
"  ____V_____   __________V   V__________   _____V____",
" |          | |          |   |          | |          |",
" | Hh,30(f) | | Hh,330(f)|   | Hh,330(f)| | Hh,30(f) |",
" |__________| |__________|   |__________| |__________|",
"      \\     ___      /           \\      ___     /",
"       \\   /   \\    /    _____    \\    /   \\   /",
"        `->| + |<--'    /     \\    `-->| + |<-'",
"           \\___/      _/       \\_      \\___/",
"               \\     / \\       / \\     /",
"                `--->| |       | |<---'",
"                     \\_/       \\_/",
"",
"                       Headphones",
  NULL
};

sox_effect_handler_t const *lsx_earwax_effect_fn(void)
{
  static sox_effect_handler_t handler = {
    "earwax", NULL, SOX_EFF_MCHAN,
    NULL, start_earwav, flow_earwav, NULL, NULL, NULL, sizeof(priv_t),
    extra_usage,
  };
  return &handler;
}
