def trimwavdata(wavedatain, start, duration=None): # check for a PCM file format if wavedatain[:4]!='RIFF' or wavedatain[8:12]!='WAVE' or \ wavedatain[12:16]!='fmt ' or common.LSBUint16(wavedatain[20:22])!=1: raise ValueError, 'not a PCM file' subchunk1size = common.LSBUint32(wavedatain[16:20]) subchunk2start = 20 + subchunk1size subchunk2size = common.LSBUint32(wavedatain[subchunk2start + 4:subchunk2start + 8]) subchunk2datastart = subchunk2start + 8 samplerate = common.LSBUint32(wavedatain[24:28]) blockalign = common.LSBUint16(wavedatain[32:34]) # compute new start & duration new_start = int(start * samplerate) * blockalign newsubchunk2datastart = subchunk2datastart + new_start new_size = subchunk2size - new_start if duration is not None: i = int(duration * samplerate) * blockalign if i < new_size: new_size = i # return new data return 'RIFF'+common.LSBstr32(4+8+subchunk1size+8+new_size)+\ wavedatain[8:subchunk2start]+\ 'data'+common.LSBstr32(new_size)+\ wavedatain[newsubchunk2datastart:newsubchunk2datastart+new_size]
def trimwavfile(wavfilename, wavoutfilename, start, duration=None, gain=None): with file(wavfilename, 'rb') as f: # read in the headers headers = f.read(20) subchunk1size = common.LSBUint32(headers[16:20]) headers += f.read(subchunk1size) subchunk2id = f.read(4) subchunk2size = common.LSBUint32(f.read(4)) # check for a PCM file format if headers[:4]!='RIFF' or headers[8:12]!='WAVE' or \ headers[12:16]!='fmt ' or common.LSBUint16(headers[20:22])!=1: # not a PCM file raise TypeError subchunk2start = 20 + subchunk1size subchunk2datastart = subchunk2start + 8 samplerate = common.LSBUint32(headers[24:28]) blockalign = common.LSBUint16(headers[32:34]) # compute new start & duration new_start = int(start * samplerate) * blockalign new_size = subchunk2size - new_start if duration is not None: i = int(duration * samplerate) * blockalign if i < new_size: new_size = i # go get it f.seek(new_start, 1) open(wavoutfilename, 'wb').write("".join([ 'RIFF', common.LSBstr32(4 + 8 + subchunk1size + 8 + new_size), headers[8:], 'data', common.LSBstr32(new_size), f.read(new_size) ])) if gain is not None: adjustwavfilevolume(wavoutfilename, gain)
def adjustwavfilevolume(wavfilename, gain): """ Ajdust the volume of a wav file. """ with file(wavfilename, 'rb') as f: # read in the headers headers = f.read(20) subchunk1size = common.LSBUint32(headers[16:20]) headers += f.read(subchunk1size) headers += f.read(8) # 4 byte ID and 4 byte length subchunk2size = common.LSBUint32(headers[-4:]) bitspersample = common.LSBUint16(headers[34:36]) if bitspersample != 16: print 'Volume adjustment only works with 16-bit wav file', bitspersample return sample_num = subchunk2size / 2 # always 16-bit per channel per sample temp_name = common.gettempfilename("wav") with file(temp_name, 'wb') as f_temp: f_temp.write(headers) delta = pow(10.0, (gain / 10.0)) for i in range(sample_num): d = int(struct.unpack('<h', f.read(2))[0] * delta) if d > 32767: d = 32767 elif d < -32768: d = -32768 f_temp.write(struct.pack('<h', d)) os.remove(wavfilename) os.rename(temp_name, wavfilename)
def convertlgbittobmp(bit_data): """Takes a BIT image file (LG proprietary) and returns BMP @param bit_data: 16BPP BIT image file data @return: 24BPP BMP image file data """ width = common.LSBUint16(bit_data[0:2]) height = common.LSBUint16(bit_data[2:4]) img = 'BM' img += common.LSBstr32(width * height * 3 + 54) # file size img += common.LSBstr16(0) # unused img += common.LSBstr16(0) # unused img += common.LSBstr32(54) # offset to pixel data (from byte 0) img += common.LSBstr32(40) # info section size img += common.LSBstr32(width) # image width img += common.LSBstr32(height) # image height img += common.LSBstr16(1) # image planes img += common.LSBstr16(24) # bits-per-pixel img += common.LSBstr32(0) # compression type (0=uncompressed) img += common.LSBstr32(0) # image size (may be 0 for uncompressed images) img += common.LSBstr32(0) # (ignored) img += common.LSBstr32(0) # (ignored) img += common.LSBstr32(0) # (ignored) img += common.LSBstr32(0) # (ignored) # Now on to the char data for h in range(height): for w in range(width): # images can be zero len on phone if len(bit_data) == 0: bitdata = 0xffff else: bitind = (height - h - 1) * width * 2 + (w * 2) + 4 bitdata = common.LSBUint16(bit_data[bitind:bitind + 2]) red = (bitdata & 0xf800) >> 8 green = (bitdata & 0x07e0) >> 3 blue = (bitdata & 0x001f) << 3 if (red & 0x8) != 0: red = red | 0x7 if (green & 0x4) != 0: green = green | 0x3 if (blue & 0x8) != 0: blue = blue | 0x7 img += chr(blue) img += chr(green) img += chr(red) return img
def convertbmptolgbit(bmp_data): """Takes a BMP image file and returns BIT image file (LG proprietary) @param bit_data: 8BPP or 24BPP BMP image file data @return: 16BPP LGBIT image file data """ # This function only exists for the LG proprietary images (wallpaper, etc.) # on the LG-VX3200. if bmp_data[0:2] != 'BM': return None width = common.LSBUint32(bmp_data[18:22]) height = common.LSBUint32(bmp_data[22:26]) offset = common.LSBUint32(bmp_data[10:14]) bpp = common.LSBUint16(bmp_data[28:30]) img = common.LSBstr16(width) img += common.LSBstr16(height) # Now on to the char data if bpp == 8: # 8BPP (paletted) BMP data palette = bmp_data[54:54 + 256 * 4] for h in range(height): for w in range(width): bitind = (height - h - 1) * width + w + offset palind = ord(bmp_data[bitind:bitind + 1]) * 4 blue = ord(palette[palind:palind + 1]) green = ord(palette[palind + 1:palind + 2]) red = ord(palette[palind + 2:palind + 3]) bitval = ((red & 0xf8) << 8) | ((green & 0xfc) << 3) | ( (blue & 0xf8) >> 3) img += common.LSBstr16(bitval) elif bpp == 24: # 24BPP (non-paletted) BMP data for h in range(height): for w in range(width): bitind = (height - h - 1) * width * 3 + (w * 3) + offset blue = ord(bmp_data[bitind:bitind + 1]) green = ord(bmp_data[bitind + 1:bitind + 2]) red = ord(bmp_data[bitind + 2:bitind + 3]) bitval = ((red & 0xf8) << 8) | ((green & 0xfc) << 3) | ( (blue & 0xf8) >> 3) img += common.LSBstr16(bitval) else: return None return img
def savemedia(self, mediakey, mediaindexkey, maps, results, merge, reindexfunction): """Actually saves out the media @param mediakey: key of the media (eg 'wallpapers' or 'ringtone') @param mediaindexkey: index key (eg 'wallpaper-index') @param maps: list index files and locations @param results: results dict @param merge: are we merging or overwriting what is there? @param reindexfunction: the media is re-indexed at the end. this function is called to do it """ print results.keys() # I humbly submit this as the longest function in the bitpim code ... # wp and wpi are used as variable names as this function was originally # written to do wallpaper. it works just fine for ringtones as well # # LG-VX3200 special notes: # The index entries for both the wallpaper and ringers are # hacked just a bit AND are hacked in slightly different # ways. In addition to that the media for the wallpapers is # currently in BMP format inside the program and must be # converted to BIT (LG proprietary) on the way out. # # In the following: # wp has file references that nave no suffixes # wpi has file references that are mixed, i.e. some have file suffixes (comes from UI that way) wp=results[mediakey].copy() wpi=results[mediaindexkey].copy() # Walk through wp and remove any suffixes that make their way in via the UI for k in wp.keys(): wp[k]['name']=re.sub("\....$", "", wp[k]['name']) # remove builtins for k in wpi.keys(): if wpi[k]['origin']=='builtin': del wpi[k] # build up list into init init={} for offset,indexfile,location,type,maxentries in maps: init[type]={} for k in wpi.keys(): if wpi[k]['origin']==type: index=k-offset name=wpi[k]['name'] data=None del wpi[k] for w in wp.keys(): if wp[w]['name']==name and wp[w]['origin']==type: data=wp[w]['data'] del wp[w] if not merge and data is None: # delete the entry continue init[type][index]={'name': name, 'data': data} # init now contains everything from wallpaper-index print init.keys() # now look through wallpapers and see if anything remaining was assigned a particular # origin for w in wp.keys(): o=wp[w].get("origin", "") if o is not None and len(o) and o in init: idx=-1 while idx in init[o]: idx-=1 init[o][idx]=wp[w] del wp[w] # we now have init[type] with the entries and index number as key (negative indices are # unallocated). Proceed to deal with each one, taking in stuff from wp as we have space for offset,indexfile,location,type,maxentries in maps: if type=="camera": break index=init[type] try: dirlisting=self.getfilesystem(location) except com_brew.BrewNoSuchDirectoryException: self.mkdirs(location) dirlisting={} # rename keys to basename for i in dirlisting.keys(): dirlisting[i[len(location)+1:]]=dirlisting[i] del dirlisting[i] # what we will be deleting dellist=[] if not merge: # get existing wpi for this location wpi=results[mediaindexkey] for i in wpi: entry=wpi[i] if entry['origin']==type: # it is in the original index, are we writing it back out? delit=True for idx in index: if index[idx]['name']==entry['name']: delit=False break if delit: # Add the media file suffixes back to match real filenames on phone if type=="ringers": entryname=entry['name']+".mid" else: entryname=entry['name']+".bit" if entryname in dirlisting: dellist.append(entryname) else: self.log("%s in %s index but not filesystem" % (entryname, type)) # go ahead and delete unwanted files print "deleting",dellist for f in dellist: self.mediacache.rmfile(location+"/"+f) # LG-VX3200 special case: # We only keep (upload) wallpaper images if there was a legit # one on the phone to begin with. This code will weed out any # attempts to the contrary. if type=="images": losem=[] # get existing wpi for this location wpi=results[mediaindexkey] for idx in index: delit=True for i in wpi: entry=wpi[i] if entry['origin']==type: if index[idx]['name']==entry['name']: delit=False break if delit: self.log("Inhibited upload of illegit image (not originally on phone): "+index[idx]['name']) losem.append(idx) # Now actually remove any illegit images from upload attempt for idx in losem: del index[idx] # slurp up any from wp we can take while len(index)<maxentries and len(wp): idx=-1 while idx in index: idx-=1 k=wp.keys()[0] index[idx]=wp[k] del wp[k] # normalise indices index=self._normaliseindices(index) # hey look, I called a function! # move any overflow back into wp if len(index)>maxentries: keys=index.keys() keys.sort() for k in keys[maxentries:]: idx=-1 while idx in wp: idx-=1 wp[idx]=index[k] del index[k] # Now add the proper media file suffixes back to the internal index # prior to the finish up steps coming for k in index.keys(): if type=="ringers": index[k]['name']=index[k]['name']+".mid" else: index[k]['name']=index[k]['name']+".bit" # write out the new index keys=index.keys() keys.sort() ifile=self.protocolclass.indexfile() ifile.numactiveitems=len(keys) for k in keys: entry=self.protocolclass.indexentry() entry.index=k entry.name=index[k]['name'] ifile.items.append(entry) while len(ifile.items)<maxentries: ifile.items.append(self.protocolclass.indexentry()) buffer=prototypes.buffer() ifile.writetobuffer(buffer, autolog=False) if type!="images": # The images index file on the LG-VX3200 is a noop - don't write self.logdata("Updated index file "+indexfile, buffer.getvalue(), ifile) self.writefile(indexfile, buffer.getvalue()) # Write out files - we compare against existing dir listing and don't rewrite if they # are the same size for k in keys: entry=index[k] entryname=entry['name'] data=entry.get("data", None) # Special test for wallpaper files - LG-VX3200 will ONLY accept these files if type=="images": if entryname!="wallpaper.bit" and entryname!="poweron.bit" and entryname!="poweroff.bit": self.log("The wallpaper files can only be wallpaper.bmp, poweron.bmp or poweroff.bmp. "+entry['name']+" does not conform - skipping upload.") continue if data is None: if entryname not in dirlisting: self.log("Index error. I have no data for "+entryname+" and it isn't already in the filesystem - skipping upload.") continue # Some of the wallpaper media files came here from the UI as BMP files. # These need to be converted to LGBIT files on the way out. if type=="images" and data[0:2]=="BM": data=conversions.convertbmptolgbit(data) if data is None: self.log("The wallpaper BMP images must be 8BPP or 24BPP, "+entry['name']+", does not comply - skipping upload.") continue # Can only upload 128x128 images if type=="images" and (common.LSBUint16(data[0:2])!=128 or common.LSBUint16(data[2:4])!=128): self.log("The wallpaper must be 128x128, "+entry['name']+", does not comply - skipping upload.") continue # This following test does not apply to wallpaper - it is always the same size on the LG-VX3200! if type!="images": if entryname in dirlisting and len(data)==dirlisting[entryname]['size']: self.log("Skipping writing %s/%s as there is already a file of the same length" % (location,entryname)) continue self.mediacache.writefile(location+"/"+entryname, data) self.log("Wrote media file: "+location+"/"+entryname) # did we have too many if len(wp): for k in wp: self.log("Unable to put %s on the phone as there weren't any spare index entries" % (wp[k]['name'],)) # Note that we don't write to the camera area # tidy up - reread indices del results[mediakey] # done with it # This excessive and expensive so do not do # self.mediacache.flush() # self.log("Flushed ringer cache") reindexfunction(results) return results