diff options
| -rw-r--r-- | buttons.py | 40 | ||||
| -rw-r--r-- | console.py | 175 | ||||
| -rw-r--r-- | faders.py | 60 | ||||
| -rw-r--r-- | fw_1884_buttons.py (renamed from fw-1884-buttons.py) | 14 | ||||
| -rw-r--r-- | osc.py | 60 | ||||
| -rw-r--r-- | strips.py | 88 |
6 files changed, 328 insertions, 109 deletions
@@ -22,7 +22,7 @@ import re import pyautogui -from osc import osc +import osc keymap = {'CTRL': 'ctrl', @@ -90,7 +90,7 @@ class StripSelButton(Button): def __init__(self, console, name, strip): super().__init__(console, name) self.strip = strip - self.strip.select = self + self.strip.select_button = self def press(self): @@ -98,9 +98,9 @@ class StripSelButton(Button): print('handle_strip_sel', strip) if self.get('REC ENABLE', 0): if strip.recenable: - osc.send_message('/strip/recenable', (strip.num, 0)) + osc.client.sent_message('/strip/recenable', (strip.num, 0)) else: - osc.send_message('/strip/recenable', (strip.num, 1)) + osc.client.sent_message('/strip/recenable', (strip.num, 1)) super().press() @@ -108,17 +108,17 @@ class StripMuteButton(Button): def __init__(self, console, name, strip): super().__init__(console, name) self.strip = strip - self.strip.mute = self + self.strip.mute_button = self def press(self): strip = self.strip if strip.mute: strip.mute = False - osc.send_message('/strip/mute', (strip.num, 0)) + osc.client.sent_message('/strip/mute', (strip.num, 0)) else: strip.mute = True - osc.send_message('/strip/mute', (strip.num, 1)) + osc.client.sent_message('/strip/mute', (strip.num, 1)) super().press() @@ -126,17 +126,17 @@ class StripSoloButton(Button): def __init__(self, console, name, strip): super().__init__(console, name) self.strip = strip - self.strip.solo = self + self.strip.solo_button = self def press(self): strip = self.strip if strip.solo: strip.solo = False - osc.send_message('/strip/solo', (strip.num, 0)) + osc.client.sent_message('/strip/solo', (strip.num, 0)) else: strip.solo = True - osc.send_message('/strip/solo', (strip.num, 1)) + osc.client.sent_message('/strip/solo', (strip.num, 1)) super().press() @@ -175,7 +175,7 @@ class ComputerButton(Button): super().__init__(console, name) def press(self): - osc.send_message('/set_surface', (8, 7, 19, 1, 8, 11)) + osc.client.sent_message('/set_surface', (8, 7, 19, 1, 8, 11)) super().press() @@ -184,7 +184,7 @@ class ClrSoloButton(Button): super().__init__(console, name) def press(self): - osc.send_message('/cancel_all_solos', 1) + osc.client.sent_message('/cancel_all_solos', 1) super().press() @@ -195,10 +195,10 @@ class LoopButton(Button): def press(self, *args): if self.console.state.get('loop', 0): print('******* loop off') - osc.send_message('/loop_toggle', 0) + osc.client.sent_message('/loop_toggle', 0) else: print('******* loop on') - osc.send_message('/loop_toggle', 1) + osc.client.sent_message('/loop_toggle', 1) super().press() @@ -209,7 +209,7 @@ class TransportButton(Button): def press(self): addr = osc_addrs.get(self.name, None) if addr: - osc.send_message(addr, 1) + osc.client.sent_message(addr, 1) super().press() @@ -224,10 +224,10 @@ class BankSwitchButton(Button): print(direction, more_banks_up, more_banks_down) if direction > 0 and more_banks_up: print('calling /bank_up 1') - osc.send_message('/bank_up', 1) + osc.client.sent_message('/bank_up', 1) elif direction < 0 and more_banks_down: print('calling /bank_down 1') - osc.send_message('/bank_down', 1) + osc.client.sent_message('/bank_down', 1) super().press() @@ -240,7 +240,9 @@ class NudgeButton(Button): direction = self.direction print(direction) if direction > 0: - osc.send_message('/access_action', 'Common/nudge-next-forward') + osc.client.sent_message('/access_action', + 'Common/nudge-next-forward') else: - osc.send_message('/access_action', 'Common/nudge-next-backward') + osc.client.sent_message('/access_action', + 'Common/nudge-next-backward') super().press() @@ -26,6 +26,7 @@ import time import threading from pathlib import Path +import osc import strips import fw_1884_buttons @@ -38,13 +39,17 @@ from hinawa_utils.tscm.tscm_console_unit import TscmConsoleUnit # noqa: E402 bits32 = '{:032b}'.format -current_bank = 1 -more_banks_up = False -more_banks_down = False +class ConsoleStatus(): + def __init__(self, quadlets): + self.quadlets = quadlets -def check_bitR2L(data, bit): - return bool(data & (0b1 << bit)) + def field(self, quadlet, first_bit=0, last_bit=32): + bits = self.quadlets[quadlet] + + # reverse the bit order before slicing so the index '0' is the LSB + # and reverse back before converting to int + return int(bits32(bits)[::-1][first_bit:last_bit][::-1], 2) class RunningStatusThread(): @@ -53,13 +58,14 @@ class RunningStatusThread(): :type interval: int :param interval: Check interval, in seconds """ + self.console = console self.interval = interval + self.callbacks = set() + self.last_status = [] - thread = threading.Thread(target=self.run, args=(console)) + thread = threading.Thread(target=self.run) thread.daemon = True # Daemonize thread thread.start() # Start the execution - self.callbacks = set() - self.last_status = [] def add_callback(self, obj): self.callbacks.add(obj) @@ -67,36 +73,14 @@ class RunningStatusThread(): def remove_callback(self, obj): self.callbacks.remove(obj) - def fetch_status_values(self, quadlets=None, first_bit=0, last_bit=32): - if quadlets is None: - quadlets = [i for i in range(0, 16)] - - def getvalue(bits): - # reverse the bit order before slicing so the index '0' is the LSB - # and reverse back before converting to int - return int(bits32(bits)[::-1][first_bit:last_bit][::-1], 2) - - values = [getvalue(self.last_status[q]) for q in quadlets] - - if len(values) == 1: - return values[0] - - return values - - def run(self, console): + def run(self): """ Method that runs forever """ while True: - - if not self.callbacks: - continue - - try: - self.last_status = console.unit.get_status() - except Exception: - continue # we should emit warning or error here! - - for callback in self.callbacks: - callback.call(self.fetch_status_values(**callback.args)) + for obj in self.callbacks: + value = self.console.status.field(obj.status_quadlet, + **obj.status_bits, + ) + obj.status_callback(value) time.sleep(self.interval) @@ -111,19 +95,29 @@ class Console(): exit() self.state = {} - self.strips = strips.init_strips(self.unit) + self.current_bank = 1 + self.more_banks_up = False + self.more_banks_down = False + self.strips = strips.init_strips(self) self.buttons = fw_1884_buttons.init_buttons(self) self.unit.connect('control', self.handle_control) status_thread = RunningStatusThread(self) # noqa F841 + @property + def status(self): + try: + return ConsoleStatus(self.unit.get_status()) + except Exception as e: + raise e + def handle_bit_flags(self, index, before, after): changed = before ^ after for bit in [i for i, b in enumerate(bits32(changed)) if int(b)]: - high = check_bitR2L(after, bit) + high = bool(after & (0b1 << bit)) button = self.buttons[index][int(bit)] if button is None: print('unhandled control bit {}:{}'.format(index, bit)) @@ -146,6 +140,106 @@ class Console(): self.handle_encoder(index, before, after) return + def strip_fader_handler(self, addr, args, ssid, pos): + print('fader_handler', addr, ssid, pos) + strip = self.strips(int(ssid)) + if strip.name == ' ': + return + pos = pos * 1023 + strip.fader.position = pos + + def master_fader_handler(self, addr, args, pos): + print('master_fader_handler', pos) + self.strips[0].fader.position(1023 * pos) + + def default_handler(self, addr, *args): + print(addr, args) + + def strip_mute_handler(self, addr, args, ssid, state): + strip = self.strips(int(ssid)) + print('mute_handler', strip, state) + if strip.name == ' ': + return + if state: + strip.mute = True + else: + strip.mute = False + + def strip_solo_handler(self, addr, args, ssid, state): + strip = self.strips(int(ssid)) + print('solo_handler', strip, state) + if strip.name == ' ': + return + if state: + strip.solo = True + else: + strip.solo = False + + def strip_recenable_handler(self, addr, args, ssid, state): + strip = self.strips(int(ssid)) + print('recenable_handler', strip, state) + if strip.name == ' ': + return + if state: + strip.recenable = True + else: + strip.recenable = False + + def loop_toggle_handler(self, addr, state): + print(addr, state) + if state: + self.state['LOOP'] = 1 + self.unit.leds.loop.turn_on() + else: + self.state['LOOP'] = 0 + self.unit.leds.loop.turn_off() + + def transport_stop_handler(self, addr, args, state): + print(addr, args, state) + if state: + self.unit.leds.stop.turn_on() + else: + self.unit.leds.stop.turn_off() + + def transport_play_handler(self, addr, args, state): + print(addr, args, state) + if state: + self.unit.leds.play.turn_on() + else: + self.unit.leds.play.turn_off() + + def ffwd_handler(self, addr, args, state): + if state: + self.unit.leds.f_fwd.turn_on() + else: + self.unit.leds.f_fwd.turn_off() + + def rewind_handler(self, addr, args, state): + if state: + self.unit.leds.rew.turn_on() + else: + self.unit.leds.rew.turn_off() + + def rec_enable_toggle_handler(self, addr, args, state): + if state: + self.unit.leds.rec.turn_on() + else: + self.unit.leds.rec.turn_off() + + def pan_stereo_position_handler(self, addr, pos, pan): + self.strips[pos].recv_pan(pan) + + def strip_name_handler(self, addr, ssid, name): + self.strips[int(ssid)].name = name + + def bank_up_handler(self, addr, more_up): + print(addr, more_up) + self.more_banks_up = bool(more_up) + + def bank_down_handler(self, addr, more_down): + print(addr, more_down) + self.more_banks_down = bool(more_down) + def _check_hexadecimal(literal): if literal.find('0x') == 0: @@ -200,3 +294,8 @@ if __name__ == "__main__": args = parser.parse_args() console = Console(args.card) + osc.init_client(args.ip, args.port) + osc.init_server(args.listen_ip, args.listen_port, console) + + print("Serving on {}".format(osc.server.server_address)) + osc.server.serve_forever() @@ -21,7 +21,19 @@ """ from buttons import Button -from osc import osc +import osc + +status_quadlets = (4, 0, 0, 1, 1, 2, 2, 3, 3) +status_bits = ((1, 16), + (1, 16), + (17, 32), + (1, 16), + (17, 32), + (1, 16), + (17, 32), + (1, 16), + (17, 32), + ) class Fader(Button): @@ -31,20 +43,21 @@ class Fader(Button): self.strip = strip self.pos = 0 self.addr = '/strip/fader' - self.console_callback = self.send_pos - - @position.setter - def position(self, pos): - self.pos = pos - self.console.unit.strips - + self.status_quadlet = status_quadlets[self.strip.num] + self.status_bits = status_bits[self.strip.num] + self.status_callback = self.send_pos @property def position(self): return self.pos + @position.setter + def position(self, pos): + self.pos = pos + self.console.unit.strips[self.strip.num].set_position(pos) + def send_pos(self): - osc.send_message(self.addr, (self.strip, self.pos/1023)) + osc.client.sent_message(self.addr, (self.strip, self.pos/1023)) def handle_console_status(self, pos): if pos != self.pos: @@ -59,32 +72,3 @@ class Fader(Button): def release(self): self.console.status_thread.remove_callback(self) super().press() - - - - -def strip_fader_handler(addr, args, ssid, pos): - unit = args[0] - strip = int(ssid) - print('fader_handler', addr, ssid, pos) - pos = pos * 1023 - if strip_states[strip].name != ' ': - unit.strips[strip].fader.set_position(pos) - strip_states[strip].fader = pos - - strips[strip].fader.set_position(pos) - - - unit = args[0] - if state: - unit.leds.rew.turn_on() - else: - unit.leds.rew.turn_off() - - - -def master_fader_handler(addr, args, pos): - unit = args[0] - print('master_fader_handler', pos) - unit.master_fader.set_position(1023 * pos) - diff --git a/fw-1884-buttons.py b/fw_1884_buttons.py index b9486d5..e1ed548 100644 --- a/fw-1884-buttons.py +++ b/fw_1884_buttons.py @@ -20,7 +20,7 @@ :license: GPL-2.0, see COPYING for details """ -import buttons.py +import buttons def init_buttons(console): @@ -133,16 +133,16 @@ def init_buttons(console): buttons.Button(unit, 'UNDO'), buttons.ModButton(unit, 'CTRL'), buttons.Button(unit, 'Foot Switch'), - buttons.Button(unit, 'Gain Enc A', None), - buttons.Button(unit, 'Gain Enc B', None), - buttons.Button(unit, 'Q Enc A', None), - buttons.Button(unit, 'Q Enc B', None), + buttons.Button(unit, 'Gain Enc A'), + buttons.Button(unit, 'Gain Enc B'), + buttons.Button(unit, 'Q Enc A'), + buttons.Button(unit, 'Q Enc B'), None, None, None, None, - buttons.Button(unit, 'Freq Enc A', None), - buttons.Button(unit, 'Freq Enc B', None), + buttons.Button(unit, 'Freq Enc A'), + buttons.Button(unit, 'Freq Enc B'), None, None, None, @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +""" + Open Sound Control send/recieve daemon for Tascam Firewire control surface + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright (c) 2012-2014 Scott Bahling + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program (see the file COPYING); if not, write to the + Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + :license: GPL-2.0, see COPYING for details +""" + +from pythonosc import dispatcher +from pythonosc import osc_server +from pythonosc import udp_client + +client = None +server = None +dispatcher = dispatcher.Dispatcher() + + +def init_dispatcher(console): + dispatcher.map("/loop_toggle", console.loop_toggle_handler) + dispatcher.map("/transport_stop", console.transport_stop_handler) + dispatcher.map("/transport_play", console.transport_play_handler) + dispatcher.map("/ffwd", console.ffwd_handler) + dispatcher.map("/rewind", console.rewind_handler) + dispatcher.map("/rec_enable_toggle", console.rec_enable_toggle_handler) + dispatcher.map('/strip/fader', console.strip_fader_handler) + dispatcher.map('/strip/mute', console.strip_mute_handler) + dispatcher.map('/strip/solo', console.strip_solo_handler) + dispatcher.map('/strip/recenable', console.strip_recenable_handler) + dispatcher.map('/master/fader', console.master_fader_handler) + dispatcher.map('/strip/pan_stereo_position', + console.pan_stereo_position_handler) + dispatcher.map('/strip/name', console.strip_name_handler) + dispatcher.map('/bank_up', console.bank_up_handler) + dispatcher.map('/bank_down', console.bank_down_handler) + dispatcher.set_default_handler(console.default_handler) + + +def init_client(ip, port): + global client + client = udp_client.SimpleUDPClient(ip, port) + + +def init_server(ip, port, console): + global server + init_dispatcher(console) + server = osc_server.ThreadingOSCUDPServer((ip, port), dispatcher) @@ -21,19 +21,93 @@ """ from faders import Fader +import osc + class Strip(): - def __init__(self, unit, num): + def __init__(self, console, num): + self.console = console self.num = 0 - self.mute = None - self.solo = None - self.select = None - self.recenable = False + self.mute_button = None + self._mute = False + self.solo_button = None + self._solo = False + self.select_button = None + self._select = False + self._rec = False self.pan = 0.5 - self.fader = Fader(unit, self) + self.fader = Fader(self.console.unit, self) self.name = None + @property + def mute_led(self): + return self.console.unit.strips[self.num].mute_led + + @property + def solo_led(self): + return self.console.unit.strips[self.num].solo_led + + @property + def sel_led(self): + return self.console.unit.strips[self.num].sel_led + + @property + def rec_led(self): + return self.console.unit.strips[self.num].rec_led + + @property + def mute(self): + return self._mute + + @mute.setter + def mute(self, state): + state = bool(state) + self._mute = state + if state: + self.mute_led.turn_on() + else: + self.mute_led.turn_off() + + @property + def solo(self): + return self._solo + + @solo.setter + def solo(self, state): + state = bool(state) + self._solo = state + if state: + self.solo_led.turn_on() + else: + self.solo_led.turn_off() + + @property + def select(self): + return self._select + + @select.setter + def select(self, state): + state = bool(state) + self._select = state + if state: + self.sel_led.turn_on() + else: + self.sel_led.turn_off() + + @property + def rec(self): + return self._rec + + @rec.setter + def rec(self, state): + state = bool(state) + self._rec = state + if state: + self.rec_led.turn_on() + else: + self.rec_led.turn_off() + def send_pan(self, delta): addr = '/strip/pan_stereo_position' pan = self.pan + delta * 0.02 @@ -41,7 +115,7 @@ class Strip(): pan = 0 if pan > 1.0: pan = 1.0 - osc.send_message(addr, (self.num, pan)) + osc.client.send_message(addr, (self.num, pan)) def recv_pan(self, pan): print('Pan: {} {}'.format(self.num, pan)) |
