Note
Go to the end to download the full example code.
Synchronize sound and trigger🔗
Often, a trigger must be emitted simultenously with a sound onset, with as little delay
and jitter as possible. With stimuli
, similarly to psychtoolbox
, the key concept
is to schedule the sound.
First, let’s have a look at our default device latency.
import sounddevice as sd
from stimuli.audio import SoundAM
from stimuli.time import sleep
from stimuli.trigger import MockTrigger
idx = sd.default.device["output"]
print(f"Low-latency (s): {sd.query_devices()[idx]['default_low_output_latency']}")
Low-latency (s): 0.008684807256235827
Next, we can schedule a sound with the argument when
which will use the clock
provided in the argument clock
to schedule the sound. Then, we wait for this
duration to elapse before sending the trigger.
trigger = MockTrigger() # replace with your trigger object
sound = SoundAM(
frequency_carrier=1000,
frequency_modulation=40,
method="dsbsc",
volume=10,
duration=1,
)
sound.play(when=0.5, blocking=False)
sleep(0.5)
trigger.signal(1)
[mock.signal] INFO: Mock set to 1.
A quick measurement on a dual-boot Windows/Linux computer connected to a USB Crimson 3
soundcard shows that the delay and jitter of stimuli
are similar to
psychtoolbox
on linux.
On different computers with different soundcards, the performance may vary. For instance, with on-board soundcards on Linux, both psychtoolbox and stimuli are usually perfectly synchronized with the trigger. In the end, the performance should always be measured on the target system.
from stimuli.audio import Tone
from stimuli.time import sleep
from stimuli.trigger import ParallelPortTrigger
sound = Tone(frequency=440, volume=100, duration=0.3)
trigger = ParallelPortTrigger("/dev/parport0")
for k in range(10):
sound.play(when=0.2)
sleep(0.2)
trigger.signal(1)
sleep(0.5)
Total running time of the script: (0 minutes 0.712 seconds)
Estimated memory usage: 149 MB