forked from sox_ng/sox_ng
They are the default MP3 codecs anyway, and lame/mad/twolame may one day become formats that launch the command-line utils.
345 lines
10 KiB
C
345 lines
10 KiB
C
/*
|
|
* libdolbyb.c - API to libdolbyb
|
|
*
|
|
* Copyright (C) 2025 Martin Guy <martinwguy@gmail.com>
|
|
* based on dolbybcsoftwaredecode by Richard Evans 2018.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation. See COPYING for details.
|
|
*
|
|
* This program 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 General
|
|
* Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "dolbyb.h"
|
|
#include "Param.h"
|
|
#include "Mixers.h"
|
|
#include "Calibrate.h"
|
|
#include "SidePath.h"
|
|
#include "FindOutSmp.h"
|
|
|
|
#include "../src/soxconfig.h" /* for WORDS_BIGENDIAN */
|
|
|
|
#include <stdlib.h> /* for free() */
|
|
#include <string.h> /* for memset() */
|
|
|
|
/* Set default values */
|
|
void dolbyb_init(dolbyb_t *Param)
|
|
{
|
|
memset((void *)Param, 0, sizeof(*Param));
|
|
|
|
/* Set default values */
|
|
Param->NumChn = 1;
|
|
Param->BDepth = 16;
|
|
Param->FltTyp = 4;
|
|
Param->DecAdB = -5.0;
|
|
Param->ThGndB = 0.0;
|
|
|
|
/* Set initial values */
|
|
Param->FETGVt = 75000*(int64_t)100000;
|
|
}
|
|
|
|
/* Update things that depend on changed values of the
|
|
* settable parameters ThGndB, DecAdB, UpSamp, AllHig, FltTyp.
|
|
* It will also readjust for changed BDepth and SmpSec but
|
|
* I'm not sure what that means in the context of a session.
|
|
*/
|
|
char *dolbyb_restart(dolbyb_t *Param)
|
|
{
|
|
return dolbyb_start(Param);
|
|
}
|
|
|
|
static char *SecondInit(dolbyb_t *Param)
|
|
{
|
|
char *err;
|
|
|
|
/* More initialising after the input wave header has been read, */
|
|
/* so the sample rate and number of samples is now known. */
|
|
|
|
Param->DecAMX = ParamConvertDb(Param->DecAdB);
|
|
Param->ThGain = ParamConvertDb(Param->ThGndB);
|
|
|
|
/* Set up sample rates */
|
|
if (Param->UpSamp < 0) return "Upsampling cannot be negative";
|
|
if (Param->UpSamp > 0)
|
|
Param->UpSmp = Param->UpSamp;
|
|
else {
|
|
Param->UpSmp = 1;
|
|
while (Param->UpSmp * Param->SmpSec < 200000L)
|
|
Param->UpSmp++;
|
|
}
|
|
if (Param->AllHig)
|
|
Param->InUS = Param->UpSmp;
|
|
else
|
|
Param->InUS = 1;
|
|
|
|
/* Initialise side path routines */
|
|
if ((err = SidePathInit(Param))) return err;
|
|
FindOutSmpInit(Param);
|
|
return NULL;
|
|
}
|
|
|
|
/* Do initialisation that depends on parameter changes */
|
|
char *dolbyb_start(dolbyb_t *Param)
|
|
{
|
|
char *err;
|
|
|
|
/* Check validity of parameters */
|
|
|
|
if (Param->SmpSec <= 0)
|
|
return "Did you forget to set dolbyb.SmpSec before calling dolbyb_start()?";
|
|
|
|
if (Param->NumChn < 1 || Param->NumChn > 2)
|
|
return "libdolbyb can only process mono and stereo audio";
|
|
|
|
/* Check filter type is valid */
|
|
if (Param->FltTyp < 1 || Param->FltTyp > 4)
|
|
return "dolbyb.FltTyp must be from 1 to 4";
|
|
|
|
switch (Param->FltTyp) {
|
|
case 1: Param->FETSVt = ParamFETSVt1; Param->SidAmp = ParamSidAmp1; break;
|
|
case 2: Param->FETSVt = ParamFETSVt2; Param->SidAmp = ParamSidAmp2; break;
|
|
case 3: Param->FETSVt = ParamFETSVt3; Param->SidAmp = ParamSidAmp3; break;
|
|
case 4: Param->FETSVt = ParamFETSVt4; Param->SidAmp = ParamSidAmp4; break;
|
|
}
|
|
|
|
/* Set multiplier for wave input and divider for wave output */
|
|
switch (Param->BDepth) {
|
|
case 8: Param->SmpMux = ParamSMux08; break;
|
|
case 16: Param->SmpMux = ParamSMux16; break;
|
|
case 24: Param->SmpMux = ParamSMux24; break;
|
|
default:
|
|
return "dolbyb.BDepth must be 8, 16 or 24";
|
|
}
|
|
|
|
if ((err = SecondInit(Param))) return err;
|
|
|
|
Calibrate(Param);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char *dolbyb_encode(dolbyb_t *Param, void *in, void *out, size_t nframes)
|
|
{
|
|
unsigned char *inp = in;
|
|
unsigned char *outp = out;
|
|
uint16_t Chn, UpCnt;
|
|
int64_t SidSmp, TotSmp, OutSmp;
|
|
size_t SmpCnt;
|
|
int64_t MaxSamp, SubSamp;
|
|
uint16_t NumByt;
|
|
int32_t MaxVal, MinVal, AddVal;
|
|
|
|
switch (Param->BDepth) {
|
|
case 8: NumByt = 1; MaxSamp = -1; SubSamp = 128;
|
|
MaxVal = 127; MinVal = -128; AddVal = 128;
|
|
break;
|
|
case 16: NumByt = 2; MaxSamp = 32767; SubSamp = 65536L;
|
|
MaxVal = 32767; MinVal = -32768L; AddVal = 65536L;
|
|
break;
|
|
case 24: NumByt = 3; MaxSamp = 8388607L; SubSamp = 16777216L;
|
|
MaxVal = 8388607L; MinVal = -8388608L; AddVal = 16777216L;
|
|
break;
|
|
default:
|
|
/* Shut the compiler warnings up */
|
|
NumByt = MaxSamp = SubSamp = 0;
|
|
MaxVal = MinVal = AddVal = 0;
|
|
return "BDepth is not 8/16/24. Did you set it before calling dolbyb_start()?";
|
|
}
|
|
|
|
for (SmpCnt = 0; SmpCnt < nframes; SmpCnt++) {
|
|
for (Chn = 1; Chn <= Param->NumChn; Chn++) {
|
|
int64_t SmpVal = 0; /* Shut compiler warning up */
|
|
union {
|
|
unsigned char c[4];
|
|
signed int i;
|
|
} u;
|
|
|
|
/* Get input */
|
|
switch (NumByt) {
|
|
case 1: SmpVal = (int64_t)(inp[0]);
|
|
break;
|
|
case 2: u.i = 0; u.c[0] = inp[0]; u.c[1] = inp[1];
|
|
SmpVal = u.i;
|
|
break;
|
|
case 3: u.i = 0; u.c[0] = inp[0]; u.c[1] = inp[1]; u.c[2] = inp[2];
|
|
SmpVal = u.i;
|
|
break;
|
|
}
|
|
inp += NumByt;
|
|
if (SmpVal > MaxSamp) SmpVal -= SubSamp;
|
|
SmpVal *= Param->SmpMux;
|
|
|
|
/* If upsampling at the start then the same sample */
|
|
/* is processed several times */
|
|
TotSmp = 0;
|
|
for (UpCnt = 1; UpCnt <= Param->InUS; UpCnt++) {
|
|
/* Add audio from side path */
|
|
SidSmp = SidePath(Param, SmpVal, Chn);
|
|
TotSmp += MixersEncode(SmpVal, SidSmp);
|
|
}
|
|
OutSmp = (TotSmp / Param->InUS) / Param->SmpMux;
|
|
|
|
/* Check for sample value out of range */
|
|
if (OutSmp > MaxVal)
|
|
SmpVal = MaxVal;
|
|
else if (OutSmp < MinVal)
|
|
SmpVal = MinVal;
|
|
else
|
|
SmpVal = OutSmp;
|
|
/* Convert negative values */
|
|
if (SmpVal < 0 || Param->BDepth == 8)
|
|
SmpVal += AddVal;
|
|
|
|
/* Send as output bytes */
|
|
|
|
switch (NumByt) {
|
|
case 1: *outp++ = SmpVal & 0xff;
|
|
break;
|
|
#ifndef WORDS_BIGENDIAN
|
|
case 2: *outp++ = SmpVal & 0xff;
|
|
*outp++ = (SmpVal >> 8) & 0xff;
|
|
break;
|
|
case 3: *outp++ = SmpVal & 0xff;
|
|
*outp++ = (SmpVal >> 8) & 0xff;
|
|
*outp++ = (SmpVal >> 16) & 0xff;
|
|
#else
|
|
case 2: *outp++ = (SmpVal >> 8) & 0xff;
|
|
*outp++ = SmpVal & 0xff;
|
|
break;
|
|
case 3: *outp++ = (SmpVal >> 16) & 0xff;
|
|
*outp++ = (SmpVal >> 8) & 0xff;
|
|
*outp++ = SmpVal & 0xff;
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *dolbyb_decode(dolbyb_t *Param, void *in, void *out, size_t nframes)
|
|
{
|
|
unsigned char *inp = in;
|
|
unsigned char *outp = out;
|
|
uint16_t NumChn, UpCnt, NumByt;
|
|
int64_t TotSmp;
|
|
int64_t MaxSamp, SubSamp, MinVal, MaxVal, AddVal;
|
|
size_t SmpCnt;
|
|
|
|
switch (Param->BDepth) {
|
|
case 8: NumByt = 1; MaxSamp = -1; SubSamp = 128;
|
|
MaxVal = 127; MinVal = -128; AddVal = 128;
|
|
break;
|
|
case 16: NumByt = 2; MaxSamp = 32767; SubSamp = 65536L;
|
|
MaxVal = 32767; MinVal = -32768L; AddVal = 65536L;
|
|
break;
|
|
case 24: NumByt = 3; MaxSamp = 8388607L; SubSamp = 16777216L;
|
|
MaxVal = 8388607L; MinVal = -8388608L; AddVal = 16777216L;
|
|
break;
|
|
default:
|
|
/* Shut the compiler warnings up */
|
|
NumByt = MaxSamp = SubSamp = 0;
|
|
MaxVal = MinVal = AddVal = 0;
|
|
return "BDepth is not 8/16/24. Did you set it before calling dolbyb_start()?";
|
|
}
|
|
|
|
/* Process samples */
|
|
for (SmpCnt = 0; SmpCnt < nframes; SmpCnt++) {
|
|
for (NumChn = 1; NumChn <= Param->NumChn; NumChn++) {
|
|
int64_t SmpVal, OutSmp;
|
|
|
|
/* Get input */
|
|
switch (NumByt) {
|
|
case 1: SmpVal = (int64_t)(inp[0]);
|
|
break;
|
|
#ifndef WORDS_BIGENDIAN
|
|
case 2: SmpVal = (int64_t)((int32_t)inp[0] | ((int32_t)inp[1] << 8));
|
|
break;
|
|
case 3: SmpVal = (int64_t)((int32_t)inp[0] | ((int32_t)inp[1] << 8) | ((int32_t)inp[2] << 16));
|
|
#else
|
|
case 2: SmpVal = (int64_t)((int32_t)inp[1] | ((int32_t)inp[0] << 8));
|
|
break;
|
|
case 3: SmpVal = (int64_t)((int32_t)inp[2] | ((int32_t)inp[1] << 8) | ((int32_t)inp[0] << 16));
|
|
#endif
|
|
break;
|
|
}
|
|
inp += NumByt;
|
|
if (SmpVal > MaxSamp) SmpVal -= SubSamp;
|
|
SmpVal *= Param->SmpMux;
|
|
|
|
/* If upsampling at the start then the same sample */
|
|
/* is processed several times */
|
|
TotSmp = 0;
|
|
for (UpCnt = 1; UpCnt <= Param->InUS; UpCnt++) {
|
|
/* Set and process output */
|
|
(void) FindOutSmp(Param, SmpVal, NumChn);
|
|
TotSmp += MixersDecode(SmpVal, SidePathUpdate(Param, NumChn));
|
|
}
|
|
OutSmp = (TotSmp / Param->InUS) / Param->SmpMux;
|
|
|
|
/* Check for sample value out of range */
|
|
if (OutSmp > MaxVal)
|
|
SmpVal = MaxVal;
|
|
else if (OutSmp < MinVal)
|
|
SmpVal = MinVal;
|
|
else
|
|
SmpVal = OutSmp;
|
|
/* Convert negative values */
|
|
if (SmpVal < 0 || Param->BDepth == 8)
|
|
SmpVal += AddVal;
|
|
|
|
/* Send as output bytes */
|
|
switch (NumByt) {
|
|
case 1: *outp++ = SmpVal & 0xff;
|
|
break;
|
|
#ifndef WORDS_BIGENDIAN
|
|
case 2: *outp++ = SmpVal & 0xff;
|
|
*outp++ = (SmpVal >> 8) & 0xff;
|
|
break;
|
|
case 3: *outp++ = SmpVal & 0xff;
|
|
*outp++ = (SmpVal >> 8) & 0xff;
|
|
*outp++ = (SmpVal >> 16) & 0xff;
|
|
#else
|
|
case 2: *outp++ = (SmpVal >> 8) & 0xff;
|
|
*outp++ = SmpVal & 0xff;
|
|
break;
|
|
case 3: *outp++ = (SmpVal >> 16) & 0xff;
|
|
*outp++ = (SmpVal >> 8) & 0xff;
|
|
*outp++ = SmpVal & 0xff;
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void dolbyb_free(dolbyb_t *Param)
|
|
{
|
|
if (Param->HPF2SetValsPotTab) {
|
|
free(Param->HPF2SetValsPotTab); Param->HPF2SetValsPotTab = NULL;
|
|
}
|
|
if (Param->HPF2SetValsAlpTab) {
|
|
free(Param->HPF2SetValsAlpTab); Param->HPF2SetValsAlpTab = NULL;
|
|
}
|
|
if (Param->HPF2SetValsFLTATb) {
|
|
free(Param->HPF2SetValsFLTATb); Param->HPF2SetValsFLTATb = NULL;
|
|
}
|
|
if (Param->SetGateAttTab) {
|
|
free(Param->SetGateAttTab); Param->SetGateAttTab = NULL;
|
|
}
|
|
if (Param->SetGateAlpTab1) {
|
|
free(Param->SetGateAlpTab1); Param->SetGateAlpTab1 = NULL;
|
|
}
|
|
if (Param->SetGateAlpTab2) {
|
|
free(Param->SetGateAlpTab2); Param->SetGateAlpTab2 = NULL;
|
|
}
|
|
}
|