<!DOCTYPE html><html><head><title></title><style type="text/css">p.MsoNormal,p.MsoNoSpacing{margin:0}</style></head><body><div>On Mon, Aug 17, 2020, at 11:57, Rémi Denis-Courmont wrote:<br></div><blockquote type="cite" id="qt" style=""><div>Hi,<br></div><div><br></div><div>I reject any new code that invokes vlc_object_parent(). Breaks encapsulation.<br></div></blockquote><div><br></div><div>Indeed, this is a RFC, I planned but I forget to talk about this hack in the cover letter.<br></div><div><br></div><div>What about the following solution?<br></div><div> <br></div><div>Add a const struct filter_audio_callbacks *audio in filter_owner_t, like the following:<br></div><div><br></div><div>struct filter_audio_callbacks<br></div><div>{<br></div><div> union {<br></div><div> struct {<br></div><div> void (*on_changed)(...); <br></div><div> } meter_loudness;<br></div><div> };<br></div><div>};<br></div><div><br></div><div>And have the "audio meter" plugin call filter->owner.audio.meter_loudness.on_changed() via a static inline filter_* helper.<br></div><div><br></div><div><br></div><blockquote type="cite" id="qt" style=""><div class="qt-gmail_quote"><div>Le 14 août 2020 15:18:03 GMT+02:00, Thomas Guillem <thomas@gllm.fr> a écrit :<br></div><blockquote class="qt-gmail_quote" style="margin-top:0pt;margin-right:0pt;margin-bottom:0pt;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><pre class="qt-k9mail"><div><hr> configure.ac | 5 +<br></div><div> modules/audio_filter/Makefile.am | 9 +-<br></div><div> modules/audio_filter/libebur128.c | 290 ++++++++++++++++++++++++++++++<br></div><div> 3 files changed, 303 insertions(+), 1 deletion(-)<br></div><div> create mode 100644 modules/audio_filter/libebur128.c<br></div><div><br></div><div>diff --git a/configure.ac b/configure.ac<br></div><div>index 149c7c926b4..862546e49f3 100644<br></div><div>--- a/configure.ac<br></div><div>+++ b/configure.ac<br></div><div>@@ -3919,6 +3919,11 @@ dnl soxr module<br></div><div> dnl<br></div><div> PKG_ENABLE_MODULES_VLC([SOXR], [], [soxr >= 0.1.2], [SoX Resampler library], [auto])<br></div><div> <br></div><div>+dnl<br></div><div>+dnl libebur128 module<br></div><div>+dnl<br></div><div>+PKG_ENABLE_MODULES_VLC([EBUR128], [], [libebur128 >= 1.2.4], [EBU R 128 standard for loudness normalisation], [auto])<br></div><div>+<br></div><div> dnl<br></div><div> dnl OS/2 KAI plugin<br></div><div> dnl<br></div><div>diff --git a/modules/audio_filter/Makefile.am b/modules/audio_filter/Makefile.am<br></div><div>index 279be02040f..028b188bfe2 100644<br></div><div>--- a/modules/audio_filter/Makefile.am<br></div><div>+++ b/modules/audio_filter/Makefile.am<br></div><div>@@ -125,14 +125,21 @@ libsoxr_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(SOXR_CFLAGS)<br></div><div> libsoxr_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(audio_filterdir)'<br></div><div> libsoxr_plugin_la_LIBADD = $(SOXR_LIBS) $(LIBM)<br></div><div> <br></div><div>+libebur128_plugin_la_SOURCES = audio_filter/libebur128.c<br></div><div>+libebur128_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(EBUR128_CFLAGS)<br></div><div>+libebur128_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(audio_filterdir)'<br></div><div>+libebur128_plugin_la_LIBADD = $(EBUR128_LIBS)<br></div><div>+<br></div><div> audio_filter_LTLIBRARIES += \<br></div><div> $(LTLIBsamplerate) \<br></div><div> $(LTLIBsoxr) \<br></div><div>+ $(LTLIBebur128) \<br></div><div> libugly_resampler_plugin.la<br></div><div> EXTRA_LTLIBRARIES += \<br></div><div> libbandlimited_resampler_plugin.la \<br></div><div> libsamplerate_plugin.la \<br></div><div>- libsoxr_plugin.la<br></div><div>+ libsoxr_plugin.la \<br></div><div>+ libebur128_plugin.la<br></div><div> <br></div><div> libspeex_resampler_plugin_la_SOURCES = audio_filter/resampler/speex.c<br></div><div> libspeex_resampler_plugin_la_CFLAGS = $(AM_CFLAGS) $(SPEEXDSP_CFLAGS)<br></div><div>diff --git a/modules/audio_filter/libebur128.c b/modules/audio_filter/libebur128.c<br></div><div>new file mode 100644<br></div><div>index 00000000000..bd5464dc6d1<br></div><div>--- /dev/null<br></div><div>+++ b/modules/audio_filter/libebur128.c<br></div><div>@@ -0,0 +1,290 @@<br></div><div>+/*****************************************************************************<br></div><div>+ * libebur128.c : libebur128 filter<br></div><div>+ *****************************************************************************<br></div><div>+ * Copyright © 2020 Videolabs<br></div><div>+ *<br></div><div>+ * This program is free software; you can redistribute it and/or modify it<br></div><div>+ * under the terms of the GNU Lesser General Public License as published by<br></div><div>+ * the Free Software Foundation; either version 2.1 of the License, or<br></div><div>+ * (at your option) any later version.<br></div><div>+ *<br></div><div>+ * This program is distributed in the hope that it will be useful,<br></div><div>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br></div><div>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br></div><div>+ * GNU Lesser General Public License for more details.<br></div><div>+ *<br></div><div>+ * You should have received a copy of the GNU Lesser General Public License<br></div><div>+ * along with this program; if not, write to the Free Software Foundation,<br></div><div>+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.<br></div><div>+ *****************************************************************************/<br></div><div>+<br></div><div>+#ifdef HAVE_CONFIG_H<br></div><div>+# include "config.h"<br></div><div>+#endif<br></div><div>+<br></div><div>+#include <vlc_common.h><br></div><div>+#include <vlc_aout.h><br></div><div>+#include <vlc_filter.h><br></div><div>+#include <vlc_modules.h><br></div><div>+#include <vlc_plugin.h><br></div><div>+<br></div><div>+#include <ebur128.h><br></div><div>+<br></div><div>+#define UPDATE_INTERVAL VLC_TICK_FROM_MS(400)<br></div><div>+<br></div><div>+struct filter_sys<br></div><div>+{<br></div><div>+ ebur128_state *state;<br></div><div>+ vlc_tick_t last_update;<br></div><div>+ bool new_frames;<br></div><div>+};<br></div><div>+<br></div><div>+static ebur128_state *<br></div><div>+CreateEbuR128State(filter_t *filter)<br></div><div>+{<br></div><div>+ int mode = var_InheritBool(filter, "ebur128-fullmeter")<br></div><div>+ ? EBUR128_MODE_TRUE_PEAK|EBUR128_MODE_LRA : EBUR128_MODE_M;<br></div><div>+<br></div><div>+ ebur128_state *state =<br></div><div>+ ebur128_init(filter->fmt_in.audio.i_channels, filter->fmt_in.audio.i_rate, mode);<br></div><div>+ if (state == NULL)<br></div><div>+ return NULL;<br></div><div>+<br></div><div>+ /* TODO: improve */<br></div><div>+ unsigned channels_set = 2;<br></div><div>+ int error;<br></div><div>+ if (filter->fmt_in.audio.i_physical_channels == AOUT_CHANS_5_1<br></div><div>+ || filter->fmt_in.audio.i_physical_channels == AOUT_CHANS_7_1)<br></div><div>+ {<br></div><div>+ error = ebur128_set_channel(state, 2, EBUR128_LEFT_SURROUND);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ goto error;<br></div><div>+ ebur128_set_channel(state, 3, EBUR128_RIGHT_SURROUND);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ goto error;<br></div><div>+ ebur128_set_channel(state, 4, EBUR128_CENTER);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ goto error;<br></div><div>+<br></div><div>+ channels_set += 3;<br></div><div>+ }<br></div><div>+<br></div><div>+ for (unsigned i = channels_set; i < filter->fmt_in.audio.i_channels; ++i)<br></div><div>+ {<br></div><div>+ error = ebur128_set_channel(state, i, EBUR128_UNUSED);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ goto error;<br></div><div>+ }<br></div><div>+<br></div><div>+ return state;<br></div><div>+error:<br></div><div>+ ebur128_destroy(&state);<br></div><div>+ return NULL;<br></div><div>+}<br></div><div>+<br></div><div>+static int<br></div><div>+SendLoudnessMeter(filter_t *filter)<br></div><div>+{<br></div><div>+ struct filter_sys *sys = filter->p_sys;<br></div><div>+<br></div><div>+ int error;<br></div><div>+ struct vlc_audio_loudness_meter meter = { 0, 0, 0, 0, 0 };<br></div><div>+<br></div><div>+ error = ebur128_loudness_momentary(sys->state, &meter.loudness_momentary);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ return error;<br></div><div>+<br></div><div>+ if ((sys->state->mode & EBUR128_MODE_S) == EBUR128_MODE_S)<br></div><div>+ {<br></div><div>+ error = ebur128_loudness_shortterm(sys->state, &meter.loudness_shortterm);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ return error;<br></div><div>+ }<br></div><div>+ if ((sys->state->mode & EBUR128_MODE_I) == EBUR128_MODE_I)<br></div><div>+ {<br></div><div>+ error = ebur128_loudness_global(sys->state, &meter.loudness_integrated);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ return error;<br></div><div>+<br></div><div>+ }<br></div><div>+ if ((sys->state->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA)<br></div><div>+ {<br></div><div>+ error = ebur128_loudness_range(sys->state, &meter.loudness_range);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ return error;<br></div><div>+ }<br></div><div>+ if ((sys->state->mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK)<br></div><div>+ {<br></div><div>+ for (unsigned i = 0; i < filter->fmt_in.audio.i_channels; ++i)<br></div><div>+ {<br></div><div>+ double truepeak;<br></div><div>+ error = ebur128_true_peak(sys->state, 0, &truepeak);<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ return error;<br></div><div>+ if (truepeak > meter.truepeak)<br></div><div>+ meter.truepeak = truepeak;<br></div><div>+ }<br></div><div>+ }<br></div><div>+<br></div><div>+ var_SetAddress(vlc_object_parent(VLC_OBJECT(filter)), "loudness-meter", &meter);<br></div><div>+<br></div><div>+ return EBUR128_SUCCESS;<br></div><div>+}<br></div><div>+<br></div><div>+static block_t *<br></div><div>+Process(filter_t *filter, block_t *block)<br></div><div>+{<br></div><div>+ struct filter_sys *sys = filter->p_sys;<br></div><div>+ int error;<br></div><div>+ block_t *out = block;<br></div><div>+<br></div><div>+ if (unlikely(sys->state == NULL))<br></div><div>+ {<br></div><div>+ /* Can happen after a flush */<br></div><div>+ sys->state = CreateEbuR128State(filter);<br></div><div>+ if (sys->state == NULL)<br></div><div>+ return out;<br></div><div>+ }<br></div><div>+<br></div><div>+ switch (filter->fmt_in.i_codec)<br></div><div>+ {<br></div><div>+ case VLC_CODEC_U8:<br></div><div>+ {<br></div><div>+ /* Convert to S16N */<br></div><div>+ block_t *block_s16 = block_Alloc(block->i_buffer * 2);<br></div><div>+ if (unlikely(block_s16 == NULL))<br></div><div>+ return out;<br></div><div>+<br></div><div>+ block_CopyProperties(block_s16, block);<br></div><div>+ uint8_t *src = (uint8_t *)block->p_buffer;<br></div><div>+ int16_t *dst = (int16_t *)block_s16->p_buffer;<br></div><div>+ for (size_t i = block->i_buffer; i--;)<br></div><div>+ *dst++ = ((*src++) << 8) - 0x8000;<br></div><div>+<br></div><div>+ block = block_s16;<br></div><div>+ }<br></div><div>+ /* Fallthrough */<br></div><div>+ case VLC_CODEC_S16N:<br></div><div>+ error = ebur128_add_frames_short(sys->state,<br></div><div>+ (const short *)block->p_buffer,<br></div><div>+ block->i_nb_samples);<br></div><div>+ break;<br></div><div>+ case VLC_CODEC_S32N:<br></div><div>+ error = ebur128_add_frames_int(sys->state,<br></div><div>+ (const int *) block->p_buffer,<br></div><div>+ block->i_nb_samples);<br></div><div>+ break;<br></div><div>+ case VLC_CODEC_FL32:<br></div><div>+ error = ebur128_add_frames_float(sys->state,<br></div><div>+ (const float *) block->p_buffer,<br></div><div>+ block->i_nb_samples);<br></div><div>+ break;<br></div><div>+ case VLC_CODEC_FL64:<br></div><div>+ error = ebur128_add_frames_double(sys->state,<br></div><div>+ (const double *) block->p_buffer,<br></div><div>+ block->i_nb_samples);<br></div><div>+ break;<br></div><div>+ default: vlc_assert_unreachable();<br></div><div>+ }<br></div><div>+<br></div><div>+ if (error != EBUR128_SUCCESS)<br></div><div>+ {<br></div><div>+ msg_Warn(filter, "ebur128_add_frames_*() failed: %d\n", error);<br></div><div>+ return out;<br></div><div>+ }<br></div><div>+<br></div><div>+ if (sys->last_update == VLC_TICK_INVALID)<br></div><div>+ sys->last_update = out->i_pts;<br></div><div>+<br></div><div>+ if (out->i_pts + out->i_length - sys->last_update >= UPDATE_INTERVAL)<br></div><div>+ {<br></div><div>+ error = SendLoudnessMeter(filter);<br></div><div>+ if (error == EBUR128_SUCCESS)<br></div><div>+ {<br></div><div>+ sys->last_update = out->i_pts + out->i_length;<br></div><div>+ sys->new_frames = false;<br></div><div>+ }<br></div><div>+ }<br></div><div>+ else<br></div><div>+ sys->new_frames = true;<br></div><div>+<br></div><div>+ return out;<br></div><div>+}<br></div><div>+<br></div><div>+static void<br></div><div>+Flush(filter_t *filter)<br></div><div>+{<br></div><div>+ struct filter_sys *sys = filter->p_sys;<br></div><div>+<br></div><div>+ if (sys->state != NULL)<br></div><div>+ {<br></div><div>+ if (sys->new_frames)<br></div><div>+ {<br></div><div>+ SendLoudnessMeter(filter);<br></div><div>+ sys->new_frames = false;<br></div><div>+ }<br></div><div>+ sys->last_update = VLC_TICK_INVALID;<br></div><div>+<br></div><div>+ ebur128_destroy(&sys->state);<br></div><div>+ }<br></div><div>+}<br></div><div>+<br></div><div>+static int Open(vlc_object_t *this)<br></div><div>+{<br></div><div>+ filter_t *filter = (filter_t *) this;<br></div><div>+<br></div><div>+ switch (filter->fmt_in.i_codec)<br></div><div>+ {<br></div><div>+ case VLC_CODEC_U8:<br></div><div>+ case VLC_CODEC_S16N:<br></div><div>+ case VLC_CODEC_S32N:<br></div><div>+ case VLC_CODEC_FL32:<br></div><div>+ case VLC_CODEC_FL64:<br></div><div>+ break;<br></div><div>+ default:<br></div><div>+ return VLC_EGENERIC;<br></div><div>+ }<br></div><div>+<br></div><div>+ if (var_Type(vlc_object_parent(this), "loudness-meter") != VLC_VAR_ADDRESS)<br></div><div>+ return VLC_EGENERIC;<br></div><div>+<br></div><div>+ struct filter_sys *sys = malloc(sizeof(*sys));<br></div><div>+ if (sys == NULL)<br></div><div>+ return VLC_ENOMEM;<br></div><div>+<br></div><div>+ sys->last_update = VLC_TICK_INVALID;<br></div><div>+ sys->new_frames = false;<br></div><div>+ sys->state = CreateEbuR128State(filter);<br></div><div>+ if (sys->state == NULL)<br></div><div>+ {<br></div><div>+ free(sys);<br></div><div>+ return VLC_EGENERIC;<br></div><div>+ }<br></div><div>+<br></div><div>+ filter->p_sys = sys;<br></div><div>+ filter->fmt_out.audio = filter->fmt_in.audio;<br></div><div>+ filter->pf_audio_filter = Process;<br></div><div>+ filter->pf_flush = Flush;<br></div><div>+ return VLC_SUCCESS;<br></div><div>+}<br></div><div>+<br></div><div>+static void<br></div><div>+Close(vlc_object_t *this)<br></div><div>+{<br></div><div>+ filter_t *filter = (filter_t*) this;<br></div><div>+ struct filter_sys *sys = filter->p_sys;<br></div><div>+<br></div><div>+ if (sys->state != NULL)<br></div><div>+ ebur128_destroy(&sys->state);<br></div><div>+ free(filter->p_sys);<br></div><div>+}<br></div><div>+<br></div><div>+vlc_module_begin()<br></div><div>+ set_shortname("EBU R 128")<br></div><div>+ set_description("EBU R128 standard for loudness normalisation")<br></div><div>+ set_category(CAT_AUDIO)<br></div><div>+ set_subcategory(SUBCAT_AUDIO_AFILTER)<br></div><div>+ add_bool("ebur128-fullmeter", false, NULL, NULL, false)<br></div><div>+ set_capability("audio meter", 0)<br></div><div>+ set_callbacks(Open, Close)<br></div><div>+vlc_module_end()<br></div></pre></blockquote></div><div><br></div><div>-- <br></div><div>Envoyé de mon appareil Android avec Courriel K-9 Mail. Veuillez excuser ma brièveté. <br></div><div>_______________________________________________<br></div><div>vlc-devel mailing list<br></div><div>To unsubscribe or modify your subscription options:<br></div><div><a href="https://mailman.videolan.org/listinfo/vlc-devel">https://mailman.videolan.org/listinfo/vlc-devel</a><br></div></blockquote><div><br></div></body></html>