Skip to content

Commit

Permalink
Haphazard blind implementation of the Matrix 6 as adaption - it is ve…
Browse files Browse the repository at this point in the history
…ry 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.
  • Loading branch information
christofmuc committed May 13, 2020
1 parent 8df18d8 commit ed0dad2
Showing 1 changed file with 54 additions and 43 deletions.
97 changes: 54 additions & 43 deletions adaptions/Matrix 6.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)]

0 comments on commit ed0dad2

Please sign in to comment.