[vlc-devel] [PATCH 2/6] equalizer: Add support for extra sets of ISO bands

Ronald Wright logiconcepts819 at gmail.com
Sat Nov 28 07:31:02 CET 2015


Note:  This patch cannot be applied without patch 1, and patches 2 through 6
must be applied all together.

Let me know if the string for BANDS_LONGTEXT is still too long.
---
 include/vlc_eqz_util.h                   |  92 +++++
 modules/audio_filter/equalizer.c         | 134 +++---
 modules/audio_filter/equalizer_presets.h | 672 +++++++++++++++++++++++++++++--
 src/misc/eqz_util.c                      | 297 ++++++++++++++
 4 files changed, 1116 insertions(+), 79 deletions(-)
 create mode 100644 include/vlc_eqz_util.h
 create mode 100644 src/misc/eqz_util.c

diff --git a/include/vlc_eqz_util.h b/include/vlc_eqz_util.h
new file mode 100644
index 0000000..25bc421
--- /dev/null
+++ b/include/vlc_eqz_util.h
@@ -0,0 +1,92 @@
+/*****************************************************************************
+ * vlc_eqz_util.h: Equalizer utility functions
+ *****************************************************************************
+ * Copyright (C) 2015 Ronald Wright
+ * $Id$
+ *
+ * Author: Ronald Wright <logiconcepts819 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_EQZ_UTIL_H
+#define VLC_EQZ_UTIL_H
+
+/**
+ * \file
+ * This file is the interface definition of several useful equalizer utility
+ * functions (implementation in src/misc/eqz_util.c)
+ */
+
+/*****************************************************************************
+ * Documentation
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Type definitions
+ *****************************************************************************/
+
+/**
+ * The preset interpolation data type. This is a pointer to a private structure
+ * defined in misc/eqz_util.c.
+ */
+typedef struct _vlc_band_parse_t * vlc_band_parse_t;
+
+/*****************************************************************************
+ * Interpolation routines
+ *****************************************************************************/
+
+/**
+ * Start a new band parser.
+ *
+ * \param p_obj The object that holds VLC object data
+ * \param p_parser_ctx The object that shall hold band parse data
+ * \param psz_bands The bands string (e.g. "2 0 -2 -4 -2 0 2 4 2 0")
+ * \param i_type The current equalizer type
+ * \param pf_amp An optional memory location to store interpolated values
+ * \return VLC_SUCCESS for success or VLC_ENOMEM for memory failure
+ *
+ * \note If pf_bands is NULL, then memory is automatically allocated in the
+ *       preset parser data context according to the given equalizer type. If
+ *       the provided bands string provides a valid number of bands (i.e. 10,
+ *       15, or 31), then the preset values are interpolated according to the
+ *       current equalizer type. Otherwise, each value is mapped to the
+ *       corresponding band of the current equalizer type, starting at the
+ *       lowest band, until there are no more spaces or values left.
+ */
+VLC_API int vlc_eqz_util_band_parser_init( vlc_object_t * p_obj,
+                                           vlc_band_parse_t * p_parse_ctx,
+                                           const char * psz_bands, int i_type,
+                                           float * pf_amp );
+
+/**
+ * Find the amplification factors from the given band parse context.
+ *
+ * \param interp_ctx The object that holds band parse data
+ * \return A pointer to the amplification array in the context, which
+ *         corresponds to either its own memory if a non-NULL value of pf_amp
+ *         was provided in vlc_eqz_util_band_parser_init, or pf_amp otherwise
+ */
+VLC_API const float *
+vlc_eqz_util_get_amp_array( const vlc_band_parse_t parse_ctx );
+
+/**
+ * Destroy the band parse context.
+ *
+ * \param interp_ctx The object that holds band parse data
+ */
+VLC_API void vlc_eqz_util_band_parser_destroy( vlc_band_parse_t parse_ctx );
+
+#endif
diff --git a/modules/audio_filter/equalizer.c b/modules/audio_filter/equalizer.c
index 98a4a7e..c73dda1 100644
--- a/modules/audio_filter/equalizer.c
+++ b/modules/audio_filter/equalizer.c
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * equalizer.c:
  *****************************************************************************
- * Copyright (C) 2004-2012 VLC authors and VideoLAN
+ * Copyright (C) 2004-2012, 2015 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Laurent Aimar <fenrir at via.ecp.fr>
@@ -34,16 +34,16 @@
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_charset.h>
+#include <vlc_eqz_util.h>
 
 #include <vlc_aout.h>
 #include <vlc_filter.h>
 
+#define EQZ_INCLUDE_MATH_ROUTINES 1
 #include "equalizer_presets.h"
 
 /* TODO:
  *  - optimize a bit (you can hardly do slower ;)
- *  - add tables for more bands (15 and 32 would be cool), maybe with auto coeffs
- *    computation (not too hard once the Q is found).
  *  - support for external preset
  *  - callback to handle preset changes on the fly
  *  - ...
@@ -58,16 +58,26 @@ static void Close( vlc_object_t * );
 #define PRESET_TEXT N_( "Equalizer preset" )
 #define PRESET_LONGTEXT N_("Preset to use for the equalizer." )
 
-#define BANDS_TEXT N_( "Bands gain")
+#define BANDS_TEXT N_( "Bands gain" )
 #define BANDS_LONGTEXT N_( \
-         "Don't use presets, but manually specified bands. You need to " \
-         "provide 10 values between -20dB and 20dB, separated by spaces, " \
-         "e.g. \"0 2 4 2 0 -2 -4 -2 0 2\"." )
+         "Don't use presets, but manually specified bands. You need to "      \
+         "provide 10, 15, or 31 values between -20dB and 20dB, separated by " \
+         "spaces (e.g. \"0 2 4 2 0 -2 -4 -2 0 2\"). You may optionally "      \
+         "append a parameter \"v\" (valid for 10 values only) or \"i\" "      \
+         "(valid for 10, 15, and 31 values) to help VLC differentiate "       \
+         "between VLC bands and ISO bands, respectively. The band values "    \
+         "are interpolated if the number of bands suggested by the "          \
+         "specified string is valid (i.e. 10, 15, or 31) and differs from "   \
+         "the current number of bands." )
 
 #define VLC_BANDS_TEXT N_( "Use VLC frequency bands" )
 #define VLC_BANDS_LONGTEXT N_( \
          "Use the VLC frequency bands. Otherwise, use the ISO Standard " \
-         "frequency bands." )
+         "frequency bands. This option is valid for 10 bands only." )
+
+#define BANDS_COUNT_TEXT N_( "Number of bands" )
+#define BANDS_COUNT_LONGTEXT N_( \
+         "Select the number of bands to use for the equalizer." )
 
 #define TWOPASS_TEXT N_( "Two pass" )
 #define TWOPASS_LONGTEXT N_( "Filter the audio twice. This provides a more "  \
@@ -92,6 +102,9 @@ vlc_module_begin ()
               TWOPASS_LONGTEXT, true )
     add_bool( "equalizer-vlcfreqs", true, VLC_BANDS_TEXT,
               VLC_BANDS_LONGTEXT, true )
+    add_string( "equalizer-bands-count", "10", BANDS_COUNT_TEXT,
+                BANDS_COUNT_LONGTEXT, true )
+        change_string_list( eqz_band_counts, eqz_band_counts_text )
     add_float( "equalizer-preamp", 12.0f, PREAMP_TEXT,
                PREAMP_LONGTEXT, true )
     set_callbacks( Open, Close )
@@ -105,6 +118,7 @@ struct filter_sys_t
 {
     /* Filter static config */
     int i_band;
+    int i_type;
     float *f_alpha;
     float *f_beta;
     float *f_gamma;
@@ -214,25 +228,22 @@ typedef struct
 } eqz_config_t;
 
 /* Equalizer coefficient calculation function based on equ-xmms */
-static void EqzCoeffs( int i_rate, float f_octave_percent,
-                       bool b_use_vlc_freqs,
-                       eqz_config_t *p_eqz_config )
+static void EqzCoeffs( int i_rate, int i_type, eqz_config_t *p_eqz_config )
 {
-    const float *f_freq_table_10b = b_use_vlc_freqs
-                                  ? f_vlc_frequency_table_10b
-                                  : f_iso_frequency_table_10b;
+    const eqz_base_freqtable_t *p_freq_table = EqzGetFrequencyTable( i_type );
+    int i_band = EqzGetNumBandsByType( i_type );
+    float f_octave_percent = p_freq_table->f_bandwidth;
     float f_rate = (float) i_rate;
     float f_nyquist_freq = 0.5f * f_rate;
     float f_octave_factor = powf( 2.0f, 0.5f * f_octave_percent );
     float f_octave_factor_1 = 0.5f * ( f_octave_factor + 1.0f );
     float f_octave_factor_2 = 0.5f * ( f_octave_factor - 1.0f );
 
-    p_eqz_config->i_band = EQZ_BANDS_MAX;
+    p_eqz_config->i_band = i_band;
 
-    for( int i = 0; i < EQZ_BANDS_MAX; i++ )
+    for( int i = 0; i < i_band; i++ )
     {
-        float f_freq = f_freq_table_10b[i];
-
+        float f_freq = EqzGetTrueFrequency( i_type, i );
         p_eqz_config->band[i].f_frequency = f_freq;
 
         if( f_freq <= f_nyquist_freq )
@@ -285,11 +296,28 @@ static int EqzInit( filter_t *p_filter, int i_rate )
     vlc_object_t *p_aout = p_filter->p_parent;
     int i_ret = VLC_ENOMEM;
 
+    /* Fetch the type of equalizer */
+    const char *psz_eqz_count = var_InheritString( p_aout,
+            "equalizer-bands-count" );
     bool b_vlcFreqs = var_InheritBool( p_aout, "equalizer-vlcfreqs" );
-    EqzCoeffs( i_rate, 1.0f, b_vlcFreqs, &cfg );
+    int i_type = EqzGetTypeNumber( p_aout, atoi( psz_eqz_count ), b_vlcFreqs );
+    if( i_type == EQZ_UNKNOWN_TYPE )
+    {
+        msg_Err( p_aout, "band count specification of '%s' is invalid",
+                         psz_eqz_count );
+        msg_Info( p_aout, "full list of valid band counts:" );
+        for( unsigned i = 0; i < NB_EQZ_BAND_COUNTS; i++ )
+             msg_Info( p_aout, "  - '%s'", eqz_band_counts[i] );
+        return VLC_EGENERIC;
+    }
+
+    /* Load the equalizer configuration with frequencies and coefficients
+     * according to the equalizer type */
+    EqzCoeffs( i_rate, i_type, &cfg );
 
     /* Create the static filter config */
     p_sys->i_band = cfg.i_band;
+    p_sys->i_type = i_type;
     p_sys->f_alpha = malloc( p_sys->i_band * sizeof(float) );
     p_sys->f_beta  = malloc( p_sys->i_band * sizeof(float) );
     p_sys->f_gamma = malloc( p_sys->i_band * sizeof(float) );
@@ -469,32 +497,41 @@ static void EqzClean( filter_t *p_filter )
 static int PresetCallback( vlc_object_t *p_aout, char const *psz_cmd,
                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
-    const eqz_preset_t *preset = NULL;
+    filter_sys_t *p_sys = p_data;
+    const eqz_base_preset_t *table = EqzGetPresetTable( p_sys->i_type );
+    const eqz_base_preset_t *preset = NULL;
     const char *psz_preset = newval.psz_string;
 
     for( unsigned i = 0; i < NB_PRESETS; i++ )
-        if( !strcasecmp( eqz_preset_10b[i].psz_name, psz_preset ) )
+    {
+        const eqz_base_preset_t *p = EqzGetPresetAt( table, i );
+        if( !strcasecmp( p->psz_name, psz_preset ) )
         {
-            preset = eqz_preset_10b + i;
+            preset = p;
             break;
         }
+    }
 
     if( preset == NULL )
     {
         msg_Err( p_aout, "equalizer preset '%s' not found", psz_preset );
         msg_Info( p_aout, "full list:" );
         for( unsigned i = 0; i < NB_PRESETS; i++ )
-             msg_Info( p_aout, "  - '%s'", eqz_preset_10b[i].psz_name );
+        {
+             msg_Info( p_aout, "  - '%s'",
+                       EqzGetPresetAt( table, i )->psz_name );
+        }
         return VLC_EGENERIC;
     }
 
     char *bands = NULL;
+    const float *pf_amp = EqzGetAmpPtr( preset );
 
-    for( unsigned i = 0; i < EQZ_BANDS_MAX; i++ )
+    for( int i = 0; i < p_sys->i_band; i++ )
     {
         char *psz;
 
-        lldiv_t d = lldiv( lroundf(preset->f_amp[i] * 10000000.f), 10000000 );
+        lldiv_t d = lldiv( lroundf(pf_amp[i] * 10000000.f), 10000000 );
 
         if( asprintf( &psz, "%s %lld.%07llu", i ? bands : "",
                       d.quot, d.rem ) == -1 )
@@ -506,10 +543,25 @@ static int PresetCallback( vlc_object_t *p_aout, char const *psz_cmd,
         bands = psz;
     }
 
+    /* For 10 bands, we must help VLC differentiate between VLC bands and ISO
+     * bands */
+    if( p_sys->i_band == EQZ_ISO10_BANDS_MAX )
+    {
+        char * psz;
+        if( asprintf( &psz, "%s %c", bands,
+                      EqzGetIdentifier( p_sys->i_type ) ) == -1 )
+            psz = NULL;
+
+        free( bands );
+        if( unlikely( psz == NULL ) )
+            return VLC_ENOMEM;
+        bands = psz;
+    }
+
     var_SetFloat( p_aout, "equalizer-preamp", preset->f_preamp );
     var_SetString( p_aout, "equalizer-bands", bands );
     free( bands );
-    (void) psz_cmd; (void) oldval; (void) p_data;
+    (void) psz_cmd; (void) oldval;
     return VLC_SUCCESS;
 }
 
@@ -536,31 +588,23 @@ static int PreampCallback( vlc_object_t *p_this, char const *psz_cmd,
 static int BandsCallback( vlc_object_t *p_this, char const *psz_cmd,
                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
-    VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+    VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
     filter_sys_t *p_sys = p_data;
-    const char *p = newval.psz_string;
-    int i = 0;
+    vlc_band_parse_t ctx;
 
-    /* Same thing for bands */
     vlc_mutex_lock( &p_sys->lock );
-    while( i < p_sys->i_band )
+    int i_ret_val = vlc_eqz_util_band_parser_init( VLC_OBJECT( p_this ), &ctx,
+            newval.psz_string, p_sys->i_type, p_sys->f_amp );
+    if( i_ret_val == VLC_SUCCESS )
     {
-        char *next;
-        /* Read dB -20/20 */
-        float f = us_strtof( p, &next );
-        if( next == p || isnan( f ) )
-            break; /* no conversion */
-
-        p_sys->f_amp[i++] = EqzConvertdB( f );
-
-        if( *next == '\0' )
-            break; /* end of line */
-        p = &next[1];
+        const float * pf_amp = vlc_eqz_util_get_amp_array( ctx );
+        for( int k = 0; k < p_sys->i_band; k++ )
+            p_sys->f_amp[k] = EqzConvertdB( pf_amp[k] );
+        vlc_eqz_util_band_parser_destroy( ctx );
     }
-    while( i < p_sys->i_band )
-        p_sys->f_amp[i++] = EqzConvertdB( 0.f );
     vlc_mutex_unlock( &p_sys->lock );
-    return VLC_SUCCESS;
+
+    return i_ret_val;
 }
 static int TwoPassCallback( vlc_object_t *p_this, char const *psz_cmd,
                             vlc_value_t oldval, vlc_value_t newval, void *p_data )
diff --git a/modules/audio_filter/equalizer_presets.h b/modules/audio_filter/equalizer_presets.h
index 3ea70ca..01b1109 100644
--- a/modules/audio_filter/equalizer_presets.h
+++ b/modules/audio_filter/equalizer_presets.h
@@ -1,10 +1,11 @@
 /*****************************************************************************
  * equalizer_presets.h:
  *****************************************************************************
- * Copyright (C) 2004 VLC authors and VideoLAN
+ * Copyright (C) 2004, 2015 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Laurent Aimar <fenrir at via.ecp.fr>
+ *          Ronald Wright <logiconcepts819 at gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
@@ -31,19 +32,256 @@
  * get these values even if the equalizer is not enabled.
  */
 
-#define EQZ_BANDS_MAX 10
+/* Maximum number of equalizer bands */
+#define EQZ_ISO10_BANDS_MAX 10
+#define EQZ_VLC10_BANDS_MAX EQZ_ISO10_BANDS_MAX
+#define EQZ_ISO15_BANDS_MAX 15
+#define EQZ_ISO31_BANDS_MAX 31
 
-/* The frequency tables */
-static const float f_vlc_frequency_table_10b[EQZ_BANDS_MAX] =
+#define EQZ_BANDS_MAX EQZ_ISO31_BANDS_MAX
+
+/* **************************** Equalizer types **************************** */
+
+/* Type numbers */
+#define EQZ_UNKNOWN_TYPE -1
+#define EQZ_VLC10_TYPE 0
+#define EQZ_ISO10_TYPE 1
+#define EQZ_ISO15_TYPE 2
+#define EQZ_ISO31_TYPE 3
+
+/* Identifiers */
+#define EQZ_VLC_IDENTIFIER 'v'
+#define EQZ_ISO_IDENTIFIER 'i'
+
+#define NB_EQZ_BAND_COUNTS 3
+static const char *const eqz_band_counts[NB_EQZ_BAND_COUNTS] = {
+    "10", "15", "31"
+};
+
+static const char *const eqz_band_counts_text[NB_EQZ_BAND_COUNTS] = {
+    N_("10"), N_("15"), N_("31"),
+};
+
+/* Lookup equalizer type number from the given band count and VLC frequency
+ * boolean variable */
+static inline int EqzGetTypeNumber( vlc_object_t * p_obj, int i_bands,
+                                    bool b_vlcFreqs )
+{
+    if( i_bands == 10 )
+    {
+        return b_vlcFreqs ? EQZ_VLC10_TYPE : EQZ_ISO10_TYPE;
+    }
+    else if( i_bands == 15 || i_bands == 31 )
+    {
+        if( b_vlcFreqs )
+            msg_Warn( p_obj, "VLC frequencies undefined for 15 and 31 bands; "
+                             "using ISO frequencies instead" );
+        return i_bands == 15 ? EQZ_ISO15_TYPE : EQZ_ISO31_TYPE;
+    }
+    return EQZ_UNKNOWN_TYPE;
+}
+
+/* Lookup equalizer identifier from the given type number */
+static inline char EqzGetIdentifier( int i_type_number )
+{
+    switch( i_type_number )
+    {
+        case EQZ_ISO10_TYPE:
+            /* fall-through */
+        case EQZ_ISO15_TYPE:
+            /* fall-through */
+        case EQZ_ISO31_TYPE:
+            return EQZ_ISO_IDENTIFIER;
+        default:
+            return EQZ_VLC_IDENTIFIER;
+    }
+}
+
+/* Lookup default equalizer identifier from the given number of bands */
+static inline char EqzGetDefaultIdentifier( int i_band )
+{
+    return i_band > EQZ_ISO10_BANDS_MAX ? EQZ_ISO_IDENTIFIER
+                                        : EQZ_VLC_IDENTIFIER;
+}
+
+/* Determine string identifier from the given character identifier */
+#define EqzGetStringIdentifier( ch ) \
+    ( ( ch ) == EQZ_ISO_IDENTIFIER ? "ISO" : "VLC" )
+
+/* Macro for declaring equalizer and preset tables with an arbitrary number of
+ * bands */
+#define EQZ_FREQTABLE_TYPE( nbands ) eqz_freqtable##nbands##_t
+#define EQZ_PRESET_TYPE( nbands ) eqz_preset##nbands##_t
+
+/* Casts to the base type */
+#define EQZ_BASE_FREQTABLE( ptr ) ( ( const eqz_base_freqtable_t * ) ( ptr ) )
+#define EQZ_BASE_PRESET( ptr ) ( ( const eqz_base_preset_t * ) ( ptr ) )
+
+/* Members common to all frequency table types */
+#define EQZ_FREQTABLE_COMMON_MEMBERS                                        \
+    int i_band_offset; /* offset of the first band from 1 kHz */            \
+    float f_bandwidth; /* bandwidth */                                      \
+
+/* Members common to all preset types */
+#define EQZ_PRESET_COMMON_MEMBERS                                           \
+    const char psz_name[16]; /* preset name */                              \
+    int  i_band;             /* number of bands */                          \
+    float f_preamp;          /* preamp */                                   \
+
+/* Base structure for a frequency table */
+typedef struct
+{
+    EQZ_FREQTABLE_COMMON_MEMBERS
+} eqz_base_freqtable_t;
+
+/* Base structure for a preset table */
+typedef struct
 {
-    60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000,
+    EQZ_PRESET_COMMON_MEMBERS
+} eqz_base_preset_t;
+
+/* Macro for defining a frequency table and a preset, which have an arbitrary
+ * number of bands */
+#define DEFINE_EQZ_TYPES( nbands )                                          \
+    /* Structure for defining a frequency table */                          \
+    typedef struct                                                          \
+    {                                                                       \
+        EQZ_FREQTABLE_COMMON_MEMBERS /* common struct members */            \
+        float f_frequencies[nbands]; /* array of displayable frequencies */ \
+    } EQZ_FREQTABLE_TYPE( nbands );                                         \
+                                                                            \
+    /* Structure for defining a preset */                                   \
+    typedef struct                                                          \
+    {                                                                       \
+        EQZ_PRESET_COMMON_MEMBERS /* common struct members */               \
+        float f_amp[nbands];      /* array of boosts and cuts */            \
+    } EQZ_PRESET_TYPE( nbands );                                            \
+
+/* We have the 10-band VLC equalizer, and we also have 10-, 15-, and 31-band
+ * ISO equalizers */
+DEFINE_EQZ_TYPES( EQZ_ISO10_BANDS_MAX )
+DEFINE_EQZ_TYPES( EQZ_ISO15_BANDS_MAX )
+DEFINE_EQZ_TYPES( EQZ_ISO31_BANDS_MAX )
+
+/* *************************** Frequency tables **************************** */
+
+/* 10-band VLC table */
+static const EQZ_FREQTABLE_TYPE( 10 ) vlc_frequency_table_10b =
+{
+    -4, 1.0f,
+    {
+        60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000
+    }
 };
 
-static const float f_iso_frequency_table_10b[EQZ_BANDS_MAX] =
+/* 10-band ISO table */
+static const EQZ_FREQTABLE_TYPE( 10 ) iso_frequency_table_10b =
 {
-    31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 16000,
+    -5, 1.0f,
+    {
+        31.5, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000
+    }
+};
+
+/* 15-band ISO table */
+static const EQZ_FREQTABLE_TYPE( 15 ) iso_frequency_table_15b =
+{
+    -8, 2.0f / 3.0f,
+    {
+        25, 40, 63, 100, 160, 250, 400, 630, 1000, 1600, 2500, 4000, 6300,
+        10000, 16000
+    }
 };
 
+/* 31-band ISO table */
+static const EQZ_FREQTABLE_TYPE( 31 ) iso_frequency_table_31b =
+{
+    -17, 1.0f / 3.0f,
+    {
+        20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500,
+        630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000,
+        10000, 12500, 16000, 20000
+    }
+};
+
+/* ******************* Frequency table helper functions ******************** */
+
+/* Function for retrieving a pointer to the (displayable) frequencies */
+static inline
+const float * EqzGetFrequencyPtr( const eqz_base_freqtable_t * p_freq_table )
+{
+    return ( const float * ) ( p_freq_table + 1 );
+}
+
+/* Function for retrieving the correct frequency table by type */
+static inline
+const eqz_base_freqtable_t * EqzGetFrequencyTable( int i_type_number )
+{
+    switch( i_type_number )
+    {
+        case EQZ_VLC10_TYPE:
+            return EQZ_BASE_FREQTABLE( &vlc_frequency_table_10b );
+        case EQZ_ISO10_TYPE:
+            return EQZ_BASE_FREQTABLE( &iso_frequency_table_10b );
+        case EQZ_ISO15_TYPE:
+            return EQZ_BASE_FREQTABLE( &iso_frequency_table_15b );
+        case EQZ_ISO31_TYPE:
+            return EQZ_BASE_FREQTABLE( &iso_frequency_table_31b );
+        default:
+            return NULL;
+    }
+}
+
+/* Function for retrieving the type of equalizer by number of bands and
+ * equalizer identifier */
+static inline int EqzGetTypeByNumBands( int i_band, char identifier )
+{
+    switch( i_band )
+    {
+        case EQZ_ISO10_BANDS_MAX:
+        {
+            if( identifier == EQZ_ISO_IDENTIFIER )
+                return EQZ_ISO10_TYPE;
+            else
+                return EQZ_VLC10_TYPE;
+        }
+        case EQZ_ISO15_BANDS_MAX:
+            return EQZ_ISO15_TYPE;
+        case EQZ_ISO31_BANDS_MAX:
+            return EQZ_ISO31_TYPE;
+        default:
+            return EQZ_UNKNOWN_TYPE;
+    }
+}
+
+/* Function for retrieving the true frequency at the specified index.  A build
+ * error will result if math.h is not included before equalizer_presets.h is
+ * included, so this routine is guarded by the EQZ_INCLUDE_MATH_ROUTINES macro.
+ * If math.h is included before equalizer_presets.h, then
+ * EQZ_INCLUDE_MATH_ROUTINES may be optionally defined before the inclusion of
+ * equalizer_presets.h so that EqzGetTrueFrequency() can be called */
+#ifdef EQZ_INCLUDE_MATH_ROUTINES
+static inline float EqzGetTrueFrequency( int i_type, int i_index )
+{
+    if( i_type == EQZ_VLC10_TYPE )
+    {
+        /* True VLC frequencies are the same as displayable VLC frequencies */
+        return EqzGetFrequencyPtr( EqzGetFrequencyTable( i_type ) )[ i_index ];
+    }
+    else
+    {
+        /* True ISO frequencies are slightly different from displayable ISO
+         * frequencies */
+        const eqz_base_freqtable_t *p_table = EqzGetFrequencyTable( i_type );
+        int i_offset = p_table->i_band_offset + i_index;
+        float f_octave_percent = p_table->f_bandwidth;
+        return 1000.0f * powf( 2.0f, i_offset * f_octave_percent );
+    }
+}
+#endif
+
+/* ***************************** Preset tables ***************************** */
+
 #define NB_PRESETS 18
 static const char *const preset_list[NB_PRESETS] = {
     "flat", "classical", "club", "dance", "fullbass", "fullbasstreble",
@@ -57,101 +295,467 @@ static const char *const preset_list_text[NB_PRESETS] = {
     N_("Rock"), N_("Ska"), N_("Soft"), N_("Soft rock"), N_("Techno"),
 };
 
-typedef struct
-{
-    const char psz_name[16];
-    int  i_band;
-    float f_preamp;
-    float f_amp[EQZ_BANDS_MAX];
-} eqz_preset_t;
-
-static const eqz_preset_t eqz_preset_10b[NB_PRESETS] =
+/* Preset table for the 10-band VLC equalizer */
+static const EQZ_PRESET_TYPE( 10 ) eqz_preset_vlc_10b[NB_PRESETS] =
 {
     {
-        "flat", 10, 12.0f,
+        "flat", EQZ_VLC10_BANDS_MAX, 12.0f,
         { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
     },
     {
-        "classical", 10, 12.0f,
+        "classical", EQZ_VLC10_BANDS_MAX, 12.0f,
         { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
           -1.11022e-15f, -1.11022e-15f, -7.2f, -7.2f, -7.2f, -9.6f }
     },
     {
-        "club", 10, 6.0f,
+        "club", EQZ_VLC10_BANDS_MAX, 6.0f,
         { -1.11022e-15f, -1.11022e-15f, 8.0f, 5.6f, 5.6f, 5.6f, 3.2f,
           -1.11022e-15f, -1.11022e-15f, -1.11022e-15f }
     },
     {
-        "dance", 10, 5.0f,
+        "dance", EQZ_VLC10_BANDS_MAX, 5.0f,
         { 9.6f, 7.2f, 2.4f, -1.11022e-15f, -1.11022e-15f, -5.6f, -7.2f, -7.2f,
           -1.11022e-15f, -1.11022e-15f }
     },
     {
-        "fullbass", 10, 5.0f,
+        "fullbass", EQZ_VLC10_BANDS_MAX, 5.0f,
         { -8.0f, 9.6f, 9.6f, 5.6f, 1.6f, -4.0f, -8.0f, -10.4f, -11.2f, -11.2f }
     },
     {
-        "fullbasstreble", 10, 4.0f,
+        "fullbasstreble", EQZ_VLC10_BANDS_MAX, 4.0f,
         { 7.2f, 5.6f, -1.11022e-15f, -7.2f, -4.8f, 1.6f, 8.0f, 11.2f,
           12.0f, 12.0f }
     },
     {
-        "fulltreble", 10, 3.0f,
+        "fulltreble", EQZ_VLC10_BANDS_MAX, 3.0f,
         { -9.6f, -9.6f, -9.6f, -4.0f, 2.4f, 11.2f, 16.0f, 16.0f, 16.0f, 16.8f }
     },
     {
-        "headphones", 10, 4.0f,
+        "headphones", EQZ_VLC10_BANDS_MAX, 4.0f,
         { 4.8f, 11.2f, 5.6f, -3.2f, -2.4f, 1.6f, 4.8f, 9.6f, 12.8f, 14.4f }
     },
     {
-        "largehall", 10, 5.0f,
+        "largehall", EQZ_VLC10_BANDS_MAX, 5.0f,
         { 10.4f, 10.4f, 5.6f, 5.6f, -1.11022e-15f, -4.8f, -4.8f, -4.8f,
           -1.11022e-15f, -1.11022e-15f }
     },
     {
-        "live", 10, 7.0f,
+        "live", EQZ_VLC10_BANDS_MAX, 7.0f,
         { -4.8f, -1.11022e-15f, 4.0f, 5.6f, 5.6f, 5.6f, 4.0f, 2.4f,
           2.4f, 2.4f }
     },
     {
-        "party", 10, 6.0f,
+        "party", EQZ_VLC10_BANDS_MAX, 6.0f,
         { 7.2f, 7.2f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
           -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, 7.2f, 7.2f }
     },
     {
-        "pop", 10, 6.0f,
+        "pop", EQZ_VLC10_BANDS_MAX, 6.0f,
         { -1.6f, 4.8f, 7.2f, 8.0f, 5.6f, -1.11022e-15f, -2.4f, -2.4f,
           -1.6f, -1.6f }
     },
     {
-        "reggae", 10, 8.0f,
+        "reggae", EQZ_VLC10_BANDS_MAX, 8.0f,
         { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -5.6f, -1.11022e-15f,
           6.4f, 6.4f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f }
     },
     {
-        "rock", 10, 5.0f,
+        "rock", EQZ_VLC10_BANDS_MAX, 5.0f,
         { 8.0f, 4.8f, -5.6f, -8.0f, -3.2f, 4.0f, 8.8f, 11.2f, 11.2f, 11.2f }
     },
     {
-        "ska", 10, 6.0f,
+        "ska", EQZ_VLC10_BANDS_MAX, 6.0f,
         { -2.4f, -4.8f, -4.0f, -1.11022e-15f, 4.0f, 5.6f, 8.8f, 9.6f,
           11.2f, 9.6f }
     },
     {
-        "soft", 10, 5.0f,
+        "soft", EQZ_VLC10_BANDS_MAX, 5.0f,
         { 4.8f, 1.6f, -1.11022e-15f, -2.4f, -1.11022e-15f, 4.0f, 8.0f, 9.6f,
           11.2f, 12.0f }
     },
     {
-        "softrock", 10, 7.0f,
+        "softrock", EQZ_VLC10_BANDS_MAX, 7.0f,
         { 4.0f, 4.0f, 2.4f, -1.11022e-15f, -4.0f, -5.6f, -3.2f, -1.11022e-15f,
           2.4f, 8.8f }
     },
     {
-        "techno", 10, 5.0f,
+        "techno", EQZ_VLC10_BANDS_MAX, 5.0f,
         { 8.0f, 5.6f, -1.11022e-15f, -5.6f, -4.8f, -1.11022e-15f, 8.0f, 9.6f,
           9.6f, 8.8f }
     },
 };
 
+/* Preset table for the 10-band ISO equalizer.  The boosts and cuts were
+ * sampled from the legacy VLC presets at the true ISO frequency centers using
+ * monotone cubic interpolation and then rounded to the nearest tenth */
+static const EQZ_PRESET_TYPE( 10 ) eqz_preset_iso_10b[NB_PRESETS] =
+{
+    {
+        "flat", EQZ_ISO10_BANDS_MAX, 12.0f,
+        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+    },
+    {
+        "classical", EQZ_ISO10_BANDS_MAX, 12.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.9f, -7.2f, -9.6f }
+    },
+    {
+        "club", EQZ_ISO10_BANDS_MAX, 6.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, 4.9f, 6.3f, 5.6f, 5.6f,
+          5.1f, 1.8f, -1.11022e-15f }
+    },
+    {
+        "dance", EQZ_ISO10_BANDS_MAX, 5.0f,
+        { 9.6f, 9.5f, 8.3f, 4.2f, 0.3f, -1.11022e-15f, -2.6f, -6.4f, -7.2f,
+          -1.11022e-15f }
+    },
+    {
+        "fullbass", EQZ_ISO10_BANDS_MAX, 5.0f,
+        { -8.0f, -7.6f, 4.9f, 9.6f, 7.2f, 1.6f, -2.0f, -5.7f, -9.0f, -11.2f }
+    },
+    {
+        "fullbasstreble", EQZ_ISO10_BANDS_MAX, 4.0f,
+        { 7.2f, 7.2f, 6.4f, 2.5f, -5.9f, -4.8f, -1.1f, 4.2f, 9.4f, 12.0f }
+    },
+    {
+        "fulltreble", EQZ_ISO10_BANDS_MAX, 3.0f,
+        { -9.6f, -9.6f, -9.6f, -9.6f, -6.3f, 2.4f, 8.2f, 13.5f, 16.0f, 16.8f }
+    },
+    {
+        "headphones", EQZ_ISO10_BANDS_MAX, 4.0f,
+        { 4.8f, 4.9f, 9.5f, 8.5f, -1.6f, -2.4f, -0.3f, 2.9f, 6.4f, 14.4f }
+    },
+    {
+        "largehall", EQZ_ISO10_BANDS_MAX, 5.0f,
+        { 10.4f, 10.4f, 10.4f, 7.5f, 5.6f, -1.11022e-15f, -3.6f, -4.8f, -4.8f,
+          -1.11022e-15f }
+    },
+    {
+        "live", EQZ_ISO10_BANDS_MAX, 7.0f,
+        { -4.8f, -4.7f, -1.8f, 2.7f, 5.4f, 5.6f, 5.6f, 5.3f, 3.3f, 2.4f }
+    },
+    {
+        "party", EQZ_ISO10_BANDS_MAX, 6.0f,
+        { 7.2f, 7.2f, 7.2f, 2.8f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, 7.2f }
+    },
+    {
+        "pop", EQZ_ISO10_BANDS_MAX, 6.0f,
+        { -1.6f, -1.5f, 2.7f, 6.6f, 7.9f, 5.6f, 2.1f, -1.2f, -2.4f, -1.6f }
+    },
+    {
+        "reggae", EQZ_ISO10_BANDS_MAX, 8.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -4.1f, -1.11022e-15f, 4.7f, 6.4f, 4.7f, -1.11022e-15f }
+    },
+    {
+        "rock", EQZ_ISO10_BANDS_MAX, 5.0f,
+        { 8.0f, 7.9f, 6.3f, -1.8f, -7.7f, -3.2f, 1.4f, 6.1f, 10.0f, 11.2f }
+    },
+    {
+        "ska", EQZ_ISO10_BANDS_MAX, 6.0f,
+        { -2.4f, -2.5f, -4.2f, -4.5f, -1.4f, 4.0f, 5.0f, 6.8f, 9.1f, 9.6f }
+    },
+    {
+        "soft", EQZ_ISO10_BANDS_MAX, 5.0f,
+        { 4.8f, 4.7f, 2.7f, 0.6f, -2.0f, -1.11022e-15, 2.4f, 5.7f, 8.6f,
+          12.0f }
+    },
+    {
+        "softrock", EQZ_ISO10_BANDS_MAX, 7.0f,
+        { 4.0f, 4.0f, 4.0f, 3.2f, 0.8f, -4.0f, -5.3f, -5.1f, -2.1f, 8.8f }
+    },
+    {
+        "techno", EQZ_ISO10_BANDS_MAX, 5.0f,
+        { 8.0f, 7.9f, 6.7f, 2.3f, -4.6f, -4.8f, -2.5f, 3.1f, 8.9f, 8.8f }
+    },
+};
+
+/* Preset table for the 15-band ISO equalizer.  The boosts and cuts were
+ * sampled from the legacy VLC presets at the true ISO frequency centers using
+ * monotone cubic interpolation and then rounded to the nearest tenth */
+static const EQZ_PRESET_TYPE( 15 ) eqz_preset_iso_15b[NB_PRESETS] =
+{
+    {
+        "flat", EQZ_ISO15_BANDS_MAX, 12.0f,
+        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+          0.0f, 0.0f, 0.0f, 0.0f },
+    },
+    {
+        "classical", EQZ_ISO15_BANDS_MAX, 12.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.9f, -7.2f, -7.2f,
+          -9.6f }
+    },
+    {
+        "club", EQZ_ISO15_BANDS_MAX, 6.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, 4.9f, 7.5f, 5.6f, 5.6f, 5.6f, 5.6f, 5.1f, 3.0f, 0.5f,
+          -1.11022e-15f }
+    },
+    {
+        "dance", EQZ_ISO15_BANDS_MAX, 5.0f,
+        { 9.6f, 9.6f, 9.5f, 8.7f, 7.5f, 4.2f, 1.3f, -1.11022e-15f,
+          -1.11022e-15f, -1.1f, -4.5f, -6.4f, -7.2f, -7.2f, -1.11022e-15f }
+    },
+    {
+        "fullbass", EQZ_ISO15_BANDS_MAX, 5.0f,
+        { -8.0f, -8.0f, -7.6f, -0.3f, 9.1f, 9.6f, 9.0f, 5.2f, 1.6f, -0.8f,
+          -3.1f, -5.7f, -8.2f, -9.7f, -11.2f }
+    },
+    {
+        "fullbasstreble", EQZ_ISO15_BANDS_MAX, 4.0f,
+        { 7.2f, 7.2f, 7.2f, 6.7f, 5.8f, 2.5f, -2.9f, -7.2f, -4.8f, -2.5f, 0.3f,
+          4.2f, 8.3f, 10.4f, 12.0f }
+    },
+    {
+        "fulltreble", EQZ_ISO15_BANDS_MAX, 3.0f,
+        { -9.6f, -9.6f, -9.6f, -9.6f, -9.6f, -9.6f, -8.7f, -3.5f, 2.4f, 6.3f,
+          9.9f, 13.5f, 16.0f, 16.0f, 16.8f }
+    },
+    {
+        "headphones", EQZ_ISO15_BANDS_MAX, 4.0f,
+        { 4.8f, 4.8f, 4.9f, 7.6f, 11.0f, 8.5f, 2.2f, -3.2f, -2.4f, -1.2f, 0.8f,
+          2.9f, 5.1f, 7.8f, 14.4f }
+    },
+    {
+        "largehall", EQZ_ISO15_BANDS_MAX, 5.0f,
+        { 10.4f, 10.4f, 10.4f, 10.4f, 10.4f, 7.5f, 5.6f, 5.5f, -1.11022e-15f,
+          -2.4f, -4.5f, -4.8f, -4.8f, -4.8f, -1.11022e-15f }
+    },
+    {
+        "live", EQZ_ISO15_BANDS_MAX, 7.0f,
+        { -4.8f, -4.8f, -4.7f, -3.0f, -0.5f, 2.7f, 4.7f, 5.6f, 5.6f, 5.6f,
+          5.6f, 5.3f, 3.9f, 2.6f, 2.4f }
+    },
+    {
+        "party", EQZ_ISO15_BANDS_MAX, 6.0f,
+        { 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 2.8f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, 7.2f }
+    },
+    {
+        "pop", EQZ_ISO15_BANDS_MAX, 6.0f,
+        { -1.6f, -1.6f, -1.5f, 1.0f, 4.4f, 6.6f, 7.6f, 8.0f, 5.6f, 3.4f, 0.8f,
+          -1.2f, -2.4f, -2.4f, -1.6f }
+    },
+    {
+        "reggae", EQZ_ISO15_BANDS_MAX, 8.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.2f, -5.5f, -1.11022e-15f, 3.1f,
+          6.0f, 6.4f, 6.3f, 1.5f, -1.11022e-15f }
+    },
+    {
+        "rock", EQZ_ISO15_BANDS_MAX, 5.0f,
+        { 8.0f, 8.0f, 7.9f, 7.0f, 5.3f, -1.8f, -6.8f, -7.9f, -3.2f, -0.1f,
+          2.9f, 6.1f, 9.0f, 10.9f, 11.2f }
+    },
+    {
+        "ska", EQZ_ISO15_BANDS_MAX, 6.0f,
+        { -2.4f, -2.4f, -2.5f, -3.5f, -4.7f, -4.5f, -3.0f, 0.4f, 4.0f, 4.8f,
+          5.3f, 6.8f, 8.9f, 9.3f, 9.6f }
+    },
+    {
+        "soft", EQZ_ISO15_BANDS_MAX, 5.0f,
+        { 4.8f, 4.8f, 4.7f, 3.5f, 1.8f, 0.6f, -0.9f, -2.4f, -1.11022e-15, 1.6f,
+          3.3f, 5.7f, 8.2f, 9.0f, 12.0f }
+    },
+    {
+        "softrock", EQZ_ISO15_BANDS_MAX, 7.0f,
+        { 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 3.2f, 1.6f, -0.3f, -4.0f, -4.9f, -5.5f,
+          -5.1f, -3.0f, -1.2f, 8.8f }
+    },
+    {
+        "techno", EQZ_ISO15_BANDS_MAX, 5.0f,
+        { 8.0f, 8.0f, 7.9f, 7.2f, 5.9f, 2.3f, -2.4f, -5.6f, -4.8f, -3.4f,
+          -1.2f, 3.1f, 8.2f, 9.4f, 8.8f }
+    },
+};
+
+/* Preset table for the 31-band ISO equalizer.  The boosts and cuts were
+ * sampled from the legacy VLC presets at the true ISO frequency centers using
+ * monotone cubic interpolation and then rounded to the nearest tenth */
+static const EQZ_PRESET_TYPE( 31 ) eqz_preset_iso_31b[NB_PRESETS] =
+{
+    {
+        "flat", EQZ_ISO31_BANDS_MAX, 12.0f,
+        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+          0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+          0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+    },
+    {
+        "classical", EQZ_ISO31_BANDS_MAX, 12.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -0.1f, -1.9f, -5.5f, -7.2f, -7.2f,
+          -7.2f, -7.2f, -9.6f, -9.6f }
+    },
+    {
+        "club", EQZ_ISO31_BANDS_MAX, 6.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, 0.9f, 4.9f, 8.0f, 7.5f, 6.3f, 5.6f,
+          5.6f, 5.6f, 5.6f, 5.6f, 5.6f, 5.6f, 5.6f, 5.1f, 4.1f, 3.0f, 1.8f,
+          0.5f, -1.11022e-15f, -1.11022e-15f }
+    },
+    {
+        "dance", EQZ_ISO31_BANDS_MAX, 5.0f,
+        { 9.6f, 9.6f, 9.6f, 9.6f, 9.6f, 9.5f, 9.2f, 8.7f, 8.3f, 7.5f, 6.3f,
+          4.2f, 2.3f, 1.3f, 0.3f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -0.2f, -1.1f, -2.6f, -4.5f, -5.8f, -6.4f, -7.0f, -7.2f, -7.2f, -7.2f,
+          -5.2f, -1.11022e-15f, -1.11022e-15f }
+    },
+    {
+        "fullbass", EQZ_ISO31_BANDS_MAX, 5.0f,
+        { -8.0f, -8.0f, -8.0f, -8.0f, -8.0f, -7.6f, -4.6f, -0.3f, 4.9f, 9.1f,
+          9.6f, 9.6f, 9.6f, 9.0f, 7.2f, 5.2f, 3.3f, 1.6f, 0.4f, -0.8f, -2.0f,
+          -3.1f, -4.3f, -5.7f, -7.1f, -8.2f, -9.0f, -9.7f, -10.7f, -11.2f,
+          -11.2f }
+    },
+    {
+        "fullbasstreble", EQZ_ISO31_BANDS_MAX, 4.0f,
+        { 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 6.9f, 6.7f, 6.4f, 5.8f, 4.8f,
+          2.5f, -0.2f, -2.9f, -5.9f, -7.2f, -6.3f, -4.8f, -3.7f, -2.5f, -1.1f,
+          0.3f, 2.1f, 4.2f, 6.6f, 8.3f, 9.4f, 10.4f, 11.6f, 12.0f, 12.0f }
+    },
+    {
+        "fulltreble", EQZ_ISO31_BANDS_MAX, 3.0f,
+        { -9.6f, -9.6f, -9.6f, -9.6f, -9.6f, -9.6f, -9.6f, -9.6f, -9.6f, -9.6f,
+          -9.6f, -9.6f, -9.6f, -8.7f, -6.3f, -3.5f, -0.4f, 2.4f, 4.3f, 6.3f,
+          8.2f, 9.9f, 11.6f, 13.5f, 15.3f, 16.0f, 16.0f, 16.0f, 16.0f, 16.8f,
+          16.8f }
+    },
+    {
+        "headphones", EQZ_ISO31_BANDS_MAX, 4.0f,
+        { 4.8f, 4.8f, 4.8f, 4.8f, 4.8f, 4.9f, 6.0f, 7.6f, 9.5f, 11.0f, 10.8f,
+          8.5f, 5.4f, 2.2f, -1.6f, -3.2f, -2.9f, -2.4f, -1.9f, -1.2f, -0.3f,
+          0.8f, 1.8f, 2.9f, 3.9f, 5.1f, 6.4f, 7.8f, 10.7f, 14.4f, 14.4f }
+    },
+    {
+        "largehall", EQZ_ISO31_BANDS_MAX, 5.0f,
+        { 10.4f, 10.4f, 10.4f, 10.4f, 10.4f, 10.4f, 10.4f, 10.4f, 10.4f, 10.4f,
+          9.9f, 7.5f, 5.6f, 5.6f, 5.6f, 5.5f, 3.2f, -1.11022e-15f, -1.2f,
+          -2.4f, -3.6f, -4.5f, -4.8f, -4.8f, -4.8f, -4.8f, -4.8f, -4.8f, -3.5f,
+          -1.11022e-15f, -1.11022e-15f }
+    },
+    {
+        "live", EQZ_ISO31_BANDS_MAX, 7.0f,
+        { -4.8f, -4.8f, -4.8f, -4.8f, -4.8f, -4.7f, -4.0f, -3.0f, -1.8f, -0.5f,
+          1.0f, 2.7f, 4.0f, 4.7f, 5.4f, 5.6f, 5.6f, 5.6f, 5.6f, 5.6f, 5.6f,
+          5.6f, 5.6f, 5.3f, 4.6f, 3.9f, 3.3f, 2.6f, 2.4f, 2.4f, 2.4f }
+    },
+    {
+        "party", EQZ_ISO31_BANDS_MAX, 6.0f,
+        { 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 7.2f, 6.4f,
+          2.8f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, 2.0f,
+          7.2f, 7.2f }
+    },
+    {
+        "pop", EQZ_ISO31_BANDS_MAX, 6.0f,
+        { -1.6f, -1.6f, -1.6f, -1.6f, -1.6f, -1.5f, -0.4f, 1.0f, 2.7f, 4.4f,
+          5.5f, 6.6f, 7.2f, 7.6f, 7.9f, 8.0f, 7.1f, 5.6f, 4.6f, 3.4f, 2.1f,
+          0.8f, -0.2f, -1.2f, -2.1f, -2.4f, -2.4f, -2.4f, -2.2f, -1.6f, -1.6f }
+    },
+    {
+        "reggae", EQZ_ISO31_BANDS_MAX, 8.0f,
+        { -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f, -1.11022e-15f, -1.11022e-15f,
+          -1.11022e-15f, -1.2f, -4.1f, -5.5f, -3.2f, -1.11022e-15f, 1.5f, 3.1f,
+          4.7f, 6.0f, 6.4f, 6.4f, 6.4f, 6.3f, 4.7f, 1.5f, -1.11022e-15f,
+          -1.11022e-15f, -1.11022e-15f }
+    },
+    {
+        "rock", EQZ_ISO31_BANDS_MAX, 5.0f,
+        { 8.0f, 8.0f, 8.0f, 8.0f, 8.0f, 7.9f, 7.5f, 7.0f, 6.3f, 5.3f, 3.0f,
+          -1.8f, -5.7f, -6.8f, -7.7f, -7.9f, -6.0f, -3.2f, -1.7f, -0.1f,
+          1.4f, 2.9f, 4.4f, 6.1f, 7.8f, 9.0f, 10.0f, 10.9f, 11.2f, 11.2f,
+          11.2f }
+    },
+    {
+        "ska", EQZ_ISO31_BANDS_MAX, 6.0f,
+        { -2.4f, -2.4f, -2.4f, -2.4f, -2.4f, -2.5f, -2.9f, -3.5f, -4.2f, -4.7f,
+          -4.7f, -4.5f, -4.0f, -3.0f, -1.4, 0.4f, 2.4f, 4.0f, 4.4f, 4.8f, 5.0f,
+          5.3f, 5.8f, 6.8f, 8.1f, 8.9f, 9.1f, 9.3f, 10.1f, 9.6f, 9.6f }
+    },
+    {
+        "soft", EQZ_ISO31_BANDS_MAX, 5.0f,
+        { 4.8f, 4.8f, 4.8f, 4.8f, 4.8f, 4.7f, 4.2f, 3.5f, 2.7f, 1.8f, 1.1f,
+          0.6f, -1.11022e-15, -0.9f, -2.0f, -2.4f, -1.4f, -1.11022e-15, 0.8f,
+          1.6f, 2.4f, 3.3f, 4.3f, 5.7f, 7.2f, 8.2f, 8.6f, 9.0f, 10.1f, 12.0f,
+          12.0f }
+    },
+    {
+        "softrock", EQZ_ISO31_BANDS_MAX, 7.0f,
+        { 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 3.9f,
+          3.2f, 2.4f, 1.6f, 0.8f, -0.3f, -2.3f, -4.0f, -4.4f, -4.9f, -5.3f,
+          -5.5f, -5.6f, -5.1f, -4.1f, -3.0f, -2.1f, -1.2f, 0.6f, 8.8f, 8.8f }
+    },
+    {
+        "techno", EQZ_ISO31_BANDS_MAX, 5.0f,
+        { 8.0f, 8.0f, 8.0f, 8.0f, 8.0f, 7.9f, 7.6f, 7.2f, 6.7f, 5.9f, 4.6f,
+          2.3f, -0.1f, -2.4f, -4.7f, -5.6f, -5.3f, -4.8f, -4.2f, -3.4f, -2.5f,
+          -1.2f, 0.5f, 3.1f, 6.4f, 8.2f, 8.9f, 9.4f, 9.6f, 8.8f, 8.8f }
+    },
+};
+
+/* ********************* Preset table helper functions ********************* */
+
+/* Function for retrieving a pointer to the boosts/cuts */
+static inline
+const float * EqzGetAmpPtr( const eqz_base_preset_t * p_preset )
+{
+    return ( const float * ) ( p_preset + 1 );
+}
+
+/* Function for easily finding the size of a single preset table structure */
+static inline
+size_t EqzGetPresetSize( const eqz_base_preset_t * p_preset )
+{
+    return sizeof( *p_preset ) + p_preset->i_band * sizeof( float );
+}
+
+/* Function for retrieving the preset table at the specified zero-based
+ * index */
+static inline const eqz_base_preset_t *
+EqzGetPresetAt( const eqz_base_preset_t * p_preset_table, int i_index )
+{
+    return ( const eqz_base_preset_t * ) ( ( int8_t * ) p_preset_table
+        + i_index * EqzGetPresetSize( p_preset_table ) );
+}
+
+/* Function for retrieving the correct preset table by type */
+static inline
+const eqz_base_preset_t * EqzGetPresetTable( int i_type_number )
+{
+    switch( i_type_number )
+    {
+        case EQZ_VLC10_TYPE:
+            return EQZ_BASE_PRESET( &eqz_preset_vlc_10b );
+        case EQZ_ISO10_TYPE:
+            return EQZ_BASE_PRESET( &eqz_preset_iso_10b );
+        case EQZ_ISO15_TYPE:
+            return EQZ_BASE_PRESET( &eqz_preset_iso_15b );
+        case EQZ_ISO31_TYPE:
+            return EQZ_BASE_PRESET( &eqz_preset_iso_31b );
+        default:
+            return NULL;
+    }
+}
+
+/* ********************** More type-related functions ********************** */
+
+/* Function for retrieving the number of bands in an equalizer by its type */
+static inline int EqzGetNumBandsByType( int i_type_number )
+{
+    return EqzGetPresetTable( i_type_number )->i_band;
+}
+
 #endif
diff --git a/src/misc/eqz_util.c b/src/misc/eqz_util.c
new file mode 100644
index 0000000..caf0171
--- /dev/null
+++ b/src/misc/eqz_util.c
@@ -0,0 +1,297 @@
+/*****************************************************************************
+ * vlc_eqz_util.h: Equalizer utility functions
+ *****************************************************************************
+ * Copyright (C) 2015 Ronald Wright
+ * $Id$
+ *
+ * Author: Ronald Wright <logiconcepts819 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <vlc_common.h>
+#include <vlc_charset.h>
+#include <vlc_interpolation.h>
+#include <vlc_eqz_util.h>
+
+#define EQZ_INCLUDE_MATH_ROUTINES 1
+#include "../../modules/audio_filter/equalizer_presets.h"
+
+/*****************************************************************************
+ * Documentation : Read vlc_interpolation.h
+ *****************************************************************************/
+
+/*****************************************************************************
+ *  Private types.
+ *****************************************************************************/
+
+struct _vlc_band_parse_t
+{
+    float * pf_orig_amp;
+    int i_orig_bands;
+    vlc_interpolant_t itp;
+    float * pf_amp;
+    int i_type;
+};
+
+/*****************************************************************************
+ * Implementation of routines.
+ *****************************************************************************/
+
+/* Determine the most valid equalizer identifier from the number of bands and
+ * a string with a possibly invalid identifier */
+static char GetValidIdentifier( vlc_object_t *p_obj, int i_cur_type,
+                                const char * psz_identifier, int i_bands )
+{
+    char eqz_identifier;
+    switch( i_bands )
+    {
+        case EQZ_ISO10_BANDS_MAX:
+            /* fall-through */
+        case EQZ_ISO15_BANDS_MAX:
+            /* fall-through */
+        case EQZ_ISO31_BANDS_MAX:
+        {
+            size_t i_id_len = psz_identifier ? strlen( psz_identifier ) : 0;
+            if( i_id_len != 1 ||
+                ( psz_identifier[0] != EQZ_VLC_IDENTIFIER &&
+                  psz_identifier[0] != EQZ_ISO_IDENTIFIER ) )
+            {
+                eqz_identifier = EqzGetDefaultIdentifier( i_bands );
+                if( i_id_len == 0 )
+                {
+                    /* No identifier specified in string */
+                    if( i_bands == EQZ_ISO10_BANDS_MAX )
+                    {
+                        msg_Warn( p_obj, "equalizer identifier not specified "
+                            "for 10 bands; assuming VLC bands" );
+                    }
+                }
+                else
+                {
+                    /* Found invalid identifier */
+                    msg_Warn( p_obj, "invalid equalizer identifier '%s'; "
+                        "assuming default equalizer type for %d bands ('%c')",
+                        psz_identifier, i_bands, eqz_identifier );
+                }
+            }
+            else if( i_bands != EQZ_ISO10_BANDS_MAX &&
+                psz_identifier[0] == EQZ_VLC_IDENTIFIER )
+            {
+                /* Found VLC equalizer type for 15 and 31 bands, but we only
+                 * provide ISO bands in these cases */
+                eqz_identifier = EQZ_ISO_IDENTIFIER;
+                msg_Warn( p_obj, "equalizer identifier '%s' not defined for "
+                    "%d bands; assuming ISO bands", psz_identifier, i_bands );
+            }
+            else
+            {
+                /* Found VLC or ISO identifier for 10 bands and ISO identifier
+                 * for 15 and 31 bands */
+                eqz_identifier = psz_identifier[0];
+            }
+            break;
+        }
+        default:
+        {
+            /* Default is to assume current mode */
+            int i_cur_bands = EqzGetNumBandsByType( i_cur_type );
+            int i_smallest_bands = __MIN( i_cur_bands, i_bands );
+            eqz_identifier = EqzGetIdentifier( i_cur_type );
+            if( i_smallest_bands == 1 )
+            {
+                msg_Warn( p_obj, "found only one band (which is not 10, 15, "
+                    "or 31, as it ought to be); assuming it corresponds to "
+                    "the first band of the currently active equalizer type "
+                    "(%d-band %s equalizer)", i_cur_bands,
+                    EqzGetStringIdentifier( eqz_identifier ) );
+            }
+            else
+            {
+                msg_Warn( p_obj, "found unexpected number of bands (%d, which "
+                    "is not 10, 15, or 31, as it ought to be); assuming the "
+                    "%s%d bands correspond to the %s%d bands of the currently "
+                    "active equalizer type (%d-band %s equalizer)", i_bands,
+                    i_cur_bands < i_bands ? "first " : "", i_smallest_bands,
+                    i_cur_bands > i_bands ? "first " : "", i_smallest_bands,
+                    i_cur_bands, EqzGetStringIdentifier( eqz_identifier ) );
+            }
+            break;
+        }
+    }
+    return eqz_identifier;
+}
+
+/* Try setting up an interpolant */
+static int SetUpInterpolant( int i_bands, int i_type, float * pf_amp,
+                             vlc_interpolant_t * itp )
+{
+    int i_ret_val = VLC_ENOMEM;
+    float * pf_freq = malloc( i_bands * sizeof( *pf_freq ) );
+    if( likely( pf_freq != NULL ) )
+    {
+        for( int k = 0; k < i_bands; k++ )
+            pf_freq[k] = EqzGetTrueFrequency( i_type, k );
+        i_ret_val = vlc_create_interpolant( itp, pf_freq, pf_amp, i_bands );
+        free( pf_freq );
+    }
+    return i_ret_val;
+}
+
+int vlc_eqz_util_band_parser_init( vlc_object_t * p_obj,
+                                   vlc_band_parse_t * p_parse_ctx,
+                                   const char * psz_bands, int i_type,
+                                   float * pf_amp )
+{
+    vlc_band_parse_t parse_ctx;
+    vlc_interpolant_t itp = NULL;
+    float * pf_orig_amp;
+    int i = 0, i_toks = 0;
+
+    /* Duplicate memory of bands string value */
+    char *p = strdup( psz_bands ), *q = NULL, *saved;
+    if( unlikely( !p ) )
+        return VLC_ENOMEM;
+
+    /* Allocate memory for storing the context data */
+    int i_ret_val = VLC_ENOMEM;
+    int i_size = EQZ_BANDS_MAX;
+    if( !pf_amp )
+        i_size += EqzGetNumBandsByType( i_type );
+    parse_ctx = malloc( sizeof( *parse_ctx ) + i_size * sizeof( *pf_amp ) );
+    if( likely( parse_ctx != NULL ) )
+    {
+        pf_orig_amp = ( float * ) ( parse_ctx + 1 );
+        if( !pf_amp )
+            pf_amp = pf_orig_amp + EQZ_BANDS_MAX;
+
+        for( char *tok = strtok_r( p, " ", &saved ); tok != NULL;
+                   tok = strtok_r( NULL, " ", &saved ) )
+        {
+            if( i < EQZ_BANDS_MAX )
+            {
+                char *r = tok;
+                float f = us_strtof( tok, &r );
+                if( r == tok || *r != '\0' || isnan( f ) )
+                    pf_orig_amp[i++] = 0.f;
+                else
+                    pf_orig_amp[i++] = f;
+            }
+
+            /* Even if we exceed EQZ_BANDS_MAX or encounter malformed tokens,
+             * keep counting the number of tokens */
+            i_toks++;
+            q = tok;
+        }
+
+        /* Check whether we have a potential identifier at the end of the
+         * string. One way to check is if the last token is numeric. If not,
+         * then we consider it as a potential identifier. */
+        if( q ) /* q is non-NULL if we have at least one token */
+        {
+            char *r = q;
+            float f = us_strtof( q, &r );
+            if( r == q || *r != '\0' || isnan( f ) )
+            {
+                /* In this case, the last token could not be converted into a
+                 * float, so we treat it as a potential identifier by excluding
+                 * it from all counts */
+                if( i <= EQZ_BANDS_MAX )
+                    i--; /* potential identifier found within bounds */
+                i_toks--;
+            }
+            else
+                q = NULL; /* don't treat any tokens as potential identifiers */
+        }
+
+        i_ret_val = VLC_SUCCESS;
+        if( i > 0 )
+        {
+            /* Should there be a mismatch in the number or type of bands,
+             * interpolate */
+            char id = GetValidIdentifier( p_obj, i_type, q, i_toks );
+            if( i != EqzGetNumBandsByType( i_type ) ||
+                id != EqzGetIdentifier( i_type ) )
+            {
+                int i_bands_type = EqzGetTypeByNumBands( i_toks, id );
+                if( i_bands_type != EQZ_UNKNOWN_TYPE )
+                    /* Try creating the interpolant */
+                    i_ret_val = SetUpInterpolant( i, i_bands_type, pf_orig_amp,
+                                                  &itp );
+            }
+        }
+    }
+
+    /* Check status of i_ret_val.  If it is not equal to VLC_SUCCESS, then
+     * clean up.  Otherwise, set up structure */
+    if( i_ret_val == VLC_SUCCESS )
+    {
+        parse_ctx->itp = itp;
+        parse_ctx->pf_orig_amp = pf_orig_amp;
+        parse_ctx->i_orig_bands = i;
+        parse_ctx->pf_amp = pf_amp;
+        parse_ctx->i_type = i_type;
+        *p_parse_ctx = parse_ctx;
+    }
+    else
+    {
+        free( parse_ctx );
+    }
+
+    free( p );
+    return i_ret_val;
+}
+
+const float * vlc_eqz_util_get_amp_array( const vlc_band_parse_t parse_ctx )
+{
+    int i_band = EqzGetNumBandsByType( parse_ctx->i_type );
+    if( parse_ctx->itp )
+    {
+        /* Construct the interpolated set of bands */
+        for( int k = 0; k < i_band; k++ )
+        {
+            float f_freq = EqzGetTrueFrequency( parse_ctx->i_type, k );
+            parse_ctx->pf_amp[k] = vlc_interpolate( parse_ctx->itp, f_freq );
+        }
+    }
+    else
+    {
+        /* Read bands as they are */
+        int k;
+        for( k = 0; k < parse_ctx->i_orig_bands; k++ )
+            parse_ctx->pf_amp[k] = parse_ctx->pf_orig_amp[k];
+        while( k < i_band )
+            parse_ctx->pf_amp[k++] = 0.f;
+    }
+    return parse_ctx->pf_amp;
+}
+
+void vlc_eqz_util_band_parser_destroy( vlc_band_parse_t parse_ctx )
+{
+    if( likely( parse_ctx ) )
+    {
+        free( parse_ctx->itp );
+        free( parse_ctx );
+    }
+}
-- 
1.9.1



More information about the vlc-devel mailing list