Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/renderer/config-blocks/ElementName.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@

function handleActionChange(data: ActionData) {
const matches = data.script.match(information.valueRegex);
scriptValue = matches[1];
if (matches && matches[1] !== undefined) {
scriptValue = matches[1];
}
}

$: {
Expand Down
132 changes: 93 additions & 39 deletions src/renderer/config-blocks/MidiNRPN.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
export let action: GridAction;

const dispatch = createEventDispatcher();
let event = action.parent as GridEvent;
let event: GridEvent;

const validators = [
{
Expand Down Expand Up @@ -93,49 +93,101 @@
},
];

let channel: string;
let msb: string;
let lsb: string;
let nrpnCC: string;
let value: string;
let hiRes: boolean;
let channel: string = "";
let msb: string = "";
let lsb: string = "";
let nrpnCC: string = "";
let value: string = "";
let hiRes: boolean = false;
let lastParsedScript: string = "";
let isInitialized: boolean = false;

// Keep the parent event reference in sync with the current action
$: event = action.parent as GridEvent;

$: if (action.short === "gmnp") {
if (!$action.invalid && $action.script !== lastParsedScript) {
handleActionChange($action);
}
} else {
// Reset state when this component instance is used for a non-MidiNRPN action
if (lastParsedScript !== "") {
channel = "";
msb = "";
lsb = "";
nrpnCC = "";
value = "";
hiRes = false;
lastParsedScript = "";
isInitialized = false;
}
}

$: if (!$action.invalid) {
handleActionChange($action);
function resetLocalState() {
channel = "";
msb = "";
lsb = "";
nrpnCC = "";
value = "";
hiRes = false;
}

function handleActionChange(data: ActionData) {
// Extract all contents
const matches = [];
const regex = /gms\((.*?[^)])\)(?=\s|$)/g;

let match;
while ((match = regex.exec(data.script)) !== null) {
matches.push(`gms(${match[1].trim()})`); // trim to remove any extra spaces
if (!data?.script) {
resetLocalState();
lastParsedScript = "";
isInitialized = false;
return;
}

let midiLSB = [];
let midiMSB = [];
try {
// Extract all gms(...) calls (allowing no spaces between them)
const matches: string[] = [];
const regex = /gms\((.*?)\)/g;

for (let i = 0; i < matches.length; ++i) {
let part = Script.toSegments({ short: "gms", script: matches[i] });
if (i % 2 === 0) {
midiMSB.push(part[3]);
} else {
midiLSB.push(part[3]);
let match;
while ((match = regex.exec(data.script)) !== null) {
matches.push(`gms(${match[1].trim()})`); // trim to remove any extra spaces
}
}

value = midiMSB[1].split("//")[0];
if (value.startsWith("(") && value.endsWith(")")) {
value = value.slice(1, -1);
}
const midiLSB: string[] = [];
const midiMSB: string[] = [];

channel = Script.toSegments({ short: "gms", script: matches[0] })[0];
msb = midiMSB[0];
lsb = midiLSB[0];
nrpnCC = calculateNRPNCC(midiMSB[0], midiLSB[0]);
hiRes = midiLSB.length > 1 ? true : false;
for (let i = 0; i < matches.length; ++i) {
const part = Script.toSegments({ short: "gms", script: matches[i] });
if (i % 2 === 0) {
midiMSB.push(part[3]);
} else {
midiLSB.push(part[3]);
}
}

// Original parsing logic: value comes from the second MSB entry
let parsedValue = midiMSB[1].split("//")[0];
if (parsedValue.startsWith("(") && parsedValue.endsWith(")")) {
parsedValue = parsedValue.slice(1, -1);
}

const channelSegments = Script.toSegments({
short: "gms",
script: matches[0],
});

channel = channelSegments[0];
msb = midiMSB[0];
lsb = midiLSB[0];
value = parsedValue;
nrpnCC = calculateNRPNCC(midiMSB[0], midiLSB[0]);
hiRes = midiLSB.length > 1 ? true : false;

lastParsedScript = data.script;
isInitialized = true;
} catch (err) {
// If parsing fails for any reason, fall back to a safe empty state
console.error("[MidiNRPN] Failed to parse script", err, data?.script);
resetLocalState();
isInitialized = false;
}
}

function sendData() {
Expand All @@ -154,9 +206,7 @@
});
}

$: handleHighResValueChange(hiRes);

function handleHighResValueChange(hiRes: boolean) {
function handleHighResChange() {
sendData();
dispatch("sync");
}
Expand Down Expand Up @@ -196,7 +246,7 @@
suggestions[3] = [...localDefinitions];
}

$: if ($event) {
$: if (action.short === "gmnp" && $event) {
renderSuggestions();
}

Expand Down Expand Up @@ -355,7 +405,11 @@
postProcessor={GridScript.shortify}
preProcessor={GridScript.humanize}
/>
<MeltCheckbox bind:target={hiRes} title="14bit Resolution" />
<MeltCheckbox
bind:target={hiRes}
title="14bit Resolution"
on:change={handleHighResChange}
/>
</div>

<div class="mt-2">
Expand Down
27 changes: 27 additions & 0 deletions src/renderer/config-blocks/headers/MidiFourteenBitFace.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,23 @@
}

function handleActionChange(data: ActionData) {
if (!data?.script) {
scriptSegments = ["", "", ""];
midiLSB = "";
midiMSB = "";
return;
}

const arr = data.script.split(" gms");

// Expect at least two gms segments for a valid 14-bit style script
if (arr.length < 2) {
scriptSegments = ["", "", ""];
midiLSB = "";
midiMSB = "";
return;
}

let lsb = whatsInParenthesis.exec(arr[0]);

if (lsb !== null) {
Expand All @@ -36,8 +51,20 @@
}
}

// If we failed to extract LSB parameters, bail out safely
if (!midiLSB) {
scriptSegments = ["", "", ""];
return;
}

let param_array = midiLSB.split(",").map((c) => c.trim());

// Require at least 4 parameters: channel, status, base, value
if (param_array.length < 4 || !param_array[3]) {
scriptSegments = ["", "", ""];
return;
}

let value = param_array[3].split("//").slice(0, -1).join("//");

let param_object = {
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/main/panels/configuration/ActionList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
in:fade|global={{ delay: 0 }}
>
<div class="flex flex-row gap-2">
{#key $latestComponentVersionKeys.get(action.short)}
{#key `${$latestComponentVersionKeys.get(action.short)}-${action.short}`}
<DynamicWrapper
{index}
{action}
Expand Down
26 changes: 16 additions & 10 deletions src/renderer/main/panels/configuration/Configuration.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
const setup = element.findEvent(EventTypeToNumber(EventType.SETUP));

if (setup.actionAt(0)?.short !== elementNameInformation.short) {
if (typeof value === "undefined" || value.length === 0) {
return;
}
const data = new ActionData(
elementNameInformation.short,
generateScript(value),
Expand All @@ -86,8 +89,11 @@
}

const action = setup.actionAt(0);
const regex = elementNameInformation.valueRegex;
const name = action.script.match(regex)[1];
const name = action.script.match(elementNameInformation.valueRegex)?.[1];

if (typeof name === "undefined") {
return;
}

if (name !== value) {
const data = new ActionData(
Expand All @@ -108,15 +114,15 @@
const setup = element.findEvent(EventTypeToNumber(EventType.SETUP));
const action = setup.actionAt(0);

if (action?.short === elementNameInformation.short) {
const regex = elementNameInformation.valueRegex;
const value = action.script.match(regex)[1];
if (value !== elementName) {
elementName = value;
element.name = value;
}
} else {
if (action?.short !== elementNameInformation.short) {
elementName = "";
return;
}

const value = action.script.match(elementNameInformation.valueRegex)?.[1];
if (typeof value !== "undefined" && value !== elementName) {
elementName = value;
element.name = value;
}
}

Expand Down
Loading