Browse Source

all controllers good state

asobolev 2 years ago
parent
commit
0c6d3a0836
5 changed files with 473 additions and 286 deletions
  1. 3 3
      background.ipynb
  2. 200 161
      controllers-2.ipynb
  3. 56 24
      controllers.ipynb
  4. 139 80
      sandbox/multiprocessing.ipynb
  5. 75 18
      sandbox/sound.ipynb

+ 3 - 3
background.ipynb

@@ -2,7 +2,7 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -20,7 +20,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -32,7 +32,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [],
    "source": [

File diff suppressed because it is too large
+ 200 - 161
controllers-2.ipynb


+ 56 - 24
controllers.ipynb

@@ -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()"
    ]
   },
   {

File diff suppressed because it is too large
+ 139 - 80
sandbox/multiprocessing.ipynb


+ 75 - 18
sandbox/sound.ipynb

@@ -2,7 +2,8 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 1,
+   "id": "6c693209",
    "metadata": {},
    "outputs": [],
    "source": [
@@ -14,7 +15,8 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 2,
+   "id": "19bdae6a",
    "metadata": {
     "scrolled": false
    },
@@ -29,7 +31,8 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 3,
+   "id": "6863eedc",
    "metadata": {},
    "outputs": [],
    "source": [
@@ -40,6 +43,7 @@
   },
   {
    "cell_type": "markdown",
+   "id": "cda62a58",
    "metadata": {},
    "source": [
     "### List available devices"
@@ -48,6 +52,7 @@
   {
    "cell_type": "code",
    "execution_count": 5,
+   "id": "a5504f48",
    "metadata": {
     "scrolled": true
    },
@@ -95,6 +100,7 @@
   {
    "cell_type": "code",
    "execution_count": 6,
+   "id": "b5c1ccbe",
    "metadata": {},
    "outputs": [
     {
@@ -114,6 +120,7 @@
   },
   {
    "cell_type": "markdown",
+   "id": "7ff264f8",
    "metadata": {},
    "source": [
     "### Play pure tone of a sample frequency"
@@ -122,6 +129,7 @@
   {
    "cell_type": "code",
    "execution_count": 8,
+   "id": "b8de8497",
    "metadata": {},
    "outputs": [],
    "source": [
@@ -140,6 +148,7 @@
   {
    "cell_type": "code",
    "execution_count": 7,
+   "id": "f22e0b7d",
    "metadata": {},
    "outputs": [],
    "source": [
@@ -161,6 +170,7 @@
   {
    "cell_type": "code",
    "execution_count": 12,
+   "id": "4dab0ea9",
    "metadata": {},
    "outputs": [],
    "source": [
@@ -171,6 +181,7 @@
   {
    "cell_type": "code",
    "execution_count": 10,
+   "id": "81fcd1e9",
    "metadata": {},
    "outputs": [],
    "source": [
@@ -186,6 +197,7 @@
   },
   {
    "cell_type": "markdown",
+   "id": "1b83ab86",
    "metadata": {},
    "source": [
     "### Plot pure tone"
@@ -194,6 +206,7 @@
   {
    "cell_type": "code",
    "execution_count": 9,
+   "id": "427da2ad",
    "metadata": {
     "scrolled": true
    },
@@ -229,6 +242,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
+   "id": "b71db3e2",
    "metadata": {},
    "outputs": [],
    "source": []
@@ -236,34 +250,75 @@
   {
    "cell_type": "code",
    "execution_count": 22,
+   "id": "f2e0104c",
    "metadata": {},
    "outputs": [],
    "source": [
-    "tone = get_pure_tone(440, 0.05, 44100) * 0.5\n",
-    "tone_stereo = np.column_stack((tone, tone))\n",
+    "tone1 = get_pure_tone(440, 0.05, 44100) * 0.5\n",
+    "tone2 = get_pure_tone(880, 0.05, 44100) * 0.5\n",
+    "silence = np.zeros(len(tone1), dtype='float32')\n",
+    "silence = np.zeros(44, dtype='float32')\n",
     "\n",
-    "stream = sd.OutputStream(samplerate=sd.default.samplerate, channels=2, dtype='float32')\n",
+    "tone1_stereo = np.column_stack((tone1, tone1))\n",
+    "tone2_stereo = np.column_stack((tone2, tone2))\n",
+    "silence_stereo = np.column_stack((silence, silence))\n",
     "\n",
-    "t_start = time.time()\n",
-    "last_chirp = time.time()\n",
-    "fps = []\n",
-    "lps = []\n",
+    "blocks = int(latency / pulse_duration)\n",
+    "sounds = {\n",
+    "    0: np.concatenate([silence_stereo for x in range(blocks)]),\n",
+    "    1: np.concatenate([tone1_stereo] + [silence_stereo for x in range(blocks - 1)]),\n",
+    "    2: np.concatenate([tone2_stereo] + [silence_stereo for x in range(blocks - 1)])\n",
+    "}\n",
+    "\n",
+    "latency = 0.25\n",
+    "pulse_duration = 0.05\n",
     "\n",
+    "stream = sd.OutputStream(samplerate=sd.default.samplerate, channels=2, dtype='float32')\n",
     "stream.start()\n",
-    "while time.time() < t_start + 10:\n",
+    "\n",
+    "t_start = time.time()\n",
+    "next_beat = time.time() + latency\n",
+    "while time.time() < t_start + 2:\n",
     "    t0 = time.time()\n",
-    "    if t0 > last_chirp + 0.25:\n",
-    "        last_chirp = t0\n",
-    "        #sd.play(self.tone)\n",
-    "        fps.append(last_chirp)\n",
-    "        stream.write(tone_stereo)\n",
+    "    if t0 < next_beat:\n",
+    "        #time.sleep(0.0001)        # not to spin the wheels too much\n",
+    "        #stream.write(sounds[0])  # silence\n",
+    "        if stream.write_available > 0:\n",
+    "            stream.write(silence_stereo)\n",
+    "        continue\n",
+    "\n",
+    "    #stream.write(sounds[1])\n",
+    "    stream.write(tone1_stereo)\n",
+    "    next_beat += latency\n",
     "        \n",
     "stream.stop()"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "id": "8ce68442",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "5"
+      ]
+     },
+     "execution_count": 17,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "blocks"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": 23,
+   "id": "035cba14",
    "metadata": {},
    "outputs": [
     {
@@ -296,6 +351,7 @@
   {
    "cell_type": "code",
    "execution_count": 25,
+   "id": "c4b4eef1",
    "metadata": {},
    "outputs": [
     {
@@ -323,6 +379,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
+   "id": "5e3822f8",
    "metadata": {},
    "outputs": [],
    "source": []
@@ -330,7 +387,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": "Python 3 (ipykernel)",
    "language": "python",
    "name": "python3"
   },
@@ -344,7 +401,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.10"
+   "version": "3.8.5"
   }
  },
  "nbformat": 4,