@onjmin/dtm 0.1.22 → 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 +20 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.js +517 -138
- package/dist/index.mjs +517 -138
- 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) {
|
|
@@ -4028,7 +4137,7 @@ var PITCH_MAP2 = {
|
|
|
4028
4137
|
b: 11
|
|
4029
4138
|
};
|
|
4030
4139
|
var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
4031
|
-
var META_DIRECTIVE = /#(inst|drum|volume)=([\w-]+)/gi;
|
|
4140
|
+
var META_DIRECTIVE = /#(inst|drum|volume|mode)=([\w-]+)/gi;
|
|
4032
4141
|
var parseMmlMeta = (mml) => {
|
|
4033
4142
|
const meta = {};
|
|
4034
4143
|
for (const m of mml.matchAll(META_DIRECTIVE)) {
|
|
@@ -4038,6 +4147,10 @@ var parseMmlMeta = (mml) => {
|
|
|
4038
4147
|
else if (key === "volume") {
|
|
4039
4148
|
const v = Number.parseInt(m[2], 10);
|
|
4040
4149
|
if (!Number.isNaN(v)) meta.volume = v;
|
|
4150
|
+
} else if (key === "mode") {
|
|
4151
|
+
if (m[2] === "simple" || m[2] === "advanced") {
|
|
4152
|
+
meta.mode = m[2];
|
|
4153
|
+
}
|
|
4041
4154
|
}
|
|
4042
4155
|
}
|
|
4043
4156
|
return meta;
|
|
@@ -4048,6 +4161,7 @@ var formatMmlMeta = (meta) => {
|
|
|
4048
4161
|
if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
|
|
4049
4162
|
if (meta.drum) parts.push(`#drum=${meta.drum}`);
|
|
4050
4163
|
if (meta.volume !== void 0) parts.push(`#volume=${meta.volume}`);
|
|
4164
|
+
if (meta.mode) parts.push(`#mode=${meta.mode}`);
|
|
4051
4165
|
return parts.join(" ");
|
|
4052
4166
|
};
|
|
4053
4167
|
var parseMML = (mml, options = {}) => {
|
|
@@ -4064,6 +4178,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4064
4178
|
bpm,
|
|
4065
4179
|
tokenTracks: collectTokens ? tokenTracks : void 0,
|
|
4066
4180
|
lyrics: collectLyrics ? /* @__PURE__ */ new Map() : void 0,
|
|
4181
|
+
mergedTrackCount: 0,
|
|
4067
4182
|
meta: {}
|
|
4068
4183
|
};
|
|
4069
4184
|
}
|
|
@@ -4076,14 +4191,26 @@ var parseMML = (mml, options = {}) => {
|
|
|
4076
4191
|
const fullMML = stripLyrics(noMeta).replace(endRegex, "").replace(/[\n\r]+/g, " ").trim();
|
|
4077
4192
|
const parts = fullMML.split(/(@\d+)/).filter((p) => p.trim().length > 0);
|
|
4078
4193
|
let trackIndex = 0;
|
|
4194
|
+
let sourceTrackIndex = 0;
|
|
4079
4195
|
let octave = 4;
|
|
4080
4196
|
let currentStep = 0;
|
|
4081
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
|
+
};
|
|
4082
4207
|
for (const rawPart of parts) {
|
|
4083
4208
|
const part = rawPart.trim();
|
|
4084
4209
|
if (part.startsWith("@")) {
|
|
4085
4210
|
let idx = Number.parseInt(part.substring(1), 10);
|
|
4086
|
-
|
|
4211
|
+
sourceTrackIndex = idx;
|
|
4212
|
+
if (clampTrackCount !== void 0 && idx >= clampTrackCount)
|
|
4213
|
+
idx = clampTrackCount - 1;
|
|
4087
4214
|
trackIndex = idx;
|
|
4088
4215
|
octave = 4;
|
|
4089
4216
|
currentStep = 0;
|
|
@@ -4205,6 +4332,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4205
4332
|
}
|
|
4206
4333
|
if (j < body.length && body[j] === "]") j++;
|
|
4207
4334
|
const steps = parseLength();
|
|
4335
|
+
if (chordNotes.length > 0) recordContributor();
|
|
4208
4336
|
for (const p of chordNotes) {
|
|
4209
4337
|
placements.push({
|
|
4210
4338
|
trackIndex,
|
|
@@ -4228,6 +4356,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4228
4356
|
}
|
|
4229
4357
|
const midiPitch = (octave + 1) * 12 + pitch;
|
|
4230
4358
|
const steps = parseLength();
|
|
4359
|
+
recordContributor();
|
|
4231
4360
|
placements.push({
|
|
4232
4361
|
trackIndex,
|
|
4233
4362
|
startStep: currentStep,
|
|
@@ -4241,11 +4370,16 @@ var parseMML = (mml, options = {}) => {
|
|
|
4241
4370
|
}
|
|
4242
4371
|
}
|
|
4243
4372
|
}
|
|
4373
|
+
let mergedTrackCount = 0;
|
|
4374
|
+
for (const set of contributors.values()) {
|
|
4375
|
+
if (set.size >= 2) mergedTrackCount++;
|
|
4376
|
+
}
|
|
4244
4377
|
return {
|
|
4245
4378
|
placements,
|
|
4246
4379
|
bpm,
|
|
4247
4380
|
tokenTracks: collectTokens ? tokenTracks : void 0,
|
|
4248
4381
|
lyrics,
|
|
4382
|
+
mergedTrackCount,
|
|
4249
4383
|
meta
|
|
4250
4384
|
};
|
|
4251
4385
|
};
|
|
@@ -4307,6 +4441,8 @@ var createSequencer = (options) => {
|
|
|
4307
4441
|
let active = false;
|
|
4308
4442
|
let fromStepValue = 0;
|
|
4309
4443
|
let trackVolumeMap = /* @__PURE__ */ new Map();
|
|
4444
|
+
let lastRealTime = -1;
|
|
4445
|
+
let lastAudioTime = -1;
|
|
4310
4446
|
let isLooping = false;
|
|
4311
4447
|
let loopStartStep = 0;
|
|
4312
4448
|
let loopEndStep = 0;
|
|
@@ -4380,6 +4516,21 @@ var createSequencer = (options) => {
|
|
|
4380
4516
|
const sps = secondsPerStep();
|
|
4381
4517
|
const time = options.getAudioTime() - startTime;
|
|
4382
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;
|
|
4383
4534
|
for (const track of options.getTracks()) {
|
|
4384
4535
|
trackVolumeMap.set(track.id, track.volume);
|
|
4385
4536
|
}
|
|
@@ -4456,7 +4607,7 @@ var createSequencer = (options) => {
|
|
|
4456
4607
|
const lastDuration = last?.duration ?? 0;
|
|
4457
4608
|
if (nowIndex >= timeline.length && time > lastWhen + lastDuration + 0.1) {
|
|
4458
4609
|
stop();
|
|
4459
|
-
options.onEnd();
|
|
4610
|
+
options.onEnd(false);
|
|
4460
4611
|
}
|
|
4461
4612
|
}
|
|
4462
4613
|
};
|
|
@@ -4497,6 +4648,8 @@ var createSequencer = (options) => {
|
|
|
4497
4648
|
}
|
|
4498
4649
|
loopBase = 0;
|
|
4499
4650
|
lastPlayStep = fromStepValue - 1e-4;
|
|
4651
|
+
lastRealTime = -1;
|
|
4652
|
+
lastAudioTime = -1;
|
|
4500
4653
|
intervalId = setInterval(scheduleTick, TICK_INTERVAL_MS);
|
|
4501
4654
|
animationId = requestAnimationFrame(animate);
|
|
4502
4655
|
};
|
|
@@ -4694,11 +4847,11 @@ var DAW_CSS = `
|
|
|
4694
4847
|
}
|
|
4695
4848
|
.dtm-btn:active { transform: translate(3px,3px); box-shadow: none; }
|
|
4696
4849
|
.dtm-btn:disabled { opacity: .3; cursor: default; box-shadow: none; }
|
|
4850
|
+
.dtm-btn--ghost { background: transparent; border-color: var(--dtm-border2); }
|
|
4697
4851
|
.dtm-btn--primary { border-color: var(--dtm-primary); background: var(--dtm-primary); color: var(--dtm-pfg); }
|
|
4698
4852
|
.dtm-btn--success { border-color: var(--dtm-success); background: var(--dtm-success); color: var(--c-black); }
|
|
4699
4853
|
.dtm-btn--danger { border-color: var(--dtm-danger); background: var(--dtm-danger); color: var(--c-white); }
|
|
4700
4854
|
.dtm-btn--accent { border-color: var(--dtm-accent); background: var(--dtm-accent); color: var(--c-black); }
|
|
4701
|
-
.dtm-btn--ghost { background: transparent; border-color: var(--dtm-border2); }
|
|
4702
4855
|
.dtm-btn--icon { padding: 0; }
|
|
4703
4856
|
|
|
4704
4857
|
/* \u2500\u2500\u2500 \u30A2\u30A4\u30B3\u30F3\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */
|
|
@@ -5332,6 +5485,18 @@ var DAW_CSS = `
|
|
|
5332
5485
|
}
|
|
5333
5486
|
|
|
5334
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 "; }
|
|
5335
5500
|
.dtm-grow { flex: 1 1 auto; }
|
|
5336
5501
|
.dtm-lyric-icon {
|
|
5337
5502
|
flex: 0 0 auto;
|
|
@@ -5431,6 +5596,62 @@ var DAW_CSS = `
|
|
|
5431
5596
|
white-space: nowrap;
|
|
5432
5597
|
}
|
|
5433
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
|
+
}
|
|
5434
5655
|
.dtm-player-emoji {
|
|
5435
5656
|
position: relative;
|
|
5436
5657
|
display: inline-flex;
|
|
@@ -5706,6 +5927,75 @@ var showBalloon = (balloonEl) => {
|
|
|
5706
5927
|
hideActiveBalloon();
|
|
5707
5928
|
}, 3e3);
|
|
5708
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
|
+
};
|
|
5709
5999
|
var mountMmlPlayer = (target, mml, options = {}) => {
|
|
5710
6000
|
injectStyles(target.ownerDocument ?? document);
|
|
5711
6001
|
const {
|
|
@@ -5851,6 +6141,83 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
5851
6141
|
emojiEls.push(em);
|
|
5852
6142
|
emojiByTrack.set(index, em);
|
|
5853
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
|
+
});
|
|
5854
6221
|
const promotedToImage = /* @__PURE__ */ new Set();
|
|
5855
6222
|
for (const [index, lt] of lyricTracks) {
|
|
5856
6223
|
const em = emojiByTrack.get(index);
|
|
@@ -6159,7 +6526,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6159
6526
|
onTick: (step) => {
|
|
6160
6527
|
renderPlayhead(step);
|
|
6161
6528
|
},
|
|
6162
|
-
onEnd: () => finish(),
|
|
6529
|
+
onEnd: (interrupted) => finish(),
|
|
6163
6530
|
stepsPerBar: STEPS_PER_BAR
|
|
6164
6531
|
});
|
|
6165
6532
|
let playing = false;
|
|
@@ -6254,6 +6621,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6254
6621
|
else play();
|
|
6255
6622
|
});
|
|
6256
6623
|
const destroy = () => {
|
|
6624
|
+
doc.removeEventListener("click", handleOutsideClick);
|
|
6257
6625
|
seq.stop();
|
|
6258
6626
|
peekVoices()?.stopStream();
|
|
6259
6627
|
if (activePlayer === instance) activePlayer = null;
|
|
@@ -7172,6 +7540,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
7172
7540
|
0,
|
|
7173
7541
|
Math.floor(step / snapGridSteps) * snapGridSteps
|
|
7174
7542
|
);
|
|
7543
|
+
if (playbackState === "paused") {
|
|
7544
|
+
playbackState = "stopped";
|
|
7545
|
+
updateTransport();
|
|
7546
|
+
}
|
|
7175
7547
|
redrawAll();
|
|
7176
7548
|
});
|
|
7177
7549
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
@@ -7247,9 +7619,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
7247
7619
|
}
|
|
7248
7620
|
redrawAll();
|
|
7249
7621
|
},
|
|
7250
|
-
onEnd: () => {
|
|
7251
|
-
|
|
7252
|
-
|
|
7622
|
+
onEnd: (interrupted) => {
|
|
7623
|
+
if (interrupted) {
|
|
7624
|
+
playbackState = "paused";
|
|
7625
|
+
pausedPlayStep = currentPlayStep;
|
|
7626
|
+
} else {
|
|
7627
|
+
playbackState = "stopped";
|
|
7628
|
+
currentPlayStep = 0;
|
|
7629
|
+
}
|
|
7253
7630
|
updateTransport();
|
|
7254
7631
|
redrawAll();
|
|
7255
7632
|
},
|
|
@@ -7339,13 +7716,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
7339
7716
|
redrawAll();
|
|
7340
7717
|
};
|
|
7341
7718
|
const togglePlay = () => {
|
|
7342
|
-
if (playbackState === "playing")
|
|
7719
|
+
if (playbackState === "playing") pause();
|
|
7343
7720
|
else play();
|
|
7344
7721
|
};
|
|
7345
7722
|
const updateTransport = () => {
|
|
7346
7723
|
const playing = playbackState === "playing";
|
|
7347
|
-
const label = playing ? "\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
|
|
7348
|
-
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>`;
|
|
7349
7726
|
refs.playBtn.classList.toggle("dtm-play--stop", playing);
|
|
7350
7727
|
};
|
|
7351
7728
|
const updateUndoRedo = () => {
|
|
@@ -7611,7 +7988,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
7611
7988
|
const metaLine = formatMmlMeta({
|
|
7612
7989
|
instrument: currentInstrument || void 0,
|
|
7613
7990
|
drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
|
|
7614
|
-
volume: masterVolume
|
|
7991
|
+
volume: masterVolume,
|
|
7992
|
+
mode
|
|
7615
7993
|
});
|
|
7616
7994
|
if (refs.decomposeChordToggle.checked) {
|
|
7617
7995
|
const ignoreHeavy = refs.ignoreChordHeavyToggle.checked;
|
|
@@ -7733,20 +8111,25 @@ var mountDAW = (target, options = {}) => {
|
|
|
7733
8111
|
};
|
|
7734
8112
|
const loadMML = (mml) => {
|
|
7735
8113
|
if (!mml) return;
|
|
8114
|
+
stop();
|
|
7736
8115
|
clearAll();
|
|
7737
8116
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
7738
8117
|
const {
|
|
7739
8118
|
placements,
|
|
7740
8119
|
bpm: parsedBpm,
|
|
7741
8120
|
lyrics,
|
|
7742
|
-
meta
|
|
8121
|
+
meta,
|
|
8122
|
+
mergedTrackCount
|
|
7743
8123
|
} = parseMML(mml, {
|
|
7744
8124
|
stepsPerBar: renderConfig.stepsPerBar,
|
|
7745
8125
|
collectLyrics: true,
|
|
7746
8126
|
// このDAWのトラック数を超えるチャンネルはベースへ畳み込む(従来挙動)
|
|
7747
8127
|
clampTrackCount: trackStates.length
|
|
7748
8128
|
});
|
|
7749
|
-
|
|
8129
|
+
if (meta.instrument && INSTRUMENT_PRESETS[meta.instrument]) {
|
|
8130
|
+
currentInstrument = meta.instrument;
|
|
8131
|
+
options.onInstrumentChange?.(meta.instrument);
|
|
8132
|
+
}
|
|
7750
8133
|
if (meta.drum && drumPatterns[meta.drum]) {
|
|
7751
8134
|
currentDrumPattern = meta.drum;
|
|
7752
8135
|
refs.drumSelect.value = meta.drum;
|
|
@@ -7798,6 +8181,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
7798
8181
|
redrawAll();
|
|
7799
8182
|
updateTrackPanel();
|
|
7800
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
|
+
}
|
|
7801
8191
|
};
|
|
7802
8192
|
const applyChord = () => {
|
|
7803
8193
|
const active = getActive();
|
|
@@ -7830,6 +8220,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7830
8220
|
applyMidiSelection(midi, selected);
|
|
7831
8221
|
};
|
|
7832
8222
|
const applyMidiSelection = (midi, selectedIndices) => {
|
|
8223
|
+
stop();
|
|
7833
8224
|
clearAll();
|
|
7834
8225
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
7835
8226
|
const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
|
|
@@ -8261,6 +8652,25 @@ var mountDAW = (target, options = {}) => {
|
|
|
8261
8652
|
const setLoading = (loading) => {
|
|
8262
8653
|
refs.topbar.classList.toggle("is-loading", loading);
|
|
8263
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
|
+
};
|
|
8264
8674
|
return {
|
|
8265
8675
|
play,
|
|
8266
8676
|
pause,
|
|
@@ -8298,9 +8708,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
8298
8708
|
exportMIDI: exportMIDI2,
|
|
8299
8709
|
setBpm,
|
|
8300
8710
|
getPlaybackState: () => playbackState,
|
|
8711
|
+
getCurrentPlayStep,
|
|
8712
|
+
forcePauseAt,
|
|
8301
8713
|
setLoading,
|
|
8302
8714
|
destroy: () => {
|
|
8303
8715
|
sequencer.stop();
|
|
8716
|
+
options.singingVoices?.stopStream();
|
|
8304
8717
|
resizeObserver.disconnect();
|
|
8305
8718
|
document.removeEventListener("pointermove", onPointerMove);
|
|
8306
8719
|
document.removeEventListener("pointerup", onPointerUp);
|
|
@@ -8310,110 +8723,6 @@ var mountDAW = (target, options = {}) => {
|
|
|
8310
8723
|
};
|
|
8311
8724
|
};
|
|
8312
8725
|
|
|
8313
|
-
// src/instrument-presets.ts
|
|
8314
|
-
var INSTRUMENT_PRESETS = {
|
|
8315
|
-
// --- STANDARD: 汎用性と完成度重視 ---
|
|
8316
|
-
piano: {
|
|
8317
|
-
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
8318
|
-
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",
|
|
8319
|
-
melody: "Acoustic Grand Piano",
|
|
8320
|
-
submelody: "Vibraphone",
|
|
8321
|
-
bass: "Electric Bass (finger)",
|
|
8322
|
-
chord: "Pad 2 (warm)"
|
|
8323
|
-
},
|
|
8324
|
-
acoustic: {
|
|
8325
|
-
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
8326
|
-
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
8327
|
-
melody: "Acoustic Guitar (steel)",
|
|
8328
|
-
submelody: "Harmonica",
|
|
8329
|
-
bass: "Acoustic Bass",
|
|
8330
|
-
chord: "Acoustic Guitar (nylon)"
|
|
8331
|
-
},
|
|
8332
|
-
jazz_night: {
|
|
8333
|
-
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
8334
|
-
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",
|
|
8335
|
-
melody: "Electric Piano 1",
|
|
8336
|
-
submelody: "Flute",
|
|
8337
|
-
bass: "Acoustic Bass",
|
|
8338
|
-
chord: "Electric Guitar (jazz)"
|
|
8339
|
-
},
|
|
8340
|
-
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
8341
|
-
synth_pop: {
|
|
8342
|
-
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
8343
|
-
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
8344
|
-
melody: "Lead 2 (sawtooth)",
|
|
8345
|
-
submelody: "Lead 4 (chiff)",
|
|
8346
|
-
bass: "Synth Bass 2",
|
|
8347
|
-
chord: "Pad 3 (polysynth)"
|
|
8348
|
-
},
|
|
8349
|
-
cyber_punk: {
|
|
8350
|
-
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
8351
|
-
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",
|
|
8352
|
-
melody: "Lead 8 (bass + lead)",
|
|
8353
|
-
submelody: "Lead 5 (charang)",
|
|
8354
|
-
bass: "Synth Bass 2",
|
|
8355
|
-
chord: "Pad 8 (sweep)"
|
|
8356
|
-
},
|
|
8357
|
-
rock: {
|
|
8358
|
-
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
8359
|
-
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
8360
|
-
melody: "Distortion Guitar",
|
|
8361
|
-
submelody: "Rock Organ",
|
|
8362
|
-
bass: "Electric Bass (pick)",
|
|
8363
|
-
chord: "Overdriven Guitar"
|
|
8364
|
-
},
|
|
8365
|
-
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
8366
|
-
orchestra: {
|
|
8367
|
-
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
8368
|
-
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
8369
|
-
melody: "French Horn",
|
|
8370
|
-
submelody: "Pizzicato Strings",
|
|
8371
|
-
bass: "Cello",
|
|
8372
|
-
chord: "Tremolo Strings"
|
|
8373
|
-
},
|
|
8374
|
-
japanese_wa: {
|
|
8375
|
-
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
8376
|
-
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
8377
|
-
melody: "Koto",
|
|
8378
|
-
submelody: "Shamisen",
|
|
8379
|
-
bass: "Taiko Drum",
|
|
8380
|
-
chord: "Shakuhachi"
|
|
8381
|
-
},
|
|
8382
|
-
arabic_exotic: {
|
|
8383
|
-
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
8384
|
-
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",
|
|
8385
|
-
melody: "Sitar",
|
|
8386
|
-
submelody: "Bagpipe",
|
|
8387
|
-
bass: "Fretless Bass",
|
|
8388
|
-
chord: "Kalimba"
|
|
8389
|
-
},
|
|
8390
|
-
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
8391
|
-
fantasy_rpg: {
|
|
8392
|
-
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
8393
|
-
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
8394
|
-
melody: "Ocarina",
|
|
8395
|
-
submelody: "Celesta",
|
|
8396
|
-
bass: "Timpani",
|
|
8397
|
-
chord: "Orchestral Harp"
|
|
8398
|
-
},
|
|
8399
|
-
ambient_cloud: {
|
|
8400
|
-
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
8401
|
-
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
8402
|
-
melody: "Lead 6 (voice)",
|
|
8403
|
-
submelody: "Music Box",
|
|
8404
|
-
bass: "Synth Bass 1",
|
|
8405
|
-
chord: "Pad 7 (halo)"
|
|
8406
|
-
},
|
|
8407
|
-
retro_game: {
|
|
8408
|
-
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
8409
|
-
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",
|
|
8410
|
-
melody: "Lead 1 (square)",
|
|
8411
|
-
submelody: "Lead 2 (sawtooth)",
|
|
8412
|
-
bass: "Synth Bass 1",
|
|
8413
|
-
chord: "Clavinet"
|
|
8414
|
-
}
|
|
8415
|
-
};
|
|
8416
|
-
|
|
8417
8726
|
// src/headless-player.ts
|
|
8418
8727
|
var STEPS_PER_BAR2 = 192;
|
|
8419
8728
|
var playMML = (mml, options = {}) => {
|
|
@@ -8464,7 +8773,7 @@ var playMML = (mml, options = {}) => {
|
|
|
8464
8773
|
},
|
|
8465
8774
|
onTick: () => {
|
|
8466
8775
|
},
|
|
8467
|
-
onEnd: () => finish(),
|
|
8776
|
+
onEnd: (interrupted) => finish(),
|
|
8468
8777
|
stepsPerBar: STEPS_PER_BAR2
|
|
8469
8778
|
});
|
|
8470
8779
|
const finish = () => {
|
|
@@ -9157,32 +9466,56 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9157
9466
|
return p;
|
|
9158
9467
|
};
|
|
9159
9468
|
const defaultPreset = options.defaultPreset ?? "retro_game";
|
|
9469
|
+
const getRoleForTrackIndex = (idx, mode = "simple") => {
|
|
9470
|
+
if (mode === "simple") {
|
|
9471
|
+
if (idx === 0) return "melody";
|
|
9472
|
+
if (idx === 1) return "submelody";
|
|
9473
|
+
if (idx === 2) return "bass";
|
|
9474
|
+
return "chord";
|
|
9475
|
+
} else {
|
|
9476
|
+
return TRACK_ROLES[idx] ?? `t${idx}`;
|
|
9477
|
+
}
|
|
9478
|
+
};
|
|
9479
|
+
const getRoleFromTrackId = (trackId, mode = "simple") => {
|
|
9480
|
+
if (trackId === "melody" || trackId === "submelody" || trackId === "bass" || trackId === "chord") {
|
|
9481
|
+
return trackId;
|
|
9482
|
+
}
|
|
9483
|
+
if (trackId.startsWith("t")) {
|
|
9484
|
+
const idx = Number(trackId.substring(1));
|
|
9485
|
+
if (!isNaN(idx)) {
|
|
9486
|
+
return getRoleForTrackIndex(idx, mode);
|
|
9487
|
+
}
|
|
9488
|
+
}
|
|
9489
|
+
return trackId;
|
|
9490
|
+
};
|
|
9160
9491
|
const instrumentNameFor = (preset, trackId) => preset[trackId] ?? preset.melody;
|
|
9161
|
-
const resolveSoundFont = (presetKey, trackId) => {
|
|
9492
|
+
const resolveSoundFont = (presetKey, trackId, mode = "simple") => {
|
|
9162
9493
|
const preset = INSTRUMENT_PRESETS[presetKey];
|
|
9163
9494
|
if (!preset) return void 0;
|
|
9164
|
-
const
|
|
9495
|
+
const role = getRoleFromTrackId(trackId, mode);
|
|
9496
|
+
const key = nameToKey[instrumentNameFor(preset, role)];
|
|
9165
9497
|
return key ? soundFonts.get(key) : void 0;
|
|
9166
9498
|
};
|
|
9167
|
-
const loadPreset = async (presetKey, trackIds = [...TRACK_ROLES]) => {
|
|
9499
|
+
const loadPreset = async (presetKey, trackIds = [...TRACK_ROLES], mode = "simple") => {
|
|
9168
9500
|
const preset = INSTRUMENT_PRESETS[presetKey];
|
|
9169
9501
|
if (!preset) return;
|
|
9170
9502
|
await listReady;
|
|
9171
9503
|
const keys = /* @__PURE__ */ new Set();
|
|
9172
9504
|
for (const trackId of trackIds) {
|
|
9173
|
-
const
|
|
9505
|
+
const role = getRoleFromTrackId(trackId, mode);
|
|
9506
|
+
const key = nameToKey[instrumentNameFor(preset, role)];
|
|
9174
9507
|
if (key) keys.add(key);
|
|
9175
9508
|
}
|
|
9176
9509
|
await Promise.all([...keys].map((key) => loadInstrument(key)));
|
|
9177
9510
|
};
|
|
9178
|
-
const applyPreset = async (daw, presetKey, trackIds, loadingTarget) => {
|
|
9511
|
+
const applyPreset = async (daw, presetKey, trackIds, loadingTarget, mode = "simple") => {
|
|
9179
9512
|
const wasPlaying = daw.getPlaybackState() === "playing";
|
|
9180
9513
|
if (wasPlaying) daw.pause();
|
|
9181
9514
|
const overlay = loadingTarget ? showLoadingOverlay(loadingTarget) : null;
|
|
9182
9515
|
daw.setLoading?.(true);
|
|
9183
9516
|
try {
|
|
9184
9517
|
daw.setInstrument(presetKey);
|
|
9185
|
-
await loadPreset(presetKey, trackIds);
|
|
9518
|
+
await loadPreset(presetKey, trackIds, mode);
|
|
9186
9519
|
} finally {
|
|
9187
9520
|
overlay?.remove();
|
|
9188
9521
|
daw.setLoading?.(false);
|
|
@@ -9217,8 +9550,10 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9217
9550
|
const key = select.value;
|
|
9218
9551
|
opts.onChange?.(key);
|
|
9219
9552
|
const trackIds = opts.getTrackIds?.() ?? [...TRACK_ROLES];
|
|
9553
|
+
const isAdvanced = trackIds.includes("t0");
|
|
9554
|
+
const mode = isAdvanced ? "advanced" : "simple";
|
|
9220
9555
|
try {
|
|
9221
|
-
await applyPreset(daw, key, trackIds, opts.loadingTarget);
|
|
9556
|
+
await applyPreset(daw, key, trackIds, opts.loadingTarget, mode);
|
|
9222
9557
|
} finally {
|
|
9223
9558
|
busy = false;
|
|
9224
9559
|
}
|
|
@@ -9259,13 +9594,20 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9259
9594
|
const mountedPlayers = [];
|
|
9260
9595
|
const mountedModeSwitches = [];
|
|
9261
9596
|
const mountEditor = (target, opts = {}) => {
|
|
9262
|
-
const { preset, presetUI, ...dawOverrides } = opts;
|
|
9597
|
+
const { preset, presetUI, onInstrumentChange, ...dawOverrides } = opts;
|
|
9263
9598
|
const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
|
|
9264
9599
|
const trackIds = tracks.map((t) => t.id);
|
|
9265
9600
|
const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
|
|
9266
|
-
|
|
9601
|
+
const meta = opts.initialMML ? parseMmlMeta(opts.initialMML) : {};
|
|
9602
|
+
const initialPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : presetKey;
|
|
9603
|
+
let editorPreset = initialPreset;
|
|
9604
|
+
const isAdvancedMode = dawOverrides.mode === "advanced";
|
|
9267
9605
|
const playNote = (e) => {
|
|
9268
|
-
const sf = resolveSoundFont(
|
|
9606
|
+
const sf = resolveSoundFont(
|
|
9607
|
+
editorPreset,
|
|
9608
|
+
e.trackId,
|
|
9609
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9610
|
+
);
|
|
9269
9611
|
if (!sf) return;
|
|
9270
9612
|
sf.play({
|
|
9271
9613
|
ctx: audioCtx,
|
|
@@ -9276,6 +9618,14 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9276
9618
|
duration: e.duration
|
|
9277
9619
|
});
|
|
9278
9620
|
};
|
|
9621
|
+
let presetSelect = null;
|
|
9622
|
+
const handleInstrumentChange = (key) => {
|
|
9623
|
+
editorPreset = key;
|
|
9624
|
+
if (presetSelect) {
|
|
9625
|
+
presetSelect.setValue(key);
|
|
9626
|
+
}
|
|
9627
|
+
onInstrumentChange?.(key);
|
|
9628
|
+
};
|
|
9279
9629
|
const base = {
|
|
9280
9630
|
getAudioTime: () => audioCtx.currentTime,
|
|
9281
9631
|
onResumeAudio: resumeAudio,
|
|
@@ -9284,19 +9634,19 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9284
9634
|
singingVoices,
|
|
9285
9635
|
parseMidi,
|
|
9286
9636
|
onToggleRecord,
|
|
9637
|
+
onInstrumentChange: handleInstrumentChange,
|
|
9287
9638
|
...dawOverrides
|
|
9288
9639
|
};
|
|
9289
9640
|
const daw = mountDAW(target, base);
|
|
9290
9641
|
mountedEditors.push(daw);
|
|
9291
9642
|
const wantPresetUI = presetUI ?? features.presetUI;
|
|
9292
|
-
let presetSelect = null;
|
|
9293
9643
|
if (wantPresetUI) {
|
|
9294
9644
|
editorPresetSelects.get(target)?.destroy();
|
|
9295
9645
|
const rollEl = target.querySelector('[data-dtm="roll"]');
|
|
9296
9646
|
presetSelect = mountPresetSelect(target, {
|
|
9297
9647
|
getDaw: () => daw,
|
|
9298
9648
|
getTrackIds: () => trackIds,
|
|
9299
|
-
value:
|
|
9649
|
+
value: initialPreset,
|
|
9300
9650
|
loadingTarget: rollEl ?? target,
|
|
9301
9651
|
position: "prepend",
|
|
9302
9652
|
// 楽器変更時、このエディタの発音解決が使うプリセットも追従させる。
|
|
@@ -9306,9 +9656,13 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9306
9656
|
});
|
|
9307
9657
|
editorPresetSelects.set(target, presetSelect);
|
|
9308
9658
|
}
|
|
9309
|
-
daw.setInstrument(
|
|
9659
|
+
daw.setInstrument(initialPreset);
|
|
9310
9660
|
daw.setLoading?.(true);
|
|
9311
|
-
void loadPreset(
|
|
9661
|
+
void loadPreset(
|
|
9662
|
+
initialPreset,
|
|
9663
|
+
trackIds,
|
|
9664
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9665
|
+
).finally(() => {
|
|
9312
9666
|
daw.setLoading?.(false);
|
|
9313
9667
|
});
|
|
9314
9668
|
const destroy = () => {
|
|
@@ -9319,7 +9673,17 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9319
9673
|
const i = mountedEditors.indexOf(daw);
|
|
9320
9674
|
if (i >= 0) mountedEditors.splice(i, 1);
|
|
9321
9675
|
};
|
|
9322
|
-
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
|
+
};
|
|
9323
9687
|
};
|
|
9324
9688
|
const mountModeSwitch = (target, opts) => {
|
|
9325
9689
|
const doc = target.ownerDocument;
|
|
@@ -9409,15 +9773,30 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9409
9773
|
const parsed = parseMML(mml, {});
|
|
9410
9774
|
const meta = parsed.meta ?? {};
|
|
9411
9775
|
const playerPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : defaultPreset;
|
|
9776
|
+
const isAdvancedMode = meta.mode === "advanced";
|
|
9412
9777
|
const trackIndices = [
|
|
9413
9778
|
...new Set(parsed.placements.map((p) => p.trackIndex))
|
|
9414
9779
|
];
|
|
9415
|
-
const trackIds = trackIndices.map(
|
|
9780
|
+
const trackIds = trackIndices.map(
|
|
9781
|
+
(idx) => getRoleForTrackIndex(idx, isAdvancedMode ? "advanced" : "simple")
|
|
9782
|
+
);
|
|
9416
9783
|
const loadTrackIds = trackIds.length > 0 ? trackIds : [...TRACK_ROLES];
|
|
9417
|
-
void loadPreset(
|
|
9784
|
+
void loadPreset(
|
|
9785
|
+
playerPreset,
|
|
9786
|
+
loadTrackIds,
|
|
9787
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9788
|
+
);
|
|
9418
9789
|
const playPlayerNote = (e) => {
|
|
9419
|
-
const
|
|
9420
|
-
const
|
|
9790
|
+
const idx = Number(e.trackId);
|
|
9791
|
+
const role = getRoleForTrackIndex(
|
|
9792
|
+
idx,
|
|
9793
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9794
|
+
);
|
|
9795
|
+
const sf = resolveSoundFont(
|
|
9796
|
+
playerPreset,
|
|
9797
|
+
role,
|
|
9798
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9799
|
+
);
|
|
9421
9800
|
if (!sf) return;
|
|
9422
9801
|
sf.play({
|
|
9423
9802
|
ctx: audioCtx,
|