Skip to content

Commit

Permalink
Optimized min_std param for different PEQ configurations. Removed ten…
Browse files Browse the repository at this point in the history
…sorflow from requirements.txt. Added example PEQ config file and improved usage instructions about it.
  • Loading branch information
jaakkopasanen committed Sep 11, 2022
1 parent 6c7abce commit 2b7386c
Show file tree
Hide file tree
Showing 10 changed files with 577 additions and 22 deletions.
22 changes: 16 additions & 6 deletions autoeq.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,23 +256,33 @@ def cli_args():
'no value needed.')
arg_parser.add_argument('--ten_band_eq', action='store_true',
help='Shortcut parameter for activating standard ten band eq optimization.')
arg_parser.add_argument('--parametric_eq_config', type=str, default='4_PEAKING_WITH_LOW_SHELF,4_PEAKING_WITH_HIGH_SHELF',
help='Name of parametric equalizer configuration or a path to a configuration file. \n\n'
arg_parser.add_argument('--parametric_eq_config', type=str,
default='4_PEAKING_WITH_LOW_SHELF,4_PEAKING_WITH_HIGH_SHELF',
help='Name of parametric equalizer configuration or a path to a configuration file. '
'Available named configurations are "10_PEAKING" for 10 peaking filters, '
'"8_PEAKING_WITH_SHELVES" for 8 peaking filters and a low shelf at 105 Hz for bass '
'adjustment and high shelf at 10 kHz for treble adjustment, '
'adjustment and a high shelf at 10 kHz for treble adjustment, '
'"4_PEAKING_WITH_LOW_SHELF" for 4 peaking filters and a low shelf at 105 Hz for bass '
'adjustment, "4_PEAKING_WITH_HIGH_SHELF" for 4 peaking filters and a high shelf '
'at 10 kHz for treble adjustments. You can give multiple named configurations by '
'separating the names with commas. \n\n'
'separating the names with commas and filter sets will be built on top of each other. '
'When the value is a file path, the file will be read and used as a configuration. '
'The file needs to be a YAML file with "filters" field as a list of filter '
'configurations, each of which can define "fc", "min_fc", "max_fc", "q", "min_q", '
'"max_q", "gain", "min_gain", "max_gain" and "type" fields. When the fc, q or gain '
'value is given, the parameter won\'t be optimized for the filter. "type" needs to '
'be either "LOW_SHELF", "PEAKING" or "HIGH_SHELF". Also "filter_defaults" field is '
'supported on the top level and it can have the same fields as the filters do.\n\n'
'Defaults to "4_PEAKING_WITH_LOW_SHELF,4_PEAKING_WITH_HIGH_SHELF".'),
'supported on the top level and it can have the same fields as the filters do. '
'All fields missing from the filters will be read from "filter_defaults". '
'Defaults to "4_PEAKING_WITH_LOW_SHELF,4_PEAKING_WITH_HIGH_SHELF". '
'Optimizer behavior can be adjusted by defining "optimizer" field which has fields '
'"min_f" and "max_f" for lower and upper bounds of the optimization range, "max_time" '
'for maximum optimization duration in seconds, "target_loss" for RMSE target level '
'upon reaching which the optimization is ended, "min_change_rate" for minimum rate '
'of improvement in db/s and "min_std" for minimum standard deviation of the last few '
'loss values. "min_change_rate" and "min_std" end the optimization when further time '
'spent optimizing can\'t be expected to improve the results dramatically. See '
'peq.yaml for an example.'),
arg_parser.add_argument('--fixed_band_eq_config', type=str, default='10_BAND_GRAPHIC_EQ',
help='Path to fixed band equalizer configuration. The file format is the same YAML as '
'for parametric equalizer.')
Expand Down
7 changes: 5 additions & 2 deletions constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
DEFAULT_PEQ_OPTIMIZER_MAX_TIME = None
DEFAULT_PEQ_OPTIMIZER_TARGET_LOSS = None
DEFAULT_PEQ_OPTIMIZER_MIN_CHANGE_RATE = None
DEFAULT_PEQ_OPTIMIZER_MIN_STD = 0.1
DEFAULT_PEQ_OPTIMIZER_MIN_STD = 0.002

DEFAULT_FIXED_BAND_FILTER_MIN_GAIN = -12
DEFAULT_FIXED_BAND_FILTER_MAX_GAIN = 12
Expand Down Expand Up @@ -75,6 +75,9 @@
'filters': [{'type': 'PEAKING'}] * 10
},
'8_PEAKING_WITH_SHELVES': {
'optimizer': {
'min_std': 0.008
},
'filters': [{
'type': 'LOW_SHELF',
'fc': 105,
Expand All @@ -87,7 +90,7 @@
},
'4_PEAKING_WITH_LOW_SHELF': {
'optimizer': {
'max_f': 10000
'max_f': 10000,
},
'filters': [{
'type': 'LOW_SHELF',
Expand Down
5 changes: 5 additions & 0 deletions frequency_response.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*_

import os
from copy import deepcopy
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import math
Expand Down Expand Up @@ -438,6 +439,8 @@ def write_readme(self, file_path, parametric_eq_peqs=None, fixed_band_eq_peq=Non
filter_ranges = ''
preamps = ''
for i, peq in enumerate(parametric_eq_peqs):
peq = deepcopy(peq)
peq.sort_filters()
for filt in peq.filters:
compound.add_filter(filt)
filter_ranges += f'1-{len(peq.filters) + n}'
Expand All @@ -453,6 +456,8 @@ def write_readme(self, file_path, parametric_eq_peqs=None, fixed_band_eq_peq=Non
else:
compound = PEQ(self.frequency.copy(), parametric_eq_peqs[0].fs, [])
for peq in parametric_eq_peqs:
peq = deepcopy(peq)
peq.sort_filters()
for filt in peq.filters:
compound.add_filter(filt)
s += f'Apply preamp of -{compound.max_gain + 0.1:.1f} dB when using parametric equalizer.\n\n'
Expand Down
2 changes: 1 addition & 1 deletion peq.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ def optimize(self):
except OptimizationFinished as err:
# Restore best params
self._parse_optimizer_params(self.history.params[np.argmin(self.history.loss)])
self.sort_filters()
#print(err)

def plot(self, fig=None, ax=None):
if fig is None:
Expand Down
31 changes: 31 additions & 0 deletions peq.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
optimizer:
min_f: 20
max_f: 20000 # Only optimizing up to 10 kHz because of we only have 6 filters
max_time: null # Letting optimizer finish since this is probably for a one-off custom optimization, not for API
min_change_rate: null # Letting optimizer finish
min_std: null # Letting optimizer finish
filter_defaults:
min_fc: 20
max_fc: 10000
min_q: 0.18248
max_q: 6
min_gain: -20
max_gain: 20
filters:
- type: PEAKING
- type: PEAKING
- type: PEAKING
- type: PEAKING
- type: PEAKING
- type: PEAKING
- type: PEAKING
- type: PEAKING
- type: LOW_SHELF
fc: 105 # Fixed center frequency at 105 Hz
q: 0.7 # Fixed quality of 0.7
- type: HIGH_SHELF
fc: 10000
min_fc: 5000 # Letting the optimizer adjust high shelf between 5 kHz and...
max_fc: 12000 # ...12 kHz
min_q: 0.4 # Shelf filters start to overshoot below 0.4
max_q: 0.7 # Shelf filters start to overshoot after 0.7
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ matplotlib~=3.5.2
pandas~=1.4.3
scipy~=1.8.1
numpy~=1.23.1
tensorflow~=2.9.1
tabulate~=0.8.10
soundfile~=0.10.2
pyyaml~=6.0
61 changes: 61 additions & 0 deletions research/neo_peq/animation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import sys
from pathlib import Path
from time import time
from copy import deepcopy
import yaml
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, FFMpegWriter
ROOT_PATH = Path().absolute().parent.parent
if str(ROOT_PATH) not in sys.path:
sys.path.insert(1, str(ROOT_PATH))
from frequency_response import FrequencyResponse
from constants import DEFAULT_FS, PEQ_CONFIGS

with open(ROOT_PATH.joinpath('peq.yaml')) as fh:
config = yaml.safe_load(fh)
fr = FrequencyResponse.read_from_csv(ROOT_PATH.joinpath('my_results/F3/Custom Art FIBAE 3 (CIEM).csv'))
config = PEQ_CONFIGS['4_PEAKING_WITH_LOW_SHELF']
config['optimizer']['min_std'] = 0.0
peqs = fr.optimize_parametric_eq(config, DEFAULT_FS)
peq = peqs[0]
#peq.plot()
#plt.show()


def interpolate(current, previous, ratio):
return previous + ratio * (current - previous)


param_history = []
time_history = []
times = []
for i in range(1, len(peq.history.params)):
for j in range(10):
param_history.append(interpolate(np.array(peq.history.params[i]), np.array(peq.history.params[i - 1]), j / 10))
time_history.append(interpolate(np.array(peq.history.time[i]), np.array(peq.history.time[i - 1]), j / 10))

fig, ax = FrequencyResponse.init_plot()
fills = [ax.fill_between([], [], []) for _ in peq.filters]
lines = [ax.plot([], [], linewidth=1, color=f'C{i}')[0] for i in range(len(peq.filters))]
lines.append(ax.plot([], [], color='black', linestyle='--')[0])
lines.append(ax.plot([], [], color='black')[0])
ax.set_ylim([-10, 15])


def animate(i):
peq._parse_optimizer_params(param_history[i])
ax.collections.clear()
for j, filt in enumerate(peq.filters):
ax.fill_between(filt.f, np.zeros(filt.fr.shape), filt.fr, alpha=0.3, color=f'C{j}')
lines[j].set_data(filt.f, filt.fr)
lines[-2].set_data(peq.f, peq.target)
lines[-1].set_data(peq.f, peq.fr)
ax.set_title(f't={time_history[i] * 1000:.0f} ms')
return lines


ani = FuncAnimation(fig, animate, frames=len(param_history), interval=3, repeat=False)
ani.save('peq.mp4', writer=FFMpegWriter(fps=60))
plt.show()
228 changes: 216 additions & 12 deletions research/neo_peq/loss.ipynb

Large diffs are not rendered by default.

242 changes: 242 additions & 0 deletions research/neo_peq/min_std.ipynb

Large diffs are not rendered by default.

Binary file added research/neo_peq/peq.mp4
Binary file not shown.

0 comments on commit 2b7386c

Please sign in to comment.