|
@@ -209,49 +209,81 @@
|
|
|
"metadata": {},
|
|
|
"outputs": [],
|
|
|
"source": [
|
|
|
- "class SoundController:\n",
|
|
|
+ "class SoundController(FPSTimes):\n",
|
|
|
" \n",
|
|
|
- " def __init__(self):\n",
|
|
|
- " sd.default.samplerate = 44100\n",
|
|
|
+ " default_cfg = {\n",
|
|
|
+ " 'frequencies': [440, 880, 6000],\n",
|
|
|
+ " 'pulse_duration': 0.05,\n",
|
|
|
+ " 'sample_rate': 44100,\n",
|
|
|
+ " 'latency': 0.25,\n",
|
|
|
+ " 'log_file': 'sound_log.csv'\n",
|
|
|
+ " }\n",
|
|
|
+ " \n",
|
|
|
+ " def __init__(self, cfg):\n",
|
|
|
+ " super(SoundController, self).__init__()\n",
|
|
|
+ "\n",
|
|
|
+ " #sd.default.samplerate = cfg['sample_rate']\n",
|
|
|
" #sd.default.device = 20 # set the device id\n",
|
|
|
" self.stopped = False\n",
|
|
|
- " self.frame_counter = []\n",
|
|
|
- " \n",
|
|
|
- " tone = SoundController.get_pure_tone(440, 0.05, 44100) * 0.5\n",
|
|
|
- " self.tone_stereo = np.column_stack((tone, tone))\n",
|
|
|
+ " self.cfg = cfg # sound settings\n",
|
|
|
+ " self.actual_sound = 0 # current sound to play\n",
|
|
|
+ " with open(self.cfg['log_file'], 'w') as f: # refresh log file\n",
|
|
|
+ " f.write('time,sound_id\\n')\n",
|
|
|
+ "\n",
|
|
|
+ " # Initialize sounds\n",
|
|
|
+ " silence = np.zeros(int(cfg['pulse_duration'] * cfg['sample_rate']), dtype='float32')\n",
|
|
|
+ " self.sounds = {\n",
|
|
|
+ " 0: np.column_stack((silence, silence))\n",
|
|
|
+ " }\n",
|
|
|
+ " for i, freq in enumerate(cfg['frequencies']):\n",
|
|
|
+ " tone = SoundController.get_pure_tone(freq, cfg['pulse_duration'], cfg['sample_rate']) * 0.75\n",
|
|
|
+ " \n",
|
|
|
+ " # TODO add tone onset offset\n",
|
|
|
+ " \n",
|
|
|
+ " self.sounds[i + 1] = np.column_stack((tone, tone))\n",
|
|
|
" \n",
|
|
|
- " self.stream = sd.OutputStream(samplerate=sd.default.samplerate, channels=2, dtype='float32', blocksize=100)\n",
|
|
|
+ " # Initialize sound stream\n",
|
|
|
+ " self.stream = sd.OutputStream(samplerate=cfg['sample_rate'], channels=2, dtype='float32')\n",
|
|
|
"\n",
|
|
|
" @staticmethod\n",
|
|
|
" def get_pure_tone(freq, duration, sample_rate=44100):\n",
|
|
|
" x = np.linspace(0, duration * freq * 2*np.pi, int(duration*sample_rate), dtype=np.float32)\n",
|
|
|
" return np.sin(x)\n",
|
|
|
"\n",
|
|
|
+ " def log_event(self, t_event, sound_id):\n",
|
|
|
+ " with open(self.cfg['log_file'], 'a') as f:\n",
|
|
|
+ " f.write(\",\".join([str(x) for x in (t_event, sound_id)]) + \"\\n\")\n",
|
|
|
+ " \n",
|
|
|
" def start(self):\n",
|
|
|
" self.stream.start()\n",
|
|
|
- " self.last_chirp = time.time()\n",
|
|
|
- " th = threading.Thread(target=self.update, args=())\n",
|
|
|
- " th.start()\n",
|
|
|
- " return th\n",
|
|
|
+ "\n",
|
|
|
+ " self._th = threading.Thread(target=self.update, args=())\n",
|
|
|
+ " self._th.start()\n",
|
|
|
" \n",
|
|
|
" def stop(self):\n",
|
|
|
" self.stopped = True\n",
|
|
|
- " \n",
|
|
|
+ " time.sleep(0.3)\n",
|
|
|
+ " self._th.join()\n",
|
|
|
+ " print('Sound controller stopped')\n",
|
|
|
+ "\n",
|
|
|
" def update(self):\n",
|
|
|
+ " next_beat = time.time() + self.cfg['latency']\n",
|
|
|
+ " \n",
|
|
|
" while not self.stopped:\n",
|
|
|
" t0 = time.time()\n",
|
|
|
- " if t0 > self.last_chirp + 0.25:\n",
|
|
|
- " self.last_chirp = t0\n",
|
|
|
- " #sd.play(self.tone)\n",
|
|
|
- " self.frame_counter.append(time.time())\n",
|
|
|
- " self.stream.write(self.tone_stereo)\n",
|
|
|
- " self.stream.stop() \n",
|
|
|
+ " if t0 < next_beat:\n",
|
|
|
+ " continue\n",
|
|
|
" \n",
|
|
|
- " def get_frame_diffs(self):\n",
|
|
|
- " return 1.0/np.diff(np.array(self.frame_counter))\n",
|
|
|
- " \n",
|
|
|
- " def get_avg_fps(self):\n",
|
|
|
- " return self.get_frame_diffs().mean() "
|
|
|
+ " self.count() # count FPS\n",
|
|
|
+ " \n",
|
|
|
+ " #sd.play(self.tone) # has underrun errors, maybe try blocking?\n",
|
|
|
+ " actual_s = self.actual_sound\n",
|
|
|
+ " actual_t = self.frame_times[-1]\n",
|
|
|
+ " self.stream.write(self.sounds[actual_s])\n",
|
|
|
+ " self.log_event(actual_t, actual_s)\n",
|
|
|
+ " next_beat += self.cfg['latency']\n",
|
|
|
+ " \n",
|
|
|
+ " self.stream.stop()"
|
|
|
]
|
|
|
},
|
|
|
{
|