forked from paulnasca/paulstretch_python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
paulstretch_stereo.py
executable file
·154 lines (119 loc) · 4.46 KB
/
paulstretch_stereo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env python
#
# Paul's Extreme Sound Stretch (Paulstretch) - Python version
#
# by Nasca Octavian PAUL, Targu Mures, Romania
# http://www.paulnasca.com/
#
# http://hypermammut.sourceforge.net/paulstretch/
#
# this file is released under Public Domain
#
#
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 Window window
# window=0.5-cos(arange(windowsize,dtype='float')*2.0*pi/(windowsize-1))*0.5
window=pow(1.0-pow(linspace(-1.0,1.0,windowsize),2.0),1.25)
old_windowed_buf=zeros((2,windowsize))
# hinv_sqrt2=(1+sqrt(0.5))*0.5
# hinv_buf=2.0*(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
#update: there is no need to the new windowing function
#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
sys.stdout.write ("%d %% \r" % int(100.0*start_pos/nsamples))
sys.stdout.flush()
outfile.close()
########################################
print ("Paul's Extreme Sound Stretch (Paulstretch) - Python version 20141220")
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 = %g" % options.stretch)
print ("window size = %g seconds" % options.window_size)
(samplerate,smp)=load_wav(args[0])
paulstretch(samplerate,smp,options.stretch,options.window_size,args[1])