예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
    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