@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.mjs
CHANGED
|
@@ -1107,6 +1107,9 @@ var ICONS = {
|
|
|
1107
1107
|
},
|
|
1108
1108
|
info: {
|
|
1109
1109
|
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"
|
|
1110
|
+
},
|
|
1111
|
+
more: {
|
|
1112
|
+
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"
|
|
1110
1113
|
}
|
|
1111
1114
|
};
|
|
1112
1115
|
var icon = (name, size = 20) => {
|
|
@@ -1233,6 +1236,7 @@ var buildUI = (target, options) => {
|
|
|
1233
1236
|
<textarea class="dtm-textarea dtm-grow" data-dtm="mml-input" placeholder="MML\u3092\u5165\u529B"></textarea>
|
|
1234
1237
|
<button class="dtm-btn dtm-btn--primary" data-dtm="mml-load">\u8AAD\u8FBC</button>
|
|
1235
1238
|
</div>
|
|
1239
|
+
<p class="dtm-load-note dtm-hidden" data-dtm="mml-load-note"></p>
|
|
1236
1240
|
</div>
|
|
1237
1241
|
</details>
|
|
1238
1242
|
|
|
@@ -1358,6 +1362,7 @@ var buildUI = (target, options) => {
|
|
|
1358
1362
|
midiPanel: sel("midi-panel"),
|
|
1359
1363
|
mmlInput: sel("mml-input"),
|
|
1360
1364
|
mmlLoadBtn: sel("mml-load"),
|
|
1365
|
+
mmlLoadNote: sel("mml-load-note"),
|
|
1361
1366
|
shiftSelect: sel("shift-select"),
|
|
1362
1367
|
shiftApplyBtn: sel("shift-apply"),
|
|
1363
1368
|
macroClear: sel("macro-clear"),
|
|
@@ -1511,6 +1516,110 @@ var DRUM_PATTERNS = {
|
|
|
1511
1516
|
]
|
|
1512
1517
|
};
|
|
1513
1518
|
|
|
1519
|
+
// src/instrument-presets.ts
|
|
1520
|
+
var INSTRUMENT_PRESETS = {
|
|
1521
|
+
// --- STANDARD: 汎用性と完成度重視 ---
|
|
1522
|
+
piano: {
|
|
1523
|
+
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
1524
|
+
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",
|
|
1525
|
+
melody: "Acoustic Grand Piano",
|
|
1526
|
+
submelody: "Vibraphone",
|
|
1527
|
+
bass: "Electric Bass (finger)",
|
|
1528
|
+
chord: "Pad 2 (warm)"
|
|
1529
|
+
},
|
|
1530
|
+
acoustic: {
|
|
1531
|
+
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
1532
|
+
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
1533
|
+
melody: "Acoustic Guitar (steel)",
|
|
1534
|
+
submelody: "Harmonica",
|
|
1535
|
+
bass: "Acoustic Bass",
|
|
1536
|
+
chord: "Acoustic Guitar (nylon)"
|
|
1537
|
+
},
|
|
1538
|
+
jazz_night: {
|
|
1539
|
+
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
1540
|
+
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",
|
|
1541
|
+
melody: "Electric Piano 1",
|
|
1542
|
+
submelody: "Flute",
|
|
1543
|
+
bass: "Acoustic Bass",
|
|
1544
|
+
chord: "Electric Guitar (jazz)"
|
|
1545
|
+
},
|
|
1546
|
+
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
1547
|
+
synth_pop: {
|
|
1548
|
+
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
1549
|
+
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
1550
|
+
melody: "Lead 2 (sawtooth)",
|
|
1551
|
+
submelody: "Lead 4 (chiff)",
|
|
1552
|
+
bass: "Synth Bass 2",
|
|
1553
|
+
chord: "Pad 3 (polysynth)"
|
|
1554
|
+
},
|
|
1555
|
+
cyber_punk: {
|
|
1556
|
+
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
1557
|
+
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",
|
|
1558
|
+
melody: "Lead 8 (bass + lead)",
|
|
1559
|
+
submelody: "Lead 5 (charang)",
|
|
1560
|
+
bass: "Synth Bass 2",
|
|
1561
|
+
chord: "Pad 8 (sweep)"
|
|
1562
|
+
},
|
|
1563
|
+
rock: {
|
|
1564
|
+
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
1565
|
+
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
1566
|
+
melody: "Distortion Guitar",
|
|
1567
|
+
submelody: "Rock Organ",
|
|
1568
|
+
bass: "Electric Bass (pick)",
|
|
1569
|
+
chord: "Overdriven Guitar"
|
|
1570
|
+
},
|
|
1571
|
+
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
1572
|
+
orchestra: {
|
|
1573
|
+
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
1574
|
+
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
1575
|
+
melody: "French Horn",
|
|
1576
|
+
submelody: "Pizzicato Strings",
|
|
1577
|
+
bass: "Cello",
|
|
1578
|
+
chord: "Tremolo Strings"
|
|
1579
|
+
},
|
|
1580
|
+
japanese_wa: {
|
|
1581
|
+
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
1582
|
+
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
1583
|
+
melody: "Koto",
|
|
1584
|
+
submelody: "Shamisen",
|
|
1585
|
+
bass: "Taiko Drum",
|
|
1586
|
+
chord: "Shakuhachi"
|
|
1587
|
+
},
|
|
1588
|
+
arabic_exotic: {
|
|
1589
|
+
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
1590
|
+
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",
|
|
1591
|
+
melody: "Sitar",
|
|
1592
|
+
submelody: "Bagpipe",
|
|
1593
|
+
bass: "Fretless Bass",
|
|
1594
|
+
chord: "Kalimba"
|
|
1595
|
+
},
|
|
1596
|
+
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
1597
|
+
fantasy_rpg: {
|
|
1598
|
+
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
1599
|
+
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
1600
|
+
melody: "Ocarina",
|
|
1601
|
+
submelody: "Celesta",
|
|
1602
|
+
bass: "Timpani",
|
|
1603
|
+
chord: "Orchestral Harp"
|
|
1604
|
+
},
|
|
1605
|
+
ambient_cloud: {
|
|
1606
|
+
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
1607
|
+
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
1608
|
+
melody: "Lead 6 (voice)",
|
|
1609
|
+
submelody: "Music Box",
|
|
1610
|
+
bass: "Synth Bass 1",
|
|
1611
|
+
chord: "Pad 7 (halo)"
|
|
1612
|
+
},
|
|
1613
|
+
retro_game: {
|
|
1614
|
+
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
1615
|
+
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",
|
|
1616
|
+
melody: "Lead 1 (square)",
|
|
1617
|
+
submelody: "Lead 2 (sawtooth)",
|
|
1618
|
+
bass: "Synth Bass 1",
|
|
1619
|
+
chord: "Clavinet"
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
|
|
1514
1623
|
// node_modules/.pnpm/@onjmin+koe@1.0.3/node_modules/@onjmin/koe/dist/index.js
|
|
1515
1624
|
var MAGIC = 1263486208;
|
|
1516
1625
|
function parseKoeHeader(headerBytes) {
|
|
@@ -3919,7 +4028,7 @@ var PITCH_MAP2 = {
|
|
|
3919
4028
|
b: 11
|
|
3920
4029
|
};
|
|
3921
4030
|
var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
3922
|
-
var META_DIRECTIVE = /#(inst|drum|volume)=([\w-]+)/gi;
|
|
4031
|
+
var META_DIRECTIVE = /#(inst|drum|volume|mode)=([\w-]+)/gi;
|
|
3923
4032
|
var parseMmlMeta = (mml) => {
|
|
3924
4033
|
const meta = {};
|
|
3925
4034
|
for (const m of mml.matchAll(META_DIRECTIVE)) {
|
|
@@ -3929,6 +4038,10 @@ var parseMmlMeta = (mml) => {
|
|
|
3929
4038
|
else if (key === "volume") {
|
|
3930
4039
|
const v = Number.parseInt(m[2], 10);
|
|
3931
4040
|
if (!Number.isNaN(v)) meta.volume = v;
|
|
4041
|
+
} else if (key === "mode") {
|
|
4042
|
+
if (m[2] === "simple" || m[2] === "advanced") {
|
|
4043
|
+
meta.mode = m[2];
|
|
4044
|
+
}
|
|
3932
4045
|
}
|
|
3933
4046
|
}
|
|
3934
4047
|
return meta;
|
|
@@ -3939,6 +4052,7 @@ var formatMmlMeta = (meta) => {
|
|
|
3939
4052
|
if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
|
|
3940
4053
|
if (meta.drum) parts.push(`#drum=${meta.drum}`);
|
|
3941
4054
|
if (meta.volume !== void 0) parts.push(`#volume=${meta.volume}`);
|
|
4055
|
+
if (meta.mode) parts.push(`#mode=${meta.mode}`);
|
|
3942
4056
|
return parts.join(" ");
|
|
3943
4057
|
};
|
|
3944
4058
|
var parseMML = (mml, options = {}) => {
|
|
@@ -3955,6 +4069,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3955
4069
|
bpm,
|
|
3956
4070
|
tokenTracks: collectTokens ? tokenTracks : void 0,
|
|
3957
4071
|
lyrics: collectLyrics ? /* @__PURE__ */ new Map() : void 0,
|
|
4072
|
+
mergedTrackCount: 0,
|
|
3958
4073
|
meta: {}
|
|
3959
4074
|
};
|
|
3960
4075
|
}
|
|
@@ -3967,14 +4082,26 @@ var parseMML = (mml, options = {}) => {
|
|
|
3967
4082
|
const fullMML = stripLyrics(noMeta).replace(endRegex, "").replace(/[\n\r]+/g, " ").trim();
|
|
3968
4083
|
const parts = fullMML.split(/(@\d+)/).filter((p) => p.trim().length > 0);
|
|
3969
4084
|
let trackIndex = 0;
|
|
4085
|
+
let sourceTrackIndex = 0;
|
|
3970
4086
|
let octave = 4;
|
|
3971
4087
|
let currentStep = 0;
|
|
3972
4088
|
let baseLength = 16;
|
|
4089
|
+
const contributors = /* @__PURE__ */ new Map();
|
|
4090
|
+
const recordContributor = () => {
|
|
4091
|
+
let set = contributors.get(trackIndex);
|
|
4092
|
+
if (!set) {
|
|
4093
|
+
set = /* @__PURE__ */ new Set();
|
|
4094
|
+
contributors.set(trackIndex, set);
|
|
4095
|
+
}
|
|
4096
|
+
set.add(sourceTrackIndex);
|
|
4097
|
+
};
|
|
3973
4098
|
for (const rawPart of parts) {
|
|
3974
4099
|
const part = rawPart.trim();
|
|
3975
4100
|
if (part.startsWith("@")) {
|
|
3976
4101
|
let idx = Number.parseInt(part.substring(1), 10);
|
|
3977
|
-
|
|
4102
|
+
sourceTrackIndex = idx;
|
|
4103
|
+
if (clampTrackCount !== void 0 && idx >= clampTrackCount)
|
|
4104
|
+
idx = clampTrackCount - 1;
|
|
3978
4105
|
trackIndex = idx;
|
|
3979
4106
|
octave = 4;
|
|
3980
4107
|
currentStep = 0;
|
|
@@ -4096,6 +4223,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4096
4223
|
}
|
|
4097
4224
|
if (j < body.length && body[j] === "]") j++;
|
|
4098
4225
|
const steps = parseLength();
|
|
4226
|
+
if (chordNotes.length > 0) recordContributor();
|
|
4099
4227
|
for (const p of chordNotes) {
|
|
4100
4228
|
placements.push({
|
|
4101
4229
|
trackIndex,
|
|
@@ -4119,6 +4247,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
4119
4247
|
}
|
|
4120
4248
|
const midiPitch = (octave + 1) * 12 + pitch;
|
|
4121
4249
|
const steps = parseLength();
|
|
4250
|
+
recordContributor();
|
|
4122
4251
|
placements.push({
|
|
4123
4252
|
trackIndex,
|
|
4124
4253
|
startStep: currentStep,
|
|
@@ -4132,11 +4261,16 @@ var parseMML = (mml, options = {}) => {
|
|
|
4132
4261
|
}
|
|
4133
4262
|
}
|
|
4134
4263
|
}
|
|
4264
|
+
let mergedTrackCount = 0;
|
|
4265
|
+
for (const set of contributors.values()) {
|
|
4266
|
+
if (set.size >= 2) mergedTrackCount++;
|
|
4267
|
+
}
|
|
4135
4268
|
return {
|
|
4136
4269
|
placements,
|
|
4137
4270
|
bpm,
|
|
4138
4271
|
tokenTracks: collectTokens ? tokenTracks : void 0,
|
|
4139
4272
|
lyrics,
|
|
4273
|
+
mergedTrackCount,
|
|
4140
4274
|
meta
|
|
4141
4275
|
};
|
|
4142
4276
|
};
|
|
@@ -4198,6 +4332,8 @@ var createSequencer = (options) => {
|
|
|
4198
4332
|
let active = false;
|
|
4199
4333
|
let fromStepValue = 0;
|
|
4200
4334
|
let trackVolumeMap = /* @__PURE__ */ new Map();
|
|
4335
|
+
let lastRealTime = -1;
|
|
4336
|
+
let lastAudioTime = -1;
|
|
4201
4337
|
let isLooping = false;
|
|
4202
4338
|
let loopStartStep = 0;
|
|
4203
4339
|
let loopEndStep = 0;
|
|
@@ -4271,6 +4407,21 @@ var createSequencer = (options) => {
|
|
|
4271
4407
|
const sps = secondsPerStep();
|
|
4272
4408
|
const time = options.getAudioTime() - startTime;
|
|
4273
4409
|
const soloId = options.getSoloTrackId();
|
|
4410
|
+
const nowReal = performance.now() / 1e3;
|
|
4411
|
+
if (lastRealTime > 0 && lastAudioTime >= 0) {
|
|
4412
|
+
const realDelta = nowReal - lastRealTime;
|
|
4413
|
+
const audioDelta = time - lastAudioTime;
|
|
4414
|
+
if (realDelta > 0.5 || audioDelta > 0.5) {
|
|
4415
|
+
console.warn(
|
|
4416
|
+
`[sequencer] Interruption detected (realDelta: ${realDelta.toFixed(3)}s, audioDelta: ${audioDelta.toFixed(3)}s). Stopping playback.`
|
|
4417
|
+
);
|
|
4418
|
+
stop();
|
|
4419
|
+
options.onEnd(true);
|
|
4420
|
+
return;
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
lastRealTime = nowReal;
|
|
4424
|
+
lastAudioTime = time;
|
|
4274
4425
|
for (const track of options.getTracks()) {
|
|
4275
4426
|
trackVolumeMap.set(track.id, track.volume);
|
|
4276
4427
|
}
|
|
@@ -4347,7 +4498,7 @@ var createSequencer = (options) => {
|
|
|
4347
4498
|
const lastDuration = last?.duration ?? 0;
|
|
4348
4499
|
if (nowIndex >= timeline.length && time > lastWhen + lastDuration + 0.1) {
|
|
4349
4500
|
stop();
|
|
4350
|
-
options.onEnd();
|
|
4501
|
+
options.onEnd(false);
|
|
4351
4502
|
}
|
|
4352
4503
|
}
|
|
4353
4504
|
};
|
|
@@ -4388,6 +4539,8 @@ var createSequencer = (options) => {
|
|
|
4388
4539
|
}
|
|
4389
4540
|
loopBase = 0;
|
|
4390
4541
|
lastPlayStep = fromStepValue - 1e-4;
|
|
4542
|
+
lastRealTime = -1;
|
|
4543
|
+
lastAudioTime = -1;
|
|
4391
4544
|
intervalId = setInterval(scheduleTick, TICK_INTERVAL_MS);
|
|
4392
4545
|
animationId = requestAnimationFrame(animate);
|
|
4393
4546
|
};
|
|
@@ -4585,11 +4738,11 @@ var DAW_CSS = `
|
|
|
4585
4738
|
}
|
|
4586
4739
|
.dtm-btn:active { transform: translate(3px,3px); box-shadow: none; }
|
|
4587
4740
|
.dtm-btn:disabled { opacity: .3; cursor: default; box-shadow: none; }
|
|
4741
|
+
.dtm-btn--ghost { background: transparent; border-color: var(--dtm-border2); }
|
|
4588
4742
|
.dtm-btn--primary { border-color: var(--dtm-primary); background: var(--dtm-primary); color: var(--dtm-pfg); }
|
|
4589
4743
|
.dtm-btn--success { border-color: var(--dtm-success); background: var(--dtm-success); color: var(--c-black); }
|
|
4590
4744
|
.dtm-btn--danger { border-color: var(--dtm-danger); background: var(--dtm-danger); color: var(--c-white); }
|
|
4591
4745
|
.dtm-btn--accent { border-color: var(--dtm-accent); background: var(--dtm-accent); color: var(--c-black); }
|
|
4592
|
-
.dtm-btn--ghost { background: transparent; border-color: var(--dtm-border2); }
|
|
4593
4746
|
.dtm-btn--icon { padding: 0; }
|
|
4594
4747
|
|
|
4595
4748
|
/* \u2500\u2500\u2500 \u30A2\u30A4\u30B3\u30F3\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */
|
|
@@ -5223,6 +5376,18 @@ var DAW_CSS = `
|
|
|
5223
5376
|
}
|
|
5224
5377
|
|
|
5225
5378
|
.dtm-hidden { display: none !important; }
|
|
5379
|
+
/* \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 */
|
|
5380
|
+
.dtm-load-note {
|
|
5381
|
+
margin: 6px 0 0;
|
|
5382
|
+
padding: 0 2px;
|
|
5383
|
+
font-family: var(--dtm-font);
|
|
5384
|
+
font-size: 10px;
|
|
5385
|
+
line-height: 1.5;
|
|
5386
|
+
letter-spacing: .04em;
|
|
5387
|
+
color: var(--dtm-muted);
|
|
5388
|
+
opacity: .85;
|
|
5389
|
+
}
|
|
5390
|
+
.dtm-load-note::before { content: "\u24D8 "; }
|
|
5226
5391
|
.dtm-grow { flex: 1 1 auto; }
|
|
5227
5392
|
.dtm-lyric-icon {
|
|
5228
5393
|
flex: 0 0 auto;
|
|
@@ -5322,6 +5487,62 @@ var DAW_CSS = `
|
|
|
5322
5487
|
white-space: nowrap;
|
|
5323
5488
|
}
|
|
5324
5489
|
.dtm-player-mml-link:hover { color: var(--dtm-primary); }
|
|
5490
|
+
.dtm-player-more-container {
|
|
5491
|
+
position: relative;
|
|
5492
|
+
display: inline-flex;
|
|
5493
|
+
align-items: center;
|
|
5494
|
+
}
|
|
5495
|
+
.dtm-player-more-btn {
|
|
5496
|
+
background: transparent;
|
|
5497
|
+
border: none;
|
|
5498
|
+
color: var(--dtm-muted);
|
|
5499
|
+
cursor: pointer;
|
|
5500
|
+
padding: 2px;
|
|
5501
|
+
display: flex;
|
|
5502
|
+
align-items: center;
|
|
5503
|
+
justify-content: center;
|
|
5504
|
+
border-radius: 50%;
|
|
5505
|
+
width: 24px;
|
|
5506
|
+
height: 24px;
|
|
5507
|
+
transition: color 0.15s, background-color 0.15s;
|
|
5508
|
+
}
|
|
5509
|
+
.dtm-player-more-btn:hover,
|
|
5510
|
+
.dtm-player-more-btn.is-active {
|
|
5511
|
+
color: var(--dtm-text);
|
|
5512
|
+
background: var(--dtm-border2);
|
|
5513
|
+
}
|
|
5514
|
+
.dtm-player-menu {
|
|
5515
|
+
position: absolute;
|
|
5516
|
+
top: calc(100% + 4px);
|
|
5517
|
+
right: 0;
|
|
5518
|
+
background: var(--dtm-deep);
|
|
5519
|
+
border: 2px solid var(--dtm-border2);
|
|
5520
|
+
box-shadow: 4px 4px 0 var(--c-black);
|
|
5521
|
+
z-index: 200;
|
|
5522
|
+
display: flex;
|
|
5523
|
+
flex-direction: column;
|
|
5524
|
+
padding: 4px 0;
|
|
5525
|
+
min-width: 130px;
|
|
5526
|
+
font-family: var(--dtm-font);
|
|
5527
|
+
}
|
|
5528
|
+
.dtm-player-menu-item {
|
|
5529
|
+
background: transparent;
|
|
5530
|
+
border: none;
|
|
5531
|
+
color: var(--dtm-text);
|
|
5532
|
+
padding: 6px 12px;
|
|
5533
|
+
text-align: left;
|
|
5534
|
+
cursor: pointer;
|
|
5535
|
+
font-size: 11px;
|
|
5536
|
+
font-family: inherit;
|
|
5537
|
+
white-space: nowrap;
|
|
5538
|
+
width: 100%;
|
|
5539
|
+
box-sizing: border-box;
|
|
5540
|
+
transition: background-color 0.1s, color 0.1s;
|
|
5541
|
+
}
|
|
5542
|
+
.dtm-player-menu-item:hover {
|
|
5543
|
+
background: var(--dtm-primary);
|
|
5544
|
+
color: var(--dtm-pfg);
|
|
5545
|
+
}
|
|
5325
5546
|
.dtm-player-emoji {
|
|
5326
5547
|
position: relative;
|
|
5327
5548
|
display: inline-flex;
|
|
@@ -5597,6 +5818,75 @@ var showBalloon = (balloonEl) => {
|
|
|
5597
5818
|
hideActiveBalloon();
|
|
5598
5819
|
}, 3e3);
|
|
5599
5820
|
};
|
|
5821
|
+
var copyToClipboard = async (doc, text) => {
|
|
5822
|
+
try {
|
|
5823
|
+
await navigator.clipboard.writeText(text);
|
|
5824
|
+
return true;
|
|
5825
|
+
} catch {
|
|
5826
|
+
try {
|
|
5827
|
+
const textarea = doc.createElement("textarea");
|
|
5828
|
+
textarea.value = text;
|
|
5829
|
+
textarea.style.position = "fixed";
|
|
5830
|
+
textarea.style.opacity = "0";
|
|
5831
|
+
doc.body.appendChild(textarea);
|
|
5832
|
+
textarea.select();
|
|
5833
|
+
const ok = doc.execCommand("copy");
|
|
5834
|
+
doc.body.removeChild(textarea);
|
|
5835
|
+
return ok;
|
|
5836
|
+
} catch {
|
|
5837
|
+
return false;
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5840
|
+
};
|
|
5841
|
+
var toBase64Url = (bytes) => {
|
|
5842
|
+
let bin = "";
|
|
5843
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
5844
|
+
bin += String.fromCharCode(bytes[i]);
|
|
5845
|
+
}
|
|
5846
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
5847
|
+
};
|
|
5848
|
+
var HIRAGANA_START = 12353;
|
|
5849
|
+
var HIRAGANA_END = 12447;
|
|
5850
|
+
var KATAKANA_START = 12449;
|
|
5851
|
+
var KATAKANA_END = 12543;
|
|
5852
|
+
var PROLONGED_MARK = 12540;
|
|
5853
|
+
var SHIFT_KATAKANA = 255;
|
|
5854
|
+
var VALUE_PROLONGED = 223;
|
|
5855
|
+
var customEncode = (str) => {
|
|
5856
|
+
const bytes = [];
|
|
5857
|
+
for (let i = 0; i < str.length; i++) {
|
|
5858
|
+
const code = str.charCodeAt(i);
|
|
5859
|
+
if (code <= 127) {
|
|
5860
|
+
bytes.push(code);
|
|
5861
|
+
} else if (code === PROLONGED_MARK) {
|
|
5862
|
+
bytes.push(VALUE_PROLONGED);
|
|
5863
|
+
} else if (code >= HIRAGANA_START && code <= HIRAGANA_END) {
|
|
5864
|
+
bytes.push(128 + (code - HIRAGANA_START));
|
|
5865
|
+
} else if (code >= KATAKANA_START && code <= KATAKANA_END) {
|
|
5866
|
+
bytes.push(SHIFT_KATAKANA);
|
|
5867
|
+
bytes.push(128 + (code - 96 - HIRAGANA_START));
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
return new Uint8Array(bytes);
|
|
5871
|
+
};
|
|
5872
|
+
var encodeMml = async (mml) => {
|
|
5873
|
+
try {
|
|
5874
|
+
if (typeof CompressionStream !== "undefined") {
|
|
5875
|
+
const cs = new CompressionStream("gzip");
|
|
5876
|
+
const w = cs.writable.getWriter();
|
|
5877
|
+
w.write(customEncode(mml));
|
|
5878
|
+
w.close();
|
|
5879
|
+
const buf = await new Response(cs.readable).arrayBuffer();
|
|
5880
|
+
return `z.${toBase64Url(new Uint8Array(buf))}`;
|
|
5881
|
+
}
|
|
5882
|
+
} catch (e) {
|
|
5883
|
+
console.warn(
|
|
5884
|
+
"[dtm] CompressionStream failed, fallback to encodeURIComponent",
|
|
5885
|
+
e
|
|
5886
|
+
);
|
|
5887
|
+
}
|
|
5888
|
+
return `u.${encodeURIComponent(mml)}`;
|
|
5889
|
+
};
|
|
5600
5890
|
var mountMmlPlayer = (target, mml, options = {}) => {
|
|
5601
5891
|
injectStyles(target.ownerDocument ?? document);
|
|
5602
5892
|
const {
|
|
@@ -5742,6 +6032,83 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
5742
6032
|
emojiEls.push(em);
|
|
5743
6033
|
emojiByTrack.set(index, em);
|
|
5744
6034
|
}
|
|
6035
|
+
const menuContainer = doc.createElement("div");
|
|
6036
|
+
menuContainer.className = "dtm-player-more-container";
|
|
6037
|
+
const moreBtn = doc.createElement("button");
|
|
6038
|
+
moreBtn.type = "button";
|
|
6039
|
+
moreBtn.className = "dtm-player-more-btn";
|
|
6040
|
+
moreBtn.innerHTML = icon("more", 14);
|
|
6041
|
+
moreBtn.title = "\u30E1\u30CB\u30E5\u30FC";
|
|
6042
|
+
menuContainer.appendChild(moreBtn);
|
|
6043
|
+
const menuDropdown = doc.createElement("div");
|
|
6044
|
+
menuDropdown.className = "dtm-player-menu";
|
|
6045
|
+
menuDropdown.style.display = "none";
|
|
6046
|
+
const copyMmlItem = doc.createElement("button");
|
|
6047
|
+
copyMmlItem.type = "button";
|
|
6048
|
+
copyMmlItem.className = "dtm-player-menu-item";
|
|
6049
|
+
copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
|
|
6050
|
+
const embedItem = doc.createElement("button");
|
|
6051
|
+
embedItem.type = "button";
|
|
6052
|
+
embedItem.className = "dtm-player-menu-item";
|
|
6053
|
+
embedItem.textContent = "\u57CB\u3081\u8FBC\u3080";
|
|
6054
|
+
menuDropdown.appendChild(copyMmlItem);
|
|
6055
|
+
menuDropdown.appendChild(embedItem);
|
|
6056
|
+
menuContainer.appendChild(menuDropdown);
|
|
6057
|
+
mmlHeader.appendChild(menuContainer);
|
|
6058
|
+
const toggleMenu = (show) => {
|
|
6059
|
+
const visible = show !== void 0 ? show : menuDropdown.style.display === "none";
|
|
6060
|
+
menuDropdown.style.display = visible ? "flex" : "none";
|
|
6061
|
+
if (visible) {
|
|
6062
|
+
moreBtn.classList.add("is-active");
|
|
6063
|
+
doc.addEventListener("click", handleOutsideClick);
|
|
6064
|
+
} else {
|
|
6065
|
+
moreBtn.classList.remove("is-active");
|
|
6066
|
+
doc.removeEventListener("click", handleOutsideClick);
|
|
6067
|
+
}
|
|
6068
|
+
};
|
|
6069
|
+
const handleOutsideClick = (e) => {
|
|
6070
|
+
if (!menuContainer.contains(e.target)) {
|
|
6071
|
+
toggleMenu(false);
|
|
6072
|
+
}
|
|
6073
|
+
};
|
|
6074
|
+
moreBtn.addEventListener("click", (e) => {
|
|
6075
|
+
e.stopPropagation();
|
|
6076
|
+
toggleMenu();
|
|
6077
|
+
});
|
|
6078
|
+
copyMmlItem.addEventListener("click", async (e) => {
|
|
6079
|
+
e.stopPropagation();
|
|
6080
|
+
const success = await copyToClipboard(doc, mml);
|
|
6081
|
+
if (success) {
|
|
6082
|
+
copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
|
|
6083
|
+
} else {
|
|
6084
|
+
copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
|
|
6085
|
+
}
|
|
6086
|
+
setTimeout(() => {
|
|
6087
|
+
copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
|
|
6088
|
+
}, 2e3);
|
|
6089
|
+
});
|
|
6090
|
+
embedItem.addEventListener("click", async (e) => {
|
|
6091
|
+
e.stopPropagation();
|
|
6092
|
+
embedItem.textContent = "\u751F\u6210\u4E2D...";
|
|
6093
|
+
try {
|
|
6094
|
+
const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
|
|
6095
|
+
const payload = await encodeMml(mml);
|
|
6096
|
+
const url2 = `${embedBase}#${payload}`;
|
|
6097
|
+
const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
|
|
6098
|
+
const success = await copyToClipboard(doc, snippet);
|
|
6099
|
+
if (success) {
|
|
6100
|
+
embedItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
|
|
6101
|
+
} else {
|
|
6102
|
+
embedItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
|
|
6103
|
+
}
|
|
6104
|
+
} catch (err2) {
|
|
6105
|
+
console.error("[dtm] failed to generate embed snippet", err2);
|
|
6106
|
+
embedItem.textContent = "\u751F\u6210\u5931\u6557";
|
|
6107
|
+
}
|
|
6108
|
+
setTimeout(() => {
|
|
6109
|
+
embedItem.textContent = "\u57CB\u3081\u8FBC\u3080";
|
|
6110
|
+
}, 2e3);
|
|
6111
|
+
});
|
|
5745
6112
|
const promotedToImage = /* @__PURE__ */ new Set();
|
|
5746
6113
|
for (const [index, lt] of lyricTracks) {
|
|
5747
6114
|
const em = emojiByTrack.get(index);
|
|
@@ -6050,7 +6417,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6050
6417
|
onTick: (step) => {
|
|
6051
6418
|
renderPlayhead(step);
|
|
6052
6419
|
},
|
|
6053
|
-
onEnd: () => finish(),
|
|
6420
|
+
onEnd: (interrupted) => finish(),
|
|
6054
6421
|
stepsPerBar: STEPS_PER_BAR
|
|
6055
6422
|
});
|
|
6056
6423
|
let playing = false;
|
|
@@ -6145,6 +6512,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6145
6512
|
else play();
|
|
6146
6513
|
});
|
|
6147
6514
|
const destroy = () => {
|
|
6515
|
+
doc.removeEventListener("click", handleOutsideClick);
|
|
6148
6516
|
seq.stop();
|
|
6149
6517
|
peekVoices()?.stopStream();
|
|
6150
6518
|
if (activePlayer === instance) activePlayer = null;
|
|
@@ -7063,6 +7431,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
7063
7431
|
0,
|
|
7064
7432
|
Math.floor(step / snapGridSteps) * snapGridSteps
|
|
7065
7433
|
);
|
|
7434
|
+
if (playbackState === "paused") {
|
|
7435
|
+
playbackState = "stopped";
|
|
7436
|
+
updateTransport();
|
|
7437
|
+
}
|
|
7066
7438
|
redrawAll();
|
|
7067
7439
|
});
|
|
7068
7440
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
@@ -7138,9 +7510,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
7138
7510
|
}
|
|
7139
7511
|
redrawAll();
|
|
7140
7512
|
},
|
|
7141
|
-
onEnd: () => {
|
|
7142
|
-
|
|
7143
|
-
|
|
7513
|
+
onEnd: (interrupted) => {
|
|
7514
|
+
if (interrupted) {
|
|
7515
|
+
playbackState = "paused";
|
|
7516
|
+
pausedPlayStep = currentPlayStep;
|
|
7517
|
+
} else {
|
|
7518
|
+
playbackState = "stopped";
|
|
7519
|
+
currentPlayStep = 0;
|
|
7520
|
+
}
|
|
7144
7521
|
updateTransport();
|
|
7145
7522
|
redrawAll();
|
|
7146
7523
|
},
|
|
@@ -7230,13 +7607,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
7230
7607
|
redrawAll();
|
|
7231
7608
|
};
|
|
7232
7609
|
const togglePlay = () => {
|
|
7233
|
-
if (playbackState === "playing")
|
|
7610
|
+
if (playbackState === "playing") pause();
|
|
7234
7611
|
else play();
|
|
7235
7612
|
};
|
|
7236
7613
|
const updateTransport = () => {
|
|
7237
7614
|
const playing = playbackState === "playing";
|
|
7238
|
-
const label = playing ? "\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
|
|
7239
|
-
refs.playBtn.innerHTML = `${icon(playing ? "
|
|
7615
|
+
const label = playing ? "\u4E00\u6642\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
|
|
7616
|
+
refs.playBtn.innerHTML = `${icon(playing ? "pause" : "play")}<span>${label}</span>`;
|
|
7240
7617
|
refs.playBtn.classList.toggle("dtm-play--stop", playing);
|
|
7241
7618
|
};
|
|
7242
7619
|
const updateUndoRedo = () => {
|
|
@@ -7502,7 +7879,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
7502
7879
|
const metaLine = formatMmlMeta({
|
|
7503
7880
|
instrument: currentInstrument || void 0,
|
|
7504
7881
|
drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
|
|
7505
|
-
volume: masterVolume
|
|
7882
|
+
volume: masterVolume,
|
|
7883
|
+
mode
|
|
7506
7884
|
});
|
|
7507
7885
|
if (refs.decomposeChordToggle.checked) {
|
|
7508
7886
|
const ignoreHeavy = refs.ignoreChordHeavyToggle.checked;
|
|
@@ -7624,20 +8002,25 @@ var mountDAW = (target, options = {}) => {
|
|
|
7624
8002
|
};
|
|
7625
8003
|
const loadMML = (mml) => {
|
|
7626
8004
|
if (!mml) return;
|
|
8005
|
+
stop();
|
|
7627
8006
|
clearAll();
|
|
7628
8007
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
7629
8008
|
const {
|
|
7630
8009
|
placements,
|
|
7631
8010
|
bpm: parsedBpm,
|
|
7632
8011
|
lyrics,
|
|
7633
|
-
meta
|
|
8012
|
+
meta,
|
|
8013
|
+
mergedTrackCount
|
|
7634
8014
|
} = parseMML(mml, {
|
|
7635
8015
|
stepsPerBar: renderConfig.stepsPerBar,
|
|
7636
8016
|
collectLyrics: true,
|
|
7637
8017
|
// このDAWのトラック数を超えるチャンネルはベースへ畳み込む(従来挙動)
|
|
7638
8018
|
clampTrackCount: trackStates.length
|
|
7639
8019
|
});
|
|
7640
|
-
|
|
8020
|
+
if (meta.instrument && INSTRUMENT_PRESETS[meta.instrument]) {
|
|
8021
|
+
currentInstrument = meta.instrument;
|
|
8022
|
+
options.onInstrumentChange?.(meta.instrument);
|
|
8023
|
+
}
|
|
7641
8024
|
if (meta.drum && drumPatterns[meta.drum]) {
|
|
7642
8025
|
currentDrumPattern = meta.drum;
|
|
7643
8026
|
refs.drumSelect.value = meta.drum;
|
|
@@ -7689,6 +8072,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
7689
8072
|
redrawAll();
|
|
7690
8073
|
updateTrackPanel();
|
|
7691
8074
|
updateUndoRedo();
|
|
8075
|
+
if (!isAdvanced && mergedTrackCount > 0) {
|
|
8076
|
+
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";
|
|
8077
|
+
refs.mmlLoadNote.classList.remove("dtm-hidden");
|
|
8078
|
+
} else {
|
|
8079
|
+
refs.mmlLoadNote.textContent = "";
|
|
8080
|
+
refs.mmlLoadNote.classList.add("dtm-hidden");
|
|
8081
|
+
}
|
|
7692
8082
|
};
|
|
7693
8083
|
const applyChord = () => {
|
|
7694
8084
|
const active = getActive();
|
|
@@ -7721,6 +8111,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7721
8111
|
applyMidiSelection(midi, selected);
|
|
7722
8112
|
};
|
|
7723
8113
|
const applyMidiSelection = (midi, selectedIndices) => {
|
|
8114
|
+
stop();
|
|
7724
8115
|
clearAll();
|
|
7725
8116
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
7726
8117
|
const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
|
|
@@ -8152,6 +8543,25 @@ var mountDAW = (target, options = {}) => {
|
|
|
8152
8543
|
const setLoading = (loading) => {
|
|
8153
8544
|
refs.topbar.classList.toggle("is-loading", loading);
|
|
8154
8545
|
};
|
|
8546
|
+
const getCurrentPlayStep = () => {
|
|
8547
|
+
if (playbackState === "playing") return currentPlayStep;
|
|
8548
|
+
if (playbackState === "paused") return pausedPlayStep;
|
|
8549
|
+
return playStartStep;
|
|
8550
|
+
};
|
|
8551
|
+
const forcePauseAt = (step) => {
|
|
8552
|
+
playStartStep = step;
|
|
8553
|
+
pausedPlayStep = step;
|
|
8554
|
+
currentPlayStep = step;
|
|
8555
|
+
playbackState = "paused";
|
|
8556
|
+
const canvas = getGridCanvas();
|
|
8557
|
+
currentOffsetX = Math.max(
|
|
8558
|
+
0,
|
|
8559
|
+
step * renderConfig.stepWidth - canvas.width * 0.5
|
|
8560
|
+
);
|
|
8561
|
+
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8562
|
+
updateTransport();
|
|
8563
|
+
redrawAll();
|
|
8564
|
+
};
|
|
8155
8565
|
return {
|
|
8156
8566
|
play,
|
|
8157
8567
|
pause,
|
|
@@ -8189,9 +8599,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
8189
8599
|
exportMIDI: exportMIDI2,
|
|
8190
8600
|
setBpm,
|
|
8191
8601
|
getPlaybackState: () => playbackState,
|
|
8602
|
+
getCurrentPlayStep,
|
|
8603
|
+
forcePauseAt,
|
|
8192
8604
|
setLoading,
|
|
8193
8605
|
destroy: () => {
|
|
8194
8606
|
sequencer.stop();
|
|
8607
|
+
options.singingVoices?.stopStream();
|
|
8195
8608
|
resizeObserver.disconnect();
|
|
8196
8609
|
document.removeEventListener("pointermove", onPointerMove);
|
|
8197
8610
|
document.removeEventListener("pointerup", onPointerUp);
|
|
@@ -8201,110 +8614,6 @@ var mountDAW = (target, options = {}) => {
|
|
|
8201
8614
|
};
|
|
8202
8615
|
};
|
|
8203
8616
|
|
|
8204
|
-
// src/instrument-presets.ts
|
|
8205
|
-
var INSTRUMENT_PRESETS = {
|
|
8206
|
-
// --- STANDARD: 汎用性と完成度重視 ---
|
|
8207
|
-
piano: {
|
|
8208
|
-
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
8209
|
-
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",
|
|
8210
|
-
melody: "Acoustic Grand Piano",
|
|
8211
|
-
submelody: "Vibraphone",
|
|
8212
|
-
bass: "Electric Bass (finger)",
|
|
8213
|
-
chord: "Pad 2 (warm)"
|
|
8214
|
-
},
|
|
8215
|
-
acoustic: {
|
|
8216
|
-
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
8217
|
-
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
8218
|
-
melody: "Acoustic Guitar (steel)",
|
|
8219
|
-
submelody: "Harmonica",
|
|
8220
|
-
bass: "Acoustic Bass",
|
|
8221
|
-
chord: "Acoustic Guitar (nylon)"
|
|
8222
|
-
},
|
|
8223
|
-
jazz_night: {
|
|
8224
|
-
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
8225
|
-
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",
|
|
8226
|
-
melody: "Electric Piano 1",
|
|
8227
|
-
submelody: "Flute",
|
|
8228
|
-
bass: "Acoustic Bass",
|
|
8229
|
-
chord: "Electric Guitar (jazz)"
|
|
8230
|
-
},
|
|
8231
|
-
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
8232
|
-
synth_pop: {
|
|
8233
|
-
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
8234
|
-
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
8235
|
-
melody: "Lead 2 (sawtooth)",
|
|
8236
|
-
submelody: "Lead 4 (chiff)",
|
|
8237
|
-
bass: "Synth Bass 2",
|
|
8238
|
-
chord: "Pad 3 (polysynth)"
|
|
8239
|
-
},
|
|
8240
|
-
cyber_punk: {
|
|
8241
|
-
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
8242
|
-
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",
|
|
8243
|
-
melody: "Lead 8 (bass + lead)",
|
|
8244
|
-
submelody: "Lead 5 (charang)",
|
|
8245
|
-
bass: "Synth Bass 2",
|
|
8246
|
-
chord: "Pad 8 (sweep)"
|
|
8247
|
-
},
|
|
8248
|
-
rock: {
|
|
8249
|
-
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
8250
|
-
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
8251
|
-
melody: "Distortion Guitar",
|
|
8252
|
-
submelody: "Rock Organ",
|
|
8253
|
-
bass: "Electric Bass (pick)",
|
|
8254
|
-
chord: "Overdriven Guitar"
|
|
8255
|
-
},
|
|
8256
|
-
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
8257
|
-
orchestra: {
|
|
8258
|
-
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
8259
|
-
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
8260
|
-
melody: "French Horn",
|
|
8261
|
-
submelody: "Pizzicato Strings",
|
|
8262
|
-
bass: "Cello",
|
|
8263
|
-
chord: "Tremolo Strings"
|
|
8264
|
-
},
|
|
8265
|
-
japanese_wa: {
|
|
8266
|
-
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
8267
|
-
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
8268
|
-
melody: "Koto",
|
|
8269
|
-
submelody: "Shamisen",
|
|
8270
|
-
bass: "Taiko Drum",
|
|
8271
|
-
chord: "Shakuhachi"
|
|
8272
|
-
},
|
|
8273
|
-
arabic_exotic: {
|
|
8274
|
-
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
8275
|
-
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",
|
|
8276
|
-
melody: "Sitar",
|
|
8277
|
-
submelody: "Bagpipe",
|
|
8278
|
-
bass: "Fretless Bass",
|
|
8279
|
-
chord: "Kalimba"
|
|
8280
|
-
},
|
|
8281
|
-
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
8282
|
-
fantasy_rpg: {
|
|
8283
|
-
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
8284
|
-
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
8285
|
-
melody: "Ocarina",
|
|
8286
|
-
submelody: "Celesta",
|
|
8287
|
-
bass: "Timpani",
|
|
8288
|
-
chord: "Orchestral Harp"
|
|
8289
|
-
},
|
|
8290
|
-
ambient_cloud: {
|
|
8291
|
-
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
8292
|
-
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
8293
|
-
melody: "Lead 6 (voice)",
|
|
8294
|
-
submelody: "Music Box",
|
|
8295
|
-
bass: "Synth Bass 1",
|
|
8296
|
-
chord: "Pad 7 (halo)"
|
|
8297
|
-
},
|
|
8298
|
-
retro_game: {
|
|
8299
|
-
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
8300
|
-
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",
|
|
8301
|
-
melody: "Lead 1 (square)",
|
|
8302
|
-
submelody: "Lead 2 (sawtooth)",
|
|
8303
|
-
bass: "Synth Bass 1",
|
|
8304
|
-
chord: "Clavinet"
|
|
8305
|
-
}
|
|
8306
|
-
};
|
|
8307
|
-
|
|
8308
8617
|
// src/headless-player.ts
|
|
8309
8618
|
var STEPS_PER_BAR2 = 192;
|
|
8310
8619
|
var playMML = (mml, options = {}) => {
|
|
@@ -8355,7 +8664,7 @@ var playMML = (mml, options = {}) => {
|
|
|
8355
8664
|
},
|
|
8356
8665
|
onTick: () => {
|
|
8357
8666
|
},
|
|
8358
|
-
onEnd: () => finish(),
|
|
8667
|
+
onEnd: (interrupted) => finish(),
|
|
8359
8668
|
stepsPerBar: STEPS_PER_BAR2
|
|
8360
8669
|
});
|
|
8361
8670
|
const finish = () => {
|
|
@@ -9047,32 +9356,56 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9047
9356
|
return p;
|
|
9048
9357
|
};
|
|
9049
9358
|
const defaultPreset = options.defaultPreset ?? "retro_game";
|
|
9359
|
+
const getRoleForTrackIndex = (idx, mode = "simple") => {
|
|
9360
|
+
if (mode === "simple") {
|
|
9361
|
+
if (idx === 0) return "melody";
|
|
9362
|
+
if (idx === 1) return "submelody";
|
|
9363
|
+
if (idx === 2) return "bass";
|
|
9364
|
+
return "chord";
|
|
9365
|
+
} else {
|
|
9366
|
+
return TRACK_ROLES[idx] ?? `t${idx}`;
|
|
9367
|
+
}
|
|
9368
|
+
};
|
|
9369
|
+
const getRoleFromTrackId = (trackId, mode = "simple") => {
|
|
9370
|
+
if (trackId === "melody" || trackId === "submelody" || trackId === "bass" || trackId === "chord") {
|
|
9371
|
+
return trackId;
|
|
9372
|
+
}
|
|
9373
|
+
if (trackId.startsWith("t")) {
|
|
9374
|
+
const idx = Number(trackId.substring(1));
|
|
9375
|
+
if (!isNaN(idx)) {
|
|
9376
|
+
return getRoleForTrackIndex(idx, mode);
|
|
9377
|
+
}
|
|
9378
|
+
}
|
|
9379
|
+
return trackId;
|
|
9380
|
+
};
|
|
9050
9381
|
const instrumentNameFor = (preset, trackId) => preset[trackId] ?? preset.melody;
|
|
9051
|
-
const resolveSoundFont = (presetKey, trackId) => {
|
|
9382
|
+
const resolveSoundFont = (presetKey, trackId, mode = "simple") => {
|
|
9052
9383
|
const preset = INSTRUMENT_PRESETS[presetKey];
|
|
9053
9384
|
if (!preset) return void 0;
|
|
9054
|
-
const
|
|
9385
|
+
const role = getRoleFromTrackId(trackId, mode);
|
|
9386
|
+
const key = nameToKey[instrumentNameFor(preset, role)];
|
|
9055
9387
|
return key ? soundFonts.get(key) : void 0;
|
|
9056
9388
|
};
|
|
9057
|
-
const loadPreset = async (presetKey, trackIds = [...TRACK_ROLES]) => {
|
|
9389
|
+
const loadPreset = async (presetKey, trackIds = [...TRACK_ROLES], mode = "simple") => {
|
|
9058
9390
|
const preset = INSTRUMENT_PRESETS[presetKey];
|
|
9059
9391
|
if (!preset) return;
|
|
9060
9392
|
await listReady;
|
|
9061
9393
|
const keys = /* @__PURE__ */ new Set();
|
|
9062
9394
|
for (const trackId of trackIds) {
|
|
9063
|
-
const
|
|
9395
|
+
const role = getRoleFromTrackId(trackId, mode);
|
|
9396
|
+
const key = nameToKey[instrumentNameFor(preset, role)];
|
|
9064
9397
|
if (key) keys.add(key);
|
|
9065
9398
|
}
|
|
9066
9399
|
await Promise.all([...keys].map((key) => loadInstrument(key)));
|
|
9067
9400
|
};
|
|
9068
|
-
const applyPreset = async (daw, presetKey, trackIds, loadingTarget) => {
|
|
9401
|
+
const applyPreset = async (daw, presetKey, trackIds, loadingTarget, mode = "simple") => {
|
|
9069
9402
|
const wasPlaying = daw.getPlaybackState() === "playing";
|
|
9070
9403
|
if (wasPlaying) daw.pause();
|
|
9071
9404
|
const overlay = loadingTarget ? showLoadingOverlay(loadingTarget) : null;
|
|
9072
9405
|
daw.setLoading?.(true);
|
|
9073
9406
|
try {
|
|
9074
9407
|
daw.setInstrument(presetKey);
|
|
9075
|
-
await loadPreset(presetKey, trackIds);
|
|
9408
|
+
await loadPreset(presetKey, trackIds, mode);
|
|
9076
9409
|
} finally {
|
|
9077
9410
|
overlay?.remove();
|
|
9078
9411
|
daw.setLoading?.(false);
|
|
@@ -9107,8 +9440,10 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9107
9440
|
const key = select.value;
|
|
9108
9441
|
opts.onChange?.(key);
|
|
9109
9442
|
const trackIds = opts.getTrackIds?.() ?? [...TRACK_ROLES];
|
|
9443
|
+
const isAdvanced = trackIds.includes("t0");
|
|
9444
|
+
const mode = isAdvanced ? "advanced" : "simple";
|
|
9110
9445
|
try {
|
|
9111
|
-
await applyPreset(daw, key, trackIds, opts.loadingTarget);
|
|
9446
|
+
await applyPreset(daw, key, trackIds, opts.loadingTarget, mode);
|
|
9112
9447
|
} finally {
|
|
9113
9448
|
busy = false;
|
|
9114
9449
|
}
|
|
@@ -9149,13 +9484,20 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9149
9484
|
const mountedPlayers = [];
|
|
9150
9485
|
const mountedModeSwitches = [];
|
|
9151
9486
|
const mountEditor = (target, opts = {}) => {
|
|
9152
|
-
const { preset, presetUI, ...dawOverrides } = opts;
|
|
9487
|
+
const { preset, presetUI, onInstrumentChange, ...dawOverrides } = opts;
|
|
9153
9488
|
const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
|
|
9154
9489
|
const trackIds = tracks.map((t) => t.id);
|
|
9155
9490
|
const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
|
|
9156
|
-
|
|
9491
|
+
const meta = opts.initialMML ? parseMmlMeta(opts.initialMML) : {};
|
|
9492
|
+
const initialPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : presetKey;
|
|
9493
|
+
let editorPreset = initialPreset;
|
|
9494
|
+
const isAdvancedMode = dawOverrides.mode === "advanced";
|
|
9157
9495
|
const playNote = (e) => {
|
|
9158
|
-
const sf = resolveSoundFont(
|
|
9496
|
+
const sf = resolveSoundFont(
|
|
9497
|
+
editorPreset,
|
|
9498
|
+
e.trackId,
|
|
9499
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9500
|
+
);
|
|
9159
9501
|
if (!sf) return;
|
|
9160
9502
|
sf.play({
|
|
9161
9503
|
ctx: audioCtx,
|
|
@@ -9166,6 +9508,14 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9166
9508
|
duration: e.duration
|
|
9167
9509
|
});
|
|
9168
9510
|
};
|
|
9511
|
+
let presetSelect = null;
|
|
9512
|
+
const handleInstrumentChange = (key) => {
|
|
9513
|
+
editorPreset = key;
|
|
9514
|
+
if (presetSelect) {
|
|
9515
|
+
presetSelect.setValue(key);
|
|
9516
|
+
}
|
|
9517
|
+
onInstrumentChange?.(key);
|
|
9518
|
+
};
|
|
9169
9519
|
const base = {
|
|
9170
9520
|
getAudioTime: () => audioCtx.currentTime,
|
|
9171
9521
|
onResumeAudio: resumeAudio,
|
|
@@ -9174,19 +9524,19 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9174
9524
|
singingVoices,
|
|
9175
9525
|
parseMidi,
|
|
9176
9526
|
onToggleRecord,
|
|
9527
|
+
onInstrumentChange: handleInstrumentChange,
|
|
9177
9528
|
...dawOverrides
|
|
9178
9529
|
};
|
|
9179
9530
|
const daw = mountDAW(target, base);
|
|
9180
9531
|
mountedEditors.push(daw);
|
|
9181
9532
|
const wantPresetUI = presetUI ?? features.presetUI;
|
|
9182
|
-
let presetSelect = null;
|
|
9183
9533
|
if (wantPresetUI) {
|
|
9184
9534
|
editorPresetSelects.get(target)?.destroy();
|
|
9185
9535
|
const rollEl = target.querySelector('[data-dtm="roll"]');
|
|
9186
9536
|
presetSelect = mountPresetSelect(target, {
|
|
9187
9537
|
getDaw: () => daw,
|
|
9188
9538
|
getTrackIds: () => trackIds,
|
|
9189
|
-
value:
|
|
9539
|
+
value: initialPreset,
|
|
9190
9540
|
loadingTarget: rollEl ?? target,
|
|
9191
9541
|
position: "prepend",
|
|
9192
9542
|
// 楽器変更時、このエディタの発音解決が使うプリセットも追従させる。
|
|
@@ -9196,9 +9546,13 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9196
9546
|
});
|
|
9197
9547
|
editorPresetSelects.set(target, presetSelect);
|
|
9198
9548
|
}
|
|
9199
|
-
daw.setInstrument(
|
|
9549
|
+
daw.setInstrument(initialPreset);
|
|
9200
9550
|
daw.setLoading?.(true);
|
|
9201
|
-
void loadPreset(
|
|
9551
|
+
void loadPreset(
|
|
9552
|
+
initialPreset,
|
|
9553
|
+
trackIds,
|
|
9554
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9555
|
+
).finally(() => {
|
|
9202
9556
|
daw.setLoading?.(false);
|
|
9203
9557
|
});
|
|
9204
9558
|
const destroy = () => {
|
|
@@ -9209,7 +9563,17 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9209
9563
|
const i = mountedEditors.indexOf(daw);
|
|
9210
9564
|
if (i >= 0) mountedEditors.splice(i, 1);
|
|
9211
9565
|
};
|
|
9212
|
-
return {
|
|
9566
|
+
return {
|
|
9567
|
+
...daw,
|
|
9568
|
+
setInstrument: (name) => {
|
|
9569
|
+
daw.setInstrument(name);
|
|
9570
|
+
editorPreset = name;
|
|
9571
|
+
if (presetSelect) {
|
|
9572
|
+
presetSelect.setValue(name);
|
|
9573
|
+
}
|
|
9574
|
+
},
|
|
9575
|
+
destroy
|
|
9576
|
+
};
|
|
9213
9577
|
};
|
|
9214
9578
|
const mountModeSwitch = (target, opts) => {
|
|
9215
9579
|
const doc = target.ownerDocument;
|
|
@@ -9299,15 +9663,30 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9299
9663
|
const parsed = parseMML(mml, {});
|
|
9300
9664
|
const meta = parsed.meta ?? {};
|
|
9301
9665
|
const playerPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : defaultPreset;
|
|
9666
|
+
const isAdvancedMode = meta.mode === "advanced";
|
|
9302
9667
|
const trackIndices = [
|
|
9303
9668
|
...new Set(parsed.placements.map((p) => p.trackIndex))
|
|
9304
9669
|
];
|
|
9305
|
-
const trackIds = trackIndices.map(
|
|
9670
|
+
const trackIds = trackIndices.map(
|
|
9671
|
+
(idx) => getRoleForTrackIndex(idx, isAdvancedMode ? "advanced" : "simple")
|
|
9672
|
+
);
|
|
9306
9673
|
const loadTrackIds = trackIds.length > 0 ? trackIds : [...TRACK_ROLES];
|
|
9307
|
-
void loadPreset(
|
|
9674
|
+
void loadPreset(
|
|
9675
|
+
playerPreset,
|
|
9676
|
+
loadTrackIds,
|
|
9677
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9678
|
+
);
|
|
9308
9679
|
const playPlayerNote = (e) => {
|
|
9309
|
-
const
|
|
9310
|
-
const
|
|
9680
|
+
const idx = Number(e.trackId);
|
|
9681
|
+
const role = getRoleForTrackIndex(
|
|
9682
|
+
idx,
|
|
9683
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9684
|
+
);
|
|
9685
|
+
const sf = resolveSoundFont(
|
|
9686
|
+
playerPreset,
|
|
9687
|
+
role,
|
|
9688
|
+
isAdvancedMode ? "advanced" : "simple"
|
|
9689
|
+
);
|
|
9311
9690
|
if (!sf) return;
|
|
9312
9691
|
sf.play({
|
|
9313
9692
|
ctx: audioCtx,
|