Skip to content

Commit

Permalink
Merge pull request psychopy#6953 from TEParsons/dev-rf-speaker-stream-v2
Browse files Browse the repository at this point in the history
RF: Handle psychtoolbox streams per-speaker rather than per-sound
  • Loading branch information
TEParsons authored Nov 1, 2024
2 parents 2e3c396 + 23e6951 commit 19b77b3
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 389 deletions.
20 changes: 1 addition & 19 deletions psychopy/experiment/components/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def __init__(
self.depends = []
self.order = [
'expName', 'expVersion',
'Audio lib', 'Audio latency priority', "Force stereo", # Audio tab
'Audio lib', "Force stereo", # Audio tab
'HTML path', 'exportHTML', 'Completed URL', 'Incomplete URL', 'End Message', 'Resources', # Online tab
]
self.depends = []
Expand Down Expand Up @@ -379,20 +379,6 @@ def getVersions():
hint=_translate("Which Python sound engine do you want to play your sounds?"),
label=_translate("Audio library"), categ='Audio')

audioLatencyLabels = [
'0: ' + _translate('Latency not important'),
'1: ' + _translate('Share low-latency driver'),
'2: ' + _translate('Exclusive low-latency'),
'3: ' + _translate('Aggressive low-latency'),
'4: ' + _translate('Latency critical'),
]
self.params['Audio latency priority'] = Param(
'3', valType='str', inputType="choice",
allowedVals=['0', '1', '2', '3', '4'],
allowedLabels=audioLatencyLabels,
hint=_translate("How important is audio latency for you? If essential then you may need to get all your sounds in correct formats."),
label=_translate("Audio latency priority"), categ='Audio')

# --- Data params ---
self.order += [
"Data filename",
Expand Down Expand Up @@ -920,10 +906,6 @@ def writeInitCode(self, buff, version, localDateTime):
buff.writelines(
"prefs.hardware['audioLib'] = {}\n".format(self.params['Audio lib'])
)
if self.params['Audio latency priority'].val.lower() != 'use prefs':
buff.writelines(
"prefs.hardware['audioLatencyMode'] = {}\n".format(self.params['Audio latency priority'])
)
buff.write(
"from psychopy import %s\n" % ', '.join(psychopyImports) +
"from psychopy.tools import environmenttools\n"
Expand Down
70 changes: 48 additions & 22 deletions psychopy/experiment/components/sound/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,28 @@ class SoundComponent(BaseDeviceComponent):
validatorClasses = ["AudioValidatorRoutine"]

def __init__(
self,
exp, parentName,
# basic
name='sound_1',
sound='A',
startType='time (s)', startVal='0.0',
stopType='duration (s)', stopVal='1.0',
startEstim='', durationEstim='',
syncScreenRefresh=True,
# device
deviceLabel="",
speakerIndex=-1,
# playback
volume=1,
stopWithRoutine=True,
forceEndRoutine=False,
# testing
validator="",
disabled=False,
):
self,
exp, parentName,
# basic
name='sound_1',
sound='A',
startType='time (s)', startVal='0.0',
stopType='duration (s)', stopVal='1.0',
startEstim='', durationEstim='',
syncScreenRefresh=True,
# device
deviceLabel="",
speakerIndex=-1,
resampling="load",
exclusive=False,
# playback
volume=1,
stopWithRoutine=True,
forceEndRoutine=False,
# testing
validator="",
disabled=False,
):
super(SoundComponent, self).__init__(
exp, parentName, name,
startType=startType, startVal=startVal,
Expand Down Expand Up @@ -117,7 +119,9 @@ def __init__(

# --- Device params ---
self.order += [
"speakerIndex"
"speakerIndex",
"resampling",
"exclusive",
]
def getSpeakerLabels():
from psychopy.hardware.speaker import SpeakerDevice
Expand All @@ -143,6 +147,26 @@ def getSpeakerValues():
"What speaker to play this sound on"
),
label=_translate("Speaker"))
self.params['resampling'] = Param(
resampling, valType="str", inputType="choice", categ="Device",
allowedVals=["load", "play", "none"],
allowedLabels=[
_translate("On load"), _translate("When playing"), _translate("Do not resample")
],
label=_translate("Resampling"),
hint=_translate(
"If the sample rate of a clip doesn't match the sample rate of the speaker, when "
"should resampling happen?"
)
)
self.params['exclusive'] = Param(
exclusive, valType="code", inputType="bool", categ="Device",
label=_translate("Exclusive control"),
hint=_translate(
"Take exclusive control of the speaker, so other apps can't use it during your "
"experiment."
)
)

# --- Testing ---
self.params['validator'] = Param(
Expand All @@ -163,7 +187,9 @@ def writeDeviceCode(self, buff):
"deviceManager.addDevice(\n"
" deviceName=%(deviceLabel)s,\n"
" deviceClass='psychopy.hardware.speaker.SpeakerDevice',\n"
" index=%(speakerIndex)s\n"
" index=%(speakerIndex)s,\n"
" resampling=%(resampling)s,\n"
" exclusive=%(exclusive)s,\n"
")\n"
)
buff.writeOnceIndentedLines(code % inits)
Expand Down
1 change: 1 addition & 0 deletions psychopy/experiment/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def _findParam(name, node):
# version" warnings
legacyParams = [
'lineColorSpace', 'borderColorSpace', 'fillColorSpace', 'foreColorSpace', # 2021.1, we standardised colorSpace to be object-wide rather than param-specific
'Audio latency priority', # from 2025.1, latency priority is handled by a combination of exclusivity and resampling settings
]

class Param():
Expand Down
Loading

0 comments on commit 19b77b3

Please sign in to comment.