comparison training-sessions/dave-ronan/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_min, from_=D_max, 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 self.T60 = T60_min
302 self.D = D_max
303 self.C = C_max
304 self.Tc = (Tc_max-Tc_min)/2.0
305 self.SC = SC_max
306
307
308
309
310 self.label_T60 = Label(master, text="Reverberation Time: ")
311 # self.label_T60.grid(row=2,column=6,sticky=N+S+E+W)
312 self.label_D = Label(master, text="Echo Density: ")
313 # self.label_D.grid(row=3,column=6,sticky=N+S+E+W)
314 self.label_C = Label(master, text="Clarity: ")
315 # self.label_C.grid(row=4,column=6,sticky=N+S+E+W)
316 self.label_Tc = Label(master, text="Central Time: ")
317 # self.label_Tc.grid(row=5,column=6,sticky=N+S+E+W)
318 self.label_SC = Label(master, text="Spectral Centroid: ")
319 # self.label_SC.grid(row=6,column=6,sticky=N+S+E+W)
320
321
322 self.label_d1 = Label(master, text="d_1: ")
323 # self.label_d1.grid(row=7, column=6, sticky=N+S+E+W)
324
325 self.label_g1 = Label(master, text="g_1: ")
326 # self.label_g1.grid(row=8, column=6, sticky=N+S+E+W)
327
328 self.label_da = Label(master, text="d_a: ")
329 # self.label_da.grid(row=9, column=6, sticky=N+S+E+W)
330
331 self.label_gc = Label(master, text="gc: ")
332 # self.label_gc.grid(row=10, column=6, sticky=N+S+E+W)
333
334 self.label_G = Label(master, text="G: ")
335 # self.label_G.grid(row=11, column=6, sticky=N+S+E+W)
336
337 # Buttons
338
339 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)
340 # 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)
341 # 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)
342 self.button_play_raw = Button(master, text="Play Raw", bg="green", fg="white", width=15, command=self.callback_play_raw).grid(row=18, column=6,sticky=N+S+E+W)
343 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)
344 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)
345 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)
346 self.button_reset = Button(master, text="Undo", width=15, command=self.callback_reset).grid(row=22, column=6,sticky=N+S+E+W)
347 self.button_next = Button(master, text="Next >>", width=15, command=self.callback_next).grid(row=23, column=6,sticky=N+S+E+W)
348
349
350
351 # Figure
352
353 self.figure = Figure( dpi=50)
354
355 self.figure.text(0.5,0.5,'No plot selected', weight = "bold", horizontalalignment='center')
356
357
358 self.canvas = FigureCanvasTkAgg(self.figure, master=root)
359 self.canvas.show()
360 self.canvas.get_tk_widget().grid(row=0, column=7, rowspan=17, padx=20,sticky=E+W+N+S)
361 self.canvas._tkcanvas.grid(row=0, column=7, rowspan=23)
362
363 # Toolbar for canvas
364
365 self.toolbar_frame = Frame(master)
366 self.toolbar_frame.grid(row=23,column=7,sticky=E+W+N+S, padx=19)
367 self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.toolbar_frame)
368
369
370
371 Grid.columnconfigure(master, 7, weight=1)
372 Grid.rowconfigure(master, 1, weight=1)
373
374 # Status bar
375
376 self.status_bar_text = Label(text="Ready.")
377 self.status_bar_text.grid(row=18, column=0, columnspan = 8, sticky=N+S+E, padx=10)
378
379 self.lastplot = ''
380 self.parameterschanged_render = True
381 self.pendingactions = []
382
383 # Initial values
384 d1t = 0.05
385 self.d1 = d1t*self.SR
386 dat = 0.012
387 self.da = dat*self.SR
388 g1 = 0.5
389 self.g1 = g1
390 G = 0.5
391 self.G = G
392 gc = 0.01
393 self.gc = gc
394
395
396 # self.scale_d1.set(d1t)
397 # self.scale_da.set(dat)
398 # self.scale_g1.set(g1)
399 # self.scale_gc.set(gc)
400 # self.scale_G.set(G)
401
402 t = zeros((self.SR*120,))
403 t[0] = 1
404
405 self.impulse = t
406
407
408
409 # Presets
410 self.presets=[]
411 self.var_presets = StringVar()
412 self.drop_presets = OptionMenu(master, self.var_presets, self.presets, command=self.callback_load_preset)
413 self.drop_presets.grid(row=0, column=6, sticky=N+S+E+W)
414 self.callback_load_presets()
415
416
417 # Preset options
418 self.button_save_preset = Button(master, text="Save Preset", command=self.callback_save_preset)
419 self.button_save_preset.grid(row=1, column=6, sticky=N+E+W)
420
421 # Pool
422 self.pool = Pool()
423 self.pool.set('filename', self.filename)
424 self.pool.set('sampleRate', self.SR)
425 self.pool.set('nChannels', self.numChannels)
426
427
428
429
430 # Finally
431 self.callback_update_parameters_high(None)
432 self.plot_impulse()
433
434
435
436
437
438 def pyaudio_callback_raw(self, in_data, frame_count, time_info, status):
439 if self.player_command == 'Stop':
440 return (0, pyaudio.paAbort)
441
442 data = self.audio[self.player_idx:self.player_idx+frame_count, :]
443
444 data = reshape(data, (data.shape[0]*data.shape[1], 1))
445
446 # print data
447 self.player_idx += frame_count
448
449
450 return (data, pyaudio.paContinue)
451
452
453 def play_reverb(self):
454
455 self.calculate_impulse_response()
456 ly, ry = self.impulse_response_left_channel, self.impulse_response_right_channel
457
458 lx = self.audio[:,0]
459 rx = self.audio[:,1]
460
461 print "Convolving left channel"
462 l_out = fftconvolve(ly, lx)
463
464 if max(l_out) > 1.0:
465 l_out = l_out/max(l_out)
466
467 # l_out = l_out/max(l_out)
468
469 print "Convolving right channel"
470 r_out = fftconvolve(ry, rx)
471
472 if max(r_out) > 1.0:
473 r_out = r_out/max(r_out)
474 # r_out = r_out/max(r_out)
475
476
477
478 lim = min(len(l_out), len(r_out))
479
480
481
482 if self.numChannels == 1:
483 audio_out = l_out[0:lim]
484 else:
485 audio_out = concatenate((matrix(l_out[0:lim]).T,
486 matrix(r_out[0:lim]).T
487 ),
488 axis=1)
489
490 reverb_filename = "%s_reverb_%s" % (self.filename.split('.')[0], self.filename.split('.')[1])
491
492 audio_file = Sndfile(reverb_filename, 'w', Format(self.filename.split('.')[1]), self.numChannels, self.SR)
493 audio_file.write_frames(audio_out)
494 audio_file.close()
495
496 self.reverberated_audio = audio_out
497
498 self.reverb_filename = reverb_filename
499
500 self.playerprocess = subprocess.Popen("mplayer %s" % reverb_filename,
501 stdout = subprocess.PIPE,
502 shell=True,
503 preexec_fn=os.setsid)
504
505 def play_raw(self):
506 self.playerprocess = subprocess.Popen("mplayer %s" % self.filename,
507 stdout = subprocess.PIPE,
508 shell=True,
509 preexec_fn=os.setsid)
510
511
512
513
514
515 def remove_action_from_status(self, text):
516
517 self.pendingactions.remove(text)
518
519 if len(self.pendingactions) == 0:
520 self.status_bar_text.config(text='Ready.')
521 elif len(self.pendingactions) == 1:
522 self.status_bar_text.config(text=self.pendingactions[0]+'.')
523 else:
524 self.status_bar_text.config(text=reduce(lambda h,t: h+','+t, self.pendingactions)+'.')
525
526
527 def add_action_to_status(self, text):
528
529 self.pendingactions.append(text)
530
531 if len(self.pendingactions) == 0:
532 self.status_bar_text.config(text='Ready.')
533 elif len(self.pendingactions) == 1:
534 self.status_bar_text.config(text=text+'.')
535 else:
536 self.status_bar_text.config(text=reduce(lambda h,t: h+', '+t, self.pendingactions)+'.')
537
538 print self.pendingactions, len(self.pendingactions)
539
540
541 def load_song(self, filename):
542 tup = AudioLoader(filename=filename)()
543 self.audio = tup[0]
544 self.SR = tup[1]
545 global SC_max
546 SC_max = self.SR/4.0
547 self.numChannels = tup[2]
548 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)
549 self.saved = False
550
551
552
553
554 def estimate_T60(self, d1, g1, gc, G, SR):
555 ga = 1/sqrt(2)
556 return d1/SR/log(g1)*log(10**-3/ga/(1-gc)/G)
557
558 def calculate_parameters(self,d1,g1):
559
560 d2 = int(round((1.5)**(-1)*d1))
561
562 while gcd(d2,d1) != 1:
563 d2 += 1
564
565 d3 = int(round((1.5)**(-2)*d1))
566
567 while gcd(d3, d2) != 1 or gcd(d3, d1) != 1:
568 d3 += 1
569
570 d4 = int(round((1.5)**(-3)*d1))
571
572 while gcd(d4, d3) != 1 or gcd(d4, d2) != 1 or gcd(d4, d1) != 1:
573 d4 += 1
574
575
576 d5 = int(round((1.5)**(-4)*d1))
577
578 while gcd(d5, d4) != 1 or gcd(d5, d3) != 1 or gcd(d5, d2) != 1 or gcd(d5, d1) != 1:
579 d5 += 1
580
581 d6 = int(round((1.5)**(-5)*d1))
582 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:
583 d6 += 1
584 g2 = g1**(1.5)**(-1)*g1
585 g3 = g1**(1.5)**(-2)*g1
586 g4 = g1**(1.5)**(-3)*g1
587 g5 = g1**(1.5)**(-4)*g1
588 g6 = g1**(1.5)**(-5)*g1
589
590 return (d1, d2, d3, d4, d5, d6, g1, g2, g3, g4, g5, g6)
591 def estimate_C(self, g1, G, gc):
592 g2 = g1**(1.5)**(-1)*g1
593 g3 = g1**(1.5)**(-2)*g1
594 g4 = g1**(1.5)**(-3)*g1
595 g5 = g1**(1.5)**(-4)*g1
596 g6 = g1**(1.5)**(-5)*g1
597 gains = zeros((6,1))
598 gains[0] = g1
599 gains[1] = g2
600 gains[2] = g3
601 gains[3] = g4
602 gains[4] = g5
603 gains[5] = g6
604
605 return -10*log10(G**2*(1-gc)/(1+gc)*sum(1/(1-gains**2)))
606
607
608 def estimate_D(self, d1, g1, da, SR):
609 kS = 20.78125
610
611
612 return kS*0.1/d1/da*self.SR**2
613
614 def estimate_Tc(self, d1, g1, da, SR):
615 delays = zeros((6,))
616 gains = zeros((6,1))
617 (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)
618 return sum(delays/SR*gains**2/(1-gains**2)**2)/sum(gains**2/(1-gains**2)) + da/SR
619
620 def update_parameters_high(self, _):
621
622 self.T60_old = self.T60
623 self.D_old = self.D
624 self.C_old = self.C
625 self.Tc_old = self.Tc
626 self.SC_old = self.SC
627
628 T60 = self.scale_T60.get()
629 D = self.scale_D.get()
630 C = self.scale_C.get()
631 Tc = self.scale_Tc.get()
632 SC = self.scale_SC.get()
633 print self.SR
634 print (T60, D, C, Tc, SC)
635 (d1t, dat, g1, gc, G) = inverse_mapping(T60,D,C,Tc,SC,SR=self.SR)
636
637 print "da",(d1t, dat, g1, gc, G)
638
639 self.scale_d1.set(d1t)
640 self.scale_da.set(dat)
641 self.scale_g1.set(g1)
642 self.scale_gc.set(gc)
643 self.scale_G.set(G)
644 self.parameterschanged_render = True
645
646 def callback_update_parameters_high(self, _):
647 print("callback_update_parameters_high")
648 self.update_parameters_high(_)
649
650
651 self.update_parameters(_)
652
653 self.callback_plot_impulse()
654
655 def update_parameters(self, _):
656 SR = self.SR
657 d1t = self.scale_d1.get()
658
659 d1 = round(d1t*SR)
660 g1 = self.scale_g1.get()
661 dat = self.scale_da.get()
662 da = round(dat*SR)
663 G = self.scale_G.get()
664 gc = self.scale_gc.get()
665
666
667 T60 = self.estimate_T60(d1,g1,gc,G,SR)
668 D = self.estimate_D(d1, g1, da, SR)
669 C = self.estimate_C(g1, G, gc)
670 Tc = self.estimate_Tc(d1,g1,da,SR)
671 SC = self.estimate_SC(gc, SR)
672
673
674 self.d1_old = self.d1
675 self.G_old = self.G
676 self.gc_old = self.gc
677 self.g1_old = self.g1
678 self.da_old = self.da
679
680 self.d1 = d1
681 self.G = G
682 self.gc = gc
683 self.g1 = g1
684 self.da = da
685
686
687
688 self.pool.set('parameters.d1', d1t)
689 self.pool.set('parameters.G', G)
690 self.pool.set('parameters.gc', gc)
691 self.pool.set('parameters.g1', g1)
692 self.pool.set('parameters.da', dat)
693
694
695
696 self.T60 = T60
697 self.D = D
698 self.Tc = Tc
699 self.SC = SC
700 self.C = C
701
702 self.pool.set('parameters.T60', T60)
703 self.pool.set('parameters.D', D)
704 self.pool.set('parameters.C', C)
705 self.pool.set('parameters.Tc', Tc)
706 self.pool.set('parameters.SC', SC)
707
708 self.label_T60.config(text="Reverberation Time: %.3fs" % T60)
709 self.label_D.config(text="Echo Density: %.3f at 0.1s" % D)
710 self.label_C.config(text="Clarity: %.3f dB" % C)
711 self.label_Tc.config(text="Central Time: %.3fs" % Tc)
712 self.label_SC.config(text="Spectral Centroid: %.3f Hz" % SC)
713
714 self.label_d1.config(text="d_1: %.3fs" % d1t)
715 self.label_g1.config(text="g_1: %.3f" % g1)
716 self.label_da.config(text="d_a: %.3fs" % dat)
717 self.label_gc.config(text="g_c: %.3f" % gc)
718 self.label_G.config(text="G: %.3f" % G)
719 self.lastplot = ''
720
721
722 self.parameterschanged_render = True
723
724
725
726 def callback_update_parameters(self,_):
727 self.update_parameters(_)
728
729
730 def estimate_SC(self, gc, SR):
731 n = arange(0, SR/2+1)
732 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)))
733
734
735
736
737 def say_hi(self):
738 print "Hi, there"
739
740
741
742 def callback_save_preset(self):
743 print self.presets
744 name= tkFileDialog.asksaveasfilename(defaultextension=".pre",filetypes=[("presets",".pre")])
745 print name
746 p = Pool()
747 p.set('d1',self.scale_d1.get())
748 p.set('g1',self.scale_g1.get())
749 p.set('da',self.scale_da.get())
750 p.set('gc',self.scale_gc.get())
751 p.set('G',self.scale_G.get())
752 y = YamlOutput(filename=name)
753 y(p)
754
755 self.callback_load_presets()
756
757 #f.write(p)
758 #f.close()
759
760
761 def callback_load_presets(self):
762 presets = glob('*.pre')
763
764 self.presets = tuple([os.path.splitext(f)[0] for f in presets])
765
766
767 self.drop_presets.destroy()
768
769 if len(self.presets) == 0:
770 # self.drop_presets = OptionMenu(root, self.var_presets, ('No presets') ,command=self.callback_load_preset)
771 pass
772 else:
773 self.drop_presets = OptionMenu(root, self.var_presets, *tuple(self.presets),command=self.callback_load_preset)
774 self.drop_presets.grid(row=0, column=6,sticky=S+W+E)
775
776
777 def callback_load_preset(self, preset):
778 self.var_presets.set(preset)
779 print "loading preset:", preset
780
781 p = YamlInput(filename = '%s.pre' % preset)()
782
783 self.scale_d1.set(p['d1'])
784 self.scale_g1.set(p['g1'])
785 self.scale_da.set(p['da'])
786 self.scale_gc.set(p['gc'])
787 self.scale_G.set(p['G'])
788
789 self.callback_update_parameters(None)
790
791 self.scale_T60.set(self.T60)
792 self.scale_D.set(self.D)
793 self.scale_C.set(self.C)
794 self.scale_Tc.set(self.Tc)
795 self.scale_SC.set(self.SC)
796
797 self.plot_impulse()
798
799 def callback_plot_impulse(self):
800 self.plot_impulse()
801
802 def calculate_impulse_response(self):
803 self.add_action_to_status('Calculating impulse response')
804 N = self.numChannels
805 SR = self.SR
806 T = 1.0/self.SR
807
808 delta = self.impulse[0:int(self.T60*self.SR)]
809
810
811 d1 = int(self.d1)
812 g1 = self.g1
813 da = int(self.da)
814 G = self.G
815 gc = self.gc
816
817 mt = 0.002
818 m = int(mt*SR)
819
820 (ly, ry) = zafar(delta,delta,d1,g1,da,G,gc,m)
821
822 limt = 2*self.T60
823
824 lim = int(limt*SR)
825 t = arange(0, lim)*T
826
827 padded_y = zeros(shape(t))
828 padded_y[0:len(ly)] = ly
829
830
831 padded_y = zeros(shape(t))
832 padded_y[0:len(ry)] = ry
833
834 ry = padded_y
835
836 self.impulse_response_left_channel = ly
837 self.impulse_response_right_channel = ry
838
839
840 self.remove_action_from_status('Calculating impulse response')
841
842
843
844 def plot_impulse(self):
845 if self.lastplot != 'impulse':
846 self.add_action_to_status('Plotting impulse response')
847 N = self.numChannels
848 SR = self.SR
849 T = 1.0/self.SR
850 limt = max(self.T60,1.0)
851
852 lim = int(limt*SR)
853 delta = self.impulse[0:int(lim)]
854 # print "delta:"
855 # print delta
856
857 d1 = int(self.d1)
858 g1 = self.g1
859 da = int(self.da)
860 G = self.G
861 gc = self.gc
862
863 mt = 0.002
864 m = int(mt*SR)
865
866 print "Calculating zafar"
867 (ly, ry) = zafar(delta,delta,d1,g1,da,G,gc,m)
868
869 print "Stopped calculating zafar"#ly.shape
870
871 # print "lim:", lim
872
873 t = arange(0, lim)*T
874 # print t
875
876 # Pad ly to t
877 # print "Shape ly"
878 # print ly
879 # print len(ly)
880 padded_y = zeros(shape(t))
881 padded_y[0:len(ly)] = ly
882
883 print "Padded y"
884 #print padded_y
885
886 # ly = padded_y
887
888 # Pad ry to t
889
890 padded_y = zeros(shape(t))
891 padded_y[0:len(ry)] = ry
892
893 ry = padded_y
894
895
896
897 self.figure.clear()
898
899 # print "Passed A"
900 subplt0 = self.figure.add_subplot(2,1,1)
901 # subplt0.ion()
902
903 subplt0.plot(t[1:lim],abs(ly[1:lim]))
904 subplt0.set_title('Left Channel')
905 subplt0.set_xlabel('time (s)')
906 subplt0.set_ylabel('amplitude')
907 subplt0.axvspan(self.d1/self.SR,self.d1/self.SR+0.1, alpha=0.1,color='cyan')
908 subplt0.axvline(self.Tc, color='red', linestyle='--')
909 subplt0.axvline(self.d1/self.SR+0.1, color='cyan', linestyle='--')
910 subplt0.axhline(0.001, color='black', linestyle='--')
911 subplt0.axvline(self.d1/self.SR, color='cyan', linestyle='--')
912
913 subplt0.annotate('Central Time (Tc)', xy=(self.Tc, 0.25), xytext=(self.Tc+0.01, 0.25), arrowprops=dict(facecolor='black',width=1))
914 # subplt0.annotate('Echo Density (D) Measurement Range', xytext=(self.d1/self.SR, 0.62), arrowprops=dict(facecolor='black',width=1))
915
916 #
917
918 subplt1 = self.figure.add_subplot(2,1,2,sharex=subplt0)
919 subplt1.set_title('Right Channel')
920
921 subplt1.plot(t[1:lim],abs(ry[1:lim]))
922 subplt1.set_xlabel('time (s)')
923 subplt1.set_ylabel('amplitude')
924 subplt1.axvspan(self.d1/self.SR,self.d1/self.SR+0.1, alpha=0.1,color='cyan')
925
926 subplt1.axvline(self.Tc, color='red', linestyle='--')
927 subplt1.axvline(self.d1/self.SR+0.1, color='cyan', linestyle='--')
928 subplt1.axvline(self.d1/self.SR, color='cyan', linestyle='--')
929 subplt1.axhline(0.001, color='black', linestyle='--')
930
931
932 self.figure.suptitle("Reverberation Impulse Response")
933
934 # print "Passed B"
935 #
936 self.remove_action_from_status('Plotting impulse response')
937 self.canvas.draw()
938
939 self.lastplot = 'impulse'
940 #
941 # thread.exit_thread()
942
943
944
945 def plot_raw(self):
946 if self.lastplot != 'raw':
947 self.add_action_to_status('Plotting raw')
948 N = self.numChannels
949 print "Channels: %d" % N
950 L = len(self.audio[:,0])
951
952
953
954
955 self.figure.clear()
956
957 T = 1.0/self.SR
958 t = arange(0, L)*T
959
960 oldsubplt = None
961 for n in range(0, N):
962 if oldsubplt is not None:
963 subplt = self.figure.add_subplot(N,1,n+1,sharex=oldsubplt)
964 else:
965 subplt = self.figure.add_subplot(N,1,n+1)
966 subplt.plot(t,self.audio[:,n])
967 subplt.set_title('Channel %d' % n)
968 subplt.set_xlabel('time (s)')
969 subplt.set_ylabel('amplitude')
970
971 oldsubplt = subplt
972
973
974 self.figure.suptitle('Raw Signal')
975 self.canvas.draw()
976
977 self.lastplot = 'raw'
978 self.remove_action_from_status('Plotting raw')
979 # thread.exit_thread()
980 def callback_plot_raw(self):
981 try:
982 #thread.start_new_thread(self.plot_raw, ())
983 self.plot_raw()
984 except:
985 print "[EE] Could not start new thread"
986
987
988
989 # show()
990
991 def plot_reverb(self):
992 if self.lastplot != 'reverb':
993 self.add_action_to_status('Plotting reverberated signal')
994
995 self.calculate_impulse_response()
996 ly, ry = self.impulse_response_left_channel, self.impulse_response_right_channel
997
998 lx = self.audio[:,0]
999 rx = self.audio[:,1]
1000
1001 print "Concolving left channel"
1002 l_out = fftconvolve(ly, lx)
1003
1004 print "Convolving right channel"
1005 r_out = fftconvolve(ry, rx)
1006
1007
1008
1009 lim = min(len(l_out), len(r_out))
1010 # N = self.numChannels
1011 # SR = self.SR
1012 # T = 1.0/self.SR
1013 #
1014 #
1015 # d1 = int(self.d1)
1016 # g1 = self.g1
1017 # da = int(self.da)
1018 # G = self.G
1019 # gc = self.gc
1020 #
1021 # mt = 0.002
1022 # m = int(mt*SR)
1023 #
1024 # lchannel = ravel(self.audio[:,0])
1025 # rchannel = ravel(self.audio[:,1])
1026 #
1027 # print "Calculating zafar"
1028 #
1029 # if self.parameterschanged_render == True:
1030 # (ly, ry) = zafar(lchannel,rchannel,d1,g1,da,G,gc,m)
1031 #
1032 # self.reverberated_signal_left_channel = ly
1033 # self.reverberated_signal_right_channel = ry
1034 #
1035 # self.parameterschanged_render = 0
1036 # else:
1037 # ly = self.reverberated_signal_left_channel
1038 # ry = self.reverberated_signal_right_channel
1039 #
1040 # print "Stopped calculating zafar"#ly.shape
1041 # # limt = self.T60
1042 #
1043 # lim = int(limt*SR)
1044
1045 # lim = len(lchannel)
1046 # print "lim:", lim
1047 T = 1/self.SR
1048 t = arange(0, lim)*T
1049 # print t
1050
1051 # Pad ly to t
1052 # print "Shape ly"
1053 ## print ly
1054 # print len(ly)
1055 # padded_y = zeros(shape(t))
1056 # padded_y[0:len(ly)] = ly
1057
1058 # print "Padded y"
1059 #print padded_y
1060
1061 # ly = padded_y
1062
1063 # Pad ry to t
1064
1065 # padded_y = zeros(shape(t))
1066 # padded_y[0:len(ry)] = ry
1067
1068 # ry = padded_y
1069 #
1070
1071
1072 self.figure.clear()
1073
1074 # print "Passed A"
1075 subplt0 = self.figure.add_subplot(2,1,1)
1076
1077 subplt0.semilogy(t,l_out[0:lim])
1078 subplt0.set_title('Left Channel')
1079 subplt0.set_xlabel('time (s)')
1080 subplt0.set_ylabel('amplitude')
1081
1082
1083 subplt1 = self.figure.add_subplot(2,1,2,sharex=subplt0)
1084 subplt1.set_title('Right Channel')
1085
1086 subplt1.semilogy(t,r_out[0:lim])
1087 subplt1.set_xlabel('time (s)')
1088 subplt1.set_ylabel('amplitude')
1089
1090 self.figure.suptitle("Reverberated Signal")
1091
1092 self.remove_action_from_status('Plotting reverberated signal')
1093 self.canvas.draw()
1094
1095 self.lastplot = 'reverb'
1096 # thread.exit_thread()
1097 def callback_plot_reverb(self):
1098 self.plot_reverb()
1099
1100 def callback_play_raw(self):
1101 print "[II] Called callback_play_raw"
1102 try:
1103 self.playerprocess.terminate()
1104 except:
1105 pass
1106 self.play_raw()
1107
1108 def callback_play_reverb(self):
1109
1110 print "[II] Called callback_play_reverb"
1111 try:
1112 self.playerprocess.terminate()
1113 except:
1114 pass
1115
1116 self.play_reverb()
1117
1118 def callback_stop(self):
1119 self.playerprocess.terminate()
1120
1121 def callback_save(self):
1122 outf = "%s_parameters.yaml" % self.filename.split('.')[0]
1123 outr = "%s_original_reverb.wav.2" % self.filename.split('.')[0]
1124 out = YamlOutput(filename=outf)
1125 out(self.pool)
1126 print "[II] Parameters Saved"
1127
1128 self.calculate_impulse_response()
1129 ly, ry = self.impulse_response_left_channel, self.impulse_response_right_channel
1130
1131 lx = self.audio[:,0]
1132 rx = self.audio[:,1]
1133
1134 print "Convolving left channel"
1135 l_out = fftconvolve(ly, lx)
1136 #l_out = l_out/max(l_out)
1137 if max(l_out) > 1.0:
1138 l_out = l_out/max(l_out)
1139
1140 print "Convolving right channel"
1141 r_out = fftconvolve(ry, rx)
1142
1143 if max(r_out) > 1.0:
1144 r_out = r_out/max(r_out)
1145 #r_out = r_out/max(r_out)
1146
1147 # print "maxrout", max(r_out)
1148
1149
1150
1151 lim = min(len(l_out), len(r_out))
1152
1153
1154
1155 if self.numChannels == 1:
1156 audio_out = l_out[0:lim]
1157 else:
1158 audio_out = concatenate((matrix(l_out[0:lim]).T,
1159 matrix(r_out[0:lim]).T
1160 ),
1161 axis=1)
1162
1163 audio_file = Sndfile(outr, 'w', Format(self.filename.split('.')[1]), self.numChannels, self.SR)
1164 audio_file.write_frames(audio_out)
1165 audio_file.close()
1166
1167 print "[II] File %s saved" % outr
1168
1169
1170
1171 self.saved = True
1172
1173 def callback_reset(self):
1174 d1 = self.d1
1175 g1 = self.g1
1176 da = self.da
1177 G = self.G
1178 gc = self.gc
1179
1180 self.d1 = self.d1_old
1181 self.g1 = self.g1_old
1182 self.G = self.G_old
1183 self.gc = self.gc_old
1184 self.da = self.da_old
1185
1186 self.T60 = self.T60_old
1187 self.D = self.D_old
1188 self.C = self.C_old
1189 self.Tc = self.Tc_old
1190 self.SC = self.SC_old
1191
1192 self.scale_d1.set(self.d1/self.SR)
1193 self.scale_g1.set(self.g1)
1194 self.scale_da.set(self.da/self.SR)
1195 self.scale_G.set(self.G)
1196 self.scale_gc.set(self.gc)
1197
1198
1199 self.scale_T60.set(self.T60)
1200 self.scale_C.set(self.C)
1201 self.scale_D.set(self.D)
1202 self.scale_Tc.set(self.Tc)
1203 self.scale_SC.set(self.SC)
1204
1205 self.callback_update_parameters_high(None)
1206
1207
1208 def callback_next(self):
1209 if self.saved == False:
1210 tkMessageBox.showerror("File not saved", "You need to save your changes first")
1211 return
1212
1213
1214 self.visited_files.append(self.filename)
1215 self.sessionpool.add('visited_files', self.filename)
1216 self.files_to_visit.pop()
1217 self.sessionpool.remove('files_to_visit')
1218 for i in self.files_to_visit:
1219 self.sessionpool.add('files_to_visit', i)
1220 outp = YamlOutput(filename="session.yaml")(self.sessionpool)
1221
1222 if len(self.files_to_visit) == 0:
1223 tkMessageBox.showinfo("Congratulations!", "You finished the training session!")
1224 self.master.destroy()
1225 return
1226 self.filename = self.files_to_visit[-1]
1227 self.load_song(self.filename)
1228
1229
1230
1231
1232 if __name__ == "__main__":
1233 if len(argv) != 2:
1234 print "[EE] Wrong number of arguments"
1235 print "[II] Correct syntax is:"
1236 print "[II] \t%s <trainingdir>"
1237 print "[II] where <trainingdir> contains the segments in .wav format and their corresponding .yaml files"
1238
1239 exit(-1)
1240
1241 print "[II] Using directory: %s" % argv[1]
1242 root = Tk()
1243 app = UI(root, argv[1])
1244 root.mainloop()