def verify_res_divisor_rounding( target_int: int, res_divisor: float, speed_hack: bool, ): """Ensure that pathological-case float rounding errors don't cause inconsistent dimensions and assertion errors.""" target_dim = target_int + 0.5 undivided_dim = round(target_dim * res_divisor) cfg = RendererConfig(undivided_dim, undivided_dim, res_divisor=res_divisor) cfg.before_preview() with ExitStack() as stack: if speed_hack: stack.enter_context( patch.object(AbstractMatplotlibRenderer, "_save_background") ) datas = [] else: datas = [RENDER_Y_ZEROS] try: renderer = Renderer(cfg, LayoutConfig(), datas, None, None) if not speed_hack: renderer.update_main_lines(datas) renderer.get_frame() except Exception: perr(cfg.divided_width) raise
def template_config(**kwargs) -> Config: """Default template values do NOT indicate optional attributes.""" cfg = Config( master_audio="", fps=_FPS, amplification=1, trigger_ms=40, render_ms=40, trigger_subsampling=1, render_subsampling=2, trigger=CorrelationTriggerConfig( mean_responsiveness=0.0, edge_strength=1.0, reset_below=0.3, responsiveness=0.5, pitch_tracking=SpectrumConfig(), # post_trigger=ZeroCrossingTriggerConfig(), ), channels=[], layout=LayoutConfig(orientation="v", ncols=1), render=RendererConfig( 1280, 720, res_divisor=4 / 3, grid_color="#55aaff", v_midline=True, h_midline=True, ), ) return attr.evolve(cfg, **kwargs)
def default_config(**kwargs) -> Config: """ Default template values do NOT indicate optional attributes. """ cfg = Config( render_subfps=1, master_audio="", fps=_FPS, amplification=1, trigger_ms=40, render_ms=40, trigger_subsampling=1, render_subsampling=2, trigger=CorrelationTriggerConfig( edge_strength=2, responsiveness=0.5, buffer_falloff=0.5, use_edge_trigger=False, # Removed due to speed hit. # post=LocalPostTriggerConfig(strength=0.1), ), channels=[], layout=LayoutConfig(orientation="v", ncols=1), render=RendererConfig( 1280, 720, res_divisor=4 / 3, midline_color="#404040", v_midline=True, h_midline=True, ), ) return attr.evolve(cfg, **kwargs)
def test_label_render(label_position: LabelPosition, data, hide_lines): """Test that text labels are drawn: - in the correct quadrant - with the correct color (defaults to init_line_color) - even if no lines are drawn at all """ font_str = "#FF00FF" font_u8 = color_to_bytes(font_str) # If hide_lines: set line color to purple, draw text using the line color. # Otherwise: draw lines white, draw text purple, cfg_kwargs = {} if hide_lines: cfg_kwargs.update(init_line_color=font_str) cfg = RendererConfig( WIDTH, HEIGHT, antialiasing=False, label_font=Font(size=16, bold=True), label_position=label_position, label_color_override=font_str, **cfg_kwargs, ) lcfg = LayoutConfig() nplots = 1 labels = ["#"] * nplots datas = [data] * nplots r = Renderer(cfg, lcfg, datas, None, None) r.add_labels(labels) if not hide_lines: r.update_main_lines(datas) frame_buffer: np.ndarray = np.frombuffer(r.get_frame(), dtype=np.uint8).reshape( (r.h, r.w, BYTES_PER_PIXEL) ) # Allow mutation frame_buffer = frame_buffer.copy() yslice = label_position.y.match( top=slice(None, r.h // 2), bottom=slice(r.h // 2, None) ) xslice = label_position.x.match( left=slice(None, r.w // 2), right=slice(r.w // 2, None) ) quadrant = frame_buffer[yslice, xslice] assert np.prod(quadrant == font_u8, axis=-1).any(), "Missing text" quadrant[:] = 0 assert not np.prod( frame_buffer == font_u8, axis=-1 ).any(), "Text appeared in wrong area of screen"
def test_renderer_layout(): # 2 columns cfg = RendererConfig(WIDTH, HEIGHT) lcfg = LayoutConfig(ncols=2) nplots = 15 r = MatplotlibRenderer(cfg, lcfg, nplots, None) r.render_frame([RENDER_Y_ZEROS] * nplots) layout = r.layout # 2 columns, 8 rows assert layout.wave_ncol == 2 assert layout.wave_nrow == 8
def test_renderer_layout(): # 2 columns cfg = RendererConfig(WIDTH, HEIGHT) lcfg = LayoutConfig(ncols=2) nplots = 15 datas = [RENDER_Y_ZEROS] * nplots r = Renderer(cfg, lcfg, datas, None, None) r.update_main_lines(RenderInput.wrap_datas(datas), [0] * nplots) layout = r.layout # 2 columns, 8 rows assert layout.wave_ncol == 2 assert layout.wave_nrow == 8
def test_stereo_render_integration(mocker: "pytest_mock.MockFixture"): """Ensure corrscope plays/renders in stereo, without crashing.""" # Stub out FFplay output. mocker.patch.object(FFplayOutputConfig, "cls") # Render in stereo. cfg = template_config( channels=[ChannelConfig("tests/stereo in-phase.wav")], render_stereo=Flatten.Stereo, end_time=0.5, # Reduce test duration render=RendererConfig(WIDTH, HEIGHT), ) # Make sure it doesn't crash. corr = CorrScope(cfg, Arguments(".", [FFplayOutputConfig()])) corr.play()
def get_renderer_config(appear: Appearance) -> RendererConfig: cfg = RendererConfig( WIDTH, HEIGHT, # BG bg_color=appear.bg.color, # FG init_line_color=appear.fg.color, line_width=appear.fg.line_width, viewport_width=appear.debug.viewport_width, # Grid grid_color=appear.grid.color, grid_line_width=appear.grid.line_width, stereo_grid_opacity=OPACITY, antialiasing=False, ) return cfg
def test_line_colors(bg_str, fg_str, grid_str, data): """ Test channel-specific line color overrides """ cfg = RendererConfig( WIDTH, HEIGHT, bg_color=bg_str, init_line_color="#888888", grid_color=grid_str, stereo_grid_opacity=OPACITY, line_width=2.0, antialiasing=False, ) lcfg = LayoutConfig() chan = ChannelConfig(wav_path="", line_color=fg_str) channels = [chan] * NPLOTS r = MatplotlibRenderer(cfg, lcfg, NPLOTS, channels) verify(r, bg_str, fg_str, grid_str, data)
def test_default_colors(bg_str, fg_str, grid_str, data): """ Test the default background/foreground colors. """ cfg = RendererConfig( WIDTH, HEIGHT, bg_color=bg_str, init_line_color=fg_str, grid_color=grid_str, stereo_grid_opacity=OPACITY, line_width=2.0, antialiasing=False, ) lcfg = LayoutConfig() r = MatplotlibRenderer(cfg, lcfg, NPLOTS, None) verify(r, bg_str, fg_str, grid_str, data) # Ensure default ChannelConfig(line_color=None) does not override line color chan = ChannelConfig(wav_path="") channels = [chan] * NPLOTS r = MatplotlibRenderer(cfg, lcfg, NPLOTS, channels) verify(r, bg_str, fg_str, grid_str, data)
popen.stdin.write.side_effect = exc popen.wait.return_value = 0 return popen Popen = mocker.patch.object(subprocess, "Popen", autospec=True) Popen.side_effect = popen_factory return Popen class DummyException(Exception): pass NULL_FFMPEG_OUTPUT = FFmpegOutputConfig(None, "-f null") render_cfg = RendererConfig(WIDTH, HEIGHT) CFG = default_config(render=render_cfg) def sine440_config(): cfg = default_config( channels=[ChannelConfig("tests/sine440.wav")], master_audio="tests/sine440.wav", end_time=0.5, # Reduce test duration render=render_cfg, ) return cfg ## Begin tests # Calls MatplotlibRenderer, FFmpegOutput, FFmpeg.