paulstretch_stereo.py (4570B)
1 #!/usr/bin/env python 2 # 3 # Paul's Extreme Sound Stretch (Paulstretch) - Python version 4 # 5 # by Nasca Octavian PAUL, Targu Mures, Romania 6 # http://www.paulnasca.com/ 7 # 8 # http://hypermammut.sourceforge.net/paulstretch/ 9 # 10 # this file is released under Public Domain 11 # 12 # 13 14 15 import sys 16 from numpy import * 17 import scipy.io.wavfile 18 import wave 19 from optparse import OptionParser 20 21 def load_wav(filename): 22 try: 23 wavedata=scipy.io.wavfile.read(filename) 24 samplerate=int(wavedata[0]) 25 smp=wavedata[1]*(1.0/32768.0) 26 smp=smp.transpose() 27 if len(smp.shape)==1: #convert to stereo 28 smp=tile(smp,(2,1)) 29 return (samplerate,smp) 30 except: 31 print ("Error loading wav: "+filename) 32 return None 33 34 35 36 def optimize_windowsize(n): 37 orig_n=n 38 while True: 39 n=orig_n 40 while (n%2)==0: 41 n/=2 42 while (n%3)==0: 43 n/=3 44 while (n%5)==0: 45 n/=5 46 47 if n<2: 48 break 49 orig_n+=1 50 return orig_n 51 52 def paulstretch(samplerate,smp,stretch,windowsize_seconds,outfilename): 53 nchannels=smp.shape[0] 54 55 outfile=wave.open(outfilename,"wb") 56 outfile.setsampwidth(2) 57 outfile.setframerate(samplerate) 58 outfile.setnchannels(nchannels) 59 60 #make sure that windowsize is even and larger than 16 61 windowsize=int(windowsize_seconds*samplerate) 62 if windowsize<16: 63 windowsize=16 64 windowsize=optimize_windowsize(windowsize) 65 windowsize=int(windowsize/2)*2 66 half_windowsize=int(windowsize/2) 67 68 #correct the end of the smp 69 nsamples=smp.shape[1] 70 end_size=int(samplerate*0.05) 71 if end_size<16: 72 end_size=16 73 74 smp[:,nsamples-end_size:nsamples]*=linspace(1,0,end_size) 75 76 77 #compute the displacement inside the input file 78 start_pos=0.0 79 displace_pos=(windowsize*0.5)/stretch 80 81 #create Window window 82 # window=0.5-cos(arange(windowsize,dtype='float')*2.0*pi/(windowsize-1))*0.5 83 window=pow(1.0-pow(linspace(-1.0,1.0,windowsize),2.0),1.25) 84 85 old_windowed_buf=zeros((2,windowsize)) 86 # hinv_sqrt2=(1+sqrt(0.5))*0.5 87 # hinv_buf=2.0*(hinv_sqrt2-(1.0-hinv_sqrt2)*cos(arange(half_windowsize,dtype='float')*2.0*pi/half_windowsize))/hinv_sqrt2 88 89 while True: 90 #get the windowed buffer 91 istart_pos=int(floor(start_pos)) 92 buf=smp[:,istart_pos:istart_pos+windowsize] 93 if buf.shape[1]<windowsize: 94 buf=append(buf,zeros((2,windowsize-buf.shape[1])),1) 95 buf=buf*window 96 97 #get the amplitudes of the frequency components and discard the phases 98 freqs=abs(fft.rfft(buf)) 99 100 #randomize the phases by multiplication with a random complex number with modulus=1 101 ph=random.uniform(0,2*pi,(nchannels,freqs.shape[1]))*1j 102 freqs=freqs*exp(ph) 103 104 #do the inverse FFT 105 buf=fft.irfft(freqs) 106 107 #window again the output buffer 108 buf*=window 109 110 #overlap-add the output 111 output=buf[:,0:half_windowsize]+old_windowed_buf[:,half_windowsize:windowsize] 112 old_windowed_buf=buf 113 114 #remove the resulted amplitude modulation 115 #update: there is no need to the new windowing function 116 #output*=hinv_buf 117 118 #clamp the values to -1..1 119 output[output>1.0]=1.0 120 output[output<-1.0]=-1.0 121 122 #write the output to wav file 123 outfile.writeframes(int16(output.ravel(1)*32767.0).tostring()) 124 125 start_pos+=displace_pos 126 if start_pos>=nsamples: 127 print ("100 %") 128 break 129 sys.stdout.write ("%d %% \r" % int(100.0*start_pos/nsamples)) 130 sys.stdout.flush() 131 132 outfile.close() 133 134 ######################################## 135 print ("Paul's Extreme Sound Stretch (Paulstretch) - Python version 20141220") 136 print ("by Nasca Octavian PAUL, Targu Mures, Romania\n") 137 parser = OptionParser(usage="usage: %prog [options] input_wav output_wav") 138 parser.add_option("-s", "--stretch", dest="stretch",help="stretch amount (1.0 = no stretch)",type="float",default=8.0) 139 parser.add_option("-w", "--window_size", dest="window_size",help="window size (seconds)",type="float",default=0.25) 140 (options, args) = parser.parse_args() 141 142 143 if (len(args)<2) or (options.stretch<=0.0) or (options.window_size<=0.001): 144 print ("Error in command line parameters. Run this program with --help for help.") 145 sys.exit(1) 146 147 print ("stretch amount = %g" % options.stretch) 148 print ("window size = %g seconds" % options.window_size) 149 (samplerate,smp)=load_wav(args[0]) 150 151 paulstretch(samplerate,smp,options.stretch,options.window_size,args[1]) 152 153 154