Skip to content

Instantly share code, notes, and snippets.

@mxmilkiib
Last active September 26, 2025 01:50
Show Gist options
  • Select an option

  • Save mxmilkiib/5e7d7cc3974a98580f9053f14b85c5d7 to your computer and use it in GitHub Desktop.

Select an option

Save mxmilkiib/5e7d7cc3974a98580f9053f14b85c5d7 to your computer and use it in GitHub Desktop.
Uncleaned example script demonstrating UTF-8 string controls in Mixxx, plus snippet from my Launchpad Pro MK3 controller script
// Example script demonstrating UTF-8 string controls in Mixxx
// This script shows how to use the new engine.setStringValue/getStringValue methods
// Example 1: Setting hotcue labels with UTF-8 text (emojis, international characters)
function demonstrateUTF8HotcueLabels() {
const group = "[Channel1]";
// Set various UTF-8 labels
engine.setStringValue(group, "hotcue_1_label_text", "🎵 Intro");
engine.setStringValue(group, "hotcue_2_label_text", "🔥 Drop");
engine.setStringValue(group, "hotcue_3_label_text", "🎤 Vocals");
engine.setStringValue(group, "hotcue_4_label_text", "🎸 Guitar Solo");
engine.setStringValue(group, "hotcue_5_label_text", "🥁 Drum Break");
engine.setStringValue(group, "hotcue_6_label_text", "🎺 Brass Section");
engine.setStringValue(group, "hotcue_7_label_text", "💯 Climax");
engine.setStringValue(group, "hotcue_8_label_text", "🏁 Outro");
// International text examples
engine.setStringValue(group, "hotcue_9_label_text", "Καταπληκτικό"); // Greek
engine.setStringValue(group, "hotcue_10_label_text", "すばらしい"); // Japanese
engine.setStringValue(group, "hotcue_11_label_text", "Fantástico"); // Spanish
engine.setStringValue(group, "hotcue_12_label_text", "Потрясающий"); // Russian
print("UTF-8 hotcue labels set successfully!");
}
// Example 2: Reading hotcue labels back
function readHotcueLabels() {
const group = "[Channel1]";
for (let i = 1; i <= 12; i++) {
const label = engine.getStringValue(group, "hotcue_" + i + "_label_text");
if (label) {
print("Hotcue " + i + " label: " + label);
}
}
}
// Example 3: Creating labeled hotcues programmatically
function createLabeledHotcue(group, hotcueNumber, position, label, color) {
// Set the hotcue at the position
engine.setValue(group, "hotcue_" + hotcueNumber + "_set", 1);
// Set the color
if (color) {
engine.setValue(group, "hotcue_" + hotcueNumber + "_color", color);
}
// Set the UTF-8 label
engine.setStringValue(group, "hotcue_" + hotcueNumber + "_label_text", label);
print("Created hotcue " + hotcueNumber + " with label: " + label);
}
// Example 4: Bulk operations
function setupMixLabels() {
const group = "[Channel1]";
// Standard DJ mix structure with emojis
const mixStructure = [
{hotcue: 1, label: "🎵 Intro Start", color: 0x00FF00},
{hotcue: 2, label: "🔊 Intro End", color: 0x00FF00},
{hotcue: 3, label: "🎤 Verse 1", color: 0x0000FF},
{hotcue: 4, label: "🎶 Chorus 1", color: 0xFF0000},
{hotcue: 5, label: "🎸 Break", color: 0xFFFF00},
{hotcue: 6, label: "🎶 Chorus 2", color: 0xFF0000},
{hotcue: 7, label: "🔥 Drop", color: 0xFF8000},
{hotcue: 8, label: "🏁 Outro", color: 0x800080}
];
mixStructure.forEach(function(item) {
engine.setStringValue(group, "hotcue_" + item.hotcue + "_label_text", item.label);
engine.setValue(group, "hotcue_" + item.hotcue + "_color", item.color);
});
print("Mix structure labels applied!");
}
// Example 5: Error handling
function safeSetLabel(group, hotcueNumber, label) {
try {
engine.setStringValue(group, "hotcue_" + hotcueNumber + "_label_text", label);
return true;
} catch (e) {
print("Error setting label for hotcue " + hotcueNumber + ": " + e);
return false;
}
}
function safeGetLabel(group, hotcueNumber) {
try {
return engine.getStringValue(group, "hotcue_" + hotcueNumber + "_label_text");
} catch (e) {
print("Error getting label for hotcue " + hotcueNumber + ": " + e);
return "";
}
}
// Run demonstrations
print("=== UTF-8 String Controls Demo ===");
demonstrateUTF8HotcueLabels();
readHotcueLabels();
setupMixLabels();
print("=== Demo Complete ===");
...
// MARK: p0 create4LeadupDropHotcues()
// Create a sequence of hotcues for DJ mixing: 6 leadup + drop + 1 outro
// Each hotcue is positioned at specific beat intervals for smooth transitions
var leadupCues = {
"1": { control: "beatjump_256_backward", colour: 0x006838, label: "-265" }, // -265, dark green
"2": { control: "beatjump_64_forward", colour: 0x006838, label: "-192" }, // -192, dark green
"3": { control: "beatjump_64_forward", colour: 0x006838, label: "-128" }, // -128, dark green
"4": { control: "beatjump_64_forward", colour: 0x006838, label: "-64" }, // -64, dark green
"5": { control: "beatjump_32_forward", colour: 0xff8000, label: "-32" }, // -32, orange
"6": { control: "beatjump_16_forward", colour: 0xff8000, label: "-16" }, // -16, orange
"7": { control: "beatjump_16_forward", colour: 0xc71136, label: "DROP" }, // drop, red
"8": { control: "beatjump_128_forward", colour: 0x5C3F97, label: "+128" }, // +128, purple
}
// Helper function to set hotcue label using new UTF-8 string controls
LaunchpadProMK3.setHotcueLabel = function(group, hotcueNumber, labelText) {
try {
const labelControl = "hotcue_" + hotcueNumber + "_label_text";
engine.setStringValue(group, labelControl, labelText);
print("LaunchpadProMK3: Set hotcue " + hotcueNumber + " label to: " + labelText);
} catch (e) {
print("LaunchpadProMK3: Error setting hotcue label: " + e);
}
};
// Helper function to get hotcue label using new UTF-8 string controls
LaunchpadProMK3.getHotcueLabel = function(group, hotcueNumber) {
try {
const labelControl = "hotcue_" + hotcueNumber + "_label_text";
return engine.getStringValue(group, labelControl);
} catch (e) {
print("LaunchpadProMK3: Error getting hotcue label: " + e);
return "";
}
};
function isCloseEnough(array, num, precision = 2) {
return array.some(n => Math.abs(n - num) < Math.pow(10, -precision));
}
LaunchpadProMK3.create4LeadupDropHotcues = function (deck, value) {
DEBUG(`create4LeadupDropHotcues: ## create hotcues ${C.Y} -192 -128 -64 -32 -16 ${C.R}drop ${C.RE}on ${C.O}${deck}`, C.G, 2);
if (value === 0 || value === undefined) return 0;
const group = `[Channel${deck}]`;
// Check if deck has a track loaded before continuing
const trackLoaded = engine.getValue(group, "track_loaded");
if (trackLoaded !== 1) {
DEBUG(`create4LeadupDropHotcues: deck ${deck} has no track loaded (track_loaded: ${trackLoaded}), aborting`, C.R);
return;
}
// what time is it right now?
const now = Date.now()
// original playPosition
const originalPlayPosition = engine.getValue(group, "playposition");
// is now at least a second after the last time?
if (now < (lastHotcueCreationTime + 1000)) {
DEBUG("create4LeadupDropHotcues: DENIED " + lastHotcueCreationTime + " " + now, C.R);
return
}
// record now as the new last time
lastHotcueCreationTime = now
// how long is the track in samples?
const samplesTotal = engine.getValue(group, "track_samples");
const hotcuePositions = [];
// get the first twenty hotcue positions, store in an array
for (let h = 0; h <= 35; h++) {
hotcuePositions[h] = engine.getValue(group, "hotcue_" + (+h + 1) + "_position")
//if (hotcuePositions[h]) hotcueRightmost = h;
}
DEBUG("create4LeadupDropHotcues: hotcuePositions creation " + C.O + hotcuePositions);
// for each of the controls in the object;
DEBUG("create4LeadupDropHotcues: leadupCues " + C.O + JSON.stringify(leadupCues));
// Non-blocking sequence runner to avoid busy-wait sleeps
const steps = Object.entries(leadupCues);
let idx = 0;
// Proactively cancel any in-flight sequence for this deck, then prep registries
try { LaunchpadProMK3.cancelHotcueSequenceTimer(deck); } catch (e) {}
LaunchpadProMK3._hotcueSequenceTimers = LaunchpadProMK3._hotcueSequenceTimers || {};
// Initialize per-deck cancellation token and store a local reference
LaunchpadProMK3._hotcueSequenceTokens = LaunchpadProMK3._hotcueSequenceTokens || {};
const token = {};
LaunchpadProMK3._hotcueSequenceTokens[deck] = token;
DEBUG("create4LeadupDropHotcues: token created for deck " + C.O + deck, C.G);
const processStep = function () {
// Abort early if sequence was canceled before this step
if (LaunchpadProMK3._hotcueSequenceTokens && LaunchpadProMK3._hotcueSequenceTokens[deck] !== token) {
DEBUG("create4LeadupDropHotcues: sequence canceled before step; abort", C.Y);
return;
}
if (idx >= steps.length) {
// Sequence finished: restore original play position
try { engine.setValue(group, "playposition", originalPlayPosition); } catch (e) {}
LaunchpadProMK3._hotcueSequenceTimers[deck] = null;
if (LaunchpadProMK3._hotcueSequenceTokens) { LaunchpadProMK3._hotcueSequenceTokens[deck] = null; }
return;
}
const number = steps[idx];
DEBUG(JSON.stringify(number));
DEBUG("number " + C.O + number[1].control);
const control = number[1].control;
const colour = number[1].colour;
DEBUG(`control ${C.O}${control}${C.RE} colour ${C.O}#${colour.toString(16)}`, C.G, 1);
// perform it - handle multiple controls separated by semicolon
const controls = control.split(';');
DEBUG(`create4LeadupDropHotcues: controls ${C.O}${controls}`, C.G, 1);
// Trigger control(s) immediately via script.triggerControl for proper on/off pulsing
for (const singleControl of controls) {
DEBUG(`create4LeadupDropHotcues: singleControl ${C.O}${singleControl}`, C.G, 1);
const trimmedControl = singleControl.trim();
if (trimmedControl) {
try { script.triggerControl(group, trimmedControl, 50); } catch (e) { try { engine.setValue(group, trimmedControl, 1); } catch (e2) {} }
DEBUG(`create4LeadupDropHotcues: ${C.O}${trimmedControl}${C.RE}`, C.G, 1);
}
}
// After a short delay, read position and set hotcue, then proceed
let localTimerId = null;
DEBUG("create4LeadupDropHotcues: scheduling timer; prev id " + C.O + (LaunchpadProMK3._hotcueSequenceTimers[deck] || "none") + C.RE + " deck " + C.O + deck, C.G);
localTimerId = engine.beginTimer(100, function () {
DEBUG("create4LeadupDropHotcues: timer fired id " + C.O + localTimerId + C.RE + " deck " + C.O + deck, C.G);
// One-shot timer: just clear stored id
LaunchpadProMK3._hotcueSequenceTimers[deck] = null;
// Abort if sequence was canceled mid-tick
if (LaunchpadProMK3._hotcueSequenceTokens && LaunchpadProMK3._hotcueSequenceTokens[deck] !== token) {
DEBUG("create4LeadupDropHotcues: timer fired after cancel; abort", C.Y);
try { engine.setValue(group, "playposition", originalPlayPosition); } catch (e) {}
return;
}
// Validate deck is still loaded
if (engine.getValue(group, "track_loaded") !== 1) {
DEBUG("create4LeadupDropHotcues: deck unloaded mid-sequence; aborting", C.Y);
return;
}
// how far through the track is the playhead now, between 0 and 1
const playPosition = engine.getValue(group, "playposition");
DEBUG("create4LeadupDropHotcues: playPosition " + C.O + playPosition);
if (playPosition >= 0 && playPosition < 1) {
// find the first unused hotcue
DEBUG("create4LeadupDropHotcues: hotcuePositions mid " + C.O + hotcuePositions);
// how many samples into the track right now?
const samplesNow = samplesTotal * playPosition;
DEBUG("create4LeadupDropHotcues: samplesNow " + C.O + samplesNow);
// has this sample position got a hotcue already?
if (!isCloseEnough(hotcuePositions, samplesNow, 3)) {
const hotcueSpace = hotcuePositions.findIndex((hotcueSpaceFree) => hotcueSpaceFree === -1);
DEBUG("create4LeadupDropHotcues: hotcueSpace " + C.O + hotcueSpace);
// if there is no hotcue space then give up
if (hotcueSpace !== -1) {
const hotcueSpaceTitle = "hotcue_" + (hotcueSpace + 1);
DEBUG("create4LeadupDropHotcues: hotcueSpaceTitle " + C.O + hotcueSpaceTitle);
// create new hotcue
engine.setValue(group, hotcueSpaceTitle + "_set", 1);
// give that hotcue its colour
engine.setValue(group, hotcueSpaceTitle + "_color", colour);
// set the hotcue label using new string controls
const hotcueNum = hotcueSpace + 1;
const leadupCueKey = (idx + 1).toString();
if (leadupCues[leadupCueKey] && leadupCues[leadupCueKey].label) {
LaunchpadProMK3.setHotcueLabel(group, hotcueNum, leadupCues[leadupCueKey].label);
}
// what is its pad?
DEBUG("create4LeadupDropHotcues: LaunchpadProMK3.decks[deck].deckMainSliceStartIndex " + C.O + LaunchpadProMK3.decks[deck].deckMainSliceStartIndex);
const pad = LaunchpadProMK3.decks[deck].deckMainSliceStartIndex + hotcueSpace;
DEBUG("create4LeadupDropHotcues: pad " + C.O + pad);
// add to undo list
LaunchpadProMK3.lastHotcue.unshift([group, hotcueSpaceTitle, pad, deck, colour]);
// add to existing check
hotcuePositions[hotcueSpace] = samplesNow;
DEBUG("create4LeadupDropHotcues: hotcuePositions end " + C.O + hotcuePositions, C.R, 0, 1);
} else {
DEBUG("create4LeadupDropHotcues: no hotcue space", C.R);
}
}
} else {
// out of bounds; skip
DEBUG("create4LeadupDropHotcues: out-of-bounds playPosition; skipping", C.O);
}
// Next step
idx += 1;
// Check cancellation before proceeding to next step
if (LaunchpadProMK3._hotcueSequenceTokens && LaunchpadProMK3._hotcueSequenceTokens[deck] !== token) {
DEBUG("create4LeadupDropHotcues: canceled before next step; stop", C.Y);
try { engine.setValue(group, "playposition", originalPlayPosition); } catch (e) {}
return;
}
processStep();
}, true);
LaunchpadProMK3._hotcueSequenceTimers[deck] = localTimerId;
DEBUG("create4LeadupDropHotcues: scheduled id " + C.O + localTimerId + C.RE + " deck " + C.O + deck, C.G);
};
processStep();
//for (let X = hotcueRightmost; X <= 19; X++) {
// LaunchpadProMK3.sleep(25);
DEBUG("create4LeadupDropHotcues: # end multi hotcue creation", C.R, 0, 2);
};
...

Prompts used

Claude Sonnet 4 (Thinking)

Didn't copy/paste the first to a scratchpad, n Windsurf has no prompt history feature, or I at least can't see the icon for it atm

Umm, I think these were in this order? I always, at the end, ask what prompt I should have asked to one-shot the implementation, then I revert to the original code n run again

Prompt #2

"I need to implement a comprehensive UTF-8 string control system for Mixxx that enables controllers to set/get unlimited-length strings natively. This should be a foundational system usable for any string-based control (hotcue labels, track metadata, effect descriptions, etc.).

Please implement:

  1. Core String Control Architecture: - Create ControlString class parallel to ControlObject but for UTF-8 strings - Thread-safe with QMutex protection for real-time audio thread compatibility - Persistent storage via UserSettings (like double controls) - Full Qt signal/slot integration with valueChanged signals - Support unlimited string length (no encoding limitations)

  2. JavaScript Controller Integration: - Add engine.setStringValue(group, name, string) and engine.getStringValue(group, name) methods - Integrate with existing controller scripting architecture - Add Q_INVOKABLE methods to ControllerScriptInterfaceLegacy

  3. Hotcue Label Implementation (First Use Case): - Create hotcue_X_label_text controls using ControlString for all 36 hotcues - Maintain existing hotcue_X_label (double-encoded) for backward compatibility - Bidirectional sync between both control types and underlying Cue objects - Integrate with HotcueControl class via value change request handlers

  4. Build System Integration: - Add new files to CMakeLists.txt - Ensure MOC processing for Q_OBJECT classes - Include necessary headers in controller scripting

  5. Controller Script Updates: - Modify LaunchpadProMK3 to use new string controls for hotcue labels - Maintain backward compatibility with existing encoded approach - Use both string and encoded methods for maximum compatibility

Requirements: - Full UTF-8 support (emojis, international characters) - Same unlimited length as UI hotcue popup dialog - Thread-safe for real-time audio processing - Zero breaking changes to existing controllers - Extensible foundation for future string controls - Production-ready code quality

Goal: Create the foundation for any future string-based control in Mixxx, starting with hotcue labels as the first implementation.

Prompt 3

"I want to implement full UTF-8 string support in Mixxx's control system, starting with hotcue labels. The current control system only supports doubles, which forces string encoding that creates malformed display issues. Please implement:

  1. Create a complete ControlString class parallel to ControlObject that natively handles UTF-8 strings of unlimited length, with thread safety, persistence, and Qt signal/slot integration

  2. Add JavaScript controller support by extending the controller scripting engine with engine.setStringValue() and engine.getStringValue() methods

  3. Implement dual hotcue label controls: hotcue_X_label (encoded doubles for backward compatibility) and hotcue_X_label_text (native UTF-8 strings), with bidirectional sync between both and the underlying Cue objects

  4. Update the build system to include the new string control files and ensure everything compiles

  5. Modify my LaunchpadProMK3 controller script to use the new string controls for setting hotcue labels, while maintaining backward compatibility

The goal is to allow controllers to set labels like "🎵 Epic Breakdown Section 🔥" or "minus thirty two" directly as strings, exa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment