Remove slope strength (reuse edge strength), always enable slope

pull/416/head
nyanpasu64 2022-03-14 11:26:03 -07:00
rodzic d103e7a978
commit 55cd73fca8
4 zmienionych plików z 52 dodań i 39 usunięć

Wyświetl plik

@ -1121,7 +1121,6 @@ class ChannelModel(qc.QAbstractTableModel):
Column("trigger__reset_below", float, None, "Reset Below\nMatch"),
Column("trigger__edge_direction", plus_minus_one, None),
Column("trigger__edge_strength", float, None),
Column("trigger__slope_strength", float, None),
Column("trigger__slope_width", float, None),
]

Wyświetl plik

@ -364,14 +364,6 @@ class MainWindow(QWidget):
name="trigger__edge_strength",
):
s.widget.setMinimum(0.0)
with add_row(
s,
tr("Slope Strength\n(for PSG/C64/FM)"),
BoundDoubleSpinBox,
name="trigger__slope_strength",
):
s.widget.setSingleStep(10)
s.widget.setMaximum(200)
with add_row(
s,
tr("Slope Width"),

Wyświetl plik

@ -1,5 +1,14 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Type, Optional, ClassVar, Union, TypeVar, Generic
from typing import (
TYPE_CHECKING,
Type,
Optional,
ClassVar,
Union,
TypeVar,
Generic,
cast,
)
import attr
import numpy as np
@ -260,7 +269,7 @@ class CorrelationTriggerConfig(
always_dump="""
mean_responsiveness
pitch_tracking
slope_strength slope_width
slope_width
"""
# deprecated
" buffer_falloff ",
@ -272,7 +281,6 @@ class CorrelationTriggerConfig(
edge_strength: float
# Slope detection
slope_strength: float = 0
slope_width: float = with_units("period", default=0.07)
# Correlation detection
@ -353,6 +361,7 @@ class CorrelationTrigger(MainTrigger):
_prev_mean: float
_prev_period: Optional[int]
_prev_buffer_std: float
_prev_slope_finder: "Optional[npt.NDArray[f32]]"
"""(mutable) [A+B] Amplitude"""
_prev_window: "npt.NDArray[f32]"
@ -366,6 +375,10 @@ class CorrelationTrigger(MainTrigger):
def scfg(self) -> Optional[SpectrumConfig]:
return self.cfg.pitch_tracking
def calc_buffer_std(self, period: float) -> float:
period = period or (self.A + self.B)
return period * self.cfg.buffer_falloff
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.A = self.B = self._tsamp // 2
@ -378,6 +391,7 @@ class CorrelationTrigger(MainTrigger):
self._prev_mean = 0.0
# Will be overwritten on the first frame.
self._prev_period = None
self._prev_buffer_std = self.calc_buffer_std(0)
self._prev_slope_finder = None
self._prev_window = np.zeros(self.A + self.B, f32)
@ -395,25 +409,39 @@ class CorrelationTrigger(MainTrigger):
else:
self._spectrum_calc = DummySpectrum()
def _calc_slope_finder(self, period: float) -> Optional[np.ndarray]:
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,
with length A+B."""
cfg = self.cfg
if cfg.slope_strength:
# noinspection PyTypeChecker
slope_width: float = np.clip(cfg.slope_width * period, 1.0, self.A / 3)
slope_strength = cfg.slope_strength * cfg.buffer_falloff
# noinspection PyTypeChecker
slope_width: float = np.clip(cfg.slope_width * period, 1.0, self.A / 3)
slope_finder = np.empty(self.A + self.B, dtype=f32) # type: np.ndarray[f32]
slope_finder[: self.A] = -slope_strength / 2
slope_finder[self.A :] = slope_strength / 2
# We want:
# abs_area(slope) / abs_area(buffer) = (E=edge_strength) / (B=B)
#
# Assume:
# abs_area(slope) ∝ w(slope) * h(slope)
# abs_area(buffer) ∝ w(buffer) * (B=h(buffer))
# w(buffer) = _prev_buffer_std
# w(slope) = slope_width
#
# Solve for h(slope)=slope_strength:
# abs_area(slope) / abs_area(buffer) = E / B
# abs_area(slope) / E = abs_area(buffer) / B
# (w(slope) * h(slope)) / E = (w(buffer) * B) / B
# slope_width * h(slope) / E = _prev_buffer_std
# h(slope) = E * _prev_buffer_std / slope_width
slope_strength = cfg.edge_strength * self._prev_buffer_std / slope_width
# slope_width is 1.0 or greater, so this doesn't divide by 0.
slope_finder *= windows.gaussian(self.A + self.B, std=slope_width)
return slope_finder
else:
return None
slope_finder = np.empty(self.A + self.B, dtype=f32) # type: np.ndarray[f32]
slope_finder[: self.A] = -slope_strength / 2
slope_finder[self.A :] = slope_strength / 2
slope_finder *= windows.gaussian(self.A + self.B, std=slope_width)
return slope_finder
# end setup
@ -477,7 +505,7 @@ class CorrelationTrigger(MainTrigger):
self._prev_period = period
self._prev_slope_finder = slope_finder
else:
slope_finder = self._prev_slope_finder
slope_finder = cast(np.ndarray, self._prev_slope_finder)
corr_enabled = bool(cfg.buffer_strength) and bool(cfg.responsiveness)
@ -515,13 +543,10 @@ class CorrelationTrigger(MainTrigger):
corr_quality = np.zeros(corr_nsamp, f32)
# array[A+B] Amplitude
corr_kernel: np.ndarray = (
self._corr_buffer * cfg.buffer_strength
if corr_enabled
else np.zeros(kernel_size, f32)
)
if slope_finder is not None:
corr_kernel += slope_finder
corr_kernel = slope_finder
del slope_finder
if corr_enabled:
corr_kernel += self._corr_buffer * cfg.buffer_strength
# `corr[x]` = correlation of kernel placed at position `x` in data.
# `corr_kernel` is not allowed to move past the boundaries of `data`.
@ -699,10 +724,10 @@ class CorrelationTrigger(MainTrigger):
"""
assert cache.mean is not None
assert cache.period is not None
buffer_falloff = self.cfg.buffer_falloff
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(
@ -713,9 +738,8 @@ class CorrelationTrigger(MainTrigger):
# New waveform
data -= cache.mean
normalize_buffer(data)
window = gaussian_or_zero(
N, std=(cache.period / self._stride or N) * buffer_falloff
)
self._prev_buffer_std = self.calc_buffer_std(cache.period / self._stride)
window = gaussian_or_zero(N, std=self._prev_buffer_std)
data *= window
self._prev_window = window

Wyświetl plik

@ -36,15 +36,13 @@ def trigger_template(**kwargs) -> CorrelationTriggerConfig:
@fixture
@parametrize("trigger_diameter", [0.5, 1.0])
@parametrize("pitch_tracking", [None, SpectrumConfig()])
@parametrize("slope_strength", [0, 100])
@parametrize("sign_strength", [0, 1])
def trigger_cfg(
trigger_diameter, pitch_tracking, slope_strength, sign_strength
trigger_diameter, pitch_tracking, sign_strength
) -> CorrelationTriggerConfig:
return trigger_template(
trigger_diameter=trigger_diameter,
pitch_tracking=pitch_tracking,
slope_strength=slope_strength,
sign_strength=sign_strength,
slope_width=0.14,
)