forked from sox_ng/sox_ng
200 lines
5.6 KiB
C
200 lines
5.6 KiB
C
/*
|
|
* FindOutSmp.c - Routines to find the right output value
|
|
* by trying different values through the side path.
|
|
*
|
|
* 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 "FindOutSmp.h"
|
|
|
|
#include "DiodeClip.h"
|
|
#include "Mixers.h"
|
|
#include "Param.h"
|
|
#include "SidePath.h"
|
|
|
|
#include <math.h>
|
|
|
|
/* This is not declared when compiling -ansi (C90) */
|
|
extern double round(double x);
|
|
|
|
#define FindOutSmpMinLoop 3
|
|
#define FindOutSmpNumberOfFilters ParamMaxChnl
|
|
|
|
#define FindOutSmpHigVal 500000000000.0
|
|
|
|
/*******************************************/
|
|
/**** Routines to find output value ****/
|
|
/*******************************************/
|
|
|
|
void FindOutSmpInit(dolbyb_t *Param)
|
|
{
|
|
uint16_t Chn;
|
|
|
|
Param->FindOutSmpMaxDif = round(Param->DecAMX * (ParamVltMux / Param->SmpMux));
|
|
if (Param->FindOutSmpMaxDif < 10)
|
|
Param->FindOutSmpMaxDif = 10;
|
|
for (Chn = 0; Chn <= FindOutSmpNumberOfFilters - 1; Chn++) {
|
|
Param->FindOutSmpPrvIn[Chn] = 0;
|
|
Param->FindOutSmpPrvOt[Chn] = 0;
|
|
}
|
|
}
|
|
|
|
static uint64_t FindOutSmpAmpVal(int64_t InVal)
|
|
{
|
|
if (InVal < 0)
|
|
return (-InVal);
|
|
else
|
|
return InVal;
|
|
}
|
|
|
|
static int64_t FindOutSmpCheckRange(int64_t InSamp, int64_t HigSmp, int64_t LowSmp)
|
|
{
|
|
if (InSamp <= LowSmp) return (LowSmp + 1);
|
|
else if (InSamp >= HigSmp) return (HigSmp - 1);
|
|
else return InSamp;
|
|
}
|
|
|
|
static double FindOutSmpCalcGuessMux(int64_t Gs1, int64_t GsP, int64_t Ot1, int64_t OtP)
|
|
{
|
|
/* Calculate multiple used to guess next sample */
|
|
int TryGus;
|
|
int64_t DGs, DOt;
|
|
uint64_t ADG, ADO;
|
|
|
|
DGs = GsP - Gs1;
|
|
DOt = OtP - Ot1;
|
|
TryGus = (DGs <= 0 || DOt <= 0);
|
|
if (!TryGus)
|
|
TryGus = (DGs < 0 && DOt < 0);
|
|
if (TryGus) {
|
|
ADG = FindOutSmpAmpVal(DGs);
|
|
ADO = FindOutSmpAmpVal(DOt);
|
|
TryGus = (ADO < ADG * 20);
|
|
}
|
|
if (TryGus)
|
|
return (double)DOt / DGs;
|
|
else {
|
|
return 1.0;
|
|
/* 1.0 means it won't be used */
|
|
}
|
|
}
|
|
|
|
static int64_t FindOutSmpTestGuess(dolbyb_t *Param, int64_t InSamp, int64_t TstSmp, uint16_t Chn)
|
|
{
|
|
return MixersDecode(InSamp, SidePathCheck(Param, TstSmp, Chn));
|
|
}
|
|
|
|
static void FindOutSmpUpdateHigLow(int64_t NxtVal, int64_t OutVal, int64_t *HigVal,
|
|
int64_t *LowVal)
|
|
{
|
|
if (OutVal == NxtVal) { /* Guess was correct */
|
|
*LowVal = NxtVal - 1;
|
|
*HigVal = NxtVal + 1;
|
|
}
|
|
if (OutVal > NxtVal) { /* Guess was too low */
|
|
*LowVal = NxtVal;
|
|
if (OutVal < *HigVal)
|
|
*HigVal = OutVal + 1;
|
|
}
|
|
if (OutVal >= NxtVal) /* Guess was too high */
|
|
return;
|
|
*HigVal = NxtVal;
|
|
if (OutVal > *LowVal)
|
|
*LowVal = OutVal - 1;
|
|
}
|
|
|
|
int64_t FindOutSmp(dolbyb_t *Param, int64_t InSamp, uint16_t Chn)
|
|
{
|
|
int64_t LowVal = -FindOutSmpHigVal, HigVal = FindOutSmpHigVal;
|
|
int64_t NxtGus, NxtOut, NxtDif;
|
|
uint16_t GusCnt = 0, GusSta = 0; /* 0=No Valid guess so far */
|
|
/* 1= 1 valid guess so far */
|
|
/* 2= 2 or more valid guesses */
|
|
/* 3= Switch to binary search only */
|
|
/* Guess Variables */
|
|
int32_t GusVal[2], OutVal[2];
|
|
double GusMux = 0.0;
|
|
int GusNow;
|
|
|
|
/* To stop compiler warnings */
|
|
GusVal[0] = 0;
|
|
GusVal[1] = 0;
|
|
OutVal[0] = 0;
|
|
OutVal[1] = 0;
|
|
/* Initalise high and low, using very large values */
|
|
|
|
/* Initialize guess method status */
|
|
|
|
/* Make initial guess */
|
|
NxtGus = Param->FindOutSmpPrvOt[Chn-1] + InSamp - Param->FindOutSmpPrvIn[Chn-1];
|
|
|
|
/* Try initial guess */
|
|
NxtOut = FindOutSmpTestGuess(Param, InSamp, NxtGus, Chn);
|
|
/* Try again if clipped */
|
|
if (!Param->DiodeClipCliped) {
|
|
NxtGus = NxtOut;
|
|
NxtOut = FindOutSmpTestGuess(Param, InSamp, NxtGus, Chn);
|
|
}
|
|
|
|
if (!Param->DiodeClipCliped) {
|
|
GusSta = 1;
|
|
GusVal[0] = NxtGus;
|
|
OutVal[0] = NxtOut;
|
|
}
|
|
|
|
FindOutSmpUpdateHigLow(NxtGus, NxtOut, &HigVal, &LowVal);
|
|
NxtDif = Param->FindOutSmpMaxDif + 1; /* To make it sure there is a loop */
|
|
|
|
/* Loop to make further guesses */
|
|
while (NxtDif > Param->FindOutSmpMaxDif) {
|
|
/* Decide whether to use guess or binary search */
|
|
GusNow = (GusSta == 2 && GusCnt < 2);
|
|
if (GusNow) {
|
|
GusMux = FindOutSmpCalcGuessMux(GusVal[0], GusVal[1], OutVal[0],
|
|
OutVal[1]);
|
|
GusNow = (GusMux < 0.95 || GusMux > 1.05);
|
|
}
|
|
if (GusNow) {
|
|
NxtGus = round((GusMux * GusVal[0] - OutVal[0]) / (GusMux - 1.0));
|
|
GusNow = (NxtGus > LowVal && NxtGus < HigVal);
|
|
}
|
|
|
|
/* If guess was not OK then use binary search */
|
|
if (GusNow)
|
|
GusCnt++;
|
|
else
|
|
NxtGus = LowVal + (HigVal - LowVal) / 2;
|
|
|
|
/* Check that guess is not out of range */
|
|
NxtGus = FindOutSmpCheckRange(NxtGus, HigVal, LowVal);
|
|
/* Try out new value */
|
|
NxtOut = FindOutSmpTestGuess(Param, InSamp, NxtGus, Chn);
|
|
if (!Param->DiodeClipCliped) {
|
|
if (GusSta < 2)
|
|
GusSta++;
|
|
GusVal[GusSta-1] = NxtGus;
|
|
OutVal[GusSta-1] = NxtOut;
|
|
}
|
|
FindOutSmpUpdateHigLow(NxtGus, NxtOut, &HigVal, &LowVal);
|
|
NxtDif = HigVal - LowVal;
|
|
}
|
|
Param->FindOutSmpPrvIn[Chn-1] = InSamp;
|
|
Param->FindOutSmpPrvOt[Chn-1] = NxtOut;
|
|
return NxtOut;
|
|
}
|