From d9798eac226a1de5bebe3d8bd4aa9e7f4221e446 Mon Sep 17 00:00:00 2001 From: cruc Date: Sun, 3 Jan 2021 23:42:54 +0100 Subject: [PATCH] Do add the Bank loading to the Kawai K1, and extend the "SoundDiver" code we talked about in #59. --- adaptions/BundledAdaptation.cpp | 5 +- adaptions/CMakeLists.txt | 2 +- adaptions/KawaiK1.py | 193 +++++++++++++++++++++++++++----- 3 files changed, 168 insertions(+), 32 deletions(-) diff --git a/adaptions/BundledAdaptation.cpp b/adaptions/BundledAdaptation.cpp index 39bdf1b8..50009c09 100644 --- a/adaptions/BundledAdaptation.cpp +++ b/adaptions/BundledAdaptation.cpp @@ -18,13 +18,14 @@ namespace knobkraft { { "DSI Prophet 08", "DSI_Prophet_08", std::string(DSI_Prophet_08_py, DSI_Prophet_08_py + DSI_Prophet_08_py_size) }, { "DSI Prophet 12", "DSI_Prophet_12", std::string(DSI_Prophet_12_py, DSI_Prophet_12_py + DSI_Prophet_12_py_size) }, { "Electra One", "Electra_one", std::string(ElectraOne_py, ElectraOne_py + ElectraOne_py_size) }, + { "Kawai K1", "Kawai_K1", std::string(KawaiK1_py, KawaiK1_py + KawaiK1_py_size) }, { "Korg DW-6000", "Korg_DW_6000", std::string(KorgDW6000_py, KorgDW6000_py + KorgDW6000_py_size) }, { "Korg MS2000", "Korg_MS2000", std::string(KorgMS2000_py, KorgMS2000_py + KorgMS2000_py_size) }, { "Matrix 6", "Matrix_6", std::string(Matrix_6_py, Matrix_6_py + Matrix_6_py_size) }, - { "Oberheim OB-8", "Oberheim_OB8", std::string(OberheimOB8_py, OberheimOB8_py + OberheimOB8_py_size ) }, + { "Oberheim OB-8", "Oberheim_OB8", std::string(OberheimOB8_py, OberheimOB8_py + OberheimOB8_py_size) }, #ifdef _DEBUG { "Matrix 1000 Test", "Matrix_1000", std::string(Matrix1000_py, Matrix1000_py + Matrix1000_py_size) }, - { "Korg DW-8000 Test", "Korg_DW_8000_Adaption", std::string(KorgDW8000_py, KorgDW8000_py + KorgDW8000_py_size ) }, + { "Korg DW-8000 Test", "Korg_DW_8000_Adaption", std::string(KorgDW8000_py, KorgDW8000_py + KorgDW8000_py_size) }, { "Kawai K3 Test", "Kawai_K3", std::string(KawaiK3_py, KawaiK3_py + KawaiK3_py_size) }, #endif { "Pioneer Toraiz AS1", "Pioneer_Toraiz_AS1", std::string(PioneerToraiz_AS1_py, PioneerToraiz_AS1_py + PioneerToraiz_AS1_py_size) }, diff --git a/adaptions/CMakeLists.txt b/adaptions/CMakeLists.txt index 443ad596..e8ad58f8 100644 --- a/adaptions/CMakeLists.txt +++ b/adaptions/CMakeLists.txt @@ -10,7 +10,7 @@ project(KnobKraft-Generic-Adaptation) set(adaptation_files "Behringer Deepmind 12.py" "DSI Pro 2.py" "DSI Prophet 08.py" - "DSI Prophet 12.py" "ElectraOne.py" "KawaiK3.py" "KorgDW6000.py" "KorgDW8000.py" "KorgMS2000.py" "Matrix 6.py" + "DSI Prophet 12.py" "ElectraOne.py" "KawaiK1.py" "KawaiK3.py" "KorgDW6000.py" "KorgDW8000.py" "KorgMS2000.py" "Matrix 6.py" "Matrix1000.py" "OberheimOB8.py" "PioneerToraiz-AS1.py" "Roland JX-8P.py" "RolandD50.py" "Sequential Pro 3.py" "Sequential Prophet 5 Rev4.py" "Sequential Prophet 6.py" "Sequential Prophet X.py" "Waldorf Blofeld.py" ) diff --git a/adaptions/KawaiK1.py b/adaptions/KawaiK1.py index d17779c8..695fcba0 100644 --- a/adaptions/KawaiK1.py +++ b/adaptions/KawaiK1.py @@ -6,7 +6,7 @@ adaptation = { "Manufacturer Name": "Kawai", - "Manufacturer MIDI ID": 0x40, + # "Manufacturer MIDI ID": 0x40, "Model": "K1", "Device ID": (2, 1, 16), # sysex offset, min value, max value. this is the sysex device ID, not the K1 ID "Default Timeout": 200, # milliseconds to wait during Scan @@ -17,7 +17,7 @@ "Data Types": {"Single": {"Size": 87, "Name Size": 10, "Name Offset": 0, "Name format": "Ascii"}}, "Bank Drivers": [{"Bank Name": "Int-Singles I/1", "Data Type": "Single", "# of Entries": 32, "Transmission Format": "7bit", "Checksum Type": "Kawai K1/K4", - "Memory Bank": True, + # "Memory Bank": True, "Offsets": (0, 0, 0), # These are the program offsets for single, bank, program change "Single Request": [0xf0, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, "EN#", 0xf7], "Single Reply": [0xf0, 0x40, 0x00, 0x20, 0x00, 0x03, 0x00, "EN#", "SUM", "SIN", "CHK", 0xf7], @@ -26,12 +26,30 @@ 0xf7]}, {"Bank Name": "Int-Singles i/2", "Data Type": "Single", "# of Entries": 32, "Transmission Format": "7bit", "Checksum Type": "Kawai K1/K4", - "Memory Bank": True, + # "Memory Bank": True, "Offsets": (32, 32, 32), # These are the program offsets for single, bank, program change "Single Request": [0xf0, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, "EN#", 0xf7], "Single Reply": [0xf0, 0x40, 0x00, 0x20, 0x00, 0x03, 0x00, "EN#", "SUM", "SIN", "CHK", 0xf7], - "Bank Request": [0xf0, 0x40, 0x00, 0x01, 0x00, 0x03, 0x20, 0x00, 0xf7], - "Bank Reply": [0xf0, 0x40, 0x00, 0x21, 0x00, 0x03, 0x20, 0x00, "[", "SUM", "SIN", "CHK", "]", + "Bank Request": [0xf0, 0x40, 0x00, 0x01, 0x00, 0x03, 0x00, 0x20, 0xf7], + "Bank Reply": [0xf0, 0x40, 0x00, 0x21, 0x00, 0x03, 0x00, 0x20, "[", "SUM", "SIN", "CHK", "]", + 0xf7]}, + {"Bank Name": "Ext-Singles E/1", "Data Type": "Single", "# of Entries": 32, + "Transmission Format": "7bit", "Checksum Type": "Kawai K1/K4", + # "Memory Bank": True, + "Offsets": (0, 0, 0), # These are the program offsets for single, bank, program change + "Single Request": [0xf0, 0x40, 0x00, 0x00, 0x00, 0x03, 0x01, "EN#", 0xf7], + "Single Reply": [0xf0, 0x40, 0x00, 0x20, 0x00, 0x03, 0x01, "EN#", "SUM", "SIN", "CHK", 0xf7], + "Bank Request": [0xf0, 0x40, 0x00, 0x01, 0x00, 0x03, 0x01, 0x00, 0xf7], + "Bank Reply": [0xf0, 0x40, 0x00, 0x21, 0x00, 0x03, 0x01, 0x00, "[", "SUM", "SIN", "CHK", "]", + 0xf7]}, + {"Bank Name": "Ext-Singles e/2", "Data Type": "Single", "# of Entries": 32, + "Transmission Format": "7bit", "Checksum Type": "Kawai K1/K4", + # "Memory Bank": True, + "Offsets": (32, 32, 32), # These are the program offsets for single, bank, program change + "Single Request": [0xf0, 0x40, 0x00, 0x00, 0x00, 0x03, 0x01, "EN#", 0xf7], + "Single Reply": [0xf0, 0x40, 0x00, 0x20, 0x00, 0x03, 0x01, "EN#", "SUM", "SIN", "CHK", 0xf7], + "Bank Request": [0xf0, 0x40, 0x00, 0x01, 0x00, 0x03, 0x01, 0x20, 0xf7], + "Bank Reply": [0xf0, 0x40, 0x00, 0x21, 0x00, 0x03, 0x01, 0x20, "[", "SUM", "SIN", "CHK", "]", 0xf7]} ], } @@ -56,6 +74,14 @@ def needsChannelSpecificDetection(): return True +def deviceDetectWaitMilliseconds(): + return adaptation["Default Timeout"] + + +def generalMessageDelay(): + return adaptation["Default Send Pause"] + + def channelIfValidDeviceResponse(message): if ignoreDeviceID(message) == adaptation["Scan reply"]: return message[2] & 0x0f # I can't believe device ID is really 1-based? @@ -82,7 +108,7 @@ def createProgramDumpRequest(channel, program_no): if c == "EN#": result.append(program_no - adaptation["Bank Drivers"][bank]["Offsets"][0]) else: - pass # For now, ignore other pseude-bytes + pass # For now, ignore other pseud0-bytes else: result.append(c) return insertDeviceID(channel, result) @@ -90,19 +116,25 @@ def createProgramDumpRequest(channel, program_no): def isSingleProgramDump(message): for b in range(numberOfBanks()): - worked, program, data = parseMessage(b, message) + worked, program, data = parseMessage(message, adaptation["Bank Drivers"][b]["Single Reply"]) if worked: return True return False -def convertToProgramDump(channel, data, program_no): - raise Exception("Not implemented yet") +def convertToProgramDump(channel, message, program_no): + if isSingleProgramDump(message): + for b in range(numberOfBanks()): + worked, program, data = parseMessage(message, adaptation["Bank Drivers"][b]["Single Reply"]) + if worked: + single_dump = createMessage(adaptation["Bank Drivers"][b]["Single Reply"], program_no, data) + return insertDeviceID(channel, single_dump) + raise Exception("Can only convert single program dumps!") def nameFromDump(message): for bank in range(numberOfBanks()): - worked, program, data = parseMessage(bank, message) + worked, program, data = parseMessage(message, adaptation["Bank Drivers"][bank]["Single Reply"]) if worked: name = [] for i in range(adaptation["Data Types"]["Single"]["Name Size"]): @@ -114,42 +146,91 @@ def nameFromDump(message): def numberFromDump(message): for b in range(numberOfBanks()): - worked, program, data = parseMessage(b, message) + worked, program, data = parseMessage(message, adaptation["Bank Drivers"][b]["Single Reply"]) if worked: return program raise Exception("Only single program dumps have program numbers") +def createBankDumpRequest(channel, bank): + return insertDeviceID(channel, adaptation["Bank Drivers"][bank]["Bank Request"]) + + +def isPartOfBankDump(message): + for b in range(numberOfBanks()): + worked, program, data = parseMessage(message, adaptation["Bank Drivers"][b]["Bank Reply"], + adaptation["Bank Drivers"][b]) + if worked: + return True + return False + + +def isBankDumpFinished(messages): + for message in messages: + if isPartOfBankDump(message): + return True + return False + + +def extractPatchesFromBank(message): + result = [] + for b in range(numberOfBanks()): + worked, programs, datas = parseMessage(message, adaptation["Bank Drivers"][b]["Bank Reply"], + adaptation["Bank Drivers"][b]) + if worked: + for i in range(len(datas)): + single_dump = createMessage(adaptation["Bank Drivers"][b]["Single Reply"], i, datas[i]) + assert isSingleProgramDump(single_dump) + result = result + single_dump + return result + + def friendlyBankName(bank): return adaptation["Bank Drivers"][bank]["Bank Name"] -def parseMessage(bank, message): +def parseMessage(message, reply_expected, bank_driver=None): reply = ignoreDeviceID(message) - reply_expected = adaptation["Bank Drivers"][bank]["Single Reply"] - data = [] - data_block = False + prg_result = -1 + result = [] + nested = False reply_ptr = 0 expected_ptr = 0 - program_no = -1 + loop_start = -1 while reply_ptr < len(reply): if type(reply_expected[expected_ptr]) is str: - if data_block: - # We're in the status of reading the data block! - while len(data) < adaptation["Data Types"]["Single"]["Size"]: - data.append(reply[reply_ptr]) - reply_ptr = reply_ptr + 1 - data_block = False + if reply_expected[expected_ptr] == "[": + nested = True + if type(prg_result) is not list: + prg_result = [] + loop_start = expected_ptr expected_ptr = expected_ptr + 1 + elif reply_expected[expected_ptr] == "]": + # Check if we have enough + if len(result) < bank_driver["# of Entries"]: + expected_ptr = loop_start + else: + expected_ptr = expected_ptr + 1 elif reply_expected[expected_ptr] == "SUM": summation_start = reply_ptr expected_ptr = expected_ptr + 1 elif reply_expected[expected_ptr] == "EN#": - program_no = reply[reply_ptr] + if nested: + prg_result.append(reply[reply_ptr]) + else: + prg_result = reply[reply_ptr] expected_ptr = expected_ptr + 1 reply_ptr = reply_ptr + 1 elif reply_expected[expected_ptr] == "SIN": - data_block = True + data = [] + while len(data) < adaptation["Data Types"]["Single"]["Size"]: + data.append(reply[reply_ptr]) + reply_ptr = reply_ptr + 1 + expected_ptr = expected_ptr + 1 + if nested: + result.append(data) + else: + result = data elif reply_expected[expected_ptr] == "CHK": # Ignore checksum for now expected_ptr = expected_ptr + 1 @@ -162,7 +243,34 @@ def parseMessage(bank, message): return False, -1, [] reply_ptr = reply_ptr + 1 expected_ptr = expected_ptr + 1 - return True, program_no, data + return True, prg_result, result + + +def createMessage(expected_message, program_no, data_block): + data = [] + ptr = 0 + while ptr < len(expected_message): + if type(expected_message[ptr]) is str: + if expected_message[ptr] == "SUM": + summation_start = ptr + ptr = ptr + 1 + elif expected_message[ptr] == "EN#": + data.append(program_no) + ptr = ptr + 1 + elif expected_message[ptr] == "SIN": + data = data + data_block + ptr = ptr + 1 + elif expected_message[ptr] == "CHK": + # Ignore checksum for now + data.append(0) + ptr = ptr + 1 + else: + raise Exception("Unknown pseudo byte" + expected_message[ptr]) + else: + # Just copy byte verbatim + data.append(expected_message[ptr]) + ptr = ptr + 1 + return data def bankNoForProgramNo(program_number): @@ -184,16 +292,34 @@ def ignoreDeviceID(message): return message[0:adaptation["Device ID"][0]] + [0] + message[adaptation["Device ID"][0] + 1:] +def kawaiK1K4Checksum(data): + return (0xA4 + sum(data)) & 0x7f + + import binascii +def splitSysexMessage(messages): + result = [] + start = 0 + read = 0 + while read < len(messages): + if messages[read] == 0xf0: + start = read + elif messages[read] == 0xf7: + result.append(messages[start:read + 1]) + read = read + 1 + return result + + def runTests(): print("Adaption for", name()) - print("Autodetection message", createDeviceDetectMessage(0)) + assert name() == "Kawai K1" + assert createDeviceDetectMessage(0) == [240, 64, 0, 96, 247] for channel in range(16): reply = [0xf0, 0x40, channel, 0x61, 0x00, 0x03, 0xf7] assert channelIfValidDeviceResponse(reply) == channel - assert numberOfBanks() == 2 + assert numberOfBanks() == 4 assert numberOfPatchesPerBank() == 32 assert friendlyBankName(0) == "Int-Singles I/1" assert friendlyBankName(1) == "Int-Singles i/2" @@ -202,10 +328,19 @@ def runTests(): assert bankNoForProgramNo(32) == 1 test_single = "F040002000030000467265746C65737320313B2432323E02150010005F320032343237484848483D3C3D6F0E0E0A2A4E515164000000000C100E073C3B3C2A000000001A1616224D4D435E323232321D1E2B143B3D3E321F323236323232320BF7" - test_single2 = "F040002000030001467265746C65737320325D0C32323E021D00321B323200323235363C4E403C57255A6E0E0F2F0E64646464000000000B09062D40251F4B000000001517111E4233613D323232323200212C323232321832170C3232323265F7" + # test_single2 = "F040002000030001467265746C65737320325D0C32323E021D00321B323200323235363C4E403C57255A6E0E0F2F0E64646464000000000B09062D40251F4B000000001517111E4233613D323232323200212C323232321832170C3232323265F7" single_program = list(binascii.unhexlify(test_single)) assert isSingleProgramDump(single_program) - print(nameFromDump(single_program)) + assert nameFromDump(single_program) == "Fretless 1" + + bank_1 = "F0400021000301005065616365202020202057053242320C1E21342F3332002C332E33545454546D6A3D3D1B0B2F2B37373D36082615003F43261E3C3E38360715000031232F2C3B473A4132323232322E57323232323232323232323232323C5374726176696E736B792C0532323202270032293232002F323934484854546D6A733E0B077E2A415D285D00180000331F171A3F2B11155A5E6445323B2C0F3F3332323232323232323232323232323232323232322A326748616C6C6F7765656E20631432643203180332003239005C32002F325447487F7F6D6A050B03033A642D26340000004828374B30464A4A0700343B4D4A3B4732323332393239390D00644C32324C3231313D323200323C01547562627327202020202705323232021E0431303232002C37303248484848616A6A612F1F0F2F6464646400000000000000374555504500000000242D2F244A4A644A32323232303030303232323200322D003232323251456C6563416375737463400532323D021B0032003232001C34303448484848553365571F0E1A6F643E5564000000000B110C00333D372D000000001C1724215A6464643232323224322B2B3232323217322032323232325C427265617468652020202605323232022C0031303232002E372B31484848486D6C6B6A0F1B1F1B6464643E000000175634595141413D461C002F003F343F445753464332323232301E3030003200002D322D323232323243467265746C65737320203B2432323E02150010005F320032343237484848483D3C3D6F0E0E0A2A4E515164000000000C100E073C3B3C2A000000001A1616224D4D435E323232321D1E2B143B3D3E321F323236323232327A536C6176654C61626F7240043249320336013200323200352F382C004B541B76766E53212121216464646400004349231E0034343434270000000022222A2C535353533232323232321E3032323232323227323232323205537472696E674F726368370532323C02000031303232002F313533545454542B6A2B6A222322233D5E3D5E00030E142E30303000000000646464642E2A2D2E4B4B4B4B353535353232323232323232323232323232323213537472696E67204F6374370532323C02000031303232002F313533545448482B6A2B6A222302033D5E3D5E00030E142E30303000000000646464642E2A2D2E64643D433232323232323232323232323232323232323232054272617373537472696B3F04323234022E01322B32320037333A224D4F5454525252630E0E0E0B5D5B5264030602001E1B1522243D2C153C41424610181518414B5047303232323E41323232323232323232323B3B32322D466174686F726E7320203B243237320204083200323200382F313448484818175252030A0E0A285F5C4A56040000003C3022102729292A64566400222615143341414B3A32423232554E38293232323232323232323232774B6172617465204B69644025323232023B004708493208323232344848483C646B77231F0F0E0F64502F090000000131322D2D36413F64000000001E2328235548643232323232323232320000323219323232323232323F57686973746C652020202C01322632021C00221132320C324732386C6C6060000002010E3A0E0E64490A09000A00002C2828203627262521210007181E161432643C3D32323232262F2B2C32323232323232323232343253456C65634772616E64204005323232023B00320032320032223331484848485C612D2D0223020264624B4B000000000B051316393E444A0000000023182323645C4A4A3232323228173030323232321F0F1412323232322A4A617A7A5069616E6F2038053236320002003200323200342131325454545456605859022302025B295757000000000A070A054443444400000000221B222351646464323232323C0F35213232323215151515323232320F556E64725072657373723B15323932021200161032320037322D35484848540F27115F0A0E0E0B56624E4C010000000C282F054A404924006000002828291F4C323C64326432322E0A2E2E323232322632323232323232385068616E746F6D2120201E043240320C2702320032320032303238484854600A0A0A0A0B0B0B0B46466464000301021F20222564642222646464642A2A2A2A323232323232323226261E2132323232323232323A32323271477269747479436C61763B04323232023B0032003232002131323248484848553E63331F1B0E0E5464533A000000000000000042233C3C000000000012120B574F585A32323232263232443232453232322732323232324A436C6173736963616C2059043232320211003200323208323232323C483C48355935640E2B0E0E4D335421010002010A0915142C14352B000000001C1723214E5D4C5032323232262B2B393232323232322332323232321D4D61676963616C446F6738043232320226103200323200323232355454545460242127065F1E06433C453F0000000000050A00311A00322F00640C001613004855635E32323232006464003232323232323232326432324C526574726E32344576722D4532324102180032005F3200322E32326054546C333E0D434A0E0B6A49644B4900000008050D050C2E3E583200000000221C2527433C494A3232323232393132333B32324232323232323232034D656C6C6F774B657973272532323E020F002A0E32320032302E344848480A343A55000A1E1E386464642C000000000C090C09383831340000000031312F2C644E643332323232323232323232323232323232323232327846756E6B2042617373205204323232021300320032320032343237484848483A5A48570E2B2F0E4F646458000000000B050009352C1C2E000000001816141743595053323232322329211D3E3A324532123031323232322A536B79205772697465724654325832030000320032320031323232004A3C586B6C6B6B050505054E4E64640000000064555B5B3E34254A64646464474A474A3232323239393939313131313232323231313131323232327857617465724472616D61540532643202580C3000323200353130363C48543C6B6C746D1F2B5B1B3C4A393E000000000707134D473B4855642648003F36394064646446323232320000645732323232323232213232323206537061636542616E6A6F4205503D3202140028083232002D393430545454546161616126262222646464640000000005050505313131310000000031313131646464643232323228282828323232323232323232323232555A756C752020202020203E0532323602130432003232003232323223484854513E6D5221062F0F4B5233640000000005280000313931170000000037273717323232323232323203323223323232323232323232323232544B72797374616C567962350532323E02090C301B32320030342E3660606060626262622A2A2A2A555855530000000000000A0A3436363600000000343636364D4D4D4D32323232323232323233333232323232323232320D526576657273654869743805323D32026400462532320033312F35403C503C6E517B4F2B1B191B64646464333300330A062F0034440E3000000000353A342E6464615D32323232321F3232323232323232323232323232567B7B42617272656C7D7D4505323232023B0032003232002D2F3537484848486D6B596B232323235F64645F00000000000002003827253400000000382728345151515132323232333333333232323232322132323232324A4761746564204B26536E4C05324F3E022910301B32320035312F325049573E6F6E4C4F29292929645A6464000000000006000023252225000000001F1E1F1E55644D6432323232646400003232323232323232323232324EF7" + # bank_2 = "F040002100030120416972792020202020206304353333023C02320032320030342E365454545438384B4B0F1F2F3F3A3A564500000000292923243E3E444000000000363636364744603B3232373728282A2A3232323232321A2D32323232634E696E6F20526F7461204505323235022B103200323200372F3C2E48484848386D0504221317435F50354F00000000001A16163C524B572100004A1B301907433447453232323200646464323232323232323232322A32384D7973746572794169725C043246320300003200323200323130344E4852546B6B6C6C232327275C5A4D6431004248554343464E4E4E471B1B1B1B4A474A47525252523E3E3E3E28281E4C323232320000143232323232784A616E277320536F6C6F2401323333023404320032320C32343457545454542D2057622F0E162164646447000000000010140025343C1A006400001D23231D554F4F6432323232292B2B45323232322533203232323232304D7574654775697461724C08323232023B10320032320C3532323248485B3C565556560F1B0F0B64644B6200010E00000015102633261B000000001D1A272141645C5832323232323200643232323232153232323232327852656A6F69636521202040053A3C3B023D01301B32320030383834544860485C6D326A1F1B1A1B594D645A030A00001F18001C6442323364571A443838362C6464645232323232003264323233333232323232323232323A42656C6C7320202020203D04323232003B00320032320032213216545154542800292A0F0A0F0F6417563F0000000018170A083D3B2F3A000000003B3A2E3B484848483232323226262626323232321E1F201E32323232145072656461746F7220205305324F3E022910301B32320035312F32453F3F3C7E7E6E7E3B4B2B4B58644164112F003B40403D424444464400000000373741374A48645532323232323232323232323232323232323232325756656C6F537472696E673F05323C3C02160031303232003B32372E545454542B6A2B2B0E0B0E0A3D643D3D00030E144C514E4E00000000646464642E2A2D2E474D47473A3A3A3A301E3030000000002D322D2D3232323230537472696E6735746873370532323C02000031303232002F31353354545B5B2B6A2B6A222322233D5E3D5E00030E142E30303000000000646464642E2A2D2E4B4B4B4B3535353532323232323232323232323232323232195472756D7065745365633E0432323202200032003232003631332E5454545452516352222627264852644C030100061A130E173434643464640C641616161658585858414141413232323232323232323232323232323200576F6F6477696E6473203104323434022000320032320031313032545460543E7573730E4E0E0E64332D480002030312260F173E1F294600000000171F1E1F5A554E5432323232483264323232323232323232323232326D4D6964646C654561737459142D32320216042D1232320033302F35484848486A58546E27232B26646464640000000019090005362B3039000000002F2B272F56565656323232322B2B2B2B32323232251F1D2532323232574861726D6F6E696361202A0432323D011120240B0A33003237323154545454407245460E0E0F0F5F635B52000000002227212722252424644F42370B11050B3D46595C3232323232323232321E322B32323232322A32323B416C756D696E756D20204505325632020621161E32320031392D3248484877776C6C021A1A1A18412E3C6400010000050505053E3E3B230000000025282C1D6453555732323232322E2E6432323232322632323632323242506E6F46726D48656C6C2A0532643E02000628186432003138383448484848606058581B1B2B3B64646464000000000303232332322F2F000000002E2E392C5454323232323232323232323232323232322020323232320D4163636F7264696F6E203600323A39021420370033330C323E353D545454604F4F61610E0E0E0E5F615246000500002D303333302F2F3540474F490F0E0C0C3232325C3232363228283232321E322B32323232322C323247526564204F6E696F6E73220432393E0017203200323200392B2A364848483C070807050F0F0F0A645A645F0003010003060303646430306464640007070707323232643232323226260F323232323232323232263232323B43757474696E4B6579733E0532333E02230432083232003636323254545454574227781F0E27266450643300000000000E00002F3F1E31000000001A201B226448645E32323232323232323232323200322B3232323232614865652D48617720202054053632320214002F083232003232343C54545454612E61572627222364646464000000000000000031203131000000001A172B276464646432323232282828283232323232323232323232321B486F6C69646179496E6E630532323302181032006433003230322D4848483C3C2D6A5D4E222322404C602C00000000090650052F333E371800000026263424323C293C3232323200646464323232323232323232322A3266436869636B20536F6C6F3315322844021B00322D32320032323034545454540E490E0D1F0E1F0F6464646400000000050F0A0F1A000000006464640E100E106448635E32323232292A201F32323232323232323232322B4656656C7665744B6579733405323032020F0232003232002F30353454545454262D262D1E2B1E2B5D625D6200000000100506053B2C3B2C000000001E1C1E1C64646464323232323223322332323232323232323232323237446967696261737332206304323232023B00320032320032322C384848484838403F1F0E4B0F0F46644F3200000000070105033D192627000000000B020A0A3C5C4647323232322A262626323232323232323232323232024B696C6C546865466C796345503232020E00564E3332003A35482E465066626262175301010405646424644C4C004C00003D002C1C0022000000002C1C1F1F32323232323232323232323232323232143232323232323261506F6C74657267656973400532323B020000320033320031383834484A4D4E6D6D6D6D1F1F1F1F6442422E004C484E3D3D3D3D40403F47000000003E44474941414141323232323232323232333332323232323232323264496E646961746F776E2063043232320200003B1255320432323230486C4848587A307C0F0A0F0E4B28592F0000000007001C2F2D3F3338000000002C3128354C324945323232323252322D32323232323232323232323274426F74746C6573202020632D503832020E00280832320032333634546C546C35003500232223226464646400000000000000001F191919000000001F1F1F1F5151515132323232282828283232323232323232323232320D476C61737379202020203305323D3E021B00301B32320033312F3548484848626262620A1A2A4A5555494900000000333335332E2D2E2E464646462E2E392E52514F583232323232323232323333323232323232323232214563686F707C657820203704323232023B00320032320032343636484848480101010106060606644229160038474C0B0B0B0B2B2B2B2B0000000037373737323232323232323232323232323232323232323232323232734B6574746C654472756D6305323D32026400462532320033312F35403C3C3C6E516B652B2B2F2B214B4864000000000A0607003444383000000000353A342E6464615D32323232321F32323232323232323232323232321A536C61706261636B20206304324F3E022900301B32320033312F3540484849625162513B3B3B3B5F5F64640C0B00000A060000243A1E2E00000000243A272E555C595532323232323232323232323232323232323232320AF7" + # bank_3 = "F040 0021000301404D494E49534552494553632929290C070F171F3C3C0000000000007F7F3B7F7F7F7F7F00271311111111114040404040404040130C180C1818181831333232323232326464646464646464614D49414D492E2E2E2020630303230C070F171F00000000000000007F7F7F7F7F7F7F7F002950111111111140400140404040401818180C181818182E36323232323232646449646464646410494E4E4552574F524C446319030518070F171F00003F00000000007F3E7F7F7F7F7F7F1400201121011111404040404040404018180C1818181818323232323232323264506463646464645F425245415448535452476305281009070F171F3C3C0000000000007F7F3B3B7F7F7F7F20060023210111114040404040404040180C18181818181830363A323232323264644D64646464645C504C4159465553494F4E592E150604070F171F43430000000000007F7F42427F7F7F7F00261010111111114040404040404040180C1818181818183133323232323232646464316464646460504F4C494353544F5259630B1E0303070F171F3C000000000000007F3B7F7F7F7F7F7F001327212101111140404040404040401818181818181818363A32323232323248644646646464644E5A4F5242412120202020591B3A1E0C070F171F00400000000000007F7F3F7F7F7F7F7F002510111111111140404040404040400C18240C1818181833322F32323232325A645064646464642E53434152594D4F564945632202020C070F171F00000000000000007F7F7F7F7F7F7F7F200001111111111140404040404040400C18180C18181818373737323232323264646464646464645D454C4543545242414E44631B15061B070F171F003F0000000000007F7F3E7F7F7F7F7F22171211111111116040404340414040180C180C181818182F343232323232323C5A64646464646458434E5452592657455354633333171A070F171F3C3C0000000000007F7F3B7F7F7F7F7F200010212101111140404040404040400C0C180C1818181830363232323232326464516464646464604A415A5A535452494E47632929060C070F171F3C3C0000000000007F7F3B7F7F7F7F7F002713111111111140404040404040400C0C180C181818183133323232323232646464646464646455455049432046494C4D2063290A3E1E070F171F40400000000000007F7F3F3F7F7F7F7F2004101311111111404040404040404000130C001818181832323232323232324F45583E64646464385049414E4F53504C4954590F0E060C070F0E1F3C3C0000000000007F7F3B7F7F7F7F7F082012111111111140404040404040400C18180C181818183133323232323232646464646464646406434F4D50274E42415353572E04171A070F171F3C000000000000007F7F3B7F7F7F7F7F280020212101111140404040404040401818180C1818181830363A323232323264506464646464646F4A4F45204245414D202063130604000000000044004400000000007F437F7F7F7F7F7F261200111111111140404040404040400C180C0C181818183034343232323232644D2364646464643F4C2E412E4E4947485453540A0B0606070F171F40400000000000007F7F3F3F7F7F7F7F002512131111111140404040404040400C182418181818182E362C3232323232644749486464646438424C4F57494E472020203F050C0C0C070F171F00000000000000007F7F7F7F7F7F7F7F002021111111111140404040404040401818180C18181818373737323232323264646464646464644C4F56455254555245202063250B2430110F171F00000000000000007F7F7F7F7F7F7F7F2700505050505050404001020304050618181818181818183232323232323232504764646464646471434C415353494353545263281300000000000000000000000000007F7F7F7F7F7F7F7F200711111111111140404040404040400C18180C18181818303241323232323245646464646464640952414741202020202020592C3A00000000000000000000000000007F7F7F7F7F7F7F7F200711111111111140404040404040401818180C18181818283C4132323232326464646464646464375348494D4D4552494E474020001E10070F171F00000000000000007F7F3B397F7F7F7F29001111210111114040404040404040181818181818181830363A323232323264646449646464644E53504147484554544920522130300C070F171F00000000000000007F7F7F7F7F7F7F7F07202111111111114040404040404040180C0C0C18181818373737323232323264646464646464640946414952592054414C45422B013B18070F171F00000000000000007F7F7F7F7F7F7F7F20191111111111114040404040404040181818181818181831343A32323232324E434564646464642D57414E47204348554E4B59033B2410070F171F00004040000000003F3F7F7F7F7F7F7F13122000111111114040404040404040180C0C0C181818183232303C32323232575164646464646437302047524156495459204A22273918070F171F00003F00000000007F3E7F7F7F7F7F7F1000201121011111404040404040404018181818181818183232323232323232643F64636464646430544845204E494E4A41204D0C072722070F171F40004025000000007F247F3F247F7F7F200202142211111140404040404040401818182418181818323432322F3232324E5E4B645E6464641F444F4746494748542020631818020C070F171F30300000000000007F7F2F7F7F7F7F7F20001011111111114040404040404040181A180C18181818322F32323032323264646464646464640C544F4D49544120202020480D051C071D1117044000004A50403C3C7F7F3F7F7F4F3F4F231004110101011140404040404040400C18180C181818183232323232323232642F29353E4D6431084841554E544544485345631102111C070F171F40004000000000007F3F7F7F7F7F7F7F201300011111111140404040404040400C18182418181818313537323232323232641E45646464640D50554E4B204A554E4B204F073E1E1D3B26021F39394F004F4F00004E4E7F387F7F387F2403141000201011404040404040404018000018000018183232473232323232646464643D5264640B474C415353594543484F453C3D3D0C070F171F00000000000000007F7F7F7F7F7F7F7F072021111111111140404040404040401818180C181818183737373232323232646464646464646472534C4150204452554D534F3F1F3F1D070F171F4E394E00000000007F4D7F387F7F7F7F2413041011111111404040404040404003180518181818183232473232323232646464644764646478F7" + bank_dump = list(binascii.unhexlify(bank_1)) + assert isPartOfBankDump(bank_dump) + patches = extractPatchesFromBank(bank_dump) + single_patches = splitSysexMessage(patches) + assert isSingleProgramDump(single_patches[0]) if __name__ == "__main__":