psychopy 2024.2.1__py3-none-any.whl → 2024.2.4__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.

Files changed (276) hide show
  1. psychopy/.DS_Store +0 -0
  2. psychopy/GIT_SHA +1 -1
  3. psychopy/VERSION +1 -1
  4. psychopy/__init__.py +10 -1
  5. psychopy/__init__.py.orig +65 -0
  6. psychopy/app/{locale/ar_001/.DS_Store → .DS_Store} +0 -0
  7. psychopy/app/Resources/.DS_Store +0 -0
  8. psychopy/app/_psychopyApp.py +11 -3
  9. psychopy/app/appData.spec +1 -1
  10. psychopy/app/builder/builder.py +1 -1
  11. psychopy/app/builder/builder.py.orig +3932 -0
  12. psychopy/app/builder/dialogs/__init__.py.orig +1679 -0
  13. psychopy/app/builder/dialogs/paramCtrls.py +1 -1
  14. psychopy/app/builder/dialogs/paramCtrls.py.orig +713 -0
  15. psychopy/app/colorpicker/__init__.py.orig +411 -0
  16. psychopy/app/cortex.log +0 -0
  17. psychopy/app/jobs.py +8 -1
  18. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +2452 -1731
  19. psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN.mo +0 -0
  20. psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN.po +6127 -0
  21. psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN_allFlagged.mo +0 -0
  22. psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN_allFlagged.po +7366 -0
  23. psychopy/app/plugin_manager/dialog.py +9 -7
  24. psychopy/app/ribbon.py +2 -1
  25. psychopy/app/runner/runner.py +7 -5
  26. psychopy/clock.py +8 -4
  27. psychopy/core.py.orig +169 -0
  28. psychopy/demos/builder/Design Templates/randomisedBlocks/html/index.html +23 -0
  29. psychopy/demos/builder/Design Templates/randomisedBlocks/html/randomisedBlocks-legacy-browsers.js +423 -0
  30. psychopy/demos/builder/Design Templates/randomisedBlocks/html/randomisedBlocks.js +427 -0
  31. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/chooseBlock.xlsx +0 -0
  32. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/facesBlock.xlsx +0 -0
  33. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/housesBlock.xlsx +0 -0
  34. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face01.jpg +0 -0
  35. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face02.jpg +0 -0
  36. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face03.jpg +0 -0
  37. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house01.jpg +0 -0
  38. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house02.jpg +0 -0
  39. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house03.jpg +0 -0
  40. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.py +330 -0
  41. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks_lastrun.py +330 -0
  42. psychopy/demos/builder/Feature Demos/eyetracking/eyetracking.xml +298 -0
  43. psychopy/demos/builder/Feature Demos/eyetracking/eyetracking.xsd +120 -0
  44. psychopy/demos/builder/Tools/.DS_Store +0 -0
  45. psychopy/demos/builder/Tools/gammaCalibration/.DS_Store +0 -0
  46. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.csv +38 -0
  47. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.log +3418 -0
  48. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.psydat +0 -0
  49. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.csv +2 -0
  50. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.log +15 -0
  51. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.psydat +0 -0
  52. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual.psyexp +323 -0
  53. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual.py +562 -0
  54. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual_lastrun.py +562 -0
  55. psychopy/demos/builder/Tools/gammaCalibration/questStairs.xlsx +0 -0
  56. psychopy/demos/builder/Tools/gammaCalibration/readme.md +0 -0
  57. psychopy/demos/builder/Tools/gammaCalibration/resources/low_contrast.png +0 -0
  58. psychopy/demos/builder/Tools/gammaCalibration/resources/make_2nd_order_tex.py +59 -0
  59. psychopy/demos/builder/Tools/gammaCalibration/resources/second_order_tex.png +0 -0
  60. psychopy/demos/coder/.DS_Store +0 -0
  61. psychopy/demos/coder/experiment control/info_gamma.pickle +0 -0
  62. psychopy/demos/coder/iohub/.iohpid +1 -0
  63. psychopy/demos/coder/iohub/eyetracking/.iohpid +1 -0
  64. psychopy/demos/coder/iohub/wintab/.DS_Store +0 -0
  65. psychopy/demos/coder/stimuli/.DS_Store +0 -0
  66. psychopy/demos/coder/stimuli/radialGratingContracting.py +29 -0
  67. psychopy/experiment/_experiment.py.orig +1032 -0
  68. psychopy/experiment/components/.DS_Store +0 -0
  69. psychopy/experiment/components/_base.py +13 -4
  70. psychopy/experiment/components/_base.py.orig +823 -0
  71. psychopy/experiment/components/form/.DS_Store +0 -0
  72. psychopy/experiment/components/microphone/__init__.py +10 -1
  73. psychopy/experiment/components/microphone/__init__.py.orig +490 -0
  74. psychopy/experiment/components/polygon/__init__.py +21 -22
  75. psychopy/experiment/components/settings/__init__.py +13 -14
  76. psychopy/experiment/components/settings/__init__.py.orig +1337 -0
  77. psychopy/experiment/components/textbox/__init__.py.orig +310 -0
  78. psychopy/experiment/components/webcam/.DS_Store +0 -0
  79. psychopy/experiment/components/webcam/light/.DS_Store +0 -0
  80. psychopy/experiment/flow.py +10 -8
  81. psychopy/experiment/loops.py.orig +829 -0
  82. psychopy/experiment/params.py +8 -3
  83. psychopy/experiment/params.py.orig +408 -0
  84. psychopy/experiment/routine.py.orig +503 -0
  85. psychopy/experiment/routines/_base.py +15 -6
  86. psychopy/experiment/routines/counterbalance/__init__.py +1 -0
  87. psychopy/gui/qtgui.py +14 -7
  88. psychopy/gui/util.py +10 -14
  89. psychopy/gui/wxgui.py +10 -4
  90. psychopy/hardware/.DS_Store +0 -0
  91. psychopy/hardware/brainproducts.py.orig +680 -0
  92. psychopy/hardware/iolab.py.orig +238 -0
  93. psychopy/hardware/manager.py +1 -1
  94. psychopy/hardware/photodiode.py +59 -27
  95. psychopy/hardware/serialport.py +51 -0
  96. psychopy/hardware/speaker.py +4 -4
  97. psychopy/iohub/datastore/__init__.py.orig +443 -0
  98. psychopy/iohub/datastore/util.py.orig +692 -0
  99. psychopy/iohub/devices/mouse/darwin.py.orig +427 -0
  100. psychopy/iohub/devices/mouse/linux2.py.orig +198 -0
  101. psychopy/preferences/.DS_Store +0 -0
  102. psychopy/projects/pavlovia.py +10 -3
  103. psychopy/projects/pavlovia.py.orig +1295 -0
  104. psychopy/sound/backend_ptb.py +22 -5
  105. psychopy/sound/transcribe.py +24 -4
  106. psychopy/tests/.DS_Store +0 -0
  107. psychopy/tests/data/.DS_Store +0 -0
  108. psychopy/tests/data/TestCircle_fill_local.png +0 -0
  109. psychopy/tests/data/__test.png +0 -0
  110. psychopy/tests/data/aperture1_normHexbackground_local.png +0 -0
  111. psychopy/tests/data/aperture1_norm_local.png +0 -0
  112. psychopy/tests/data/aperture2_normHexbackground_local.png +0 -0
  113. psychopy/tests/data/beatandrcos_height_local.png +0 -0
  114. psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
  115. psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
  116. psychopy/tests/data/beatandrcos_norm_local.png +0 -0
  117. psychopy/tests/data/beatandrcos_stencil_local.png +0 -0
  118. psychopy/tests/data/blend_add_height_local.png +0 -0
  119. psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
  120. psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
  121. psychopy/tests/data/blend_add_normNoShade_local.png +0 -0
  122. psychopy/tests/data/blend_add_norm_local.png +0 -0
  123. psychopy/tests/data/blend_add_stencil_local.png +0 -0
  124. psychopy/tests/data/bufferimg_gabor_height_local.png +0 -0
  125. psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
  126. psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
  127. psychopy/tests/data/bufferimg_gabor_normNoShade_local.png +0 -0
  128. psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
  129. psychopy/tests/data/bufferimg_gabor_stencil_local.png +0 -0
  130. psychopy/tests/data/circleHex_height_local.png +0 -0
  131. psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
  132. psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
  133. psychopy/tests/data/circleHex_normNoShade_local.png +0 -0
  134. psychopy/tests/data/circleHex_norm_local.png +0 -0
  135. psychopy/tests/data/circleHex_stencil_local.png +0 -0
  136. psychopy/tests/data/color_comparison_local.png +0 -0
  137. psychopy/tests/data/corrFullRandom_local.csv +16 -0
  138. psychopy/tests/data/corrFullRandom_local.tsv +6 -0
  139. psychopy/tests/data/correctScript/.DS_Store +0 -0
  140. psychopy/tests/data/dots_height_local.png +0 -0
  141. psychopy/tests/data/dots_normAddBlend_local.png +0 -0
  142. psychopy/tests/data/dots_normHexbackground_local.png +0 -0
  143. psychopy/tests/data/dots_normNoShade_local.png +0 -0
  144. psychopy/tests/data/dots_norm_local.png +0 -0
  145. psychopy/tests/data/dots_stencil_local.png +0 -0
  146. psychopy/tests/data/elarray1_height_local.png +0 -0
  147. psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
  148. psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
  149. psychopy/tests/data/elarray1_norm_local.png +0 -0
  150. psychopy/tests/data/elarray1_stencil_local.png +0 -0
  151. psychopy/tests/data/envelopeandrcos_height_local.png +0 -0
  152. psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
  153. psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
  154. psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
  155. psychopy/tests/data/envelopeandrcos_stencil_local.png +0 -0
  156. psychopy/tests/data/envelopepowerandrcos_height_local.png +0 -0
  157. psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
  158. psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
  159. psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
  160. psychopy/tests/data/envelopepowerandrcos_stencil_local.png +0 -0
  161. psychopy/tests/data/gabor1_height_local.png +0 -0
  162. psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
  163. psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
  164. psychopy/tests/data/gabor1_normNoShade_local.png +0 -0
  165. psychopy/tests/data/gabor1_norm_local.png +0 -0
  166. psychopy/tests/data/gabor1_stencil_local.png +0 -0
  167. psychopy/tests/data/greyscale_normHexbackground_local.png +0 -0
  168. psychopy/tests/data/imageAndGauss_height_local.png +0 -0
  169. psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
  170. psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
  171. psychopy/tests/data/imageAndGauss_normNoShade_local.png +0 -0
  172. psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
  173. psychopy/tests/data/imageAndGauss_stencil_local.png +0 -0
  174. psychopy/tests/data/movFrame1_stencil_local.png +0 -0
  175. psychopy/tests/data/noiseAndRcos_height_local.png +0 -0
  176. psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
  177. psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
  178. psychopy/tests/data/noiseAndRcos_normNoShade_local.png +0 -0
  179. psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
  180. psychopy/tests/data/noiseAndRcos_stencil_local.png +0 -0
  181. psychopy/tests/data/noiseFiltersAndRcos_height_local.png +0 -0
  182. psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
  183. psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
  184. psychopy/tests/data/noiseFiltersAndRcos_normNoShade_local.png +0 -0
  185. psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
  186. psychopy/tests/data/noiseFiltersAndRcos_stencil_local.png +0 -0
  187. psychopy/tests/data/numpyImage_height_local.png +0 -0
  188. psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
  189. psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
  190. psychopy/tests/data/numpyImage_normNoShade_local.png +0 -0
  191. psychopy/tests/data/numpyImage_norm_local.png +0 -0
  192. psychopy/tests/data/numpyImage_stencil_local.png +0 -0
  193. psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
  194. psychopy/tests/data/shape2_1_normHexbackground_local.png +0 -0
  195. psychopy/tests/data/shape2_1_normNoShade_local.png +0 -0
  196. psychopy/tests/data/shape2_1_norm_local.png +0 -0
  197. psychopy/tests/data/shape2_1_stencil_local.png +0 -0
  198. psychopy/tests/data/testLoopsBlocks.psyexp_local.py +328 -0
  199. psychopy/tests/data/text1_height_local.png +0 -0
  200. psychopy/tests/data/text1_normAddBlend_local.png +0 -0
  201. psychopy/tests/data/text1_normHexbackground_local.png +0 -0
  202. psychopy/tests/data/text1_norm_local.png +0 -0
  203. psychopy/tests/data/text1_stencil_local.png +0 -0
  204. psychopy/tests/data/text2_height.png +0 -0
  205. psychopy/tests/data/text2_normAddBlend.png +0 -0
  206. psychopy/tests/data/text2_normHexbackground.png +0 -0
  207. psychopy/tests/data/text2_stencil.png +0 -0
  208. psychopy/tests/data/wedge1_height_local.png +0 -0
  209. psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
  210. psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
  211. psychopy/tests/data/wedge1_normNoShade_local.png +0 -0
  212. psychopy/tests/data/wedge1_norm_local.png +0 -0
  213. psychopy/tests/data/wedge1_stencil_local.png +0 -0
  214. psychopy/tests/test_app/.DS_Store +0 -0
  215. psychopy/tests/test_app/test_builder/.DS_Store +0 -0
  216. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.csv +9 -0
  217. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.log +177 -0
  218. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.psydat +0 -0
  219. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.xlsx +0 -0
  220. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.csv +9 -0
  221. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.log +168 -0
  222. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.psydat +0 -0
  223. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.xlsx +0 -0
  224. psychopy/tests/test_data/.DS_Store +0 -0
  225. psychopy/tests/test_hardware/test_CRS_BitsSharp.py.orig +68 -0
  226. psychopy/tests/test_tools/test_arraytools.py +112 -0
  227. psychopy/tests/test_visual/test_image.py.orig +219 -0
  228. psychopy/tools/arraytools.py +47 -0
  229. psychopy/tools/versionchooser.py +1 -1
  230. psychopy/visual/backends/pygletbackend.py +26 -8
  231. psychopy/visual/basevisual.py.orig +1723 -0
  232. psychopy/visual/form.py.orig +1181 -0
  233. psychopy/visual/text.py.orig +752 -0
  234. psychopy/visual/textbox2/textbox2.py.orig +1315 -0
  235. psychopy/visual/window.py +13 -5
  236. psychopy/visual/windowwarp.py.orig +463 -0
  237. {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/METADATA +9 -9
  238. {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/RECORD +244 -78
  239. {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/WHEEL +1 -1
  240. {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/entry_points.txt +2 -0
  241. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  242. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  243. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  244. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  245. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  246. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  247. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  248. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  249. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  250. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  251. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  252. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  253. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  254. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  255. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  256. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  257. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  258. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  259. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  260. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  261. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  262. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  263. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  264. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  265. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  266. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  267. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  268. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  269. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  270. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  271. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  272. psychopy-2024.2.1.dist-info/licenses/AUTHORS.md +0 -138
  273. /psychopy/{app/locale/ar_001/LC_MESSAGE → demos/builder}/.DS_Store +0 -0
  274. /psychopy/{app/locale/es_ES/LC_MESSAGE → demos/builder/Experiments}/.DS_Store +0 -0
  275. /psychopy/{visual → demos/builder/Tools/gammaCalibration/data}/.DS_Store +0 -0
  276. {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/licenses/LICENSE +0 -0
@@ -48,6 +48,10 @@ class MicrophoneComponent(BaseDeviceComponent):
48
48
  onlineTranscribers = {
49
49
  "Google": "google",
50
50
  }
51
+ # dict mapping transcriber names to importable paths
52
+ transcriberPaths = {
53
+ 'google': "psychopy.sound.transcribe:GoogleCloudTranscriber"
54
+ }
51
55
 
52
56
  def __init__(self, exp, parentName, name='mic',
53
57
  startType='time (s)', startVal=0.0,
@@ -352,13 +356,18 @@ class MicrophoneComponent(BaseDeviceComponent):
352
356
 
353
357
  def writeRunOnceInitCode(self, buff):
354
358
  inits = getInitVals(self.params)
359
+ # get transcriber path
360
+ if inits['transcribeBackend'].val in MicrophoneComponent.transcriberPaths:
361
+ inits['transcriberPath'] = MicrophoneComponent.transcriberPaths[inits['transcribeBackend'].val]
362
+ else:
363
+ inits['transcriberPath'] = inits['transcribeBackend'].val
355
364
  # check if the user wants to do transcription
356
365
  if inits['transcribe'].val:
357
366
  code = (
358
367
  "# Setup speech-to-text transcriber for audio recordings\n"
359
368
  "from psychopy.sound.transcribe import setupTranscriber\n"
360
369
  "setupTranscriber(\n"
361
- " '%(transcribeBackend)s'")
370
+ " '%(transcriberPath)s'")
362
371
 
363
372
  # handle advanced config options
364
373
  if inits['transcribeBackend'].val == 'Whisper':
@@ -0,0 +1,490 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # Part of the PsychoPy library
5
+ # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2021 Open Science Tools Ltd.
6
+ # Distributed under the terms of the GNU General Public License (GPL).
7
+
8
+ # Author: Jeremy R. Gray, 2012
9
+
10
+ from __future__ import absolute_import, print_function
11
+ from builtins import super # provides Py3-style super() using python-future
12
+
13
+ from os import path
14
+ from pathlib import Path
15
+
16
+ from psychopy.alerts import alert
17
+ from psychopy.experiment.components import BaseComponent, Param, getInitVals, _translate
18
+ from psychopy.sound.microphone import Microphone, _hasPTB
19
+ from psychopy.sound.audiodevice import sampleRateQualityLevels
20
+ from psychopy.sound.audioclip import AUDIO_SUPPORTED_CODECS
21
+ from psychopy.localization import _localized as __localized
22
+
23
+ _localized = __localized.copy()
24
+ _localized.update({'stereo': _translate('Stereo'),
25
+ 'channel': _translate('Channel')})
26
+ from psychopy.tests import _vmTesting
27
+
28
+ if _hasPTB and not _vmTesting:
29
+ devices = {d.deviceName: d for d in Microphone.getDevices()}
30
+ else:
31
+ devices = {}
32
+ sampleRates = {r[1]: r[0] for r in sampleRateQualityLevels.values()}
33
+ devices['default'] = None
34
+
35
+ onlineTranscribers = {
36
+ "Google": "GOOGLE",
37
+ "Microsoft Azure": "AZURE",
38
+ }
39
+ localTranscribers = {
40
+ <<<<<<< HEAD
41
+ "Built-In": "built-in",
42
+ "Google": "google",
43
+ "Microsoft Azure": "azure",
44
+ =======
45
+ "Google": "googleCloud",
46
+ "Microsoft Azure": "azure",
47
+ "Built-in": "sphinx",
48
+ >>>>>>> 31b648a29f77d0927dc5de635816423c07eaeb69
49
+ }
50
+ allTranscribers = {**localTranscribers, **onlineTranscribers}
51
+
52
+
53
+ class MicrophoneComponent(BaseComponent):
54
+ """An event class for capturing short sound stimuli"""
55
+ categories = ['Responses']
56
+ targets = ['PsychoPy', 'PsychoJS']
57
+ iconFile = Path(__file__).parent / 'microphone.png'
58
+ tooltip = _translate('Microphone: basic sound capture (fixed onset & '
59
+ 'duration), okay for spoken words')
60
+
61
+ def __init__(self, exp, parentName, name='mic',
62
+ startType='time (s)', startVal=0.0,
63
+ stopType='duration (s)', stopVal=2.0,
64
+ startEstim='', durationEstim='',
65
+ channels='auto', device="default",
66
+ sampleRate='Voice (16kHz)', maxSize=24000,
67
+ outputType='default', speakTimes=True, trimSilent=False,
68
+ <<<<<<< HEAD
69
+ transcribe=True, transcribeBackend="BUILT-IN", transcribeLang="en-US", transcribeWords="",
70
+ =======
71
+ transcribe=True, transcribeBackend="Built-in", transcribeLang="en-US", transcribeWords="",
72
+ >>>>>>> 31b648a29f77d0927dc5de635816423c07eaeb69
73
+ #legacy
74
+ stereo=None, channel=None):
75
+ super(MicrophoneComponent, self).__init__(
76
+ exp, parentName, name=name,
77
+ startType=startType, startVal=startVal,
78
+ stopType=stopType, stopVal=stopVal,
79
+ startEstim=startEstim, durationEstim=durationEstim)
80
+
81
+ self.type = 'Microphone'
82
+ self.url = "https://www.psychopy.org/builder/components/microphone.html"
83
+ self.exp.requirePsychopyLibs(['sound'])
84
+
85
+ self.order += []
86
+
87
+ self.params['stopType'].allowedVals = ['duration (s)']
88
+ msg = _translate(
89
+ 'The duration of the recording in seconds; blank = 0 sec')
90
+ self.params['stopType'].hint = msg
91
+
92
+ # params
93
+ msg = _translate("What microphone device would you like the use to record? This will only affect local "
94
+ "experiments - online experiments ask the participant which mic to use.")
95
+ self.params['device'] = Param(
96
+ device, valType='str', inputType="choice", categ="Basic",
97
+ allowedVals=list(devices),
98
+ hint=msg,
99
+ label=_translate("Device")
100
+ )
101
+
102
+ msg = _translate(
103
+ "Record two channels (stereo) or one (mono, smaller file). Select 'auto' to use as many channels "
104
+ "as the selected device allows.")
105
+ if stereo is not None:
106
+ # If using a legacy mic component, work out channels from old bool value of stereo
107
+ channels = ['mono', 'stereo'][stereo]
108
+ self.params['channels'] = Param(
109
+ channels, valType='str', inputType="choice", categ='Hardware',
110
+ allowedVals=['auto', 'mono', 'stereo'],
111
+ hint=msg,
112
+ label=_translate('Channels'))
113
+
114
+ msg = _translate(
115
+ "How many samples per second (Hz) to record at")
116
+ self.params['sampleRate'] = Param(
117
+ sampleRate, valType='num', inputType="choice", categ='Hardware',
118
+ allowedVals=list(sampleRates),
119
+ hint=msg,
120
+ label=_translate('Sample Rate (Hz)'))
121
+
122
+ msg = _translate(
123
+ "To avoid excessively large output files, what is the biggest file size you are likely to expect?")
124
+ self.params['maxSize'] = Param(
125
+ maxSize, valType='num', inputType="single", categ='Hardware',
126
+ hint=msg,
127
+ label=_translate('Max Recording Size (kb)'))
128
+
129
+ msg = _translate(
130
+ "What file type should output audio files be saved as?")
131
+ self.params['outputType'] = Param(
132
+ outputType, valType='code', inputType='choice', categ='Data',
133
+ allowedVals=["default"] + AUDIO_SUPPORTED_CODECS,
134
+ hint=msg,
135
+ label=_translate("Output File Type")
136
+ )
137
+
138
+ msg = _translate(
139
+ "Tick this to save times when the participant starts and stops speaking")
140
+ self.params['speakTimes'] = Param(
141
+ speakTimes, valType='bool', inputType='bool', categ='Data',
142
+ hint=msg,
143
+ label=_translate("Speaking Start / Stop Times")
144
+ )
145
+
146
+ msg = _translate(
147
+ "Trim periods of silence from the output file")
148
+ self.params['trimSilent'] = Param(
149
+ trimSilent, valType='bool', inputType='bool', categ='Data',
150
+ hint=msg,
151
+ label=_translate("Trim Silent")
152
+ )
153
+
154
+ # Transcription params
155
+ self.order += [
156
+ 'transcribe',
157
+ 'transcribeBackend',
158
+ 'transcribeLang',
159
+ 'transcribeWords',
160
+ ]
161
+ self.params['transcribe'] = Param(
162
+ transcribe, valType='bool', inputType='bool', categ='Transcription',
163
+ hint=_translate("Whether to transcribe the audio recording and store the transcription"),
164
+ label=_translate("Transcribe Audio")
165
+ )
166
+
167
+ for depParam in ['transcribeBackend', 'transcribeLang', 'transcribeWords']:
168
+ self.depends.append({
169
+ "dependsOn": "transcribe",
170
+ "condition": "==True",
171
+ "param": depParam,
172
+ "true": "enable", # what to do with param if condition is True
173
+ "false": "disable", # permitted: hide, show, enable, disable
174
+ })
175
+
176
+ self.params['transcribeBackend'] = Param(
177
+ transcribeBackend, valType='code', inputType='choice', categ='Transcription',
178
+ allowedVals=list(allTranscribers),
179
+ hint=_translate("What transcription service to use to transcribe audio?"),
180
+ label=_translate("Transcription Backend")
181
+ )
182
+
183
+ self.params['transcribeLang'] = Param(
184
+ transcribeLang, valType='str', inputType='single', categ='Transcription',
185
+ hint=_translate("What language you expect the recording to be spoken in, e.g. en-GB for English"),
186
+ label=_translate("Transcription Language")
187
+ )
188
+
189
+ self.params['transcribeWords'] = Param(
190
+ transcribeWords, valType='list', inputType='single', categ='Transcription',
191
+ hint=_translate("Set list of words to listen for - if blank will listen for all words in chosen language"),
192
+ label=_translate("Expected Words")
193
+ )
194
+
195
+ def writeStartCode(self, buff):
196
+ inits = getInitVals(self.params)
197
+ # Use filename with a suffix to store recordings
198
+ code = (
199
+ "# Make folder to store recordings from %(name)s\n"
200
+ "%(name)sRecFolder = filename + '_%(name)s_recorded'\n"
201
+ "if not os.path.isdir(%(name)sRecFolder):\n"
202
+ )
203
+ buff.writeIndentedLines(code % inits)
204
+ buff.setIndentLevel(1, relative=True)
205
+ code = (
206
+ "os.mkdir(%(name)sRecFolder)\n"
207
+ )
208
+ buff.writeIndentedLines(code % inits)
209
+ buff.setIndentLevel(-1, relative=True)
210
+
211
+ def writeStartCodeJS(self, buff):
212
+ inits = getInitVals(self.params)
213
+ code = (
214
+ "// Define folder to store recordings from %(name)s"
215
+ "%(name)sRecFolder = filename + '_%(name)s_recorded"
216
+ )
217
+ buff.writeIndentedLines(code % inits)
218
+
219
+ def writeInitCode(self, buff):
220
+ inits = getInitVals(self.params)
221
+ # Substitute sample rate value for numeric equivalent
222
+ inits['sampleRate'] = sampleRates[inits['sampleRate'].val]
223
+ # Substitute channel value for numeric equivalent
224
+ inits['channels'] = {'mono': 1, 'stereo': 2, 'auto': None}[self.params['channels'].val]
225
+ # Substitute device name for device index
226
+ device = devices[self.params['device'].val]
227
+ if hasattr(device, "deviceIndex"):
228
+ inits['device'] = device.deviceIndex
229
+ else:
230
+ inits['device'] = None
231
+ # Create Microphone object and clips dict
232
+ code = (
233
+ "%(name)s = sound.microphone.Microphone(\n"
234
+ )
235
+ buff.writeIndentedLines(code % inits)
236
+ buff.setIndentLevel(1, relative=True)
237
+ code = (
238
+ "device=%(device)s, channels=%(channels)s, \n"
239
+ "sampleRateHz=%(sampleRate)s, maxRecordingSize=%(maxSize)s\n"
240
+ )
241
+ buff.writeIndentedLines(code % inits)
242
+ buff.setIndentLevel(-1, relative=True)
243
+ code = (
244
+ ")\n"
245
+ )
246
+ buff.writeIndentedLines(code % inits)
247
+
248
+ def writeInitCodeJS(self, buff):
249
+ inits = getInitVals(self.params)
250
+ inits['sampleRate'] = sampleRates[inits['sampleRate'].val]
251
+ # Alert user if non-default value is selected for device
252
+ if inits['device'].val != 'default':
253
+ alert(5055, strFields={'name': inits['name'].val})
254
+ # Write code
255
+ code = (
256
+ "%(name)s = new sound.Microphone({\n"
257
+ )
258
+ buff.writeIndentedLines(code % inits)
259
+ buff.setIndentLevel(1, relative=True)
260
+ code = (
261
+ "win : psychoJS.window, \n"
262
+ "name:'%(name)s',\n"
263
+ "sampleRateHz : %(sampleRate)s,\n"
264
+ "channels : %(channels)s,\n"
265
+ "maxRecordingSize : %(maxSize)s,\n"
266
+ "loopback : true,\n"
267
+ "policyWhenFull : 'ignore',\n"
268
+ )
269
+ buff.writeIndentedLines(code % inits)
270
+ buff.setIndentLevel(-1, relative=True)
271
+ code = (
272
+ "});\n"
273
+ )
274
+ buff.writeIndentedLines(code % inits)
275
+
276
+ def writeFrameCode(self, buff):
277
+ """Write the code that will be called every frame"""
278
+ inits = getInitVals(self.params)
279
+ inits['routine'] = self.parentName
280
+ # Start the recording
281
+ code = (
282
+ "\n"
283
+ "# %(name)s updates"
284
+ )
285
+ buff.writeIndentedLines(code % inits)
286
+ self.writeStartTestCode(buff)
287
+ code = (
288
+ "# start recording with %(name)s\n"
289
+ "%(name)s.start()\n"
290
+ "%(name)s.status = STARTED\n"
291
+ )
292
+ buff.writeIndentedLines(code % inits)
293
+ buff.setIndentLevel(-1, relative=True)
294
+ # Get clip each frame
295
+ code = (
296
+ "if %(name)s.status == STARTED:\n"
297
+ )
298
+ buff.writeIndentedLines(code % inits)
299
+ buff.setIndentLevel(1, relative=True)
300
+ code = (
301
+ "# update recorded clip for %(name)s\n"
302
+ "%(name)s.poll()\n"
303
+ )
304
+ buff.writeIndentedLines(code % inits)
305
+ buff.setIndentLevel(-1, relative=True)
306
+ # Stop recording
307
+ self.writeStopTestCode(buff)
308
+ code = (
309
+ "# stop recording with %(name)s\n"
310
+ "%(name)s.stop()\n"
311
+ "%(name)s.status = FINISHED\n"
312
+ )
313
+ buff.writeIndentedLines(code % inits)
314
+ buff.setIndentLevel(-2, relative=True)
315
+
316
+ def writeFrameCodeJS(self, buff):
317
+ inits = getInitVals(self.params)
318
+ inits['routine'] = self.parentName
319
+ # Start the recording
320
+ self.writeStartTestCodeJS(buff)
321
+ code = (
322
+ "await %(name)s.start();\n"
323
+ )
324
+ buff.writeIndentedLines(code % inits)
325
+ buff.setIndentLevel(-1, relative=True)
326
+ code = (
327
+ "}"
328
+ )
329
+ buff.writeIndentedLines(code % inits)
330
+ if self.params['stopVal'].val not in ['', None, -1, 'None']:
331
+ # Stop the recording
332
+ self.writeStopTestCodeJS(buff)
333
+ code = (
334
+ "%(name)s.pause();\n"
335
+ )
336
+ buff.writeIndentedLines(code % inits)
337
+ buff.setIndentLevel(-1, relative=True)
338
+ code = (
339
+ "}"
340
+ )
341
+ buff.writeIndentedLines(code % inits)
342
+
343
+ def writeRoutineEndCode(self, buff):
344
+ inits = getInitVals(self.params)
345
+ # Alter inits
346
+ if len(self.exp.flow._loopList):
347
+ inits['loop'] = self.exp.flow._loopList[-1].params['name']
348
+ inits['filename'] = f"'recording_{inits['name']}_{inits['loop']}_%s.{inits['outputType']}' % {inits['loop']}.thisTrialN"
349
+ else:
350
+ inits['loop'] = "thisExp"
351
+ inits['filename'] = f"'recording_{inits['name']}'"
352
+ transcribe = inits['transcribe'].val
353
+ if inits['transcribe'].val == False:
354
+ inits['transcribeBackend'].val = None
355
+ if inits['outputType'].val == 'default':
356
+ inits['outputType'].val = 'wav'
357
+ # Warn user if their transcriber won't work locally
358
+ if inits['transcribe'].val:
359
+ if inits['transcribeBackend'].val in localTranscribers:
360
+ inits['transcribeBackend'].val = localTranscribers[self.params['transcribeBackend'].val]
361
+ else:
362
+ alert(4605, strFields={"transcriber": inits['transcribeBackend'].val})
363
+ inits['transcribeBackend'].val = list(localTranscribers.values())[0]
364
+ # Store recordings from this routine
365
+ code = (
366
+ "# tell mic to keep hold of current recording in %(name)s.clips and transcript (if applicable) in %(name)s.scripts\n"
367
+ "# this will also update %(name)s.lastClip and %(name)s.lastScript\n"
368
+ "%(name)sClip, %(name)sScript = %(name)s.bank(\n"
369
+ )
370
+ buff.writeIndentedLines(code % inits)
371
+ buff.setIndentLevel(1, relative=True)
372
+ code = (
373
+ "tag='%(loop)s', transcribe='%(transcribeBackend)s',\n"
374
+ )
375
+ buff.writeIndentedLines(code % inits)
376
+ if transcribe:
377
+ code = (
378
+ "language=%(transcribeLang)s, expectedWords=%(transcribeWords)s\n"
379
+ )
380
+ else:
381
+ code = (
382
+ "config=None\n"
383
+ )
384
+ buff.writeIndentedLines(code % inits)
385
+ buff.setIndentLevel(-1, relative=True)
386
+ code = (
387
+ ")\n"
388
+ "%(loop)s.addData('%(name)s.clip', os.path.join(%(name)sRecFolder, %(filename)s))\n"
389
+ )
390
+ buff.writeIndentedLines(code % inits)
391
+ if transcribe:
392
+ code = (
393
+ "%(loop)s.addData('%(name)s.script', %(name)sScript)\n"
394
+ )
395
+ buff.writeIndentedLines(code % inits)
396
+ # Write base end routine code
397
+ BaseComponent.writeRoutineEndCode(self, buff)
398
+
399
+ def writeRoutineEndCodeJS(self, buff):
400
+ inits = getInitVals(self.params)
401
+ inits['routine'] = self.parentName
402
+ if len(self.exp.flow._loopList):
403
+ inits['loop'] = self.exp.flow._loopList[-1].params['name']
404
+ inits['filename'] = f"'recording_{inits['name']}_{inits['loop']}_' + {inits['loop']}.thisN"
405
+ else:
406
+ inits['loop'] = ""
407
+ inits['filename'] = f"'recording_{inits['name']}'"
408
+ if inits['transcribeBackend'].val in allTranscribers:
409
+ inits['transcribeBackend'].val = allTranscribers[self.params['transcribeBackend'].val]
410
+ # Warn user if their transcriber won't work online
411
+ if inits['transcribe'].val and inits['transcribeBackend'].val not in onlineTranscribers.values():
412
+ alert(4605, strFields={"transcriber": inits['transcribeBackend'].val})
413
+ inits['transcribeBackend'].val = list(onlineTranscribers.values())[0]
414
+
415
+ # Write base end routine code
416
+ BaseComponent.writeRoutineEndCodeJS(self, buff)
417
+ # Store recordings from this routine
418
+ code = (
419
+ "// stop the microphone (make the audio data ready for upload)\n"
420
+ "await %(name)s.stop();\n"
421
+ "// get the recording\n"
422
+ "%(name)s.lastClip = await %(name)s.getRecording({\n"
423
+ )
424
+ buff.writeIndentedLines(code % inits)
425
+ buff.setIndentLevel(1, relative=True)
426
+ code = (
427
+ "tag: %(filename)s,\n"
428
+ "flush: false\n"
429
+ )
430
+ buff.writeIndentedLines(code % inits)
431
+ buff.setIndentLevel(-1, relative=True)
432
+ code = (
433
+ "});\n"
434
+ "psychoJS.experiment.addData('%(name)s.clip', %(filename)s);\n"
435
+ "// start the asynchronous upload to the server\n"
436
+ "%(name)s.lastClip.upload();\n"
437
+ )
438
+ buff.writeIndentedLines(code % inits)
439
+ if self.params['transcribe'].val:
440
+ code = (
441
+ "// transcribe the recording\n"
442
+ "const transcription = await %(name)s.lastClip.transcribe({\n"
443
+ )
444
+ buff.writeIndentedLines(code % inits)
445
+ buff.setIndentLevel(1, relative=True)
446
+ code = (
447
+ "languageCode: %(transcribeLang)s,\n"
448
+ "engine: sound.AudioClip.Engine.%(transcribeBackend)s,\n"
449
+ "wordList: %(transcribeWords)s\n"
450
+ )
451
+ buff.writeIndentedLines(code % inits)
452
+ buff.setIndentLevel(-1, relative=True)
453
+ code = (
454
+ "});\n"
455
+ "%(name)s.lastScript = transcription.transcript;\n"
456
+ "%(name)s.lastConf = transcription.confidence;\n"
457
+ "psychoJS.experiment.addData('%(name)s.transcript', %(name)s.lastScript);\n"
458
+ "psychoJS.experiment.addData('%(name)s.confidence', %(name)s.lastConf);\n"
459
+ )
460
+ buff.writeIndentedLines(code % inits)
461
+
462
+ def writeExperimentEndCode(self, buff):
463
+ """Write the code that will be called at the end of
464
+ an experiment (e.g. save log files or reset hardware)
465
+ """
466
+ inits = getInitVals(self.params)
467
+ if len(self.exp.flow._loopList):
468
+ currLoop = self.exp.flow._loopList[-1] # last (outer-most) loop
469
+ else:
470
+ currLoop = self.exp._expHandler
471
+ inits['loop'] = currLoop.params['name']
472
+ if inits['outputType'].val == 'default':
473
+ inits['outputType'].val = 'wav'
474
+ # Save recording
475
+ code = (
476
+ "# save %(name)s recordings\n"
477
+ "for tag in %(name)s.clips:"
478
+ )
479
+ buff.writeIndentedLines(code % inits)
480
+ buff.setIndentLevel(1, relative=True)
481
+ code = (
482
+ "for i, clip in enumerate(%(name)s.clips[tag]):\n"
483
+ )
484
+ buff.writeIndentedLines(code % inits)
485
+ buff.setIndentLevel(1, relative=True)
486
+ code = (
487
+ "clip.save(os.path.join(%(name)sRecFolder, 'recording_%(name)s_%%s_%%s.%(outputType)s' %% (tag, i)))\n"
488
+ )
489
+ buff.writeIndentedLines(code % inits)
490
+ buff.setIndentLevel(-2, relative=True)
@@ -293,28 +293,27 @@ class PolygonComponent(BaseVisualComponent):
293
293
  if self.params['interpolate'].val != 'linear':
294
294
  interpolate = 'false'
295
295
 
296
- code += (" ori: {ori}, \n"
297
- " pos: {pos}, \n"
298
- " draggable: {draggable}, \n"
299
- " anchor: {anchor},\n"
300
- " lineWidth: {lineWidth}, \n"
301
- " colorSpace: {colorSpace},\n")
302
-
303
- if inits['lineColor'] == 'undefined':
304
- code += " lineColor: {lineColor},\n"
305
- else:
306
- code += " lineColor: new util.Color({lineColor}),\n"
307
-
308
- if inits['fillColor'] == 'undefined':
309
- code += " fillColor: {fillColor},\n"
310
- else:
311
- code += " fillColor: new util.Color({fillColor}),\n"
312
-
313
-
314
- code += ( " fillColor: {fillColor},\n"
315
- " opacity: {opacity}, depth: {depth}, interpolate: {interpolate},\n"
316
- "}});\n\n")
317
-
296
+ # make a util.Color object for non-transparent
297
+ for key in ("fillColor", "lineColor"):
298
+ if inits[key].val != "undefined":
299
+ inits[key].val = "new util.Color(%s)" % inits[key]
300
+ inits[key].valType = "code"
301
+ # add other params
302
+ code += (
303
+ " ori: {ori}, \n"
304
+ " pos: {pos}, \n"
305
+ " draggable: {draggable}, \n"
306
+ " anchor: {anchor}, \n"
307
+ " lineWidth: {lineWidth}, \n"
308
+ " lineColor: {lineColor}, \n"
309
+ " fillColor: {fillColor}, \n"
310
+ " colorSpace: {colorSpace}, \n"
311
+ " opacity: {opacity}, \n"
312
+ " depth: {depth}, \n"
313
+ " interpolate: {interpolate}, \n"
314
+ "}});\n"
315
+ "\n"
316
+ )
318
317
  buff.writeIndentedLines(code.format(name=inits['name'],
319
318
  unitsStr=unitsStr,
320
319
  anchor=inits['anchor'],
@@ -1881,11 +1881,11 @@ class SettingsComponent:
1881
1881
  " # if not given a window to setup, make one\n"
1882
1882
  " win = visual.Window(\n"
1883
1883
  " size=_winSize, fullscr=_fullScr, screen=%(screenNumber)s,\n"
1884
- " winType=%(winType)s, allowStencil=%(allowStencil)s,\n"
1884
+ " winType=%(winType)s, allowGUI=%(allowGUI)s, allowStencil=%(allowStencil)s,\n"
1885
1885
  " monitor=%(Monitor)s, color=%(color)s, colorSpace=%(colorSpace)s,\n"
1886
1886
  " backgroundImage=%(backgroundImg)s, backgroundFit=%(backgroundFit)s,\n"
1887
1887
  " blendMode=%(blendMode)s, useFBO=%(useFBO)s,\n"
1888
- " units=%(Units)s, \n"
1888
+ " units=%(Units)s,\n"
1889
1889
  " checkTiming=False # we're going to do this ourselves in a moment\n"
1890
1890
  " )\n"
1891
1891
  "else:\n"
@@ -1914,12 +1914,6 @@ class SettingsComponent:
1914
1914
  )
1915
1915
  buff.writeIndentedLines(code % params)
1916
1916
 
1917
- # Show/hide mouse according to param
1918
- code = (
1919
- "win.mouseVisible = %s\n"
1920
- )
1921
- buff.writeIndentedLines(code % allowGUI)
1922
-
1923
1917
  # Reset splash message
1924
1918
  code = (
1925
1919
  "win.hideMessage()\n"
@@ -2194,12 +2188,17 @@ class SettingsComponent:
2194
2188
  buff.writeIndentedLines(code)
2195
2189
 
2196
2190
  # Write End Experiment code component
2197
- for thisRoutine in list(self.exp.routines.values()):
2198
- # a single routine is a list of components:
2199
- for thisComp in thisRoutine:
2200
- if hasattr(thisComp, "writeExperimentEndCodeJS"):
2201
- thisComp.writeExperimentEndCodeJS(buff)
2202
-
2191
+ for thisRoutine in self.exp.flow:
2192
+ # write for regular Routines
2193
+ if isinstance(thisRoutine, Routine):
2194
+ for thisComp in thisRoutine:
2195
+ if hasattr(thisComp, "writeExperimentEndCodeJS"):
2196
+ thisComp.writeExperimentEndCodeJS(buff)
2197
+ # write for standalone Routines
2198
+ if isinstance(thisRoutine, BaseStandaloneRoutine):
2199
+ if hasattr(thisRoutine, "writeExperimentEndCodeJS"):
2200
+ thisRoutine.writeExperimentEndCodeJS(buff)
2201
+
2203
2202
  code = ("psychoJS.window.close();\n"
2204
2203
  "psychoJS.quit({message: message, isCompleted: isCompleted});\n\n"
2205
2204
  "return Scheduler.Event.QUIT;\n")