def dur_from_vol(self, vol): """ Given a desired volume, compute an open duration. Must have calibration available in prefs, see :meth:`~.Terminal.calibrate_ports`. Args: vol (float, int): desired reward volume in uL Returns: int: computed opening duration for given volume """ # find our pin name if not self.name: self.name = self.get_name() # prefs should have loaded any calibration try: self.calibration = prefs.get('PORT_CALIBRATION')[self.name] except KeyError: # try using name prepended with PORTS_, which happens for hardware objects with implicit names self.calibration = prefs.get('PORT_CALIBRATION')[self.name.replace( 'PORTS_', '')] except Exception as e: self.logger.exception( f'couldnt get calibration, using default LUT y = 3.5 + 2. got error {e}' ) self.calibration = {'slope': 3.5, 'intercept': 2} # compute duration from slope and intercept duration = round( float(self.calibration['intercept']) + (float(self.calibration['slope']) * float(vol))) return duration
def _process(self, transform): self.transform = autopilot.transform.make_transform(transform) self.node = Net_Node(f"{prefs.get('NAME')}_TRANSFORMER", upstream=prefs.get('NAME'), port=prefs.get('MSGPORT'), listens={'CONTINUOUS': self.l_process}, instance=False) self.node.send(self.return_id, 'STATE', value='READY') while True: try: # value = self.input_q.get_nowait() value = self.input_q.popleft() # except Empty: except IndexError: sleep(0.001) continue result = self.transform.process(value) self.node.logger.debug(f'Processed frame, result: {result}') if self.operation == "trigger": if result != self._last_result: self.node.send(self.return_id, self.return_key, result) self._last_result = result elif self.operation == 'stream': # FIXME: Another key that's not TRIGGER self.node.send(self.return_id, self.return_key, result) elif self.operation == 'debug': pass
def update_protocols(self): """ If we change the protocol file, update the stored version in subject files """ # # get list of protocol files protocols = os.listdir(prefs.get('PROTOCOLDIR')) protocols = [p for p in protocols if p.endswith('.json')] updated_subjects = [] subjects = self.subject_list for subject in subjects: if subject not in self.subjects.keys(): self.subjects[subject] = Subject(subject) protocol_bool = [ self.subjects[subject].protocol_name == os.path.splitext(p)[0] for p in protocols ] if any(protocol_bool): which_prot = np.where(protocol_bool)[0][0] protocol = protocols[which_prot] self.subjects[subject].assign_protocol( os.path.join(prefs.get('PROTOCOLDIR'), protocol), step_n=self.subjects[subject].step) updated_subjects.append(subject) msgbox = QtWidgets.QMessageBox() msgbox.setText("Subject Protocols Updated for:") msgbox.setDetailedText("\n".join(sorted(updated_subjects))) msgbox.exec_()
def init_networking(self, listens=None, **kwargs): """ Spawn a :class:`.Net_Node` to :attr:`Hardware.node` for streaming or networked command Args: listens (dict): Dictionary mapping message keys to handling methods **kwargs: Passed to :class:`.Net_Node` Returns: """ if not listens: listens = self.listens self.node = Net_Node( self.name, upstream=prefs.get('NAME'), port=prefs.get('MSGPORT'), listens=listens, instance=False, **kwargs #upstream_ip=prefs.get('TERMINALIP'), #daemon=False )
def __init__(self, stage_block = None, **kwargs): super(Parallax, self).__init__() self.stage_block = stage_block self.init_hardware() self.node = Net_Node(id="{}_TASK".format(prefs.get('NAME')), upstream=prefs.get('NAME'), port=prefs.get('MSGPORT'), listens = {}, instance = False) self.subject = kwargs['subject'] # value = { # 'child': {'parent': prefs.get('NAME'), 'subject': self.subject}, # 'subject' : self.subject, # # } # value.update(self.CHILDREN['HEADCAM']) # # self.node.send(to=prefs.get('NAME'), key='CHILD', value=value) self.stages = itertools.cycle([self.test]) self.n_trials = itertools.count() # print(self.hardware) # self.hardware['CAMS']['EYE'].capture() self.hardware['CAMERAS']['SIDE'].stream(to="T") self.hardware['CAMERAS']['SIDE'].capture()
def start_jackd(): if not JACKD: raise ImportError('jackd was not found in autopilot.external or as a system install') # get specific launch string from prefs if prefs.get("JACKDSTRING"): jackd_string = prefs.get('JACKDSTRING').lstrip('jackd') else: jackd_string = "" # replace string fs with number if prefs.get('FS'): jackd_string = jackd_string.replace('-rfs', f"-r{prefs.get('FS')}") # construct rest of launch string! # if JACKD_MODULE: # jackd_path = os.path.join(autopilot_jack.__path__._path[0]) # # # now set as env variables... # # specify location of libraries when starting jackd # # lib_string = "LD_LIBRARY_PATH=" + os.path.join(jackd_path, 'lib') # # # # specify location of drivers when starting jackd # # os.environ['JACK_DRIVER_DIR'] = os.path.join(jackd_path, 'lib', 'jack') # # driver_string = "JACK_DRIVER_DIR=" + os.path.join(jackd_path, 'lib', 'jack') # # jackd_bin = os.path.join(jackd_path, 'bin', 'jackd') # # # combine all the pieces # # launch_jackd = " ".join([lib_string, driver_string, jackd_bin, jackd_string]) # # else: jackd_bin = shutil.which('jackd') #jackd_bin = 'jackd' # launch_jackd = " ".join([jackd_bin, jackd_string]) launch_jackd = " ".join([jackd_bin, jackd_string]) proc = subprocess.Popen(launch_jackd, shell=True) globals()['JACKD_PROCESS'] = proc # kill process when session ends def kill_proc(*args): proc.kill() sys.exit(1) atexit.register(kill_proc) signal.signal(signal.SIGTERM, kill_proc) # sleep to let it boot sleep(2) return proc
def request(self): # wait for the subject to hold the wheel still # Set the event lock self.stage_block.clear() # wait on any ongoing punishment stimulus self.punish_block.wait() # Reset all the variables that need to be #for v in self.resetting_variables: # v = None # reset triggers if there are any left self.triggers = {} # calculate orientation change # half the time, don't change, otherwise, do change if random() < 0.5: self.shift = 0 self.target = False else: self.shift = random() * 180.0 self.target = True # Set sound trigger and LEDs # We make two triggers to play the sound and change the light color change_to_blue = lambda: self.hardware['LEDS']['C'].set_color( [0, 0, 255]) # set triggers self.triggers['F'] = [ change_to_blue, lambda: self.stim.play('shift', self.shift) ] # set to green in the meantime self.set_leds({'C': [0, 255, 0]}) # tell our wheel to start measuring self.node.send(to=[prefs.get('NAME'), prefs.get('CHILDID'), 'wheel_0'], key="MEASURE", value={ 'mode': 'steady', 'thresh': 100 }) self.current_trial = next(self.trial_counter) data = { 'target': self.target, 'shift': self.shift, 'trial_num': self.current_trial } self.current_stage = 0 return data
def test_prefs_defaults(default_pref): # make sure that we didnt' actually load anything from some phantom uh prefs file idk if prefs._INITIALIZED.value: warnings.warn('prefs was initialized, so defaults could not be tested') return if 'default' in default_pref[1].keys(): with pytest.warns(UserWarning): assert prefs.get(default_pref[0]) == default_pref[1]['default'] else: assert prefs.get(default_pref[0]) is None
def blank_LEDs(self): """ If any 'LEDS' are defined in `prefs.get('HARDWARE')` , instantiate them, set their color to [0,0,0], and then release them. """ if 'LEDS' not in prefs.get('HARDWARE').keys(): return for position, pins in prefs.get('HARDWARE')['LEDS'].items(): led = gpio.LED_RGB(pins=pins) time.sleep(1.) led.set_color(col=[0, 0, 0]) led.release()
def __init__(self, *args, **kwargs): super(ImageItem_TimedUpdate, self).__init__(*args, **kwargs) if globals()['VIDEO_TIMER'] is None: globals()['VIDEO_TIMER'] = QtCore.QTimer() self.timer = globals()['VIDEO_TIMER'] self.timer.stop() self.timer.timeout.connect(self.update_img) if prefs.get('DRAWFPS'): self.fps = prefs.get('DRAWFPS') else: self.fps = 10. self.timer.start(1. / self.fps)
def new_protocol(self): """ Open a :class:`.gui.Protocol_Wizard` to create a new protocol. Prompts for name of protocol, then saves in `prefs.get('PROTOCOLDIR')` """ self.new_protocol_window = Protocol_Wizard() self.new_protocol_window.exec_() if self.new_protocol_window.result() == 1: steps = self.new_protocol_window.steps # The values useful to the step functions are stored with a 'value' key in the param_dict save_steps = [] for s in steps: param_values = {} for k, v in s.items(): if 'value' in v.keys(): param_values[k] = v['value'] elif k == 'stim': # TODO: Super hacky - don't do this. Refactor params already. param_values[k] = {} for stimtype, stim in v.items(): param_values[k][stimtype] = stim save_steps.append(param_values) # Name the protocol name, ok = QtWidgets.QInputDialog.getText(self, "Name Protocol", "Protocol Name:") if ok and name != '': protocol_file = os.path.join(prefs.get('PROTOCOLDIR'), name + '.json') with open(protocol_file, 'w') as pfile_open: json.dump(save_steps, pfile_open, indent=4, separators=(',', ': '), sort_keys=True) elif name == '' or not ok: placeholder_name = 'protocol_created_{}'.format( datetime.date.today().isoformat()) protocol_file = os.path.join(prefs.get('PROTOCOLDIR'), placeholder_name + '.json') with open(protocol_file, 'w') as pfile_open: json.dump(save_steps, pfile_open, indent=4, separators=(',', ': '), sort_keys=True)
def calibrate_port(self, port_name, n_clicks, open_dur, iti): """ Run port calibration routine Open a :class:`.hardware.gpio.Solenoid` repeatedly, measure volume of water dispersed, compute lookup table mapping valve open times to volume. Continuously sends progress of test with ``CAL_PROGRESS`` messages Args: port_name (str): Port name as specified in ``prefs`` n_clicks (int): number of times the valve should be opened open_dur (int, float): how long the valve should be opened for in ms iti (int, float): how long we should :func:`~time.sleep` between openings """ pin_num = prefs.get('HARDWARE')['PORTS'][port_name] port = gpio.Solenoid(pin_num, duration=int(open_dur)) msg = {'click_num': 0, 'pilot': self.name, 'port': port_name} iti = float(iti) / 1000.0 cal_name = "Cal_{}".format(self.name) for i in range(int(n_clicks)): port.open() msg['click_num'] = i + 1 self.node.send(to=cal_name, key='CAL_PROGRESS', value=msg) time.sleep(iti) port.release()
def l_cal_result(self, value): """ Save the results of a port calibration """ # files for storing raw and fit calibration results cal_fn = os.path.join(prefs.get('BASEDIR'), 'port_calibration.json') if os.path.exists(cal_fn): try: with open(cal_fn, 'r') as cal_file: calibration = json.load(cal_file) except ValueError: # usually no json can be decoded, that's fine calibrations aren't expensive calibration = {} else: calibration = {} for port, results in value.items(): if port in calibration.keys(): calibration[port].extend(results) else: calibration[port] = results with open(cal_fn, 'w+') as cal_file: json.dump(calibration, cal_file)
def pyo_server(debug=False): """ Returns a booted and started pyo audio server Warning: Use of pyo is generally discouraged due to dropout issues and the general opacity of the module. Args: debug (bool): If true, setVerbosity of pyo server to 8. """ # Jackd should already be running from the launch script created by setup_pilot, we we just pyo_server = pyo.Server(audio='jack', nchnls=int(prefs.get('NCHANNELS')), duplex=0, buffersize=4096, sr=192000, ichnls=0) # Deactivate MIDI because we don't use it and it's expensive pyo_server.deactivateMidi() # We have to set pyo to not automatically try to connect to inputs when there aren't any pyo_server.setJackAuto(False, True) # debug if debug: pyo_server.setVerbosity(8) # Then boot and start pyo_server.boot() pyo_server.start() return pyo_server
def l_update(self, value): # receive two points, convert to distance, angle, and then to color if any(value[:, 2] < 0.2): return angle = self.transforms['angle'].process(value) distance = self.transforms['distance'].process(value) color = self.transforms['color'].process((angle, 1, distance)) acquired = self.led_lock.acquire(blocking=False) if acquired: try: self.hardware['LEDS']['C'].set(r=color[0], g=color[1], b=color[2]) finally: self.led_lock.release() # self.stream.put({ # 'angle': angle, # 'distance': distance, # 'timestamp': datetime.now().isoformat(), # 'subject': self.subject # }) self.node.send( 'T', 'DATA', { 'angle': angle, 'distance': distance, 'timestamp': datetime.now().isoformat(), 'subject': self.subject, 'pilot': prefs.get('NAME'), 'continuous': True, 't': time() })
def load_pilotdb(file_name=None, reverse=False): """ Try to load the file_db Args: reverse (bool): Return inverted pilot db mapping subjects: pilots (default False) file_name (str): Path of ``pilot_db.json``, if None, use ``prefs.get('PILOT_DB')`` Returns: :class:`collections.OrderedDict` : pilot_db.json or reversed pilot_db """ if file_name is None: file_name = prefs.get('PILOT_DB') with open(file_name) as pilot_file: pilot_db = json.load(pilot_file, object_pairs_hook=odict) if reverse: # simplify pilot db pilot_db = odict({k: v['subjects'] for k, v in pilot_db.items()}) pilot_dict = odict() for pilot, subjectlist in pilot_db.items(): for ms in subjectlist: pilot_dict[ms] = pilot pilot_db = pilot_dict return pilot_db
def init_audio(self): """ Initialize an audio server depending on the value of `prefs.get('AUDIOSERVER')` * 'pyo' = :func:`.pyoserver.pyo_server` * 'jack' = :class:`.jackclient.JackClient` """ if prefs.get('AUDIOSERVER') == 'pyo': self.server = pyoserver.pyo_server() self.logger.info("pyo server started") elif prefs.get('AUDIOSERVER') in ('jack', True): self.jackd = external.start_jackd() self.server = jackclient.JackClient() self.server.start() self.logger.info('Started jack audio server')
def __init__(self, videos, fps=None): """ Display Video data as it is collected. Uses the :class:`ImageItem_TimedUpdate` class to do timed frame updates. Args: videos (list, tuple): Names of video streams that will be displayed fps (int): if None, draw according to ``prefs.get('DRAWFPS')``. Otherwise frequency of widget update Attributes: videos (list, tuple): Names of video streams that will be displayed fps (int): if None, draw according to ``prefs.get('DRAWFPS')``. Otherwise frequency of widget update ifps (int): 1/fps, duration of frame in s qs (dict): Dictionary of :class:`~queue.Queue`s in which frames will be dumped quitting (:class:`threading.Event`): Signal to quit drawing update_thread (:class:`threading.Thread`): Thread with target=:meth:`~.Video._update_frame` layout (:class:`PySide2.QtWidgets.QGridLayout`): Widget layout vid_widgets (dict): dict containing widgets for each of the individual video streams. """ super(Video, self).__init__() self.videos = videos if fps is None: if prefs.get('DRAWFPS'): self.fps = prefs.get('DRAWFPS') else: self.fps = 10 else: self.fps = fps self.ifps = 1.0 / self.fps self.layout = None self.vid_widgets = {} #self.q = Queue(maxsize=1) self.qs = {} self.quitting = Event() self.quitting.clear() self.init_gui() self.update_thread = Thread(target=self._update_frame) self.update_thread.setDaemon(True) self.update_thread.start()
def __init__(self, stim=None, reward=50, timeout=1000, stage_block=None, **kwargs): super(GoNoGo, self).__init__() self.stage_block = stage_block self.trial_counter = itertools.count() self.punish_dur = 500.0 self.reward = reward self.timeout = timeout self.init_hardware() self.set_reward(self.reward) self.node = Net_Node(id="T_{}".format(prefs.get('NAME')), upstream=prefs.get('NAME'), port=prefs.get('MSGPORT'), listens={}, instance=True) # get our child started self.subject = kwargs['subject'] value = { 'child': { 'parent': prefs.get('NAME'), 'subject': kwargs['subject'] }, 'task_type': 'Wheel Child', 'subject': kwargs['subject'] } self.node.send(to=prefs.get('NAME'), key='CHILD', value=value) # hardcoding stimulus for testing self.stim = Grating(angle=0, freq=(4, 0), rate=1, size=(1, 1), debug=True) self.stages = itertools.cycle( [self.request, self.discrim, self.reinforce])
def get_name(self): """ Usually Hardware is only instantiated with its pin number, but we can get its name from prefs """ # TODO: Unify identification of hardware types across prefs and hardware objects try: our_type = prefs.get('HARDWARE')[self.type] except KeyError: our_type = prefs.get('HARDWARE')[self.__class__.__name__] for name, pin in our_type.items(): if self.pin == pin: return name elif isinstance(pin, dict): if self.pin == pin['pin']: return name
def __init__(self, pilot, x_width=50, parent=None): """ Args: pilot (str): The name of our pilot x_width (int): How many trials in the past should we plot? """ #super(Plot, self).__init__(QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers), parent) super(Plot, self).__init__() self.logger = init_logger(self) self.parent = parent self.layout = None self.infobox = None self.n_trials = None self.session_trials = 0 self.info = {} self.plot = None self.xrange = None self.plot_params = {} self.data = { } # Keep a dict of the data we are keeping track of, will be instantiated on start self.plots = {} self.state = "IDLE" self.continuous = False self.last_time = 0 self.video = None self.videos = [] self.invoker = get_invoker() # The name of our pilot, used to listen for events self.pilot = pilot # Set initial x-value, will update when data starts coming in self.x_width = x_width self.last_trial = self.x_width # Inits the basic widget settings self.init_plots() ## Station # Start the listener, subscribes to terminal_networking that will broadcast data self.listens = { 'START': self.l_start, # Receiving a new task 'DATA': self.l_data, # Receiving a new datapoint 'CONTINUOUS': self.l_data, 'STOP': self.l_stop, 'PARAM': self.l_param, # changing some param 'STATE': self.l_state } self.node = Net_Node(id='P_{}'.format(self.pilot), upstream="T", port=prefs.get('MSGPORT'), listens=self.listens, instance=True)
def open_file(self): """ Setup a table to store data locally. Opens `prefs.get('DATADIR')/local.h5`, creates a group for the current subject, a new table for the current day. .. todo:: This needs to be unified with a general file constructor abstracted from :class:`.Subject` so it doesn't reimplement file creation!! Returns: (:class:`tables.File`, :class:`tables.Table`, :class:`tables.tableextension.Row`): The file, table, and row for the local data table """ local_file = os.path.join(prefs.get('DATADIR'), 'local.h5') try: h5f = tables.open_file(local_file, mode='a') except (IOError, tables.HDF5ExtError) as e: self.logger.warning("local file was broken, making new") self.logger.warning(e) os.remove(local_file) h5f = tables.open_file(local_file, mode='w') os.chmod(local_file, 0o777) try: h5f.create_group("/", self.subject, "Local Data for {}".format(self.subject)) except tables.NodeError: # already made it pass subject_group = h5f.get_node('/', self.subject) # Make a table for today's data, appending a conflict-avoidance int if one already exists datestring = datetime.date.today().isoformat() conflict_avoid = 0 while datestring in subject_group: conflict_avoid += 1 datestring = datetime.date.today().isoformat() + '-' + str( conflict_avoid) # Get data table descriptor if hasattr(self.task, 'TrialData'): table_descriptor = self.task.TrialData table = h5f.create_table( subject_group, datestring, table_descriptor, "Subject {} on {}".format(self.subject, datestring)) # The Row object is what we write data into as it comes in row = table.row return h5f, table, row else: return h5f, None, None
def boot_server(self): """ Called by :meth:`.JackClient.run` to boot the server upon starting the process. Activates the client and connects it to the number of outports determined by `prefs.get('NCHANNELS')` :class:`jack.Client` s can't be kept alive, so this must be called just before processing sample starts. """ self.client = jack.Client(self.name) self.blocksize = self.client.blocksize self.fs = self.client.samplerate self.zero_arr = np.zeros((self.blocksize, 1), dtype='float32') self.client.set_process_callback(self.process) self.client.outports.register('out_0') self.client.activate() target_ports = self.client.get_ports(is_physical=True, is_input=True, is_audio=True) if prefs.get('OUTCHANNELS'): if isinstance(prefs.get('OUTCHANNELS'), list): for outchan in prefs.get('OUTCHANNELS'): self.client.outports[0].connect(target_ports[int(outchan)]) elif isinstance(prefs.get('OUTCHANNELS'), int): self.client.outports[0].connect( target_ports[prefs.get('OUTCHANNELS')]) elif isinstance(prefs.get('OUTCHANNELS'), str): try: self.client.outports[0].connect(target_ports[int( prefs.get('OUTCHANNELS'))]) except TypeError: Exception( 'Could not coerce prefs.get(\'OUTCHANNELS\') to an integer or list of ints. Connecting to port 0. got {}' .format(prefs.get('OUTCHANNELS'))) self.client.outports[0].connect(target_ports[0]) else: self.client.outports[0].connect(target_ports[0]) if prefs.get('NCHANNELS') == 2: # TODO: Limited, obvs. want to handle arbitrary output arrangements. self.client.outports[0].connect(target_ports[1])
def calibration_curve(self, path=None, calibration=None): """ # compute curve to compute duration from desired volume Args: calibration: path: If present, use calibration file specified, otherwise use default. """ lut_fn = os.path.join(prefs.get('BASEDIR'), 'port_calibration_fit.json') if not calibration: # if we weren't given calibration results, load them if path: open_fn = path else: open_fn = os.path.join(prefs.get('BASEDIR'), "port_calibration.json") with open(open_fn, 'r') as open_f: calibration = json.load(open_f) luts = {} for port, samples in calibration.items(): sample_df = pd.DataFrame(samples) # TODO: Filter for only most recent timestamps # volumes are saved in mL because of how they are measured, durations are stored in ms # but reward volumes are typically in the uL range, so we make the conversion # by multiplying by 1000 line_fit = linregress( (sample_df['vol'] / sample_df['n_clicks']) * 1000., sample_df['dur']) luts[port] = { 'intercept': line_fit.intercept, 'slope': line_fit.slope } # write to file, overwriting any previous with open(lut_fn, 'w') as lutf: json.dump(luts, lutf)
def protocols(self): """ Returns: list: list of protocol files in ``prefs.get('PROTOCOLDIR')`` """ # get list of protocol files protocols = os.listdir(prefs.get('PROTOCOLDIR')) protocols = [ os.path.splitext(p)[0] for p in protocols if p.endswith('.json') ] return protocols
def _stream(self): self.node = Net_Node("T_CHILD", upstream=prefs.get('NAME'), port=prefs.get('MSGPORT'), listens={}, instance=True) while True: for name, cam in self.cams.items(): try: frame, timestamp = cam.q.get_nowait() self.node.send(key='CONTINUOUS', value={ cam.name: frame, 'timestamp': timestamp }, repeat=False, flags={'MINPRINT': True}) except Empty: pass
def dlc_dir(self) -> str: """ ``{prefs.get('BASE_DIR')}/dlc`` Returns: str """ if 'DLCDIR' in prefs._PREFS.keys(): dlc_dir = prefs.get('DLCDIR') else: dlc_dir = os.path.join(prefs.get('BASEDIR'), 'dlc') if not os.path.exists(dlc_dir): try: os.mkdir(dlc_dir) except OSError as e: raise OSError( f'No DLC dir found and one could not be created!\n{e}') prefs.set('DLC_DIR', dlc_dir) return dlc_dir
def __init__(self, stage_block=None, fs=10, thresh=100, **kwargs): self.fs = fs self.thresh = thresh self.hardware = {} self.hardware['OUTPUT'] = Digital_Out(prefs.get('HARDWARE')['OUTPUT']) self.hardware['WHEEL'] = Wheel(digi_out=self.hardware['OUTPUT'], fs=self.fs, thresh=self.thresh, mode="steady") self.stages = cycle([self.noop]) self.stage_block = stage_block
def start_pigpiod(): if not PIGPIO: raise ImportError('the pigpiod daemon was not found! use autopilot.setup.') with globals()['PIGPIO_LOCK']: if globals()['PIGPIO_DAEMON'] is not None: return globals()['PIGPIO_DAEMON'] launch_pigpiod = shutil.which('pigpiod') if launch_pigpiod is None: raise RuntimeError('the pigpiod binary was not found!') if prefs.get( 'PIGPIOARGS'): launch_pigpiod += ' ' + prefs.get('PIGPIOARGS') if prefs.get( 'PIGPIOMASK'): # if it's been converted to an integer, convert back to a string and zfill any leading zeros that were lost if isinstance(prefs.get('PIGPIOMASK'), int): prefs.set('PIGPIOMASK', str(prefs.get('PIGPIOMASK')).zfill(28)) launch_pigpiod += ' -x ' + prefs.get('PIGPIOMASK') proc = subprocess.Popen('sudo ' + launch_pigpiod, shell=True) globals()['PIGPIO_DAEMON'] = proc # kill process when session ends def kill_proc(*args): proc.kill() sys.exit(1) atexit.register(kill_proc) signal.signal(signal.SIGTERM, kill_proc) # sleep to let it boot up sleep(1) return proc
def __init__(self, path, amplitude=0.01, **kwargs): """ Args: path (str): Path to a .wav file relative to the `prefs.get('SOUNDDIR')` amplitude (float): amplitude of the sound as a proportion of 1. **kwargs: extraneous parameters that might come along with instantiating us """ super(File, self).__init__() if os.path.exists(path): self.path = path elif os.path.exists(os.path.join(prefs.get('SOUNDDIR'), path)): self.path = os.path.join(prefs.get('SOUNDDIR'), path) else: Exception( 'Could not find {} in current directory or sound directory'. format(path)) self.amplitude = float(amplitude) # because files can be v memory intensive, we only load the sound once we're called to buffer them # store our initialization status self.initialized = False