@onjmin/dtm 0.1.23 → 0.1.24
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 +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +445 -123
- package/dist/index.mjs +445 -123
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1216,6 +1216,9 @@ var ICONS = {
|
|
|
1216
1216
|
},
|
|
1217
1217
|
info: {
|
|
1218
1218
|
d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"
|
|
1219
|
+
},
|
|
1220
|
+
more: {
|
|
1221
|
+
d: "M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"
|
|
1219
1222
|
}
|
|
1220
1223
|
};
|
|
1221
1224
|
var icon = (name, size = 20) => {
|
|
@@ -1342,6 +1345,7 @@ var buildUI = (target, options) => {
|
|
|
1342
1345
|
<textarea class="dtm-textarea dtm-grow" data-dtm="mml-input" placeholder="MML\u3092\u5165\u529B"></textarea>
|
|
1343
1346
|
<button class="dtm-btn dtm-btn--primary" data-dtm="mml-load">\u8AAD\u8FBC</button>
|
|
1344
1347
|
</div>
|
|
1348
|
+
<p class="dtm-load-note dtm-hidden" data-dtm="mml-load-note"></p>
|
|
1345
1349
|
</div>
|
|
1346
1350
|
</details>
|
|
1347
1351
|
|
|
@@ -1467,6 +1471,7 @@ var buildUI = (target, options) => {
|
|
|
1467
1471
|
midiPanel: sel("midi-panel"),
|
|
1468
1472
|
mmlInput: sel("mml-input"),
|
|
1469
1473
|
mmlLoadBtn: sel("mml-load"),
|
|
1474
|
+
mmlLoadNote: sel("mml-load-note"),
|
|
1470
1475
|
shiftSelect: sel("shift-select"),
|
|
1471
1476
|
shiftApplyBtn: sel("shift-apply"),
|
|
1472
1477
|
macroClear: sel("macro-clear"),
|
|
@@ -1620,6 +1625,110 @@ var DRUM_PATTERNS = {
|
|
|
1620
1625
|
]
|
|
1621
1626
|
};
|
|
1622
1627
|
|
|
1628
|
+
// src/instrument-presets.ts
|
|
1629
|
+
var INSTRUMENT_PRESETS = {
|
|
1630
|
+
// --- STANDARD: 汎用性と完成度重視 ---
|
|
1631
|
+
piano: {
|
|
1632
|
+
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
1633
|
+
description: "\u6700\u3082\u7834\u7DBB\u3057\u306B\u304F\u3044\u69CB\u6210\u3002\u697D\u66F2\u5236\u4F5C\u306E\u30B9\u30B1\u30C3\u30C1\u306B\u3082\u6700\u9069\u3002",
|
|
1634
|
+
melody: "Acoustic Grand Piano",
|
|
1635
|
+
submelody: "Vibraphone",
|
|
1636
|
+
bass: "Electric Bass (finger)",
|
|
1637
|
+
chord: "Pad 2 (warm)"
|
|
1638
|
+
},
|
|
1639
|
+
acoustic: {
|
|
1640
|
+
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
1641
|
+
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
1642
|
+
melody: "Acoustic Guitar (steel)",
|
|
1643
|
+
submelody: "Harmonica",
|
|
1644
|
+
bass: "Acoustic Bass",
|
|
1645
|
+
chord: "Acoustic Guitar (nylon)"
|
|
1646
|
+
},
|
|
1647
|
+
jazz_night: {
|
|
1648
|
+
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
1649
|
+
description: "Rhodes\u98A8\u306EEP\u3068\u30A6\u30C3\u30C9\u30D9\u30FC\u30B9\u306B\u3088\u308B\u3001\u5927\u4EBA\u3073\u305F\u30A2\u30F3\u30B5\u30F3\u30D6\u30EB\u3002",
|
|
1650
|
+
melody: "Electric Piano 1",
|
|
1651
|
+
submelody: "Flute",
|
|
1652
|
+
bass: "Acoustic Bass",
|
|
1653
|
+
chord: "Electric Guitar (jazz)"
|
|
1654
|
+
},
|
|
1655
|
+
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
1656
|
+
synth_pop: {
|
|
1657
|
+
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
1658
|
+
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
1659
|
+
melody: "Lead 2 (sawtooth)",
|
|
1660
|
+
submelody: "Lead 4 (chiff)",
|
|
1661
|
+
bass: "Synth Bass 2",
|
|
1662
|
+
chord: "Pad 3 (polysynth)"
|
|
1663
|
+
},
|
|
1664
|
+
cyber_punk: {
|
|
1665
|
+
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
1666
|
+
description: "\u30C7\u30B8\u30BF\u30EB\u306A\u51B7\u305F\u3055\u3068\u6B6A\u307F\u304C\u6DF7\u3056\u308A\u5408\u3046\u3001\u672A\u6765\u7684\u306A\u97FF\u304D\u3002",
|
|
1667
|
+
melody: "Lead 8 (bass + lead)",
|
|
1668
|
+
submelody: "Lead 5 (charang)",
|
|
1669
|
+
bass: "Synth Bass 2",
|
|
1670
|
+
chord: "Pad 8 (sweep)"
|
|
1671
|
+
},
|
|
1672
|
+
rock: {
|
|
1673
|
+
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
1674
|
+
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
1675
|
+
melody: "Distortion Guitar",
|
|
1676
|
+
submelody: "Rock Organ",
|
|
1677
|
+
bass: "Electric Bass (pick)",
|
|
1678
|
+
chord: "Overdriven Guitar"
|
|
1679
|
+
},
|
|
1680
|
+
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
1681
|
+
orchestra: {
|
|
1682
|
+
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
1683
|
+
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
1684
|
+
melody: "French Horn",
|
|
1685
|
+
submelody: "Pizzicato Strings",
|
|
1686
|
+
bass: "Cello",
|
|
1687
|
+
chord: "Tremolo Strings"
|
|
1688
|
+
},
|
|
1689
|
+
japanese_wa: {
|
|
1690
|
+
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
1691
|
+
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
1692
|
+
melody: "Koto",
|
|
1693
|
+
submelody: "Shamisen",
|
|
1694
|
+
bass: "Taiko Drum",
|
|
1695
|
+
chord: "Shakuhachi"
|
|
1696
|
+
},
|
|
1697
|
+
arabic_exotic: {
|
|
1698
|
+
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
1699
|
+
description: "\u30B7\u30BF\u30FC\u30EB\u3084\u30D0\u30B0\u30D1\u30A4\u30D7\u306B\u3088\u308B\u3001\u7570\u56FD\u60C5\u7DD2\u6EA2\u308C\u308B\u30B5\u30A6\u30F3\u30C9\u3002",
|
|
1700
|
+
melody: "Sitar",
|
|
1701
|
+
submelody: "Bagpipe",
|
|
1702
|
+
bass: "Fretless Bass",
|
|
1703
|
+
chord: "Kalimba"
|
|
1704
|
+
},
|
|
1705
|
+
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
1706
|
+
fantasy_rpg: {
|
|
1707
|
+
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
1708
|
+
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
1709
|
+
melody: "Ocarina",
|
|
1710
|
+
submelody: "Celesta",
|
|
1711
|
+
bass: "Timpani",
|
|
1712
|
+
chord: "Orchestral Harp"
|
|
1713
|
+
},
|
|
1714
|
+
ambient_cloud: {
|
|
1715
|
+
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
1716
|
+
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
1717
|
+
melody: "Lead 6 (voice)",
|
|
1718
|
+
submelody: "Music Box",
|
|
1719
|
+
bass: "Synth Bass 1",
|
|
1720
|
+
chord: "Pad 7 (halo)"
|
|
1721
|
+
},
|
|
1722
|
+
retro_game: {
|
|
1723
|
+
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
1724
|
+
description: "\u77E9\u5F62\u6CE2\u3092\u60F3\u8D77\u3055\u305B\u308B\u3001\u521D\u671F\u30B2\u30FC\u30E0\u6A5F\u306E\u3088\u3046\u306A\u61D0\u304B\u3057\u3044\u97FF\u304D\u3002",
|
|
1725
|
+
melody: "Lead 1 (square)",
|
|
1726
|
+
submelody: "Lead 2 (sawtooth)",
|
|
1727
|
+
bass: "Synth Bass 1",
|
|
1728
|
+
chord: "Clavinet"
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1623
1732
|
// node_modules/.pnpm/@onjmin+koe@1.0.3/node_modules/@onjmin/koe/dist/index.js
|
|
1624
1733
|
var MAGIC = 1263486208;
|
|
1625
1734
|
function parseKoeHeader(headerBytes) {
|
|
@@ -4069,6 +4178,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4069
4178
|
bpm,
|
|
4070
4179
|
tokenTracks: collectTokens ? tokenTracks : void 0,
|
|
4071
4180
|
lyrics: collectLyrics ? /* @__PURE__ */ new Map() : void 0,
|
|
4181
|
+
mergedTrackCount: 0,
|
|
4072
4182
|
meta: {}
|
|
4073
4183
|
};
|
|
4074
4184
|
}
|
|
@@ -4081,13 +4191,24 @@ var parseMML = (mml, options = {}) => {
|
|
|
4081
4191
|
const fullMML = stripLyrics(noMeta).replace(endRegex, "").replace(/[\n\r]+/g, " ").trim();
|
|
4082
4192
|
const parts = fullMML.split(/(@\d+)/).filter((p) => p.trim().length > 0);
|
|
4083
4193
|
let trackIndex = 0;
|
|
4194
|
+
let sourceTrackIndex = 0;
|
|
4084
4195
|
let octave = 4;
|
|
4085
4196
|
let currentStep = 0;
|
|
4086
4197
|
let baseLength = 16;
|
|
4198
|
+
const contributors = /* @__PURE__ */ new Map();
|
|
4199
|
+
const recordContributor = () => {
|
|
4200
|
+
let set = contributors.get(trackIndex);
|
|
4201
|
+
if (!set) {
|
|
4202
|
+
set = /* @__PURE__ */ new Set();
|
|
4203
|
+
contributors.set(trackIndex, set);
|
|
4204
|
+
}
|
|
4205
|
+
set.add(sourceTrackIndex);
|
|
4206
|
+
};
|
|
4087
4207
|
for (const rawPart of parts) {
|
|
4088
4208
|
const part = rawPart.trim();
|
|
4089
4209
|
if (part.startsWith("@")) {
|
|
4090
4210
|
let idx = Number.parseInt(part.substring(1), 10);
|
|
4211
|
+
sourceTrackIndex = idx;
|
|
4091
4212
|
if (clampTrackCount !== void 0 && idx >= clampTrackCount)
|
|
4092
4213
|
idx = clampTrackCount - 1;
|
|
4093
4214
|
trackIndex = idx;
|
|
@@ -4211,6 +4332,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4211
4332
|
}
|
|
4212
4333
|
if (j < body.length && body[j] === "]") j++;
|
|
4213
4334
|
const steps = parseLength();
|
|
4335
|
+
if (chordNotes.length > 0) recordContributor();
|
|
4214
4336
|
for (const p of chordNotes) {
|
|
4215
4337
|
placements.push({
|
|
4216
4338
|
trackIndex,
|
|
@@ -4234,6 +4356,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4234
4356
|
}
|
|
4235
4357
|
const midiPitch = (octave + 1) * 12 + pitch;
|
|
4236
4358
|
const steps = parseLength();
|
|
4359
|
+
recordContributor();
|
|
4237
4360
|
placements.push({
|
|
4238
4361
|
trackIndex,
|
|
4239
4362
|
startStep: currentStep,
|
|
@@ -4247,11 +4370,16 @@ var parseMML = (mml, options = {}) => {
|
|
|
4247
4370
|
}
|
|
4248
4371
|
}
|
|
4249
4372
|
}
|
|
4373
|
+
let mergedTrackCount = 0;
|
|
4374
|
+
for (const set of contributors.values()) {
|
|
4375
|
+
if (set.size >= 2) mergedTrackCount++;
|
|
4376
|
+
}
|
|
4250
4377
|
return {
|
|
4251
4378
|
placements,
|
|
4252
4379
|
bpm,
|
|
4253
4380
|
tokenTracks: collectTokens ? tokenTracks : void 0,
|
|
4254
4381
|
lyrics,
|
|
4382
|
+
mergedTrackCount,
|
|
4255
4383
|
meta
|
|
4256
4384
|
};
|
|
4257
4385
|
};
|
|
@@ -4313,6 +4441,8 @@ var createSequencer = (options) => {
|
|
|
4313
4441
|
let active = false;
|
|
4314
4442
|
let fromStepValue = 0;
|
|
4315
4443
|
let trackVolumeMap = /* @__PURE__ */ new Map();
|
|
4444
|
+
let lastRealTime = -1;
|
|
4445
|
+
let lastAudioTime = -1;
|
|
4316
4446
|
let isLooping = false;
|
|
4317
4447
|
let loopStartStep = 0;
|
|
4318
4448
|
let loopEndStep = 0;
|
|
@@ -4386,6 +4516,21 @@ var createSequencer = (options) => {
|
|
|
4386
4516
|
const sps = secondsPerStep();
|
|
4387
4517
|
const time = options.getAudioTime() - startTime;
|
|
4388
4518
|
const soloId = options.getSoloTrackId();
|
|
4519
|
+
const nowReal = performance.now() / 1e3;
|
|
4520
|
+
if (lastRealTime > 0 && lastAudioTime >= 0) {
|
|
4521
|
+
const realDelta = nowReal - lastRealTime;
|
|
4522
|
+
const audioDelta = time - lastAudioTime;
|
|
4523
|
+
if (realDelta > 0.5 || audioDelta > 0.5) {
|
|
4524
|
+
console.warn(
|
|
4525
|
+
`[sequencer] Interruption detected (realDelta: ${realDelta.toFixed(3)}s, audioDelta: ${audioDelta.toFixed(3)}s). Stopping playback.`
|
|
4526
|
+
);
|
|
4527
|
+
stop();
|
|
4528
|
+
options.onEnd(true);
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
lastRealTime = nowReal;
|
|
4533
|
+
lastAudioTime = time;
|
|
4389
4534
|
for (const track of options.getTracks()) {
|
|
4390
4535
|
trackVolumeMap.set(track.id, track.volume);
|
|
4391
4536
|
}
|
|
@@ -4462,7 +4607,7 @@ var createSequencer = (options) => {
|
|
|
4462
4607
|
const lastDuration = last?.duration ?? 0;
|
|
4463
4608
|
if (nowIndex >= timeline.length && time > lastWhen + lastDuration + 0.1) {
|
|
4464
4609
|
stop();
|
|
4465
|
-
options.onEnd();
|
|
4610
|
+
options.onEnd(false);
|
|
4466
4611
|
}
|
|
4467
4612
|
}
|
|
4468
4613
|
};
|
|
@@ -4503,6 +4648,8 @@ var createSequencer = (options) => {
|
|
|
4503
4648
|
}
|
|
4504
4649
|
loopBase = 0;
|
|
4505
4650
|
lastPlayStep = fromStepValue - 1e-4;
|
|
4651
|
+
lastRealTime = -1;
|
|
4652
|
+
lastAudioTime = -1;
|
|
4506
4653
|
intervalId = setInterval(scheduleTick, TICK_INTERVAL_MS);
|
|
4507
4654
|
animationId = requestAnimationFrame(animate);
|
|
4508
4655
|
};
|
|
@@ -4700,11 +4847,11 @@ var DAW_CSS = `
|
|
|
4700
4847
|
}
|
|
4701
4848
|
.dtm-btn:active { transform: translate(3px,3px); box-shadow: none; }
|
|
4702
4849
|
.dtm-btn:disabled { opacity: .3; cursor: default; box-shadow: none; }
|
|
4850
|
+
.dtm-btn--ghost { background: transparent; border-color: var(--dtm-border2); }
|
|
4703
4851
|
.dtm-btn--primary { border-color: var(--dtm-primary); background: var(--dtm-primary); color: var(--dtm-pfg); }
|
|
4704
4852
|
.dtm-btn--success { border-color: var(--dtm-success); background: var(--dtm-success); color: var(--c-black); }
|
|
4705
4853
|
.dtm-btn--danger { border-color: var(--dtm-danger); background: var(--dtm-danger); color: var(--c-white); }
|
|
4706
4854
|
.dtm-btn--accent { border-color: var(--dtm-accent); background: var(--dtm-accent); color: var(--c-black); }
|
|
4707
|
-
.dtm-btn--ghost { background: transparent; border-color: var(--dtm-border2); }
|
|
4708
4855
|
.dtm-btn--icon { padding: 0; }
|
|
4709
4856
|
|
|
4710
4857
|
/* \u2500\u2500\u2500 \u30A2\u30A4\u30B3\u30F3\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */
|
|
@@ -5338,6 +5485,18 @@ var DAW_CSS = `
|
|
|
5338
5485
|
}
|
|
5339
5486
|
|
|
5340
5487
|
.dtm-hidden { display: none !important; }
|
|
5488
|
+
/* \u8AAD\u8FBC\u6642\u306E\u63A7\u3048\u3081\u306A\u304A\u77E5\u3089\u305B\uFF08\u4F8B: \u30B7\u30F3\u30D7\u30EB\u30E2\u30FC\u30C9\u3067\u306E\u30C8\u30E9\u30C3\u30AF\u5408\u7B97\uFF09\u3002\u4E3B\u5F35\u3057\u3059\u304E\u306A\u3044 muted \u8868\u793A\u3002 */
|
|
5489
|
+
.dtm-load-note {
|
|
5490
|
+
margin: 6px 0 0;
|
|
5491
|
+
padding: 0 2px;
|
|
5492
|
+
font-family: var(--dtm-font);
|
|
5493
|
+
font-size: 10px;
|
|
5494
|
+
line-height: 1.5;
|
|
5495
|
+
letter-spacing: .04em;
|
|
5496
|
+
color: var(--dtm-muted);
|
|
5497
|
+
opacity: .85;
|
|
5498
|
+
}
|
|
5499
|
+
.dtm-load-note::before { content: "\u24D8 "; }
|
|
5341
5500
|
.dtm-grow { flex: 1 1 auto; }
|
|
5342
5501
|
.dtm-lyric-icon {
|
|
5343
5502
|
flex: 0 0 auto;
|
|
@@ -5437,6 +5596,62 @@ var DAW_CSS = `
|
|
|
5437
5596
|
white-space: nowrap;
|
|
5438
5597
|
}
|
|
5439
5598
|
.dtm-player-mml-link:hover { color: var(--dtm-primary); }
|
|
5599
|
+
.dtm-player-more-container {
|
|
5600
|
+
position: relative;
|
|
5601
|
+
display: inline-flex;
|
|
5602
|
+
align-items: center;
|
|
5603
|
+
}
|
|
5604
|
+
.dtm-player-more-btn {
|
|
5605
|
+
background: transparent;
|
|
5606
|
+
border: none;
|
|
5607
|
+
color: var(--dtm-muted);
|
|
5608
|
+
cursor: pointer;
|
|
5609
|
+
padding: 2px;
|
|
5610
|
+
display: flex;
|
|
5611
|
+
align-items: center;
|
|
5612
|
+
justify-content: center;
|
|
5613
|
+
border-radius: 50%;
|
|
5614
|
+
width: 24px;
|
|
5615
|
+
height: 24px;
|
|
5616
|
+
transition: color 0.15s, background-color 0.15s;
|
|
5617
|
+
}
|
|
5618
|
+
.dtm-player-more-btn:hover,
|
|
5619
|
+
.dtm-player-more-btn.is-active {
|
|
5620
|
+
color: var(--dtm-text);
|
|
5621
|
+
background: var(--dtm-border2);
|
|
5622
|
+
}
|
|
5623
|
+
.dtm-player-menu {
|
|
5624
|
+
position: absolute;
|
|
5625
|
+
top: calc(100% + 4px);
|
|
5626
|
+
right: 0;
|
|
5627
|
+
background: var(--dtm-deep);
|
|
5628
|
+
border: 2px solid var(--dtm-border2);
|
|
5629
|
+
box-shadow: 4px 4px 0 var(--c-black);
|
|
5630
|
+
z-index: 200;
|
|
5631
|
+
display: flex;
|
|
5632
|
+
flex-direction: column;
|
|
5633
|
+
padding: 4px 0;
|
|
5634
|
+
min-width: 130px;
|
|
5635
|
+
font-family: var(--dtm-font);
|
|
5636
|
+
}
|
|
5637
|
+
.dtm-player-menu-item {
|
|
5638
|
+
background: transparent;
|
|
5639
|
+
border: none;
|
|
5640
|
+
color: var(--dtm-text);
|
|
5641
|
+
padding: 6px 12px;
|
|
5642
|
+
text-align: left;
|
|
5643
|
+
cursor: pointer;
|
|
5644
|
+
font-size: 11px;
|
|
5645
|
+
font-family: inherit;
|
|
5646
|
+
white-space: nowrap;
|
|
5647
|
+
width: 100%;
|
|
5648
|
+
box-sizing: border-box;
|
|
5649
|
+
transition: background-color 0.1s, color 0.1s;
|
|
5650
|
+
}
|
|
5651
|
+
.dtm-player-menu-item:hover {
|
|
5652
|
+
background: var(--dtm-primary);
|
|
5653
|
+
color: var(--dtm-pfg);
|
|
5654
|
+
}
|
|
5440
5655
|
.dtm-player-emoji {
|
|
5441
5656
|
position: relative;
|
|
5442
5657
|
display: inline-flex;
|
|
@@ -5712,6 +5927,75 @@ var showBalloon = (balloonEl) => {
|
|
|
5712
5927
|
hideActiveBalloon();
|
|
5713
5928
|
}, 3e3);
|
|
5714
5929
|
};
|
|
5930
|
+
var copyToClipboard = async (doc, text) => {
|
|
5931
|
+
try {
|
|
5932
|
+
await navigator.clipboard.writeText(text);
|
|
5933
|
+
return true;
|
|
5934
|
+
} catch {
|
|
5935
|
+
try {
|
|
5936
|
+
const textarea = doc.createElement("textarea");
|
|
5937
|
+
textarea.value = text;
|
|
5938
|
+
textarea.style.position = "fixed";
|
|
5939
|
+
textarea.style.opacity = "0";
|
|
5940
|
+
doc.body.appendChild(textarea);
|
|
5941
|
+
textarea.select();
|
|
5942
|
+
const ok = doc.execCommand("copy");
|
|
5943
|
+
doc.body.removeChild(textarea);
|
|
5944
|
+
return ok;
|
|
5945
|
+
} catch {
|
|
5946
|
+
return false;
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
};
|
|
5950
|
+
var toBase64Url = (bytes) => {
|
|
5951
|
+
let bin = "";
|
|
5952
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
5953
|
+
bin += String.fromCharCode(bytes[i]);
|
|
5954
|
+
}
|
|
5955
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
5956
|
+
};
|
|
5957
|
+
var HIRAGANA_START = 12353;
|
|
5958
|
+
var HIRAGANA_END = 12447;
|
|
5959
|
+
var KATAKANA_START = 12449;
|
|
5960
|
+
var KATAKANA_END = 12543;
|
|
5961
|
+
var PROLONGED_MARK = 12540;
|
|
5962
|
+
var SHIFT_KATAKANA = 255;
|
|
5963
|
+
var VALUE_PROLONGED = 223;
|
|
5964
|
+
var customEncode = (str) => {
|
|
5965
|
+
const bytes = [];
|
|
5966
|
+
for (let i = 0; i < str.length; i++) {
|
|
5967
|
+
const code = str.charCodeAt(i);
|
|
5968
|
+
if (code <= 127) {
|
|
5969
|
+
bytes.push(code);
|
|
5970
|
+
} else if (code === PROLONGED_MARK) {
|
|
5971
|
+
bytes.push(VALUE_PROLONGED);
|
|
5972
|
+
} else if (code >= HIRAGANA_START && code <= HIRAGANA_END) {
|
|
5973
|
+
bytes.push(128 + (code - HIRAGANA_START));
|
|
5974
|
+
} else if (code >= KATAKANA_START && code <= KATAKANA_END) {
|
|
5975
|
+
bytes.push(SHIFT_KATAKANA);
|
|
5976
|
+
bytes.push(128 + (code - 96 - HIRAGANA_START));
|
|
5977
|
+
}
|
|
5978
|
+
}
|
|
5979
|
+
return new Uint8Array(bytes);
|
|
5980
|
+
};
|
|
5981
|
+
var encodeMml = async (mml) => {
|
|
5982
|
+
try {
|
|
5983
|
+
if (typeof CompressionStream !== "undefined") {
|
|
5984
|
+
const cs = new CompressionStream("gzip");
|
|
5985
|
+
const w = cs.writable.getWriter();
|
|
5986
|
+
w.write(customEncode(mml));
|
|
5987
|
+
w.close();
|
|
5988
|
+
const buf = await new Response(cs.readable).arrayBuffer();
|
|
5989
|
+
return `z.${toBase64Url(new Uint8Array(buf))}`;
|
|
5990
|
+
}
|
|
5991
|
+
} catch (e) {
|
|
5992
|
+
console.warn(
|
|
5993
|
+
"[dtm] CompressionStream failed, fallback to encodeURIComponent",
|
|
5994
|
+
e
|
|
5995
|
+
);
|
|
5996
|
+
}
|
|
5997
|
+
return `u.${encodeURIComponent(mml)}`;
|
|
5998
|
+
};
|
|
5715
5999
|
var mountMmlPlayer = (target, mml, options = {}) => {
|
|
5716
6000
|
injectStyles(target.ownerDocument ?? document);
|
|
5717
6001
|
const {
|
|
@@ -5857,6 +6141,83 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
5857
6141
|
emojiEls.push(em);
|
|
5858
6142
|
emojiByTrack.set(index, em);
|
|
5859
6143
|
}
|
|
6144
|
+
const menuContainer = doc.createElement("div");
|
|
6145
|
+
menuContainer.className = "dtm-player-more-container";
|
|
6146
|
+
const moreBtn = doc.createElement("button");
|
|
6147
|
+
moreBtn.type = "button";
|
|
6148
|
+
moreBtn.className = "dtm-player-more-btn";
|
|
6149
|
+
moreBtn.innerHTML = icon("more", 14);
|
|
6150
|
+
moreBtn.title = "\u30E1\u30CB\u30E5\u30FC";
|
|
6151
|
+
menuContainer.appendChild(moreBtn);
|
|
6152
|
+
const menuDropdown = doc.createElement("div");
|
|
6153
|
+
menuDropdown.className = "dtm-player-menu";
|
|
6154
|
+
menuDropdown.style.display = "none";
|
|
6155
|
+
const copyMmlItem = doc.createElement("button");
|
|
6156
|
+
copyMmlItem.type = "button";
|
|
6157
|
+
copyMmlItem.className = "dtm-player-menu-item";
|
|
6158
|
+
copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
|
|
6159
|
+
const embedItem = doc.createElement("button");
|
|
6160
|
+
embedItem.type = "button";
|
|
6161
|
+
embedItem.className = "dtm-player-menu-item";
|
|
6162
|
+
embedItem.textContent = "\u57CB\u3081\u8FBC\u3080";
|
|
6163
|
+
menuDropdown.appendChild(copyMmlItem);
|
|
6164
|
+
menuDropdown.appendChild(embedItem);
|
|
6165
|
+
menuContainer.appendChild(menuDropdown);
|
|
6166
|
+
mmlHeader.appendChild(menuContainer);
|
|
6167
|
+
const toggleMenu = (show) => {
|
|
6168
|
+
const visible = show !== void 0 ? show : menuDropdown.style.display === "none";
|
|
6169
|
+
menuDropdown.style.display = visible ? "flex" : "none";
|
|
6170
|
+
if (visible) {
|
|
6171
|
+
moreBtn.classList.add("is-active");
|
|
6172
|
+
doc.addEventListener("click", handleOutsideClick);
|
|
6173
|
+
} else {
|
|
6174
|
+
moreBtn.classList.remove("is-active");
|
|
6175
|
+
doc.removeEventListener("click", handleOutsideClick);
|
|
6176
|
+
}
|
|
6177
|
+
};
|
|
6178
|
+
const handleOutsideClick = (e) => {
|
|
6179
|
+
if (!menuContainer.contains(e.target)) {
|
|
6180
|
+
toggleMenu(false);
|
|
6181
|
+
}
|
|
6182
|
+
};
|
|
6183
|
+
moreBtn.addEventListener("click", (e) => {
|
|
6184
|
+
e.stopPropagation();
|
|
6185
|
+
toggleMenu();
|
|
6186
|
+
});
|
|
6187
|
+
copyMmlItem.addEventListener("click", async (e) => {
|
|
6188
|
+
e.stopPropagation();
|
|
6189
|
+
const success = await copyToClipboard(doc, mml);
|
|
6190
|
+
if (success) {
|
|
6191
|
+
copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
|
|
6192
|
+
} else {
|
|
6193
|
+
copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
|
|
6194
|
+
}
|
|
6195
|
+
setTimeout(() => {
|
|
6196
|
+
copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
|
|
6197
|
+
}, 2e3);
|
|
6198
|
+
});
|
|
6199
|
+
embedItem.addEventListener("click", async (e) => {
|
|
6200
|
+
e.stopPropagation();
|
|
6201
|
+
embedItem.textContent = "\u751F\u6210\u4E2D...";
|
|
6202
|
+
try {
|
|
6203
|
+
const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
|
|
6204
|
+
const payload = await encodeMml(mml);
|
|
6205
|
+
const url2 = `${embedBase}#${payload}`;
|
|
6206
|
+
const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
|
|
6207
|
+
const success = await copyToClipboard(doc, snippet);
|
|
6208
|
+
if (success) {
|
|
6209
|
+
embedItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
|
|
6210
|
+
} else {
|
|
6211
|
+
embedItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
|
|
6212
|
+
}
|
|
6213
|
+
} catch (err2) {
|
|
6214
|
+
console.error("[dtm] failed to generate embed snippet", err2);
|
|
6215
|
+
embedItem.textContent = "\u751F\u6210\u5931\u6557";
|
|
6216
|
+
}
|
|
6217
|
+
setTimeout(() => {
|
|
6218
|
+
embedItem.textContent = "\u57CB\u3081\u8FBC\u3080";
|
|
6219
|
+
}, 2e3);
|
|
6220
|
+
});
|
|
5860
6221
|
const promotedToImage = /* @__PURE__ */ new Set();
|
|
5861
6222
|
for (const [index, lt] of lyricTracks) {
|
|
5862
6223
|
const em = emojiByTrack.get(index);
|
|
@@ -6165,7 +6526,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6165
6526
|
onTick: (step) => {
|
|
6166
6527
|
renderPlayhead(step);
|
|
6167
6528
|
},
|
|
6168
|
-
onEnd: () => finish(),
|
|
6529
|
+
onEnd: (interrupted) => finish(),
|
|
6169
6530
|
stepsPerBar: STEPS_PER_BAR
|
|
6170
6531
|
});
|
|
6171
6532
|
let playing = false;
|
|
@@ -6260,6 +6621,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6260
6621
|
else play();
|
|
6261
6622
|
});
|
|
6262
6623
|
const destroy = () => {
|
|
6624
|
+
doc.removeEventListener("click", handleOutsideClick);
|
|
6263
6625
|
seq.stop();
|
|
6264
6626
|
peekVoices()?.stopStream();
|
|
6265
6627
|
if (activePlayer === instance) activePlayer = null;
|
|
@@ -7178,6 +7540,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
7178
7540
|
0,
|
|
7179
7541
|
Math.floor(step / snapGridSteps) * snapGridSteps
|
|
7180
7542
|
);
|
|
7543
|
+
if (playbackState === "paused") {
|
|
7544
|
+
playbackState = "stopped";
|
|
7545
|
+
updateTransport();
|
|
7546
|
+
}
|
|
7181
7547
|
redrawAll();
|
|
7182
7548
|
});
|
|
7183
7549
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
@@ -7253,9 +7619,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
7253
7619
|
}
|
|
7254
7620
|
redrawAll();
|
|
7255
7621
|
},
|
|
7256
|
-
onEnd: () => {
|
|
7257
|
-
|
|
7258
|
-
|
|
7622
|
+
onEnd: (interrupted) => {
|
|
7623
|
+
if (interrupted) {
|
|
7624
|
+
playbackState = "paused";
|
|
7625
|
+
pausedPlayStep = currentPlayStep;
|
|
7626
|
+
} else {
|
|
7627
|
+
playbackState = "stopped";
|
|
7628
|
+
currentPlayStep = 0;
|
|
7629
|
+
}
|
|
7259
7630
|
updateTransport();
|
|
7260
7631
|
redrawAll();
|
|
7261
7632
|
},
|
|
@@ -7345,13 +7716,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
7345
7716
|
redrawAll();
|
|
7346
7717
|
};
|
|
7347
7718
|
const togglePlay = () => {
|
|
7348
|
-
if (playbackState === "playing")
|
|
7719
|
+
if (playbackState === "playing") pause();
|
|
7349
7720
|
else play();
|
|
7350
7721
|
};
|
|
7351
7722
|
const updateTransport = () => {
|
|
7352
7723
|
const playing = playbackState === "playing";
|
|
7353
|
-
const label = playing ? "\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
|
|
7354
|
-
refs.playBtn.innerHTML = `${icon(playing ? "
|
|
7724
|
+
const label = playing ? "\u4E00\u6642\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
|
|
7725
|
+
refs.playBtn.innerHTML = `${icon(playing ? "pause" : "play")}<span>${label}</span>`;
|
|
7355
7726
|
refs.playBtn.classList.toggle("dtm-play--stop", playing);
|
|
7356
7727
|
};
|
|
7357
7728
|
const updateUndoRedo = () => {
|
|
@@ -7740,20 +8111,25 @@ var mountDAW = (target, options = {}) => {
|
|
|
7740
8111
|
};
|
|
7741
8112
|
const loadMML = (mml) => {
|
|
7742
8113
|
if (!mml) return;
|
|
8114
|
+
stop();
|
|
7743
8115
|
clearAll();
|
|
7744
8116
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
7745
8117
|
const {
|
|
7746
8118
|
placements,
|
|
7747
8119
|
bpm: parsedBpm,
|
|
7748
8120
|
lyrics,
|
|
7749
|
-
meta
|
|
8121
|
+
meta,
|
|
8122
|
+
mergedTrackCount
|
|
7750
8123
|
} = parseMML(mml, {
|
|
7751
8124
|
stepsPerBar: renderConfig.stepsPerBar,
|
|
7752
8125
|
collectLyrics: true,
|
|
7753
8126
|
// このDAWのトラック数を超えるチャンネルはベースへ畳み込む(従来挙動)
|
|
7754
8127
|
clampTrackCount: trackStates.length
|
|
7755
8128
|
});
|
|
7756
|
-
|
|
8129
|
+
if (meta.instrument && INSTRUMENT_PRESETS[meta.instrument]) {
|
|
8130
|
+
currentInstrument = meta.instrument;
|
|
8131
|
+
options.onInstrumentChange?.(meta.instrument);
|
|
8132
|
+
}
|
|
7757
8133
|
if (meta.drum && drumPatterns[meta.drum]) {
|
|
7758
8134
|
currentDrumPattern = meta.drum;
|
|
7759
8135
|
refs.drumSelect.value = meta.drum;
|
|
@@ -7805,6 +8181,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
7805
8181
|
redrawAll();
|
|
7806
8182
|
updateTrackPanel();
|
|
7807
8183
|
updateUndoRedo();
|
|
8184
|
+
if (!isAdvanced && mergedTrackCount > 0) {
|
|
8185
|
+
refs.mmlLoadNote.textContent = "\u30B7\u30F3\u30D7\u30EB\u30E2\u30FC\u30C9\u306E\u305F\u3081\u3001\u4E00\u90E8\u306E\u30C8\u30E9\u30C3\u30AF\u3092\u5408\u7B97\u3057\u3066\u8AAD\u307F\u8FBC\u307F\u307E\u3057\u305F";
|
|
8186
|
+
refs.mmlLoadNote.classList.remove("dtm-hidden");
|
|
8187
|
+
} else {
|
|
8188
|
+
refs.mmlLoadNote.textContent = "";
|
|
8189
|
+
refs.mmlLoadNote.classList.add("dtm-hidden");
|
|
8190
|
+
}
|
|
7808
8191
|
};
|
|
7809
8192
|
const applyChord = () => {
|
|
7810
8193
|
const active = getActive();
|
|
@@ -7837,6 +8220,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7837
8220
|
applyMidiSelection(midi, selected);
|
|
7838
8221
|
};
|
|
7839
8222
|
const applyMidiSelection = (midi, selectedIndices) => {
|
|
8223
|
+
stop();
|
|
7840
8224
|
clearAll();
|
|
7841
8225
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
7842
8226
|
const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
|
|
@@ -8268,6 +8652,25 @@ var mountDAW = (target, options = {}) => {
|
|
|
8268
8652
|
const setLoading = (loading) => {
|
|
8269
8653
|
refs.topbar.classList.toggle("is-loading", loading);
|
|
8270
8654
|
};
|
|
8655
|
+
const getCurrentPlayStep = () => {
|
|
8656
|
+
if (playbackState === "playing") return currentPlayStep;
|
|
8657
|
+
if (playbackState === "paused") return pausedPlayStep;
|
|
8658
|
+
return playStartStep;
|
|
8659
|
+
};
|
|
8660
|
+
const forcePauseAt = (step) => {
|
|
8661
|
+
playStartStep = step;
|
|
8662
|
+
pausedPlayStep = step;
|
|
8663
|
+
currentPlayStep = step;
|
|
8664
|
+
playbackState = "paused";
|
|
8665
|
+
const canvas = getGridCanvas();
|
|
8666
|
+
currentOffsetX = Math.max(
|
|
8667
|
+
0,
|
|
8668
|
+
step * renderConfig.stepWidth - canvas.width * 0.5
|
|
8669
|
+
);
|
|
8670
|
+
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8671
|
+
updateTransport();
|
|
8672
|
+
redrawAll();
|
|
8673
|
+
};
|
|
8271
8674
|
return {
|
|
8272
8675
|
play,
|
|
8273
8676
|
pause,
|
|
@@ -8305,9 +8708,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
8305
8708
|
exportMIDI: exportMIDI2,
|
|
8306
8709
|
setBpm,
|
|
8307
8710
|
getPlaybackState: () => playbackState,
|
|
8711
|
+
getCurrentPlayStep,
|
|
8712
|
+
forcePauseAt,
|
|
8308
8713
|
setLoading,
|
|
8309
8714
|
destroy: () => {
|
|
8310
8715
|
sequencer.stop();
|
|
8716
|
+
options.singingVoices?.stopStream();
|
|
8311
8717
|
resizeObserver.disconnect();
|
|
8312
8718
|
document.removeEventListener("pointermove", onPointerMove);
|
|
8313
8719
|
document.removeEventListener("pointerup", onPointerUp);
|
|
@@ -8317,110 +8723,6 @@ var mountDAW = (target, options = {}) => {
|
|
|
8317
8723
|
};
|
|
8318
8724
|
};
|
|
8319
8725
|
|
|
8320
|
-
// src/instrument-presets.ts
|
|
8321
|
-
var INSTRUMENT_PRESETS = {
|
|
8322
|
-
// --- STANDARD: 汎用性と完成度重視 ---
|
|
8323
|
-
piano: {
|
|
8324
|
-
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
8325
|
-
description: "\u6700\u3082\u7834\u7DBB\u3057\u306B\u304F\u3044\u69CB\u6210\u3002\u697D\u66F2\u5236\u4F5C\u306E\u30B9\u30B1\u30C3\u30C1\u306B\u3082\u6700\u9069\u3002",
|
|
8326
|
-
melody: "Acoustic Grand Piano",
|
|
8327
|
-
submelody: "Vibraphone",
|
|
8328
|
-
bass: "Electric Bass (finger)",
|
|
8329
|
-
chord: "Pad 2 (warm)"
|
|
8330
|
-
},
|
|
8331
|
-
acoustic: {
|
|
8332
|
-
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
8333
|
-
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
8334
|
-
melody: "Acoustic Guitar (steel)",
|
|
8335
|
-
submelody: "Harmonica",
|
|
8336
|
-
bass: "Acoustic Bass",
|
|
8337
|
-
chord: "Acoustic Guitar (nylon)"
|
|
8338
|
-
},
|
|
8339
|
-
jazz_night: {
|
|
8340
|
-
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
8341
|
-
description: "Rhodes\u98A8\u306EEP\u3068\u30A6\u30C3\u30C9\u30D9\u30FC\u30B9\u306B\u3088\u308B\u3001\u5927\u4EBA\u3073\u305F\u30A2\u30F3\u30B5\u30F3\u30D6\u30EB\u3002",
|
|
8342
|
-
melody: "Electric Piano 1",
|
|
8343
|
-
submelody: "Flute",
|
|
8344
|
-
bass: "Acoustic Bass",
|
|
8345
|
-
chord: "Electric Guitar (jazz)"
|
|
8346
|
-
},
|
|
8347
|
-
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
8348
|
-
synth_pop: {
|
|
8349
|
-
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
8350
|
-
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
8351
|
-
melody: "Lead 2 (sawtooth)",
|
|
8352
|
-
submelody: "Lead 4 (chiff)",
|
|
8353
|
-
bass: "Synth Bass 2",
|
|
8354
|
-
chord: "Pad 3 (polysynth)"
|
|
8355
|
-
},
|
|
8356
|
-
cyber_punk: {
|
|
8357
|
-
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
8358
|
-
description: "\u30C7\u30B8\u30BF\u30EB\u306A\u51B7\u305F\u3055\u3068\u6B6A\u307F\u304C\u6DF7\u3056\u308A\u5408\u3046\u3001\u672A\u6765\u7684\u306A\u97FF\u304D\u3002",
|
|
8359
|
-
melody: "Lead 8 (bass + lead)",
|
|
8360
|
-
submelody: "Lead 5 (charang)",
|
|
8361
|
-
bass: "Synth Bass 2",
|
|
8362
|
-
chord: "Pad 8 (sweep)"
|
|
8363
|
-
},
|
|
8364
|
-
rock: {
|
|
8365
|
-
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
8366
|
-
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
8367
|
-
melody: "Distortion Guitar",
|
|
8368
|
-
submelody: "Rock Organ",
|
|
8369
|
-
bass: "Electric Bass (pick)",
|
|
8370
|
-
chord: "Overdriven Guitar"
|
|
8371
|
-
},
|
|
8372
|
-
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
8373
|
-
orchestra: {
|
|
8374
|
-
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
8375
|
-
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
8376
|
-
melody: "French Horn",
|
|
8377
|
-
submelody: "Pizzicato Strings",
|
|
8378
|
-
bass: "Cello",
|
|
8379
|
-
chord: "Tremolo Strings"
|
|
8380
|
-
},
|
|
8381
|
-
japanese_wa: {
|
|
8382
|
-
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
8383
|
-
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
8384
|
-
melody: "Koto",
|
|
8385
|
-
submelody: "Shamisen",
|
|
8386
|
-
bass: "Taiko Drum",
|
|
8387
|
-
chord: "Shakuhachi"
|
|
8388
|
-
},
|
|
8389
|
-
arabic_exotic: {
|
|
8390
|
-
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
8391
|
-
description: "\u30B7\u30BF\u30FC\u30EB\u3084\u30D0\u30B0\u30D1\u30A4\u30D7\u306B\u3088\u308B\u3001\u7570\u56FD\u60C5\u7DD2\u6EA2\u308C\u308B\u30B5\u30A6\u30F3\u30C9\u3002",
|
|
8392
|
-
melody: "Sitar",
|
|
8393
|
-
submelody: "Bagpipe",
|
|
8394
|
-
bass: "Fretless Bass",
|
|
8395
|
-
chord: "Kalimba"
|
|
8396
|
-
},
|
|
8397
|
-
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
8398
|
-
fantasy_rpg: {
|
|
8399
|
-
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
8400
|
-
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
8401
|
-
melody: "Ocarina",
|
|
8402
|
-
submelody: "Celesta",
|
|
8403
|
-
bass: "Timpani",
|
|
8404
|
-
chord: "Orchestral Harp"
|
|
8405
|
-
},
|
|
8406
|
-
ambient_cloud: {
|
|
8407
|
-
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
8408
|
-
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
8409
|
-
melody: "Lead 6 (voice)",
|
|
8410
|
-
submelody: "Music Box",
|
|
8411
|
-
bass: "Synth Bass 1",
|
|
8412
|
-
chord: "Pad 7 (halo)"
|
|
8413
|
-
},
|
|
8414
|
-
retro_game: {
|
|
8415
|
-
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
8416
|
-
description: "\u77E9\u5F62\u6CE2\u3092\u60F3\u8D77\u3055\u305B\u308B\u3001\u521D\u671F\u30B2\u30FC\u30E0\u6A5F\u306E\u3088\u3046\u306A\u61D0\u304B\u3057\u3044\u97FF\u304D\u3002",
|
|
8417
|
-
melody: "Lead 1 (square)",
|
|
8418
|
-
submelody: "Lead 2 (sawtooth)",
|
|
8419
|
-
bass: "Synth Bass 1",
|
|
8420
|
-
chord: "Clavinet"
|
|
8421
|
-
}
|
|
8422
|
-
};
|
|
8423
|
-
|
|
8424
8726
|
// src/headless-player.ts
|
|
8425
8727
|
var STEPS_PER_BAR2 = 192;
|
|
8426
8728
|
var playMML = (mml, options = {}) => {
|
|
@@ -8471,7 +8773,7 @@ var playMML = (mml, options = {}) => {
|
|
|
8471
8773
|
},
|
|
8472
8774
|
onTick: () => {
|
|
8473
8775
|
},
|
|
8474
|
-
onEnd: () => finish(),
|
|
8776
|
+
onEnd: (interrupted) => finish(),
|
|
8475
8777
|
stepsPerBar: STEPS_PER_BAR2
|
|
8476
8778
|
});
|
|
8477
8779
|
const finish = () => {
|
|
@@ -9292,11 +9594,13 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9292
9594
|
const mountedPlayers = [];
|
|
9293
9595
|
const mountedModeSwitches = [];
|
|
9294
9596
|
const mountEditor = (target, opts = {}) => {
|
|
9295
|
-
const { preset, presetUI, ...dawOverrides } = opts;
|
|
9597
|
+
const { preset, presetUI, onInstrumentChange, ...dawOverrides } = opts;
|
|
9296
9598
|
const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
|
|
9297
9599
|
const trackIds = tracks.map((t) => t.id);
|
|
9298
9600
|
const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
|
|
9299
|
-
|
|
9601
|
+
const meta = opts.initialMML ? parseMmlMeta(opts.initialMML) : {};
|
|
9602
|
+
const initialPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : presetKey;
|
|
9603
|
+
let editorPreset = initialPreset;
|
|
9300
9604
|
const isAdvancedMode = dawOverrides.mode === "advanced";
|
|
9301
9605
|
const playNote = (e) => {
|
|
9302
9606
|
const sf = resolveSoundFont(
|
|
@@ -9314,6 +9618,14 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9314
9618
|
duration: e.duration
|
|
9315
9619
|
});
|
|
9316
9620
|
};
|
|
9621
|
+
let presetSelect = null;
|
|
9622
|
+
const handleInstrumentChange = (key) => {
|
|
9623
|
+
editorPreset = key;
|
|
9624
|
+
if (presetSelect) {
|
|
9625
|
+
presetSelect.setValue(key);
|
|
9626
|
+
}
|
|
9627
|
+
onInstrumentChange?.(key);
|
|
9628
|
+
};
|
|
9317
9629
|
const base = {
|
|
9318
9630
|
getAudioTime: () => audioCtx.currentTime,
|
|
9319
9631
|
onResumeAudio: resumeAudio,
|
|
@@ -9322,19 +9634,19 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9322
9634
|
singingVoices,
|
|
9323
9635
|
parseMidi,
|
|
9324
9636
|
onToggleRecord,
|
|
9637
|
+
onInstrumentChange: handleInstrumentChange,
|
|
9325
9638
|
...dawOverrides
|
|
9326
9639
|
};
|
|
9327
9640
|
const daw = mountDAW(target, base);
|
|
9328
9641
|
mountedEditors.push(daw);
|
|
9329
9642
|
const wantPresetUI = presetUI ?? features.presetUI;
|
|
9330
|
-
let presetSelect = null;
|
|
9331
9643
|
if (wantPresetUI) {
|
|
9332
9644
|
editorPresetSelects.get(target)?.destroy();
|
|
9333
9645
|
const rollEl = target.querySelector('[data-dtm="roll"]');
|
|
9334
9646
|
presetSelect = mountPresetSelect(target, {
|
|
9335
9647
|
getDaw: () => daw,
|
|
9336
9648
|
getTrackIds: () => trackIds,
|
|
9337
|
-
value:
|
|
9649
|
+
value: initialPreset,
|
|
9338
9650
|
loadingTarget: rollEl ?? target,
|
|
9339
9651
|
position: "prepend",
|
|
9340
9652
|
// 楽器変更時、このエディタの発音解決が使うプリセットも追従させる。
|
|
@@ -9344,10 +9656,10 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9344
9656
|
});
|
|
9345
9657
|
editorPresetSelects.set(target, presetSelect);
|
|
9346
9658
|
}
|
|
9347
|
-
daw.setInstrument(
|
|
9659
|
+
daw.setInstrument(initialPreset);
|
|
9348
9660
|
daw.setLoading?.(true);
|
|
9349
9661
|
void loadPreset(
|
|
9350
|
-
|
|
9662
|
+
initialPreset,
|
|
9351
9663
|
trackIds,
|
|
9352
9664
|
isAdvancedMode ? "advanced" : "simple"
|
|
9353
9665
|
).finally(() => {
|
|
@@ -9361,7 +9673,17 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9361
9673
|
const i = mountedEditors.indexOf(daw);
|
|
9362
9674
|
if (i >= 0) mountedEditors.splice(i, 1);
|
|
9363
9675
|
};
|
|
9364
|
-
return {
|
|
9676
|
+
return {
|
|
9677
|
+
...daw,
|
|
9678
|
+
setInstrument: (name) => {
|
|
9679
|
+
daw.setInstrument(name);
|
|
9680
|
+
editorPreset = name;
|
|
9681
|
+
if (presetSelect) {
|
|
9682
|
+
presetSelect.setValue(name);
|
|
9683
|
+
}
|
|
9684
|
+
},
|
|
9685
|
+
destroy
|
|
9686
|
+
};
|
|
9365
9687
|
};
|
|
9366
9688
|
const mountModeSwitch = (target, opts) => {
|
|
9367
9689
|
const doc = target.ownerDocument;
|