diff options
-rw-r--r-- | Cargo.lock | 24 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | res/default-pa-effects-preset.json | 898 | ||||
-rw-r--r-- | src/cli.rs | 113 | ||||
-rw-r--r-- | src/main.rs | 49 | ||||
-rw-r--r-- | src/pa_effects.rs | 125 | ||||
-rw-r--r-- | src/pa_eq.rs | 110 | ||||
-rw-r--r-- | src/parsing/equalizer_apo.lalrpop | 8 | ||||
-rw-r--r-- | src/parsing/equalizer_apo.rs | 9 | ||||
-rw-r--r-- | src/utils.rs | 96 |
10 files changed, 1282 insertions, 152 deletions
@@ -280,6 +280,11 @@ dependencies = [ | |||
280 | ] | 280 | ] |
281 | 281 | ||
282 | [[package]] | 282 | [[package]] |
283 | name = "itoa" | ||
284 | version = "0.4.3" | ||
285 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
286 | |||
287 | [[package]] | ||
283 | name = "kernel32-sys" | 288 | name = "kernel32-sys" |
284 | version = "0.2.2" | 289 | version = "0.2.2" |
285 | source = "registry+https://github.com/rust-lang/crates.io-index" | 290 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -553,6 +558,11 @@ version = "0.1.9" | |||
553 | source = "registry+https://github.com/rust-lang/crates.io-index" | 558 | source = "registry+https://github.com/rust-lang/crates.io-index" |
554 | 559 | ||
555 | [[package]] | 560 | [[package]] |
561 | name = "ryu" | ||
562 | version = "0.2.6" | ||
563 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
564 | |||
565 | [[package]] | ||
556 | name = "serde" | 566 | name = "serde" |
557 | version = "1.0.75" | 567 | version = "1.0.75" |
558 | source = "registry+https://github.com/rust-lang/crates.io-index" | 568 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -568,6 +578,16 @@ dependencies = [ | |||
568 | ] | 578 | ] |
569 | 579 | ||
570 | [[package]] | 580 | [[package]] |
581 | name = "serde_json" | ||
582 | version = "1.0.32" | ||
583 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
584 | dependencies = [ | ||
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]] | ||
571 | name = "set_eq" | 591 | name = "set_eq" |
572 | version = "0.1.0" | 592 | version = "0.1.0" |
573 | dependencies = [ | 593 | dependencies = [ |
@@ -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" |
@@ -25,3 +25,5 @@ failure = "0.1.2" | |||
25 | 25 | ||
26 | lalrpop-util = "0.15.2" | 26 | lalrpop-util = "0.15.2" |
27 | regex = "1.0.4" | 27 | regex = "1.0.4" |
28 | |||
29 | serde_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 | } | ||
@@ -21,31 +21,16 @@ pub struct Cli { | |||
21 | 21 | ||
22 | #[derive(StructOpt, Debug)] | 22 | #[derive(StructOpt, Debug)] |
23 | pub enum Command { | 23 | pub 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 |
33 | pub 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 | ||
51 | arg_enum! { | 36 | arg_enum! { |
@@ -55,5 +40,79 @@ arg_enum! { | |||
55 | } | 40 | } |
56 | } | 41 | } |
57 | 42 | ||
58 | #[derive(StructOpt, Debug)] | 43 | pub mod pa_eq { |
59 | pub 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 | |||
80 | pub 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 | ||
15 | extern crate lalrpop_util; | 15 | extern crate lalrpop_util; |
16 | 16 | ||
17 | #[macro_use] | ||
18 | extern crate serde_json; | ||
19 | |||
17 | mod cli; | 20 | mod cli; |
18 | mod dbus_api; | 21 | mod dbus_api; |
19 | mod parsing; | 22 | mod parsing; |
20 | mod utils; | 23 | mod utils; |
21 | 24 | ||
22 | use utils::*; | 25 | mod pa_eq; |
23 | use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer; | 26 | mod pa_effects; |
27 | |||
24 | use cli::*; | 28 | use cli::*; |
25 | 29 | ||
26 | use failure::Error; | 30 | use failure::Error; |
27 | use structopt::StructOpt; | 31 | use structopt::StructOpt; |
28 | 32 | ||
29 | use std::fs::File; | ||
30 | use std::io; | ||
31 | |||
32 | #[derive(Debug)] | 33 | #[derive(Debug)] |
33 | pub struct Filter { | 34 | pub 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 | |||
82 | fn 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 | |||
97 | fn 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 @@ | |||
1 | use cli::pa_effects::*; | ||
2 | use utils::*; | ||
3 | use Filter; | ||
4 | |||
5 | use failure::Error; | ||
6 | |||
7 | use serde_json; | ||
8 | |||
9 | const DEFAULT_PRESET: &'static str = include_str!("../res/default-pa-effects-preset.json"); | ||
10 | |||
11 | pub 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 | |||
19 | fn 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 | |||
34 | fn 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 | |||
63 | fn 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 | /* | ||
91 | trait 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 | |||
107 | impl<I> MultiPartitionnable for Iterator<Item = I> { | ||
108 | type Item = I; | ||
109 | } | ||
110 | |||
111 | struct MultiPartition<I: Iterator> { | ||
112 | iter: I, | ||
113 | size: usize, | ||
114 | wanted_parts: usize, | ||
115 | cumulative_error: f64, | ||
116 | } | ||
117 | |||
118 | impl<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 @@ | |||
1 | use Filter; | ||
2 | use cli::pa_eq::*; | ||
3 | use utils::*; | ||
4 | |||
5 | use dbus_api::equalizing_manager::OrgPulseAudioExtEqualizing1Manager; | ||
6 | use dbus_api::server_lookup::OrgPulseAudioServerLookup1; | ||
7 | use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer; | ||
8 | |||
9 | use dbus::{BusType, ConnPath, Connection}; | ||
10 | |||
11 | use failure::{Error, ResultExt}; | ||
12 | |||
13 | #[derive(Fail, Debug)] | ||
14 | #[fail(display = "No equalized sink found")] | ||
15 | struct NoEqualizedSink; | ||
16 | |||
17 | pub 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 | |||
28 | pub 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 | |||
43 | pub 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 | |||
55 | fn 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 | |||
61 | fn 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 | |||
69 | fn 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 | |||
75 | fn 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 | |||
90 | fn 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 | |||
103 | fn 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; | |||
5 | grammar; | 5 | grammar; |
6 | 6 | ||
7 | pub Main: Filter = { | 7 | pub 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 | ||
16 | Preamp: f64 = { | 12 | Preamp: 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 |
3 | use ::Filter; | 3 | use ::Filter; |
4 | use std::str::FromStr; | 4 | use 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 @@ | |||
1 | use {EqualizerConfFormat, Filter}; | 1 | use Filter; |
2 | 2 | ||
3 | use cli::EqualizerConfFormat; | ||
3 | use parsing::EqualizerApoParser; | 4 | use parsing::EqualizerApoParser; |
4 | 5 | ||
5 | use dbus_api::equalizing_manager::OrgPulseAudioExtEqualizing1Manager; | 6 | use failure::Error; |
6 | use dbus_api::server_lookup::OrgPulseAudioServerLookup1; | ||
7 | use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer; | ||
8 | |||
9 | use dbus::{BusType, ConnPath, Connection}; | ||
10 | use failure::{Error, ResultExt}; | ||
11 | use lalrpop_util; | 7 | use lalrpop_util; |
12 | 8 | ||
13 | use std::fmt; | 9 | use std::fmt; |
10 | use std::fs::File; | ||
14 | use std::io; | 11 | use std::io; |
15 | 12 | ||
16 | #[derive(Fail, Debug)] | 13 | #[derive(Fail, Debug)] |
17 | #[fail(display = "No equalized sink found")] | ||
18 | struct 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 | ||
31 | pub fn connect() -> Result<Connection, Error> { | 24 | pub 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 | |||
37 | fn 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 | |||
45 | pub 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 | |||
51 | fn 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 | ||
66 | pub fn send_filter(conn_sink: &ConnPath<&Connection>, filter: Filter) -> Result<(), Error> { | 40 | pub 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 | ||
79 | pub fn read_filter<T>(file: &mut T) -> Result<Filter, Error> | 46 | pub fn parse_filter(content: &str) -> Result<Filter, Error> { |
80 | where | ||
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 | ||
112 | fn get_pulse_dbus_sock() -> Result<String, Error> { | 71 | pub 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 | /* |