summaryrefslogtreecommitdiff
path: root/tascam_fw_console
diff options
context:
space:
mode:
authorsbahling <sbahling@mudgum.net>2018-10-26 14:23:08 +0200
committersbahling <sbahling@mudgum.net>2018-10-26 14:23:08 +0200
commit1d1b23e5154e093a3e3e3e40b05946ab06c9738c (patch)
treea57773bf632c21cecc6427619ec23fd8c88d8b5d /tascam_fw_console
parent38c4e8310ef6bde30d3628ee93103c631caed47b (diff)
downloadtascam-fw-osc-1d1b23e5154e093a3e3e3e40b05946ab06c9738c.tar.gz
tascam-fw-osc-1d1b23e5154e093a3e3e3e40b05946ab06c9738c.tar.xz
tascam-fw-osc-1d1b23e5154e093a3e3e3e40b05946ab06c9738c.zip
Create initial package setup with setuptools
Diffstat (limited to 'tascam_fw_console')
-rw-r--r--tascam_fw_console/__init__.py0
-rw-r--r--tascam_fw_console/buttons.py252
-rw-r--r--tascam_fw_console/cli.py60
-rw-r--r--tascam_fw_console/console.py340
-rw-r--r--tascam_fw_console/faders.py67
-rw-r--r--tascam_fw_console/fw_1884_buttons.py193
-rw-r--r--tascam_fw_console/osc.py60
-rw-r--r--tascam_fw_console/strips.py130
8 files changed, 1102 insertions, 0 deletions
diff --git a/tascam_fw_console/__init__.py b/tascam_fw_console/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tascam_fw_console/__init__.py
diff --git a/tascam_fw_console/buttons.py b/tascam_fw_console/buttons.py
new file mode 100644
index 0000000..c818760
--- /dev/null
+++ b/tascam_fw_console/buttons.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python3
+"""
+ Open Sound Control send/recieve daemon for Tascam Firewire control surface
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright (c) 2018 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
+"""
+
+import re
+import pyautogui
+from tascam_fw_console import osc
+
+
+keymap = {'CTRL': 'ctrl',
+ 'ALT': 'alt',
+ 'SHIFT': 'shift',
+ }
+
+
+osc_addrs = {'STOP': '/transport_stop',
+ 'PLAY': '/transport_play',
+ 'F.FWD': '/ffwd',
+ 'REW': '/rewind',
+ 'REC': '/rec_enable_toggle',
+ }
+
+re_strip_num = re.compile('.*([1-9])')
+
+current_bank = 1
+more_banks_up = False
+more_banks_down = False
+
+
+class Button():
+ def __init__(self, console, name):
+ self.name = name
+ self.state = 0
+ self.console = console
+
+ def press(self):
+ self.state = 1
+ print('%s pressed' % self.name)
+
+ def release(self):
+ self.state = 0
+ print('%s released' % self.name)
+
+ @property
+ def pressed(self):
+ if self.state:
+ return True
+ return False
+
+
+class EncoderButton(Button):
+ def __init__(self, console, name):
+ super().__init__(console, name)
+
+ def press(self):
+
+ encoder_mode = self.console.state.get('encoder_mode', None)
+ print(encoder_mode)
+ if encoder_mode == self.name:
+ return
+
+ self.state = 1
+ if encoder_mode:
+ self.console.unit.leds.turn_off(encoder_mode)
+ self.console.state[encoder_mode] = 0
+ self.console.unit.leds.turn_on(self.name)
+ self.console.state['encoder_mode'] = self.name
+ super().press()
+
+
+class StripSelButton(Button):
+ def __init__(self, console, name, strip):
+ super().__init__(console, name)
+ self.strip = strip
+ self.strip.select_button = self
+
+ def press(self):
+
+ strip = self.strip
+ print('handle_strip_sel', strip)
+ recenable = self.console.buttons.get('REC ENABLE', None)
+ if recenable.pressed:
+ if strip.rec:
+ osc.client.send_message('/strip/recenable', (strip.num, 0))
+ else:
+ osc.client.send_message('/strip/recenable', (strip.num, 1))
+ super().press()
+
+
+class StripMuteButton(Button):
+ def __init__(self, console, name, strip):
+ super().__init__(console, name)
+ self.strip = strip
+ self.strip.mute_button = self
+
+ def press(self):
+
+ strip = self.strip
+ if strip.mute:
+ strip.mute = False
+ osc.client.send_message('/strip/mute', (strip.num, 0))
+ else:
+ strip.mute = True
+ osc.client.send_message('/strip/mute', (strip.num, 1))
+ super().press()
+
+
+class StripSoloButton(Button):
+ def __init__(self, console, name, strip):
+ super().__init__(console, name)
+ self.strip = strip
+ self.strip.solo_button = self
+
+ def press(self):
+
+ strip = self.strip
+ if strip.solo:
+ strip.solo = False
+ osc.client.send_message('/strip/solo', (strip.num, 0))
+ else:
+ strip.solo = True
+ osc.client.send_message('/strip/solo', (strip.num, 1))
+ super().press()
+
+
+class ArrowButton(Button):
+ def __init__(self, console, name):
+ super().__init__(console, name)
+
+ def press(self):
+ key = keymap.get(self.name.lower(), self.name.lower())
+ pyautogui.press(key)
+
+
+class ModButton(Button):
+ def __init__(self, console, name):
+ super().__init__(console, name)
+
+ def mod_button_press(self):
+ key = keymap.get(self.name, None)
+ if key is None:
+ return
+
+ pyautogui.keyDown(key)
+ super().press()
+
+ def release(self):
+ key = keymap.get(self.name, None)
+ if key is None:
+ return
+
+ pyautogui.keyUp(key)
+ super().release()
+
+
+class ComputerButton(Button):
+ def __init__(self, console, name):
+ super().__init__(console, name)
+
+ def press(self):
+ osc.client.send_message('/set_surface', (8, 7, 19, 1, 8, 11))
+ super().press()
+
+
+class ClrSoloButton(Button):
+ def __init__(self, console, name):
+ super().__init__(console, name)
+
+ def press(self):
+ osc.client.send_message('/cancel_all_solos', 1)
+ super().press()
+
+
+class LoopButton(Button):
+ def __init__(self, console, name):
+ super().__init__(console, name)
+
+ def press(self, *args):
+ if self.console.state.get('loop', 0):
+ print('******* loop off')
+ osc.client.send_message('/loop_toggle', 0)
+ else:
+ print('******* loop on')
+ osc.client.send_message('/loop_toggle', 1)
+ super().press()
+
+
+class TransportButton(Button):
+ def __init__(self, console, name):
+ super().__init__(console, name)
+
+ def press(self):
+ addr = osc_addrs.get(self.name, None)
+ if addr:
+ osc.client.send_message(addr, 1)
+ super().press()
+
+
+class BankSwitchButton(Button):
+ def __init__(self, console, name, direction):
+ super().__init__(console, name)
+ self.direction = direction
+
+ def press(self):
+
+ direction = self.direction
+ print(direction, more_banks_up, more_banks_down)
+ if direction > 0 and more_banks_up:
+ print('calling /bank_up 1')
+ osc.client.send_message('/bank_up', 1)
+ elif direction < 0 and more_banks_down:
+ print('calling /bank_down 1')
+ osc.client.send_message('/bank_down', 1)
+ super().press()
+
+
+class NudgeButton(Button):
+ def __init__(self, console, name, direction):
+ super().__init__(console, name)
+ self.direction = direction
+
+ def nudge_press(self):
+ direction = self.direction
+ print(direction)
+ if direction > 0:
+ osc.client.send_message('/access_action',
+ 'Common/nudge-next-forward')
+ else:
+ osc.client.send_message('/access_action',
+ 'Common/nudge-next-backward')
+ super().press()
+
+
+NULLBUTTON = Button(None, None)
diff --git a/tascam_fw_console/cli.py b/tascam_fw_console/cli.py
new file mode 100644
index 0000000..401a953
--- /dev/null
+++ b/tascam_fw_console/cli.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+"""
+ Open Sound Control send/recieve daemon for Tascam Firewire control surface
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright (c) 2018 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
+
+Usage:
+ tascam-fw-osc [options]
+
+Options:
+ -h --help Show this screen.
+ -l --list List connected Tascam FW console units
+ -c CARD_NUM --card=CARD_NUM Number of the ALSA sound card
+ -d GUID --device=GUID GUID of the FW unit
+ -I IP --listen-ip=IP IP to listen on [default: 127.0.0.1]
+ -P PORT --listen-port=PORT Port to listen on [default: 5005]
+ -i IP --ip=IP IP to communicate to [default: 127.0.0.1]
+ -p PORT --port=PORT Port to communicate to [default: 3919]
+"""
+
+from docopt import docopt
+from tascam_fw_console.console import Console, list_units
+from tascam_fw_console import osc
+
+
+def main():
+
+ args = docopt(__doc__, version='0.1')
+
+ if args['--list']:
+ for model, fullpath, guid in list_units():
+ print('{0} path: {1} GUID: 0x{2:016x}'.format(model,
+ fullpath, guid))
+ exit()
+
+ console = Console(card_id=args['--card'], guid=args['--device'])
+ osc.init_client(args['--ip'], int(args['--port']))
+ osc.init_server(args['--listen-ip'], int(args['--listen-port']), console)
+
+ print("Serving on {}".format(osc.server.server_address))
+ osc.server.serve_forever()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tascam_fw_console/console.py b/tascam_fw_console/console.py
new file mode 100644
index 0000000..103f755
--- /dev/null
+++ b/tascam_fw_console/console.py
@@ -0,0 +1,340 @@
+#!/usr/bin/env python3
+"""
+ Open Sound Control send/recieve daemon for Tascam Firewire control surface
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright (c) 2018 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
+"""
+
+import string
+import time
+import threading
+from pathlib import Path
+
+from tascam_fw_console import strips
+from tascam_fw_console import fw_1884_buttons
+
+import gi
+
+gi.require_version('Hinawa', '2.0')
+from gi.repository import Hinawa # noqa: E402
+from hinawa_utils.tscm.config_rom_parser import TscmConfigRomParser # noqa: E402 E501
+from hinawa_utils.tscm.tscm_console_unit import TscmConsoleUnit # noqa: E402
+
+
+bits32 = '{:032b}'.format
+
+
+class ConsoleStatus():
+ def __init__(self, quadlets):
+ self.quadlets = quadlets
+
+ 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-1:last_bit][::-1], 2)
+
+
+class RunningStatusThread():
+ def __init__(self, console, interval=0.1):
+ """ Constructor
+ :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)
+ thread.daemon = True # Daemonize thread
+ thread.start() # Start the execution
+
+ def add_callback(self, obj):
+ self.callbacks.add(obj)
+
+ def remove_callback(self, obj):
+ self.callbacks.remove(obj)
+
+ def run(self):
+ """ Method that runs forever """
+ while True:
+ for obj in self.callbacks:
+ value = self.console.status.field(obj.status_quadlet,
+ *obj.status_bits,
+ )
+ obj.status_callback(value)
+
+ time.sleep(self.interval)
+
+
+class Console():
+ def __init__(self, card_id=None, guid=None):
+
+ fullpath = None
+ if guid:
+ fullpath = self._seek_snd_unit_from_guid(card_id)
+ elif card_id:
+ fullpath = '/dev/snd/hwC{0}D0'.format(card_id)
+ else:
+ try:
+ units = list_units()
+ if units:
+ model, fullpath, guid = units[0]
+ except Exception:
+ raise Exception('No Tascam FW Console unit found')
+
+ if fullpath:
+ self.unit = TscmConsoleUnit(fullpath)
+ model = self.unit.model_name
+ guid = self.unit.get_property('guid')
+ print('Found Tascam {0} unit with GUID: {1:016x}'.format(model, guid)) # noqa E501
+ else:
+ raise Exception('No Tascam FW Console unit found')
+
+ self.state = {}
+ self.current_bank = 1
+ self.more_banks_up = False
+ self.more_banks_down = False
+ self.strips = strips.init_strips(self)
+ self.buttons = {}
+ self.init_buttons()
+
+ self.unit.connect('control', self.handle_control)
+
+ self.status_thread = RunningStatusThread(self) # noqa F841
+
+ def _seek_snd_unit_from_guid(self, guid):
+ for fullpath in Path('/dev/snd').glob('hw*'):
+ fullpath = str(fullpath)
+ try:
+ unit = Hinawa.SndUnit()
+ unit.open(fullpath)
+ if unit.get_property('guid') == guid:
+ return fullpath
+ except Exception as e:
+ pass
+ finally:
+ del unit
+ return None
+
+ def init_buttons(self):
+ self.button_map = fw_1884_buttons.init_buttons(self)
+ for index, items in self.button_map.items():
+ for item in items:
+ if item is None:
+ continue
+ self.buttons[item.name] = item
+
+ @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
+
+ bits = reversed(bits32(changed))
+ for bit in [i for i, b in enumerate(bits) if int(b)]:
+ high = bool(after & (0b1 << bit))
+ button = self.button_map[index][int(bit)]
+ if button is None:
+ print('unhandled control bit {}:{}'.format(index, bit))
+ continue
+
+ if high:
+ button.release()
+ else:
+ button.press()
+
+ def handle_encoder(self, index, before, after):
+ strip1 = (index * 2 - 19)
+ strip2 = (index * 2 - 18)
+ bval1 = before & 0xffff
+ bval2 = before >> 0x10
+ aval1 = after & 0xffff
+ aval2 = after >> 0x10
+
+ delta1 = roll_over_delta(aval1 - bval1)
+ delta2 = roll_over_delta(aval2 - bval2)
+
+ if delta1:
+ if self.state.get('encoder_mode', '') == 'PAN':
+ self.strips[strip1].send_pan(delta1)
+
+ if delta2:
+ if self.state.get('encoder_mode', '') == 'PAN':
+ self.strips[strip2].send_pan(delta2)
+
+ def handle_control(self, unit, index, before, after):
+
+ print('{0:02d}: {1:08x} {2:08x}'.format(index, before, after))
+
+ if index in [5, 6, 7, 8, 9]:
+ self.handle_bit_flags(index, before, after)
+ return
+
+ if index in [10, 11, 12, 13, 14, 15]:
+ self.handle_encoder(index, before, after)
+ return
+
+ def strip_fader_handler(self, addr, 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, 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, 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, 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, ssid, state):
+ strip = self.strips[int(ssid)]
+ print('recenable_handler', strip, state)
+ if strip.name == ' ':
+ return
+ if state:
+ strip.rec = True
+ else:
+ strip.rec = 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, state):
+ print(addr, state)
+ if state:
+ self.unit.leds.stop.turn_on()
+ else:
+ self.unit.leds.stop.turn_off()
+
+ def transport_play_handler(self, addr, state):
+ print(addr, state)
+ if state:
+ self.unit.leds.play.turn_on()
+ else:
+ self.unit.leds.play.turn_off()
+
+ def ffwd_handler(self, addr, state):
+ if state:
+ self.unit.leds.f_fwd.turn_on()
+ else:
+ self.unit.leds.f_fwd.turn_off()
+
+ def rewind_handler(self, addr, state):
+ if state:
+ self.unit.leds.rew.turn_on()
+ else:
+ self.unit.leds.rew.turn_off()
+
+ def rec_enable_toggle_handler(self, addr, 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 roll_over_delta(delta, ceiling=0xffff):
+
+ print(delta)
+ if delta > ceiling - 10:
+ return delta - (ceiling + 1)
+ if delta < -ceiling + 10:
+ return delta + (ceiling + 1)
+ return delta
+
+
+def _check_hexadecimal(literal):
+ if literal.find('0x') == 0:
+ literal = literal[2:]
+ if len(literal) != 16:
+ return False
+ for character in literal:
+ if character not in string.hexdigits:
+ return False
+ else:
+ return True
+
+
+def list_units():
+ units = []
+ for fullpath in Path('/dev/snd').glob('hw*'):
+ fullpath = str(fullpath)
+ unit = None
+ try:
+ unit = Hinawa.SndUnit()
+ unit.open(fullpath)
+ parser = TscmConfigRomParser()
+ info = parser.parse_rom(unit.get_config_rom())
+ model = info['model-name']
+ guid = unit.get_property('guid')
+ if model in ('FW-1082', 'FW-1884'):
+ units.append((model, fullpath, guid))
+ except Exception as e:
+ pass
+
+ return units
diff --git a/tascam_fw_console/faders.py b/tascam_fw_console/faders.py
new file mode 100644
index 0000000..c9145a2
--- /dev/null
+++ b/tascam_fw_console/faders.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+"""
+ Open Sound Control send/recieve daemon for Tascam Firewire control surface
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright (c) 2018 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 tascam_fw_console.buttons import Button
+from tascam_fw_console 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):
+ def __init__(self, console, strip):
+ name = 'Strip {} Fader'. format(strip.num)
+ super().__init__(console, name)
+ self.strip = strip
+ self.addr = '/strip/fader'
+ 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.console.status.field(self.status_quadlet, self.status_bits)
+
+ @position.setter
+ def position(self, pos):
+ self.console.unit.strips[self.strip.num].fader.set_position(pos)
+
+ def send_pos(self, pos):
+ print('{}, ({}, {})'.format(self.addr, self.strip.num, pos/1023))
+ osc.client.send_message(self.addr, (self.strip.num, pos/1023))
+
+ def press(self):
+ self.console.status_thread.add_callback(self)
+ super().press()
+
+ def release(self):
+ self.console.status_thread.remove_callback(self)
+ super().press()
diff --git a/tascam_fw_console/fw_1884_buttons.py b/tascam_fw_console/fw_1884_buttons.py
new file mode 100644
index 0000000..44f3b60
--- /dev/null
+++ b/tascam_fw_console/fw_1884_buttons.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+"""
+ Open Sound Control send/recieve daemon for Tascam Firewire control surface
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright (c) 2018 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 tascam_fw_console import buttons
+
+
+def init_buttons(console):
+ strips = console.strips
+ return {5: [None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ strips[1].fader,
+ strips[2].fader,
+ strips[3].fader,
+ strips[4].fader,
+ strips[5].fader,
+ strips[6].fader,
+ strips[7].fader,
+ strips[8].fader,
+ strips[0].fader,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ ],
+ 6: [None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ buttons.StripSelButton(console, 'Strip 1 Sel', strips[1]),
+ buttons.StripSelButton(console, 'Strip 2 Sel', strips[2]),
+ buttons.StripSelButton(console, 'Strip 3 Sel', strips[3]),
+ buttons.StripSelButton(console, 'Strip 4 Sel', strips[4]),
+ buttons.StripSelButton(console, 'Strip 5 Sel', strips[5]),
+ buttons.StripSelButton(console, 'Strip 6 Sel', strips[6]),
+ buttons.StripSelButton(console, 'Strip 7 Sel', strips[7]),
+ buttons.StripSelButton(console, 'Strip 8 Sel', strips[8]),
+ buttons.StripSoloButton(console, 'Strip 1 Solo', strips[1]),
+ buttons.StripSoloButton(console, 'Strip 2 Solo', strips[2]),
+ buttons.StripSoloButton(console, 'Strip 3 Solo', strips[3]),
+ buttons.StripSoloButton(console, 'Strip 4 Solo', strips[4]),
+ buttons.StripSoloButton(console, 'Strip 5 Solo', strips[5]),
+ buttons.StripSoloButton(console, 'Strip 6 Solo', strips[6]),
+ buttons.StripSoloButton(console, 'Strip 7 Solo', strips[7]),
+ buttons.StripSoloButton(console, 'Strip 8 Solo', strips[8]),
+ ],
+ 7: [buttons.StripMuteButton(console, 'Strip 1 Mute', strips[1]),
+ buttons.StripMuteButton(console, 'Strip 2 Mute', strips[2]),
+ buttons.StripMuteButton(console, 'Strip 3 Mute', strips[3]),
+ buttons.StripMuteButton(console, 'Strip 4 Mute', strips[4]),
+ buttons.StripMuteButton(console, 'Strip 5 Mute', strips[5]),
+ buttons.StripMuteButton(console, 'Strip 6 Mute', strips[6]),
+ buttons.StripMuteButton(console, 'Strip 7 Mute', strips[7]),
+ buttons.StripMuteButton(console, 'Strip 8 Mute', strips[8]),
+ buttons.EncoderButton(console, 'AUX5'),
+ buttons.EncoderButton(console, 'AUX7'),
+ buttons.EncoderButton(console, 'AUX6'),
+ buttons.EncoderButton(console, 'AUX8'),
+ None,
+ None,
+ None,
+ None,
+ buttons.Button(console, 'FLIP'),
+ buttons.EncoderButton(console, 'AUX1'),
+ buttons.EncoderButton(console, 'AUX3'),
+ buttons.EncoderButton(console, 'PAN'),
+ buttons.EncoderButton(console, 'AUX2'),
+ buttons.EncoderButton(console, 'AUX4'),
+ None,
+ None,
+ buttons.Button(console, 'Control Panel'),
+ buttons.Button(console, 'F1'),
+ buttons.Button(console, 'ALL SAFE'),
+ buttons.Button(console, 'F5'),
+ buttons.Button(console, 'CUT'),
+ buttons.Button(console, 'COPY'),
+ buttons.ModButton(console, 'ALT'),
+ buttons.ModButton(console, 'SHIFT'),
+ ],
+ 8: [buttons.Button(console, 'F2'),
+ buttons.ClrSoloButton(console, 'CLR SOLO'),
+ buttons.LoopButton(console, 'LOOP'),
+ buttons.Button(console, 'DEL'),
+ buttons.Button(console, 'PASTE'),
+ buttons.Button(console, 'UNDO'),
+ buttons.ModButton(console, 'CTRL'),
+ buttons.Button(console, 'Foot Switch'),
+ buttons.Button(console, 'Gain Enc A'),
+ buttons.Button(console, 'Gain Enc B'),
+ buttons.Button(console, 'Q Enc A'),
+ buttons.Button(console, 'Q Enc B'),
+ None,
+ None,
+ None,
+ None,
+ buttons.Button(console, 'Freq Enc A'),
+ buttons.Button(console, 'Freq Enc B'),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ buttons.ComputerButton(console, 'COMPUTER'),
+ None,
+ None,
+ buttons.Button(console, 'CLOCK'),
+ buttons.Button(console, 'ROUTE'),
+ None,
+ None,
+ ],
+ 9: [buttons.Button(console, 'F7'),
+ buttons.Button(console, 'F8'),
+ buttons.Button(console, 'F9'),
+ buttons.Button(console, 'F10'),
+ buttons.Button(console, 'READ'),
+ buttons.Button(console, 'WRT'),
+ buttons.Button(console, 'TCH'),
+ buttons.Button(console, 'LATCH'),
+ buttons.Button(console, 'HIGH'),
+ buttons.Button(console, 'HI-MID'),
+ buttons.Button(console, 'LOW-MID'),
+ buttons.Button(console, 'LOW'),
+ buttons.ArrowButton(console, 'UP'),
+ buttons.ArrowButton(console, 'LEFT'),
+ buttons.ArrowButton(console, 'DOWN'),
+ buttons.ArrowButton(console, 'RIGHT'),
+ buttons.Button(console, 'REC ENABLE'),
+ buttons.NudgeButton(console, 'NUDGE LEFT', -1),
+ buttons.NudgeButton(console, 'NUDGE RIGHT', 1),
+ buttons.BankSwitchButton(console, 'BANK LEFT', -1),
+ buttons.BankSwitchButton(console, 'BANK RIGHT', 1),
+ buttons.Button(console, 'LOCATE LEFT'),
+ buttons.Button(console, 'LOCATE RIGHT'),
+ buttons.Button(console, 'SHTL'),
+ buttons.Button(console, 'SET'),
+ buttons.Button(console, 'IN'),
+ buttons.Button(console, 'OUT'),
+ buttons.TransportButton(console, 'REW'),
+ buttons.TransportButton(console, 'F.FWD'),
+ buttons.TransportButton(console, 'STOP'),
+ buttons.TransportButton(console, 'PLAY'),
+ buttons.TransportButton(console, 'REC'),
+ ]
+ }
diff --git a/tascam_fw_console/osc.py b/tascam_fw_console/osc.py
new file mode 100644
index 0000000..fb17449
--- /dev/null
+++ b/tascam_fw_console/osc.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+"""
+ Open Sound Control send/recieve daemon for Tascam Firewire control surface
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright (c) 2018 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)
diff --git a/tascam_fw_console/strips.py b/tascam_fw_console/strips.py
new file mode 100644
index 0000000..8d195f8
--- /dev/null
+++ b/tascam_fw_console/strips.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+"""
+ Open Sound Control send/recieve daemon for Tascam Firewire control surface
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright (c) 2018 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 tascam_fw_console.faders import Fader
+from tascam_fw_console import osc
+
+
+class Strip():
+
+ def __init__(self, console, num):
+ self.console = console
+ self.num = num
+ 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(self.console, 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
+ if pan < 0:
+ pan = 0
+ if pan > 1.0:
+ pan = 1.0
+ osc.client.send_message(addr, (self.num, pan))
+
+ def recv_pan(self, pan):
+ print('Pan: {} {}'.format(self.num, pan))
+ self.pan = pan
+
+
+def init_strips(unit):
+ strips = []
+ for strip_num in range(0, 9):
+ strips.append(Strip(unit, strip_num))
+
+ return strips