class Motu: def __init__(self, ipAddr=None, port=80): self.ipAddr = ipAddr self.port = port self.url = "http://{}:{}".format(ipAddr, port) self.uid = self._getUid() self.waitOnline() self.motuSettings = self._getSettings() self.settings = Settings() if self.motuSettings: print("========== Motu AVB: ===========") print("* Name : {}".format(self.motuSettings["hostname"])) print("* uid : {}".format(self.uid)) print("* Sample rate : {}".format( self.motuSettings["cfg/0/current_sampling_rate"])) print("================================") def _query(self, address, value): """ Send the corresponding message - address is a string - value is an array Return True if well received by the motu soundcard """ value = json.dumps(value) url = "{}/datastore{}".format(self.url, address) r = requests.post(url, {"json": value}) print("{} : {}".format(url, value)) return True if r.status_code is 200 else False def _get(self, address): """ Get the value at a given endPoint """ url = "{}/datastore{}".format(self.url, address) r = requests.get(url) return r.json() if r.status_code is 200 else False def _getUid(self): """ Get the uid of the motu soundCard """ uidJson = self._get("/uid") return uidJson["value"] if uidJson else False def _getSettings(self): """ Get the settings of the motu soundCard """ settings = self._get("/avb/{}/".format(self.uid)) return settings def isOnline(self): return self._getUid() def waitOnline(self, timeout=10): cpt = 0 while not self.isOnline(): cpt = cpt + 1 message = "Motu not online or unreachable, trying again ({})".format( cpt) sys.stdout.write("\r\x1b[K" + message.__str__()) sys.stdout.flush() time.sleep(1) if cpt == timeout: print( "\n Please verify that the soundCard is reachable on the network at {}" .format(self.url)) quit(404) ################################################### def setSolo(self, ch, solo): """ Solo a given channel """ address = "/mix/chan/{}/matrix/solo".format(ch) values = {"value": 1 if solo else 0} return self._query(address, values) def getSolo(self, ch): """ Get Solo status of a given channel """ address = "/mix/chan/{}/matrix/solo".format(ch) solo = self._get(address) return bool(solo["value"]) def clearSolo(self): """ Remove all solo status of the strips :return: """ for ch in range(0, self.settings.getStripCount()): self.setSolo(ch, False) ################################################### def setMute(self, ch, mute): """ Mute a given channel """ address = "/mix/chan/{}/matrix/mute".format(ch) values = {"value": 1 if mute else 0} return self._query(address, values) def getMute(self, ch): """ Get Mute status of a given channel """ address = "/mix/chan/{}/matrix/mute".format(ch) mute = self._get(address) return bool(mute["value"]) def muteAll(self, mute): """ Mute / Unmute all strips :param mute: true to mute all, false otherwise :return: """ for ch in range(0, self.settings.getStripCount()): self.setMute(ch, mute) ################################################### def setGain(self, ch, gain): """ Set gain of a given channel """ address = "/mix/chan/{}/matrix/???".format(ch) values = {"value": gain} print("TODO: set Gain") # return self._query(address, values) def getGain(self, ch): """ Get gain of a given channel """ address = "/mix/chan/{}/matrix/???".format(ch) print("TODO: get Gain") # return self._query(address, values) ################################################### def setFader(self, ch, fader): """ Set fader of a given channel """ address = "/mix/chan/{}/matrix/fader".format(ch) values = {"value": fader} return self._query(address, values) def getFader(self, ch, datatype="api"): """ Get fader of a given channel """ address = "/mix/chan/{}/matrix/fader".format(ch) fader = self._get(address) if datatype is "midi": return convertValueToMidiRange(fader["value"], fader_api_range, fader_midi_range, "log") else: return fader["value"] ################################################### def setMainFader(self, fader): """ Set fader of a main """ address = "/mix/main/{}/matrix/fader".format(0) values = {"value": fader} return self._query(address, values) def getMainFader(self, datatype="api"): """ Get fader of a main """ address = "/mix/main/{}/matrix/fader".format(0) fader = self._get(address) if fader: if datatype is "midi": return convertValueToMidiRange(fader["value"], fader_api_range, fader_midi_range, "log") else: return fader["value"] else: return False def setMonitorFader(self, fader): """ Set fader of a monitor output """ address = "/mix/monitor/{}/matrix/fader".format(0) values = {"value": fader} return self._query(address, values) def getMonitorFader(self, datatype="api"): """ Get fader of a monitor """ address = "/mix/monitor/{}/matrix/fader".format(0) fader = self._get(address) if fader: if datatype is "midi": return convertValueToMidiRange(fader["value"], fader_api_range, fader_midi_range, "log") else: return fader["value"] else: return False ################################################### def setPan(self, ch, pan): """ Set Pan of a given channel > from -1 to 1 """ if -1 <= pan <= 1: address = "/mix/chan/{}/matrix/pan".format(ch) values = {"value": pan} return self._query(address, values) else: return False def getPan(self, ch, datatype="api"): """ Get Pan of a given channel > from -1 to 1 """ address = "/mix/chan/{}/matrix/pan".format(ch) pan = self._get(address) if datatype is "midi": return convertValueToMidiRange(pan["value"], [-1, 1], [0, 127]) else: return pan["value"] ################################################### def setEq(self, ch, eq): """ Big stuff to do here """ print("TODO set EQ") eq = { "hpf": { "enabled": True, "freq": 40 }, "lowShelf": { "enabled": True, "freq": 80, "gain": 0, "bw": 1, "mode": 0 }, "mid1": { "enabled": True, "freq": 800, "gain": 0, "bw": 1, }, "mid2": { "enabled": True, "freq": 10000, "gain": 0, "bw": 1, }, "highShelf": { "enabled": True, "freq": 18000, "gain": 0, "bw": 1, "mode": 0 } } def getEq(self, ch): """ Get eq Object of the given chan """ address = "/mix/chan/{}/eq".format(ch) return self._get(address) ################################################### def setComp(self, ch): """ Enable compressor on given channel """ print("TODO set Comp") def getComp(self, ch): """ Knoww if compressor is enabled on given channel """ print("TODO set Comp") ################################################### def muteMain(self, mute): """ Mute main outputs """ address = "/mix/main/{}/matrix/mute".format(0) values = {"value": 1 if mute else 0} self._query(address, values) return mute def getMainMute(self): """ Is Main muted? """ address = "/mix/main/{}/matrix/mute".format(0) mute = self._get(address) return bool(mute["value"] if mute else False) def muteMonitor(self, mute): """ Mute monitor outputs """ address = "/mix/monitor/{}/matrix/mute".format(0) values = {"value": 1 if mute else 0} self._query(address, values) return mute def getMonitorMute(self): """ Is monitor muted? """ address = "/mix/monitor/{}/matrix/mute".format(0) mute = self._get(address) return bool(mute["value"] if mute else False) def dim(self, status): """ Apply -20dB on current master value """ gain = self.settings.getDimValue("api") if status: self.setMainFader(gain) else: # revert to 0dB self.setMainFader(1) def getTrackName(self, ch): """ Get the track name of a given channel :param ch: :return: """ # TODO return "Track {}".format(ch) def recallSettings(self): """ Get all settings in one request :return: """ address = "/" dataStore = self._get(address) print(dataStore)
class MidiWait: def __init__(self, ipAddr=None, port=80): # Init Midi client and display available devices midiINPort = mido.get_input_names()[0] midiOUTPort = mido.get_output_names()[0] self.midiIN = mido.open_input(midiINPort) self.midiOUT = mido.open_output(midiOUTPort) self.mcu = MCU() self.motu = Motu(ipAddr, port) self.settings = Settings() self.hwSetup = Setup() self.mixerUrl = "http://{}:{}".format(ipAddr, port) print("Will send events to mixer at {}".format(self.mixerUrl)) self.recall() def setup(self): """ Set up the bridge """ self.settings.restoreDefault() # self.hwSetup.setupInterface() def read(self): """ Read Midi message """ msg = self.midiIN.receive() self.routeMessage(msg) return msg def sendQueryMessage(self, address, value): """ Send the corresponding message - address is a string - value is an array """ value = json.dumps(value) url = "{}/datastore{}".format(self.mixerUrl, address) r = requests.post(url, {"json": value}) print("[{}] {} {}".format(r.status_code, url, value)) # print(">Query [{}] {} {} with data {}".format(r.status_code,self.mixerUrl, address,value)) #print("{}".format(r)) def routeMessage(self, midiMessage): """ Route the message to corresponding function :param midiMessage: """ # print("midiIN {}:".format(midiMessage)) # Faders if midiMessage.type == "pitchwheel": self._handlePitchWheel(midiMessage.channel, midiMessage.pitch) # vPots if midiMessage.type == "control_change": self._handleVpotRot(midiMessage.control, midiMessage.value) if midiMessage.type == "note_on" and midiMessage.velocity == 127: soloMidiNotes = ["E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0"] muteMidiNotes = ["C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1"] vPotMidiNotes = [ "G#1", "A1", "A#1", "B1", "C2", "C#2", "D2", "D#2" ] bankMidiNotes = ["A#2", "B2"] functionMidiNotes = [ "G#2", "G2", "F#2", "F2", "G6", "G#6", "A6", "A#6" ] midiFullNote = midiNumberToFullNote(midiMessage.note) # Solo if midiFullNote in soloMidiNotes: ch = soloMidiNotes.index(midiFullNote) self._handleSoloButton(ch) # Mute if midiFullNote in muteMidiNotes: ch = muteMidiNotes.index(midiFullNote) self._handleMuteButton(ch) # vPots click if midiFullNote in vPotMidiNotes: ch = vPotMidiNotes.index(midiFullNote) self._handleVpotClick(ch) # Bank buttons if midiFullNote in bankMidiNotes: bankUp = bankMidiNotes.index(midiFullNote) == 0 self._handleBankButton(bankUp) # Function buttons if midiFullNote in functionMidiNotes: f = functionMidiNotes.index(midiFullNote) self._handleFunctionButton(f) # Encoder groups : only 2 of 4 are working... need investigation # Top right if midiFullNote in ["A#4", "D#3"]: self._handleEncoderGrpButtons(midiFullNote) def _handleEncoderGrpButtons(self, note): """ Handle the Encoder groups button to change mode (only top and bottom right) """ print("encoder group note {}", note) if note == "A#4": self.mcu.setMode("main") elif note == "D#3": self.mcu.setMode("mixing") self.recall() def _handleFunctionButton(self, fNum): """ Handle the function Buttons F1 -> F8 """ print("Button F{} clicked".format(fNum)) # get previous know state and toggle it fState = self.settings.getFunction(fNum) if fNum == 0: # Mute/unMute All self.motu.clearSolo() self.recall() if fNum == 1: # Mute/unMute All self.motu.muteAll(not fState) fState = self.settings.setFunction(fNum, not fState) self.mcu.fLed(1, fState) self.recall() if fNum == 7: self.motu.dim(not fState) fState = self.settings.setFunction(fNum, not fState) # update led on button self.mcu.fLed(7, fState) mainFaderValue = self.motu.getMainFader("midi") self.mcu.faderPos(7, mainFaderValue) def _handleBankButton(self, up): """ Handle bank buttons """ bank = self.settings.getCurrentBank() if up: bank = bank + 1 else: bank = bank - 1 if bank < 0: bank = 0 print("BANK is {}".format(bank)) self.settings.setCurrentBank(bank) def _handlePitchWheel(self, ch, value): """ Handle fader moves """ self.settings.setFaderPos(ch, value) # TODO handle banking apiRange = [0, 4] midiRange = [-8192, 8192] faderValue = convertValueToOSCRange(value, apiRange, midiRange, "log") if self.mcu.getMode() is "main": if ch is 7: # and mode is monito! self.motu.setMainFader(faderValue) elif ch is 6: # and mode is monito! self.motu.setMonitorFader(faderValue) else: self.motu.setFader(ch, faderValue) # use this to save fader pos on mcu self.mcu.faderPos(ch, value) def _handleVpotClick(self, ch): """ Handle vPot click: restore default pan or gain """ apiRange = [-1, 1] midiRange = [0, 127] newPos = 0 address = "/mix/chan/{}/matrix/pan".format(ch) values = {"value": newPos} self.sendQueryMessage(address, values) self.settings.setVpotPos(ch, newPos) # update led ring midiVal = convertValueToMidiRange(newPos, apiRange, midiRange) self.mcu.vPotRing(ch, midiVal, "boost-cut") def _handleVpotRot(self, cc, value): """ Handle vPot rotation TODO: speed """ vPotCC = [16, 17, 18, 19, 20, 21, 22, 23] ch = 0 if cc in vPotCC: ch = vPotCC.index(cc) currentPos = self.motu.getPan(ch) apiRange = [-1, 1] midiRange = [0, 127] address = "/mix/chan/{}/matrix/pan".format(ch) direction = 1 if 1 <= value <= 15 else -1 speed = 15 - value if direction == 1 else 79 - value _speeds = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 10] speedPos = (direction * 5 * (pow(10, -1 * _speeds[speed]))) newPos = round(currentPos + speedPos, 3) if newPos > apiRange[1]: newPos = apiRange[1] if newPos < apiRange[0]: newPos = apiRange[0] self.settings.setVpotPos(ch, newPos) values = {"value": newPos} self.sendQueryMessage(address, values) # update led ring midiVal = convertValueToMidiRange(newPos, apiRange, midiRange) self.mcu.vPotRing(ch, midiVal, "boost-cut") def _handleSoloButton(self, ch): """ Handle soloButton click """ # get motu state and toggle it mcuMode = self.mcu.getMode() if mcuMode is "main": if ch == 6: # monitoring toggle mute = self.motu.getMonitorMute() m = self.motu.muteMonitor(not mute) self.mcu.l1Led(6, not m) if ch == 7: # main toggle mute = self.motu.getMainMute() m = self.motu.muteMain(not mute) self.mcu.l1Led(7, not m) else: solo = self.settings.getSolo(ch) address = "/mix/chan/{}/matrix/solo".format(ch) newStatus = not solo values = {"value": 1 if newStatus else 0} self.sendQueryMessage(address, values) self.mcu.l1Led(ch, newStatus) self.settings.setSolo(ch, newStatus) def _handleMuteButton(self, ch): """ Handle muteButton click """ # get previous know state and toggle it mute = self.settings.getMute(ch) address = "/mix/chan/{}/matrix/mute".format(ch) newStatus = not mute values = {"value": 1 if newStatus else 0} self.sendQueryMessage(address, values) self.mcu.l2Led(ch, newStatus) self.settings.setMute(ch, newStatus) def recall(self): """ Set back everything """ mainFaderValue = self.motu.getMainFader("midi") monitorFaderValue = self.motu.getMonitorFader("midi") # recall all in once #self.motu.recallSettings() self.mcu.resetController() self.mcu.setMode(self.mcu.getMode()) if self.mcu.getMode() is "main": self.mcu.resetController() self.mcu.faderPos(6, monitorFaderValue) self.mcu.faderPos(7, mainFaderValue) self.mcu.l1Led(6, not self.motu.getMonitorMute()) self.mcu.l1Led(7, not self.motu.getMainMute()) elif self.mcu.getMode() is "mixing": for ch in range(0, self.settings.getStripCount()): solo = self.motu.getSolo(ch) mute = self.motu.getMute(ch) fader = self.motu.getFader(ch, "midi") pan = self.motu.getPan(ch, "midi") self.mcu.l1Led(ch, solo) self.mcu.l2Led(ch, mute) self.mcu.faderPos(ch, fader) self.mcu.vPotRing(ch, pan, "boost-cut")