{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "from nbmultitask import ProcessWithLogAndControls\n",
    "from IPython.display import clear_output\n",
    "import multiprocessing as mp\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from time import sleep\n",
    "\n",
    "def fn(x):\n",
    "    while True:\n",
    "        print(x.value)\n",
    "        x.value = x.value**1.01\n",
    "        sleep(0.1)\n",
    "\n",
    "from multiprocessing import Value\n",
    "x = Value('f',2)\n",
    "task = ProcessWithLogAndControls(target=fn, args=(x,), name=\"exponential growth\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#task.control_panel()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.3297741413116455\n",
      "2.80667781829834\n"
     ]
    }
   ],
   "source": [
    "task.start()\n",
    "time.sleep(2)\n",
    "print(x.value)\n",
    "time.sleep(2)\n",
    "print(x.value)\n",
    "task.stop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "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)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_func(selector):\n",
    "    import sounddevice as sd\n",
    "    import numpy as np\n",
    "    import time\n",
    "\n",
    "    tone1 = np.sin(np.linspace(0, 0.05 * 440 * 2*np.pi, int(0.05*44100), dtype=np.float32))\n",
    "    tone2 = np.sin(np.linspace(0, 0.05 * 880 * 2*np.pi, int(0.05*44100), dtype=np.float32))\n",
    "    ts1 = np.column_stack((tone1, tone1))\n",
    "    ts2 = np.column_stack((tone2, tone2))\n",
    "\n",
    "    sd.default.samplerate = 44100\n",
    "    stream = sd.OutputStream(samplerate=44100, channels=2, dtype='float32')\n",
    "    stream.start()\n",
    "\n",
    "    latency = 0.25\n",
    "    next_beat = time.time() + latency\n",
    "    with open('sound_log.csv', 'w') as f:\n",
    "        f.write(\"time,id\\n\")\n",
    "\n",
    "    beats = 0\n",
    "    while beats < 40:\n",
    "        t0 = time.time()\n",
    "        if t0 < next_beat:\n",
    "            time.sleep(0.0001)  # not to spin the wheels too much\n",
    "            continue\n",
    "\n",
    "        \n",
    "        with open('sound_log.csv', 'a') as f:\n",
    "            f.write(\",\".join([str(x) for x in (t0, 1)]) + \"\\n\")\n",
    "            \n",
    "        #sd.play(self.tone)  # has underrun errors, maybe try blocking?\n",
    "        if selector.value == 1:\n",
    "            stream.write(ts1)\n",
    "        else:\n",
    "            stream.write(ts2)\n",
    "            \n",
    "        beats += 1\n",
    "        next_beat += latency\n",
    "        \n",
    "    stream.stop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n",
      "ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred\n"
     ]
    }
   ],
   "source": [
    "selector = mp.Value('f', 0)\n",
    "#is_running = Value(c_bool, True)\n",
    "#sc = ProcessWithLogAndControls(target=sound_controller, args=(selector, is_running), name=\"sounds\")\n",
    "sc = ProcessWithLogAndControls(target=test_func, args=(selector,), name=\"foo\")\n",
    "sc.start()\n",
    "time.sleep(2)\n",
    "selector.value = 1\n",
    "time.sleep(2)\n",
    "sc.stop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [],
   "source": [
    "sc.stop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "ds = np.loadtxt('sound_log.csv', delimiter=',', skiprows=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7f03b1413c10>]"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 10408 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(np.diff(ds[:, 0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "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 sound_controller(selector, is_running, latency=0.25):\n",
    "    \"\"\"\n",
    "    selector        mp.Value object to set the sound to be played\n",
    "    is_running      mp.Value object to stop the loop\n",
    "    \"\"\"\n",
    "    print('Loading SC..')\n",
    "    \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",
    "\n",
    "    sounds = {\n",
    "        0: np.column_stack((silence, silence)),\n",
    "        1: np.column_stack((tone1, tone1)),\n",
    "        2: np.column_stack((tone2, tone2))\n",
    "    }\n",
    "\n",
    "    stream = sd.OutputStream(samplerate=44100, channels=2, dtype='float32')\n",
    "    stream.start()\n",
    "    \n",
    "    print('Creating sounds..')\n",
    "        \n",
    "    next_beat = time.time() + latency\n",
    "    beat_times = collections.deque(maxlen=10000)\n",
    "\n",
    "    \n",
    "    while is_running.value:\n",
    "        print('Running loop..')\n",
    "        \n",
    "        t0 = time.time()\n",
    "        if t0 < next_beat:\n",
    "            continue\n",
    "\n",
    "        beat_times.append(t0)\n",
    "        #sd.play(self.tone)  # has underrun errors, maybe try blocking?\n",
    "        stream.write(sounds[int(selector.value)])\n",
    "        next_beat += latency\n",
    "        \n",
    "    stream.stop()\n",
    "    print('Sound controller stopped')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
