499 lines
16 KiB
Diff
499 lines
16 KiB
Diff
|
src/DynamicAudioNormalizerSoX.c | 479 ++++++++++++++++++++++++++++++++++++++++
|
||
|
src/effects.h | 1 +
|
||
|
2 files changed, 480 insertions(+)
|
||
|
|
||
|
diff --git a/src/DynamicAudioNormalizerSoX.c b/src/DynamicAudioNormalizerSoX.c
|
||
|
new file mode 100644
|
||
|
index 0000000..e901f94
|
||
|
--- /dev/null
|
||
|
+++ b/src/DynamicAudioNormalizerSoX.c
|
||
|
@@ -0,0 +1,479 @@
|
||
|
+/* ================================================================================== */
|
||
|
+/* Dynamic Audio Normalizer - SoX Effect Wrapper */
|
||
|
+/* Copyright (c) 2014 LoRd_MuldeR <mulder2@gmx.de>. Some rights reserved. */
|
||
|
+/* */
|
||
|
+/* Permission is hereby granted, free of charge, to any person obtaining a copy */
|
||
|
+/* of this software and associated documentation files (the "Software"), to deal */
|
||
|
+/* in the Software without restriction, including without limitation the rights */
|
||
|
+/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
|
||
|
+/* copies of the Software, and to permit persons to whom the Software is */
|
||
|
+/* furnished to do so, subject to the following conditions: */
|
||
|
+/* */
|
||
|
+/* The above copyright notice and this permission notice shall be included in */
|
||
|
+/* all copies or substantial portions of the Software. */
|
||
|
+/* */
|
||
|
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
|
||
|
+/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
|
||
|
+/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
|
||
|
+/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
|
||
|
+/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
|
||
|
+/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
|
||
|
+/* THE SOFTWARE. */
|
||
|
+/* */
|
||
|
+/* http://opensource.org/licenses/MIT */
|
||
|
+/* ================================================================================== */
|
||
|
+
|
||
|
+/*Shut up warnings*/
|
||
|
+#define _CRT_SECURE_NO_WARNINGS
|
||
|
+
|
||
|
+/*printf() macros*/
|
||
|
+#ifndef __STDC_FORMAT_MACROS
|
||
|
+#define __STDC_FORMAT_MACROS 1
|
||
|
+#endif
|
||
|
+
|
||
|
+/*SoX internal stuff*/
|
||
|
+#include "sox_i.h"
|
||
|
+
|
||
|
+/*Win32 Unicode support*/
|
||
|
+#ifdef _WIN32
|
||
|
+#include "unicode_support.h"
|
||
|
+#else
|
||
|
+#define lsx_fopen(X,Y) fopen((X),(Y))
|
||
|
+#endif /*_WIN32*/
|
||
|
+
|
||
|
+/*StdLib*/
|
||
|
+#include <string.h>
|
||
|
+#include <stdarg.h>
|
||
|
+#include <ctype.h>
|
||
|
+#include <inttypes.h>
|
||
|
+
|
||
|
+/*Linkage*/
|
||
|
+#if defined(_MSC_VER) && defined(_MT)
|
||
|
+#define MDYNAMICAUDIONORMALIZER_STATIC
|
||
|
+static const char *LINKAGE = "Static";
|
||
|
+#else
|
||
|
+static const char *LINKAGE = "Shared";
|
||
|
+#endif
|
||
|
+
|
||
|
+/*Dynamic Audio Normalizer*/
|
||
|
+#include <DynamicAudioNormalizer.h>
|
||
|
+
|
||
|
+/* ================================================================================== */
|
||
|
+/* Private Data */
|
||
|
+/* ================================================================================== */
|
||
|
+
|
||
|
+typedef struct
|
||
|
+{
|
||
|
+ uint32_t frameLenMsec;
|
||
|
+ uint32_t filterSize;
|
||
|
+ double peakValue;
|
||
|
+ double maxAmplification;
|
||
|
+ double targetRms;
|
||
|
+ double compressFactor;
|
||
|
+ int channelsCoupled;
|
||
|
+ int enableDCCorrection;
|
||
|
+ int altBoundaryMode;
|
||
|
+ FILE *logFile;
|
||
|
+}
|
||
|
+settings_t;
|
||
|
+
|
||
|
+typedef struct
|
||
|
+{
|
||
|
+ settings_t settings;
|
||
|
+ MDynamicAudioNormalizer_Handle *instance;
|
||
|
+ double **temp;
|
||
|
+ size_t tempSize;
|
||
|
+}
|
||
|
+priv_t;
|
||
|
+
|
||
|
+/* ================================================================================== */
|
||
|
+/* Internal Functions */
|
||
|
+/* ================================================================================== */
|
||
|
+
|
||
|
+static int parseArgInt(const char *const str, uint32_t *parameter, const uint32_t min_val, const uint32_t max_val)
|
||
|
+{
|
||
|
+ uint32_t temp;
|
||
|
+ if(sscanf(str, "%u", &temp) == 1)
|
||
|
+ {
|
||
|
+ *parameter = max(min_val, min(max_val, temp));
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+ lsx_fail("Failed to parse integral value `%s'", str);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int parseArgDbl(const char *const str, double *parameter, const double min_val, const double max_val)
|
||
|
+{
|
||
|
+ double temp;
|
||
|
+ if(sscanf(str, "%lf", &temp) == 1)
|
||
|
+ {
|
||
|
+ *parameter = max(min_val, min(max_val, temp));
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+ lsx_fail("Failed to parse floating point value `%s'", str);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+#define TRY_PARSE(TYPE, PARAM, MIN, MAX) do \
|
||
|
+{ \
|
||
|
+ if(!parseArg##TYPE(optstate.arg, &(PARAM), (MIN), (MAX))) \
|
||
|
+ { \
|
||
|
+ return 0; \
|
||
|
+ } \
|
||
|
+} \
|
||
|
+while(0)
|
||
|
+
|
||
|
+static void dynaudnorm_defaults(settings_t *settings)
|
||
|
+{
|
||
|
+ memset(settings, 0, sizeof(settings_t));
|
||
|
+
|
||
|
+ settings->frameLenMsec = 500;
|
||
|
+ settings->filterSize = 31;
|
||
|
+ settings->peakValue = 0.95;
|
||
|
+ settings->maxAmplification = 10.0;
|
||
|
+ settings->targetRms = 0.0;
|
||
|
+ settings->compressFactor = 0.0;
|
||
|
+ settings->channelsCoupled = 1;
|
||
|
+ settings->enableDCCorrection = 0;
|
||
|
+ settings->altBoundaryMode = 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int dynaudnorm_parse_args(settings_t *settings, int argc, char **argv)
|
||
|
+{
|
||
|
+ static const char *const opts = "+f:g:p:m:r:ncbs:l:";
|
||
|
+ lsx_getopt_t optstate; char c;
|
||
|
+ lsx_getopt_init(argc, argv, opts, NULL, lsx_getopt_flag_opterr, 1, &optstate);
|
||
|
+
|
||
|
+ while((c = lsx_getopt(&optstate)) != -1)
|
||
|
+ {
|
||
|
+ switch(tolower(c))
|
||
|
+ {
|
||
|
+ case 'f':
|
||
|
+ TRY_PARSE(Int, settings->frameLenMsec, 10, 8000);
|
||
|
+ break;
|
||
|
+ case 'g':
|
||
|
+ TRY_PARSE(Int, settings->filterSize, 3, 301);
|
||
|
+ settings->filterSize += ((settings->filterSize + 1) % 2);
|
||
|
+ break;
|
||
|
+ case 'p':
|
||
|
+ TRY_PARSE(Dbl, settings->peakValue, 0.0, 1.0);
|
||
|
+ break;
|
||
|
+ case 'm':
|
||
|
+ TRY_PARSE(Dbl, settings->maxAmplification, 1.0, 100.0);
|
||
|
+ break;
|
||
|
+ case 'r':
|
||
|
+ TRY_PARSE(Dbl, settings->targetRms, 0.0, 1.0);
|
||
|
+ break;
|
||
|
+ case 'n':
|
||
|
+ settings->channelsCoupled = 0;
|
||
|
+ break;
|
||
|
+ case 'c':
|
||
|
+ settings->enableDCCorrection = 1;
|
||
|
+ break;
|
||
|
+ case 'b':
|
||
|
+ settings->altBoundaryMode = 1;
|
||
|
+ break;
|
||
|
+ case 's':
|
||
|
+ TRY_PARSE(Dbl, settings->compressFactor, 0.0, 30.0);
|
||
|
+ break;
|
||
|
+ case 'l':
|
||
|
+ if(!settings->logFile)
|
||
|
+ {
|
||
|
+ settings->logFile = lsx_fopen(optstate.arg, "w");
|
||
|
+ if(!settings->logFile)
|
||
|
+ {
|
||
|
+ lsx_warn("Failed to open logfile `%s'", optstate.arg);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return 1;
|
||
|
+}
|
||
|
+
|
||
|
+static void dynaudnorm_deinterleave(double **temp, const sox_sample_t *const in, const size_t samples_per_channel, const unsigned channels)
|
||
|
+{
|
||
|
+ size_t i, c, in_pos = 0;
|
||
|
+
|
||
|
+ for(i = 0; i < samples_per_channel; i++)
|
||
|
+ {
|
||
|
+ for(c = 0; c < channels; c++)
|
||
|
+ {
|
||
|
+ temp[c][i] = ((double)in[in_pos++]) / ((double)SOX_INT32_MAX);
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void dynaudnorm_interleave(sox_sample_t *const out, const double *const *const temp, const size_t samples_per_channel, const unsigned channels)
|
||
|
+{
|
||
|
+ size_t i, c, out_pos = 0;
|
||
|
+
|
||
|
+ for(i = 0; i < samples_per_channel; i++)
|
||
|
+ {
|
||
|
+ for(c = 0; c < channels; c++)
|
||
|
+ {
|
||
|
+ out[out_pos++] = (sox_sample_t) round(min(1.0, max(-1.0, temp[c][i])) * ((double)SOX_INT32_MAX));
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void dynaudnorm_print(sox_effect_t *effp, const char *const fmt, ...)
|
||
|
+{
|
||
|
+ if(effp->global_info->global_info->output_message_handler)
|
||
|
+ {
|
||
|
+ va_list arg_list;
|
||
|
+ va_start(arg_list, fmt);
|
||
|
+ vfprintf(stderr, fmt, arg_list);
|
||
|
+ va_end(arg_list);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void dynaudnorm_log(const int logLevel, const char *const message)
|
||
|
+{
|
||
|
+ switch(logLevel)
|
||
|
+ {
|
||
|
+ case 0:
|
||
|
+ lsx_report("%s", message);
|
||
|
+ break;
|
||
|
+ case 1:
|
||
|
+ lsx_warn("%s", message);
|
||
|
+ break;
|
||
|
+ case 2:
|
||
|
+ lsx_fail("%s", message);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void dynaudnorm_update_buffsize(const sox_effect_t *const effp, priv_t *const p, const size_t input_samples)
|
||
|
+{
|
||
|
+ size_t c;
|
||
|
+ if(input_samples > p->tempSize)
|
||
|
+ {
|
||
|
+ lsx_warn("Increasing buffer size: %" PRIu64 " -> %" PRIu64, (uint64_t)p->tempSize, (uint64_t)input_samples);
|
||
|
+ for(c = 0; c < effp->in_signal.channels; c++)
|
||
|
+ {
|
||
|
+ p->temp[c] = lsx_realloc(p->temp[c], input_samples * sizeof(double));
|
||
|
+ }
|
||
|
+ p->tempSize = input_samples;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/* ================================================================================== */
|
||
|
+/* SoX Callback Functions */
|
||
|
+/* ================================================================================== */
|
||
|
+
|
||
|
+static int dynaudnorm_kill(sox_effect_t *effp)
|
||
|
+{
|
||
|
+ lsx_report("dynaudnorm_kill()");
|
||
|
+ lsx_report("flows=%" PRIu64 ", flow=%" PRIu64 , (uint64_t)effp->flows, (uint64_t)effp->flow);
|
||
|
+ return SOX_SUCCESS;
|
||
|
+}
|
||
|
+
|
||
|
+static int dynaudnorm_create(sox_effect_t *effp, int argc, char **argv)
|
||
|
+{
|
||
|
+ priv_t *const p = (priv_t *)effp->priv;
|
||
|
+
|
||
|
+ lsx_report("dynaudnorm_create()");
|
||
|
+ lsx_report("flows=%" PRIu64 ", flow=%" PRIu64 , (uint64_t)effp->flows, (uint64_t)effp->flow);
|
||
|
+
|
||
|
+ memset(effp->priv, 0, sizeof(priv_t));
|
||
|
+ dynaudnorm_defaults(&p->settings);
|
||
|
+
|
||
|
+ if(!dynaudnorm_parse_args(&p->settings, argc, argv))
|
||
|
+ {
|
||
|
+ return lsx_usage(effp);
|
||
|
+ }
|
||
|
+
|
||
|
+ return SOX_SUCCESS;
|
||
|
+}
|
||
|
+
|
||
|
+static int dynaudnorm_stop(sox_effect_t *effp)
|
||
|
+{
|
||
|
+ priv_t *const p = (priv_t *)effp->priv;
|
||
|
+ size_t c;
|
||
|
+
|
||
|
+ lsx_report("dynaudnorm_stop()");
|
||
|
+
|
||
|
+ if(p->instance)
|
||
|
+ {
|
||
|
+ MDYNAMICAUDIONORMALIZER_FUNCTION(destroyInstance)(&p->instance);
|
||
|
+ p->instance = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if(p->settings.logFile)
|
||
|
+ {
|
||
|
+ fclose(p->settings.logFile);
|
||
|
+ p->settings.logFile = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if(p->temp)
|
||
|
+ {
|
||
|
+ for(c = 0; c < effp->in_signal.channels; c++)
|
||
|
+ {
|
||
|
+ free(p->temp[c]);
|
||
|
+ p->temp[c] = NULL;
|
||
|
+ }
|
||
|
+ free(p->temp);
|
||
|
+ p->temp = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ return SOX_SUCCESS;
|
||
|
+}
|
||
|
+
|
||
|
+static int dynaudnorm_start(sox_effect_t *effp)
|
||
|
+{
|
||
|
+ priv_t *const p = (priv_t *)effp->priv;
|
||
|
+
|
||
|
+ lsx_report("dynaudnorm_start()");
|
||
|
+ lsx_report("flows=%" PRIu64 ", flow=%" PRIu64 ", in_signal.rate=%.2f, in_signal.channels=%" PRIu64, (uint64_t)effp->flows, (uint64_t)effp->flow, effp->in_signal.rate, (uint64_t)effp->in_signal.channels);
|
||
|
+
|
||
|
+ if((effp->flow == 0) && (effp->global_info->global_info->verbosity > 1))
|
||
|
+ {
|
||
|
+ uint32_t versionMajor, versionMinor, versionPatch;
|
||
|
+ const char *buildDate, *buildTime, *buildCompiler, *buildArch; int buildDebug;
|
||
|
+
|
||
|
+ MDYNAMICAUDIONORMALIZER_FUNCTION(getVersionInfo)(&versionMajor, &versionMinor, &versionPatch);
|
||
|
+ MDYNAMICAUDIONORMALIZER_FUNCTION(getBuildInfo)(&buildDate, &buildTime, &buildCompiler, &buildArch, &buildDebug);
|
||
|
+
|
||
|
+ dynaudnorm_print(effp, "\n---------------------------------------------------------------------------\n");
|
||
|
+ dynaudnorm_print(effp, "Dynamic Audio Normalizer (SoX Wrapper), Version %u.%02u-%u, %s\n", versionMajor, versionMinor, versionPatch, LINKAGE);
|
||
|
+ dynaudnorm_print(effp, "Copyright (c) 2014 LoRd_MuldeR <mulder2@gmx.de>. Some rights reserved.\n");
|
||
|
+ dynaudnorm_print(effp, "Built on %s at %s with %s for %s.\n\n", buildDate, buildTime, buildCompiler, buildArch);
|
||
|
+ dynaudnorm_print(effp, "This program is free software: you can redistribute it and/or modify\n");
|
||
|
+ dynaudnorm_print(effp, "it under the terms of the GNU General Public License <http://www.gnu.org/>.\n");
|
||
|
+ dynaudnorm_print(effp, "Note that this program is distributed with ABSOLUTELY NO WARRANTY.\n");
|
||
|
+ dynaudnorm_print(effp, "---------------------------------------------------------------------------\n\n");
|
||
|
+ }
|
||
|
+
|
||
|
+ p->tempSize = (size_t) max(ceil(effp->in_signal.rate), 8192.0); /*initial buffer size is one second*/
|
||
|
+
|
||
|
+ p->instance = MDYNAMICAUDIONORMALIZER_FUNCTION(createInstance)
|
||
|
+ (
|
||
|
+ effp->in_signal.channels,
|
||
|
+ (uint32_t) round(effp->in_signal.rate),
|
||
|
+ p->settings.frameLenMsec,
|
||
|
+ p->settings.filterSize,
|
||
|
+ p->settings.peakValue,
|
||
|
+ p->settings.maxAmplification,
|
||
|
+ p->settings.targetRms,
|
||
|
+ p->settings.compressFactor,
|
||
|
+ p->settings.channelsCoupled,
|
||
|
+ p->settings.enableDCCorrection,
|
||
|
+ p->settings.altBoundaryMode,
|
||
|
+ p->settings.logFile
|
||
|
+ );
|
||
|
+
|
||
|
+ if(p->instance)
|
||
|
+ {
|
||
|
+ size_t c;
|
||
|
+ p->temp = (double**) lsx_calloc(effp->in_signal.channels, sizeof(double*));
|
||
|
+ for(c = 0; c < effp->in_signal.channels; c++)
|
||
|
+ {
|
||
|
+ p->temp[c] = (double*) lsx_calloc(p->tempSize, sizeof(double));
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return (p->instance) ? SOX_SUCCESS : SOX_EINVAL;
|
||
|
+}
|
||
|
+
|
||
|
+static int dynaudnorm_flow(sox_effect_t *effp, const sox_sample_t *ibuf, sox_sample_t *obuf, size_t *isamp, size_t *osamp)
|
||
|
+{
|
||
|
+ priv_t *const p = (priv_t *)effp->priv;
|
||
|
+ const size_t input_samples = min((*isamp), (*osamp)) / effp->in_signal.channels; /*this is per channel!*/
|
||
|
+ int64_t output_samples = 0;
|
||
|
+
|
||
|
+ lsx_debug("dynaudnorm_flow()");
|
||
|
+ dynaudnorm_update_buffsize(effp, p, input_samples);
|
||
|
+
|
||
|
+ if(input_samples > 0)
|
||
|
+ {
|
||
|
+ dynaudnorm_deinterleave(p->temp, ibuf, input_samples, effp->in_signal.channels);
|
||
|
+ if(!MDYNAMICAUDIONORMALIZER_FUNCTION(processInplace)(p->instance, p->temp, ((int64_t) input_samples), &output_samples))
|
||
|
+ {
|
||
|
+ return SOX_EOF;
|
||
|
+ }
|
||
|
+ if(output_samples > 0)
|
||
|
+ {
|
||
|
+ dynaudnorm_interleave(obuf, ((const double**) p->temp), ((size_t) output_samples), effp->in_signal.channels);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+ *isamp = (size_t)(input_samples * effp->in_signal.channels);
|
||
|
+ *osamp = (size_t)(output_samples * effp->in_signal.channels);
|
||
|
+
|
||
|
+ return SOX_SUCCESS;
|
||
|
+}
|
||
|
+
|
||
|
+static int dynaudnorm_drain(sox_effect_t * effp, sox_sample_t * obuf, size_t * osamp)
|
||
|
+{
|
||
|
+ priv_t *const p = (priv_t *)effp->priv;
|
||
|
+ const size_t input_samples = (*osamp) / effp->in_signal.channels; /*this is per channel!*/
|
||
|
+ int64_t output_samples = 0;
|
||
|
+
|
||
|
+ lsx_debug("dynaudnorm_drain()");
|
||
|
+ dynaudnorm_update_buffsize(effp, p, input_samples);
|
||
|
+
|
||
|
+ if(input_samples > 0)
|
||
|
+ {
|
||
|
+ if(!MDYNAMICAUDIONORMALIZER_FUNCTION(flushBuffer)(p->instance, p->temp, ((int64_t) input_samples), &output_samples))
|
||
|
+ {
|
||
|
+ return SOX_EOF;
|
||
|
+ }
|
||
|
+ if(output_samples > 0)
|
||
|
+ {
|
||
|
+ dynaudnorm_interleave(obuf, ((const double**) p->temp), ((size_t) output_samples), effp->in_signal.channels);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ lsx_warn("drain() was called with zero-size output buffer!");
|
||
|
+ }
|
||
|
+
|
||
|
+ *osamp = (size_t)(output_samples * effp->in_signal.channels);
|
||
|
+ return SOX_SUCCESS;
|
||
|
+}
|
||
|
+
|
||
|
+/* ================================================================================== */
|
||
|
+/* SoX Public API */
|
||
|
+/* ================================================================================== */
|
||
|
+
|
||
|
+sox_effect_handler_t const * lsx_dynaudnorm_effect_fn(void)
|
||
|
+{
|
||
|
+ static sox_effect_handler_t handler =
|
||
|
+ {
|
||
|
+ "dynaudnorm", NULL, SOX_EFF_MCHAN,
|
||
|
+ dynaudnorm_create, dynaudnorm_start, dynaudnorm_flow, dynaudnorm_drain, dynaudnorm_stop, dynaudnorm_kill, sizeof(priv_t)
|
||
|
+ };
|
||
|
+
|
||
|
+ static char const * lines[] =
|
||
|
+ {
|
||
|
+ "[options]",
|
||
|
+ "",
|
||
|
+ "Algorithm Tweaks:",
|
||
|
+ " -f <value> Frame length, in milliseconds",
|
||
|
+ " -g <value> Gauss filter size, in frames",
|
||
|
+ " -p <value> Target peak magnitude, 0.1-1.0",
|
||
|
+ " -m <value> Maximum gain factor",
|
||
|
+ " -r <value> Target RMS value",
|
||
|
+ " -n Disable channel coupling",
|
||
|
+ " -c Enable the DC bias correction",
|
||
|
+ " -b Use alternative boundary mode",
|
||
|
+ " -s <value> Compress the input data",
|
||
|
+ "",
|
||
|
+ "Diagnostics:",
|
||
|
+ " -l <file> Create a log file",
|
||
|
+ "",
|
||
|
+ "",
|
||
|
+ "Please refer to the manual for a detailed explanation!"
|
||
|
+ };
|
||
|
+
|
||
|
+ static char *usage;
|
||
|
+ handler.usage = lsx_usage_lines(&usage, lines, array_length(lines));
|
||
|
+
|
||
|
+ MDYNAMICAUDIONORMALIZER_FUNCTION(setLogFunction)(dynaudnorm_log);
|
||
|
+ return &handler;
|
||
|
+}
|
||
|
diff --git a/src/effects.h b/src/effects.h
|
||
|
index 450a5c2..78fdabf 100644
|
||
|
--- a/src/effects.h
|
||
|
+++ b/src/effects.h
|
||
|
@@ -88,3 +88,4 @@
|
||
|
EFFECT(upsample)
|
||
|
EFFECT(vad)
|
||
|
EFFECT(vol)
|
||
|
+ EFFECT(dynaudnorm)
|