/
sound.py
46 lines (39 loc) · 1.97 KB
/
sound.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
import numpy
from math import ceil, log
from functools import lru_cache
from . sound_sequence import sampleRate
class Sound:
def __init__(self, soundSequences):
self.soundSequences = soundSequences
def getSamplesInRange(self, start, end):
if end <= start: raise ValueError("Invaild range!")
start, end = int(start * sampleRate), int(end * sampleRate)
samples = numpy.zeros(end - start + 1)
for sequence in self.soundSequences:
sequenceStart = int(sequence.start * sampleRate)
sequenceEnd = int(sequence.end * sampleRate)
if start > sequenceEnd or end < sequenceStart: continue
sequenceStartOffset = int(sequence.startOffset * sampleRate)
i, j = max(start, sequenceStart), min(end, sequenceEnd)
chunk = sequence.data.samples[i - sequenceStart + sequenceStartOffset:
j - sequenceStart + sequenceStartOffset] * sequence.volume
samples[i - start:i - start + len(chunk)] += chunk
return samples
def computeSpectrum(self, start, end, beta = 6):
samples = self.getSamplesInRange(start, end)
chunk = numpy.zeros(2**ceil(log(len(samples), 2)))
chunk[:len(samples)] = samples * getCachedKaiser(len(samples), beta)
return numpy.abs(numpy.fft.rfft(chunk)) / len(samples) * 2
def computeTimeSmoothedSpectrum(self, start, end, attack, release, smoothingSamples = 5, beta = 6):
FFT = None
duration = end - start
for i in range(smoothingSamples, -1, -1):
newFFT = self.computeSpectrum(start - i * duration, end - i * duration, beta = beta)
if FFT is None: FFT = newFFT
else:
factor = numpy.array((attack, release))[(newFFT < FFT).astype(int)]
FFT = FFT * factor + newFFT * (1 - factor)
return FFT
@lru_cache(maxsize = 16)
def getCachedKaiser(length, beta):
return numpy.kaiser(length, beta)