def _update_buffer(self, data: np.ndarray, cache: PerFrameCache) -> None: """ Update self._buffer by adding `data` and a step function. Data is reshaped to taper away from the center. :param data: Wave data. WILL BE MODIFIED. """ assert cache.mean is not None assert cache.period is not None buffer_falloff = self.cfg.buffer_falloff responsiveness = self.cfg.responsiveness N = len(data) if N != self._buffer_nsamp: raise ValueError(f"invalid data length {len(data)} does not match " f"CorrelationTrigger {self._buffer_nsamp}") # New waveform data -= cache.mean normalize_buffer(data) window = gaussian_or_zero(N, std=(cache.period / self._stride) * buffer_falloff) data *= window # Old buffer normalize_buffer(self._buffer) self._buffer = lerp(self._buffer, data, responsiveness)
def _update_buffer(self, data: np.ndarray, cache: PerFrameCache) -> None: """ Update self._buffer by adding `data` and a step function. Data is reshaped to taper away from the center. :param data: Wave data. WILL BE MODIFIED. """ assert cache.mean is not None assert cache.period is not None responsiveness = self.cfg.responsiveness if self.cfg.buffer_strength and responsiveness: # N should equal self.A + self.B. N = len(data) if N != self._corr_buffer.size: raise ValueError( f"invalid data length {len(data)} does not match " f"CorrelationTrigger {self._corr_buffer.size}") # New waveform data -= cache.mean normalize_buffer(data) window = gaussian_or_zero(N, std=self.calc_buffer_std(cache.period / self._stride)) data *= window self._prev_window = window # Old buffer normalize_buffer(self._corr_buffer) self._corr_buffer = lerp(self._corr_buffer, data, responsiveness)
def get_trigger(self, index: int, cache: "PerFrameCache") -> int: N = self._buffer_nsamp cfg = self.cfg # Get data (1D, downmixed to mono) stride = self._stride data = self._wave.get_around(index, N, stride) if cfg.sign_strength != 0: signs = sign_times_peak(data) data += cfg.sign_strength * signs # Remove mean from data data -= np.add.reduce(data) / N # Window data period = get_period(data, self.subsmp_s, self.cfg.max_freq, self) cache.period = period * stride semitones = self._is_window_invalid(period) # If pitch changed... if semitones: # Gaussian window period_symmetric_window = gaussian_or_zero( N, period * cfg.data_falloff) # Left-sided falloff lag_prevention_window = self._lag_prevention_window # Both combined. window = np.minimum(period_symmetric_window, lag_prevention_window) # Slope finder slope_finder = self._calc_slope_finder(period) data *= window # If pitch tracking enabled, rescale buffer to match data's pitch. if self.scfg and (data != 0).any(): # Mutates self._buffer. self.spectrum_rescale_buffer(data) self._prev_period = period self._prev_window = window self._prev_slope_finder = slope_finder else: window = self._prev_window slope_finder = self._prev_slope_finder data *= window prev_buffer: np.ndarray = self._buffer * self.cfg.buffer_strength prev_buffer += self._edge_finder + slope_finder # Calculate correlation if self.cfg.trigger_diameter is not None: radius = round(N * self.cfg.trigger_diameter / 2) else: radius = None trigger_score = correlate_data(data, prev_buffer, radius) peak_offset = trigger_score.peak trigger = index + (stride * peak_offset) del data if self.post: new_data = self._wave.get_around(trigger, N, stride) cache.mean = np.add.reduce(new_data) / N # Apply post trigger (before updating correlation buffer) trigger = self.post.get_trigger(trigger, cache) # Avoid time traveling backwards. self._prev_trigger = trigger = max(trigger, self._prev_trigger) # Update correlation buffer (distinct from visible area) aligned = self._wave.get_around(trigger, N, stride) if cache.mean is None: cache.mean = np.add.reduce(aligned) / N self._update_buffer(aligned, cache) self.frames_since_spectrum += 1 self.offset_viewport(peak_offset) return trigger