Exemplo n.º 1
0
 def get_min_period() -> int:
     """
     Avoid picking periods shorter than `max_freq`.
     - Yamaha FM feedback produces nearly inaudible high frequencies,
       which tend to produce erroneously short period estimates,
       causing correlation to fail.
     - Most music does not go this high.
     - Overestimating period of high notes is mostly harmless.
     """
     max_cyc_s = max_freq
     min_s_cyc = 1 / max_cyc_s
     min_subsmp_cyc = subsmp_s * min_s_cyc
     return iround(min_subsmp_cyc)
Exemplo n.º 2
0
    def _calc_slope_finder(self, period: float) -> np.ndarray:
        """ Called whenever period changes substantially.
        Returns a kernel to be correlated with input data,
        to find positive slopes."""

        N = self._buffer_nsamp
        halfN = N // 2
        slope_finder = np.zeros(N)

        cfg = self.cfg
        slope_width = max(iround(cfg.slope_width * period), 1)
        slope_strength = cfg.slope_strength * cfg.buffer_falloff

        slope_finder[halfN - slope_width:halfN] = -slope_strength
        slope_finder[halfN:halfN + slope_width] = slope_strength
        return slope_finder
Exemplo n.º 3
0
    def spectrum_rescale_buffer(self, data: np.ndarray) -> None:
        """
        - Cross-correlate the log-frequency spectrum of `data` with `buffer`.
        - Rescale `buffer` until its pitch matches `data`.
        """

        # Setup
        scfg = self.scfg
        Ntrigger = self._corr_buffer.size
        if self._frames_since_spectrum < self.scfg.min_frames_between_recompute:
            return
        self._frames_since_spectrum = 0

        calc_spectrum = self._spectrum_calc.calc_spectrum

        # Compute log-frequency spectrum of `data`.
        spectrum = calc_spectrum(data)
        normalize_buffer(spectrum)
        assert not np.any(np.isnan(spectrum))

        # Compute log-frequency spectrum of `self._buffer`.
        prev_spectrum = calc_spectrum(self._corr_buffer)
        # Don't normalize self._spectrum. It was already normalized when being assigned.

        # Rescale `self._buffer` until its pitch matches `data`.
        resample_notes = correlate_spectrum(spectrum, prev_spectrum,
                                            scfg.max_notes_to_resample).peak
        if resample_notes != 0:
            # If we want to double pitch, we must divide data length by 2.
            new_len = iround(Ntrigger /
                             2**(resample_notes / scfg.notes_per_octave))

            def rescale_mut(corr_kernel_mut):
                buf = np.interp(
                    np.linspace(0, 1, new_len),
                    np.linspace(0, 1, Ntrigger),
                    corr_kernel_mut,
                )
                # assert len(buf) == new_len
                buf = midpad(buf, Ntrigger)
                corr_kernel_mut[:] = buf

            # Copy+resample self._buffer.
            rescale_mut(self._corr_buffer)
Exemplo n.º 4
0
    def _calc_lag_prevention(self) -> np.ndarray:
        """ Returns input-data window,
        which zeroes out all data older than 1-ish frame old.
        See https://github.com/jimbo1qaz/corrscope/wiki/Correlation-Trigger
        """
        N = self._buffer_nsamp
        halfN = N // 2

        # - Create a cosine taper of `width` <= 1 frame
        # - Right-pad(value=1, len=1 frame)
        # - Place in left half of N-sample buffer.

        # To avoid cutting off data, use a narrow transition zone (invariant to stride).
        lag_prevention = self.cfg.lag_prevention
        tsamp_frame = self._tsamp_frame
        transition_nsamp = round(tsamp_frame *
                                 lag_prevention.transition_frames)

        # Left half of a Hann cosine taper
        # Width (type=subsample) = min(frame * lag_prevention, 1 frame)
        assert transition_nsamp <= tsamp_frame
        width = transition_nsamp
        taper = windows.hann(width * 2)[:width]

        # Right-pad=1 taper to lag_prevention.max_frames long [t-#*f, t]
        taper = rightpad(taper,
                         iround(tsamp_frame * lag_prevention.max_frames))

        # Left-pad=0 taper to left `halfN` of data_taper [t-halfN, t]
        taper = leftpad(taper, halfN)

        # Generate left half-taper to prevent correlating with 1-frame-old data.
        # Right-pad=1 taper to [t-halfN, t-halfN+N]
        # TODO switch to rightpad()? Does it return FLOAT or not?
        data_taper = np.ones(N, dtype=FLOAT)
        data_taper[:halfN] = np.minimum(data_taper[:halfN], taper)

        return data_taper
Exemplo n.º 5
0
 def on_begin(self, begin_time, end_time):
     self.setRange(iround(begin_time), iround(end_time))