def get_phase(cfg): read = WaveReader(WAVE_DIR, cfg) instr = read.read() spectrum = rfft_norm(instr.waves[0]) phase = np.angle(spectrum[1]) return phase
def test_reader_multi_waves(): cfg_str = '''\ files: - path: sine440.69.wav - path: sine440.69.wav speed: 2 volume: 1.1 - path: sine256.59.624.wav speed: 3 volume: 0 pitch_estimate: 69 nwave: 1 nsamp: 16 ''' harmonics = [1, 2] # plus 3 at volume=0 cfg = n163_cfg(**yaml.load(cfg_str)) read = WaveReader(WAVE_DIR, cfg) instr = read.read() # Calculate spectrum of resulting signal spectrum: np.ndarray = np.abs(rfft_norm(instr.waves[0])) spectrum[0] = 0 threshold = np.amax(spectrum) / 2 assert threshold > 0.1 # Ensure pitches present assert (spectrum[harmonics] > threshold).all() # Ensure no other pitches present spectrum[harmonics] = 0 spectrum[0] = 0 assert (spectrum < threshold).all()
def _get_periodic_fft_freq(self, data) -> Tuple[SpectrumType, float]: """ Returns periodic FFT and frequency (Hz) of data. """ # Number of samples nsamp = self.wcfg.nsamp freq_mul = self.freq_mul mode = self.cfg.mode periodic_fft = [] fundamental_bin: float = self._get_fundamental_bin(data) if mode == 'stft': # Get STFT. stft = self._stft(data) # Convert STFT to periodic FFT. for harmonic in range(rfft_length(nsamp, freq_mul)): begin = fundamental_bin * (harmonic - 0.5) end = fundamental_bin * (harmonic + 0.5) bands = stft[math.ceil(begin):math.ceil(end)] amplitude: complex = self.power_sum(bands) periodic_fft.append(amplitude) elif mode == 'cycle': period = round(len(data) / fundamental_bin) # Pick 1 period of data, from the middle of the region. end = (len(data) + period) // 2 begin = end - period periodic_fft = fourier.rfft_norm(data[begin:end]) else: raise ValueError(f'mode=[stft, cycle] (you supplied {mode})') # Multiply pitch of FFT. freq_mul_fft = zero_pad(periodic_fft, freq_mul) # Ensure we didn't omit any harmonics <= Nyquist. if mode == 'stft': fft_plus_harmonic_length = len(freq_mul_fft) + freq_mul assert fft_plus_harmonic_length > rfft_length(nsamp), \ f'fft len={len(freq_mul_fft)} + {freq_mul} not > {rfft_length(nsamp)}' # cyc/s = cyc/bin * bin/samp * samp/s freq_hz = fundamental_bin / len(data) * self.smp_s return freq_mul_fft, freq_hz
def filter_wave(wave: WaveType, transfer) -> WaveType: harm = fourier.rfft_norm(wave) harm[1:] *= transfer(np.arange(1, len(harm))) return fourier.irfft_norm(harm)
def _stft(self, data: np.ndarray) -> np.ndarray: """ Phasor phases will match center of data, or peak of window. """ data *= self.window phased_data = np.roll(data, len(data) // 2) return fourier.rfft_norm(phased_data)
def test_repeat(): cat = rfft_zoh(pulse * 3) pad = zero_pad(rfft_zoh(pulse), 3) assert_close(cat, pad) assert not np.allclose(cat, rfft_norm(pulse * 3))
def test_irfft(): zoh1 = rfft_norm(pulse) zoh3 = zero_pad(zoh1, 3) ipulse3 = irfft_norm(zoh3) assert_close(ipulse3, pulse3)
# noinspection PyUnresolvedReferences import numpy as np from wavetable.dsp.fourier import zero_pad, rfft_norm, irfft_norm, rfft_zoh, irfft_zoh, \ nyquist_real_idx, rfft_length def assert_close(a, b): assert len(a) == len(b) assert np.allclose(a, b) pulse = [1, 0, 0, 0] pulse3 = pulse * 3 expected = [0.25] * 3 fft1 = rfft_norm(pulse) assert_close(fft1, expected) def test_nyquist_real(): assert nyquist_real_idx(4) == 2 assert nyquist_real_idx(5) == 3 def test_nyquist_inclusive(): assert rfft_length(4) == 3 assert rfft_length(5) == 3 # A 16-sample wave has harmonics [0..8] with length 9. assert rfft_length(16, 1) == 9