def process(self): if self._inputBuffer is None or self._outputBuffer is None: return if not self._inputBufferValid(0, buffer_type=effect.AudioBuffer.__name__): self._outputBuffer[0] = None return color = self._inputBuffer[1] if color is None: color = self._default_color y = self._inputBuffer[0].audio fs = self._inputBuffer[0].sample_rate if self.lowcut_hz > 0 or self.highcut_hz < 20000: # construct filter if needed if self._bandpass is None: self._bandpass = dsp.Bandpass(self.lowcut_hz, self.highcut_hz, fs, 3) # update bandpass self._bandpass.updateParams(self.lowcut_hz, self.highcut_hz, fs, 3) # process audio y = self._bandpass.filter(np.array(y), fs) rms = dsp.rms(y) # calculate rms over hold_time while len(self._hold_values) > self.n_overlaps: self._hold_values.pop() self._hold_values.insert(0, rms) rms = dsp.rms(self._hold_values) db = 20 * math.log10(max(rms, 1e-16)) scal_value = (self.db_range + db) / self.db_range bar = np.zeros(self._num_pixels) * np.array([[0], [0], [0]]) index = int(self._num_pixels * scal_value) index = np.clip(index, 0, self._num_pixels - 1) bar[0:3, 0:index] = color[0:3, 0:index] self._outputBuffer[0] = bar
def process(self): if self._inputBuffer is None or self._outputBuffer is None: return buffer = self._inputBuffer[0] if buffer is None: self._outputBuffer[0] = None return color = self._inputBuffer[1] if color is None: color = self._default_color y = self._inputBuffer[0] rms = dsp.rms(y) # calculate rms over hold_time while len(self._hold_values) > self.n_overlaps: self._hold_values.pop() self._hold_values.insert(0, rms) rms = dsp.rms(self._hold_values) db = 20 * math.log10(max(rms, 1e-16)) scal_value = (self.db_range + db) / self.db_range bar = np.zeros(self._num_pixels) * np.array([[0], [0], [0]]) index = int(self._num_pixels * scal_value) index = np.clip(index, 0, self._num_pixels - 1) bar[0:3, 0:index] = color[0:3, 0:index] self._outputBuffer[0] = bar
def process(self): if self._inputBuffer is None or self._outputBuffer is None: return if not self._inputBufferValid(0, buffer_type=effect.AudioBuffer.__name__): return y = self._inputBuffer[0].audio rms = dsp.rms(y) # calculate rms over hold_time while len(self._hold_values) > 20 * self.smoothing: self._hold_values.pop() self._hold_values.insert(0, rms) rms = dsp.rms(self._hold_values) db = 20 * math.log10(max(rms, 1e-16)) scal_value = (self.db_range + db) / self.db_range self._outputBuffer[0] = self._inputBuffer[1] * (1 - self.amount) + self._inputBuffer[1] * scal_value * self.amount
def process(self): if self._inputBuffer is None or self._outputBuffer is None: return if not self._inputBufferValid(0, buffer_type=effect.AudioBuffer.__name__): return if not self._inputBufferValid(1): self._outputBuffer[0] = None return # Init audio audio = self._inputBuffer[0].audio fs = self._inputBuffer[0].sample_rate # construct filter if needed if self._bandpass is None: self._bandpass = dsp.Bandpass(self.lowcut_hz, self.highcut_hz, fs, 3) # update bandpass self._bandpass.updateParams(self.lowcut_hz, self.highcut_hz, fs, 3) # apply bandpass to audio y = self._bandpass.filter(np.array(audio), fs) x = self._inputBuffer[1] rms = dsp.rms(y) # calculate rms over hold_time while len(self._hold_values) > 20 * self.smoothing: self._hold_values.pop() self._hold_values.insert(0, rms) rms = dsp.rms(self._hold_values) db = 20 * math.log10(max(rms, 1e-16)) db = max(db, -self.db_range) scal_value = (self.db_range + db) / self.db_range try: scal_value = scal_value**self.peak_filter except Exception: scal_value = scal_value scal_value = scal_value * self.peak_scale dt_move = self._t - self._last_t shift = dt_move * self.speed * 0.1 * scal_value self._shift_pixels = math.fmod((self._shift_pixels + shift), np.size(x, axis=1)) self._last_t = self._t self._outputBuffer[0] = sp.ndimage.interpolation.shift(x, [0, self._shift_pixels], mode='wrap', prefilter=True)
def _handleSysex(self, data, serverconfig: serverconfiguration.ServerConfiguration, proj: project.Project): if len(data) < 2: logger.error("Sysex message too short") return if data[0] == 0x00 and data[1] == 0x00: # Version logger.info("MIDI-BLE REQ Version") if self._sendMidiCallback is not None: self._sendMidiCallback(self._createVersionMsg()) elif data[0] == 0x00 and data[1] == 0x10: # Update logger.info("MIDI-BLE REQ Update") logger.info("Checking for update, current version is {}".format( version.get_version())) self._updatePathsAndUrls(serverconfig) if not self._isUpdating: self._isUpdating = True # New Downloaders will be created on request self.client.refresh() app_update = self.client.update_check('Molecole', version.get_version()) if app_update is not None: logger.info("Update {} available".format( app_update.current_version)) threading.Thread(target=self._updateApp, args=[app_update]).start() else: self._isUpdating = False logger.info("Update check returned no update") if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateNotAvailableMsg()) else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createUpdateBusyMsg()) elif data[0] == 0x00 and data[1] == 0x11: # Update check logger.info("MIDI-BLE REQ Update check") logger.info("Checking for update, current version is {}".format( version.get_version())) self._updatePathsAndUrls(serverconfig) if not self._isUpdating: self._isUpdating = True try: # Move version.gz to force a reload oldFile = os.path.join(self.client.data_dir, self.client.version_file) if os.path.exists(oldFile): shutil.move(oldFile, oldFile.replace('.gz', '.gz.bak')) oldFile = os.path.join(self.client.data_dir, self.client.version_file_compat) if os.path.exists(oldFile): shutil.move(oldFile, oldFile.replace('.gz', '.gz.bak')) self.client.refresh() app_update = self.client.update_check( 'Molecole', version.get_version()) self._isUpdating = False if app_update is not None: logger.info("Update {} available".format( app_update.version)) if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateVersionAvailableMsg( app_update.version)) else: logger.info("Update check returned no update") if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateNotAvailableMsg()) except Exception as e: logger.error("Error trying to update: {}".format(e)) self._isUpdating = False else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createUpdateBusyMsg()) elif data[0] == 0x00 and data[1] == 0x20: # Active project metadata if self._sendMidiCallback is not None: metadata = serverconfig.getProjectMetadata(proj.id) self._sendMidiCallback(self._createActiveProjectMsg(metadata)) elif data[0] == 0x00 and data[1] == 0x30: # Get projects if self._sendMidiCallback is not None: metadata = serverconfig.getProjectsMetadata() self._sendMidiCallback(self._createProjectsMsg(metadata)) elif data[0] == 0x00 and data[1] == 0x40: # Activate project projUid = str(bytes(sysex_data.decode(data[2:])), encoding='utf8') logger.info("MIDI-BLE REQ Activate project {}".format(projUid)) proj = serverconfig.getProject(projUid) if proj is not None: if self._sendMidiCallback is not None: self._sendMidiCallback( self._createActivateProjSuccessfulMsg()) else: if self._sendMidiCallback is not None: self._sendMidiCallback( self._createActivateProjNotFoundMsg()) elif data[0] == 0x00 and data[1] == 0x50: # Import project logger.info("MIDI-BLE REQ Import project") dec = sysex_data.decode(data[2:]) projGzip = zlib.decompress(bytes(dec)) projJson = str(projGzip, encoding='utf8') try: serverconfig.importProject(projJson) if self._sendMidiCallback is not None: self._sendMidiCallback( self._createImportProjSuccessfulMsg()) except Exception: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createImportProjErrorMsg()) elif data[0] == 0x00 and data[1] == 0x60: # Export project byteArr = bytes(sysex_data.decode(data[2:])) logger.debug("Decoded {} to {}".format([hex(c) for c in data[2:]], byteArr)) projUid = str(byteArr, encoding='utf8') logger.info("MIDI-BLE REQ Export project {}".format(projUid)) proj = serverconfig.getProject(projUid) if proj is not None: if self._sendMidiCallback is not None: self._sendMidiCallback( self._createExportProjSuccessfulMsg(proj)) else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createExportProjNotFoundMsg()) elif data[0] == 0x00 and data[1] == 0x70: # Activate project projUid = str(bytes(sysex_data.decode(data[2:])), encoding='utf8') logger.info("MIDI-BLE REQ Delete project {}".format(projUid)) proj = serverconfig.getProject(projUid) if proj is not None: serverconfig.deleteProject(projUid) if self._sendMidiCallback is not None: self._sendMidiCallback(self._deleteProjSuccessfulMsg()) else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._deleteProjNotFoundMsg()) elif data[0] == 0x01 and data[1] == 0x00: # Get active scene ID logger.info("MIDI-BLE REQ Active scene index") sceneId = serverconfig.getActiveProjectOrDefault().activeSceneId if self._sendMidiCallback is not None: self._sendMidiCallback( self._createGetActiveSceneIdMsg(sceneId)) elif data[0] == 0x01 and data[1] == 0x10: # Get active scene metadata logger.info("MIDI-BLE REQ Get active scene metadata") proj = serverconfig.getActiveProjectOrDefault() metadata = proj.getSceneMetadata(proj.activeSceneId) if self._sendMidiCallback is not None: self._sendMidiCallback( self._createActiveSceneMetadataMsg(metadata)) elif data[0] == 0x01 and data[1] == 0x20: # Get scenes metadata logger.info("MIDI-BLE REQ Get scenes") proj = serverconfig.getActiveProjectOrDefault() metadata = proj.sceneMetadata if self._sendMidiCallback is not None: self._sendMidiCallback(self._createScenesMetadataMsg(metadata)) elif data[0] == 0x01 and data[1] == 0x30: # Get enabled controllers for active scene logger.info("MIDI-BLE REQ Get controller enabled for active scene") proj = serverconfig.getActiveProjectOrDefault() if self._sendMidiCallback is not None: self._sendMidiCallback(self._createEnabledControllersMsg(proj)) elif data[0] == 0x01 and data[1] == 0x40: # Request controller values for active project logger.info("MIDI-BLE REQ Get controller values for active scene") proj = serverconfig.getActiveProjectOrDefault() self._sendControllerStatus(proj) elif data[0] == 0x01 and data[1] == 0x50: # Reset controller values for active project logger.info("MIDI-BLE REQ Reset controller for active scene") proj = serverconfig.getActiveProjectOrDefault() proj.resetControllerModulation() self._sendControllerStatus(proj) elif data[0] == 0x02 and data[1] == 0x00: # Get server configuration logger.info("MIDI-BLE REQ Get server configuration") config = serverconfig.getFullConfiguration() if self._sendMidiCallback is not None: self._sendMidiCallback(self._createGetServerConfigMsg(config)) elif data[0] == 0x02 and data[1] == 0x10: # Update server configuration logger.info("MIDI-BLE REQ Update server configuration") dec = sysex_data.decode(data[2:]) configGzip = zlib.decompress(bytes(dec)) configJson = str(configGzip, encoding='utf8') config = json.loads(configJson) try: serverconfig.setConfiguration(config) if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateServerConfigSuccessfulMsg()) except Exception: if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateServerConfigErrorMsg()) elif data[0] == 0x02 and data[1] == 0x20: logger.info("MIDI-BLE REQ Audio rms") rms = {"0": "NO_BUFFER"} if audio.GlobalAudio.buffer is not None: if isinstance(audio.GlobalAudio.buffer, Iterable): numChannels = len(audio.GlobalAudio.buffer) for i in range(numChannels): rmsFromChannel = dsp.rms(audio.GlobalAudio.buffer[i]) rms[str(i)] = rmsFromChannel else: logger.debug("audio.GlobalAudio.buffer not iterateable") if self._sendMidiCallback is not None: self._sendMidiCallback(self._createAudioRMSMsg( json.dumps(rms))) else: logger.error("MIDI-BLE Unknown sysex {} {}".format( hex(data[0]), hex(data[1])))