jaakkopasanen / AutoEq

Automatic headphone equalization from frequency responses
MIT License
13.2k stars 2.47k forks source link

Voicemeeter BANANA EQ preset support routine (fixes #682) / SuperEQ preset support routine (fixes #679) #689

Closed JuhaTP closed 1 year ago

JuhaTP commented 1 year ago

Here's a routine which writes EQ settings for Voicemeeter Banana EQ.

I have tested functionality with command line script:

python3 -m autoeq --input-file="measurements/oratory1990/data/over-ear/AKG K240 Studio.csv" --output-dir="my_results" --bass-boost 3 --compensation="compensation/harman_over-ear_2018_wo_bass.csv" --voicemeeterpeq --parametric-eq --parametric-eq-config=VOICEMEETER_PEQ

which produces preset file AKG K240 Studio.xml :

<?xml version="1.0" encoding="utf-8"?>
<VBAudioVoicemeeterBUSEQConfig>
<VoiceMeeterBUSEQ>
    <Bus index='1' channel='1' cell='1' EQon='1' EQtype='5' dblevel='6.09' freq='65.00' Q='0.70' />
    <Bus index='1' channel='1' cell='2' EQon='1' EQtype='0' dblevel='-3.37' freq='205.49' Q='0.40' />
    <Bus index='1' channel='1' cell='3' EQon='1' EQtype='0' dblevel='5.36' freq='4220.58' Q='2.96' />
    <Bus index='1' channel='1' cell='4' EQon='1' EQtype='0' dblevel='3.52' freq='71.50' Q='0.93' />
    <Bus index='1' channel='1' cell='5' EQon='1' EQtype='0' dblevel='5.50' freq='1591.25' Q='3.11' />
    <Bus index='1' channel='1' cell='6' EQon='1' EQtype='6' dblevel='3.70' freq='16744.04' Q='0.70' />
    <Bus index='1' channel='2' cell='1' EQon='1' EQtype='5' dblevel='6.09' freq='65.00' Q='0.70' />
    <Bus index='1' channel='2' cell='2' EQon='1' EQtype='0' dblevel='-3.37' freq='205.49' Q='0.40' />
    <Bus index='1' channel='2' cell='3' EQon='1' EQtype='0' dblevel='5.36' freq='4220.58' Q='2.96' />
    <Bus index='1' channel='2' cell='4' EQon='1' EQtype='0' dblevel='3.52' freq='71.50' Q='0.93' />
    <Bus index='1' channel='2' cell='5' EQon='1' EQtype='0' dblevel='5.50' freq='1591.25' Q='3.11' />
    <Bus index='1' channel='2' cell='6' EQon='1' EQtype='6' dblevel='3.70' freq='16744.04' Q='0.70' />
    <Bus index='1' channel='3' cell='1' EQon='1' EQtype='5' dblevel='6.09' freq='65.00' Q='0.70' />
    <Bus index='1' channel='3' cell='2' EQon='1' EQtype='0' dblevel='-3.37' freq='205.49' Q='0.40' />
    <Bus index='1' channel='3' cell='3' EQon='1' EQtype='0' dblevel='5.36' freq='4220.58' Q='2.96' />
    <Bus index='1' channel='3' cell='4' EQon='1' EQtype='0' dblevel='3.52' freq='71.50' Q='0.93' />
    <Bus index='1' channel='3' cell='5' EQon='1' EQtype='0' dblevel='5.50' freq='1591.25' Q='3.11' />
    <Bus index='1' channel='3' cell='6' EQon='1' EQtype='6' dblevel='3.70' freq='16744.04' Q='0.70' />
    <Bus index='1' channel='4' cell='1' EQon='1' EQtype='5' dblevel='6.09' freq='65.00' Q='0.70' />
    <Bus index='1' channel='4' cell='2' EQon='1' EQtype='0' dblevel='-3.37' freq='205.49' Q='0.40' />
    <Bus index='1' channel='4' cell='3' EQon='1' EQtype='0' dblevel='5.36' freq='4220.58' Q='2.96' />
    <Bus index='1' channel='4' cell='4' EQon='1' EQtype='0' dblevel='3.52' freq='71.50' Q='0.93' />
    <Bus index='1' channel='4' cell='5' EQon='1' EQtype='0' dblevel='5.50' freq='1591.25' Q='3.11' />
    <Bus index='1' channel='4' cell='6' EQon='1' EQtype='6' dblevel='3.70' freq='16744.04' Q='0.70' />
    <Bus index='1' channel='5' cell='1' EQon='1' EQtype='5' dblevel='6.09' freq='65.00' Q='0.70' />
    <Bus index='1' channel='5' cell='2' EQon='1' EQtype='0' dblevel='-3.37' freq='205.49' Q='0.40' />
    <Bus index='1' channel='5' cell='3' EQon='1' EQtype='0' dblevel='5.36' freq='4220.58' Q='2.96' />
    <Bus index='1' channel='5' cell='4' EQon='1' EQtype='0' dblevel='3.52' freq='71.50' Q='0.93' />
    <Bus index='1' channel='5' cell='5' EQon='1' EQtype='0' dblevel='5.50' freq='1591.25' Q='3.11' />
    <Bus index='1' channel='5' cell='6' EQon='1' EQtype='6' dblevel='3.70' freq='16744.04' Q='0.70' />
    <Bus index='1' channel='6' cell='1' EQon='1' EQtype='5' dblevel='6.09' freq='65.00' Q='0.70' />
    <Bus index='1' channel='6' cell='2' EQon='1' EQtype='0' dblevel='-3.37' freq='205.49' Q='0.40' />
    <Bus index='1' channel='6' cell='3' EQon='1' EQtype='0' dblevel='5.36' freq='4220.58' Q='2.96' />
    <Bus index='1' channel='6' cell='4' EQon='1' EQtype='0' dblevel='3.52' freq='71.50' Q='0.93' />
    <Bus index='1' channel='6' cell='5' EQon='1' EQtype='0' dblevel='5.50' freq='1591.25' Q='3.11' />
    <Bus index='1' channel='6' cell='6' EQon='1' EQtype='6' dblevel='3.70' freq='16744.04' Q='0.70' />
    <Bus index='1' channel='7' cell='1' EQon='1' EQtype='5' dblevel='6.09' freq='65.00' Q='0.70' />
    <Bus index='1' channel='7' cell='2' EQon='1' EQtype='0' dblevel='-3.37' freq='205.49' Q='0.40' />
    <Bus index='1' channel='7' cell='3' EQon='1' EQtype='0' dblevel='5.36' freq='4220.58' Q='2.96' />
    <Bus index='1' channel='7' cell='4' EQon='1' EQtype='0' dblevel='3.52' freq='71.50' Q='0.93' />
    <Bus index='1' channel='7' cell='5' EQon='1' EQtype='0' dblevel='5.50' freq='1591.25' Q='3.11' />
    <Bus index='1' channel='7' cell='6' EQon='1' EQtype='6' dblevel='3.70' freq='16744.04' Q='0.70' />
    <Bus index='1' channel='8' cell='1' EQon='1' EQtype='5' dblevel='6.09' freq='65.00' Q='0.70' />
    <Bus index='1' channel='8' cell='2' EQon='1' EQtype='0' dblevel='-3.37' freq='205.49' Q='0.40' />
    <Bus index='1' channel='8' cell='3' EQon='1' EQtype='0' dblevel='5.36' freq='4220.58' Q='2.96' />
    <Bus index='1' channel='8' cell='4' EQon='1' EQtype='0' dblevel='3.52' freq='71.50' Q='0.93' />
    <Bus index='1' channel='8' cell='5' EQon='1' EQtype='0' dblevel='5.50' freq='1591.25' Q='3.11' />
    <Bus index='1' channel='8' cell='6' EQon='1' EQtype='6' dblevel='3.70' freq='16744.04' Q='0.70' />
</VoiceMeeterBUSEQ>
</VBAudioVoicemeeterBUSEQConfig>

AKG K240 Studio

Also tested with a config file voicemeeterpeq.yaml :

optimizer:
  min_f: 20
  max_f: 20000 
  max_time: null 
  min_change_rate: null
  min_std: null 
filter_defaults:
  min_fc: 20
  max_fc: 10000
  min_q: 0.18248
  max_q: 6
  min_gain: -20
  max_gain: 20
filters:
  - type: LOW_SHELF
    fc: 65.406392
    q: 0.7
  - type: PEAKING
  - type: PEAKING
  - type: PEAKING
  - type: PEAKING
  - type: HIGH_SHELF
    fc:  16744.036
    min_fc: 15000 
    max_fc: 20000
    min_q: 0.4 
    max_q: 0.7

which generates similar .xml file:

python3 -m autoeq --input-file="measurements/oratory1990/data/over-ear/AKG K240 Studio.csv" --output-dir="my_results" --bass-boost 3 --compensation="compensation/harman_over-ear_2018_wo_bass.csv" --voicemeeterpeq --parametric-eq --parametric-eq-config=voicemeeterpeq.yaml

Both .xml files loaded properly in Voicemeeter BANANA.

As an alternative, one can use lxml for to write the preset file. Needed changes are then:

install lxml library

in frequency_response.py

from lxml import etree

and use re-written write_voicemeeter_peq() :

    def write_voicemeeter_peq(self, file_path, peqs):
        """Writes Voicemeeter BANANA EQ settings to a XML type preset file."""
        file_path = os.path.abspath(file_path)
        f = self.generate_frequencies(f_step=DEFAULT_BIQUAD_OPTIMIZATION_F_STEP)
        compound = PEQ(f, peqs[0].fs, [])
        for peq in peqs:
            for filt in peq.filters:
                compound.add_filter(filt)

        EQType = 0  # equalizer type 0 = peak, 5 = low-shelf, 6=high-self

        BUSConfig = etree.Element('VBAudioVoicemeeterBUSEQConfig')
        BUSEQ = etree.SubElement(BUSConfig, 'VoiceMeeterBUSEQ')

        for idx in range(1,2):
            for channel in range(1,9):
                for i, filt in enumerate(compound.filters):
                    if 0 < i < 5:
                        EQType = 0
                    elif i == 0:
                        EQType = 5
                    else:
                        EQType = 6

                    etree.SubElement(BUSEQ, 'Bus', index='{:d}'.format(idx), channel='{:d}'.format(channel), cell='{:d}'.format(i+1), \
                                                EQon='1', EQType='{:d}'.format(EQType), dblevel='{:.2f}'.format(filt.gain), freq='{:.2f}'.format(filt.fc), Q='{:.2f}'.format(filt.q))

            tree = etree.ElementTree(BUSConfig)
            tree.write(file_path.replace('.txt', '.xml'), encoding = "UTF-8", xml_declaration=True, pretty_print=True)

=====

SuperEQ, which can be found on few software as like Foobar2k Desktop/Foobar2k Mobile and DeaDBeeF (originally SuperEQ was made for Winamp by N. Shibata), preset "generator" calculates integer type gains for EQ 18 bands by interpolating gain values directly from Equalization curve data (green line in plot) at equalizer band center frequencies. Center frequencies are [55, 77, 110, 156, 220, 311, 440, 622, 880, 1200, 1800, 2500, 3500, 5000, 7000, 10000, 14000, 20000] (Hz). The 18 bands covers the following frequency ranges:

 # | f (low)  | f (high)
 1 | 0 Hz     | 65 Hz
 2 | 65 Hz    | 93 Hz
 3 | 93 Hz    | 131 Hz
 4 | 131 Hz   | 185 Hz
 5 | 185 Hz   | 262 Hz
 6 | 262 Hz   | 370 Hz
 7 | 370 Hz   | 523 Hz
 8 | 523 Hz   | 740 Hz
 9 | 740 Hz   | 1047 Hz
10 | 1047 Hz  | 1480 Hz
11 | 1480 Hz  | 2093 Hz
12 | 2093 Hz  | 2960 Hz
13 | 2960 Hz  | 4186 Hz
14 | 4186 Hz  | 5920 Hz
15 | 5920 Hz  | 8372 Hz
16 | 8372 Hz  | 11840 Hz
17 | 11840 Hz | 16744 Hz
18 | 16744 Hz | 22000±Hz

Test script:

python3 -m autoeq --input-file="measurements/oratory1990/data/over-ear/AKG K240 Studio.csv" --output-dir="my_results" --bass-boost 3 --compensation="compensation/harman_over-ear_2018_wo_bass.csv" --supereq

Plot shows result for AKG K240 Studio:

AKG K240 Studio

Saved preset file (AKG K240 Studio supereq.feq) format is:

6
4
0
-2
-3
-3
-2
-1
-1
1
4
-1
3
4
-1
-1
1
5
jaakkopasanen commented 1 year ago

I added Voicemeeter to webapp. I won't add Supereq since the filters aren't standard. Also no more special eq options (like Voicemeeter or Rockbox) in the command line interface.

Thanks a lot for this PR, helped a lot with adding Voicemeeter!

JuhaTP commented 1 year ago

I added Voicemeeter to webapp. I won't add Supereq since the filters aren't standard. Also no more special eq options (like Voicemeeter or Rockbox) in the command line interface.

Thanks a lot for this PR, helped a lot with adding Voicemeeter!

You're welcome.

OK with the webapp implementation only. Yes, Supereq needs to be implemented as it is originally done to get it work 100% properly. If some one needs this feature I have the coding available within a fork of AutoEq.