Play audio on a digital input
This demo presents a white square onscreen and waits for an input on a specified digital input channel.
When the channel detects activity, the time is recorded and a tone plays.
from pypixxlib import _libdpx as dp
from psychopy import core, visual
import numpy as np
### HELPER FUNCTIONS
def convertDinValuesToList():
''' Some nice formatting of DinValues as a list that aligns bit position with VPixx dout vals
eg. [1,1,1 .... 1] corresponds to [statusDin0, statusDin1, statusDin2 ... statusDin25]
Makes it easy to query individual bits to see their current state'''
#get the activity of all channels as a binary string
dinString = bin(dp.DPxGetDinValue())
#convert binary to a list of values for indexing
dinList = list(map(int, list(dinString[2:])))
#reverse list so it runs from least to most sig, thus aligning with VPixx dout labelling (e.g., position 0 corresponds with dout0, etc)
dinList.reverse()
return dinList
def getTriggerDefaultValue(channel=0):
'''Call at the beginning of experiment to get nonactive trigger value.
Some systems have a default of 1, while others are 0, so it is best to check'''
#get latest status of box
dp.DPxUpdateRegCache()
#get list of default port values
noActivityList = convertDinValuesToList()
#check the specific channel
result = noActivityList[channel]
return result
def listenForTrigger(channel=0, noActivity=0):
'''Listens for a trigger on the digital input channel specified by channel
and returns when the trigger is detected'''
while True:
#get latest status of box
dp.DPxUpdateRegCache()
#get current list of port values
currentActivity = convertDinValuesToList()
#get box time
time = dp.DPxGetTime()
#if channel value is not "off" as specified by noActivity, break out of listener
if currentActivity[channel] != noActivity:
break
return time
def generateBeep(frequency=400, duration=2):
'''Make a little beep for testing'''
# Audio settings
sample_rate = 44100 # Samples per second
# Generate time values from 0 to duration
t = np.arange(0, duration, 1/sample_rate)
# Generate a 400Hz sine wave
audio_data = np.sin(2 * np.pi * frequency * t)
# Normalize the audio data to be in the range of [-1, 1]
audio_data_normalized = audio_data / np.max(np.abs(audio_data))
return audio_data_normalized
def setupBoxAudio(myAudio):
''' Does all the fiddly audio setup'''
dp.DPxSetAudVolume(0.5) #between 0 and 1
#Some audio parameters
audBufferAddress = int(16e6)
audioSize=len(myAudio)
onset = 0.0 #no delay
#setup the box audio with a few writing/formatting calls
dp.DPxWriteAudioBuffer(myAudio, audBufferAddress)
dp.DPxSetAudioSchedule(onset, 44100, audioSize, 'mono', audBufferAddress)
#push updates to the box
dp.DPxWriteRegCache()
def playBoxAudio():
dp.DPxStartAudSched()
dp.DPxWriteRegCache()
def stopBoxAudio():
dp.DPxStopAudSched()
dp.DPxWriteRegCache()
### EXPERIMENT START ###
#connect to VPixx hardware
dp.DPxOpen()
#set the dout channel where you expect the trigger to appear.
#Here it's 0. Get the default value for that channel
triggerChannel = 0
noTrigger = getTriggerDefaultValue(triggerChannel)
#create and prepare audio for playback on box
myAudio = generateBeep(400,2)
setupBoxAudio(myAudio)
#Present a stimulus onscreen:
win = visual.Window(screen=1, fullscr=True, color='black', units='pix')
stimulus = visual.Rect(win, width=100, height=100, fillColor=[1,1,1], pos=(0, 0))
stimulus.draw()
#Get a box timestamp on next video flip, save this as startTime
dp.DPxUpdateRegCacheAfterVideoSync()
win.flip()
startTime = dp.DPxGetTime()
#Wait for our trigger and log the time the trigger was recorded
trigTime = listenForTrigger(triggerChannel, noTrigger)
#Play tone and flip display
playBoxAudio()
win.flip()
#Wait 2 seconds and stop playback
core.wait(2)
stopBoxAudio()
#Print some results and shut down
print('Trigger time: ', trigTime-startTime, ' seconds.')
win.close()