Mercurial > hg > chourdakisreiss2016
comparison training-sessions/jacob.harrison/ui.py @ 0:246d5546657c
initial commit, needs cleanup
author | Emmanouil Theofanis Chourdakis <e.t.chourdakis@qmul.ac.uk> |
---|---|
date | Wed, 14 Dec 2016 13:15:48 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:246d5546657c |
---|---|
1 # -*- coding: utf-8 -*- | |
2 """ | |
3 Created on Thu Jun 11 11:03:04 2015 | |
4 | |
5 @author: mmxgn | |
6 """ | |
7 | |
8 | |
9 | |
10 from essentia.standard import YamlInput, YamlOutput, AudioLoader, AudioWriter | |
11 from essentia import Pool | |
12 import matplotlib | |
13 matplotlib.use("TkAgg") | |
14 from matplotlib.figure import Figure | |
15 from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg | |
16 from mapping import * | |
17 | |
18 from sys import argv, exit | |
19 | |
20 from numpy import * | |
21 from matplotlib.pyplot import * | |
22 | |
23 from Tkinter import * | |
24 import tkMessageBox, tkFileDialog | |
25 | |
26 from numpy.core._internal import _gcd as gcd | |
27 | |
28 import os | |
29 import subprocess | |
30 from scikits.audiolab import Format, Sndfile | |
31 from scipy.signal import fftconvolve | |
32 from glob import glob | |
33 | |
34 | |
35 def zafar(lx, rx, d1, g1, da, G, gc, m): | |
36 """ Rafii & Pardo Reverberator (2009) controlled by High Level parameters | |
37 Inputs: | |
38 lx : left channel input | |
39 rx : right channel input | |
40 d1 : delay of first comb filter in samples | |
41 g1 : gain of first comb filters | |
42 da : delay of allpass filter in samples | |
43 G : dry/wet mix gain | |
44 gc : lowpass filter gain | |
45 m : difference between left and right channel phases | |
46 | |
47 Outputs: | |
48 ly: left channel output | |
49 ry: right channel output | |
50 """ | |
51 | |
52 def calculate_parameters(d1,g1): | |
53 | |
54 d2 = int(round((1.5)**(-1)*d1)) | |
55 | |
56 while gcd(d2,d1) != 1: | |
57 d2 += 1 | |
58 | |
59 d3 = int(round((1.5)**(-2)*d1)) | |
60 | |
61 while gcd(d3, d2) != 1 or gcd(d3, d1) != 1: | |
62 d3 += 1 | |
63 | |
64 d4 = int(round((1.5)**(-3)*d1)) | |
65 | |
66 while gcd(d4, d3) != 1 or gcd(d4, d2) != 1 or gcd(d4, d1) != 1: | |
67 d4 += 1 | |
68 | |
69 | |
70 d5 = int(round((1.5)**(-4)*d1)) | |
71 | |
72 while gcd(d5, d4) != 1 or gcd(d5, d3) != 1 or gcd(d5, d2) != 1 or gcd(d5, d1) != 1: | |
73 d5 += 1 | |
74 | |
75 d6 = int(round((1.5)**(-5)*d1)) | |
76 while gcd(d6, d5) != 1 or gcd(d6, d4) != 1 or gcd(d6, d3) != 1 or gcd(d6, d2) != 1 or gcd(d6, d1) != 1: | |
77 d6 += 1 | |
78 g2 = g1**(1.5)**(-1)*g1 | |
79 g3 = g1**(1.5)**(-2)*g1 | |
80 g4 = g1**(1.5)**(-3)*g1 | |
81 g5 = g1**(1.5)**(-4)*g1 | |
82 g6 = g1**(1.5)**(-5)*g1 | |
83 | |
84 return (d1, d2, d3, d4, d5, d6, g1, g2, g3, g4, g5, g6) | |
85 def comb_array(x, g1, d1): | |
86 | |
87 (d1,d2,d3,d4,d5,d6,g1,g2,g3,g4,g5,g6) = calculate_parameters(d1,g1) | |
88 | |
89 | |
90 | |
91 c1out = comb(x, g1, d1) | |
92 c2out = comb(x, g2, d2) | |
93 c3out = comb(x, g3, d3) | |
94 c4out = comb(x, g4, d4) | |
95 c5out = comb(x, g5, d5) | |
96 c6out = comb(x, g6, d6) | |
97 | |
98 | |
99 Lc1 = len(c1out) | |
100 Lc2 = len(c2out) | |
101 Lc3 = len(c3out) | |
102 Lc4 = len(c4out) | |
103 Lc5 = len(c5out) | |
104 Lc6 = len(c6out) | |
105 | |
106 Lc = max(Lc1, Lc2, Lc3, Lc4, Lc5, Lc6) | |
107 | |
108 y = zeros((Lc, )) | |
109 | |
110 y[0:Lc1] = c1out | |
111 y[0:Lc2] += c2out | |
112 y[0:Lc3] += c3out | |
113 y[0:Lc4] += c4out | |
114 y[0:Lc5] += c5out | |
115 y[0:Lc6] += c6out | |
116 | |
117 return y | |
118 | |
119 def comb(x, g, d): | |
120 LEN = len(x)+d | |
121 # print d | |
122 y = zeros((LEN,)) | |
123 for n in range(0, LEN): | |
124 if n - d < 0: | |
125 y[n] = 0 | |
126 else: | |
127 y[n] = x[n-d] + g*y[n-d] | |
128 | |
129 return y | |
130 | |
131 def allpass(x, g, d): | |
132 LENx = len(x) | |
133 LENy = LENx+d | |
134 y = zeros((LENy,)) | |
135 for n in range(0, LENy): | |
136 if n-d < 0: | |
137 y[n] = -g*x[n] | |
138 elif n >= LENx: | |
139 y[n] = x[n-d] + g*y[n-d] | |
140 else: | |
141 y[n] = x[n-d] - g*x[n] + g*y[n-d] | |
142 | |
143 return y | |
144 | |
145 def lowpass(x, g): | |
146 LEN = len(x) | |
147 y = zeros((LEN,)) | |
148 | |
149 for n in range(0, LEN): | |
150 if n-1 < 0: | |
151 y[n] = (1-g)*x[n] | |
152 else: | |
153 y[n] = (1-g)*x[n] + g*y[n-1] | |
154 | |
155 return y | |
156 | |
157 ga = 1./sqrt(2.) | |
158 | |
159 cin = 0.5*lx + 0.5*rx | |
160 cout = comb_array(cin, g1, d1) | |
161 | |
162 | |
163 ra = allpass(cout, ga, da+m/2) | |
164 la = allpass(cout, ga, da-m/2) | |
165 | |
166 ral = lowpass(ra, gc) | |
167 lal = lowpass(la, gc) | |
168 | |
169 ralg = G*ral | |
170 lalg = G*lal | |
171 | |
172 ry = ralg[0:len(rx)] + (1-G)*rx | |
173 ly = lalg[0:len(lx)] + (1-G)*lx | |
174 | |
175 return (ry, ly) | |
176 | |
177 class UI: | |
178 | |
179 def __init__(self, master, directory): | |
180 self.master = master | |
181 | |
182 self.directory = directory | |
183 | |
184 | |
185 | |
186 yamlinput = YamlInput(filename="session.yaml") | |
187 | |
188 try: | |
189 self.sessionpool = yamlinput() | |
190 try: | |
191 self.files_to_visit = self.sessionpool['files_to_visit'] | |
192 except: | |
193 self.files_to_visit = [] | |
194 | |
195 try: | |
196 self.visited_files = self.sessionpool['visited_files'] | |
197 except: | |
198 self.visited_files = [] | |
199 | |
200 | |
201 | |
202 except: | |
203 print "[II] Could not open sessionpool file, creating a new one" | |
204 self.sessionpool = Pool() | |
205 self.files_to_visit = glob("%s/*.wav" % directory) | |
206 for i in self.files_to_visit: | |
207 self.sessionpool.add('files_to_visit', i) | |
208 self.visited_files = [] | |
209 | |
210 if len(self.files_to_visit) == 0: | |
211 tkMessageBox.showinfo("","No files to visit") | |
212 master.destroy() | |
213 return | |
214 | |
215 filename = self.files_to_visit[-1] | |
216 self.filename = filename | |
217 # visited_files.append(filename) | |
218 self.label_top = Label(master, text="") | |
219 self.label_top.grid(row=0, column=0, columnspan=6) | |
220 | |
221 self.load_song(filename) | |
222 | |
223 | |
224 # Top Label | |
225 | |
226 self.label_top.config( text="Training song: %s (sampleRate: %.0f, nChannels: %d) - %d songs left" % (filename, self.SR, self.numChannels, len(self.files_to_visit)-1)) | |
227 | |
228 # High Level Parameters | |
229 | |
230 | |
231 | |
232 # Sliders | |
233 self.scale_T60 = Scale(master, to_=T60_min, from_=T60_max, resolution=0.01, label="RT60", showvalue=False) | |
234 self.scale_T60.bind("<ButtonRelease-1>",self.callback_update_parameters_high) | |
235 self.scale_T60.grid(row=1,column=0,rowspan=23,sticky=N+S+E+W) | |
236 | |
237 self.scale_D = Scale(master, to_=D_max, from_=D_min, resolution=0.01, label="D", showvalue=False) | |
238 self.scale_D.bind("<ButtonRelease-1>",self.callback_update_parameters_high) | |
239 self.scale_D.grid(row=1,column=1,rowspan=23,sticky=N+S+E+W) | |
240 | |
241 self.scale_C = Scale(master, to_=C_min, from_=C_max, resolution=0.01, label="C", showvalue=False) | |
242 self.scale_C.bind("<ButtonRelease-1>",self.callback_update_parameters_high) | |
243 self.scale_C.grid(row=1,column=2,rowspan=23,sticky=N+S+E+W) | |
244 | |
245 self.scale_Tc = Scale(master, to_=Tc_min, from_=Tc_max, resolution=0.01, label="T_c", showvalue=False) | |
246 self.scale_Tc.bind("<ButtonRelease-1>",self.callback_update_parameters_high) | |
247 self.scale_Tc.grid(row=1,column=3,rowspan=23,sticky=N+S+E+W) | |
248 | |
249 self.scale_SC = Scale(master, to_=SC_min, from_=SC_max, resolution=0.01, label="SC", showvalue=False) | |
250 self.scale_SC.bind("<ButtonRelease-1>",self.callback_update_parameters_high) | |
251 self.scale_SC.grid(row=1,column=4,rowspan=23,sticky=N+S+E+W) | |
252 | |
253 | |
254 # Fine Tuning (coefficients) | |
255 | |
256 # Labels | |
257 #self.label_p = Label(master, text="Coefficients Fine Tuning:") | |
258 #self.label_p.grid(row=13,column=1,sticky=N+W) | |
259 | |
260 | |
261 self.label_legend1 = Label(master, text="Legend:") | |
262 self.label_legend1.grid(row=2,column=6,sticky=N+E+W) | |
263 self.label_legend2 = Label(master, text="RT60: Reverberation time") | |
264 self.label_legend2.grid(row=3,column=6,sticky=N+W) | |
265 self.label_legend3 = Label(master, text="D: Echo density") | |
266 self.label_legend3.grid(row=4,column=6,sticky=N+W) | |
267 self.label_legend4 = Label(master, text="C: Clarity") | |
268 self.label_legend4.grid(row=5,column=6,sticky=N+W) | |
269 self.label_legend5 = Label(master, text="Tc: Central Time") | |
270 self.label_legend5.grid(row=6,column=6,sticky=N+W) | |
271 self.label_legend6 = Label(master, text="SC: Spectral Centroid") | |
272 self.label_legend6.grid(row=7,column=6,sticky=N+W) | |
273 | |
274 | |
275 | |
276 | |
277 # Sliders | |
278 self.scale_d1 = Scale(master, to_=d1_min, from_=d1_max, resolution=0.01, label="d1", showvalue=False)#, command=self.callback_update_parameters) | |
279 self.scale_d1.bind("<ButtonRelease-1>",self.callback_update_parameters) | |
280 # self.scale_d1.grid(row=16,column=0,rowspan=8,sticky=N+S+E+W) | |
281 self.scale_g1 = Scale(master,to_=g1_min, from_=g1_max, resolution=0.001, label="g1", showvalue=False)#, command=self.callback_update_parameters) | |
282 self.scale_g1.bind("<ButtonRelease-1>",self.callback_update_parameters) | |
283 | |
284 # self.scale_g1.grid(row=16,column=1,rowspan=8,sticky=N+S+E+W) | |
285 self.scale_da = Scale(master, to_=da_min, from_=da_max, resolution=0.001, label="da", showvalue=False)#, command=self.callback_update_parameters) | |
286 self.scale_da.bind("<ButtonRelease-1>",self.callback_update_parameters) | |
287 | |
288 # self.scale_da.grid(row=16,column=2,rowspan=8,sticky=N+S+E+W) | |
289 self.scale_G = Scale(master,to_=G_min, from_=G_max, resolution=0.001, label="G", showvalue=False)#, command=self.callback_update_parameters) | |
290 self.scale_G.bind("<ButtonRelease-1>",self.callback_update_parameters) | |
291 | |
292 # self.scale_G.grid(row=16,column=3,rowspan=8,sticky=N+S+E+W) | |
293 self.scale_gc = Scale(master, to_=gc_min, from_=gc_max, resolution=0.001, label="gc", showvalue=False)#, command=self.callback_update_parameters) | |
294 self.scale_gc.bind("<ButtonRelease-1>",self.callback_update_parameters) | |
295 | |
296 # self.scale_gc.grid(row=16,column=4,rowspan=8,sticky=N+S+E+W) | |
297 | |
298 | |
299 # Labels | |
300 | |
301 | |
302 | |
303 self.label_T60 = Label(master, text="Reverberation Time: ") | |
304 # self.label_T60.grid(row=2,column=6,sticky=N+S+E+W) | |
305 self.label_D = Label(master, text="Echo Density: ") | |
306 # self.label_D.grid(row=3,column=6,sticky=N+S+E+W) | |
307 self.label_C = Label(master, text="Clarity: ") | |
308 # self.label_C.grid(row=4,column=6,sticky=N+S+E+W) | |
309 self.label_Tc = Label(master, text="Central Time: ") | |
310 # self.label_Tc.grid(row=5,column=6,sticky=N+S+E+W) | |
311 self.label_SC = Label(master, text="Spectral Centroid: ") | |
312 # self.label_SC.grid(row=6,column=6,sticky=N+S+E+W) | |
313 | |
314 | |
315 self.label_d1 = Label(master, text="d_1: ") | |
316 # self.label_d1.grid(row=7, column=6, sticky=N+S+E+W) | |
317 | |
318 self.label_g1 = Label(master, text="g_1: ") | |
319 # self.label_g1.grid(row=8, column=6, sticky=N+S+E+W) | |
320 | |
321 self.label_da = Label(master, text="d_a: ") | |
322 # self.label_da.grid(row=9, column=6, sticky=N+S+E+W) | |
323 | |
324 self.label_gc = Label(master, text="gc: ") | |
325 # self.label_gc.grid(row=10, column=6, sticky=N+S+E+W) | |
326 | |
327 self.label_G = Label(master, text="G: ") | |
328 # self.label_G.grid(row=11, column=6, sticky=N+S+E+W) | |
329 | |
330 # Buttons | |
331 | |
332 self.button_plot_impulse = Button(master, text="Plot Impulse",command=self.callback_plot_impulse, width=15).grid(row=13,column=6,sticky=N+S+E+W) | |
333 # self.button_plot_raw = Button(master, text="Plot Raw", width=15,command=self.callback_plot_raw).grid(row=16,column=6,sticky=N+S+E+W) | |
334 # self.button_plot_reverb = Button(master, text="Plot Reverb", width=15, command=self.callback_plot_reverb).grid(row=17, column=6,sticky=N+S+E+W) | |
335 self.button_play_raw = Button(master, text="Play Dry", bg="green", fg="white", width=15, command=self.callback_play_raw).grid(row=18, column=6,sticky=N+S+E+W) | |
336 self.button_play_reverb = Button(master, text="Play Reverb", bg="green", fg="white", width=15, command=self.callback_play_reverb).grid(row=19, column=6,sticky=N+S+E+W) | |
337 self.button_stop = Button(master, text="Stop Playing", bg="red", fg="white", width=15, command=self.callback_stop).grid(row=20, column=6,sticky=N+S+E+W) | |
338 self.button_save = Button(master, text="Save", fg="white", bg="orange", width=15, command=self.callback_save).grid(row=21, column=6,sticky=N+S+E+W) | |
339 self.button_reset = Button(master, text="Undo", width=15, command=self.callback_reset).grid(row=22, column=6,sticky=N+S+E+W) | |
340 self.button_next = Button(master, text="Next >>", width=15, command=self.callback_next).grid(row=23, column=6,sticky=N+S+E+W) | |
341 | |
342 | |
343 | |
344 # Figure | |
345 | |
346 self.figure = Figure( dpi=50) | |
347 | |
348 self.figure.text(0.5,0.5,'No plot selected', weight = "bold", horizontalalignment='center') | |
349 | |
350 | |
351 self.canvas = FigureCanvasTkAgg(self.figure, master=root) | |
352 self.canvas.show() | |
353 self.canvas.get_tk_widget().grid(row=0, column=7, rowspan=17, padx=20,sticky=E+W+N+S) | |
354 self.canvas._tkcanvas.grid(row=0, column=7, rowspan=23) | |
355 | |
356 # Toolbar for canvas | |
357 | |
358 self.toolbar_frame = Frame(master) | |
359 self.toolbar_frame.grid(row=23,column=7,sticky=E+W+N+S, padx=19) | |
360 self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.toolbar_frame) | |
361 | |
362 | |
363 | |
364 Grid.columnconfigure(master, 7, weight=1) | |
365 Grid.rowconfigure(master, 1, weight=1) | |
366 | |
367 # Status bar | |
368 | |
369 self.status_bar_text = Label(text="Ready.") | |
370 self.status_bar_text.grid(row=18, column=0, columnspan = 8, sticky=N+S+E, padx=10) | |
371 | |
372 self.lastplot = '' | |
373 self.parameterschanged_render = True | |
374 self.pendingactions = [] | |
375 | |
376 # Initial values | |
377 d1t = 0.05 | |
378 self.d1 = d1t*self.SR | |
379 dat = 0.006 | |
380 self.da = dat*self.SR | |
381 g1 = 0.5 | |
382 self.g1 = g1 | |
383 G = 0.5 | |
384 self.G = G | |
385 gc = 0.5 | |
386 self.gc = gc | |
387 | |
388 self.scale_d1.set(d1t) | |
389 self.scale_da.set(dat) | |
390 self.scale_g1.set(g1) | |
391 self.scale_gc.set(gc) | |
392 self.scale_G.set(G) | |
393 | |
394 t = zeros((self.SR*120,)) | |
395 t[0] = 1 | |
396 | |
397 self.impulse = t | |
398 | |
399 | |
400 | |
401 # Presets | |
402 self.presets=[] | |
403 self.var_presets = StringVar() | |
404 self.drop_presets = OptionMenu(master, self.var_presets, self.presets, command=self.callback_load_preset) | |
405 self.drop_presets.grid(row=0, column=6, sticky=N+S+E+W) | |
406 self.callback_load_presets() | |
407 | |
408 | |
409 # Preset options | |
410 self.button_save_preset = Button(master, text="Save Preset", command=self.callback_save_preset) | |
411 self.button_save_preset.grid(row=1, column=6, sticky=N+E+W) | |
412 | |
413 # Pool | |
414 self.pool = Pool() | |
415 self.pool.set('filename', self.filename) | |
416 self.pool.set('sampleRate', self.SR) | |
417 self.pool.set('nChannels', self.numChannels) | |
418 | |
419 | |
420 | |
421 self.callback_load_preset('almost_no_reverb') | |
422 | |
423 # Finally | |
424 self.callback_update_parameters(None) | |
425 | |
426 | |
427 | |
428 | |
429 | |
430 def pyaudio_callback_raw(self, in_data, frame_count, time_info, status): | |
431 if self.player_command == 'Stop': | |
432 return (0, pyaudio.paAbort) | |
433 | |
434 data = self.audio[self.player_idx:self.player_idx+frame_count, :] | |
435 | |
436 data = reshape(data, (data.shape[0]*data.shape[1], 1)) | |
437 | |
438 # print data | |
439 self.player_idx += frame_count | |
440 | |
441 | |
442 return (data, pyaudio.paContinue) | |
443 | |
444 | |
445 def play_reverb(self): | |
446 | |
447 self.calculate_impulse_response() | |
448 ly, ry = self.impulse_response_left_channel, self.impulse_response_right_channel | |
449 | |
450 lx = self.audio[:,0] | |
451 rx = self.audio[:,1] | |
452 | |
453 print "Convolving left channel" | |
454 l_out = fftconvolve(ly, lx) | |
455 l_out = l_out/max(l_out) | |
456 | |
457 print "Convolving right channel" | |
458 r_out = fftconvolve(ry, rx) | |
459 r_out = r_out/max(r_out) | |
460 | |
461 | |
462 | |
463 lim = min(len(l_out), len(r_out)) | |
464 | |
465 | |
466 | |
467 if self.numChannels == 1: | |
468 audio_out = l_out[0:lim] | |
469 else: | |
470 audio_out = concatenate((matrix(l_out[0:lim]).T, | |
471 matrix(r_out[0:lim]).T | |
472 ), | |
473 axis=1) | |
474 | |
475 reverb_filename = "%s_reverb_%s" % (self.filename.split('.')[0], self.filename.split('.')[1]) | |
476 | |
477 audio_file = Sndfile(reverb_filename, 'w', Format(self.filename.split('.')[1]), self.numChannels, self.SR) | |
478 audio_file.write_frames(audio_out) | |
479 audio_file.close() | |
480 | |
481 self.reverberated_audio = audio_out | |
482 | |
483 self.reverb_filename = reverb_filename | |
484 | |
485 self.playerprocess = subprocess.Popen("mplayer %s" % reverb_filename, | |
486 stdout = subprocess.PIPE, | |
487 shell=True, | |
488 preexec_fn=os.setsid) | |
489 | |
490 def play_raw(self): | |
491 self.playerprocess = subprocess.Popen("mplayer %s" % self.filename, | |
492 stdout = subprocess.PIPE, | |
493 shell=True, | |
494 preexec_fn=os.setsid) | |
495 | |
496 | |
497 | |
498 | |
499 | |
500 def remove_action_from_status(self, text): | |
501 | |
502 self.pendingactions.remove(text) | |
503 | |
504 if len(self.pendingactions) == 0: | |
505 self.status_bar_text.config(text='Ready.') | |
506 elif len(self.pendingactions) == 1: | |
507 self.status_bar_text.config(text=self.pendingactions[0]+'.') | |
508 else: | |
509 self.status_bar_text.config(text=reduce(lambda h,t: h+','+t, self.pendingactions)+'.') | |
510 | |
511 | |
512 def add_action_to_status(self, text): | |
513 | |
514 self.pendingactions.append(text) | |
515 | |
516 if len(self.pendingactions) == 0: | |
517 self.status_bar_text.config(text='Ready.') | |
518 elif len(self.pendingactions) == 1: | |
519 self.status_bar_text.config(text=text+'.') | |
520 else: | |
521 self.status_bar_text.config(text=reduce(lambda h,t: h+', '+t, self.pendingactions)+'.') | |
522 | |
523 print self.pendingactions, len(self.pendingactions) | |
524 | |
525 | |
526 def load_song(self, filename): | |
527 tup = AudioLoader(filename=filename)() | |
528 self.audio = tup[0] | |
529 self.SR = tup[1] | |
530 global SC_max | |
531 SC_max = self.SR/4.0 | |
532 self.numChannels = tup[2] | |
533 self.label_top.config(text="Training song: %s (sampleRate: %.0f, nChannels: %d) \n %d songs left" % (filename, self.SR, self.numChannels, len(self.files_to_visit)-1),wraplength=500) | |
534 self.saved = False | |
535 | |
536 | |
537 | |
538 | |
539 def estimate_T60(self, d1, g1, gc, G, SR): | |
540 ga = 1/sqrt(2) | |
541 return d1/SR/log(g1)*log(10**-3/ga/(1-gc)/G) | |
542 | |
543 def calculate_parameters(self,d1,g1): | |
544 | |
545 d2 = int(round((1.5)**(-1)*d1)) | |
546 | |
547 while gcd(d2,d1) != 1: | |
548 d2 += 1 | |
549 | |
550 d3 = int(round((1.5)**(-2)*d1)) | |
551 | |
552 while gcd(d3, d2) != 1 or gcd(d3, d1) != 1: | |
553 d3 += 1 | |
554 | |
555 d4 = int(round((1.5)**(-3)*d1)) | |
556 | |
557 while gcd(d4, d3) != 1 or gcd(d4, d2) != 1 or gcd(d4, d1) != 1: | |
558 d4 += 1 | |
559 | |
560 | |
561 d5 = int(round((1.5)**(-4)*d1)) | |
562 | |
563 while gcd(d5, d4) != 1 or gcd(d5, d3) != 1 or gcd(d5, d2) != 1 or gcd(d5, d1) != 1: | |
564 d5 += 1 | |
565 | |
566 d6 = int(round((1.5)**(-5)*d1)) | |
567 while gcd(d6, d5) != 1 or gcd(d6, d4) != 1 or gcd(d6, d3) != 1 or gcd(d6, d2) != 1 or gcd(d6, d1) != 1: | |
568 d6 += 1 | |
569 g2 = g1**(1.5)**(-1)*g1 | |
570 g3 = g1**(1.5)**(-2)*g1 | |
571 g4 = g1**(1.5)**(-3)*g1 | |
572 g5 = g1**(1.5)**(-4)*g1 | |
573 g6 = g1**(1.5)**(-5)*g1 | |
574 | |
575 return (d1, d2, d3, d4, d5, d6, g1, g2, g3, g4, g5, g6) | |
576 def estimate_C(self, g1, G, gc): | |
577 g2 = g1**(1.5)**(-1)*g1 | |
578 g3 = g1**(1.5)**(-2)*g1 | |
579 g4 = g1**(1.5)**(-3)*g1 | |
580 g5 = g1**(1.5)**(-4)*g1 | |
581 g6 = g1**(1.5)**(-5)*g1 | |
582 gains = zeros((6,1)) | |
583 gains[0] = g1 | |
584 gains[1] = g2 | |
585 gains[2] = g3 | |
586 gains[3] = g4 | |
587 gains[4] = g5 | |
588 gains[5] = g6 | |
589 | |
590 return -10*log10(G**2*(1-gc)/(1+gc)*sum(1/(1-gains**2))) | |
591 | |
592 | |
593 def estimate_D(self, d1, g1, da, SR): | |
594 kS = 20.78125 | |
595 | |
596 | |
597 return kS*0.1/d1/da*self.SR**2 | |
598 | |
599 def estimate_Tc(self, d1, g1, da, SR): | |
600 delays = zeros((6,)) | |
601 gains = zeros((6,1)) | |
602 (delays[0],delays[1],delays[2],delays[3],delays[4],delays[5],gains[0],gains[1],gains[2],gains[3],gains[4],gains[5]) = self.calculate_parameters(d1,g1) | |
603 return sum(delays/SR*gains**2/(1-gains**2)**2)/sum(gains**2/(1-gains**2)) + da/SR | |
604 | |
605 def update_parameters_high(self, _): | |
606 | |
607 self.T60_old = self.T60 | |
608 self.D_old = self.D | |
609 self.C_old = self.C | |
610 self.Tc_old = self.Tc | |
611 self.SC_old = self.SC | |
612 | |
613 T60 = self.scale_T60.get() | |
614 D = self.scale_D.get() | |
615 C = self.scale_C.get() | |
616 Tc = self.scale_Tc.get() | |
617 SC = self.scale_SC.get() | |
618 print self.SR | |
619 print (T60, D, C, Tc, SC) | |
620 (d1t, dat, g1, gc, G) = inverse_mapping(T60,D,C,Tc,SC,SR=self.SR) | |
621 | |
622 print "da",(d1t, dat, g1, gc, G) | |
623 | |
624 self.scale_d1.set(d1t) | |
625 self.scale_da.set(dat) | |
626 self.scale_g1.set(g1) | |
627 self.scale_gc.set(gc) | |
628 self.scale_G.set(G) | |
629 self.parameterschanged_render = True | |
630 | |
631 def callback_update_parameters_high(self, _): | |
632 print("callback_update_parameters_high") | |
633 self.update_parameters_high(_) | |
634 | |
635 | |
636 self.update_parameters(_) | |
637 | |
638 self.callback_plot_impulse() | |
639 | |
640 def update_parameters(self, _): | |
641 SR = self.SR | |
642 d1t = self.scale_d1.get() | |
643 | |
644 d1 = round(d1t*SR) | |
645 g1 = self.scale_g1.get() | |
646 dat = self.scale_da.get() | |
647 da = round(dat*SR) | |
648 G = self.scale_G.get() | |
649 gc = self.scale_gc.get() | |
650 | |
651 | |
652 T60 = self.estimate_T60(d1,g1,gc,G,SR) | |
653 D = self.estimate_D(d1, g1, da, SR) | |
654 C = self.estimate_C(g1, G, gc) | |
655 Tc = self.estimate_Tc(d1,g1,da,SR) | |
656 SC = self.estimate_SC(gc, SR) | |
657 | |
658 | |
659 self.d1_old = self.d1 | |
660 self.G_old = self.G | |
661 self.gc_old = self.gc | |
662 self.g1_old = self.g1 | |
663 self.da_old = self.da | |
664 | |
665 self.d1 = d1 | |
666 self.G = G | |
667 self.gc = gc | |
668 self.g1 = g1 | |
669 self.da = da | |
670 | |
671 | |
672 | |
673 self.pool.set('parameters.d1', d1t) | |
674 self.pool.set('parameters.G', G) | |
675 self.pool.set('parameters.gc', gc) | |
676 self.pool.set('parameters.g1', g1) | |
677 self.pool.set('parameters.da', dat) | |
678 | |
679 | |
680 | |
681 self.T60 = T60 | |
682 self.D = D | |
683 self.Tc = Tc | |
684 self.SC = SC | |
685 self.C = C | |
686 | |
687 self.pool.set('parameters.T60', T60) | |
688 self.pool.set('parameters.D', D) | |
689 self.pool.set('parameters.C', C) | |
690 self.pool.set('parameters.Tc', Tc) | |
691 self.pool.set('parameters.SC', SC) | |
692 | |
693 self.label_T60.config(text="Reverberation Time: %.3fs" % T60) | |
694 self.label_D.config(text="Echo Density: %.3f at 0.1s" % D) | |
695 self.label_C.config(text="Clarity: %.3f dB" % C) | |
696 self.label_Tc.config(text="Central Time: %.3fs" % Tc) | |
697 self.label_SC.config(text="Spectral Centroid: %.3f Hz" % SC) | |
698 | |
699 self.label_d1.config(text="d_1: %.3fs" % d1t) | |
700 self.label_g1.config(text="g_1: %.3f" % g1) | |
701 self.label_da.config(text="d_a: %.3fs" % dat) | |
702 self.label_gc.config(text="g_c: %.3f" % gc) | |
703 self.label_G.config(text="G: %.3f" % G) | |
704 self.lastplot = '' | |
705 | |
706 | |
707 self.parameterschanged_render = True | |
708 | |
709 | |
710 | |
711 def callback_update_parameters(self,_): | |
712 self.update_parameters(_) | |
713 | |
714 | |
715 def estimate_SC(self, gc, SR): | |
716 n = arange(0, SR/2+1) | |
717 return sum(n/(1+gc**2-2*gc*cos(2*pi*n/SR)))/sum(1/(1+gc**2-2*gc*cos(2*pi*n/SR))) | |
718 | |
719 | |
720 | |
721 | |
722 def say_hi(self): | |
723 print "Hi, there" | |
724 | |
725 | |
726 | |
727 def callback_save_preset(self): | |
728 print self.presets | |
729 name= tkFileDialog.asksaveasfilename(defaultextension=".pre",filetypes=[("presets",".pre")]) | |
730 print name | |
731 p = Pool() | |
732 p.set('d1',self.scale_d1.get()) | |
733 p.set('g1',self.scale_g1.get()) | |
734 p.set('da',self.scale_da.get()) | |
735 p.set('gc',self.scale_gc.get()) | |
736 p.set('G',self.scale_G.get()) | |
737 y = YamlOutput(filename=name) | |
738 y(p) | |
739 | |
740 | |
741 #f.write(p) | |
742 #f.close() | |
743 self.callback_load_presets() | |
744 | |
745 | |
746 def callback_load_presets(self): | |
747 presets = glob('*.pre') | |
748 | |
749 self.presets = tuple([os.path.splitext(f)[0] for f in presets]) | |
750 | |
751 | |
752 self.drop_presets.destroy() | |
753 | |
754 if len(self.presets) == 0: | |
755 # self.drop_presets = OptionMenu(root, self.var_presets, ('No presets') ,command=self.callback_load_preset) | |
756 pass | |
757 else: | |
758 self.drop_presets = OptionMenu(root, self.var_presets, *tuple(self.presets),command=self.callback_load_preset) | |
759 self.drop_presets.grid(row=0, column=6,sticky=S+W+E) | |
760 | |
761 | |
762 def callback_load_preset(self, preset): | |
763 self.var_presets.set(preset) | |
764 print "loading preset:", preset | |
765 | |
766 p = YamlInput(filename = '%s.pre' % preset)() | |
767 | |
768 self.scale_d1.set(p['d1']) | |
769 self.scale_g1.set(p['g1']) | |
770 self.scale_da.set(p['da']) | |
771 self.scale_gc.set(p['gc']) | |
772 self.scale_G.set(p['G']) | |
773 | |
774 self.callback_update_parameters(None) | |
775 | |
776 self.scale_T60.set(self.T60) | |
777 self.scale_D.set(self.D) | |
778 self.scale_C.set(self.C) | |
779 self.scale_Tc.set(self.Tc) | |
780 self.scale_SC.set(self.SC) | |
781 | |
782 self.plot_impulse() | |
783 | |
784 def callback_plot_impulse(self): | |
785 self.plot_impulse() | |
786 | |
787 def calculate_impulse_response(self): | |
788 self.add_action_to_status('Calculating impulse response') | |
789 N = self.numChannels | |
790 SR = self.SR | |
791 T = 1.0/self.SR | |
792 | |
793 delta = self.impulse[0:int(self.T60*self.SR)] | |
794 | |
795 | |
796 d1 = int(self.d1) | |
797 g1 = self.g1 | |
798 da = int(self.da) | |
799 G = self.G | |
800 gc = self.gc | |
801 | |
802 mt = 0.002 | |
803 m = int(mt*SR) | |
804 | |
805 (ly, ry) = zafar(delta,delta,d1,g1,da,G,gc,m) | |
806 | |
807 limt = 2*self.T60 | |
808 | |
809 lim = int(limt*SR) | |
810 t = arange(0, lim)*T | |
811 | |
812 padded_y = zeros(shape(t)) | |
813 padded_y[0:len(ly)] = ly | |
814 | |
815 | |
816 padded_y = zeros(shape(t)) | |
817 padded_y[0:len(ry)] = ry | |
818 | |
819 ry = padded_y | |
820 | |
821 self.impulse_response_left_channel = ly | |
822 self.impulse_response_right_channel = ry | |
823 | |
824 | |
825 self.remove_action_from_status('Calculating impulse response') | |
826 | |
827 | |
828 | |
829 def plot_impulse(self): | |
830 if self.lastplot != 'impulse': | |
831 self.add_action_to_status('Plotting impulse response') | |
832 N = self.numChannels | |
833 SR = self.SR | |
834 T = 1.0/self.SR | |
835 limt = max(self.T60,1.0) | |
836 | |
837 lim = int(limt*SR) | |
838 delta = self.impulse[0:int(lim)] | |
839 # print "delta:" | |
840 # print delta | |
841 | |
842 d1 = int(self.d1) | |
843 g1 = self.g1 | |
844 da = int(self.da) | |
845 G = self.G | |
846 gc = self.gc | |
847 | |
848 mt = 0.002 | |
849 m = int(mt*SR) | |
850 | |
851 print "Calculating zafar" | |
852 (ly, ry) = zafar(delta,delta,d1,g1,da,G,gc,m) | |
853 | |
854 print "Stopped calculating zafar"#ly.shape | |
855 | |
856 # print "lim:", lim | |
857 | |
858 t = arange(0, lim)*T | |
859 # print t | |
860 | |
861 # Pad ly to t | |
862 # print "Shape ly" | |
863 # print ly | |
864 # print len(ly) | |
865 padded_y = zeros(shape(t)) | |
866 padded_y[0:len(ly)] = ly | |
867 | |
868 print "Padded y" | |
869 #print padded_y | |
870 | |
871 # ly = padded_y | |
872 | |
873 # Pad ry to t | |
874 | |
875 padded_y = zeros(shape(t)) | |
876 padded_y[0:len(ry)] = ry | |
877 | |
878 ry = padded_y | |
879 | |
880 | |
881 | |
882 self.figure.clear() | |
883 | |
884 # print "Passed A" | |
885 subplt0 = self.figure.add_subplot(2,1,1) | |
886 # subplt0.ion() | |
887 | |
888 subplt0.plot(t[1:lim],abs(ly[1:lim])) | |
889 subplt0.set_title('Left Channel') | |
890 subplt0.set_xlabel('time (s)') | |
891 subplt0.set_ylabel('amplitude') | |
892 subplt0.axvspan(self.d1/self.SR,self.d1/self.SR+0.1, alpha=0.1,color='cyan') | |
893 subplt0.axvline(self.Tc, color='red', linestyle='--') | |
894 subplt0.axvline(self.d1/self.SR+0.1, color='cyan', linestyle='--') | |
895 subplt0.axhline(0.001, color='black', linestyle='--') | |
896 subplt0.axvline(self.d1/self.SR, color='cyan', linestyle='--') | |
897 | |
898 subplt0.annotate('Central Time (Tc)', xy=(self.Tc, 0.25), xytext=(self.Tc+0.01, 0.25), arrowprops=dict(facecolor='black',width=1)) | |
899 # subplt0.annotate('Echo Density (D) Measurement Range', xytext=(self.d1/self.SR, 0.62), arrowprops=dict(facecolor='black',width=1)) | |
900 | |
901 # | |
902 | |
903 subplt1 = self.figure.add_subplot(2,1,2,sharex=subplt0) | |
904 subplt1.set_title('Right Channel') | |
905 | |
906 subplt1.plot(t[1:lim],abs(ry[1:lim])) | |
907 subplt1.set_xlabel('time (s)') | |
908 subplt1.set_ylabel('amplitude') | |
909 subplt1.axvspan(self.d1/self.SR,self.d1/self.SR+0.1, alpha=0.1,color='cyan') | |
910 | |
911 subplt1.axvline(self.Tc, color='red', linestyle='--') | |
912 subplt1.axvline(self.d1/self.SR+0.1, color='cyan', linestyle='--') | |
913 subplt1.axvline(self.d1/self.SR, color='cyan', linestyle='--') | |
914 subplt1.axhline(0.001, color='black', linestyle='--') | |
915 | |
916 | |
917 self.figure.suptitle("Reverberation Impulse Response") | |
918 | |
919 # print "Passed B" | |
920 # | |
921 self.remove_action_from_status('Plotting impulse response') | |
922 self.canvas.draw() | |
923 | |
924 self.lastplot = 'impulse' | |
925 # | |
926 # thread.exit_thread() | |
927 | |
928 | |
929 | |
930 def plot_raw(self): | |
931 if self.lastplot != 'raw': | |
932 self.add_action_to_status('Plotting raw') | |
933 N = self.numChannels | |
934 print "Channels: %d" % N | |
935 L = len(self.audio[:,0]) | |
936 | |
937 | |
938 | |
939 | |
940 self.figure.clear() | |
941 | |
942 T = 1.0/self.SR | |
943 t = arange(0, L)*T | |
944 | |
945 oldsubplt = None | |
946 for n in range(0, N): | |
947 if oldsubplt is not None: | |
948 subplt = self.figure.add_subplot(N,1,n+1,sharex=oldsubplt) | |
949 else: | |
950 subplt = self.figure.add_subplot(N,1,n+1) | |
951 subplt.plot(t,self.audio[:,n]) | |
952 subplt.set_title('Channel %d' % n) | |
953 subplt.set_xlabel('time (s)') | |
954 subplt.set_ylabel('amplitude') | |
955 | |
956 oldsubplt = subplt | |
957 | |
958 | |
959 self.figure.suptitle('Raw Signal') | |
960 self.canvas.draw() | |
961 | |
962 self.lastplot = 'raw' | |
963 self.remove_action_from_status('Plotting raw') | |
964 # thread.exit_thread() | |
965 def callback_plot_raw(self): | |
966 try: | |
967 #thread.start_new_thread(self.plot_raw, ()) | |
968 self.plot_raw() | |
969 except: | |
970 print "[EE] Could not start new thread" | |
971 | |
972 | |
973 | |
974 # show() | |
975 | |
976 def plot_reverb(self): | |
977 if self.lastplot != 'reverb': | |
978 self.add_action_to_status('Plotting reverberated signal') | |
979 | |
980 self.calculate_impulse_response() | |
981 ly, ry = self.impulse_response_left_channel, self.impulse_response_right_channel | |
982 | |
983 lx = self.audio[:,0] | |
984 rx = self.audio[:,1] | |
985 | |
986 print "Concolving left channel" | |
987 l_out = fftconvolve(ly, lx) | |
988 | |
989 print "Convolving right channel" | |
990 r_out = fftconvolve(ry, rx) | |
991 | |
992 | |
993 | |
994 lim = min(len(l_out), len(r_out)) | |
995 # N = self.numChannels | |
996 # SR = self.SR | |
997 # T = 1.0/self.SR | |
998 # | |
999 # | |
1000 # d1 = int(self.d1) | |
1001 # g1 = self.g1 | |
1002 # da = int(self.da) | |
1003 # G = self.G | |
1004 # gc = self.gc | |
1005 # | |
1006 # mt = 0.002 | |
1007 # m = int(mt*SR) | |
1008 # | |
1009 # lchannel = ravel(self.audio[:,0]) | |
1010 # rchannel = ravel(self.audio[:,1]) | |
1011 # | |
1012 # print "Calculating zafar" | |
1013 # | |
1014 # if self.parameterschanged_render == True: | |
1015 # (ly, ry) = zafar(lchannel,rchannel,d1,g1,da,G,gc,m) | |
1016 # | |
1017 # self.reverberated_signal_left_channel = ly | |
1018 # self.reverberated_signal_right_channel = ry | |
1019 # | |
1020 # self.parameterschanged_render = 0 | |
1021 # else: | |
1022 # ly = self.reverberated_signal_left_channel | |
1023 # ry = self.reverberated_signal_right_channel | |
1024 # | |
1025 # print "Stopped calculating zafar"#ly.shape | |
1026 # # limt = self.T60 | |
1027 # | |
1028 # lim = int(limt*SR) | |
1029 | |
1030 # lim = len(lchannel) | |
1031 # print "lim:", lim | |
1032 T = 1/self.SR | |
1033 t = arange(0, lim)*T | |
1034 # print t | |
1035 | |
1036 # Pad ly to t | |
1037 # print "Shape ly" | |
1038 ## print ly | |
1039 # print len(ly) | |
1040 # padded_y = zeros(shape(t)) | |
1041 # padded_y[0:len(ly)] = ly | |
1042 | |
1043 # print "Padded y" | |
1044 #print padded_y | |
1045 | |
1046 # ly = padded_y | |
1047 | |
1048 # Pad ry to t | |
1049 | |
1050 # padded_y = zeros(shape(t)) | |
1051 # padded_y[0:len(ry)] = ry | |
1052 | |
1053 # ry = padded_y | |
1054 # | |
1055 | |
1056 | |
1057 self.figure.clear() | |
1058 | |
1059 # print "Passed A" | |
1060 subplt0 = self.figure.add_subplot(2,1,1) | |
1061 | |
1062 subplt0.semilogy(t,l_out[0:lim]) | |
1063 subplt0.set_title('Left Channel') | |
1064 subplt0.set_xlabel('time (s)') | |
1065 subplt0.set_ylabel('amplitude') | |
1066 | |
1067 | |
1068 subplt1 = self.figure.add_subplot(2,1,2,sharex=subplt0) | |
1069 subplt1.set_title('Right Channel') | |
1070 | |
1071 subplt1.semilogy(t,r_out[0:lim]) | |
1072 subplt1.set_xlabel('time (s)') | |
1073 subplt1.set_ylabel('amplitude') | |
1074 | |
1075 self.figure.suptitle("Reverberated Signal") | |
1076 | |
1077 self.remove_action_from_status('Plotting reverberated signal') | |
1078 self.canvas.draw() | |
1079 | |
1080 self.lastplot = 'reverb' | |
1081 # thread.exit_thread() | |
1082 def callback_plot_reverb(self): | |
1083 self.plot_reverb() | |
1084 | |
1085 def callback_play_raw(self): | |
1086 print "[II] Called callback_play_raw" | |
1087 try: | |
1088 self.playerprocess.terminate() | |
1089 except: | |
1090 pass | |
1091 self.play_raw() | |
1092 | |
1093 def callback_play_reverb(self): | |
1094 | |
1095 print "[II] Called callback_play_reverb" | |
1096 try: | |
1097 self.playerprocess.terminate() | |
1098 except: | |
1099 pass | |
1100 | |
1101 self.play_reverb() | |
1102 | |
1103 def callback_stop(self): | |
1104 self.playerprocess.terminate() | |
1105 | |
1106 def callback_save(self): | |
1107 outf = "%s_parameters.yaml" % self.filename.split('.')[0] | |
1108 out = YamlOutput(filename=outf) | |
1109 out(self.pool) | |
1110 print "[II] Parameters Saved" | |
1111 self.saved = True | |
1112 | |
1113 def callback_reset(self): | |
1114 d1 = self.d1 | |
1115 g1 = self.g1 | |
1116 da = self.da | |
1117 G = self.G | |
1118 gc = self.gc | |
1119 | |
1120 self.d1 = self.d1_old | |
1121 self.g1 = self.g1_old | |
1122 self.G = self.G_old | |
1123 self.gc = self.gc_old | |
1124 self.da = self.da_old | |
1125 | |
1126 self.T60 = self.T60_old | |
1127 self.D = self.D_old | |
1128 self.C = self.C_old | |
1129 self.Tc = self.Tc_old | |
1130 self.SC = self.SC_old | |
1131 | |
1132 self.scale_d1.set(self.d1/self.SR) | |
1133 self.scale_g1.set(self.g1) | |
1134 self.scale_da.set(self.da/self.SR) | |
1135 self.scale_G.set(self.G) | |
1136 self.scale_gc.set(self.gc) | |
1137 | |
1138 | |
1139 self.scale_T60.set(self.T60) | |
1140 self.scale_C.set(self.C) | |
1141 self.scale_D.set(self.D) | |
1142 self.scale_Tc.set(self.Tc) | |
1143 self.scale_SC.set(self.SC) | |
1144 | |
1145 self.callback_update_parameters_high(None) | |
1146 | |
1147 | |
1148 def callback_next(self): | |
1149 if self.saved == False: | |
1150 tkMessageBox.showerror("File not saved", "You need to save your changes first") | |
1151 return | |
1152 | |
1153 | |
1154 self.visited_files.append(self.filename) | |
1155 self.sessionpool.add('visited_files', self.filename) | |
1156 self.files_to_visit.pop() | |
1157 self.sessionpool.remove('files_to_visit') | |
1158 for i in self.files_to_visit: | |
1159 self.sessionpool.add('files_to_visit', i) | |
1160 outp = YamlOutput(filename="session.yaml")(self.sessionpool) | |
1161 | |
1162 if len(self.files_to_visit) == 0: | |
1163 tkMessageBox.showinfo("Congratulations!", "You finished the training session!") | |
1164 self.master.destroy() | |
1165 return | |
1166 self.filename = self.files_to_visit[-1] | |
1167 self.load_song(self.filename) | |
1168 | |
1169 | |
1170 | |
1171 | |
1172 if __name__ == "__main__": | |
1173 if len(argv) != 2: | |
1174 print "[EE] Wrong number of arguments" | |
1175 print "[II] Correct syntax is:" | |
1176 print "[II] \t%s <trainingdir>" | |
1177 print "[II] where <trainingdir> contains the segments in .wav format and their corresponding .yaml files" | |
1178 | |
1179 exit(-1) | |
1180 | |
1181 print "[II] Using directory: %s" % argv[1] | |
1182 root = Tk() | |
1183 app = UI(root, argv[1]) | |
1184 root.mainloop() |