Open IanG opened 5 months ago
Hi,
I don't have Akai MPK Mini Plus so I haven't tried anything similar. Looking at the numbers I would say that "Plus" version has a lot more data most likely it is step sequencer data. Just to avoid confusion I assume that you are talking about: https://www.akaipro.com/mpk-mini-plus.html
Looking at the product page I can see that it supports DAW transport control mapping. However after downloading original editor I can see that it only supports switching transport controls "on" or "on/off" MPK mini Plus Editor v1.0.7 (Mac - 10.2 MB) MPK mini Plus Editor v1.0.6 (PC - 50.6 MB)
When I save presets with transport "on" and another with "on/off" there is single byte change in preset.
Original MPK Mini uses simple SysEx command to send preset and it saves preset as simple bytes that you can directly send using any midi program that supports sending SysEx commands. Plus version has different format and probably different method of sending presets to controller.
It looks to me that transport control buttons have fixed values that you detected.
However there are 2 control scripts for available download: MPK Mini Plus Ableton Remote Script (Beta - 2.54 kB) MPK mini Plus FL Studio Beta Script (142.80 kB)
Looking at Ableton script:
[TransportControls]
# The settings below allow you to control various
# transport-related features.
# StopButton stops playback.
StopButton: 117
# PlayButton starts playback.
PlayButton: 118
# RecButton toggles Arrangement Record.
RecButton: 119
# SessionRecButton toggles Session Record.
SessionRecButton: -1
# OverButton toggles Arrangement Overdub.
OverButton: -1
# MetroButton toggles the Metronome.
MetroButton: -1
# LoopButton toggles the Arrangement Loop.
LoopButton: -1
# RwdButton rewinds playback.
RwdButton: 115
# FwdButton fast-forwards playback.
FfwdButton: 116
# PunchInButton controls Punch-In.
PunchInButton: -1
# PunchOutButton controls Punch-Out.
PunchOutButton: -1
# NudgeUpButton triggers Phase Nudge Up.
NudgeUpButton: -1
# NudgeDownButton triggers Phase Nudge Down.
NudgeDownButton: -1
# TapTempoButton triggers Tap Tempo.
TapTempoButton: -1
It is similar for FL Studio (uses Python for scripting DAW)...
ID_PREV = 115
ID_NEXT = 116
ID_STOP = 117
ID_PLAY = 118
ID_REC = 119
And Python code that handles transport ...
def OnControlChange(self, event):
# transport
if event.data1 == ID_PREV:
event.handled = True
self.ToggleLED(ID_PREV, event.data2 > 0)
if event.data2 > 0:
# TODO: move Toggle Knob Mode to different control
#self.CurrentKnobMode = max(0, self.CurrentKnobMode - 1)
#self.UpdateKnobBank(self.CurrentKnobMode)
print("Current knob mode: " + str(self.CurrentKnobMode))
# RWD one bar
curr = transport.getSongPos(midi.SONGLENGTH_ABSTICKS)
transport.setSongPos(curr - self.ticksPerBar, midi.SONGLENGTH_ABSTICKS)
elif event.data1 == ID_NEXT:
event.handled = True
self.ToggleLED(ID_NEXT, event.data2 > 0)
if event.data2 > 0:
# TODO: move Toggle Knob Mode to different control
#self.CurrentKnobMode = min(9, self.CurrentKnobMode + 1)
#self.UpdateKnobBank(self.CurrentKnobMode)
print("Current knob mode: " + str(self.CurrentKnobMode))
# FWD one bar
curr = transport.getSongPos(midi.SONGLENGTH_ABSTICKS)
transport.setSongPos(curr + self.ticksPerBar, midi.SONGLENGTH_ABSTICKS)
elif event.data1 == ID_STOP and event.data2 == 127:
event.handled = True
transport.stop()
ui.setHintMsg("Stop")
elif event.data1 == ID_PLAY and event.data2 == 127:
event.handled = True
transport.start()
ui.setHintMsg("Play/Pause")
elif event.data1 == ID_REC and event.data2 == 127:
event.handled = True
transport.record()
ui.setHintMsg("Record")
# knobs
elif (event.data1 >= ID_KNOB_BASE) & (event.data1 <= ID_KNOB_BASE + KNOB_COUNT):
self.OnEndlessKnob(event)
# mod wheel
elif event.data1 == ID_MOD:
event.handled = True
print("Mod Wheel: " + str(event.data2))
Looking further at FL Studio python scripts looks like it possible to modify preset and state of controller using SySex commands however I don't see that is possible to change transport controls assignment, I might be wrong on that assumption but I couldn't find anything to see that is possible.
class SysExHandler():
def requestIntroduction(self):
arr = [0] * 12
arr[0] = SYSEX_START
arr[1] = SYSEX_MANUFACTURER_ID
arr[2] = SYSEX_DEVICE_ID
arr[3] = SYSEX_MODEL_ID
arr[4] = 0x60 # Message Type
arr[5] = 0x00
arr[6] = 0x04
arr[7] = 0x00
arr[8] = FL_STUDIO_VER_MAJOR
arr[9] = FL_STUDIO_VER_MINOR
arr[10] = FL_STUDIO_VER_BUGFIX
arr[11] = SYSEX_END
device.midiOutSysex(bytes(arr))
print("Sending SysEx introduction...")
def updateKnobValue(self):
arr = [0] * 11
arr[0] = SYSEX_START
arr[1] = SYSEX_MANUFACTURER_ID
arr[2] = SYSEX_DEVICE_ID
arr[3] = SYSEX_MODEL_ID
arr[4] = 0x12 # Message Type
arr[5] = 0x04 # dataLength
arr[6] = 0x01 # Knob ID 1-8
arr[7] = 0x31 # Value? Or Name?
arr[10] = SYSEX_END
device.midiOutSysex(bytes(arr))
print("Sending SysEx knob value update...")
def updatePadColour(self):
numberOfPads = 1
byteLength = 8 + numberOfPads * 13
arr = [0] * byteLength
arr[0] = SYSEX_START # Sysex Start
arr[1] = SYSEX_MANUFACTURER_ID # Manufacturer ID
arr[2] = 0x7F # SysEx Device ID
arr[3] = 0x54 # Product Model ID
arr[4] = 0x70 # MessageType
# Calculate the length of the remaining message in bytes
# The sysex terminator is *not* included in this number
dataLength = 13 * numberOfPads
dlMSB = (dataLength &0xFF00) >> 8
dlLSB = dataLength & 0x00FF
arr[5] = dlMSB
arr[6] = dlLSB
for i in range(0, numberOfPads):
base = 7 + i * 13
arr[base] = 0x00 # padName
arr[base + 1] = 0x00 # Red Off MSB
arr[base + 2] = 0x00 # Red Off LSB
arr[base + 3] = 0x00 # Green Off MSB
arr[base + 4] = 0x00 # Green Off LSB
arr[base + 5] = 0x00 # Blue Off MSB
arr[base + 6] = 0x00 # Blue Off LSB
arr[base + 7] = 0x00 # Red On MSB
arr[base + 8] = 0x00 # Red On LSB
arr[base + 9] = 0x00 # Green On MSB
arr[base + 10] = 0x00 # Green On LSB
arr[base + 11] = 0x01 # Blue On MSB
arr[base + 12] = 0x7f # Blue On LSB
arr[byteLength - 1] = 0xF7 # Sysex terminator
print(bytes(arr))
for x in arr:
print(hex(x))
device.midiOutSysex(bytes(arr))
print("Sending SysEx colour change...")
def requestProgramData(self, programId):
'''Retrieve the data of a program/preset, numbered 1-8.'''
arr = [0] * 9
arr[0] = SYSEX_START
arr[1] = SYSEX_MANUFACTURER_ID
arr[2] = SYSEX_DEVICE_ID
arr[3] = SYSEX_MODEL_ID
arr[4] = 0x66
arr[5] = 0x00
arr[6] = 0x01
arr[7] = programId
arr[8] = SYSEX_END
device.midiOutSysex(bytes(arr))
print("Requested SysEx program data...")
def updateProgramData(self):
'''This updates the device's FL Studio program to use relative encodings for the endless knobs (K1-8).'''
print("Updating program data...")
device.midiOutSysex(bytes(program_FLStudio20Rel.programData))
def loadProgram(self, number):
print("Loading program " + str(number) + "...")
arr = [0] * 10
arr[0] = SYSEX_START
arr[1] = SYSEX_MANUFACTURER_ID
arr[2] = SYSEX_DEVICE_ID
arr[3] = SYSEX_MODEL_ID
arr[4] = 0x62 # message type
arr[5] = 0x00
arr[6] = 0x01
arr[7] = number
arr[8] = SYSEX_END
device.midiOutSysex(bytes(arr))
In conclusion looks like it is not possible to modify Akai MPK Mini Plus preset to send different byte sequence for transport control and you need to write specific script for each DAW you want to support since there is only FL Studio and Ableton supported by Akai.
In case of LUNA as you said:
Will make it very easy for people to use the transport in LUNA in the light that Universal Audio haven't built configuration for their transport controls (yet) and the Akai editor doesn't expose any functionality to change what the transport controls do.
Looks like you are out of luck to use preset modification for transport control in LUNA or to cover multiple DAWs with simple byte sequence change.
I'm not familiar with LUNA DAW but there might be some way to map controller and/or use "learn" option for transport controls and map them to control LUNA transport.
@gljubojevic thanks for you insight sir.
I had previously looked at the scripts for Ableton and FL Studio - yes they re-program the DAW to use the exact CC messages I caputred from the midi monitor that the transport controls emit. I know Logic has functionality to 'learn' transport controls so in those cases its software configuration changes to match the hardware and not the other way round.
LUNA doesn't currently have support for 'learning' commands from devices so I was just trying to see if I could side-step that problem from the hardware side. I did find an interesting file buried in the LUNA .app
bundle. It looks like the file:
/Applications/LUNA.app/Contents/Resources/panel/luna_surface/hardware/luna_surface_items.json
Is how they control MIDI input/output commands both out of the DAW controls and into UI elements. As its .json
its a little easier to follow whats happening. So you will see things like:
{
"name": "FRwd Switch",
"type": "switch_midi",
"address": "/recv_cmd/144/91",
"path": "/control/transport/frwd_switch"
},
{
"name": "FFwd Switch",
"type": "switch_midi",
"address": "/recv_cmd/144/92",
"path": "/control/transport/ffwd_switch"
},
{
"name": "Stop Switch",
"type": "switch_midi",
"address": "/recv_cmd/144/93",
"path": "/control/transport/stop_switch"
},
{
"name": "Play Switch",
"type": "switch_midi",
"address": "/recv_cmd/144/94",
"path": "/control/transport/play_switch"
},
{
"name": "Record Switch",
"type": "switch_midi",
"address": "/recv_cmd/144/95",
"path": "/control/transport/record_switch"
},
{
"name": "FRwd Led",
"type": "led_midi",
"address": "/send_cmd/144/91",
"path": "/control/transport/frwd_led"
},
{
"name": "FFwd Led",
"type": "led_midi",
"address": "/send_cmd/144/92",
"path": "/control/transport/ffwd_led"
},
{
"name": "Stop Led",
"type": "led_midi",
"address": "/send_cmd/144/93",
"path": "/control/transport/stop_led"
},
{
"name": "Play Led",
"type": "led_midi",
"address": "/send_cmd/144/94",
"path": "/control/transport/play_led"
},
{
"name": "Record Led",
"type": "led_midi",
"address": "/send_cmd/144/95",
"path": "/control/transport/record_led"
},
So recv_cmd
and send_cmd
maybe suggest respectivity when a CC command arrives, send a CC command when some internal event happens but its not obvious to me what the 2 int
values that follow those mean. Possible type
values are:
"type": "switch_midi",
"type": "led_midi",
"type": "fader_position_midi",
"type": "fader_setpoint_midi",
"type": "knob_position_midi",
"type": "jog_wheel_position_midi",
"type": "knob_halo_midi",
"type": "strip_lcd_midi",
"type": "timecode_display_midi",
"type": "strip_meter_midi",
Possible address
values are always in the format <action>/<value>/<value>
and actions can be:
recv_cmd
send_cmd
send_sysex
which maybe gives more indication this maybe controls keyboard/device interactions with their UI. Maybe if I can understand how the <value>/<value>
bit maps onto the midi CC commands I can change their map to align it with the Akai device.
I will make a backup of that file and experiment with changing these values - could just break the software but you never know I guess.
So the trick to this is discovering how CC values emitted from the Akai map onto values in the map in LUNA
Akai CC | LUNA |
---|---|
115 (RW) | /recv_cmd/144/91 |
116 (FF) | /recv_cmd/144/92 |
117 (STOP) | /recv_cmd/144/93 |
118 (PLAY) | /recv_cmd/144/94 |
119 (REC) | /recv_cmd/144/95 |
Will let you know if I figure anything out and thanks again for your input 👍🏻
Remember your first capture:
Transport Button,MPK Device Port,CC#,Value,HEX
<<, 1, 115, 127, B0 73 7F
>>, 1, 116, 127, B0 74 7F
STOP, 1, 117, 127, B0 75 7F
PLAY, 1, 118, 127 B0 76 7F
REC, 1, 119, 127, B0 77 7F
If I'm reading this correctly AKAI sent on channel 1
0xB0 - Chan 1 Control/Mode Change
0x73 - RW command
0x7F - Don't care value in ext command or end of command marker
See here: https://midi.org/expanded-midi-1-0-messages-list 0xB0 == 176 is designated as "Chan 1 Control/Mode Change" Looks like LUNA uses 0x90 == 144 is designated as "Chan 1 note on"
So I would try to map it to luna like this since 0xB0 == 176:
Akai CC | LUNA Default | LUNA AKAI Map |
---|---|---|
115 (RW) | /recv_cmd/144/91 | /recv_cmd/176/115 |
116 (FF) | /recv_cmd/144/92 | /recv_cmd/176/116 |
117 (STOP) | /recv_cmd/144/93 | /recv_cmd/176/117 |
118 (PLAY) | /recv_cmd/144/94 | /recv_cmd/176/118 |
119 (REC) | /recv_cmd/144/95 | /recv_cmd/176/119 |
Should be easy to try... you got nothing to lose.
You might even consider that LUNA is capable receiving more than 2 bytes for transport so mapping would look like this:
Akai CC | LUNA Default | LUNA AKAI Map |
---|---|---|
115 (RW) | /recv_cmd/144/91 | /recv_cmd/176/115/127 |
116 (FF) | /recv_cmd/144/92 | /recv_cmd/176/116/127 |
117 (STOP) | /recv_cmd/144/93 | /recv_cmd/176/117/127 |
118 (PLAY) | /recv_cmd/144/94 | /recv_cmd/176/118/127 |
119 (REC) | /recv_cmd/144/95 | /recv_cmd/176/119/127 |
@gljubojevic - thanks again for taking the time to respond. I know this largely has nothing to do with your own project but its good to bounce ideas. I'll try and switch out some of those values and see what happens. Your second point is interesting to see if /
is just a delimiter for each byte of the MIDI command.
I did a quick grep "\"address\": \"/recv_cmd" luna_surface_items.json | uniq
just to see if there are ever more than 2 additional values supplied after a recv_cmd
and there doesn't appear to be more than 2 int represented values. There are a few which only supply one value though.
Will let you know what I find and thanks again sir 👍🏻
Well - some good news...
I modified the line of the file from:
❯ diff luna_surface_items.json luna_surface_items.json.backup
450c450
< "address": "/recv_cmd/176/118",
---
> "address": "/recv_cmd/144/94",
And now if iI go into the Luna Controllers settings and tell it to use MPK mini Plus Port 1
the play button now controls the transport play function in LUNA 💪🏻. Its latching so a 2nd press stops the playback.
Only problem now is I have no more note control for midi note programming in instruments. I think I just need to configure either the Akai or Luna to listen to notes on its 2nd channel now.
Lets see.
Hey Goran,
did you try and do anything like this with the Akai MPK mini plus ?
I'm currently trying to figure out how I can change the MIDI CC messages its transport buttons fire so I can get them controlling the transport controls in Universal Audio's LUNA DAW.
If I use MorningStar Midi Monitor I was able to determine the CC commands fired are:
So I at least know they reside within the undefined range 102-119 in the MIDI spec. I cant locate any of those sequences of bytes in the
.mpkminiplus
file the editor creates 😩 They could be split - or it could be that they are hard-wired into the device firmware which would be a shame.By comparison this is the size difference between the files the 2 keyboards respective editors create:
(The mini plus has a sequencer built in so a lot of the extra space is configuration for that)
My ultimate goal is to write a little utility to modify the
.mpkminiplus
file so you can specify what DAW you are using and it will update the byte sequences in the file appropriately. Will make it very easy for people to use the transport in LUNA in the light that Universal Audio haven't built configuration for their transport controls (yet) and the Akai editor doesn't expose any functionality to change what the transport controls do. I hope they are not hard-wired in the firmware.