paulstretch_python

PaulStretch python version
Log | Files | Refs | README

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