def sendMidi(name, code, val): global previous if name == 'pitchwheel': # the value should be limited between -8192 to 8191 val = EEGsynth.limit(val, -8192, 8191) val = int(val) else: # the value should be limited between 0 and 127 val = EEGsynth.limit(val, 0, 127) val = int(val) if name == 'note': # note_on and note_off messages are dealt with in another function SetNoteOn(val, velocity_note) return elif name.startswith('note'): # note_on and note_off messages are dealt with in another function SetNoteOn(code, val) return monitor.info(str(name) + " " + str(code) + " " + str(val)) if name.startswith('control'): if midichannel is None: msg = mido.Message('control_change', control=code, value=val) else: msg = mido.Message('control_change', control=code, value=val, channel=midichannel) elif name.startswith('polytouch'): if midichannel is None: msg = mido.Message('polytouch', note=code, value=val) else: msg = mido.Message('polytouch', note=code, value=val, channel=midichannel) elif name == 'aftertouch': if midichannel is None: msg = mido.Message('aftertouch', value=val) else: msg = mido.Message('aftertouch', value=val, channel=midichannel) elif name == 'pitchwheel': if midichannel is None: msg = mido.Message('pitchwheel', pitch=val) else: msg = mido.Message('pitchwheel', pitch=val, channel=midichannel) # the following MIDI messages are not channel specific elif name == 'start': msg = mido.Message('start') elif name == 'continue': msg = mido.Message('continue') elif name == 'stop': msg = mido.Message('stop') elif name == 'reset': msg = mido.Message('reset') # send the MIDI message outputport.send(msg)
def run(self): pubsub = r.pubsub() # this message unblocks the Redis listen command pubsub.subscribe('OUTPUTGPIO_UNBLOCK') # this message triggers the event pubsub.subscribe(self.redischannel) while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel'] == self.redischannel: # the scale and offset options are channel specific and can be changed on the fly scale = patch.getfloat('scale', self.gpio, default=100) offset = patch.getfloat('offset', self.gpio, default=0) # switch to the PWM value specified in the event val = float(item['data']) val = EEGsynth.rescale(val, slope=scale, offset=offset) val = int(val) SetGPIO(self.gpio, val) if self.duration != None: # schedule a timer to switch it off after the specified duration duration = patch.getfloat('duration', self.gpio) duration = EEGsynth.rescale(duration, slope=scale_duration, offset=offset_duration) # some minimal time is needed for the delay duration = EEGsynth.limit(duration, 0.05, float('Inf')) t = threading.Timer(duration, SetGPIO, args=[self.gpio, 0]) t.start()
def _loop_once(): '''Run the main loop once This uses the global variables from setup and start, and adds a set of global variables ''' global parser, args, config, r, response, patch global monitor, control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val global cmd, val, msg for name, cmd in zip(control_name, control_code): # loop over the control values val = patch.getfloat('control', name) if val == None: continue # it should be skipped when not present if val == previous_val[name]: continue # it should be skipped when identical to the previous value previous_val[name] = val # map the Redis values to MIDI values val = EEGsynth.rescale(val, slope=scale, offset=offset) val = EEGsynth.limit(val, 0, 127) val = int(val) msg = mido.Message('control_change', control=cmd, value=val, channel=midichannel) monitor.debug(cmd, val, name) with lock: outputport.send(msg) # there should not be any local variables in this function, they should all be global if len(locals()): print('LOCALS: ' + ', '.join(locals().keys()))
def _loop_once(): '''Run the main loop once This uses the global variables from setup and start, and adds a set of global variables ''' global parser, args, config, r, response, patch for gpio, channel in config.items('control'): val = patch.getfloat('control', gpio) if val == None: continue # it should be skipped when not present if val == previous_val[gpio]: continue # it should be skipped when identical to the previous value previous_val[gpio] = val # the scale and offset options are channel specific and can be changed on the fly scale = patch.getfloat('scale', gpio, default=100) offset = patch.getfloat('offset', gpio, default=0) val = EEGsynth.rescale(val, slope=scale, offset=offset) val = EEGsynth.limit(val, 0, 100) val = int(val) with lock: wiringpi.softPwmWrite(pin[gpio], val) # there should not be any local variables in this function, they should all be global if len(locals()): print('LOCALS: ' + ', '.join(locals().keys()))
def run(self): global monitor, scale, offset pubsub = r.pubsub() pubsub.subscribe('VOLCAKEYS_UNBLOCK' ) # this message unblocks the redis listen command pubsub.subscribe(self.redischannel) # this message contains the note while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel'] == self.redischannel: monitor.trace(item) # map the Redis values to MIDI values val = EEGsynth.rescale(float(item['data']), slope=scale, offset=offset) val = EEGsynth.limit(val, 0, 127) val = int(val) monitor.update(item['channel'], val) msg = mido.Message('note_on', note=self.note, velocity=val, channel=midichannel) with lock: outputport.send(msg)
def paintEvent(self, e): qp = QtGui.QPainter() qp.begin(self) green = QtGui.QColor(10, 255, 10) red = QtGui.QColor(255, 10, 10) w = qp.window().width() h = qp.window().height() # determine the width (x) and height (y) of each bar barx = int(w / len(input_name)) bary = h # subtract some padding from each side padx = int(barx / 10) pady = int(h / 20) barx -= 2 * padx bary -= 2 * pady # this is the position for the first bar x = padx for name in input_name: scale = patch.getfloat('scale', name, default=1) offset = patch.getfloat('offset', name, default=0) val = patch.getfloat('input', name, default=np.nan) val = EEGsynth.rescale(val, slope=scale, offset=offset) monitor.update(name, val) threshold = patch.getfloat('threshold', name, default=1) threshold = EEGsynth.rescale(threshold, slope=scale, offset=offset) if val >= 0 and val <= threshold: qp.setBrush(green) qp.setPen(green) else: qp.setBrush(red) qp.setPen(red) if not np.isnan(val): val = EEGsynth.limit(val, 0, 1) r = QtCore.QRect(x, pady + (1 - val) * bary, barx, val * bary) qp.drawRect(r) r = QtCore.QRect(x, pady, barx, bary) qp.setPen(QtGui.QColor('white')) qp.drawText(r, QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, name) # update the position for the next bar x += 2 * padx + barx # add horizontal lines every 10% for i in range(1, 10): qp.setPen(QtGui.QColor('black')) y = h - pady - float(i) / 10 * bary qp.drawLine(0, y, w, y) qp.end() self.show()
def run(self): pubsub = r.pubsub() pubsub.subscribe('VOLCABASS_UNBLOCK' ) # this message unblocks the redis listen command pubsub.subscribe(self.redischannel) # this message contains the note while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel'] == self.redischannel: # map the Redis values to MIDI values val = EEGsynth.rescale(item['data'], slope=scale, offset=offset) val = EEGsynth.limit(val, 0, 127) val = int(val) if debug > 1: print item['channel'], "=", val msg = mido.Message('note_on', note=self.note, velocity=val, channel=midichannel) lock.acquire() outputport.send(msg) lock.release()
def run(self): pubsub = r.pubsub() # this message unblocks the Redis listen command pubsub.subscribe('OUTPUTCVGATE_UNBLOCK') # this message triggers the event pubsub.subscribe(self.redischannel) while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel'] == self.redischannel: # switch to the value specified in the event, it can be 0 or 1 val = float(item['data']) > 0 SetGate(self.gate, val) if self.duration != None: # schedule a timer to switch it off after the specified duration duration = patch.getfloat('duration', self.duration) duration = EEGsynth.rescale(duration, slope=duration_scale, offset=duration_offset) # some minimal time is needed for the delay duration = EEGsynth.limit(duration, 0.05, float('Inf')) t = threading.Timer(duration, SetGate, args=[self.gate, False]) t.start()
def run(self): global modulation, frequencies, control, channame, scale_amplitude, offset_amplitude, scale_frequency, offset_frequency pubsub = r.pubsub() # this message unblocks the Redis listen command pubsub.subscribe('MODULATETONE_UNBLOCK') # this message triggers the event pubsub.subscribe(self.redischannel) while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel'] == self.redischannel: chanval = float(item['data']) with lock: if modulation == 'am': chanval = EEGsynth.rescale(chanval, slope=scale_amplitude, offset=offset_amplitude) chanval = EEGsynth.limit(chanval, lo=0, hi=1) elif modulation == 'fm': chanval = EEGsynth.rescale(chanval, slope=scale_frequency, offset=offset_frequency) control[self.tone, self.audiochannel] = chanval monitor.update( channame[self.audiochannel] + " tone" + str(self.tone + 1), chanval)
def UpdateDuration(): global duration_note duration_note = patch.getfloat('duration', 'note', default=None) if duration_note != None: duration_note = EEGsynth.rescale(duration_note, slope=scale_duration, offset=offset_duration) # some minimal time is needed for the duration duration_note = EEGsynth.limit(duration_note, 0.05, float('Inf'))
def UpdateParameters(): global patch, velocity_note, scale_velocity, offset_velocity, duration_note, scale_duration, offset_duration velocity_note = patch.getfloat('velocity', 'note', default=64) velocity_note = int(EEGsynth.rescale(velocity_note, slope=scale_velocity, offset=offset_velocity)) duration_note = patch.getfloat('duration', 'note', default=None) if duration_note != None: duration_note = EEGsynth.rescale(duration_note, slope=scale_duration, offset=offset_duration) # some minimal time is needed for the duration duration_note = EEGsynth.limit(duration_note, 0.05, float('Inf'))
def run(self): pubsub = r.pubsub() # this message unblocks the Redis listen command pubsub.subscribe('OUTPUTCVGATE_UNBLOCK') # this message triggers the event pubsub.subscribe(self.redischannel) while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel'] == self.redischannel: chanval = float(item['data']) if self.chanstr.startswith('cv'): # the value should be between 0 and 4095 scale = patch.getfloat('scale', self.chanstr, default=4095) offset = patch.getfloat('offset', self.chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) chanval = EEGsynth.limit(chanval, lo=0, hi=4095) chanval = int(chanval) SetControl(self.chanindx, chanval) monitor.update(self.chanstr, chanval) elif self.chanstr.startswith('gate'): # the value should be 0 or 1 scale = patch.getfloat('scale', self.chanstr, default=1) offset = patch.getfloat('offset', self.chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) chanval = int(chanval > 0) SetGate(self.chanindx, chanval) monitor.update(self.chanstr, chanval) # schedule a timer to switch the gate off after the specified duration duration = patch.getfloat('duration', self.chanstr, default=None) if duration != None: duration = EEGsynth.rescale(duration, slope=duration_scale, offset=duration_offset) # some minimal time is needed for the delay duration = EEGsynth.limit(duration, 0.05, float('Inf')) t = threading.Timer(duration, SetGate, args=[self.chanindx, False]) t.start()
def _loop_once(): '''Run the main loop once This uses the global variables from setup and start, and adds a set of global variables ''' global parser, args, config, r, response, patch global monitor, duration_scale, duration_offset, serialdevice, s, lock, trigger, chanindx, chanstr, redischannel, thread monitor.loop() time.sleep(patch.getfloat('general', 'delay')) # loop over the control voltages for chanindx in range(1, 5): chanstr = "cv%d" % chanindx # this returns None when the channel is not present chanval = patch.getfloat('input', chanstr) if chanval == None: # the value is not present in Redis, skip it monitor.trace(chanstr, 'not available') continue # the scale and offset options are channel specific scale = patch.getfloat('scale', chanstr, default=4095) offset = patch.getfloat('offset', chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) # ensure that it is within limits chanval = EEGsynth.limit(chanval, lo=0, hi=4095) chanval = int(chanval) SetControl(chanindx, chanval) monitor.update(chanstr, chanval) # loop over the gates for chanindx in range(1, 5): chanstr = "gate%d" % chanindx chanval = patch.getfloat('input', chanstr) if chanval == None: # the value is not present in Redis, skip it monitor.trace(chanstr, 'not available') continue # the scale and offset options are channel specific scale = patch.getfloat('scale', chanstr, default=4095) offset = patch.getfloat('offset', chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) # the value for the gate should be 0 or 1 chanval = int(chanval > 0) SetGate(chanindx, chanval) monitor.update(chanstr, chanval)
def _loop_once(): '''Run the main loop once This uses the global variables from setup and start, and adds a set of global variables ''' global parser, args, config, r, response, patch global monitor, debug, address, artnet, dmxsize, dmxframe, prevtime global update, chanindx, chanstr, chanval, scale, offset update = False # loop over the control values, these are 1-offset in the ini file for chanindx in range(0, dmxsize): chanstr = "channel%03d" % (chanindx + 1) # this returns None when the channel is not present chanval = patch.getfloat('input', chanstr) if chanval == None: # the value is not present in Redis, skip it continue # the scale and offset options are channel specific scale = patch.getfloat('scale', chanstr, default=255) offset = patch.getfloat('offset', chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) # ensure that it is within limits chanval = EEGsynth.limit(chanval, lo=0, hi=255) chanval = int(chanval) # only update if the value has changed if dmxframe[chanindx] != chanval: monitor.info("DMX channel%03d = %g" % (chanindx, chanval)) dmxframe[chanindx] = chanval update = True if update: artnet.broadcastDMX(dmxframe, address) prevtime = time.time() elif (time.time() - prevtime) > 0.5: # send a maintenance frame every 0.5 seconds artnet.broadcastDMX(dmxframe, address) prevtime = time.time() # there should not be any local variables in this function, they should all be global if len(locals()): print('LOCALS: ' + ', '.join(locals().keys()))
def run(self): pubsub = r.pubsub() pubsub.subscribe('OUTPUTMIDI_UNBLOCK') # this message unblocks the redis listen command pubsub.subscribe(self.redischannel) # this message contains the note while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel']==self.redischannel: if debug>1: print(item['channel'], '=', item['data']) # map the Redis values to MIDI values val = item['data'] val = EEGsynth.rescale(val, slope=input_scale, offset=input_offset) val = EEGsynth.limit(val, 0, 127) val = int(val) sendMidi(self.name, self.code, val)
def _loop_once(): '''Run the main loop once This uses the global variables from setup and start, and adds a set of global variables ''' global parser, args, config, r, response, patch global monitor, debug, address, artnet, dmxdata, prevtime global chanindx, chanstr, chanval, scale, offset monitor.loop() time.sleep(patch.getfloat('general', 'delay')) # loop over the control values, these are 1-offset in the ini file for chanindx in range(1, 512): chanstr = "channel%03d" % chanindx # this returns None when the channel is not present chanval = patch.getfloat('input', chanstr) if chanval==None: # the value is not present in Redis, skip it monitor.trace(chanstr, 'not available') continue # the scale and offset options are channel specific scale = patch.getfloat('scale', chanstr, default=255) offset = patch.getfloat('offset', chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) # ensure that it is within limits chanval = EEGsynth.limit(chanval, lo=0, hi=255) chanval = int(chanval) if dmxdata[chanindx-1]!=chanval: # update the DMX value for this channel dmxdata[chanindx-1] = chanval monitor.debug("DMX channel%03d" % chanindx, '=', chanval) artnet.broadcastDMX(dmxdata,address) elif (time.time()-prevtime)>1: # send a maintenance packet now and then artnet.broadcastDMX(dmxdata,address) prevtime = time.time() # there should not be any local variables in this function, they should all be global if len(locals()): print('LOCALS: ' + ', '.join(locals().keys()))
def _loop_once(): '''Run the main loop once This uses the global variables from setup and start, and adds a set of global variables ''' global parser, args, config, r, response, patch global monitor, stepsize, clock, prefix, scale_active, scale_transpose, scale_note, scale_duration, offset_active, offset_transpose, offset_note, offset_duration, lock, key, sequencethread global active, sequence, transpose, duration, elapsed, naptime # the active sequence is specified as an integer between 0 and 127 active = patch.getfloat('sequence', 'active', default=0) active = EEGsynth.rescale(active, slope=scale_active, offset=offset_active) active = int(active) # get the corresponding sequence as a list sequence = "sequence%03d" % (active) sequence = patch.getstring('sequence', sequence, multiple=True) transpose = patch.getfloat('sequence', 'transpose', default=0.) transpose = EEGsynth.rescale(transpose, slope=scale_transpose, offset=offset_transpose) # the duration is relative to the time between clock ticks duration = patch.getfloat('sequence', 'duration', default=0.) duration = EEGsynth.rescale(duration, slope=scale_duration, offset=offset_duration) if duration > 0: # a duration of 0 or less means that the note will not switch off duration = EEGsynth.limit(duration, 0.1, 0.9) # show the parameters whose value has changed monitor.update("active", active) monitor.update("sequence", sequence) monitor.update("transpose", transpose) monitor.update("duration", duration) sequencethread.setSequence(sequence) sequencethread.setTranspose(transpose) sequencethread.setDuration(duration) # there should not be any local variables in this function, they should all be global if len(locals()): print('LOCALS: ' + ', '.join(locals().keys()))
def run(self): pubsub = r.pubsub() pubsub.subscribe('VOLCAKEYS_UNBLOCK') # this message unblocks the redis listen command pubsub.subscribe(self.redischannel) # this message contains the note while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel']==self.redischannel: # map the Redis values to MIDI values val = EEGsynth.rescale(item['data'], slope=scale, offset=offset) val = EEGsynth.limit(val, 0, 127) val = int(val) if debug>1: print item['channel'], "=", val msg = mido.Message('note_on', note=self.note, velocity=val, channel=midichannel) lock.acquire() outputport.send(msg) lock.release()
f.setnchannels(nchans) f.setnframes(0) f.setsampwidth(4) # 1, 2 or 4 f.setframerate(1. / delay) else: raise NotImplementedError('unsupported file format') if recording: D = [] for chan in channels: xval = r.get(chan) try: xval = float(xval) except ValueError: xval = 0. xval = EEGsynth.limit(xval, physical_min, physical_max) D.append([xval]) sample += 1 if (sample % synchronize) == 0: patch.setvalue("recordcontrol.synchronize", sample) if debug > 1: print "Writing", D elif debug > 0: print "Writing sample", sample, "as", np.shape(D) if fileformat == 'edf': f.writeBlock(D) elif fileformat == 'wav': D = np.asarray(D)
while True: monitor.loop() time.sleep(patch.getfloat('general', 'delay')) for gpio, channel in config.items('control'): val = patch.getfloat('control', gpio) if val == None: continue # it should be skipped when not present if val == previous_val[gpio]: continue # it should be skipped when identical to the previous value previous_val[gpio] = val # the scale and offset options are channel specific and can be changed on the fly scale = patch.getfloat('scale', gpio, default=100) offset = patch.getfloat('offset', gpio, default=0) val = EEGsynth.rescale(val, slope=scale, offset=offset) val = EEGsynth.limit(val, 0, 100) val = int(val) lock.acquire() wiringpi.softPwmWrite(pin[gpio], val) lock.release() except KeyboardInterrupt: monitor.success('Closing threads') for thread in trigger: thread.stop() r.publish('OUTPUTGPIO_UNBLOCK', 1) for thread in trigger: thread.join() sys.exit()
print name, 'not available' continue # the scale and offset options are channel specific scale = patch.getfloat('scale', name, default=1) offset = patch.getfloat('offset', name, default=0) # map the Redis values to MIDI pitch values val = EEGsynth.rescale(val, slope=scale, offset=offset) # portamento range is hardcoded 0-127, so no need for user-config port_val = EEGsynth.rescale(port_val, slope=127, offset=0) # ensure that values are within limits if patch.getstring('general', 'mode') == 'note': val = EEGsynth.limit(val, lo=0, hi=127) val = int(val) port_val = EEGsynth.limit(port_val, lo=0, hi=127) port_val = int(port_val) elif patch.getstring('general', 'mode') == 'pitchbend': val = EEGsynth.limit(val, lo=-8192, hi=8191) val = int(val) else: print 'No output mode (note or pitchbend) specified!' break if val != previous_val[name] or not val: # it should be skipped when identical to the previous value previous_val[name] = val if debug > 0:
patch.getfloat('recording', 'physical_max') ] chan_info['digital_min'] = nchans * [-32768] chan_info['digital_max'] = nchans * [32768] chan_info['ch_names'] = channelz chan_info['n_samps'] = nchans * [1] # write the header to file if debug > 0: print "Opening", fname f = EDF.EDFWriter(fname) f.writeHeader((meas_info, chan_info)) if recording: D = [] for chan in channels: xval = EEGsynth.limit(r.get(chan), patch.getfloat('recording', 'physical_min'), patch.getfloat('recording', 'physical_max')) D.append([xval]) if debug > 1: print "Writing", D f.writeBlock(D) time.sleep(adjust * delay) elapsed = time.time() - now # adjust the relative delay for the next iteration # the adjustment factor should only change a little per iteration adjust = 0.1 * delay / elapsed + 0.9 * adjust
print name, 'not available' continue # the scale and offset options are channel specific scale = patch.getfloat('scale', name, default=1) offset = patch.getfloat('offset', name, default=0) # map the Redis values to MIDI pitch values val = EEGsynth.rescale(val, slope=scale, offset=offset) # portamento range is hardcoded 0-127, so no need for user-config port_val = EEGsynth.rescale(port_val, slope=127, offset=0) # ensure that values are within limits if patch.getstring('general', 'mode') == 'note': val = EEGsynth.limit(val, lo=0, hi=127) val = int(val) port_val = EEGsynth.limit(port_val, lo=0, hi=127) port_val = int(port_val) elif patch.getstring('general', 'mode') == 'pitchbend': val = EEGsynth.limit(val, lo=-8192, hi=8191) val = int(val) else: print 'No output mode (note or pitchbend) specified!' break if val != previous_val[ name] or not val: # it should be skipped when identical to the previous value previous_val[name] = val
midithread.setEnabled(False) previous_midi_play = False # do something whenever the value changes if midi_start and not previous_midi_start: if midiport != None: midiport.send(mido.Message('start')) previous_midi_start = True elif not midi_start and previous_midi_start: if midiport != None: midiport.send(mido.Message('stop')) previous_midi_start = False rate = patch.getfloat('input', 'rate', default=0) rate = EEGsynth.rescale(rate, slope=scale_rate, offset=offset_rate) rate = EEGsynth.limit(rate, 30., 240.) shift = patch.getfloat('input', 'shift', default=0) shift = EEGsynth.rescale(shift, slope=scale_shift, offset=offset_shift) shift = int(shift) ppqn = patch.getfloat('input', 'ppqn', default=0) ppqn = EEGsynth.rescale(ppqn, slope=scale_ppqn, offset=offset_ppqn) ppqn = find_nearest_value([1, 2, 3, 4, 6, 8, 12, 24], ppqn) if debug>0: # show the parameters whose value has changed show_change("redis_play", redis_play) show_change("midi_play", midi_play) show_change("midi_start", midi_start) show_change("rate", rate)
def _loop_once(): """Run the main loop once This uses the global variables from setup and start, and adds a set of global variables """ global parser, args, config, r, response, patch global monitor, debug, mididevice, outputport, lock, trigger, port, channel, previous_val, previous_port_val global name, val, port_val, scale, offset, midichannel, msg # loop over the control values for channel in range(0, 16): # channels are one-offset in the ini file, zero-offset in the code name = 'channel{}'.format(channel + 1) val = patch.getfloat('control', name) port_val = patch.getfloat('portamento', name, default=0) if val is None: # the value is not present in Redis, skip it monitor.trace(name + ' is not available') continue if port_val is None: # the value is not present in Redis, skip it monitor.trace(name + ' is not available') continue # the scale and offset options are channel specific scale = patch.getfloat('scale', name, default=1) offset = patch.getfloat('offset', name, default=0) # map the Redis values to MIDI pitch values val = EEGsynth.rescale(val, slope=scale, offset=offset) # portamento range is hardcoded 0-127, so no need for user-config port_val = EEGsynth.rescale(port_val, slope=127, offset=0) # ensure that values are within limits if patch.getstring('general', 'mode') == 'note': val = EEGsynth.limit(val, lo=0, hi=127) val = int(val) port_val = EEGsynth.limit(port_val, lo=0, hi=127) port_val = int(port_val) elif patch.getstring('general', 'mode') == 'pitchbend': val = EEGsynth.limit(val, lo=-8192, hi=8191) val = int(val) else: monitor.info('No output mode (note or pitchbend) specified!') break if val != previous_val[name] or not val: # it should be skipped when identical to the previous value previous_val[name] = val # midi channels in the inifile are 1-16, in the code 0-15 midichannel = channel if patch.getstring('general', 'mode') == 'pitchbend': msg = mido.Message('pitchwheel', pitch=val, channel=midichannel) elif patch.getstring('general', 'mode') == 'note': msg = mido.Message('note_on', note=val, velocity=127, time=0, channel=midichannel) monitor.debug(msg) with lock: outputport.send(msg) # keep it at the present value for a minimal amount of time time.sleep(patch.getfloat('general', 'pulselength')) # it should be skipped when identical to the previous value if port_val != previous_port_val[name] and patch.getstring('general', 'mode') != 'note' or not port_val: previous_port_val[name] = port_val # CC#5 sets portamento msg = mido.Message('control_change', control=5, value=int(port_val), channel=midichannel) monitor.debug(msg) with lock: outputport.send(msg) # keep it at the present value for a minimal amount of time time.sleep(patch.getfloat('general', 'pulselength')) # there should not be any local variables in this function, they should all be global if len(locals()): print("LOCALS: " + ", ".join(locals().keys()))
for thread in trigger: thread.start() try: while True: time.sleep(patch.getfloat('general', 'delay')) for gpio, channel in config.items('control'): val = patch.getfloat('control', gpio) if val == None: continue # it should be skipped when not present if val == previous_val[gpio]: continue # it should be skipped when identical to the previous value previous_val[gpio] = val val = EEGsynth.rescale(val, slope=input_scale, offset=input_offset) val = EEGsynth.limit(val, 0, 100) val = int(val) lock.acquire() wiringpi.softPwmWrite(pin[gpio], val) lock.release() except KeyboardInterrupt: print("Closing threads") for thread in trigger: thread.stop() r.publish('OUTPUTGPIO_UNBLOCK', 1) for thread in trigger: thread.join() sys.exit()
serialthread.disable() previous_use_serial = False if use_redis and not previous_use_redis: redisthread.enable() previous_use_redis = True elif not use_redis and previous_use_redis: redisthread.disable() previous_use_redis = False rate = patch.getfloat('input', 'rate', default=60./127) scale_rate = patch.getfloat('scale', 'rate', default=127) offset_rate = patch.getfloat('offset', 'rate', default=0) rate = EEGsynth.rescale(rate, slope=scale_rate, offset=offset_rate) # ensure that the rate is within meaningful limits rate = EEGsynth.limit(rate, 40., 240.) multiply = patch.getfloat('input', 'multiply', default=0.5) scale_multiply = patch.getfloat('scale', 'multiply', default=4) offset_multiply = patch.getfloat('offset', 'multiply', default=-2.015748031495) multiply = EEGsynth.rescale(multiply, slope=scale_multiply, offset=offset_multiply) # map the multiply value exponentially, i.e. -1 becomes 1/2, 0 becomes 1, 1 becomes 2 multiply = math.pow(2, multiply) # it should be 1, 2, 3, 4 or 1/2, 1/3, 1/4, etc if multiply>1: multiply = round(multiply) elif multiply<1: multiply = 1.0/round(1.0/multiply); # this is for the analog trigger on the CV/Gate, expressed in seconds duration = patch.getfloat('general', 'duration')
def _loop_once(): """Run the main loop once This uses the global variables from setup and start, and adds a set of global variables """ global parser, args, config, r, response, patch global monitor, stepsize, scale_rate, offset_rate, scale_shift, offset_shift, scale_ppqn, offset_ppqn, lock, clock, i, clockthread, midithread, redisthread, midiport, previous_midi_play, previous_midi_start, previous_redis_play global start, redis_play, midi_play, midi_start, rate, shift, ppqn, elapsed, naptime redis_play = patch.getint('redis', 'play') midi_play = patch.getint('midi', 'play') midi_start = patch.getint('midi', 'start') if previous_redis_play is None and redis_play is not None: previous_redis_play = not (redis_play) if previous_midi_play is None and midi_play is not None: previous_midi_play = not (midi_play) if previous_midi_start is None and midi_start is not None: previous_midi_start = not (midi_start) # the MIDI port should only be opened once, and only if needed if midi_play and midiport == None: mididevice = patch.getstring('midi', 'device') mididevice = EEGsynth.trimquotes(mididevice) mididevice = process.extractOne( mididevice, mido.get_output_names())[0] # select the closest match try: outputport = mido.open_output(mididevice) monitor.success('Connected to MIDI output') except: raise RuntimeError("cannot connect to MIDI output") # do something whenever the value changes if redis_play and not previous_redis_play: redisthread.setEnabled(True) previous_redis_play = True elif not redis_play and previous_redis_play: redisthread.setEnabled(False) previous_redis_play = False # do something whenever the value changes if midi_play and not previous_midi_play: midithread.setEnabled(True) previous_midi_play = True elif not midi_play and previous_midi_play: midithread.setEnabled(False) previous_midi_play = False # do something whenever the value changes if midi_start and not previous_midi_start: if midiport != None: midiport.send(mido.Message('start')) previous_midi_start = True elif not midi_start and previous_midi_start: if midiport != None: midiport.send(mido.Message('stop')) previous_midi_start = False rate = patch.getfloat('input', 'rate', default=0) rate = EEGsynth.rescale(rate, slope=scale_rate, offset=offset_rate) rate = EEGsynth.limit(rate, 30., 240.) shift = patch.getfloat('input', 'shift', default=0) shift = EEGsynth.rescale(shift, slope=scale_shift, offset=offset_shift) shift = int(shift) ppqn = patch.getfloat('input', 'ppqn', default=0) ppqn = EEGsynth.rescale(ppqn, slope=scale_ppqn, offset=offset_ppqn) ppqn = find_nearest_value([1, 2, 3, 4, 6, 8, 12, 24], ppqn) # show the parameters whose value has changed monitor.update("redis_play", redis_play) monitor.update("midi_play", midi_play) monitor.update("midi_start", midi_start) monitor.update("rate", rate) monitor.update("shift", shift) monitor.update("ppqn", ppqn) # update the clock and redis clockthread.setRate(rate) redisthread.setShift(shift) redisthread.setPpqn(ppqn) # there should not be any local variables in this function, they should all be global if len(locals()): print("LOCALS: " + ", ".join(locals().keys()))
def run(self): pubsub = r.pubsub() pubsub.subscribe('KEYBOARD_UNBLOCK') # this message unblocks the Redis listen command pubsub.subscribe(self.onset) # this message triggers the note while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel']==self.onset: # the trigger may contain a value that should be mapped to MIDI val = item['data'] val = EEGsynth.rescale(val, slope=input_scale, offset=input_offset) val = EEGsynth.limit(val, 0, 127) val = int(val) if self.velocity == None: # use the value of the onset trigger velocity = val elif type(self.velocity) == str: velocity = float(r.get(self.velocity)) velocity = EEGsynth.rescale(velocity, slope=scale_velocity, offset=offset_velocity) velocity = EEGsynth.limit(velocity, 0, 127) velocity = int(velocity) else: velocity = self.velocity if type(self.pitch) == str: pitch = float(r.get(self.pitch)) pitch = EEGsynth.rescale(pitch, slope=scale_pitch, offset=offset_pitch) pitch = EEGsynth.limit(pitch, 0, 127) pitch = int(pitch) else: pitch = self.pitch if type(self.duration) == str: duration = float(r.get(self.duration)) duration = EEGsynth.rescale(duration, slope=scale_duration, offset=offset_duration) duration = EEGsynth.limit(duration, 0.05, float('Inf')) # some minimal time is needed for the delay else: duration = self.duration if debug>1: print '----------------------------------------------' print "onset ", self.onset, "=", val print "velocity", self.velocity, "=", velocity print "pitch ", self.pitch, "=", pitch print "duration", self.duration, "=", duration if midichannel is None: msg = mido.Message('note_on', note=pitch, velocity=velocity) else: msg = mido.Message('note_on', note=pitch, velocity=velocity, channel=midichannel) SendMessage(msg) if duration != None: # schedule a delayed MIDI message to be sent to switch the note off if midichannel is None: msg = mido.Message('note_on', note=pitch, velocity=0) else: msg = mido.Message('note_on', note=pitch, velocity=0, channel=midichannel) t = threading.Timer(duration, SendMessage, args=[msg]) t.start()
try: while True: monitor.loop() time.sleep(patch.getfloat('general', 'delay')) for name, cmd in zip(control_name, control_code): # loop over the control values val = patch.getfloat('control', name) if val == None: continue # it should be skipped when not present if val == previous_val[name]: continue # it should be skipped when identical to the previous value previous_val[name] = val # map the Redis values to MIDI values val = EEGsynth.rescale(val, slope=scale, offset=offset) val = EEGsynth.limit(val, 0, 127) val = int(val) msg = mido.Message('control_change', control=cmd, value=val, channel=midichannel) monitor.debug(cmd, val, name) lock.acquire() outputport.send(msg) lock.release() except KeyboardInterrupt: monitor.success('Closing threads') for thread in trigger: thread.stop() r.publish('VOLCABASS_UNBLOCK', 1)
active = EEGsynth.rescale(active, slope=scale_active, offset=offset_active) active = int(active) # get the corresponding sequence as a single string try: sequence = patch.getstring('sequence', "sequence%03d" % active, multiple=True) except: sequence = [] transpose = patch.getfloat('sequence', 'transpose', default=0.) transpose = EEGsynth.rescale(transpose, slope=scale_transpose, offset=offset_transpose) # the duration is relative to the time between clock ticks duration = patch.getfloat('sequence', 'duration', default=0.) duration = EEGsynth.rescale(duration, slope=scale_duration, offset=offset_duration) duration = EEGsynth.limit(duration, 0.1, 0.9) if debug > 0: # show the parameters whose value has changed show_change("active", active) show_change("sequence", sequence) show_change("transpose", transpose) show_change("duration", duration) sequencethread.setSequence(sequence) sequencethread.setTranspose(transpose) sequencethread.setDuration(duration) elapsed = time.time() - now naptime = patch.getfloat('general', 'delay') - elapsed if naptime > 0:
def run(self): pubsub = r.pubsub() pubsub.subscribe('KEYBOARD_UNBLOCK' ) # this message unblocks the Redis listen command pubsub.subscribe(self.onset) # this message triggers the note while self.running: for item in pubsub.listen(): if not self.running or not item['type'] == 'message': break if item['channel'] == self.onset: # the trigger may contain a value that should be mapped to MIDI val = item['data'] val = EEGsynth.rescale(val, slope=input_scale, offset=input_offset) val = EEGsynth.limit(val, 0, 127) val = int(val) if self.velocity == None: # use the value of the onset trigger velocity = val elif type(self.velocity) == str: velocity = float(r.get(self.velocity)) velocity = EEGsynth.rescale(velocity, slope=scale_velocity, offset=offset_velocity) velocity = EEGsynth.limit(velocity, 0, 127) velocity = int(velocity) else: velocity = self.velocity if type(self.pitch) == str: pitch = float(r.get(self.pitch)) pitch = EEGsynth.rescale(pitch, slope=scale_pitch, offset=offset_pitch) pitch = EEGsynth.limit(pitch, 0, 127) pitch = int(pitch) else: pitch = self.pitch if type(self.duration) == str: duration = float(r.get(self.duration)) duration = EEGsynth.rescale(duration, slope=scale_duration, offset=offset_duration) duration = EEGsynth.limit( duration, 0.05, float('Inf') ) # some minimal time is needed for the delay else: duration = self.duration if debug > 1: print('----------------------------------------------') print("onset ", self.onset, "=", val) print("velocity", self.velocity, "=", velocity) print("pitch ", self.pitch, "=", pitch) print("duration", self.duration, "=", duration) if midichannel is None: msg = mido.Message('note_on', note=pitch, velocity=velocity) else: msg = mido.Message('note_on', note=pitch, velocity=velocity, channel=midichannel) SendMessage(msg) if duration != None: # schedule a delayed MIDI message to be sent to switch the note off if midichannel is None: msg = mido.Message('note_on', note=pitch, velocity=0) else: msg = mido.Message('note_on', note=pitch, velocity=0, channel=midichannel) t = threading.Timer(duration, SendMessage, args=[msg]) t.start()
# this returns None when the channel is not present chanval = patch.getfloat('input', chanstr) if chanval==None: # the value is not present in Redis, skip it if debug>2: print chanstr, 'not available' continue # the scale and offset options are channel specific scale = patch.getfloat('scale', chanstr, default=255) offset = patch.getfloat('offset', chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) # ensure that it is within limits chanval = EEGsynth.limit(chanval, lo=0, hi=255) chanval = int(chanval) if dmxdata[chanindx]!=chr(chanval): if debug>0: print "DMX channel%03d" % chanindx, '=', chanval # update the DMX value for this channel dmxdata = senddmx(dmxdata,chanindx,chanval) elif (time.time()-prevtime)>1: # send a maintenance packet now and then dmxdata = senddmx(dmxdata,chanindx,chanval) prevtime = time.time() except KeyboardInterrupt: if debug>0: print "closing..."
# this returns None when the channel is not present chanval = patch.getfloat('input', chanstr) if chanval == None: # the value is not present in Redis, skip it if debug > 2: print(chanstr, 'not available') continue # the scale and offset options are channel specific scale = patch.getfloat('scale', chanstr, default=255) offset = patch.getfloat('offset', chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) # ensure that it is within limits chanval = EEGsynth.limit(chanval, lo=0, hi=255) chanval = int(chanval) if dmxdata[chanindx - 1] != chanval: # update the DMX value for this channel dmxdata[chanindx - 1] = chanval if debug > 1: print("DMX channel%03d" % chanindx, '=', chanval) artnet.broadcastDMX(dmxdata, address) elif (time.time() - prevtime) > 1: # send a maintenance packet now and then artnet.broadcastDMX(dmxdata, address) prevtime = time.time() except KeyboardInterrupt: # blank out
except: sequence = [] transpose = patch.getfloat('sequence', 'transpose', default=0.) transpose = EEGsynth.rescale(transpose, slope=scale_transpose, offset=offset_transpose) # the duration is relative to the time between clock ticks duration = patch.getfloat('sequence', 'duration', default=0.) duration = EEGsynth.rescale(duration, slope=scale_duration, offset=offset_duration) if duration > 0: # a duration of 0 or less means that the note will not switch off duration = EEGsynth.limit(duration, 0.1, 0.9) if debug > 0: # show the parameters whose value has changed show_change("active", active) show_change("sequence", sequence) show_change("transpose", transpose) show_change("duration", duration) sequencethread.setSequence(sequence) sequencethread.setTranspose(transpose) sequencethread.setDuration(duration) elapsed = time.time() - now naptime = patch.getfloat('general', 'delay') - elapsed if naptime > 0:
# this returns None when the channel is not present chanval = patch.getfloat('input', chanstr) if chanval == None: # the value is not present in Redis, skip it if debug > 2: print(chanstr, 'not available') continue # the scale and offset options are channel specific scale = patch.getfloat('scale', chanstr, default=4095) offset = patch.getfloat('offset', chanstr, default=0) # apply the scale and offset chanval = EEGsynth.rescale(chanval, slope=scale, offset=offset) # ensure that it is within limits chanval = EEGsynth.limit(chanval, lo=0, hi=4095) chanval = int(chanval) if debug > 0: show_change(chanstr, chanval) lock.acquire() s.write('*c%dv%04d#' % (chanindx, chanval)) lock.release() for chanindx in range(1, 5): chanstr = "gate%d" % chanindx chanval = patch.getfloat('input', chanstr) if chanval == None: # the value is not present in Redis, skip it
midithread.setEnabled(False) previous_midi_play = False # do something whenever the value changes if midi_start and not previous_midi_start: if midiport != None: midiport.send(mido.Message('start')) previous_midi_start = True elif not midi_start and previous_midi_start: if midiport != None: midiport.send(mido.Message('stop')) previous_midi_start = False rate = patch.getfloat('input', 'rate', default=0) rate = EEGsynth.rescale(rate, slope=scale_rate, offset=offset_rate) rate = EEGsynth.limit(rate, 30., 240.) shift = patch.getfloat('input', 'shift', default=0) shift = EEGsynth.rescale(shift, slope=scale_shift, offset=offset_shift) shift = int(shift) ppqn = patch.getfloat('input', 'ppqn', default=0) ppqn = EEGsynth.rescale(ppqn, slope=scale_ppqn, offset=offset_ppqn) ppqn = find_nearest_value([1, 2, 3, 4, 6, 8, 12, 24], ppqn) if debug > 0: # show the parameters whose value has changed show_change("redis_play", redis_play) show_change("midi_play", midi_play) show_change("midi_start", midi_start) show_change("rate", rate)
try: while True: time.sleep(patch.getfloat('general', 'delay')) for name, cmd in zip(control_name, control_code): # loop over the control values val = patch.getfloat('control', name) if val==None: continue # it should be skipped when not present if val==previous_val[name]: continue # it should be skipped when identical to the previous value previous_val[name] = val # map the Redis values to MIDI values val = EEGsynth.rescale(val, slope=scale, offset=offset) val = EEGsynth.limit(val, 0, 127) val = int(val) msg = mido.Message('control_change', control=cmd, value=val, channel=midichannel) if debug>1: print cmd, val, name lock.acquire() outputport.send(msg) lock.release() except KeyboardInterrupt: print "Closing threads" for thread in trigger: thread.stop() r.publish('VOLCAKEYS_UNBLOCK', 1) for thread in trigger: thread.join()
name = 'channel{}'.format(channel + 1) val = patch.getfloat('control', name) if val is None: # the value is not present in Redis, skip it if debug > 2: print name, 'not available' continue # the scale and offset options are channel specific scale = patch.getfloat('scale', name, default=1) offset = patch.getfloat('offset', name, default=0) # map the Redis values to MIDI pitch values val = EEGsynth.rescale(val, slope=scale, offset=offset) # ensure that it is within limits val = EEGsynth.limit(val, lo=-8192, hi=8191) val = int(val) if val == previous_val[name]: continue # it should be skipped when identical to the previous value else: previous_val[name] = val if debug > 0: print name, val # midi channels in the inifile are 1-16, in the code 0-15 midichannel = channel msg = mido.Message('pitchwheel', pitch=val, channel=midichannel) if debug > 1: print msg