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()