{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import queue\n", "import sys\n", "\n", "import sounddevice as sd\n", "import soundfile as sf\n", "import numpy # Make sure NumPy is loaded before it is used in the callback\n", "assert numpy # avoid \"imported but unused\" message (W0611)\n", "\n", "from situtils import FPSTimes\n", "\n", "import threading\n", "\n", "import time" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class method version" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting microphones.py\n" ] } ], "source": [ "%%writefile microphones.py \n", "#^IMPORTANT: essential to make multiprocessing work\n", "\n", "import queue\n", "import sys\n", "\n", "import sounddevice as sd\n", "import soundfile as sf\n", "import numpy # Make sure NumPy is loaded before it is used in the callback\n", "assert numpy # avoid \"imported but unused\" message (W0611)\n", "\n", "from situtils import FPSTimes\n", "\n", "import time\n", "\n", "class MicrophoneController(FPSTimes):\n", " # https://python-sounddevice.readthedocs.io/en/0.3.15/examples.html#recording-with-arbitrary-duration\n", " \n", " @staticmethod\n", " def callback(indata, frames, time, status):\n", " \"\"\"This is called (from a separate thread) for each audio block.\"\"\"\n", " if status:\n", " print(status, file=sys.stderr)\n", " MicrophoneController.queue.put(indata.copy())\n", " \n", " \n", " @classmethod\n", " def run(cls, status, cfg):\n", " # MicrophoneController.initialize(cfg)\n", " print(\"Running.\")\n", " import sounddevice as sd # must be inside the function\n", " \n", " if cfg['channel_selectors']: # make it work without ASIO\n", " # https://python-sounddevice.readthedocs.io/en/0.3.15/api/platform-specific-settings.html\n", " asio_in = sd.AsioSettings(channel_selectors=cfg['channel_selectors'])\n", " else:\n", " asio_in = None\n", "\n", " MicrophoneController.queue = queue.Queue() # kind of a hack\n", "\n", " stream = sd.InputStream(samplerate=cfg['sample_rate'], device=cfg['device'], channels=cfg['number_channels'], callback=MicrophoneController.callback, extra_settings = asio_in)\n", " \n", " filename = cfg['file_path']\n", " file = sf.SoundFile(filename, mode='w', samplerate=cfg['sample_rate'], channels=cfg['number_channels'],subtype='PCM_32') # 'w': overwrite mode, 'x': raises error if file exists\n", "\n", " # experiment status: 1 - idle, 2 - running (recording, logging), 0 - stopped\n", " with file as f:\n", " while status.value > 0:\n", " try:\n", " if status.value == 2:\n", "\n", " # start stream if not active yet\n", " if not stream.active:\n", " print(\"Audio input stream started.\")\n", " t0 = time.time()\n", " stream.start()\n", " with open(cfg['csv_path'], 'a') as f_csv:\n", " f_csv.write(\",\".join([str(x) for x in (t0,)]) + \"\\n\")\n", "\n", " f.write(MicrophoneController.queue.get())\n", "\n", " else:\n", " time.sleep(0.005)\n", " except KeyboardInterrupt:\n", " stream.stop()\n", " stream.close()\n", " break\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Instance version" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing microphones_instance.py\n" ] } ], "source": [ "%%writefile microphones_instance.py \n", "#^IMPORTANT: essential to make multiprocessing work\n", "\n", "import queue\n", "import sys\n", "\n", "import sounddevice as sd\n", "import soundfile as sf\n", "import numpy # Make sure NumPy is loaded before it is used in the callback\n", "assert numpy # avoid \"imported but unused\" message (W0611)\n", "\n", "from situtils import FPSTimes\n", "\n", "import time\n", "\n", "import threading\n", "\n", "class MicrophoneControllerInstance(FPSTimes):\n", " # https://python-sounddevice.readthedocs.io/en/0.3.15/examples.html#recording-with-arbitrary-duration\n", " def __init__(self, status, cfg):\n", " import sounddevice as sd # must be inside the function? TODO\n", " self.cfg = cfg\n", " self.samplerate = cfg['sample_rate']\n", " self.device = cfg['device']\n", " self.channels = cfg['number_channels']\n", " self.status = status\n", " \n", " if cfg['channel_selectors']: # make it work without ASIO\n", " # https://python-sounddevice.readthedocs.io/en/0.3.15/api/platform-specific-settings.html\n", " self.channel_selectors = cfg['channel_selectors']\n", " asio_in = sd.AsioSettings(channel_selectors=self.channel_selectors)\n", " else:\n", " asio_in = None\n", "\n", " MicrophoneControllerInstance.queue = queue.Queue() # kind of a hack\n", " \n", " self.stream = sd.InputStream(samplerate=self.samplerate, device=self.device, channels=self.channels, callback=self.callback, extra_settings = asio_in)\n", "\n", " self.filename = cfg['file_path']\n", " self.file = sf.SoundFile(self.filename, mode='w', samplerate=self.samplerate, channels=self.channels) # 'w': overwrite mode, 'x': raises error if file exists\n", " \n", " \n", " def start(self):\n", " self._th = threading.Thread(target=self.run, args=())\n", " self._th.start()\n", "\n", " def stop(self):\n", " time.sleep(0.2) # wait until device is released\n", " self._th.join()\n", " print('Microphones recording stopped')\n", "\n", " def start_stream(self):\n", " self.stream.start()\n", "\n", " def stop_stream(self):\n", " self.stream.stop()\n", " self.stream.close()\n", "\n", " # Used in all versions\n", " @staticmethod\n", " def callback(indata, frames, time, status):\n", " \"\"\"This is called (from a separate thread) for each audio block.\"\"\"\n", " if status:\n", " print(status, file=sys.stderr)\n", " MicrophoneControllerInstance.queue.put(indata.copy())\n", " \n", " # instance method\n", " def run(self):\n", " import sounddevice as sd # must be inside the function? TODO\n", " # experiment status: 1 - idle, 2 - running (recording, logging), 0 - stopped\n", " with self.file as file:\n", " while self.status.value > 0:\n", " if self.status.value == 2:\n", " \n", " # start stream if not active yet\n", " if not self.stream.active:\n", " print(\"Audio input stream started.\")\n", " self.stream.start()\n", " \n", " file.write(MicrophoneControllerInstance.queue.get())\n", " \n", " self.stop_stream()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Test microphones" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Import config file" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'record_audio': True,\n", " 'sample_rate': 44000,\n", " 'device': 1,\n", " 'number_channels': 2,\n", " 'channel_selectors': False,\n", " 'file_path': 'audio_new.mat5'}" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import json\n", "import os\n", "import multiprocess as mp\n", "\n", "# cfg_filename = os.path.join('..','profiles', 'miguel_socialSIT_test.json')\n", "cfg_filename = os.path.join('..','profiles', 'miguel_socialSIT_test_lord_sith.json')\n", "with open(cfg_filename) as json_file:\n", " cfg = json.load(json_file)\n", "\n", "cfg[\"microphones\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Test version using instance methods and no parallelization -> Works!" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Recording started\n", "Audio input stream started.\n", "Recording stopped\n" ] } ], "source": [ "# experiment status: 1 - idle, 2 - running (recording, logging), 0 - stopped\n", "status = mp.Value('i', 1)\n", "\n", "mc = MicrophoneControllerInstance(status, cfg[\"microphones\"])\n", "try:\n", " status.value = 2\n", " print(\"Recording started\")\n", " mc.run()\n", "except KeyboardInterrupt:\n", " status.value = 0\n", " mc.stop_stream()\n", " print(\"Recording stopped\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Test with class method instead of instance method -> Works!" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running.\n", "Audio input stream started.\n" ] }, { "ename": "TypeError", "evalue": "write() argument must be str, not numpy.ndarray", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", "Cell \u001b[1;32mIn[22], line 4\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[39m# experiment status: 1 - idle, 2 - running (recording, logging), 0 - stopped\u001b[39;00m\n\u001b[0;32m 3\u001b[0m status \u001b[39m=\u001b[39m mp\u001b[39m.\u001b[39mValue(\u001b[39m'\u001b[39m\u001b[39mi\u001b[39m\u001b[39m'\u001b[39m, \u001b[39m2\u001b[39m)\n\u001b[1;32m----> 4\u001b[0m MicrophoneController\u001b[39m.\u001b[39;49mrun(status,cfg[\u001b[39m\"\u001b[39;49m\u001b[39mmicrophones\u001b[39;49m\u001b[39m\"\u001b[39;49m])\n\u001b[0;32m 5\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m\"\u001b[39m\u001b[39mRecording stopped\u001b[39m\u001b[39m\"\u001b[39m)\n", "File \u001b[1;32mn:\\Miguel\\runSIT\\controllers\\microphones.py:77\u001b[0m, in \u001b[0;36mMicrophoneController.run\u001b[1;34m(cls, status, cfg)\u001b[0m\n\u001b[0;32m 75\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m\"\u001b[39m\u001b[39mAudio input stream started.\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m 76\u001b[0m stream\u001b[39m.\u001b[39mstart()\n\u001b[1;32m---> 77\u001b[0m \u001b[39mprint\u001b[39m(MicrophoneController\u001b[39m.\u001b[39mqueue\u001b[39m.\u001b[39mget())\n\u001b[0;32m 79\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 80\u001b[0m time\u001b[39m.\u001b[39msleep(\u001b[39m0.01\u001b[39m)\n", "\u001b[1;31mTypeError\u001b[0m: write() argument must be str, not numpy.ndarray" ] } ], "source": [ "from microphones import MicrophoneController\n", "# experiment status: 1 - idle, 2 - running (recording, logging), 0 - stopped\n", "status = mp.Value('i', 2)\n", "MicrophoneController.run(status,cfg[\"microphones\"])\n", "print(\"Recording stopped\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Test with multiprocessing using class method -> Works!" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [], "source": [ "from microphones import MicrophoneController # IMPORTANT: class must be imported from separate .py file, not from the notebook\n", "\n", "# experiment status: 1 - idle, 2 - running (recording, logging), 0 - stopped\n", "status = mp.Value('i', 1)\n", "\n", "mc = mp.Process(target=MicrophoneController.run, args=(status,cfg[\"microphones\"]))\n", "mc.start()" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mc" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Recording started\n" ] } ], "source": [ "status.value = 2\n", "\n", "print(\"Recording started\")" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [], "source": [ "status.value = 0\n", "\n", "mc.join()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Test with multiprocessing using instance method -> Error!" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "scrolled": true }, "outputs": [ { "ename": "TypeError", "evalue": "cannot pickle '_cffi_backend.__CDataOwnGC' object", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", "Cell \u001b[1;32mIn[23], line 9\u001b[0m\n\u001b[0;32m 6\u001b[0m microphoneController \u001b[39m=\u001b[39m MicrophoneControllerInstance(status, cfg[\u001b[39m\"\u001b[39m\u001b[39mmicrophones\u001b[39m\u001b[39m\"\u001b[39m])\n\u001b[0;32m 8\u001b[0m mc \u001b[39m=\u001b[39m mp\u001b[39m.\u001b[39mProcess(target\u001b[39m=\u001b[39mmicrophoneController\u001b[39m.\u001b[39mrun, args\u001b[39m=\u001b[39m())\n\u001b[1;32m----> 9\u001b[0m mc\u001b[39m.\u001b[39;49mstart()\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\multiprocess\\process.py:121\u001b[0m, in \u001b[0;36mBaseProcess.start\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 118\u001b[0m \u001b[39massert\u001b[39;00m \u001b[39mnot\u001b[39;00m _current_process\u001b[39m.\u001b[39m_config\u001b[39m.\u001b[39mget(\u001b[39m'\u001b[39m\u001b[39mdaemon\u001b[39m\u001b[39m'\u001b[39m), \\\n\u001b[0;32m 119\u001b[0m \u001b[39m'\u001b[39m\u001b[39mdaemonic processes are not allowed to have children\u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 120\u001b[0m _cleanup()\n\u001b[1;32m--> 121\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_popen \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_Popen(\u001b[39mself\u001b[39;49m)\n\u001b[0;32m 122\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_sentinel \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_popen\u001b[39m.\u001b[39msentinel\n\u001b[0;32m 123\u001b[0m \u001b[39m# Avoid a refcycle if the target function holds an indirect\u001b[39;00m\n\u001b[0;32m 124\u001b[0m \u001b[39m# reference to the process object (see bpo-30775)\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\multiprocess\\context.py:224\u001b[0m, in \u001b[0;36mProcess._Popen\u001b[1;34m(process_obj)\u001b[0m\n\u001b[0;32m 222\u001b[0m \u001b[39m@staticmethod\u001b[39m\n\u001b[0;32m 223\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_Popen\u001b[39m(process_obj):\n\u001b[1;32m--> 224\u001b[0m \u001b[39mreturn\u001b[39;00m _default_context\u001b[39m.\u001b[39;49mget_context()\u001b[39m.\u001b[39;49mProcess\u001b[39m.\u001b[39;49m_Popen(process_obj)\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\multiprocess\\context.py:336\u001b[0m, in \u001b[0;36mSpawnProcess._Popen\u001b[1;34m(process_obj)\u001b[0m\n\u001b[0;32m 333\u001b[0m \u001b[39m@staticmethod\u001b[39m\n\u001b[0;32m 334\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_Popen\u001b[39m(process_obj):\n\u001b[0;32m 335\u001b[0m \u001b[39mfrom\u001b[39;00m \u001b[39m.\u001b[39;00m\u001b[39mpopen_spawn_win32\u001b[39;00m \u001b[39mimport\u001b[39;00m Popen\n\u001b[1;32m--> 336\u001b[0m \u001b[39mreturn\u001b[39;00m Popen(process_obj)\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\multiprocess\\popen_spawn_win32.py:93\u001b[0m, in \u001b[0;36mPopen.__init__\u001b[1;34m(self, process_obj)\u001b[0m\n\u001b[0;32m 91\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m 92\u001b[0m reduction\u001b[39m.\u001b[39mdump(prep_data, to_child)\n\u001b[1;32m---> 93\u001b[0m reduction\u001b[39m.\u001b[39;49mdump(process_obj, to_child)\n\u001b[0;32m 94\u001b[0m \u001b[39mfinally\u001b[39;00m:\n\u001b[0;32m 95\u001b[0m set_spawning_popen(\u001b[39mNone\u001b[39;00m)\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\multiprocess\\reduction.py:63\u001b[0m, in \u001b[0;36mdump\u001b[1;34m(obj, file, protocol, *args, **kwds)\u001b[0m\n\u001b[0;32m 61\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mdump\u001b[39m(obj, file, protocol\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, \u001b[39m*\u001b[39margs, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwds):\n\u001b[0;32m 62\u001b[0m \u001b[39m \u001b[39m\u001b[39m'''Replacement for pickle.dump() using ForkingPickler.'''\u001b[39;00m\n\u001b[1;32m---> 63\u001b[0m ForkingPickler(file, protocol, \u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwds)\u001b[39m.\u001b[39;49mdump(obj)\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:394\u001b[0m, in \u001b[0;36mPickler.dump\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m 392\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mdump\u001b[39m(\u001b[39mself\u001b[39m, obj): \u001b[39m#NOTE: if settings change, need to update attributes\u001b[39;00m\n\u001b[0;32m 393\u001b[0m logger\u001b[39m.\u001b[39mtrace_setup(\u001b[39mself\u001b[39m)\n\u001b[1;32m--> 394\u001b[0m StockPickler\u001b[39m.\u001b[39;49mdump(\u001b[39mself\u001b[39;49m, obj)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:487\u001b[0m, in \u001b[0;36m_Pickler.dump\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m 485\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mproto \u001b[39m>\u001b[39m\u001b[39m=\u001b[39m \u001b[39m4\u001b[39m:\n\u001b[0;32m 486\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mframer\u001b[39m.\u001b[39mstart_framing()\n\u001b[1;32m--> 487\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49msave(obj)\n\u001b[0;32m 488\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwrite(STOP)\n\u001b[0;32m 489\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mframer\u001b[39m.\u001b[39mend_framing()\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:603\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 599\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(\u001b[39m\"\u001b[39m\u001b[39mTuple returned by \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m must have \u001b[39m\u001b[39m\"\u001b[39m\n\u001b[0;32m 600\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mtwo to six elements\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m reduce)\n\u001b[0;32m 602\u001b[0m \u001b[39m# Save the reduce() output and finally memoize the object\u001b[39;00m\n\u001b[1;32m--> 603\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49msave_reduce(obj\u001b[39m=\u001b[39;49mobj, \u001b[39m*\u001b[39;49mrv)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:717\u001b[0m, in \u001b[0;36m_Pickler.save_reduce\u001b[1;34m(self, func, args, state, listitems, dictitems, state_setter, obj)\u001b[0m\n\u001b[0;32m 715\u001b[0m \u001b[39mif\u001b[39;00m state \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 716\u001b[0m \u001b[39mif\u001b[39;00m state_setter \u001b[39mis\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 717\u001b[0m save(state)\n\u001b[0;32m 718\u001b[0m write(BUILD)\n\u001b[0;32m 719\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 720\u001b[0m \u001b[39m# If a state_setter is specified, call it instead of load_build\u001b[39;00m\n\u001b[0;32m 721\u001b[0m \u001b[39m# to update obj's with its previous state.\u001b[39;00m\n\u001b[0;32m 722\u001b[0m \u001b[39m# First, push state_setter and its tuple of expected arguments\u001b[39;00m\n\u001b[0;32m 723\u001b[0m \u001b[39m# (obj, state) onto the stack.\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:560\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 558\u001b[0m f \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mdispatch\u001b[39m.\u001b[39mget(t)\n\u001b[0;32m 559\u001b[0m \u001b[39mif\u001b[39;00m f \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 560\u001b[0m f(\u001b[39mself\u001b[39;49m, obj) \u001b[39m# Call unbound method with explicit self\u001b[39;00m\n\u001b[0;32m 561\u001b[0m \u001b[39mreturn\u001b[39;00m\n\u001b[0;32m 563\u001b[0m \u001b[39m# Check private dispatch table if any, or else\u001b[39;00m\n\u001b[0;32m 564\u001b[0m \u001b[39m# copyreg.dispatch_table\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:1186\u001b[0m, in \u001b[0;36msave_module_dict\u001b[1;34m(pickler, obj)\u001b[0m\n\u001b[0;32m 1183\u001b[0m \u001b[39mif\u001b[39;00m is_dill(pickler, child\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m) \u001b[39mand\u001b[39;00m pickler\u001b[39m.\u001b[39m_session:\n\u001b[0;32m 1184\u001b[0m \u001b[39m# we only care about session the first pass thru\u001b[39;00m\n\u001b[0;32m 1185\u001b[0m pickler\u001b[39m.\u001b[39m_first_pass \u001b[39m=\u001b[39m \u001b[39mFalse\u001b[39;00m\n\u001b[1;32m-> 1186\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave_dict(pickler, obj)\n\u001b[0;32m 1187\u001b[0m logger\u001b[39m.\u001b[39mtrace(pickler, \u001b[39m\"\u001b[39m\u001b[39m# D2\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m 1188\u001b[0m \u001b[39mreturn\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:972\u001b[0m, in \u001b[0;36m_Pickler.save_dict\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m 969\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwrite(MARK \u001b[39m+\u001b[39m DICT)\n\u001b[0;32m 971\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mmemoize(obj)\n\u001b[1;32m--> 972\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_batch_setitems(obj\u001b[39m.\u001b[39;49mitems())\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:998\u001b[0m, in \u001b[0;36m_Pickler._batch_setitems\u001b[1;34m(self, items)\u001b[0m\n\u001b[0;32m 996\u001b[0m \u001b[39mfor\u001b[39;00m k, v \u001b[39min\u001b[39;00m tmp:\n\u001b[0;32m 997\u001b[0m save(k)\n\u001b[1;32m--> 998\u001b[0m save(v)\n\u001b[0;32m 999\u001b[0m write(SETITEMS)\n\u001b[0;32m 1000\u001b[0m \u001b[39melif\u001b[39;00m n:\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:560\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 558\u001b[0m f \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mdispatch\u001b[39m.\u001b[39mget(t)\n\u001b[0;32m 559\u001b[0m \u001b[39mif\u001b[39;00m f \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 560\u001b[0m f(\u001b[39mself\u001b[39;49m, obj) \u001b[39m# Call unbound method with explicit self\u001b[39;00m\n\u001b[0;32m 561\u001b[0m \u001b[39mreturn\u001b[39;00m\n\u001b[0;32m 563\u001b[0m \u001b[39m# Check private dispatch table if any, or else\u001b[39;00m\n\u001b[0;32m 564\u001b[0m \u001b[39m# copyreg.dispatch_table\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:1427\u001b[0m, in \u001b[0;36msave_instancemethod0\u001b[1;34m(pickler, obj)\u001b[0m\n\u001b[0;32m 1424\u001b[0m \u001b[39m@register\u001b[39m(MethodType)\n\u001b[0;32m 1425\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39msave_instancemethod0\u001b[39m(pickler, obj):\n\u001b[0;32m 1426\u001b[0m logger\u001b[39m.\u001b[39mtrace(pickler, \u001b[39m\"\u001b[39m\u001b[39mMe1: \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m\"\u001b[39m, obj)\n\u001b[1;32m-> 1427\u001b[0m pickler\u001b[39m.\u001b[39;49msave_reduce(MethodType, (obj\u001b[39m.\u001b[39;49m\u001b[39m__func__\u001b[39;49m, obj\u001b[39m.\u001b[39;49m\u001b[39m__self__\u001b[39;49m), obj\u001b[39m=\u001b[39;49mobj)\n\u001b[0;32m 1428\u001b[0m logger\u001b[39m.\u001b[39mtrace(pickler, \u001b[39m\"\u001b[39m\u001b[39m# Me1\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m 1429\u001b[0m \u001b[39mreturn\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:692\u001b[0m, in \u001b[0;36m_Pickler.save_reduce\u001b[1;34m(self, func, args, state, listitems, dictitems, state_setter, obj)\u001b[0m\n\u001b[0;32m 690\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 691\u001b[0m save(func)\n\u001b[1;32m--> 692\u001b[0m save(args)\n\u001b[0;32m 693\u001b[0m write(REDUCE)\n\u001b[0;32m 695\u001b[0m \u001b[39mif\u001b[39;00m obj \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 696\u001b[0m \u001b[39m# If the object is already in the memo, this means it is\u001b[39;00m\n\u001b[0;32m 697\u001b[0m \u001b[39m# recursive. In this case, throw away everything we put on the\u001b[39;00m\n\u001b[0;32m 698\u001b[0m \u001b[39m# stack, and fetch the object back from the memo.\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:560\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 558\u001b[0m f \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mdispatch\u001b[39m.\u001b[39mget(t)\n\u001b[0;32m 559\u001b[0m \u001b[39mif\u001b[39;00m f \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 560\u001b[0m f(\u001b[39mself\u001b[39;49m, obj) \u001b[39m# Call unbound method with explicit self\u001b[39;00m\n\u001b[0;32m 561\u001b[0m \u001b[39mreturn\u001b[39;00m\n\u001b[0;32m 563\u001b[0m \u001b[39m# Check private dispatch table if any, or else\u001b[39;00m\n\u001b[0;32m 564\u001b[0m \u001b[39m# copyreg.dispatch_table\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:887\u001b[0m, in \u001b[0;36m_Pickler.save_tuple\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m 885\u001b[0m \u001b[39mif\u001b[39;00m n \u001b[39m<\u001b[39m\u001b[39m=\u001b[39m \u001b[39m3\u001b[39m \u001b[39mand\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mproto \u001b[39m>\u001b[39m\u001b[39m=\u001b[39m \u001b[39m2\u001b[39m:\n\u001b[0;32m 886\u001b[0m \u001b[39mfor\u001b[39;00m element \u001b[39min\u001b[39;00m obj:\n\u001b[1;32m--> 887\u001b[0m save(element)\n\u001b[0;32m 888\u001b[0m \u001b[39m# Subtle. Same as in the big comment below.\u001b[39;00m\n\u001b[0;32m 889\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mid\u001b[39m(obj) \u001b[39min\u001b[39;00m memo:\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:603\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 599\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(\u001b[39m\"\u001b[39m\u001b[39mTuple returned by \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m must have \u001b[39m\u001b[39m\"\u001b[39m\n\u001b[0;32m 600\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mtwo to six elements\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m reduce)\n\u001b[0;32m 602\u001b[0m \u001b[39m# Save the reduce() output and finally memoize the object\u001b[39;00m\n\u001b[1;32m--> 603\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49msave_reduce(obj\u001b[39m=\u001b[39;49mobj, \u001b[39m*\u001b[39;49mrv)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:717\u001b[0m, in \u001b[0;36m_Pickler.save_reduce\u001b[1;34m(self, func, args, state, listitems, dictitems, state_setter, obj)\u001b[0m\n\u001b[0;32m 715\u001b[0m \u001b[39mif\u001b[39;00m state \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 716\u001b[0m \u001b[39mif\u001b[39;00m state_setter \u001b[39mis\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 717\u001b[0m save(state)\n\u001b[0;32m 718\u001b[0m write(BUILD)\n\u001b[0;32m 719\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 720\u001b[0m \u001b[39m# If a state_setter is specified, call it instead of load_build\u001b[39;00m\n\u001b[0;32m 721\u001b[0m \u001b[39m# to update obj's with its previous state.\u001b[39;00m\n\u001b[0;32m 722\u001b[0m \u001b[39m# First, push state_setter and its tuple of expected arguments\u001b[39;00m\n\u001b[0;32m 723\u001b[0m \u001b[39m# (obj, state) onto the stack.\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:560\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 558\u001b[0m f \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mdispatch\u001b[39m.\u001b[39mget(t)\n\u001b[0;32m 559\u001b[0m \u001b[39mif\u001b[39;00m f \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 560\u001b[0m f(\u001b[39mself\u001b[39;49m, obj) \u001b[39m# Call unbound method with explicit self\u001b[39;00m\n\u001b[0;32m 561\u001b[0m \u001b[39mreturn\u001b[39;00m\n\u001b[0;32m 563\u001b[0m \u001b[39m# Check private dispatch table if any, or else\u001b[39;00m\n\u001b[0;32m 564\u001b[0m \u001b[39m# copyreg.dispatch_table\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:1186\u001b[0m, in \u001b[0;36msave_module_dict\u001b[1;34m(pickler, obj)\u001b[0m\n\u001b[0;32m 1183\u001b[0m \u001b[39mif\u001b[39;00m is_dill(pickler, child\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m) \u001b[39mand\u001b[39;00m pickler\u001b[39m.\u001b[39m_session:\n\u001b[0;32m 1184\u001b[0m \u001b[39m# we only care about session the first pass thru\u001b[39;00m\n\u001b[0;32m 1185\u001b[0m pickler\u001b[39m.\u001b[39m_first_pass \u001b[39m=\u001b[39m \u001b[39mFalse\u001b[39;00m\n\u001b[1;32m-> 1186\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave_dict(pickler, obj)\n\u001b[0;32m 1187\u001b[0m logger\u001b[39m.\u001b[39mtrace(pickler, \u001b[39m\"\u001b[39m\u001b[39m# D2\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m 1188\u001b[0m \u001b[39mreturn\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:972\u001b[0m, in \u001b[0;36m_Pickler.save_dict\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m 969\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwrite(MARK \u001b[39m+\u001b[39m DICT)\n\u001b[0;32m 971\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mmemoize(obj)\n\u001b[1;32m--> 972\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_batch_setitems(obj\u001b[39m.\u001b[39;49mitems())\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:998\u001b[0m, in \u001b[0;36m_Pickler._batch_setitems\u001b[1;34m(self, items)\u001b[0m\n\u001b[0;32m 996\u001b[0m \u001b[39mfor\u001b[39;00m k, v \u001b[39min\u001b[39;00m tmp:\n\u001b[0;32m 997\u001b[0m save(k)\n\u001b[1;32m--> 998\u001b[0m save(v)\n\u001b[0;32m 999\u001b[0m write(SETITEMS)\n\u001b[0;32m 1000\u001b[0m \u001b[39melif\u001b[39;00m n:\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:603\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 599\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(\u001b[39m\"\u001b[39m\u001b[39mTuple returned by \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m must have \u001b[39m\u001b[39m\"\u001b[39m\n\u001b[0;32m 600\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mtwo to six elements\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m reduce)\n\u001b[0;32m 602\u001b[0m \u001b[39m# Save the reduce() output and finally memoize the object\u001b[39;00m\n\u001b[1;32m--> 603\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49msave_reduce(obj\u001b[39m=\u001b[39;49mobj, \u001b[39m*\u001b[39;49mrv)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:717\u001b[0m, in \u001b[0;36m_Pickler.save_reduce\u001b[1;34m(self, func, args, state, listitems, dictitems, state_setter, obj)\u001b[0m\n\u001b[0;32m 715\u001b[0m \u001b[39mif\u001b[39;00m state \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 716\u001b[0m \u001b[39mif\u001b[39;00m state_setter \u001b[39mis\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 717\u001b[0m save(state)\n\u001b[0;32m 718\u001b[0m write(BUILD)\n\u001b[0;32m 719\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 720\u001b[0m \u001b[39m# If a state_setter is specified, call it instead of load_build\u001b[39;00m\n\u001b[0;32m 721\u001b[0m \u001b[39m# to update obj's with its previous state.\u001b[39;00m\n\u001b[0;32m 722\u001b[0m \u001b[39m# First, push state_setter and its tuple of expected arguments\u001b[39;00m\n\u001b[0;32m 723\u001b[0m \u001b[39m# (obj, state) onto the stack.\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:560\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 558\u001b[0m f \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mdispatch\u001b[39m.\u001b[39mget(t)\n\u001b[0;32m 559\u001b[0m \u001b[39mif\u001b[39;00m f \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 560\u001b[0m f(\u001b[39mself\u001b[39;49m, obj) \u001b[39m# Call unbound method with explicit self\u001b[39;00m\n\u001b[0;32m 561\u001b[0m \u001b[39mreturn\u001b[39;00m\n\u001b[0;32m 563\u001b[0m \u001b[39m# Check private dispatch table if any, or else\u001b[39;00m\n\u001b[0;32m 564\u001b[0m \u001b[39m# copyreg.dispatch_table\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:1186\u001b[0m, in \u001b[0;36msave_module_dict\u001b[1;34m(pickler, obj)\u001b[0m\n\u001b[0;32m 1183\u001b[0m \u001b[39mif\u001b[39;00m is_dill(pickler, child\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m) \u001b[39mand\u001b[39;00m pickler\u001b[39m.\u001b[39m_session:\n\u001b[0;32m 1184\u001b[0m \u001b[39m# we only care about session the first pass thru\u001b[39;00m\n\u001b[0;32m 1185\u001b[0m pickler\u001b[39m.\u001b[39m_first_pass \u001b[39m=\u001b[39m \u001b[39mFalse\u001b[39;00m\n\u001b[1;32m-> 1186\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave_dict(pickler, obj)\n\u001b[0;32m 1187\u001b[0m logger\u001b[39m.\u001b[39mtrace(pickler, \u001b[39m\"\u001b[39m\u001b[39m# D2\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m 1188\u001b[0m \u001b[39mreturn\u001b[39;00m\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:972\u001b[0m, in \u001b[0;36m_Pickler.save_dict\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m 969\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwrite(MARK \u001b[39m+\u001b[39m DICT)\n\u001b[0;32m 971\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mmemoize(obj)\n\u001b[1;32m--> 972\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_batch_setitems(obj\u001b[39m.\u001b[39;49mitems())\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:998\u001b[0m, in \u001b[0;36m_Pickler._batch_setitems\u001b[1;34m(self, items)\u001b[0m\n\u001b[0;32m 996\u001b[0m \u001b[39mfor\u001b[39;00m k, v \u001b[39min\u001b[39;00m tmp:\n\u001b[0;32m 997\u001b[0m save(k)\n\u001b[1;32m--> 998\u001b[0m save(v)\n\u001b[0;32m 999\u001b[0m write(SETITEMS)\n\u001b[0;32m 1000\u001b[0m \u001b[39melif\u001b[39;00m n:\n", "File \u001b[1;32md:\\miniconda3\\lib\\site-packages\\dill\\_dill.py:388\u001b[0m, in \u001b[0;36mPickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 386\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mCan\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt pickle \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m: attribute lookup builtins.generator failed\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m GeneratorType\n\u001b[0;32m 387\u001b[0m \u001b[39mraise\u001b[39;00m PicklingError(msg)\n\u001b[1;32m--> 388\u001b[0m StockPickler\u001b[39m.\u001b[39;49msave(\u001b[39mself\u001b[39;49m, obj, save_persistent_id)\n", "File \u001b[1;32md:\\miniconda3\\lib\\pickle.py:578\u001b[0m, in \u001b[0;36m_Pickler.save\u001b[1;34m(self, obj, save_persistent_id)\u001b[0m\n\u001b[0;32m 576\u001b[0m reduce \u001b[39m=\u001b[39m \u001b[39mgetattr\u001b[39m(obj, \u001b[39m\"\u001b[39m\u001b[39m__reduce_ex__\u001b[39m\u001b[39m\"\u001b[39m, \u001b[39mNone\u001b[39;00m)\n\u001b[0;32m 577\u001b[0m \u001b[39mif\u001b[39;00m reduce \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m--> 578\u001b[0m rv \u001b[39m=\u001b[39m reduce(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mproto)\n\u001b[0;32m 579\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 580\u001b[0m reduce \u001b[39m=\u001b[39m \u001b[39mgetattr\u001b[39m(obj, \u001b[39m\"\u001b[39m\u001b[39m__reduce__\u001b[39m\u001b[39m\"\u001b[39m, \u001b[39mNone\u001b[39;00m)\n", "\u001b[1;31mTypeError\u001b[0m: cannot pickle '_cffi_backend.__CDataOwnGC' object" ] } ], "source": [ "from microphones_instance import MicrophoneControllerInstance\n", "\n", "# experiment status: 1 - idle, 2 - running (recording, logging), 0 - stopped\n", "status = mp.Value('i', 1)\n", "\n", "microphoneController = MicrophoneControllerInstance(status, cfg[\"microphones\"])\n", "\n", "mc = mp.Process(target=microphoneController.run, args=())\n", "mc.start()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "status.value = 2\n", "\n", "print(\"Recording started\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "status.value = 0\n", "\n", "mc.join()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Test with threading (instance methods) -> Works!" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# experiment status: 1 - idle, 2 - running (recording, logging), 0 - stopped\n", "status = mp.Value('i', 1)\n", "\n", "mc = MicrophoneControllerInstance(status,cfg[\"microphones\"])\n", "\n", "mc.start()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Audio input stream started.\n" ] } ], "source": [ "status.value = 2" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "import time" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "status.value = 0" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Microphones recording stopped\n" ] } ], "source": [ "mc.stop()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.8" }, "vscode": { "interpreter": { "hash": "af8259ad5c1c9c7a69bd6ea085234cf8fd3a6a37a71ca551828b314c4d89b0ad" } } }, "nbformat": 4, "nbformat_minor": 2 }