Spaces:
Running
Running
Upload 4 files
Browse files- app.js +9 -3
- index.html +1 -0
- worker.js +26 -98
app.js
CHANGED
|
@@ -123,6 +123,7 @@ const midiBpmModeGroup = document.querySelector("#midiBpmModeGroup");
|
|
| 123 |
const midiBpmModeButtons = midiBpmModeGroup ? midiBpmModeGroup.querySelectorAll(".toggle-btn") : [];
|
| 124 |
|
| 125 |
let midiSeedActive = false;
|
|
|
|
| 126 |
let midiBpmMode = "lock";
|
| 127 |
let midiTokens = [];
|
| 128 |
let midiStartBar = 0;
|
|
@@ -581,8 +582,6 @@ const pianoSamples = {
|
|
| 581 |
C8: "C8.mp3",
|
| 582 |
};
|
| 583 |
|
| 584 |
-
const abcNotePattern = /^(?:\^\^|__|\^|_|=)?[A-Ga-g][,']*$/;
|
| 585 |
-
|
| 586 |
// Status Handler
|
| 587 |
function setStatus(message, state = "idle") {
|
| 588 |
if (appStatusEl) appStatusEl.textContent = message;
|
|
@@ -781,7 +780,8 @@ async function start() {
|
|
| 781 |
midiSeedActive: midiSeedActive,
|
| 782 |
midiTokens: midiSeedActive ? midiTokens : [],
|
| 783 |
midiStartBar: midiStartBar,
|
| 784 |
-
midiBpmMode: midiBpmMode
|
|
|
|
| 785 |
});
|
| 786 |
});
|
| 787 |
|
|
@@ -1487,9 +1487,15 @@ toggleButtons.forEach(btn => {
|
|
| 1487 |
if (value === "midi") {
|
| 1488 |
midiSeedActive = true;
|
| 1489 |
midiSeedControls.style.display = "block";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1490 |
} else {
|
| 1491 |
midiSeedActive = false;
|
| 1492 |
midiSeedControls.style.display = "none";
|
|
|
|
| 1493 |
}
|
| 1494 |
});
|
| 1495 |
});
|
|
|
|
| 123 |
const midiBpmModeButtons = midiBpmModeGroup ? midiBpmModeGroup.querySelectorAll(".toggle-btn") : [];
|
| 124 |
|
| 125 |
let midiSeedActive = false;
|
| 126 |
+
let bachSeedActive = false;
|
| 127 |
let midiBpmMode = "lock";
|
| 128 |
let midiTokens = [];
|
| 129 |
let midiStartBar = 0;
|
|
|
|
| 582 |
C8: "C8.mp3",
|
| 583 |
};
|
| 584 |
|
|
|
|
|
|
|
| 585 |
// Status Handler
|
| 586 |
function setStatus(message, state = "idle") {
|
| 587 |
if (appStatusEl) appStatusEl.textContent = message;
|
|
|
|
| 780 |
midiSeedActive: midiSeedActive,
|
| 781 |
midiTokens: midiSeedActive ? midiTokens : [],
|
| 782 |
midiStartBar: midiStartBar,
|
| 783 |
+
midiBpmMode: midiBpmMode,
|
| 784 |
+
bachSeedActive: bachSeedActive
|
| 785 |
});
|
| 786 |
});
|
| 787 |
|
|
|
|
| 1487 |
if (value === "midi") {
|
| 1488 |
midiSeedActive = true;
|
| 1489 |
midiSeedControls.style.display = "block";
|
| 1490 |
+
bachSeedActive = false;
|
| 1491 |
+
} else if (value === "bach") {
|
| 1492 |
+
midiSeedActive = false;
|
| 1493 |
+
midiSeedControls.style.display = "none";
|
| 1494 |
+
bachSeedActive = true;
|
| 1495 |
} else {
|
| 1496 |
midiSeedActive = false;
|
| 1497 |
midiSeedControls.style.display = "none";
|
| 1498 |
+
bachSeedActive = false;
|
| 1499 |
}
|
| 1500 |
});
|
| 1501 |
});
|
index.html
CHANGED
|
@@ -121,6 +121,7 @@
|
|
| 121 |
<div class="toggle-group" id="seedSourceGroup">
|
| 122 |
<button type="button" class="toggle-btn active" data-value="default">Start from Blank</button>
|
| 123 |
<button type="button" class="toggle-btn" data-value="midi">Start from MIDI</button>
|
|
|
|
| 124 |
</div>
|
| 125 |
</div>
|
| 126 |
</div>
|
|
|
|
| 121 |
<div class="toggle-group" id="seedSourceGroup">
|
| 122 |
<button type="button" class="toggle-btn active" data-value="default">Start from Blank</button>
|
| 123 |
<button type="button" class="toggle-btn" data-value="midi">Start from MIDI</button>
|
| 124 |
+
<button type="button" class="toggle-btn" data-value="bach">Bach Seed Test</button>
|
| 125 |
</div>
|
| 126 |
</div>
|
| 127 |
</div>
|
worker.js
CHANGED
|
@@ -10,7 +10,6 @@ let session = null;
|
|
| 10 |
let stoi = null;
|
| 11 |
let itos = null;
|
| 12 |
let vocabSize = 0;
|
| 13 |
-
let activeTokenizer = "abc";
|
| 14 |
|
| 15 |
let h = null;
|
| 16 |
let c = null;
|
|
@@ -29,6 +28,7 @@ let midiBpmMode = "lock";
|
|
| 29 |
|
| 30 |
// Seeding settings
|
| 31 |
let midiSeedActive = false;
|
|
|
|
| 32 |
let midiTokens = [];
|
| 33 |
let midiStartBar = 0;
|
| 34 |
let isWarmingUp = false;
|
|
@@ -37,8 +37,7 @@ let isWarmingUp = false;
|
|
| 37 |
let tempQueue = [];
|
| 38 |
let parser = null;
|
| 39 |
|
| 40 |
-
|
| 41 |
-
const abcNotePattern = /^(?:\^\^|__|\^|_|=)?[A-Ga-g][,']*$/;
|
| 42 |
|
| 43 |
function tokenId(token) {
|
| 44 |
return stoi?.[token] ?? null;
|
|
@@ -119,21 +118,7 @@ function sampleFromLogits(logits) {
|
|
| 119 |
}
|
| 120 |
|
| 121 |
// Duration converters
|
| 122 |
-
|
| 123 |
-
let eighths = 1;
|
| 124 |
-
if (duration) {
|
| 125 |
-
if (duration === "/") eighths = 0.5;
|
| 126 |
-
else if (duration.startsWith("/")) eighths = 1 / Number(duration.slice(1));
|
| 127 |
-
else if (duration.includes("/")) {
|
| 128 |
-
const [a, b] = duration.split("/").map(Number);
|
| 129 |
-
eighths = a / b;
|
| 130 |
-
} else {
|
| 131 |
-
eighths = Number(duration);
|
| 132 |
-
}
|
| 133 |
-
}
|
| 134 |
-
const quarterSeconds = 60 / currentBpm;
|
| 135 |
-
return Math.max(0.08, eighths * quarterSeconds * 0.5);
|
| 136 |
-
}
|
| 137 |
|
| 138 |
function durationToSecondsFromEventSteps(steps, grid) {
|
| 139 |
const quarterSeconds = 60 / currentBpm;
|
|
@@ -145,17 +130,7 @@ function midiToTonePitch(midi) {
|
|
| 145 |
return `${names[((midi % 12) + 12) % 12]}${Math.floor(midi / 12) - 1}`;
|
| 146 |
}
|
| 147 |
|
| 148 |
-
|
| 149 |
-
const match = token.match(/^(\^\^|__|\^|_|=)?([A-Ga-g])([,']*)$/);
|
| 150 |
-
if (!match) return null;
|
| 151 |
-
const accidental = match[1] || "";
|
| 152 |
-
const step = match[2];
|
| 153 |
-
const marks = match[3] || "";
|
| 154 |
-
let octave = step === step.toLowerCase() ? 5 : 4;
|
| 155 |
-
for (const mark of marks) octave += mark === "'" ? 1 : -1;
|
| 156 |
-
const accidentalText = accidental === "^^" ? "##" : accidental === "__" ? "bb" : accidental === "^" ? "#" : accidental === "_" ? "b" : "";
|
| 157 |
-
return `${step.toUpperCase()}${accidentalText}${octave}`;
|
| 158 |
-
}
|
| 159 |
|
| 160 |
function pushEvent(event) {
|
| 161 |
if (isWarmingUp) return; // Discard prompt history events
|
|
@@ -178,68 +153,7 @@ function getTokensUpToBar(tokens, targetBar) {
|
|
| 178 |
return sliced;
|
| 179 |
}
|
| 180 |
|
| 181 |
-
|
| 182 |
-
function makeAbcParser() {
|
| 183 |
-
return {
|
| 184 |
-
pending: null,
|
| 185 |
-
chord: null,
|
| 186 |
-
reset() {
|
| 187 |
-
this.pending = null;
|
| 188 |
-
this.chord = null;
|
| 189 |
-
},
|
| 190 |
-
feed(token) {
|
| 191 |
-
if (token === "Q:") {
|
| 192 |
-
this.pending = { type: "tempoHeader" };
|
| 193 |
-
return;
|
| 194 |
-
}
|
| 195 |
-
if (this.pending?.type === "tempoHeader") {
|
| 196 |
-
const bpm = Number(String(token).split("=").pop());
|
| 197 |
-
if (Number.isFinite(bpm) && bpm >= 40 && bpm <= 220) {
|
| 198 |
-
if (!midiSeedActive || isWarmingUp || midiBpmMode === "model") {
|
| 199 |
-
currentBpm = bpm;
|
| 200 |
-
self.postMessage({ action: "tempo", bpm: bpm });
|
| 201 |
-
}
|
| 202 |
-
}
|
| 203 |
-
this.pending = null;
|
| 204 |
-
return;
|
| 205 |
-
}
|
| 206 |
-
if (token === "[") {
|
| 207 |
-
this.chord = [];
|
| 208 |
-
this.pending = null;
|
| 209 |
-
return;
|
| 210 |
-
}
|
| 211 |
-
if (this.chord) {
|
| 212 |
-
if (token === "]") {
|
| 213 |
-
this.pending = { type: "chord", notes: this.chord.map(abcToTonePitch).filter(Boolean) };
|
| 214 |
-
this.chord = null;
|
| 215 |
-
return;
|
| 216 |
-
}
|
| 217 |
-
if (abcNotePattern.test(token)) this.chord.push(token);
|
| 218 |
-
return;
|
| 219 |
-
}
|
| 220 |
-
if (token === "z") {
|
| 221 |
-
this.pending = { type: "rest" };
|
| 222 |
-
return;
|
| 223 |
-
}
|
| 224 |
-
if (abcNotePattern.test(token)) {
|
| 225 |
-
this.pending = { type: "note", note: abcToTonePitch(token) };
|
| 226 |
-
return;
|
| 227 |
-
}
|
| 228 |
-
if (token.startsWith("DUR:") && this.pending) {
|
| 229 |
-
this.emitPending(token.slice(4));
|
| 230 |
-
return;
|
| 231 |
-
}
|
| 232 |
-
if (this.pending && (token === "|" || token === "<NL>" || token === "<EOP>")) this.emitPending(null);
|
| 233 |
-
},
|
| 234 |
-
emitPending(durationToken) {
|
| 235 |
-
const duration = durationToSecondsFromAbc(durationToken);
|
| 236 |
-
if (this.pending.type === "rest") pushEvent({ type: "rest", duration });
|
| 237 |
-
if (this.pending.type === "note" && this.pending.note) pushEvent({ type: "note", notes: [this.pending.note], duration, advance: duration });
|
| 238 |
-
if (this.pending.type === "chord" && this.pending.notes.length) pushEvent({ type: "note", notes: this.pending.notes.slice(0, 8), duration, advance: duration });
|
| 239 |
-
this.pending = null;
|
| 240 |
-
},
|
| 241 |
-
};
|
| 242 |
-
}
|
| 243 |
|
| 244 |
// Event Parser
|
| 245 |
function makeEventParser() {
|
|
@@ -346,18 +260,32 @@ function makeEventParser() {
|
|
| 346 |
}
|
| 347 |
|
| 348 |
function makeParser() {
|
| 349 |
-
return
|
| 350 |
}
|
| 351 |
|
| 352 |
async function warmPrompt() {
|
| 353 |
h = zeroState();
|
| 354 |
c = zeroState();
|
| 355 |
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
|
| 362 |
isWarmingUp = true;
|
| 363 |
|
|
@@ -406,7 +334,6 @@ self.onmessage = async function (e) {
|
|
| 406 |
stoi = data.vocab.stoi;
|
| 407 |
itos = Object.fromEntries(Object.entries(data.vocab.itos).map(([key, value]) => [Number(key), value]));
|
| 408 |
vocabSize = data.vocab.vocab_size;
|
| 409 |
-
activeTokenizer = data.vocab.tokenizer || "abc";
|
| 410 |
|
| 411 |
session = await ort.InferenceSession.create(data.modelBuffer, {
|
| 412 |
executionProviders: ["wasm"],
|
|
@@ -430,6 +357,7 @@ self.onmessage = async function (e) {
|
|
| 430 |
midiTokens = data.midiTokens;
|
| 431 |
midiStartBar = data.midiStartBar;
|
| 432 |
midiBpmMode = data.midiBpmMode || "lock";
|
|
|
|
| 433 |
|
| 434 |
parser = makeParser();
|
| 435 |
parser.reset();
|
|
|
|
| 10 |
let stoi = null;
|
| 11 |
let itos = null;
|
| 12 |
let vocabSize = 0;
|
|
|
|
| 13 |
|
| 14 |
let h = null;
|
| 15 |
let c = null;
|
|
|
|
| 28 |
|
| 29 |
// Seeding settings
|
| 30 |
let midiSeedActive = false;
|
| 31 |
+
let bachSeedActive = false;
|
| 32 |
let midiTokens = [];
|
| 33 |
let midiStartBar = 0;
|
| 34 |
let isWarmingUp = false;
|
|
|
|
| 37 |
let tempQueue = [];
|
| 38 |
let parser = null;
|
| 39 |
|
| 40 |
+
|
|
|
|
| 41 |
|
| 42 |
function tokenId(token) {
|
| 43 |
return stoi?.[token] ?? null;
|
|
|
|
| 118 |
}
|
| 119 |
|
| 120 |
// Duration converters
|
| 121 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
function durationToSecondsFromEventSteps(steps, grid) {
|
| 124 |
const quarterSeconds = 60 / currentBpm;
|
|
|
|
| 130 |
return `${names[((midi % 12) + 12) % 12]}${Math.floor(midi / 12) - 1}`;
|
| 131 |
}
|
| 132 |
|
| 133 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
|
| 135 |
function pushEvent(event) {
|
| 136 |
if (isWarmingUp) return; // Discard prompt history events
|
|
|
|
| 153 |
return sliced;
|
| 154 |
}
|
| 155 |
|
| 156 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
// Event Parser
|
| 159 |
function makeEventParser() {
|
|
|
|
| 260 |
}
|
| 261 |
|
| 262 |
function makeParser() {
|
| 263 |
+
return makeEventParser();
|
| 264 |
}
|
| 265 |
|
| 266 |
async function warmPrompt() {
|
| 267 |
h = zeroState();
|
| 268 |
c = zeroState();
|
| 269 |
|
| 270 |
+
let allTokensStr;
|
| 271 |
+
if (midiSeedActive && midiTokens.length > 0) {
|
| 272 |
+
allTokensStr = getTokensUpToBar(midiTokens, midiStartBar);
|
| 273 |
+
} else if (bachSeedActive) {
|
| 274 |
+
allTokensStr = [
|
| 275 |
+
"BOS", "BPM_100", "GRID_64",
|
| 276 |
+
"BAR",
|
| 277 |
+
"POS_0", "NOTE_81", "DUR_4", "VEL_6",
|
| 278 |
+
"POS_4", "NOTE_79", "DUR_4", "VEL_6",
|
| 279 |
+
"POS_8", "NOTE_77", "DUR_4", "VEL_6",
|
| 280 |
+
"POS_12", "NOTE_76", "DUR_4", "VEL_6",
|
| 281 |
+
"POS_16", "NOTE_74", "DUR_4", "VEL_6",
|
| 282 |
+
"POS_20", "NOTE_73", "DUR_4", "VEL_6",
|
| 283 |
+
"POS_24", "NOTE_74", "DUR_16", "VEL_6",
|
| 284 |
+
"BAR"
|
| 285 |
+
];
|
| 286 |
+
} else {
|
| 287 |
+
allTokensStr = ["BOS", "BPM_120", "GRID_64", "BAR", "POS_0"];
|
| 288 |
+
}
|
| 289 |
|
| 290 |
isWarmingUp = true;
|
| 291 |
|
|
|
|
| 334 |
stoi = data.vocab.stoi;
|
| 335 |
itos = Object.fromEntries(Object.entries(data.vocab.itos).map(([key, value]) => [Number(key), value]));
|
| 336 |
vocabSize = data.vocab.vocab_size;
|
|
|
|
| 337 |
|
| 338 |
session = await ort.InferenceSession.create(data.modelBuffer, {
|
| 339 |
executionProviders: ["wasm"],
|
|
|
|
| 357 |
midiTokens = data.midiTokens;
|
| 358 |
midiStartBar = data.midiStartBar;
|
| 359 |
midiBpmMode = data.midiBpmMode || "lock";
|
| 360 |
+
bachSeedActive = data.bachSeedActive || false;
|
| 361 |
|
| 362 |
parser = makeParser();
|
| 363 |
parser.reset();
|