summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2018-10-07 23:40:35 +0200
committerMinijackson <minijackson@riseup.net>2018-10-07 23:40:35 +0200
commita6dfec305a53c6a7618b98f43a6b4b89cfb76102 (patch)
treef1ee180a53c47e31eaeb19bc3d749965023b6448
parent600df820b205a0ab81294e795ae691158553ffb9 (diff)
downloadset_eq-a6dfec305a53c6a7618b98f43a6b4b89cfb76102.tar.gz
set_eq-a6dfec305a53c6a7618b98f43a6b4b89cfb76102.zip
Add support for PulseEffects
-rw-r--r--Cargo.lock24
-rw-r--r--Cargo.toml2
-rw-r--r--res/default-pa-effects-preset.json898
-rw-r--r--src/cli.rs113
-rw-r--r--src/main.rs49
-rw-r--r--src/pa_effects.rs125
-rw-r--r--src/pa_eq.rs110
-rw-r--r--src/parsing/equalizer_apo.lalrpop8
-rw-r--r--src/parsing/equalizer_apo.rs9
-rw-r--r--src/utils.rs96
10 files changed, 1282 insertions, 152 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3e0b2fa..a2d4b2b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -280,6 +280,11 @@ dependencies = [
280] 280]
281 281
282[[package]] 282[[package]]
283name = "itoa"
284version = "0.4.3"
285source = "registry+https://github.com/rust-lang/crates.io-index"
286
287[[package]]
283name = "kernel32-sys" 288name = "kernel32-sys"
284version = "0.2.2" 289version = "0.2.2"
285source = "registry+https://github.com/rust-lang/crates.io-index" 290source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -553,6 +558,11 @@ version = "0.1.9"
553source = "registry+https://github.com/rust-lang/crates.io-index" 558source = "registry+https://github.com/rust-lang/crates.io-index"
554 559
555[[package]] 560[[package]]
561name = "ryu"
562version = "0.2.6"
563source = "registry+https://github.com/rust-lang/crates.io-index"
564
565[[package]]
556name = "serde" 566name = "serde"
557version = "1.0.75" 567version = "1.0.75"
558source = "registry+https://github.com/rust-lang/crates.io-index" 568source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -568,6 +578,16 @@ dependencies = [
568] 578]
569 579
570[[package]] 580[[package]]
581name = "serde_json"
582version = "1.0.32"
583source = "registry+https://github.com/rust-lang/crates.io-index"
584dependencies = [
585 "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
586 "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
587 "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
588]
589
590[[package]]
571name = "set_eq" 591name = "set_eq"
572version = "0.1.0" 592version = "0.1.0"
573dependencies = [ 593dependencies = [
@@ -580,6 +600,7 @@ dependencies = [
580 "lalrpop-util 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", 600 "lalrpop-util 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
581 "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 601 "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
582 "regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 602 "regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
603 "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
583 "structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", 604 "structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
584] 605]
585 606
@@ -853,6 +874,7 @@ dependencies = [
853"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" 874"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
854"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" 875"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
855"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" 876"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450"
877"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
856"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 878"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
857"checksum lalrpop 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba451f7bd819b7afc99d4cf4bdcd5a4861e64955ba9680ac70df3a50625ad6cf" 879"checksum lalrpop 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba451f7bd819b7afc99d4cf4bdcd5a4861e64955ba9680ac70df3a50625ad6cf"
858"checksum lalrpop-snap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60013fd6be14317d43f47658b1440956a9ca48a9ed0257e0e0a59aac13e43a1f" 880"checksum lalrpop-snap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60013fd6be14317d43f47658b1440956a9ca48a9ed0257e0e0a59aac13e43a1f"
@@ -885,8 +907,10 @@ dependencies = [
885"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" 907"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
886"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" 908"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d"
887"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" 909"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
910"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7"
888"checksum serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "22d340507cea0b7e6632900a176101fea959c7065d93ba555072da90aaaafc87" 911"checksum serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "22d340507cea0b7e6632900a176101fea959c7065d93ba555072da90aaaafc87"
889"checksum serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "234fc8b737737b148ccd625175fc6390f5e4dacfdaa543cb93a3430d984a9119" 912"checksum serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "234fc8b737737b148ccd625175fc6390f5e4dacfdaa543cb93a3430d984a9119"
913"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce"
890"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" 914"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0"
891"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" 915"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
892"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" 916"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423"
diff --git a/Cargo.toml b/Cargo.toml
index e6093d1..840e065 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,3 +25,5 @@ failure = "0.1.2"
25 25
26lalrpop-util = "0.15.2" 26lalrpop-util = "0.15.2"
27regex = "1.0.4" 27regex = "1.0.4"
28
29serde_json = "1.0.32"
diff --git a/res/default-pa-effects-preset.json b/res/default-pa-effects-preset.json
new file mode 100644
index 0000000..0d24279
--- /dev/null
+++ b/res/default-pa-effects-preset.json
@@ -0,0 +1,898 @@
1{
2 "spectrum": {
3 "show": "true",
4 "n-points": "150",
5 "height": "100",
6 "use-custom-color": "false",
7 "color": [
8 "1",
9 "1",
10 "1",
11 "1"
12 ]
13 },
14 "input": {
15 "plugins_order": [
16 "gate",
17 "multiband_gate",
18 "webrtc",
19 "limiter",
20 "compressor",
21 "multiband_compressor",
22 "filter",
23 "equalizer",
24 "deesser",
25 "reverb",
26 "pitch"
27 ],
28 "compressor": {
29 "state": "false",
30 "detection": "RMS",
31 "stereo-link": "Average",
32 "mix": "0",
33 "attack": "20",
34 "release": "250",
35 "threshold": "-18",
36 "ratio": "2",
37 "knee": "9",
38 "makeup": "0"
39 },
40 "deesser": {
41 "state": "false",
42 "detection": "RMS",
43 "mode": "Wide",
44 "threshold": "-18",
45 "ratio": "3",
46 "laxity": "15",
47 "makeup": "0",
48 "f1-freq": "6000",
49 "f2-freq": "4500",
50 "f1-level": "0",
51 "f2-level": "12",
52 "f2-q": "1",
53 "sc-listen": "false"
54 },
55 "equalizer": {
56 "state": "false",
57 "num-bands": "30",
58 "input-gain": "0",
59 "output-gain": "0",
60 "band0": {
61 "gain": "0",
62 "frequency": "22.59",
63 "width": "5.1799999999999997",
64 "type": "peak"
65 },
66 "band1": {
67 "gain": "0",
68 "frequency": "28.440000000000001",
69 "width": "6.5199999999999996",
70 "type": "peak"
71 },
72 "band2": {
73 "gain": "0",
74 "frequency": "35.799999999999997",
75 "width": "8.2100000000000009",
76 "type": "peak"
77 },
78 "band3": {
79 "gain": "0",
80 "frequency": "45.07",
81 "width": "10.33",
82 "type": "peak"
83 },
84 "band4": {
85 "gain": "0",
86 "frequency": "56.740000000000002",
87 "width": "13",
88 "type": "peak"
89 },
90 "band5": {
91 "gain": "0",
92 "frequency": "71.430000000000007",
93 "width": "16.379999999999999",
94 "type": "peak"
95 },
96 "band6": {
97 "gain": "0",
98 "frequency": "89.930000000000007",
99 "width": "20.620000000000001",
100 "type": "peak"
101 },
102 "band7": {
103 "gain": "0",
104 "frequency": "113.20999999999999",
105 "width": "25.949999999999999",
106 "type": "peak"
107 },
108 "band8": {
109 "gain": "0",
110 "frequency": "142.53",
111 "width": "32.670000000000002",
112 "type": "peak"
113 },
114 "band9": {
115 "gain": "0",
116 "frequency": "179.43000000000001",
117 "width": "41.130000000000003",
118 "type": "peak"
119 },
120 "band10": {
121 "gain": "0",
122 "frequency": "225.88999999999999",
123 "width": "51.789999999999999",
124 "type": "peak"
125 },
126 "band11": {
127 "gain": "0",
128 "frequency": "284.38",
129 "width": "65.189999999999998",
130 "type": "peak"
131 },
132 "band12": {
133 "gain": "0",
134 "frequency": "358.01999999999998",
135 "width": "82.069999999999993",
136 "type": "peak"
137 },
138 "band13": {
139 "gain": "0",
140 "frequency": "450.72000000000003",
141 "width": "103.33",
142 "type": "peak"
143 },
144 "band14": {
145 "gain": "0",
146 "frequency": "567.41999999999996",
147 "width": "130.08000000000001",
148 "type": "peak"
149 },
150 "band15": {
151 "gain": "0",
152 "frequency": "714.34000000000003",
153 "width": "163.75999999999999",
154 "type": "peak"
155 },
156 "band16": {
157 "gain": "0",
158 "frequency": "899.28999999999996",
159 "width": "206.16",
160 "type": "peak"
161 },
162 "band17": {
163 "gain": "0",
164 "frequency": "1132.1500000000001",
165 "width": "259.54000000000002",
166 "type": "peak"
167 },
168 "band18": {
169 "gain": "0",
170 "frequency": "1425.29",
171 "width": "326.74000000000001",
172 "type": "peak"
173 },
174 "band19": {
175 "gain": "0",
176 "frequency": "1794.3299999999999",
177 "width": "411.33999999999997",
178 "type": "peak"
179 },
180 "band20": {
181 "gain": "0",
182 "frequency": "2258.9299999999998",
183 "width": "517.85000000000002",
184 "type": "peak"
185 },
186 "band21": {
187 "gain": "0",
188 "frequency": "2843.8200000000002",
189 "width": "651.94000000000005",
190 "type": "peak"
191 },
192 "band22": {
193 "gain": "0",
194 "frequency": "3580.1599999999999",
195 "width": "820.74000000000001",
196 "type": "peak"
197 },
198 "band23": {
199 "gain": "0",
200 "frequency": "4507.1499999999996",
201 "width": "1033.25",
202 "type": "peak"
203 },
204 "band24": {
205 "gain": "0",
206 "frequency": "5674.1599999999999",
207 "width": "1300.78",
208 "type": "peak"
209 },
210 "band25": {
211 "gain": "0",
212 "frequency": "7143.3500000000004",
213 "width": "1637.5899999999999",
214 "type": "peak"
215 },
216 "band26": {
217 "gain": "0",
218 "frequency": "8992.9400000000005",
219 "width": "2061.5999999999999",
220 "type": "peak"
221 },
222 "band27": {
223 "gain": "0",
224 "frequency": "11321.450000000001",
225 "width": "2595.4000000000001",
226 "type": "peak"
227 },
228 "band28": {
229 "gain": "0",
230 "frequency": "14252.860000000001",
231 "width": "3267.4200000000001",
232 "type": "peak"
233 },
234 "band29": {
235 "gain": "0",
236 "frequency": "17943.279999999999",
237 "width": "4113.4399999999996",
238 "type": "peak"
239 }
240 },
241 "filter": {
242 "state": "false",
243 "input-gain": "0",
244 "output-gain": "0",
245 "frequency": "2000",
246 "resonance": "-3",
247 "mode": "12dB\/oct Lowpass",
248 "inertia": "20"
249 },
250 "gate": {
251 "state": "false",
252 "detection": "RMS",
253 "stereo-link": "Average",
254 "range": "-24",
255 "attack": "20",
256 "release": "250",
257 "threshold": "-18",
258 "ratio": "2",
259 "knee": "9",
260 "makeup": "0"
261 },
262 "limiter": {
263 "state": "false",
264 "input-gain": "0",
265 "limit": "0",
266 "lookahead": "5",
267 "release": "50",
268 "asc": "false",
269 "asc-level": "0.5",
270 "oversampling": "1"
271 },
272 "pitch": {
273 "state": "false",
274 "cents": "0",
275 "semitones": "0",
276 "octaves": "0",
277 "crispness": "3",
278 "formant-preserving": "false",
279 "faster": "false"
280 },
281 "reverb": {
282 "state": "false",
283 "input-gain": "0",
284 "output-gain": "0",
285 "room-size": "Large",
286 "decay-time": "1.5",
287 "hf-damp": "5000",
288 "diffusion": "0.5",
289 "amount": "-12",
290 "dry": "0",
291 "predelay": "0",
292 "bass-cut": "300",
293 "treble-cut": "5000"
294 },
295 "webrtc": {
296 "state": "false",
297 "high-pass-filter": "true",
298 "echo-cancel": "true",
299 "echo-suppression-level": "moderate",
300 "noise-suppression": "true",
301 "noise-suppression-level": "moderate",
302 "gain-control": "true",
303 "extended-filter": "true",
304 "delay-agnostic": "false",
305 "target-level-dbfs": "3",
306 "compression-gain-db": "9",
307 "limiter": "true",
308 "gain-control-mode": "adaptive-digital",
309 "voice-detection": "false",
310 "voice-detection-frame-size-ms": "10",
311 "voice-detection-likelihood": "low"
312 },
313 "multiband_compressor": {
314 "state": "false",
315 "input-gain": "0",
316 "output-gain": "0",
317 "freq0": "120",
318 "freq1": "1000",
319 "freq2": "6000",
320 "mode": "LR8",
321 "subband": {
322 "threshold": "-12",
323 "ratio": "2",
324 "attack": "150",
325 "release": "300",
326 "makeup": "0",
327 "knee": "9",
328 "detection": "RMS",
329 "bypass": "false",
330 "solo": "false"
331 },
332 "lowband": {
333 "threshold": "-12",
334 "ratio": "2",
335 "attack": "150",
336 "release": "300",
337 "makeup": "0",
338 "knee": "9",
339 "detection": "RMS",
340 "bypass": "false",
341 "solo": "false"
342 },
343 "midband": {
344 "threshold": "-12",
345 "ratio": "2",
346 "attack": "150",
347 "release": "300",
348 "makeup": "0",
349 "knee": "9",
350 "detection": "RMS",
351 "bypass": "false",
352 "solo": "false"
353 },
354 "highband": {
355 "threshold": "-12",
356 "ratio": "2",
357 "attack": "150",
358 "release": "300",
359 "makeup": "0",
360 "knee": "9",
361 "detection": "RMS",
362 "bypass": "false",
363 "solo": "false"
364 }
365 },
366 "multiband_gate": {
367 "state": "false",
368 "input-gain": "0",
369 "output-gain": "0",
370 "freq0": "120",
371 "freq1": "1000",
372 "freq2": "6000",
373 "mode": "LR8",
374 "subband": {
375 "reduction": "-24",
376 "threshold": "-12",
377 "ratio": "2",
378 "attack": "150",
379 "release": "300",
380 "makeup": "0",
381 "knee": "9",
382 "detection": "RMS",
383 "bypass": "false",
384 "solo": "false"
385 },
386 "lowband": {
387 "reduction": "-24",
388 "threshold": "-12",
389 "ratio": "2",
390 "attack": "150",
391 "release": "300",
392 "makeup": "0",
393 "knee": "9",
394 "detection": "RMS",
395 "bypass": "false",
396 "solo": "false"
397 },
398 "midband": {
399 "reduction": "-24",
400 "threshold": "-12",
401 "ratio": "2",
402 "attack": "150",
403 "release": "300",
404 "makeup": "0",
405 "knee": "9",
406 "detection": "RMS",
407 "bypass": "false",
408 "solo": "false"
409 },
410 "highband": {
411 "reduction": "-24",
412 "threshold": "-12",
413 "ratio": "2",
414 "attack": "150",
415 "release": "300",
416 "makeup": "0",
417 "knee": "9",
418 "detection": "RMS",
419 "bypass": "false",
420 "solo": "false"
421 }
422 }
423 },
424 "output": {
425 "plugins_order": [
426 "limiter",
427 "autogain",
428 "gate",
429 "multiband_gate",
430 "compressor",
431 "multiband_compressor",
432 "convolver",
433 "bass_enhancer",
434 "exciter",
435 "crystalizer",
436 "stereo_tools",
437 "reverb",
438 "equalizer",
439 "deesser",
440 "crossfeed",
441 "loudness",
442 "maximizer",
443 "filter"
444 ],
445 "bass_enhancer": {
446 "state": "false",
447 "input-gain": "0",
448 "output-gain": "0",
449 "amount": "0",
450 "harmonics": "8.5",
451 "scope": "100",
452 "floor": "20",
453 "blend": "0",
454 "floor-active": "false",
455 "listen": "false"
456 },
457 "compressor": {
458 "state": "false",
459 "detection": "RMS",
460 "stereo-link": "Average",
461 "mix": "0",
462 "attack": "20",
463 "release": "250",
464 "threshold": "-18",
465 "ratio": "2",
466 "knee": "9",
467 "makeup": "0"
468 },
469 "crossfeed": {
470 "state": "false",
471 "fcut": "700",
472 "feed": "4.5"
473 },
474 "deesser": {
475 "state": "false",
476 "detection": "RMS",
477 "mode": "Wide",
478 "threshold": "-18",
479 "ratio": "3",
480 "laxity": "15",
481 "makeup": "0",
482 "f1-freq": "6000",
483 "f2-freq": "4500",
484 "f1-level": "0",
485 "f2-level": "12",
486 "f2-q": "1",
487 "sc-listen": "false"
488 },
489 "equalizer": {
490 "state": "false",
491 "num-bands": "30",
492 "input-gain": "0",
493 "output-gain": "0",
494 "band0": {
495 "gain": "0",
496 "frequency": "22.59",
497 "width": "5.1799999999999997",
498 "type": "peak"
499 },
500 "band1": {
501 "gain": "0",
502 "frequency": "28.440000000000001",
503 "width": "6.5199999999999996",
504 "type": "peak"
505 },
506 "band2": {
507 "gain": "0",
508 "frequency": "35.799999999999997",
509 "width": "8.2100000000000009",
510 "type": "peak"
511 },
512 "band3": {
513 "gain": "0",
514 "frequency": "45.07",
515 "width": "10.33",
516 "type": "peak"
517 },
518 "band4": {
519 "gain": "0",
520 "frequency": "56.740000000000002",
521 "width": "13",
522 "type": "peak"
523 },
524 "band5": {
525 "gain": "0",
526 "frequency": "71.430000000000007",
527 "width": "16.379999999999999",
528 "type": "peak"
529 },
530 "band6": {
531 "gain": "0",
532 "frequency": "89.930000000000007",
533 "width": "20.620000000000001",
534 "type": "peak"
535 },
536 "band7": {
537 "gain": "0",
538 "frequency": "113.20999999999999",
539 "width": "25.949999999999999",
540 "type": "peak"
541 },
542 "band8": {
543 "gain": "0",
544 "frequency": "142.53",
545 "width": "32.670000000000002",
546 "type": "peak"
547 },
548 "band9": {
549 "gain": "0",
550 "frequency": "179.43000000000001",
551 "width": "41.130000000000003",
552 "type": "peak"
553 },
554 "band10": {
555 "gain": "0",
556 "frequency": "225.88999999999999",
557 "width": "51.789999999999999",
558 "type": "peak"
559 },
560 "band11": {
561 "gain": "0",
562 "frequency": "284.38",
563 "width": "65.189999999999998",
564 "type": "peak"
565 },
566 "band12": {
567 "gain": "0",
568 "frequency": "358.01999999999998",
569 "width": "82.069999999999993",
570 "type": "peak"
571 },
572 "band13": {
573 "gain": "0",
574 "frequency": "450.72000000000003",
575 "width": "103.33",
576 "type": "peak"
577 },
578 "band14": {
579 "gain": "0",
580 "frequency": "567.41999999999996",
581 "width": "130.08000000000001",
582 "type": "peak"
583 },
584 "band15": {
585 "gain": "0",
586 "frequency": "714.34000000000003",
587 "width": "163.75999999999999",
588 "type": "peak"
589 },
590 "band16": {
591 "gain": "0",
592 "frequency": "899.28999999999996",
593 "width": "206.16",
594 "type": "peak"
595 },
596 "band17": {
597 "gain": "0",
598 "frequency": "1132.1500000000001",
599 "width": "259.54000000000002",
600 "type": "peak"
601 },
602 "band18": {
603 "gain": "0",
604 "frequency": "1425.29",
605 "width": "326.74000000000001",
606 "type": "peak"
607 },
608 "band19": {
609 "gain": "0",
610 "frequency": "1794.3299999999999",
611 "width": "411.33999999999997",
612 "type": "peak"
613 },
614 "band20": {
615 "gain": "0",
616 "frequency": "2258.9299999999998",
617 "width": "517.85000000000002",
618 "type": "peak"
619 },
620 "band21": {
621 "gain": "0",
622 "frequency": "2843.8200000000002",
623 "width": "651.94000000000005",
624 "type": "peak"
625 },
626 "band22": {
627 "gain": "0",
628 "frequency": "3580.1599999999999",
629 "width": "820.74000000000001",
630 "type": "peak"
631 },
632 "band23": {
633 "gain": "0",
634 "frequency": "4507.1499999999996",
635 "width": "1033.25",
636 "type": "peak"
637 },
638 "band24": {
639 "gain": "0",
640 "frequency": "5674.1599999999999",
641 "width": "1300.78",
642 "type": "peak"
643 },
644 "band25": {
645 "gain": "0",
646 "frequency": "7143.3500000000004",
647 "width": "1637.5899999999999",
648 "type": "peak"
649 },
650 "band26": {
651 "gain": "0",
652 "frequency": "8992.9400000000005",
653 "width": "2061.5999999999999",
654 "type": "peak"
655 },
656 "band27": {
657 "gain": "0",
658 "frequency": "11321.450000000001",
659 "width": "2595.4000000000001",
660 "type": "peak"
661 },
662 "band28": {
663 "gain": "0",
664 "frequency": "14252.860000000001",
665 "width": "3267.4200000000001",
666 "type": "peak"
667 },
668 "band29": {
669 "gain": "0",
670 "frequency": "17943.279999999999",
671 "width": "4113.4399999999996",
672 "type": "peak"
673 }
674 },
675 "exciter": {
676 "state": "false",
677 "input-gain": "0",
678 "output-gain": "0",
679 "amount": "0",
680 "harmonics": "8.5",
681 "scope": "7500",
682 "ceil": "16000",
683 "blend": "0",
684 "ceil-active": "false",
685 "listen": "false"
686 },
687 "filter": {
688 "state": "false",
689 "input-gain": "0",
690 "output-gain": "0",
691 "frequency": "2000",
692 "resonance": "-3",
693 "mode": "12dB\/oct Lowpass",
694 "inertia": "20"
695 },
696 "gate": {
697 "state": "false",
698 "detection": "RMS",
699 "stereo-link": "Average",
700 "range": "-24",
701 "attack": "20",
702 "release": "250",
703 "threshold": "-18",
704 "ratio": "2",
705 "knee": "9",
706 "makeup": "0"
707 },
708 "limiter": {
709 "state": "false",
710 "input-gain": "0",
711 "limit": "0",
712 "lookahead": "5",
713 "release": "50",
714 "asc": "false",
715 "asc-level": "0.5",
716 "oversampling": "1"
717 },
718 "maximizer": {
719 "state": "false",
720 "release": "3.1600000000000001",
721 "ceiling": "0",
722 "threshold": "0"
723 },
724 "reverb": {
725 "state": "false",
726 "input-gain": "0",
727 "output-gain": "0",
728 "room-size": "Large",
729 "decay-time": "1.5",
730 "hf-damp": "5000",
731 "diffusion": "0.5",
732 "amount": "-12",
733 "dry": "0",
734 "predelay": "0",
735 "bass-cut": "300",
736 "treble-cut": "5000"
737 },
738 "multiband_compressor": {
739 "state": "false",
740 "input-gain": "0",
741 "output-gain": "0",
742 "freq0": "120",
743 "freq1": "1000",
744 "freq2": "6000",
745 "mode": "LR8",
746 "subband": {
747 "threshold": "-12",
748 "ratio": "2",
749 "attack": "150",
750 "release": "300",
751 "makeup": "0",
752 "knee": "9",
753 "detection": "RMS",
754 "bypass": "false",
755 "solo": "false"
756 },
757 "lowband": {
758 "threshold": "-12",
759 "ratio": "2",
760 "attack": "150",
761 "release": "300",
762 "makeup": "0",
763 "knee": "9",
764 "detection": "RMS",
765 "bypass": "false",
766 "solo": "false"
767 },
768 "midband": {
769 "threshold": "-12",
770 "ratio": "2",
771 "attack": "150",
772 "release": "300",
773 "makeup": "0",
774 "knee": "9",
775 "detection": "RMS",
776 "bypass": "false",
777 "solo": "false"
778 },
779 "highband": {
780 "threshold": "-12",
781 "ratio": "2",
782 "attack": "150",
783 "release": "300",
784 "makeup": "0",
785 "knee": "9",
786 "detection": "RMS",
787 "bypass": "false",
788 "solo": "false"
789 }
790 },
791 "loudness": {
792 "state": "false",
793 "loudness": "-3.1000000000000001",
794 "output": "-6",
795 "link": "-9.0999999999999996"
796 },
797 "multiband_gate": {
798 "state": "false",
799 "input-gain": "0",
800 "output-gain": "0",
801 "freq0": "120",
802 "freq1": "1000",
803 "freq2": "6000",
804 "mode": "LR8",
805 "subband": {
806 "reduction": "-24",
807 "threshold": "-12",
808 "ratio": "2",
809 "attack": "150",
810 "release": "300",
811 "makeup": "0",
812 "knee": "9",
813 "detection": "RMS",
814 "bypass": "false",
815 "solo": "false"
816 },
817 "lowband": {
818 "reduction": "-24",
819 "threshold": "-12",
820 "ratio": "2",
821 "attack": "150",
822 "release": "300",
823 "makeup": "0",
824 "knee": "9",
825 "detection": "RMS",
826 "bypass": "false",
827 "solo": "false"
828 },
829 "midband": {
830 "reduction": "-24",
831 "threshold": "-12",
832 "ratio": "2",
833 "attack": "150",
834 "release": "300",
835 "makeup": "0",
836 "knee": "9",
837 "detection": "RMS",
838 "bypass": "false",
839 "solo": "false"
840 },
841 "highband": {
842 "reduction": "-24",
843 "threshold": "-12",
844 "ratio": "2",
845 "attack": "150",
846 "release": "300",
847 "makeup": "0",
848 "knee": "9",
849 "detection": "RMS",
850 "bypass": "false",
851 "solo": "false"
852 }
853 },
854 "stereo_tools": {
855 "state": "false",
856 "input-gain": "0",
857 "output-gain": "0",
858 "balance-in": "0",
859 "balance-out": "0",
860 "softclip": "false",
861 "mutel": "false",
862 "muter": "false",
863 "phasel": "false",
864 "phaser": "false",
865 "mode": "LR > LR (Stereo Default)",
866 "side-level": "0",
867 "side-balance": "0",
868 "middle-level": "0",
869 "middle-panorama": "0",
870 "stereo-base": "0",
871 "delay": "0",
872 "sc-level": "1",
873 "stereo-phase": "0"
874 },
875 "convolver": {
876 "state": "false",
877 "input-gain": "0",
878 "output-gain": "0",
879 "kernel-path": "",
880 "ir-width": "100"
881 },
882 "crystalizer": {
883 "state": "false",
884 "input-gain": "0",
885 "output-gain": "0",
886 "intensity": "1"
887 },
888 "autogain": {
889 "state": "false",
890 "input-gain": "0",
891 "output-gain": "0",
892 "target": "-23",
893 "weight-m": "1",
894 "weight-s": "1",
895 "weight-i": "1"
896 }
897 }
898}
diff --git a/src/cli.rs b/src/cli.rs
index f24b222..4a499b5 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -21,31 +21,16 @@ pub struct Cli {
21 21
22#[derive(StructOpt, Debug)] 22#[derive(StructOpt, Debug)]
23pub enum Command { 23pub enum Command {
24 #[structopt(name = "load",)] 24 #[structopt(name = "pa-eq")]
25 /// Load and switch to a given equalizer configuration 25 /// PulseAudio equalizer related commands
26 Load(LoadCli), 26 ///
27 #[structopt(name = "reset")] 27 /// Warning: the PulseAudio equalizer has been deprecated for a while,
28 /// Switch to a neutral equalizer 28 /// and is known to sometimes cause crashes, latency or audible
29 Reset(ResetCli), 29 /// artifacts
30} 30 PaEq(pa_eq::Command),
31 31 #[structopt(name = "pa-effects")]
32#[derive(StructOpt, Debug)] 32 /// PulseEffects equalizer related commands
33pub struct LoadCli { 33 PaEffects(pa_effects::Command),
34 #[structopt(default_value = "-")]
35 /// The file from which to load the equalizer configuration
36 ///
37 /// If "-" is given, read the configuration from the command-line.
38 pub file: String,
39 #[structopt(
40 short = "f",
41 raw(
42 possible_values = "&EqualizerConfFormat::variants()",
43 case_insensitive = "true"
44 ),
45 default_value = "EqualizerAPO"
46 )]
47 /// The file format of the equalizer configuration
48 pub format: EqualizerConfFormat,
49} 34}
50 35
51arg_enum! { 36arg_enum! {
@@ -55,5 +40,79 @@ arg_enum! {
55 } 40 }
56} 41}
57 42
58#[derive(StructOpt, Debug)] 43pub mod pa_eq {
59pub struct ResetCli {} 44 use super::EqualizerConfFormat;
45
46 #[derive(StructOpt, Debug)]
47 pub enum Command {
48 #[structopt(name = "load",)]
49 /// Load and switch to a given equalizer configuration
50 Load(LoadCli),
51 #[structopt(name = "reset")]
52 /// Switch to a neutral equalizer
53 Reset(ResetCli),
54 }
55
56 #[derive(StructOpt, Debug)]
57 pub struct LoadCli {
58 #[structopt(default_value = "-")]
59 /// The file from which to load the equalizer configuration
60 ///
61 /// If "-" is given, read the configuration from the command-line.
62 pub file: String,
63 #[structopt(
64 short = "f",
65 raw(
66 possible_values = "&EqualizerConfFormat::variants()",
67 case_insensitive = "true"
68 ),
69 default_value = "EqualizerAPO"
70 )]
71 /// The file format of the equalizer configuration
72 pub format: EqualizerConfFormat,
73 }
74
75 #[derive(StructOpt, Debug)]
76 pub struct ResetCli {}
77
78}
79
80pub mod pa_effects {
81 use super::EqualizerConfFormat;
82
83 #[derive(StructOpt, Debug)]
84 pub enum Command {
85 #[structopt(name = "export-preset",)]
86 /// Export a PulseEffects preset
87 ExportPreset(ExportPresetCli),
88 }
89
90 #[derive(StructOpt, Debug)]
91 pub struct ExportPresetCli {
92 #[structopt(default_value = "-")]
93 /// The file from which to load the equalizer configuration
94 ///
95 /// If "-" is given, read the configuration from the command-line.
96 pub file: String,
97 #[structopt(
98 short = "f",
99 raw(
100 possible_values = "&EqualizerConfFormat::variants()",
101 case_insensitive = "true"
102 ),
103 default_value = "EqualizerAPO"
104 )]
105 /// The file format of the equalizer configuration
106 pub format: EqualizerConfFormat,
107 #[structopt(short = "p")]
108 /// Use a given file as a base for PulseEffects preset instead of the
109 /// default one.
110 ///
111 /// If "-" is given, read the base preset from the command-line.
112 pub base_preset: Option<String>,
113 #[structopt(short = "o")]
114 /// Write the preset to the given file, instead of the standard output
115 pub output: Option<String>,
116 }
117
118}
diff --git a/src/main.rs b/src/main.rs
index 85d2443..a23034e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,21 +14,22 @@ extern crate structopt;
14 14
15extern crate lalrpop_util; 15extern crate lalrpop_util;
16 16
17#[macro_use]
18extern crate serde_json;
19
17mod cli; 20mod cli;
18mod dbus_api; 21mod dbus_api;
19mod parsing; 22mod parsing;
20mod utils; 23mod utils;
21 24
22use utils::*; 25mod pa_eq;
23use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer; 26mod pa_effects;
27
24use cli::*; 28use cli::*;
25 29
26use failure::Error; 30use failure::Error;
27use structopt::StructOpt; 31use structopt::StructOpt;
28 32
29use std::fs::File;
30use std::io;
31
32#[derive(Debug)] 33#[derive(Debug)]
33pub struct Filter { 34pub struct Filter {
34 preamp: f64, 35 preamp: f64,
@@ -74,41 +75,7 @@ fn start() -> Result<(), Error> {
74 use Command::*; 75 use Command::*;
75 76
76 match args.cmd { 77 match args.cmd {
77 Load(args) => load(args), 78 PaEq(args) => pa_eq::main(args),
78 Reset(args) => reset(args), 79 PaEffects(args) => pa_effects::main(args),
79 } 80 }
80} 81}
81
82fn reset(args: ResetCli) -> Result<(), Error> {
83 let conn = connect()?;
84 let conn_sink = get_equalized_sink(&conn)?;
85 let filter_rate = conn_sink.get_filter_sample_rate()?;
86 let filter = Filter {
87 preamp: 1f64,
88 frequencies: vec![],
89 coefficients: vec![],
90 }.pad(filter_rate);
91
92 send_filter(&conn_sink, filter)?;
93
94 Ok(())
95}
96
97fn load(args: LoadCli) -> Result<(), Error> {
98 let conn = connect()?;
99 let conn_sink = get_equalized_sink(&conn)?;
100
101 let filter = if args.file == "-" {
102 let stdin = io::stdin();
103 let mut handle = stdin.lock();
104 read_filter(&mut handle)?
105 } else {
106 let mut file = File::open(args.file)?;
107 read_filter(&mut file)?
108 };
109
110 let filter_rate = conn_sink.get_filter_sample_rate()?;
111 send_filter(&conn_sink, filter.pad(filter_rate))?;
112
113 Ok(())
114}
diff --git a/src/pa_effects.rs b/src/pa_effects.rs
new file mode 100644
index 0000000..9ed0a1f
--- /dev/null
+++ b/src/pa_effects.rs
@@ -0,0 +1,125 @@
1use cli::pa_effects::*;
2use utils::*;
3use Filter;
4
5use failure::Error;
6
7use serde_json;
8
9const DEFAULT_PRESET: &'static str = include_str!("../res/default-pa-effects-preset.json");
10
11pub fn main(cmd: Command) -> Result<(), Error> {
12 use cli::pa_effects::Command::*;
13
14 match cmd {
15 ExportPreset(args) => export_preset(args),
16 }
17}
18
19fn export_preset(args: ExportPresetCli) -> Result<(), Error> {
20 debug!("Parsing base preset");
21 let mut preset: serde_json::Value = match args.base_preset {
22 Some(file) => serde_json::from_str(&read_filearg_to_str(&file)?),
23 None => serde_json::from_str(&DEFAULT_PRESET),
24 }?;
25
26 let filter = read_filter_from_arg(&args.file)?;
27
28 preset["output"]["equalizer"] = filter_to_eq_preset(filter);
29
30 println!("{}", preset);
31 Ok(())
32}
33
34fn filter_to_eq_preset(mut filter: Filter) -> serde_json::Value {
35 if filter.frequencies.len() > 30 {
36 info!("More than 30 frequencies specified, taking the approximative approach");
37 filter = simplify_filter(filter);
38 }
39
40 let mut equalizer: serde_json::Value = json!({
41 "state": "true",
42 "num-bands": filter.frequencies.len(),
43 "input-gain": 0,
44 "output-gain": 0,
45 });
46
47 for (i, (frequency, coeff)) in filter
48 .frequencies
49 .into_iter()
50 .zip(filter.coefficients)
51 .enumerate()
52 {
53 equalizer[format!("band{}", i)] = json!({
54 "gain": coeff,
55 "frequency": frequency,
56 "type": "peak",
57 });
58 }
59
60 equalizer
61}
62
63fn simplify_filter(filter: Filter) -> Filter {
64 //let partition_size = (filter.frequencies.len() as f64 / 30f64).floor() as usize;
65 let mut partition_size = filter.frequencies.len() / 30;
66 let step_error = filter.frequencies.len() as f64 % 30f64;
67 if step_error != 0f64 {
68 info!("The approximation will be imperfect");
69 partition_size += 1;
70 }
71
72 //let mut cumulative_error = 0f64;
73
74 let frequencies = filter.frequencies.chunks(partition_size).map(|vec| {
75 let sum: u32 = vec.iter().sum();
76 sum / vec.len() as u32
77 }).collect();
78
79 let coefficients = filter.coefficients.chunks(partition_size).map(|vec| {
80 let sum: f64 = vec.iter().sum();
81 sum / vec.len() as f64
82 }).collect();
83
84 Filter {
85 preamp: filter.preamp,
86 frequencies, coefficients
87 }
88}
89
90/*
91trait MultiPartitionnable {
92 type Item;
93
94 fn multi_partition(self, size: usize, wanted_parts: usize) -> MultiPartition<Self>
95 where
96 Self: Iterator + Sized,
97 {
98 MultiPartition {
99 iter: self,
100 size,
101 wanted_parts,
102 cumulative_error: 0f64,
103 }
104 }
105}
106
107impl<I> MultiPartitionnable for Iterator<Item = I> {
108 type Item = I;
109}
110
111struct MultiPartition<I: Iterator> {
112 iter: I,
113 size: usize,
114 wanted_parts: usize,
115 cumulative_error: f64,
116}
117
118impl<I: Iterator> Iterator for MultiPartition<I> {
119 type Item = Vec<I::Item>;
120
121 fn next(&mut self) -> Option<Self::Item> {
122
123 }
124}
125*/
diff --git a/src/pa_eq.rs b/src/pa_eq.rs
new file mode 100644
index 0000000..4086c31
--- /dev/null
+++ b/src/pa_eq.rs
@@ -0,0 +1,110 @@
1use Filter;
2use cli::pa_eq::*;
3use utils::*;
4
5use dbus_api::equalizing_manager::OrgPulseAudioExtEqualizing1Manager;
6use dbus_api::server_lookup::OrgPulseAudioServerLookup1;
7use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer;
8
9use dbus::{BusType, ConnPath, Connection};
10
11use failure::{Error, ResultExt};
12
13#[derive(Fail, Debug)]
14#[fail(display = "No equalized sink found")]
15struct NoEqualizedSink;
16
17pub fn main(cmd: Command) -> Result<(), Error> {
18 use cli::pa_eq::Command::*;
19
20 warn!("The PulseAudio equalizer has been deprecated for a while, and is known to sometimes cause crashes, latency or audible artifacts");
21
22 match cmd {
23 Load(args) => load(args),
24 Reset(args) => reset(args),
25 }
26}
27
28pub fn reset(_args: ResetCli) -> Result<(), Error> {
29 let conn = connect()?;
30 let conn_sink = get_equalized_sink(&conn)?;
31 let filter_rate = conn_sink.get_filter_sample_rate()?;
32 let filter = Filter {
33 preamp: 1f64,
34 frequencies: vec![],
35 coefficients: vec![],
36 }.pad(filter_rate);
37
38 send_filter(&conn_sink, filter)?;
39
40 Ok(())
41}
42
43pub fn load(args: LoadCli) -> Result<(), Error> {
44 let conn = connect()?;
45 let conn_sink = get_equalized_sink(&conn)?;
46
47 let filter = read_filter_from_arg(&args.file)?;
48
49 let filter_rate = conn_sink.get_filter_sample_rate()?;
50 send_filter(&conn_sink, filter.pad(filter_rate))?;
51
52 Ok(())
53}
54
55fn connect() -> Result<Connection, Error> {
56 Ok(connect_impl().context(
57 "Could not connect to PulseAudio's D-Bus socket. Have you loaded the 'module-dbus-protocol' module?"
58 )?)
59}
60
61fn connect_impl() -> Result<Connection, Error> {
62 let pulse_sock_path = get_pulse_dbus_sock()?;
63 info!("PulseAudio's D-Bus socket path is: {}", pulse_sock_path);
64
65 trace!("Connecting to PulseAudio's D-Bus socket");
66 Ok(Connection::open_private(&pulse_sock_path)?)
67}
68
69fn get_equalized_sink<'a>(conn: &'a Connection) -> Result<ConnPath<'a, &'a Connection>, Error> {
70 Ok(get_equalized_sink_impl(conn).context(
71 "Could not find an equalized sink. Have you loaded the 'module-equalizer-sink' module?",
72 )?)
73}
74
75fn get_equalized_sink_impl<'a>(
76 conn: &'a Connection,
77) -> Result<ConnPath<'a, &'a Connection>, Error> {
78 let conn_manager = conn.with_path("org.PulseAudio.Core1", "/org/pulseaudio/equalizing1", 2000);
79
80 // TODO: make that a command-line option
81 trace!("Getting (one of) the equalized sink(s)");
82 let mut sinks = conn_manager.get_equalized_sinks()?;
83 let sink_path = sinks.pop().ok_or(NoEqualizedSink {})?;
84 info!("Using equalized sink: {:?}", sink_path.as_cstr());
85
86 trace!("Connecting to equalized sink");
87 Ok(conn.with_path("org.PulseAudio.Core1", sink_path, 2000))
88}
89
90fn send_filter(conn_sink: &ConnPath<&Connection>, filter: Filter) -> Result<(), Error> {
91 let channel = conn_sink.get_nchannels()?;
92 info!("Using channel: {}", channel);
93 trace!("Sending filter: {:?}", filter);
94 conn_sink.seed_filter(
95 channel,
96 filter.frequencies,
97 filter.coefficients.into_iter().map(decibel_to_ratio).collect(),
98 decibel_to_ratio(filter.preamp),
99 )?;
100 Ok(())
101}
102
103fn get_pulse_dbus_sock() -> Result<String, Error> {
104 trace!("Connecting to the D-Bus' session bus");
105 let conn = Connection::get_private(BusType::Session)?;
106 let conn = conn.with_path("org.PulseAudio1", "/org/pulseaudio/server_lookup1", 2000);
107
108 trace!("Checking PulseAudio's D-Bus socket path");
109 Ok(conn.get_address()?)
110}
diff --git a/src/parsing/equalizer_apo.lalrpop b/src/parsing/equalizer_apo.lalrpop
index 39dda67..752aee4 100644
--- a/src/parsing/equalizer_apo.lalrpop
+++ b/src/parsing/equalizer_apo.lalrpop
@@ -5,12 +5,8 @@ use std::str::FromStr;
5grammar; 5grammar;
6 6
7pub Main: Filter = { 7pub Main: Filter = {
8 <preamp: Preamp> <eq: Eq> => { 8 <preamp: Preamp> <eq: Eq> => Filter { preamp, frequencies: eq.0, coefficients: eq.1 }
9 let coefficients: Vec<_> = eq.1.iter().map(|decibel| 10f64.powf(decibel / 10f64).sqrt()).collect(); 9
10 // TODO: add decibel_to_ratio conversion function
11 let preamp = 10f64.powf(preamp / 10f64);
12 Filter { preamp, frequencies: eq.0, coefficients }
13 }
14} 10}
15 11
16Preamp: f64 = { 12Preamp: f64 = {
diff --git a/src/parsing/equalizer_apo.rs b/src/parsing/equalizer_apo.rs
index a24cf99..b0aa8f8 100644
--- a/src/parsing/equalizer_apo.rs
+++ b/src/parsing/equalizer_apo.rs
@@ -1,5 +1,5 @@
1// auto-generated: "lalrpop 0.15.2" 1// auto-generated: "lalrpop 0.15.2"
2// sha256: a735e8f9bd5cf5f3aac915d12b24752d366481c3952258e22871eef395f44 2// sha256: 3981cad2c0ee5c1d80c3b7278bb1bc9926ee7b273c2b68423accdd84e43c494
3use ::Filter; 3use ::Filter;
4use std::str::FromStr; 4use std::str::FromStr;
5#[allow(unused_extern_crates)] 5#[allow(unused_extern_crates)]
@@ -879,12 +879,7 @@ fn __action1<
879 (_, eq, _): (usize, (Vec<u32>, Vec<f64>), usize), 879 (_, eq, _): (usize, (Vec<u32>, Vec<f64>), usize),
880) -> Filter 880) -> Filter
881{ 881{
882 { 882 Filter { preamp, frequencies: eq.0, coefficients: eq.1 }
883 let coefficients: Vec<_> = eq.1.iter().map(|decibel| 10f64.powf(decibel / 10f64).sqrt()).collect();
884 // TODO: add decibel_to_ratio conversion function
885 let preamp = 10f64.powf(preamp / 10f64);
886 Filter { preamp, frequencies: eq.0, coefficients }
887 }
888} 883}
889 884
890#[allow(unused_variables)] 885#[allow(unused_variables)]
diff --git a/src/utils.rs b/src/utils.rs
index de9ed3c..d75971a 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,23 +1,16 @@
1use {EqualizerConfFormat, Filter}; 1use Filter;
2 2
3use cli::EqualizerConfFormat;
3use parsing::EqualizerApoParser; 4use parsing::EqualizerApoParser;
4 5
5use dbus_api::equalizing_manager::OrgPulseAudioExtEqualizing1Manager; 6use failure::Error;
6use dbus_api::server_lookup::OrgPulseAudioServerLookup1;
7use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer;
8
9use dbus::{BusType, ConnPath, Connection};
10use failure::{Error, ResultExt};
11use lalrpop_util; 7use lalrpop_util;
12 8
13use std::fmt; 9use std::fmt;
10use std::fs::File;
14use std::io; 11use std::io;
15 12
16#[derive(Fail, Debug)] 13#[derive(Fail, Debug)]
17#[fail(display = "No equalized sink found")]
18struct NoEqualizedSink;
19
20#[derive(Fail, Debug)]
21#[fail( 14#[fail(
22 display = "Could not parse using the {} format: {}", 15 display = "Could not parse using the {} format: {}",
23 format, 16 format,
@@ -28,66 +21,32 @@ struct ParseError {
28 message: String, 21 message: String,
29} 22}
30 23
31pub fn connect() -> Result<Connection, Error> { 24pub fn read_filearg_to_str(file: &str) -> Result<String, Error> {
32 Ok(connect_impl().context( 25 use std::io::Read;
33 "Could not connect to PulseAudio's D-Bus socket. Have you loaded the 'module-dbus-protocol' module?"
34 )?)
35}
36
37fn connect_impl() -> Result<Connection, Error> {
38 let pulse_sock_path = get_pulse_dbus_sock()?;
39 info!("PulseAudio's D-Bus socket path is: {}", pulse_sock_path);
40
41 trace!("Connecting to PulseAudio's D-Bus socket");
42 Ok(Connection::open_private(&pulse_sock_path)?)
43}
44
45pub fn get_equalized_sink<'a>(conn: &'a Connection) -> Result<ConnPath<'a, &'a Connection>, Error> {
46 Ok(get_equalized_sink_impl(conn).context(
47 "Could not find an equalized sink. Have you loaded the 'module-equalizer-sink' module?",
48 )?)
49}
50
51fn get_equalized_sink_impl<'a>(
52 conn: &'a Connection,
53) -> Result<ConnPath<'a, &'a Connection>, Error> {
54 let conn_manager = conn.with_path("org.PulseAudio.Core1", "/org/pulseaudio/equalizing1", 2000);
55 26
56 // TODO: make that a command-line option 27 let mut buffer = String::new();
57 trace!("Getting (one of) the equalized sink(s)"); 28 if file == "-" {
58 let mut sinks = conn_manager.get_equalized_sinks()?; 29 info!("Reading file from the command line");
59 let sink_path = sinks.pop().ok_or(NoEqualizedSink {})?; 30 let stdin = io::stdin();
60 info!("Using equalized sink: {:?}", sink_path.as_cstr()); 31 let mut handle = stdin.lock();
61 32 handle.read_to_string(&mut buffer)?;
62 trace!("Connecting to equalized sink"); 33 } else {
63 Ok(conn.with_path("org.PulseAudio.Core1", sink_path, 2000)) 34 let mut file = File::open(file)?;
35 file.read_to_string(&mut buffer)?;
36 }
37 Ok(buffer)
64} 38}
65 39
66pub fn send_filter(conn_sink: &ConnPath<&Connection>, filter: Filter) -> Result<(), Error> { 40pub fn read_filter_from_arg(file: &str) -> Result<Filter, Error> {
67 let channel = conn_sink.get_nchannels()?; 41 debug!("Reading filter from '{}' in the EqualizerAPO format", file);
68 info!("Using channel: {}", channel); 42 let content = read_filearg_to_str(file)?;
69 trace!("Sending filter: {:?}", filter); 43 parse_filter(&content)
70 conn_sink.seed_filter(
71 channel,
72 filter.frequencies,
73 filter.coefficients,
74 filter.preamp,
75 )?;
76 Ok(())
77} 44}
78 45
79pub fn read_filter<T>(file: &mut T) -> Result<Filter, Error> 46pub fn parse_filter(content: &str) -> Result<Filter, Error> {
80where
81 T: io::Read,
82{
83 let mut buffer = String::new();
84
85 info!("Reading filter in GraphicEQ format from the command line");
86 file.read_to_string(&mut buffer)?;
87
88 // TODO: lifetime issue when "throwing" parse error 47 // TODO: lifetime issue when "throwing" parse error
89 let filter = EqualizerApoParser::new() 48 let filter = EqualizerApoParser::new()
90 .parse(&buffer) 49 .parse(&content)
91 .map_err(|e| convert_parse_error(EqualizerConfFormat::EqualizerAPO, e))?; 50 .map_err(|e| convert_parse_error(EqualizerConfFormat::EqualizerAPO, e))?;
92 trace!("Parsed filter: {:?}", filter); 51 trace!("Parsed filter: {:?}", filter);
93 52
@@ -109,13 +68,8 @@ where
109 } 68 }
110} 69}
111 70
112fn get_pulse_dbus_sock() -> Result<String, Error> { 71pub fn decibel_to_ratio(decibel: f64) -> f64 {
113 trace!("Connecting to the D-Bus' session bus"); 72 10f64.powf(decibel / 10f64).sqrt()
114 let conn = Connection::get_private(BusType::Session)?;
115 let conn = conn.with_path("org.PulseAudio1", "/org/pulseaudio/server_lookup1", 2000);
116
117 trace!("Checking PulseAudio's D-Bus socket path");
118 Ok(conn.get_address()?)
119} 73}
120 74
121/* 75/*