@onjmin/dtm 0.1.46 → 0.1.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +57 -2
- package/dist/index.d.ts +57 -2
- package/dist/index.js +419 -55
- package/dist/index.mjs +416 -55
- package/dist/voice-worker.js +90 -25
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -90,12 +90,14 @@ __export(index_exports, {
|
|
|
90
90
|
init: () => init,
|
|
91
91
|
injectStyles: () => injectStyles,
|
|
92
92
|
isChordHeavyTrack: () => isChordHeavyTrack,
|
|
93
|
+
isValidHttpUrl: () => isValidHttpUrl,
|
|
93
94
|
koeUrl: () => koeUrl,
|
|
94
95
|
mountDAW: () => mountDAW,
|
|
95
96
|
mountMmlPlayer: () => mountMmlPlayer,
|
|
96
97
|
normalizeLyrics: () => normalizeLyrics,
|
|
97
98
|
onClick: () => onClick,
|
|
98
99
|
panToStereo: () => panToStereo,
|
|
100
|
+
parseCustomVocals: () => parseCustomVocals,
|
|
99
101
|
parseLyrics: () => parseLyrics,
|
|
100
102
|
parseMML: () => parseMML,
|
|
101
103
|
parseMmlMeta: () => parseMmlMeta,
|
|
@@ -104,6 +106,7 @@ __export(index_exports, {
|
|
|
104
106
|
setDrawOffset: () => setDrawOffset,
|
|
105
107
|
shiftNotes: () => shiftNotes,
|
|
106
108
|
showLoadingOverlay: () => showLoadingOverlay,
|
|
109
|
+
stripCustomVocals: () => stripCustomVocals,
|
|
107
110
|
stripLyrics: () => stripLyrics,
|
|
108
111
|
stripMmlMeta: () => stripMmlMeta,
|
|
109
112
|
vocalVolumeToGain: () => vocalVolumeToGain
|
|
@@ -1831,7 +1834,7 @@ var INSTRUMENT_PRESETS = {
|
|
|
1831
1834
|
}
|
|
1832
1835
|
};
|
|
1833
1836
|
|
|
1834
|
-
// node_modules/.pnpm/@onjmin+koe@1.0.
|
|
1837
|
+
// node_modules/.pnpm/@onjmin+koe@1.0.4/node_modules/@onjmin/koe/dist/index.js
|
|
1835
1838
|
var MAGIC = 1263486208;
|
|
1836
1839
|
function parseKoeHeader(headerBytes) {
|
|
1837
1840
|
const view = new DataView(headerBytes);
|
|
@@ -1841,6 +1844,8 @@ function parseKoeHeader(headerBytes) {
|
|
|
1841
1844
|
return { jsonLength: view.getUint32(4, true) };
|
|
1842
1845
|
}
|
|
1843
1846
|
var pcmBase = (jsonLength) => 8 + jsonLength;
|
|
1847
|
+
var MAX_PHONEME_SAMPLES = 5242880;
|
|
1848
|
+
var MAX_JSON_LENGTH = 50 * 1024 * 1024;
|
|
1844
1849
|
var BlobVoiceSource = class {
|
|
1845
1850
|
constructor(blob2, base) {
|
|
1846
1851
|
this.blob = blob2;
|
|
@@ -1862,22 +1867,60 @@ var RangeVoiceSource = class {
|
|
|
1862
1867
|
base;
|
|
1863
1868
|
async readBytes(offset, length) {
|
|
1864
1869
|
const start = this.base + offset;
|
|
1865
|
-
|
|
1866
|
-
headers: { Range: `bytes=${start}-${start + length - 1}` }
|
|
1867
|
-
});
|
|
1868
|
-
if (!res.ok && res.status !== 206) {
|
|
1869
|
-
throw new Error(`.koe range request failed: ${res.status}`);
|
|
1870
|
-
}
|
|
1871
|
-
return res.arrayBuffer();
|
|
1870
|
+
return rangeFetch(this.url, start, length);
|
|
1872
1871
|
}
|
|
1873
1872
|
};
|
|
1874
1873
|
async function rangeFetch(url2, start, length) {
|
|
1875
1874
|
const res = await fetch(url2, {
|
|
1876
|
-
headers: { Range: `bytes=${start}-${start + length - 1}` }
|
|
1875
|
+
headers: { Range: `bytes=${start}-${start + length - 1}` },
|
|
1876
|
+
credentials: "omit"
|
|
1877
|
+
// never leak cookies / auth to a MML-supplied URL
|
|
1877
1878
|
});
|
|
1878
|
-
if (
|
|
1879
|
-
throw new Error(
|
|
1880
|
-
|
|
1879
|
+
if (res.status !== 206) {
|
|
1880
|
+
throw new Error(
|
|
1881
|
+
`.koe fetch failed: expected 206 Partial Content, got ${res.status}`
|
|
1882
|
+
);
|
|
1883
|
+
}
|
|
1884
|
+
return readCapped(res, length);
|
|
1885
|
+
}
|
|
1886
|
+
async function readCapped(res, length) {
|
|
1887
|
+
const reader = res.body?.getReader();
|
|
1888
|
+
if (!reader) {
|
|
1889
|
+
const buf = await res.arrayBuffer();
|
|
1890
|
+
if (buf.byteLength > length) {
|
|
1891
|
+
throw new Error(
|
|
1892
|
+
`.koe fetch failed: response exceeds requested ${length} bytes`
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
return buf;
|
|
1896
|
+
}
|
|
1897
|
+
const out = new Uint8Array(length);
|
|
1898
|
+
let received = 0;
|
|
1899
|
+
for (; ; ) {
|
|
1900
|
+
const { done, value } = await reader.read();
|
|
1901
|
+
if (done) break;
|
|
1902
|
+
if (received + value.byteLength > length) {
|
|
1903
|
+
await reader.cancel();
|
|
1904
|
+
throw new Error(
|
|
1905
|
+
`.koe fetch failed: response exceeds requested ${length} bytes`
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
out.set(value, received);
|
|
1909
|
+
received += value.byteLength;
|
|
1910
|
+
}
|
|
1911
|
+
return received === length ? out.buffer : out.buffer.slice(0, received);
|
|
1912
|
+
}
|
|
1913
|
+
function validateJsonLength(jsonLength) {
|
|
1914
|
+
if (!Number.isInteger(jsonLength) || jsonLength < 0 || jsonLength > MAX_JSON_LENGTH) {
|
|
1915
|
+
throw new Error(`manifest JSON length out of bounds: ${jsonLength}`);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
function parseManifest(json) {
|
|
1919
|
+
const manifest = JSON.parse(new TextDecoder().decode(json));
|
|
1920
|
+
if (!manifest || typeof manifest !== "object" || typeof manifest.phonemes !== "object" || manifest.phonemes === null) {
|
|
1921
|
+
throw new Error("invalid manifest: missing phonemes table");
|
|
1922
|
+
}
|
|
1923
|
+
return manifest;
|
|
1881
1924
|
}
|
|
1882
1925
|
var VoiceBank = class _VoiceBank {
|
|
1883
1926
|
constructor(manifest, source) {
|
|
@@ -1891,20 +1934,41 @@ var VoiceBank = class _VoiceBank {
|
|
|
1891
1934
|
* @param koe a Blob/File of the .koe archive, or a URL (served with Range support)
|
|
1892
1935
|
*/
|
|
1893
1936
|
static async load(koe) {
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1937
|
+
try {
|
|
1938
|
+
if (typeof koe === "string") {
|
|
1939
|
+
if (/^blob:/i.test(koe)) {
|
|
1940
|
+
const res = await fetch(koe);
|
|
1941
|
+
if (!res.ok) {
|
|
1942
|
+
throw new Error(`blob: URL fetch failed: ${res.status}`);
|
|
1943
|
+
}
|
|
1944
|
+
return await _VoiceBank.fromBlob(await res.blob());
|
|
1945
|
+
}
|
|
1946
|
+
if (!/^https?:/i.test(koe)) {
|
|
1947
|
+
throw new Error(`unsupported URL protocol: ${koe}`);
|
|
1948
|
+
}
|
|
1949
|
+
const header = await rangeFetch(koe, 0, 8);
|
|
1950
|
+
const { jsonLength } = parseKoeHeader(header);
|
|
1951
|
+
validateJsonLength(jsonLength);
|
|
1952
|
+
const json = await rangeFetch(koe, 8, jsonLength);
|
|
1953
|
+
const manifest = parseManifest(json);
|
|
1954
|
+
return new _VoiceBank(
|
|
1955
|
+
manifest,
|
|
1956
|
+
new RangeVoiceSource(koe, pcmBase(jsonLength))
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
return await _VoiceBank.fromBlob(koe);
|
|
1960
|
+
} catch (error) {
|
|
1961
|
+
throw new Error(
|
|
1962
|
+
`Failed to load .koe voice bank: ${error instanceof Error ? error.message : String(error)}`
|
|
1902
1963
|
);
|
|
1903
1964
|
}
|
|
1965
|
+
}
|
|
1966
|
+
static async fromBlob(koe) {
|
|
1904
1967
|
const header = await koe.slice(0, 8).arrayBuffer();
|
|
1905
1968
|
const { jsonLength } = parseKoeHeader(header);
|
|
1969
|
+
validateJsonLength(jsonLength);
|
|
1906
1970
|
const json = await koe.slice(8, 8 + jsonLength).arrayBuffer();
|
|
1907
|
-
const manifest =
|
|
1971
|
+
const manifest = parseManifest(json);
|
|
1908
1972
|
return new _VoiceBank(
|
|
1909
1973
|
manifest,
|
|
1910
1974
|
new BlobVoiceSource(koe, pcmBase(jsonLength))
|
|
@@ -1912,7 +1976,7 @@ var VoiceBank = class _VoiceBank {
|
|
|
1912
1976
|
}
|
|
1913
1977
|
/** True if the bank contains a phoneme under this alias. */
|
|
1914
1978
|
has(phoneme) {
|
|
1915
|
-
return this.manifest.phonemes
|
|
1979
|
+
return Object.hasOwn(this.manifest.phonemes, phoneme);
|
|
1916
1980
|
}
|
|
1917
1981
|
/**
|
|
1918
1982
|
* Raw Int16 PCM bytes (48 kHz / mono) for a phoneme, or null if unknown.
|
|
@@ -1920,8 +1984,11 @@ var VoiceBank = class _VoiceBank {
|
|
|
1920
1984
|
* worker / AudioWorklet.
|
|
1921
1985
|
*/
|
|
1922
1986
|
async readPcmBytes(phoneme) {
|
|
1987
|
+
if (!Object.hasOwn(this.manifest.phonemes, phoneme)) return null;
|
|
1923
1988
|
const entry = this.manifest.phonemes[phoneme];
|
|
1924
|
-
if (!entry)
|
|
1989
|
+
if (!Number.isInteger(entry.offset) || !Number.isInteger(entry.length) || entry.offset < 0 || entry.length < 0 || entry.length > MAX_PHONEME_SAMPLES) {
|
|
1990
|
+
throw new Error(`manifest entry out of bounds for phoneme: ${phoneme}`);
|
|
1991
|
+
}
|
|
1925
1992
|
return this.source.readBytes(entry.offset, entry.length * 2);
|
|
1926
1993
|
}
|
|
1927
1994
|
/**
|
|
@@ -1931,7 +1998,7 @@ var VoiceBank = class _VoiceBank {
|
|
|
1931
1998
|
async getPcm(phoneme) {
|
|
1932
1999
|
const buf = await this.readPcmBytes(phoneme);
|
|
1933
2000
|
if (!buf) return null;
|
|
1934
|
-
const int16 = new Int16Array(buf);
|
|
2001
|
+
const int16 = new Int16Array(buf, 0, Math.floor(buf.byteLength / 2));
|
|
1935
2002
|
const f64 = new Float64Array(int16.length);
|
|
1936
2003
|
for (let i = 0; i < int16.length; i++) f64[i] = int16[i] / 32768;
|
|
1937
2004
|
return f64;
|
|
@@ -2122,7 +2189,8 @@ var Worldline = class _Worldline {
|
|
|
2122
2189
|
}
|
|
2123
2190
|
const outLen = WL._PhraseSynthSynth(ps, yPtrPtr, 0);
|
|
2124
2191
|
const yPtr = WL.getValue(yPtrPtr, "*");
|
|
2125
|
-
const audio = outLen > 0 ? new Float32Array(WL.HEAPF32.buffer, yPtr, outLen).slice() : null;
|
|
2192
|
+
const audio = outLen > 0 && yPtr ? new Float32Array(WL.HEAPF32.buffer, yPtr, outLen).slice() : null;
|
|
2193
|
+
if (yPtr) WL._free(yPtr);
|
|
2126
2194
|
WL._free(yPtrPtr);
|
|
2127
2195
|
WL._PhraseSynthDelete(ps);
|
|
2128
2196
|
return audio;
|
|
@@ -2313,7 +2381,7 @@ var parseLyrics = (mml) => {
|
|
|
2313
2381
|
let pan = 64;
|
|
2314
2382
|
let octave = 0;
|
|
2315
2383
|
const modelMatch = rest.match(
|
|
2316
|
-
/^([a-z_]
|
|
2384
|
+
/^([a-z_][a-z0-9_]*?)(?=(?:[vqpo]-?\d)|[^a-z0-9_]|$)(?::(\d+))?/i
|
|
2317
2385
|
);
|
|
2318
2386
|
let model = "";
|
|
2319
2387
|
const metaTokens = [];
|
|
@@ -2388,6 +2456,40 @@ var stripLyrics = (mml) => {
|
|
|
2388
2456
|
}
|
|
2389
2457
|
return kept.join("\n");
|
|
2390
2458
|
};
|
|
2459
|
+
var CUSTOM_VOCAL_LINE = /^@@([a-zA-Z_][a-zA-Z0-9_]*)\s+(\S+)\s+(\S+)\s*$/;
|
|
2460
|
+
var CUSTOM_VOCAL_URL_MAX_LEN = 2048;
|
|
2461
|
+
var isValidHttpUrl = (s) => {
|
|
2462
|
+
if (s.length > CUSTOM_VOCAL_URL_MAX_LEN) return false;
|
|
2463
|
+
try {
|
|
2464
|
+
const u = new URL(s);
|
|
2465
|
+
return u.protocol === "http:" || u.protocol === "https:";
|
|
2466
|
+
} catch {
|
|
2467
|
+
return false;
|
|
2468
|
+
}
|
|
2469
|
+
};
|
|
2470
|
+
var parseCustomVocals = (mml) => {
|
|
2471
|
+
const map = /* @__PURE__ */ new Map();
|
|
2472
|
+
for (const seg of splitSegments(mml)) {
|
|
2473
|
+
const m = seg.match(CUSTOM_VOCAL_LINE);
|
|
2474
|
+
if (!m) continue;
|
|
2475
|
+
const key = m[1].toLowerCase();
|
|
2476
|
+
const iconUrl = m[2];
|
|
2477
|
+
const koeUrl2 = m[3];
|
|
2478
|
+
if (!isValidHttpUrl(koeUrl2)) {
|
|
2479
|
+
console.warn(
|
|
2480
|
+
`[dtm] \u30AB\u30B9\u30BF\u30E0\u30DC\u30FC\u30AB\u30EB "${key}": koe URL \u304C\u4E0D\u6B63\u307E\u305F\u306F\u9577\u3059\u304E\u308B\u305F\u3081\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3059`,
|
|
2481
|
+
koeUrl2.slice(0, 80)
|
|
2482
|
+
);
|
|
2483
|
+
continue;
|
|
2484
|
+
}
|
|
2485
|
+
const resolvedIconUrl = isValidHttpUrl(iconUrl) ? iconUrl : "";
|
|
2486
|
+
map.set(key, { key, iconUrl: resolvedIconUrl, url: koeUrl2 });
|
|
2487
|
+
}
|
|
2488
|
+
return [...map.values()];
|
|
2489
|
+
};
|
|
2490
|
+
var stripCustomVocals = (mml) => mml.split(/[\n\r]+/).map(
|
|
2491
|
+
(line) => line.split(";").filter((seg) => !CUSTOM_VOCAL_LINE.test(seg.trim())).join(";")
|
|
2492
|
+
).join("\n");
|
|
2391
2493
|
var panToStereo = (pan) => Math.max(-1, Math.min(1, (pan - 64) / 64));
|
|
2392
2494
|
var createLyricsConductor = (lyrics) => {
|
|
2393
2495
|
const pointers = /* @__PURE__ */ new Map();
|
|
@@ -2514,7 +2616,10 @@ var KOE_VOICEBANKS = {
|
|
|
2514
2616
|
ruko_male: "\u6B32\u97F3\u30EB\u30B3\u2642\u9023\u7D9A\u97F3Ver.1.03.koe",
|
|
2515
2617
|
ruko_female: "\u6B32\u97F3\u30EB\u30B3\u2640\u6B4C\u9023\u7D9A\u97F3\u666E1.00.koe",
|
|
2516
2618
|
teto: "\u91CD\u97F3\u30C6\u30C8\u5358\u72EC\u97F3.koe",
|
|
2517
|
-
shiyo: "\u9769\u547D\u30B7\u30E8.koe"
|
|
2619
|
+
shiyo: "\u9769\u547D\u30B7\u30E8.koe",
|
|
2620
|
+
rei: "\u8DB3\u7ACB\u30EC\u30A4ver3.5.0.koe",
|
|
2621
|
+
MGRoid: "MGRoid_\u539F\u97F3\u8A2D\u5B9A\u6E08\u307F.koe",
|
|
2622
|
+
MOTRoid: "MOTRoid\u5B8C\u5168\u7248V2.koe"
|
|
2518
2623
|
};
|
|
2519
2624
|
var KOE_VOICEBANK_LABELS = {
|
|
2520
2625
|
tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093",
|
|
@@ -2523,7 +2628,10 @@ var KOE_VOICEBANK_LABELS = {
|
|
|
2523
2628
|
ruko_male: "\u6B32\u97F3\u30EB\u30B3\u2642",
|
|
2524
2629
|
ruko_female: "\u6B32\u97F3\u30EB\u30B3\u2640",
|
|
2525
2630
|
teto: "\u91CD\u97F3\u30C6\u30C8",
|
|
2526
|
-
shiyo: "\u9769\u547D\u30B7\u30E8"
|
|
2631
|
+
shiyo: "\u9769\u547D\u30B7\u30E8",
|
|
2632
|
+
rei: "\u8DB3\u7ACB\u30EC\u30A4",
|
|
2633
|
+
MGRoid: "MGRoid",
|
|
2634
|
+
MOTRoid: "MOTRoid"
|
|
2527
2635
|
};
|
|
2528
2636
|
var VOICE_IMAGE_KEY = {
|
|
2529
2637
|
klatt: "puyuyu",
|
|
@@ -2533,7 +2641,10 @@ var VOICE_IMAGE_KEY = {
|
|
|
2533
2641
|
ruko_male: "ruko",
|
|
2534
2642
|
ruko_female: "ruko",
|
|
2535
2643
|
teto: "teto",
|
|
2536
|
-
shiyo: "shiyo"
|
|
2644
|
+
shiyo: "shiyo",
|
|
2645
|
+
rei: "rei",
|
|
2646
|
+
MGRoid: "MGRoid",
|
|
2647
|
+
MOTRoid: "MOTRoid"
|
|
2537
2648
|
};
|
|
2538
2649
|
var KOE_VOICEBANK_TERMS = {
|
|
2539
2650
|
tsukuyomi: "https://tyc.rei-yumesaki.net/material/utau/terms/",
|
|
@@ -2542,7 +2653,10 @@ var KOE_VOICEBANK_TERMS = {
|
|
|
2542
2653
|
ruko_male: "https://long-sleeper.net/index.php?id=22",
|
|
2543
2654
|
ruko_female: "https://long-sleeper.net/index.php?id=22",
|
|
2544
2655
|
teto: "https://kasaneteto.jp/guidelines/voice.html",
|
|
2545
|
-
shiyo: "https://kakumeisiyo.my.canva.site/dagkuyjwycs"
|
|
2656
|
+
shiyo: "https://kakumeisiyo.my.canva.site/dagkuyjwycs",
|
|
2657
|
+
rei: "https://mechanicalgirl.jp/guidelines/",
|
|
2658
|
+
MGRoid: "https://x.com/nisusansu/status/1048825378188353536",
|
|
2659
|
+
MOTRoid: "https://www.nicovideo.jp/watch/sm40031282"
|
|
2546
2660
|
};
|
|
2547
2661
|
var koeUrl = (name, base = KOE_BASE_URL) => `${base}/${encodeURIComponent(name)}`;
|
|
2548
2662
|
var DEFAULT_WORLDLINE_SCRIPT = "https://onjmin.github.io/koe/demo/world/worldline.js";
|
|
@@ -2909,6 +3023,16 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2909
3023
|
for (const m of models) if (m) set.add(m.toLowerCase());
|
|
2910
3024
|
await Promise.all([...set].map((m) => load2(m)));
|
|
2911
3025
|
};
|
|
3026
|
+
const registerVoicebanks = (banks) => {
|
|
3027
|
+
for (const [k, v] of Object.entries(banks)) {
|
|
3028
|
+
const key = k.toLowerCase();
|
|
3029
|
+
if (key === FALLBACK_MODEL) continue;
|
|
3030
|
+
if (catalog[key] === v) continue;
|
|
3031
|
+
catalog[key] = v;
|
|
3032
|
+
loaded.delete(key);
|
|
3033
|
+
loading.delete(key);
|
|
3034
|
+
}
|
|
3035
|
+
};
|
|
2912
3036
|
const forEachSungNote = (track, fn) => {
|
|
2913
3037
|
let prevVowel = "";
|
|
2914
3038
|
for (const note of track.notes) {
|
|
@@ -3015,7 +3139,14 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
3015
3139
|
stopStream();
|
|
3016
3140
|
for (const v of loaded.values()) v.reset?.();
|
|
3017
3141
|
};
|
|
3018
|
-
return {
|
|
3142
|
+
return {
|
|
3143
|
+
loadModels,
|
|
3144
|
+
registerVoicebanks,
|
|
3145
|
+
warm,
|
|
3146
|
+
startStream,
|
|
3147
|
+
stopStream,
|
|
3148
|
+
reset
|
|
3149
|
+
};
|
|
3019
3150
|
};
|
|
3020
3151
|
var createVoiceRegistry = (models = {}, fallback = "klatt") => {
|
|
3021
3152
|
const sing = (model, syllable, e) => {
|
|
@@ -4469,7 +4600,8 @@ var parseMML = (mml, options = {}) => {
|
|
|
4469
4600
|
meta: {}
|
|
4470
4601
|
};
|
|
4471
4602
|
}
|
|
4472
|
-
const
|
|
4603
|
+
const noCustomVocals = stripCustomVocals(mml);
|
|
4604
|
+
const noComments = noCustomVocals.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
|
|
4473
4605
|
const meta = parseMmlMeta(noComments);
|
|
4474
4606
|
const noMeta = stripMmlMeta(noComments);
|
|
4475
4607
|
const lyrics = collectLyrics ? parseLyrics(noMeta) : void 0;
|
|
@@ -4482,6 +4614,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4482
4614
|
let octave = 4;
|
|
4483
4615
|
let currentStep = 0;
|
|
4484
4616
|
let baseLength = 16;
|
|
4617
|
+
let velocity = 100;
|
|
4485
4618
|
const contributors = /* @__PURE__ */ new Map();
|
|
4486
4619
|
const recordContributor = () => {
|
|
4487
4620
|
let set = contributors.get(trackIndex);
|
|
@@ -4502,6 +4635,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4502
4635
|
octave = 4;
|
|
4503
4636
|
currentStep = 0;
|
|
4504
4637
|
baseLength = 16;
|
|
4638
|
+
velocity = 100;
|
|
4505
4639
|
continue;
|
|
4506
4640
|
}
|
|
4507
4641
|
const body = part.replace(/\s+/g, "").toLowerCase();
|
|
@@ -4580,6 +4714,8 @@ var parseMML = (mml, options = {}) => {
|
|
|
4580
4714
|
if (bpm === null) {
|
|
4581
4715
|
bpm = clamp2(Number.parseInt(numStr, 10), 1, 255);
|
|
4582
4716
|
}
|
|
4717
|
+
} else if (ch === "v" && numStr) {
|
|
4718
|
+
velocity = clamp2(Number.parseInt(numStr, 10), 0, 127);
|
|
4583
4719
|
}
|
|
4584
4720
|
pushTok("ctrl", currentStep, 0, tokStart);
|
|
4585
4721
|
} else if (ch === "[") {
|
|
@@ -4625,7 +4761,8 @@ var parseMML = (mml, options = {}) => {
|
|
|
4625
4761
|
trackIndex,
|
|
4626
4762
|
startStep: currentStep,
|
|
4627
4763
|
pitch: p,
|
|
4628
|
-
durationSteps: Math.max(1, steps)
|
|
4764
|
+
durationSteps: Math.max(1, steps),
|
|
4765
|
+
velocity
|
|
4629
4766
|
});
|
|
4630
4767
|
}
|
|
4631
4768
|
pushTok("chord", currentStep, Math.max(1, steps), tokStart);
|
|
@@ -4648,7 +4785,8 @@ var parseMML = (mml, options = {}) => {
|
|
|
4648
4785
|
trackIndex,
|
|
4649
4786
|
startStep: currentStep,
|
|
4650
4787
|
pitch: midiPitch,
|
|
4651
|
-
durationSteps: Math.max(1, steps)
|
|
4788
|
+
durationSteps: Math.max(1, steps),
|
|
4789
|
+
velocity
|
|
4652
4790
|
});
|
|
4653
4791
|
pushTok("note", currentStep, Math.max(1, steps), tokStart);
|
|
4654
4792
|
currentStep += steps;
|
|
@@ -5808,6 +5946,7 @@ var DAW_CSS = `
|
|
|
5808
5946
|
height: 20px;
|
|
5809
5947
|
border-radius: 50%;
|
|
5810
5948
|
object-fit: cover;
|
|
5949
|
+
image-rendering: pixelated;
|
|
5811
5950
|
}
|
|
5812
5951
|
|
|
5813
5952
|
/* \u2500\u2500\u2500 \u5E83\u5E45\u62E1\u5F35 \u2500\u2500\u2500 */
|
|
@@ -6293,26 +6432,38 @@ var createSynth = (ctx, destination = ctx.destination) => {
|
|
|
6293
6432
|
return { playNote, playDrum };
|
|
6294
6433
|
};
|
|
6295
6434
|
|
|
6435
|
+
// assets/404Chip.png
|
|
6436
|
+
var Chip_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVR42u2WOw7AIAxD7Yr7X9kdqi5IIXQKBWckKHrOTyEAodAuFJsBDGAAA7TIIT0LkuTQ/1r0L4u3XgZ6ZbNKsvf9mlBSqu5fU9DXMFP3teujeOtPQbYPZnplFK88A4xuwmM2IX0VG8AABjge4AZHeT8uyZjZYAAAAABJRU5ErkJggg==";
|
|
6437
|
+
|
|
6296
6438
|
// assets/puyuyu.png
|
|
6297
|
-
var puyuyu_default = "data:image/png;base64,
|
|
6439
|
+
var puyuyu_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB60lEQVR42tWXPUxTURiGn9vWMCgtEQoDcWhAFgdtB0dMGjfcIAwMjcYoKIObq3F1EwKixNR0cCC46YQ4ODqIi4kRjUPj0Jaa1roYi9fh9Kuck3tvb7v08C73J+/5Tt7nnPvlHoeQend3yKULXXxQc8L4IvRZsU6JM9NJAKLZOQCazox6jiU0/2Gzrgq6r5RvetsFeP+2EkjEPgJ+yd3olDL81RObEp+My7Ct6oLrRcIeAmbyyKULngOikf2uJpA6GT54kug7gfZ6HO6cdwF+pYYBGBxT12vzrwF49mKpq8JXZzcAyG9dBqBRqgJw8lNRob+ybwkBc+2vrx1oBknQq4Sg6OnyCEf7g319YGU1rSM6taWujtou9S/ZwILxid1gf2sP2NsJZZdGZz57DkhMvlG9/uVZvT906bePwO9GDYCBwSFtDUWLN1UfePxkw/tzCumXeawh0O4D59InNAL54gIAN27d1wZsPrqnvu8zz/V+EdIvBD7u/bGUgEhI/BjLae9PlwqBBf385trbQ0Buvj2ccAFGR396kuhVZvJyOQ5A6s5Xx64+UPlebd2p/wAhYSYQ3S40tef1XCxwIkn+fx4s2wPmv2FyXCdhJjcT+703k1v3V+x0OhkJiV7ll9x+Ap1Px25giWNzOv4HHKK9IIEhxAYAAAAASUVORK5CYII=";
|
|
6440
|
+
|
|
6441
|
+
// assets/rei.png
|
|
6442
|
+
var rei_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACXElEQVR42s2XQWhTQRCGv1c8CFa9SSPEKkQPtfIOBQUlBgThEVIjEgRvEuuhCDkVL7Ug2N4ilqB4aCveFOnBanm+gxRUKlgJUmgraEA0YEpPRT3opeth30p2eXk0XrJzecxkZnbm39mZicM26dXAGUEblKkuOFHyvn09AmBtY90B6KLD5LSKUNH9ZB8Aw/W1/zpA2StSfuxDQGWeyWX1u5/3Nf7t8RORjk4uvYs9aOVqt8a/CQ5gBQI7WlZ9mLkZOaxGZ+jqesGpCgCe50nB2JFIO3sQ+FftRuaNbFkzSPgjGv9j+SgAe9zV2INMvbT3zRIEtkZT4buXEa3QrVUpQUWLuBX9GXwgv+O35d1Tkj94nwBYbvyUflxba0Bl7CZ2A3B6fhKA17krsQ72Tk3qSLQZQOc7oTnlMtWFVtMwRGZGGiYOxTreGk1F94XFkmU10DS3BYAQEhBx47CMcKIWadhKT2Vu8iOXixIBo7Pa8wo2c/lYRXX32yXR+CK/d8/GdkR79oHNXF4AbBzrBSCZL0Qa7OxNtodEiED/1C9t5tgzC0zB+ZknADwNeYXE0rWbkbWgOqUpf/HhIwC7wg5bfhhO0bGyZZ2wqXOJ5rsyqfpMIuPPPdLkF0rXAfhcuaPJU12+NmPS71/KjhgEWke0rwbUJqQif+weBKA+NwtANn9J0//9ta7xtbTcAVOLvmafNqfgRM2xCwEzY8LNZbBQUAoADJy7qNWE+XrKIQLckpsQQ0Oa3vNZieRwsSjsegUqIkX3pqf1qg33+/6e/dH/C9a/R+or3iSFRMcR+AtdbNaYOcb0dwAAAABJRU5ErkJggg==";
|
|
6298
6443
|
|
|
6299
6444
|
// assets/rino.png
|
|
6300
|
-
var rino_default = "data:image/png;base64,
|
|
6445
|
+
var rino_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABvklEQVR42mNkIACC8yv/MzAwMKSF+jOQAmat3ojCXzuxnRGbOiaGAQaM1PY5oRBBD4kBDwEWdIGdR078p6YFesryeOUHTwjAfI7u4kt3HzLgEyfWp4M/BHD5zE1BhoGBgYFh+vZdDAwMDAzKauoo4jAAk8cFkHLTf+TcMPhC4O6tm6gCaD6Fy6OJZ7o5Q0Ji194hmgbQfZ6VFA+h0TRMm7cQtSiVkcFrwc4jJ4ZYLoD5/P9/SIEobGCNNa5hPsel7t3FY1hz06xBHwLoccxj5IhV4/8nT/CqWz61D4V/bNu2wZkGGNHrAlhu2HvxBgMDAwPDmtJssgze9eAJStyHdE8d5O0BdxsLRuSWEHrcWXl5EWUgzKdwQKBFNfjbhEXailjlTf79Q+GfYYL4pe/qfQYGBgaGU9dQW8WPd18bnK1ieBqQddVCiXtOyU8MDAwMDBGrJzAwMDAw2Duj5vNMIX2UtDF9STtUH1TBNVSL0M0fvCUhIYDu81ioz3EBWMhJv0KtFZdd/j5IQwDm4oN792PVAEsTDDAaTR86iNO6xMDAwMCwiMECa0gMnTSAK0RwySNC5BJKSCTv/T64QgAAipubBW9f1GUAAAAASUVORK5CYII=";
|
|
6301
6446
|
|
|
6302
6447
|
// assets/roze.png
|
|
6303
|
-
var roze_default = "data:image/png;base64,
|
|
6448
|
+
var roze_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB0klEQVR42s2XPUsDQRCGnxVbC4OVFkELlRR+lBY2dkmlpSLYWVmIpA7BOkL+gQghjYVWiaRVsBFNgiZGQbGIlR+FP2AtzEh23TsTEW6nudu529l53/nYXUVHZqbmNEC9VVWEiPwXJEHzg+wPELEMime12yvRaQCllHjunPjYfjDG42MTxnxAuZDbDEbOwHc8Rss5DfCcTBvIQ5Aa3229MDp2skfHrjM3os8BeQlCLsgWZhadBkRfLB84megSbVWFZ1VQb1UN5GvJDePH8/ppX4Znp+cB+LBySJDLev7kgC2/Ia4dFb+Qrqz9aWF/cuC/DIVkv7Nq/OkD4pFk5Y+OZsVaxiqRCI1tr7kVPQNB2S6MCFLdaJhtTWuj13/LdsEYPmUzAMTSafzeC+wO1bVLumc2mwC04ymzA1LoywF/+4Ad4/dSCYDhVKqvBeLZ3dDq8JeB970v5Ne2vsOESK+MvOTXARixqsQ/BiTrY0mzbt9yuVDk0i/sE9XQYcZ5b5BziD+nYvFIYmWfFYNkdWnLqb9pnxl9RZi1T8f+3Qts5BL7/XwegIvJZQAu7917yKZ+BWCnUjGqpmsvUH5XwW/IbaS2nrtjZ+fUnadKJIy7Y+QMfAL8JMMLhDWQAQAAAABJRU5ErkJggg==";
|
|
6304
6449
|
|
|
6305
6450
|
// assets/ruko.png
|
|
6306
|
-
var ruko_default = "data:image/png;base64,
|
|
6451
|
+
var ruko_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACC0lEQVR42s2XsUsbURzHP08kinNvkWAXiy0F6dBJM0S7Bf+AUK2CkKCzS6GDZJBObkJBcNFS7NRFApaCEbRdHERQTJwKGYS4dJE2FM/h7id9P1/SKx3ufZfk3r17d9/P+/1+9ztDQgVBEPIParVaJsm8HlKWyedyTme1gwPzp/OnIyOJFjyt17sS0fdLn4D8kSfTDpI6T0pE1hPCqRPo7RQD/6tm38PYsZuExFb6BDqdkL16VSwC8DiTAeC83bbmbW1vW/NEbz99s0hA3c860CtPLk60ZHwv/n2jnK7Mzkbjm5vWeFbFgkiTSr8ObKytWVnwulKRyuW8IGw2AZiInQgZk8065w+/mAbgx8lnZ2z5kwWy1+I8DCMwS4NDzgv3VMyIU9HFl/cRGWPs87++e5YFeiCfy1nHxavLRAs9eDLeNQY6ZZe//YBod2YGgMzUVKIF2zs7ABz1RN7mPu5HdUHtvb8xIO9pIdO3sOCsA39znlT+vg1119RoNEKAr9UqAM9vbizHweQkAGf70Z6XSiUTZ0HoIutvTyhPKjpcXw8BgnwegEcDA86FLq6v7S64VgNgvFw2qs6EXvWE975etGOtwmLFOq6+W3Z/B8QEtDQRfwiI87uuNSZQnp+3Oh/R0slvAFZH7UTSnZG8NSV7NAn/6sBYoeCcKM5+PnvpJNF//KHrjfS6Qjx1ArcCiMI3kOeT7AAAAABJRU5ErkJggg==";
|
|
6307
6452
|
|
|
6308
6453
|
// assets/shiyo.png
|
|
6309
|
-
var shiyo_default = "data:image/png;base64,
|
|
6454
|
+
var shiyo_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJklEQVR42s2Xv09TURTHP5eAJJrI3ISBhIFJiaBp08GFAYKTA6FxejVBGgeTtzjxF7gwaWoNtCPEoSwSli4aa+uPSHFiaGLiQFwkDDWBBK7De6fpvd4+YOKe5eW8e+557/u959dV9BcdP1Xvy0ajoXv1dDoNQLPZBCCTyaik/bYMcMWiEpBjITcQiwhyBxOG3cbqJAC5sKW8YmDwPAOttYHMRn7n+nMATn7Iygtjn1LKZkL3MuFvDBzv3Qfg+98I0c9PywA8nBm51AeGb793xoK/MSB/eG18NUJeenwh5NXakdNOd75FVN+YNtbF3h8GJDoXn6wnbhBmRDZjhhYX3kX62we4/AizEhMSY/7WgU3r7LtnTGjogtyWk3Zo6MJILpz2PAtEcmHL+d7O425BGR1N/NDxXuhnNxw87+w+z84a+r2FNaej/+x2dpy9wK6Q/sbAbtztfm9tJZ71bq0GwOHU1KVixt+JSCriyM1nAKTGxoz1yYkJQ2/t7zsdd9ptAH51XiZml38xIJNKEHzQAEunp6aBxYDNSH17+0J1RbqkfzFgz/3FYhGApWzWsLt7dmboXwciLG/qdQAqlYpy+ZNp2V8GgiDQAIVCARcTIq+tuX85vjeI2Pu7N6lbQ55ngZx1Oq54+ZUVAMrlcqTn81H+V6sAvEqljP1iZ98jxB+Hcc/588gzBj6WShogOz/vZMRG+GVuDoCnBwdG9+ubPX3mhStn4B+i9swYdEa2xAAAAABJRU5ErkJggg==";
|
|
6310
6455
|
|
|
6311
6456
|
// assets/teto.png
|
|
6312
|
-
var teto_default = "data:image/png;base64,
|
|
6457
|
+
var teto_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB00lEQVR42tVXPUsDQRB9q8GvQiyjWKS0CApBCEENafwoYiMWIoJX+gMiKFoqFskPsFQQsbLxisQqRCQIEgiksEzhRyspREU9i705suNecqmyviozszfsezs7OxEIDqfZyMTSAIBcxebrRAc50YMuQ/gx5CDGhFR/HwCg+PGp+DWKtFTICAUcHUMOYhzp1e+5/v2jVYSjSSFhhgKZWNrRMTx5ew/EvJ0S1tCA1p+r2GYoEKIffMeEcHxSKlK6BwCsJaeV+AXzhyng+nl+4/pAiFc5BzHMHslbsr1rK0r5+f1uEVfC/BoghmJxS7bLp6pkvLwn49GEW9UyDp9+8v9qgDpWFnpG2atDvZ8pZs2uml0D3ltgJ1YAAOnypfa9dwrH8gv3zP+gVlaY8+95fnPeAs08IJptv8nHKRy4jPfRZlISPvnNm4gUJebXdwAA15sRAMDCaR0A8PL8CABYarwCAPLDIwCA0bFxZT3VQrsZ0Zw+wJl723eZkJ+YWl9yXqi6Nl8fTW3Iy1E8c1opYZQCCryzPIdy9qQATUwEincKc28BgdcEx9RDTbGrE1HFr+kH5teA0rk4Q46bwYZizwVkbnQNBPp3TIjPJBX77rbUUe6uK/ALBWuzghq8Lc4AAAAASUVORK5CYII=";
|
|
6313
6458
|
|
|
6314
6459
|
// assets/tsukuyomi.png
|
|
6315
|
-
var tsukuyomi_default = "data:image/png;base64,
|
|
6460
|
+
var tsukuyomi_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB3UlEQVR42tWXPUsDQRCGnwuijYIoFqKmEDSCxIgIgqnsogbsrC3ERiwN4g8Ikk6sFH+BnYofvcTKxoigBCzEYGGjWIiCOYvNBHe5TdQUt051czc3e++zO7tzHnUst7vn04BlFue9Ws8jhGxN9ZTPzU43NkIlj41E6AQ8m/JYfByAoWgXAPtHJzRCRN43SbizBkzlv1X2VzLuVoHYzf0TAMnys+aLCTGThMTJGnKfQOpyWF1cvgHQsh4FYPDzRd3vmQHgoPShJbi9uqhJyPk1UK3Jwsq1/115rLst8AX/4UFP0NsbGHf7+Kr579l7zR/ZGvbcrAJR7vvqEOwcTdZMYIuTPCYJUS7Ewydgzn2Bay2gdWzqR4nMuMPlhcC5l/HcWQOnCaU4lVU3xI9tnwFQnOgDoDmdDs5QKmlxYhdGmBA2ibhTBfKFHCkCm00dAKymJn+USAidHx8DkI+0q7PBUC7j5BJ7jlSB7RyXziVT8fM7Oz7AeLmsz3VEaWi7UvtAcmvJC+qmq4T/XT8gdTsw2692to07AF7jeU35wFq/7COqzhdVnVOnu3aHgNm1VpVXlImJX9zQffO5kDg1dlb3CNj6dZsVK2vAVP7Xf8bwCfxWuc23Eanmt/xlh07gC6oBviFE8rZHAAAAAElFTkSuQmCC";
|
|
6461
|
+
|
|
6462
|
+
// assets/MGRoid.png
|
|
6463
|
+
var MGRoid_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABy0lEQVR42tWXv0tCURTHPy9dwog0AssiVLChNmnQaHaJIoj+gQgkoj+jsSEiGmpodosWA9ck6g1BDg0qQYNLDpG1GK/h3RvvxfU+nbyd6f4473C/33ve95xr0b853kk2m2UQs21bDi3v+ghDtnCQQzEWcwDO2u2+kC83mwDcTEwEMWkZx4Cjc7xMJAA4DkCeGx11GRDrjUZDefdG5YA+uwUyaQetlstEPK5ELi0ajfZ1gKEzYEkG/iK/23l0B7MDRnwV1O51fYx6dABj/wI9coHsdz1g3j0NCyZsLRPmK+HL4jMAixdlAD4KB0pmxsquQtSOCgDMs6Bkwkgd0FoqlQIgs7oFQGhjRemX+ZwW/lMAfNfo9RdZxumAshZ0r8K+Ow6lT9zBUk4d6anqIq/v+5CH17tm1wIrqAOSTHiQuJvlMzdAoYiKuSDkRjPgiE4IgPtkUlv3q19fWj/ZSRndDyhNIsyJ+n+dzyvna7e3ALwJvZisVFD1F7ZtO/9DB+SJHw53lVneqytW1PteVdDwd4HU/u3zivKDzbkZbc2QViqVtMiNZMDy5oJE8Pcu3zsdAMYjEWVAzzvg/yrhQK/ker3u20yn0wPFHjoDP1pgkXMfnLDSAAAAAElFTkSuQmCC";
|
|
6464
|
+
|
|
6465
|
+
// assets/MOTRoid.png
|
|
6466
|
+
var MOTRoid_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAByElEQVR42s2WTyiEURTFfyMbS1ZSlIUxScpCNrNAkpLdhClFlJRJdpQ/5V+xE7PQFJHFyJKUJKLZaBZqkhjKQslCLG3UWIw7ec/7xidq3lnNve++23fOO2/u86ChPbyd+hp3tDbzH9g5OFLivVCXByCPHMPjlrkw+KsiuhI5VyBfT/z3mev9JJZ1ezywdfeccnNmbhVyuy/3Coj7nVwv2KwpAaAn8ZiVkezT652UsO8WCK5jJ0pcH9OYOrha9un1GVivwMzEOADJ6CoApQtx48bdQNnnr3cAugMNAMRWpoz1/usbALzBQQB8/kZLFZAv0/EwVpdVESfGMV+lsb94xz4FMu4f6lTybpkLdObf+lurwNTcvOLWh6XpXzX0n54Z89JP+ts/CwRtj1cAFHq9Sj4+uWisr5sdVeLXZBKA/ZIqy6fhT3O7YmNJPdOWFmNd8vBQiW97R7JOTXtfxYKjyAwAzQPp//jxp8v0tAuFjA3Pw2EA5ourjfvt84DTQkFNVQrgrb9Dyb/UNhlvhe76ootjtd9a+gXxlrjy2P0eEOZOyDDTGLpFRtlPJezzgNPZC6LBPgDKlyPG9fvhAQCC0XWzAms7dt2CDzsGp242tsuqAAAAAElFTkSuQmCC";
|
|
6316
6467
|
|
|
6317
6468
|
// src/voice-images.ts
|
|
6318
6469
|
var VOICE_IMAGES = {
|
|
@@ -6322,8 +6473,12 @@ var VOICE_IMAGES = {
|
|
|
6322
6473
|
ruko: ruko_default,
|
|
6323
6474
|
shiyo: shiyo_default,
|
|
6324
6475
|
teto: teto_default,
|
|
6325
|
-
tsukuyomi: tsukuyomi_default
|
|
6476
|
+
tsukuyomi: tsukuyomi_default,
|
|
6477
|
+
rei: rei_default,
|
|
6478
|
+
MGRoid: MGRoid_default,
|
|
6479
|
+
MOTRoid: MOTRoid_default
|
|
6326
6480
|
};
|
|
6481
|
+
var FALLBACK_VOCAL_ICON = Chip_default;
|
|
6327
6482
|
|
|
6328
6483
|
// src/mml-player.ts
|
|
6329
6484
|
var STEPS_PER_BEAT3 = 48;
|
|
@@ -6522,6 +6677,9 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6522
6677
|
collectLyrics: true
|
|
6523
6678
|
});
|
|
6524
6679
|
const lyricTracks = lyrics ?? /* @__PURE__ */ new Map();
|
|
6680
|
+
const customVocalByKey = new Map(
|
|
6681
|
+
parseCustomVocals(mml).map((d) => [d.key, d])
|
|
6682
|
+
);
|
|
6525
6683
|
const bpm = parsedBpm ?? options.defaultBpm ?? DEFAULT_BPM;
|
|
6526
6684
|
const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
|
|
6527
6685
|
const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
|
|
@@ -6570,7 +6728,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6570
6728
|
startStep: p.startStep,
|
|
6571
6729
|
durationSteps: p.durationSteps,
|
|
6572
6730
|
pitch: p.pitch,
|
|
6573
|
-
velocity:
|
|
6731
|
+
velocity: p.velocity
|
|
6574
6732
|
}));
|
|
6575
6733
|
return { id: String(index), volume: trackVolume, notes };
|
|
6576
6734
|
});
|
|
@@ -6923,15 +7081,23 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6923
7081
|
for (const [index, lt] of lyricTracks) {
|
|
6924
7082
|
const em = emojiByTrack.get(index);
|
|
6925
7083
|
if (!em) continue;
|
|
7084
|
+
const custom = customVocalByKey.get(lt.model.toLowerCase());
|
|
6926
7085
|
const imgKey = VOICE_IMAGE_KEY[lt.model.toLowerCase()];
|
|
6927
|
-
const src = imgKey ? VOICE_IMAGES[imgKey] : void 0;
|
|
7086
|
+
const src = custom ? custom.iconUrl || FALLBACK_VOCAL_ICON : imgKey ? VOICE_IMAGES[imgKey] : void 0;
|
|
6928
7087
|
if (!src) continue;
|
|
6929
7088
|
const img = doc.createElement("img");
|
|
6930
7089
|
img.src = src;
|
|
7090
|
+
if (custom) {
|
|
7091
|
+
img.onerror = () => {
|
|
7092
|
+
img.onerror = null;
|
|
7093
|
+
img.src = FALLBACK_VOCAL_ICON;
|
|
7094
|
+
};
|
|
7095
|
+
}
|
|
6931
7096
|
img.width = 20;
|
|
6932
7097
|
img.height = 20;
|
|
6933
7098
|
img.style.borderRadius = "50%";
|
|
6934
7099
|
img.style.objectFit = "cover";
|
|
7100
|
+
img.style.imageRendering = "pixelated";
|
|
6935
7101
|
img.draggable = false;
|
|
6936
7102
|
promotedToImage.add(em);
|
|
6937
7103
|
em.textContent = "";
|
|
@@ -7366,6 +7532,13 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
7366
7532
|
}
|
|
7367
7533
|
});
|
|
7368
7534
|
try {
|
|
7535
|
+
if (customVocalByKey.size > 0) {
|
|
7536
|
+
v.registerVoicebanks?.(
|
|
7537
|
+
Object.fromEntries(
|
|
7538
|
+
[...customVocalByKey].map(([k, d]) => [k, d.url])
|
|
7539
|
+
)
|
|
7540
|
+
);
|
|
7541
|
+
}
|
|
7369
7542
|
await v.loadModels(tracks.map((t) => t.model));
|
|
7370
7543
|
if (skipSinging) return;
|
|
7371
7544
|
const startTime = performance.now();
|
|
@@ -7521,6 +7694,29 @@ var MIDI_INFO_HTML = `
|
|
|
7521
7694
|
<p style="margin-top:4px;"><small>\u6B4C\u8A5E\u306E\u62BD\u51FA: <a href="https://rpgen3.github.io/ust2txt/" target="_blank" rel="noopener">ust2txt</a></small></p>
|
|
7522
7695
|
</div>
|
|
7523
7696
|
`;
|
|
7697
|
+
var KOE_INFO_HTML = `
|
|
7698
|
+
<div class="dtm-modal-body-content">
|
|
7699
|
+
<h4>1. UTAU\u97F3\u6E90\u3092 .koe \u306B\u5909\u63DB\u3059\u308B</h4>
|
|
7700
|
+
<p>UTAU\u7528\u306E\u97F3\u6E90\uFF08zip\uFF09\u3092\u305D\u306E\u307E\u307E\u4F7F\u3046\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093\u3002\u4E0B\u8A18\u306E\u5909\u63DB\u30B5\u30A4\u30C8\u3067 <code>.koe</code> \u5F62\u5F0F\u306B\u5909\u63DB\u3057\u3066\u304F\u3060\u3055\u3044\u3002</p>
|
|
7701
|
+
<p style="margin-top:4px;"><small>\u5909\u63DB\u30B5\u30A4\u30C8: <a href="https://onjmin.github.io/koe/demo/" target="_blank" rel="noopener">koe\u5909\u63DB\u30C7\u30E2\uFF08onjmin.github.io/koe/demo\uFF09</a></small></p>
|
|
7702
|
+
|
|
7703
|
+
<h4>2. \u5909\u63DB\u3057\u305F .koe \u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3059\u308B</h4>
|
|
7704
|
+
<p>\u5909\u63DB\u5F8C\u306E <code>.koe</code> \u30D5\u30A1\u30A4\u30EB\u306F\u3001\u8AB0\u3067\u3082\u76F4\u63A5\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3067\u304D\u308B\u5F62\u3067\u30CD\u30C3\u30C8\u4E0A\u306B\u7F6E\u304F\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u30ED\u30FC\u30AB\u30EB\u306E\u30D5\u30A1\u30A4\u30EB\u30D1\u30B9\u306F\u4F7F\u3048\u307E\u305B\u3093\uFF09\u3002</p>
|
|
7705
|
+
<ul>
|
|
7706
|
+
<li><strong>Google\u30C9\u30E9\u30A4\u30D6</strong>: \u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5F8C\u3001\u5171\u6709\u8A2D\u5B9A\u3092\u300C\u30EA\u30F3\u30AF\u3092\u77E5\u3063\u3066\u3044\u308B\u5168\u54E1\u300D\u306B\u5909\u66F4 \u2192 \u5171\u6709\u30EA\u30F3\u30AF\u306E<code>/d/</code>\u3068<code>/view</code>\u306E\u9593\u306EID\uFF08\u30D5\u30A1\u30A4\u30EBID\uFF09\u3092 controlled URL \u306B\u7D44\u307F\u8FBC\u3093\u3067\u76F4\u63A5\u30EA\u30F3\u30AF\u5316\u3057\u307E\u3059\uFF08\u4F8B: <code>https://drive.google.com/uc?export=download&id=\u30D5\u30A1\u30A4\u30EBID</code>\uFF09\u3002</li>
|
|
7707
|
+
<li>\u305D\u306E\u4ED6\u3001\u76F4\u63A5\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9URL\u3092\u767A\u884C\u3067\u304D\u308B\u30DB\u30B9\u30C6\u30A3\u30F3\u30B0\uFF08GitHub Pages\u3001Cloudflare R2 \u306A\u3069\uFF09\u3067\u3082\u69CB\u3044\u307E\u305B\u3093\u3002</li>
|
|
7708
|
+
</ul>
|
|
7709
|
+
|
|
7710
|
+
<h4>3. DTM\u3067\u4F7F\u3046</h4>
|
|
7711
|
+
<ul>
|
|
7712
|
+
<li>\u6B4C\u5531\u30E2\u30C7\u30EB\u306E\u30D7\u30EB\u30C0\u30A6\u30F3\u304B\u3089\u300C\u30AB\u30B9\u30BF\u30E0\u97F3\u6E90\u3092\u8FFD\u52A0\u2026\u300D\u3092\u9078\u3073\u307E\u3059\u3002</li>
|
|
7713
|
+
<li>\u300C\u97F3\u6E90URL\u300D\u306B\u624B\u98062\u306E\u76F4\u63A5\u30EA\u30F3\u30AF\u3092\u8CBC\u308A\u4ED8\u3051\u307E\u3059\u3002</li>
|
|
7714
|
+
<li>\u30A2\u30A4\u30B3\u30F3\u753B\u50CFUR\u30EB\u30FB\u8868\u793A\u540D\u30FB\u8B58\u5225\u5B50\u306F\u4EFB\u610F\u3067\u3059\uFF08\u7701\u7565\u53EF\uFF09\u3002</li>
|
|
7715
|
+
<li>\u300C\u8FFD\u52A0\u300D\u3092\u62BC\u3059\u3068\u30D7\u30EB\u30C0\u30A6\u30F3\u306B\u767B\u9332\u3055\u308C\u3001\u4EE5\u964D\u305D\u306E\u30C8\u30E9\u30C3\u30AF\u3067\u4F7F\u3048\u307E\u3059\u3002</li>
|
|
7716
|
+
</ul>
|
|
7717
|
+
<p style="margin-top:4px;"><small>\u203B\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3057\u305F\u97F3\u6E90\u306F\u81EA\u5DF1\u8CAC\u4EFB\u3067\u7BA1\u7406\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u6A29\u5229\u95A2\u4FC2\uFF08\u914D\u5E03\u5143\u306E\u5229\u7528\u898F\u7D04\uFF09\u306B\u3082\u5F93\u3063\u3066\u304F\u3060\u3055\u3044\u3002</small></p>
|
|
7718
|
+
</div>
|
|
7719
|
+
`;
|
|
7524
7720
|
var BASE_STEP_WIDTH = 0.5;
|
|
7525
7721
|
var BASE_KEY_HEIGHT = 15;
|
|
7526
7722
|
var TRACKS_SIMPLE = [
|
|
@@ -7661,12 +7857,14 @@ var TRACKS_ADVANCED = [
|
|
|
7661
7857
|
}
|
|
7662
7858
|
];
|
|
7663
7859
|
var DEFAULT_TRACKS = TRACKS_SIMPLE;
|
|
7664
|
-
var
|
|
7665
|
-
var
|
|
7860
|
+
var BASE_LYRIC_MODELS = ["klatt", ...Object.keys(KOE_VOICEBANKS)];
|
|
7861
|
+
var BASE_LYRIC_MODEL_LABELS = {
|
|
7666
7862
|
klatt: "\u8EFD\u91CF\u30ED\u30DC\u58F0",
|
|
7667
7863
|
...KOE_VOICEBANK_LABELS
|
|
7668
7864
|
};
|
|
7669
|
-
var lyricModelLabel = (model) =>
|
|
7865
|
+
var lyricModelLabel = (model, customMap) => BASE_LYRIC_MODEL_LABELS[model] ?? customMap.get(model)?.label ?? model;
|
|
7866
|
+
var CUSTOM_VOCAL_ADD_VALUE = "+custom";
|
|
7867
|
+
var CUSTOM_VOCAL_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
7670
7868
|
var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
|
|
7671
7869
|
var normalizeInstrumentName = (name) => {
|
|
7672
7870
|
if (!name) return "";
|
|
@@ -7723,6 +7921,22 @@ var mountDAW = (target, options = {}) => {
|
|
|
7723
7921
|
let currentPlayStep = 0;
|
|
7724
7922
|
let ready = false;
|
|
7725
7923
|
let isLoading = false;
|
|
7924
|
+
const customVocalsMap = /* @__PURE__ */ new Map();
|
|
7925
|
+
const registerCustomVocal = (def) => {
|
|
7926
|
+
const key = def.key.toLowerCase();
|
|
7927
|
+
if (BASE_LYRIC_MODELS.includes(key)) return;
|
|
7928
|
+
customVocalsMap.set(key, { ...def, key });
|
|
7929
|
+
};
|
|
7930
|
+
const resetCustomVocals = () => {
|
|
7931
|
+
customVocalsMap.clear();
|
|
7932
|
+
for (const d of options.customVocals ?? []) registerCustomVocal(d);
|
|
7933
|
+
};
|
|
7934
|
+
resetCustomVocals();
|
|
7935
|
+
const genCustomVocalKey = () => {
|
|
7936
|
+
let n = 1;
|
|
7937
|
+
while (customVocalsMap.has(`custom${n}`)) n++;
|
|
7938
|
+
return `custom${n}`;
|
|
7939
|
+
};
|
|
7726
7940
|
let selectedNotes = [];
|
|
7727
7941
|
let selectionRect = null;
|
|
7728
7942
|
let copiedNotes = [];
|
|
@@ -8476,6 +8690,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
8476
8690
|
const overlay = showLoadingOverlay(refs.rollContainer);
|
|
8477
8691
|
setLoading(true);
|
|
8478
8692
|
try {
|
|
8693
|
+
if (customVocalsMap.size > 0) {
|
|
8694
|
+
voices.registerVoicebanks?.(
|
|
8695
|
+
Object.fromEntries(
|
|
8696
|
+
[...customVocalsMap].map(([k, d]) => [k, d.url])
|
|
8697
|
+
)
|
|
8698
|
+
);
|
|
8699
|
+
}
|
|
8479
8700
|
await voices.loadModels(streamTracks.map((t) => t.model));
|
|
8480
8701
|
await voices.warm(streamTracks);
|
|
8481
8702
|
} catch (err2) {
|
|
@@ -8640,6 +8861,20 @@ var mountDAW = (target, options = {}) => {
|
|
|
8640
8861
|
<a data-dtm="lyric-terms-link" target="_blank" rel="noopener" style="color:var(--dtm-primary);text-decoration:underline"></a>
|
|
8641
8862
|
<span>\u306E\u5229\u7528\u898F\u7D04\u306B\u5F93\u3063\u3066\u304F\u3060\u3055\u3044</span>
|
|
8642
8863
|
</div>
|
|
8864
|
+
<div class="dtm-row dtm-hidden" data-dtm="lyric-custom" style="flex-direction:column;align-items:stretch;gap:4px">
|
|
8865
|
+
<div class="dtm-row" style="justify-content: space-between; align-items: center;">
|
|
8866
|
+
<a data-dtm="lyric-custom-conv-link" href="https://onjmin.github.io/koe/demo/" target="_blank" rel="noopener" style="font-size:11px;color:var(--dtm-primary);text-decoration:underline">UTAU\u97F3\u6E90(zip)\u3092.koe\u306B\u5909\u63DB</a>
|
|
8867
|
+
<button class="dtm-btn dtm-btn--ghost dtm-btn--xs" data-dtm="lyric-custom-guide">\u4F7F\u3044\u65B9\u30AC\u30A4\u30C9</button>
|
|
8868
|
+
</div>
|
|
8869
|
+
<input type="url" class="dtm-input" data-dtm="lyric-custom-src" placeholder="\u97F3\u6E90URL\uFF08https://\u301C.koe\uFF09" aria-label="\u30AB\u30B9\u30BF\u30E0\u97F3\u6E90\uFF08.koe\uFF09\u306EURL">
|
|
8870
|
+
<input type="url" class="dtm-input" data-dtm="lyric-custom-icon" placeholder="\u30A2\u30A4\u30B3\u30F3\u753B\u50CFURL\uFF08\u4EFB\u610F\uFF09" aria-label="\u30AB\u30B9\u30BF\u30E0\u97F3\u6E90\u306E\u30A2\u30A4\u30B3\u30F3\u753B\u50CFURL">
|
|
8871
|
+
<input type="text" class="dtm-input" data-dtm="lyric-custom-alias" placeholder="\u8868\u793A\u540D\uFF08\u4EFB\u610F\uFF09" aria-label="\u30AB\u30B9\u30BF\u30E0\u97F3\u6E90\u306E\u8868\u793A\u540D\uFF08\u30D7\u30EB\u30C0\u30A6\u30F3\u306E\u5225\u540D\uFF09" maxlength="64">
|
|
8872
|
+
<input type="text" class="dtm-input" data-dtm="lyric-custom-key" placeholder="\u8B58\u5225\u5B50\uFF08\u4EFB\u610F\u30FB\u7701\u7565\u3067\u81EA\u52D5\u63A1\u756A\u3001\u82F1\u5B57\u59CB\u307E\u308A\u82F1\u6570\u5B57\u3068_\uFF09" aria-label="\u30AB\u30B9\u30BF\u30E0\u97F3\u6E90\u306EMML\u8B58\u5225\u5B50\uFF08\u30AD\u30FC\uFF09" maxlength="32">
|
|
8873
|
+
<div class="dtm-row">
|
|
8874
|
+
<span class="dtm-label dtm-grow" data-dtm="lyric-custom-note" style="color:var(--dtm-warn)"></span>
|
|
8875
|
+
<button class="dtm-btn dtm-btn--primary" data-dtm="lyric-custom-apply">\u8FFD\u52A0</button>
|
|
8876
|
+
</div>
|
|
8877
|
+
</div>
|
|
8643
8878
|
<div class="dtm-row" data-dtm="lyric-body" style="flex-direction:column;align-items:stretch">
|
|
8644
8879
|
<div class="dtm-row">
|
|
8645
8880
|
<span class="dtm-label">\u58F0\u91CF</span>
|
|
@@ -8690,6 +8925,30 @@ var mountDAW = (target, options = {}) => {
|
|
|
8690
8925
|
const lyricTermsLink = lyricDiv.querySelector(
|
|
8691
8926
|
'[data-dtm="lyric-terms-link"]'
|
|
8692
8927
|
);
|
|
8928
|
+
const lyricCustom = lyricDiv.querySelector(
|
|
8929
|
+
'[data-dtm="lyric-custom"]'
|
|
8930
|
+
);
|
|
8931
|
+
const lyricCustomSrc = lyricDiv.querySelector(
|
|
8932
|
+
'[data-dtm="lyric-custom-src"]'
|
|
8933
|
+
);
|
|
8934
|
+
const lyricCustomIcon = lyricDiv.querySelector(
|
|
8935
|
+
'[data-dtm="lyric-custom-icon"]'
|
|
8936
|
+
);
|
|
8937
|
+
const lyricCustomAlias = lyricDiv.querySelector(
|
|
8938
|
+
'[data-dtm="lyric-custom-alias"]'
|
|
8939
|
+
);
|
|
8940
|
+
const lyricCustomKey = lyricDiv.querySelector(
|
|
8941
|
+
'[data-dtm="lyric-custom-key"]'
|
|
8942
|
+
);
|
|
8943
|
+
const lyricCustomGuide = lyricDiv.querySelector(
|
|
8944
|
+
'[data-dtm="lyric-custom-guide"]'
|
|
8945
|
+
);
|
|
8946
|
+
const lyricCustomNote = lyricDiv.querySelector(
|
|
8947
|
+
'[data-dtm="lyric-custom-note"]'
|
|
8948
|
+
);
|
|
8949
|
+
const lyricCustomApply = lyricDiv.querySelector(
|
|
8950
|
+
'[data-dtm="lyric-custom-apply"]'
|
|
8951
|
+
);
|
|
8693
8952
|
const fmtPan = (pan) => pan === 64 ? "C" : pan < 64 ? `L${64 - pan}` : `R${pan - 64}`;
|
|
8694
8953
|
const addOpt = (value, label) => {
|
|
8695
8954
|
const o = document.createElement("option");
|
|
@@ -8698,9 +8957,15 @@ var mountDAW = (target, options = {}) => {
|
|
|
8698
8957
|
lyricModelSel.appendChild(o);
|
|
8699
8958
|
};
|
|
8700
8959
|
addOpt("", "\u30DC\u30FC\u30AB\u30EB\u306A\u3057");
|
|
8701
|
-
for (const m of
|
|
8702
|
-
|
|
8703
|
-
|
|
8960
|
+
for (const m of BASE_LYRIC_MODELS)
|
|
8961
|
+
addOpt(m, lyricModelLabel(m, customVocalsMap));
|
|
8962
|
+
for (const [k, def] of customVocalsMap) addOpt(k, def.label ?? k);
|
|
8963
|
+
addOpt(CUSTOM_VOCAL_ADD_VALUE, "\u30AB\u30B9\u30BF\u30E0\u97F3\u6E90\u3092\u8FFD\u52A0\u2026");
|
|
8964
|
+
if (active.lyricModel && !BASE_LYRIC_MODELS.includes(active.lyricModel) && !customVocalsMap.has(active.lyricModel)) {
|
|
8965
|
+
addOpt(
|
|
8966
|
+
active.lyricModel,
|
|
8967
|
+
lyricModelLabel(active.lyricModel, customVocalsMap)
|
|
8968
|
+
);
|
|
8704
8969
|
}
|
|
8705
8970
|
lyricModelSel.value = active.lyricModel;
|
|
8706
8971
|
lyricOctaveSel.value = String(active.vocalOctave);
|
|
@@ -8714,9 +8979,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
8714
8979
|
lyricCount.textContent = active.lyricModel && n > 0 ? `${n}\u97F3\u7BC0` : "";
|
|
8715
8980
|
};
|
|
8716
8981
|
const syncLyricTerms = () => {
|
|
8982
|
+
if (customVocalsMap.has(active.lyricModel)) {
|
|
8983
|
+
lyricTerms.classList.add("dtm-hidden");
|
|
8984
|
+
return;
|
|
8985
|
+
}
|
|
8717
8986
|
const url2 = active.lyricModel ? KOE_VOICEBANK_TERMS[active.lyricModel] : void 0;
|
|
8718
8987
|
if (url2) {
|
|
8719
|
-
const label = lyricModelLabel(active.lyricModel);
|
|
8988
|
+
const label = lyricModelLabel(active.lyricModel, customVocalsMap);
|
|
8720
8989
|
lyricTermsLink.textContent = `${label}UTAU\u97F3\u6E90`;
|
|
8721
8990
|
lyricTermsLink.href = url2;
|
|
8722
8991
|
lyricTerms.classList.remove("dtm-hidden");
|
|
@@ -8725,7 +8994,24 @@ var mountDAW = (target, options = {}) => {
|
|
|
8725
8994
|
}
|
|
8726
8995
|
};
|
|
8727
8996
|
const syncLyricIcon = () => {
|
|
8728
|
-
|
|
8997
|
+
if (!active.lyricModel) {
|
|
8998
|
+
lyricIcon.removeAttribute("src");
|
|
8999
|
+
lyricIcon.classList.add("dtm-hidden");
|
|
9000
|
+
return;
|
|
9001
|
+
}
|
|
9002
|
+
const customDef = customVocalsMap.get(active.lyricModel);
|
|
9003
|
+
if (customDef !== void 0) {
|
|
9004
|
+
const iconSrc = customDef.iconUrl || FALLBACK_VOCAL_ICON;
|
|
9005
|
+
lyricIcon.src = iconSrc;
|
|
9006
|
+
lyricIcon.classList.remove("dtm-hidden");
|
|
9007
|
+
lyricIcon.onerror = () => {
|
|
9008
|
+
lyricIcon.onerror = null;
|
|
9009
|
+
lyricIcon.src = FALLBACK_VOCAL_ICON;
|
|
9010
|
+
};
|
|
9011
|
+
return;
|
|
9012
|
+
}
|
|
9013
|
+
lyricIcon.onerror = null;
|
|
9014
|
+
const imgKey = VOICE_IMAGE_KEY[active.lyricModel.toLowerCase()];
|
|
8729
9015
|
const src = imgKey ? VOICE_IMAGES[imgKey] : void 0;
|
|
8730
9016
|
if (src) {
|
|
8731
9017
|
lyricIcon.src = src;
|
|
@@ -8744,11 +9030,64 @@ var mountDAW = (target, options = {}) => {
|
|
|
8744
9030
|
};
|
|
8745
9031
|
syncLyricVisibility();
|
|
8746
9032
|
lyricModelSel.addEventListener("change", () => {
|
|
9033
|
+
if (lyricModelSel.value === CUSTOM_VOCAL_ADD_VALUE) {
|
|
9034
|
+
lyricCustomNote.textContent = "";
|
|
9035
|
+
lyricCustomAlias.value = "";
|
|
9036
|
+
lyricCustomKey.value = "";
|
|
9037
|
+
lyricCustom.classList.remove("dtm-hidden");
|
|
9038
|
+
return;
|
|
9039
|
+
}
|
|
9040
|
+
lyricCustom.classList.add("dtm-hidden");
|
|
8747
9041
|
active.lyricModel = lyricModelSel.value;
|
|
8748
9042
|
syncLyricVisibility();
|
|
8749
9043
|
syncInstDisabled();
|
|
8750
9044
|
fireLyricsChange(active);
|
|
8751
9045
|
});
|
|
9046
|
+
lyricCustomGuide.addEventListener("click", () => {
|
|
9047
|
+
showModal("\u30AB\u30B9\u30BF\u30E0\u97F3\u6E90(.koe)\u306E\u4F7F\u3044\u65B9", KOE_INFO_HTML);
|
|
9048
|
+
});
|
|
9049
|
+
lyricCustomApply.addEventListener("click", () => {
|
|
9050
|
+
const src = lyricCustomSrc.value.trim();
|
|
9051
|
+
if (!isValidHttpUrl(src)) {
|
|
9052
|
+
lyricCustomNote.textContent = "\u97F3\u6E90URL\u304C\u4E0D\u6B63\u3067\u3059\uFF08http/https\u306E\u307F\u30FB2048\u6587\u5B57\u307E\u3067\uFF09";
|
|
9053
|
+
return;
|
|
9054
|
+
}
|
|
9055
|
+
const iconRaw = lyricCustomIcon.value.trim();
|
|
9056
|
+
const iconUrl = isValidHttpUrl(iconRaw) ? iconRaw : "";
|
|
9057
|
+
const alias = lyricCustomAlias.value.trim();
|
|
9058
|
+
const existing = [...customVocalsMap.values()].find(
|
|
9059
|
+
(d) => d.url === src
|
|
9060
|
+
);
|
|
9061
|
+
const keyRaw = lyricCustomKey.value.trim().toLowerCase();
|
|
9062
|
+
let key;
|
|
9063
|
+
if (keyRaw) {
|
|
9064
|
+
if (!CUSTOM_VOCAL_KEY_RE.test(keyRaw)) {
|
|
9065
|
+
lyricCustomNote.textContent = "\u8B58\u5225\u5B50\u304C\u4E0D\u6B63\u3067\u3059\uFF08\u82F1\u5B57\u307E\u305F\u306F\u30A2\u30F3\u30C0\u30FC\u30B9\u30B3\u30A2\u3067\u59CB\u307E\u308B\u82F1\u6570\u5B57\u30FB_\u306E\u307F\uFF09";
|
|
9066
|
+
return;
|
|
9067
|
+
}
|
|
9068
|
+
if (BASE_LYRIC_MODELS.includes(keyRaw)) {
|
|
9069
|
+
lyricCustomNote.textContent = "\u305D\u306E\u8B58\u5225\u5B50\u306F\u5185\u8535\u30E2\u30C7\u30EB\u540D\u3068\u885D\u7A81\u3057\u3066\u3044\u307E\u3059";
|
|
9070
|
+
return;
|
|
9071
|
+
}
|
|
9072
|
+
const conflict = customVocalsMap.get(keyRaw);
|
|
9073
|
+
if (conflict && conflict.url !== src) {
|
|
9074
|
+
lyricCustomNote.textContent = "\u305D\u306E\u8B58\u5225\u5B50\u306F\u5225\u306E\u97F3\u6E90\u306B\u4F7F\u7528\u6E08\u307F\u3067\u3059";
|
|
9075
|
+
return;
|
|
9076
|
+
}
|
|
9077
|
+
key = keyRaw;
|
|
9078
|
+
} else {
|
|
9079
|
+
key = existing?.key ?? genCustomVocalKey();
|
|
9080
|
+
}
|
|
9081
|
+
registerCustomVocal({
|
|
9082
|
+
key,
|
|
9083
|
+
iconUrl: iconUrl || (existing?.iconUrl ?? ""),
|
|
9084
|
+
url: src,
|
|
9085
|
+
label: alias || existing?.label
|
|
9086
|
+
});
|
|
9087
|
+
active.lyricModel = key;
|
|
9088
|
+
fireLyricsChange(active);
|
|
9089
|
+
updateTrackPanel();
|
|
9090
|
+
});
|
|
8752
9091
|
lyricOctaveSel.addEventListener("change", () => {
|
|
8753
9092
|
active.vocalOctave = Number.parseInt(lyricOctaveSel.value, 10);
|
|
8754
9093
|
fireLyricsChange(active);
|
|
@@ -8932,9 +9271,25 @@ var mountDAW = (target, options = {}) => {
|
|
|
8932
9271
|
const head = params ? `${x.model} ${params}` : x.model;
|
|
8933
9272
|
return `@@${x.i} ${head} ${x.text}`;
|
|
8934
9273
|
});
|
|
8935
|
-
const
|
|
9274
|
+
const customVocalDecls = [];
|
|
9275
|
+
for (const [key, def] of customVocalsMap) {
|
|
9276
|
+
const isUsed = trackStates.some(
|
|
9277
|
+
(t) => t.lyricModel.trim().toLowerCase() === key && t.lyrics.trim().length > 0 && clipNotes(t.core.getNotes()).length > 0
|
|
9278
|
+
);
|
|
9279
|
+
if (!isUsed) continue;
|
|
9280
|
+
const iconPart = def.iconUrl || "-";
|
|
9281
|
+
customVocalDecls.push(`@@${key} ${iconPart} ${def.url}`);
|
|
9282
|
+
}
|
|
9283
|
+
const full = [
|
|
9284
|
+
metaLineFull,
|
|
9285
|
+
...customVocalDecls,
|
|
9286
|
+
...trackLines,
|
|
9287
|
+
...lyricLines,
|
|
9288
|
+
MML_END_MARKER
|
|
9289
|
+
].filter((s) => s.length > 0).join(";\n");
|
|
8936
9290
|
const minified = [
|
|
8937
9291
|
metaLineMini,
|
|
9292
|
+
...customVocalDecls,
|
|
8938
9293
|
...trackLinesMini,
|
|
8939
9294
|
...lyricLines,
|
|
8940
9295
|
MML_END_MARKER
|
|
@@ -8999,6 +9354,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
8999
9354
|
stop();
|
|
9000
9355
|
clearAll();
|
|
9001
9356
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
9357
|
+
resetCustomVocals();
|
|
9358
|
+
for (const def of parseCustomVocals(mml)) registerCustomVocal(def);
|
|
9002
9359
|
const {
|
|
9003
9360
|
placements,
|
|
9004
9361
|
bpm: parsedBpm,
|
|
@@ -9059,7 +9416,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
9059
9416
|
const t = trackStates[p.trackIndex];
|
|
9060
9417
|
if (!t) continue;
|
|
9061
9418
|
t.core.addNote(p.startStep, p.pitch, {
|
|
9062
|
-
noteLengthSteps: p.durationSteps
|
|
9419
|
+
noteLengthSteps: p.durationSteps,
|
|
9420
|
+
velocity: p.velocity
|
|
9063
9421
|
});
|
|
9064
9422
|
}
|
|
9065
9423
|
if (parsedBpm) setBpm(parsedBpm);
|
|
@@ -9795,7 +10153,7 @@ var playMML = (mml, options = {}) => {
|
|
|
9795
10153
|
startStep: p.startStep,
|
|
9796
10154
|
durationSteps: p.durationSteps,
|
|
9797
10155
|
pitch: p.pitch,
|
|
9798
|
-
velocity:
|
|
10156
|
+
velocity: p.velocity
|
|
9799
10157
|
}));
|
|
9800
10158
|
return { id: String(index), volume: masterVolume, notes };
|
|
9801
10159
|
});
|
|
@@ -10357,6 +10715,9 @@ var SoundFont = class _SoundFont {
|
|
|
10357
10715
|
this.ch = ch;
|
|
10358
10716
|
this.isDrum = isDrum;
|
|
10359
10717
|
}
|
|
10718
|
+
zones;
|
|
10719
|
+
ch;
|
|
10720
|
+
isDrum;
|
|
10360
10721
|
static afterTime = 0.5;
|
|
10361
10722
|
static fonts = /* @__PURE__ */ new Map();
|
|
10362
10723
|
static ch = -1;
|
|
@@ -11323,12 +11684,14 @@ var createDtmStudio = async (options = {}) => {
|
|
|
11323
11684
|
init,
|
|
11324
11685
|
injectStyles,
|
|
11325
11686
|
isChordHeavyTrack,
|
|
11687
|
+
isValidHttpUrl,
|
|
11326
11688
|
koeUrl,
|
|
11327
11689
|
mountDAW,
|
|
11328
11690
|
mountMmlPlayer,
|
|
11329
11691
|
normalizeLyrics,
|
|
11330
11692
|
onClick,
|
|
11331
11693
|
panToStereo,
|
|
11694
|
+
parseCustomVocals,
|
|
11332
11695
|
parseLyrics,
|
|
11333
11696
|
parseMML,
|
|
11334
11697
|
parseMmlMeta,
|
|
@@ -11337,6 +11700,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
11337
11700
|
setDrawOffset,
|
|
11338
11701
|
shiftNotes,
|
|
11339
11702
|
showLoadingOverlay,
|
|
11703
|
+
stripCustomVocals,
|
|
11340
11704
|
stripLyrics,
|
|
11341
11705
|
stripMmlMeta,
|
|
11342
11706
|
vocalVolumeToGain
|