This source file includes following definitions.
- ebur128_init_filter
- ebur128_init_channel_map
- init_histogram
- ff_ebur128_init
- ff_ebur128_destroy
- EBUR128_FILTER
- find_histogram_index
- ebur128_calc_gating_block
- ff_ebur128_set_channel
- FF_EBUR128_ADD_FRAMES_PLANAR
- FF_EBUR128_ADD_FRAMES
- ebur128_gated_loudness
- ff_ebur128_relative_threshold
- ff_ebur128_loudness_global
- ff_ebur128_loudness_global_multiple
- ebur128_energy_in_interval
- ebur128_energy_shortterm
- ff_ebur128_loudness_momentary
- ff_ebur128_loudness_shortterm
- ff_ebur128_loudness_window
- ff_ebur128_loudness_range_multiple
- ff_ebur128_loudness_range
- ff_ebur128_sample_peak
#include "ebur128.h"
#include <float.h>
#include <limits.h>
#include <math.h>
#include "libavutil/common.h"
#include "libavutil/mem.h"
#include "libavutil/thread.h"
#define CHECK_ERROR(condition, errorcode, goto_point) \
if ((condition)) { \
errcode = (errorcode); \
goto goto_point; \
}
#define ALMOST_ZERO 0.000001
#define RELATIVE_GATE (-10.0)
#define RELATIVE_GATE_FACTOR pow(10.0, RELATIVE_GATE / 10.0)
#define MINUS_20DB pow(10.0, -20.0 / 10.0)
struct FFEBUR128StateInternal {
double *audio_data;
size_t audio_data_frames;
size_t audio_data_index;
unsigned long needed_frames;
int *channel_map;
unsigned long samples_in_100ms;
double b[5];
double a[5];
double v[5][5];
unsigned long *block_energy_histogram;
unsigned long *short_term_block_energy_histogram;
size_t short_term_frame_counter;
double *sample_peak;
unsigned long window;
void **data_ptrs;
};
static AVOnce histogram_init = AV_ONCE_INIT;
static DECLARE_ALIGNED(32, double, histogram_energies)[1000];
static DECLARE_ALIGNED(32, double, histogram_energy_boundaries)[1001];
static void ebur128_init_filter(FFEBUR128State * st)
{
int i, j;
double f0 = 1681.974450955533;
double G = 3.999843853973347;
double Q = 0.7071752369554196;
double K = tan(M_PI * f0 / (double) st->samplerate);
double Vh = pow(10.0, G / 20.0);
double Vb = pow(Vh, 0.4996667741545416);
double pb[3] = { 0.0, 0.0, 0.0 };
double pa[3] = { 1.0, 0.0, 0.0 };
double rb[3] = { 1.0, -2.0, 1.0 };
double ra[3] = { 1.0, 0.0, 0.0 };
double a0 = 1.0 + K / Q + K * K;
pb[0] = (Vh + Vb * K / Q + K * K) / a0;
pb[1] = 2.0 * (K * K - Vh) / a0;
pb[2] = (Vh - Vb * K / Q + K * K) / a0;
pa[1] = 2.0 * (K * K - 1.0) / a0;
pa[2] = (1.0 - K / Q + K * K) / a0;
f0 = 38.13547087602444;
Q = 0.5003270373238773;
K = tan(M_PI * f0 / (double) st->samplerate);
ra[1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K);
ra[2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K);
st->d->b[0] = pb[0] * rb[0];
st->d->b[1] = pb[0] * rb[1] + pb[1] * rb[0];
st->d->b[2] = pb[0] * rb[2] + pb[1] * rb[1] + pb[2] * rb[0];
st->d->b[3] = pb[1] * rb[2] + pb[2] * rb[1];
st->d->b[4] = pb[2] * rb[2];
st->d->a[0] = pa[0] * ra[0];
st->d->a[1] = pa[0] * ra[1] + pa[1] * ra[0];
st->d->a[2] = pa[0] * ra[2] + pa[1] * ra[1] + pa[2] * ra[0];
st->d->a[3] = pa[1] * ra[2] + pa[2] * ra[1];
st->d->a[4] = pa[2] * ra[2];
for (i = 0; i < 5; ++i) {
for (j = 0; j < 5; ++j) {
st->d->v[i][j] = 0.0;
}
}
}
static int ebur128_init_channel_map(FFEBUR128State * st)
{
size_t i;
st->d->channel_map =
(int *) av_malloc_array(st->channels, sizeof(*st->d->channel_map));
if (!st->d->channel_map)
return AVERROR(ENOMEM);
if (st->channels == 4) {
st->d->channel_map[0] = FF_EBUR128_LEFT;
st->d->channel_map[1] = FF_EBUR128_RIGHT;
st->d->channel_map[2] = FF_EBUR128_LEFT_SURROUND;
st->d->channel_map[3] = FF_EBUR128_RIGHT_SURROUND;
} else if (st->channels == 5) {
st->d->channel_map[0] = FF_EBUR128_LEFT;
st->d->channel_map[1] = FF_EBUR128_RIGHT;
st->d->channel_map[2] = FF_EBUR128_CENTER;
st->d->channel_map[3] = FF_EBUR128_LEFT_SURROUND;
st->d->channel_map[4] = FF_EBUR128_RIGHT_SURROUND;
} else {
for (i = 0; i < st->channels; ++i) {
switch (i) {
case 0:
st->d->channel_map[i] = FF_EBUR128_LEFT;
break;
case 1:
st->d->channel_map[i] = FF_EBUR128_RIGHT;
break;
case 2:
st->d->channel_map[i] = FF_EBUR128_CENTER;
break;
case 3:
st->d->channel_map[i] = FF_EBUR128_UNUSED;
break;
case 4:
st->d->channel_map[i] = FF_EBUR128_LEFT_SURROUND;
break;
case 5:
st->d->channel_map[i] = FF_EBUR128_RIGHT_SURROUND;
break;
default:
st->d->channel_map[i] = FF_EBUR128_UNUSED;
break;
}
}
}
return 0;
}
static inline void init_histogram(void)
{
int i;
histogram_energy_boundaries[0] = pow(10.0, (-70.0 + 0.691) / 10.0);
for (i = 0; i < 1000; ++i) {
histogram_energies[i] =
pow(10.0, ((double) i / 10.0 - 69.95 + 0.691) / 10.0);
}
for (i = 1; i < 1001; ++i) {
histogram_energy_boundaries[i] =
pow(10.0, ((double) i / 10.0 - 70.0 + 0.691) / 10.0);
}
}
FFEBUR128State *ff_ebur128_init(unsigned int channels,
unsigned long samplerate,
unsigned long window, int mode)
{
int errcode;
FFEBUR128State *st;
st = (FFEBUR128State *) av_malloc(sizeof(*st));
CHECK_ERROR(!st, 0, exit)
st->d = (struct FFEBUR128StateInternal *)
av_malloc(sizeof(*st->d));
CHECK_ERROR(!st->d, 0, free_state)
st->channels = channels;
errcode = ebur128_init_channel_map(st);
CHECK_ERROR(errcode, 0, free_internal)
st->d->sample_peak =
(double *) av_mallocz_array(channels, sizeof(*st->d->sample_peak));
CHECK_ERROR(!st->d->sample_peak, 0, free_channel_map)
st->samplerate = samplerate;
st->d->samples_in_100ms = (st->samplerate + 5) / 10;
st->mode = mode;
if ((mode & FF_EBUR128_MODE_S) == FF_EBUR128_MODE_S) {
st->d->window = FFMAX(window, 3000);
} else if ((mode & FF_EBUR128_MODE_M) == FF_EBUR128_MODE_M) {
st->d->window = FFMAX(window, 400);
} else {
goto free_sample_peak;
}
st->d->audio_data_frames = st->samplerate * st->d->window / 1000;
if (st->d->audio_data_frames % st->d->samples_in_100ms) {
st->d->audio_data_frames = st->d->audio_data_frames
+ st->d->samples_in_100ms
- (st->d->audio_data_frames % st->d->samples_in_100ms);
}
st->d->audio_data =
(double *) av_mallocz_array(st->d->audio_data_frames,
st->channels * sizeof(*st->d->audio_data));
CHECK_ERROR(!st->d->audio_data, 0, free_sample_peak)
ebur128_init_filter(st);
st->d->block_energy_histogram =
av_mallocz(1000 * sizeof(*st->d->block_energy_histogram));
CHECK_ERROR(!st->d->block_energy_histogram, 0, free_audio_data)
st->d->short_term_block_energy_histogram =
av_mallocz(1000 * sizeof(*st->d->short_term_block_energy_histogram));
CHECK_ERROR(!st->d->short_term_block_energy_histogram, 0,
free_block_energy_histogram)
st->d->short_term_frame_counter = 0;
st->d->needed_frames = st->d->samples_in_100ms * 4;
st->d->audio_data_index = 0;
if (ff_thread_once(&histogram_init, &init_histogram) != 0)
goto free_short_term_block_energy_histogram;
st->d->data_ptrs = av_malloc_array(channels, sizeof(*st->d->data_ptrs));
CHECK_ERROR(!st->d->data_ptrs, 0,
free_short_term_block_energy_histogram);
return st;
free_short_term_block_energy_histogram:
av_free(st->d->short_term_block_energy_histogram);
free_block_energy_histogram:
av_free(st->d->block_energy_histogram);
free_audio_data:
av_free(st->d->audio_data);
free_sample_peak:
av_free(st->d->sample_peak);
free_channel_map:
av_free(st->d->channel_map);
free_internal:
av_free(st->d);
free_state:
av_free(st);
exit:
return NULL;
}
void ff_ebur128_destroy(FFEBUR128State ** st)
{
av_free((*st)->d->block_energy_histogram);
av_free((*st)->d->short_term_block_energy_histogram);
av_free((*st)->d->audio_data);
av_free((*st)->d->channel_map);
av_free((*st)->d->sample_peak);
av_free((*st)->d->data_ptrs);
av_free((*st)->d);
av_free(*st);
*st = NULL;
}
#define EBUR128_FILTER(type, scaling_factor) \
static void ebur128_filter_##type(FFEBUR128State* st, const type** srcs, \
size_t src_index, size_t frames, \
int stride) { \
double* audio_data = st->d->audio_data + st->d->audio_data_index; \
size_t i, c; \
\
if ((st->mode & FF_EBUR128_MODE_SAMPLE_PEAK) == FF_EBUR128_MODE_SAMPLE_PEAK) { \
for (c = 0; c < st->channels; ++c) { \
double max = 0.0; \
for (i = 0; i < frames; ++i) { \
type v = srcs[c][src_index + i * stride]; \
if (v > max) { \
max = v; \
} else if (-v > max) { \
max = -1.0 * v; \
} \
} \
max /= scaling_factor; \
if (max > st->d->sample_peak[c]) st->d->sample_peak[c] = max; \
} \
} \
for (c = 0; c < st->channels; ++c) { \
int ci = st->d->channel_map[c] - 1; \
if (ci < 0) continue; \
else if (ci == FF_EBUR128_DUAL_MONO - 1) ci = 0; \
for (i = 0; i < frames; ++i) { \
st->d->v[ci][0] = (double) (srcs[c][src_index + i * stride] / scaling_factor) \
- st->d->a[1] * st->d->v[ci][1] \
- st->d->a[2] * st->d->v[ci][2] \
- st->d->a[3] * st->d->v[ci][3] \
- st->d->a[4] * st->d->v[ci][4]; \
audio_data[i * st->channels + c] = \
st->d->b[0] * st->d->v[ci][0] \
+ st->d->b[1] * st->d->v[ci][1] \
+ st->d->b[2] * st->d->v[ci][2] \
+ st->d->b[3] * st->d->v[ci][3] \
+ st->d->b[4] * st->d->v[ci][4]; \
st->d->v[ci][4] = st->d->v[ci][3]; \
st->d->v[ci][3] = st->d->v[ci][2]; \
st->d->v[ci][2] = st->d->v[ci][1]; \
st->d->v[ci][1] = st->d->v[ci][0]; \
} \
st->d->v[ci][4] = fabs(st->d->v[ci][4]) < DBL_MIN ? 0.0 : st->d->v[ci][4]; \
st->d->v[ci][3] = fabs(st->d->v[ci][3]) < DBL_MIN ? 0.0 : st->d->v[ci][3]; \
st->d->v[ci][2] = fabs(st->d->v[ci][2]) < DBL_MIN ? 0.0 : st->d->v[ci][2]; \
st->d->v[ci][1] = fabs(st->d->v[ci][1]) < DBL_MIN ? 0.0 : st->d->v[ci][1]; \
} \
}
EBUR128_FILTER(short, -((double)SHRT_MIN))
EBUR128_FILTER(int, -((double)INT_MIN))
EBUR128_FILTER(float, 1.0)
EBUR128_FILTER(double, 1.0)
static double ebur128_energy_to_loudness(double energy)
{
return 10 * log10(energy) - 0.691;
}
static size_t find_histogram_index(double energy)
{
size_t index_min = 0;
size_t index_max = 1000;
size_t index_mid;
do {
index_mid = (index_min + index_max) / 2;
if (energy >= histogram_energy_boundaries[index_mid]) {
index_min = index_mid;
} else {
index_max = index_mid;
}
} while (index_max - index_min != 1);
return index_min;
}
static void ebur128_calc_gating_block(FFEBUR128State * st,
size_t frames_per_block,
double *optional_output)
{
size_t i, c;
double sum = 0.0;
double channel_sum;
for (c = 0; c < st->channels; ++c) {
if (st->d->channel_map[c] == FF_EBUR128_UNUSED)
continue;
channel_sum = 0.0;
if (st->d->audio_data_index < frames_per_block * st->channels) {
for (i = 0; i < st->d->audio_data_index / st->channels; ++i) {
channel_sum += st->d->audio_data[i * st->channels + c] *
st->d->audio_data[i * st->channels + c];
}
for (i = st->d->audio_data_frames -
(frames_per_block -
st->d->audio_data_index / st->channels);
i < st->d->audio_data_frames; ++i) {
channel_sum += st->d->audio_data[i * st->channels + c] *
st->d->audio_data[i * st->channels + c];
}
} else {
for (i =
st->d->audio_data_index / st->channels - frames_per_block;
i < st->d->audio_data_index / st->channels; ++i) {
channel_sum +=
st->d->audio_data[i * st->channels +
c] * st->d->audio_data[i *
st->channels +
c];
}
}
if (st->d->channel_map[c] == FF_EBUR128_Mp110 ||
st->d->channel_map[c] == FF_EBUR128_Mm110 ||
st->d->channel_map[c] == FF_EBUR128_Mp060 ||
st->d->channel_map[c] == FF_EBUR128_Mm060 ||
st->d->channel_map[c] == FF_EBUR128_Mp090 ||
st->d->channel_map[c] == FF_EBUR128_Mm090) {
channel_sum *= 1.41;
} else if (st->d->channel_map[c] == FF_EBUR128_DUAL_MONO) {
channel_sum *= 2.0;
}
sum += channel_sum;
}
sum /= (double) frames_per_block;
if (optional_output) {
*optional_output = sum;
} else if (sum >= histogram_energy_boundaries[0]) {
++st->d->block_energy_histogram[find_histogram_index(sum)];
}
}
int ff_ebur128_set_channel(FFEBUR128State * st,
unsigned int channel_number, int value)
{
if (channel_number >= st->channels) {
return 1;
}
if (value == FF_EBUR128_DUAL_MONO &&
(st->channels != 1 || channel_number != 0)) {
return 1;
}
st->d->channel_map[channel_number] = value;
return 0;
}
static int ebur128_energy_shortterm(FFEBUR128State * st, double *out);
#define FF_EBUR128_ADD_FRAMES_PLANAR(type) \
void ff_ebur128_add_frames_planar_##type(FFEBUR128State* st, const type** srcs, \
size_t frames, int stride) { \
size_t src_index = 0; \
while (frames > 0) { \
if (frames >= st->d->needed_frames) { \
ebur128_filter_##type(st, srcs, src_index, st->d->needed_frames, stride); \
src_index += st->d->needed_frames * stride; \
frames -= st->d->needed_frames; \
st->d->audio_data_index += st->d->needed_frames * st->channels; \
\
if ((st->mode & FF_EBUR128_MODE_I) == FF_EBUR128_MODE_I) { \
ebur128_calc_gating_block(st, st->d->samples_in_100ms * 4, NULL); \
} \
if ((st->mode & FF_EBUR128_MODE_LRA) == FF_EBUR128_MODE_LRA) { \
st->d->short_term_frame_counter += st->d->needed_frames; \
if (st->d->short_term_frame_counter == st->d->samples_in_100ms * 30) { \
double st_energy; \
ebur128_energy_shortterm(st, &st_energy); \
if (st_energy >= histogram_energy_boundaries[0]) { \
++st->d->short_term_block_energy_histogram[ \
find_histogram_index(st_energy)]; \
} \
st->d->short_term_frame_counter = st->d->samples_in_100ms * 20; \
} \
} \
\
st->d->needed_frames = st->d->samples_in_100ms; \
\
if (st->d->audio_data_index == st->d->audio_data_frames * st->channels) { \
st->d->audio_data_index = 0; \
} \
} else { \
ebur128_filter_##type(st, srcs, src_index, frames, stride); \
st->d->audio_data_index += frames * st->channels; \
if ((st->mode & FF_EBUR128_MODE_LRA) == FF_EBUR128_MODE_LRA) { \
st->d->short_term_frame_counter += frames; \
} \
st->d->needed_frames -= frames; \
frames = 0; \
} \
} \
}
FF_EBUR128_ADD_FRAMES_PLANAR(short)
FF_EBUR128_ADD_FRAMES_PLANAR(int)
FF_EBUR128_ADD_FRAMES_PLANAR(float)
FF_EBUR128_ADD_FRAMES_PLANAR(double)
#define FF_EBUR128_ADD_FRAMES(type) \
void ff_ebur128_add_frames_##type(FFEBUR128State* st, const type* src, \
size_t frames) { \
int i; \
const type **buf = (const type**)st->d->data_ptrs; \
for (i = 0; i < st->channels; i++) \
buf[i] = src + i; \
ff_ebur128_add_frames_planar_##type(st, buf, frames, st->channels); \
}
FF_EBUR128_ADD_FRAMES(short)
FF_EBUR128_ADD_FRAMES(int)
FF_EBUR128_ADD_FRAMES(float)
FF_EBUR128_ADD_FRAMES(double)
static int ebur128_calc_relative_threshold(FFEBUR128State **sts, size_t size,
double *relative_threshold)
{
size_t i, j;
int above_thresh_counter = 0;
*relative_threshold = 0.0;
for (i = 0; i < size; i++) {
unsigned long *block_energy_histogram = sts[i]->d->block_energy_histogram;
for (j = 0; j < 1000; ++j) {
*relative_threshold += block_energy_histogram[j] * histogram_energies[j];
above_thresh_counter += block_energy_histogram[j];
}
}
if (above_thresh_counter != 0) {
*relative_threshold /= (double)above_thresh_counter;
*relative_threshold *= RELATIVE_GATE_FACTOR;
}
return above_thresh_counter;
}
static int ebur128_gated_loudness(FFEBUR128State ** sts, size_t size,
double *out)
{
double gated_loudness = 0.0;
double relative_threshold;
size_t above_thresh_counter;
size_t i, j, start_index;
for (i = 0; i < size; i++)
if ((sts[i]->mode & FF_EBUR128_MODE_I) != FF_EBUR128_MODE_I)
return AVERROR(EINVAL);
if (!ebur128_calc_relative_threshold(sts, size, &relative_threshold)) {
*out = -HUGE_VAL;
return 0;
}
above_thresh_counter = 0;
if (relative_threshold < histogram_energy_boundaries[0]) {
start_index = 0;
} else {
start_index = find_histogram_index(relative_threshold);
if (relative_threshold > histogram_energies[start_index]) {
++start_index;
}
}
for (i = 0; i < size; i++) {
for (j = start_index; j < 1000; ++j) {
gated_loudness += sts[i]->d->block_energy_histogram[j] *
histogram_energies[j];
above_thresh_counter += sts[i]->d->block_energy_histogram[j];
}
}
if (!above_thresh_counter) {
*out = -HUGE_VAL;
return 0;
}
gated_loudness /= (double) above_thresh_counter;
*out = ebur128_energy_to_loudness(gated_loudness);
return 0;
}
int ff_ebur128_relative_threshold(FFEBUR128State * st, double *out)
{
double relative_threshold;
if ((st->mode & FF_EBUR128_MODE_I) != FF_EBUR128_MODE_I)
return AVERROR(EINVAL);
if (!ebur128_calc_relative_threshold(&st, 1, &relative_threshold)) {
*out = -70.0;
return 0;
}
*out = ebur128_energy_to_loudness(relative_threshold);
return 0;
}
int ff_ebur128_loudness_global(FFEBUR128State * st, double *out)
{
return ebur128_gated_loudness(&st, 1, out);
}
int ff_ebur128_loudness_global_multiple(FFEBUR128State ** sts, size_t size,
double *out)
{
return ebur128_gated_loudness(sts, size, out);
}
static int ebur128_energy_in_interval(FFEBUR128State * st,
size_t interval_frames, double *out)
{
if (interval_frames > st->d->audio_data_frames) {
return AVERROR(EINVAL);
}
ebur128_calc_gating_block(st, interval_frames, out);
return 0;
}
static int ebur128_energy_shortterm(FFEBUR128State * st, double *out)
{
return ebur128_energy_in_interval(st, st->d->samples_in_100ms * 30,
out);
}
int ff_ebur128_loudness_momentary(FFEBUR128State * st, double *out)
{
double energy;
int error = ebur128_energy_in_interval(st, st->d->samples_in_100ms * 4,
&energy);
if (error) {
return error;
} else if (energy <= 0.0) {
*out = -HUGE_VAL;
return 0;
}
*out = ebur128_energy_to_loudness(energy);
return 0;
}
int ff_ebur128_loudness_shortterm(FFEBUR128State * st, double *out)
{
double energy;
int error = ebur128_energy_shortterm(st, &energy);
if (error) {
return error;
} else if (energy <= 0.0) {
*out = -HUGE_VAL;
return 0;
}
*out = ebur128_energy_to_loudness(energy);
return 0;
}
int ff_ebur128_loudness_window(FFEBUR128State * st,
unsigned long window, double *out)
{
double energy;
size_t interval_frames = st->samplerate * window / 1000;
int error = ebur128_energy_in_interval(st, interval_frames, &energy);
if (error) {
return error;
} else if (energy <= 0.0) {
*out = -HUGE_VAL;
return 0;
}
*out = ebur128_energy_to_loudness(energy);
return 0;
}
int ff_ebur128_loudness_range_multiple(FFEBUR128State ** sts, size_t size,
double *out)
{
size_t i, j;
size_t stl_size;
double stl_power, stl_integrated;
double h_en, l_en;
unsigned long hist[1000] = { 0 };
size_t percentile_low, percentile_high;
size_t index;
for (i = 0; i < size; ++i) {
if (sts[i]) {
if ((sts[i]->mode & FF_EBUR128_MODE_LRA) !=
FF_EBUR128_MODE_LRA) {
return AVERROR(EINVAL);
}
}
}
stl_size = 0;
stl_power = 0.0;
for (i = 0; i < size; ++i) {
if (!sts[i])
continue;
for (j = 0; j < 1000; ++j) {
hist[j] += sts[i]->d->short_term_block_energy_histogram[j];
stl_size += sts[i]->d->short_term_block_energy_histogram[j];
stl_power += sts[i]->d->short_term_block_energy_histogram[j]
* histogram_energies[j];
}
}
if (!stl_size) {
*out = 0.0;
return 0;
}
stl_power /= stl_size;
stl_integrated = MINUS_20DB * stl_power;
if (stl_integrated < histogram_energy_boundaries[0]) {
index = 0;
} else {
index = find_histogram_index(stl_integrated);
if (stl_integrated > histogram_energies[index]) {
++index;
}
}
stl_size = 0;
for (j = index; j < 1000; ++j) {
stl_size += hist[j];
}
if (!stl_size) {
*out = 0.0;
return 0;
}
percentile_low = (size_t) ((stl_size - 1) * 0.1 + 0.5);
percentile_high = (size_t) ((stl_size - 1) * 0.95 + 0.5);
stl_size = 0;
j = index;
while (stl_size <= percentile_low) {
stl_size += hist[j++];
}
l_en = histogram_energies[j - 1];
while (stl_size <= percentile_high) {
stl_size += hist[j++];
}
h_en = histogram_energies[j - 1];
*out =
ebur128_energy_to_loudness(h_en) -
ebur128_energy_to_loudness(l_en);
return 0;
}
int ff_ebur128_loudness_range(FFEBUR128State * st, double *out)
{
return ff_ebur128_loudness_range_multiple(&st, 1, out);
}
int ff_ebur128_sample_peak(FFEBUR128State * st,
unsigned int channel_number, double *out)
{
if ((st->mode & FF_EBUR128_MODE_SAMPLE_PEAK) !=
FF_EBUR128_MODE_SAMPLE_PEAK) {
return AVERROR(EINVAL);
} else if (channel_number >= st->channels) {
return AVERROR(EINVAL);
}
*out = st->d->sample_peak[channel_number];
return 0;
}