forked from paulnasca/paulstretch_python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
paulstretch_mono.py
117 lines (86 loc) · 3.09 KB
/
paulstretch_mono.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
#
# 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
def load_wav(filename):
try:
wavedata=scipy.io.wavfile.read(filename)
samplerate=int(wavedata[0])
smp=wavedata[1]*(1.0/32768.0)
if len(smp.shape)>1: #convert to mono
smp=(smp[:,0]+smp[:,1])*0.5
return (samplerate,smp)
except:
print ("Error loading wav: "+filename)
return None
########################################
def paulstretch(samplerate,smp,stretch,windowsize_seconds,outfilename):
outfile=wave.open(outfilename,"wb")
outfile.setsampwidth(2)
outfile.setframerate(samplerate)
outfile.setnchannels(1)
#make sure that windowsize is even and larger than 16
windowsize=int(windowsize_seconds*samplerate)
if windowsize<16:
windowsize=16
windowsize=int(windowsize/2)*2
half_windowsize=int(windowsize/2)
#correct the end of the smp
end_size=int(samplerate*0.05)
if end_size<16:
end_size=16
smp[len(smp)-end_size:len(smp)]*=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(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)
while True:
#get the windowed buffer
istart_pos=int(floor(start_pos))
buf=smp[istart_pos:istart_pos+windowsize]
if len(buf)<windowsize:
buf=append(buf,zeros(windowsize-len(buf)))
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,len(freqs))*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*32767.0).tostring())
start_pos+=displace_pos
if start_pos>=len(smp):
print ("100 %")
break
sys.stdout.write ("%d %% \r" % int(100.0*start_pos/len(smp)))
sys.stdout.flush()
outfile.close()
########################################
(samplerate,smp)=load_wav("input.wav")
paulstretch(samplerate,smp,8.0,0.25,"out.wav")