Okay, setting the Device name to “Morningstar MC8 Pro Port 1” and editing the script to send the payload for the MC8 Pro made it work!
Would also love to set up my controller with the Song Name in the Bank Name display (between B and F switches) and the Section Name in the Preset Name display (between C and G switches) if possible!
Here is an updated Script for the MC8 Pro for anyone that wants it. I will try and figure out how to utilize the extra display to show the Section Name independently as well.
/**
* MC8 PRO – Update bank name via temporary SysEx
*
* Sends the current song or section name to the MC8 Pro by temporarily
* overriding the bank name via SysEx.
*/
const MIDI_DEVICE = "Morningstar MC8 Pro Port 1"; // Replace with your MIDI output device name
const MIDI_CHANNEL = 1; // Example: MIDI channel 1 (change as needed)
/**
* Calculate checksum for the SysEx payload according to Morningstar's spec.
*/
function calculateChecksum(bytes) {
return bytes.reduce((acc, cur) => (acc ^ cur), 0xF0) & 0x7F;
}
/**
* Send a bank name update to the MC8 Pro.
* Normalizes the string to 32 characters (padded with spaces if shorter).
*/
function sendBankName(name) {
const normalized = (name ?? "No Song").slice(0, 32).padEnd(32);
const songBytes = makeAscii(normalized);
const payload = [
0x00, 0x21, 0x24, 0x08, 0x00, 0x70, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
...songBytes
];
payload.push(calculateChecksum(payload));
sendMidiSysex(MIDI_DEVICE, payload);
}
/**
* Send BPM to the MC8 Pro by encoding it into CC5 (MSB) + CC6 (LSB).
*
* This avoids using MIDI Clock (which can be unstable over Bluetooth)
* while still allowing a numeric tempo readout on the MC8.
*
* Range is clamped between 20–300 BPM.
*/
function sendMC8Bpm(bpm) {
const b = Math.max(20, Math.min(300, Math.round(Number(bpm))));
const msb = Math.floor(b / 128);
const lsb = b % 128;
sendOsc("/midi/send/cc", MIDI_DEVICE, MIDI_CHANNEL, 5, msb);
sendOsc("/midi/send/cc", MIDI_DEVICE, MIDI_CHANNEL, 6, lsb);
log(`MC8 BPM Update → ${b} (MSB=${msb}, LSB=${lsb})`);
}
/**
* State tracking for current and queued songs.
*/
let currentSong = osc("/setlist/activeSongName") ?? "No Song";
let queuedSong = osc("/setlist/queuedName") ?? null;
/**
* Decide which title should be shown on the MC8:
* - If a song is queued, prefer that name.
* - Otherwise, show the currently active song.
*/
function displayPreferredTitle() {
const title = queuedSong ?? currentSong;
sendBankName(title);
}
/**
* Initial display when the script loads.
* (onOscChange handlers with `true` will also fire once, so this mostly
* ensures we send something immediately at startup.)
*/
displayPreferredTitle();
/**
* When Live loads a new active song.
* - Update internal state.
* - Refresh the title on the MC8.
* - Send the current tempo once for this song.
*/
onOscChange("/setlist/activeSongName", ([name]) => {
currentSong = name ?? "No Song";
log("Got active song:", name);
displayPreferredTitle();
const t = Number(osc("/global/tempo"));
if (!Number.isNaN(t)) sendMC8Bpm(t);
}, true);
/**
* When a song or section is queued.
*
* AbleSet sends [songName, sectionName] on /setlist/queuedName.
* - If a section is queued, briefly show its name on the MC8.
* - Then fall back to the normal title logic (queued > active).
*/
onOscChange("/setlist/queuedName", async ([song, section]) => {
queuedSong = song || null;
log("Got queued song/section:", song, section);
if (section) {
// Show the section name briefly when it is queued
sendBankName(section);
await sleep(1000);
}
// Restore the preferred title after the brief section display
displayPreferredTitle();
}, true);
/**
* When the active section changes while playing.
* - Show the section name for 1 second.
* - Then restore the preferred title (queued > active).
*/
onOscChange("/setlist/activeSectionName", async ([section]) => {
if (!section) return;
sendBankName(section);
await sleep(1000);
displayPreferredTitle();
}, true);
/* =======================================================================
Footswitch letter → Morningstar index (A–X)
======================================================================= */
/**
* Convert a footswitch letter (A–X) into its Morningstar index (0–23).
* Example: 'A' → 0, 'B' → 1, ..., 'X' → 23.
*/
function footswitchLetterToIndex(letter) {
if (!letter || typeof letter !== "string") return null;
const l = letter.trim().toUpperCase();
const code = l.charCodeAt(0);
// 'A' = 65, 'X' = 88
if (code < 65 || code > 88) {
log(`Invalid footswitch letter: "${letter}". Must be A–X.`);
return null;
}
return code - 65;
}
/**
* Loop state mapping
* Choose the loop footswitch with a letter (A–X).
* MC8 Pro MIDI Implementation:
* - CC number selects the action (e.g. 2 = Set Toggle, 3 = Clear Toggle).
* - Velocity selects the footswitch index:
* 0 = A, 1 = B, 2 = C, ..., 23 = X.
*/
const loopFootswitch = "B"; // Example: "B" → index 1
const loopFootswitchIndex = footswitchLetterToIndex(loopFootswitch);
/* =======================================================================
Loop state → MC8 Pro
- Whenever the loop bracket is enabled/disabled in AbleSet/Live,
send a CC to the MC8 Pro so it can update a Set/Clear Toggle state.
======================================================================= */
onOscChange("/setlist/loopEnabled", ([enabled]) => {
const isEnabled = Number(enabled) === 1;
if (isEnabled) {
// Loop ON → send "Set Toggle" (example: CC2) for the chosen footswitch
sendMidiCc(MIDI_DEVICE, MIDI_CHANNEL, 2, loopFootswitchIndex);
} else {
// Loop OFF → send "Clear Toggle" (example: CC3) for the chosen footswitch
sendMidiCc(MIDI_DEVICE, MIDI_CHANNEL, 3, loopFootswitchIndex);
}
log("Loop state changed →", isEnabled ? "ENABLED" : "DISABLED");
}, true);
/**
* Live tempo changes.
* - Whenever the global tempo changes (automation, manual, etc.),
* forward the updated BPM to the MC8.
*/
onOscChange("/global/tempo", ([t]) => {
const n = Number(t);
if (!Number.isNaN(n)) sendMC8Bpm(n);
}, true);
/* =======================================================================
Handle loading-project-name so mouse selection behaves like arrows/MC8
======================================================================= */
/**
* Remove trailing ".als" from a project name (if present).
*/
function cleanAlsName(str) {
if (!str) return null;
return str.endsWith(".als") ? str.slice(0, -4) : str;
}
/**
* When clicking on any other song with the mouse:
* - AbleSet will expose a "Loading: ..." project name.
* - We strip "Loading:" on AbleSet's side and ".als" here so the MC8
* always shows a clean song title, even when selecting with the mouse.
*/
onOscChange("/global/loadingProjectName", ([loadingName]) => {
if (!loadingName) return;
const clean = cleanAlsName(loadingName);
log("Loading project:", clean);
// Show the title directly (no "Loading:" and no ".als")
sendBankName(clean);
}, true);