@onjmin/dtm 0.1.57 → 0.1.59
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/README.md +28 -0
- package/dist/index.d.mts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +346 -93
- package/dist/index.mjs +346 -93
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5142,8 +5142,6 @@ var DAW_CSS = `
|
|
|
5142
5142
|
max-height: 180px;
|
|
5143
5143
|
overflow-y: auto;
|
|
5144
5144
|
padding: 8px 10px 10px;
|
|
5145
|
-
overflow-x: auto;
|
|
5146
|
-
white-space: nowrap;
|
|
5147
5145
|
scrollbar-width: none;
|
|
5148
5146
|
display: flex;
|
|
5149
5147
|
flex-direction: column;
|
|
@@ -5172,6 +5170,7 @@ var DAW_CSS = `
|
|
|
5172
5170
|
.dtm-cp-bar {
|
|
5173
5171
|
display: flex;
|
|
5174
5172
|
align-items: center;
|
|
5173
|
+
flex-wrap: wrap;
|
|
5175
5174
|
gap: 2px;
|
|
5176
5175
|
margin-top: 2px;
|
|
5177
5176
|
}
|
|
@@ -5540,6 +5539,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
5540
5539
|
const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
|
|
5541
5540
|
const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
|
|
5542
5541
|
const trackVolume = meta.volume ?? options.volume ?? 100;
|
|
5542
|
+
let masterVolume = options.masterVolume ?? 100;
|
|
5543
5543
|
const drumVolume = meta.drumVolume ?? 80;
|
|
5544
5544
|
const colors = options.trackColors ?? DEFAULT_TRACK_COLORS;
|
|
5545
5545
|
const useSynth = options.synth ?? !options.onPlayNote;
|
|
@@ -6319,11 +6319,12 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6319
6319
|
const em = emojiByTrack.get(trackIdx);
|
|
6320
6320
|
if (em) jumpEmojiAt(em, e.when);
|
|
6321
6321
|
if (lyricTracks.has(trackIdx) && !skipSinging) return;
|
|
6322
|
-
|
|
6323
|
-
|
|
6322
|
+
const volume = e.volume * (masterVolume / 100);
|
|
6323
|
+
options.onPlayNote?.({ ...e, volume });
|
|
6324
|
+
if (useSynth) ensureSynth().playNote({ ...e, volume });
|
|
6324
6325
|
},
|
|
6325
6326
|
onPlayDrum: (e) => {
|
|
6326
|
-
const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100);
|
|
6327
|
+
const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100) * (masterVolume / 100);
|
|
6327
6328
|
options.onPlayDrum?.({ ...e, velocity });
|
|
6328
6329
|
if (useSynth) ensureSynth().playDrum({ ...e, velocity });
|
|
6329
6330
|
},
|
|
@@ -6347,6 +6348,9 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6347
6348
|
if (activePlayer === instance) activePlayer = null;
|
|
6348
6349
|
options.onStop?.();
|
|
6349
6350
|
};
|
|
6351
|
+
const setVolume = (volume) => {
|
|
6352
|
+
masterVolume = volume;
|
|
6353
|
+
};
|
|
6350
6354
|
const buildStreamTracks = () => [...lyricTracks.entries()].map(([index, lt]) => {
|
|
6351
6355
|
const seqTrack = seqTracks.find((t) => Number(t.id) === index);
|
|
6352
6356
|
const sorted = [...seqTrack?.notes ?? []].sort(
|
|
@@ -6368,7 +6372,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6368
6372
|
return {
|
|
6369
6373
|
id: String(index),
|
|
6370
6374
|
model: lt.model,
|
|
6371
|
-
volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (trackVolume / 100),
|
|
6375
|
+
volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (trackVolume / 100) * (masterVolume / 100),
|
|
6372
6376
|
pan: panToStereo(lt.pan ?? DEFAULT_PAN),
|
|
6373
6377
|
notes
|
|
6374
6378
|
};
|
|
@@ -6482,6 +6486,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6482
6486
|
play,
|
|
6483
6487
|
stop,
|
|
6484
6488
|
isPlaying: () => playing,
|
|
6489
|
+
setVolume,
|
|
6485
6490
|
destroy
|
|
6486
6491
|
};
|
|
6487
6492
|
return instance;
|
|
@@ -6874,11 +6879,13 @@ var loadDrumFont = (ctx, pitch) => {
|
|
|
6874
6879
|
_drumLoading.set(pitch, p);
|
|
6875
6880
|
return p;
|
|
6876
6881
|
};
|
|
6882
|
+
var BAR_SEP = /[|ll→]/;
|
|
6883
|
+
var NON_EVENT_RE = /^[=_]$|^N\.C\.$|^N$/i;
|
|
6877
6884
|
var buildDisplayLines = (chords, eventsCount) => {
|
|
6878
6885
|
const rawLines = chords.split("\n").map((l) => l.trim()).filter((l) => l);
|
|
6879
6886
|
const displayLines = [];
|
|
6880
6887
|
let eventCounter = 0;
|
|
6881
|
-
let colorIdx =
|
|
6888
|
+
let colorIdx = 0;
|
|
6882
6889
|
for (const line of rawLines) {
|
|
6883
6890
|
if (/^#/.test(line)) {
|
|
6884
6891
|
const label = line.replace(/^#\s*/, "").trim();
|
|
@@ -6886,19 +6893,21 @@ var buildDisplayLines = (chords, eventsCount) => {
|
|
|
6886
6893
|
if (/^tone\s*=/i.test(label)) continue;
|
|
6887
6894
|
if (/^instrument\s*=/i.test(label)) continue;
|
|
6888
6895
|
if (/^metronome/i.test(label)) continue;
|
|
6889
|
-
colorIdx++;
|
|
6896
|
+
if (displayLines.length > 0 || eventCounter > 0) colorIdx++;
|
|
6890
6897
|
displayLines.push({
|
|
6891
6898
|
type: "section",
|
|
6892
6899
|
section: label,
|
|
6893
|
-
colorIdx
|
|
6900
|
+
colorIdx
|
|
6894
6901
|
});
|
|
6895
6902
|
continue;
|
|
6896
6903
|
}
|
|
6897
|
-
const segments = line.split(
|
|
6898
|
-
if (segments.length
|
|
6904
|
+
const segments = line.split(BAR_SEP);
|
|
6905
|
+
if (segments.length === 0) continue;
|
|
6906
|
+
const hasMultiSeg = segments.length > 1;
|
|
6899
6907
|
const parts = [];
|
|
6900
6908
|
for (let i = 0; i < segments.length; i++) {
|
|
6901
|
-
if (i > 0)
|
|
6909
|
+
if (hasMultiSeg && i > 0)
|
|
6910
|
+
parts.push({ text: "|", isChord: false, eventIdx: -1 });
|
|
6902
6911
|
const bar = segments[i].trim();
|
|
6903
6912
|
if (!bar) continue;
|
|
6904
6913
|
const splitAt = [];
|
|
@@ -6921,7 +6930,7 @@ var buildDisplayLines = (chords, eventsCount) => {
|
|
|
6921
6930
|
splitAt.push(j + 1);
|
|
6922
6931
|
continue;
|
|
6923
6932
|
}
|
|
6924
|
-
if (/^[A-G]
|
|
6933
|
+
if (/^[A-G]$/.test(char)) {
|
|
6925
6934
|
const prev = bar[j - 1];
|
|
6926
6935
|
const prev2 = bar.slice(Math.max(0, j - 2), j);
|
|
6927
6936
|
if (prev === "/" || prev2.toLowerCase() === "on") continue;
|
|
@@ -6930,12 +6939,13 @@ var buildDisplayLines = (chords, eventsCount) => {
|
|
|
6930
6939
|
}
|
|
6931
6940
|
const uniqueSplitAt = Array.from(new Set(splitAt)).filter((idx) => idx >= 0 && idx < bar.length).sort((a, b) => a - b);
|
|
6932
6941
|
if (uniqueSplitAt.length === 0) {
|
|
6942
|
+
const isEvent = !NON_EVENT_RE.test(bar);
|
|
6933
6943
|
parts.push({
|
|
6934
6944
|
text: bar,
|
|
6935
6945
|
isChord: true,
|
|
6936
|
-
eventIdx: eventCounter < eventsCount ? eventCounter : -1
|
|
6946
|
+
eventIdx: isEvent && eventCounter < eventsCount ? eventCounter : -1
|
|
6937
6947
|
});
|
|
6938
|
-
eventCounter++;
|
|
6948
|
+
if (isEvent) eventCounter++;
|
|
6939
6949
|
continue;
|
|
6940
6950
|
}
|
|
6941
6951
|
for (let ci = 0; ci < uniqueSplitAt.length; ci++) {
|
|
@@ -6944,19 +6954,20 @@ var buildDisplayLines = (chords, eventsCount) => {
|
|
|
6944
6954
|
const end = ci < uniqueSplitAt.length - 1 ? uniqueSplitAt[ci + 1] : bar.length;
|
|
6945
6955
|
const symbol = bar.slice(start, end).trim();
|
|
6946
6956
|
if (symbol) {
|
|
6957
|
+
const isEvent = !NON_EVENT_RE.test(symbol);
|
|
6947
6958
|
parts.push({
|
|
6948
6959
|
text: symbol,
|
|
6949
6960
|
isChord: true,
|
|
6950
|
-
eventIdx: eventCounter < eventsCount ? eventCounter : -1
|
|
6961
|
+
eventIdx: isEvent && eventCounter < eventsCount ? eventCounter : -1
|
|
6951
6962
|
});
|
|
6952
|
-
eventCounter++;
|
|
6963
|
+
if (isEvent) eventCounter++;
|
|
6953
6964
|
}
|
|
6954
6965
|
}
|
|
6955
6966
|
}
|
|
6956
6967
|
if (parts.length > 0) {
|
|
6957
6968
|
displayLines.push({
|
|
6958
6969
|
type: "bar",
|
|
6959
|
-
colorIdx
|
|
6970
|
+
colorIdx,
|
|
6960
6971
|
parts
|
|
6961
6972
|
});
|
|
6962
6973
|
}
|
|
@@ -6970,6 +6981,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
6970
6981
|
let activeIndex = -1;
|
|
6971
6982
|
let activePlayback = null;
|
|
6972
6983
|
let isPlaying = false;
|
|
6984
|
+
let masterVolume = options.volume ?? 80;
|
|
6973
6985
|
let isLoading = false;
|
|
6974
6986
|
let loadAbortId = 0;
|
|
6975
6987
|
let loopEnabled = false;
|
|
@@ -7329,7 +7341,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
7329
7341
|
container2.innerHTML = "";
|
|
7330
7342
|
modalSamplePlayer = mountChordPlayer(container2, sampleChords, {
|
|
7331
7343
|
audioContext: options.audioContext ?? ownCtx ?? void 0,
|
|
7332
|
-
volume:
|
|
7344
|
+
volume: masterVolume,
|
|
7333
7345
|
bpm: options.bpm ?? 120,
|
|
7334
7346
|
studio: options.studio,
|
|
7335
7347
|
// 再帰的なモーダル生成を防ぐ。
|
|
@@ -7670,7 +7682,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
7670
7682
|
};
|
|
7671
7683
|
const playInternal = (startIdx, ctx, cutGain) => {
|
|
7672
7684
|
const C32 = 48;
|
|
7673
|
-
const volume =
|
|
7685
|
+
const volume = masterVolume;
|
|
7674
7686
|
const placements = [];
|
|
7675
7687
|
for (const event of chordEvents) {
|
|
7676
7688
|
if (event.when < playOffsetSec) continue;
|
|
@@ -7827,6 +7839,10 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
7827
7839
|
}
|
|
7828
7840
|
}
|
|
7829
7841
|
};
|
|
7842
|
+
const setVolume = (volume) => {
|
|
7843
|
+
masterVolume = Math.max(0, Math.min(100, volume));
|
|
7844
|
+
activePlayback?.setVolume(masterVolume);
|
|
7845
|
+
};
|
|
7830
7846
|
const destroy = () => {
|
|
7831
7847
|
stop();
|
|
7832
7848
|
closeInfoModal();
|
|
@@ -7956,6 +7972,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
7956
7972
|
element: container,
|
|
7957
7973
|
play,
|
|
7958
7974
|
stop,
|
|
7975
|
+
setVolume,
|
|
7959
7976
|
destroy,
|
|
7960
7977
|
toggleMetronome,
|
|
7961
7978
|
isPlaying: () => isPlaying
|
|
@@ -8890,6 +8907,18 @@ var MidiSearchClient = class {
|
|
|
8890
8907
|
if (!res.ok) throw new Error(`picotune fetch failed: ${res.status}`);
|
|
8891
8908
|
return res.arrayBuffer();
|
|
8892
8909
|
}
|
|
8910
|
+
async searchRechord(word) {
|
|
8911
|
+
if (!this.enabled) return [];
|
|
8912
|
+
const qs = new URLSearchParams();
|
|
8913
|
+
qs.set("word", word);
|
|
8914
|
+
qs.set("guest", "true");
|
|
8915
|
+
const url2 = `${this.baseUrl}/rechord/scores?${qs.toString()}`;
|
|
8916
|
+
const res = await fetch(url2, { headers: this.headers() });
|
|
8917
|
+
if (!res.ok) throw new Error(`rechord search failed: ${res.status}`);
|
|
8918
|
+
const body = await res.json();
|
|
8919
|
+
const list = body.result ?? body.data ?? body;
|
|
8920
|
+
return Array.isArray(list) ? list : [];
|
|
8921
|
+
}
|
|
8893
8922
|
};
|
|
8894
8923
|
|
|
8895
8924
|
// src/linked-list.ts
|
|
@@ -9977,6 +10006,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
9977
10006
|
showChord,
|
|
9978
10007
|
showMidiSearch
|
|
9979
10008
|
});
|
|
10009
|
+
refs.masterVolume.value = String(options.masterVolume ?? 50);
|
|
10010
|
+
refs.masterVolumeLabel.textContent = `${options.masterVolume ?? 50}%`;
|
|
10011
|
+
refs.drumVolume.value = String(options.drumVolume ?? 80);
|
|
10012
|
+
refs.drumVolumeLabel.textContent = `${options.drumVolume ?? 80}%`;
|
|
9980
10013
|
const renderConfig = {
|
|
9981
10014
|
stepsPerBar: 192,
|
|
9982
10015
|
keyCount: 128,
|
|
@@ -9988,8 +10021,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
9988
10021
|
let zoomX = 100;
|
|
9989
10022
|
let zoomY = 100;
|
|
9990
10023
|
let bpm = options.defaultBpm ?? DEFAULT_BPM;
|
|
9991
|
-
let masterVolume = 50;
|
|
9992
|
-
let drumVolume = 80;
|
|
10024
|
+
let masterVolume = options.masterVolume ?? 50;
|
|
10025
|
+
let drumVolume = options.drumVolume ?? 80;
|
|
9993
10026
|
let currentDrumPattern = refs.drumSelect.value;
|
|
9994
10027
|
let currentInstrument = "";
|
|
9995
10028
|
let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
|
|
@@ -11314,6 +11347,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
11314
11347
|
<div style="display: inline-flex; align-items: center; gap: 6px;">
|
|
11315
11348
|
<span class="dtm-label">\u548C\u97F3</span>
|
|
11316
11349
|
<button class="dtm-infobtn" data-dtm="chord-info" title="\u30B3\u30FC\u30C9\u9032\u884C\u306E\u66F8\u304D\u65B9\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
11350
|
+
${showMidiSearch ? `<button class="dtm-btn dtm-btn--ghost" data-dtm="chord-search" title="\u30B3\u30FC\u30C9\u9032\u884C\u691C\u7D22" style="min-height: 24px; min-width: auto; height: 24px; padding: 0 6px; font-size: 11px; display: inline-flex; align-items: center;">\u30B3\u30FC\u30C9\u691C\u7D22</button>` : ""}
|
|
11317
11351
|
</div>
|
|
11318
11352
|
<select class="dtm-select" data-dtm="chord-pattern">
|
|
11319
11353
|
<option value="block">\u30D6\u30ED\u30C3\u30AF</option>
|
|
@@ -11349,6 +11383,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
11349
11383
|
save();
|
|
11350
11384
|
applyChord();
|
|
11351
11385
|
});
|
|
11386
|
+
const searchChordBtn = div.querySelector(
|
|
11387
|
+
'[data-dtm="chord-search"]'
|
|
11388
|
+
);
|
|
11389
|
+
if (searchChordBtn) {
|
|
11390
|
+
searchChordBtn.addEventListener("click", () => {
|
|
11391
|
+
openChordSearchModal(input);
|
|
11392
|
+
});
|
|
11393
|
+
}
|
|
11352
11394
|
}
|
|
11353
11395
|
};
|
|
11354
11396
|
const switchTrack = (id) => {
|
|
@@ -12212,6 +12254,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
12212
12254
|
searchPreviewBtn = null;
|
|
12213
12255
|
}
|
|
12214
12256
|
};
|
|
12257
|
+
let mmlSearchCache = null;
|
|
12258
|
+
let chordSearchCache = null;
|
|
12215
12259
|
const wireMidiSearch = () => {
|
|
12216
12260
|
if (!midiSearchClient) return;
|
|
12217
12261
|
refs.midiSearchOpenBtn.addEventListener("click", () => {
|
|
@@ -12222,6 +12266,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
12222
12266
|
<input type="text" class="dtm-input dtm-grow" data-dtm="modal-midi-search-input" placeholder="\u66F2\u540D\u3067MML\u691C\u7D22" style="min-width:0">
|
|
12223
12267
|
<button class="dtm-btn dtm-btn--primary" data-dtm="modal-midi-search-btn" style="flex-shrink:0">\u691C\u7D22</button>
|
|
12224
12268
|
</div>
|
|
12269
|
+
<div data-dtm="modal-midi-search-status" style="font-size:11px;color:rgba(255,255,255,0.5);min-height:1.4em"></div>
|
|
12225
12270
|
<div class="dtm-row" data-dtm="modal-midi-search-results" style="flex-direction:column;align-items:stretch;gap:4px"></div>
|
|
12226
12271
|
`
|
|
12227
12272
|
);
|
|
@@ -12231,9 +12276,17 @@ var mountDAW = (target, options = {}) => {
|
|
|
12231
12276
|
const searchBtn = refs.modalBody.querySelector(
|
|
12232
12277
|
'[data-dtm="modal-midi-search-btn"]'
|
|
12233
12278
|
);
|
|
12279
|
+
const statusEl = refs.modalBody.querySelector(
|
|
12280
|
+
'[data-dtm="modal-midi-search-status"]'
|
|
12281
|
+
);
|
|
12234
12282
|
const results = refs.modalBody.querySelector(
|
|
12235
12283
|
'[data-dtm="modal-midi-search-results"]'
|
|
12236
12284
|
);
|
|
12285
|
+
if (mmlSearchCache) {
|
|
12286
|
+
input.value = mmlSearchCache.query;
|
|
12287
|
+
statusEl.textContent = `\u300C${mmlSearchCache.query}\u300D\u306E\u691C\u7D22\u7D50\u679C\u3000${mmlSearchCache.count}\u4EF6\u306E\u30D2\u30C3\u30C8`;
|
|
12288
|
+
mmlSearchCache.renderResults(results);
|
|
12289
|
+
}
|
|
12237
12290
|
const runSearch = async () => {
|
|
12238
12291
|
const q2 = input.value.trim();
|
|
12239
12292
|
if (!q2) return;
|
|
@@ -12241,37 +12294,85 @@ var mountDAW = (target, options = {}) => {
|
|
|
12241
12294
|
searchBtn.disabled = true;
|
|
12242
12295
|
searchBtn.textContent = "\u691C\u7D22\u4E2D...";
|
|
12243
12296
|
results.innerHTML = "";
|
|
12297
|
+
statusEl.textContent = "";
|
|
12244
12298
|
try {
|
|
12245
12299
|
const songs = await midiSearchClient.searchSongs({ title: q2 });
|
|
12246
12300
|
if (songs.length === 0) {
|
|
12301
|
+
statusEl.textContent = `\u300C${q2}\u300D\u306E\u691C\u7D22\u7D50\u679C\u30000\u4EF6\u306E\u30D2\u30C3\u30C8`;
|
|
12247
12302
|
results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
|
|
12303
|
+
mmlSearchCache = {
|
|
12304
|
+
query: q2,
|
|
12305
|
+
count: 0,
|
|
12306
|
+
renderResults: (c) => {
|
|
12307
|
+
c.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
|
|
12308
|
+
}
|
|
12309
|
+
};
|
|
12248
12310
|
return;
|
|
12249
12311
|
}
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
const
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
|
|
12257
|
-
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12312
|
+
statusEl.textContent = `\u300C${q2}\u300D\u306E\u691C\u7D22\u7D50\u679C\u3000${songs.length}\u4EF6\u306E\u30D2\u30C3\u30C8`;
|
|
12313
|
+
const renderMmlResults = (container) => {
|
|
12314
|
+
container.innerHTML = "";
|
|
12315
|
+
for (const song of songs) {
|
|
12316
|
+
const box = document.createElement("div");
|
|
12317
|
+
box.className = "dtm-modal-sample-box";
|
|
12318
|
+
const row = document.createElement("div");
|
|
12319
|
+
row.className = "dtm-row";
|
|
12320
|
+
row.style.cssText = "flex-wrap:nowrap;gap:4px;padding:2px 0";
|
|
12321
|
+
const label = document.createElement("span");
|
|
12322
|
+
label.className = "dtm-label";
|
|
12323
|
+
label.style.cssText = "flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
12324
|
+
label.textContent = `${song.title} - ${song.user}`;
|
|
12325
|
+
const previewBtn = document.createElement("button");
|
|
12326
|
+
previewBtn.className = "dtm-btn dtm-btn--ghost";
|
|
12327
|
+
previewBtn.style.cssText = "flex-shrink:0";
|
|
12328
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
12329
|
+
const loadBtn = document.createElement("button");
|
|
12330
|
+
loadBtn.className = "dtm-btn dtm-btn--primary";
|
|
12331
|
+
loadBtn.style.cssText = "flex-shrink:0";
|
|
12332
|
+
loadBtn.textContent = "\u8AAD\u307F\u8FBC\u307F";
|
|
12333
|
+
const playerContainer = document.createElement("div");
|
|
12334
|
+
playerContainer.className = "dtm-modal-sample-player-container";
|
|
12335
|
+
previewBtn.addEventListener("click", async () => {
|
|
12336
|
+
if (searchPreviewBtn === previewBtn) {
|
|
12337
|
+
if (searchPreviewPlayer?.isPlaying()) {
|
|
12338
|
+
searchPreviewPlayer.stop();
|
|
12339
|
+
} else {
|
|
12340
|
+
stop();
|
|
12341
|
+
previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
12342
|
+
try {
|
|
12343
|
+
const midi = await fetchVerifiedMidi(song.file);
|
|
12344
|
+
const mml = buildPreviewMml(midi);
|
|
12345
|
+
playerContainer.innerHTML = "";
|
|
12346
|
+
const player = mountMmlPlayer(playerContainer, mml, {
|
|
12347
|
+
onPlayNote: options.onPlayNote,
|
|
12348
|
+
onPlayDrum: options.onPlayDrum,
|
|
12349
|
+
onResumeAudio: options.onResumeAudio,
|
|
12350
|
+
getAudioTime: options.getAudioTime,
|
|
12351
|
+
singingVoices: options.singingVoices,
|
|
12352
|
+
drumPatterns: options.drumPatterns,
|
|
12353
|
+
volume: masterVolume,
|
|
12354
|
+
_skipInfoModals: true,
|
|
12355
|
+
onStop: () => {
|
|
12356
|
+
if (searchPreviewBtn === previewBtn) {
|
|
12357
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
12358
|
+
previewBtn.classList.remove("dtm-btn--danger");
|
|
12359
|
+
previewBtn.classList.add("dtm-btn--ghost");
|
|
12360
|
+
}
|
|
12361
|
+
}
|
|
12362
|
+
});
|
|
12363
|
+
searchPreviewPlayer = player;
|
|
12364
|
+
searchPreviewBtn = previewBtn;
|
|
12365
|
+
player.play();
|
|
12366
|
+
previewBtn.textContent = "\u25A0 \u505C\u6B62";
|
|
12367
|
+
previewBtn.classList.remove("dtm-btn--ghost");
|
|
12368
|
+
previewBtn.classList.add("dtm-btn--danger");
|
|
12369
|
+
} catch (e) {
|
|
12370
|
+
console.error("[dtm] MIDI preview failed", e);
|
|
12371
|
+
previewBtn.textContent = "\u5931\u6557";
|
|
12372
|
+
}
|
|
12373
|
+
}
|
|
12274
12374
|
} else {
|
|
12375
|
+
collapseSearchPreview();
|
|
12275
12376
|
stop();
|
|
12276
12377
|
previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
12277
12378
|
try {
|
|
@@ -12306,31 +12407,169 @@ var mountDAW = (target, options = {}) => {
|
|
|
12306
12407
|
previewBtn.textContent = "\u5931\u6557";
|
|
12307
12408
|
}
|
|
12308
12409
|
}
|
|
12410
|
+
});
|
|
12411
|
+
loadBtn.addEventListener("click", async () => {
|
|
12412
|
+
loadBtn.disabled = true;
|
|
12413
|
+
loadBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
12414
|
+
try {
|
|
12415
|
+
const pending = await fetchVerifiedMidi(song.file);
|
|
12416
|
+
const analysis = analyzeMidiTracks(pending);
|
|
12417
|
+
const sel = analysis.filter((a) => a.selected).map((a) => a.index);
|
|
12418
|
+
collapseSearchPreview();
|
|
12419
|
+
overlayDuring(() => applyMidiSelection(pending, sel));
|
|
12420
|
+
refs.modalOverlay.setAttribute("hidden", "");
|
|
12421
|
+
} catch (e) {
|
|
12422
|
+
console.error("[dtm] MIDI fetch failed", e);
|
|
12423
|
+
loadBtn.textContent = "\u5931\u6557";
|
|
12424
|
+
} finally {
|
|
12425
|
+
loadBtn.disabled = false;
|
|
12426
|
+
}
|
|
12427
|
+
});
|
|
12428
|
+
row.appendChild(label);
|
|
12429
|
+
row.appendChild(previewBtn);
|
|
12430
|
+
row.appendChild(loadBtn);
|
|
12431
|
+
box.appendChild(row);
|
|
12432
|
+
box.appendChild(playerContainer);
|
|
12433
|
+
container.appendChild(box);
|
|
12434
|
+
}
|
|
12435
|
+
};
|
|
12436
|
+
renderMmlResults(results);
|
|
12437
|
+
mmlSearchCache = {
|
|
12438
|
+
query: q2,
|
|
12439
|
+
count: songs.length,
|
|
12440
|
+
renderResults: renderMmlResults
|
|
12441
|
+
};
|
|
12442
|
+
} catch (e) {
|
|
12443
|
+
console.error("[dtm] MIDI search failed", e);
|
|
12444
|
+
results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F</span>';
|
|
12445
|
+
} finally {
|
|
12446
|
+
searchBtn.disabled = false;
|
|
12447
|
+
searchBtn.textContent = "\u691C\u7D22";
|
|
12448
|
+
}
|
|
12449
|
+
};
|
|
12450
|
+
searchBtn.addEventListener("click", () => void runSearch());
|
|
12451
|
+
input.addEventListener("keydown", (e) => {
|
|
12452
|
+
if (e.key === "Enter") void runSearch();
|
|
12453
|
+
});
|
|
12454
|
+
input.focus();
|
|
12455
|
+
});
|
|
12456
|
+
};
|
|
12457
|
+
const openChordSearchModal = (chordTextArea) => {
|
|
12458
|
+
if (!midiSearchClient) return;
|
|
12459
|
+
const active = trackStates.find((t) => t.config.id === activeTrackId);
|
|
12460
|
+
if (!active) return;
|
|
12461
|
+
showModal(
|
|
12462
|
+
"\u30B3\u30FC\u30C9\u9032\u884C\u691C\u7D22",
|
|
12463
|
+
`
|
|
12464
|
+
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
12465
|
+
<input type="text" class="dtm-input dtm-grow" data-dtm="modal-chord-search-input" placeholder="\u66F2\u540D\u3084\u30B3\u30FC\u30C9\u9032\u884C\u3067\u691C\u7D22" style="min-width:0">
|
|
12466
|
+
<button class="dtm-btn dtm-btn--primary" data-dtm="modal-chord-search-btn" style="flex-shrink:0">\u691C\u7D22</button>
|
|
12467
|
+
</div>
|
|
12468
|
+
<div data-dtm="modal-chord-search-status" style="font-size:11px;color:rgba(255,255,255,0.5);min-height:1.4em"></div>
|
|
12469
|
+
<div class="dtm-row" data-dtm="modal-chord-search-results" style="flex-direction:column;align-items:stretch;gap:4px"></div>
|
|
12470
|
+
`
|
|
12471
|
+
);
|
|
12472
|
+
const input = refs.modalBody.querySelector(
|
|
12473
|
+
'[data-dtm="modal-chord-search-input"]'
|
|
12474
|
+
);
|
|
12475
|
+
const searchBtn = refs.modalBody.querySelector(
|
|
12476
|
+
'[data-dtm="modal-chord-search-btn"]'
|
|
12477
|
+
);
|
|
12478
|
+
const statusEl = refs.modalBody.querySelector(
|
|
12479
|
+
'[data-dtm="modal-chord-search-status"]'
|
|
12480
|
+
);
|
|
12481
|
+
const results = refs.modalBody.querySelector(
|
|
12482
|
+
'[data-dtm="modal-chord-search-results"]'
|
|
12483
|
+
);
|
|
12484
|
+
if (chordSearchCache) {
|
|
12485
|
+
input.value = chordSearchCache.query;
|
|
12486
|
+
statusEl.textContent = `\u300C${chordSearchCache.query}\u300D\u306E\u691C\u7D22\u7D50\u679C\u3000${chordSearchCache.count}\u4EF6\u306E\u30D2\u30C3\u30C8`;
|
|
12487
|
+
chordSearchCache.renderResults(results);
|
|
12488
|
+
}
|
|
12489
|
+
const runSearch = async () => {
|
|
12490
|
+
const q2 = input.value.trim();
|
|
12491
|
+
if (!q2) return;
|
|
12492
|
+
collapseSearchPreview();
|
|
12493
|
+
searchBtn.disabled = true;
|
|
12494
|
+
searchBtn.textContent = "\u691C\u7D22\u4E2D...";
|
|
12495
|
+
results.innerHTML = "";
|
|
12496
|
+
statusEl.textContent = "";
|
|
12497
|
+
try {
|
|
12498
|
+
const scores = await midiSearchClient.searchRechord(q2);
|
|
12499
|
+
if (scores.length === 0) {
|
|
12500
|
+
statusEl.textContent = `\u300C${q2}\u300D\u306E\u691C\u7D22\u7D50\u679C\u30000\u4EF6\u306E\u30D2\u30C3\u30C8`;
|
|
12501
|
+
results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
|
|
12502
|
+
chordSearchCache = {
|
|
12503
|
+
query: q2,
|
|
12504
|
+
count: 0,
|
|
12505
|
+
renderResults: (c) => {
|
|
12506
|
+
c.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F</span>';
|
|
12507
|
+
}
|
|
12508
|
+
};
|
|
12509
|
+
return;
|
|
12510
|
+
}
|
|
12511
|
+
statusEl.textContent = `\u300C${q2}\u300D\u306E\u691C\u7D22\u7D50\u679C\u3000${scores.length}\u4EF6\u306E\u30D2\u30C3\u30C8`;
|
|
12512
|
+
const renderChordResults = (container) => {
|
|
12513
|
+
container.innerHTML = "";
|
|
12514
|
+
for (const score of scores) {
|
|
12515
|
+
const box = document.createElement("div");
|
|
12516
|
+
box.className = "dtm-modal-sample-box";
|
|
12517
|
+
const row = document.createElement("div");
|
|
12518
|
+
row.className = "dtm-row";
|
|
12519
|
+
row.style.cssText = "flex-wrap:nowrap;gap:4px;padding:2px 0";
|
|
12520
|
+
const label = document.createElement("span");
|
|
12521
|
+
label.className = "dtm-label";
|
|
12522
|
+
label.style.cssText = "flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
12523
|
+
label.textContent = `${score.title}${score.user?.screen_name ? ` - ${score.user.screen_name}` : ""}`;
|
|
12524
|
+
const previewBtn = document.createElement("button");
|
|
12525
|
+
previewBtn.className = "dtm-btn dtm-btn--ghost";
|
|
12526
|
+
previewBtn.style.cssText = "flex-shrink:0";
|
|
12527
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
12528
|
+
const loadBtn = document.createElement("button");
|
|
12529
|
+
loadBtn.className = "dtm-btn dtm-btn--primary";
|
|
12530
|
+
loadBtn.style.cssText = "flex-shrink:0";
|
|
12531
|
+
loadBtn.textContent = "\u8AAD\u307F\u8FBC\u307F";
|
|
12532
|
+
const playerContainer = document.createElement("div");
|
|
12533
|
+
playerContainer.className = "dtm-modal-sample-player-container";
|
|
12534
|
+
previewBtn.addEventListener("click", async () => {
|
|
12535
|
+
if (searchPreviewBtn === previewBtn) {
|
|
12536
|
+
if (searchPreviewPlayer?.isPlaying()) {
|
|
12537
|
+
searchPreviewPlayer.stop();
|
|
12538
|
+
} else {
|
|
12539
|
+
stop();
|
|
12540
|
+
previewBtn.textContent = "\u518D\u751F\u4E2D...";
|
|
12541
|
+
try {
|
|
12542
|
+
searchPreviewPlayer?.play();
|
|
12543
|
+
previewBtn.textContent = "\u25A0 \u505C\u6B62";
|
|
12544
|
+
previewBtn.classList.remove("dtm-btn--ghost");
|
|
12545
|
+
previewBtn.classList.add("dtm-btn--danger");
|
|
12546
|
+
} catch (e) {
|
|
12547
|
+
console.error("[dtm] Chord preview play failed", e);
|
|
12548
|
+
previewBtn.textContent = "\u5931\u6557";
|
|
12549
|
+
}
|
|
12550
|
+
}
|
|
12309
12551
|
} else {
|
|
12310
12552
|
collapseSearchPreview();
|
|
12311
12553
|
stop();
|
|
12312
12554
|
previewBtn.textContent = "\u8AAD\u8FBC\u4E2D...";
|
|
12313
12555
|
try {
|
|
12314
|
-
const midi = await fetchVerifiedMidi(song.file);
|
|
12315
|
-
const mml = buildPreviewMml(midi);
|
|
12316
12556
|
playerContainer.innerHTML = "";
|
|
12317
|
-
const player =
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12328
|
-
|
|
12329
|
-
|
|
12330
|
-
previewBtn.classList.add("dtm-btn--ghost");
|
|
12557
|
+
const player = mountChordPlayer(
|
|
12558
|
+
playerContainer,
|
|
12559
|
+
score.content,
|
|
12560
|
+
{
|
|
12561
|
+
volume: masterVolume,
|
|
12562
|
+
bpm: score.bpm ?? bpm ?? 120,
|
|
12563
|
+
_skipInfoModals: true,
|
|
12564
|
+
onStop: () => {
|
|
12565
|
+
if (searchPreviewBtn === previewBtn) {
|
|
12566
|
+
previewBtn.textContent = "\u25B6 \u8A66\u8074";
|
|
12567
|
+
previewBtn.classList.remove("dtm-btn--danger");
|
|
12568
|
+
previewBtn.classList.add("dtm-btn--ghost");
|
|
12569
|
+
}
|
|
12331
12570
|
}
|
|
12332
12571
|
}
|
|
12333
|
-
|
|
12572
|
+
);
|
|
12334
12573
|
searchPreviewPlayer = player;
|
|
12335
12574
|
searchPreviewBtn = previewBtn;
|
|
12336
12575
|
player.play();
|
|
@@ -12338,49 +12577,48 @@ var mountDAW = (target, options = {}) => {
|
|
|
12338
12577
|
previewBtn.classList.remove("dtm-btn--ghost");
|
|
12339
12578
|
previewBtn.classList.add("dtm-btn--danger");
|
|
12340
12579
|
} catch (e) {
|
|
12341
|
-
console.error("[dtm]
|
|
12580
|
+
console.error("[dtm] Chord preview mount failed", e);
|
|
12342
12581
|
previewBtn.textContent = "\u5931\u6557";
|
|
12343
12582
|
}
|
|
12344
12583
|
}
|
|
12345
12584
|
});
|
|
12346
|
-
loadBtn.addEventListener("click",
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
const sel = analysis.filter((a) => a.selected).map((a) => a.index);
|
|
12353
|
-
collapseSearchPreview();
|
|
12354
|
-
overlayDuring(() => applyMidiSelection(pending, sel));
|
|
12355
|
-
refs.modalOverlay.setAttribute("hidden", "");
|
|
12356
|
-
} catch (e) {
|
|
12357
|
-
console.error("[dtm] MIDI fetch failed", e);
|
|
12358
|
-
loadBtn.textContent = "\u5931\u6557";
|
|
12359
|
-
} finally {
|
|
12360
|
-
loadBtn.disabled = false;
|
|
12585
|
+
loadBtn.addEventListener("click", () => {
|
|
12586
|
+
collapseSearchPreview();
|
|
12587
|
+
chordTextArea.value = score.content;
|
|
12588
|
+
active.savedChordInput = score.content;
|
|
12589
|
+
if (score.bpm) {
|
|
12590
|
+
setBpm(score.bpm);
|
|
12361
12591
|
}
|
|
12592
|
+
applyChord();
|
|
12593
|
+
refs.modalOverlay.setAttribute("hidden", "");
|
|
12362
12594
|
});
|
|
12363
12595
|
row.appendChild(label);
|
|
12364
12596
|
row.appendChild(previewBtn);
|
|
12365
12597
|
row.appendChild(loadBtn);
|
|
12366
12598
|
box.appendChild(row);
|
|
12367
12599
|
box.appendChild(playerContainer);
|
|
12368
|
-
|
|
12600
|
+
container.appendChild(box);
|
|
12369
12601
|
}
|
|
12370
|
-
}
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
}
|
|
12377
|
-
}
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12602
|
+
};
|
|
12603
|
+
renderChordResults(results);
|
|
12604
|
+
chordSearchCache = {
|
|
12605
|
+
query: q2,
|
|
12606
|
+
count: scores.length,
|
|
12607
|
+
renderResults: renderChordResults
|
|
12608
|
+
};
|
|
12609
|
+
} catch (e) {
|
|
12610
|
+
console.error("[dtm] Chord search failed", e);
|
|
12611
|
+
results.innerHTML = '<span class="dtm-label" style="color:var(--dtm-warn)">\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F</span>';
|
|
12612
|
+
} finally {
|
|
12613
|
+
searchBtn.disabled = false;
|
|
12614
|
+
searchBtn.textContent = "\u691C\u7D22";
|
|
12615
|
+
}
|
|
12616
|
+
};
|
|
12617
|
+
searchBtn.addEventListener("click", () => void runSearch());
|
|
12618
|
+
input.addEventListener("keydown", (e) => {
|
|
12619
|
+
if (e.key === "Enter") void runSearch();
|
|
12383
12620
|
});
|
|
12621
|
+
input.focus();
|
|
12384
12622
|
};
|
|
12385
12623
|
const onKeyDown = (e) => {
|
|
12386
12624
|
if (!(e.ctrlKey || e.metaKey)) return;
|
|
@@ -12542,6 +12780,21 @@ var mountDAW = (target, options = {}) => {
|
|
|
12542
12780
|
getCurrentPlayStep,
|
|
12543
12781
|
forcePauseAt,
|
|
12544
12782
|
setLoading,
|
|
12783
|
+
setMasterVolume: (volume) => {
|
|
12784
|
+
masterVolume = clamp3(volume, 0, 100);
|
|
12785
|
+
refs.masterVolume.value = String(masterVolume);
|
|
12786
|
+
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
12787
|
+
},
|
|
12788
|
+
setVolume: (volume) => {
|
|
12789
|
+
masterVolume = clamp3(volume, 0, 100);
|
|
12790
|
+
refs.masterVolume.value = String(masterVolume);
|
|
12791
|
+
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
12792
|
+
},
|
|
12793
|
+
setDrumVolume: (volume) => {
|
|
12794
|
+
drumVolume = clamp3(volume, 0, 100);
|
|
12795
|
+
refs.drumVolume.value = String(drumVolume);
|
|
12796
|
+
refs.drumVolumeLabel.textContent = `${drumVolume}%`;
|
|
12797
|
+
},
|
|
12545
12798
|
applyPatch: (trackId, added, removed) => {
|
|
12546
12799
|
const track = trackStates.find((t) => t.config.id === trackId);
|
|
12547
12800
|
if (!track) return;
|