forked from jaakkopasanen/AutoEq
-
Notifications
You must be signed in to change notification settings - Fork 0
/
__main__.py
212 lines (200 loc) · 16 KB
/
__main__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import os
import argparse
import multiprocessing
import warnings
from autoeq.constants import DEFAULT_MAX_GAIN, DEFAULT_TREBLE_F_LOWER, DEFAULT_TREBLE_F_UPPER, \
DEFAULT_TREBLE_GAIN_K, DEFAULT_FS, DEFAULT_BIT_DEPTH, DEFAULT_PHASE, DEFAULT_F_RES, DEFAULT_BASS_BOOST_FC, \
DEFAULT_BASS_BOOST_Q, DEFAULT_SMOOTHING_WINDOW_SIZE, DEFAULT_TREBLE_SMOOTHING_WINDOW_SIZE, DEFAULT_TREBLE_BOOST_FC, \
DEFAULT_TREBLE_BOOST_Q, DEFAULT_PREAMP, DEFAULT_SOUND_SIGNATURE_SMOOTHING_WINDOW_SIZE, DEFAULT_MAX_SLOPE
from autoeq.batch_processing import batch_processing
def cli_args():
"""Parses command line arguments."""
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--input-file', type=str, required=False,
help='Path to measurement CSV data')
arg_parser.add_argument('--input-dir', type=str, required=False,
help='Path to input data directory. Will look for CSV files in the data directory and '
'recursively in sub-directories.')
arg_parser.add_argument('--output-dir', type=str, required=True,
help='Path to results directory. Will keep the same relative paths for files found '
'in input-dir.')
arg_parser.add_argument('--standardize-input', action='store_true',
help='Overwrite input data in standardized sampling and bias?')
arg_parser.add_argument('--new-only', action='store_true',
help='Only process input files which don\'t have results in output directory.')
arg_parser.add_argument('--target', type=str,
help='File path to CSV containing target curve.')
arg_parser.add_argument('--equalize', action='store_true',
help='Will run equalization if this parameter exists, no value needed.')
arg_parser.add_argument('--parametric-eq', action='store_true',
help='Will produce parametric eq settings if this parameter exists, no value needed.')
arg_parser.add_argument('--fixed-band-eq', action='store_true',
help='Will produce fixed band eq settings if this parameter exists, 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. '
'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 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 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. '
'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.')
arg_parser.add_argument('--convolution-eq', action='store_true',
help='Will produce impulse response for convolution equalizers if this parameter exists, '
'no value needed.')
arg_parser.add_argument('--fs', type=str, default=str(DEFAULT_FS),
help='Sampling frequency in Hertz for impulse response and parametric eq filters. Single '
'value or multiple values separated by commas eg 44100,48000. When multiple values '
'are given only the first one will be used for parametric eq. '
f'Defaults to {DEFAULT_FS}.')
arg_parser.add_argument('--bit-depth', type=int, default=DEFAULT_BIT_DEPTH,
help='Number of bits for every sample in impulse response. '
f'Defaults to {DEFAULT_BIT_DEPTH}.')
arg_parser.add_argument('--phase', type=str, default=DEFAULT_PHASE,
help='Impulse response phase characteristic. "minimum", "linear" or "both". '
f'Defaults to "{DEFAULT_PHASE}"')
arg_parser.add_argument('--f-res', type=float, default=DEFAULT_F_RES,
help='Frequency resolution for impulse responses. If this is 20 then impulse response '
'frequency domain will be sampled every 20 Hz. Filter length for '
f'impulse responses will be fs/f_res. Defaults to {DEFAULT_F_RES}.')
arg_parser.add_argument('--bass-boost', type=str, default=argparse.SUPPRESS,
help='Bass boost shelf. Sub-bass frequencies will be boosted by this amount. Can be '
'either a single value for a gain in dB or a comma separated list of three values '
'for parameters of a low shelf filter, where the first is gain in dB, second is '
'center frequency (Fc) in Hz and the last is quality (Q). When only a single '
'value (gain) is given, default values for Fc and Q are used which are '
f'{DEFAULT_BASS_BOOST_FC} Hz and {DEFAULT_BASS_BOOST_Q}, '
'respectively. For example "--bass-boost=6" or "--bass-boost=9.5,150,0.69".')
arg_parser.add_argument('--treble-boost', type=str, default=argparse.SUPPRESS,
help='Treble boost shelf. > 10 kHz frequencies will be boosted by this amount. Can be '
'either a single value for a gain in dB or a comma separated list of three values '
'for parameters of a high shelf filter, where the first is gain in dB, second is '
'center frequency (Fc) in Hz and the last is quality (Q). When only a single '
'value (gain) is given, default values for Fc and Q are used which are '
f'{DEFAULT_TREBLE_BOOST_FC} Hz and {DEFAULT_TREBLE_BOOST_Q}, '
'respectively. For example "--treble-boost=3" or "--treble-boost=-4,12000,0.69".')
arg_parser.add_argument('--tilt', type=float, default=argparse.SUPPRESS,
help='Target tilt in dB/octave. Positive value (upwards slope) will result in brighter '
'frequency response and negative value (downwards slope) will result in darker '
'frequency response. 1 dB/octave will produce nearly 10 dB difference in '
'desired value between 20 Hz and 20 kHz. Tilt is applied with bass boost and both '
'will affect the bass gain.')
arg_parser.add_argument('--sound-signature', type=str,
help='File path to a sound signature CSV file. Sound signature is added to the '
'target curve. Error data will be used as the sound signature target if '
'the CSV file contains an error column and otherwise the raw column will be used. '
'This means there are two different options for using sound signature: 1st is '
'pointing it to a result CSV file of a previous run and the 2nd is to create a '
'CSV file with just frequency and raw columns by hand (or other means). The Sound '
'signature graph will be interpolated so any number of point at any frequencies '
'will do, making it easy to create simple signatures with as little as two or '
'three points.')
arg_parser.add_argument('--sound-signature-smoothing-window-size', type=float,
help=f'Smoothing window size for sound signature. '
f'Defaults to {DEFAULT_SOUND_SIGNATURE_SMOOTHING_WINDOW_SIZE}.')
arg_parser.add_argument('--max-gain', type=float, default=DEFAULT_MAX_GAIN,
help='Maximum positive gain in equalization. Higher max gain allows to equalize deeper '
'dips in frequency response but will limit output volume if no analog gain is '
'available because positive gain requires negative digital preamp equal to '
f'maximum positive gain. Defaults to {DEFAULT_MAX_GAIN}.')
arg_parser.add_argument('--max-slope', type=float, default=DEFAULT_MAX_SLOPE,
help='Maximum slope steepness for equalizer frequency response in dB/oct. Defaults to '
f'{DEFAULT_MAX_SLOPE}')
arg_parser.add_argument('--window-size', type=float, default=DEFAULT_SMOOTHING_WINDOW_SIZE,
help='Smoothing window size in octaves.')
arg_parser.add_argument('--treble-window-size', type=float, default=DEFAULT_TREBLE_SMOOTHING_WINDOW_SIZE,
help='Smoothing window size in octaves in the treble region.')
arg_parser.add_argument('--treble-f-lower', type=float, default=DEFAULT_TREBLE_F_LOWER,
help='Lower bound for transition region between normal and treble frequencies. Treble '
'frequencies can have different max gain and gain K. Defaults to '
f'{DEFAULT_TREBLE_F_LOWER}.')
arg_parser.add_argument('--treble-f-upper', type=float, default=DEFAULT_TREBLE_F_UPPER,
help='Upper bound for transition region between normal and treble frequencies. Treble '
'frequencies can have different max gain and gain K. Defaults to '
f'{DEFAULT_TREBLE_F_UPPER}.')
arg_parser.add_argument('--treble-gain-k', type=float, default=DEFAULT_TREBLE_GAIN_K,
help='Coefficient for treble gain, affects both positive and negative gain. Useful for '
'disabling or reducing equalization power in treble region. Defaults to '
f'{DEFAULT_TREBLE_GAIN_K}.')
arg_parser.add_argument('--thread-count', default=1,
help='Amount of threads to use for processing results. If set to "max" all the threads '
'available will be used. Using more threads result in higher memory usage. '
'Defaults to 1.')
arg_parser.add_argument('--preamp', type=float, default=DEFAULT_PREAMP,
help='Extra pre-amplification to be applied to equalizer settings in dB')
args = vars(arg_parser.parse_args())
# Replace hyphens with underscores to be compatible with the batch_processing method signature
args = {key.replace('-', '_'): val for key, val in args.items()}
if 'equalize' in args and args['equalize']:
warnings.warn(
'"equalize" parameter is no longer supported. The equalization target is created automatically every time.')
del args['equalize']
if 'bass_boost' in args:
bass_boost = args['bass_boost'].split(',')
if len(bass_boost) == 1:
args['bass_boost_gain'] = float(bass_boost[0])
args['bass_boost_fc'] = DEFAULT_BASS_BOOST_FC
args['bass_boost_q'] = DEFAULT_BASS_BOOST_Q
elif len(bass_boost) == 3:
args['bass_boost_gain'] = float(bass_boost[0])
args['bass_boost_fc'] = float(bass_boost[1])
args['bass_boost_q'] = float(bass_boost[2])
else:
raise ValueError('"--bass-boost" must have one value or three values separated by commas!')
del args['bass_boost']
if 'treble_boost' in args:
treble_boost = args['treble_boost'].split(',')
if len(treble_boost) == 1:
args['treble_boost_gain'] = float(treble_boost[0])
args['treble_boost_fc'] = DEFAULT_TREBLE_BOOST_FC
args['treble_boost_q'] = DEFAULT_TREBLE_BOOST_Q
elif len(treble_boost) == 3:
args['treble_boost_gain'] = float(treble_boost[0])
args['treble_boost_fc'] = float(treble_boost[1])
args['treble_boost_q'] = float(treble_boost[2])
else:
raise ValueError('"--treble-boost" must have one value or three values separated by commas!')
del args['treble_boost']
if 'parametric_eq_config' in args:
if not os.path.isfile(args['parametric_eq_config']):
# Named configurations, split by commas
args['parametric_eq_config'] = args['parametric_eq_config'].split(',')
if 'fs' in args and args['fs'] is not None:
args['fs'] = [int(x) for x in args['fs'].split(',')]
if thread_count := args.get('thread_count'):
if thread_count == 'max':
args['thread_count'] = multiprocessing.cpu_count()
else:
try:
thread_count = int(thread_count)
except ValueError:
raise ValueError('"--thread_count" must have a value greater than 0 or equal to "max"!')
if thread_count <= 0:
raise ValueError('"--thread_count" must have a value greater than 0 or equal to "max"!')
args['thread_count'] = thread_count
return args
if __name__ == '__main__':
batch_processing(**cli_args())