def recover_from_stft_spectrogram(Zxx, fs): ''' Recover the time-domain signal from a spectrogram via the inverse STFT :param : Zxx. np.array. The complex spectrogram :param : fs. int. Sample rate of the signal :return : data. time-domain signal ''' # According to the paper, the spectrogram is computed using a Hann window with a length of 25 ms, # a hop length of 10 ms and FFt size of 512 # I believe the length of each segment is the hann window length n_per_seg = int(hann_win_length * fs) # The hop size H = n_per_seg - n_overlap according to scipy n_hop_size = int(hop_length * fs) n_overlap = n_per_seg - n_hop_size # Compute inverse STFT if the nonzero overlap add constraint is satisfied if check_NOLA('hann', n_per_seg, n_overlap): t, data = istft(Zxx, fs, window='hann', nperseg=n_per_seg, noverlap=n_overlap, nfft=fft_size) return t, data else: raise Exception( "The nonzero overlap constraint was not met while computing an inverse STFT" )
def get_stft_spectrogram(data, fs): ''' Compute the spectrogram of the signal in the data array via the STFT :param : data. np.array. The signal :param : fs. int. Sample rate of the signal :return : spec. Spectogram of the signal ''' # According to the paper, the spectrogram is computed using a Hann window with a length of 25 ms, # a hop length of 10 ms and FFt size of 512 # I believe the length of each segment is the hann window length n_per_seg = int(hann_win_length * fs) # The hop size H = n_per_seg - n_overlap according to scipy n_hop_size = int(hop_length * fs) n_overlap = n_per_seg - n_hop_size # Compute STFT if the nonzero overlap add constraint is satisfied if check_NOLA('hann', n_per_seg, n_overlap): f, t, Zxx = stft(data, fs, window='hann', nperseg=n_per_seg, noverlap=n_overlap, nfft=fft_size) # Return the complex spectrogram return f, t, Zxx else: raise Exception( "The nonzero overlap constraint was not met while computing a STFT" )
def _check_NOLA(window, hop_len): """https://gauss256.github.io/blog/cola.html""" if hop_len > len(window): WARN("`hop_len > len(window)`; STFT not invertible") elif not sig.check_NOLA(window, len(window), len(window) - hop_len): WARN("`window` fails Non-zero Overlap Add (NOLA) criterion; " "STFT not invertible")
def list_stft_hop_size(window): (window_size, ) = window.shape assert window_size > 0 l = [] for i in range(1, window_size): if sp.check_COLA(window, window_size, window_size - i) and sp.check_NOLA( window, window_size, window_size - i): l.append(i) return l
def set_window(self, window: object = 'hann', noverlap: int = None, NOLA_check: bool = True): if self._window_type == window and self._noverlap == noverlap: return else: self._window_type = window if noverlap is None: noverlap = self._nperseg // 2 if type(window) is str or \ type(window) is tuple or \ type(window) == float: window = scisig.get_window(window, self._nperseg) elif window: assert len(window) == self._nperseg, 'control size of window' if NOLA_check: assert scisig.check_NOLA(window, self._nperseg, noverlap) elif self._window_type is None: window = None self._window_type = None self._window = window # refresh self._noverlap = noverlap self._nhop = self._nperseg - self._noverlap if self._mono_mode: self._iwin_buffer = [np.zeros(self._nperseg)] self._win_buffer = [np.zeros(self._nperseg), np.zeros(self._nperseg)] else: self._iwin_buffer = [ np.zeros(self._nperseg) for i in range(self.nchannels) ] self._win_buffer = [[np.zeros(self._nperseg), np.zeros(self._nperseg)] for i in range(self.nchannels)] self._main_stream.clear()
# "Shaky" reconstructions. For check_COLA, see ola.py # window = "boxcar" # 25%: COLA not satisfied # window = "blackmanharris" # 50%: COLA not satisfied window_name = window # Rename explicitly if necessary # # Generate data # cutoff_duration = sum(durations) * 0.4 noverlap = int(window_length * overlap_percent) cola = signal.check_COLA(window, window_length, noverlap) nola = signal.check_NOLA(window, window_length, noverlap) # A4 C5 E5 for "A4" chord = notes.minor_chord(scale_name=scale_name, n_notes=3, output="chord") melody = m21.stream.Stream() for i in range(1, n_chords + 1): inv = i % 3 melody.append(chord) chord_copy = copy.deepcopy(chord) chord_copy.inversion(inv) # Move root note to the top chord = chord_copy # Add the extra notes to each chord - we want to remove these after STFT for i, name in enumerate(extra_notes): melody[i].add(name)
("Rectangular, 75%: ", rect120, 120, 90), # True , True ("Rectangular, 25%: ", rect120, 120, 30), # False, True ("Hann symmetrical, 50%: ", hann_sym120, 120, 60), # False, True ("Hann Periodic/DFT-even, 1/2: ", hann_asym120, 120, 60), # True , True ("Hann Periodic/DFT-even, 2/3: ", hann_asym120, 120, 80), # True , True ("Hann Periodic/DFT-even, 3/4: ", hann_asym120, 120, 90), # True , True ("Blackmanharris, 50%: ", blackmanharris120, 120, 60), # False, True ("Blackmanharris, 75%: ", blackmanharris120, 120, 90), # True , True # NOLA ("62 ones with 2 zeros appended, 50%: ", ones64, 64, 32), # False, False ("Hann symmetrical, 1/64 (not enough overlap): ", hann_sym64, 64, 1), # False, False ("Hann symmetrical, 2/64 (not enough overlap): ", hann_sym64, 64, 2), # False, True ("Hann symmetrical, 3/64 (not enough overlap): ", hann_sym64, 64, 3 ) # False, True ] results = np.empty((len(tests), 3), dtype="<U64") # no. tests rows, 3 columns for i, (desc, w, nperseg, noverlap) in enumerate(tests): results[i] = np.array([ desc, str(signal.check_COLA(w, nperseg, noverlap)), str(signal.check_NOLA(w, nperseg, noverlap)) ]) df = pd.DataFrame(data=results, columns=["Description", "COLA", "NOLA"]) print(df)