1
2
3
4
5
6
7
8
9
10
11 from __future__ import with_statement
12
13 import os
14 import time
15 import wx
16 from wx.lib import masked
17
18 import fileview
19 import guihelper
20 import pubsub
21 import aggregatedisplay
22 import wallpaper
23 import common
24 import fileinfo
25 import conversions
26 import helpids
27 import guihelper
28 import rangedslider
29
30
31
32
33
34 -class DisplayItem(fileview.FileViewDisplayItem):
38
41 CURRENTFILEVERSION=2
42
43
44
45 __publisher=pubsub.Publisher
46 media_notification_type=pubsub.ringtone_type
47 database_key='ringtone-index'
48 media_key='ringtone'
49 default_origin="ringers"
50
51 - def __init__(self, mainwindow, parent, media_root, id=-1):
61
68
75
78
81
84
85 - def OnAdd(self, evt=None):
86 self._raw_media=self._shift_down
87 super(RingerView, self).OnAdd(evt)
88
89 self._shift_down=False
90
92 assert w==self.thumbnail.GetWidth() and h==self.thumbnail.GetHeight()
93 return self.thumbnail
94
123
125 return self.sections[sectionnumber][1]
126
139
141 return sectionheader.itemsize
142
145
148
149 - def ReplaceContents(self, name, origin, new_file_name):
150 """Replace the contents of 'file_name' by the contents of
151 'new_file_name' by going through the image converter dialog
152 """
153 file_stat=os.stat(new_file_name)
154 mtime=file_stat.st_mtime
155 afi=fileinfo.identify_audiofile(new_file_name)
156 if afi.size<=0:
157 return
158 newext,convertinfo=self.mainwindow.phoneprofile.QueryAudio(
159 None, common.getext(new_file_name), afi)
160 if convertinfo is not afi:
161 filedata=None
162 try:
163 filedata=self.ConvertFormat(new_file_name, convertinfo)
164 except:
165 pass
166 if filedata is None:
167 return
168 else:
169 filedata=open(new_file_name, "rb").read()
170
171 max_size=getattr(convertinfo, 'MAXSIZE', None)
172 if max_size is not None and len(filedata)>max_size:
173
174 self.log('ringtone %s is too big!'%common.basename(file))
175 with guihelper.WXDialogWrapper(wx.MessageDialog(self,
176 'Ringtone %s may be too big. Do you want to proceed anway?'%common.basename(file),
177 'Warning',
178 style=wx.YES_NO|wx.ICON_ERROR),
179 True) as (dlg, dlg_resp):
180 if dlg_resp==wx.ID_NO:
181 return
182 self.AddToIndex(name, origin, filedata, self._data, mtime)
183
184 @guihelper.BusyWrapper
186 for file in filenames:
187 if file is None: continue
188 file_stat=os.stat(file)
189 mtime=file_stat.st_mtime
190 if self._raw_media:
191 target=self.get_media_name_from_filename(file)
192 data=open(file, 'rb').read()
193 self.AddToIndex(target, self.active_section, data, self._data, mtime)
194 else:
195
196 afi=fileinfo.identify_audiofile(file)
197 if afi.size<=0: continue
198 newext,convertinfo=self.mainwindow.phoneprofile.QueryAudio(None, common.getext(file), afi)
199 if convertinfo is not afi:
200 filedata=None
201 wx.EndBusyCursor()
202 try:
203 filedata=self.ConvertFormat(file, convertinfo)
204 finally:
205
206 wx.BeginBusyCursor()
207 if filedata is None:
208 continue
209 else:
210 filedata=open(file, "rb").read()
211
212 max_size=getattr(convertinfo, 'MAXSIZE', None)
213 if max_size is not None and len(filedata)>max_size:
214
215 self.log('ringtone %s is too big!'%common.basename(file))
216 with guihelper.WXDialogWrapper(wx.MessageDialog(self,
217 'Ringtone %s may be too big. Do you want to proceed anway?'%common.basename(file),
218 'Warning',
219 style=wx.YES_NO|wx.ICON_ERROR),
220 True) as (dlg, dlg_resp):
221 if dlg_resp==wx.ID_NO:
222 continue
223 target=self.get_media_name_from_filename(file, newext)
224 self.AddToIndex(target, self.active_section, filedata, self._data, mtime)
225 self.OnRefresh()
226
231
233 """Upgrade old data format read from disk
234
235 @param dict: The dict that was read in
236 @param version: version number of the data on disk
237 """
238
239
240 if version==0:
241 version=1
242
243
244 if version==1:
245 print "converting to version 2"
246 version=2
247 d={}
248 input=dict.get(self.database_key, {})
249 for i in input:
250 d[i]={'name': input[i]}
251 dict[self.database_key]=d
252 return dict
253
255
256 ID_CONVERT=wx.NewId()
257 ID_PLAY=wx.NewId()
258 ID_PLAY_CLIP=wx.NewId()
259 ID_STOP=wx.NewId()
260 ID_TIMER=wx.NewId()
261 ID_SLIDER=wx.NewId()
262
263
264
265
266 PARAMETERS={
267 'MP3': {
268 'formats': ["MP3"],
269 'samplerates': ["16000", "22050", "24000", "32000", "44100", "48000"],
270 'channels': ["1", "2"],
271 'bitrates': ["8", "16", "24", "32", "40", "48", "56", "64", "80", "96", "112", "128", "144", "160", "192", "224", "256", "320"],
272 'setup': 'mp3setup',
273 'convert': 'mp3convert',
274 'filelength': 'mp3filelength',
275 'final': 'mp3final',
276 },
277
278 'QCP': {
279 'formats': ["QCP"],
280 'samplerates': ["8000"],
281 'channels': ["1"],
282 'bitrates': ["13000"],
283 'optimization': ['0-Best Sound Quality', '1', '2', '3-Smallest File Size'],
284 'setup': 'qcpsetup',
285 'convert': 'qcpconvert',
286 'filelength': 'qcpfilelength',
287 'final': 'qcpfinal',
288 }
289
290 }
291
292
293
294 - def __init__(self, parent, file, convertinfo):
295 wx.Dialog.__init__(self, parent, title="Convert Audio File", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.SYSTEM_MENU|wx.MAXIMIZE_BOX)
296 self.file=file
297 self.convertinfo=convertinfo
298 self.afi=None
299 self.temporaryfiles=[]
300 self.wavfile=common.gettempfilename("wav")
301 self.clipwavfile=common.gettempfilename("wav")
302 self.temporaryfiles.extend([self.wavfile, self.clipwavfile])
303
304 getattr(self, self.PARAMETERS[convertinfo.format]['setup'])()
305
306 vbs=wx.BoxSizer(wx.VERTICAL)
307
308 self.create_convert_pane(vbs, file, convertinfo)
309
310 self.create_crop_panel(vbs)
311
312 vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALL|wx.ALIGN_RIGHT, 5)
313
314 self.SetSizer(vbs)
315 vbs.Fit(self)
316
317
318 self.FindWindowById(wx.ID_OK).Enable(False)
319 for i in self.cropids:
320 self.FindWindowById(i).Enable(False)
321
322
323
324 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk)
325 wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel)
326 wx.EVT_TIMER(self, self.ID_TIMER, self.OnTimer)
327 wx.EVT_BUTTON(self, wx.ID_HELP, lambda _: wx.GetApp().displayhelpid(helpids.ID_DLG_AUDIOCONVERT))
328
329
330 self.sound=None
331 self.timer=wx.Timer(self, self.ID_TIMER)
332
333
334
335 if guihelper.IsMac():
336 self.zerolenwav=guihelper.getresourcefile("zerolen.wav")
337
338
340 params=self.PARAMETERS[convertinfo.format]
341
342 bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, "Convert"), wx.VERTICAL)
343 bs.Add(wx.StaticText(self, -1, "Input File: "+file), 0, wx.ALL, 5)
344 gs=wx.FlexGridSizer(2, 4, 5, 5)
345 gs.Add(wx.StaticText(self, -1, "New Type"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
346 self.type=wx.ComboBox(self, style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=params['formats'])
347 gs.Add(self.type, 0, wx.ALL|wx.EXPAND, 5)
348 gs.Add(wx.StaticText(self, -1, "Sample Rate (per second)"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
349 self.samplerate=wx.ComboBox(self, style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=params['samplerates'])
350 gs.Add(self.samplerate, 0, wx.ALL|wx.EXPAND, 5)
351 gs.Add(wx.StaticText(self, -1, "Channels (Mono/Stereo)"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
352 self.channels=wx.ComboBox(self, style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=params['channels'])
353 gs.Add(self.channels, 0, wx.ALL|wx.EXPAND, 5)
354 gs.Add(wx.StaticText(self, -1, "Bitrate (kbits per second)"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
355 self.bitrate=wx.ComboBox(self, style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=params['bitrates'])
356 gs.Add(self.bitrate, 0, wx.ALL|wx.EXPAND, 5)
357 if params.has_key('optimization'):
358 gs.Add(wx.StaticText(self, -1, 'Optimization'), 0,
359 wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
360 self.optimization=wx.ComboBox(self,
361 style=wx.CB_DROPDOWN|wx.CB_READONLY,
362 choices=params['optimization'])
363 self.optimization.SetSelection(1)
364 gs.Add(self.optimization, 0, wx.ALL|wx.EXPAND, 5)
365 gs.AddGrowableCol(1, 1)
366 gs.AddGrowableCol(3, 1)
367 bs.Add(gs, 0, wx.EXPAND)
368
369 bs.Add(wx.Button(self, self.ID_CONVERT, "Convert"), 0, wx.ALIGN_RIGHT|wx.ALL, 5)
370
371 vbs.Add(bs, 0, wx.EXPAND|wx.ALL, 5)
372
373 if self.type.GetCount()==1:
374 self.type.SetSelection(0)
375 else:
376 self.type.SetStringSelection(convertinfo.format)
377
378 if self.channels.GetCount()==1:
379 self.channels.SetSelection(0)
380 else:
381 self.channels.SetStringSelection(`convertinfo.channels`)
382
383 if self.bitrate.GetCount()==1:
384 self.bitrate.SetSelection(0)
385 else:
386 self.bitrate.SetStringSelection(`convertinfo.bitrate`)
387
388 if self.samplerate.GetCount()==1:
389 self.samplerate.SetSelection(0)
390 else:
391 self.samplerate.SetStringSelection(`convertinfo.samplerate`)
392
393
394 wx.EVT_BUTTON(self, self.ID_CONVERT, self.OnConvert)
395
397
398 bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, "Crop"), wx.VERTICAL)
399 hbs=wx.BoxSizer(wx.HORIZONTAL)
400 hbs.Add(wx.StaticText(self, -1, "Current Position"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
401 self.positionlabel=wx.StaticText(self, -1, "0 ")
402 hbs.Add(self.positionlabel, 0, wx.ALL, 5)
403 hbs.Add(wx.StaticText(self, -1, "Est. Clip File length"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
404 self.lengthlabel=wx.StaticText(self, -1, "0 ")
405 hbs.Add(self.lengthlabel, 0, wx.ALL, 5)
406 bs.Add(hbs, 0, wx.ALL, 5)
407
408 hbs=wx.GridSizer(-1, 2, 0, 0)
409 hbs.Add(wx.StaticText(self, -1, 'Clip Start (sec):'), 0, wx.EXPAND|wx.ALL, 5)
410 self.clip_start=masked.NumCtrl(self, wx.NewId(), fractionWidth=2)
411 hbs.Add(self.clip_start, 1, wx.EXPAND|wx.ALL, 5)
412 hbs.Add(wx.StaticText(self, -1, 'Clip Duration (sec):'), 0, wx.EXPAND|wx.ALL, 5)
413 self.clip_duration=masked.NumCtrl(self, wx.NewId(), fractionWidth=2)
414 hbs.Add(self.clip_duration, 1, wx.EXPAND|wx.ALL, 5)
415 hbs.Add(wx.StaticText(self, -1, 'Volume Adjustment (dB):'), 0,
416 wx.EXPAND|wx.ALL, 5)
417 self.clip_volume=masked.NumCtrl(self, wx.NewId(), fractionWidth=1)
418 hbs.Add(self.clip_volume, 1, wx.EXPAND|wx.ALL, 5)
419 clip_set_btn=wx.Button(self, wx.NewId(), 'Set')
420 hbs.Add(clip_set_btn, 0, wx.EXPAND|wx.ALL, 5)
421 bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5)
422 hbs=wx.BoxSizer(wx.HORIZONTAL)
423 self.slider=rangedslider.RangedSlider(self, id=self.ID_SLIDER, size=(-1, 30))
424 hbs.Add(self.slider, 1, wx.EXPAND|wx.ALL, 5)
425 bs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5)
426 hbs=wx.BoxSizer(wx.HORIZONTAL)
427 hbs.Add(wx.Button(self, self.ID_STOP, "Stop"), 0, wx.ALL, 5)
428 hbs.Add(wx.Button(self, self.ID_PLAY, "Play Position"), 0, wx.ALL, 5)
429 hbs.Add(wx.Button(self, self.ID_PLAY_CLIP, "Play Clip"), 0, wx.ALL, 5)
430 bs.Add(hbs, 0, wx.ALL|wx.ALIGN_RIGHT, 5)
431 vbs.Add(bs, 0, wx.EXPAND|wx.ALL, 5)
432 wx.EVT_BUTTON(self, self.ID_PLAY, self.OnPlayPosition)
433 wx.EVT_BUTTON(self, self.ID_PLAY_CLIP, self.OnPlayClip)
434 wx.EVT_BUTTON(self, self.ID_STOP, self.OnStop)
435 wx.EVT_BUTTON(self, clip_set_btn.GetId(), self.OnSetClip)
436
437 rangedslider.EVT_POS_CHANGED(self, self.ID_SLIDER, self.OnSliderCurrentChanged)
438 rangedslider.EVT_CHANGING(self, self.ID_SLIDER, self.OnSliderChanging)
439
440 self.cropids=[self.ID_SLIDER, self.ID_STOP, self.ID_PLAY,
441 self.ID_PLAY_CLIP, self.clip_start.GetId(),
442 self.clip_duration.GetId(), self.clip_volume.GetId(),
443 clip_set_btn.GetId()]
444
445 @guihelper.BusyWrapper
447 self.OnStop()
448 for i in self.cropids:
449 self.FindWindowById(i).Enable(False)
450 self.FindWindowById(wx.ID_OK).Enable(False)
451 getattr(self, self.PARAMETERS[self.convertinfo.format]['convert'])()
452 self.wfi=fileinfo.getpcmfileinfo(self.wavfile)
453 max_duration=round(self.wfi.duration, 2)+0.01
454 self.clip_start.SetParameters(min=0.0, max=max_duration, limited=True)
455 self.clip_duration.SetParameters(min=0.0, max=max_duration, limited=True)
456 self.UpdateCrop()
457 for i in self.cropids:
458 self.FindWindowById(i).Enable(True)
459 self.FindWindowById(wx.ID_OK).Enable(True)
460
468
469
470
474
477
478 - def _Play(self, start, end, volume=None):
479 self.OnStop()
480 assert start<=end
481 self.playstart=start
482 self.playend=end
483
484 self.playduration=(self.playend-self.playstart)*self.wfi.duration
485
486 conversions.trimwavfile(self.wavfile, self.clipwavfile,
487 self.playstart*self.wfi.duration,
488 self.playduration, volume)
489 self.sound=wx.Sound(self.clipwavfile)
490 assert self.sound.IsOk()
491 res=self.sound.Play(wx.SOUND_ASYNC)
492 assert res
493 self.starttime=time.time()
494 self.endtime=self.starttime+self.playduration
495 self.timer.Start(100, wx.TIMER_CONTINUOUS)
496
498 now=time.time()
499 if now>self.endtime:
500 self.timer.Stop()
501
502 self.slider.SetCurrent(self.playend)
503 self.UpdateCrop()
504 return
505
506 newval=self.playstart+((now-self.starttime)/(self.endtime-self.starttime))*(self.playend-self.playstart)
507 self.slider.SetCurrent(newval)
508 self.UpdateCrop()
509
510
512 self.timer.Stop()
513 if self.sound is not None:
514 if guihelper.IsMac():
515
516
517 self.sound=None
518 sound=wx.Sound(self.zerolenwav)
519 sound.Play(wx.SOUND_ASYNC)
520 else:
521 self.sound.Stop()
522 self.sound=None
523
527
530
532 for file in self.temporaryfiles:
533 if os.path.exists(file):
534 os.remove(file)
535
536 @guihelper.BusyWrapper
537 - def OnOk(self, evt):
548
553
562
563
564
565
566
572
580
582
583 frames=self.afi.frames
584 self.beginframe=int(self.slider.GetStart()*len(frames))
585 self.endframe=int(self.slider.GetEnd()*len(frames))
586 length=sum([frames[frame].nextoffset-frames[frame].offset for frame in range(self.beginframe, self.endframe)])
587 return length
588
590
591 frames=self.afi.frames
592 offset=frames[self.beginframe].offset
593 length=frames[self.endframe-1].nextoffset-offset
594 with file(self.mp3file, 'rb', 0) as f:
595 f.seek(offset)
596 return f.read(length)
597
610
611 - def mp3final(self, start, duration, volume=None):
617
618
619
620
621
625
636
638
639
640
641
642 return int(duration*1625*1.05)
643
644 - def qcpfinal(self, start, duration, volume=None):
652