-
Notifications
You must be signed in to change notification settings - Fork 17
/
stft.py
127 lines (76 loc) · 3.28 KB
/
stft.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
from numpy.lib.stride_tricks import sliding_window_view
import numpy as np
def stft(x, framesize, hopsize):
# check input
x = np.atleast_1d(x)
assert x.ndim == 1, \
f'Expected 1D array (samples,),' + \
f' got {x.shape}!'
# construct window
analysis_window_size = np.ravel(framesize)[0]
synthesis_window_size = np.ravel(framesize)[-1]
assert analysis_window_size >= synthesis_window_size, \
f'Analysis window ({analysis_window_size}) must be greater' + \
f' or equal to synthesis window ({synthesis_window_size})!'
W = asymmetric_analysis_window(analysis_window_size, synthesis_window_size) \
if analysis_window_size != synthesis_window_size else \
symmetric_window(analysis_window_size)
# perform analysis
frames0 = sliding_window_view(x, analysis_window_size, writeable=False)[::hopsize]
frames1 = np.fft.rfft(frames0 * W, axis=-1, norm='forward')
if False: # optionally zero dc and nyquist
frames1[:, 0] = 0
frames1[:, -1] = 0
return frames1
def istft(frames, framesize, hopsize):
# check input
frames = np.atleast_2d(frames)
assert frames.ndim == 2, \
f'Expected 2D array (samples,frequencies),' + \
f' got {frames.shape}!'
# construct window
analysis_window_size = np.ravel(framesize)[0]
synthesis_window_size = np.ravel(framesize)[-1]
assert analysis_window_size >= synthesis_window_size, \
f'Analysis window ({analysis_window_size}) must be greater' + \
f' or equal to synthesis window ({synthesis_window_size})!'
A = asymmetric_analysis_window(analysis_window_size, synthesis_window_size) \
if analysis_window_size != synthesis_window_size else \
symmetric_window(analysis_window_size)
S = asymmetric_synthesis_window(analysis_window_size, synthesis_window_size) \
if analysis_window_size != synthesis_window_size else \
symmetric_window(synthesis_window_size)
W = S * hopsize / np.sum(A * S)
# perform synthesis
N = frames.shape[0] * hopsize + analysis_window_size
y = np.zeros((N), float)
if True: # optionally zero dc and nyquist
frames[:, 0] = 0
frames[:, -1] = 0
frames0 = sliding_window_view(y, analysis_window_size, writeable=True)[::hopsize]
frames1 = np.fft.irfft(frames, axis=-1, norm='forward') * W
for i in range(min(len(frames0), len(frames1))):
frames0[i] += frames1[i]
return y
def symmetric_window(symmetric_window_size):
n = symmetric_window_size
window = 0.5 - 0.5 * np.cos(2 * np.pi * np.arange(n) / n)
return window
def asymmetric_analysis_window(analysis_window_size, synthesis_window_size):
n = analysis_window_size
m = synthesis_window_size // 2
left = symmetric_window(2 * n - 2 * m)
right = symmetric_window(2 * m)
window = np.zeros(n)
window[:n-m] = left[:n-m]
window[-m:] = right[-m:]
return window
def asymmetric_synthesis_window(analysis_window_size, synthesis_window_size):
n = analysis_window_size
m = synthesis_window_size // 2
left = symmetric_window(2 * n - 2 * m)
right = symmetric_window(2 * m)
window = np.zeros(n)
window[n-m-m:n-m] = np.square(right[:m]) / left[n-m-m:n-m]
window[-m:] = right[-m:]
return window