From ed0dad279e1222c60c764f106432acefc6aeb29f Mon Sep 17 00:00:00 2001 From: cruc Date: Wed, 13 May 2020 18:28:29 +0200 Subject: [PATCH] Haphazard blind implementation of the Matrix 6 as adaption - it is very different from the Matrix 1000 in that it has no device ID messag, and no edit buffer. The patch dump seems to be the same, though. --- adaptions/Matrix 6.py | 97 ++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/adaptions/Matrix 6.py b/adaptions/Matrix 6.py index 67191c90..8c75ddcd 100644 --- a/adaptions/Matrix 6.py +++ b/adaptions/Matrix 6.py @@ -4,72 +4,83 @@ # Dual licensed: Distributed under Affero GPL license by default, an MIT license is available for purchase # -# Note that for real life usage the native C++ implementation of the Matrix1000 is more powerful and should be used -# This is an example adaption to show how a fully working adaption can look like def name(): - return "Matrix 1000 Adaption" + return "Matrix 6/6R" + def createDeviceDetectMessage(channel): - # This is a sysex generic device detect message - return [0xf0, 0x7e, channel, 0x06, 0x01, 0xf7] + # Trying to detect the Matrix 6/6R with a request to the master data + return [0xf0, 0x10, 0x06, 0x04, 3, 0, 0xf7] + def deviceDetectWaitMilliseconds(): - return 200 + return 200 + def needsChannelSpecificDetection(): - return True + return False + def channelIfValidDeviceResponse(message): - # The Matrix 1000 answers with 15 bytes - if (len(message) == 15 - and message[0] == 0xf0 # Sysex - and message[1] == 0x7e # Non-realtime - and message[3] == 0x06 # Device request - and message[4] == 0x02 # Device request reply - and message[5] == 0x10 # Oberheim - and message[6] == 0x06 # Matrix - and message[7] == 0x00 - and message[8] == 0x02 # Family member Matrix 1000 - and message[9] == 0x00 - ): - # Extrat the current MIDI channel from index 2 of the message - return message[2] - return -1 + # The answer is a master data dump + if (len(message) == 15 + and message[0] == 0xf0 # Sysex + and message[1] == 0x10 # Oberheim + and message[2] == 0x06 # Matrix 6/6R + and message[3] == 0x03 # Master parameter data + ): + # Extract the current MIDI channel from byte 11 of the master data + master_data = denibble(message, 4) + if len(master_data) != 236: + print("Error, expected 236 bytes of master data") + return master_data[11] + return -1 + def createEditBufferRequest(channel): - return [0xf0, 0x10, 0x06, 0x04, 4, 0, 0xf7] + # The Matrix 6/6R has no edit buffer request, in contrast to the later Matrix 1000. Leave this empty + return [0xf0, 0xf7] + def isEditBufferDump(message): - # The Matrix1000 sends the edit buffer in the same format as a single program - return isSingleProgramDump(message) + return False + def numberOfBanks(): - return 10 + return 1 + def numberOfPatchesPerBank(): - return 100 + return 100 + def createProgramDumpRequest(channel, patchNo): - bank = patchNo // numberOfPatchesPerBank() - program = patchNo % numberOfPatchesPerBank() - return [0xf0, 0x10, 0x06, 0x0a, bank, 0xf7] + [0xf0, 0x10, 0x06, 0x04, 1, program, 0xf7] + program = patchNo % numberOfPatchesPerBank() + return [0xf0, 0x10, 0x06, 0x04, 1, program, 0xf7] + def isSingleProgramDump(message): - return (len(message) > 3 - and message[0] == 0xf0 - and message[1] == 0x10 # Oberheim - and message[2] == 0x06 # Matrix - and message[3] == 0x01) # Single Patch Data + return (len(message) > 3 + and message[0] == 0xf0 + and message[1] == 0x10 # Oberheim + and message[2] == 0x06 # Matrix + and message[3] == 0x01) # Single Patch Data + def nameFromDump(message): - if isSingleProgramDump(message): - # To extract the name from the Matrix 1000 program dump, we need to correctly de-nibble and then force the first 8 bytes into ASCII - patchData = [message[x] | (message[x + 1] << 4) for x in range(5, len(message) - 2, 2)] - return ''.join([chr(x if x >= 32 else x + 0x40) for x in patchData[0:8]]) + if isSingleProgramDump(message): + # To extract the name from the Matrix 6 program dump, we need to correctly de-nibble and then force the first 8 bytes into ASCII + patchData = denibble(message, 5) + return ''.join([chr(x if x >= 32 else x + 0x40) for x in patchData[0:8]]) + def convertToEditBuffer(channel, message): - if isSingleProgramDump(message) or isEditBufferDump(message): - # Both are "single patch data", but must be converted to "single patch data to edit buffer" - return message[0:3] + [0x0d] + [0x00] + message[5:] - raise "Neither edit buffer nor program dump can't be converted" + if isSingleProgramDump(message): + # The Matrix 6 cannot send to the edit buffer, so we send into program 100 + return message[0:4] + 99 + message[5:] + raise Exception("This is not a program dump, can't be converted") + + +def denibble(message, start_index): + return [message[x] | (message[x + 1] << 4) for x in range(start_index, len(message) - 2, 2)]