#!/usr/pkg/bin/python "transpose WAV to another key by lengthening or shortening" import sys, os, wave, struct, array from winsound import * format = {1: 'b', 2: 'h', 4: 'i'} amount = 0 # amount to adjust pitch, in semitones def transpose(): global format, amount if sys.argv[1][0] == '-': sys.argv.pop(1) amount = sys.argv.pop(1) if int(amount) == 0: raise SyntaxError, 'transpose factor must be positive or negative integer' amount = int(amount) for filename in sys.argv[1:]: print filename infile = wave.open(filename, "rb") outfile = wave.open(filename + "%+d.wav" % amount, "wb") outfile.setparams(infile.getparams()) (channels, samplewidth, framerate) = infile.getparams()[0:3] while True: chunk = infile.readframes(framerate) if len(chunk) == 0: break data = list(struct.unpack(format[samplewidth] * \ (len(chunk) / samplewidth), chunk)) data = array.array(format[samplewidth], data) data = modify(data, amount) outfile.writeframes(data.tostring()) def modify(data, amount): twelfth = 1.0 / 12 length = len(data) # if the adjustment amount is negative (lower pitch) then obviously the # sample must be lengthened (more data played back at same speed), so we # negate the amount; then the exponent becomes positive newlength = int(length * pow(2, twelfth * -amount)) ratio = float(length) / newlength data.append(data[-1]) # duplicate the last so we can average with sample + 1 modified = array.array(data.typecode, [0] * newlength) try: for index in range(0, len(modified)): # if we're taking four samples and spreading them over 5, you have: # old[0] | old[1] | old[2] | old[3] | # new[0] | new[1] | new[2] | new[3] | new[4] | # new[0] will be the about the same as old[0], same with new[4] to old[3] # but new[2] will be somewhere between old[1] and old[2] # if old is [0, 1, 2, 3] then new is [0, .8, 1.6, 2.4, 3] # so the calculation is, in the case of new[1]: oldoffset = index * ratio # 4/5 * 1 = .8 oldindex = int(oldoffset) # int(.8) = 0 newvalue = data[oldindex] + \ ((data[oldindex + 1] - data[oldindex]) * (oldoffset - oldindex)) modified[index] = int(newvalue) except: raise return modified if __name__ == "__main__": transpose()