|
|
@@ -31,7 +31,8 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
/* |
|
|
|
More information about high definition audio cds: |
|
|
|
HDCD is High Definition Compatible Digital |
|
|
|
More information about HDCD-encoded audio CDs: |
|
|
|
http://www.audiomisc.co.uk/HFN/HDCD/Enigma.html |
|
|
|
http://www.audiomisc.co.uk/HFN/HDCD/Examined.html |
|
|
|
*/ |
|
|
@@ -823,11 +824,29 @@ typedef struct { |
|
|
|
int code_counterA; |
|
|
|
int code_counterB; |
|
|
|
int code_counterC; |
|
|
|
|
|
|
|
/* For user information/stats, pulled up into HDCDContext |
|
|
|
* by filter_frame() */ |
|
|
|
int count_peak_extend; |
|
|
|
int count_transient_filter; |
|
|
|
/* target_gain is a 4-bit (3.1) fixed-point value, always |
|
|
|
* negative, but stored positive. |
|
|
|
* The 16 possible values range from -7.5 to 0.0 dB in |
|
|
|
* steps of 0.5, but no value below -6.0 dB should appear. */ |
|
|
|
int gain_counts[16]; /* for cursiosity, mostly */ |
|
|
|
int max_gain; |
|
|
|
int cb6, cb7; /* watch bits 6 and 7 of the control code, for curiosity */ |
|
|
|
} hdcd_state_t; |
|
|
|
|
|
|
|
typedef struct HDCDContext { |
|
|
|
const AVClass *class; |
|
|
|
hdcd_state_t state[2]; |
|
|
|
|
|
|
|
/* User information/stats */ |
|
|
|
int hdcd_detected; |
|
|
|
int uses_peak_extend; |
|
|
|
int uses_transient_filter; /* detected, but not implemented */ |
|
|
|
float max_gain_adjustment; /* in dB, expected in the range -6.0 to 0.0 */ |
|
|
|
} HDCDContext; |
|
|
|
|
|
|
|
static const AVOption hdcd_options[] = { |
|
|
@@ -840,6 +859,8 @@ AVFILTER_DEFINE_CLASS(hdcd); |
|
|
|
|
|
|
|
static void hdcd_reset(hdcd_state_t *state, unsigned rate) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
|
|
|
|
state->window = 0; |
|
|
|
state->readahead = 32; |
|
|
|
state->arg = 0; |
|
|
@@ -853,6 +874,13 @@ static void hdcd_reset(hdcd_state_t *state, unsigned rate) |
|
|
|
state->code_counterA = 0; |
|
|
|
state->code_counterB = 0; |
|
|
|
state->code_counterC = 0; |
|
|
|
|
|
|
|
state->count_peak_extend = 0; |
|
|
|
state->count_transient_filter = 0; |
|
|
|
for(i = 0; i < 16; i++) state->gain_counts[i] = 0; |
|
|
|
state->max_gain = 0; |
|
|
|
state->cb6 = 0; |
|
|
|
state->cb7 = 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int integrate(hdcd_state_t *state, int *flag, const int32_t *samples, int count, int stride) |
|
|
@@ -949,6 +977,7 @@ static int hdcd_envelope(int32_t *samples, int count, int stride, int gain, int |
|
|
|
int len = FFMIN(count, target_gain - gain); |
|
|
|
/* attenuate slowly */ |
|
|
|
for (i = 0; i < len; i++) { |
|
|
|
++gain; |
|
|
|
APPLY_GAIN(*samples, gain); |
|
|
|
samples += stride; |
|
|
|
} |
|
|
@@ -982,6 +1011,18 @@ static int hdcd_envelope(int32_t *samples, int count, int stride, int gain, int |
|
|
|
return gain; |
|
|
|
} |
|
|
|
|
|
|
|
/* update the user info/flags */ |
|
|
|
static void hdcd_update_info(hdcd_state_t *state) |
|
|
|
{ |
|
|
|
if (state->control & 16) state->count_peak_extend++; |
|
|
|
if (state->control & 32) state->count_transient_filter++; |
|
|
|
state->gain_counts[state->control & 15]++; |
|
|
|
state->max_gain = FFMAX(state->max_gain, (state->control & 15)); |
|
|
|
|
|
|
|
if (state->control & 64) state->cb6++; |
|
|
|
if (state->control & 128) state->cb7++; |
|
|
|
} |
|
|
|
|
|
|
|
static void hdcd_process(hdcd_state_t *state, int32_t *samples, int count, int stride) |
|
|
|
{ |
|
|
|
int32_t *samples_end = samples + count * stride; |
|
|
@@ -990,6 +1031,8 @@ static void hdcd_process(hdcd_state_t *state, int32_t *samples, int count, int s |
|
|
|
int target_gain = (state->control & 15) << 7; |
|
|
|
int lead = 0; |
|
|
|
|
|
|
|
hdcd_update_info(state); |
|
|
|
|
|
|
|
while (count > lead) { |
|
|
|
int envelope_run; |
|
|
|
int run; |
|
|
@@ -1006,6 +1049,7 @@ static void hdcd_process(hdcd_state_t *state, int32_t *samples, int count, int s |
|
|
|
lead = run - envelope_run; |
|
|
|
peak_extend = (state->control & 16); |
|
|
|
target_gain = (state->control & 15) << 7; |
|
|
|
hdcd_update_info(state); |
|
|
|
} |
|
|
|
if (lead > 0) { |
|
|
|
av_assert0(samples + lead * stride <= samples_end); |
|
|
@@ -1015,6 +1059,10 @@ static void hdcd_process(hdcd_state_t *state, int32_t *samples, int count, int s |
|
|
|
state->running_gain = gain; |
|
|
|
} |
|
|
|
|
|
|
|
/* convert to float from 4-bit (3.1) fixed-point |
|
|
|
* 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 |
|
|
|
|
|
|
|
static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
|
|
|
{ |
|
|
|
AVFilterContext *ctx = inlink->dst; |
|
|
@@ -1042,6 +1090,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
|
|
|
for (c = 0; c < inlink->channels; c++) { |
|
|
|
hdcd_state_t *state = &s->state[c]; |
|
|
|
hdcd_process(state, out_data + c, in->nb_samples, out->channels); |
|
|
|
|
|
|
|
s->uses_peak_extend |= !!state->count_peak_extend; |
|
|
|
s->uses_transient_filter |= !!state->count_transient_filter; |
|
|
|
s->max_gain_adjustment = FFMIN(s->max_gain_adjustment, GAINTOFLOAT(state->max_gain)); |
|
|
|
s->hdcd_detected |= state->code_counterC || state->code_counterB || state->code_counterA; |
|
|
|
} |
|
|
|
|
|
|
|
av_frame_free(&in); |
|
|
@@ -1097,13 +1150,28 @@ static int query_formats(AVFilterContext *ctx) |
|
|
|
static av_cold void uninit(AVFilterContext *ctx) |
|
|
|
{ |
|
|
|
HDCDContext *s = ctx->priv; |
|
|
|
int i; |
|
|
|
int i, j; |
|
|
|
|
|
|
|
/* dump the state for each channel for AV_LOG_VERBOSE */ |
|
|
|
for (i = 0; i < 2; i++) { |
|
|
|
hdcd_state_t *state = &s->state[i]; |
|
|
|
av_log(ctx, AV_LOG_VERBOSE, "Channel %d: counter A: %d, B: %d, C: %d\n", i, state->code_counterA, |
|
|
|
state->code_counterB, state->code_counterC); |
|
|
|
av_log(ctx, AV_LOG_VERBOSE, "Channel %d: c(pe): %d, c(tf): %d, cb6: %d, cb7: %d\n", i, |
|
|
|
state->count_peak_extend, state->count_transient_filter, state->cb6, state->cb7); |
|
|
|
for (j = 0; j <= state->max_gain; j++) { |
|
|
|
av_log(ctx, AV_LOG_VERBOSE, "Channel %d: tg %0.1f - %d\n", i, GAINTOFLOAT(j), state->gain_counts[j]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* log the HDCD decode information */ |
|
|
|
av_log(ctx, AV_LOG_INFO, |
|
|
|
"HDCD detected: %s, peak_extend: %s, max_gain_adj: %0.1f dB, transient_filter: %s\n", |
|
|
|
(s->hdcd_detected) ? "yes" : "no", |
|
|
|
(s->uses_peak_extend) ? "enabled" : "never enabled", |
|
|
|
s->max_gain_adjustment, |
|
|
|
(s->uses_transient_filter) ? "detected" : "not detected" |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
static av_cold int init(AVFilterContext *ctx) |
|
|
@@ -1112,6 +1180,8 @@ static av_cold int init(AVFilterContext *ctx) |
|
|
|
HDCDContext *s = ctx->priv; |
|
|
|
int c; |
|
|
|
|
|
|
|
s->max_gain_adjustment = 0.0; |
|
|
|
|
|
|
|
for (c = 0; c < 2; c++) { |
|
|
|
hdcd_reset(&s->state[c], 44100); |
|
|
|
} |
|
|
@@ -1138,7 +1208,7 @@ static const AVFilterPad avfilter_af_hdcd_outputs[] = { |
|
|
|
|
|
|
|
AVFilter ff_af_hdcd = { |
|
|
|
.name = "hdcd", |
|
|
|
.description = NULL_IF_CONFIG_SMALL("Apply high definition audio cd decoding."), |
|
|
|
.description = NULL_IF_CONFIG_SMALL("Apply High Definition Compatible Digital (HDCD) decoding."), |
|
|
|
.priv_size = sizeof(HDCDContext), |
|
|
|
.priv_class = &hdcd_class, |
|
|
|
.init = init, |
|
|
|