diff --git a/adaptations/Oberheim_Teo-5.py b/adaptations/Oberheim_Teo-5.py new file mode 100644 index 00000000..741497dd --- /dev/null +++ b/adaptations/Oberheim_Teo-5.py @@ -0,0 +1,47 @@ +# +# Copyright (c) 2021 Christof Ruch. All rights reserved. +# +# Dual licensed: Distributed under Affero GPL license by default, an MIT license is available for purchase +# +import sys +from typing import List + +import sequential +import testing + +this_module = sys.modules[__name__] + + +# +# Configure the GenericSequential module +# +def teo5_bank_name(bank) -> str: + return str(bank) if bank < 10 else chr(ord("A") + (bank - 10)) + + +def teo5_program_name(program) -> str: + bank = program // 16 + program = program % 16 + return teo5_bank_name(bank) + f"{program:02d}" + + +sequential.GenericSequential(name="Oberheim Teo-5", + manufacturer=0x10, # Oberheim, not Sequential + device_id=0x5a, + banks=16, + patches_per_bank=16, + name_len=20, + name_position=159, + friendlyBankName=teo5_bank_name, + friendlyProgramName=teo5_program_name, + ).install(this_module) + + +# Test data picked up by test_adaptation.py +def make_test_data(): + def programs(data: testing.TestData) -> List[testing.ProgramTestData]: + yield testing.ProgramTestData(message=data.all_messages[2], name='OB-X Pad', number=2, friendly_number="002") + yield testing.ProgramTestData(message=data.all_messages[17], name='Waterfall Cavern', number=17, friendly_number="101") + yield testing.ProgramTestData(message=data.all_messages[80], name='FlyLike a TEO', number=80, friendly_number="500") + + return testing.TestData(sysex="testData/Oberheim_Teo5/TEO5_Factory_Programs_v1.00.syx", program_generator=programs, friendly_bank_name=(11, "B")) diff --git a/adaptations/sequential/GenericSequential.py b/adaptations/sequential/GenericSequential.py index 4ef9e416..8f691624 100644 --- a/adaptations/sequential/GenericSequential.py +++ b/adaptations/sequential/GenericSequential.py @@ -24,10 +24,12 @@ # Prophet 5 - 0b00110010 0x32 (this is the Rev 4 of course) or 0b00110011 0x33 (Desktop module?) # Take 5 - 0x35 (they left 0x34 empty - maybe the desktop Prophet 5 and...?) # Trigon-6 - 0b00111001 0x39 (the manual is not updated but uses the Prophet 6 ID) +# Teo-5 - 0b01011010 0x5a (the manual is not updated but uses the Take 5 ID) - this also additionally uses the MIDI ID for Oberheim, 0x10 class GenericSequential: def __init__(self, name, device_id, banks, patches_per_bank, + manufacturer=0x01, # Default is Sequential, obviously name_len=None, name_position=None, file_version=None, @@ -45,6 +47,7 @@ def __init__(self, name, device_id, banks, patches_per_bank, self.__id_list = id_list self.__banks = banks self.__patches_per_bank = patches_per_bank + self.__manufacturer = manufacturer self.__name_len = name_len self.__name_position = name_position self.__file_version = file_version @@ -78,7 +81,7 @@ def channelIfValidDeviceResponse(self, message): and message[1] == 0x7e # Non-realtime and message[3] == 0x06 # Device request and message[4] == 0x02 # Device request reply - and message[5] == 0x01 # Sequential / Dave Smith Instruments + and message[5] == self.__manufacturer and message[6] in self.__id_list): # Family seems to be different, the Prophet 12 has (0x01, 0x00, 0x00) while the Evolver has (0, 0, 0) # and message[7] == 0x01 # Family MS is 1 @@ -106,7 +109,7 @@ def createEditBufferRequest(self, channel): def isEditBufferDump(self, message): return (len(message) > 3 and message[0] == 0xf0 - and message[1] == 0x01 # Sequential + and message[1] == self.__manufacturer and message[2] in self.__id_list and (self.__file_version is None and message[3] == 0b00000011 # Edit Buffer Data or message[3] == self.__file_version and message[4] == 0b00000011)) # Edit Buffer Data @@ -125,15 +128,15 @@ def createProgramDumpRequest(self, channel, patchNo): program = patchNo % self.numberOfPatchesPerBank() if self.__file_version is None: # Modern style - return [0xf0, 0x01, self.__id, 0b00000101, bank, program, 0xf7] + return [0xf0, self.__manufacturer, self.__id, 0b00000101, bank, program, 0xf7] else: # Evolver style - return [0xf0, 0x01, self.__id, self.__file_version, 0b00000101, bank, program, 0xf7] + return [0xf0, self.__manufacturer, self.__id, self.__file_version, 0b00000101, bank, program, 0xf7] def isSingleProgramDump(self, message): return (len(message) > 3 and message[0] == 0xf0 - and message[1] == 0x01 # Sequential + and message[1] == self.__manufacturer and message[2] in self.__id_list and (self.__file_version is None and message[3] == 0b00000010 # Program Data or message[3] == self.__file_version and message[4] == 0b00000010)) # Program Data diff --git a/adaptations/testData/Oberheim_Teo5/TEO5_Factory_Programs_v1.00.syx b/adaptations/testData/Oberheim_Teo5/TEO5_Factory_Programs_v1.00.syx new file mode 100644 index 00000000..dcd264ca Binary files /dev/null and b/adaptations/testData/Oberheim_Teo5/TEO5_Factory_Programs_v1.00.syx differ