Programming MIDI Settings for MC-24
In this session, we will explore how to use Python programming to update and configure the settings of the Nakedboards MC-24 MIDI controller.

The main goal of this workshop is to learn how to write a simple Python script that communicates directly with the MC-24.

By the end of the workshop, you will clearly understand the basics of sending and receiving MIDI messages programmatically. You will also gain a practical skill: how to automate the process of reprogramming your MIDI controller with just one script.

Later, if you wish to expand your project, you can create a graphical interface on top of this code. That way, you will have a complete application for managing the settings of the Nakedboards MC-24.

This workshop is designed to be practical and beginner-friendly: we go step by step, carefully explaining every block of code, so you not only copy the script, but also understand how it works.

Prerequisites

- Python 3.8+
- Mido library installed
- A connected Nakedboards MC-24 MIDI-Controller
- The OS must expose its MIDI ports (you can check with a MIDI monitor or via python listing functions).

MIDI concepts used in this workshop

- Channel Pressure / Aftertouch — status 0xD0 + channel (used to 'unlock' access to the device memory).
- Control Change — status 0xB0 + channel, followed by (controller number, value). We send two blocks of 24 CCs:
• CC 26..49 — values are the MIDI channel numbers for each knob (1..16).
• CC 50..73 — values are the CC numbers (0..127) that each knob will emit.
- Song Select — status 0xF3, used by the device to confirm actions. We expect F3 01 as confirmation that the device has loaded the settings.

Program workflow

1. Open MIDI ports (output + input) for the MC-24 device.
2. Send Aftertouch (Channel Pressure) to unlock device memory: 0xD0, value=40.
3. Send 24 Control Change messages CC 26..49 with values = desired MIDI channels (the device interprets those bytes as channel assignments).
4. Send 24 Control Change messages CC 50..73 with values = desired CC numbers (which CC each knob will send later).
5. Wait up to a short timeout for the device confirmation: Song Select 0xF3 0x01.
6. If confirmation received — print success and exit; else — print timeout error and exit.
1. Imports
    import time
    import mido
    import sys
Explanation:

- import time — allows us to add delays between MIDI messages.
- import mido — a Python library for working with MIDI devices. This is what we use to send and receive MIDI messages.
- import sys – allows the program to exit gracefully using sys.exit() in case of errors (for example, when the device is not found).
2. Define settings
    midi_channels = [
        1, 2, 3, 4, 5, 6, 7, 8,
        9, 10, 11, 12, 13, 14, 15, 16,
        1, 2, 3, 4, 5, 6, 7, 8
    ]

    midi_ccs = [
        20, 21, 22, 23, 24, 25, 26, 27,
        28, 29, 30, 31, 32, 33, 34, 35,
        36, 37, 38, 39, 40, 41, 42, 43
    ]
Explanation:

- midi_channels — a list of 24 channel numbers for the knobs.
- midi_ccs — a list of 24 control change numbers corresponding to the same knobs.
3. Open MIDI ports and check device name
target_name = "MC-24"

outport_name = next((name for name in mido.get_output_names() if target_name in name), None)
inport_name = next((name for name in mido.get_input_names() if target_name in name), None)

if not outport_name or not inport_name:
    print(f"MIDI device '{target_name}' not found or not connected.")
    sys.exit(0)

outport = mido.open_output(outport_name)
inport = mido.open_input(inport_name)

print(f"Connected to {target_name}")
print("Sending settings to MIDI controller...")
Explanation:

- The program searches for an output and input port containing "MC-24" in its name.
- If not found, it prints a message and exits gracefully, avoiding Python errors.
4. Unlock memory access
unlock_msg = mido.Message('aftertouch', channel=0, value=40)
outport.send(unlock_msg)
time.sleep(0.05)
Explanation:

- The controller requires a special MIDI message to unlock memory.
- Aftertouch is used instead of note_on.
- channel=0 — the MIDI channel used to send this unlock message.
- value=40 — a specific value the controller expects.
- time.sleep(0.05) — small delay to ensure the message is received.
5. Send 24 MIDI Channels
for i, ch in enumerate(midi_channels, start=0):
        msg = mido.Message('control_change', channel=0, control=i+26, value=ch)
        outport.send(msg)
        time.sleep(0.05)
Explanation:

- Loops over all channel numbers.
- Each message is a Control Change message, sent to control=i+26.
- value=ch — the MIDI channel value for that knob.
- Delay ensures proper reception by the controller.
6. Send 24 MIDI CC numbers
for i, ch in enumerate(midi_channels, start=0):
        msg = mido.Message('control_change', channel=0, control=i+26, value=ch)
        outport.send(msg)
        time.sleep(0.05)
Explanation:

- Loops over all channel numbers.
- Each message is a Control Change message, sent to control=i+26.
- value=ch — the MIDI channel value for that knob.
- Delay ensures proper reception by the controller.
7. Wait for confirmation
print("Waiting for confirmation...")
start = time.time()
confirmed = False

while time.time() - start < 1:
    msg = inport.poll()
    if msg and msg.type == 'song_select' and msg.song == 1:
        confirmed = True
        break

if confirmed:
    print("All settings loaded successfully!")
else:
    print("Error: confirmation not received.")
Explanation:

- The controller sends F3 with value=1 to confirm successful upload.
- We check for this message for up to 1 seconds.
- If received, success message is printed; otherwise, timeout error.
8. Full script
import time
import mido
import sys

# === Define your settings ===
midi_channels = [
    1, 2, 3, 4, 5, 6, 7, 8,
    9, 10, 11, 12, 13, 14, 15, 16,
    1, 2, 3, 4, 5, 6, 7, 8
]

midi_ccs = [
    20, 21, 22, 23, 24, 25, 26, 27,
    28, 29, 30, 31, 32, 33, 34, 35,
    36, 37, 38, 39, 40, 41, 42, 43
]

# === Open MIDI ports with name filter ===
target_name = "MC-24"

outport_name = next((name for name in mido.get_output_names() if target_name in name), None)
inport_name = next((name for name in mido.get_input_names() if target_name in name), None)

if not outport_name or not inport_name:
    print(f"MIDI device '{target_name}' not found or not connected.")
    sys.exit(0)

outport = mido.open_output(outport_name)
inport = mido.open_input(inport_name)

print(f"Connected to {target_name}")
print("Sending settings to MIDI controller...")

# === Step 1. Unlock memory access (Aftertouch) ===
unlock_msg = mido.Message('aftertouch', channel=0, value=40)
outport.send(unlock_msg)
time.sleep(0.05)

# === Step 2. Send 24 MIDI channels ===
for i, ch in enumerate(midi_channels, start=0):
    msg = mido.Message('control_change', channel=0, control=i+26, value=ch)
    outport.send(msg)
    time.sleep(0.05)

# === Step 3. Send 24 MIDI CC numbers ===
for i, cc in enumerate(midi_ccs, start=0):
    msg = mido.Message('control_change', channel=0, control=i+50, value=cc)
    outport.send(msg)
    time.sleep(0.05)

# === Step 4. Wait for confirmation (F3 01) ===
print("Waiting for confirmation...")
start = time.time()
confirmed = False

while time.time() - start < 1:
    msg = inport.poll()
    if msg and msg.type == 'song_select' and msg.song == 1:
        confirmed = True
        break

if confirmed:
    print("All settings loaded successfully!")
else:
    print("Error: confirmation not received.")
Troubleshooting / Notes

- If you see JACK/ALSA messages while using MIDO, they are harmless backend messages.
- If the script cannot find the MC-24 port, run a MIDI monitor (or print mido.get_output_names()) to see actual port names and adjust the TARGET_SUBSTRINGS accordingly.