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

Source Code for Module conversions

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2003-2004 Stephen Wood <sawecw@users.sf.net> 
  4  ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.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: conversions.py 4381 2007-08-29 00:19:51Z djpham $ 
 10   
 11  "Routines to do various file format conversions" 
 12  from __future__ import with_statement 
 13  import contextlib 
 14  import os 
 15  import tempfile 
 16  import struct 
 17  import subprocess 
 18  import sys 
 19  import wx 
 20   
 21   
 22  import common 
 23   
24 -class ConversionFailed(Exception): pass
25 26 helperdir=os.path.join(common.get_main_dir(), "helpers") 27 28 osext={'win32': '.exe', 29 'darwin': '.mbin', 30 'linux2': '.lbin'} \ 31 [sys.platform] 32 33 # This shortname crap is needed because Windows programs (including ffmpeg) 34 # don't correctly parse command line arguments. 35 if sys.platform=='win32': 36 import win32api
37 - def shortfilename(x):
38 # the name may already be short (eg from tempfile which always returns short names) 39 # and may not exist, so we are careful to only call GetShortPathName if necessary 40 if " " in x: 41 return win32api.GetShortPathName(x) 42 return x
43 else:
44 - def shortfilename(x): return x
45
46 -def gethelperbinary(basename):
47 "Returns the full pathname to the specified helper binary" 48 if basename=="pvconv": 49 return getpvconvbinary() 50 f=os.path.join(helperdir, basename)+osext 51 try: 52 f=shortfilename(f) 53 except: 54 # this craps out if the helper does not exist! 55 raise common.HelperBinaryNotFound(basename, basename+osext, [helperdir]) 56 if not os.path.isfile(f): 57 raise common.HelperBinaryNotFound(basename, basename+osext, [helperdir]) 58 return f
59 60 61 _foundpvconv=None 62
63 -def getpvconvbinary():
64 "PVConv can't be distributed with BitPim so the user has to install it and we have to find it" 65 global _foundpvconv 66 # check each time as user could delete or more binary 67 if _foundpvconv is not None and os.path.isfile(_foundpvconv): 68 return _foundpvconv 69 _foundpvconv=None 70 lookin=[] 71 if sys.platform=='win32': 72 binary="pvconv.exe" 73 lookin.append("c:\\bin") 74 from win32com.shell import shell, shellcon 75 path=shell.SHGetFolderPath(0, shellcon.CSIDL_PROGRAM_FILES, None, 0) 76 if path: 77 lookin.append(os.path.join(path, "Qualcomm")) 78 lookin.append(os.path.join(path, "pvconv")) 79 path=shell.SHGetFolderPath(0, shellcon.CSIDL_WINDOWS, None, 0) 80 if path: 81 lookin.append(path) 82 elif sys.platform=='linux2': 83 binary="pvconv" 84 lookin.append(_expand("~/bin")) 85 lookin.append(_expand("~")) 86 lookin.append(_expand("/usr/local/bin")) 87 elif sys.platform=='darwin': 88 binary="pvconv" 89 lookin.append(_expand("~/bin")) 90 lookin.append(_expand("~")) 91 lookin.append(_expand("/usr/local/bin")) 92 lookin.append(_expand("/usr/bin")) 93 else: 94 raise Exception("Unknown platform "+sys.platform) 95 for dir in lookin: 96 f=os.path.join(dir, binary) 97 if os.path.exists(f): 98 _foundpvconv=f 99 return _foundpvconv 100 101 raise common.HelperBinaryNotFound("pvconv", binary, lookin)
102 103 104
105 -def _expand(x):
106 return os.path.expandvars(os.path.expanduser(x))
107
108 -def run(*args):
109 """Runs the specified command (args[0]) with supplied parameters. 110 Note that your path is not searched for the command.""" 111 print args 112 p=subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 113 universal_newlines=True) 114 _res=p.communicate() 115 if p.returncode: 116 # an error occurred, log it 117 print _res[1] 118 raise common.CommandExecutionFailed(p.returncode, args, _res[1])
119
120 -def convertto8bitpng(pngdata, maxsize):
121 "Convert a PNG file to 8bit color map" 122 123 # Return files small enough, or not PNG as is 124 size=len(pngdata) 125 if size<=maxsize or pngdata[1:4]!='PNG': 126 return pngdata 127 128 p=sys.path[0] 129 if os.path.isfile(p): 130 p=os.path.dirname(p) 131 helpersdirectory=os.path.abspath(os.path.join(p, 'helpers')) 132 print "Helper Directory: "+helpersdirectory 133 if sys.platform=='win32': 134 osext=".exe" 135 if sys.platform=='darwin': 136 osext=".mbin" 137 if sys.platform=='linux2': 138 osext=".lbin" 139 140 pngtopnmbin=gethelperbinary('pngtopnm') 141 ppmquantbin=gethelperbinary('ppmquant') 142 pnmtopngbin=gethelperbinary('pnmtopng') 143 print "pngtopnm: "+pngtopnmbin 144 print "ppmquant: "+ppmquantbin 145 print "pnmtopng: "+pnmtopngbin 146 147 # Write original image to a temp file 148 png=common.gettempfilename("png") 149 open(png, "wb").write(pngdata) 150 151 # Convert this image to pnm 152 pnm=common.gettempfilename("pnm") 153 s='"'+pngtopnmbin+'"' + ' < '+png+' > '+pnm 154 os.system(s) 155 #self.log(s) 156 os.remove(png) 157 158 # Binary search to find largest # of colors with a file size still 159 # less than maxsize 160 161 ncolormax=257 162 ncolormin=1 163 ncolortry=256 164 ncolor=ncolortry 165 pnmq=common.gettempfilename("pnm") 166 167 while size>maxsize or ncolormax-ncolor>1: 168 ncolor=ncolortry 169 s='"'+ppmquantbin+'"'+' '+`ncolortry`+' '+pnm+ ' > '+pnmq 170 #self.log(s) 171 os.system(s) 172 s ='"'+pnmtopngbin+'"' + ' < ' + pnmq + ' > '+png 173 #self.log(s) 174 os.system(s) 175 os.remove(pnmq) 176 pngquantdata=open(png,"rb").read() 177 os.remove(png) 178 size=len(pngquantdata) 179 print `ncolor`+' '+`size` 180 if size>maxsize: 181 ncolormax=ncolor 182 ncolortry=(ncolor+ncolormin)/2 183 else: 184 ncolormin=ncolor 185 ncolortry=(ncolor+ncolormax)/2 186 187 os.remove(pnm) 188 return pngquantdata
189
190 -def convertto8bitpng_joe(pngdata):
191 "Convert a PNG file to 8bit color map" 192 "Separate routine for now so not to screw up existing one, may merge later" 193 if pngdata[1:4]!='PNG': 194 return pngdata 195 # get the path to helper 196 197 pngtopnmbin=gethelperbinary('pngtopnm') 198 ppmquantbin=gethelperbinary('ppmquant') 199 pnmtopngbin=gethelperbinary('pnmtopng') 200 print "pngtopnm: "+pngtopnmbin 201 print "ppmquant: "+ppmquantbin 202 print "pnmtopng: "+pnmtopngbin 203 # Write original image to a temp file 204 png=common.gettempfilename("png") 205 open(png, "wb").write(pngdata) 206 num_of_colors=wx.Image(png).ComputeHistogram(wx.ImageHistogram()) 207 print 'number of colors:', num_of_colors 208 if num_of_colors>256: 209 # no optimization possible, just return 210 os.remove(png) 211 return pngdata 212 # else optimize it 213 # Convert this image to pnm 214 pnm=common.gettempfilename("pnm") 215 s='"'+pngtopnmbin+'"' + ' < '+png+' > '+pnm 216 os.system(s) 217 os.remove(png) 218 # quantize & convert 219 pnmq=common.gettempfilename("pnm") 220 s='"'+ppmquantbin+'"'+' '+`num_of_colors`+' '+pnm+ ' > '+pnmq 221 os.system(s) 222 s ='"'+pnmtopngbin+'"' + ' < ' + pnmq + ' > '+png 223 os.system(s) 224 os.remove(pnmq) 225 pngquantdata=open(png, 'rb').read() 226 os.remove(png) 227 os.remove(pnm) 228 print 'old size: ',len(pngdata),', new size: ',len(pngquantdata) 229 return pngquantdata
230 231
232 -def converttomp3(inputfilename, bitrate, samplerate, channels):
233 """Reads inputfilename and returns data for an mp3 conversion 234 235 @param bitrate: bitrate to use in khz (ie 16 is 16000 bits per second) 236 @param samplerate: audio sampling rate in Hertz 237 @param channels: 1 is mono, 2 is stereo 238 """ 239 ffmpeg=gethelperbinary("ffmpeg") 240 with common.usetempfile('mp3') as mp3file: 241 try: 242 run(ffmpeg, "-i", shortfilename(inputfilename), "-hq", "-ab", `bitrate`, "-ar", `samplerate`, "-ac", `channels`, shortfilename(mp3file)) 243 except common.CommandExecutionFailed, e: 244 # we get this exception on bad parameters, or any other 245 # issue so we assume bad parameters for the moment. 246 raise ConversionFailed, ' '.join(e.args)+'\n'+e.logstr 247 return file(mp3file, "rb").read()
248
249 -def converttowav(mp3filename, wavfilename, samplerate=None, 250 channels=None, start=None, duration=None):
251 ffmpeg=gethelperbinary("ffmpeg") 252 cmd=(ffmpeg, "-i", shortfilename(mp3filename)) 253 if samplerate is not None: 254 cmd+=('-ar', str(samplerate)) 255 if channels is not None: 256 cmd+=('-ac', str(channels)) 257 if start is not None: 258 cmd+=('-ss', str(start)) 259 if duration is not None: 260 cmd+=('-t', str(duration)) 261 cmd+=(shortfilename(wavfilename),) 262 # ffmpeg queries about overwrite - grrr 263 try: os.remove(cmd[-1]) 264 except OSError: pass 265 266 run(*cmd)
267 268 _qcp_optimization_params=('ffr', 'vfr', 'fhr', 'vhr')
269 -def convertwavtoqcp(wavfile, qcpfile, optimization=None):
270 pvconv=shortfilename(gethelperbinary('pvconv')) 271 w_name=shortfilename(wavfile) 272 q_name=common.stripext(w_name)+'.qcp' 273 try: 274 os.remove(q_name) 275 except: 276 pass 277 # Have not figured out how to specify output file for pvconv 278 if optimization is None: 279 run(pvconv, w_name) 280 else: 281 run(pvconv, '-r', _qcp_optimization_params[optimization], w_name) 282 # mv output file to qcpfile 283 try: 284 os.remove(qcpfile) 285 except: 286 pass 287 os.rename(q_name, qcpfile)
288
289 -def convertqcptowav(qcpfile, wavfile):
290 pvconv=shortfilename(gethelperbinary('pvconv')) 291 q_name=shortfilename(qcpfile) 292 w_name=common.stripext(q_name)+'.wav' 293 try: 294 os.remove(w_name) 295 except: 296 pass 297 run(pvconv, q_name) 298 try: 299 os.remove(wavfile) 300 except: 301 pass 302 os.rename(w_name, wavfile)
303
304 -def adjustwavfilevolume(wavfilename, gain):
305 """ Ajdust the volume of a wav file. 306 """ 307 with file(wavfilename, 'rb') as f: 308 # read in the headers 309 headers=f.read(20) 310 subchunk1size=common.LSBUint32(headers[16:20]) 311 headers+=f.read(subchunk1size) 312 headers+=f.read(8) # 4 byte ID and 4 byte length 313 subchunk2size=common.LSBUint32(headers[-4:]) 314 bitspersample=common.LSBUint16(headers[34:36]) 315 if bitspersample!=16: 316 print 'Volume adjustment only works with 16-bit wav file',bitspersample 317 return 318 sample_num=subchunk2size/2 # always 16-bit per channel per sample 319 temp_name=common.gettempfilename("wav") 320 with file(temp_name, 'wb') as f_temp: 321 f_temp.write(headers) 322 delta=pow(10.0, (gain/10.0)) 323 for i in range(sample_num): 324 d=int(struct.unpack('<h', f.read(2))[0]*delta) 325 if d>32767: 326 d=32767 327 elif d<-32768: 328 d=-32768 329 f_temp.write(struct.pack('<h', d)) 330 os.remove(wavfilename) 331 os.rename(temp_name, wavfilename)
332
333 -def trimwavfile(wavfilename, wavoutfilename, start, duration=None, gain=None):
334 with file(wavfilename, 'rb') as f: 335 # read in the headers 336 headers=f.read(20) 337 subchunk1size=common.LSBUint32(headers[16:20]) 338 headers+=f.read(subchunk1size) 339 subchunk2id=f.read(4) 340 subchunk2size=common.LSBUint32(f.read(4)) 341 # check for a PCM file format 342 if headers[:4]!='RIFF' or headers[8:12]!='WAVE' or \ 343 headers[12:16]!='fmt ' or common.LSBUint16(headers[20:22])!=1: 344 # not a PCM file 345 raise TypeError 346 subchunk2start=20+subchunk1size 347 subchunk2datastart=subchunk2start+8 348 samplerate=common.LSBUint32(headers[24:28]) 349 blockalign=common.LSBUint16(headers[32:34]) 350 # compute new start & duration 351 new_start=int(start*samplerate)*blockalign 352 new_size=subchunk2size-new_start 353 if duration is not None: 354 i=int(duration*samplerate)*blockalign 355 if i<new_size: 356 new_size=i 357 # go get it 358 f.seek(new_start, 1) 359 open(wavoutfilename, 'wb').write("".join(['RIFF', 360 common.LSBstr32(4+8+subchunk1size+8+new_size), 361 headers[8:], 362 'data', 363 common.LSBstr32(new_size), 364 f.read(new_size)])) 365 if gain is not None: 366 adjustwavfilevolume(wavoutfilename, gain)
367
368 -def trimwavdata(wavedatain, start, duration=None):
369 # check for a PCM file format 370 if wavedatain[:4]!='RIFF' or wavedatain[8:12]!='WAVE' or \ 371 wavedatain[12:16]!='fmt ' or common.LSBUint16(wavedatain[20:22])!=1: 372 raise ValueError, 'not a PCM file' 373 subchunk1size=common.LSBUint32(wavedatain[16:20]) 374 subchunk2start=20+subchunk1size 375 subchunk2size=common.LSBUint32(wavedatain[subchunk2start+4:subchunk2start+8]) 376 subchunk2datastart=subchunk2start+8 377 samplerate=common.LSBUint32(wavedatain[24:28]) 378 blockalign=common.LSBUint16(wavedatain[32:34]) 379 # compute new start & duration 380 new_start=int(start*samplerate)*blockalign 381 newsubchunk2datastart=subchunk2datastart+new_start 382 new_size=subchunk2size-new_start 383 if duration is not None: 384 i=int(duration*samplerate)*blockalign 385 if i<new_size: 386 new_size=i 387 # return new data 388 return 'RIFF'+common.LSBstr32(4+8+subchunk1size+8+new_size)+\ 389 wavedatain[8:subchunk2start]+\ 390 'data'+common.LSBstr32(new_size)+\ 391 wavedatain[newsubchunk2datastart:newsubchunk2datastart+new_size]
392
393 -def convertjpgtoavi(jpg_data, avi_file_name, fps=4, new_file=False):
394 bmp2avi=shortfilename(gethelperbinary('bmp2avi')) 395 if new_file: 396 # delete any existing file and start fresh 397 try: 398 os.remove(avi_file_name) 399 except: 400 pass 401 # convert the jpg data to bmp data 402 with contextlib.nested(common.usetempfile('jpg'), 403 common.usetempfile('bmp')) as (_jpg, _bmp): 404 jpg_name=shortfilename(_jpg) 405 bmp_name=shortfilename(_bmp) 406 file(jpg_name, "wb").write(jpg_data) 407 wx.Image(jpg_name).SaveFile(bmp_name, wx.BITMAP_TYPE_BMP) 408 # add the bmp frame to the avi file 409 run(bmp2avi, '-f', `fps`, '-i', bmp_name, '-o', avi_file_name)
410
411 -def convertavitobmp(avi_data, frame_num=0):
412 with common.usetempfile('avi') as _avi: 413 avi_file=shortfilename(_avi) 414 file(avi_file, 'wb').write(avi_data) 415 return convertfileavitobmp(avi_file, frame_num)
416
417 -def convertfileavitobmp(avi_file_name, frame_num=0):
418 bmp2avi=shortfilename(gethelperbinary('bmp2avi')) 419 with common.usetempfile('bmp') as _bmp: 420 bmp_file_name=shortfilename(_bmp) 421 run(bmp2avi, '-t', `frame_num`, '-i', shortfilename(avi_file_name), 422 '-o', bmp_file_name) 423 return wx.Image(bmp_file_name)
424
425 -def convertfilelgbittobmp(bit_file_name):
426 "File-based wrapper for convertlgbittobmp." 427 with common.usetempfile('png') as bmp: 428 bmpdata=convertlgbittobmp(file(bit_file_name,"rb").read()) 429 file(bmp, "wb").write(bmpdata) 430 return wx.Image(bmp)
431
432 -def convertlgbittobmp(bit_data):
433 """Takes a BIT image file (LG proprietary) and returns BMP 434 435 @param bit_data: 16BPP BIT image file data 436 @return: 24BPP BMP image file data 437 """ 438 width=common.LSBUint16(bit_data[0:2]) 439 height=common.LSBUint16(bit_data[2:4]) 440 img='BM' 441 img+=common.LSBstr32(width*height*3+54) # file size 442 img+=common.LSBstr16(0) # unused 443 img+=common.LSBstr16(0) # unused 444 img+=common.LSBstr32(54) # offset to pixel data (from byte 0) 445 img+=common.LSBstr32(40) # info section size 446 img+=common.LSBstr32(width) # image width 447 img+=common.LSBstr32(height) # image height 448 img+=common.LSBstr16(1) # image planes 449 img+=common.LSBstr16(24) # bits-per-pixel 450 img+=common.LSBstr32(0) # compression type (0=uncompressed) 451 img+=common.LSBstr32(0) # image size (may be 0 for uncompressed images) 452 img+=common.LSBstr32(0) # (ignored) 453 img+=common.LSBstr32(0) # (ignored) 454 img+=common.LSBstr32(0) # (ignored) 455 img+=common.LSBstr32(0) # (ignored) 456 # Now on to the char data 457 for h in range(height): 458 for w in range(width): 459 # images can be zero len on phone 460 if len(bit_data)==0: 461 bitdata = 0xffff 462 else: 463 bitind=(height-h-1)*width*2+(w*2)+4 464 bitdata=common.LSBUint16(bit_data[bitind:bitind+2]) 465 red=(bitdata & 0xf800) >> 8 466 green=(bitdata & 0x07e0) >> 3 467 blue=(bitdata & 0x001f) << 3 468 if (red & 0x8) != 0: 469 red=red | 0x7 470 if (green & 0x4) != 0: 471 green=green | 0x3 472 if (blue & 0x8) != 0: 473 blue=blue | 0x7 474 img+=chr(blue) 475 img+=chr(green) 476 img+=chr(red) 477 return img
478
479 -def convertbmptolgbit(bmp_data):
480 """Takes a BMP image file and returns BIT image file (LG proprietary) 481 482 @param bit_data: 8BPP or 24BPP BMP image file data 483 @return: 16BPP LGBIT image file data 484 """ 485 # This function only exists for the LG proprietary images (wallpaper, etc.) 486 # on the LG-VX3200. 487 if bmp_data[0:2]!='BM': 488 return None 489 width=common.LSBUint32(bmp_data[18:22]) 490 height=common.LSBUint32(bmp_data[22:26]) 491 offset=common.LSBUint32(bmp_data[10:14]) 492 bpp=common.LSBUint16(bmp_data[28:30]) 493 img=common.LSBstr16(width) 494 img+=common.LSBstr16(height) 495 # Now on to the char data 496 if bpp==8: 497 # 8BPP (paletted) BMP data 498 palette=bmp_data[54:54+256*4] 499 for h in range(height): 500 for w in range(width): 501 bitind=(height-h-1)*width+w+offset 502 palind=ord(bmp_data[bitind:bitind+1])*4 503 blue=ord(palette[palind:palind+1]) 504 green=ord(palette[palind+1:palind+2]) 505 red=ord(palette[palind+2:palind+3]) 506 bitval=((red & 0xf8) << 8) | ((green & 0xfc) << 3) | ((blue & 0xf8) >> 3) 507 img+=common.LSBstr16(bitval) 508 elif bpp==24: 509 # 24BPP (non-paletted) BMP data 510 for h in range(height): 511 for w in range(width): 512 bitind=(height-h-1)*width*3+(w*3)+offset 513 blue=ord(bmp_data[bitind:bitind+1]) 514 green=ord(bmp_data[bitind+1:bitind+2]) 515 red=ord(bmp_data[bitind+2:bitind+3]) 516 bitval=((red & 0xf8) << 8) | ((green & 0xfc) << 3) | ((blue & 0xf8) >> 3) 517 img+=common.LSBstr16(bitval) 518 else: 519 return None 520 return img
521
522 -def helperavailable(helper_name):
523 try: 524 f=gethelperbinary(helper_name) 525 return True 526 except common.HelperBinaryNotFound: 527 return False 528 except: 529 if __debug__: raise 530 return False
531