Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Nasca Octavian PAUL
committed
Feb 19, 2011
0 parents
commit ec0dac9
Showing
2 changed files
with
154 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
This is Paulstretch , Python version | ||
by Nasca Octavian PAUL, Targu Mures, Romania | ||
|
||
Requirements: Numpy, Scipy | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
#!/usr/bin/env python | ||
# | ||
# Paul's Extreme Sound Stretch (Paulstretch) - Python version | ||
# | ||
# by Nasca Octavian PAUL, Targu Mures, Romania | ||
# | ||
# http://hypermammut.sourceforge.net/paulstretch/ | ||
# | ||
# | ||
|
||
|
||
import sys | ||
from numpy import * | ||
import scipy.io.wavfile | ||
import wave | ||
from optparse import OptionParser | ||
|
||
def load_wav(filename): | ||
try: | ||
wavedata=scipy.io.wavfile.read(filename) | ||
samplerate=int(wavedata[0]) | ||
smp=wavedata[1]*(1.0/32768.0) | ||
smp=smp.transpose() | ||
if len(smp.shape)==1: #convert to stereo | ||
smp=tile(smp,(2,1)) | ||
return (samplerate,smp) | ||
except: | ||
print "Error loading wav: "+filename | ||
return None | ||
|
||
|
||
|
||
def optimize_windowsize(n): | ||
orig_n=n | ||
while True: | ||
n=orig_n | ||
while (n%2)==0: | ||
n/=2 | ||
while (n%3)==0: | ||
n/=3 | ||
while (n%5)==0: | ||
n/=5 | ||
|
||
if n<2: | ||
break | ||
orig_n+=1 | ||
return orig_n | ||
|
||
def paulstretch(samplerate,smp,stretch,windowsize_seconds,outfilename): | ||
nchannels=smp.shape[0] | ||
|
||
outfile=wave.open(outfilename,"wb") | ||
outfile.setsampwidth(2) | ||
outfile.setframerate(samplerate) | ||
outfile.setnchannels(nchannels) | ||
|
||
#make sure that windowsize is even and larger than 16 | ||
windowsize=int(windowsize_seconds*samplerate) | ||
if windowsize<16: | ||
windowsize=16 | ||
windowsize=optimize_windowsize(windowsize) | ||
windowsize=int(windowsize/2)*2 | ||
half_windowsize=int(windowsize/2) | ||
|
||
#correct the end of the smp | ||
nsamples=smp.shape[1] | ||
end_size=int(samplerate*0.05) | ||
if end_size<16: | ||
end_size=16 | ||
|
||
smp[:,nsamples-end_size:nsamples]*=linspace(1,0,end_size) | ||
|
||
|
||
#compute the displacement inside the input file | ||
start_pos=0.0 | ||
displace_pos=(windowsize*0.5)/stretch | ||
|
||
#create Hann window | ||
window=0.5-cos(arange(windowsize,dtype='float')*2.0*pi/(windowsize-1))*0.5 | ||
|
||
old_windowed_buf=zeros((2,windowsize)) | ||
hinv_sqrt2=(1+sqrt(0.5))*0.5 | ||
hinv_buf=(hinv_sqrt2-(1.0-hinv_sqrt2)*cos(arange(half_windowsize,dtype='float')*2.0*pi/half_windowsize))/hinv_sqrt2 | ||
|
||
while True: | ||
#get the windowed buffer | ||
istart_pos=int(floor(start_pos)) | ||
buf=smp[:,istart_pos:istart_pos+windowsize] | ||
if buf.shape[1]<windowsize: | ||
buf=append(buf,zeros((2,windowsize-buf.shape[1])),1) | ||
buf=buf*window | ||
|
||
#get the amplitudes of the frequency components and discard the phases | ||
freqs=abs(fft.rfft(buf)) | ||
|
||
#randomize the phases by multiplication with a random complex number with modulus=1 | ||
ph=random.uniform(0,2*pi,(nchannels,freqs.shape[1]))*1j | ||
freqs=freqs*exp(ph) | ||
|
||
#do the inverse FFT | ||
buf=fft.irfft(freqs) | ||
|
||
#window again the output buffer | ||
buf*=window | ||
|
||
#overlap-add the output | ||
output=buf[:,0:half_windowsize]+old_windowed_buf[:,half_windowsize:windowsize] | ||
old_windowed_buf=buf | ||
|
||
#remove the resulted amplitude modulation | ||
output*=hinv_buf | ||
|
||
#clamp the values to -1..1 | ||
output[output>1.0]=1.0 | ||
output[output<-1.0]=-1.0 | ||
|
||
#write the output to wav file | ||
outfile.writeframes(int16(output.ravel(1)*32767.0).tostring()) | ||
|
||
start_pos+=displace_pos | ||
if start_pos>=nsamples: | ||
print "100 %" | ||
break | ||
print "%d %% \r" % int(100.0*start_pos/nsamples), | ||
sys.stdout.flush() | ||
|
||
outfile.close() | ||
|
||
######################################## | ||
print "Paul's Extreme Sound Stretch (Paulstretch) - Python version 20110219" | ||
print "by Nasca Octavian PAUL, Targu Mures, Romania\n" | ||
parser = OptionParser(usage="usage: %prog [options] input_wav output_wav") | ||
parser.add_option("-s", "--stretch", dest="stretch",help="stretch amount (1.0 = no stretch)",type="float",default=8.0) | ||
parser.add_option("-w", "--window_size", dest="window_size",help="window size (seconds)",type="float",default=0.25) | ||
(options, args) = parser.parse_args() | ||
|
||
|
||
if (len(args)<2) or (options.stretch<=0.0) or (options.window_size<=0.001): | ||
print "Error in command line parameters. Run this program with --help for help." | ||
sys.exit(1) | ||
|
||
print "stretch amount =",options.stretch | ||
print "window size =",options.window_size,"seconds" | ||
(samplerate,smp)=load_wav(args[0]) | ||
|
||
paulstretch(samplerate,smp,options.stretch,options.window_size,args[1]) | ||
|
||
|
||
|