@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.mjs
CHANGED
|
@@ -1215,7 +1215,13 @@ var icon = (name, size = 20) => {
|
|
|
1215
1215
|
// src/daw-ui.ts
|
|
1216
1216
|
var q = (root, sel) => root.querySelector(sel);
|
|
1217
1217
|
var buildUI = (target, options) => {
|
|
1218
|
-
const {
|
|
1218
|
+
const {
|
|
1219
|
+
drumPatternNames,
|
|
1220
|
+
defaultDrumPattern,
|
|
1221
|
+
defaultBpm,
|
|
1222
|
+
showMidi,
|
|
1223
|
+
showMidiSearch
|
|
1224
|
+
} = options;
|
|
1219
1225
|
const drumOptions = [`<option value="none">\u306A\u3057</option>`].concat(
|
|
1220
1226
|
drumPatternNames.map(
|
|
1221
1227
|
(name) => `<option value="${name}" ${name === defaultDrumPattern ? "selected" : ""}>${name}</option>`
|
|
@@ -1293,6 +1299,12 @@ var buildUI = (target, options) => {
|
|
|
1293
1299
|
<span class="dtm-label" data-dtm="zoomy-label">100%</span>
|
|
1294
1300
|
<button class="dtm-iconbtn" data-dtm="zoomy-in" title="\u62E1\u5927">\uFF0B</button>
|
|
1295
1301
|
</div>
|
|
1302
|
+
<div class="dtm-row">
|
|
1303
|
+
<span class="dtm-label">\u80CC\u666F\u753B\u50CF</span>
|
|
1304
|
+
<input type="file" accept="image/*" data-dtm="bg-file-input" class="dtm-hidden">
|
|
1305
|
+
<button class="dtm-btn dtm-btn--primary" data-dtm="bg-upload">\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9</button>
|
|
1306
|
+
<button class="dtm-btn dtm-btn--danger dtm-hidden" data-dtm="bg-remove">\u524A\u9664</button>
|
|
1307
|
+
</div>
|
|
1296
1308
|
</div>
|
|
1297
1309
|
</details>
|
|
1298
1310
|
|
|
@@ -1331,6 +1343,9 @@ var buildUI = (target, options) => {
|
|
|
1331
1343
|
<textarea class="dtm-textarea dtm-grow" data-dtm="mml-input" placeholder="MML\u3092\u5165\u529B"></textarea>
|
|
1332
1344
|
<button class="dtm-btn dtm-btn--primary" data-dtm="mml-load" style="flex-shrink:0">\u8AAD\u8FBC</button>
|
|
1333
1345
|
</div>
|
|
1346
|
+
<div class="dtm-row" data-dtm="midi-search-row" style="justify-content:flex-end;${showMidiSearch ? "" : "display:none"}">
|
|
1347
|
+
<button class="dtm-btn dtm-btn--primary" data-dtm="midi-search-open">MML\u691C\u7D22</button>
|
|
1348
|
+
</div>
|
|
1334
1349
|
<p class="dtm-load-note dtm-hidden" data-dtm="mml-load-note"></p>
|
|
1335
1350
|
</div>
|
|
1336
1351
|
</details>
|
|
@@ -1441,6 +1456,9 @@ var buildUI = (target, options) => {
|
|
|
1441
1456
|
zoomXOut: sel("zoomx-out"),
|
|
1442
1457
|
zoomYIn: sel("zoomy-in"),
|
|
1443
1458
|
zoomYOut: sel("zoomy-out"),
|
|
1459
|
+
bgFileInput: sel("bg-file-input"),
|
|
1460
|
+
bgUploadBtn: sel("bg-upload"),
|
|
1461
|
+
bgRemoveBtn: sel("bg-remove"),
|
|
1444
1462
|
rollContainer: sel("roll"),
|
|
1445
1463
|
wrapper: sel("wrapper"),
|
|
1446
1464
|
vScroll: sel("vscroll"),
|
|
@@ -1459,6 +1477,7 @@ var buildUI = (target, options) => {
|
|
|
1459
1477
|
midiInfoBtn: sel("midi-info"),
|
|
1460
1478
|
midiTrackSelection: sel("midi-track-selection"),
|
|
1461
1479
|
midiPanel: sel("midi-panel"),
|
|
1480
|
+
midiSearchOpenBtn: sel("midi-search-open"),
|
|
1462
1481
|
mmlInput: sel("mml-input"),
|
|
1463
1482
|
mmlLoadBtn: sel("mml-load"),
|
|
1464
1483
|
mmlLoadNote: sel("mml-load-note"),
|
|
@@ -2503,8 +2522,9 @@ var KOE_VOICEBANKS = {
|
|
|
2503
2522
|
teto: "\u91CD\u97F3\u30C6\u30C8\u5358\u72EC\u97F3.koe",
|
|
2504
2523
|
shiyo: "\u9769\u547D\u30B7\u30E8.koe",
|
|
2505
2524
|
rei: "\u8DB3\u7ACB\u30EC\u30A4ver3.5.0.koe",
|
|
2506
|
-
|
|
2507
|
-
|
|
2525
|
+
mgroid: "MGRoid_\u539F\u97F3\u8A2D\u5B9A\u6E08\u307F.koe",
|
|
2526
|
+
motroid: "MOTRoid\u5B8C\u5168\u7248V2.koe",
|
|
2527
|
+
nynroid: "NYNRoidver1.4.koe"
|
|
2508
2528
|
};
|
|
2509
2529
|
var KOE_VOICEBANK_LABELS = {
|
|
2510
2530
|
tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093",
|
|
@@ -2515,8 +2535,9 @@ var KOE_VOICEBANK_LABELS = {
|
|
|
2515
2535
|
teto: "\u91CD\u97F3\u30C6\u30C8",
|
|
2516
2536
|
shiyo: "\u9769\u547D\u30B7\u30E8",
|
|
2517
2537
|
rei: "\u8DB3\u7ACB\u30EC\u30A4",
|
|
2518
|
-
|
|
2519
|
-
|
|
2538
|
+
mgroid: "MGRoid",
|
|
2539
|
+
motroid: "MOTRoid",
|
|
2540
|
+
nynroid: "NYNRoid"
|
|
2520
2541
|
};
|
|
2521
2542
|
var VOICE_IMAGE_KEY = {
|
|
2522
2543
|
klatt: "puyuyu",
|
|
@@ -2528,8 +2549,9 @@ var VOICE_IMAGE_KEY = {
|
|
|
2528
2549
|
teto: "teto",
|
|
2529
2550
|
shiyo: "shiyo",
|
|
2530
2551
|
rei: "rei",
|
|
2531
|
-
|
|
2532
|
-
|
|
2552
|
+
mgroid: "MGRoid",
|
|
2553
|
+
motroid: "MOTRoid",
|
|
2554
|
+
nynroid: "NYNRoid"
|
|
2533
2555
|
};
|
|
2534
2556
|
var KOE_VOICEBANK_TERMS = {
|
|
2535
2557
|
tsukuyomi: "https://tyc.rei-yumesaki.net/material/utau/terms/",
|
|
@@ -2540,8 +2562,9 @@ var KOE_VOICEBANK_TERMS = {
|
|
|
2540
2562
|
teto: "https://kasaneteto.jp/guidelines/voice.html",
|
|
2541
2563
|
shiyo: "https://kakumeisiyo.my.canva.site/dagkuyjwycs",
|
|
2542
2564
|
rei: "https://mechanicalgirl.jp/guidelines/",
|
|
2543
|
-
|
|
2544
|
-
|
|
2565
|
+
mgroid: "https://x.com/nisusansu/status/1048825378188353536",
|
|
2566
|
+
motroid: "https://www.nicovideo.jp/watch/sm40031282",
|
|
2567
|
+
nynroid: "https://www.bilibili.com/video/BV1V24y1a7qs"
|
|
2545
2568
|
};
|
|
2546
2569
|
var koeUrl = (name, base = KOE_BASE_URL) => `${base}/${encodeURIComponent(name)}`;
|
|
2547
2570
|
var DEFAULT_WORLDLINE_SCRIPT = "https://onjmin.github.io/koe/demo/world/worldline.js";
|
|
@@ -3365,6 +3388,19 @@ var extractMidiPlacements = (midi, selectedTrackIndices) => {
|
|
|
3365
3388
|
}
|
|
3366
3389
|
return { placements, bpm };
|
|
3367
3390
|
};
|
|
3391
|
+
var isPlausibleMidiTranscription = (midi, selectedIndices) => {
|
|
3392
|
+
const { placements } = extractMidiPlacements(midi, selectedIndices);
|
|
3393
|
+
if (placements.length < 4) return false;
|
|
3394
|
+
const shortCount = placements.filter((p) => p.durationSteps <= 1).length;
|
|
3395
|
+
const shortRatio = shortCount / placements.length;
|
|
3396
|
+
const countByStep = /* @__PURE__ */ new Map();
|
|
3397
|
+
for (const p of placements) {
|
|
3398
|
+
countByStep.set(p.startStep, (countByStep.get(p.startStep) ?? 0) + 1);
|
|
3399
|
+
}
|
|
3400
|
+
const denseSteps = [...countByStep.values()].filter((n) => n >= 6).length;
|
|
3401
|
+
const denseRatio = denseSteps / countByStep.size;
|
|
3402
|
+
return shortRatio < 0.6 && denseRatio < 0.35;
|
|
3403
|
+
};
|
|
3368
3404
|
var extractMidiPlacementsByTrack = (midi, selectedIndices, trackIds) => {
|
|
3369
3405
|
const { tracks, division } = midi;
|
|
3370
3406
|
const ticksPerBeat = division;
|
|
@@ -3517,6 +3553,45 @@ var exportMIDI = (options) => {
|
|
|
3517
3553
|
return new Blob([new Uint8Array(arr).buffer], { type: "audio/midi" });
|
|
3518
3554
|
};
|
|
3519
3555
|
|
|
3556
|
+
// src/midi-search.ts
|
|
3557
|
+
var MidiSearchClient = class {
|
|
3558
|
+
config;
|
|
3559
|
+
constructor(config = {}) {
|
|
3560
|
+
this.config = config;
|
|
3561
|
+
}
|
|
3562
|
+
get enabled() {
|
|
3563
|
+
return !!this.config.apiKey;
|
|
3564
|
+
}
|
|
3565
|
+
get baseUrl() {
|
|
3566
|
+
return this.config.baseUrl ?? "https://rpgen-search.pages.dev/api";
|
|
3567
|
+
}
|
|
3568
|
+
headers() {
|
|
3569
|
+
const h = {};
|
|
3570
|
+
if (this.config.apiKey) {
|
|
3571
|
+
h.Authorization = `Bearer ${this.config.apiKey}`;
|
|
3572
|
+
}
|
|
3573
|
+
return h;
|
|
3574
|
+
}
|
|
3575
|
+
async searchSongs(params) {
|
|
3576
|
+
if (!this.enabled) return [];
|
|
3577
|
+
const qs = new URLSearchParams();
|
|
3578
|
+
if (params.title) qs.set("title", params.title);
|
|
3579
|
+
if (params.user) qs.set("user", params.user);
|
|
3580
|
+
if (params.twitter_id) qs.set("twitter_id", params.twitter_id);
|
|
3581
|
+
const url2 = `${this.baseUrl}/picotune/songs${qs.toString() ? `?${qs.toString()}` : ""}`;
|
|
3582
|
+
const res = await fetch(url2, { headers: this.headers() });
|
|
3583
|
+
if (!res.ok) throw new Error(`picotune search failed: ${res.status}`);
|
|
3584
|
+
const body = await res.json();
|
|
3585
|
+
return body.data ?? body.songs ?? body;
|
|
3586
|
+
}
|
|
3587
|
+
async fetchMidi(fileName) {
|
|
3588
|
+
const url2 = `${this.baseUrl}/picotune/songs/${encodeURIComponent(fileName)}`;
|
|
3589
|
+
const res = await fetch(url2, { headers: this.headers() });
|
|
3590
|
+
if (!res.ok) throw new Error(`picotune fetch failed: ${res.status}`);
|
|
3591
|
+
return res.arrayBuffer();
|
|
3592
|
+
}
|
|
3593
|
+
};
|
|
3594
|
+
|
|
3520
3595
|
// src/linked-list.ts
|
|
3521
3596
|
var LinkedList = class {
|
|
3522
3597
|
#cursor;
|
|
@@ -3582,6 +3657,10 @@ var HEADER_HEIGHT = 20;
|
|
|
3582
3657
|
var getRenderConfig = () => g_config;
|
|
3583
3658
|
var g_draw_offset_x = 0;
|
|
3584
3659
|
var g_draw_offset_y = 0;
|
|
3660
|
+
var g_bg_active = false;
|
|
3661
|
+
var setBackgroundActive = (active) => {
|
|
3662
|
+
g_bg_active = active;
|
|
3663
|
+
};
|
|
3585
3664
|
var getDrawOffset = () => ({
|
|
3586
3665
|
x: g_draw_offset_x,
|
|
3587
3666
|
y: g_draw_offset_y
|
|
@@ -3731,7 +3810,7 @@ var drawHeader = () => {
|
|
|
3731
3810
|
const { stepWidth, stepsPerBar } = g_config;
|
|
3732
3811
|
g_header_ctx.save();
|
|
3733
3812
|
g_header_ctx.translate(-g_draw_offset_x, 0);
|
|
3734
|
-
g_header_ctx.fillStyle = "#0a0f1f";
|
|
3813
|
+
g_header_ctx.fillStyle = g_bg_active ? "rgba(10,15,31,0.55)" : "#0a0f1f";
|
|
3735
3814
|
g_header_ctx.fillRect(
|
|
3736
3815
|
g_draw_offset_x,
|
|
3737
3816
|
0,
|
|
@@ -3777,7 +3856,7 @@ var drawGrid = (noteLengthSteps = 1) => {
|
|
|
3777
3856
|
const octave = Math.floor(totalPitch / 12) - 1;
|
|
3778
3857
|
const isC4Range = octave === 4;
|
|
3779
3858
|
const screenY = y - g_draw_offset_y;
|
|
3780
|
-
g_grid_ctx.fillStyle = isBlackKey ? "#080b16" : "#111628";
|
|
3859
|
+
g_grid_ctx.fillStyle = g_bg_active ? isBlackKey ? "rgba(8,11,22,0.55)" : "rgba(17,22,40,0.45)" : isBlackKey ? "#080b16" : "#111628";
|
|
3781
3860
|
g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
|
|
3782
3861
|
if (isC4Range) {
|
|
3783
3862
|
g_grid_ctx.fillStyle = "rgba(41,173,255,0.05)";
|
|
@@ -5349,6 +5428,18 @@ var DAW_CSS = `
|
|
|
5349
5428
|
4px 4px 0 var(--c-black);
|
|
5350
5429
|
overflow: hidden;
|
|
5351
5430
|
}
|
|
5431
|
+
/* \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 */
|
|
5432
|
+
.dtm-roll::before {
|
|
5433
|
+
content: '';
|
|
5434
|
+
position: absolute;
|
|
5435
|
+
inset: 0;
|
|
5436
|
+
background-image: var(--dtm-roll-bg-image, none);
|
|
5437
|
+
background-size: cover;
|
|
5438
|
+
background-position: center;
|
|
5439
|
+
background-repeat: no-repeat;
|
|
5440
|
+
opacity: 0.4;
|
|
5441
|
+
pointer-events: none;
|
|
5442
|
+
}
|
|
5352
5443
|
.dtm-vscroll {
|
|
5353
5444
|
position: relative;
|
|
5354
5445
|
width: 20px;
|
|
@@ -5721,9 +5812,14 @@ var DAW_CSS = `
|
|
|
5721
5812
|
.dtm-modal-body {
|
|
5722
5813
|
padding: 12px;
|
|
5723
5814
|
overflow-y: auto;
|
|
5815
|
+
overflow-x: hidden;
|
|
5724
5816
|
font-size: 13px;
|
|
5725
5817
|
line-height: 1.6;
|
|
5726
5818
|
}
|
|
5819
|
+
.dtm-modal-body * {
|
|
5820
|
+
max-width: 100%;
|
|
5821
|
+
box-sizing: border-box;
|
|
5822
|
+
}
|
|
5727
5823
|
.dtm-modal-body a {
|
|
5728
5824
|
color: var(--dtm-primary);
|
|
5729
5825
|
text-decoration: underline;
|
|
@@ -6326,6 +6422,9 @@ var MGRoid_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA
|
|
|
6326
6422
|
// assets/MOTRoid.png
|
|
6327
6423
|
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";
|
|
6328
6424
|
|
|
6425
|
+
// assets/NYNRoid.png
|
|
6426
|
+
var NYNRoid_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABo0lEQVR42tWXoU8DMRTGf7csCAQBOcz4A6aOIOZAETKLBsQGCCwhiIWQCUKCRDAyAWgcWQgoPOHU/gBmOAkJggRTRPeSa7m7lSHWPdNc23u97+v3vvYCrCiVSir5HMdxwAjhmqfAmCPI+uIURKnPcRxjIc1d0GZi7AwU7Y63qyMA5reODaR268qEnc9fDdhaaDabAERRZLRhGDollnmtVsvvKihmqb3b7QJQq9WcGOh0OgDU63Xj/SyN+KeBMAxV2h4K8peTbQAWDy9HGpeIoiiYDA3Y6l9Z29NI788NpII8WN0FYLmgU0ZDfMJ/BiSU0tI4m57RHZWqRn5XzZ33ZDmn/wwk1KmSKg4C3a0eLvIzPbYB2P/61H7wW0ue3wdSvFxZ9Wvu+YARUb/tH1l1PzkMiBYkxOMldpZuAWg/r6eeCa5r+s+A3AuknjcrNwDMvZ4C8L5wAMB1b8Ood7kHpGjFfwZU0tkajYaxt6Ly3tSsNsbvj/QqGbwvPpLFtscMWHtnnxGuMezPyv8q+AdylzX8ZsDJIfv9vjFYLpf/lHvsDPwA5Wy6PgZtokAAAAAASUVORK5CYII=";
|
|
6427
|
+
|
|
6329
6428
|
// assets/puyuyu.png
|
|
6330
6429
|
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=";
|
|
6331
6430
|
|
|
@@ -6361,7 +6460,8 @@ var VOICE_IMAGES = {
|
|
|
6361
6460
|
tsukuyomi: tsukuyomi_default,
|
|
6362
6461
|
rei: rei_default,
|
|
6363
6462
|
MGRoid: MGRoid_default,
|
|
6364
|
-
MOTRoid: MOTRoid_default
|
|
6463
|
+
MOTRoid: MOTRoid_default,
|
|
6464
|
+
NYNRoid: NYNRoid_default
|
|
6365
6465
|
};
|
|
6366
6466
|
var FALLBACK_VOCAL_ICON = Chip_default;
|
|
6367
6467
|
|
|
@@ -7594,12 +7694,12 @@ var KOE_INFO_HTML = `
|
|
|
7594
7694
|
|
|
7595
7695
|
<h4>3. DTM\u3067\u4F7F\u3046</h4>
|
|
7596
7696
|
<ul>
|
|
7597
|
-
<li>\u6B4C\u5531\u30E2\u30C7\u30EB\u306E\u30D7\u30EB\u30C0\u30A6\u30F3\u304B\u3089\u300C\u30AB\u30B9\u30BF\u30E0\u97F3\
|
|
7598
|
-
<li>\u300C\u97F3\
|
|
7599
|
-
<li>\u30A2\u30A4\u30B3\u30F3\u753B\
|
|
7697
|
+
<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>
|
|
7698
|
+
<li>\u300C\u97F3\u58F0URL\u300D\u306B\u624B\u98062\u306E\u76F4\u63A5\u30EA\u30F3\u30AF\u3092\u8CBC\u308A\u4ED8\u3051\u307E\u3059\u3002</li>
|
|
7699
|
+
<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>
|
|
7600
7700
|
<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>
|
|
7601
7701
|
</ul>
|
|
7602
|
-
<p style="margin-top:4px;"><small>\
|
|
7702
|
+
<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>
|
|
7603
7703
|
</div>
|
|
7604
7704
|
`;
|
|
7605
7705
|
var BASE_STEP_WIDTH = 0.5;
|
|
@@ -7750,6 +7850,18 @@ var BASE_LYRIC_MODEL_LABELS = {
|
|
|
7750
7850
|
var lyricModelLabel = (model, customMap) => BASE_LYRIC_MODEL_LABELS[model] ?? customMap.get(model)?.label ?? model;
|
|
7751
7851
|
var CUSTOM_VOCAL_ADD_VALUE = "+custom";
|
|
7752
7852
|
var CUSTOM_VOCAL_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
7853
|
+
var deriveCustomVocalKeyFromUrl = (url2) => {
|
|
7854
|
+
let name;
|
|
7855
|
+
try {
|
|
7856
|
+
const path = new URL(url2).pathname;
|
|
7857
|
+
name = decodeURIComponent(path.slice(path.lastIndexOf("/") + 1));
|
|
7858
|
+
} catch {
|
|
7859
|
+
return "";
|
|
7860
|
+
}
|
|
7861
|
+
name = name.replace(/\.[^.]*$/, "").normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
7862
|
+
if (name && /^[0-9]/.test(name)) name = `_${name}`;
|
|
7863
|
+
return CUSTOM_VOCAL_KEY_RE.test(name) ? name : "";
|
|
7864
|
+
};
|
|
7753
7865
|
var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
|
|
7754
7866
|
var normalizeInstrumentName = (name) => {
|
|
7755
7867
|
if (!name) return "";
|
|
@@ -7767,13 +7879,16 @@ var mountDAW = (target, options = {}) => {
|
|
|
7767
7879
|
const drumPatterns = options.drumPatterns ?? DRUM_PATTERNS;
|
|
7768
7880
|
const showMidi = !!options.parseMidi;
|
|
7769
7881
|
const showChord = !isAdvanced;
|
|
7882
|
+
const midiSearchClient = options.midiSearch ? new MidiSearchClient(options.midiSearch) : void 0;
|
|
7883
|
+
const showMidiSearch = !!midiSearchClient?.enabled;
|
|
7770
7884
|
const refs = buildUI(target, {
|
|
7771
7885
|
tracks: trackConfigs,
|
|
7772
7886
|
drumPatternNames: Object.keys(drumPatterns),
|
|
7773
7887
|
defaultDrumPattern: drumPatterns.dance ? "dance" : Object.keys(drumPatterns)[0] ?? "none",
|
|
7774
7888
|
defaultBpm: options.defaultBpm ?? DEFAULT_BPM,
|
|
7775
7889
|
showMidi,
|
|
7776
|
-
showChord
|
|
7890
|
+
showChord,
|
|
7891
|
+
showMidiSearch
|
|
7777
7892
|
});
|
|
7778
7893
|
const renderConfig = {
|
|
7779
7894
|
stepsPerBar: 192,
|
|
@@ -8386,6 +8501,28 @@ var mountDAW = (target, options = {}) => {
|
|
|
8386
8501
|
redrawAll();
|
|
8387
8502
|
}
|
|
8388
8503
|
};
|
|
8504
|
+
const ROLL_BG_STORAGE_KEY = "dtm-piano-roll-bg";
|
|
8505
|
+
const resizeImageToDataUrl = (img, maxW, maxH) => {
|
|
8506
|
+
const scale = Math.min(1, maxW / img.width, maxH / img.height);
|
|
8507
|
+
const width = Math.max(1, Math.round(img.width * scale));
|
|
8508
|
+
const height = Math.max(1, Math.round(img.height * scale));
|
|
8509
|
+
const canvas = document.createElement("canvas");
|
|
8510
|
+
canvas.width = width;
|
|
8511
|
+
canvas.height = height;
|
|
8512
|
+
const ctx = canvas.getContext("2d");
|
|
8513
|
+
if (!ctx) return img.src;
|
|
8514
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
8515
|
+
return canvas.toDataURL("image/jpeg", 0.7);
|
|
8516
|
+
};
|
|
8517
|
+
const applyRollBackground = (dataUrl) => {
|
|
8518
|
+
refs.rollContainer.style.setProperty(
|
|
8519
|
+
"--dtm-roll-bg-image",
|
|
8520
|
+
dataUrl ? `url(${dataUrl})` : "none"
|
|
8521
|
+
);
|
|
8522
|
+
refs.bgRemoveBtn.classList.toggle("dtm-hidden", !dataUrl);
|
|
8523
|
+
setBackgroundActive(!!dataUrl);
|
|
8524
|
+
redrawAll();
|
|
8525
|
+
};
|
|
8389
8526
|
const setupCanvas = () => {
|
|
8390
8527
|
const w = refs.rollContainer.clientWidth || 800;
|
|
8391
8528
|
const h = refs.rollContainer.clientHeight || 450;
|
|
@@ -8751,10 +8888,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
8751
8888
|
<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>
|
|
8752
8889
|
<button class="dtm-btn dtm-btn--ghost dtm-btn--xs" data-dtm="lyric-custom-guide">\u4F7F\u3044\u65B9\u30AC\u30A4\u30C9</button>
|
|
8753
8890
|
</div>
|
|
8754
|
-
<input type="url" class="dtm-input" data-dtm="lyric-custom-src" placeholder="\u97F3\
|
|
8755
|
-
<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\
|
|
8756
|
-
<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">
|
|
8757
|
-
<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">
|
|
8891
|
+
<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">
|
|
8892
|
+
<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">
|
|
8758
8893
|
<div class="dtm-row">
|
|
8759
8894
|
<span class="dtm-label dtm-grow" data-dtm="lyric-custom-note" style="color:var(--dtm-warn)"></span>
|
|
8760
8895
|
<button class="dtm-btn dtm-btn--primary" data-dtm="lyric-custom-apply">\u8FFD\u52A0</button>
|
|
@@ -8819,12 +8954,6 @@ var mountDAW = (target, options = {}) => {
|
|
|
8819
8954
|
const lyricCustomIcon = lyricDiv.querySelector(
|
|
8820
8955
|
'[data-dtm="lyric-custom-icon"]'
|
|
8821
8956
|
);
|
|
8822
|
-
const lyricCustomAlias = lyricDiv.querySelector(
|
|
8823
|
-
'[data-dtm="lyric-custom-alias"]'
|
|
8824
|
-
);
|
|
8825
|
-
const lyricCustomKey = lyricDiv.querySelector(
|
|
8826
|
-
'[data-dtm="lyric-custom-key"]'
|
|
8827
|
-
);
|
|
8828
8957
|
const lyricCustomGuide = lyricDiv.querySelector(
|
|
8829
8958
|
'[data-dtm="lyric-custom-guide"]'
|
|
8830
8959
|
);
|
|
@@ -8845,7 +8974,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
8845
8974
|
for (const m of BASE_LYRIC_MODELS)
|
|
8846
8975
|
addOpt(m, lyricModelLabel(m, customVocalsMap));
|
|
8847
8976
|
for (const [k, def] of customVocalsMap) addOpt(k, def.label ?? k);
|
|
8848
|
-
addOpt(CUSTOM_VOCAL_ADD_VALUE, "\u30AB\u30B9\u30BF\u30E0\u97F3\
|
|
8977
|
+
addOpt(CUSTOM_VOCAL_ADD_VALUE, "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u3092\u8FFD\u52A0\u2026");
|
|
8849
8978
|
if (active.lyricModel && !BASE_LYRIC_MODELS.includes(active.lyricModel) && !customVocalsMap.has(active.lyricModel)) {
|
|
8850
8979
|
addOpt(
|
|
8851
8980
|
active.lyricModel,
|
|
@@ -8917,9 +9046,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
8917
9046
|
lyricModelSel.addEventListener("change", () => {
|
|
8918
9047
|
if (lyricModelSel.value === CUSTOM_VOCAL_ADD_VALUE) {
|
|
8919
9048
|
lyricCustomNote.textContent = "";
|
|
8920
|
-
lyricCustomAlias.value = "";
|
|
8921
|
-
lyricCustomKey.value = "";
|
|
8922
9049
|
lyricCustom.classList.remove("dtm-hidden");
|
|
9050
|
+
lyricIcon.removeAttribute("src");
|
|
9051
|
+
lyricIcon.classList.add("dtm-hidden");
|
|
9052
|
+
lyricTerms.classList.add("dtm-hidden");
|
|
8923
9053
|
return;
|
|
8924
9054
|
}
|
|
8925
9055
|
lyricCustom.classList.add("dtm-hidden");
|
|
@@ -8929,45 +9059,41 @@ var mountDAW = (target, options = {}) => {
|
|
|
8929
9059
|
fireLyricsChange(active);
|
|
8930
9060
|
});
|
|
8931
9061
|
lyricCustomGuide.addEventListener("click", () => {
|
|
8932
|
-
showModal("\u30AB\u30B9\u30BF\u30E0\u97F3\
|
|
9062
|
+
showModal("\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0(.koe)\u306E\u4F7F\u3044\u65B9", KOE_INFO_HTML);
|
|
8933
9063
|
});
|
|
8934
9064
|
lyricCustomApply.addEventListener("click", () => {
|
|
8935
9065
|
const src = lyricCustomSrc.value.trim();
|
|
8936
9066
|
if (!isValidHttpUrl(src)) {
|
|
8937
|
-
lyricCustomNote.textContent = "\u97F3\
|
|
9067
|
+
lyricCustomNote.textContent = "\u97F3\u58F0URL\u304C\u4E0D\u6B63\u3067\u3059\uFF08http/https\u306E\u307F\u30FB2048\u6587\u5B57\u307E\u3067\uFF09";
|
|
8938
9068
|
return;
|
|
8939
9069
|
}
|
|
8940
9070
|
const iconRaw = lyricCustomIcon.value.trim();
|
|
8941
9071
|
const iconUrl = isValidHttpUrl(iconRaw) ? iconRaw : "";
|
|
8942
|
-
const alias = lyricCustomAlias.value.trim();
|
|
8943
9072
|
const existing = [...customVocalsMap.values()].find(
|
|
8944
9073
|
(d) => d.url === src
|
|
8945
9074
|
);
|
|
8946
|
-
const keyRaw = lyricCustomKey.value.trim().toLowerCase();
|
|
8947
9075
|
let key;
|
|
8948
|
-
if (
|
|
8949
|
-
|
|
8950
|
-
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";
|
|
8951
|
-
return;
|
|
8952
|
-
}
|
|
8953
|
-
if (BASE_LYRIC_MODELS.includes(keyRaw)) {
|
|
8954
|
-
lyricCustomNote.textContent = "\u305D\u306E\u8B58\u5225\u5B50\u306F\u5185\u8535\u30E2\u30C7\u30EB\u540D\u3068\u885D\u7A81\u3057\u3066\u3044\u307E\u3059";
|
|
8955
|
-
return;
|
|
8956
|
-
}
|
|
8957
|
-
const conflict = customVocalsMap.get(keyRaw);
|
|
8958
|
-
if (conflict && conflict.url !== src) {
|
|
8959
|
-
lyricCustomNote.textContent = "\u305D\u306E\u8B58\u5225\u5B50\u306F\u5225\u306E\u97F3\u6E90\u306B\u4F7F\u7528\u6E08\u307F\u3067\u3059";
|
|
8960
|
-
return;
|
|
8961
|
-
}
|
|
8962
|
-
key = keyRaw;
|
|
9076
|
+
if (existing) {
|
|
9077
|
+
key = existing.key;
|
|
8963
9078
|
} else {
|
|
8964
|
-
|
|
9079
|
+
const derived = deriveCustomVocalKeyFromUrl(src);
|
|
9080
|
+
if (derived && !BASE_LYRIC_MODELS.includes(derived)) {
|
|
9081
|
+
if (!customVocalsMap.has(derived)) {
|
|
9082
|
+
key = derived;
|
|
9083
|
+
} else {
|
|
9084
|
+
let n = 2;
|
|
9085
|
+
while (customVocalsMap.has(`${derived}${n}`)) n++;
|
|
9086
|
+
key = `${derived}${n}`;
|
|
9087
|
+
}
|
|
9088
|
+
} else {
|
|
9089
|
+
key = genCustomVocalKey();
|
|
9090
|
+
}
|
|
8965
9091
|
}
|
|
8966
9092
|
registerCustomVocal({
|
|
8967
9093
|
key,
|
|
8968
9094
|
iconUrl: iconUrl || (existing?.iconUrl ?? ""),
|
|
8969
9095
|
url: src,
|
|
8970
|
-
label:
|
|
9096
|
+
label: existing?.label
|
|
8971
9097
|
});
|
|
8972
9098
|
active.lyricModel = key;
|
|
8973
9099
|
fireLyricsChange(active);
|
|
@@ -9468,9 +9594,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
9468
9594
|
refs.playBtn.addEventListener("click", togglePlay);
|
|
9469
9595
|
refs.playBtn.disabled = false;
|
|
9470
9596
|
refs.prevBarBtn.addEventListener("click", () => {
|
|
9597
|
+
const barsBack = playbackState === "playing" ? 2 : 1;
|
|
9471
9598
|
const targetStep = Math.max(
|
|
9472
9599
|
0,
|
|
9473
|
-
Math.floor((getCurrentPlayStep() - 1) / renderConfig.stepsPerBar) * renderConfig.stepsPerBar
|
|
9600
|
+
(Math.floor((getCurrentPlayStep() - 1) / renderConfig.stepsPerBar) - barsBack) * renderConfig.stepsPerBar
|
|
9474
9601
|
);
|
|
9475
9602
|
jumpTo(targetStep);
|
|
9476
9603
|
});
|
|
@@ -9515,6 +9642,35 @@ var mountDAW = (target, options = {}) => {
|
|
|
9515
9642
|
applyZoomY();
|
|
9516
9643
|
notifyViewState();
|
|
9517
9644
|
});
|
|
9645
|
+
refs.bgUploadBtn.addEventListener("click", () => refs.bgFileInput.click());
|
|
9646
|
+
refs.bgFileInput.addEventListener("change", () => {
|
|
9647
|
+
const file = refs.bgFileInput.files?.[0];
|
|
9648
|
+
refs.bgFileInput.value = "";
|
|
9649
|
+
if (!file) return;
|
|
9650
|
+
const reader = new FileReader();
|
|
9651
|
+
reader.onload = () => {
|
|
9652
|
+
const img = new Image();
|
|
9653
|
+
img.onload = () => {
|
|
9654
|
+
const maxW = refs.rollContainer.clientWidth || 800;
|
|
9655
|
+
const maxH = refs.rollContainer.clientHeight || 450;
|
|
9656
|
+
const dataUrl = resizeImageToDataUrl(img, maxW, maxH);
|
|
9657
|
+
try {
|
|
9658
|
+
localStorage.setItem(ROLL_BG_STORAGE_KEY, dataUrl);
|
|
9659
|
+
} catch {
|
|
9660
|
+
}
|
|
9661
|
+
applyRollBackground(dataUrl);
|
|
9662
|
+
};
|
|
9663
|
+
img.src = reader.result;
|
|
9664
|
+
};
|
|
9665
|
+
reader.readAsDataURL(file);
|
|
9666
|
+
});
|
|
9667
|
+
refs.bgRemoveBtn.addEventListener("click", () => {
|
|
9668
|
+
try {
|
|
9669
|
+
localStorage.removeItem(ROLL_BG_STORAGE_KEY);
|
|
9670
|
+
} catch {
|
|
9671
|
+
}
|
|
9672
|
+
applyRollBackground(null);
|
|
9673
|
+
});
|
|
9518
9674
|
refs.decomposeChordToggle.addEventListener("change", notifyViewState);
|
|
9519
9675
|
refs.ignoreChordHeavyToggle.addEventListener("change", notifyViewState);
|
|
9520
9676
|
refs.masterVolume.addEventListener("input", () => {
|
|
@@ -9625,6 +9781,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9625
9781
|
};
|
|
9626
9782
|
showModal = (title, bodyHTML) => {
|
|
9627
9783
|
collapseActiveSample();
|
|
9784
|
+
collapseSearchPreview();
|
|
9628
9785
|
refs.modalTitle.textContent = title;
|
|
9629
9786
|
refs.modalBody.innerHTML = bodyHTML;
|
|
9630
9787
|
refs.modalOverlay.removeAttribute("hidden");
|
|
@@ -9713,11 +9870,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
9713
9870
|
};
|
|
9714
9871
|
refs.modalClose.addEventListener("click", () => {
|
|
9715
9872
|
collapseActiveSample();
|
|
9873
|
+
collapseSearchPreview();
|
|
9716
9874
|
refs.modalOverlay.setAttribute("hidden", "");
|
|
9717
9875
|
});
|
|
9718
9876
|
refs.modalOverlay.addEventListener("click", (e) => {
|
|
9719
9877
|
if (e.target === refs.modalOverlay) {
|
|
9720
9878
|
collapseActiveSample();
|
|
9879
|
+
collapseSearchPreview();
|
|
9721
9880
|
refs.modalOverlay.setAttribute("hidden", "");
|
|
9722
9881
|
}
|
|
9723
9882
|
});
|
|
@@ -9738,6 +9897,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9738
9897
|
})
|
|
9739
9898
|
);
|
|
9740
9899
|
if (showMidi) wireMidi();
|
|
9900
|
+
if (showMidiSearch) wireMidiSearch();
|
|
9741
9901
|
document.addEventListener("keydown", onKeyDown);
|
|
9742
9902
|
refs.root.addEventListener("keydown", (e) => {
|
|
9743
9903
|
const t = e.target;
|
|
@@ -9802,6 +9962,236 @@ var mountDAW = (target, options = {}) => {
|
|
|
9802
9962
|
overlayDuring(() => applyMidiSelection(pendingMidi, selected));
|
|
9803
9963
|
});
|
|
9804
9964
|
};
|
|
9965
|
+
const buildPreviewMml = (midi) => {
|
|
9966
|
+
const analysis = analyzeMidiTracks(midi);
|
|
9967
|
+
const selected = analysis.filter((a) => a.selected).map((a) => a.index);
|
|
9968
|
+
const { placements, bpm: parsedBpm } = extractMidiPlacements(
|
|
9969
|
+
midi,
|
|
9970
|
+
selected
|
|
9971
|
+
);
|
|
9972
|
+
const byTrack = /* @__PURE__ */ new Map();
|
|
9973
|
+
for (const p of placements) {
|
|
9974
|
+
if (!byTrack.has(p.trackId)) byTrack.set(p.trackId, []);
|
|
9975
|
+
byTrack.get(p.trackId)?.push(p);
|
|
9976
|
+
}
|
|
9977
|
+
const refCore = trackStates[0].core;
|
|
9978
|
+
const tempo = Math.round(parsedBpm) || bpm;
|
|
9979
|
+
const lines = [];
|
|
9980
|
+
let i = 0;
|
|
9981
|
+
for (const notes of byTrack.values()) {
|
|
9982
|
+
const asNotes = notes.map((p) => ({
|
|
9983
|
+
id: 0,
|
|
9984
|
+
startStep: p.startStep,
|
|
9985
|
+
durationSteps: p.durationSteps,
|
|
9986
|
+
pitch: p.pitch,
|
|
9987
|
+
velocity: p.velocity
|
|
9988
|
+
}));
|
|
9989
|
+
const mml = refCore.getMMLFromNotes(asNotes, tempo, 100).trim();
|
|
9990
|
+
lines.push(`@${i} ${mml}`);
|
|
9991
|
+
i++;
|
|
9992
|
+
}
|
|
9993
|
+
return [...lines, MML_END_MARKER].join(";\n");
|
|
9994
|
+
};
|
|
9995
|
+
const fetchVerifiedMidi = async (fileName) => {
|
|
9996
|
+
if (!options.parseMidi || !midiSearchClient) {
|
|
9997
|
+
throw new Error("parseMidi/midiSearchClient not injected");
|
|
9998
|
+
}
|
|
9999
|
+
const midiBytes = await midiSearchClient.fetchMidi(fileName);
|
|
10000
|
+
const midi = await options.parseMidi(new Uint8Array(midiBytes));
|
|
10001
|
+
const analysis = analyzeMidiTracks(midi);
|
|
10002
|
+
const selected = analysis.filter((a) => a.selected).map((a) => a.index);
|
|
10003
|
+
if (!isPlausibleMidiTranscription(midi, selected)) {
|
|
10004
|
+
throw new Error("implausible MIDI transcription");
|
|
10005
|
+
}
|
|
10006
|
+
return midi;
|
|
10007
|
+
};
|
|
10008
|
+
let searchPreviewPlayer = null;
|
|
10009
|
+
let searchPreviewBtn = null;
|
|
10010
|
+
const collapseSearchPreview = () => {
|
|
10011
|
+
if (searchPreviewPlayer) {
|
|
10012
|
+
searchPreviewPlayer.stop();
|
|
10013
|
+
searchPreviewPlayer.destroy();
|
|
10014
|
+
searchPreviewPlayer = null;
|
|
10015
|
+
}
|
|
10016
|
+
if (searchPreviewBtn) {
|
|
10017
|
+
searchPreviewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
10018
|
+
searchPreviewBtn.classList.remove("dtm-btn--danger");
|
|
10019
|
+
searchPreviewBtn.classList.add("dtm-btn--ghost");
|
|
10020
|
+
const container = searchPreviewBtn.closest(".dtm-modal-sample-box")?.querySelector(".dtm-modal-sample-player-container");
|
|
10021
|
+
if (container) container.innerHTML = "";
|
|
10022
|
+
searchPreviewBtn = null;
|
|
10023
|
+
}
|
|
10024
|
+
};
|
|
10025
|
+
const wireMidiSearch = () => {
|
|
10026
|
+
if (!midiSearchClient) return;
|
|
10027
|
+
refs.midiSearchOpenBtn.addEventListener("click", () => {
|
|
10028
|
+
showModal(
|
|
10029
|
+
"MML\u691C\u7D22",
|
|
10030
|
+
`
|
|
10031
|
+
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
10032
|
+
<input type="text" class="dtm-input dtm-grow" data-dtm="modal-midi-search-input" placeholder="\u66F2\u540D\u3067MML\u691C\u7D22" style="min-width:0">
|
|
10033
|
+
<button class="dtm-btn dtm-btn--primary" data-dtm="modal-midi-search-btn" style="flex-shrink:0">\u691C\u7D22</button>
|
|
10034
|
+
</div>
|
|
10035
|
+
<div class="dtm-row" data-dtm="modal-midi-search-results" style="flex-direction:column;align-items:stretch;gap:4px"></div>
|
|
10036
|
+
`
|
|
10037
|
+
);
|
|
10038
|
+
const input = refs.modalBody.querySelector(
|
|
10039
|
+
'[data-dtm="modal-midi-search-input"]'
|
|
10040
|
+
);
|
|
10041
|
+
const searchBtn = refs.modalBody.querySelector(
|
|
10042
|
+
'[data-dtm="modal-midi-search-btn"]'
|
|
10043
|
+
);
|
|
10044
|
+
const results = refs.modalBody.querySelector(
|
|
10045
|
+
'[data-dtm="modal-midi-search-results"]'
|
|
10046
|
+
);
|
|
10047
|
+
const runSearch = async () => {
|
|
10048
|
+
const q2 = input.value.trim();
|
|
10049
|
+
if (!q2) return;
|
|
10050
|
+
collapseSearchPreview();
|
|
10051
|
+
searchBtn.disabled = true;
|
|
10052
|
+
searchBtn.textContent = "\u691C\u7D22\u4E2D...";
|
|
10053
|
+
results.innerHTML = "";
|
|
10054
|
+
try {
|
|
10055
|
+
const songs = await midiSearchClient.searchSongs({ title: q2 });
|
|
10056
|
+
if (songs.length === 0) {
|
|
10057
|
+
results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
|
|
10058
|
+
return;
|
|
10059
|
+
}
|
|
10060
|
+
for (const song of songs) {
|
|
10061
|
+
const box = document.createElement("div");
|
|
10062
|
+
box.className = "dtm-modal-sample-box";
|
|
10063
|
+
const row = document.createElement("div");
|
|
10064
|
+
row.className = "dtm-row";
|
|
10065
|
+
row.style.cssText = "flex-wrap:nowrap;gap:4px;padding:2px 0";
|
|
10066
|
+
const label = document.createElement("span");
|
|
10067
|
+
label.className = "dtm-label";
|
|
10068
|
+
label.style.cssText = "flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
10069
|
+
label.textContent = `${song.title} - ${song.user}`;
|
|
10070
|
+
const previewBtn = document.createElement("button");
|
|
10071
|
+
previewBtn.className = "dtm-btn dtm-btn--ghost";
|
|
10072
|
+
previewBtn.style.cssText = "flex-shrink:0";
|
|
10073
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
10074
|
+
const loadBtn = document.createElement("button");
|
|
10075
|
+
loadBtn.className = "dtm-btn dtm-btn--primary";
|
|
10076
|
+
loadBtn.style.cssText = "flex-shrink:0";
|
|
10077
|
+
loadBtn.textContent = "\u8AAD\u307F\u8FBC\u307F";
|
|
10078
|
+
const playerContainer = document.createElement("div");
|
|
10079
|
+
playerContainer.className = "dtm-modal-sample-player-container";
|
|
10080
|
+
previewBtn.addEventListener("click", async () => {
|
|
10081
|
+
if (searchPreviewBtn === previewBtn) {
|
|
10082
|
+
if (searchPreviewPlayer?.isPlaying()) {
|
|
10083
|
+
searchPreviewPlayer.stop();
|
|
10084
|
+
} else {
|
|
10085
|
+
stop();
|
|
10086
|
+
previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
10087
|
+
try {
|
|
10088
|
+
const midi = await fetchVerifiedMidi(song.file);
|
|
10089
|
+
const mml = buildPreviewMml(midi);
|
|
10090
|
+
playerContainer.innerHTML = "";
|
|
10091
|
+
const player = mountMmlPlayer(playerContainer, mml, {
|
|
10092
|
+
onPlayNote: options.onPlayNote,
|
|
10093
|
+
onPlayDrum: options.onPlayDrum,
|
|
10094
|
+
onResumeAudio: options.onResumeAudio,
|
|
10095
|
+
getAudioTime: options.getAudioTime,
|
|
10096
|
+
singingVoices: options.singingVoices,
|
|
10097
|
+
drumPatterns: options.drumPatterns,
|
|
10098
|
+
volume: masterVolume,
|
|
10099
|
+
_skipInfoModals: true,
|
|
10100
|
+
onStop: () => {
|
|
10101
|
+
if (searchPreviewBtn === previewBtn) {
|
|
10102
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
10103
|
+
previewBtn.classList.remove("dtm-btn--danger");
|
|
10104
|
+
previewBtn.classList.add("dtm-btn--ghost");
|
|
10105
|
+
}
|
|
10106
|
+
}
|
|
10107
|
+
});
|
|
10108
|
+
searchPreviewPlayer = player;
|
|
10109
|
+
searchPreviewBtn = previewBtn;
|
|
10110
|
+
player.play();
|
|
10111
|
+
previewBtn.textContent = "\u25A0 \u505C\u6B62";
|
|
10112
|
+
previewBtn.classList.remove("dtm-btn--ghost");
|
|
10113
|
+
previewBtn.classList.add("dtm-btn--danger");
|
|
10114
|
+
} catch (e) {
|
|
10115
|
+
console.error("[dtm] MIDI preview failed", e);
|
|
10116
|
+
previewBtn.textContent = "\u5931\u6557";
|
|
10117
|
+
}
|
|
10118
|
+
}
|
|
10119
|
+
} else {
|
|
10120
|
+
collapseSearchPreview();
|
|
10121
|
+
stop();
|
|
10122
|
+
previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
10123
|
+
try {
|
|
10124
|
+
const midi = await fetchVerifiedMidi(song.file);
|
|
10125
|
+
const mml = buildPreviewMml(midi);
|
|
10126
|
+
playerContainer.innerHTML = "";
|
|
10127
|
+
const player = mountMmlPlayer(playerContainer, mml, {
|
|
10128
|
+
onPlayNote: options.onPlayNote,
|
|
10129
|
+
onPlayDrum: options.onPlayDrum,
|
|
10130
|
+
onResumeAudio: options.onResumeAudio,
|
|
10131
|
+
getAudioTime: options.getAudioTime,
|
|
10132
|
+
singingVoices: options.singingVoices,
|
|
10133
|
+
drumPatterns: options.drumPatterns,
|
|
10134
|
+
volume: masterVolume,
|
|
10135
|
+
_skipInfoModals: true,
|
|
10136
|
+
onStop: () => {
|
|
10137
|
+
if (searchPreviewBtn === previewBtn) {
|
|
10138
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
10139
|
+
previewBtn.classList.remove("dtm-btn--danger");
|
|
10140
|
+
previewBtn.classList.add("dtm-btn--ghost");
|
|
10141
|
+
}
|
|
10142
|
+
}
|
|
10143
|
+
});
|
|
10144
|
+
searchPreviewPlayer = player;
|
|
10145
|
+
searchPreviewBtn = previewBtn;
|
|
10146
|
+
player.play();
|
|
10147
|
+
previewBtn.textContent = "\u25A0 \u505C\u6B62";
|
|
10148
|
+
previewBtn.classList.remove("dtm-btn--ghost");
|
|
10149
|
+
previewBtn.classList.add("dtm-btn--danger");
|
|
10150
|
+
} catch (e) {
|
|
10151
|
+
console.error("[dtm] MIDI preview failed", e);
|
|
10152
|
+
previewBtn.textContent = "\u5931\u6557";
|
|
10153
|
+
}
|
|
10154
|
+
}
|
|
10155
|
+
});
|
|
10156
|
+
loadBtn.addEventListener("click", async () => {
|
|
10157
|
+
loadBtn.disabled = true;
|
|
10158
|
+
loadBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
10159
|
+
try {
|
|
10160
|
+
const pending = await fetchVerifiedMidi(song.file);
|
|
10161
|
+
const analysis = analyzeMidiTracks(pending);
|
|
10162
|
+
const sel = analysis.filter((a) => a.selected).map((a) => a.index);
|
|
10163
|
+
collapseSearchPreview();
|
|
10164
|
+
overlayDuring(() => applyMidiSelection(pending, sel));
|
|
10165
|
+
refs.modalOverlay.setAttribute("hidden", "");
|
|
10166
|
+
} catch (e) {
|
|
10167
|
+
console.error("[dtm] MIDI fetch failed", e);
|
|
10168
|
+
loadBtn.textContent = "\u5931\u6557";
|
|
10169
|
+
} finally {
|
|
10170
|
+
loadBtn.disabled = false;
|
|
10171
|
+
}
|
|
10172
|
+
});
|
|
10173
|
+
row.appendChild(label);
|
|
10174
|
+
row.appendChild(previewBtn);
|
|
10175
|
+
row.appendChild(loadBtn);
|
|
10176
|
+
box.appendChild(row);
|
|
10177
|
+
box.appendChild(playerContainer);
|
|
10178
|
+
results.appendChild(box);
|
|
10179
|
+
}
|
|
10180
|
+
} catch (e) {
|
|
10181
|
+
console.error("[dtm] MIDI search failed", e);
|
|
10182
|
+
results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F</span>';
|
|
10183
|
+
} finally {
|
|
10184
|
+
searchBtn.disabled = false;
|
|
10185
|
+
searchBtn.textContent = "\u691C\u7D22";
|
|
10186
|
+
}
|
|
10187
|
+
};
|
|
10188
|
+
searchBtn.addEventListener("click", () => void runSearch());
|
|
10189
|
+
input.addEventListener("keydown", (e) => {
|
|
10190
|
+
if (e.key === "Enter") void runSearch();
|
|
10191
|
+
});
|
|
10192
|
+
input.focus();
|
|
10193
|
+
});
|
|
10194
|
+
};
|
|
9805
10195
|
const onKeyDown = (e) => {
|
|
9806
10196
|
if (!(e.ctrlKey || e.metaKey)) return;
|
|
9807
10197
|
if (e.code === "KeyZ" && !e.shiftKey) {
|
|
@@ -9857,6 +10247,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
9857
10247
|
updateUndoRedo();
|
|
9858
10248
|
redrawAll();
|
|
9859
10249
|
if (options.initialMML) loadMML(options.initialMML);
|
|
10250
|
+
try {
|
|
10251
|
+
const storedBg = localStorage.getItem(ROLL_BG_STORAGE_KEY);
|
|
10252
|
+
if (storedBg) applyRollBackground(storedBg);
|
|
10253
|
+
} catch {
|
|
10254
|
+
}
|
|
9860
10255
|
let resizeTimer = null;
|
|
9861
10256
|
const resizeObserver = new ResizeObserver(() => {
|
|
9862
10257
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
@@ -11177,13 +11572,17 @@ var createDtmStudio = async (options = {}) => {
|
|
|
11177
11572
|
const mountedPlayers = [];
|
|
11178
11573
|
const mountedModeSwitches = [];
|
|
11179
11574
|
const mountEditor = (target, opts = {}) => {
|
|
11575
|
+
const mergedOpts = {
|
|
11576
|
+
midiSearch: options.midiSearch,
|
|
11577
|
+
...opts
|
|
11578
|
+
};
|
|
11180
11579
|
const {
|
|
11181
11580
|
preset,
|
|
11182
11581
|
presetUI,
|
|
11183
11582
|
onInstrumentChange,
|
|
11184
11583
|
onTrackInstrumentChange: externalOnTrackInstrumentChange,
|
|
11185
11584
|
...dawOverrides
|
|
11186
|
-
} =
|
|
11585
|
+
} = mergedOpts;
|
|
11187
11586
|
const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
|
|
11188
11587
|
const trackIds = tracks.map((t) => t.id);
|
|
11189
11588
|
const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
|
|
@@ -11518,6 +11917,7 @@ export {
|
|
|
11518
11917
|
MAX_VOCAL_VOLUME,
|
|
11519
11918
|
MMLCore,
|
|
11520
11919
|
MML_END_MARKER,
|
|
11920
|
+
MidiSearchClient,
|
|
11521
11921
|
PITCH_MAP,
|
|
11522
11922
|
PREWARM_NOTES,
|
|
11523
11923
|
TRACKS_ADVANCED,
|
|
@@ -11567,6 +11967,7 @@ export {
|
|
|
11567
11967
|
init,
|
|
11568
11968
|
injectStyles,
|
|
11569
11969
|
isChordHeavyTrack,
|
|
11970
|
+
isPlausibleMidiTranscription,
|
|
11570
11971
|
isValidHttpUrl,
|
|
11571
11972
|
koeUrl,
|
|
11572
11973
|
mountDAW,
|
|
@@ -11580,6 +11981,7 @@ export {
|
|
|
11580
11981
|
parseMmlMeta,
|
|
11581
11982
|
playMML,
|
|
11582
11983
|
playSingingMML,
|
|
11984
|
+
setBackgroundActive,
|
|
11583
11985
|
setDrawOffset,
|
|
11584
11986
|
shiftNotes,
|
|
11585
11987
|
showLoadingOverlay,
|