├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── excel_logger.py ├── helix_usb.py ├── modes ├── __init__.py ├── connect.py ├── reconfigure_x1.py ├── request_preset.py ├── request_preset_name.py ├── request_preset_names.py └── standard.py ├── modules.py ├── out_packet.py └── utils ├── __init__.py ├── formatter.py ├── ieee754_convert.py ├── packetdump_parser.py ├── pcapng_to_xlsx.py ├── preset_reader.py ├── presets.py ├── simple_filter.py ├── usb_helper.py ├── usb_monitor.py └── xls_to_py.py /.gitignore: -------------------------------------------------------------------------------- 1 | # These are some examples of commonly ignored file patterns. 2 | # You should customize this list as applicable to your project. 3 | # Learn more about .gitignore: 4 | # https://www.atlassian.com/git/tutorials/saving-changes/gitignore 5 | 6 | # Node artifact files 7 | node_modules/ 8 | dist/ 9 | 10 | # virtual environments 11 | venv/ 12 | 13 | # Compiled Java class files 14 | *.class 15 | 16 | # Compiled Python bytecode 17 | *.py[cod] 18 | 19 | # Log files 20 | *.log 21 | 22 | # Package files 23 | *.jar 24 | 25 | # Maven 26 | target/ 27 | dist/ 28 | 29 | # JetBrains IDE 30 | .idea/ 31 | 32 | # Unit test reports 33 | TEST*.xml 34 | 35 | # Generated by MacOS 36 | .DS_Store 37 | 38 | # Generated by Windows 39 | Thumbs.db 40 | 41 | # Applications 42 | *.app 43 | *.exe 44 | *.war 45 | 46 | # Large media files 47 | *.mp4 48 | *.tiff 49 | *.avi 50 | *.flv 51 | *.mov 52 | *.wmv 53 | 54 | # excel dump of last helix_ubs session 55 | *.xlsx 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Heiko Sparenberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # helix_usb 2 | helix_usb is a set of python scripts allowing for communications with Line6's HX Stomp (and maybe all other Helix products like the Helix (LT), maybe Pod Go, HX Effects and the rack version) over USB. For this, I kind of reverse engineered the protocol being send between HX Edit and the HX Stomp. 3 | 4 | ## Getting Started 5 | git clone https://github.com/kempline/helix_usb.git 6 | 7 | It is recommended to create a virtual python environment. Please install the following modules: 8 | 9 | * pip install pyusb 10 | * pip install xlsxwriter 11 | 12 | Documentation: https://github.com/kempline/helix_usb/wiki 13 | 14 | ## In Action 15 | An overview of the current features is given in 16 | 17 | [helix_usb - Feature Overview](https://www.youtube.com/watch?v=mRKcDVy7ZhU) 18 | 19 | Admitting that those features seem useless on their own, here's another video showing a typical use-case of helix_usb. 20 | 21 | [Combining helix_usb with a Line6 FBV3](https://www.youtube.com/watch?v=1Qndof3cb20) 22 | 23 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | from .utils.usb_monitor import UsbMonitor 4 | from .modules import modules 5 | cwd = os.getcwd() 6 | helix_usb_path = os.path.join(cwd, 'helix_usb') 7 | sys.path.append(helix_usb_path) 8 | 9 | from .helix_usb import HelixUsb 10 | -------------------------------------------------------------------------------- /excel_logger.py: -------------------------------------------------------------------------------- 1 | import xlsxwriter 2 | from datetime import datetime 3 | import logging 4 | log = logging.getLogger(__name__) 5 | 6 | 7 | class ExcelLogger: 8 | def __init__(self, out_path='./dump.xlsx'): 9 | self.out_path = out_path 10 | self.excel_workbook = xlsxwriter.Workbook(out_path) 11 | 12 | self.format_x1_x10 = self.excel_workbook.add_format() 13 | self.format_x2_x10 = self.excel_workbook.add_format() 14 | self.format_x80_x10 = self.excel_workbook.add_format() 15 | 16 | self.format_x1_x10.set_pattern(1) 17 | self.format_x2_x10.set_pattern(1) 18 | self.format_x80_x10.set_pattern(1) 19 | self.format_x1_x10.set_bg_color('edf0e0') 20 | self.format_x2_x10.set_bg_color('f5c9ce') 21 | self.format_x80_x10.set_bg_color('fbeaa5') 22 | 23 | self.worksheet_all_ports = self.excel_workbook.add_worksheet("all") 24 | self.worksheet_x1x10 = self.excel_workbook.add_worksheet("x1x10") 25 | self.worksheet_x2x10 = self.excel_workbook.add_worksheet("x2x10") 26 | self.worksheet_x80x10 = self.excel_workbook.add_worksheet("x80x10") 27 | 28 | self.xlsx_row_num = 0 29 | self.x1_x10_row_num = 0 30 | self.x2_x10_row_num = 0 31 | self.x80_x10_row_num = 0 32 | 33 | self.session_start_time = datetime.now() 34 | self.packet_count = 0 35 | 36 | def save(self): 37 | self.excel_workbook.close() 38 | self.excel_workbook = None 39 | log.info("Saved Excel sheet at: " + str(self.out_path)) 40 | 41 | def log(self, data): 42 | if self.excel_workbook is None: 43 | return 44 | 45 | time_offset = datetime.now() - self.session_start_time 46 | full_data_str = '' 47 | 48 | for i, d in enumerate(data): 49 | e = hex(d) 50 | if i == 9: 51 | full_data_str += (e + ', ') 52 | else: 53 | full_data_str += (e + ', ') 54 | 55 | if full_data_str.endswith(', '): 56 | full_data_str = full_data_str[:-2] 57 | 58 | # OUT (Host to Device) 59 | if data[4] == 0x1: 60 | 61 | self.worksheet_all_ports.write('A' + str(self.xlsx_row_num), self.packet_count) 62 | self.worksheet_all_ports.write('B' + str(self.xlsx_row_num), str(time_offset)) 63 | self.worksheet_all_ports.write('C' + str(self.xlsx_row_num), full_data_str, self.format_x1_x10) 64 | 65 | self.worksheet_x1x10.write('A' + str(self.x1_x10_row_num), self.packet_count) 66 | self.worksheet_x1x10.write('B' + str(self.x1_x10_row_num), str(time_offset)) 67 | self.worksheet_x1x10.write('C' + str(self.x1_x10_row_num), full_data_str, self.format_x1_x10) 68 | 69 | self.x1_x10_row_num += 1 70 | 71 | elif data[4] == 0x2: 72 | 73 | self.worksheet_all_ports.write('A' + str(self.xlsx_row_num), self.packet_count) 74 | self.worksheet_all_ports.write('B' + str(self.xlsx_row_num), str(time_offset)) 75 | self.worksheet_all_ports.write('C' + str(self.xlsx_row_num), full_data_str, self.format_x2_x10) 76 | 77 | self.worksheet_x2x10.write('A' + str(self.x2_x10_row_num), self.packet_count) 78 | self.worksheet_x2x10.write('B' + str(self.x2_x10_row_num), str(time_offset)) 79 | self.worksheet_x2x10.write('C' + str(self.x2_x10_row_num), full_data_str, self.format_x2_x10) 80 | 81 | self.x2_x10_row_num += 1 82 | 83 | elif data[4] == 0x80: 84 | 85 | self.worksheet_all_ports.write('A' + str(self.xlsx_row_num), self.packet_count) 86 | self.worksheet_all_ports.write('B' + str(self.xlsx_row_num), str(time_offset)) 87 | self.worksheet_all_ports.write('C' + str(self.xlsx_row_num), full_data_str, self.format_x80_x10) 88 | 89 | 90 | self.worksheet_x80x10.write('A' + str(self.x80_x10_row_num), self.packet_count) 91 | self.worksheet_x80x10.write('B' + str(self.x80_x10_row_num), str(time_offset)) 92 | self.worksheet_x80x10.write('C' + str(self.x80_x10_row_num), full_data_str, self.format_x80_x10) 93 | 94 | self.x80_x10_row_num += 1 95 | 96 | # IN (Device to Host) 97 | elif data[6] == 0x1: 98 | self.worksheet_all_ports.write('A' + str(self.xlsx_row_num), self.packet_count) 99 | self.worksheet_all_ports.write('B' + str(self.xlsx_row_num), str(time_offset)) 100 | self.worksheet_all_ports.write('E' + str(self.xlsx_row_num), full_data_str, self.format_x1_x10) 101 | 102 | self.worksheet_x1x10.write('A' + str(self.x1_x10_row_num), self.packet_count) 103 | self.worksheet_x1x10.write('B' + str(self.x1_x10_row_num), str(time_offset)) 104 | self.worksheet_x1x10.write('E' + str(self.x1_x10_row_num), full_data_str, self.format_x1_x10) 105 | 106 | self.x1_x10_row_num += 1 107 | elif data[6] == 0x2: 108 | self.worksheet_all_ports.write('A' + str(self.xlsx_row_num), self.packet_count) 109 | self.worksheet_all_ports.write('B' + str(self.xlsx_row_num), str(time_offset)) 110 | self.worksheet_all_ports.write('E' + str(self.xlsx_row_num), full_data_str, self.format_x2_x10) 111 | 112 | self.worksheet_x2x10.write('A' + str(self.x2_x10_row_num), self.packet_count) 113 | self.worksheet_x2x10.write('B' + str(self.x2_x10_row_num), str(time_offset)) 114 | self.worksheet_x2x10.write('E' + str(self.x2_x10_row_num), full_data_str, self.format_x2_x10) 115 | self.x2_x10_row_num += 1 116 | 117 | elif data[6] == 0x80: 118 | self.worksheet_all_ports.write('A' + str(self.xlsx_row_num), self.packet_count) 119 | self.worksheet_all_ports.write('B' + str(self.xlsx_row_num), str(time_offset)) 120 | self.worksheet_all_ports.write('E' + str(self.xlsx_row_num), full_data_str, self.format_x80_x10) 121 | 122 | 123 | self.worksheet_x80x10.write('A' + str(self.x80_x10_row_num), self.packet_count) 124 | self.worksheet_x80x10.write('B' + str(self.x80_x10_row_num), str(time_offset)) 125 | self.worksheet_x80x10.write('E' + str(self.x80_x10_row_num), full_data_str, self.format_x80_x10) 126 | 127 | self.x80_x10_row_num += 1 128 | 129 | else: 130 | print("WARNING: Unknown communication path in endpoint 0x1") 131 | 132 | self.xlsx_row_num += 1 133 | self.packet_count += 1 134 | 135 | -------------------------------------------------------------------------------- /helix_usb.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import signal 3 | import usb.core 4 | import usb.util 5 | import threading 6 | import time 7 | from utils.usb_monitor import UsbMonitor 8 | import struct 9 | import binascii 10 | from utils.formatter import ca_splitter 11 | from utils.simple_filter import EmptySlotInfo 12 | from excel_logger import ExcelLogger 13 | import logging 14 | import copy 15 | import getopt 16 | from modes.connect import Connect 17 | from modes.reconfigure_x1 import ReconfigureX1 18 | from modes.standard import Standard 19 | from modes.request_preset import RequestPreset 20 | from modes.request_preset_names import RequestPresetNames 21 | from modes.request_preset_name import RequestPresetName 22 | log = logging.getLogger(__name__) 23 | 24 | 25 | class HelixUsb: 26 | GET_STRING_VENDOR = 0x01 27 | GET_STRING_PRODUCT = 0x02 28 | GET_STRING_SERIAL = 0x03 29 | 30 | GET_STRING_APP = 0x04 31 | GET_STRING_VERSION = 0x05 32 | GET_STRING_CPU_ID = 0x06 33 | GET_STRING_MAC_ADDR = 0x07 34 | GET_STRING_VMGR = 0x08 35 | GET_STRING_VDEF = 0x09 36 | GET_STRING_UNDEFINED = 0x0a 37 | 38 | ON = 0x01 39 | OFF = 0x00 40 | 41 | LED_COLORS = [ 42 | 'off', 'white', 'red', 'dark_orange', 'light_orange', 'yellow', 'green', 'turquoise', 'blue', 'violet', 43 | 'pink', 'auto_color'] 44 | 45 | UI_MODES = ['Stomp Mode', 'Scroll Mode', 'Preset Mode', 'Snapshot Mode'] 46 | 47 | FOOT_SWITCH_FUNCTIONS = \ 48 | ["Tap/Tuner", "Stomp3", "PresetUp", "PresetDown", "SnapshotUp", "SnapshotDown", "AllBypass", "ToggleExp"] 49 | 50 | MODULE_COLORS = { 51 | "Distortion": "light_orange", 52 | "Dynamic": "yellow", 53 | "EQ": "yellow", 54 | "Modulation": "blue", 55 | "Delay": "green", 56 | "Reverb": "dark_orange", 57 | "Pitch/Synth": "violet", 58 | "Filter": "violet", 59 | "Wah": "violet", 60 | "Amp+Cab": "red", 61 | "Amp": "red", 62 | "Preamp": "red", 63 | "Cab": "red", 64 | "Impulse Response": "pink", 65 | "Volume/Pan": "turquoise", 66 | "Send/Return": "turquoise", 67 | "Looper": "white" 68 | } 69 | 70 | FOOT_SWITCHES = { 71 | "FS3": 0x61, 72 | "FS4": 0x62, 73 | "FS5": 0x63 74 | } 75 | 76 | VIEWS = { 77 | 194: "Play View", 78 | 195: "Edit View" 79 | } 80 | 81 | MIDI_CC = { 82 | "EmulateFS1": 49, 83 | "EmulateFS2": 50, 84 | "EmulateFS3": 51, 85 | "EmulateFS4": 52, 86 | "EmulateFS5": 53 87 | } 88 | 89 | def __init__(self): 90 | 91 | self.preset_no = 0 92 | self.current_snapshot = 9 93 | self.current_preset_no = -1 94 | 95 | self.usb_device = None 96 | self.active_configuration = None 97 | self.interface = None 98 | self.interface_2_1 = None 99 | self.interface_3_1 = None 100 | self.interface_4 = None 101 | self.endpoint_0x1_bulk_out = None 102 | self.endpoint_0x81_bulk_in = None 103 | self.endpoint_0x2_bulk_out = None 104 | self.endpoint_0x82_bulk_in = None 105 | self.endpoint_0x3_isochronous_out = None 106 | self.endpoint_0x83_isochronous_in = None 107 | 108 | self.usb_io_exception_cb = self.on_usb_io_exception 109 | 110 | self.maybe_session_no = 0x1a 111 | self.preset_data_double_cnt = [0x1e, 0x00] 112 | 113 | self.stop_threads = False 114 | self.x81_reader = None 115 | 116 | self.stop_communication = False 117 | self.stop_x80x10_communication = False 118 | 119 | self.x80x10_keep_alive_thread = None 120 | self.x1x10_keep_alive_thread = None 121 | self.x2x10_keep_alive_thread = None 122 | 123 | self.x1x10_cnt = 0x2 124 | self.x2x10_cnt = 0x2 125 | self.x80x10_cnt = 0x2 126 | 127 | self.expecting_x80_x10_response = False 128 | self.expecting_x1_x10_response = False 129 | self.expecting_x2_x10_response = False 130 | 131 | self.last_x80_x10_keep_alive_out = time.time() 132 | self.last_x2_x10_keep_alive_out = time.time() 133 | self.last_x1_x10_keep_alive_out = time.time() 134 | 135 | self.session_quadruple = [0xf4, 0x1e, 0x00, 0x00] 136 | 137 | self.active_mode = None 138 | self.connected = False 139 | self.reconfigured_x1 = False 140 | self.got_preset_name = False 141 | self.got_preset = False 142 | self.got_preset_names = False 143 | 144 | self.preset_name = '' 145 | self.preset_name_change_cb_fct_list = list() 146 | self.preset_no_change_cb_fct_list = list() 147 | 148 | self.slot_data = [] 149 | for i in range(0, 16): 150 | si = EmptySlotInfo() 151 | si.slot_no = i 152 | self.slot_data.append(si) 153 | self.slot_data_change_cb_fct_list = list() 154 | self.snapshot_change_cb_fct_list = list() 155 | 156 | self.excel_logger = None 157 | 158 | self.preset_change_cnt = 0 159 | 160 | # Modes 161 | self.request_preset_mode = RequestPreset(self) 162 | 163 | def set_excel_logger(self, excel_log_path): 164 | if excel_log_path: 165 | self.excel_logger = ExcelLogger(excel_log_path) 166 | else: 167 | self.excel_logger = None 168 | 169 | def switch_callback(self, id, val): 170 | log.info('switch: ' + str(id) + ', value: ' + str(val)) 171 | if val == 0: 172 | return 173 | 174 | def switch_hold_callback(self, id): 175 | log.info('switch hold: ' + str(id)) 176 | 177 | def switch_double_click_callback(self, id): 178 | log.info('switch double clicked: ' + str(id)) 179 | 180 | def pedal_callback(self, id, val): 181 | log.info('pedal: ' + str(id) + ' moved: ' + str(val)) 182 | 183 | def on_serial_exception(self, exc): 184 | if self.serial_interface is not None: 185 | self.open_fbv.stop() 186 | self.serial_interface.close() 187 | 188 | def register_snapshot_change_cb_fct(self, p_cb_fct): 189 | if p_cb_fct is not None: 190 | self.snapshot_change_cb_fct_list.append(p_cb_fct) 191 | 192 | def register_preset_name_change_cb_fct(self, p_cb_fct): 193 | if p_cb_fct is not None: 194 | self.preset_name_change_cb_fct_list.append(p_cb_fct) 195 | 196 | def register_preset_no_change_cb_fct(self, p_cb_fct): 197 | if p_cb_fct is not None: 198 | self.preset_no_change_cb_fct_list.append(p_cb_fct) 199 | 200 | def register_slot_data_change_cb_fct(self, p_cb_fct): 201 | if p_cb_fct is not None: 202 | self.slot_data_change_cb_fct_list.append(p_cb_fct) 203 | 204 | def set_preset_name(self, name): 205 | self.got_preset_name = True 206 | self.preset_name = name 207 | for cb_fct in self.preset_name_change_cb_fct_list: 208 | cb_fct(self.preset_name) 209 | 210 | def set_slot_info(self, slot_info_list): 211 | 212 | if len(slot_info_list) != 16: 213 | log.error('Wrong size for slot info - expected 16 slots, CHECK PRESET - resetting to all empty') 214 | self.slot_data = [] 215 | for i in range(0, 16): 216 | si = EmptySlotInfo() 217 | si.slot_no = i 218 | self.slot_data.append(si) 219 | return 220 | 221 | for i in range(0, 16): 222 | if self.slot_data[i] == slot_info_list[i]: 223 | pass 224 | else: 225 | for cb_fct in self.slot_data_change_cb_fct_list: 226 | cb_fct(i, slot_info_list[i]) 227 | self.slot_data = slot_info_list 228 | 229 | def set_snapshot(self, current_snapshot): 230 | self.current_snapshot = current_snapshot 231 | for cb_fct in self.snapshot_change_cb_fct_list: 232 | cb_fct(self.current_snapshot) 233 | 234 | def increase_session_quadruple_x11(self): 235 | self.session_quadruple[0] += 0x11 236 | if self.session_quadruple[0] > 0xff: 237 | self.session_quadruple[0] %= 0x100 238 | self.session_quadruple[1] += 0x1 239 | if self.session_quadruple[1] > 0xff: 240 | self.session_quadruple[1] %= 0x100 241 | self.session_quadruple[2] += 0x1 242 | if self.session_quadruple[2] > 0xff: 243 | self.session_quadruple[2] %= 0x100 244 | self.session_quadruple[3] += 0x1 245 | 246 | def check_keep_alive_response(self, data): 247 | # x1 keep alive response from device 248 | if self.my_byte_cmp(left=data, right=[0x8, 0x0, 0x0, 0x18, 0xef, 0x3, 0x1, 0x10, 0x0, "XX", 0x0, 0x10, "XX", 0x2, 0x0, 0x0], length=16): 249 | self.expecting_x1_x10_response = False 250 | return True 251 | 252 | # x2 keep alive response from device 253 | elif self.my_byte_cmp(left=data, right=[0x8, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x10, "XX", 0x2, 0x0, 0x0], length=16): 254 | self.expecting_x2_x10_response = False 255 | return True 256 | 257 | # x80 keep alive response from device 258 | elif self.my_byte_cmp(left=data, right=[0x8, 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, "XX", 0x0, 0x10, "XX", "XX", "XX", "XX"], length=16): 259 | self.expecting_x80_x10_response = False 260 | return True 261 | 262 | return False 263 | 264 | def config(self, usb_device): 265 | self.usb_device = usb_device 266 | 267 | if self.usb_device is None: 268 | log.error('Device not found') 269 | return 1 270 | # self.usb_device.set_configuration() 271 | 272 | self.active_configuration = self.usb_device.get_active_configuration() 273 | self.interface = self.active_configuration[(0, 0)] 274 | 275 | if self.usb_device.is_kernel_driver_active(0): 276 | log.info("Detaching kernel driver") 277 | self.usb_device.detach_kernel_driver(0) 278 | 279 | for endpoint in self.interface: 280 | desc = str(endpoint) 281 | if "ENDPOINT 0x1: Bulk OUT" in desc: 282 | self.endpoint_0x1_bulk_out = endpoint 283 | elif "ENDPOINT 0x81: Bulk IN" in desc: 284 | self.endpoint_0x81_bulk_in = endpoint 285 | 286 | try: 287 | self.interface_4 = self.active_configuration[(4, 0)] 288 | for endpoint in self.interface_4: 289 | desc = str(endpoint) 290 | if "ENDPOINT 0x2: Bulk OUT" in desc: 291 | self.endpoint_0x2_bulk_out = endpoint 292 | elif "ENDPOINT 0x82: Bulk IN" in desc: 293 | self.endpoint_0x82_bulk_in = endpoint 294 | except usb.core.USBError as e: 295 | log.error('While trying to claim interface') 296 | 297 | try: 298 | self.interface_2_1 = self.active_configuration[(2, 1)] 299 | for endpoint in self.interface_2_1: 300 | desc = str(endpoint) 301 | if "ENDPOINT 0x3: Isochronous OUT" in desc: 302 | self.endpoint_0x3_isochronous_out = endpoint 303 | except usb.core.USBError as e: 304 | log.error('While trying to claim interface') 305 | 306 | try: 307 | self.interface_3_1 = self.active_configuration[(3, 1)] 308 | for endpoint in self.interface_3_1: 309 | desc = str(endpoint) 310 | if "ENDPOINT 0x83: Isochronous IN" in desc: 311 | self.endpoint_0x83_isochronous_in = endpoint 312 | except usb.core.USBError as e: 313 | log.error('While trying to claim interface') 314 | 315 | self.x81_reader = threading.Thread(target=self.endpoint_listener, args=('0x81', self.endpoint_0x81_bulk_in)) 316 | 317 | return 0 318 | 319 | def begin(self): 320 | self.stop_threads = False 321 | for request_string_id in [self.GET_STRING_VENDOR, self.GET_STRING_PRODUCT, self.GET_STRING_SERIAL, 322 | self.GET_STRING_APP, self.GET_STRING_VERSION, self.GET_STRING_CPU_ID, 323 | self.GET_STRING_MAC_ADDR, self.GET_STRING_VMGR, self.GET_STRING_VDEF, 324 | self.GET_STRING_UNDEFINED, self.GET_STRING_VENDOR]: 325 | try: 326 | tst = usb.util.get_string(self.usb_device, request_string_id, langid=0x0409) 327 | log.info(tst) 328 | except usb.core.USBError as e: 329 | log.warning("Caught exception while trying to get string (" + str(request_string_id) + "): " + str(e)) 330 | pass 331 | 332 | # very important for usb.control.clear_feature to work 333 | try: 334 | usb.util.claim_interface(self.usb_device, self.interface) 335 | except usb.core.USBError as e: 336 | log.error('While trying to claim interface') 337 | 338 | try: 339 | usb.util.claim_interface(self.usb_device, self.interface_3_1) 340 | except usb.core.USBError as e: 341 | log.error('While trying to claim interface 3.1, error: ' + str(e)) 342 | 343 | ''' 344 | If you receive a libusb exception (USBError: [Errno 2] Entity not found) here, 345 | it might be the case that the operating system has already taken control of the 346 | midi device. You can try to unplug the usb cable, start this script and plug 347 | the cable in again. Usually, this script is faster than the OS and you can 348 | run it... 349 | ''' 350 | usb.control.clear_feature( 351 | dev=self.usb_device, feature=usb.control.ENDPOINT_HALT, recipient=self.endpoint_0x1_bulk_out) 352 | usb.control.clear_feature( 353 | dev=self.usb_device, feature=usb.control.ENDPOINT_HALT, recipient=self.endpoint_0x81_bulk_in) 354 | 355 | self.x81_reader.start() 356 | 357 | def on_usb_io_exception(self, exc): 358 | return 359 | 360 | def on_serial_exception(self, exc): 361 | return 362 | 363 | def parse_preset(self): 364 | all_data = list() 365 | for packet in self.preset_data: 366 | # hex_str = ''.join('0x{:x}, '.format(x) for x in packet) 367 | # all_presets += hex_str 368 | for b in packet[16:]: 369 | all_data.append(b) 370 | 371 | str_rep_hex = ''.join('{:02x}'.format(x) for x in all_data) 372 | # print(str_rep_hex) 373 | ca_splitter(str_rep_hex) 374 | 375 | def next_preset_data_packet_double(self): 376 | next_preset_data_packet_double = self.preset_data_double_cnt 377 | self.preset_data_double_cnt[0] += 1 378 | if self.preset_data_double_cnt[0] > 0xff: 379 | self.preset_data_double_cnt[0] = 0 380 | self.preset_data_double_cnt[1] += 1 381 | 382 | if self.preset_data_double_cnt[1] > 0xff: 383 | self.preset_data_double_cnt[1] = 0 384 | return next_preset_data_packet_double 385 | 386 | def preset_data_packet_double(self): 387 | return self.preset_data_double_cnt 388 | 389 | def next_x80x10_packet_no(self): 390 | next_no = self.x80x10_cnt 391 | self.x80x10_cnt += 1 392 | if self.x80x10_cnt > 0xFF: 393 | self.x80x10_cnt = 0 394 | # self.x80x10_cnt %= 0xFF 395 | # log.info("x80x10: " + str(next_no)) 396 | return next_no 397 | 398 | def x80x10_keep_alive_thread_fct(self, start_delay): 399 | log.info("Starting x80x10_keep_alive_thread, delay is: " + str(start_delay)) 400 | time.sleep(start_delay) 401 | 402 | t = threading.currentThread() 403 | 404 | if start_delay < 1.04: 405 | preset_data_packet_double = self.preset_data_packet_double() 406 | self.endpoint_0x1_out( 407 | [0x8, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x10, self.maybe_session_no, preset_data_packet_double[0], preset_data_packet_double[1], 0x0], 408 | silent=True 409 | ) 410 | self.expecting_x80_x10_response = True 411 | self.last_x80_x10_keep_alive_out = time.time() 412 | 413 | while getattr(t, "do_run", True): 414 | 415 | while self.last_x80_x10_keep_alive_out + 1.04 > time.time(): 416 | time.sleep(0.05) 417 | # log.info('Delayed x80_x10_ timer') 418 | # if self.awaiting_preset_name_data is False and self.awaiting_preset_data is False: 419 | if self.expecting_x80_x10_response: 420 | log.error('No x80x10 response!') 421 | 422 | preset_data_packet_double = self.preset_data_packet_double() 423 | self.endpoint_0x1_out( 424 | [0x8, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x10, self.maybe_session_no, preset_data_packet_double[0], preset_data_packet_double[1], 0x0], 425 | silent=True 426 | ) 427 | self.expecting_x80_x10_response = True 428 | time.sleep(1.04) 429 | log.info("Finished x80x10_keep_alive_thread") 430 | 431 | def next_x1x10_packet_no(self): 432 | next_no = self.x1x10_cnt 433 | self.x1x10_cnt += 1 434 | if self.x1x10_cnt > 0xFF: 435 | self.x1x10_cnt = 0 436 | # self.x1x10_cnt %= 0xFF 437 | return next_no 438 | 439 | def x1x10_keep_alive_thread_fct(self, start_delay): 440 | log.info("Starting x1x10_keep_alive_thread, delay is: " + str(start_delay)) 441 | time.sleep(start_delay) 442 | while self.stop_communication is False: 443 | if self.expecting_x1_x10_response: 444 | log.error('No x1x10 response!') 445 | 446 | self.endpoint_0x1_out( 447 | [0x8, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x8, 0x72, 0x1e, 0x0, 0x0], 448 | silent=True) 449 | self.expecting_x1_x10_response = True 450 | time.sleep(1.04) 451 | 452 | def next_x2x10_packet_no(self): 453 | next_no = self.x2x10_cnt 454 | self.x2x10_cnt += 1 455 | if self.x2x10_cnt > 0xFF: 456 | self.x2x10_cnt = 0 457 | # self.x2x10_cnt %= 0xFF 458 | return next_no 459 | 460 | def x2x10_keep_alive_thread_fct(self, start_delay): 461 | log.info("Starting x2x10_keep_alive_thread, delay is: " + str(start_delay)) 462 | time.sleep(start_delay) 463 | 464 | t = threading.currentThread() 465 | 466 | if start_delay < 1.04: 467 | self.endpoint_0x1_out( 468 | [0x8, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x10, 0x9, 0x10, 0x0, 0x0], 469 | silent=True) 470 | self.expecting_x2_x10_response = True 471 | self.last_x2_x10_keep_alive_out = time.time() 472 | 473 | while getattr(t, "do_run", True): 474 | 475 | while self.last_x2_x10_keep_alive_out + 1.04 > time.time(): 476 | time.sleep(0.05) 477 | 478 | if self.expecting_x2_x10_response: 479 | log.error('No x2x10 response!') 480 | 481 | self.endpoint_0x1_out( 482 | [0x8, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x10, 0x9, 0x10, 0x0, 0x0], 483 | silent=True) 484 | self.expecting_x2_x10_response = True 485 | time.sleep(1.04) 486 | log.info("Finished x2x10_keep_alive_thread") 487 | 488 | def start_x1x10_keep_alive_thread(self, delay=0.0): 489 | self.x1x10_keep_alive_thread = threading.Thread(target=self.x1x10_keep_alive_thread_fct, args=(delay,)) 490 | self.x1x10_keep_alive_thread.start() 491 | 492 | def start_x2x10_keep_alive_thread(self, delay=0.0): 493 | self.x2x10_keep_alive_thread = threading.Thread(target=self.x2x10_keep_alive_thread_fct, args=(delay,)) 494 | self.x2x10_keep_alive_thread.start() 495 | 496 | def start_x80x10_keep_alive_thread(self, delay=1.0): 497 | if self.x80x10_keep_alive_thread is not None: 498 | return 499 | self.stop_x80x10_communication = False 500 | self.x80x10_keep_alive_thread = threading.Thread(target=self.x80x10_keep_alive_thread_fct, args=(delay,)) 501 | self.x80x10_keep_alive_thread.start() 502 | 503 | def start_keep_alive_messages(self, delay_x80x10=0.3, delay_x1x10=0.3, delayx2_x10=0.7): 504 | 505 | if self.x80x10_keep_alive_thread is not None: 506 | return 507 | 508 | self.stop_communication = False 509 | self.stop_x80x10_communication = False 510 | 511 | self.x80x10_keep_alive_thread = threading.Thread(target=self.x80x10_keep_alive_thread_fct, args=(delay_x80x10,)) 512 | self.x1x10_keep_alive_thread = threading.Thread(target=self.x1x10_keep_alive_thread_fct, args=(delay_x1x10,)) 513 | self.x2x10_keep_alive_thread = threading.Thread(target=self.x2x10_keep_alive_thread_fct, args=(delayx2_x10,)) 514 | 515 | self.x80x10_keep_alive_thread.start() 516 | self.x1x10_keep_alive_thread.start() 517 | self.x2x10_keep_alive_thread.start() 518 | 519 | def switch_mode(self, mode_name="Standard"): 520 | if self.active_mode is not None: 521 | self.active_mode.shutdown() 522 | 523 | if mode_name == "Standard": 524 | ''' 525 | self.connected = False 526 | self.reconfigured_x1 = False 527 | self.got_preset_name = False 528 | self.got_preset = False 529 | self.got_preset_names = False 530 | ''' 531 | if self.connected is False: 532 | self.active_mode = Connect(self) 533 | self.active_mode.start() 534 | elif self.connected is True and self.reconfigured_x1 is False: 535 | self.active_mode = ReconfigureX1(self) 536 | self.active_mode.start() 537 | elif self.reconfigured_x1 is True and self.got_preset_name is False: 538 | self.active_mode = RequestPresetName(self) 539 | self.active_mode.start() 540 | elif self.got_preset_name is True and self.got_preset is False: 541 | self.active_mode = self.request_preset_mode 542 | self.active_mode.start() 543 | elif self.got_preset is True and self.got_preset_names is False: 544 | self.active_mode = RequestPresetNames(self) 545 | self.active_mode.start() 546 | else: 547 | self.active_mode = Standard(self, name="standard") 548 | self.active_mode.start() 549 | elif mode_name == "Connect": 550 | self.active_mode = Connect(self) 551 | self.active_mode.start() 552 | elif mode_name == "ReconfigureX1": 553 | self.active_mode = ReconfigureX1(self) 554 | self.active_mode.start() 555 | elif mode_name == "RequestPreset": 556 | self.active_mode = RequestPreset(self) 557 | self.active_mode.start() 558 | elif mode_name == "RequestPresetNames": 559 | self.active_mode = RequestPresetNames(self) 560 | self.active_mode.start() 561 | elif mode_name == "RequestPresetName": 562 | self.active_mode = RequestPresetName(self) 563 | self.active_mode.start() 564 | elif mode_name == "Standard": 565 | self.active_mode = Standard(self, name="standard") 566 | self.active_mode.start() 567 | else: 568 | log.error('Unknown mode: ' + mode_name) 569 | 570 | def endpoint_listener(self, description, endpoint): 571 | 572 | log.info('Started ' + description + ' thread') 573 | while self.stop_threads is False: 574 | try: 575 | data = endpoint.read(size_or_buffer=endpoint.wMaxPacketSize, timeout=0) 576 | self.data_in(description, data) 577 | except ValueError as _: 578 | pass 579 | except usb.core.USBError as e: 580 | if self.usb_io_exception_cb is not None: 581 | self.usb_io_exception_cb(str(e)) 582 | log.info('Stopped thread reading endpoint data!') 583 | # self.x81_reader = threading.Thread(target=self.endpoint_listener, args=()) 584 | 585 | @staticmethod 586 | def my_byte_cmp(left, right, length=-1): 587 | if length == -1: 588 | length = min(len(left), len(right)) 589 | 590 | if len(left) < length: 591 | return False 592 | if len(right) < length: 593 | return False 594 | 595 | for i in range(0, length): 596 | if left[i] == 'XX' or right[i] == 'XX': 597 | continue 598 | if left[i] != right[i]: 599 | return False 600 | break 601 | 602 | return True 603 | 604 | @staticmethod 605 | def ieee754_to_rendered_str(val): 606 | value = struct.unpack('>f', binascii.unhexlify(val)) 607 | rounded_val = round(value[0], 2) * 10 608 | return rounded_val 609 | 610 | def log_data_in(self, data): 611 | if data[6] not in [0x1, 0x2, 0x80]: 612 | return 613 | hex_str = ''.join('0x{:x}, '.format(x) for x in data) 614 | str_rep_hex = ''.join('{:02x}'.format(x) for x in data) 615 | # str_rep = str_rep_hex.decode("hex") 616 | if hex_str.endswith(', '): 617 | hex_str = hex_str[:-2] 618 | log.info('\t\t' + hex_str) 619 | # log.info('\t\t' + str_rep) 620 | 621 | 622 | def log_data_out(self, data): 623 | if data[4] not in [0x1, 0x2, 0x80]: 624 | return 625 | 626 | hex_str = ''.join('0x{:x}, '.format(x) for x in data) 627 | if hex_str.endswith(', '): 628 | hex_str = hex_str[:-2] 629 | log.info(hex_str) 630 | 631 | def out_packet_to_endpoint_0x1(self, out_packet, silent=False): 632 | data_to_send = copy.deepcopy(out_packet.data) 633 | if out_packet.delay == 0.0: 634 | self.endpoint_0x1_out(data_to_send, silent) 635 | else: 636 | threading.Timer(out_packet.delay, self.endpoint_0x1_out, [data_to_send]).start() 637 | 638 | def endpoint_0x1_out(self, data, silent=False): 639 | 640 | if data[9] == "XX": 641 | if data[4] == 0x1: 642 | data[9] = self.next_x1x10_packet_no() 643 | elif data[4] == 0x2: 644 | data[9] = self.next_x2x10_packet_no() 645 | elif data[4] == 0x80: 646 | data[9] = self.next_x80x10_packet_no() 647 | if not silent: 648 | self.log_data_out(data) 649 | # hex_str = ''.join('{:02x} '.format(x) for x in out_data) 650 | if self.excel_logger: 651 | self.excel_logger.log(data) 652 | if data[4] == 0x1: 653 | self.last_x1_x10_keep_alive_out = time.time() 654 | elif data[4] == 0x2: 655 | self.last_x2_x10_keep_alive_out = time.time() 656 | elif data[4] == 0x80: 657 | self.last_x80_x10_keep_alive_out = time.time() 658 | 659 | self.endpoint_0x1_bulk_out.write(data) 660 | 661 | def data_in(self, endpoint_id, data): 662 | if endpoint_id == '0x81': 663 | if self.excel_logger: 664 | self.excel_logger.log(data) 665 | try: 666 | print_to_console = self.active_mode.data_in(data) 667 | # if print_to_console: 668 | # self.log_data_in(data) 669 | 670 | except IndexError as _: 671 | pass 672 | 673 | def usb_device_found_cb(self, usb_descriptor): 674 | log.info('Found: ' + str(usb_descriptor)) 675 | if usb_descriptor.device_id in ['0e41:4246', '0e41:5055']: 676 | if 0 == self.config(usb_descriptor.device): 677 | self.begin() 678 | self.switch_mode(mode_name="Connect") 679 | else: 680 | log.error('While trying to configure the OpenKpaUsb instance') 681 | return 682 | 683 | def usb_device_lost_cb(self, usb_descriptor): 684 | # print('Lost: 4 685 | # ' + str(usb_descriptor)) 686 | if usb_descriptor.device_id == '133e:0001': 687 | log.warn('Lost connection to KPA - going to stop all used threads!') 688 | self.stop_threads = True 689 | 690 | elif usb_descriptor.device_id in ['0e41:4246', '0e41:5055']: 691 | # find connected open_fbv instance 692 | for open_fbv in self.open_fbvs: 693 | fbv_usb_descriptor = UsbMonitor.device_to_usb_descriptor(open_fbv.io_interface.usb_device) 694 | if fbv_usb_descriptor == usb_descriptor: 695 | open_fbv.stop() 696 | self.open_fbvs.remove(open_fbv) 697 | log.info('Removed FBV instance') 698 | 699 | 700 | def set_midi_cc(self, switch_no, cc): 701 | if switch_no not in [0, 1, 2]: 702 | log.error("switch_no must be either 0, 1 or 2") 703 | return 704 | # 0x21, 0x0, 0x0, 0x18, 0x03, 0x10, 0xed, 0x3, 0x0, 0x4a, 0x0, 0x4, 0xb0, 0x1c, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0x11, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xf2, 0x64, 0x44, 0x65, 0x84, 0x18, 0x6 , 0x4d, 0x2, 0x1c, 0x2, 0x51, 0x33, 0x00, 0x00, 0x00 0x51 = i[39] 705 | data = [0x21, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0x42, 0x1b, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0x11, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xf3, 0x64, 0x44, 0x65, 0x84, 0x18, 0x6 + switch_no, 0x4d, 0x1, 0x1c, 0x2, 0x51, 0x2, 0x0, 0x0, 0x0] 706 | data = [0x21, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0xb0, 0x1c, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0x11, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xf2, 0x64, 0x44, 0x65, 0x84, 0x18, 0x6 + switch_no, 0x4d, 0x2, 0x1c, 0x2, 0x51, 0x33, 0x0, 0x0, 0x0] 707 | 708 | try: 709 | data[40] = int(cc) 710 | self.endpoint_0x1_out(data) 711 | except ValueError: 712 | log.error('Given midi channel is no integer: ' + str(midi_channel)) 713 | return 714 | 715 | def set_midi_channel(self, switch_no, midi_channel): 716 | if switch_no not in [0, 1, 2]: 717 | log.error("switch_no must be either 0, 1 or 2") 718 | return 719 | 720 | data = [0x21, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0x42, 0x1b, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0x11, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xf3, 0x64, 0x44, 0x65, 0x84, 0x18, 0x6 + switch_no, 0x4d, 0x1, 0x1c, 0x1, 0x51, 0x1, 0x0, 0x0, 0x0] 721 | 722 | try: 723 | data[40] = int(midi_channel) 724 | self.endpoint_0x1_out(data) 725 | except ValueError: 726 | log.error('Given midi channel is no integer: ' + str(midi_channel)) 727 | return 728 | 729 | def set_custom_foot_switch_function(self, switch_no, function_code): 730 | if switch_no not in [0, 1, 2]: 731 | log.error("switch_no must be either 0, 1 or 2") 732 | return 733 | try: 734 | function_code = int(function_code) 735 | if function_code == 5: 736 | # Hotkey 737 | data = [0x1d, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0x87, 0x1b, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0xd, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xf6, 0x64, 0x43, 0x65, 0x82, 0x18, 0x6 + switch_no, 0x4d, 0x2, 0x0, 0x0, 0x0] 738 | else: 739 | data = [0x1d, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0x42, 0x27, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0xd, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x4, 0x43, 0x64, 0x43, 0x65, 0x82, 0x18, 0x6 + switch_no, 0x4d, 0x0, 0x0, 0x0, 0x0] 740 | data[36] = function_code 741 | self.endpoint_0x1_out(data) 742 | except ValueError: 743 | log.error('Given function_codeis no integer: ' + str(function_code)) 744 | return 745 | 746 | def set_color(self, switch_no, color_id): 747 | if switch_no not in [0, 1, 2]: 748 | log.error("switch_no must be either 0, 1 or 2") 749 | return 750 | 751 | alt_cnt = 0xa6 # it's a legacy variable 752 | 753 | data = [0x1d, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, self.session_quadruple[0], self.session_quadruple[1], self.session_quadruple[2], self.session_quadruple[3], 0x1, 0x0, 0x6, 0x0, 0xd, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, alt_cnt, 0xf0, 0x64, 0x3d, 0x65, 0x82, 0x66, switch_no, 0x42, color_id, 0x0, 0x7f, 0x0] 754 | self.endpoint_0x1_out(data) 755 | 756 | def set_label(self, switch_no, text): 757 | if switch_no not in [0, 1, 2]: 758 | log.error("switch_no must be either 0, 1 or 2") 759 | return 760 | 761 | msg_size_byte = 0x1e + len(text) 762 | length_byte = 0xa1 + len(text) 763 | second_length_byte = msg_size_byte - 0x10 764 | 765 | data = [msg_size_byte, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, self.session_quadruple[0], self.session_quadruple[1], self.session_quadruple[2], self.session_quadruple[3], 0x1, 0x0, 0x6, 0x0, second_length_byte, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xf0, 0x64, 0x3b, 0x65, 0x82, 0x66, switch_no, 0x6d, length_byte] 766 | for character in text: 767 | data.append(ord(character)) 768 | 769 | while len(data) < msg_size_byte + 9 + 2: 770 | data.append(0x0) 771 | 772 | self.endpoint_0x1_out(data) 773 | 774 | def highlight_slot(self, slot_no): 775 | data = [0x1d, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0x44, 0x26, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0xd, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x4, 0x34, 0x64, 0x4e, 0x65, 0x82, 0x62, 0x1, 0x1a, 0x0, 0x0, 0x0, 0x0] 776 | 777 | try: 778 | data[34] = int(slot_no) 779 | self.endpoint_0x1_out(data) 780 | except ValueError: 781 | log.error('Given function_code is no integer: ' + str(slot_no)) 782 | return 783 | 784 | def set_fs_function(self, foot_switch_name, function_name): 785 | 786 | try: 787 | foot_switch_id = self.FOOT_SWITCHES[foot_switch_name] 788 | except KeyError: 789 | log.error("Unknown foot switch with name: " + foot_switch_name) 790 | return 791 | 792 | try: 793 | foot_switch_function_id = self.FOOT_SWITCH_FUNCTIONS.index(function_name) 794 | except ValueError: 795 | log.error("Unknown foot switch function with name: " + function_name) 796 | return 797 | 798 | log.info(foot_switch_name + ": setting foot switch function to: " + function_name) 799 | data = [0x1d, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0xc6, 0x1e, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0xd, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x4, 0x4, 0x64, 0x19, 0x65, 0x82, 0x76, foot_switch_id, 0x77, foot_switch_function_id, 0x0, 0x0, 0x0] 800 | self.endpoint_0x1_out(data) 801 | 802 | def set_preset_label_be_careful(self, prog_no, text): 803 | msg_size_byte = 0x20 + len(text) 804 | length_byte = 0xa1 + len(text) 805 | second_length_byte = msg_size_byte - 0x10 806 | data = [msg_size_byte, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x4, 0x77, 0x1e, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, second_length_byte, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xed, 0x64, 0x6, 0x65, 0x83, 0x6b, 0x0, 0x6c, prog_no, 0x6d, length_byte] 807 | 808 | for character in text: 809 | data.append(ord(character)) 810 | 811 | while len(data) < msg_size_byte + 9 + 2: 812 | data.append(0x0) 813 | 814 | self.endpoint_0x1_out(data) 815 | 816 | def on_preset_name_update(self, preset_name): 817 | log.info("*************************** Preset Name: " + preset_name) 818 | 819 | def on_slot_update(self, slot_no, slot_info): 820 | log.info('Slot ' + str(slot_no) + ' change: ' + slot_info.to_string()) 821 | 822 | def on_snapshot_change(self, current_snapshot): 823 | log.info('Snapshot change to: ' + str(current_snapshot)) 824 | 825 | def set_preset(self, preset_no): 826 | self.current_preset_no = preset_no 827 | for cb_fct in self.preset_no_change_cb_fct_list: 828 | cb_fct(self.current_preset_no) 829 | 830 | def on_preset_change(self, preset_no): 831 | self.preset_change_cnt += 1 832 | log.info("******************** PRESET switch no: " + str(self.preset_change_cnt) +" to: " + str(preset_no)) 833 | self.preset_no = preset_no 834 | 835 | def signal_handler(self, sig, frame): 836 | if self.excel_logger: 837 | self.excel_logger.save() 838 | 839 | 840 | def print_usage(p_b_exit=True): 841 | print() 842 | print() 843 | print("Usage: %s [args]" % sys.argv[0]) 844 | print() 845 | print("args:") 846 | print('\t-x: dumps session data to given xlsx file (Excel format)') 847 | print() 848 | print("switches:") 849 | print('\t-h: prints this text') 850 | print() 851 | 852 | if p_b_exit is True: 853 | sys.exit(1) 854 | 855 | 856 | def main(argv): 857 | logging.basicConfig( 858 | level='INFO', 859 | format="%(asctime)s - %(levelname)s - %(message)s (%(name)s)", 860 | datefmt="%Y-%m-%d %H:%M:%S") 861 | 862 | excel_log_path = None 863 | try: 864 | opts, args = getopt.getopt(argv[1:], 'x:h', []) 865 | for opt, arg in opts: 866 | if opt in '-h': 867 | print_usage() 868 | elif opt in '-x': 869 | excel_log_path = arg 870 | else: 871 | print_usage() 872 | except getopt.GetoptError as e: 873 | log.error("While trying to parse command line arguments: " + str(e)) 874 | print_usage() 875 | 876 | helix_usb = HelixUsb() 877 | helix_usb.set_excel_logger(excel_log_path) 878 | helix_usb.register_preset_name_change_cb_fct(helix_usb.on_preset_name_update) 879 | helix_usb.register_slot_data_change_cb_fct(helix_usb.on_slot_update) 880 | helix_usb.register_snapshot_change_cb_fct(helix_usb.on_snapshot_change) 881 | helix_usb.register_preset_no_change_cb_fct(helix_usb.on_preset_change) 882 | 883 | signal.signal(signal.SIGINT, helix_usb.signal_handler) 884 | 885 | # only report Line6 Helix devices 886 | usb_monitor = UsbMonitor(['0e41:4246', '0e41:5055']) 887 | 888 | usb_monitor.register_device_found_cb(helix_usb.usb_device_found_cb) 889 | usb_monitor.register_device_lost_cb(helix_usb.usb_device_lost_cb) 890 | usb_monitor.start() 891 | 892 | while True: 893 | 894 | try: 895 | python_version = int(sys.version_info[0]) 896 | if python_version <= 2: 897 | text = raw_input("command: ") 898 | else: 899 | text = input("command: ") 900 | text = text.rstrip() 901 | tokens = text.split(' ') 902 | if len(tokens) == 1: 903 | try: 904 | if text == "save": 905 | helix_usb.excel_logger.save() 906 | else: 907 | text = int(text) 908 | if text == 0: 909 | helix_usb.switch_mode("RequestPresetName") 910 | elif text == 1: 911 | helix_usb.switch_mode("RequestPreset") 912 | elif text == 2: 913 | helix_usb.switch_mode("RequestPresetNames") 914 | else: 915 | log.warning('Unknown command id: ' + str(text)) 916 | except ValueError: 917 | log.error('Invalid value - only integer values allowed') 918 | continue 919 | elif len(tokens) == 2: 920 | try: 921 | switch_id = int(tokens[0]) 922 | except ValueError: 923 | log.error('Invalid value - only integer values allowed') 924 | continue 925 | if switch_id in [11, 12, 13]: 926 | if tokens[1] in HelixUsb.LED_COLORS: 927 | color_id = HelixUsb.LED_COLORS.index(tokens[1]) 928 | helix_usb.set_color(switch_id - 11, color_id) 929 | elif switch_id in [21, 22, 23]: 930 | helix_usb.set_label(switch_id - 21, tokens[1]) 931 | elif switch_id in [31, 32, 33]: 932 | helix_usb.set_midi_channel(switch_id - 31, tokens[1]) 933 | elif switch_id in [41, 42, 43]: 934 | helix_usb.set_midi_cc(switch_id - 41, tokens[1]) 935 | elif switch_id in [51, 52, 53]: 936 | helix_usb.set_custom_foot_switch_function(switch_id - 51, tokens[1]) 937 | elif switch_id in [63, 64, 65]: 938 | if switch_id == 63: 939 | helix_usb.set_fs_function("FS3", tokens[1]) 940 | if switch_id == 64: 941 | helix_usb.set_fs_function("FS4", tokens[1]) 942 | if switch_id == 65: 943 | helix_usb.set_fs_function("FS5", tokens[1]) 944 | elif switch_id in [18]: 945 | helix_usb.highlight_slot(tokens[1]) 946 | elif switch_id in [19]: 947 | helix_usb.set_preset_label_be_careful(tokens[1]) 948 | 949 | except KeyboardInterrupt as _: 950 | usb_monitor.request_terminate = True 951 | helix_usb.stop_threads = True 952 | return 0 953 | 954 | 955 | if __name__ == '__main__': 956 | main(sys.argv) 957 | -------------------------------------------------------------------------------- /modes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kempline/helix_usb/02435433129a85610d009793097e0b02c489d9af/modes/__init__.py -------------------------------------------------------------------------------- /modes/connect.py: -------------------------------------------------------------------------------- 1 | from modes.standard import Standard 2 | from out_packet import OutPacket 3 | import logging 4 | log = logging.getLogger(__name__) 5 | 6 | 7 | class Connect(Standard): 8 | def __init__(self, helix_usb): 9 | Standard.__init__(self, helix_usb=helix_usb, name="connect") 10 | self.alive_msg_counter = [0, 0, 0] 11 | self.reset_x1x10_done = False 12 | self.received_x11_on_x2 = False 13 | self.received_x11_on_x80 = False 14 | 15 | 16 | def start(self): 17 | log.info('Starting mode') 18 | 19 | self.helix_usb.x1x10_cnt = 0x2 20 | self.helix_usb.x2x10_cnt = 0x2 21 | self.helix_usb.x80x10_cnt = 0x2 22 | self.reset_x1x10_done = False 23 | 24 | data = [0xc, 0x0, 0x0, 0x28, 0x1, 0x10, 0xef, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x21, 0x0, 0x10, 0x0, 0x0] 25 | self.helix_usb.endpoint_0x1_out(data) 26 | 27 | def shutdown(self): 28 | log.info('Shutting down mode') 29 | 30 | def data_in(self, data): 31 | 32 | if self.helix_usb.my_byte_cmp(left=data, right=[0xc, 0x0, 0x0, 0x28, 0xef, 0x3, 0x1, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0], length=20): 33 | out = OutPacket(data=[0x11, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x5, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0]) 34 | self.helix_usb.out_packet_to_endpoint_0x1(out) 35 | 36 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x28, 0x0, 0x0, 0x18, 0xef, 0x3, 0x1, 0x10, 0x0, 0x2, 0x0, 0x4, 0x9, 0x2], length=14): 37 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x8, 0x20, 0x10, 0x0, 0x0]) 38 | self.helix_usb.out_packet_to_endpoint_0x1(out) 39 | 40 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x8, 0x0, 0x0, 0x18, 0xef, 0x3, 0x1, 0x10, 0x0, 0x3, 0x0, "XX", 0x9, 0x2, 0x0, 0x0], length=16): 41 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x2, 0x20, 0x10, 0x0, 0x0]) 42 | self.helix_usb.out_packet_to_endpoint_0x1(out) 43 | # later - after reconfiguraion 44 | # self.helix_usb.start_x1x10_keep_alive_thread(delay=0.0) 45 | 46 | # start 80x10 47 | # ToDo falsche Response (falscher Kanal) 48 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x8, 0x0, 0x0, 0x18, 0xef, 0x3, 0x1, 0x10, 0x0, 0x4, 0x0, "XX", 0x9, 0x2, 0x0, 0x0], length=16): 49 | out = OutPacket(data=[0xc, 0x0, 0x0, 0x28, 0x80, 0x10, 0xed, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x21, 0x0, 0x10, 0x0, 0x0], delay=0.0) 50 | self.helix_usb.out_packet_to_endpoint_0x1(out) 51 | 52 | elif self.helix_usb.my_byte_cmp(left=data, right=[0xc, 0x0, 0x0, 0x28, 0xed, 0x3, 0x80, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0], length=20): 53 | out = OutPacket(data=[0x11, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0]) 54 | self.helix_usb.out_packet_to_endpoint_0x1(out) 55 | 56 | # ToDo falsche Response (falscher Kanal) 57 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x11, 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x2], length=10): 58 | self.received_x11_on_x80 = True 59 | out = OutPacket(data=[0xc, 0x0, 0x0, 0x28, 0x2, 0x10, 0xf0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x21, 0x0, 0x10, 0x0, 0x0], delay=0.0) 60 | self.helix_usb.out_packet_to_endpoint_0x1(out) 61 | self.helix_usb.start_x80x10_keep_alive_thread(delay=0.0) 62 | 63 | # ToDo falsche Response (falscher Kanal) 64 | elif self.helix_usb.my_byte_cmp(left=data, right=[0xc, 0x0, 0x0, 0x28, 0xf0, 0x3, 0x2, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0], length=20): 65 | # out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, 0x3, 0x0, 0x8, 0x9, 0x10, 0x0, 0x0]) 66 | # self.helix_usb.out_packet_to_endpoint_0x1(out) 67 | out = OutPacket(data=[0x11, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x4, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0]) 68 | self.helix_usb.out_packet_to_endpoint_0x1(out) 69 | 70 | # ToDo falsche Response (falscher Kanal) 71 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x11, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, 0x2, 0x0, 0x4, 0x9, 0x2], length=14): 72 | self.received_x11_on_x2 = True 73 | self.helix_usb.start_x2x10_keep_alive_thread() 74 | # out = OutPacket(data=[0xc, 0x0, 0x0, 0x28, 0x1, 0x10, 0xef, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x21, 0x0, 0x10, 0x0, 0x0]) 75 | # self.helix_usb.out_packet_to_endpoint_0x1(out) 76 | 77 | # ToDo falsche Response (falscher Kanal) 78 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x8, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, 0x3, 0x0, 0x8, 0x9, 0x2, 0x0, 0x0], length=16): 79 | out = OutPacket(data=[0x19, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0x9, 0x10, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0x9, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xe8, 0x64, 0x4c, 0x65, 0x80, 0x0, 0x0, 0x0], delay=0.140) 80 | self.helix_usb.out_packet_to_endpoint_0x1(out) 81 | 82 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x54, 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0], length=9): 83 | out = OutPacket(data=[0x1c, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0xc, 0x55, 0x10, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0xc, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xe9, 0x64, 0x18, 0x65, 0x81, 0x76, 0xcc, 0x80]) 84 | self.helix_usb.out_packet_to_endpoint_0x1(out) 85 | 86 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x1f, 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, "XX", 0x0, 0x4, 0x2e, 0x2, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0xf, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xe9, 0x67, 0x0, 0x68, 0x82, 0x76, 0xcd, 0x0, 0x80, 0x77, 0x0, 0xdc], length=9): 87 | out = OutPacket(data=[0x19, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0xc, 0x6c, 0x10, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0x9, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xea, 0x64, 0x17, 0x65, 0xc0, 0x0, 0x0, 0x0]) 88 | self.helix_usb.out_packet_to_endpoint_0x1(out) 89 | elif self.helix_usb.check_keep_alive_response(data): 90 | return False # don't print incoming message to console 91 | else: 92 | hex_str = ''.join('0x{:x}, '.format(x) for x in data) 93 | log.warning("Unexpected message in connect mode: " + str(hex_str)) 94 | 95 | if self.received_x11_on_x2 and self.received_x11_on_x80: 96 | self.helix_usb.connected = True 97 | self.helix_usb.switch_mode() 98 | 99 | return True # print incoming message to console 100 | ''' 101 | if self.helix_usb.my_byte_cmp(left=data, right=[0xc, 0x0, 0x0, 0x28, 0xef, 0x3, 0x1, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0], length=20): 102 | out = OutPacket(data=[0x11, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x5, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0]) 103 | self.helix_usb.out_packet_to_endpoint_0x1(out) 104 | 105 | # 2x10: start 106 | elif self.helix_usb.my_byte_cmp(left=data, right=[0xc, 0x0, 0x0, 0x28, 0xf0, 0x3, 0x2, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0], length=20): 107 | out = OutPacket(data=[0x11, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x4, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0]) 108 | self.helix_usb.out_packet_to_endpoint_0x1(out) 109 | self.helix_usb.start_x2x10_keep_alive_thread() 110 | 111 | # x80x10: start 112 | elif self.helix_usb.my_byte_cmp(left=data, right=[0xc, 0x0, 0x0, 0x28, 0xed, 0x3, 0x80, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0], length=20): 113 | out = OutPacket(data=[0x11, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x6, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0]) 114 | self.helix_usb.out_packet_to_endpoint_0x1(out) 115 | self.helix_usb.start_x80x10_keep_alive_thread(delay=0.1) 116 | 117 | # x1x10: start 118 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x28, 0x0, 0x0, 0x18, 0xef, 0x3, 0x1, 0x10, 0x0, 0x2, 0x0, 0x4, 0x9, 0x2], length=14): 119 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x8, 0x20, 0x10, 0x0, 0x0]) 120 | # self.helix_usb.out_packet_to_endpoint_0x1(out) 121 | self.helix_usb.start_x1x10_keep_alive_thread(delay=0.0) 122 | 123 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x11, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, 0x2], length=10): 124 | self.received_x11_on_x2 = True 125 | self.helix_usb.x1x10_cnt = 0x2 126 | out = OutPacket(data=[0xc, 0x0, 0x0, 0x28, 0x1, 0x10, 0xef, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x21, 0x0, 0x10, 0x0, 0x0]) 127 | self.helix_usb.out_packet_to_endpoint_0x1(out) 128 | 129 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x11, 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x2], length=10): 130 | self.received_x11_on_x80 = True 131 | 132 | # x1x10: start 2 133 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x11, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, 0x2, 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x50, 0x33, 0x33], length=20): 134 | out = OutPacket(data=[0x11, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0]) 135 | self.helix_usb.out_packet_to_endpoint_0x1(out) 136 | 137 | elif self.helix_usb.check_keep_alive_response(data): 138 | if data[6] == 0x1: 139 | self.alive_msg_counter[0] += 1 140 | elif data[6] == 0x2: 141 | self.alive_msg_counter[1] += 1 142 | elif data[6] == 0x80: 143 | self.alive_msg_counter[2] += 1 144 | 145 | if self.alive_msg_counter[0] > 1 and self.alive_msg_counter[1] > 1 and self.alive_msg_counter[2] > 1: 146 | log.info("Connect finished") 147 | self.helix_usb.switch_mode("RequestPresetName") 148 | else: 149 | hex_str = ''.join('0x{:x}, '.format(x) for x in data) 150 | log.warning("Unexpected message in connect mode: " + str(hex_str)) 151 | 152 | if self.reset_x1x10_done is False: 153 | if self.received_x11_on_x2 and self.received_x11_on_x80 and self.alive_msg_counter[0] > 1: 154 | log.info("RESETTING x1") 155 | self.reset_x1x10_done = True 156 | self.helix_usb.x1x10_cnt = 0x2 157 | out = OutPacket(data=[0xc, 0x0, 0x0, 0x28, 0x1, 0x10, 0xef, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x21, 0x0, 0x10, 0x0, 0x0]) 158 | self.helix_usb.out_packet_to_endpoint_0x1(out) 159 | ''' -------------------------------------------------------------------------------- /modes/reconfigure_x1.py: -------------------------------------------------------------------------------- 1 | from modes.standard import Standard 2 | from out_packet import OutPacket 3 | import logging 4 | log = logging.getLogger(__name__) 5 | 6 | 7 | class ReconfigureX1(Standard): 8 | def __init__(self, helix_usb): 9 | Standard.__init__(self, helix_usb=helix_usb, name="reconfigure_x1") 10 | 11 | def start(self): 12 | log.info('Starting mode') 13 | 14 | self.helix_usb.x1x10_cnt = 0x2 15 | 16 | data = [0xc, 0x0, 0x0, 0x28, 0x1, 0x10, 0xef, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x21, 0x0, 0x10, 0x0, 0x0] 17 | self.helix_usb.endpoint_0x1_out(data) 18 | 19 | def shutdown(self): 20 | log.info('Shutting down mode') 21 | 22 | def data_in(self, data): 23 | 24 | if self.helix_usb.my_byte_cmp(left=data, right=[0xc, 0x0, 0x0, 0x28, 0xef, 0x3, 0x1, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0], length=20): 25 | # out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x10, 0x9, 0x10, 0x0, 0x0]) 26 | # self.helix_usb.out_packet_to_endpoint_0x1(out) 27 | 28 | # self.helix_usb.start_x2x10_keep_alive_thread(delay=1.0) 29 | out = OutPacket(data=[0x11, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0]) 30 | self.helix_usb.out_packet_to_endpoint_0x1(out) 31 | 32 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x11, 0x0, 0x0, 0x18, 0xef, 0x3, 0x1, 0x10, 0x0, 0x2, 0x0, 0x4], length=12): 33 | # out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x8, 0x20, 0x10, 0x0, 0x0]) 34 | # self.helix_usb.out_packet_to_endpoint_0x1(out) 35 | self.helix_usb.start_x1x10_keep_alive_thread(delay=0.0) 36 | self.helix_usb.reconfigured_x1 = True 37 | self.helix_usb.switch_mode() 38 | 39 | elif self.helix_usb.check_keep_alive_response(data): 40 | return False # don't print incoming message to console 41 | 42 | else: 43 | hex_str = ''.join('0x{:x}, '.format(x) for x in data) 44 | log.warning("Unexpected message in connect mode: " + str(hex_str)) 45 | 46 | return True # print incoming message to console -------------------------------------------------------------------------------- /modes/request_preset.py: -------------------------------------------------------------------------------- 1 | from modes.standard import Standard 2 | import random 3 | from modules import modules 4 | from utils.formatter import format_1 5 | from utils.simple_filter import slot_splitter_2 6 | import logging 7 | import threading 8 | log = logging.getLogger(__name__) 9 | 10 | 11 | class RequestPreset(Standard): 12 | def __init__(self, helix_usb): 13 | Standard.__init__(self, helix_usb=helix_usb, name="request_preset") 14 | self.preset_data = [] 15 | self.data_requests_packages_or_whatever = [] 16 | self.num_received_1f = 0 17 | self.request_preset_session_id = 0xf4 18 | self.in_transfer = False 19 | self.wait_for_next_packet_timer = None 20 | 21 | def start(self): 22 | log.info('Starting mode') 23 | self.preset_data = [] 24 | self.num_received_1f = 0 25 | next_packet_double = self.helix_usb.preset_data_packet_double() 26 | 27 | data = [0x19, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0xc, self.helix_usb.maybe_session_no, next_packet_double[0], next_packet_double[1], 0x0, 0x1, 0x0, 0x6, 0x0, 0x9, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, self.request_preset_session_id, 0x64, 0x16, 0x65, 0xc0, 0x0, 0x0, 0x0] 28 | self.helix_usb.endpoint_0x1_out(data, silent=True) 29 | 30 | self.data_requests_packages_or_whatever = range(0x10, 0x1a) 31 | self.request_preset_session_id += 2 32 | if self.request_preset_session_id > 0xff: 33 | self.request_preset_session_id -= 0xff 34 | self.in_transfer = False 35 | self.wait_for_next_packet_timer = None 36 | 37 | def shutdown(self): 38 | log.info('Shutting down mode') 39 | 40 | def preset_info_complete(self): 41 | 42 | slots = self.preset_data.split('8213') 43 | 44 | # find first slot starting with either 06 or 08 45 | slot_1_idx = -1 46 | for i, slot in enumerate(slots): 47 | if slot.startswith('06') or slot.startswith('08'): 48 | slot_1_idx = i 49 | break 50 | 51 | if slot_1_idx == -1: 52 | return False 53 | 54 | if slot_1_idx == 0: 55 | return False 56 | 57 | # remove slots we don't understand or we don't need 58 | slots = slots[slot_1_idx-1:] 59 | slots = slots[0:20] 60 | # we must have 20 slots now 61 | if len(slots) != 20: 62 | return False 63 | 64 | if not slots[0].startswith('00'): 65 | False 66 | if not slots[9].startswith('01'): 67 | False 68 | if not slots[10].startswith('02'): 69 | False 70 | if not slots[19].startswith('03'): 71 | False 72 | 73 | assignable_slots = [1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18] 74 | for slot_idx in assignable_slots: 75 | if not (slot.startswith('06') or slot.startswith('08')): 76 | return False 77 | 78 | slot_infos = [] 79 | for slot_idx in assignable_slots: 80 | slot_data = slots[slot_idx] 81 | if slot_data == '0814c0': 82 | # print(str(slot_idx) + ' is empty') 83 | empty_slot_info = EmptySlotInfo() 84 | empty_slot_info.slot_no = slot_idx 85 | slot_infos.append(empty_slot_info) 86 | continue 87 | # print(slot_data) 88 | slot_info = parse_standard_module_slot(slot_data) 89 | if slot_info is None: 90 | slot_info = parse_amp_and_cab_slot(slot_data) 91 | 92 | if slot_info is None: 93 | slot_info = parse_dual_cab_slot(slot_data) 94 | if slot_info is None: 95 | print("ERROR: Cannot read slot info: " + str(slot_idx)) 96 | continue 97 | 98 | slot_info.slot_no = slot_idx 99 | slot_infos.append(slot_info) 100 | return slot_infos 101 | 102 | def parse_preset_data(self): 103 | # log.info("TIMER exec") 104 | self.helix_usb.maybe_session_no = random.choice(range(0x04, 0xff)) 105 | 106 | # preset_data_packet_double = self.helix_usb.preset_data_packet_double() 107 | # data_out = [0x8, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x8, self.helix_usb.maybe_session_no, preset_data_packet_double[0], preset_data_packet_double[1], 0x0] 108 | # self.helix_usb.endpoint_0x1_out(data_out, silent=True) 109 | 110 | # log.info("GOT PRESET DATA, length is: " + str(len(self.preset_data))) 111 | str_out = '' 112 | for b in self.preset_data: 113 | str_out += hex(b) + ", " 114 | str_out = str_out[:-2] 115 | nice_str = format_1(str_out) 116 | slot_data = slot_splitter_2(nice_str) 117 | # print(str_out) 118 | # print(nice_str) 119 | self.helix_usb.set_slot_info(slot_data) 120 | 121 | # splitter for the labels: 87 0A 00 0B 84 00 03 05 A9 122 | # active snapshot information: 123 | if '860600070208' in nice_str and self.helix_usb.current_snapshot != 1: 124 | self.helix_usb.set_snapshot(1) 125 | elif '860601070208' in nice_str and self.helix_usb.current_snapshot != 2: 126 | self.helix_usb.set_snapshot(2) 127 | elif '860602070208' in nice_str and self.helix_usb.current_snapshot != 3: 128 | self.helix_usb.set_snapshot(3) 129 | 130 | self.helix_usb.got_preset = True 131 | self.helix_usb.switch_mode() 132 | 133 | return True # print incoming message to console 134 | 135 | def data_in(self, data_in): 136 | 137 | if self.helix_usb.check_keep_alive_response(data_in): 138 | return False # don't print incoming message to console 139 | 140 | if data_in[6] != 0x80: 141 | hex_str = ''.join('0x{:x}, '.format(x) for x in data_in) 142 | log.error("Unexpected package while trying to read preset data: " + str(hex_str)) 143 | return True # print incoming message to console 144 | 145 | if self.helix_usb.my_byte_cmp(left=data_in, right=["XX", "XX", 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, "XX", 0x0, "XX", "XX", "XX", 0x0, 0x0], length=16): 146 | if self.wait_for_next_packet_timer is not None: 147 | self.wait_for_next_packet_timer.cancel() 148 | # log.info("TIMER cancelled") 149 | self.wait_for_next_packet_timer = None 150 | 151 | self.in_transfer = False 152 | 153 | reply_here = True 154 | if len(self.preset_data) == 0: 155 | reply_here = False 156 | 157 | 158 | # try calculating the size 159 | expected_length = data_in[1] * 255 + data_in[0] 160 | # expected_length -= 9 161 | 162 | # log.info("Expected length: " + str(expected_length)) 163 | # log.info("Real length: " + str(len(data_in) - 9)) 164 | 165 | for b in data_in[16:]: 166 | self.preset_data.append(b) 167 | 168 | if reply_here is False: 169 | # Skipping reply for first data packet 170 | # log.info('Skipping reply for first data packet') 171 | return True # print incoming message to console 172 | 173 | if data_in[1] != 2: 174 | next_packet_double_no = self.helix_usb.next_preset_data_packet_double() 175 | else: 176 | next_packet_double_no = self.helix_usb.preset_data_packet_double() 177 | data_out = [0x8, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x8, self.helix_usb.maybe_session_no, next_packet_double_no[0], next_packet_double_no[1], 0x0] 178 | self.helix_usb.endpoint_0x1_out(data_out, silent=True) 179 | 180 | self.wait_for_next_packet_timer = threading.Timer(0.02, self.parse_preset_data) 181 | self.wait_for_next_packet_timer.start() 182 | # log.info("TIMER started") 183 | return True 184 | 185 | else: 186 | hex_str = ''.join('0x{:x}, '.format(x) for x in data_in) 187 | log.warning("Unexpected message in mode: " + str(hex_str)) 188 | return True 189 | 190 | 191 | -------------------------------------------------------------------------------- /modes/request_preset_name.py: -------------------------------------------------------------------------------- 1 | from modes.standard import Standard 2 | import random 3 | import threading 4 | import logging 5 | 6 | log = logging.getLogger(__name__) 7 | 8 | 9 | class RequestPresetName(Standard): 10 | def __init__(self, helix_usb): 11 | Standard.__init__(self, helix_usb=helix_usb, name="request_preset_name") 12 | self.preset_name_data = [] 13 | self.response_watch_dog_timer = None 14 | 15 | def start(self): 16 | log.info('Starting mode') 17 | self.preset_name_data = [] 18 | preset_data_packet_double = self.helix_usb.preset_data_packet_double() 19 | data = [0x19, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x4, self.helix_usb.maybe_session_no, preset_data_packet_double[0], preset_data_packet_double[1], 0x0, 0x1, 0x0, 0x6, 20 | 0x0, 0x9, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x4, 0x4, 0x64, 0x17, 0x65, 0xc0, 0x0, 0x0, 0x0] 21 | self.helix_usb.endpoint_0x1_out(data, silent=True) 22 | self.response_watch_dog_timer = threading.Timer(0.5, self.on_name_missing, []) 23 | self.response_watch_dog_timer.start() 24 | 25 | def shutdown(self): 26 | log.info('Shutting down mode') 27 | 28 | def on_name_missing(self): 29 | log.error('Didn''t receive current preset''s name. Ending mode ' + self.name + ' without success') 30 | self.helix_usb.switch_mode() 31 | 32 | def data_in(self, data_in): 33 | if self.helix_usb.check_keep_alive_response(data_in): 34 | return False # don't print incoming message to console 35 | 36 | elif self.helix_usb.my_byte_cmp(left=data_in[23:], right=[0x0, 0x83, 0x66, 0xcd, "XX", "XX", 0x67, 0x0, 0x68, 0x86, 0x6b, 0xcd, 0x0, 0x0, 0x6c, 0xcd], length=16): 37 | # self.helix_usb.log_data_in(data_in) 38 | for b in data_in[16:]: 39 | self.preset_name_data.append(b) 40 | 41 | if data_in[1] == 0x0: 42 | slot_number_idx = 27 43 | preset_name = '' 44 | for i in range(slot_number_idx, slot_number_idx + 24): 45 | if self.preset_name_data[i] == 0x00: 46 | break 47 | preset_name += chr(self.preset_name_data[i]) 48 | 49 | # log.info("*************************** Preset Name: " + preset_name) 50 | self.helix_usb.set_preset_name(preset_name) 51 | 52 | # update session no 53 | self.helix_usb.maybe_session_no = random.choice(range(0x04, 0xff)) 54 | 55 | self.response_watch_dog_timer.cancel() 56 | self.response_watch_dog_timer = None 57 | 58 | self.helix_usb.switch_mode() 59 | 60 | return False # don't print incoming message to console 61 | 62 | else: 63 | hex_str = ''.join('0x{:x}, '.format(x) for x in data_in) 64 | log.warning("Unexpected message in mode: " + str(hex_str)) 65 | return True 66 | -------------------------------------------------------------------------------- /modes/request_preset_names.py: -------------------------------------------------------------------------------- 1 | from modes.standard import Standard 2 | from out_packet import OutPacket 3 | import logging 4 | log = logging.getLogger(__name__) 5 | 6 | 7 | class RequestPresetNames(Standard): 8 | def __init__(self, helix_usb): 9 | Standard.__init__(self, helix_usb=helix_usb, name="request_preset_names") 10 | self.preset_names_data = [] 11 | 12 | def start(self): 13 | log.info('Starting mode') 14 | self.preset_names_data = [] 15 | data = [0x1d, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0xc, 0x38, 0x10, 0x0, 0x0, 0x1, 0x0, 0x2, 16 | 0x0, 0xd, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xea, 0x64, 0x1, 0x65, 0x82, 0x6b, 0x0, 0x65, 0x2, 0x0, 17 | 0x0, 0x0] 18 | # data = [0x19, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x4, 0x1a, 0x10, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, 0x9, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xe9, 0x64, 0x0, 0x65, 0xc0, 0x0, 0x0, 0x0] 19 | # data = [0x1a, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x4, 0x9, 0x10, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, 0xa, 0x0, 0x0, 0x0, 0x83, 0x66, 0xcd, 0x3, 0xe8, 0x64, 0xcc, 0xfe, 0x65, 0x80, 0x0, 0x0] 20 | self.helix_usb.endpoint_0x1_out(data, silent=True) 21 | 22 | def shutdown(self): 23 | log.info('Shutting down mode') 24 | 25 | def parse_preset_names(self): 26 | # all_presets = '' 27 | all_data = list() 28 | for packet in self.preset_names_data: 29 | # hex_str = ''.join('0x{:x}, '.format(x) for x in packet) 30 | # all_presets += hex_str 31 | # ToDo: Move this into the append data function underneath 32 | for b in packet[16:]: 33 | all_data.append(b) 34 | 35 | pattern = [0x81, 0xcd, 0x0] 36 | indexes = [(i, i + len(pattern)) for i in range(len(all_data)) if all_data[i:i + len(pattern)] == pattern] 37 | 38 | for index in indexes: 39 | name_start_idx = index[1] 40 | program_number = all_data[name_start_idx] 41 | name = '' 42 | for j in range(name_start_idx + 6, name_start_idx + 16 + 6): 43 | if all_data[j] == 0x0: 44 | break 45 | name += chr(all_data[j]) 46 | # log.info(str(program_number) + ' - ' + name) 47 | log.info('Received preset names: ' + str(len(indexes))) 48 | 49 | def data_in(self, data_in): 50 | if self.helix_usb.check_keep_alive_response(data_in): 51 | return False # don't print incoming message to console 52 | 53 | elif self.helix_usb.my_byte_cmp(left=data_in, right=[0x8, 0x1, 0x0, 0x18, 0xef, 0x3, 0x1, 0x10, 0x0, "XX", 0x0, 0x4, "XX", 0x2, 0x0, 0x0, "XX"], length=17): 54 | # one packet 55 | self.preset_names_data.append(data_in) 56 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x8, 0x38, data_in[9]+9, 0x0, 0x0]) 57 | self.helix_usb.out_packet_to_endpoint_0x1(out, silent=True) 58 | 59 | elif self.helix_usb.my_byte_cmp(left=data_in, right=["XX", 0x0, 0x0, 0x18, 0xef, 0x3, 0x1, 0x10, 0x0, "XX", 0x0, 0x4, "XX", 0x2, 0x0, 0x0], length=16): 60 | # last packet 61 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x1, 0x10, 0xef, 0x3, 0x0, "XX", 0x0, 0x8, 0x38, data_in[9] + 9, 0x0, 0x0]) 62 | self.helix_usb.out_packet_to_endpoint_0x1(out, silent=True) 63 | 64 | self.preset_names_data.append(data_in) 65 | self.parse_preset_names() 66 | # print(self.preset_names_data) 67 | self.helix_usb.got_preset_names = True 68 | self.helix_usb.switch_mode() 69 | 70 | else: 71 | hex_str = ''.join('0x{:x}, '.format(x) for x in data_in) 72 | log.warning("Unexpected message in connect mode: " + str(hex_str)) 73 | 74 | return True # print incoming message to console -------------------------------------------------------------------------------- /modes/standard.py: -------------------------------------------------------------------------------- 1 | from out_packet import OutPacket 2 | from utils.ieee754_convert import format_1, ieee754_to_rendered_str 3 | import logging 4 | log = logging.getLogger(__name__) 5 | 6 | 7 | class Standard: 8 | def __init__(self, helix_usb, name): 9 | self.helix_usb = helix_usb 10 | self.name = name 11 | 12 | def start(self): 13 | log.info('Starting mode') 14 | 15 | def shutdown(self): 16 | log.info('Shutting down mode') 17 | 18 | def data_in(self, data): 19 | 20 | # LED COLOR CHANGE 21 | if self.helix_usb.my_byte_cmp(left=data, right=["XX", 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, "XX", 0x0, 0x4, "XX", "XX", "XX", "XX"], length=16): 22 | self.helix_usb.increase_session_quadruple_x11() 23 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x80, 0x10, 0xed, 0x3, 0x0, "XX", 0x0, 0x8, self.helix_usb.session_quadruple[0], self.helix_usb.session_quadruple[1], self.helix_usb.session_quadruple[2], self.helix_usb.session_quadruple[3]], 24 | delay=0.0) 25 | self.helix_usb.out_packet_to_endpoint_0x1(out) 26 | 27 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x17, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0], length=16): 28 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x8, 0x74, 0x77, 0x0, 0x0], 29 | delay=0.01) 30 | self.helix_usb.out_packet_to_endpoint_0x1(out) 31 | 32 | 33 | # VIEW CHANGE 34 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x23, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x13, 0x0, 0x0, 0x0, 0x82, 0x69, 0x16, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x9, 0x79, 0x19, 0x6a, 0x82, 0x76, 0xcd, 0x0, 0x13, 0x77], length=42): 35 | view_id = data[42] 36 | try: 37 | view_name = self.helix_usb.VIEWS[view_id] 38 | log.info("UI changed to: " + view_name) 39 | except KeyError: 40 | log.info("Error while trying to get view name, id is: " + str(view_id)) 41 | 42 | # UI MODE CHANGE 43 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x23, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x13, 0x0, 0x0, 0x0, 0x82, 0x69, 0x16, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x9, 0x79, 0x19, 0x6a, 0x82, 0x76, 0xcd, 0x0, 0x15, 0x77], length=42): 44 | mode_idx = data[42] 45 | if 0 <= mode_idx < 4: 46 | mode_name = self.helix_usb.UI_MODES[mode_idx] 47 | log.info("UI mode changed to: " + mode_name) 48 | else: 49 | log.info("Error while trying to get UI mode name, unknown mode_idx: " + str(mode_idx)) 50 | 51 | # HIGHLIGHTED SLOT CHANGE (Cursor moved to another position on HX Stomp) 52 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x21, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x11, 0x0, 0x0, 0x0, 0x82, 0x69, 0x27, 0x6a, 0x84, 0x52, 0x1, 0x44, 0x3, 0x79, 0x13, 0x6a, 0x82, 0x62], length=38): 53 | slot_id = data[38] 54 | log.info("Selected slot id changed to: " + str(slot_id)) 55 | 56 | # SLOT-MODULE CHANGE (Cursor moved to another position on HX Stomp) 57 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x1f, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0xf, 0x0, 0x0, 0x0, 0x82, 0x69, 0x31, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x5, 0x79, 0xa, 0x6a, 0x81, 0x62], length=38): 58 | changed_slot_idx = data[38] 59 | log.info("Requesting preset data due to slot/module update in slot: " + str(changed_slot_idx)) 60 | self.helix_usb.got_preset_name = False 61 | self.helix_usb.got_preset = False 62 | self.helix_usb.switch_mode() 63 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x8, 0x74, 0x77, 0x0, 0x0], 64 | delay=0.01) 65 | self.helix_usb.out_packet_to_endpoint_0x1(out) 66 | 67 | # IEEE VALUE CHANGE0x2b, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, 0x4d, 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x1b, 0x0, 0x0, 0x0, 0x82, 0x69, 0x1e, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x6, 0x79, 0x14, 0x6a, 0x85, 0x62, 0x4, 0x1d, 0xc3, 0x1a, 0x0, 0x1c 68 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x2b, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x1b, 0x0, 0x0, 0x0, 0x82, 0x69, 0x1e, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x6, 0x79, 0x14, 0x6a, 0x85, 0x62, "XX", 0x1d, 0xc3, 0x1a, 0x0, 0x1c], length=44): 69 | parameter_idx = data[44] 70 | value = data[47:51] 71 | value_as_hex_str = ''.join('0x{:x}, '.format(x) for x in value) 72 | if value_as_hex_str.endswith(', '): 73 | value_as_hex_str = value_as_hex_str[:-2] 74 | rendered_val = format_1(value_as_hex_str) 75 | converted_value = ieee754_to_rendered_str(rendered_val) 76 | log.info("Float value change for knob " + str(parameter_idx) + ": " + str(converted_value)) 77 | 78 | # INT VALUE CHANGE 79 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x27, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x17, 0x0, 0x0, 0x0, 0x82, 0x69, 0x1e, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x6, 0x79, 0x14, 0x6a, 0x85, 0x62, "XX", 0x1d, 0xc3, 0x1a, 0x0, 0x1c], length=44): 80 | parameter_idx = data[44] 81 | value = data[46] 82 | log.info("Int value change for knob " + str(parameter_idx) + ": " + str(value)) 83 | 84 | # TRAILS ON/OFF 85 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x27, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x17, 0x0, 0x0, 0x0, 0x82, 0x69, 0x1e, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x6, 0x79, 0x14, 0x6a, 0x85, 0x62, "XX", 0x1d, 0xc2, 0x1a, 0x0, 0x1c, 0x0, 0x77], length=46): 86 | trails_on_off = data[46] 87 | if trails_on_off == 0xc2: 88 | log.info("Trails have been switched off") 89 | elif trails_on_off == 0xc3: 90 | log.info("Trails have been switched on") 91 | else: 92 | log.warning("Unknown value for switching trails on/off: " + str(trails_on_off)) 93 | 94 | # PRESET SWITCH 95 | # if self.helix_usb.my_byte_cmp(left=data, right=[0x23, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x13, 0x0, 0x0, 0x0, 0x82, 0x69, 0x16, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x9, 0x79, 0x19, 0x6a, 0x82, 0x76, 0xcd, 0x0, 0x1c, 0x77, "XX", 0x42], length=44): 96 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x21, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x11, 0x0, 0x0, 0x0, 0x82, 0x69, 0x4, 0x6a, 0x84, 0x52, 0x1, 0x44, 0x1, 0x79, "XX", 0x6a, 0x82, 0x6b, 0x0, 0x6c], length=30): 97 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x8, 0x74, 0x77, 0x0, 0x0], 98 | delay=0.00) 99 | self.helix_usb.out_packet_to_endpoint_0x1(out) 100 | 101 | self.helix_usb.set_preset(data[40]) 102 | self.helix_usb.got_preset_name = False 103 | self.helix_usb.got_preset = False 104 | self.helix_usb.switch_mode() 105 | 106 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x21, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x11, 0x0, 0x0, 0x0, 0x82, 0x69, 0x27, 0x6a, 0x84, 0x52, 0x1, 0x44, 0x3, 0x79, 0x13, 0x6a, 0x82, 0x62], length=38): 107 | # self.helix_usb.set_preset(data[40]) 108 | # log.info("******************** PRESET: " + str(self.helix_usb.preset_no)) 109 | self.helix_usb.got_preset_name = False 110 | self.helix_usb.got_preset = False 111 | self.helix_usb.switch_mode() 112 | out = OutPacket(data=[0x8, 0x0, 0x0, 0x18, 0x2, 0x10, 0xf0, 0x3, 0x0, "XX", 0x0, 0x8, 0x74, 0x77, 0x0, 0x0], 113 | delay=0.01) 114 | self.helix_usb.out_packet_to_endpoint_0x1(out, silent=True) 115 | 116 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x21, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x11, 0x0, 0x0, 0x0, 0x82, 0x69, 0x8, 0x6a, 0x84, 0x52, 0x1, 0x44, 0x1, 0x79, 0x5, 0x6a, 0x82, 0x6b, 0x0, 0x6c, "XX"]): 117 | # Occurs while every preset switch initiated at the stomp. data[40] seems to carry the preset number 118 | pass 119 | 120 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x27, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x17, 0x0, 0x0, 0x0, 0x82, 0x69, 0x16, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x9, 0x79, 0x19, 0x6a, 0x82, 0x76, 0xcd, 0x0, 0x10, 0x77, 0xca, "XX"]): 121 | # Occurs twice while every preset switch initiated at the stomp. Usage unknown 122 | # Maybe it is related to splits and merges in the sound pipeline? 123 | pass 124 | 125 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x23, 0x0, 0x0, 0x18, 0xf0, 0x3, 0x2, 0x10, 0x0, "XX", 0x0, 0x4, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x13, 0x0, 0x0, 0x0, 0x82, 0x69, 0x16, 0x6a, 0x84, 0x52, 0x0, 0x44, 0x9, 0x79, 0x19, 0x6a, 0x82, 0x76, 0xcd, 0x0, 0x1c, 0x77, "XX"]): 126 | # Occurs once while every preset switch initiated at the stomp. data[42] seems to carry the preset number 127 | pass 128 | 129 | elif self.helix_usb.my_byte_cmp(left=data, right=[0x8, 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, "XX", 0x0, 0x8, "XX", "XX", 0x0, 0x0]): 130 | # This message signals that a preset transfer has ended in data[11] == 0x08. 131 | # Indeed, it should usually be received and properly processed by request_preset mode! 132 | # At this time it was not possible to implement a stable way working with this message because it is not 133 | # sent reliably. Thus, request_preset mode works with timer instead of waiting for this message. 134 | # But if we receive it here, we can ignore it. 135 | pass 136 | 137 | else: 138 | if self.helix_usb.check_keep_alive_response(data): 139 | return False # don't print incoming message to console 140 | 141 | hex_str = ''.join('0x{:x}, '.format(x) for x in data) 142 | log.warning("Unexpected message in mode: " + str(hex_str)) 143 | 144 | return True # print incoming message to console -------------------------------------------------------------------------------- /modules.py: -------------------------------------------------------------------------------- 1 | modules = { 2 | 'cd0184': ['Distortion', 'Kinky Boost (mono)'], 3 | 'cd0185': ['Distortion', 'Kinky Boost (stereo)'], 4 | 'cd01fe': ['Distortion', 'Deranged Master (mono)'], 5 | 'cd01ff': ['Distortion', 'Deranged Master (stereo)'], 6 | '64': ['Distortion', 'Minotaur (mono)'], 7 | '70': ['Distortion', 'Minotaur (stereo)'], 8 | 'cd012e': ['Distortion', 'Teemah! (mono)'], 9 | 'cd012f': ['Distortion', 'Teemah! (stereo)'], 10 | 'cd0223': ['Distortion', 'Heir Apparent (mono)'], 11 | 'cd0224': ['Distortion', 'Heir Apparent (stereo)'], 12 | 'cd0225': ['Distortion', 'Tone Sovereign (mono)'], 13 | 'cd0226': ['Distortion', 'Tone Sovereign (stereo)'], 14 | 'cd0229': ['Distortion', 'Alpaca Rogue (mono)'], 15 | 'cd022a': ['Distortion', 'Alpaca Rogue (stereo)'], 16 | '60': ['Distortion', 'Compulsive Drive (mono)'], 17 | '6c': ['Distortion', 'Compulsive Drive (stereo)'], 18 | 'cd020d': ['Distortion', 'Dhyana Drive (mono)'], 19 | 'cd020e': ['Distortion', 'Dhyana Drive (stereo)'], 20 | 'cd0246': ['Distortion', 'Horizon Drive (mono)'], 21 | 'cd0247': ['Distortion', 'Horizon Drive (stereo)'], 22 | '69': ['Distortion', 'Valve Driver (mono)'], 23 | '75': ['Distortion', 'Valve Driver (stereo)'], 24 | '66': ['Distortion', 'Top Secret OD (mono)'], 25 | '72': ['Distortion', 'Top Secret OD (stereo)'], 26 | '65': ['Distortion', 'Scream 808 (mono)'], 27 | '71': ['Distortion', 'Scream 808 (stereo)'], 28 | '61': ['Distortion', 'Hedgehog D9 (mono)'], 29 | '6d': ['Distortion', 'Hedgehog D9 (stereo)'], 30 | 'cd0154': ['Distortion', 'Stupor OD (mono)'], 31 | 'cd0155': ['Distortion', 'Stupor OD (stereo)'], 32 | 'cd01fc': ['Distortion', 'Deez One Vintage (mono)'], 33 | 'cd01fd': ['Distortion', 'Deez One Vintage (stereo)'], 34 | 'cd01fa': ['Distortion', 'Deez One Mod (mono)'], 35 | 'cd01fb': ['Distortion', 'Deez One Mod (stereo)'], 36 | '6a': ['Distortion', 'Vermin Dist Mono, Stereo Pro Co RAT (mono)'], 37 | '76': ['Distortion', 'Vermin Dist Mono, Stereo Pro Co RAT (stereo)'], 38 | 'cd0120': ['Distortion', 'KWB Mono, Stereo Benadrian Kowloon Walled Bunny Distortion (mono)'], 39 | 'cd0121': ['Distortion', 'KWB Mono, Stereo Benadrian Kowloon Walled Bunny Distortion (stereo)'], 40 | 'cd0234': ['Distortion', 'Legendary Drive Mono, Stereo Carvin VLD1 Legacy Drive (hi gain channel) (mono)'], 41 | 'cd0235': ['Distortion', 'Legendary Drive Mono, Stereo Carvin VLD1 Legacy Drive (hi gain channel) (stereo)'], 42 | 'cd0248': ['Distortion', 'Swedish Chainsaw Mono, Stereo Boss HM-2 Heavy Metal Distortion (MIJ) (mono)'], 43 | 'cd0249': ['Distortion', 'Swedish Chainsaw Mono, Stereo Boss HM-2 Heavy Metal Distortion (MIJ) (stereo)'], 44 | '5f': ['Distortion', 'Arbitrator Fuzz Mono, Stereo Arbiter FuzzFace (mono)'], 45 | '6b': ['Distortion', 'Arbitrator Fuzz Mono, Stereo Arbiter FuzzFace (stereo)'], 46 | 'cd0253': ['Distortion', 'Pocket Fuzz Mono, Stereo Jordan Boss Tone Fuzz (mono)'], 47 | 'cd0254': ['Distortion', 'Pocket Fuzz Mono, Stereo Jordan Boss Tone Fuzz (stereo)'], 48 | 'cd0236': ['Distortion', 'Bighorn Fuzz Mono, Stereo 73 Electro-Harmonix RamGerman Ubersonic Guitar Bogner Ueberschall (mono)s Head Big Muff Pi (mono)'], 49 | 'cd0237': ['Distortion', 'Bighorn Fuzz Mono, Stereo 73 Electro-Harmonix Ram''s Head Big Muff Pi (stereo)'], 50 | '67': ['Distortion', 'Triangle Fuzz Mono, Stereo Electro-Harmonix Big Muff Pi (mono)'], 51 | '73': ['Distortion', 'Triangle Fuzz Mono, Stereo Electro-Harmonix Big Muff Pi (stereo)'], 52 | 'cd0251': ['Distortion', 'Ballistic Fuzz Mono, Stereo Euthymia ICBM Fuzz (mono)'], 53 | 'cd0252': ['Distortion', 'Ballistic Fuzz Mono, Stereo Euthymia ICBM Fuzz (stereo)'], 54 | '62': ['Distortion', 'Industrial Fuzz Mono, Stereo Z.Vex Fuzz Factory (mono)'], 55 | '6e': ['Distortion', 'Industrial Fuzz Mono, Stereo Z.Vex Fuzz Factory (stereo)'], 56 | '68': ['Distortion', 'Tycoctavia Fuzz Mono, Stereo Tycobrahe Octavia (mono)'], 57 | '74': ['Distortion', 'Tycoctavia Fuzz Mono, Stereo Tycobrahe Octavia (stereo)'], 58 | 'cd0140': ['Distortion', 'Wringer Fuzz Mono, Stereo Garbage''s modded BOSS FZ-2 (mono)'], 59 | 'cd0141': ['Distortion', 'Wringer Fuzz Mono, Stereo Garbage''s modded BOSS FZ-2 (stereo)'], 60 | 'cd0182': ['Distortion', 'Thrifter Fuzz Mono, Stereo Line 6 Original (mono)'], 61 | 'cd0183': ['Distortion', 'Thrifter Fuzz Mono, Stereo Line 6 Original (stereo)'], 62 | 'cd022b': ['Distortion', 'Xenomorph Fuzz Mono, Stereo Subdecay Harmonic Antagonizer (mono)'], 63 | 'cd022c': ['Distortion', 'Xenomorph Fuzz Mono, Stereo Subdecay Harmonic Antagonizer (stereo)'], 64 | '63': ['Distortion', 'Megaphone Mono, Stereo Megaphone (mono)'], 65 | '6f': ['Distortion', 'Megaphone Mono, Stereo Megaphone (stereo)'], 66 | 'cd0122': ['Distortion', 'Bitcrusher Mono, Stereo Line 6 Original (mono)'], 67 | 'cd0123': ['Distortion', 'Bitcrusher Mono, Stereo Line 6 Original (stereo)'], 68 | 'cd0209': ['Distortion', 'Ampeg Scrambler Mono, Stereo Ampeg Scrambler Bass Overdrive (mono)'], 69 | 'cd020a': ['Distortion', 'Ampeg Scrambler Mono, Stereo Ampeg Scrambler Bass Overdrive (stereo)'], 70 | 'cd020b': ['Distortion', 'ZeroAmp Bass DI Mono, Stereo Tech 21 SansAmp Bass Driver DI V1 (mono)'], 71 | 'cd020c': ['Distortion', 'ZeroAmp Bass DI Mono, Stereo Tech 21 SansAmp Bass Driver DI V1 (stereo)'], 72 | 'cd015f': ['Distortion', 'Obsidian 7000 Mono, Stereo Darkglass Electronics Microtubes B7K Ultra (mono)'], 73 | 'cd0160': ['Distortion', 'Obsidian 7000 Mono, Stereo Darkglass Electronics Microtubes B7K Ultra (stereo)'], 74 | 'cd01b2': ['Distortion', 'Tube Drive Legacy Chandler Tube Driver (legacy)'], 75 | 'cd01af': ['Distortion', 'Screamer Legacy Ibanez Tube Screamer (legacy)'], 76 | 'cd01ad': ['Distortion', 'Overdrive Legacy DOD Overdrive/Preamp 250 (legacy)'], 77 | 'cd01a3': ['Distortion', 'Classic Dist Legacy ProCo RAT (legacy)'], 78 | 'cd01a7': ['Distortion', 'Heavy Dist Legacy BOSS Metal Zone (legacy)'], 79 | 'cd01a4': ['Distortion', 'Colordrive Legacy Colorsound Overdriver (legacy)'], 80 | 'cd01a2': ['Distortion', 'Buzz Saw Legacy Maestro Fuzz Tone (legacy)'], 81 | 'cd01a5': ['Distortion', 'Facial Fuzz Legacy Arbiter Fuzz Face (legacy)'], 82 | 'cd01a9': ['Distortion', 'Jumbo Fuzz Legacy Vox Tone Bender (legacy)'], 83 | 'cd01a6': ['Distortion', 'Fuzz Pi (legacy)'], 84 | 'cd01a8': ['Distortion', 'Jet Fuzz (legacy)'], 85 | 'cd01ab': ['Distortion', 'L6 Drive (legacy)'], 86 | 'cd01aa': ['Distortion', 'L6 Distortion (legacy)'], 87 | 'cd01b0': ['Distortion', 'Sub Oct Fuzz (legacy)'], 88 | 'cd01ac': ['Distortion', 'Octave Fuzz (legacy)'], 89 | '77': ['Dynamic', 'Deluxe Comp (mono)'], 90 | '7c': ['Dynamic', 'Deluxe Comp (stereo)'], 91 | '79': ['Dynamic', 'Red Squeeze (mono)'], 92 | '7e': ['Dynamic', 'Red Squeeze (stereo)'], 93 | 'cd01dc': ['Dynamic', 'Kinky Comp (mono)'], 94 | 'cd01dd': ['Dynamic', 'Kinky Comp (stereo)'], 95 | 'cd022d': ['Dynamic', 'Rochester Comp (mono)'], 96 | 'cd022e': ['Dynamic', 'Rochester Comp (stereo)'], 97 | '78': ['Dynamic', 'LA Studio Comp (mono)'], 98 | '7d': ['Dynamic', 'LA Studio Comp (stereo)'], 99 | 'cd016f': ['Dynamic', '3-Band Comp (mono)'], 100 | 'cd0170': ['Dynamic', '3-Band Comp (stereo)'], 101 | '7b': ['Dynamic', 'Noise Gate (mono)'], 102 | 'cc80': ['Dynamic', 'Noise Gate (stereo)'], 103 | '7a': ['Dynamic', 'Hard Gate (mono)'], 104 | '7f': ['Dynamic', 'Hard Gate (stereo)'], 105 | 'cd0255': ['Dynamic', 'Horizon Gate (mono)'], 106 | 'cd0256': ['Dynamic', 'Horizon Gate (stereo)'], 107 | 'cd0169': ['Dynamic', 'Autoswell (mono)'], 108 | 'cd016a': ['Dynamic', 'Autoswell (stereo)'], 109 | 'Needs to be redone!!!! One legacy model is missing': ['', ' (legacy)'], 110 | 'cd01b1': ['', ' (legacy)'], 111 | 'cd01ae': ['', ' (legacy)'], 112 | 'cd019f': ['', ' (legacy)'], 113 | 'cd01a0': ['', ' (legacy)'], 114 | 'cd01b3': ['', ' (legacy)'], 115 | 'cd01b4': ['', ' (legacy)'], 116 | 'cc84': ['EQ', 'Simple EQ (mono)'], 117 | 'cc88': ['EQ', 'Simple EQ (stereo)'], 118 | 'cc82': ['EQ', 'Low and High Cut (mono)'], 119 | 'cc86': ['EQ', 'Low and High Cut (stereo)'], 120 | 'cd020f': ['EQ', 'Low High Shelf (mono)'], 121 | 'cd0210': ['EQ', 'Low High Shelf (stereo)'], 122 | 'cc83': ['EQ', 'Parametric (mono)'], 123 | 'cc87': ['EQ', 'Parametric (stereo)'], 124 | 'cd0211': ['EQ', 'Tilt (mono)'], 125 | 'cd0212': ['EQ', 'Tilt (stereo)'], 126 | 'cc81': ['EQ', '10 Band Graphic (mono)'], 127 | 'cc85': ['EQ', '10 Band Graphic (stereo)'], 128 | 'cd0143': ['EQ', 'Cali Q Graphic (mono)'], 129 | 'cd0144': ['EQ', 'Cali Q Graphic (stereo)'], 130 | 'cd0244': ['EQ', 'Acoustic Sim (mono)'], 131 | 'cd0245': ['EQ', 'Acoustic Sim (stereo)'], 132 | 'cca3': ['Modulation', 'Optical Trem (mono)'], 133 | 'ccb3': ['Modulation', 'Optical Trem (stereo)'], 134 | 'cca2': ['Modulation', '60s Bias Trem (mono)'], 135 | 'ccb2': ['Modulation', '60s Bias Trem (stereo)'], 136 | 'cd0124': ['Modulation', 'Tremolo/Autopan (mono)'], 137 | 'cd0125': ['Modulation', 'Tremolo/Autopan (stereo)'], 138 | 'cd013e': ['Modulation', 'Harmonic Tremolo (mono)'], 139 | 'cd013f': ['Modulation', 'Harmonic Tremolo (stereo)'], 140 | 'cd0186': ['Modulation', 'Bleat Chop Trem (mono)'], 141 | 'cd0187': ['Modulation', 'Bleat Chop Trem (stereo)'], 142 | 'cc9f': ['Modulation', 'Script Mod Phase (mono)'], 143 | 'ccab': ['Modulation', 'Script Mod Phase (stereo)'], 144 | 'cd022f': ['Modulation', 'Pebble Phaser (mono)'], 145 | 'cd0230': ['Modulation', 'Pebble Phaser (stereo)'], 146 | 'cca0': ['Modulation', 'Ubiquitous Vibe (mono)'], 147 | 'ccac': ['Modulation', 'Ubiquitous Vibe (stereo)'], 148 | 'cd0126': ['Modulation', 'Deluxe Phaser (mono)'], 149 | 'cd0127': ['Modulation', 'Deluxe Phaser (stereo)'], 150 | 'cc9d': ['Modulation', 'Gray Flanger (mono)'], 151 | 'cca9': ['Modulation', 'Gray Flanger (stereo)'], 152 | 'cc9e': ['Modulation', 'Harmonic Flanger (mono)'], 153 | 'ccaa': ['Modulation', 'Harmonic Flanger (stereo)'], 154 | 'cc9c': ['Modulation', 'Courtesan Flange (mono)'], 155 | 'cca8': ['Modulation', 'Courtesan Flange (stereo)'], 156 | 'cd0130': ['Modulation', 'Dynamix Flanger (mono)'], 157 | 'cd0131': ['Modulation', 'Dynamix Flanger (stereo)'], 158 | 'cc9b': ['Modulation', 'Chorus (mono)'], 159 | 'cca6': ['Modulation', 'Chorus (stereo)'], 160 | 'cc9a': ['Modulation', '70s Chorus (mono)'], 161 | 'cca5': ['Modulation', '70s Chorus (stereo)'], 162 | 'cd0177': ['Modulation', 'PlastiChorus (mono)'], 163 | 'cd0178': ['Modulation', 'PlastiChorus (stereo)'], 164 | 'cca7': ['Modulation', 'Trinity Chorus (stereo)'], 165 | 'cca4': ['Modulation', 'Bubble Vibrato (mono)'], 166 | 'ccb4': ['Modulation', 'Bubble Vibrato (stereo)'], 167 | 'ccb1': ['Modulation', 'Vibe Rotary (stereo)'], 168 | 'ccaf': ['Modulation', '122 Rotary (stereo)'], 169 | 'ccb0': ['Modulation', '145 Rotary (stereo)'], 170 | 'cd0188': ['Modulation', 'Double Take Mono, Stereo Line 6 Original (mono)'], 171 | 'cd0189': ['Modulation', 'Double Take Mono, Stereo Line 6 Original (stereo)'], 172 | 'cd0240': ['Modulation', 'Poly Detune Mono Line 6 Original (mono)'], 173 | 'cca1': ['Modulation', 'AM Ring Mod Mono, Stereo Line 6 Original (mono)'], 174 | 'ccad': ['Modulation', 'AM Ring Mod Mono, Stereo Line 6 Original (stereo)'], 175 | 'ccae': ['Modulation', 'Pitch Ring Mod (stereo)'], 176 | 'cd01d2': ['Modulation', 'Pattern Tremolo Legacy Line 6 Original (legacy)'], 177 | 'cd01d1': ['Modulation', 'Panner Legacy Line 6 Original (legacy)'], 178 | 'cd01ca': ['Modulation', 'Bias Tremolo Legacy 1960 Vox AC-15 Tremolo (legacy)'], 179 | 'cd01cf': ['Modulation', 'Opto Tremolo Legacy 1964 Fender Deluxe Reverb (legacy)'], 180 | 'cd01d8': ['Modulation', 'Panned Phaser Legacy Ibanez Flying Pan (legacy)'], 181 | 'cd01d0': ['Modulation', 'Barberpole Legacy Line 6 Original (legacy)'], 182 | 'cd01c9': ['Modulation', 'Dual Phaser Legacy Mu-Tron Bi-Phase (legacy)'], 183 | 'cd01cc': ['Modulation', 'U-Vibe Legacy Shin-ei Uni-Vibe (legacy)'], 184 | 'cd01da': ['Modulation', 'Phaser Legacy MXR Phase 90 (legacy)'], 185 | 'cd01d3': ['Modulation', 'Pitch Vibrato Legacy BOSS VB-2 (legacy)'], 186 | 'cd01d4': ['Modulation', 'Dimension Legacy Roland Dimension D (legacy)'], 187 | 'cd01cb': ['Modulation', 'Analog Chorus Legacy BOSS CE-1 (legacy)'], 188 | 'cd01c7': ['Modulation', 'Tri Chorus Legacy Dytronics Tri-Stereo Chorus (legacy)'], 189 | 'cd01d9': ['Modulation', 'Analog Flanger Legacy MXR Flanger (legacy)'], 190 | 'cd01c8': ['Modulation', 'Jet Flanger Legacy A/DA Flanger (legacy)'], 191 | 'cd01ce': ['Modulation', 'AC Flanger Legacy MXR Flanger (legacy)'], 192 | 'cd01c6': ['Modulation', '80A Flanger Legacy A/DA Flanger (legacy)'], 193 | 'cd01c5': ['Modulation', 'Frequency Shift Legacy Line 6 Original (legacy)'], 194 | 'cd01cd': ['Modulation', 'Ring Modulator Legacy Line 6 Original (legacy)'], 195 | 'cd01d5': ['Modulation', 'Rotary Drum Legacy Fender Vibratone (legacy)'], 196 | 'cd01d6': ['Modulation', 'Rotary Drum/Horn (legacy)'], 197 | 'cd01d7': ['Modulation', ' (legacy)'], 198 | '50': ['Delay', 'Simple Delay Mono, Stereo Line 6 Original (mono)'], 199 | '5c': ['Delay', 'Simple Delay Mono, Stereo Line 6 Original (stereo)'], 200 | '4f': ['Delay', 'Mod/Chorus Echo Mono, Stereo Line 6 Original (mono)'], 201 | '58': ['Delay', 'Mod/Chorus Echo Mono, Stereo Line 6 Original (stereo)'], 202 | 'cd011f': ['Delay', 'Dual Delay (stereo)'], 203 | '59': ['Delay', 'Multitap 4 (stereo)'], 204 | '5a': ['Delay', 'Multitap 6 (stereo)'], 205 | '5b': ['Delay', 'Ping Pong (stereo)'], 206 | '51': ['Delay', 'Sweep Echo Mono, Stereo Line 6 Original (mono)'], 207 | '5d': ['Delay', 'Sweep Echo Mono, Stereo Line 6 Original (stereo)'], 208 | '4d': ['Delay', 'Ducked Delay Mono, Stereo TC Electronic 2290 (mono)'], 209 | '55': ['Delay', 'Ducked Delay Mono, Stereo TC Electronic 2290 (stereo)'], 210 | 'cd011d': ['Delay', 'Reverse Delay Mono, Stereo Line 6 Original (mono)'], 211 | 'cd011e': ['Delay', 'Reverse Delay Mono, Stereo Line 6 Original (stereo)'], 212 | 'cd014b': ['Delay', 'Vintage Digital Mono, Stereo Line 6 Original (mono)'], 213 | 'cd014c': ['Delay', 'Vintage Digital Mono, Stereo Line 6 Original (stereo)'], 214 | 'cd015b': ['Delay', 'Vintage Swell Mono, Stereo Line 6 Original (mono)'], 215 | 'cd015c': ['Delay', 'Vintage Swell Mono, Stereo Line 6 Original (stereo)'], 216 | 'cd014d': ['Delay', 'Pitch Echo Mono, Stereo Line 6 Original (mono)'], 217 | 'cd014e': ['Delay', 'Pitch Echo Mono, Stereo Line 6 Original (stereo)'], 218 | '52': ['Delay', 'Transistor Tape Mono, Stereo Maestro Echoplex EP-3 (mono)'], 219 | '5e': ['Delay', 'Transistor Tape Mono, Stereo Maestro Echoplex EP-3 (stereo)'], 220 | 'cd018a': ['Delay', 'Cosmos Echo Mono, Stereo Roland RE-201 Space Echo (mono)'], 221 | 'cd018b': ['Delay', 'Cosmos Echo Mono, Stereo Roland RE-201 Space Echo (stereo)'], 222 | '57': ['Delay', 'Harmony Delay (stereo)'], 223 | '4c': ['Delay', 'Bucket Brigade Mono, Stereo BOSS DM-2 (mono)'], 224 | '54': ['Delay', 'Bucket Brigade Mono, Stereo BOSS DM-2 (stereo)'], 225 | '4b': ['Delay', 'Adriatic Delay Mono, Stereo BOSS DM-2 w/ Adrian Mod (mono)'], 226 | '53': ['Delay', 'Adriatic Delay Mono, Stereo BOSS DM-2 w/ Adrian Mod (stereo)'], 227 | 'cd0159': ['Delay', 'Adriatic Swell Mono, Stereo Line 6 Original (mono)'], 228 | 'cd015a': ['Delay', 'Adriatic Swell Mono, Stereo Line 6 Original (stereo)'], 229 | '4e': ['Delay', 'Elephant Man Mono, Stereo Electro-Harmonix Deluxe Memory Man (mono)'], 230 | '56': ['Delay', 'Elephant Man Mono, Stereo Electro-Harmonix Deluxe Memory Man (stereo)'], 231 | 'cd01e8': ['Delay', 'Multi Pass Mono, Stereo Line 6 Original (mono)'], 232 | 'cd01e9': ['Delay', 'Multi Pass Mono, Stereo Line 6 Original (stereo)'], 233 | 'cd0243': ['Delay', 'Poly Sustain Mono Line 6 Original (mono)'], 234 | 'cd0238': ['Delay', 'Glitch Delay Mono, Stereo Line 6 Original (mono)'], 235 | 'cd0239': ['Delay', 'Glitch Delay Mono, Stereo Line 6 Original (stereo)'], 236 | 'cd0198': ['Delay', 'Ping Pong Legacy Line 6 Original (legacy)'], 237 | 'cd0194': ['Delay', 'Dynamic Legacy TC Electronic 2290 (legacy)'], 238 | 'cd019a': ['Delay', 'Stereo Legacy Line 6 Original (legacy)'], 239 | 'cd0192': ['Delay', 'Digital Legacy Line 6 Original (legacy)'], 240 | 'cd0193': ['Delay', 'Dig w/Mod Legacy Line 6 Original (legacy)'], 241 | 'cd0199': ['Delay', 'Reverse Legacy Line 6 Original (legacy)'], 242 | 'cd0196': ['Delay', 'Lo Res Legacy Line 6 Original (legacy)'], 243 | 'cd019d': ['Delay', 'Tube Echo (legacy)'], 244 | 'cd019c': ['Delay', 'Tape Echo (legacy)'], 245 | 'cd019b': ['Delay', 'Sweep Echo (legacy)'], 246 | 'cd0195': ['Delay', 'Echo Platter (legacy)'], 247 | 'cd018f': ['Delay', 'Analog Echo (legacy)'], 248 | 'cd0190': ['Delay', 'Analog w/Mod (legacy)'], 249 | 'cd0191': ['Delay', 'Auto-Volume Echo (legacy)'], 250 | 'cd0197': ['Delay', 'Multi-Head (legacy)'], 251 | 'cd01ea': ['Reverb', 'Glitz (mono)'], 252 | 'cd01eb': ['Reverb', 'Glitz (stereo)'], 253 | 'cd01f0': ['Reverb', 'Ganymede (mono)'], 254 | 'cd01ec': ['Reverb', 'Ganymede (stereo)'], 255 | 'cd01f3': ['Reverb', 'Searchlights (mono)'], 256 | 'cd01ed': ['Reverb', 'Searchlights (stereo)'], 257 | 'cd01f1': ['Reverb', 'Plateaux (mono)'], 258 | 'cd01f2': ['Reverb', 'Plateaux (stereo)'], 259 | 'cd01ee': ['Reverb', 'Double Tank (mono)'], 260 | 'cd01ef': ['Reverb', 'Double Tank (stereo)'], 261 | 'ccf6': ['Reverb', 'Plate (legacy)'], 262 | 'ccf7': ['Reverb', 'Room (legacy)'], 263 | 'ccf0': ['Reverb', 'Chamber (legacy)'], 264 | 'ccf3': ['Reverb', 'Hall (legacy)'], 265 | 'ccf2': ['Reverb', 'Echo (legacy)'], 266 | 'ccf9': ['Reverb', 'Tile (legacy)'], 267 | 'ccef': ['Reverb', 'Cave (legacy)'], 268 | 'ccf1': ['Reverb', 'Ducking (legacy)'], 269 | 'ccf4': ['Reverb', 'Octo (legacy)'], 270 | 'ccee': ['Reverb', '63 Spring (legacy)'], 271 | 'ccf8': ['Reverb', 'Spring (legacy)'], 272 | 'ccf5': ['Reverb', 'Particle Verb (legacy)'], 273 | 'ccb6': ['Pitch/Synth', 'Pitch Wham (mono)'], 274 | 'ccb8': ['Pitch/Synth', 'Pitch Wham (stereo)'], 275 | 'ccb7': ['Pitch/Synth', 'Twin Harmony (mono)'], 276 | 'ccb9': ['Pitch/Synth', 'Twin Harmony (stereo)'], 277 | 'cd0128': ['Pitch/Synth', 'Simple Pitch (mono)'], 278 | 'cd0129': ['Pitch/Synth', 'Simple Pitch (stereo)'], 279 | 'cd012a': ['Pitch/Synth', 'Dual Pitch (mono)'], 280 | 'cd012b': ['Pitch/Synth', 'Dual Pitch (stereo)'], 281 | 'cd0103': ['Pitch/Synth', ' (stereo)'], 282 | 'cd023d': ['Pitch/Synth', 'Poly Pitch (mono)'], 283 | 'cd023f': ['Pitch/Synth', 'Poly Wham (mono)'], 284 | 'cd023e': ['Pitch/Synth', 'Poly Capo (mono)'], 285 | 'cd0242': ['Pitch/Synth', '12 String (mono)'], 286 | 'cd0179': ['Pitch/Synth', '3 Note Generator (mono)'], 287 | 'cd017a': ['Pitch/Synth', '3 Note Generator (stereo)'], 288 | 'cd017b': ['Pitch/Synth', '4 OSC Generator (mono)'], 289 | 'cd0175': ['Pitch/Synth', '4 OSC Generator (stereo)'], 290 | 'cd019e': ['Pitch/Synth', 'Bass Octaver (legacy)'], 291 | 'cd01db': ['Pitch/Synth', 'Smart Harmony (legacy)'], 292 | 'cd01b9': ['Pitch/Synth', 'Octi Synth (legacy)'], 293 | 'cd01be': ['Pitch/Synth', 'Synth O Matic (legacy)'], 294 | 'cd01b5': ['Pitch/Synth', 'Attack Synth (legacy)'], 295 | 'cd01bf': ['Pitch/Synth', 'Synth String (legacy)'], 296 | 'cd01b7': ['Pitch/Synth', 'Growler (legacy)'], 297 | 'cc89': ['Filter', 'Mutant Filter (mono)'], 298 | 'cc8b': ['Filter', 'Mutant Filter (stereo)'], 299 | 'cc8a': ['Filter', 'Mystery Filter (mono)'], 300 | 'cc8c': ['Filter', 'Mystery Filter (stereo)'], 301 | 'cd012c': ['Filter', 'Autofilter (mono)'], 302 | 'cd012d': ['Filter', 'Autofilter (stereo)'], 303 | 'cd0213': ['Filter', 'Asheville Pattrn (mono)'], 304 | 'cd0214': ['Filter', 'Asheville Pattrn (stereo)'], 305 | 'cd01c3': ['Filter', 'Voice Box (legacy)'], 306 | 'cd01c4': ['Filter', 'V Tron (legacy)'], 307 | 'cd01ba': ['Filter', 'Q Filter (legacy)'], 308 | 'cd01bb': ['Filter', 'Seeker (legacy)'], 309 | 'cd01b8': ['Filter', 'Obi Wah (legacy)'], 310 | 'cd01c2': ['Filter', 'Tron Up (legacy)'], 311 | 'cd01c1': ['Filter', 'Tron Down (legacy)'], 312 | 'cd01c0': ['Filter', 'Throbber (legacy)'], 313 | 'cd01bc': ['Filter', 'Slow Filter (legacy)'], 314 | 'cd01bd': ['Filter', 'Spin Cycle (legacy)'], 315 | 'cd01b6': ['Filter', 'Comet Trails (legacy)'], 316 | 'cd0110': ['Wah', 'UK Wah 846 (mono)'], 317 | 'cd011a': ['Wah', 'UK Wah 846 (stereo)'], 318 | 'cd010e': ['Wah', 'Teardrop 310 (mono)'], 319 | 'cd0118': ['Wah', 'Teardrop 310 (stereo)'], 320 | 'cd010d': ['Wah', 'Fassel (mono)'], 321 | 'cd0117': ['Wah', 'Fassel (stereo)'], 322 | 'cd0112': ['Wah', 'Weeper (mono)'], 323 | 'cd011c': ['Wah', 'Weeper (stereo)'], 324 | 'cd010a': ['Wah', 'Chrome (mono)'], 325 | 'cd0114': ['Wah', 'Chrome (stereo)'], 326 | 'cd0109': ['Wah', 'Chrome Custom (mono)'], 327 | 'cd0113': ['Wah', 'Chrome Custom (stereo)'], 328 | 'cd010f': ['Wah', 'Throaty (mono)'], 329 | 'cd0119': ['Wah', 'Throaty (stereo)'], 330 | 'cd0111': ['Wah', 'Vetta Wah (mono)'], 331 | 'cd011b': ['Wah', 'Vetta Wah (stereo)'], 332 | 'cd010b': ['Wah', 'Colorful (mono)'], 333 | 'cd0115': ['Wah', 'Colorful (stereo)'], 334 | 'cd010c': ['Wah', 'Conductor (mono)'], 335 | 'cd0116': ['Wah', 'Conductor (stereo)'], 336 | 'cd0105': ['Vol/Pan', 'Volume Pedal (mono)'], 337 | 'cd0108': ['Vol/Pan', 'Volume Pedal (stereo)'], 338 | 'cd0104': ['Vol/Pan', 'Gain (mono)'], 339 | 'cd0106': ['Vol/Pan', 'Gain (stereo)'], 340 | 'cd0107': ['Vol/Pan', 'Pan (stereo)'], 341 | 'cd018c': ['Vol/Pan', 'Stereo Width (stereo)'], 342 | 'cd024c': ['Vol/Pan', 'Stereo Imager (stereo)'], 343 | '2c': ['Amp', 'WhoWatt 100 (mono)'], 344 | '23': ['Amp', 'Soup Pro (mono)'], 345 | '24': ['Amp', 'Stone Age 185 (mono)'], 346 | 'cd018d': ['Amp', 'Voltage Queen (mono)'], 347 | '26': ['Amp', 'Tweed Blues Nrm (mono)'], 348 | '25': ['Amp', 'Tweed Blues Brt (mono)'], 349 | 'cd021d': ['Amp', 'Fullerton Nrm (mono)'], 350 | 'cd021b': ['Amp', 'Fullerton Brt (mono)'], 351 | 'cd021c': ['Amp', 'Fullerton Jump (mono)'], 352 | 'cd0217': ['Amp', 'Grammatico Nrm (mono)'], 353 | 'cd0215': ['Amp', 'Grammatico Brt (mono)'], 354 | 'cd0216': ['Amp', 'Grammatico Jump (mono)'], 355 | '2b': ['Amp', 'US Small Tweed (mono)'], 356 | 'cd024f': ['Amp', 'US Princess (mono)'], 357 | '27': ['Amp', 'US Deluxe Nrm (mono)'], 358 | '28': ['Amp', 'US Deluxe Vib (mono)'], 359 | '29': ['Amp', 'US Double Nrm (mono)'], 360 | '2a': ['Amp', 'US Double Vib (mono)'], 361 | '1d': ['Amp', 'Mail Order Twin Guitar Silvertone 1484 (mono)'], 362 | '13': ['Amp', 'Divided Duo Guitar 13 JRT 9/15 (mono)'], 363 | '18': ['Amp', 'Interstate Zed Guitar Dr Z Route 66 (mono)'], 364 | 'cd0180': ['Amp', 'Derailed Ingrid Guitar Trainwreck Circuits Express (mono)'], 365 | '19': ['Amp', 'Jazz Rivet 120 Guitar Roland JC-120 Jazz Chorus (mono)'], 366 | '14': ['Amp', 'Essex A15 Guitar Vox AC-15 (mono)'], 367 | '15': ['Amp', 'Essex A30 Guitar Vox AC-30 with top boost (mono)'], 368 | '08': ['Amp', 'A30 Fawn Nrm Guitar Vox AC-30 Fawn (normal channel) (mono)'], 369 | '07': ['Amp', 'A30 Fawn Brt Guitar Vox AC-30 Fawn (bright channel) (mono)'], 370 | 'cd0132': ['Amp', 'Matchstick Ch1 Guitar Matchless DC30 (channel 1) (mono)'], 371 | 'cd0133': ['Amp', 'Matchstick Ch2 Guitar Matchless DC30 (channel 2) (mono)'], 372 | 'cd0134': ['Amp', 'Matchstick Jump Guitar Matchless DC30 (jumped) (mono)'], 373 | '1e': ['Amp', 'Mandarin 80 Guitar Orange OR80 (mono)'], 374 | '0c': ['Amp', 'Brit J45 Nrm Guitar Marshall JTM-45 (normal channel) (mono)'], 375 | '0b': ['Amp', 'Brit J45 Brt Guitar Marshall JTM-45 (bright channel) (mono)'], 376 | 'cd01de': ['Amp', 'Brit Trem Nrm Guitar Marshall JTM-50 (normal channel) (mono)'], 377 | 'cd01df': ['Amp', 'Brit Trem Brt Guitar Marshall JTM-50 (bright channel) (mono)'], 378 | 'cd01e0': ['Amp', 'Brit Trem Jump Guitar Marshall JTM-50 (jumped) (mono)'], 379 | '11': ['Amp', 'Brit Plexi Nrm Guitar Marshall Super Lead 100 (normal channel) (mono)'], 380 | '0f': ['Amp', 'Brit Plexi Brt Guitar Marshall Super Lead 100 (bright channel) (mono)'], 381 | '10': ['Amp', 'Brit Plexi Jump Guitar Marshall Super Lead 100 (jumped) (mono)'], 382 | '0e': ['Amp', 'Brit P75 Nrm Guitar Park 75 (normal channel) (mono)'], 383 | '0d': ['Amp', 'Brit P75 Brt Guitar Park 75 (bright channel) (mono)'], 384 | '0a': ['Amp', 'Brit 2204 Guitar Marshall JCM-800 (mono)'], 385 | 'cd0200': ['Amp', 'Placater Clean Guitar Friedman BE-100 (clean channel) (mono)'], 386 | 'cd01f4': ['Amp', 'Placater Dirty Guitar Friedman BE-100 (BE/HBE channel) (mono)'], 387 | 'cd01e4': ['Amp', 'Cartographer Guitar Ben Adrian Cartographer (mono)'], 388 | '16': ['Amp', 'German Mahadeva Guitar Bogner Shiva (mono)'], 389 | '17': ['Amp', 'German Ubersonic Guitar Bogner Ueberschall (mono)'], 390 | 'cd0202': ['Amp', 'Cali Texas Ch1 Guitar MESA/Boogie Lone Star (clean channel) (mono)'], 391 | 'cd01f6': ['Amp', 'Cali Texas Ch2 Guitar MESA/Boogie Lone Star (drive channel) (mono)'], 392 | 'cd0139': ['Amp', 'Cali IV Rhythm 1 Guitar MESA/Boogie Mark IV (channel I) (mono)'], 393 | 'cd013a': ['Amp', 'Cali IV Rhythm 2 Guitar MESA/Boogie Mark IV (channel II) (mono)'], 394 | 'cd0138': ['Amp', 'Cali IV Lead Guitar MESA/Boogie Mark IV (lead channel) (mono)'], 395 | '12': ['Amp', 'Cali Rectifire Guitar MESA/Boogie Dual Rectifier (mono)'], 396 | 'cd014f': ['Amp', 'Archetype Clean Guitar Paul Reed Smith Archon (clean channel) (mono)'], 397 | 'cd0150': ['Amp', 'Archetype Lead Guitar Paul Reed Smith Archon (lead channel) (mono)'], 398 | '09': ['Amp', 'ANGL Meteor Guitar ENGL Fireball 100 (mono)'], 399 | '20': ['Amp', 'Solo Lead Clean Guitar Soldano SLO-100 (clean channel) (mono)'], 400 | '21': ['Amp', 'Solo Lead Crunch Guitar Soldano SLO-100 (crunch channel) (mono)'], 401 | '22': ['Amp', 'Solo Lead OD Guitar Soldano SLO-100 (overdrive channel) (mono)'], 402 | '1f': ['Amp', 'PV Panama Guitar Peavey 5150 (mono)'], 403 | 'cd0231': ['Amp', 'Revv Gen Purple Guitar Revv Generator 120 (purple/gain ch. 3) (mono)'], 404 | 'cd0221': ['Amp', 'Revv Gen Red Guitar Revv Generator 120 (red/high gain ch. 4) (mono)'], 405 | 'cd0259': ['Amp', 'Das Benzin Mega Guitar Diezel VH4 (mega chanel) (mono)'], 406 | 'cd0257': ['Amp', 'Das Benzin Lead Guitar Diezel VH4 (lead chanel) (mono)'], 407 | '1b': ['Amp', 'Line 6 Elektrik Guitar Line 6 Original (mono)'], 408 | '1a': ['Amp', 'Line 6 Doom Guitar Line 6 Original (mono)'], 409 | '1c': ['Amp', 'Line 6 Epic Guitar Line 6 Original (mono)'], 410 | 'cd0142': ['Amp', 'Line 6 2204 Mod Guitar Line 6 Original (mono)'], 411 | 'cd0148': ['Amp', 'Line 6 Fatality Guitar Line 6 Original (mono)'], 412 | 'cd0153': ['Amp', 'Line 6 Litigator Guitar Line 6 Original (mono)'], 413 | 'cd015d': ['Amp', 'Line 6 Badonk Guitar Line 6 Original (mono)'], 414 | '06': ['Amp', 'Ampeg B-15NF Bass Ampeg B-15NF Portaflex (mono)'], 415 | '05': ['Amp', 'Ampeg SVT Nrm Bass Ampeg SVT (normal channel) (mono)'], 416 | '04': ['Amp', 'Ampeg SVT Brt Bass Ampeg SVT (bright channel) (mono)'], 417 | 'cd0207': ['Amp', 'Ampeg SVT-4 PRO (mono)'], 418 | 'cd015e': ['Amp', 'Woody Blue (mono)'], 419 | 'cd01e6': ['Amp', 'Agua 51 (mono)'], 420 | '02': ['Amp', 'Cali Bass (mono)'], 421 | '00': ['Amp', 'Cali 400 Ch1 (mono)'], 422 | '01': ['Amp', 'Cali 400 Ch2 (mono)'], 423 | '03': ['Amp', 'G Cougar 800 (mono)'], 424 | 'cd0157': ['Amp', 'Del Sol 300 (mono)'], 425 | 'cd016b': ['Amp', 'Busy One Ch1 (mono)'], 426 | 'cd016c': ['Amp', 'Busy One Ch2 (mono)'], 427 | 'cd016d': ['Amp', 'Busy One Jump (mono)'], 428 | 'cce7': ['Amp', 'Studio Tube Pre (mono)'], 429 | 'cce6': ['Preamp', 'WhoWatt 100 (mono)'], 430 | 'ccdd': ['Preamp', 'Soup Pro (mono)'], 431 | 'ccde': ['Preamp', 'Stone Age 185 (mono)'], 432 | 'cd018e': ['Preamp', 'Voltage Queen (mono)'], 433 | 'cce0': ['Preamp', 'Tweed Blues Nrm (mono)'], 434 | 'ccdf': ['Preamp', 'Tweed Blues Brt (mono)'], 435 | 'cd0220': ['Preamp', 'Fullerton Nrm (mono)'], 436 | 'cd021e': ['Preamp', 'Fullerton Brt (mono)'], 437 | 'cd021f': ['Preamp', 'Fullerton Jump (mono)'], 438 | 'cd021a': ['Preamp', 'Grammatico Nrm (mono)'], 439 | 'cd0218': ['Preamp', 'Grammatico Brt (mono)'], 440 | 'cd0219': ['Preamp', 'Grammatico Jump (mono)'], 441 | 'cce5': ['Preamp', 'US Small Tweed (mono)'], 442 | 'cd0250': ['Preamp', 'US Princess (mono)'], 443 | 'cce1': ['Preamp', 'US Deluxe Nrm (mono)'], 444 | 'cce2': ['Preamp', 'US Deluxe Vib (mono)'], 445 | 'cce3': ['Preamp', 'US Double Nrm (mono)'], 446 | 'cce4': ['Preamp', 'US Double Vib (mono)'], 447 | 'ccd7': ['Preamp', 'Mail Order Twin Guitar Silvertone 1484 (mono)'], 448 | 'cccd': ['Preamp', 'Divided Duo Guitar 13 JRT 9/15 (mono)'], 449 | 'ccd2': ['Preamp', 'Interstate Zed Guitar Dr Z Route 66 (mono)'], 450 | 'cd0181': ['Preamp', 'Derailed Ingrid Guitar Trainwreck Circuits Express (mono)'], 451 | 'ccd3': ['Preamp', 'Jazz Rivet 120 Guitar Roland JC-120 Jazz Chorus (mono)'], 452 | 'ccce': ['Preamp', 'Essex A15 Guitar Vox AC-15 (mono)'], 453 | 'cccf': ['Preamp', 'Essex A30 Guitar Vox AC-30 with top boost (mono)'], 454 | 'ccc2': ['Preamp', 'A30 Fawn Nrm Guitar Vox AC-30 Fawn (normal channel) (mono)'], 455 | 'ccc1': ['Preamp', 'A30 Fawn Brt Guitar Vox AC-30 Fawn (bright channel) (mono)'], 456 | 'cd0135': ['Preamp', 'Matchstick Ch1 Guitar Matchless DC30 (channel 1) (mono)'], 457 | 'cd0136': ['Preamp', 'Matchstick Ch2 Guitar Matchless DC30 (channel 2) (mono)'], 458 | 'cd0137': ['Preamp', 'Matchstick Jump Guitar Matchless DC30 (jumped) (mono)'], 459 | 'ccd8': ['Preamp', 'Mandarin 80 Guitar Orange OR80 (mono)'], 460 | 'ccc6': ['Preamp', 'Brit J45 Nrm Guitar Marshall JTM-45 (normal channel) (mono)'], 461 | 'ccc5': ['Preamp', 'Brit J45 Brt Guitar Marshall JTM-45 (bright channel) (mono)'], 462 | 'cd01e1': ['Preamp', 'Brit Trem Nrm Guitar Marshall JTM-50 (normal channel) (mono)'], 463 | 'cd01e2': ['Preamp', 'Brit Trem Brt Guitar Marshall JTM-50 (bright channel) (mono)'], 464 | 'cd01e3': ['Preamp', 'Brit Trem Jump Guitar Marshall JTM-50 (jumped) (mono)'], 465 | 'cccb': ['Preamp', 'Brit Plexi Nrm Guitar Marshall Super Lead 100 (normal channel) (mono)'], 466 | 'ccc9': ['Preamp', 'Brit Plexi Brt Guitar Marshall Super Lead 100 (bright channel) (mono)'], 467 | 'ccca': ['Preamp', 'Brit Plexi Jump Guitar Marshall Super Lead 100 (jumped) (mono)'], 468 | 'ccc8': ['Preamp', 'Brit P75 Nrm Guitar Park 75 (normal channel) (mono)'], 469 | 'ccc7': ['Preamp', 'Brit P75 Brt Guitar Park 75 (bright channel) (mono)'], 470 | 'ccc4': ['Preamp', 'Brit 2204 Guitar Marshall JCM-800 (mono)'], 471 | 'cd0201': ['Preamp', 'Placater Clean Guitar Friedman BE-100 (clean channel) (mono)'], 472 | 'cd01f5': ['Preamp', 'Placater Dirty Guitar Friedman BE-100 (BE/HBE channel) (mono)'], 473 | 'cd01e5': ['Preamp', 'Cartographer Guitar Ben Adrian Cartographer (mono)'], 474 | 'ccd0': ['Preamp', 'German Mahadeva Guitar Bogner Shiva (mono)'], 475 | 'ccd1': ['Preamp', 'German Ubersonic Guitar Bogner Ueberschall (mono)'], 476 | 'cd0203': ['Preamp', 'Cali Texas Ch1 Guitar MESA/Boogie Lone Star (clean channel) (mono)'], 477 | 'cd01f7': ['Preamp', 'Cali Texas Ch2 Guitar MESA/Boogie Lone Star (drive channel) (mono)'], 478 | 'cd013c': ['Preamp', 'Cali IV Rhythm 1 Guitar MESA/Boogie Mark IV (channel I) (mono)'], 479 | 'cd013d': ['Preamp', 'Cali IV Rhythm 2 Guitar MESA/Boogie Mark IV (channel II) (mono)'], 480 | 'cd013b': ['Preamp', 'Cali IV Lead Guitar MESA/Boogie Mark IV (lead channel) (mono)'], 481 | 'cccc': ['Preamp', 'Cali Rectifire Guitar MESA/Boogie Dual Rectifier (mono)'], 482 | 'cd0151': ['Preamp', 'Archetype Clean Guitar Paul Reed Smith Archon (clean channel) (mono)'], 483 | 'cd0152': ['Preamp', 'Archetype Lead Guitar Paul Reed Smith Archon (lead channel) (mono)'], 484 | 'ccc3': ['Preamp', 'ANGL Meteor Guitar ENGL Fireball 100 (mono)'], 485 | 'ccda': ['Preamp', 'Solo Lead Clean Guitar Soldano SLO-100 (clean channel) (mono)'], 486 | 'ccdb': ['Preamp', 'Solo Lead Crunch Guitar Soldano SLO-100 (crunch channel) (mono)'], 487 | 'ccdc': ['Preamp', 'Solo Lead OD Guitar Soldano SLO-100 (overdrive channel) (mono)'], 488 | 'ccd9': ['Preamp', 'PV Panama Guitar Peavey 5150 (mono)'], 489 | 'cd0232': ['Preamp', 'Revv Gen Purple Guitar Revv Generator 120 (purple/gain ch. 3) (mono)'], 490 | 'cd0222': ['Preamp', 'Revv Gen Red Guitar Revv Generator 120 (red/high gain ch. 4) (mono)'], 491 | 'cd025a': ['Preamp', 'Das Benzin Mega Guitar Diezel VH4 (mega chanel) (mono)'], 492 | 'cd0258': ['Preamp', 'Das Benzin Lead Guitar Diezel VH4 (lead chanel) (mono)'], 493 | 'ccd5': ['Preamp', 'Line 6 Elektrik Guitar Line 6 Original (mono)'], 494 | 'ccd4': ['Preamp', 'Line 6 Doom Guitar Line 6 Original (mono)'], 495 | 'ccd6': ['Preamp', 'Line 6 Epic Guitar Line 6 Original (mono)'], 496 | 'cd014a': ['Preamp', 'Line 6 2204 Mod Guitar Line 6 Original (mono)'], 497 | 'cd0149': ['Preamp', 'Line 6 Fatality Guitar Line 6 Original (mono)'], 498 | 'cd0156': ['Preamp', 'Line 6 Litigator Guitar Line 6 Original (mono)'], 499 | 'cd0162': ['Preamp', 'Line 6 Badonk Guitar Line 6 Original (mono)'], 500 | 'ccc0': ['Preamp', 'Ampeg B-15NF Bass Ampeg B-15NF Portaflex (mono)'], 501 | 'ccbf': ['Preamp', 'Ampeg SVT Nrm Bass Ampeg SVT (normal channel) (mono)'], 502 | 'ccbe': ['Preamp', 'Ampeg SVT Brt Bass Ampeg SVT (bright channel) (mono)'], 503 | 'cd0208': ['Preamp', 'Ampeg SVT-4 PRO (mono)'], 504 | 'cd0161': ['Preamp', 'Woody Blue (mono)'], 505 | 'cd01e7': ['Preamp', 'Agua 51 (mono)'], 506 | 'ccbc': ['Preamp', 'Cali Bass (mono)'], 507 | 'ccba': ['Preamp', 'Cali 400 Ch1 (mono)'], 508 | 'ccbb': ['Preamp', 'Cali 400 Ch2 (mono)'], 509 | 'ccbd': ['Preamp', 'G Cougar 800 (mono)'], 510 | 'cd0158': ['Preamp', 'Del Sol 300 (mono)'], 511 | 'cd0172': ['Preamp', 'Busy One Ch1 (mono)'], 512 | 'cd0173': ['Preamp', 'Busy One Ch2 (mono)'], 513 | 'cd0174': ['Preamp', 'Busy One Jump (mono)'], 514 | '2c1a47': ['Amp+Cab', 'WhoWatt 100 (mono)'], 515 | '231a33': ['Amp+Cab', 'Soup Pro (mono)'], 516 | '241a2f': ['Amp+Cab', 'Stone Age 185 (mono)'], 517 | 'cd018d1a31': ['Amp+Cab', 'Voltage Queen (mono)'], 518 | '261a3d': ['Amp+Cab', 'Tweed Blues Nrm (mono)'], 519 | '251a3d': ['Amp+Cab', 'Tweed Blues Brt (mono)'], 520 | 'cd021d1acd0227': ['Amp+Cab', 'Fullerton Nrm (mono)'], 521 | 'cd021b1acd0227': ['Amp+Cab', 'Fullerton Brt (mono)'], 522 | 'cd021c1acd0227': ['Amp+Cab', 'Fullerton Jump (mono)'], 523 | 'cd02171acd0228': ['Amp+Cab', 'Grammatico Nrm (mono)'], 524 | 'cd02151acd0228': ['Amp+Cab', 'Grammatico Brt (mono)'], 525 | 'cd02161acd0228': ['Amp+Cab', 'Grammatico Jump (mono)'], 526 | '2b1a34': ['Amp+Cab', 'US Small Tweed (mono)'], 527 | 'cd024f1acd024d': ['Amp+Cab', 'US Princess (mono)'], 528 | '271a31': ['Amp+Cab', 'US Deluxe Nrm (mono)'], 529 | '281a31': ['Amp+Cab', 'US Deluxe Vib (mono)'], 530 | '291a36': ['Amp+Cab', 'US Double Nrm (mono)'], 531 | '2a1a36': ['Amp+Cab', 'US Double Vib (mono)'], 532 | '1d1a39': ['Amp+Cab', 'Mail Order Twin Guitar Silvertone 1484 (mono)'], 533 | '131a2e': ['Amp+Cab', 'Divided Duo Guitar 13 JRT 9/15 (mono)'], 534 | '181a37': ['Amp+Cab', 'Interstate Zed Guitar Dr Z Route 66 (mono)'], 535 | 'cd01801a40': ['Amp+Cab', 'Derailed Ingrid Guitar Trainwreck Circuits Express (mono)'], 536 | '191a38': ['Amp+Cab', 'Jazz Rivet 120 Guitar Roland JC-120 Jazz Chorus (mono)'], 537 | '141a2d': ['Amp+Cab', 'Essex A15 Guitar Vox AC-15 (mono)'], 538 | '151a3a': ['Amp+Cab', 'Essex A30 Guitar Vox AC-30 with top boost (mono)'], 539 | '081a35': ['Amp+Cab', 'A30 Fawn Nrm Guitar Vox AC-30 Fawn (normal channel) (mono)'], 540 | '071a35': ['Amp+Cab', 'A30 Fawn Brt Guitar Vox AC-30 Fawn (bright channel) (mono)'], 541 | 'cd01321acd0167': ['Amp+Cab', 'Matchstick Ch1 Guitar Matchless DC30 (channel 1) (mono)'], 542 | 'cd01331acd0166': ['Amp+Cab', 'Matchstick Ch2 Guitar Matchless DC30 (channel 2) (mono)'], 543 | 'cd01341acd0166': ['Amp+Cab', 'Matchstick Jump Guitar Matchless DC30 (jumped) (mono)'], 544 | '1e1a43': ['Amp+Cab', 'Mandarin 80 Guitar Orange OR80 (mono)'], 545 | '0c1a41': ['Amp+Cab', 'Brit J45 Nrm Guitar Marshall JTM-45 (normal channel) (mono)'], 546 | '0b1a41': ['Amp+Cab', 'Brit J45 Brt Guitar Marshall JTM-45 (bright channel) (mono)'], 547 | 'cd01de1a42': ['Amp+Cab', 'Brit Trem Nrm Guitar Marshall JTM-50 (normal channel) (mono)'], 548 | 'cd01df1a42': ['Amp+Cab', 'Brit Trem Brt Guitar Marshall JTM-50 (bright channel) (mono)'], 549 | 'cd01e01a42': ['Amp+Cab', 'Brit Trem Jump Guitar Marshall JTM-50 (jumped) (mono)'], 550 | '111a42': ['Amp+Cab', 'Brit Plexi Nrm Guitar Marshall Super Lead 100 (normal channel) (mono)'], 551 | '0f1a42': ['Amp+Cab', 'Brit Plexi Brt Guitar Marshall Super Lead 100 (bright channel) (mono)'], 552 | '101a42': ['Amp+Cab', 'Brit Plexi Jump Guitar Marshall Super Lead 100 (jumped) (mono)'], 553 | '0e1a3f': ['Amp+Cab', 'Brit P75 Nrm Guitar Park 75 (normal channel) (mono)'], 554 | '0d1a3f': ['Amp+Cab', 'Brit P75 Brt Guitar Park 75 (bright channel) (mono)'], 555 | '0a1a3f': ['Amp+Cab', 'Brit 2204 Guitar Marshall JCM-800 (mono)'], 556 | 'cd02001a40': ['Amp+Cab', 'Placater Clean Guitar Friedman BE-100 (clean channel) (mono)'], 557 | 'cd01f41a40': ['Amp+Cab', 'Placater Dirty Guitar Friedman BE-100 (BE/HBE channel) (mono)'], 558 | 'cd01e41a46': ['Amp+Cab', 'Cartographer Guitar Ben Adrian Cartographer (mono)'], 559 | '161a30': ['Amp+Cab', 'German Mahadeva Guitar Bogner Shiva (mono)'], 560 | '171a46': ['Amp+Cab', 'German Ubersonic Guitar Bogner Ueberschall (mono)'], 561 | 'cd02021a37': ['Amp+Cab', 'Cali Texas Ch1 Guitar MESA/Boogie Lone Star (clean channel) (mono)'], 562 | 'cd01f61a37': ['Amp+Cab', 'Cali Texas Ch2 Guitar MESA/Boogie Lone Star (drive channel) (mono)'], 563 | 'cd01391acd0164': ['Amp+Cab', 'Cali IV Rhythm 1 Guitar MESA/Boogie Mark IV (channel I) (mono)'], 564 | 'cd013a1acd0164': ['Amp+Cab', 'Cali IV Rhythm 2 Guitar MESA/Boogie Mark IV (channel II) (mono)'], 565 | 'cd01381a40': ['Amp+Cab', 'Cali IV Lead Guitar MESA/Boogie Mark IV (lead channel) (mono)'], 566 | '121a40': ['Amp+Cab', 'Cali Rectifire Guitar MESA/Boogie Dual Rectifier (mono)'], 567 | 'cd014f1a40': ['Amp+Cab', 'Archetype Clean Guitar Paul Reed Smith Archon (clean channel) (mono)'], 568 | 'cd01501a40': ['Amp+Cab', 'Archetype Lead Guitar Paul Reed Smith Archon (lead channel) (mono)'], 569 | '091a48': ['Amp+Cab', 'ANGL Meteor Guitar ENGL Fireball 100 (mono)'], 570 | '201a44': ['Amp+Cab', 'Solo Lead Clean Guitar Soldano SLO-100 (clean channel) (mono)'], 571 | '211a44': ['Amp+Cab', 'Solo Lead Crunch Guitar Soldano SLO-100 (crunch channel) (mono)'], 572 | '221a44': ['Amp+Cab', 'Solo Lead OD Guitar Soldano SLO-100 (overdrive channel) (mono)'], 573 | '1f1a40': ['Amp+Cab', 'PV Panama Guitar Peavey 5150 (mono)'], 574 | 'cd02311a40': ['Amp+Cab', 'Revv Gen Purple Guitar Revv Generator 120 (purple/gain ch. 3) (mono)'], 575 | 'cd02211a40': ['Amp+Cab', 'Revv Gen Red Guitar Revv Generator 120 (red/high gain ch. 4) (mono)'], 576 | 'cd02591a40': ['Amp+Cab', 'Das Benzin Mega Guitar Diezel VH4 (mega chanel) (mono)'], 577 | 'cd02571a40': ['Amp+Cab', 'Das Benzin Lead Guitar Diezel VH4 (lead chanel) (mono)'], 578 | '1b1a3e': ['Amp+Cab', 'Line 6 Elektrik Guitar Line 6 Original (mono)'], 579 | '1a1a47': ['Amp+Cab', 'Line 6 Doom Guitar Line 6 Original (mono)'], 580 | '1c1a40': ['Amp+Cab', 'Line 6 Epic Guitar Line 6 Original (mono)'], 581 | 'cd01421a40': ['Amp+Cab', 'Line 6 2204 Mod Guitar Line 6 Original (mono)'], 582 | 'cd01481a40': ['Amp+Cab', 'Line 6 Fatality Guitar Line 6 Original (mono)'], 583 | 'cd01531a31': ['Amp+Cab', 'Line 6 Litigator Guitar Line 6 Original (mono)'], 584 | 'cd015d1a40': ['Amp+Cab', 'Line 6 Badonk Guitar Line 6 Original (mono)'], 585 | '061a32': ['Amp+Cab', 'Ampeg B-15NF Bass Ampeg B-15NF Portaflex (mono)'], 586 | '051a4a': ['Amp+Cab', 'Ampeg SVT Nrm Bass Ampeg SVT (normal channel) (mono)'], 587 | '041a4a': ['Amp+Cab', 'Ampeg SVT Brt Bass Ampeg SVT (bright channel) (mono)'], 588 | 'cd02071a4a': ['Amp+Cab', 'Ampeg SVT-4 PRO (mono)'], 589 | 'cd015e1acd016e': ['Amp+Cab', 'Woody Blue (mono)'], 590 | 'cd01e61a3b': ['Amp+Cab', 'Agua 51 (mono)'], 591 | '021a49': ['Amp+Cab', 'Cali Bass (mono)'], 592 | '001a3b': ['Amp+Cab', 'Cali 400 Ch1 (mono)'], 593 | '011a3b': ['Amp+Cab', 'Cali 400 Ch2 (mono)'], 594 | '031a3c': ['Amp+Cab', 'G Cougar 800 (mono)'], 595 | 'cd01571acd0165': ['Amp+Cab', 'Del Sol 300 (mono)'], 596 | 'cd016b1a4a': ['Amp+Cab', 'Busy One Ch1 (mono)'], 597 | 'cd016c1a4a': ['Amp+Cab', 'Busy One Ch2 (mono)'], 598 | 'cd016d1a4a': ['Amp+Cab', 'Busy One Jump (mono)'], 599 | 'ccfa': ['Send/Return', ' (mono)'], 600 | 'ccfb': ['Send/Return', 'Send L (mono)'], 601 | 'cce8': ['Send/Return', 'Send R (mono)'], 602 | 'cce9': ['Send/Return', 'Return L (mono)'], 603 | 'cc8d': ['Send/Return', 'Return R (mono)'], 604 | 'cc8e': ['Send/Return', 'FX Loop L (mono)'], 605 | 'ccfe': ['Send/Return', 'FX Loop R (stereo)'], 606 | 'ccec': ['Send/Return', 'Send L/R (stereo)'], 607 | 'cc91': ['Send/Return', 'Return L/R (stereo)'], 608 | '33': ['Cab', 'Soup Pro Ellipse Single, Dual 1 x 6x9" Supro S6616 (mono)'], 609 | '34': ['Cab', '1x8 Small Tweed Single, Dual 1x8" Fender Champ (mono)'], 610 | 'cd024d': ['Cab', '1x10 US Princess Single, Dual 1x10" Fender Princeton Reverb (mono)'], 611 | '2f': ['Cab', '1x12 Field Coil Single, Dual 1x12" Gibson EH185 (mono)'], 612 | 'cd0227': ['Cab', '1x12 Fullerton Single, Dual 1x12" Fender 5C3 Tweed Deluxe (mono)'], 613 | 'cd0228': ['Cab', '1x12 Grammatico Single, Dual 1x12" Grammatico LaGrange (mono)'], 614 | '31': ['Cab', '1x12 US Deluxe Single, Dual 1x12" Fender Deluxe Oxford (mono)'], 615 | 'cd024e': ['Cab', '1x12 US Princess Single, Dual 1x12" Fender Princeton Reverb (mono)'], 616 | '2e': ['Cab', '1x12 Celest 12H Single, Dual 1x12" 13 JRT 9/15 G12 H30 (mono)'], 617 | '2d': ['Cab', '1x12 Blue Bell Single, Dual 1x12" Vox AC-15 Blue (mono)'], 618 | '30': ['Cab', '1x12 Lead 80 Single, Dual 1x12" Bogner Shiva CL80 (mono)'], 619 | 'cd0164': ['Cab', '1x12 Cali IV Single, Dual 1x12" MESA/Boogie Mk IV (mono)'], 620 | 'cd0163': ['Cab', '1x12 Cali Ext Single, Dual 1x12" MESA/Boogie EVM12L (mono)'], 621 | '36': ['Cab', '2x12 Double C12N Single, Dual 2x12" Fender Twin C12N (mono)'], 622 | '39': ['Cab', '2x12 Mail C12Q Single, Dual 2x12" Silvertone 1484 (mono)'], 623 | '37': ['Cab', '2x12 Interstate Single, Dual 2x12" Dr Z Z Best V30 (mono)'], 624 | '38': ['Cab', '2x12 Jazz Rivet Single, Dual 2x12" Roland JC-120 (mono)'], 625 | '3a': ['Cab', '2x12 Silver Bell Single, Dual 2x12" Vox AC-30TB Silver (mono)'], 626 | '35': ['Cab', '2x12 Blue Bell Single, Dual 2x12" Vox AC-30 Fawn Blue (mono)'], 627 | 'cd0167': ['Cab', '2x12 Match H30 Single, Dual 1x12" Matchless DC-30 G12H30 (mono)'], 628 | 'cd0166': ['Cab', '2x12 Match G25 Single, Dual 1x12" Matchless DC-30 Greenback 25 (mono)'], 629 | '3d': ['Cab', '4x10 Tweed P10R Single, Dual 4x10" Fender Bassman P10R (mono)'], 630 | '47': ['Cab', '4x12 WhoWatt 100 Single, Dual 4x12" Hiwatt AP Fane (mono)'], 631 | '43': ['Cab', '4x12 Mandarin EM Single, Dual 4x12" Orange Eminence (mono)'], 632 | '42': ['Cab', '4x12 Greenback25 Single, Dual 4x12" Marshall Basketweave G12 M25 (mono)'], 633 | '41': ['Cab', '4x12 Greenback20 Single, Dual 4x12" Marshall Basketweave G12 M20 (mono)'], 634 | '3f': ['Cab', '4x12 Blackback30 Single, Dual 4x12" Park 75 G12 H30 (mono)'], 635 | '3e': ['Cab', '4x12 1960 T75 Single, Dual 4x12" Marshall 1960 AT75 (mono)'], 636 | '46': ['Cab', '4x12 Uber V30 Single, Dual 4x12" Bogner Uberkab V30 (mono)'], 637 | '45': ['Cab', '4x12 Uber T75 Single, Dual 4x12" Bogner Uberkab T75 (mono)'], 638 | '40': ['Cab', '4x12 Cali V30 (mono)'], 639 | '48': ['Cab', '4x12 XXL V30 (mono)'], 640 | '44': ['Cab', '4x12 SoloLead EM (mono)'], 641 | 'cd0165': ['Cab', '1x12 Del Sol (mono)'], 642 | '32': ['Cab', '1x15 Ampeg B-15 (mono)'], 643 | 'cd0168': ['Cab', '1x18 Del Sol (mono)'], 644 | 'cd016e': ['Cab', '1x18 Woody Blue (mono)'], 645 | '3b': ['Cab', '2x15 Brute (mono)'], 646 | '3c': ['Cab', '4x10 Ampeg HLF (mono)'], 647 | '49': ['Cab', '6x10 Cali Power (mono)'], 648 | '4a': ['Cab', '8x10 Ampeg SVT E (mono)'], 649 | 'cc95': ['Impulse Response', '1024 samples'], 650 | 'cc96': ['Impulse Response', '2048 samples'] 651 | } 652 | -------------------------------------------------------------------------------- /out_packet.py: -------------------------------------------------------------------------------- 1 | class OutPacket: 2 | def __init__(self, data, delay=0.0): 3 | self.delay = delay 4 | self.data = data 5 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | # from utils.usb_helper import UsbHelper 2 | # from utils.usb_monitor import UsbMonitor -------------------------------------------------------------------------------- /utils/formatter.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import binascii 3 | import sys 4 | 5 | 6 | def format_1(p_input): 7 | 8 | parts = p_input.split(', ') 9 | parts_without_0x = list() 10 | for part in parts: 11 | a = part.replace('0x', '') 12 | b = a.zfill(2) 13 | parts_without_0x.append(b) 14 | 15 | output = '' 16 | for p in parts_without_0x: 17 | output += p 18 | # print(output) 19 | return output 20 | 21 | 22 | def ieee754_to_rendered_str(val): 23 | value = struct.unpack('>f', binascii.unhexlify(val)) 24 | rounded_val = round(value[0], 2) 25 | return rounded_val 26 | 27 | 28 | def slot_splitter(val): 29 | slots = val.split('8213') 30 | if len(slots) > 1: 31 | slots = slots[1:] 32 | for i, slot in enumerate(slots): 33 | if i != 3: 34 | continue 35 | # print(str(i) + ': ' + slot) 36 | values = slot.split('ca') 37 | for value in values: 38 | if len(value) == 8: 39 | ieee_val = ieee754_to_rendered_str(value) 40 | print(ieee_val) 41 | else: 42 | print(value) 43 | return 44 | 45 | 46 | def ca_splitter(val): 47 | parts = val.split('ca') 48 | for part in parts: 49 | if len(part) == 8: 50 | ieee_val = ieee754_to_rendered_str(part) 51 | print(ieee_val) 52 | else: 53 | print(part) 54 | 55 | 56 | def main(args): 57 | if len(args) > 1: 58 | print(format_1(args[1])) 59 | return 60 | 61 | print(ieee754_to_rendered_str('03000490')) 62 | print(ieee754_to_rendered_str('c30c8302')) 63 | # us double vib 64 | slot_splitter("00009f00aa0900008366cd03eb670068da099fa96c362d68656c697800da00303d000000610000004e02000050020000ca020000d6020000c6030000f00300003e000000f60300009f0900009f09000089078324a45033330023ce0301000025b376322e39322d3536352d6730323137383763000082150016dc0014821300148205010783020303030493c2cac2400000ca3f0000008213061485188317c219771aff09010ac30b83020703070497cac214666603ca3d1ba5e0ca3e4cccd0ca3f800000ca40e00000ca40c000000c8302000300049082130814c08213061485188317c3192a1a3609120ac30b83020c030c049cca3eccccccca3ec28f5cca3f333334ca3f147ae0ca3e800000ca3f4cccccca3f800000ca3f000000ca3f000000ca3ea8f5c4ca3f19999aca3f0000000c83020603050496ca40400000ca42a00000ca45fa0000ca00000000ca000000000582130814c082130814c08213061485188317c219cd01ee1aff09080ac30b83020903080499ca3e570a40ca00000000ca3e800000ca3eccccccca3e6147b0ca00000000ca42dc0000ca45bb8000c20c8302000300049082130814c082130814c0821301148206010783020203020492ca3f000000ca0000000082130214830e82050007830200030004900f8408cd01010d000ac30783020303030493ca3f000000ca3f000000c212c282130814c082130814c082130814c082130814c082130814c082130814c082130814c082130814c08213031483108206000783020003000490118408cc970d000ac30783020603060496ca00000000ca3f000000ca00000000ca3f000000c2ca0000000012c201c0038207020895918") 65 | 66 | # mail order twin in slot 2 67 | ca_splitter("0000ab28b60900008366cd03eb670068da09aba96c362d68656c697800da00303d000000610000004e02000050020000ca020000d6020000c6030000f00300003e000000f6030000ab090000ab09000089078324a45033330023ce0301000025b376322e39322d3536352d6730323137383763000082150016dc0014821300148205010783020303030493c2cac2400000ca3f0000008213061485188317c219771aff09010ac30b83020703070497cac214666603ca3d1ba5e0ca3e4cccd0ca3f800000ca40e00000ca40c000000c830200030004908213061485188317c3191d1a3909120ac30b83020c030c049cca3f400000ca3eb33333ca3ee147aeca3f47ae14ca00000000ca3f4ccccdca3f800000ca3f000000ca3f000000ca3f000000ca3f19999aca3f0000000c83020603050496ca40200000ca42a00000ca45fa0000ca00000000ca000000000582130814c082130814c082130814c08213061485188317c219cd01ee1aff09080ac30b83020903080499ca3e570a40ca00000000ca3e800000ca3eccccccca3e6147b0ca00000000ca42dc0000ca45bb8000c20c8302000300049082130814c082130814c0821301148206010783020203020492ca3f000000ca0000000082130214830e82050007830200030004900f8408cd01010d000ac30783020303030493ca3f000000ca3f000000c212c282130814c082130814c082130814c082130814c082130814c082130814c082130814c082130814c08213031483108206000783020003000490118408cc970d000ac30783020603060496ca00000000ca3f000000ca00000000ca3f000000c2ca0000000012c201c003820702089591870a070b84000305a943432056616c75650006ce0007100c07c20cc30ea1000dc210000fc291870a070b84000305a9536e617073686f740006ce0007100c07c20cc30ea1000dc210000fc291870a070b84000305a943432056616c75650006ce0007100c07c20cc30ea1000dc210000fc2c0c0049ac0c0c0c0c0c0c0c0c0c00282009dd10000d10000d10000d10000d10000d10000d10001d1000fd10001d10000d10000d10000d10000019d97cc00cc00cc00cc00cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc0097cc00cc01cc53cc01cc00cc00cc0097cc00cc00cc01cc00cc00cc00cc0097cc00cc01cc53cc01cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc0097cc00cc00cc00cc00cc00cc00cc00058e10ca42f000002d002ecabdccccd02fcabdccccd0300031c332003300340035003600370038c21e00068262021a000a8606000703080009140a938700c3019b9308c297000153010000009306c297000153010000009307c297000001000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c2970000000000000002dc004093c20dca0000000093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c003dc001492c2c292c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c304ab534e415053484f5420310005ca42f000000c008700c2019b9308c297000000000000009306c297000000000000009307c297000001000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c2970000000000000002dc004093c20dca0000000093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c003dc001492c2c292c2c392c2c392c2c392c2c392c2c392c2c392c2c292c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c292c2c392c2c304ab534e415053484f5420320005ca42f000000c008700c2019b9308c297000000000000009306c297000000000000009307c297000001000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c297000000000000009318c2970000000000000002dc004093c20dca0000000093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c093c240c003dc001492c2c292c2c392c2c392c2c392c2c392c2c392c2c392c2c292c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c392c2c292c2c392c2c304ab534e415053484f5420330005ca42f000000c000ddc0014c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c240") 68 | 69 | # mail order twin in slot 3 70 | ca_splitter("00009f28aa0900008366cd03eb670068da099fa96c362d68656c697800da00303d000000610000004e02000050020000ca020000d6020000c6030000f00300003e000000f60300009f0900009f09000089078324a45033330023ce0301000025b376322e39322d3536352d6730323137383763000082150016dc0014821300148205010783020303030493c2cac2400000ca3f0000008213061485188317c219771aff09010ac30b83020703070497cac214666603ca3d1ba5e0ca3e4cccd0ca3f800000ca40e00000ca40c000000c8302000300049082130814c08213061485188317c3191d1a3909120ac30b83020c030c049cca3f400000ca3eb33333ca3ee147aeca3f47ae14ca00000000ca3f4ccccdca3f800000ca3f000000ca3f000000ca3f000000ca3f19999aca3f0000000c83020603050496ca40200000ca42a00000ca45fa0000ca00000000ca000000000582130814c082130814c08213061485188317c219cd01ee1aff09080ac30b83020903080499ca3e570a40ca00000000ca3e800000ca3eccccccca3e6147b0ca00000000ca42dc0000ca45bb8000c20c8302000300049082130814c082130814c0821301148206010783020203020492ca3f000000ca0000000082130214830e82050007830200030004900f8408cd01010d000ac30783020303030493ca3f000000ca3f000000c212c282130814c082130814c082130814c082130814c082130814c082130814c082130814c08213") 71 | 72 | ''' 73 | str = '' 74 | for num in [62, 158, 184, 82]: 75 | str += '{:02x}'.format(int(num)) 76 | print(ieee754_to_rendered_str(str)) 77 | ''' 78 | 79 | 80 | print(ieee754_to_rendered_str('3EC28F5C')) 81 | print(ieee754_to_rendered_str('3f333334')) 82 | 83 | print(ieee754_to_rendered_str('3f147ae0')) 84 | print(ieee754_to_rendered_str('3f333334')) 85 | print(ieee754_to_rendered_str('3f147ae0')) 86 | print(ieee754_to_rendered_str('3e800000')) 87 | print(ieee754_to_rendered_str('3f4ccccc')) 88 | 89 | 90 | if __name__ == '__main__': 91 | main(sys.argv) 92 | -------------------------------------------------------------------------------- /utils/ieee754_convert.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import binascii 3 | import sys 4 | 5 | 6 | def format_1(p_input): 7 | parts = p_input.split(', ') 8 | parts_without_0x = list() 9 | for part in parts: 10 | a = part.replace('0x', '') 11 | b = a.zfill(2) 12 | parts_without_0x.append(b) 13 | 14 | output = '' 15 | for p in parts_without_0x: 16 | output += p 17 | # print(output) 18 | return output 19 | 20 | 21 | def ieee754_to_rendered_str(val): 22 | value = struct.unpack('>f', binascii.unhexlify(val)) 23 | rounded_val = round(value[0], 4) 24 | return rounded_val 25 | 26 | 27 | def main(args): 28 | for i in range(1, len(args)): 29 | value = args[i] 30 | if '0x' in value: 31 | value = format_1(value) 32 | else: 33 | value = value.replace(' ', '') 34 | if len(value) != 8: 35 | print(args[i] + ' => Length must be 8 characters. Example: "3ca3d70a" or "0x3c, 0xa3, 0xd7, 0x0a"') 36 | continue 37 | converted_value = ieee754_to_rendered_str(value) 38 | print(args[i] + ' => ' + str(converted_value)) 39 | 40 | 41 | if __name__ == '__main__': 42 | main(sys.argv) 43 | -------------------------------------------------------------------------------- /utils/packetdump_parser.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import re 3 | import sys 4 | import xlsxwriter 5 | import logging 6 | import os 7 | 8 | log = logging.getLogger(__name__) 9 | 10 | 11 | def extract_data(data_lines): 12 | data_to_capture = [] 13 | local_data = [] 14 | 15 | for line in data_lines: 16 | x = re.search("([0-9a-fA-F]{4}) ([0-9a-f ]+) (.+)", line) 17 | if x is not None: 18 | hex_only = x.group(2) 19 | single_data = hex_only.split(' ') 20 | for d in single_data: 21 | if d == '': 22 | continue 23 | local_data.append(d) 24 | pass 25 | local_data = local_data[27:] 26 | return local_data 27 | 28 | 29 | def export_all_endpoints(path_wireshark_dump, path_out_xlsx): 30 | with open(path_wireshark_dump) as csv_file: 31 | wireshark_dump_csv_reader = csv.reader(csv_file, delimiter=',') 32 | workbook = xlsxwriter.Workbook(path_out_xlsx) 33 | format_x1_x10 = workbook.add_format() 34 | format_x2_x10 = workbook.add_format() 35 | format_x80_x10 = workbook.add_format() 36 | 37 | format_x1_x10.set_pattern(1) 38 | format_x2_x10.set_pattern(1) 39 | format_x80_x10.set_pattern(1) 40 | format_x1_x10.set_bg_color('edf0e0') 41 | format_x2_x10.set_bg_color('f5c9ce') 42 | format_x80_x10.set_bg_color('fbeaa5') 43 | 44 | worksheet_all_ports = workbook.add_worksheet("all") 45 | worksheet_x1x10 = workbook.add_worksheet("x1x10") 46 | worksheet_x2x10 = workbook.add_worksheet("x2x10") 47 | worksheet_x80x10 = workbook.add_worksheet("x80x10") 48 | 49 | data_from_kpa = [] 50 | current_frame_no = 0 51 | current_endpoint_address = 0 52 | current_time = 0 53 | capture_data_line_detected = False 54 | packet_data_recorder = [] 55 | 56 | endpoint_x1_times = [0, 0, 0] 57 | endpoint_x81_times = [0, 0, 0] 58 | 59 | xlsx_row_num = 0 60 | x1_x10_row_num = 0 61 | x2_x10_row_num = 0 62 | x80_x10_row_num = 0 63 | for row in wireshark_dump_csv_reader: 64 | 65 | if len(row) == 0: 66 | continue 67 | str_row = row[0] 68 | 69 | x = re.search("([0-9]+)\s([0-9]+.[0-9]+).+host.+USB", str_row) 70 | if x is not None: 71 | current_time = x.group(2) 72 | if str_row.startswith('Frame '): 73 | idx_dp = str_row.index(':') 74 | current_frame_no = str_row[6:idx_dp] 75 | 76 | parts = str_row.split(' Endpoint: ') 77 | if len(parts) > 1: 78 | current_endpoint_address = parts[1] 79 | 80 | if capture_data_line_detected is True: 81 | packet_data_recorder.append(str_row) 82 | if str_row.startswith('Leftover Capture Data: '): 83 | capture_data_line_detected = True 84 | if str_row.startswith('No. Time'): 85 | capture_data_line_detected = False 86 | 87 | if len(packet_data_recorder) > 0: 88 | 89 | # need 272 bytes 90 | full_data = extract_data(packet_data_recorder) 91 | full_data_int = [] 92 | full_data_str = '' 93 | for d in full_data: 94 | full_data_int.append(int(d, 16)) 95 | 96 | for i, d in enumerate(full_data_int): 97 | e = hex(d) 98 | if i == 9: 99 | full_data_str += (e + ', ') 100 | else: 101 | full_data_str += (e + ', ') 102 | 103 | if full_data_str.endswith(', '): 104 | full_data_str = full_data_str[:-2] 105 | 106 | data_row = [''] * 14 107 | 108 | if current_endpoint_address == '0x01': 109 | if full_data_int[4] == 0x1 and full_data_int[5] == 0x10: 110 | 111 | worksheet_all_ports.write('A' + str(xlsx_row_num), current_frame_no) 112 | worksheet_all_ports.write('B' + str(xlsx_row_num), current_time) 113 | worksheet_all_ports.write('E' + str(xlsx_row_num), full_data_str, format_x1_x10) 114 | worksheet_all_ports.write('C' + str(xlsx_row_num), str(float(current_time) - endpoint_x1_times[0])) 115 | worksheet_all_ports.write('D' + str(xlsx_row_num), str(float(current_time) - endpoint_x81_times[0])) 116 | 117 | worksheet_x1x10.write('A' + str(x1_x10_row_num), current_frame_no) 118 | worksheet_x1x10.write('B' + str(x1_x10_row_num), current_time) 119 | worksheet_x1x10.write('E' + str(x1_x10_row_num), full_data_str, format_x1_x10) 120 | worksheet_x1x10.write('C' + str(x1_x10_row_num), str(float(current_time) - endpoint_x1_times[0])) 121 | worksheet_x1x10.write('D' + str(x1_x10_row_num), str(float(current_time) - endpoint_x81_times[0])) 122 | x1_x10_row_num += 1 123 | 124 | endpoint_x1_times[0] = float(current_time) 125 | 126 | elif full_data_int[4] == 0x2 and full_data_int[5] == 0x10: 127 | 128 | worksheet_all_ports.write('A' + str(xlsx_row_num), current_frame_no) 129 | worksheet_all_ports.write('B' + str(xlsx_row_num), current_time) 130 | worksheet_all_ports.write('E' + str(xlsx_row_num), full_data_str, format_x2_x10) 131 | worksheet_all_ports.write('C' + str(xlsx_row_num), str(float(current_time) - endpoint_x1_times[1])) 132 | worksheet_all_ports.write('D' + str(xlsx_row_num), str(float(current_time) - endpoint_x81_times[1])) 133 | 134 | worksheet_x2x10.write('A' + str(x2_x10_row_num), current_frame_no) 135 | worksheet_x2x10.write('B' + str(x2_x10_row_num), current_time) 136 | worksheet_x2x10.write('E' + str(x2_x10_row_num), full_data_str, format_x2_x10) 137 | worksheet_x2x10.write('C' + str(x2_x10_row_num), str(float(current_time) - endpoint_x1_times[1])) 138 | worksheet_x2x10.write('D' + str(x2_x10_row_num), str(float(current_time) - endpoint_x81_times[1])) 139 | x2_x10_row_num += 1 140 | 141 | endpoint_x1_times[1] = float(current_time) 142 | 143 | elif full_data_int[4] == 0x80 and full_data_int[5] == 0x10: 144 | 145 | worksheet_all_ports.write('A' + str(xlsx_row_num), current_frame_no) 146 | worksheet_all_ports.write('B' + str(xlsx_row_num), current_time) 147 | worksheet_all_ports.write('E' + str(xlsx_row_num), full_data_str, format_x80_x10) 148 | worksheet_all_ports.write('C' + str(xlsx_row_num), str(float(current_time) - endpoint_x1_times[2])) 149 | worksheet_all_ports.write('D' + str(xlsx_row_num), str(float(current_time) - endpoint_x81_times[2])) 150 | 151 | worksheet_x80x10.write('A' + str(x80_x10_row_num), current_frame_no) 152 | worksheet_x80x10.write('B' + str(x80_x10_row_num), current_time) 153 | worksheet_x80x10.write('E' + str(x80_x10_row_num), full_data_str, format_x80_x10) 154 | worksheet_x80x10.write('C' + str(x80_x10_row_num), str(float(current_time) - endpoint_x1_times[2])) 155 | worksheet_x80x10.write('D' + str(x80_x10_row_num), str(float(current_time) - endpoint_x81_times[2])) 156 | x80_x10_row_num += 1 157 | 158 | endpoint_x1_times[2] = float(current_time) 159 | 160 | else: 161 | print("WARNING: Unknown communication path in endpoint 0x1") 162 | 163 | data_from_kpa.append(data_row) 164 | xlsx_row_num += 1 165 | elif current_endpoint_address == '0x81': 166 | if full_data_int[6] == 0x1 and full_data_int[7] == 0x10: 167 | 168 | worksheet_all_ports.write('A' + str(xlsx_row_num), current_frame_no) 169 | worksheet_all_ports.write('B' + str(xlsx_row_num), current_time) 170 | worksheet_all_ports.write('F' + str(xlsx_row_num), full_data_str, format_x1_x10) 171 | 172 | worksheet_x1x10.write('A' + str(x1_x10_row_num), current_frame_no) 173 | worksheet_x1x10.write('B' + str(x1_x10_row_num), current_time) 174 | worksheet_x1x10.write('F' + str(x1_x10_row_num), full_data_str, format_x1_x10) 175 | x1_x10_row_num += 1 176 | endpoint_x81_times[0] = float(current_time) 177 | 178 | elif full_data_int[6] == 0x2 and full_data_int[7] == 0x10: 179 | 180 | worksheet_all_ports.write('A' + str(xlsx_row_num), current_frame_no) 181 | worksheet_all_ports.write('B' + str(xlsx_row_num), current_time) 182 | worksheet_all_ports.write('F' + str(xlsx_row_num), full_data_str, format_x2_x10) 183 | 184 | worksheet_x2x10.write('A' + str(x2_x10_row_num), current_frame_no) 185 | worksheet_x2x10.write('B' + str(x2_x10_row_num), current_time) 186 | worksheet_x2x10.write('F' + str(x2_x10_row_num), full_data_str, format_x2_x10) 187 | x2_x10_row_num += 1 188 | endpoint_x81_times[1] = float(current_time) 189 | 190 | elif full_data_int[6] == 0x80 and full_data_int[7] == 0x10: 191 | 192 | worksheet_all_ports.write('A' + str(xlsx_row_num), current_frame_no) 193 | worksheet_all_ports.write('B' + str(xlsx_row_num), current_time) 194 | worksheet_all_ports.write('F' + str(xlsx_row_num), full_data_str, format_x80_x10) 195 | 196 | worksheet_x80x10.write('A' + str(x80_x10_row_num), current_frame_no) 197 | worksheet_x80x10.write('B' + str(x80_x10_row_num), current_time) 198 | worksheet_x80x10.write('F' + str(x80_x10_row_num), full_data_str, format_x80_x10) 199 | x80_x10_row_num += 1 200 | 201 | endpoint_x81_times[2] = float(current_time) 202 | else: 203 | print("WARNING: Unknown communication path in endpoint 0x81") 204 | data_from_kpa.append(data_row) 205 | xlsx_row_num += 1 206 | else: 207 | print("WARNING: Unknown endpoint: " + str(current_endpoint_address)) 208 | packet_data_recorder = [] 209 | 210 | workbook.close() 211 | return 212 | 213 | 214 | def export_all_endpoints_legacy(path_wireshark_dump, path_out_csv): 215 | with open(path_wireshark_dump) as csv_file: 216 | csv_reader = csv.reader(csv_file, delimiter=',') 217 | data_from_kpa = [] 218 | current_frame_no = 0 219 | current_endpoint_address = 0 220 | current_time = 0 221 | capture_data_line_detected = False 222 | packet_data_recorder = [] 223 | 224 | endpoint_x1_times = [0, 0, 0] 225 | endpoint_x81_times = [0, 0, 0] 226 | 227 | for row in csv_reader: 228 | 229 | if len(row) == 0: 230 | continue 231 | str_row = row[0] 232 | 233 | x = re.search("([0-9]+)\s([0-9]+.[0-9]+).+host.+USB", str_row) 234 | if x is not None: 235 | current_time = x.group(2) 236 | if str_row.startswith('Frame '): 237 | idx_dp = str_row.index(':') 238 | current_frame_no = str_row[6:idx_dp] 239 | 240 | parts = str_row.split(' Endpoint: ') 241 | if len(parts) > 1: 242 | current_endpoint_address = parts[1] 243 | 244 | if capture_data_line_detected is True: 245 | packet_data_recorder.append(str_row) 246 | if str_row.startswith('Leftover Capture Data: '): 247 | capture_data_line_detected = True 248 | if str_row.startswith('No. Time'): 249 | capture_data_line_detected = False 250 | 251 | if len(packet_data_recorder) > 0: 252 | 253 | # need 272 bytes 254 | full_data = extract_data(packet_data_recorder) 255 | full_data_int = [] 256 | full_data_str = '' 257 | for d in full_data: 258 | full_data_int.append(int(d, 16)) 259 | 260 | for d in full_data_int: 261 | e = hex(d) 262 | full_data_str += (e + ', ') 263 | if full_data_str.endswith(', '): 264 | full_data_str = full_data_str[:-2] 265 | 266 | data_row = [''] * 14 267 | data_row[0] = current_frame_no 268 | data_row[1] = current_time 269 | 270 | if current_endpoint_address == '0x01': 271 | if full_data_int[4] == 0x1 and full_data_int[5] == 0x10: 272 | data_row[2] = full_data_str # x1x10 OUT 273 | data_row[3] = '' # x1x10 IN 274 | data_row[4] = str(float(current_time) - endpoint_x1_times[0]) # x1x10 out-out 275 | data_row[5] = str(float(current_time) - endpoint_x81_times[0]) # x1x10 in-out 276 | endpoint_x1_times[0] = float(current_time) 277 | 278 | elif full_data_int[4] == 0x2 and full_data_int[5] == 0x10: 279 | data_row[6] = full_data_str # x1x10 OUT 280 | data_row[7] = '' # x1x10 IN 281 | data_row[8] = str(float(current_time) - endpoint_x1_times[1]) # x1x10 out-out 282 | data_row[9] = str(float(current_time) - endpoint_x81_times[1]) # x1x10 in-out 283 | endpoint_x1_times[1] = float(current_time) 284 | 285 | elif full_data_int[4] == 0x80 and full_data_int[5] == 0x10: 286 | data_row[10] = full_data_str # x1x10 OUT 287 | data_row[11] = '' # x1x10 IN 288 | data_row[12] = str(float(current_time) - endpoint_x1_times[2]) # x1x10 out-out 289 | data_row[13] = str(float(current_time) - endpoint_x81_times[2]) # x1x10 in-out 290 | endpoint_x1_times[2] = float(current_time) 291 | 292 | else: 293 | print("WARNING: Unknown communication path in endpoint 0x1") 294 | 295 | data_from_kpa.append(data_row) 296 | elif current_endpoint_address == '0x81': 297 | if full_data_int[6] == 0x1 and full_data_int[7] == 0x10: 298 | data_row[2] = '' # x1x10 OUT 299 | data_row[3] = full_data_str # x1x10 IN 300 | data_row[4] = '' # x1x10 out-out 301 | data_row[5] = '' # x1x10 in-out 302 | endpoint_x81_times[0] = float(current_time) 303 | 304 | elif full_data_int[6] == 0x2 and full_data_int[7] == 0x10: 305 | data_row[6] = '' # x1x10 OUT 306 | data_row[7] = full_data_str # x1x10 IN 307 | data_row[8] = '' # x1x10 out-out 308 | data_row[9] = '' # x1x10 in-out 309 | endpoint_x81_times[1] = float(current_time) 310 | 311 | elif full_data_int[6] == 0x80 and full_data_int[7] == 0x10: 312 | data_row[10] = '' # x1x10 OUT 313 | data_row[11] = full_data_str # x1x10 IN 314 | data_row[12] = '' # x1x10 out-out 315 | data_row[13] = '' # x1x10 in-out 316 | endpoint_x81_times[2] = float(current_time) 317 | else: 318 | print("WARNING: Unknown communication path in endpoint 0x81") 319 | continue 320 | data_from_kpa.append(data_row) 321 | else: 322 | print("WARNING: Unknown endpoint: " + current_endpoint_address) 323 | packet_data_recorder = [] 324 | 325 | with open(path_out_csv, mode='w') as test_data_out_file: 326 | test_data_writer = csv.writer(test_data_out_file, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL) 327 | test_data_writer.writerow(['no', 'time', 'x1x10 OUT', 'x1x10 IN', 'x1x10 out-out', 'x1x10 in-out', 'x2x10 OUT', 'x2x10 IN', 'x2x10 out-out', 'x2x10 in-out', 'x1x80 OUT', 'x80x10 IN', 'x80x10 out-out', 'x80x10 in-out']) 328 | for test_data_entry in data_from_kpa: 329 | test_data_writer.writerow(test_data_entry) 330 | 331 | 332 | file_list = [ 333 | "all_amps_PLUS_cabs_in_slot_3_all_other_slots_empty", 334 | "all_amps_in_slot_3_all_other_slots_empty", 335 | "all_cabs_in_slot_3_all_other_slots_empty", 336 | "all_delays_in_slot_3_all_other_slots_empty", 337 | "all_drives_in_slot_3_all_other_slots_empty_ERROR_switching_to_Clathorn_Drive", 338 | "all_dynamics_in_slot_3_all_other_slots_empty", 339 | "all_eqs_in_slot_3_all_other_slots_empty", 340 | "all_filter_in_slot_3_all_other_slots_empty", 341 | "all_loopers_in_slot_3_all_other_slots_empty", 342 | "all_modulationsV2_in_slot_3_all_other_slots_empty", 343 | "all_modulations_in_slot_3_all_other_slots_empty", 344 | "all_pitch_synths_in_slot_3_all_other_slots_empty", 345 | "all_preamps_in_slot_3_all_other_slots_empty", 346 | "all_reverbs_in_slot_3_all_other_slots_empty", 347 | "all_send_return_in_slot_3_all_other_slots_empty", 348 | "all_vol_pan_in_slot_3_all_other_slots_empty", 349 | "all_wahs_in_slot_3_all_other_slots_empty", 350 | "all_modulationsSTEREO_in_slot_3_all_other_slots_empty", 351 | "all_reverbsSTEREO_in_slot_3_all_other_slots_empty", 352 | "all_eqsSTEREO_in_slot_3_all_other_slots_empty" 353 | ] 354 | 355 | 356 | file_list = [ 357 | "all_BASSamps_in_slot_3_all_other_slots_empty", 358 | "all_BASSamps_PLUS_cabs_in_slot_3_all_other_slots_empty", 359 | "all_BASSpreamps_in_slot_3_all_other_slots_empty", 360 | "all_cabsDUAL_in_slot_3_all_other_slots_empty", 361 | "all_delaysLEGACY_in_slot_3_all_other_slots_empty", 362 | "all_delaysSTEREO_in_slot_3_all_other_slots_empty", 363 | "all_drivesLEGACY_in_slot_3_all_other_slots_empty", 364 | "all_drivesSTEREO_in_slot_3_all_other_slots_empty", 365 | "all_dynamicsLEGACY_in_slot_3_all_other_slots_empty", 366 | "all_dynamicsSTEREO_in_slot_3_all_other_slots_empty", 367 | "all_filterLEGACY_in_slot_3_all_other_slots_empty", 368 | "all_filterSTEREO_in_slot_3_all_other_slots_empty", 369 | "all_MIC_JUST_ONE_preamps_in_slot_3_all_other_slots_empty", 370 | "all_modulationsLEGACY_in_slot_3_all_other_slots_empty", 371 | "all_pitch_synthsLEGACY_in_slot_3_all_other_slots_empty", 372 | "all_pitch_synthsSTEREO_in_slot_3_all_other_slots_empty", 373 | "all_reverbsLEGACY_in_slot_3_all_other_slots_empty", 374 | "all_send_returnSTEREO_in_slot_3_all_other_slots_empty", 375 | "all_vol_panSTEREO_in_slot_3_all_other_slots_empty", 376 | "all_wahsSTEREO_in_slot_3_all_other_slots_empty" 377 | ] 378 | 379 | file_list = [ 380 | "ich_habe_garkein_auto_auf_main_display", 381 | "moving_slots_up_and_down" 382 | ] 383 | 384 | 385 | def main(args): 386 | 387 | if len(args) > 1: 388 | input_path = args[1] 389 | if os.path.exists(input_path) is False: 390 | log.error("Given path for input does not exist: " + input_path ) 391 | else: 392 | export_all_endpoints(input_path, input_path + '.xlsx') 393 | return 394 | 395 | for file_path in file_list: 396 | export_all_endpoints('../doc/' + file_path + '.txt', '../doc/' + file_path + '.xlsx') 397 | 398 | return 399 | 400 | # export_all_endpoints('../doc/all_amps_in_slot_3_all_other_slots_empty.txt', '../doc/all_amps_in_slot_3_all_other_slots_empty.csv') 401 | export_all_endpoints('../doc/all_drives_in_slot_3_all_other_slots_empty_ERROR_switching_to_Clathorn_Drive.txt', '../doc/all_drives_in_slot_3_all_other_slots_empty_ERROR_switching_to_Clathorn_Drive.csv') 402 | export_all_endpoints('../doc/all_modulations_in_slot_3_all_other_slots_empty.txt', '../doc/all_modulations_in_slot_3_all_other_slots_empty.csv') 403 | ''' 404 | export_all_endpoints('../doc/connect_two_times.txt', '../doc/connect_two_times.csv') 405 | export_all_endpoints('../doc/connect_with_Fender_Cl_and_running_for_30s.txt', '../doc/connect_with_Fender_Cl_and_running_for_30s.csv') 406 | export_all_endpoints('../doc/connected_with_1b_1c_2a.txt', '../doc/connected_with_1b_1c_2a.csv') 407 | export_all_endpoints('../doc/ramping_drive_from_0_to_10_to_0.txt', '../doc/ramping_drive_from_0_to_10_to_0.csv') 408 | export_all_endpoints('../doc/switching_mod_slot6_PitchRingMod_to_AmRingMod.txt', '../doc/switching_mod_slot6_PitchRingMod_to_AmRingMod.csv') 409 | export_all_endpoints('../doc/switching_rvb_slot5_from_Ganymede_to_Searchlights.txt', '../doc/switching_rvb_slot5_from_Ganymede_to_Searchlights.csv') 410 | export_all_endpoints('../doc/switching_mod_slot6_PitchRingMod_to_AmRingMod.txt', '../doc/switching_mod_slot6_PitchRingMod_to_AmRingMod.csv') 411 | ''' 412 | 413 | if __name__ == '__main__': 414 | main(sys.argv) -------------------------------------------------------------------------------- /utils/pcapng_to_xlsx.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import sys 4 | import subprocess 5 | import uuid 6 | import xlsxwriter 7 | import logging 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class PcapngToXlsx: 14 | PATH_TSHARK_ABS = "/Applications/Wireshark.app/Contents/MacOS/tshark" 15 | 16 | def __init__(self): 17 | self.path_json_dump_file = None 18 | 19 | @staticmethod 20 | def check_os_supported(): 21 | if os.name != 'posix': 22 | log.error("Operating system not supported yet.") 23 | return False 24 | if os.path.exists(PcapngToXlsx.PATH_TSHARK_ABS) is False: 25 | log.error("Cannot find tshark here: " + PcapngToXlsx.PATH_TSHARK_ABS) 26 | return False 27 | return True 28 | 29 | @staticmethod 30 | def pcapng_to_json(path_pcapng_input_abs, path_json_dump_file): 31 | command = \ 32 | PcapngToXlsx.PATH_TSHARK_ABS + ' -V -T json -x -r "' + path_pcapng_input_abs + '" > "' + \ 33 | path_json_dump_file + '"' 34 | 35 | result = subprocess.run(command, stdout=subprocess.PIPE, shell=True) 36 | 37 | if result.returncode != 0: 38 | log.error("Return value of tshark command not 0") 39 | return False 40 | 41 | return True 42 | 43 | @staticmethod 44 | def json_to_xlsx(path_json_dump_file, path_xlsx_out): 45 | 46 | workbook = xlsxwriter.Workbook(path_xlsx_out) 47 | format_x1_x10 = workbook.add_format() 48 | format_x2_x10 = workbook.add_format() 49 | format_x80_x10 = workbook.add_format() 50 | 51 | format_x1_x10.set_pattern(1) 52 | format_x2_x10.set_pattern(1) 53 | format_x80_x10.set_pattern(1) 54 | format_x1_x10.set_bg_color('edf0e0') 55 | format_x2_x10.set_bg_color('f5c9ce') 56 | format_x80_x10.set_bg_color('fbeaa5') 57 | 58 | worksheet_all_ports = workbook.add_worksheet("all") 59 | worksheet_x1x10 = workbook.add_worksheet("x1x10") 60 | worksheet_x2x10 = workbook.add_worksheet("x2x10") 61 | worksheet_x80x10 = workbook.add_worksheet("x80x10") 62 | 63 | endpoint_x1_times = [0, 0, 0] 64 | endpoint_x81_times = [0, 0, 0] 65 | 66 | xlsx_row_num = 0 67 | x1_x10_row_num = 0 68 | x2_x10_row_num = 0 69 | x80_x10_row_num = 0 70 | 71 | reported_unexpected_endpoints = list() 72 | endpoints_to_report = ["0x00000001", "0x00000081"] 73 | 74 | # open json and print 75 | with open(path_json_dump_file, "r") as read_file: 76 | data = json.load(read_file) 77 | for data_packet_no, packet in enumerate(data): 78 | if data_packet_no % 1000 == 0: 79 | print('.', end='') 80 | source = packet["_source"] 81 | layers = source["layers"] 82 | usb = layers["usb"] 83 | 84 | usb_endpoint = usb["usb.endpoint_address"] 85 | if usb_endpoint in endpoints_to_report: 86 | try: 87 | # data (if any) 88 | usb_capdata_raw = layers["usb.capdata_raw"] 89 | usb_capdata_raw = usb_capdata_raw[0] 90 | 91 | full_data_int = [] 92 | full_data_str = '' 93 | for i in range(0, len(usb_capdata_raw), 2): 94 | current_str = usb_capdata_raw[i:i+2] 95 | full_data_int.append(int(current_str, 16)) 96 | 97 | for i, d in enumerate(full_data_int): 98 | e = hex(d) 99 | full_data_str += (e + ', ') 100 | 101 | if full_data_str.endswith(', '): 102 | full_data_str = full_data_str[:-2] 103 | 104 | # packet no and time since session start 105 | frame = layers["frame"] 106 | frame_time_delta = frame["frame.time_delta"] 107 | frame_time_relative = frame["frame.time_relative"] 108 | frame_number = frame["frame.number"] 109 | if usb_endpoint == "0x00000001": 110 | if full_data_int[4] == 0x1 and full_data_int[5] == 0x10: 111 | 112 | worksheet_all_ports.write('A' + str(xlsx_row_num), frame_number) 113 | worksheet_all_ports.write('B' + str(xlsx_row_num), frame_time_relative) 114 | worksheet_all_ports.write('E' + str(xlsx_row_num), full_data_str, format_x1_x10) 115 | worksheet_all_ports.write('C' + str(xlsx_row_num), 116 | str(float(frame_time_relative) - endpoint_x1_times[0])) 117 | worksheet_all_ports.write('D' + str(xlsx_row_num), 118 | str(float(frame_time_relative) - endpoint_x81_times[0])) 119 | 120 | worksheet_x1x10.write('A' + str(x1_x10_row_num), frame_number) 121 | worksheet_x1x10.write('B' + str(x1_x10_row_num), frame_time_relative) 122 | worksheet_x1x10.write('E' + str(x1_x10_row_num), full_data_str, format_x1_x10) 123 | worksheet_x1x10.write('C' + str(x1_x10_row_num), 124 | str(float(frame_time_relative) - endpoint_x1_times[0])) 125 | worksheet_x1x10.write('D' + str(x1_x10_row_num), 126 | str(float(frame_time_relative) - endpoint_x81_times[0])) 127 | x1_x10_row_num += 1 128 | 129 | endpoint_x1_times[0] = float(frame_time_relative) 130 | 131 | elif full_data_int[4] == 0x2 and full_data_int[5] == 0x10: 132 | 133 | worksheet_all_ports.write('A' + str(xlsx_row_num), frame_number) 134 | worksheet_all_ports.write('B' + str(xlsx_row_num), frame_time_relative) 135 | worksheet_all_ports.write('E' + str(xlsx_row_num), full_data_str, format_x2_x10) 136 | worksheet_all_ports.write('C' + str(xlsx_row_num), 137 | str(float(frame_time_relative) - endpoint_x1_times[1])) 138 | worksheet_all_ports.write('D' + str(xlsx_row_num), 139 | str(float(frame_time_relative) - endpoint_x81_times[1])) 140 | 141 | worksheet_x2x10.write('A' + str(x2_x10_row_num), frame_number) 142 | worksheet_x2x10.write('B' + str(x2_x10_row_num), frame_time_relative) 143 | worksheet_x2x10.write('E' + str(x2_x10_row_num), full_data_str, format_x2_x10) 144 | worksheet_x2x10.write('C' + str(x2_x10_row_num), 145 | str(float(frame_time_relative) - endpoint_x1_times[1])) 146 | worksheet_x2x10.write('D' + str(x2_x10_row_num), 147 | str(float(frame_time_relative) - endpoint_x81_times[1])) 148 | x2_x10_row_num += 1 149 | 150 | endpoint_x1_times[1] = float(frame_time_relative) 151 | 152 | elif full_data_int[4] == 0x80 and full_data_int[5] == 0x10: 153 | 154 | worksheet_all_ports.write('A' + str(xlsx_row_num), frame_number) 155 | worksheet_all_ports.write('B' + str(xlsx_row_num), frame_time_relative) 156 | worksheet_all_ports.write('E' + str(xlsx_row_num), full_data_str, format_x80_x10) 157 | worksheet_all_ports.write('C' + str(xlsx_row_num), 158 | str(float(frame_time_relative) - endpoint_x1_times[2])) 159 | worksheet_all_ports.write('D' + str(xlsx_row_num), 160 | str(float(frame_time_relative) - endpoint_x81_times[2])) 161 | 162 | worksheet_x80x10.write('A' + str(x80_x10_row_num), frame_number) 163 | worksheet_x80x10.write('B' + str(x80_x10_row_num), frame_time_relative) 164 | worksheet_x80x10.write('E' + str(x80_x10_row_num), full_data_str, format_x80_x10) 165 | worksheet_x80x10.write('C' + str(x80_x10_row_num), 166 | str(float(frame_time_relative) - endpoint_x1_times[2])) 167 | worksheet_x80x10.write('D' + str(x80_x10_row_num), 168 | str(float(frame_time_relative) - endpoint_x81_times[2])) 169 | x80_x10_row_num += 1 170 | 171 | endpoint_x1_times[2] = float(frame_time_relative) 172 | 173 | else: 174 | print("WARNING: Unknown communication path in endpoint 0x1: " + str(full_data_int[4]) + ':' + str(full_data_int[5])) 175 | 176 | xlsx_row_num += 1 177 | else: # endpoint 0x81 178 | if full_data_int[6] == 0x1 and full_data_int[7] == 0x10: 179 | 180 | worksheet_all_ports.write('A' + str(xlsx_row_num), frame_number) 181 | worksheet_all_ports.write('B' + str(xlsx_row_num), frame_time_relative) 182 | worksheet_all_ports.write('F' + str(xlsx_row_num), full_data_str, format_x1_x10) 183 | 184 | worksheet_x1x10.write('A' + str(x1_x10_row_num), frame_number) 185 | worksheet_x1x10.write('B' + str(x1_x10_row_num), frame_time_relative) 186 | worksheet_x1x10.write('F' + str(x1_x10_row_num), full_data_str, format_x1_x10) 187 | x1_x10_row_num += 1 188 | endpoint_x81_times[0] = float(frame_time_relative) 189 | 190 | elif full_data_int[6] == 0x2 and full_data_int[7] == 0x10: 191 | 192 | worksheet_all_ports.write('A' + str(xlsx_row_num), frame_number) 193 | worksheet_all_ports.write('B' + str(xlsx_row_num), frame_time_relative) 194 | worksheet_all_ports.write('F' + str(xlsx_row_num), full_data_str, format_x2_x10) 195 | 196 | worksheet_x2x10.write('A' + str(x2_x10_row_num), frame_number) 197 | worksheet_x2x10.write('B' + str(x2_x10_row_num), frame_time_relative) 198 | worksheet_x2x10.write('F' + str(x2_x10_row_num), full_data_str, format_x2_x10) 199 | x2_x10_row_num += 1 200 | endpoint_x81_times[1] = float(frame_time_relative) 201 | 202 | elif full_data_int[6] == 0x80 and full_data_int[7] == 0x10: 203 | 204 | worksheet_all_ports.write('A' + str(xlsx_row_num), frame_number) 205 | worksheet_all_ports.write('B' + str(xlsx_row_num), frame_time_relative) 206 | worksheet_all_ports.write('F' + str(xlsx_row_num), full_data_str, format_x80_x10) 207 | 208 | worksheet_x80x10.write('A' + str(x80_x10_row_num), frame_number) 209 | worksheet_x80x10.write('B' + str(x80_x10_row_num), frame_time_relative) 210 | worksheet_x80x10.write('F' + str(x80_x10_row_num), full_data_str, format_x80_x10) 211 | x80_x10_row_num += 1 212 | 213 | endpoint_x81_times[2] = float(frame_time_relative) 214 | else: 215 | print("WARNING: Unknown communication path in endpoint 0x81: " + str(full_data_int[4]) + ':' + str(full_data_int[5])) 216 | 217 | xlsx_row_num += 1 218 | except KeyError: 219 | # packet has no data we are interested in 220 | pass 221 | else: 222 | if usb_endpoint not in reported_unexpected_endpoints: 223 | reported_unexpected_endpoints.append(usb_endpoint) 224 | 225 | if len(reported_unexpected_endpoints) > 0: 226 | print() 227 | print("pcapng input file contains endpoint(s) currently not monitored: ", end="") 228 | for usb_endpoint in reported_unexpected_endpoints: 229 | print(usb_endpoint, end=" ") 230 | print() 231 | workbook.close() 232 | return True 233 | 234 | def convert(self, path_pcapng_input_abs, path_xlsx_out=None): 235 | if path_xlsx_out is None: 236 | path_xlsx_out = path_pcapng_input_abs + '.xlsx' 237 | 238 | path_json_dump_file = './tmp_dump_' + str(uuid.uuid4()) + '.json' 239 | try: 240 | if self.check_os_supported() is False: 241 | return False 242 | if os.path.exists(path_pcapng_input_abs) is False: 243 | log.error("Given input file does not exist: " + path_pcapng_input_abs) 244 | return False 245 | 246 | # call tshark to convert pcapng file to json 247 | # log.info('Converting pcapng file to json - this may take a while!') 248 | if self.pcapng_to_json(path_pcapng_input_abs, path_json_dump_file) is False: 249 | return False 250 | 251 | # log.info('Converting json file to xlsx sheet.') 252 | if self.json_to_xlsx(path_json_dump_file, path_xlsx_out) is False: 253 | return False 254 | except: 255 | pass 256 | finally: 257 | if os.path.exists(path_json_dump_file): 258 | os.remove(path_json_dump_file) 259 | log.info("Successfully stored xlsx file: " + path_xlsx_out) 260 | return True 261 | 262 | 263 | def main(args): 264 | logging.basicConfig( 265 | level='INFO', 266 | format="%(asctime)s - %(levelname)s - %(message)s (%(name)s)", 267 | datefmt="%Y-%m-%d %H:%M:%S") 268 | 269 | if len(args) < 2: 270 | log.error('Please provide at least one source file!') 271 | return 272 | 273 | pcapng_conv = PcapngToXlsx() 274 | for i in range(1, len(args)): 275 | pcapng_conv.convert(args[i]) 276 | print() 277 | print() 278 | print() 279 | 280 | 281 | if __name__ == '__main__': 282 | main(sys.argv) 283 | -------------------------------------------------------------------------------- /utils/preset_reader.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from presets import preset_1, preset_2 3 | from formatter import format_1, ieee754_to_rendered_str 4 | 5 | 6 | def main(args): 7 | full_packet_data = [] 8 | for i, packet in enumerate(preset_1): 9 | if i == 1: 10 | packet = packet[24:] 11 | else: 12 | packet = packet[16:] 13 | for b in packet: 14 | full_packet_data.append(b) 15 | 16 | pattern = [0xC2, 0x07, 0x00, 0x82] 17 | indexes = [(i, i + len(pattern)) for i in range(len(full_packet_data)) if full_packet_data[i:i + len(pattern)] == pattern] 18 | 19 | for idx in indexes: 20 | part = [] 21 | for i in range(0, 35): 22 | part.append(full_packet_data[idx[0]+i]) 23 | print(part) 24 | 25 | out = '' 26 | for b in full_packet_data: 27 | out += str(hex(b) + ', ') 28 | if out.endswith(', '): 29 | out = out[:-2] 30 | out_formatted = format_1(out) 31 | 32 | for i in range(0, 100): 33 | part = out_formatted[i*8:i*8+8] 34 | ieee = ieee754_to_rendered_str(part) 35 | print(part + ': ' + str(ieee)) 36 | 37 | 38 | if __name__ == '__main__': 39 | main(sys.argv) 40 | -------------------------------------------------------------------------------- /utils/presets.py: -------------------------------------------------------------------------------- 1 | preset_1 = [] 2 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x24, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0xc, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0x82, 0x13, 0x6, 0x14, 0x85, 0x18, 0x83, 0x17, 0xc2, 0x19, 0xcd, 0x1, 0xec, 0x1a, 0xff, 0x9, 0x8, 0xa, 0xc3, 0xb, 0x83, 0x2, 0x7, 0x3, 0x6, 0x4, 0x97, 0xca, 0x3f, 0x26, 0x66, 0x66, 0xca, 0x0, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x33, 0x33, 0x33, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x0, 0x0, 0x0, 0x0, 0xc2, 0xc, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0x82, 0x13, 0x6, 0x14, 0x85, 0x18, 0x83, 0x17, 0xc2, 0x19, 0xcc, 0xad, 0x1a, 0xff, 0x9, 0x1, 0xa, 0xc3, 0xb, 0x83, 0x2, 0xa, 0x3, 0xa, 0x4, 0x9a, 0xca, 0x44, 0xa0, 0x0, 0x0, 0xc3, 0xca, 0x44, 0xce, 0x59, 0x9a, 0xc2, 0xca, 0x40, 0xa0, 0x0, 0x0, 0x2, 0xca, 0x3f, 0x2b, 0x85, 0x1f, 0xca, 0x40, 0x90, 0x0, 0x0, 0x6, 0xc2, 0xc, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x1, 0x14, 0x82, 0x6, 0x1, 0x7, 0x83, 0x2, 0x2, 0x3, 0x2, 0x4, 0x92, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x40, 0x60, 0x0, 0x0, 0x82, 0x13, 0x2, 0x14, 0x83, 0xe, 0x82, 0x5, 0x0, 0x7, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0xf, 0x84, 0x8, 0xcd, 0x1, 0x1, 0xd, 0x0, 0xa, 0xc3, 0x7, 0x83, 0x2, 0x3, 0x3, 0x3, 0x4, 0x93, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xc2, 0x12, 0xc2, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0]) 3 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x25, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x3, 0x14, 0x83, 0x10, 0x82, 0x6, 0x0, 0x7, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0x11, 0x84, 0x8, 0xcc, 0x97, 0xd, 0x0, 0xa, 0xc3, 0x7, 0x83, 0x2, 0x6, 0x3, 0x6, 0x4, 0x96, 0xca, 0x0, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x0, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xc2, 0xca, 0x0, 0x0, 0x0, 0x0, 0x12, 0xc2, 0x1, 0xc0, 0x3, 0x82, 0x7, 0x2, 0x8, 0x95, 0x91, 0x87, 0xa, 0x7, 0xb, 0x84, 0x0, 0x3, 0x5, 0xa9, 0x43, 0x43, 0x20, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x0, 0x6, 0xce, 0x0, 0x7, 0x10, 0xc, 0x7, 0xc2, 0xc, 0xc3, 0xe, 0xa1, 0x0, 0xd, 0xc2, 0x10, 0x0, 0xf, 0xc2, 0x91, 0x87, 0xa, 0x7, 0xb, 0x84, 0x0, 0x3, 0x5, 0xa9, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x0, 0x6, 0xce, 0x0, 0x7, 0x10, 0xc, 0x7, 0xc2, 0xc, 0xc3, 0xe, 0xa1, 0x0, 0xd, 0xc2, 0x10, 0x0, 0xf, 0xc2, 0x91, 0x87, 0xa, 0x7, 0xb, 0x84, 0x0, 0x3, 0x5, 0xa9, 0x43, 0x43, 0x20, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x0, 0x6, 0xce, 0x0, 0x7, 0x10, 0xc, 0x7, 0xc2, 0xc, 0xc3, 0xe, 0xa1, 0x0, 0xd, 0xc2, 0x10, 0x0, 0xf, 0xc2, 0xc0, 0xc0, 0x4, 0x9a, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x9b, 0x82, 0x0, 0x0, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x2, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x1, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41]) 4 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x26, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x9, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x2, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x5, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x3, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x3, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x4, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x6, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x5, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x0, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x6, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x7, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x7, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x4, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x8, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0]) 5 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x27, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0x1d, 0x1, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x9, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x8, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0xa, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0xa, 0x29, 0xc2, 0x7, 0x0, 0x2, 0x82, 0x0, 0x9d, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x1, 0xd1, 0x0, 0xf, 0xd1, 0x0, 0x1, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0x1, 0x9d, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x1, 0xcc, 0x53, 0xcc, 0x1, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x1, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x1, 0xcc, 0x53, 0xcc, 0x1, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0]) 6 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x28, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x5, 0x8e, 0x10, 0xca, 0x42, 0xf0, 0x0, 0x0, 0x2d, 0x0, 0x2e, 0xca, 0xbd, 0xcc, 0xcc, 0xd0, 0x2f, 0xca, 0xbd, 0xcc, 0xcc, 0xd0, 0x30, 0x0, 0x31, 0xc2, 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, 0x36, 0x0, 0x37, 0x0, 0x38, 0xc3, 0x1e, 0x0, 0x6, 0x82, 0x62, 0x6, 0x1a, 0x0, 0xa, 0x86, 0x6, 0x0, 0x7, 0x3, 0x8, 0xb, 0x9, 0x14, 0xa, 0x93, 0x87, 0x0, 0xc3, 0x1, 0x9b, 0x93, 0x8, 0xc2, 0x97, 0x0, 0x1, 0x53, 0x1, 0x0, 0x0, 0x0, 0x93, 0x6, 0xc2, 0x97, 0x0, 0x1, 0x53, 0x1, 0x0, 0x0, 0x0, 0x93, 0x7, 0xc2, 0x97, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xdc, 0x0, 0x40, 0x93, 0xc2, 0x0, 0xca, 0x40, 0x0]) 7 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x29, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x1, 0xca, 0x41, 0xf, 0xf7, 0x27, 0x93, 0xc2, 0x2, 0xca, 0xc0, 0x88, 0xe5, 0x8a, 0x93, 0xc2, 0x3, 0xca, 0xc0, 0x20, 0x0, 0x0, 0x93, 0xc2, 0x4, 0xca, 0xc0, 0xc0, 0x0, 0x0, 0x93, 0xc2, 0x5, 0xca, 0xc0, 0x31, 0x3d, 0xd9, 0x93, 0xc2, 0x6, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x7, 0xca, 0xc0, 0x9c, 0xcc, 0xcd, 0x93, 0xc2, 0x8, 0xca, 0x40, 0x79, 0x99, 0x9a, 0x93, 0xc2, 0x9, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0xa, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2]) 8 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x2a, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x3, 0xdc, 0x0, 0x14, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x4, 0xab, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x20, 0x31, 0x0, 0x5, 0xca, 0x42, 0xf0, 0x0, 0x0, 0xc, 0x0, 0x87, 0x0, 0xc3, 0x1, 0x9b, 0x93, 0x8, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x6, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x7, 0xc2, 0x97, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xdc, 0x0, 0x40, 0x93, 0xc2, 0x0]) 9 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x2b, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x1, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x2, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x3, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x4, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x5, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x6, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x7, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x8, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x9, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0xa, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40]) 10 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x2c, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x3, 0xdc, 0x0, 0x14, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x4, 0xab, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x20, 0x32, 0x0, 0x5, 0xca, 0x42, 0xf0, 0x0, 0x0, 0xc, 0x0, 0x87, 0x0, 0xc3, 0x1, 0x9b, 0x93, 0x8, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x6, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x7, 0xc2, 0x97, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xdc, 0x0, 0x40]) 11 | preset_1.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x2d, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0x93, 0xc2, 0x0, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x1, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x2, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x3, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x4, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x5, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x6, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x7, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x8, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x9, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0xa, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0]) 12 | preset_1.append([0xa1, 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x2e, 0x0, 0x4, 0xc7, 0x7, 0x0, 0x0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x3, 0xdc, 0x0, 0x14, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x4, 0xab, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x20, 0x33, 0x0, 0x5, 0xca, 0x42, 0xf0, 0x0, 0x0, 0xc, 0x0, 0xd, 0xdc, 0x0, 0x14, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0x40, 0xc0]) 13 | 14 | preset_2 = [] 15 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x35, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0xc, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0x82, 0x13, 0x6, 0x14, 0x85, 0x18, 0x83, 0x17, 0xc2, 0x19, 0xcd, 0x1, 0xec, 0x1a, 0xff, 0x9, 0x8, 0xa, 0xc3, 0xb, 0x83, 0x2, 0x7, 0x3, 0x6, 0x4, 0x97, 0xca, 0x3f, 0x26, 0x66, 0x66, 0xca, 0x0, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x33, 0x33, 0x33, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x0, 0x0, 0x0, 0x0, 0xc2, 0xc, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0x82, 0x13, 0x6, 0x14, 0x85, 0x18, 0x83, 0x17, 0xc2, 0x19, 0xcc, 0xae, 0x1a, 0xff, 0x9, 0x1, 0xa, 0xc3, 0xb, 0x83, 0x2, 0xd, 0x3, 0xd, 0x4, 0x9d, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xff, 0xca, 0xc0, 0x19, 0x99, 0x9a, 0xca, 0x41, 0x9f, 0x33, 0x33, 0xca, 0x46, 0x9d, 0x8, 0x0, 0xca, 0x3f, 0x2b, 0x85, 0x1f, 0xca, 0x3f, 0x45, 0x1e, 0xb8, 0xca, 0x3f, 0x33, 0x33, 0x33, 0x1, 0xca, 0x3f, 0xb3, 0x33, 0x33, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x40, 0xb0, 0x0, 0x0, 0xc, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x1, 0x14, 0x82, 0x6, 0x1, 0x7, 0x83, 0x2, 0x2, 0x3, 0x2, 0x4, 0x92, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x40, 0x60, 0x0, 0x0, 0x82, 0x13, 0x2, 0x14, 0x83, 0xe, 0x82, 0x5, 0x0, 0x7, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0xf, 0x84, 0x8, 0xcd, 0x1, 0x1, 0xd, 0x0, 0xa, 0xc3, 0x7, 0x83, 0x2, 0x3, 0x3, 0x3, 0x4, 0x93, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xc2, 0x12, 0xc2, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8]) 16 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x36, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x8, 0x14, 0xc0, 0x82, 0x13, 0x3, 0x14, 0x83, 0x10, 0x82, 0x6, 0x0, 0x7, 0x83, 0x2, 0x0, 0x3, 0x0, 0x4, 0x90, 0x11, 0x84, 0x8, 0xcc, 0x97, 0xd, 0x0, 0xa, 0xc3, 0x7, 0x83, 0x2, 0x6, 0x3, 0x6, 0x4, 0x96, 0xca, 0x0, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xca, 0x0, 0x0, 0x0, 0x0, 0xca, 0x3f, 0x0, 0x0, 0x0, 0xc2, 0xca, 0x0, 0x0, 0x0, 0x0, 0x12, 0xc2, 0x1, 0xc0, 0x3, 0x82, 0x7, 0x2, 0x8, 0x95, 0x91, 0x87, 0xa, 0x7, 0xb, 0x84, 0x0, 0x3, 0x5, 0xa9, 0x43, 0x43, 0x20, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x0, 0x6, 0xce, 0x0, 0x7, 0x10, 0xc, 0x7, 0xc2, 0xc, 0xc3, 0xe, 0xa1, 0x0, 0xd, 0xc2, 0x10, 0x0, 0xf, 0xc2, 0x91, 0x87, 0xa, 0x7, 0xb, 0x84, 0x0, 0x3, 0x5, 0xa9, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x0, 0x6, 0xce, 0x0, 0x7, 0x10, 0xc, 0x7, 0xc2, 0xc, 0xc3, 0xe, 0xa1, 0x0, 0xd, 0xc2, 0x10, 0x0, 0xf, 0xc2, 0x91, 0x87, 0xa, 0x7, 0xb, 0x84, 0x0, 0x3, 0x5, 0xa9, 0x43, 0x43, 0x20, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x0, 0x6, 0xce, 0x0, 0x7, 0x10, 0xc, 0x7, 0xc2, 0xc, 0xc3, 0xe, 0xa1, 0x0, 0xd, 0xc2, 0x10, 0x0, 0xf, 0xc2, 0xc0, 0xc0, 0x4, 0x9a, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x9b, 0x82, 0x0, 0x0, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6]) 17 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x37, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0x83, 0x1c, 0x0, 0x1d, 0x2, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x1, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x9, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x2, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x5, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x3, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x3, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x4, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x6, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x5, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x0, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x6, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x7, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x7, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x4, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0]) 18 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x38, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0x8, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x1, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0x9, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0x8, 0x29, 0xc2, 0x7, 0x0, 0x82, 0x0, 0xa, 0x1, 0x88, 0x0, 0x9, 0x1, 0x4, 0x2, 0xca, 0xc1, 0x70, 0x0, 0x0, 0x3, 0xca, 0x41, 0x70, 0x0, 0x0, 0x4, 0x0, 0x5, 0x2, 0x6, 0x83, 0x1c, 0x0, 0x1d, 0xa, 0x29, 0xc2, 0x7, 0x0, 0x2, 0x82, 0x0, 0x9d, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x1, 0xd1, 0x0, 0xf, 0xd1, 0x0, 0x1, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0xd1, 0x0, 0x0, 0x1, 0x9d, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x1, 0xcc, 0x53, 0xcc, 0x1, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0]) 19 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x39, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0xcc, 0x0, 0xcc, 0x1, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x1, 0xcc, 0x53, 0xcc, 0x1, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x97, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0xcc, 0x0, 0x5, 0x8e, 0x10, 0xca, 0x42, 0xf0, 0x0, 0x0, 0x2d, 0x0, 0x2e, 0xca, 0xbd, 0xcc, 0xcc, 0xd0, 0x2f, 0xca, 0xbd, 0xcc, 0xcc, 0xd0, 0x30, 0x0, 0x31, 0xc2, 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, 0x36, 0x0, 0x37, 0x0, 0x38, 0xc3, 0x1e, 0x0, 0x6, 0x82, 0x62, 0x6, 0x1a, 0x0, 0xa, 0x86, 0x6, 0x0, 0x7, 0x3, 0x8, 0xb, 0x9, 0x14, 0xa, 0x93, 0x87, 0x0, 0xc3, 0x1, 0x9b, 0x93, 0x8, 0xc2, 0x97, 0x0, 0x1, 0x53, 0x1, 0x0, 0x0, 0x0, 0x93, 0x6, 0xc2, 0x97, 0x0, 0x1, 0x53, 0x1, 0x0, 0x0, 0x0, 0x93, 0x7, 0xc2, 0x97, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0]) 20 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x3a, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xdc, 0x0, 0x40, 0x93, 0xc2, 0x0, 0xca, 0x40, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x1, 0xca, 0x41, 0xf, 0xf7, 0x27, 0x93, 0xc2, 0x2, 0xca, 0xc0, 0x88, 0xe5, 0x8a, 0x93, 0xc2, 0x3, 0xca, 0xc0, 0x20, 0x0, 0x0, 0x93, 0xc2, 0x4, 0xca, 0xc0, 0xc0, 0x0, 0x0, 0x93, 0xc2, 0x5, 0xca, 0xc0, 0x31, 0x3d, 0xd9, 0x93, 0xc2, 0x6, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x7, 0xca, 0xc0, 0x9c, 0xcc, 0xcd, 0x93, 0xc2, 0x8, 0xca, 0x40, 0x79, 0x99, 0x9a, 0x93, 0xc2, 0x9, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0xa, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40]) 21 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x3b, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x3, 0xdc, 0x0, 0x14, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x4, 0xab, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x20, 0x31, 0x0, 0x5, 0xca, 0x42, 0xf0, 0x0, 0x0, 0xc, 0x0, 0x87, 0x0, 0xc3, 0x1, 0x9b, 0x93, 0x8, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x6, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x7, 0xc2, 0x97, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18]) 22 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x3c, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xdc, 0x0, 0x40, 0x93, 0xc2, 0x0, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x1, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x2, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x3, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x4, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x5, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x6, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x7, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x8, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x9, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0xa, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0]) 23 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x3d, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x3, 0xdc, 0x0, 0x14, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x4, 0xab, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x20, 0x32, 0x0, 0x5, 0xca, 0x42, 0xf0, 0x0, 0x0, 0xc, 0x0, 0x87, 0x0, 0xc3, 0x1, 0x9b, 0x93, 0x8, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x6, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x7, 0xc2, 0x97, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) 24 | preset_2.append([0x8, 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x3e, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x18, 0xc2, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xdc, 0x0, 0x40, 0x93, 0xc2, 0x0, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x1, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x2, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x3, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x4, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x5, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x6, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x7, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x8, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x9, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0xa, 0xca, 0x0, 0x0, 0x0, 0x0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93]) 25 | preset_2.append([0xbc, 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, 0x3f, 0x0, 0x4, 0xfc, 0x7, 0x0, 0x0, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x93, 0xc2, 0x40, 0xc0, 0x3, 0xdc, 0x0, 0x14, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc2, 0x92, 0xc2, 0xc3, 0x92, 0xc2, 0xc3, 0x4, 0xab, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x20, 0x33, 0x0, 0x5, 0xca, 0x42, 0xf0, 0x0, 0x0, 0xc, 0x0, 0xd, 0xdc, 0x0, 0x14, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3]) -------------------------------------------------------------------------------- /utils/simple_filter.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import csv 3 | from utils.formatter import format_1, ieee754_to_rendered_str 4 | from modules import modules 5 | 6 | 7 | class SlotInfo: 8 | def __init__(self): 9 | self.slot_no = 0 10 | self.on_off_state = 0 11 | 12 | def __eq__(self, other): 13 | if isinstance(other, SlotInfo) is False: 14 | return False 15 | return True 16 | 17 | def category(self): 18 | return None 19 | 20 | def to_string(self): 21 | return "SlotInfo to_string - not implemented in abstract class SlotInfo" 22 | 23 | 24 | class EmptySlotInfo(SlotInfo): 25 | def __init__(self): 26 | SlotInfo.__init__(self) 27 | self.module_id = '14c0' 28 | 29 | def __eq__(self, other): 30 | if isinstance(other, EmptySlotInfo) is False: 31 | return False 32 | return True 33 | 34 | def category(self): 35 | return None 36 | 37 | def to_string(self): 38 | # return '\'Standard Module: ' + self.module_id + ' - ' + str(self.params) 39 | return str(self.slot_no) + ':-' 40 | 41 | 42 | class StandardSlotInfo(SlotInfo): 43 | def __init__(self): 44 | SlotInfo.__init__(self) 45 | self.module_id = '' 46 | self.params = [] 47 | 48 | def __eq__(self, other): 49 | if isinstance(other, StandardSlotInfo) is False: 50 | return False 51 | if other.module_id != self.module_id: 52 | return False 53 | return True 54 | 55 | def category(self): 56 | try: 57 | description = modules[self.module_id] 58 | return description[0] 59 | except KeyError: 60 | print("ERROR: can't find module in modules: " + str(self.module_id)) 61 | return None 62 | 63 | def to_string(self): 64 | # return '\'Standard Module: ' + self.module_id + ' - ' + str(self.params) 65 | try: 66 | description = modules[self.module_id] 67 | return str(self.slot_no) + ":" + description[0] + ' (' + description[1] + ')' 68 | except KeyError: 69 | print("ERROR: can't find module in modules: " + str(self.module_id)) 70 | return str(self.slot_no) + ':NO MATCH FOR ' + self.module_id 71 | 72 | 73 | class CabsDualSlotInfo(SlotInfo): 74 | def __init__(self): 75 | SlotInfo.__init__(self) 76 | self.cab1_id = '' 77 | self.cab2_id = '' 78 | self.cab1_params = [] 79 | self.cab2_params = [] 80 | 81 | def __eq__(self, other): 82 | if isinstance(other, CabsDualSlotInfo) is False: 83 | return False 84 | if other.cab1_id != self.cab2_id: 85 | return False 86 | if other.cab2_id != self.cab2_id: 87 | return False 88 | return True 89 | 90 | def category(self): 91 | return "Cab" 92 | 93 | def to_string(self): 94 | return '\'Cab1: ' + self.cab1_id + ', Cab2: ' + self.cab2_id + ' - Cab1: ' + str(self.cab1_params) + ', Cab2: ' + str(self.cab2_params) 95 | # return str(self.module_id) 96 | 97 | 98 | class AmpCabSlotInfo(SlotInfo): 99 | def __init__(self): 100 | SlotInfo.__init__(self) 101 | self.amp_id = '' 102 | self.cab_id = '' 103 | self.amp_params = [] 104 | self.cab_params = [] 105 | 106 | def __eq__(self, other): 107 | if isinstance(other, AmpCabSlotInfo) is False: 108 | return False 109 | if other.amp_id != self.amp_id: 110 | return False 111 | if other.cab_id != self.cab_id: 112 | return False 113 | return True 114 | 115 | def category(self): 116 | return "Amp+Cab" 117 | 118 | def to_string(self): 119 | try: 120 | amp_description = modules[self.amp_id] 121 | cab_description = modules[self.cab_id] 122 | return str(self.slot_no) + ":Amp+Cab (" + amp_description[1] + ' + ' + cab_description[1] + ')' 123 | except KeyError: 124 | print("ERROR: can't find module in modules: " + str(self.module_id)) 125 | return str(self.slot_no) + ':NO MATCH FOR Amp: ' + self.amp_id + ', Cab: ' + self.cab_id 126 | 127 | # return '\'Amp: ' + self.amp_id + ', Cab: ' + self.amp_id + ' - Amp-Parameter: ' + str(self.amp_params) + ', Cab-Parameter: ' + str(self.cab_params) 128 | return str(self.amp_id + '1a' + self.cab_id) 129 | 130 | 131 | def slot_splitter(val): 132 | slots = val.split('8213') 133 | if len(slots) > 1: 134 | slots = slots[1:] 135 | for i, slot in enumerate(slots): 136 | if i != 3: 137 | continue 138 | # print(str(i) + ': ' + slot) 139 | values = slot.split('ca') 140 | effect_id = None 141 | for value in values: 142 | if len(value) == 8: 143 | ieee_val = ieee754_to_rendered_str(value) 144 | # print(ieee_val) 145 | else: 146 | try: 147 | idx = value.index('1aff09') 148 | ''' 149 | print("\'", end="") 150 | for i in range(idx-4, idx): 151 | print(value[i], end='') 152 | print() 153 | ''' 154 | # 155 | print('\'' + value[idx-4:idx]) 156 | return 157 | except ValueError as _e: 158 | pass 159 | print("No slot id found: " + value) 160 | 161 | 162 | def parse_parameter(data_stream): 163 | params = [] 164 | while len(data_stream) >= 4: 165 | if data_stream.startswith('ca'): 166 | # ieee754 representation 167 | hex_value_string = data_stream[2:10] 168 | data_stream = data_stream[10:] 169 | floating_point_rep = ieee754_to_rendered_str(hex_value_string) 170 | params.append(floating_point_rep) 171 | 172 | elif data_stream.startswith('c2'): 173 | data_stream = data_stream[2:] 174 | params.append('Opt 1') 175 | pass 176 | elif data_stream.startswith('c3'): 177 | data_stream = data_stream[2:] 178 | params.append('Opt 2') 179 | pass 180 | elif data_stream.startswith('06c2'): 181 | # maybe we reached the end here? 182 | data_stream = data_stream[4:] 183 | else: 184 | simple_int_value = data_stream[0:2] 185 | params.append(simple_int_value) 186 | data_stream = data_stream[2:] 187 | return params 188 | 189 | 190 | def parse_amp_and_cab_slot(slot_data): 191 | 192 | try: 193 | idx = slot_data.index('09120a') 194 | except ValueError: 195 | return None 196 | 197 | slot_info = AmpCabSlotInfo() 198 | ac_data = slot_data[16:idx] 199 | 200 | # special case for Amp Line 6 Doom Guitar Line 6 Original which has the id 1a (and 1a is also the splitter between 201 | # amp and cab) 202 | if ac_data.startswith('1a1a') and len(ac_data) == 6: 203 | ac_parts = ['1a', ac_data[4:6]] 204 | else: 205 | ac_parts = ac_data.split('1a') 206 | if len(ac_parts) != 2: 207 | print("ERROR: parts for amp+cab modules have unexpected length") 208 | return None 209 | slot_info.amp_id = ac_parts[0] 210 | slot_info.cab_id = ac_parts[1] 211 | try: 212 | idx_of_param_start = slot_data.index('ca') 213 | values_containing_data = slot_data[idx_of_param_start:] 214 | except ValueError: 215 | pass 216 | 217 | if len(values_containing_data) == 0: 218 | print("ERROR: No range containing data in slot: " + str(slot_idx)) 219 | return None 220 | 221 | try: 222 | idx_amp_cab_parameter_sep = values_containing_data.index('0c83020603050496') 223 | amp_params_stream = values_containing_data[:idx_amp_cab_parameter_sep] 224 | cab_params_stream = values_containing_data[idx_amp_cab_parameter_sep + 16:] 225 | amp_params = parse_parameter(amp_params_stream) 226 | cab_params = parse_parameter(cab_params_stream) 227 | slot_info.amp_params = amp_params 228 | slot_info.cab_params = cab_params 229 | return slot_info 230 | except ValueError: 231 | print("ERROR: No amp/cab parameter separator found in slot data") 232 | return None 233 | 234 | 235 | def parse_dual_cab_slot(slot_data): 236 | 237 | try: 238 | idx = slot_data.index('09100ac') 239 | except ValueError: 240 | return None 241 | 242 | slot_info = CabsDualSlotInfo() 243 | ac_data = slot_data[16:idx] 244 | 245 | ''' 246 | try: 247 | idx_od_cd = ac_data.index('cd') 248 | ac_data_tmp = ac_data[0:idx_od_cd] 249 | ac_data_tmp += ac_data[idx_od_cd + 4:] 250 | ac_data = ac_data_tmp 251 | except ValueError: 252 | pass 253 | ''' 254 | # print(slot_data) 255 | 256 | if ac_data.startswith('1a1a') and len(ac_data) == 6: 257 | ac_parts = ['1a', ac_data[4:6]] 258 | else: 259 | ac_parts = ac_data.split('1a') 260 | if len(ac_parts) != 2: 261 | print("ERROR: parts for amp+cab modules have unexpected length") 262 | return None 263 | slot_info.cab1_id = ac_parts[0] 264 | slot_info.cab2_id = ac_parts[1] 265 | 266 | try: 267 | idx_of_param_start = slot_data.index('ca') 268 | values_containing_data = slot_data[idx_of_param_start:] 269 | except ValueError: 270 | pass 271 | 272 | if len(values_containing_data) == 0: 273 | print("ERROR: No range containing data in slot: " + str(0)) 274 | return None 275 | 276 | try: 277 | idx_amp_cab_parameter_sep = values_containing_data.index('0c83020603050496') 278 | cab1_params_stream = values_containing_data[:idx_amp_cab_parameter_sep] 279 | cab2_params_stream = values_containing_data[idx_amp_cab_parameter_sep + 16:] 280 | cab1_params = parse_parameter(cab1_params_stream) 281 | cab2_params = parse_parameter(cab2_params_stream) 282 | slot_info.cab1_params = cab1_params 283 | slot_info.cab2_params = cab2_params 284 | return slot_info 285 | except ValueError: 286 | print("ERROR: No amp/cab parameter separator found in slot data") 287 | return None 288 | 289 | 290 | def parse_standard_module_slot(slot_data): 291 | try: 292 | idx = slot_data.index('1aff09') 293 | # ----------------------------------------------------------------- 294 | # Standard module 295 | # ----------------------------------------------------------------- 296 | if idx < 4: 297 | # print("ERROR: Can't read module id: " + str(slot_data)) 298 | print("ERROR: Can't read module id") 299 | return None 300 | 301 | slot_info = StandardSlotInfo() 302 | ac_data = slot_data[16:idx] 303 | 304 | on_off_state = slot_data[idx + 11] 305 | if on_off_state == '3': 306 | slot_info.on_off_state = 1 307 | elif on_off_state == '2': 308 | slot_info.on_off_state = 0 309 | else: 310 | print("ERROR: Unexpected slot on/off state") 311 | ''' 312 | try: 313 | idx_od_cd = ac_data.index('cd') 314 | ac_data_tmp = ac_data[0:idx_od_cd] 315 | ac_data_tmp += ac_data[idx_od_cd + 4:] 316 | ac_data = ac_data_tmp 317 | except ValueError: 318 | pass 319 | ''' 320 | 321 | # remove 19 322 | slot_info.module_id = ac_data 323 | 324 | # find end tag in slot data 325 | try: 326 | idx_end_tag = slot_data.index('0c83020003000490') 327 | 328 | if idx_end_tag < idx: 329 | print("ERROR: Unexpected value for end tag index: " + str(idx_end_tag)) 330 | return None 331 | values_containing_data = slot_data[idx + 28:idx_end_tag] 332 | params = parse_parameter(values_containing_data) 333 | slot_info.params = params 334 | except ValueError: 335 | idx = -1 336 | return slot_info 337 | 338 | except ValueError: 339 | return None 340 | 341 | 342 | def slot_splitter_2(val): 343 | slots = val.split('8213') 344 | 345 | # find first slot starting with either 06 or 08 346 | slot_1_idx = -1 347 | for i, slot in enumerate(slots): 348 | if slot.startswith('06') or slot.startswith('08'): 349 | slot_1_idx = i 350 | break 351 | 352 | if slot_1_idx == -1: 353 | print("ERROR: No slot 1 found in preset data") 354 | return 355 | 356 | if slot_1_idx == 0: 357 | print("ERROR: Slot 1 index is 0 - there can't be a Chain 1 IN slot") 358 | return 359 | 360 | # remove slots we don't understand or we don't need 361 | slots = slots[slot_1_idx-1:] 362 | slots = slots[0:20] 363 | # we must have 20 slots now 364 | if len(slots) != 20: 365 | print("ERROR: Expected exactly 20 slots here, got: " + str(len(slots))) 366 | return 367 | 368 | if not slots[0].startswith('00'): 369 | print("ERROR: Expected slot 0 (Chain 1 Input) to start with 00") 370 | if not slots[9].startswith('01'): 371 | print("ERROR: Expected slot 9 (Chain 1 Master) to start with 00") 372 | if not slots[10].startswith('02'): 373 | print("ERROR: Expected slot 10 (Chain 2 Input) to start with 00") 374 | if not slots[19].startswith('03'): 375 | print("ERROR: Expected slot 19 (Chain 2 Master) to start with 00") 376 | 377 | assignable_slots = [1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18] 378 | for slot_idx in assignable_slots: 379 | if not (slot.startswith('06') or slot.startswith('08')): 380 | print("ERROR: Expected slot " + str(slot_idx) + " to start with 06 or 08") 381 | 382 | slot_infos = [] 383 | for slot_idx in assignable_slots: 384 | slot_data = slots[slot_idx] 385 | if slot_data == '0814c0': 386 | # print(str(slot_idx) + ' is empty') 387 | empty_slot_info = EmptySlotInfo() 388 | empty_slot_info.slot_no = slot_idx 389 | slot_infos.append(empty_slot_info) 390 | continue 391 | # print(slot_data) 392 | slot_info = parse_standard_module_slot(slot_data) 393 | if slot_info is None: 394 | slot_info = parse_amp_and_cab_slot(slot_data) 395 | 396 | if slot_info is None: 397 | slot_info = parse_dual_cab_slot(slot_data) 398 | if slot_info is None: 399 | print("ERROR: Cannot read slot info: " + str(slot_idx)) 400 | continue 401 | 402 | slot_info.slot_no = slot_idx 403 | slot_infos.append(slot_info) 404 | return slot_infos 405 | 406 | 407 | def my_byte_cmp(left, right, length): 408 | if len(left) < length: 409 | return False 410 | if len(right) < length: 411 | return False 412 | 413 | for i in range(0, length): 414 | if left[i] == 'XX' or right[i] == 'XX': 415 | continue 416 | if left[i] != right[i]: 417 | return False 418 | break 419 | 420 | return True 421 | 422 | 423 | def main(args): 424 | file_list = { 425 | "amps_cabs": "all_amps_PLUS_cabs_in_slot_3_all_other_slots_empty", 426 | "amps": "all_amps_in_slot_3_all_other_slots_empty", 427 | "cabs": "all_cabs_in_slot_3_all_other_slots_empty", 428 | "delays": "all_delays_in_slot_3_all_other_slots_empty", 429 | "drives": "all_drives_in_slot_3_all_other_slots_empty_ERROR_switching_to_Clathorn_Drive", 430 | "dynamics": "all_dynamics_in_slot_3_all_other_slots_empty", 431 | "eqs": "all_eqs_in_slot_3_all_other_slots_empty", 432 | "filters": "all_filter_in_slot_3_all_other_slots_empty", 433 | "looper": "all_loopers_in_slot_3_all_other_slots_empty", 434 | "mods2": "all_modulationsV2_in_slot_3_all_other_slots_empty", 435 | "mods": "all_modulations_in_slot_3_all_other_slots_empty", 436 | "pitchs": "all_pitch_synths_in_slot_3_all_other_slots_empty", 437 | "preamps": "all_preamps_in_slot_3_all_other_slots_empty", 438 | "reverbs": "all_reverbs_in_slot_3_all_other_slots_empty", 439 | "send_return": "all_send_return_in_slot_3_all_other_slots_empty", 440 | "vol_pan": "all_vol_pan_in_slot_3_all_other_slots_empty", 441 | "wahs": "all_wahs_in_slot_3_all_other_slots_empty", 442 | "slot6": "switching_mod_slot6_PitchRingMod_to_AmRingMod", 443 | "slot5": "switching_rvb_slot5_from_Ganymede_to_Searchlights", 444 | "eqs_stereo": "all_eqsSTEREO_in_slot_3_all_other_slots_empty", 445 | "mods_stereo": "all_modulationsSTEREO_in_slot_3_all_other_slots_empty", 446 | "reverbs_stereo": "all_reverbsSTEREO_in_slot_3_all_other_slots_empty", 447 | "bass_amps": "all_BASSamps_in_slot_3_all_other_slots_empty", 448 | "mic_preamp": "all_MIC_JUST_ONE_preamps_in_slot_3_all_other_slots_empty", 449 | "bass_amps_cabs": "all_BASSamps_PLUS_cabs_in_slot_3_all_other_slots_empty", 450 | "bass_preamps": "all_BASSpreamps_in_slot_3_all_other_slots_empty", 451 | "delays_legacy": "all_delaysLEGACY_in_slot_3_all_other_slots_empty", 452 | "delays_stereo": "all_delaysSTEREO_in_slot_3_all_other_slots_empty", 453 | "drives_legacy": "all_drivesLEGACY_in_slot_3_all_other_slots_empty", 454 | "drives_stereo": "all_drivesSTEREO_in_slot_3_all_other_slots_empty", 455 | "dynamics_legacy": "all_dynamicsLEGACY_in_slot_3_all_other_slots_empty", 456 | "dynamics_stereo": "all_dynamicsSTEREO_in_slot_3_all_other_slots_empty", 457 | "filter_legacy": "all_filterLEGACY_in_slot_3_all_other_slots_empty", 458 | "filter_stereo": "all_filterSTEREO_in_slot_3_all_other_slots_empty", 459 | "wahs_stereo": "all_wahsSTEREO_in_slot_3_all_other_slots_empty", 460 | "mods_legacy": "all_modulationsLEGACY_in_slot_3_all_other_slots_empty", 461 | "pitch_synths_legacy": "all_pitch_synthsLEGACY_in_slot_3_all_other_slots_empty", 462 | "pitch_synths_stereo": "all_pitch_synthsSTEREO_in_slot_3_all_other_slots_empty", 463 | "reverbs_legacy": "all_reverbsLEGACY_in_slot_3_all_other_slots_empty", 464 | "send_return_stereo": "all_send_returnSTEREO_in_slot_3_all_other_slots_empty", 465 | "vol_pan_stereo": "all_vol_panSTEREO_in_slot_3_all_other_slots_empty", 466 | "cabs_dual": "all_cabsDUAL_in_slot_3_all_other_slots_empty" 467 | } 468 | 469 | last_session_id = [0x00, 0x00] 470 | preset_data = '' 471 | 472 | receiving_mode = False 473 | # for entry in file_list: 474 | with open('../doc/' + file_list["slot5"] + '.csv') as csv_file: 475 | # print(entry) 476 | csv_reader = csv.reader(csv_file, delimiter=';') 477 | line_count = 0 478 | display_message_key = '0A070B85000105' 479 | for row in csv_reader: 480 | data = row[4] 481 | if data == '': 482 | continue 483 | if data.endswith(', '): 484 | data = data[:-2] 485 | data_int = [int(x, 16) for x in data.split(', ')] 486 | 487 | if data_int[0] != 0x08 and receiving_mode is False: 488 | preset_data = '' 489 | continue 490 | 491 | if my_byte_cmp(data_int, ["XX", 0x1, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, "XX", 0x0, 0x4], 12) or my_byte_cmp(data_int, ["XX", 0x0, 0x0, 0x18, 0xed, 0x3, 0x80, 0x10, 0x0, "XX", 0x0, 0x4], 12): 492 | if data_int[0] == 0x08: 493 | receiving_mode = True 494 | 495 | hex_string = format_1(data) 496 | if len(hex_string) > 32: 497 | hex_string = hex_string[32:] 498 | preset_data += hex_string 499 | # print(hex_string) 500 | if data_int[0] != 0x08 and data_int[1] != 0x01: 501 | receiving_mode = False 502 | # print(preset_data) 503 | # get current display message 504 | try: 505 | idx_display_msg = preset_data.index(display_message_key) 506 | idx_display_msg += len(display_message_key) 507 | for i in range(1, 25): 508 | if preset_data[i] == '00': 509 | break 510 | display_msg += preset_data[i] 511 | except ValueError: 512 | display_msg = 'NOT FOUND!' 513 | 514 | slot_infos = slot_splitter_2(preset_data) 515 | if slot_infos is None: 516 | continue 517 | for slot in slot_infos: 518 | print(slot.to_string()) 519 | # if slot_infos[2] is not None: 520 | # print(slot_infos[2].to_string()) 521 | 522 | preset_data = '' 523 | # print('FINISHED') 524 | 525 | line_count += 1 526 | 527 | 528 | if __name__ == '__main__': 529 | main(sys.argv) 530 | -------------------------------------------------------------------------------- /utils/usb_helper.py: -------------------------------------------------------------------------------- 1 | 2 | # There are additional values within the USB Midi data. It seems the Midi messages are first padded to have mod 3 size. 3 | # 0x00's will be added for this. Next, either a 0x14, 0x15 or 0x16 is printed in front of every triple value segment. 4 | # I called these bytes Padding Signaling Bytes PBS. 5 | # If the original Midi message still has enough values (more or equal to three) a 0x14 is added. If one byte must 6 | # be added for padding (remaining size of original Midi message == 2) a 0x16 (yes 0x16, no typo) is added. If two 7 | # bytes must be added for padding (remaining size of original Midi message == 1) a 0x15 is added. 8 | # 9 | # Sometimes there seems to be a PSB of 0x17 being used in string values. Here, the padding seems to add 0x00s before 10 | # the 0xF7 value rather than afterwards. Example (0x2E is a '.'): 11 | # ... 0x2E 0x2E 0x17 0x2E 0x00 0xF7 => padding with PSB 0x17 12 | # ... 0x2E 0x2E 0x14 0x2E 0xF7 0x00 => padding with PSB 0x14 13 | # don't know the advantage yet 14 | # 15 | # Further, concatenated messages are split 64 byte chunks (or smaller => last chunk) in order to not exceed the 16 | # USB endpoint transfer limit. 17 | '' 18 | # Example: remove USB flags 19 | # 0x14, 0xF0, 0x00, 0x20, 0x14, 0x33, 0x02, 0x7F, 0x14, 0x47, 0x00, 0x00, 0x14, 0x00, 0x0C, 0x1A, 0x16, 0x40, 0xF7, 0x00 20 | # Read every mod % 3 == 0 position (0, 3, 6, 9, 12, ...) and check if there is one of the Padding Signaling Bytes. 21 | # If this is the case, don't copy those values to the output data. Depending on the PSB value remove former added 0x00 22 | # (not implemented yet - could be an improvement!) 23 | # 0xF0, 0x00, 0x20, 0x33, 0x02, 0x7F, 0x47, 0x00, 0x00, 0x00, 0x0C, 0x1A, 0x40, 0xF7, 0x00 24 | # Because the last PSB had a value of 0x16 one 0x00 byte was added for padding. We could remove this 25 | # 26 | # Example: add USB flags 27 | # 0xF0, 0x00, 0x20, 0x33, 0x02, 0x7F, 0x47, 0x00, 0x00, 0x00, 0x0C, 0x1A, 0x40, 0xF7 28 | # get the length of input data = 14 29 | # add 0x00's until there is a length that fulfills mod % 3 == 0. This is a length of 15 in this case. 30 | # Go to the input data and take groups of 3 bytes: 0xF0, 0x00, 0x20 31 | # if d == [0xF7, 0x00, 0x00] => append a PSB 0x15 since we padded two bytes 32 | # elif d[1:3] == [0xF7, 0x00] => append a PSB 0x16 since we padded one byte 33 | # else: append a PSB 0x14 34 | # and of course, add the three bytes afterwards 35 | 36 | 37 | class UsbHelper: 38 | def __init__(self): 39 | pass 40 | 41 | @staticmethod 42 | def remove_usb_flags(data_in): 43 | pos_in_data_in = 0 44 | data_out = [] 45 | for d in data_in: 46 | if pos_in_data_in % 4 == 0: 47 | if d not in [0x14, 0x15, 0x16, 0x17]: 48 | return data_in 49 | pos_in_data_in += 1 50 | continue 51 | data_out.append(d) 52 | pos_in_data_in += 1 53 | return data_out 54 | 55 | @staticmethod 56 | def remove_usb_flags_and_split(data_in): 57 | 58 | pos_in_data_in = 0 59 | data_out = [] 60 | for d in data_in: 61 | if pos_in_data_in % 4 == 0: 62 | if d not in [0x14, 0x15, 0x16, 0x17]: 63 | return 1, data_in, [] 64 | pos_in_data_in += 1 65 | continue 66 | data_out.append(d) 67 | pos_in_data_in += 1 68 | 69 | idx_f0 = data_out.index(0xF0) 70 | idx_f7 = data_out.index(0xF7) 71 | split_messages = [] 72 | while idx_f0 != -1 and idx_f7 != -1: 73 | split_messages.append(data_out[idx_f0:idx_f7 + 1]) 74 | try: 75 | idx_f0 = data_out.index(0xF0, idx_f7 + 1) 76 | except ValueError: 77 | idx_f0 = -1 78 | try: 79 | idx_f7 = data_out.index(0xF7, idx_f7 + 1) 80 | except ValueError: 81 | idx_f7 = -1 82 | 83 | remaining_part = [] 84 | if idx_f0 != -1 and idx_f7 == -1: 85 | # remaining parts in data_in 86 | remaining_part = UsbHelper.add_usb_flags(data_out[idx_f0:]) 87 | return 0, split_messages, remaining_part 88 | 89 | @staticmethod 90 | def add_usb_flags(data_in): 91 | length = len(data_in) 92 | padding = length % 3 93 | if padding == 0: 94 | pass 95 | elif padding == 1: 96 | data_in.append(0x00) 97 | data_in.append(0x00) 98 | elif padding == 2: 99 | data_in.append(0x00) 100 | elif padding == 3: 101 | pass 102 | 103 | data_out = [] 104 | for i in range(0, len(data_in), 3): 105 | d = data_in[i:i+3] 106 | if d == [0xF7, 0x00, 0x00]: 107 | data_out.append(0x15) 108 | elif d[1:3] == [0xF7, 0x00]: 109 | data_out.append(0x16) 110 | else: 111 | data_out.append(0x14) 112 | for x in d: 113 | data_out.append(x) 114 | 115 | return data_out 116 | 117 | @staticmethod 118 | def add_flags_and_join(messages_in): 119 | data_out = [] 120 | for msg in messages_in: 121 | data_out += UsbHelper.add_usb_flags(msg) 122 | return data_out 123 | 124 | 125 | def main(): 126 | import sys 127 | try: 128 | while True: 129 | ch = sys.stdin.read(1) 130 | if ch == '\n': 131 | sys.exit(0) 132 | 133 | try: 134 | cmd = int(ch) 135 | except ValueError as e: 136 | cmd = 0 137 | 138 | if cmd == 0: 139 | print("0") 140 | elif cmd == 1: 141 | print("1") 142 | elif cmd == 2: 143 | print("2") 144 | else: 145 | print('Unknown command: ' + ch) 146 | except KeyboardInterrupt: 147 | sys.exit(0) 148 | 149 | while len(data) > 0: 150 | sub = data[:64] 151 | print(sub) 152 | data = data[64:] 153 | 154 | input_data = [0x14, 0xF0, 0x00, 0x20, 0x14, 0x33, 0x02, 0x7F, 0x14, 0x06, 0x00, 0x00, 0x14, 0x00, 0x01, 0x04, 0x14, 0x03, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0xF7, 0x00, 0x00, 0x14, 0xF0, 0x00, 0x20, 0x14, 0x33, 0x02, 0x7F, 0x14, 0x06, 0x00, 0x00, 0x14, 0x00, 0x01, 0x04, 0x14, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0xF7, 0x00, 0x00, 0x14, 0xF0, 0x00, 0x20, 0x14, 0x33, 0x02, 0x7F] 155 | input_data2 = [0x14, 0x06, 0x00, 0x00, 0x14, 0x00, 0x01, 0x04, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x15, 0xF7, 0x00, 0x00] 156 | # input_data = [0x14, 0xF0, 0x00, 0x20, 0x14, 0x33, 0x02, 0x7F, 0x14, 0x06, 0x00, 0x00, 0x14, 0x00, 0x06, 0x20, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x0F, 0x14, 0x64, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x32, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x32, 0xF7, 0x00] 157 | # input_data = [0x14, 0xF0, 0x00, 0x20, 0x14, 0x33, 0x02, 0x7F, 0x14, 0x47, 0x00, 0x00, 0x14, 0x00, 0x0C, 0x1A, 0x16, 0x40, 0xF7, 0x00] 158 | 159 | messages, remaining_part = UsbHelper.remove_usb_flags_and_split(input_data) 160 | 161 | for d in [0x14, 0x06, 0x00, 0x00, 0x14, 0x00, 0x01, 0x04, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x15, 0xF7, 0x00, 0x00]: 162 | remaining_part.append(d) 163 | 164 | messages2, remaining_part = UsbHelper.remove_usb_flags_and_split(remaining_part) 165 | 166 | msgs = messages + messages2 167 | 168 | 169 | joint_msgs = UsbHelper.add_flags_and_join(msgs) 170 | bla = joint_msgs[:64] 171 | if input_data == bla: 172 | print('Equal!') 173 | else: 174 | print('NOT Equal!') 175 | 176 | bla2 = joint_msgs[64:] 177 | if input_data2 == bla2: 178 | print('Equal!') 179 | else: 180 | print('NOT Equal!') 181 | 182 | input_without_usb = UsbHelper.remove_usb_flags(input_data) 183 | input_with_usb = UsbHelper.add_usb_flags(input_without_usb) 184 | 185 | print(input_data) 186 | print(input_with_usb) 187 | 188 | if input_data == input_with_usb: 189 | print('Equal!') 190 | else: 191 | print('NOT Equal!') 192 | 193 | 194 | if __name__ == '__main__': 195 | main() 196 | -------------------------------------------------------------------------------- /utils/usb_monitor.py: -------------------------------------------------------------------------------- 1 | import usb.core 2 | import usb.util 3 | import time 4 | import re 5 | import threading 6 | import logging 7 | log = logging.getLogger(__name__) 8 | 9 | 10 | class UsbDescriptor: 11 | def __init__(self, device_id, bus, address, device): 12 | self.device_id = device_id 13 | self.bus = bus 14 | self.address = address 15 | self.device = device 16 | 17 | def __eq__(self, other): 18 | if self.device_id == other.device_id and self.bus == other.bus and self.address == other.address: 19 | return True 20 | else: 21 | return False 22 | 23 | 24 | class UsbMonitor: 25 | POLLING_INTERVAL_IN_SEC = 1 26 | regex = r"DEVICE ID ([a-f0-9]*:[a-f0-9]*) on Bus ([0-9][0-9][0-9]) Address ([0-9][0-9][0-9]) [=]*" 27 | 28 | def __init__(self, white_list_device_ids=list()): 29 | self.reported_devices = list() 30 | self.usb_device_found_cb_list = list() 31 | self.usb_device_lost_cb_list = list() 32 | self.request_terminate = False 33 | self.white_list_device_ids = white_list_device_ids 34 | self.monitor_thread = None 35 | 36 | @staticmethod 37 | def device_to_usb_descriptor(device): 38 | matches = re.finditer(UsbMonitor.regex, str(device), re.MULTILINE) 39 | for matchNum, match in enumerate(matches, start=1): 40 | 41 | if len(match.groups()) != 3: 42 | print('Unexpected group size while looking for USB data (3 expected): ' + 43 | str(len(match.groups()))) 44 | continue 45 | 46 | _ = match.group(0) 47 | device_id = match.group(1) 48 | bus = match.group(2) 49 | address = match.group(3) 50 | 51 | return UsbDescriptor(device_id, bus, address, device) 52 | return None 53 | 54 | def monitor(self): 55 | log.info('Looking for connected USB devices: ' + str(self.white_list_device_ids)) 56 | while not self.request_terminate: 57 | connected_devices = list() 58 | for dev in usb.core.find(find_all=True): 59 | 60 | usb_descriptor = self.device_to_usb_descriptor(dev) 61 | 62 | if usb_descriptor is not None: 63 | connected_devices.append(usb_descriptor) 64 | 65 | # look for new devices 66 | for dev in connected_devices: 67 | if dev not in self.reported_devices: 68 | if dev.device_id in self.white_list_device_ids: 69 | for cb in self.usb_device_found_cb_list: 70 | cb(dev) 71 | 72 | # look for lost devices 73 | for dev in self.reported_devices: 74 | if dev not in connected_devices: 75 | if dev.device_id in self.white_list_device_ids: 76 | for cb in self.usb_device_lost_cb_list: 77 | cb(dev) 78 | 79 | self.reported_devices = connected_devices 80 | time.sleep(self.POLLING_INTERVAL_IN_SEC) 81 | 82 | def start(self): 83 | self.monitor_thread = threading.Thread(target=self.monitor, args=()) 84 | self.monitor_thread.start() 85 | 86 | def register_device_found_cb(self, cb): 87 | self.usb_device_found_cb_list.append(cb) 88 | 89 | def register_device_lost_cb(self, cb): 90 | self.usb_device_lost_cb_list.append(cb) 91 | -------------------------------------------------------------------------------- /utils/xls_to_py.py: -------------------------------------------------------------------------------- 1 | import xlrd 2 | 3 | 4 | book = xlrd.open_workbook("./doc/Module_IDs.xls") 5 | 6 | print("The number of worksheets is {0}".format(book.nsheets)) 7 | print("Worksheet name(s): {0}".format(book.sheet_names())) 8 | 9 | for i in range(0, book.nsheets): 10 | sh = book.sheet_by_index(i) 11 | # print("{0} {1} {2}".format(sh.name, sh.nrows, sh.ncols)) 12 | # print("Cell D30 is {0}".format(sh.cell_value(rowx=29, colx=3))) 13 | for rx in range(1, sh.nrows): 14 | if sh.row(rx)[2].value != '': 15 | module_id = sh.row(rx)[2].value 16 | try: 17 | module_id = int(module_id) 18 | module_id = str(module_id) 19 | except ValueError: 20 | pass 21 | 22 | if module_id.startswith("'"): 23 | module_id = module_id[1:] 24 | if module_id == 'n/a': 25 | pass 26 | else: 27 | print("'" + module_id + "':\t['" + sh.row(rx)[1].value + "', '" + sh.row(rx)[0].value + " (mono)'],") 28 | if sh.row(rx)[3].value != '': 29 | module_id = sh.row(rx)[3].value 30 | try: 31 | module_id = int(module_id) 32 | module_id = str(module_id) 33 | if len(module_id) == 1: 34 | module_id = '0' + module_id 35 | except ValueError: 36 | pass 37 | 38 | if module_id.startswith("'"): 39 | module_id = module_id[1:] 40 | if module_id == 'n/a': 41 | pass 42 | else: 43 | print("'" + module_id + "':\t['" + sh.row(rx)[1].value + "', '" + sh.row(rx)[0].value + " (stereo)'],") 44 | if sh.row(rx)[4].value != '': 45 | module_id = sh.row(rx)[4].value 46 | try: 47 | module_id = int(module_id) 48 | module_id = str(module_id) 49 | except ValueError: 50 | pass 51 | if module_id.startswith("'"): 52 | module_id = module_id[1:] 53 | if module_id == 'n/a': 54 | pass 55 | else: 56 | print("'" + module_id + "':\t['" + sh.row(rx)[1].value + "', '" + sh.row(rx)[0].value + " (legacy)'],") 57 | --------------------------------------------------------------------------------