Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

paradigm.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Sun Jul 22 17:08:29 2018
  5. @author: ale, modified by Ioannis Vlachos
  6. """
  7. import os.path
  8. import wave
  9. from abc import ABC, abstractmethod
  10. import munch
  11. # import pyaudio
  12. import pyttsx3
  13. # import sounddevice as sd
  14. # import soundfile as sf
  15. import aux
  16. from aux import log
  17. class Paradigm(ABC):
  18. '''
  19. Description: abstract class for paradigms
  20. '''
  21. # p = pyaudio.PyAudio()
  22. # initialize audio
  23. engine = pyttsx3.init()
  24. # engine.setProperty('voice','com.apple.speech.synthesis.voice.anna.premium')
  25. engine.setProperty('voice','mb-de6') # best voice so far for german, note: https://github.com/numediart/MBROLA-voices
  26. # def __init__(self, pyttsx_rate=100):
  27. # # rate = engine.getProperty('rate')-150
  28. # self.engine.setProperty('rate',pyttsx_rate)
  29. # log.info(f'pyttsx3 rate is: {pyttsx_rate}')
  30. # return None
  31. @abstractmethod
  32. def __init__(self, *args, **kwargs):
  33. super().__init__()
  34. self.params = munch.Munch({})
  35. if 'params' in kwargs.keys():
  36. self.params = kwargs['params']
  37. else:
  38. self.params = aux.load_config()
  39. @abstractmethod
  40. def process_result(self,decision=None):
  41. '''
  42. Process the result based on the initialized paradigm
  43. Parameters:
  44. decision: boolean (value of the last decision, default None if it is a training or screening paradigm mode)
  45. Return:
  46. finish: boolean (True if the paradigm is finished, False otherwise)
  47. '''
  48. return finish
  49. @abstractmethod
  50. def present_stimulus(self,audio=True):
  51. '''
  52. Play auditorly the current stimulus
  53. Parmeters:
  54. audio: boolean (default True for enabling the audio playback)
  55. Return:
  56. stimulus: string (string version of the played stimulus)
  57. '''
  58. return stimulus
  59. @abstractmethod
  60. def get_current_state(self):
  61. '''
  62. Return the current state
  63. Parameters:
  64. nothing
  65. Return:
  66. state: list with two elements
  67. general_state: string (general state, e.g. current selected string)
  68. specific_state: string (specific state, e.g. current selection)
  69. '''
  70. return general_state, specific_state
  71. @abstractmethod
  72. def get_mode(self):
  73. '''
  74. Return the mode of the used paradigm: Screening, Training, Validation or Free
  75. Parameters:
  76. nothing
  77. Return:
  78. mode: string (paradigm mode: 'Screening', 'Training', 'Validation' or 'Free')
  79. '''
  80. pass
  81. @abstractmethod
  82. def save_log(self):
  83. '''
  84. Save the log
  85. Parameters:
  86. nothing
  87. Return:
  88. nothing
  89. '''
  90. pass
  91. def close(self):
  92. '''
  93. Close the speller and all the active processes
  94. '''
  95. #print('quit')
  96. #self.p.terminate()
  97. return
  98. def _read_config(self,file):
  99. '''
  100. Read the configuration yaml file
  101. Parameters:
  102. file: string (name of the configuration yaml file)
  103. Return:
  104. config: munch structure (configuration file)
  105. '''
  106. try:
  107. with open(file) as stream:
  108. config = munch.fromYAML(stream)
  109. return config
  110. except Exception as e:
  111. raise e
  112. def _play_audio(self,wav_file,start_trigger,end_trigger):
  113. '''
  114. Play the wav file using wave and pyaudio
  115. Parameters:
  116. wav_file: string (name of the wav file)
  117. start_trigger: int (trigger of the start of the sound)
  118. end_trigger: int (trigger of the end of the sound)
  119. '''
  120. CHUNK = 1024
  121. wf = wave.open(wav_file,'rb')
  122. stream = self.p.open(format=self.p.get_format_from_width(wf.getsampwidth()),
  123. channels = wf.getnchannels(),
  124. rate = wf.getframerate(),
  125. output=True)
  126. self._event_trigger(start_trigger)
  127. data = wf.readframes(CHUNK)
  128. while data:
  129. stream.write(data)
  130. data = wf.readframes(CHUNK)
  131. stream.stop_stream()
  132. self._event_trigger(end_trigger)
  133. stream.close()
  134. def _play_sound(self,wav_file,start_trigger,end_trigger):
  135. '''
  136. Play the wav file using wave and sounddevice
  137. Parameters:
  138. wav_file: string (name of the wav file)
  139. start_trigger: int (trigger of the start of the sound)
  140. end_trigger: int (trigger of the end of the sound)
  141. '''
  142. try:
  143. os.path.getsize(wav_file) # just to raise the correct exception if not found
  144. except Exception as e:
  145. raise e
  146. # data,fs = sf.read(wav_file)
  147. self._event_trigger(start_trigger)
  148. # sd.play(data,fs,blocking=True)
  149. os.system('aplay ' + wav_file + ' -q')
  150. self._event_trigger(end_trigger)
  151. def _say(self,text,start_trigger,end_trigger):
  152. '''
  153. Convert text to speech and play it using pyttsx3
  154. Parameters:
  155. text: string or list[string] (text to convert and play)
  156. start_trigger: int (trigger of the start of the sound)
  157. end_trigger: int (trigger of the end of the sound)
  158. '''
  159. def onStart(name):
  160. return self._event_trigger(start_trigger)
  161. def onEnd(name,completed):
  162. return self._event_trigger(end_trigger)
  163. token_start = self.engine.connect('started-utterance',onStart)
  164. token_end = self.engine.connect('finished-utterance',onEnd)
  165. if text.__class__ == list:
  166. for sentence in text:
  167. if sentence != '.':
  168. self.engine.say(sentence)
  169. else:
  170. if text != '.':
  171. self.engine.say(text)
  172. self.engine.runAndWait()
  173. self.engine.disconnect(token_start)
  174. self.engine.disconnect(token_end)
  175. def _event_trigger(self,number):
  176. '''
  177. Send a trigger for an event
  178. Parameters:
  179. number: integer (number to send as trigger)
  180. '''
  181. #do something
  182. # print(number)
  183. return
  184. def __del__(self):
  185. log.debug('speller destructed')