|
|
@@ -870,6 +870,26 @@ static const char * const pe_str[] = { |
|
|
|
* the always-negative value is stored positive, so make it negative */ |
|
|
|
#define GAINTOFLOAT(g) (g) ? -(float)(g>>1) - ((g & 1) ? 0.5 : 0.0) : 0.0 |
|
|
|
|
|
|
|
#define HDCD_ANA_OFF 0 |
|
|
|
#define HDCD_ANA_OFF_DESC "disabled" |
|
|
|
#define HDCD_ANA_LLE 1 |
|
|
|
#define HDCD_ANA_LLE_DESC "gain adjustment level at each sample" |
|
|
|
#define HDCD_ANA_PE 2 |
|
|
|
#define HDCD_ANA_PE_DESC "samples where peak extend occurs" |
|
|
|
#define HDCD_ANA_CDT 3 |
|
|
|
#define HDCD_ANA_CDT_DESC "samples where the code detect timer is active" |
|
|
|
#define HDCD_ANA_TGM 4 |
|
|
|
#define HDCD_ANA_TGM_DESC "samples where the target gain does not match between channels" |
|
|
|
#define HDCD_ANA_TOP 5 /* used in max value of AVOption */ |
|
|
|
|
|
|
|
static const char * const ana_mode_str[] = { |
|
|
|
HDCD_ANA_OFF_DESC, |
|
|
|
HDCD_ANA_LLE_DESC, |
|
|
|
HDCD_ANA_PE_DESC, |
|
|
|
HDCD_ANA_CDT_DESC, |
|
|
|
HDCD_ANA_TGM_DESC, |
|
|
|
}; |
|
|
|
|
|
|
|
typedef struct HDCDContext { |
|
|
|
const AVClass *class; |
|
|
|
hdcd_state_t state[HDCD_MAX_CHANNELS]; |
|
|
@@ -885,6 +905,12 @@ typedef struct HDCDContext { |
|
|
|
* default is off */ |
|
|
|
int force_pe; |
|
|
|
|
|
|
|
/* analyze mode replaces the audio with a solid tone and adjusts |
|
|
|
* the amplitude to signal some specific aspect of the decoding |
|
|
|
* process. See docs or HDCD_ANA_* defines. */ |
|
|
|
int analyze_mode; |
|
|
|
int ana_snb; /* used in tone generation */ |
|
|
|
|
|
|
|
/* config_input() and config_output() scan links for any resampling |
|
|
|
* or format changes. If found, warnings are issued and bad_config |
|
|
|
* is set. */ |
|
|
@@ -909,6 +935,13 @@ static const AVOption hdcd_options[] = { |
|
|
|
OFFSET(process_stereo), AV_OPT_TYPE_BOOL, { .i64 = HDCD_PROCESS_STEREO_DEFAULT }, 0, 1, A }, |
|
|
|
{ "force_pe", "Always extend peaks above -3dBFS even when PE is not signaled.", |
|
|
|
OFFSET(force_pe), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, A }, |
|
|
|
{ "analyze_mode", "Replace audio with solid tone and signal some processing aspect in the amplitude.", |
|
|
|
OFFSET(analyze_mode), AV_OPT_TYPE_INT, { .i64=HDCD_ANA_OFF }, 0, HDCD_ANA_TOP-1, A, "analyze_mode"}, |
|
|
|
{ "off", HDCD_ANA_OFF_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_OFF}, 0, 0, A, "analyze_mode" }, |
|
|
|
{ "lle", HDCD_ANA_LLE_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_LLE}, 0, 0, A, "analyze_mode" }, |
|
|
|
{ "pe", HDCD_ANA_PE_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_PE}, 0, 0, A, "analyze_mode" }, |
|
|
|
{ "cdt", HDCD_ANA_CDT_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_CDT}, 0, 0, A, "analyze_mode" }, |
|
|
|
{ "tgm", HDCD_ANA_TGM_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_TGM}, 0, 0, A, "analyze_mode" }, |
|
|
|
{NULL} |
|
|
|
}; |
|
|
|
|
|
|
@@ -1209,6 +1242,77 @@ static int hdcd_scan_stereo(HDCDContext *ctx, const int32_t *samples, int max) |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
/* encode a value in the given sample by adjusting the amplitude */ |
|
|
|
static int32_t hdcd_analyze_gen(int32_t sample, unsigned int v, unsigned int maxv) |
|
|
|
{ |
|
|
|
float sflt = sample, vv = v; |
|
|
|
vv /= maxv; |
|
|
|
if (vv > 1.0) vv = 1.0; |
|
|
|
sflt *= 1.0 + (vv * 18); |
|
|
|
return (int32_t)sflt; |
|
|
|
} |
|
|
|
|
|
|
|
/* behaves like hdcd_envelope(), but encodes processing information in |
|
|
|
* a way that is audible (and visible in an audio editor) to aid analysis. */ |
|
|
|
static int hdcd_analyze(int32_t *samples, int count, int stride, int gain, int target_gain, int extend, int mode, int cdt_active, int tg_mismatch) |
|
|
|
{ |
|
|
|
static const int maxg = 0xf << 7; |
|
|
|
int i; |
|
|
|
int32_t *samples_end = samples + stride * count; |
|
|
|
|
|
|
|
for (i = 0; i < count; i++) { |
|
|
|
samples[i * stride] <<= 15; |
|
|
|
if (mode == HDCD_ANA_PE) { |
|
|
|
int pel = (samples[i * stride] >> 16) & 1; |
|
|
|
int32_t sample = samples[i * stride]; |
|
|
|
samples[i * stride] = hdcd_analyze_gen(sample, !!(pel && extend), 1); |
|
|
|
} else if (mode == HDCD_ANA_TGM && tg_mismatch > 0) |
|
|
|
samples[i * stride] = hdcd_analyze_gen(samples[i * stride], 1, 1); |
|
|
|
else if (mode == HDCD_ANA_CDT && cdt_active) |
|
|
|
samples[i * stride] = hdcd_analyze_gen(samples[i * stride], 1, 1); |
|
|
|
} |
|
|
|
|
|
|
|
if (gain <= target_gain) { |
|
|
|
int len = FFMIN(count, target_gain - gain); |
|
|
|
/* attenuate slowly */ |
|
|
|
for (i = 0; i < len; i++) { |
|
|
|
++gain; |
|
|
|
if (mode == HDCD_ANA_LLE) |
|
|
|
*samples = hdcd_analyze_gen(*samples, gain, maxg); |
|
|
|
samples += stride; |
|
|
|
} |
|
|
|
count -= len; |
|
|
|
} else { |
|
|
|
int len = FFMIN(count, (gain - target_gain) >> 3); |
|
|
|
/* amplify quickly */ |
|
|
|
for (i = 0; i < len; i++) { |
|
|
|
gain -= 8; |
|
|
|
if (mode == HDCD_ANA_LLE) |
|
|
|
*samples = hdcd_analyze_gen(*samples, gain, maxg); |
|
|
|
samples += stride; |
|
|
|
} |
|
|
|
if (gain - 8 < target_gain) |
|
|
|
gain = target_gain; |
|
|
|
count -= len; |
|
|
|
} |
|
|
|
|
|
|
|
/* hold a steady level */ |
|
|
|
if (gain == 0) { |
|
|
|
if (count > 0) |
|
|
|
samples += count * stride; |
|
|
|
} else { |
|
|
|
while (--count >= 0) { |
|
|
|
if (mode == HDCD_ANA_LLE) |
|
|
|
*samples = hdcd_analyze_gen(*samples, gain, maxg); |
|
|
|
samples += stride; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
av_assert0(samples == samples_end); |
|
|
|
|
|
|
|
return gain; |
|
|
|
} |
|
|
|
|
|
|
|
static int hdcd_envelope(int32_t *samples, int count, int stride, int gain, int target_gain, int extend) |
|
|
|
{ |
|
|
|
int i; |
|
|
@@ -1316,7 +1420,10 @@ static void hdcd_process(HDCDContext *ctx, hdcd_state_t *state, int32_t *samples |
|
|
|
envelope_run = run - 1; |
|
|
|
|
|
|
|
av_assert0(samples + envelope_run * stride <= samples_end); |
|
|
|
gain = hdcd_envelope(samples, envelope_run, stride, gain, target_gain, peak_extend); |
|
|
|
if (ctx->analyze_mode) |
|
|
|
gain = hdcd_analyze(samples, envelope_run, stride, gain, target_gain, peak_extend, ctx->analyze_mode, state->sustain, -1); |
|
|
|
else |
|
|
|
gain = hdcd_envelope(samples, envelope_run, stride, gain, target_gain, peak_extend); |
|
|
|
|
|
|
|
samples += envelope_run * stride; |
|
|
|
count -= envelope_run; |
|
|
@@ -1325,7 +1432,10 @@ static void hdcd_process(HDCDContext *ctx, hdcd_state_t *state, int32_t *samples |
|
|
|
} |
|
|
|
if (lead > 0) { |
|
|
|
av_assert0(samples + lead * stride <= samples_end); |
|
|
|
gain = hdcd_envelope(samples, lead, stride, gain, target_gain, peak_extend); |
|
|
|
if (ctx->analyze_mode) |
|
|
|
gain = hdcd_analyze(samples, lead, stride, gain, target_gain, peak_extend, ctx->analyze_mode, state->sustain, -1); |
|
|
|
else |
|
|
|
gain = hdcd_envelope(samples, lead, stride, gain, target_gain, peak_extend); |
|
|
|
} |
|
|
|
|
|
|
|
state->running_gain = gain; |
|
|
@@ -1339,7 +1449,7 @@ static void hdcd_process_stereo(HDCDContext *ctx, int32_t *samples, int count) |
|
|
|
int peak_extend[2]; |
|
|
|
int lead = 0; |
|
|
|
|
|
|
|
hdcd_control_stereo(ctx, &peak_extend[0], &peak_extend[1]); |
|
|
|
int ctlret = hdcd_control_stereo(ctx, &peak_extend[0], &peak_extend[1]); |
|
|
|
while (count > lead) { |
|
|
|
int envelope_run, run; |
|
|
|
|
|
|
@@ -1349,7 +1459,16 @@ static void hdcd_process_stereo(HDCDContext *ctx, int32_t *samples, int count) |
|
|
|
|
|
|
|
av_assert0(samples + envelope_run * stride <= samples_end); |
|
|
|
|
|
|
|
if (envelope_run) { |
|
|
|
if (ctx->analyze_mode) { |
|
|
|
gain[0] = hdcd_analyze(samples, envelope_run, stride, gain[0], ctx->val_target_gain, peak_extend[0], |
|
|
|
ctx->analyze_mode, |
|
|
|
ctx->state[0].sustain, |
|
|
|
(ctlret == HDCD_TG_MISMATCH) ); |
|
|
|
gain[1] = hdcd_analyze(samples + 1, envelope_run, stride, gain[1], ctx->val_target_gain, peak_extend[1], |
|
|
|
ctx->analyze_mode, |
|
|
|
ctx->state[1].sustain, |
|
|
|
(ctlret == HDCD_TG_MISMATCH) ); |
|
|
|
} else { |
|
|
|
gain[0] = hdcd_envelope(samples, envelope_run, stride, gain[0], ctx->val_target_gain, peak_extend[0]); |
|
|
|
gain[1] = hdcd_envelope(samples + 1, envelope_run, stride, gain[1], ctx->val_target_gain, peak_extend[1]); |
|
|
|
} |
|
|
@@ -1358,18 +1477,32 @@ static void hdcd_process_stereo(HDCDContext *ctx, int32_t *samples, int count) |
|
|
|
count -= envelope_run; |
|
|
|
lead = run - envelope_run; |
|
|
|
|
|
|
|
hdcd_control_stereo(ctx, &peak_extend[0], &peak_extend[1]); |
|
|
|
ctlret = hdcd_control_stereo(ctx, &peak_extend[0], &peak_extend[1]); |
|
|
|
} |
|
|
|
if (lead > 0) { |
|
|
|
av_assert0(samples + lead * stride <= samples_end); |
|
|
|
gain[0] = hdcd_envelope(samples, lead, stride, gain[0], ctx->val_target_gain, peak_extend[0]); |
|
|
|
gain[1] = hdcd_envelope(samples + 1, lead, stride, gain[1], ctx->val_target_gain, peak_extend[1]); |
|
|
|
if (ctx->analyze_mode) { |
|
|
|
gain[0] = hdcd_analyze(samples, lead, stride, gain[0], ctx->val_target_gain, peak_extend[0], |
|
|
|
ctx->analyze_mode, |
|
|
|
ctx->state[0].sustain, |
|
|
|
(ctlret == HDCD_TG_MISMATCH) ); |
|
|
|
gain[1] = hdcd_analyze(samples + 1, lead, stride, gain[1], ctx->val_target_gain, peak_extend[1], |
|
|
|
ctx->analyze_mode, |
|
|
|
ctx->state[1].sustain, |
|
|
|
(ctlret == HDCD_TG_MISMATCH) ); |
|
|
|
} else { |
|
|
|
gain[0] = hdcd_envelope(samples, lead, stride, gain[0], ctx->val_target_gain, peak_extend[0]); |
|
|
|
gain[1] = hdcd_envelope(samples + 1, lead, stride, gain[1], ctx->val_target_gain, peak_extend[1]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ctx->state[0].running_gain = gain[0]; |
|
|
|
ctx->state[1].running_gain = gain[1]; |
|
|
|
} |
|
|
|
|
|
|
|
/* tone generator: sample_number, frequency, sample_rate, amplitude */ |
|
|
|
#define TONEGEN16(sn, f, sr, a) (int16_t)(sin((6.28318530718 * (sn) * (f)) /(sr)) * (a) * 0x7fff) |
|
|
|
|
|
|
|
static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
|
|
|
{ |
|
|
|
AVFilterContext *ctx = inlink->dst; |
|
|
@@ -1390,8 +1523,21 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
|
|
|
|
|
|
|
in_data = (int16_t*)in->data[0]; |
|
|
|
out_data = (int32_t*)out->data[0]; |
|
|
|
for (n = 0; n < in->nb_samples * in->channels; n++) { |
|
|
|
for (c = n = 0; n < in->nb_samples * in->channels; n++) { |
|
|
|
out_data[n] = in_data[n]; |
|
|
|
if (s->analyze_mode) { |
|
|
|
/* in analyze mode, the audio is replaced by a solid tone, and |
|
|
|
* amplitude is changed to signal when the specified feature is |
|
|
|
* used. |
|
|
|
* bit 0: HDCD signal preserved |
|
|
|
* bit 1: Original sample was above PE level */ |
|
|
|
int32_t save = (abs(in_data[n]) - 0x5981 >= 0) ? 2 : 0; /* above PE level */ |
|
|
|
save |= in_data[n] & 1; /* save LSB for HDCD packets */ |
|
|
|
out_data[n] = TONEGEN16(s->ana_snb, 277.18, 44100, 0.1); |
|
|
|
out_data[n] = (out_data[n] | 3) ^ ((~save) & 3); |
|
|
|
if (++c == in->channels) { s->ana_snb++; c = 0; } |
|
|
|
if (s->ana_snb > 0x3fffffff) s->ana_snb = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
s->det_errors = 0; /* re-sum every pass */ |
|
|
@@ -1557,6 +1703,7 @@ static av_cold int init(AVFilterContext *ctx) |
|
|
|
(s->process_stereo) ? "process stereo channels together" : "process each channel separately"); |
|
|
|
av_log(ctx, AV_LOG_VERBOSE, "Force PE: %s\n", |
|
|
|
(s->force_pe) ? "on" : "off"); |
|
|
|
av_log(ctx, AV_LOG_VERBOSE, "Analyze mode: [%d] %s\n", s->analyze_mode, ana_mode_str[s->analyze_mode] ); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|