psychopy 2025.1.0__py3-none-any.whl → 2025.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of psychopy might be problematic. Click here for more details.
- psychopy/VERSION +1 -1
- psychopy/alerts/alertsCatalogue/4810.yaml +19 -0
- psychopy/alerts/alertsCatalogue/alertCategories.yaml +4 -0
- psychopy/alerts/alertsCatalogue/alertmsg.py +15 -1
- psychopy/alerts/alertsCatalogue/generateAlertmsg.py +2 -2
- psychopy/app/Resources/classic/add_many.png +0 -0
- psychopy/app/Resources/classic/add_many@2x.png +0 -0
- psychopy/app/Resources/classic/devices.png +0 -0
- psychopy/app/Resources/classic/devices@2x.png +0 -0
- psychopy/app/Resources/classic/photometer.png +0 -0
- psychopy/app/Resources/classic/photometer@2x.png +0 -0
- psychopy/app/Resources/dark/add_many.png +0 -0
- psychopy/app/Resources/dark/add_many@2x.png +0 -0
- psychopy/app/Resources/dark/devices.png +0 -0
- psychopy/app/Resources/dark/devices@2x.png +0 -0
- psychopy/app/Resources/dark/photometer.png +0 -0
- psychopy/app/Resources/dark/photometer@2x.png +0 -0
- psychopy/app/Resources/light/add_many.png +0 -0
- psychopy/app/Resources/light/add_many@2x.png +0 -0
- psychopy/app/Resources/light/devices.png +0 -0
- psychopy/app/Resources/light/devices@2x.png +0 -0
- psychopy/app/Resources/light/photometer.png +0 -0
- psychopy/app/Resources/light/photometer@2x.png +0 -0
- psychopy/app/_psychopyApp.py +35 -13
- psychopy/app/builder/builder.py +88 -35
- psychopy/app/builder/dialogs/__init__.py +69 -220
- psychopy/app/builder/dialogs/dlgsCode.py +29 -8
- psychopy/app/builder/dialogs/paramCtrls.py +1468 -904
- psychopy/app/builder/validators.py +25 -17
- psychopy/app/coder/coder.py +12 -1
- psychopy/app/coder/repl.py +5 -2
- psychopy/app/colorpicker/__init__.py +1 -1
- psychopy/app/deviceManager/__init__.py +1 -0
- psychopy/app/deviceManager/addDialog.py +218 -0
- psychopy/app/deviceManager/dialog.py +185 -0
- psychopy/app/deviceManager/panel.py +191 -0
- psychopy/app/deviceManager/utils.py +60 -0
- psychopy/app/idle.py +7 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +12695 -10592
- psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/da_DK/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +11221 -9712
- psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/el_GR/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_NZ/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_US/LC_MESSAGE/messages.po +10195 -18
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +11917 -9101
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +11924 -9103
- psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_US/LC_MESSAGE/messages.po +11917 -9101
- psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +11084 -9569
- psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +11590 -5806
- psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fi_FI/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +11091 -9577
- psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +11072 -9549
- psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +11071 -9559
- psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hu_HU/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +11072 -9560
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +1485 -1137
- psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ko_KR/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +11463 -8757
- psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nl_NL/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nn_NO/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pl_PL/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +11288 -9434
- psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ro_RO/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ru_RU/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +11441 -8747
- psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +11069 -9545
- psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +12085 -8268
- psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +11929 -8022
- psychopy/app/plugin_manager/dialog.py +12 -3
- psychopy/app/plugin_manager/packageIndex.py +303 -0
- psychopy/app/plugin_manager/packages.py +203 -63
- psychopy/app/plugin_manager/plugins.py +120 -240
- psychopy/app/preferencesDlg.py +6 -1
- psychopy/app/psychopyApp.py +16 -4
- psychopy/app/runner/runner.py +10 -2
- psychopy/app/runner/scriptProcess.py +8 -3
- psychopy/app/stdout/stdOutRich.py +11 -4
- psychopy/app/themes/icons.py +3 -0
- psychopy/app/utils.py +61 -0
- psychopy/colors.py +10 -5
- psychopy/data/experiment.py +133 -23
- psychopy/data/routine.py +12 -0
- psychopy/data/staircase.py +42 -20
- psychopy/data/trial.py +20 -12
- psychopy/data/utils.py +43 -3
- psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +22 -5
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solutions.xlsx +0 -0
- psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +2 -12
- psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +3 -8
- psychopy/demos/builder/Feature Demos/movies/movie.psyexp +220 -0
- psychopy/demos/builder/Feature Demos/movies/readme.md +3 -0
- psychopy/demos/builder/Feature Demos/visualValidator/visualValidator.psyexp +1 -2
- psychopy/demos/builder/Hardware/camera/camera.psyexp +3 -16
- psychopy/demos/builder/Hardware/microphone/microphone.psyexp +3 -16
- psychopy/demos/coder/hardware/hdf5_extract.py +133 -0
- psychopy/event.py +20 -15
- psychopy/experiment/_experiment.py +86 -10
- psychopy/experiment/components/__init__.py +3 -10
- psychopy/experiment/components/_base.py +9 -20
- psychopy/experiment/components/button/__init__.py +1 -1
- psychopy/experiment/components/buttonBox/__init__.py +50 -54
- psychopy/experiment/components/camera/__init__.py +137 -359
- psychopy/experiment/components/keyboard/__init__.py +17 -24
- psychopy/experiment/components/microphone/__init__.py +61 -110
- psychopy/experiment/components/movie/__init__.py +2 -3
- psychopy/experiment/components/serialOut/__init__.py +192 -93
- psychopy/experiment/components/settings/__init__.py +45 -27
- psychopy/experiment/components/sound/__init__.py +82 -73
- psychopy/experiment/components/soundsensor/__init__.py +43 -80
- psychopy/experiment/devices.py +303 -0
- psychopy/experiment/exports.py +20 -18
- psychopy/experiment/flow.py +7 -0
- psychopy/experiment/loops.py +47 -29
- psychopy/experiment/monitor.py +74 -0
- psychopy/experiment/params.py +48 -10
- psychopy/experiment/plugins.py +28 -108
- psychopy/experiment/py2js_transpiler.py +1 -1
- psychopy/experiment/routines/__init__.py +1 -1
- psychopy/experiment/routines/_base.py +59 -24
- psychopy/experiment/routines/audioValidator/__init__.py +19 -155
- psychopy/experiment/routines/visualValidator/__init__.py +25 -25
- psychopy/hardware/__init__.py +20 -57
- psychopy/hardware/button.py +15 -2
- psychopy/hardware/camera/__init__.py +2237 -1394
- psychopy/hardware/joystick/__init__.py +1 -1
- psychopy/hardware/keyboard.py +5 -8
- psychopy/hardware/listener.py +4 -1
- psychopy/hardware/manager.py +75 -35
- psychopy/hardware/microphone.py +53 -7
- psychopy/hardware/monitor.py +144 -0
- psychopy/hardware/photometer/__init__.py +156 -117
- psychopy/hardware/serialdevice.py +16 -2
- psychopy/hardware/soundsensor.py +4 -1
- psychopy/iohub/devices/deviceConfigValidation.py +2 -1
- psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +2 -2
- psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/__init__.py +1 -0
- psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/eyetracker.py +10 -0
- psychopy/iohub/devices/keyboard/darwin.py +8 -5
- psychopy/iohub/util/__init__.py +7 -8
- psychopy/localization/generateTranslationTemplate.py +208 -116
- psychopy/localization/messages.pot +4305 -3502
- psychopy/monitors/MonitorCenter.py +174 -74
- psychopy/plugins/__init__.py +6 -4
- psychopy/preferences/devices.py +80 -0
- psychopy/preferences/generateHints.py +2 -1
- psychopy/preferences/preferences.py +35 -11
- psychopy/scripts/psychopy-pkgutil.py +969 -0
- psychopy/scripts/psyexpCompile.py +1 -1
- psychopy/session.py +34 -38
- psychopy/sound/__init__.py +6 -260
- psychopy/sound/audioclip.py +164 -0
- psychopy/sound/backend_ptb.py +8 -0
- psychopy/sound/backend_pygame.py +10 -0
- psychopy/sound/backend_pysound.py +9 -0
- psychopy/sound/backends/__init__.py +0 -0
- psychopy/sound/microphone.py +3 -0
- psychopy/sound/sound.py +58 -0
- psychopy/tests/data/correctScript/python/correctNoiseStimComponent.py +1 -1
- psychopy/tests/data/duplicateHeaders.csv +2 -0
- psychopy/tests/test_app/test_builder/test_BuilderFrame.py +22 -7
- psychopy/tests/test_app/test_builder/test_CompileFromBuilder.py +0 -2
- psychopy/tests/test_data/test_utils.py +5 -1
- psychopy/tests/test_experiment/test_components/test_ButtonBoxComponent.py +22 -2
- psychopy/tests/test_hardware/test_ports.py +0 -12
- psychopy/tests/test_tools/test_stringtools.py +1 -1
- psychopy/tools/attributetools.py +12 -5
- psychopy/tools/fontmanager.py +17 -14
- psychopy/tools/gltools.py +4 -2
- psychopy/tools/movietools.py +43 -2
- psychopy/tools/stringtools.py +33 -8
- psychopy/tools/versionchooser.py +1 -1
- psychopy/validation/audio.py +5 -1
- psychopy/validation/visual.py +5 -1
- psychopy/visual/basevisual.py +8 -7
- psychopy/visual/circle.py +2 -2
- psychopy/visual/helpers.py +3 -1
- psychopy/visual/image.py +29 -109
- psychopy/visual/movies/__init__.py +1800 -313
- psychopy/visual/polygon.py +4 -0
- psychopy/visual/shape.py +2 -2
- psychopy/visual/window.py +35 -12
- psychopy/voicekey/__init__.py +41 -669
- psychopy/voicekey/labjack_vks.py +7 -48
- psychopy/voicekey/parallel_vks.py +7 -42
- psychopy/voicekey/vk_tools.py +114 -263
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/METADATA +20 -13
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/RECORD +222 -190
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/WHEEL +1 -1
- psychopy/visual/movies/players/__init__.py +0 -62
- psychopy/visual/movies/players/ffpyplayer_player.py +0 -1401
- psychopy/voicekey/demo_vks.py +0 -12
- psychopy/voicekey/signal.py +0 -42
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/entry_points.txt +0 -0
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -71,6 +71,11 @@ participantIdAliases = ('participant', 'Participant', 'Subject', 'Observer')
|
|
|
71
71
|
# pass
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
def getSoundBackends():
|
|
75
|
+
from psychopy.sound.sound import Sound
|
|
76
|
+
return list(Sound.getBackends())
|
|
77
|
+
|
|
78
|
+
|
|
74
79
|
class SettingsComponent:
|
|
75
80
|
"""This component stores general info about how to run the experiment
|
|
76
81
|
"""
|
|
@@ -250,8 +255,8 @@ class SettingsComponent:
|
|
|
250
255
|
"winBackend",
|
|
251
256
|
"Screen",
|
|
252
257
|
"Full-screen window",
|
|
253
|
-
"Show mouse",
|
|
254
258
|
"Window size (pixels)",
|
|
259
|
+
"Show mouse",
|
|
255
260
|
"Units",
|
|
256
261
|
"color",
|
|
257
262
|
"blendMode",
|
|
@@ -266,16 +271,36 @@ class SettingsComponent:
|
|
|
266
271
|
fullScr, valType='bool', inputType="bool", allowedTypes=[],
|
|
267
272
|
hint=_translate("Run the experiment full-screen (recommended)"),
|
|
268
273
|
label=_translate("Full-screen window"), categ='Screen')
|
|
274
|
+
self.params['Window size (pixels)'] = Param(
|
|
275
|
+
winSize, valType='list', inputType="single", allowedTypes=[],
|
|
276
|
+
hint=_translate("Size of window (if not fullscreen)"),
|
|
277
|
+
label=_translate("Window size (pixels)"), categ='Screen'
|
|
278
|
+
)
|
|
279
|
+
self.depends.append({
|
|
280
|
+
'dependsOn': "Full-screen window", # if...
|
|
281
|
+
'condition': "", # matches
|
|
282
|
+
'param': "Window size (pixels)", # then...
|
|
283
|
+
'true': "hide", # should...
|
|
284
|
+
'false': "show", # otherwise...
|
|
285
|
+
})
|
|
286
|
+
self.params['Show mouse'] = Param(
|
|
287
|
+
showMouse, valType='bool', inputType="bool", allowedTypes=[],
|
|
288
|
+
hint=_translate("Should the mouse be visible on screen? Only applicable for fullscreen experiments."),
|
|
289
|
+
label=_translate("Show mouse"), categ='Screen'
|
|
290
|
+
)
|
|
291
|
+
self.depends.append({
|
|
292
|
+
'dependsOn': "Full-screen window", # if...
|
|
293
|
+
'condition': "", # matches
|
|
294
|
+
'param': "Show mouse", # then...
|
|
295
|
+
'true': "show", # should...
|
|
296
|
+
'false': "hide", # otherwise...
|
|
297
|
+
})
|
|
269
298
|
self.params['winBackend'] = Param(
|
|
270
299
|
winBackend, valType='str', inputType="choice", categ="Screen",
|
|
271
300
|
allowedVals=plugins.getWindowBackends(),
|
|
272
301
|
hint=_translate("What Python package should be used behind the scenes for drawing to the window?"),
|
|
273
302
|
label=_translate("Window backend")
|
|
274
|
-
)
|
|
275
|
-
self.params['Window size (pixels)'] = Param(
|
|
276
|
-
winSize, valType='list', inputType="single", allowedTypes=[],
|
|
277
|
-
hint=_translate("Size of window (if not fullscreen)"),
|
|
278
|
-
label=_translate("Window size (pixels)"), categ='Screen')
|
|
303
|
+
)
|
|
279
304
|
self.params['Screen'] = Param(
|
|
280
305
|
screen, valType='num', inputType="spin", allowedTypes=[],
|
|
281
306
|
hint=_translate("Which physical screen to run on (1 or 2)"),
|
|
@@ -323,10 +348,6 @@ class SettingsComponent:
|
|
|
323
348
|
hint=_translate("Should new stimuli be added or averaged with "
|
|
324
349
|
"the stimuli that have been drawn already"),
|
|
325
350
|
label=_translate("Blend mode"), categ='Screen')
|
|
326
|
-
self.params['Show mouse'] = Param(
|
|
327
|
-
showMouse, valType='bool', inputType="bool", allowedTypes=[],
|
|
328
|
-
hint=_translate("Should the mouse be visible on screen? Only applicable for fullscreen experiments."),
|
|
329
|
-
label=_translate("Show mouse"), categ='Screen')
|
|
330
351
|
self.params['measureFrameRate'] = Param(
|
|
331
352
|
measureFrameRate, valType="bool", inputType="bool", categ="Screen",
|
|
332
353
|
label=_translate("Measure frame rate?"),
|
|
@@ -381,7 +402,7 @@ class SettingsComponent:
|
|
|
381
402
|
label=_translate("Force stereo"))
|
|
382
403
|
self.params['Audio lib'] = Param(
|
|
383
404
|
'ptb', valType='str', inputType="choice",
|
|
384
|
-
allowedVals=
|
|
405
|
+
allowedVals=getSoundBackends,
|
|
385
406
|
hint=_translate("Which Python sound engine do you want to play your sounds?"),
|
|
386
407
|
label=_translate("Audio library"), categ='Audio')
|
|
387
408
|
|
|
@@ -906,13 +927,6 @@ class SettingsComponent:
|
|
|
906
927
|
"from psychopy import prefs\n"
|
|
907
928
|
"from psychopy import plugins\n"
|
|
908
929
|
"plugins.activatePlugins()\n" # activates plugins
|
|
909
|
-
)
|
|
910
|
-
# adjust the prefs for this study if needed
|
|
911
|
-
if self.params['Audio lib'].val.lower() != 'use prefs':
|
|
912
|
-
buff.writelines(
|
|
913
|
-
"prefs.hardware['audioLib'] = {}\n".format(self.params['Audio lib'])
|
|
914
|
-
)
|
|
915
|
-
buff.write(
|
|
916
930
|
"from psychopy import %s\n" % ', '.join(psychopyImports) +
|
|
917
931
|
"from psychopy.tools import environmenttools\n"
|
|
918
932
|
"from psychopy.constants import (\n"
|
|
@@ -926,7 +940,8 @@ class SettingsComponent:
|
|
|
926
940
|
"from numpy.random import %s\n" % ', '.join(_numpyRandomImports) +
|
|
927
941
|
"import os # handy system and path functions\n" +
|
|
928
942
|
"import sys # to get file system encoding\n"
|
|
929
|
-
"\n"
|
|
943
|
+
"\n"
|
|
944
|
+
)
|
|
930
945
|
|
|
931
946
|
if not self.params['eyetracker'] == "None" or self.params['keyboardBackend'] == "ioHub":
|
|
932
947
|
code = (
|
|
@@ -1768,14 +1783,17 @@ class SettingsComponent:
|
|
|
1768
1783
|
" )\n"
|
|
1769
1784
|
)
|
|
1770
1785
|
buff.writeIndentedLines(code % inits)
|
|
1771
|
-
#
|
|
1772
|
-
for
|
|
1773
|
-
if
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1786
|
+
# setup devices from config
|
|
1787
|
+
for deviceName in self.exp.getRequiredDeviceNames():
|
|
1788
|
+
if deviceName in prefs.devices:
|
|
1789
|
+
# write device setup if possile
|
|
1790
|
+
prefs.devices[deviceName].writeDeviceCode(buff)
|
|
1791
|
+
elif deviceName is None:
|
|
1792
|
+
# if default, let init code handle device
|
|
1793
|
+
pass
|
|
1794
|
+
else:
|
|
1795
|
+
# alert if not
|
|
1796
|
+
alert(4810, strFields={'deviceName': deviceName})
|
|
1779
1797
|
|
|
1780
1798
|
code = (
|
|
1781
1799
|
"# return True if completed successfully\n"
|
|
@@ -11,6 +11,7 @@ from pathlib import Path
|
|
|
11
11
|
from psychopy.alerts._alerts import alert
|
|
12
12
|
from psychopy.experiment.components import BaseDeviceComponent, Param, getInitVals, \
|
|
13
13
|
_translate
|
|
14
|
+
from psychopy.experiment.devices import DeviceBackend
|
|
14
15
|
from psychopy.experiment.utils import canBeNumeric
|
|
15
16
|
from psychopy.tools.audiotools import knownNoteNames
|
|
16
17
|
|
|
@@ -23,6 +24,12 @@ class SoundComponent(BaseDeviceComponent):
|
|
|
23
24
|
tooltip = _translate('Sound: play recorded files or generated sounds', )
|
|
24
25
|
deviceClasses = ["psychopy.hardware.speaker.SpeakerDevice"]
|
|
25
26
|
validatorClasses = ["AudioValidatorRoutine"]
|
|
27
|
+
legacyParams = [
|
|
28
|
+
# old device setup params, no longer needed as this is handled by DeviceManager
|
|
29
|
+
"speakerIndex",
|
|
30
|
+
"resample",
|
|
31
|
+
"latencyClass",
|
|
32
|
+
]
|
|
26
33
|
|
|
27
34
|
def __init__(
|
|
28
35
|
self,
|
|
@@ -72,7 +79,7 @@ class SoundComponent(BaseDeviceComponent):
|
|
|
72
79
|
hnt = _translate("A sound can be a note name (e.g. A or Bf), a number"
|
|
73
80
|
" to specify Hz (e.g. 440) or a filename")
|
|
74
81
|
self.params['sound'] = Param(
|
|
75
|
-
sound, valType='str', inputType="
|
|
82
|
+
sound, valType='str', inputType="soundFile", allowedTypes=[], updates='set every repeat', categ='Basic',
|
|
76
83
|
allowedUpdates=['set every repeat'],
|
|
77
84
|
hint=hnt,
|
|
78
85
|
label=_translate("Sound"))
|
|
@@ -118,61 +125,6 @@ class SoundComponent(BaseDeviceComponent):
|
|
|
118
125
|
),
|
|
119
126
|
label=_translate("Force end of Routine"))
|
|
120
127
|
|
|
121
|
-
# --- Device params ---
|
|
122
|
-
self.order += [
|
|
123
|
-
"speakerIndex",
|
|
124
|
-
"resample",
|
|
125
|
-
"latencyClass",
|
|
126
|
-
]
|
|
127
|
-
def getSpeakerLabels():
|
|
128
|
-
from psychopy.hardware.speaker import SpeakerDevice
|
|
129
|
-
labels = [_translate("From preferences")]
|
|
130
|
-
for profile in SpeakerDevice.getAvailableDevices():
|
|
131
|
-
labels.append(profile['deviceName'])
|
|
132
|
-
|
|
133
|
-
return labels
|
|
134
|
-
|
|
135
|
-
def getSpeakerValues():
|
|
136
|
-
from psychopy.hardware.speaker import SpeakerDevice
|
|
137
|
-
vals = [""]
|
|
138
|
-
for profile in SpeakerDevice.getAvailableDevices():
|
|
139
|
-
vals.append(profile['index'])
|
|
140
|
-
|
|
141
|
-
return vals
|
|
142
|
-
|
|
143
|
-
self.params['speakerIndex'] = Param(
|
|
144
|
-
speakerIndex, valType="str", inputType="choice", categ="Device",
|
|
145
|
-
allowedVals=getSpeakerValues,
|
|
146
|
-
allowedLabels=getSpeakerLabels,
|
|
147
|
-
hint=_translate(
|
|
148
|
-
"What speaker to play this sound on"
|
|
149
|
-
),
|
|
150
|
-
label=_translate("Speaker"))
|
|
151
|
-
self.params['resample'] = Param(
|
|
152
|
-
resample, valType="str", inputType="bool", categ="Device",
|
|
153
|
-
label=_translate("Resample"),
|
|
154
|
-
hint=_translate(
|
|
155
|
-
"If the sample rate of a clip doesn't match the sample rate of the speaker, should "
|
|
156
|
-
"we resample it to match?"
|
|
157
|
-
)
|
|
158
|
-
)
|
|
159
|
-
self.params['latencyClass'] = Param(
|
|
160
|
-
latencyClass, valType="code", inputType="choice", categ="Device",
|
|
161
|
-
allowedVals=[0, 1, 2, 3, 4],
|
|
162
|
-
allowedLabels=[
|
|
163
|
-
_translate("Shared"),
|
|
164
|
-
_translate("Shared low-latency (recommended)"),
|
|
165
|
-
_translate("Exclusive low-latency"),
|
|
166
|
-
_translate("Exclusive aggressive low-latency (with fallback)"),
|
|
167
|
-
_translate("Exclusive aggressive low-latency (no fallback)"),
|
|
168
|
-
],
|
|
169
|
-
label=_translate("Latency/exclusivity mode"),
|
|
170
|
-
hint=_translate(
|
|
171
|
-
"How should PsychoPy handle latency? Some options will result in other apps being "
|
|
172
|
-
"denied access to the speaker while your experiment is running."
|
|
173
|
-
)
|
|
174
|
-
)
|
|
175
|
-
|
|
176
128
|
# --- Testing ---
|
|
177
129
|
self.params['validator'] = Param(
|
|
178
130
|
validator, valType="code", inputType="choice", categ="Testing",
|
|
@@ -184,25 +136,13 @@ class SoundComponent(BaseDeviceComponent):
|
|
|
184
136
|
)
|
|
185
137
|
)
|
|
186
138
|
|
|
187
|
-
def
|
|
188
|
-
|
|
189
|
-
# alert user to inefficient configuration
|
|
190
|
-
if self.params['resample'] and self.params['latencyClass'] == 2:
|
|
191
|
-
alert(3210, strFields={'deviceName': inits['deviceLabel']})
|
|
192
|
-
# initialise speaker
|
|
139
|
+
def writeInitCode(self, buff):
|
|
140
|
+
# set sound backend (only once per exp)
|
|
193
141
|
code = (
|
|
194
|
-
"#
|
|
195
|
-
"
|
|
196
|
-
" deviceName=%(deviceLabel)s,\n"
|
|
197
|
-
" deviceClass='psychopy.hardware.speaker.SpeakerDevice',\n"
|
|
198
|
-
" index=%(speakerIndex)s,\n"
|
|
199
|
-
" resample=%(resample)s,\n"
|
|
200
|
-
" latencyClass=%(latencyClass)s,\n"
|
|
201
|
-
")\n"
|
|
142
|
+
"# set audio backend\n"
|
|
143
|
+
"sound.Sound.backend = %(Audio lib)s\n"
|
|
202
144
|
)
|
|
203
|
-
buff.writeOnceIndentedLines(code %
|
|
204
|
-
|
|
205
|
-
def writeInitCode(self, buff):
|
|
145
|
+
buff.writeOnceIndentedLines(code % self.exp.settings.params)
|
|
206
146
|
# replaces variable params with sensible defaults
|
|
207
147
|
inits = getInitVals(self.params)
|
|
208
148
|
if not canBeNumeric(inits['stopVal'].val):
|
|
@@ -430,3 +370,72 @@ class SoundComponent(BaseDeviceComponent):
|
|
|
430
370
|
params=params,
|
|
431
371
|
target=target,
|
|
432
372
|
)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class SpeakerDeviceBackend(DeviceBackend):
|
|
376
|
+
# name of this backend to display in Device Manager
|
|
377
|
+
backendLabel = "Speaker"
|
|
378
|
+
# class of the device which this backend corresponds to
|
|
379
|
+
deviceClass = "psychopy.hardware.speaker.SpeakerDevice"
|
|
380
|
+
# icon to show in device manager
|
|
381
|
+
icon = "light/sound.png"
|
|
382
|
+
|
|
383
|
+
def __init__(self, profile):
|
|
384
|
+
# init parent class
|
|
385
|
+
DeviceBackend.__init__(self, profile)
|
|
386
|
+
|
|
387
|
+
# add params
|
|
388
|
+
self.order += [
|
|
389
|
+
"latencyClass",
|
|
390
|
+
"resample",
|
|
391
|
+
]
|
|
392
|
+
self.params['latencyClass'] = Param(
|
|
393
|
+
1, valType="code", inputType="choice",
|
|
394
|
+
allowedVals=[0, 1, 2, 3, 4],
|
|
395
|
+
allowedLabels=[
|
|
396
|
+
_translate("Shared"),
|
|
397
|
+
_translate("Shared low-latency (recommended)"),
|
|
398
|
+
_translate("Exclusive low-latency"),
|
|
399
|
+
_translate("Exclusive aggressive low-latency (with fallback)"),
|
|
400
|
+
_translate("Exclusive aggressive low-latency (no fallback)"),
|
|
401
|
+
],
|
|
402
|
+
label=_translate("Latency/exclusivity mode"),
|
|
403
|
+
hint=_translate(
|
|
404
|
+
"How should PsychoPy handle latency? Some options will result in other apps being "
|
|
405
|
+
"denied access to the speaker while your experiment is running."
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
self.params['resample'] = Param(
|
|
409
|
+
True, valType="str", inputType="bool",
|
|
410
|
+
label=_translate("Resample"),
|
|
411
|
+
hint=_translate(
|
|
412
|
+
"If the sample rate of a clip doesn't match the sample rate of the speaker, should "
|
|
413
|
+
"we resample it to match?"
|
|
414
|
+
)
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
def writeDeviceCode(self, buff):
|
|
418
|
+
"""
|
|
419
|
+
Code to setup a device with this backend.
|
|
420
|
+
|
|
421
|
+
Parameters
|
|
422
|
+
----------
|
|
423
|
+
buff : io.StringIO
|
|
424
|
+
Text buffer to write code to.
|
|
425
|
+
"""
|
|
426
|
+
# alert user to inefficient configuration
|
|
427
|
+
if self.params['resample'] and self.params['latencyClass'] == 2:
|
|
428
|
+
alert(3210, strFields={'deviceName': self.params['deviceLabel']})
|
|
429
|
+
# write basic code
|
|
430
|
+
self.writeBaseDeviceCode(buff, close=False)
|
|
431
|
+
# add exclusive param and close
|
|
432
|
+
code = (
|
|
433
|
+
" resample=%(resample)s,\n"
|
|
434
|
+
" latencyClass=%(latencyClass)s,\n"
|
|
435
|
+
")\n"
|
|
436
|
+
)
|
|
437
|
+
buff.writeIndentedLines(code % self.params)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
# register backend with Component
|
|
441
|
+
SoundComponent.registerBackend(SpeakerDeviceBackend)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
+
from psychopy.experiment.devices import DeviceBackend
|
|
2
3
|
from psychopy.experiment.components import BaseComponent, BaseDeviceComponent, Param, getInitVals
|
|
3
|
-
from psychopy.experiment.plugins import PluginDevicesMixin, DeviceBackend
|
|
4
4
|
from psychopy.localization import _translate
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class SoundSensorComponent(BaseDeviceComponent
|
|
7
|
+
class SoundSensorComponent(BaseDeviceComponent):
|
|
8
8
|
"""
|
|
9
9
|
Component for getting button presses from a button box device.
|
|
10
10
|
"""
|
|
@@ -13,6 +13,14 @@ class SoundSensorComponent(BaseDeviceComponent, PluginDevicesMixin):
|
|
|
13
13
|
iconFile = Path(__file__).parent / 'soundsensor.png'
|
|
14
14
|
tooltip = _translate('Voice Key: Get input from a microphone as simple true/false values')
|
|
15
15
|
beta = True
|
|
16
|
+
legacyParams = [
|
|
17
|
+
# old device setup params, no longer needed as this is handled by DeviceManager
|
|
18
|
+
"deviceBackend",
|
|
19
|
+
"meMicrophone",
|
|
20
|
+
"meThreshold",
|
|
21
|
+
"meRange",
|
|
22
|
+
"meSamplingWindow",
|
|
23
|
+
]
|
|
16
24
|
|
|
17
25
|
def __init__(
|
|
18
26
|
self, exp, parentName,
|
|
@@ -112,25 +120,6 @@ class SoundSensorComponent(BaseDeviceComponent, PluginDevicesMixin):
|
|
|
112
120
|
),
|
|
113
121
|
label=_translate("Correct answer"), direct=False)
|
|
114
122
|
|
|
115
|
-
# --- Device params ---
|
|
116
|
-
self.order += [
|
|
117
|
-
"deviceBackend",
|
|
118
|
-
]
|
|
119
|
-
|
|
120
|
-
self.params['deviceBackend'] = Param(
|
|
121
|
-
deviceBackend, valType="str", inputType="choice", categ="Device",
|
|
122
|
-
allowedVals=self.getBackendKeys,
|
|
123
|
-
allowedLabels=self.getBackendLabels,
|
|
124
|
-
label=_translate("Device backend"),
|
|
125
|
-
hint=_translate(
|
|
126
|
-
"What kind of sound sensor is it? What package/plugin should be used to talk to it?"
|
|
127
|
-
),
|
|
128
|
-
direct=False
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
# add params for any backends
|
|
132
|
-
self.loadBackends()
|
|
133
|
-
|
|
134
123
|
def writeInitCode(self, buff):
|
|
135
124
|
inits = getInitVals(self.params)
|
|
136
125
|
# code to create object
|
|
@@ -267,17 +256,13 @@ class SoundSensorComponent(BaseDeviceComponent, PluginDevicesMixin):
|
|
|
267
256
|
|
|
268
257
|
|
|
269
258
|
class MicrophoneSoundSensorBackend(DeviceBackend):
|
|
270
|
-
""
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
"""
|
|
274
|
-
|
|
275
|
-
key = "microphone"
|
|
276
|
-
label = _translate("Microphone emulator")
|
|
277
|
-
component = SoundSensorComponent
|
|
278
|
-
deviceClasses = ['psychopy.hardware.soundsensor.MicrophoneSoundSensorEmulator']
|
|
259
|
+
backendLabel = "Microphone Sound Sensor"
|
|
260
|
+
deviceClass = "psychopy.hardware.soundsensor.MicrophoneSoundSensor"
|
|
261
|
+
icon = "light/soundsensor.png"
|
|
279
262
|
|
|
280
|
-
def
|
|
263
|
+
def __init__(self, profile):
|
|
264
|
+
# init parent class
|
|
265
|
+
DeviceBackend.__init__(self, profile)
|
|
281
266
|
# define order
|
|
282
267
|
order = [
|
|
283
268
|
"meMicrophone",
|
|
@@ -286,31 +271,8 @@ class MicrophoneSoundSensorBackend(DeviceBackend):
|
|
|
286
271
|
"meSamplingWindow",
|
|
287
272
|
]
|
|
288
273
|
# define params
|
|
289
|
-
params =
|
|
290
|
-
|
|
291
|
-
from psychopy.hardware.microphone import MicrophoneDevice
|
|
292
|
-
profiles = MicrophoneDevice.getAvailableDevices()
|
|
293
|
-
|
|
294
|
-
return [None] + [profile['index'] for profile in profiles]
|
|
295
|
-
|
|
296
|
-
def getDeviceNames():
|
|
297
|
-
from psychopy.hardware.microphone import MicrophoneDevice
|
|
298
|
-
profiles = MicrophoneDevice.getAvailableDevices()
|
|
299
|
-
|
|
300
|
-
return ["default"] + [profile['deviceName'] for profile in profiles]
|
|
301
|
-
|
|
302
|
-
params['meMicrophone'] = Param(
|
|
303
|
-
None, valType="str", inputType="choice", categ="Device",
|
|
304
|
-
updates="constant", allowedUpdates=None,
|
|
305
|
-
allowedVals=getDeviceIndices,
|
|
306
|
-
allowedLabels=getDeviceNames,
|
|
307
|
-
label=_translate("Microphone"),
|
|
308
|
-
hint=_translate(
|
|
309
|
-
"What microphone device to take volume readings from?"
|
|
310
|
-
)
|
|
311
|
-
)
|
|
312
|
-
params['meThreshold'] = Param(
|
|
313
|
-
0.5, valType="code", inputType="single", categ="Device",
|
|
274
|
+
self.params['threshold'] = Param(
|
|
275
|
+
0.5, valType="code", inputType="single",
|
|
314
276
|
updates="constant", allowedUpdates=None,
|
|
315
277
|
label=_translate("Threshold (0-1)"),
|
|
316
278
|
hint=_translate(
|
|
@@ -318,17 +280,17 @@ class MicrophoneSoundSensorBackend(DeviceBackend):
|
|
|
318
280
|
"register a sound sensor response"
|
|
319
281
|
)
|
|
320
282
|
)
|
|
321
|
-
params['
|
|
322
|
-
(0, 1), valType="list", inputType="single",
|
|
283
|
+
self.params['dbRange'] = Param(
|
|
284
|
+
(0, 1), valType="list", inputType="single",
|
|
323
285
|
updates="constant", allowedUpdates=None,
|
|
324
286
|
label=_translate("Decibel range"),
|
|
325
287
|
hint=_translate(
|
|
326
288
|
"What kind of values (dB) would you expect to receive from this device? In other "
|
|
327
|
-
"words, how many dB does a threshold of 0 and
|
|
289
|
+
"words, how many dB does a threshold of 0 and 1 correspond to?"
|
|
328
290
|
)
|
|
329
291
|
)
|
|
330
|
-
params['
|
|
331
|
-
0.03, valType="code", inputType="single",
|
|
292
|
+
self.params['samplingWindow'] = Param(
|
|
293
|
+
0.03, valType="code", inputType="single",
|
|
332
294
|
updates="constant", allowedUpdates=None,
|
|
333
295
|
label=_translate("Sampling window (s)"),
|
|
334
296
|
hint=_translate(
|
|
@@ -336,26 +298,27 @@ class MicrophoneSoundSensorBackend(DeviceBackend):
|
|
|
336
298
|
"precise, but also less subject to random noise."
|
|
337
299
|
)
|
|
338
300
|
)
|
|
301
|
+
|
|
302
|
+
def writeDeviceCode(self, buff):
|
|
303
|
+
"""
|
|
304
|
+
Code to setup a device with this backend.
|
|
339
305
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
# get inits
|
|
349
|
-
inits = getInitVals(self.params)
|
|
350
|
-
# make ButtonGroup object
|
|
306
|
+
Parameters
|
|
307
|
+
----------
|
|
308
|
+
buff : io.StringIO
|
|
309
|
+
Text buffer to write code to.
|
|
310
|
+
"""
|
|
311
|
+
# write basic code
|
|
312
|
+
self.writeBaseDeviceCode(buff, close=False)
|
|
313
|
+
# add exclusive param and close
|
|
351
314
|
code = (
|
|
352
|
-
"
|
|
353
|
-
"
|
|
354
|
-
"
|
|
355
|
-
" device=%(meMicrophone)s,\n"
|
|
356
|
-
" threshold=%(meThreshold)s, \n"
|
|
357
|
-
" dbRange=%(meRange)s, \n"
|
|
358
|
-
" samplingWindow=%(meSamplingWindow)s, \n"
|
|
315
|
+
" threshold=%(threshold)s, \n"
|
|
316
|
+
" dbRange=%(dbRange)s, \n"
|
|
317
|
+
" samplingWindow=%(samplingWindow)s,\n"
|
|
359
318
|
")\n"
|
|
360
319
|
)
|
|
361
|
-
buff.
|
|
320
|
+
buff.writeIndentedLines(code % self.params)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
# register backend with Component
|
|
324
|
+
SoundSensorComponent.registerBackend(MicrophoneSoundSensorBackend)
|