@onjmin/dtm 0.1.49 → 0.1.53
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 +48 -18
- package/dist/index.d.ts +48 -18
- package/dist/index.js +458 -53
- package/dist/index.mjs +455 -53
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
MAX_VOCAL_VOLUME: () => MAX_VOCAL_VOLUME,
|
|
42
42
|
MMLCore: () => MMLCore,
|
|
43
43
|
MML_END_MARKER: () => MML_END_MARKER,
|
|
44
|
+
MidiSearchClient: () => MidiSearchClient,
|
|
44
45
|
PITCH_MAP: () => PITCH_MAP,
|
|
45
46
|
PREWARM_NOTES: () => PREWARM_NOTES,
|
|
46
47
|
TRACKS_ADVANCED: () => TRACKS_ADVANCED,
|
|
@@ -90,6 +91,7 @@ __export(index_exports, {
|
|
|
90
91
|
init: () => init,
|
|
91
92
|
injectStyles: () => injectStyles,
|
|
92
93
|
isChordHeavyTrack: () => isChordHeavyTrack,
|
|
94
|
+
isPlausibleMidiTranscription: () => isPlausibleMidiTranscription,
|
|
93
95
|
isValidHttpUrl: () => isValidHttpUrl,
|
|
94
96
|
koeUrl: () => koeUrl,
|
|
95
97
|
mountDAW: () => mountDAW,
|
|
@@ -103,6 +105,7 @@ __export(index_exports, {
|
|
|
103
105
|
parseMmlMeta: () => parseMmlMeta,
|
|
104
106
|
playMML: () => playMML,
|
|
105
107
|
playSingingMML: () => playSingingMML,
|
|
108
|
+
setBackgroundActive: () => setBackgroundActive,
|
|
106
109
|
setDrawOffset: () => setDrawOffset,
|
|
107
110
|
shiftNotes: () => shiftNotes,
|
|
108
111
|
showLoadingOverlay: () => showLoadingOverlay,
|
|
@@ -1330,7 +1333,13 @@ var icon = (name, size = 20) => {
|
|
|
1330
1333
|
// src/daw-ui.ts
|
|
1331
1334
|
var q = (root, sel) => root.querySelector(sel);
|
|
1332
1335
|
var buildUI = (target, options) => {
|
|
1333
|
-
const {
|
|
1336
|
+
const {
|
|
1337
|
+
drumPatternNames,
|
|
1338
|
+
defaultDrumPattern,
|
|
1339
|
+
defaultBpm,
|
|
1340
|
+
showMidi,
|
|
1341
|
+
showMidiSearch
|
|
1342
|
+
} = options;
|
|
1334
1343
|
const drumOptions = [`<option value="none">\u306A\u3057</option>`].concat(
|
|
1335
1344
|
drumPatternNames.map(
|
|
1336
1345
|
(name) => `<option value="${name}" ${name === defaultDrumPattern ? "selected" : ""}>${name}</option>`
|
|
@@ -1408,6 +1417,12 @@ var buildUI = (target, options) => {
|
|
|
1408
1417
|
<span class="dtm-label" data-dtm="zoomy-label">100%</span>
|
|
1409
1418
|
<button class="dtm-iconbtn" data-dtm="zoomy-in" title="\u62E1\u5927">\uFF0B</button>
|
|
1410
1419
|
</div>
|
|
1420
|
+
<div class="dtm-row">
|
|
1421
|
+
<span class="dtm-label">\u80CC\u666F\u753B\u50CF</span>
|
|
1422
|
+
<input type="file" accept="image/*" data-dtm="bg-file-input" class="dtm-hidden">
|
|
1423
|
+
<button class="dtm-btn dtm-btn--primary" data-dtm="bg-upload">\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9</button>
|
|
1424
|
+
<button class="dtm-btn dtm-btn--danger dtm-hidden" data-dtm="bg-remove">\u524A\u9664</button>
|
|
1425
|
+
</div>
|
|
1411
1426
|
</div>
|
|
1412
1427
|
</details>
|
|
1413
1428
|
|
|
@@ -1446,6 +1461,9 @@ var buildUI = (target, options) => {
|
|
|
1446
1461
|
<textarea class="dtm-textarea dtm-grow" data-dtm="mml-input" placeholder="MML\u3092\u5165\u529B"></textarea>
|
|
1447
1462
|
<button class="dtm-btn dtm-btn--primary" data-dtm="mml-load" style="flex-shrink:0">\u8AAD\u8FBC</button>
|
|
1448
1463
|
</div>
|
|
1464
|
+
<div class="dtm-row" data-dtm="midi-search-row" style="justify-content:flex-end;${showMidiSearch ? "" : "display:none"}">
|
|
1465
|
+
<button class="dtm-btn dtm-btn--primary" data-dtm="midi-search-open">MML\u691C\u7D22</button>
|
|
1466
|
+
</div>
|
|
1449
1467
|
<p class="dtm-load-note dtm-hidden" data-dtm="mml-load-note"></p>
|
|
1450
1468
|
</div>
|
|
1451
1469
|
</details>
|
|
@@ -1556,6 +1574,9 @@ var buildUI = (target, options) => {
|
|
|
1556
1574
|
zoomXOut: sel("zoomx-out"),
|
|
1557
1575
|
zoomYIn: sel("zoomy-in"),
|
|
1558
1576
|
zoomYOut: sel("zoomy-out"),
|
|
1577
|
+
bgFileInput: sel("bg-file-input"),
|
|
1578
|
+
bgUploadBtn: sel("bg-upload"),
|
|
1579
|
+
bgRemoveBtn: sel("bg-remove"),
|
|
1559
1580
|
rollContainer: sel("roll"),
|
|
1560
1581
|
wrapper: sel("wrapper"),
|
|
1561
1582
|
vScroll: sel("vscroll"),
|
|
@@ -1574,6 +1595,7 @@ var buildUI = (target, options) => {
|
|
|
1574
1595
|
midiInfoBtn: sel("midi-info"),
|
|
1575
1596
|
midiTrackSelection: sel("midi-track-selection"),
|
|
1576
1597
|
midiPanel: sel("midi-panel"),
|
|
1598
|
+
midiSearchOpenBtn: sel("midi-search-open"),
|
|
1577
1599
|
mmlInput: sel("mml-input"),
|
|
1578
1600
|
mmlLoadBtn: sel("mml-load"),
|
|
1579
1601
|
mmlLoadNote: sel("mml-load-note"),
|
|
@@ -2618,8 +2640,9 @@ var KOE_VOICEBANKS = {
|
|
|
2618
2640
|
teto: "\u91CD\u97F3\u30C6\u30C8\u5358\u72EC\u97F3.koe",
|
|
2619
2641
|
shiyo: "\u9769\u547D\u30B7\u30E8.koe",
|
|
2620
2642
|
rei: "\u8DB3\u7ACB\u30EC\u30A4ver3.5.0.koe",
|
|
2621
|
-
|
|
2622
|
-
|
|
2643
|
+
mgroid: "MGRoid_\u539F\u97F3\u8A2D\u5B9A\u6E08\u307F.koe",
|
|
2644
|
+
motroid: "MOTRoid\u5B8C\u5168\u7248V2.koe",
|
|
2645
|
+
nynroid: "NYNRoidver1.4.koe"
|
|
2623
2646
|
};
|
|
2624
2647
|
var KOE_VOICEBANK_LABELS = {
|
|
2625
2648
|
tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093",
|
|
@@ -2630,8 +2653,9 @@ var KOE_VOICEBANK_LABELS = {
|
|
|
2630
2653
|
teto: "\u91CD\u97F3\u30C6\u30C8",
|
|
2631
2654
|
shiyo: "\u9769\u547D\u30B7\u30E8",
|
|
2632
2655
|
rei: "\u8DB3\u7ACB\u30EC\u30A4",
|
|
2633
|
-
|
|
2634
|
-
|
|
2656
|
+
mgroid: "MGRoid",
|
|
2657
|
+
motroid: "MOTRoid",
|
|
2658
|
+
nynroid: "NYNRoid"
|
|
2635
2659
|
};
|
|
2636
2660
|
var VOICE_IMAGE_KEY = {
|
|
2637
2661
|
klatt: "puyuyu",
|
|
@@ -2643,8 +2667,9 @@ var VOICE_IMAGE_KEY = {
|
|
|
2643
2667
|
teto: "teto",
|
|
2644
2668
|
shiyo: "shiyo",
|
|
2645
2669
|
rei: "rei",
|
|
2646
|
-
|
|
2647
|
-
|
|
2670
|
+
mgroid: "MGRoid",
|
|
2671
|
+
motroid: "MOTRoid",
|
|
2672
|
+
nynroid: "NYNRoid"
|
|
2648
2673
|
};
|
|
2649
2674
|
var KOE_VOICEBANK_TERMS = {
|
|
2650
2675
|
tsukuyomi: "https://tyc.rei-yumesaki.net/material/utau/terms/",
|
|
@@ -2655,8 +2680,9 @@ var KOE_VOICEBANK_TERMS = {
|
|
|
2655
2680
|
teto: "https://kasaneteto.jp/guidelines/voice.html",
|
|
2656
2681
|
shiyo: "https://kakumeisiyo.my.canva.site/dagkuyjwycs",
|
|
2657
2682
|
rei: "https://mechanicalgirl.jp/guidelines/",
|
|
2658
|
-
|
|
2659
|
-
|
|
2683
|
+
mgroid: "https://x.com/nisusansu/status/1048825378188353536",
|
|
2684
|
+
motroid: "https://www.nicovideo.jp/watch/sm40031282",
|
|
2685
|
+
nynroid: "https://www.bilibili.com/video/BV1V24y1a7qs"
|
|
2660
2686
|
};
|
|
2661
2687
|
var koeUrl = (name, base = KOE_BASE_URL) => `${base}/${encodeURIComponent(name)}`;
|
|
2662
2688
|
var DEFAULT_WORLDLINE_SCRIPT = "https://onjmin.github.io/koe/demo/world/worldline.js";
|
|
@@ -3480,6 +3506,19 @@ var extractMidiPlacements = (midi, selectedTrackIndices) => {
|
|
|
3480
3506
|
}
|
|
3481
3507
|
return { placements, bpm };
|
|
3482
3508
|
};
|
|
3509
|
+
var isPlausibleMidiTranscription = (midi, selectedIndices) => {
|
|
3510
|
+
const { placements } = extractMidiPlacements(midi, selectedIndices);
|
|
3511
|
+
if (placements.length < 4) return false;
|
|
3512
|
+
const shortCount = placements.filter((p) => p.durationSteps <= 1).length;
|
|
3513
|
+
const shortRatio = shortCount / placements.length;
|
|
3514
|
+
const countByStep = /* @__PURE__ */ new Map();
|
|
3515
|
+
for (const p of placements) {
|
|
3516
|
+
countByStep.set(p.startStep, (countByStep.get(p.startStep) ?? 0) + 1);
|
|
3517
|
+
}
|
|
3518
|
+
const denseSteps = [...countByStep.values()].filter((n) => n >= 6).length;
|
|
3519
|
+
const denseRatio = denseSteps / countByStep.size;
|
|
3520
|
+
return shortRatio < 0.6 && denseRatio < 0.35;
|
|
3521
|
+
};
|
|
3483
3522
|
var extractMidiPlacementsByTrack = (midi, selectedIndices, trackIds) => {
|
|
3484
3523
|
const { tracks, division } = midi;
|
|
3485
3524
|
const ticksPerBeat = division;
|
|
@@ -3632,6 +3671,45 @@ var exportMIDI = (options) => {
|
|
|
3632
3671
|
return new Blob([new Uint8Array(arr).buffer], { type: "audio/midi" });
|
|
3633
3672
|
};
|
|
3634
3673
|
|
|
3674
|
+
// src/midi-search.ts
|
|
3675
|
+
var MidiSearchClient = class {
|
|
3676
|
+
config;
|
|
3677
|
+
constructor(config = {}) {
|
|
3678
|
+
this.config = config;
|
|
3679
|
+
}
|
|
3680
|
+
get enabled() {
|
|
3681
|
+
return !!this.config.apiKey;
|
|
3682
|
+
}
|
|
3683
|
+
get baseUrl() {
|
|
3684
|
+
return this.config.baseUrl ?? "https://rpgen-search.pages.dev/api";
|
|
3685
|
+
}
|
|
3686
|
+
headers() {
|
|
3687
|
+
const h = {};
|
|
3688
|
+
if (this.config.apiKey) {
|
|
3689
|
+
h.Authorization = `Bearer ${this.config.apiKey}`;
|
|
3690
|
+
}
|
|
3691
|
+
return h;
|
|
3692
|
+
}
|
|
3693
|
+
async searchSongs(params) {
|
|
3694
|
+
if (!this.enabled) return [];
|
|
3695
|
+
const qs = new URLSearchParams();
|
|
3696
|
+
if (params.title) qs.set("title", params.title);
|
|
3697
|
+
if (params.user) qs.set("user", params.user);
|
|
3698
|
+
if (params.twitter_id) qs.set("twitter_id", params.twitter_id);
|
|
3699
|
+
const url2 = `${this.baseUrl}/picotune/songs${qs.toString() ? `?${qs.toString()}` : ""}`;
|
|
3700
|
+
const res = await fetch(url2, { headers: this.headers() });
|
|
3701
|
+
if (!res.ok) throw new Error(`picotune search failed: ${res.status}`);
|
|
3702
|
+
const body = await res.json();
|
|
3703
|
+
return body.data ?? body.songs ?? body;
|
|
3704
|
+
}
|
|
3705
|
+
async fetchMidi(fileName) {
|
|
3706
|
+
const url2 = `${this.baseUrl}/picotune/songs/${encodeURIComponent(fileName)}`;
|
|
3707
|
+
const res = await fetch(url2, { headers: this.headers() });
|
|
3708
|
+
if (!res.ok) throw new Error(`picotune fetch failed: ${res.status}`);
|
|
3709
|
+
return res.arrayBuffer();
|
|
3710
|
+
}
|
|
3711
|
+
};
|
|
3712
|
+
|
|
3635
3713
|
// src/linked-list.ts
|
|
3636
3714
|
var LinkedList = class {
|
|
3637
3715
|
#cursor;
|
|
@@ -3697,6 +3775,10 @@ var HEADER_HEIGHT = 20;
|
|
|
3697
3775
|
var getRenderConfig = () => g_config;
|
|
3698
3776
|
var g_draw_offset_x = 0;
|
|
3699
3777
|
var g_draw_offset_y = 0;
|
|
3778
|
+
var g_bg_active = false;
|
|
3779
|
+
var setBackgroundActive = (active) => {
|
|
3780
|
+
g_bg_active = active;
|
|
3781
|
+
};
|
|
3700
3782
|
var getDrawOffset = () => ({
|
|
3701
3783
|
x: g_draw_offset_x,
|
|
3702
3784
|
y: g_draw_offset_y
|
|
@@ -3846,7 +3928,7 @@ var drawHeader = () => {
|
|
|
3846
3928
|
const { stepWidth, stepsPerBar } = g_config;
|
|
3847
3929
|
g_header_ctx.save();
|
|
3848
3930
|
g_header_ctx.translate(-g_draw_offset_x, 0);
|
|
3849
|
-
g_header_ctx.fillStyle = "#0a0f1f";
|
|
3931
|
+
g_header_ctx.fillStyle = g_bg_active ? "rgba(10,15,31,0.55)" : "#0a0f1f";
|
|
3850
3932
|
g_header_ctx.fillRect(
|
|
3851
3933
|
g_draw_offset_x,
|
|
3852
3934
|
0,
|
|
@@ -3892,7 +3974,7 @@ var drawGrid = (noteLengthSteps = 1) => {
|
|
|
3892
3974
|
const octave = Math.floor(totalPitch / 12) - 1;
|
|
3893
3975
|
const isC4Range = octave === 4;
|
|
3894
3976
|
const screenY = y - g_draw_offset_y;
|
|
3895
|
-
g_grid_ctx.fillStyle = isBlackKey ? "#080b16" : "#111628";
|
|
3977
|
+
g_grid_ctx.fillStyle = g_bg_active ? isBlackKey ? "rgba(8,11,22,0.55)" : "rgba(17,22,40,0.45)" : isBlackKey ? "#080b16" : "#111628";
|
|
3896
3978
|
g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
|
|
3897
3979
|
if (isC4Range) {
|
|
3898
3980
|
g_grid_ctx.fillStyle = "rgba(41,173,255,0.05)";
|
|
@@ -5464,6 +5546,18 @@ var DAW_CSS = `
|
|
|
5464
5546
|
4px 4px 0 var(--c-black);
|
|
5465
5547
|
overflow: hidden;
|
|
5466
5548
|
}
|
|
5549
|
+
/* \u30AB\u30B9\u30BF\u30E0\u80CC\u666F\u753B\u50CF\u30EC\u30A4\u30E4\u30FC\uFF08\u753B\u50CF\u81EA\u4F53\u3092\u534A\u900F\u660E\u5316\u3057\u3001\u30AD\u30E3\u30F3\u30D0\u30B9\u5074\u306E\u5857\u308A\u306F\u7DAD\u6301\uFF09 */
|
|
5550
|
+
.dtm-roll::before {
|
|
5551
|
+
content: '';
|
|
5552
|
+
position: absolute;
|
|
5553
|
+
inset: 0;
|
|
5554
|
+
background-image: var(--dtm-roll-bg-image, none);
|
|
5555
|
+
background-size: cover;
|
|
5556
|
+
background-position: center;
|
|
5557
|
+
background-repeat: no-repeat;
|
|
5558
|
+
opacity: 0.4;
|
|
5559
|
+
pointer-events: none;
|
|
5560
|
+
}
|
|
5467
5561
|
.dtm-vscroll {
|
|
5468
5562
|
position: relative;
|
|
5469
5563
|
width: 20px;
|
|
@@ -5836,9 +5930,14 @@ var DAW_CSS = `
|
|
|
5836
5930
|
.dtm-modal-body {
|
|
5837
5931
|
padding: 12px;
|
|
5838
5932
|
overflow-y: auto;
|
|
5933
|
+
overflow-x: hidden;
|
|
5839
5934
|
font-size: 13px;
|
|
5840
5935
|
line-height: 1.6;
|
|
5841
5936
|
}
|
|
5937
|
+
.dtm-modal-body * {
|
|
5938
|
+
max-width: 100%;
|
|
5939
|
+
box-sizing: border-box;
|
|
5940
|
+
}
|
|
5842
5941
|
.dtm-modal-body a {
|
|
5843
5942
|
color: var(--dtm-primary);
|
|
5844
5943
|
text-decoration: underline;
|
|
@@ -6441,6 +6540,9 @@ var MGRoid_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA
|
|
|
6441
6540
|
// assets/MOTRoid.png
|
|
6442
6541
|
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";
|
|
6443
6542
|
|
|
6543
|
+
// assets/NYNRoid.png
|
|
6544
|
+
var NYNRoid_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABo0lEQVR42tWXoU8DMRTGf7csCAQBOcz4A6aOIOZAETKLBsQGCCwhiIWQCUKCRDAyAWgcWQgoPOHU/gBmOAkJggRTRPeSa7m7lSHWPdNc23u97+v3vvYCrCiVSir5HMdxwAjhmqfAmCPI+uIURKnPcRxjIc1d0GZi7AwU7Y63qyMA5reODaR268qEnc9fDdhaaDabAERRZLRhGDollnmtVsvvKihmqb3b7QJQq9WcGOh0OgDU63Xj/SyN+KeBMAxV2h4K8peTbQAWDy9HGpeIoiiYDA3Y6l9Z29NI788NpII8WN0FYLmgU0ZDfMJ/BiSU0tI4m57RHZWqRn5XzZ33ZDmn/wwk1KmSKg4C3a0eLvIzPbYB2P/61H7wW0ue3wdSvFxZ9Wvu+YARUb/tH1l1PzkMiBYkxOMldpZuAWg/r6eeCa5r+s+A3AuknjcrNwDMvZ4C8L5wAMB1b8Ood7kHpGjFfwZU0tkajYaxt6Ly3tSsNsbvj/QqGbwvPpLFtscMWHtnnxGuMezPyv8q+AdylzX8ZsDJIfv9vjFYLpf/lHvsDPwA5Wy6PgZtokAAAAAASUVORK5CYII=";
|
|
6545
|
+
|
|
6444
6546
|
// assets/puyuyu.png
|
|
6445
6547
|
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=";
|
|
6446
6548
|
|
|
@@ -6476,7 +6578,8 @@ var VOICE_IMAGES = {
|
|
|
6476
6578
|
tsukuyomi: tsukuyomi_default,
|
|
6477
6579
|
rei: rei_default,
|
|
6478
6580
|
MGRoid: MGRoid_default,
|
|
6479
|
-
MOTRoid: MOTRoid_default
|
|
6581
|
+
MOTRoid: MOTRoid_default,
|
|
6582
|
+
NYNRoid: NYNRoid_default
|
|
6480
6583
|
};
|
|
6481
6584
|
var FALLBACK_VOCAL_ICON = Chip_default;
|
|
6482
6585
|
|
|
@@ -7709,12 +7812,12 @@ var KOE_INFO_HTML = `
|
|
|
7709
7812
|
|
|
7710
7813
|
<h4>3. DTM\u3067\u4F7F\u3046</h4>
|
|
7711
7814
|
<ul>
|
|
7712
|
-
<li>\u6B4C\u5531\u30E2\u30C7\u30EB\u306E\u30D7\u30EB\u30C0\u30A6\u30F3\u304B\u3089\u300C\u30AB\u30B9\u30BF\u30E0\u97F3\
|
|
7713
|
-
<li>\u300C\u97F3\
|
|
7714
|
-
<li>\u30A2\u30A4\u30B3\u30F3\u753B\
|
|
7815
|
+
<li>\u6B4C\u5531\u30E2\u30C7\u30EB\u306E\u30D7\u30EB\u30C0\u30A6\u30F3\u304B\u3089\u300C\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u3092\u8FFD\u52A0\u2026\u300D\u3092\u9078\u3073\u307E\u3059\u3002</li>
|
|
7816
|
+
<li>\u300C\u97F3\u58F0URL\u300D\u306B\u624B\u98062\u306E\u76F4\u63A5\u30EA\u30F3\u30AF\u3092\u8CBC\u308A\u4ED8\u3051\u307E\u3059\u3002</li>
|
|
7817
|
+
<li>\u30A2\u30A4\u30B3\u30F3\u753B\u50CFURL\u306F\u4EFB\u610F\u3067\u3059\uFF08\u7701\u7565\u53EF\uFF09\u3002\u8B58\u5225\u5B50\u306F\u30D5\u30A1\u30A4\u30EB\u540D\u304B\u3089\u81EA\u52D5\u751F\u6210\u3055\u308C\u307E\u3059\u3002</li>
|
|
7715
7818
|
<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
7819
|
</ul>
|
|
7717
|
-
<p style="margin-top:4px;"><small>\
|
|
7820
|
+
<p style="margin-top:4px;color:var(--dtm-warn);"><small>\u203BUTAU\u97F3\u6E90\u3092 <code>.koe</code> \u306B\u5909\u63DB\u3057\u3066\u30CD\u30C3\u30C8\u4E0A\u306B\u7F6E\u304F\u884C\u70BA\u306F\u3001\u97F3\u6E90\u30C7\u30FC\u30BF\u306E\u518D\u52A0\u5DE5\u30FB\u518D\u914D\u5E03\u306B\u3042\u305F\u308A\u307E\u3059\u3002\u3053\u308C\u304C\u914D\u5E03\u5143\u306E\u5229\u7528\u898F\u7D04\u306B\u53CD\u3057\u3066\u3044\u306A\u3044\u304B\u306F\u5FC5\u305A\u3054\u81EA\u8EAB\u3067\u78BA\u8A8D\u3057\u3001\u305D\u306E\u8CAC\u4EFB\u3092\u8CA0\u3063\u3066\u304F\u3060\u3055\u3044\u3002</small></p>
|
|
7718
7821
|
</div>
|
|
7719
7822
|
`;
|
|
7720
7823
|
var BASE_STEP_WIDTH = 0.5;
|
|
@@ -7865,6 +7968,18 @@ var BASE_LYRIC_MODEL_LABELS = {
|
|
|
7865
7968
|
var lyricModelLabel = (model, customMap) => BASE_LYRIC_MODEL_LABELS[model] ?? customMap.get(model)?.label ?? model;
|
|
7866
7969
|
var CUSTOM_VOCAL_ADD_VALUE = "+custom";
|
|
7867
7970
|
var CUSTOM_VOCAL_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
7971
|
+
var deriveCustomVocalKeyFromUrl = (url2) => {
|
|
7972
|
+
let name;
|
|
7973
|
+
try {
|
|
7974
|
+
const path = new URL(url2).pathname;
|
|
7975
|
+
name = decodeURIComponent(path.slice(path.lastIndexOf("/") + 1));
|
|
7976
|
+
} catch {
|
|
7977
|
+
return "";
|
|
7978
|
+
}
|
|
7979
|
+
name = name.replace(/\.[^.]*$/, "").normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
7980
|
+
if (name && /^[0-9]/.test(name)) name = `_${name}`;
|
|
7981
|
+
return CUSTOM_VOCAL_KEY_RE.test(name) ? name : "";
|
|
7982
|
+
};
|
|
7868
7983
|
var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
|
|
7869
7984
|
var normalizeInstrumentName = (name) => {
|
|
7870
7985
|
if (!name) return "";
|
|
@@ -7882,13 +7997,16 @@ var mountDAW = (target, options = {}) => {
|
|
|
7882
7997
|
const drumPatterns = options.drumPatterns ?? DRUM_PATTERNS;
|
|
7883
7998
|
const showMidi = !!options.parseMidi;
|
|
7884
7999
|
const showChord = !isAdvanced;
|
|
8000
|
+
const midiSearchClient = options.midiSearch ? new MidiSearchClient(options.midiSearch) : void 0;
|
|
8001
|
+
const showMidiSearch = !!midiSearchClient?.enabled;
|
|
7885
8002
|
const refs = buildUI(target, {
|
|
7886
8003
|
tracks: trackConfigs,
|
|
7887
8004
|
drumPatternNames: Object.keys(drumPatterns),
|
|
7888
8005
|
defaultDrumPattern: drumPatterns.dance ? "dance" : Object.keys(drumPatterns)[0] ?? "none",
|
|
7889
8006
|
defaultBpm: options.defaultBpm ?? DEFAULT_BPM,
|
|
7890
8007
|
showMidi,
|
|
7891
|
-
showChord
|
|
8008
|
+
showChord,
|
|
8009
|
+
showMidiSearch
|
|
7892
8010
|
});
|
|
7893
8011
|
const renderConfig = {
|
|
7894
8012
|
stepsPerBar: 192,
|
|
@@ -8501,6 +8619,28 @@ var mountDAW = (target, options = {}) => {
|
|
|
8501
8619
|
redrawAll();
|
|
8502
8620
|
}
|
|
8503
8621
|
};
|
|
8622
|
+
const ROLL_BG_STORAGE_KEY = "dtm-piano-roll-bg";
|
|
8623
|
+
const resizeImageToDataUrl = (img, maxW, maxH) => {
|
|
8624
|
+
const scale = Math.min(1, maxW / img.width, maxH / img.height);
|
|
8625
|
+
const width = Math.max(1, Math.round(img.width * scale));
|
|
8626
|
+
const height = Math.max(1, Math.round(img.height * scale));
|
|
8627
|
+
const canvas = document.createElement("canvas");
|
|
8628
|
+
canvas.width = width;
|
|
8629
|
+
canvas.height = height;
|
|
8630
|
+
const ctx = canvas.getContext("2d");
|
|
8631
|
+
if (!ctx) return img.src;
|
|
8632
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
8633
|
+
return canvas.toDataURL("image/jpeg", 0.7);
|
|
8634
|
+
};
|
|
8635
|
+
const applyRollBackground = (dataUrl) => {
|
|
8636
|
+
refs.rollContainer.style.setProperty(
|
|
8637
|
+
"--dtm-roll-bg-image",
|
|
8638
|
+
dataUrl ? `url(${dataUrl})` : "none"
|
|
8639
|
+
);
|
|
8640
|
+
refs.bgRemoveBtn.classList.toggle("dtm-hidden", !dataUrl);
|
|
8641
|
+
setBackgroundActive(!!dataUrl);
|
|
8642
|
+
redrawAll();
|
|
8643
|
+
};
|
|
8504
8644
|
const setupCanvas = () => {
|
|
8505
8645
|
const w = refs.rollContainer.clientWidth || 800;
|
|
8506
8646
|
const h = refs.rollContainer.clientHeight || 450;
|
|
@@ -8866,10 +9006,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
8866
9006
|
<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
9007
|
<button class="dtm-btn dtm-btn--ghost dtm-btn--xs" data-dtm="lyric-custom-guide">\u4F7F\u3044\u65B9\u30AC\u30A4\u30C9</button>
|
|
8868
9008
|
</div>
|
|
8869
|
-
<input type="url" class="dtm-input" data-dtm="lyric-custom-src" placeholder="\u97F3\
|
|
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\
|
|
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">
|
|
9009
|
+
<input type="url" class="dtm-input" data-dtm="lyric-custom-src" placeholder="\u97F3\u58F0URL\uFF08https://\u301C.koe\uFF09" aria-label="\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\uFF08.koe\uFF09\u306EURL">
|
|
9010
|
+
<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\u58F0\u306E\u30A2\u30A4\u30B3\u30F3\u753B\u50CFURL">
|
|
8873
9011
|
<div class="dtm-row">
|
|
8874
9012
|
<span class="dtm-label dtm-grow" data-dtm="lyric-custom-note" style="color:var(--dtm-warn)"></span>
|
|
8875
9013
|
<button class="dtm-btn dtm-btn--primary" data-dtm="lyric-custom-apply">\u8FFD\u52A0</button>
|
|
@@ -8934,12 +9072,6 @@ var mountDAW = (target, options = {}) => {
|
|
|
8934
9072
|
const lyricCustomIcon = lyricDiv.querySelector(
|
|
8935
9073
|
'[data-dtm="lyric-custom-icon"]'
|
|
8936
9074
|
);
|
|
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
9075
|
const lyricCustomGuide = lyricDiv.querySelector(
|
|
8944
9076
|
'[data-dtm="lyric-custom-guide"]'
|
|
8945
9077
|
);
|
|
@@ -8960,7 +9092,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
8960
9092
|
for (const m of BASE_LYRIC_MODELS)
|
|
8961
9093
|
addOpt(m, lyricModelLabel(m, customVocalsMap));
|
|
8962
9094
|
for (const [k, def] of customVocalsMap) addOpt(k, def.label ?? k);
|
|
8963
|
-
addOpt(CUSTOM_VOCAL_ADD_VALUE, "\u30AB\u30B9\u30BF\u30E0\u97F3\
|
|
9095
|
+
addOpt(CUSTOM_VOCAL_ADD_VALUE, "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u3092\u8FFD\u52A0\u2026");
|
|
8964
9096
|
if (active.lyricModel && !BASE_LYRIC_MODELS.includes(active.lyricModel) && !customVocalsMap.has(active.lyricModel)) {
|
|
8965
9097
|
addOpt(
|
|
8966
9098
|
active.lyricModel,
|
|
@@ -9032,9 +9164,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
9032
9164
|
lyricModelSel.addEventListener("change", () => {
|
|
9033
9165
|
if (lyricModelSel.value === CUSTOM_VOCAL_ADD_VALUE) {
|
|
9034
9166
|
lyricCustomNote.textContent = "";
|
|
9035
|
-
lyricCustomAlias.value = "";
|
|
9036
|
-
lyricCustomKey.value = "";
|
|
9037
9167
|
lyricCustom.classList.remove("dtm-hidden");
|
|
9168
|
+
lyricIcon.removeAttribute("src");
|
|
9169
|
+
lyricIcon.classList.add("dtm-hidden");
|
|
9170
|
+
lyricTerms.classList.add("dtm-hidden");
|
|
9038
9171
|
return;
|
|
9039
9172
|
}
|
|
9040
9173
|
lyricCustom.classList.add("dtm-hidden");
|
|
@@ -9044,45 +9177,41 @@ var mountDAW = (target, options = {}) => {
|
|
|
9044
9177
|
fireLyricsChange(active);
|
|
9045
9178
|
});
|
|
9046
9179
|
lyricCustomGuide.addEventListener("click", () => {
|
|
9047
|
-
showModal("\u30AB\u30B9\u30BF\u30E0\u97F3\
|
|
9180
|
+
showModal("\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0(.koe)\u306E\u4F7F\u3044\u65B9", KOE_INFO_HTML);
|
|
9048
9181
|
});
|
|
9049
9182
|
lyricCustomApply.addEventListener("click", () => {
|
|
9050
9183
|
const src = lyricCustomSrc.value.trim();
|
|
9051
9184
|
if (!isValidHttpUrl(src)) {
|
|
9052
|
-
lyricCustomNote.textContent = "\u97F3\
|
|
9185
|
+
lyricCustomNote.textContent = "\u97F3\u58F0URL\u304C\u4E0D\u6B63\u3067\u3059\uFF08http/https\u306E\u307F\u30FB2048\u6587\u5B57\u307E\u3067\uFF09";
|
|
9053
9186
|
return;
|
|
9054
9187
|
}
|
|
9055
9188
|
const iconRaw = lyricCustomIcon.value.trim();
|
|
9056
9189
|
const iconUrl = isValidHttpUrl(iconRaw) ? iconRaw : "";
|
|
9057
|
-
const alias = lyricCustomAlias.value.trim();
|
|
9058
9190
|
const existing = [...customVocalsMap.values()].find(
|
|
9059
9191
|
(d) => d.url === src
|
|
9060
9192
|
);
|
|
9061
|
-
const keyRaw = lyricCustomKey.value.trim().toLowerCase();
|
|
9062
9193
|
let key;
|
|
9063
|
-
if (
|
|
9064
|
-
|
|
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;
|
|
9194
|
+
if (existing) {
|
|
9195
|
+
key = existing.key;
|
|
9078
9196
|
} else {
|
|
9079
|
-
|
|
9197
|
+
const derived = deriveCustomVocalKeyFromUrl(src);
|
|
9198
|
+
if (derived && !BASE_LYRIC_MODELS.includes(derived)) {
|
|
9199
|
+
if (!customVocalsMap.has(derived)) {
|
|
9200
|
+
key = derived;
|
|
9201
|
+
} else {
|
|
9202
|
+
let n = 2;
|
|
9203
|
+
while (customVocalsMap.has(`${derived}${n}`)) n++;
|
|
9204
|
+
key = `${derived}${n}`;
|
|
9205
|
+
}
|
|
9206
|
+
} else {
|
|
9207
|
+
key = genCustomVocalKey();
|
|
9208
|
+
}
|
|
9080
9209
|
}
|
|
9081
9210
|
registerCustomVocal({
|
|
9082
9211
|
key,
|
|
9083
9212
|
iconUrl: iconUrl || (existing?.iconUrl ?? ""),
|
|
9084
9213
|
url: src,
|
|
9085
|
-
label:
|
|
9214
|
+
label: existing?.label
|
|
9086
9215
|
});
|
|
9087
9216
|
active.lyricModel = key;
|
|
9088
9217
|
fireLyricsChange(active);
|
|
@@ -9583,9 +9712,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
9583
9712
|
refs.playBtn.addEventListener("click", togglePlay);
|
|
9584
9713
|
refs.playBtn.disabled = false;
|
|
9585
9714
|
refs.prevBarBtn.addEventListener("click", () => {
|
|
9715
|
+
const barsBack = playbackState === "playing" ? 2 : 1;
|
|
9586
9716
|
const targetStep = Math.max(
|
|
9587
9717
|
0,
|
|
9588
|
-
Math.floor((getCurrentPlayStep() - 1) / renderConfig.stepsPerBar) * renderConfig.stepsPerBar
|
|
9718
|
+
(Math.floor((getCurrentPlayStep() - 1) / renderConfig.stepsPerBar) - barsBack) * renderConfig.stepsPerBar
|
|
9589
9719
|
);
|
|
9590
9720
|
jumpTo(targetStep);
|
|
9591
9721
|
});
|
|
@@ -9630,6 +9760,35 @@ var mountDAW = (target, options = {}) => {
|
|
|
9630
9760
|
applyZoomY();
|
|
9631
9761
|
notifyViewState();
|
|
9632
9762
|
});
|
|
9763
|
+
refs.bgUploadBtn.addEventListener("click", () => refs.bgFileInput.click());
|
|
9764
|
+
refs.bgFileInput.addEventListener("change", () => {
|
|
9765
|
+
const file = refs.bgFileInput.files?.[0];
|
|
9766
|
+
refs.bgFileInput.value = "";
|
|
9767
|
+
if (!file) return;
|
|
9768
|
+
const reader = new FileReader();
|
|
9769
|
+
reader.onload = () => {
|
|
9770
|
+
const img = new Image();
|
|
9771
|
+
img.onload = () => {
|
|
9772
|
+
const maxW = refs.rollContainer.clientWidth || 800;
|
|
9773
|
+
const maxH = refs.rollContainer.clientHeight || 450;
|
|
9774
|
+
const dataUrl = resizeImageToDataUrl(img, maxW, maxH);
|
|
9775
|
+
try {
|
|
9776
|
+
localStorage.setItem(ROLL_BG_STORAGE_KEY, dataUrl);
|
|
9777
|
+
} catch {
|
|
9778
|
+
}
|
|
9779
|
+
applyRollBackground(dataUrl);
|
|
9780
|
+
};
|
|
9781
|
+
img.src = reader.result;
|
|
9782
|
+
};
|
|
9783
|
+
reader.readAsDataURL(file);
|
|
9784
|
+
});
|
|
9785
|
+
refs.bgRemoveBtn.addEventListener("click", () => {
|
|
9786
|
+
try {
|
|
9787
|
+
localStorage.removeItem(ROLL_BG_STORAGE_KEY);
|
|
9788
|
+
} catch {
|
|
9789
|
+
}
|
|
9790
|
+
applyRollBackground(null);
|
|
9791
|
+
});
|
|
9633
9792
|
refs.decomposeChordToggle.addEventListener("change", notifyViewState);
|
|
9634
9793
|
refs.ignoreChordHeavyToggle.addEventListener("change", notifyViewState);
|
|
9635
9794
|
refs.masterVolume.addEventListener("input", () => {
|
|
@@ -9740,6 +9899,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9740
9899
|
};
|
|
9741
9900
|
showModal = (title, bodyHTML) => {
|
|
9742
9901
|
collapseActiveSample();
|
|
9902
|
+
collapseSearchPreview();
|
|
9743
9903
|
refs.modalTitle.textContent = title;
|
|
9744
9904
|
refs.modalBody.innerHTML = bodyHTML;
|
|
9745
9905
|
refs.modalOverlay.removeAttribute("hidden");
|
|
@@ -9828,11 +9988,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
9828
9988
|
};
|
|
9829
9989
|
refs.modalClose.addEventListener("click", () => {
|
|
9830
9990
|
collapseActiveSample();
|
|
9991
|
+
collapseSearchPreview();
|
|
9831
9992
|
refs.modalOverlay.setAttribute("hidden", "");
|
|
9832
9993
|
});
|
|
9833
9994
|
refs.modalOverlay.addEventListener("click", (e) => {
|
|
9834
9995
|
if (e.target === refs.modalOverlay) {
|
|
9835
9996
|
collapseActiveSample();
|
|
9997
|
+
collapseSearchPreview();
|
|
9836
9998
|
refs.modalOverlay.setAttribute("hidden", "");
|
|
9837
9999
|
}
|
|
9838
10000
|
});
|
|
@@ -9853,6 +10015,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9853
10015
|
})
|
|
9854
10016
|
);
|
|
9855
10017
|
if (showMidi) wireMidi();
|
|
10018
|
+
if (showMidiSearch) wireMidiSearch();
|
|
9856
10019
|
document.addEventListener("keydown", onKeyDown);
|
|
9857
10020
|
refs.root.addEventListener("keydown", (e) => {
|
|
9858
10021
|
const t = e.target;
|
|
@@ -9917,6 +10080,236 @@ var mountDAW = (target, options = {}) => {
|
|
|
9917
10080
|
overlayDuring(() => applyMidiSelection(pendingMidi, selected));
|
|
9918
10081
|
});
|
|
9919
10082
|
};
|
|
10083
|
+
const buildPreviewMml = (midi) => {
|
|
10084
|
+
const analysis = analyzeMidiTracks(midi);
|
|
10085
|
+
const selected = analysis.filter((a) => a.selected).map((a) => a.index);
|
|
10086
|
+
const { placements, bpm: parsedBpm } = extractMidiPlacements(
|
|
10087
|
+
midi,
|
|
10088
|
+
selected
|
|
10089
|
+
);
|
|
10090
|
+
const byTrack = /* @__PURE__ */ new Map();
|
|
10091
|
+
for (const p of placements) {
|
|
10092
|
+
if (!byTrack.has(p.trackId)) byTrack.set(p.trackId, []);
|
|
10093
|
+
byTrack.get(p.trackId)?.push(p);
|
|
10094
|
+
}
|
|
10095
|
+
const refCore = trackStates[0].core;
|
|
10096
|
+
const tempo = Math.round(parsedBpm) || bpm;
|
|
10097
|
+
const lines = [];
|
|
10098
|
+
let i = 0;
|
|
10099
|
+
for (const notes of byTrack.values()) {
|
|
10100
|
+
const asNotes = notes.map((p) => ({
|
|
10101
|
+
id: 0,
|
|
10102
|
+
startStep: p.startStep,
|
|
10103
|
+
durationSteps: p.durationSteps,
|
|
10104
|
+
pitch: p.pitch,
|
|
10105
|
+
velocity: p.velocity
|
|
10106
|
+
}));
|
|
10107
|
+
const mml = refCore.getMMLFromNotes(asNotes, tempo, 100).trim();
|
|
10108
|
+
lines.push(`@${i} ${mml}`);
|
|
10109
|
+
i++;
|
|
10110
|
+
}
|
|
10111
|
+
return [...lines, MML_END_MARKER].join(";\n");
|
|
10112
|
+
};
|
|
10113
|
+
const fetchVerifiedMidi = async (fileName) => {
|
|
10114
|
+
if (!options.parseMidi || !midiSearchClient) {
|
|
10115
|
+
throw new Error("parseMidi/midiSearchClient not injected");
|
|
10116
|
+
}
|
|
10117
|
+
const midiBytes = await midiSearchClient.fetchMidi(fileName);
|
|
10118
|
+
const midi = await options.parseMidi(new Uint8Array(midiBytes));
|
|
10119
|
+
const analysis = analyzeMidiTracks(midi);
|
|
10120
|
+
const selected = analysis.filter((a) => a.selected).map((a) => a.index);
|
|
10121
|
+
if (!isPlausibleMidiTranscription(midi, selected)) {
|
|
10122
|
+
throw new Error("implausible MIDI transcription");
|
|
10123
|
+
}
|
|
10124
|
+
return midi;
|
|
10125
|
+
};
|
|
10126
|
+
let searchPreviewPlayer = null;
|
|
10127
|
+
let searchPreviewBtn = null;
|
|
10128
|
+
const collapseSearchPreview = () => {
|
|
10129
|
+
if (searchPreviewPlayer) {
|
|
10130
|
+
searchPreviewPlayer.stop();
|
|
10131
|
+
searchPreviewPlayer.destroy();
|
|
10132
|
+
searchPreviewPlayer = null;
|
|
10133
|
+
}
|
|
10134
|
+
if (searchPreviewBtn) {
|
|
10135
|
+
searchPreviewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
10136
|
+
searchPreviewBtn.classList.remove("dtm-btn--danger");
|
|
10137
|
+
searchPreviewBtn.classList.add("dtm-btn--ghost");
|
|
10138
|
+
const container = searchPreviewBtn.closest(".dtm-modal-sample-box")?.querySelector(".dtm-modal-sample-player-container");
|
|
10139
|
+
if (container) container.innerHTML = "";
|
|
10140
|
+
searchPreviewBtn = null;
|
|
10141
|
+
}
|
|
10142
|
+
};
|
|
10143
|
+
const wireMidiSearch = () => {
|
|
10144
|
+
if (!midiSearchClient) return;
|
|
10145
|
+
refs.midiSearchOpenBtn.addEventListener("click", () => {
|
|
10146
|
+
showModal(
|
|
10147
|
+
"MML\u691C\u7D22",
|
|
10148
|
+
`
|
|
10149
|
+
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
10150
|
+
<input type="text" class="dtm-input dtm-grow" data-dtm="modal-midi-search-input" placeholder="\u66F2\u540D\u3067MML\u691C\u7D22" style="min-width:0">
|
|
10151
|
+
<button class="dtm-btn dtm-btn--primary" data-dtm="modal-midi-search-btn" style="flex-shrink:0">\u691C\u7D22</button>
|
|
10152
|
+
</div>
|
|
10153
|
+
<div class="dtm-row" data-dtm="modal-midi-search-results" style="flex-direction:column;align-items:stretch;gap:4px"></div>
|
|
10154
|
+
`
|
|
10155
|
+
);
|
|
10156
|
+
const input = refs.modalBody.querySelector(
|
|
10157
|
+
'[data-dtm="modal-midi-search-input"]'
|
|
10158
|
+
);
|
|
10159
|
+
const searchBtn = refs.modalBody.querySelector(
|
|
10160
|
+
'[data-dtm="modal-midi-search-btn"]'
|
|
10161
|
+
);
|
|
10162
|
+
const results = refs.modalBody.querySelector(
|
|
10163
|
+
'[data-dtm="modal-midi-search-results"]'
|
|
10164
|
+
);
|
|
10165
|
+
const runSearch = async () => {
|
|
10166
|
+
const q2 = input.value.trim();
|
|
10167
|
+
if (!q2) return;
|
|
10168
|
+
collapseSearchPreview();
|
|
10169
|
+
searchBtn.disabled = true;
|
|
10170
|
+
searchBtn.textContent = "\u691C\u7D22\u4E2D...";
|
|
10171
|
+
results.innerHTML = "";
|
|
10172
|
+
try {
|
|
10173
|
+
const songs = await midiSearchClient.searchSongs({ title: q2 });
|
|
10174
|
+
if (songs.length === 0) {
|
|
10175
|
+
results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
|
|
10176
|
+
return;
|
|
10177
|
+
}
|
|
10178
|
+
for (const song of songs) {
|
|
10179
|
+
const box = document.createElement("div");
|
|
10180
|
+
box.className = "dtm-modal-sample-box";
|
|
10181
|
+
const row = document.createElement("div");
|
|
10182
|
+
row.className = "dtm-row";
|
|
10183
|
+
row.style.cssText = "flex-wrap:nowrap;gap:4px;padding:2px 0";
|
|
10184
|
+
const label = document.createElement("span");
|
|
10185
|
+
label.className = "dtm-label";
|
|
10186
|
+
label.style.cssText = "flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
10187
|
+
label.textContent = `${song.title} - ${song.user}`;
|
|
10188
|
+
const previewBtn = document.createElement("button");
|
|
10189
|
+
previewBtn.className = "dtm-btn dtm-btn--ghost";
|
|
10190
|
+
previewBtn.style.cssText = "flex-shrink:0";
|
|
10191
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
10192
|
+
const loadBtn = document.createElement("button");
|
|
10193
|
+
loadBtn.className = "dtm-btn dtm-btn--primary";
|
|
10194
|
+
loadBtn.style.cssText = "flex-shrink:0";
|
|
10195
|
+
loadBtn.textContent = "\u8AAD\u307F\u8FBC\u307F";
|
|
10196
|
+
const playerContainer = document.createElement("div");
|
|
10197
|
+
playerContainer.className = "dtm-modal-sample-player-container";
|
|
10198
|
+
previewBtn.addEventListener("click", async () => {
|
|
10199
|
+
if (searchPreviewBtn === previewBtn) {
|
|
10200
|
+
if (searchPreviewPlayer?.isPlaying()) {
|
|
10201
|
+
searchPreviewPlayer.stop();
|
|
10202
|
+
} else {
|
|
10203
|
+
stop();
|
|
10204
|
+
previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
10205
|
+
try {
|
|
10206
|
+
const midi = await fetchVerifiedMidi(song.file);
|
|
10207
|
+
const mml = buildPreviewMml(midi);
|
|
10208
|
+
playerContainer.innerHTML = "";
|
|
10209
|
+
const player = mountMmlPlayer(playerContainer, mml, {
|
|
10210
|
+
onPlayNote: options.onPlayNote,
|
|
10211
|
+
onPlayDrum: options.onPlayDrum,
|
|
10212
|
+
onResumeAudio: options.onResumeAudio,
|
|
10213
|
+
getAudioTime: options.getAudioTime,
|
|
10214
|
+
singingVoices: options.singingVoices,
|
|
10215
|
+
drumPatterns: options.drumPatterns,
|
|
10216
|
+
volume: masterVolume,
|
|
10217
|
+
_skipInfoModals: true,
|
|
10218
|
+
onStop: () => {
|
|
10219
|
+
if (searchPreviewBtn === previewBtn) {
|
|
10220
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
10221
|
+
previewBtn.classList.remove("dtm-btn--danger");
|
|
10222
|
+
previewBtn.classList.add("dtm-btn--ghost");
|
|
10223
|
+
}
|
|
10224
|
+
}
|
|
10225
|
+
});
|
|
10226
|
+
searchPreviewPlayer = player;
|
|
10227
|
+
searchPreviewBtn = previewBtn;
|
|
10228
|
+
player.play();
|
|
10229
|
+
previewBtn.textContent = "\u25A0 \u505C\u6B62";
|
|
10230
|
+
previewBtn.classList.remove("dtm-btn--ghost");
|
|
10231
|
+
previewBtn.classList.add("dtm-btn--danger");
|
|
10232
|
+
} catch (e) {
|
|
10233
|
+
console.error("[dtm] MIDI preview failed", e);
|
|
10234
|
+
previewBtn.textContent = "\u5931\u6557";
|
|
10235
|
+
}
|
|
10236
|
+
}
|
|
10237
|
+
} else {
|
|
10238
|
+
collapseSearchPreview();
|
|
10239
|
+
stop();
|
|
10240
|
+
previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
10241
|
+
try {
|
|
10242
|
+
const midi = await fetchVerifiedMidi(song.file);
|
|
10243
|
+
const mml = buildPreviewMml(midi);
|
|
10244
|
+
playerContainer.innerHTML = "";
|
|
10245
|
+
const player = mountMmlPlayer(playerContainer, mml, {
|
|
10246
|
+
onPlayNote: options.onPlayNote,
|
|
10247
|
+
onPlayDrum: options.onPlayDrum,
|
|
10248
|
+
onResumeAudio: options.onResumeAudio,
|
|
10249
|
+
getAudioTime: options.getAudioTime,
|
|
10250
|
+
singingVoices: options.singingVoices,
|
|
10251
|
+
drumPatterns: options.drumPatterns,
|
|
10252
|
+
volume: masterVolume,
|
|
10253
|
+
_skipInfoModals: true,
|
|
10254
|
+
onStop: () => {
|
|
10255
|
+
if (searchPreviewBtn === previewBtn) {
|
|
10256
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
10257
|
+
previewBtn.classList.remove("dtm-btn--danger");
|
|
10258
|
+
previewBtn.classList.add("dtm-btn--ghost");
|
|
10259
|
+
}
|
|
10260
|
+
}
|
|
10261
|
+
});
|
|
10262
|
+
searchPreviewPlayer = player;
|
|
10263
|
+
searchPreviewBtn = previewBtn;
|
|
10264
|
+
player.play();
|
|
10265
|
+
previewBtn.textContent = "\u25A0 \u505C\u6B62";
|
|
10266
|
+
previewBtn.classList.remove("dtm-btn--ghost");
|
|
10267
|
+
previewBtn.classList.add("dtm-btn--danger");
|
|
10268
|
+
} catch (e) {
|
|
10269
|
+
console.error("[dtm] MIDI preview failed", e);
|
|
10270
|
+
previewBtn.textContent = "\u5931\u6557";
|
|
10271
|
+
}
|
|
10272
|
+
}
|
|
10273
|
+
});
|
|
10274
|
+
loadBtn.addEventListener("click", async () => {
|
|
10275
|
+
loadBtn.disabled = true;
|
|
10276
|
+
loadBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
10277
|
+
try {
|
|
10278
|
+
const pending = await fetchVerifiedMidi(song.file);
|
|
10279
|
+
const analysis = analyzeMidiTracks(pending);
|
|
10280
|
+
const sel = analysis.filter((a) => a.selected).map((a) => a.index);
|
|
10281
|
+
collapseSearchPreview();
|
|
10282
|
+
overlayDuring(() => applyMidiSelection(pending, sel));
|
|
10283
|
+
refs.modalOverlay.setAttribute("hidden", "");
|
|
10284
|
+
} catch (e) {
|
|
10285
|
+
console.error("[dtm] MIDI fetch failed", e);
|
|
10286
|
+
loadBtn.textContent = "\u5931\u6557";
|
|
10287
|
+
} finally {
|
|
10288
|
+
loadBtn.disabled = false;
|
|
10289
|
+
}
|
|
10290
|
+
});
|
|
10291
|
+
row.appendChild(label);
|
|
10292
|
+
row.appendChild(previewBtn);
|
|
10293
|
+
row.appendChild(loadBtn);
|
|
10294
|
+
box.appendChild(row);
|
|
10295
|
+
box.appendChild(playerContainer);
|
|
10296
|
+
results.appendChild(box);
|
|
10297
|
+
}
|
|
10298
|
+
} catch (e) {
|
|
10299
|
+
console.error("[dtm] MIDI search failed", e);
|
|
10300
|
+
results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F</span>';
|
|
10301
|
+
} finally {
|
|
10302
|
+
searchBtn.disabled = false;
|
|
10303
|
+
searchBtn.textContent = "\u691C\u7D22";
|
|
10304
|
+
}
|
|
10305
|
+
};
|
|
10306
|
+
searchBtn.addEventListener("click", () => void runSearch());
|
|
10307
|
+
input.addEventListener("keydown", (e) => {
|
|
10308
|
+
if (e.key === "Enter") void runSearch();
|
|
10309
|
+
});
|
|
10310
|
+
input.focus();
|
|
10311
|
+
});
|
|
10312
|
+
};
|
|
9920
10313
|
const onKeyDown = (e) => {
|
|
9921
10314
|
if (!(e.ctrlKey || e.metaKey)) return;
|
|
9922
10315
|
if (e.code === "KeyZ" && !e.shiftKey) {
|
|
@@ -9972,6 +10365,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
9972
10365
|
updateUndoRedo();
|
|
9973
10366
|
redrawAll();
|
|
9974
10367
|
if (options.initialMML) loadMML(options.initialMML);
|
|
10368
|
+
try {
|
|
10369
|
+
const storedBg = localStorage.getItem(ROLL_BG_STORAGE_KEY);
|
|
10370
|
+
if (storedBg) applyRollBackground(storedBg);
|
|
10371
|
+
} catch {
|
|
10372
|
+
}
|
|
9975
10373
|
let resizeTimer = null;
|
|
9976
10374
|
const resizeObserver = new ResizeObserver(() => {
|
|
9977
10375
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
@@ -11293,13 +11691,17 @@ var createDtmStudio = async (options = {}) => {
|
|
|
11293
11691
|
const mountedPlayers = [];
|
|
11294
11692
|
const mountedModeSwitches = [];
|
|
11295
11693
|
const mountEditor = (target, opts = {}) => {
|
|
11694
|
+
const mergedOpts = {
|
|
11695
|
+
midiSearch: options.midiSearch,
|
|
11696
|
+
...opts
|
|
11697
|
+
};
|
|
11296
11698
|
const {
|
|
11297
11699
|
preset,
|
|
11298
11700
|
presetUI,
|
|
11299
11701
|
onInstrumentChange,
|
|
11300
11702
|
onTrackInstrumentChange: externalOnTrackInstrumentChange,
|
|
11301
11703
|
...dawOverrides
|
|
11302
|
-
} =
|
|
11704
|
+
} = mergedOpts;
|
|
11303
11705
|
const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
|
|
11304
11706
|
const trackIds = tracks.map((t) => t.id);
|
|
11305
11707
|
const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
|
|
@@ -11635,6 +12037,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
11635
12037
|
MAX_VOCAL_VOLUME,
|
|
11636
12038
|
MMLCore,
|
|
11637
12039
|
MML_END_MARKER,
|
|
12040
|
+
MidiSearchClient,
|
|
11638
12041
|
PITCH_MAP,
|
|
11639
12042
|
PREWARM_NOTES,
|
|
11640
12043
|
TRACKS_ADVANCED,
|
|
@@ -11684,6 +12087,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
11684
12087
|
init,
|
|
11685
12088
|
injectStyles,
|
|
11686
12089
|
isChordHeavyTrack,
|
|
12090
|
+
isPlausibleMidiTranscription,
|
|
11687
12091
|
isValidHttpUrl,
|
|
11688
12092
|
koeUrl,
|
|
11689
12093
|
mountDAW,
|
|
@@ -11697,6 +12101,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
11697
12101
|
parseMmlMeta,
|
|
11698
12102
|
playMML,
|
|
11699
12103
|
playSingingMML,
|
|
12104
|
+
setBackgroundActive,
|
|
11700
12105
|
setDrawOffset,
|
|
11701
12106
|
shiftNotes,
|
|
11702
12107
|
showLoadingOverlay,
|