Exemple #1
0
class RendererConfig(DumpableAttrs,
                     always_dump="*",
                     exclude="viewport_width viewport_height"):
    width: int
    height: int
    line_width: float = with_units("px", default=1.5)
    grid_line_width: float = with_units("px", default=1.0)

    @property
    def divided_width(self):
        return round(self.width / self.res_divisor)

    @property
    def divided_height(self):
        return round(self.height / self.res_divisor)

    bg_color: str = "#000000"
    init_line_color: str = default_color()

    grid_color: Optional[str] = None
    stereo_grid_opacity: float = 0.25

    midline_color: str = "#404040"
    v_midline: bool = False
    h_midline: bool = False

    # Label settings
    label_font: Font = attr.ib(factory=Font)

    label_position: LabelPosition = LabelPosition.LeftTop
    # The text will be located (label_padding_ratio * label_font.size) from the corner.
    label_padding_ratio: float = with_units("px/pt", default=0.5)
    label_color_override: Optional[str] = None

    @property
    def get_label_color(self):
        return coalesce(self.label_color_override, self.init_line_color)

    antialiasing: bool = True

    # Performance (skipped when recording to video)
    res_divisor: float = 1.0

    # Debugging only
    viewport_width: float = 1
    viewport_height: float = 1

    def __attrs_post_init__(self) -> None:
        # round(np.int32 / float) == np.float32, but we want int.
        assert isinstance(self.width, (int, float))
        assert isinstance(self.height, (int, float))

    # Both before_* functions should be idempotent, AKA calling twice does no harm.
    def before_preview(self) -> None:
        """ Called *once* before preview. Does nothing. """
        pass

    def before_record(self) -> None:
        """ Called *once* before recording video. Eliminates res_divisor. """
        self.res_divisor = 1
Exemple #2
0
class CorrelationTriggerConfig(
        MainTriggerConfig,
        always_dump="""
    pitch_tracking
    slope_strength slope_width
    """

        # deprecated
        " buffer_falloff ",
):
    # get_trigger()
    # Edge/area finding
    sign_strength: float = 0
    edge_strength: float

    # Slope detection
    slope_strength: float = 0
    slope_width: float = with_units("period", default=0.07)

    # Correlation detection (meow~ =^_^=)
    buffer_strength: float = 1

    # Both data and buffer uses Gaussian windows. std = wave_period * falloff.
    # get_trigger()
    data_falloff: float = 1.5

    # _update_buffer()
    buffer_falloff: float = 0.5

    # Maximum distance to move
    trigger_diameter: Optional[float] = 0.5

    recalc_semitones: float = 1.0
    lag_prevention: LagPrevention = attr.ib(factory=LagPrevention)

    # _update_buffer
    responsiveness: float

    # Period/frequency estimation (not in GUI)
    max_freq: float = with_units("Hz", default=4000)

    # Pitch tracking = compute spectrum.
    pitch_tracking: Optional["SpectrumConfig"] = None

    # region Legacy Aliases
    trigger_strength = Alias("edge_strength")
    falloff_width = Alias("buffer_falloff")

    # endregion

    def __attrs_post_init__(self) -> None:
        MainTriggerConfig.__attrs_post_init__(self)

        validate_param(self, "slope_width", 0, 0.5)

        validate_param(self, "responsiveness", 0, 1)
        # TODO trigger_falloff >= 0
        validate_param(self, "buffer_falloff", 0, np.inf)
Exemple #3
0
class Config(
    KeywordAttrs,
    always_dump="""
    begin_time end_time
    render_subfps trigger_subsampling render_subsampling
    trigger_stereo render_stereo
    show_internals
    """,
):
    """ Default values indicate optional attributes. """

    master_audio: Optional[str]
    begin_time: float = with_units("s", default=0)
    end_time: Optional[float] = None

    fps: int

    trigger_ms: int = with_units("ms")
    render_ms: int = with_units("ms")

    # Performance
    trigger_subsampling: int = 1
    render_subsampling: int = 1

    # Performance (skipped when recording to video)
    render_subfps: int = 1
    render_fps = property(lambda self: Fraction(self.fps, self.render_subfps))
    # FFmpeg accepts FPS as a fraction. (decimals may work, but are inaccurate.)

    def before_preview(self) -> None:
        """ Called *once* before preview. Decreases render fps/etc. """
        self.render.before_preview()

    def before_record(self) -> None:
        """ Called *once* before recording video. Force high-quality rendering. """
        self.render_subfps = 1
        self.render.before_record()

    # End Performance
    amplification: float

    # Stereo config
    trigger_stereo: Flatten = Flatten.SumAvg
    render_stereo: Flatten = Flatten.SumAvg

    trigger: ITriggerConfig  # Can be overriden per Wave

    # Multiplies by trigger_width, render_width. Can override trigger.
    channels: List[ChannelConfig]

    layout: LayoutConfig
    render: RendererConfig

    show_internals: List[str] = attr.Factory(list)
    benchmark_mode: BenchmarkMode = attr.ib(
        BenchmarkMode.NONE, converter=BenchmarkMode.by_name
    )
Exemple #4
0
class RendererConfig(DumpableAttrs, always_dump="*"):
    width: int
    height: int
    line_width: float = with_units("px", default=1.5)

    bg_color: str = "#000000"
    init_line_color: str = default_color()

    grid_color: Optional[str] = None
    stereo_grid_opacity: float = 0.5

    midline_color: Optional[str] = None
    v_midline: bool = False
    h_midline: bool = False

    antialiasing: bool = True

    # Performance (skipped when recording to video)
    res_divisor: float = 1.0

    def __attrs_post_init__(self) -> None:
        # round(np.int32 / float) == np.float32, but we want int.
        assert isinstance(self.width, (int, float))
        assert isinstance(self.height, (int, float))

    def before_preview(self) -> None:
        """ Called *once* before preview. Decreases render resolution/etc. """
        self.width = round(self.width / self.res_divisor)
        self.height = round(self.height / self.res_divisor)
        self.line_width /= self.res_divisor

    def before_record(self) -> None:
        """ Called *once* before recording video. Does nothing yet. """
        pass
Exemple #5
0
class MainTriggerConfig(_TriggerConfig,
                        KeywordAttrs,
                        always_dump="edge_direction post_trigger post_radius"):
    if TYPE_CHECKING:

        def __call__(self, wave: "Wave", *args, **kwargs) -> "MainTrigger":
            return self.cls(wave, self, *args, **kwargs)

    # Must be 1 or -1.
    # MainTrigger.__init__() multiplies `wave.amplification *= edge_direction`.
    # get_trigger() should ignore `edge_direction` and look for rising edges.
    edge_direction: int = 1

    # Optional trigger for postprocessing
    post_trigger: Optional["PostTriggerConfig"] = None
    post_radius: Optional[int] = with_units("smp", default=3)

    def __attrs_post_init__(self):
        if self.edge_direction not in [-1, 1]:
            raise CorrError(
                f"{obj_name(self)}.edge_direction must be {{-1, 1}}")

        if self.post_trigger:
            self.post_trigger.parent = self
            if self.post_radius is None:
                name = obj_name(self)
                raise CorrError(
                    f"Cannot supply {name}.post_trigger without supplying {name}.post_radius"
                )
Exemple #6
0
class Font(DumpableAttrs, always_dump="*"):
    # Font file selection
    family: Optional[str] = None
    bold: bool = False
    italic: bool = False
    # Font size
    size: float = with_units("pt", default=20)
    # QFont implementation details
    toString: str = None
Exemple #7
0
class Config(
    KeywordAttrs,
    always_dump="""
    begin_time end_time
    render_subfps trigger_subsampling render_subsampling
    trigger_stereo render_stereo
    """,
):
    """Default values indicate optional attributes."""

    master_audio: Optional[str]
    begin_time: float = with_units("s", default=0)
    end_time: Optional[float] = None

    fps: int

    trigger_ms: int = with_units("ms")
    render_ms: int = with_units("ms")

    # Performance
    trigger_subsampling: int = 1
    render_subsampling: int = 1

    # Performance (skipped when recording to video)
    render_subfps: int = 2
    render_fps = property(lambda self: Fraction(self.fps, self.render_subfps))
    # FFmpeg accepts FPS as a fraction. (decimals may work, but are inaccurate.)

    # Both before_* functions should be idempotent, AKA calling twice does no harm.
    def before_preview(self) -> None:
        """Called *once* before preview. Does nothing."""
        self.render.before_preview()

    def before_record(self) -> None:
        """Called *once* before recording video. Force high-quality rendering."""
        self.render_subfps = 1
        self.trigger_subsampling = 1
        self.render_subsampling = 1
        self.render.before_record()

    # End Performance
    amplification: float

    # Stereo config
    trigger_stereo: FlattenOrStr = Flatten.SumAvg
    render_stereo: FlattenOrStr = Flatten.SumAvg

    trigger: CorrelationTriggerConfig  # Can be overriden per Wave

    # Multiplies by trigger_width, render_width. Can override trigger.
    channels: List[ChannelConfig]
    default_label: DefaultLabel = DefaultLabel.NoLabel

    layout: LayoutConfig
    render: RendererConfig
    ffmpeg_cli: FFmpegOutputConfig = attr.ib(factory=lambda: FFmpegOutputConfig(None))

    def get_ffmpeg_cfg(self, video_path: str) -> FFmpegOutputConfig:
        return attr.evolve(self.ffmpeg_cli, path=os.path.abspath(video_path))

    benchmark_mode: BenchmarkMode = attr.ib(
        BenchmarkMode.NONE, converter=BenchmarkMode.by_name
    )
Exemple #8
0
 class Foo(DumpableAttrs):
     xs: int = with_units("xs")
     ys: int = with_units("ys", default=2)
     no_unit: int = 3
Exemple #9
0
class CorrelationTriggerConfig(
        MainTriggerConfig,
        always_dump="""
    mean_responsiveness
    pitch_tracking
    slope_width
    """

        # deprecated
        " buffer_falloff ",
):
    # get_trigger()
    # Edge/area finding
    sign_strength: float = 0
    mean_responsiveness: float = 1.0
    edge_strength: float

    # Slope detection
    slope_width: float = with_units("period", default=0.25)

    # Correlation detection
    buffer_strength: float = 1

    # Below a specific correlation quality, discard the buffer entirely.
    reset_below: float = 0

    # _update_buffer() (not in GUI)
    buffer_falloff: float = 0.5

    # Maximum distance to move (in terms of trigger_ms/trigger_samp) (not in GUI)
    trigger_diameter: float = 0.5

    # Maximum distance to move (in terms of estimated wave period) (not in GUI)
    trigger_radius_periods: Optional[float] = 1.5

    # (not in GUI)
    recalc_semitones: float = 1.0

    # _update_buffer
    responsiveness: float

    # Period/frequency estimation (not in GUI)
    max_freq: float = with_units("Hz", default=4000)

    # Pitch tracking = compute spectrum. (GUI only has a checkbox)
    pitch_tracking: Optional[SpectrumConfig] = None

    # region Legacy Aliases
    trigger_strength = Alias("edge_strength")
    falloff_width = Alias("buffer_falloff")
    data_falloff = Alias("trigger_radius_periods")

    # endregion

    def __attrs_post_init__(self) -> None:
        MainTriggerConfig.__attrs_post_init__(self)

        # Don't validate slope_width.

        validate_param(self, "responsiveness", 0, 1)
        # TODO trigger_falloff >= 0
        validate_param(self, "buffer_falloff", 0, np.inf)