Module ringers
[hide private]
[frames] | no frames]

Source Code for Module ringers

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 
  4  ### Copyright (C) 2003-2004 Steven Palm <n9yty@n9yty.com> 
  5  ### 
  6  ### This program is free software; you can redistribute it and/or modify 
  7  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  8  ### 
  9  ### $Id: ringers.py 4379 2007-08-28 03:06:58Z djpham $ 
 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 ### Ringers 32 ### 33 34 -class DisplayItem(fileview.FileViewDisplayItem):
35 36 datakey='ringtone-index' 37 datatype='Audio' # used in the tooltip
38
39 40 -class RingerView(fileview.FileView):
41 CURRENTFILEVERSION=2 42 43 # this is only used to prevent the pubsub module 44 # from being GC while any instance of this class exists 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):
52 self.mainwindow=mainwindow 53 self._data={self.database_key: {}} 54 fileview.FileView.__init__(self, mainwindow, parent, media_root, "ringtone-watermark") 55 self.wildcard="Audio files|*.wav;*.mid;*.qcp;*.mp3;*.pmd;*.wma|Midi files|*.mid|Purevoice files|*.qcp|MP3 files|*.mp3|WMA files|*.wma|PMD/CMX files|*.pmd|All files|*.*" 56 self.thedir=self.mainwindow.ringerpath 57 self.modified=False 58 pubsub.subscribe(self.OnListRequest, pubsub.REQUEST_RINGTONES) 59 pubsub.subscribe(self.OnDictRequest, pubsub.REQUEST_RINGTONE_INDEX) 60 self._raw_media=self._shift_down=False
61
62 - def updateprofilevariables(self, profile):
68
69 - def OnListRequest(self, msg=None):
70 l=[self._data[self.database_key][x].name \ 71 for x in self._data[self.database_key] \ 72 if self._data[self.database_key][x].origin not in self.excluded_origins ] 73 l.sort() 74 pubsub.publish(pubsub.ALL_RINGTONES, l)
75
76 - def OnDictRequest(self, msg=None):
78
79 - def GetDeleteInfo(self):
80 return guihelper.ART_DEL_RINGER, "Delete Ringer"
81
82 - def GetAddInfo(self):
83 return guihelper.ART_ADD_RINGER, "Add Ringer"
84
85 - def OnAdd(self, evt=None):
86 self._raw_media=self._shift_down 87 super(RingerView, self).OnAdd(evt) 88 # reset the fla 89 self._shift_down=False
90
91 - def GetItemThumbnail(self, item, w, h):
92 assert w==self.thumbnail.GetWidth() and h==self.thumbnail.GetHeight() 93 return self.thumbnail
94
95 - def GetSections(self):
96 # work out section and item sizes 97 self.thumbnail=wx.Image(guihelper.getresourcefile('ringer.png')).ConvertToBitmap() 98 99 dc=wx.MemoryDC() 100 dc.SelectObject(wx.EmptyBitmap(100,100)) # unused bitmap needed to keep wxMac happy 101 h=dc.GetTextExtent("I")[1] 102 itemsize=self.thumbnail.GetWidth()+160, max(self.thumbnail.GetHeight(), h*4+DisplayItem.PADDING)+DisplayItem.PADDING*2 103 104 # get all the items 105 items=[DisplayItem(self, key) for key in self._data[self.database_key] if self._data[self.database_key][key].mediadata!=None] 106 107 self.sections=[] 108 109 if len(items)==0: 110 return self.sections 111 112 # get the current sorting type 113 for sectionlabel, items in self.organizeby_Origin(items): 114 self.media_root.AddMediaNode(sectionlabel, self) 115 sh=aggregatedisplay.SectionHeader(sectionlabel) 116 sh.itemsize=itemsize 117 for item in items: 118 item.thumbnailsize=self.thumbnail.GetWidth(), self.thumbnail.GetHeight() 119 # sort items by name 120 items.sort(self.CompareItems) 121 self.sections.append( (sh, items) ) 122 return [sh for sh,items in self.sections]
123
124 - def GetItemsFromSection(self, sectionnumber, sectionheader):
125 return self.sections[sectionnumber][1]
126
127 - def organizeby_Origin(self, items):
128 types={} 129 for item in items: 130 t=item.origin 131 if t is None: t="Default" 132 l=types.get(t, []) 133 l.append(item) 134 types[t]=l 135 136 keys=types.keys() 137 keys.sort() 138 return [ (key, types[key]) for key in types]
139
140 - def GetItemSize(self, sectionnumber, sectionheader):
141 return sectionheader.itemsize
142
143 - def GetFileInfoString(self, string):
144 return fileinfo.identify_audiostring(string)
145
146 - def GetFileInfo(self, filename):
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 # zero length file or other issues 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 # check for the size limit on the file, if specified 171 max_size=getattr(convertinfo, 'MAXSIZE', None) 172 if max_size is not None and len(filedata)>max_size: 173 # the data is too big 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
185 - def OnAddFiles(self, filenames):
186 for file in filenames: 187 if file is None: continue # failed dragdrop? 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 # do we want to convert file? 196 afi=fileinfo.identify_audiofile(file) 197 if afi.size<=0: continue # zero length file or other issues 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 # ensure they match up 206 wx.BeginBusyCursor() 207 if filedata is None: 208 continue 209 else: 210 filedata=open(file, "rb").read() 211 # check for the size limit on the file, if specified 212 max_size=getattr(convertinfo, 'MAXSIZE', None) 213 if max_size is not None and len(filedata)>max_size: 214 # the data is too big 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
227 - def ConvertFormat(self, file, convertinfo):
228 with guihelper.WXDialogWrapper(ConvertDialog(self, file, convertinfo), 229 True) as (dlg, retcode): 230 return dlg.newfiledata if retcode==wx.ID_OK else None
231
232 - def versionupgrade(self, dict, version):
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 # version 0 to 1 upgrade 240 if version==0: 241 version=1 # the are the same 242 243 # 1 to 2 etc 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
254 -class ConvertDialog(wx.Dialog):
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 # we need to offer all types here rather than using derived classes as the phone may support several 264 # alternatives 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") # full length wav equivalent 301 self.clipwavfile=common.gettempfilename("wav") # used for clips from full length 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 # create the covert controls 308 self.create_convert_pane(vbs, file, convertinfo) 309 # and the crop controls 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 # diable various things 318 self.FindWindowById(wx.ID_OK).Enable(False) 319 for i in self.cropids: 320 self.FindWindowById(i).Enable(False) 321 322 323 # Events 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 # timers and sounds 330 self.sound=None 331 self.timer=wx.Timer(self, self.ID_TIMER) 332 333 # wxPython wrapper on Mac raises NotImplemented for wx.Sound.Stop() so 334 # we hack around that by supplying a zero length wav to play instead 335 if guihelper.IsMac(): 336 self.zerolenwav=guihelper.getresourcefile("zerolen.wav")
337 338
339 - def create_convert_pane(self, vbs, file, convertinfo):
340 params=self.PARAMETERS[convertinfo.format] 341 # convert bit 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 # Fill out fields - we explicitly set even when not necessary due to bugs on wxMac 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 # Events 394 wx.EVT_BUTTON(self, self.ID_CONVERT, self.OnConvert)
395
396 - def create_crop_panel(self, vbs):
397 # crop bit 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 # the start & end manual entry items 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
446 - def OnConvert(self, _):
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
461 - def UpdateCrop(self):
462 self.positionlabel.SetLabel("%.1f secs" % (self.slider.GetCurrent()*self.wfi.duration),) 463 duration=(self.slider.GetEnd()-self.slider.GetStart())*self.wfi.duration 464 self.clip_start.SetValue(self.slider.GetStart()*self.wfi.duration) 465 self.clip_duration.SetValue(duration) 466 v=getattr(self, self.PARAMETERS[self.convertinfo.format]['filelength'])(duration) 467 self.lengthlabel.SetLabel("%s" % (v,))
468 469 470
471 - def OnPlayClip(self,_):
472 self._Play(self.slider.GetStart(), self.slider.GetEnd(), 473 self.clip_volume.GetValue())
474
475 - def OnPlayPosition(self, _):
476 self._Play(self.slider.GetCurrent(), 1.0)
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
497 - def OnTimer(self,_):
498 now=time.time() 499 if now>self.endtime: 500 self.timer.Stop() 501 # assert wx.Sound.IsPlaying()==False 502 self.slider.SetCurrent(self.playend) 503 self.UpdateCrop() 504 return 505 # work out where the slider should go 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
511 - def OnStop(self, _=None):
512 self.timer.Stop() 513 if self.sound is not None: 514 if guihelper.IsMac(): 515 # There is no stop method for Mac so we play a one centisecond wav 516 # which cancels the existing playing sound 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
524 - def OnSliderCurrentChanged(self, evt):
525 self.OnStop() 526 wx.CallAfter(self.UpdateCrop)
527
528 - def OnSliderChanging(self, _):
529 wx.CallAfter(self.UpdateCrop)
530
531 - def _removetempfiles(self):
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):
538 self.OnStop() 539 # make new data 540 start=self.slider.GetStart()*self.wfi.duration 541 duration=(self.slider.GetEnd()-self.slider.GetStart())*self.wfi.duration 542 self.newfiledata=getattr(self, self.PARAMETERS[self.convertinfo.format]['final'])( 543 start, duration, self.clip_volume.GetValue()) 544 # now remove files 545 self._removetempfiles() 546 # use normal handler to quit dialog 547 evt.Skip()
548
549 - def OnCancel(self, evt):
550 self.OnStop() 551 self._removetempfiles() 552 evt.Skip()
553
554 - def OnSetClip(self, _=None):
555 s=self.clip_start.GetValue() 556 d=self.clip_duration.GetValue() 557 e=s+d 558 if e<=self.wfi.duration: 559 self.slider.SetStart(s/self.wfi.duration) 560 self.slider.SetEnd(e/self.wfi.duration) 561 self.UpdateCrop()
562 563 ### 564 ### MP3 functions 565 ### 566
567 - def mp3setup(self):
568 self.mp3file=common.gettempfilename("mp3") 569 self.tmp_mp3file=common.gettempfilename('mp3') 570 self.temporaryfiles.append(self.mp3file) 571 self.temporaryfiles.append(self.tmp_mp3file)
572
573 - def mp3convert(self):
574 # make mp3 to work with 575 open(self.mp3file, "wb").write(conversions.converttomp3(self.file, int(self.bitrate.GetStringSelection()), int(self.samplerate.GetStringSelection()), int(self.channels.GetStringSelection()))) 576 self.afi=fileinfo.getmp3fileinfo(self.mp3file) 577 print "result is",len(self.afi.frames),"frames" 578 # and corresponding wav to play 579 conversions.converttowav(self.mp3file, self.wavfile)
580
581 - def mp3filelength(self, duration):
582 # mp3 specific file length calculation 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
589 - def _trim_mp3(self, start, duration):
590 # mp3 writing out 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
598 - def _trim_and_adjust_vol_mp3(self, start, duration, volume):
599 # trim, adjust volume, and write mp3 out 600 # use the original to make a new wav, not the one that went through foo -> mp3 -> wav 601 conversions.converttowav(self.file, self.wavfile, 602 start=start, duration=duration) 603 # adjust the volume 604 conversions.adjustwavfilevolume(self.wavfile, volume) 605 # convert to mp3 606 return conversions.converttomp3(self.wavfile, 607 int(self.bitrate.GetStringSelection()), 608 int(self.samplerate.GetStringSelection()), 609 int(self.channels.GetStringSelection()))
610
611 - def mp3final(self, start, duration, volume=None):
612 if volume: 613 # need to adjust volume 614 return self._trim_and_adjust_vol_mp3(start, duration, volume) 615 else: 616 return self._trim_mp3(start, duration)
617 618 ### 619 ### QCP/PureVoice functions 620 ### 621
622 - def qcpsetup(self):
623 self.qcpfile=common.gettempfilename("qcp") 624 self.temporaryfiles.append(self.qcpfile)
625
626 - def qcpconvert(self):
627 # we verify the pvconv binary exists first 628 conversions.getpvconvbinary() 629 # convert to wav first 630 conversions.converttowav(self.file, self.wavfile, samplerate=8000, channels=1) 631 # then to qcp 632 conversions.convertwavtoqcp(self.wavfile, self.qcpfile, 633 self.optimization.GetSelection()) 634 # and finally the wav from the qcp so we can accurately hear what it sounds like 635 conversions.convertqcptowav(self.qcpfile, self.wavfile)
636
637 - def qcpfilelength(self, duration):
638 # we don't actually know unless we do a conversion as QCP is 639 # variable bitrate, so just assume the worst case of 13000 640 # bits per second (1625 bytes per second) with an additional 641 # 5% overhead due to the file format (I often see closer to 7%) 642 return int(duration*1625*1.05)
643
644 - def qcpfinal(self, start, duration, volume=None):
645 # use the original to make a new wav, not the one that went through foo -> qcp -> wav 646 conversions.converttowav(self.file, self.wavfile, samplerate=8000, channels=1, start=start, duration=duration) 647 if volume is not None: 648 conversions.adjustwavfilevolume(self.wavfile, volume) 649 conversions.convertwavtoqcp(self.wavfile, self.qcpfile, 650 self.optimization.GetSelection()) 651 return file(self.qcpfile, "rb").read()
652