A-V sync test#

This example tests synchronization between the screen and the auditory/visual playback. If a given machine (experimenta or development) is configured correctly:

  1. The inter-flip interval should be 1 / refresh_rate, i.e., ~16 ms for a 60 Hz display.

  2. The red rectangle should correspond to a typical credit card size (~3 3/8” x 2 1/8”).

If you test using an oscilloscope, which is required for actual subject presentation:

  1. There should be no jitter between the trigger and auditory or visual display when hooking the auditory output and photodiode to an oscilloscope.

  2. The auditory and visual onset should be aligned (no fixed delay between them) when viewed with an oscilloscope.

A fixed trigger-to-AV delay can in principle be adjusted afterward via analysis changes, and can be assessed using this script and an oscilloscope.

Warning

Fullscreen must be used to guarantee flip accuracy! Also, ensure that if you are using a projector, your computer screen resolution (and "SCREEN_SIZE_PIX") are configured to use the native resolution of the projector, as resolution conversion can lead to visual display jitter.

sync test
exp_name: SyncTest
date: 2024-10-08 13_43_17.581640
file: /home/circleci/project/examples/sync/sync_test.py
participant: s
session: 0
2024-10-08 13:43:17,581 - INFO    - Expyfun: Using version 2.0.0.dev0 (requested dev)
2024-10-08 13:43:17,582 - INFO    - Expyfun: Setting up sound card using pyglet backend with 2 playback channels
2024-10-08 13:43:17,744 - WARNING - Expyfun: Mismatch between reported stim sample rate (24414) and device sample rate (44100.0). Nothing will be done about this because suppress_resamp is "True"
2024-10-08 13:43:17,744 - INFO    - Expyfun: Setting up screen
2024-10-08 13:43:17,778 - EXP     - Expyfun: Set screen visibility True
2024-10-08 13:43:17,793 - INFO    - Initialized [1400  900] window on screen XlibScreen(display=<pyglet.canvas.xlib.XlibDisplay object at 0x7f392c363f10>, x=0, y=0, width=1400, height=900, xinerama=0) with DPI 69.73
2024-10-08 13:43:17,793 - INFO    - Expyfun: Initializing dummy triggering mode
2024-10-08 13:43:17,795 - INFO    - Expyfun: Initialization complete
2024-10-08 13:43:17,795 - EXP     - Expyfun: Participant: s
2024-10-08 13:43:17,795 - EXP     - Expyfun: Session: 0
2024-10-08 13:43:17,797 - EXP     - Expyfun: Loading 200 samples to buffer
2024-10-08 13:43:17,807 - EXP     - Expyfun: Starting stimuli: flipping screen and playing audio
2024-10-08 13:43:17,809 - EXP     - Stamping TTL triggers: [1]
2024-10-08 13:43:17,896 - EXP     - Stamping TTL triggers: [2, 4, 8]
2024-10-08 13:43:17,956 - EXP     - Expyfun: Audio stopped and reset.
2024-10-08 13:43:17,957 - INFO    - Expyfun: Exiting
2024-10-08 13:43:17,962 - EXP     - Expyfun: Audio stopped and reset.

# Author: Dan McCloy <drmccloy@uw.edu>
#
# License: BSD (3-clause)

import numpy as np

import expyfun.analyze as ea
from expyfun import ExperimentController, building_doc
from expyfun.visual import Circle, Rectangle

n_channels = 2
click_idx = [0]
with ExperimentController(
    "SyncTest",
    full_screen=True,
    noise_db=-np.inf,
    participant="s",
    session="0",
    output_dir=None,
    suppress_resamp=True,
    check_rms=None,
    n_channels=n_channels,
    version="dev",
) as ec:
    click = np.r_[0.1, np.zeros(99)]  # RMS = 0.01
    data = np.zeros((n_channels, len(click)))
    data[click_idx] = click
    ec.load_buffer(data)
    pressed = None
    screenshot = None
    # Make a circle so that the photodiode can be centered on the screen
    circle = Circle(ec, 1, units="deg", fill_color="k", line_color="w")
    # Make a rectangle that is the standard credit card size
    rect = Rectangle(ec, [0, 0, 8.56, 5.398], "cm", None, "#AA3377")
    while pressed != "8":  # enable a clean quit if required
        ec.set_background_color("white")
        t1 = ec.start_stimulus(start_of_trial=False)  # skip checks
        ec.set_background_color("black")
        t2 = ec.flip()
        diff = round(1000 * (t2 - t1), 2)
        ec.screen_text(f"IFI (ms): {diff}", wrap=True)
        circle.draw()
        rect.draw()
        screenshot = ec.screenshot() if screenshot is None else screenshot
        ec.flip()
        ec.stamp_triggers([2, 4, 8])
        ec.refocus()
        pressed = ec.wait_one_press(0.5)[0] if not building_doc else "8"
        ec.stop()

ea.plot_screen(screenshot)

Total running time of the script: (0 minutes 0.470 seconds)

Gallery generated by Sphinx-Gallery