459 lines
9.2 KiB
C++
459 lines
9.2 KiB
C++
|
#include "main.h"
|
||
|
#include "decoder.h"
|
||
|
#include <math.h>
|
||
|
#include <locale.h>
|
||
|
#pragma warning(disable:4244)
|
||
|
#include "shaper.h"
|
||
|
#include "api__in_vorbis.h"
|
||
|
|
||
|
Decoder::~Decoder() {if (shaper) delete shaper;}
|
||
|
|
||
|
extern CfgInt
|
||
|
cfg_mc6_dm, cfg_mc6_map;
|
||
|
/*
|
||
|
if (vorbis_cfg.use_hq_preamp)
|
||
|
{
|
||
|
sample *= pow(10., preamp_db/20);
|
||
|
|
||
|
//hard 6dB limiting
|
||
|
if (sample < -0.5)
|
||
|
sample = tanh((sample + 0.5) / (1-0.5)) * (1-0.5) - 0.5;
|
||
|
else if (sample > 0.5)
|
||
|
sample = tanh((sample - 0.5) / (1-0.5)) * (1-0.5) + 0.5;
|
||
|
} */
|
||
|
|
||
|
#if 0
|
||
|
static float q_tanh(float x)
|
||
|
{
|
||
|
double foo1, foo2;
|
||
|
foo1 = pow(2.71828182845904523536028747135266, x);
|
||
|
foo2 = 1.0 / foo1;
|
||
|
return (foo1 -foo2) / (foo1 + foo2);
|
||
|
}
|
||
|
#else
|
||
|
#define q_tanh tanh
|
||
|
#endif
|
||
|
|
||
|
float VorbisFile::GetGain()
|
||
|
{
|
||
|
float peak;
|
||
|
|
||
|
vorbis_comment * c;
|
||
|
float scale = 1.0f;
|
||
|
c = ov_comment(&vf, -1);
|
||
|
peak = 0.99f;
|
||
|
if (c)
|
||
|
{
|
||
|
if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
|
||
|
{
|
||
|
char * _peak = 0, *_gain = 0;
|
||
|
float gain = 0;
|
||
|
bool have_rg = 0;
|
||
|
float lwing_gain = 0;
|
||
|
char *gain1 = 0, *gain2 = 0, *peak1 = 0, *peak2 = 0;
|
||
|
gain1 = vorbis_comment_query(c, "replaygain_album_gain", 0);
|
||
|
if (!gain1) gain1 = vorbis_comment_query(c, "rg_audiophile", 0);
|
||
|
gain2 = vorbis_comment_query(c, "replaygain_track_gain", 0);
|
||
|
if (!gain2) gain2 = vorbis_comment_query(c, "rg_radio", 0);
|
||
|
|
||
|
peak1 = vorbis_comment_query(c, "replaygain_album_peak", 0);
|
||
|
peak2 = vorbis_comment_query(c, "replaygain_track_peak", 0);
|
||
|
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
|
||
|
{
|
||
|
case 0: // track
|
||
|
_gain = gain2;
|
||
|
if (!_gain && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||
|
_gain = gain1;
|
||
|
_peak = peak2;
|
||
|
if (!_peak && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||
|
_peak = peak1;
|
||
|
break;
|
||
|
case 1: // album
|
||
|
_gain = gain1;
|
||
|
if (!_gain && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||
|
_gain = gain2;
|
||
|
_peak = peak1;
|
||
|
if (!_peak && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||
|
_peak = peak2;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!_peak)
|
||
|
{
|
||
|
_peak = vorbis_comment_query(c, "rg_peak", 0);
|
||
|
}
|
||
|
|
||
|
_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
|
||
|
|
||
|
if (_peak) peak = _atof_l(_peak, C_locale);
|
||
|
if (_gain) gain = _atof_l(_gain, C_locale);
|
||
|
|
||
|
if (!_peak && !_gain)
|
||
|
{
|
||
|
char * l = vorbis_comment_query(c, "lwing_gain", 0);
|
||
|
if (l)
|
||
|
{
|
||
|
lwing_gain = _atof_l(l, C_locale);
|
||
|
have_rg = 1;
|
||
|
}
|
||
|
}
|
||
|
else have_rg = 1;
|
||
|
|
||
|
if (!have_rg)
|
||
|
{
|
||
|
gain = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0f);
|
||
|
}
|
||
|
|
||
|
scale = powf(10, (gain) / 20.0f);
|
||
|
if (lwing_gain)
|
||
|
scale *= lwing_gain;
|
||
|
else if (have_rg)
|
||
|
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
|
||
|
{
|
||
|
case 1: // apply gain, but don't clip
|
||
|
if (scale*peak > 1.0) scale = 1.0 / peak;
|
||
|
break;
|
||
|
case 2: // normalize
|
||
|
scale = 1.0 / peak;
|
||
|
break;
|
||
|
case 3: // no clipping
|
||
|
if (peak > 1.0f)
|
||
|
scale = 1.0 / peak;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
return scale;
|
||
|
}
|
||
|
|
||
|
void Decoder::process_rg()
|
||
|
{
|
||
|
scale = file->GetGain();
|
||
|
}
|
||
|
|
||
|
void Decoder::setup_mc()
|
||
|
{
|
||
|
if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false))
|
||
|
nch = 1;
|
||
|
else if (src_nch == 6)
|
||
|
{
|
||
|
switch (cfg_mc6_dm)
|
||
|
{
|
||
|
case 2:
|
||
|
nch = 4;
|
||
|
break;
|
||
|
case 3:
|
||
|
case 4:
|
||
|
nch = 2;
|
||
|
break;
|
||
|
case 5:
|
||
|
nch = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (nch > 2 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true))
|
||
|
nch = 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Decoder::Flush()
|
||
|
{
|
||
|
bptr = 0;
|
||
|
pcmbuf = 0;
|
||
|
data = 0;
|
||
|
pos = 0;
|
||
|
if (shaper) {delete shaper;shaper = 0;}
|
||
|
}
|
||
|
|
||
|
void Decoder::Init(VorbisFile * f, UINT _bits, UINT _nch, bool _useFloat, bool allowRG)
|
||
|
{
|
||
|
useFloat = _useFloat;
|
||
|
|
||
|
file = f;
|
||
|
vorbis_info * i = ov_info(&file->vf, -1);
|
||
|
|
||
|
if (allowRG)
|
||
|
process_rg();
|
||
|
else
|
||
|
scale = 1.0f;
|
||
|
|
||
|
if (useFloat)
|
||
|
{
|
||
|
dither = false;
|
||
|
bps = 32;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dither = AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"dither", true);
|
||
|
|
||
|
if (_bits)
|
||
|
bps = _bits;
|
||
|
else
|
||
|
bps = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
|
||
|
}
|
||
|
|
||
|
if (useFloat)
|
||
|
{
|
||
|
clipmin = -10000; // some arbitrarily large number
|
||
|
clipmax = 10000; // some arbitrarily large number
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
clipmin = - (1 << (bps - 1));
|
||
|
clipmax = (1 << (bps - 1)) - 1;
|
||
|
}
|
||
|
sr = i->rate;
|
||
|
nch = src_nch = i->channels;
|
||
|
Flush();
|
||
|
cur_link = file->vf.current_link;
|
||
|
|
||
|
if (_nch)
|
||
|
nch = _nch;
|
||
|
else
|
||
|
setup_mc();
|
||
|
}
|
||
|
|
||
|
UINT Decoder::DataAvailable()
|
||
|
{
|
||
|
return data * (bps >> 3);
|
||
|
}
|
||
|
|
||
|
int Decoder::DoFrame()
|
||
|
{
|
||
|
need_reopen = 0;
|
||
|
while (1)
|
||
|
{
|
||
|
data = ov_read_float(&file->vf, &pcmbuf, 576, 0);
|
||
|
if ((int)data <= 0)
|
||
|
{
|
||
|
if (data == OV_HOLE) {continue;}
|
||
|
data = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pos = 0;
|
||
|
if (cur_link != file->vf.current_link)
|
||
|
{
|
||
|
vorbis_info* i = ov_info(&file->vf, -1);
|
||
|
if (sr != (UINT)i->rate || src_nch != (UINT)i->channels)
|
||
|
{
|
||
|
UINT old_nch = nch, old_sr = sr;
|
||
|
if (shaper) {delete shaper;shaper = 0;}
|
||
|
sr = i->rate;
|
||
|
src_nch = nch = i->channels;
|
||
|
setup_mc();
|
||
|
if (nch != old_nch || sr != old_sr)
|
||
|
{
|
||
|
need_reopen = 1;
|
||
|
}
|
||
|
}
|
||
|
process_rg();
|
||
|
cur_link = file->vf.current_link;
|
||
|
}
|
||
|
data *= nch;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int Decoder::Read(UINT bytes, void * buf)
|
||
|
{
|
||
|
UINT wr = 0;
|
||
|
if (buf && bytes && data > 0)
|
||
|
{
|
||
|
char* out = (char*)buf;
|
||
|
UINT d;
|
||
|
double mul;
|
||
|
int ofs;
|
||
|
float * img;
|
||
|
|
||
|
d = bytes / (bps >> 3);
|
||
|
if (d > data) d = data;
|
||
|
if (!d) return 0;
|
||
|
data -= d;
|
||
|
if (useFloat)
|
||
|
{
|
||
|
mul = 1.0;
|
||
|
ofs = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mul = (double)( (1 << ((bps) - 1)) - 1 );
|
||
|
ofs = (bps == 8) ? 0x80 : 0;
|
||
|
}
|
||
|
wr += d * (bps >> 3);
|
||
|
|
||
|
img = (float*)alloca(sizeof(float) * nch);
|
||
|
do
|
||
|
{
|
||
|
UINT cur_ch;
|
||
|
if (nch == 1 && src_nch > 0)
|
||
|
{
|
||
|
UINT c;
|
||
|
img[0] = 0;
|
||
|
for (c = 0;c < src_nch;c++)
|
||
|
{
|
||
|
img[0] += pcmbuf[c][pos];
|
||
|
}
|
||
|
img[0] /= (float)src_nch;
|
||
|
}
|
||
|
else if (nch == src_nch && !(nch == 6 && cfg_mc6_dm == 1))
|
||
|
{
|
||
|
UINT c;
|
||
|
for (c = 0;c < nch;c++)
|
||
|
{
|
||
|
img[c] = pcmbuf[c][pos];
|
||
|
}
|
||
|
}
|
||
|
else if (src_nch == 6)
|
||
|
{
|
||
|
UINT FL, FR, C;
|
||
|
if (cfg_mc6_map == 1)
|
||
|
{
|
||
|
FL = 0;
|
||
|
FR = 1;
|
||
|
C = 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FL = 0;
|
||
|
C = 1;
|
||
|
FR = 2;
|
||
|
}
|
||
|
|
||
|
if (nch == 6)
|
||
|
{ //remap order for correct 5.1 output
|
||
|
img[0] = pcmbuf[FL][pos];
|
||
|
img[1] = pcmbuf[FR][pos];
|
||
|
img[2] = pcmbuf[C][pos];
|
||
|
img[3] = pcmbuf[5][pos];
|
||
|
img[4] = pcmbuf[3][pos];
|
||
|
img[5] = pcmbuf[4][pos];
|
||
|
}
|
||
|
else if (nch == 2)
|
||
|
{
|
||
|
/*
|
||
|
FL FR C BL BR LFE
|
||
|
0 1 2 3 4 5
|
||
|
|
||
|
L,C,R,SL,SR,LFE
|
||
|
0 1 2 3 4 5
|
||
|
|
||
|
|
||
|
output:
|
||
|
FL FR C LFE BL BR
|
||
|
|
||
|
|
||
|
stereo:
|
||
|
Lt=L+0.707*(V-SL-SR+LFE)
|
||
|
Rt=R+0.707*(C+SL+SR+LFE)
|
||
|
|
||
|
|
||
|
Lt=L+0.707*(C+LFE)
|
||
|
Rt=R+0.707*(C+LFE)
|
||
|
SLt=SL
|
||
|
SRt=SR
|
||
|
|
||
|
*/
|
||
|
if (cfg_mc6_dm == 4) //ds2
|
||
|
{
|
||
|
const double a = pow(10., 1.5 / 20.), b = 1 / a;
|
||
|
img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] - a * pcmbuf[3][pos] - b * pcmbuf[4][pos] + pcmbuf[5][pos]);
|
||
|
img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + b * pcmbuf[3][pos] + a * pcmbuf[4][pos] + pcmbuf[5][pos]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] - pcmbuf[3][pos] - pcmbuf[4][pos] + pcmbuf[5][pos]);
|
||
|
img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[3][pos] + pcmbuf[4][pos] + pcmbuf[5][pos]);
|
||
|
}
|
||
|
}
|
||
|
else if (nch == 4)
|
||
|
{
|
||
|
img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[5][pos]);
|
||
|
img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[5][pos]);
|
||
|
img[2] = pcmbuf[3][pos];
|
||
|
img[3] = pcmbuf[4][pos];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (cur_ch = 0;cur_ch < nch;cur_ch++)
|
||
|
{
|
||
|
float v = img[cur_ch];
|
||
|
int val;
|
||
|
v *= scale;
|
||
|
v *= mul;
|
||
|
if (dither)
|
||
|
{
|
||
|
if (!shaper)
|
||
|
{
|
||
|
//Shaper(int freq,int _nch,int min,int max,int _dtype,int pdf,double noiseamp);
|
||
|
shaper = new Shaper(sr, nch, clipmin, clipmax, 2, DITHER_TRIANGLE, 0);
|
||
|
}
|
||
|
// double peak=0;
|
||
|
val = shaper->do_shaping(v /*,&peak*/, cur_ch);
|
||
|
//shaper clips for us
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
val = (int)v;
|
||
|
if (val < clipmin) val = clipmin;
|
||
|
else if (val > clipmax) val = clipmax;
|
||
|
//1<<16 = 0x10000
|
||
|
|
||
|
}
|
||
|
val += ofs;
|
||
|
|
||
|
switch (bps)
|
||
|
{
|
||
|
case 8:
|
||
|
*(BYTE*)out = (UINT)val;
|
||
|
break;
|
||
|
case 16:
|
||
|
*(short*)out = val;
|
||
|
break;
|
||
|
case 24:
|
||
|
{
|
||
|
((BYTE*)out)[0] = (UINT)val;
|
||
|
((BYTE*)out)[1] = (UINT)val >> 8;
|
||
|
((BYTE*)out)[2] = (UINT)val >> 16;
|
||
|
}
|
||
|
break;
|
||
|
case 32:
|
||
|
if (useFloat)
|
||
|
{
|
||
|
*(float *)out = v;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//*(long*)out=val;
|
||
|
//break;
|
||
|
*(long*)out = 0;
|
||
|
}
|
||
|
break;
|
||
|
};
|
||
|
out += (bps >> 3);
|
||
|
d--;
|
||
|
}
|
||
|
pos++;
|
||
|
}
|
||
|
while (d);
|
||
|
|
||
|
}
|
||
|
return wr;
|
||
|
}
|
||
|
|
||
|
int VorbisFile::Seek(double p) { return ov_time_seek(&vf, p);}
|
||
|
|
||
|
int Decoder::Seek(double p)
|
||
|
{
|
||
|
Flush();
|
||
|
return file->Seek(p);
|
||
|
}
|
||
|
|
||
|
//char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count)
|
||
|
const char* VorbisFile::get_meta(const char* tag, UINT c)
|
||
|
{
|
||
|
return vorbis_comment_query(vf.seekable ? vf.vc + vf.current_link : vf.vc, (char*)tag, c);
|
||
|
}
|