@onjmin/dtm 0.1.12 → 0.1.14

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 CHANGED
@@ -30,6 +30,38 @@ const daw = studio.mountEditor(document.getElementById("editor"), {
30
30
  studio.mountPlayer(document.getElementById("player"), daw.getMML().full);
31
31
  ```
32
32
 
33
+ ## モード(`simple` / `advanced`)
34
+
35
+ トラック構成と MIDI の取り込み方が異なる 2 つのモードがあります。`mode` オプションで切り替え、
36
+ 合わせて `tracks` に対応するトラック構成(`TRACKS_SIMPLE` / `TRACKS_ADVANCED`)を渡します。
37
+
38
+ | モード | トラック | MIDI 取り込み | 伴奏(コード進行)UI |
39
+ | --- | --- | --- | --- |
40
+ | `simple` | メロディー / サブメロ / ベース / 伴奏 の 4 本 | 各トラックの特徴から役割へ**自動分類** | `chord` トラックに表示(歌詞欄の代わり) |
41
+ | `advanced` | TRACK 01〜16 の 16 本 | MIDI トラックを**1:1 マッピング** | なし(全トラックが通常のノート+歌詞トラック) |
42
+
43
+ ```ts
44
+ import {
45
+ createDtmStudio,
46
+ TRACKS_SIMPLE,
47
+ TRACKS_ADVANCED,
48
+ } from "@onjmin/dtm";
49
+
50
+ const studio = await createDtmStudio();
51
+
52
+ // シンプルモード(既定)
53
+ studio.mountEditor(editorEl, { mode: "simple", tracks: TRACKS_SIMPLE });
54
+
55
+ // アドバンスモード
56
+ studio.mountEditor(editorEl, { mode: "advanced", tracks: TRACKS_ADVANCED });
57
+ ```
58
+
59
+ - `mode` を省略すると `tracks` の本数から推論します(4 本以下→`simple` / 5 本以上→`advanced`)。
60
+ 4 トラックでも 1:1 で取り込みたい等、意図がトラック数とずれる場合は `mode` を明示してください。
61
+ - `tracks` には任意の独自構成も渡せます(`mode` と組み合わせて挙動を決めます)。
62
+ - MIDI のドラム(ch10)はピアノロールで編集できないため、取り込み時の**トラック選択 UI には出ません**。
63
+ - `mode` / `tracks` は低レベル API の `mountDAW` でも同じく指定できます。
64
+
33
65
  ## 低レベル API(`mountDAW` / 注入式)
34
66
 
35
67
  本体は音を持たない設計で、`onPlayNote` / `onPlayDrum` に自前のシンセを繋ぐ
package/dist/index.d.mts CHANGED
@@ -537,6 +537,13 @@ type DawViewState = {
537
537
  /** 和音分解時に和音伴奏トラックを分解対象から除外するフラグ */
538
538
  ignoreChordHeavy: boolean;
539
539
  };
540
+ /**
541
+ * DAWの動作モード。
542
+ * - `simple`: 4トラック(メロディー/サブメロ/ベース/伴奏)。MIDIは役割別に自動分類して取り込み、
543
+ * id `chord` のトラックには歌詞欄の代わりに伴奏(コード進行)UIを出す。
544
+ * - `advanced`: MIDIトラックを1:1でマッピング。全トラックが通常のノートトラックとして振る舞う。
545
+ */
546
+ type DawMode = "simple" | "advanced";
540
547
  type DawOptions = {
541
548
  /** メロディックトラックのノート発音要求 */
542
549
  onPlayNote?: (e: PlayNoteEvent) => void;
@@ -559,6 +566,12 @@ type DawOptions = {
559
566
  */
560
567
  onViewStateChange?: (state: DawViewState) => void;
561
568
  parseMidi?: ParseMidiFn;
569
+ /**
570
+ * 動作モード({@link DawMode})。
571
+ * 未指定のときは後方互換のため `tracks` の本数から推論する(4本以下→simple / 5本以上→advanced)。
572
+ * 4トラック構成でも1:1取り込みをしたい等、トラック数と意図がずれる場合は明示指定する。
573
+ */
574
+ mode?: DawMode;
562
575
  /** トラック構成。既定は melody/submelody/bass/chord の4本 */
563
576
  tracks?: TrackConfig[];
564
577
  /** ドラムパターン辞書。既定は DRUM_PATTERNS */
@@ -836,6 +849,8 @@ type MidiExtraction = {
836
849
  };
837
850
  /**
838
851
  * MIDIの各トラックを走査し、ノート数などの概要を返す(トラック選択UI用)。
852
+ * ノート数はピアノロールで編集可能なノートのみを数える(ドラム ch10 は除外)。
853
+ * ドラム(ch10)だけで構成されたトラックは編集できないため、結果に含めない。
839
854
  */
840
855
  declare const analyzeMidiTracks: (midi: unknown) => MidiTrackAnalysis[];
841
856
  /** MIDIメタイベントからBPMを取得(無ければ120)。 */
@@ -846,8 +861,13 @@ declare const getMidiBPM: (midi: unknown) => number;
846
861
  */
847
862
  declare const extractMidiPlacements: (midi: unknown, selectedTrackIndices: number[]) => MidiExtraction;
848
863
  /**
849
- * 上級者モード用:MIDIトラックインデックス N DAW トラック trackIds[N] へ直接マッピング。
850
- * 自動分類なし。選択された MIDI トラック K のノートは DAW トラック K に入る。
864
+ * advancedモード用:選択された MIDI トラックを選択順に DAW トラックへ上から詰める
865
+ * (k番目に選んだトラック trackIds[k])。自動分類なし。
866
+ *
867
+ * MIDIの実トラック番号ではなく「選択順の位置」で割り当てるため、先頭のテンポ/コンダクタ
868
+ * トラックや、空・ドラムだけのトラックが混ざっていても DAW レーンがずれない
869
+ * (これらは選択リストに出ない/選ばれないので位置を消費しない)。
870
+ * ドラム(ch10 = channel 9)のノートはスキップする。
851
871
  */
852
872
  declare const extractMidiPlacementsByTrack: (midi: unknown, selectedIndices: number[], trackIds: string[]) => MidiExtraction;
853
873
  type ExportMidiOptions = {
@@ -1266,4 +1286,4 @@ declare const DAW_CSS = "\n@font-face {\n font-family: 'k8x12';\n src: url('ht
1266
1286
  */
1267
1287
  declare const injectStyles: (doc?: Document) => void;
1268
1288
 
1269
- export { type AddNoteOptions, type ApplyChordOptions, type ChordPatternType, type ChordPlacement, type ConsumedSyllable, type CoreEventHandlers, DAW_CSS, DEFAULT_BPM, DEFAULT_GATE, DEFAULT_PAN, DEFAULT_PLAYBACK_VELOCITY, DEFAULT_STEPS_PER_BAR, DEFAULT_VELOCITY, DEFAULT_VOCAL_VOLUME, DRUM_FONT, DRUM_KEYS, DRUM_PATTERNS, type DawInstance, type DawOptions, type DawViewState, type DrumPattern, type DtmStudio, type DtmStudioEngines, type DtmStudioOptions, type ExportMidiOptions, INSTRUMENT_PRESETS, type InstrumentPreset, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KoeVoiceOptions, LinkedList, type LyricSyllable, type LyricTrack, type LyricsConductor, MAX_VOCAL_VOLUME, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MidiExtraction, type MidiNotePlacement, type MidiTrackAnalysis, type MmlMeta, type MmlPlayerInstance, type MmlPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, PITCH_MAP, PREWARM_NOTES, type ParseMMLOptions, type ParseMidiFn, type ParsedMML, type PianoRollInstance, type PianoRollOptions, type PitchToken, type PlayDrumEvent, type PlayNoteEvent, type PlaybackState, type PreviewSoundCallback, type RenderConfig, type Sequencer, type SequencerOptions, type SequencerTrack, type SingingVoices, type SingingVoicesOptions, type StreamPlaybackOptions, type StreamVoiceNote, type StreamVoiceTrack, TRACKS_ADVANCED, TRACKS_SIMPLE, type ToolMode, type TrackConfig, VOICE_IMAGE_KEY, type VoiceModel, type VoiceRegistry, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, buildChordPlacements, buildNameToKeyMapping, collectPitchTokens, createAudioContext, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createSequencer, createSingingVoices, createVoiceRegistry, decomposeToMonophonic, drawGrid, drawHeader, drawKeyboard, drawNotes, drawSelectedNotes, drawSelectionRect, exportMIDI, extractMidiPlacements, extractMidiPlacementsByTrack, fetchSoundFontList, formatMmlMeta, generateRandomPattern, getDrawOffset, getGridCanvas, getGridContext, getGridPosition, getHeaderCanvas, getMidiBPM, getRenderConfig, getXY, icon, init, injectStyles, isChordHeavyTrack, koeUrl, mountDAW, mountMmlPlayer, normalizeLyrics, onClick, panToStereo, parseLyrics, parseMML, parseMmlMeta, setDrawOffset, setupRecorder, shiftNotes, stripLyrics, stripMmlMeta, vocalVolumeToGain };
1289
+ export { type AddNoteOptions, type ApplyChordOptions, type ChordPatternType, type ChordPlacement, type ConsumedSyllable, type CoreEventHandlers, DAW_CSS, DEFAULT_BPM, DEFAULT_GATE, DEFAULT_PAN, DEFAULT_PLAYBACK_VELOCITY, DEFAULT_STEPS_PER_BAR, DEFAULT_VELOCITY, DEFAULT_VOCAL_VOLUME, DRUM_FONT, DRUM_KEYS, DRUM_PATTERNS, type DawInstance, type DawMode, type DawOptions, type DawViewState, type DrumPattern, type DtmStudio, type DtmStudioEngines, type DtmStudioOptions, type ExportMidiOptions, INSTRUMENT_PRESETS, type InstrumentPreset, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KoeVoiceOptions, LinkedList, type LyricSyllable, type LyricTrack, type LyricsConductor, MAX_VOCAL_VOLUME, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MidiExtraction, type MidiNotePlacement, type MidiTrackAnalysis, type MmlMeta, type MmlPlayerInstance, type MmlPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, PITCH_MAP, PREWARM_NOTES, type ParseMMLOptions, type ParseMidiFn, type ParsedMML, type PianoRollInstance, type PianoRollOptions, type PitchToken, type PlayDrumEvent, type PlayNoteEvent, type PlaybackState, type PreviewSoundCallback, type RenderConfig, type Sequencer, type SequencerOptions, type SequencerTrack, type SingingVoices, type SingingVoicesOptions, type StreamPlaybackOptions, type StreamVoiceNote, type StreamVoiceTrack, TRACKS_ADVANCED, TRACKS_SIMPLE, type ToolMode, type TrackConfig, VOICE_IMAGE_KEY, type VoiceModel, type VoiceRegistry, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, buildChordPlacements, buildNameToKeyMapping, collectPitchTokens, createAudioContext, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createSequencer, createSingingVoices, createVoiceRegistry, decomposeToMonophonic, drawGrid, drawHeader, drawKeyboard, drawNotes, drawSelectedNotes, drawSelectionRect, exportMIDI, extractMidiPlacements, extractMidiPlacementsByTrack, fetchSoundFontList, formatMmlMeta, generateRandomPattern, getDrawOffset, getGridCanvas, getGridContext, getGridPosition, getHeaderCanvas, getMidiBPM, getRenderConfig, getXY, icon, init, injectStyles, isChordHeavyTrack, koeUrl, mountDAW, mountMmlPlayer, normalizeLyrics, onClick, panToStereo, parseLyrics, parseMML, parseMmlMeta, setDrawOffset, setupRecorder, shiftNotes, stripLyrics, stripMmlMeta, vocalVolumeToGain };
package/dist/index.d.ts CHANGED
@@ -537,6 +537,13 @@ type DawViewState = {
537
537
  /** 和音分解時に和音伴奏トラックを分解対象から除外するフラグ */
538
538
  ignoreChordHeavy: boolean;
539
539
  };
540
+ /**
541
+ * DAWの動作モード。
542
+ * - `simple`: 4トラック(メロディー/サブメロ/ベース/伴奏)。MIDIは役割別に自動分類して取り込み、
543
+ * id `chord` のトラックには歌詞欄の代わりに伴奏(コード進行)UIを出す。
544
+ * - `advanced`: MIDIトラックを1:1でマッピング。全トラックが通常のノートトラックとして振る舞う。
545
+ */
546
+ type DawMode = "simple" | "advanced";
540
547
  type DawOptions = {
541
548
  /** メロディックトラックのノート発音要求 */
542
549
  onPlayNote?: (e: PlayNoteEvent) => void;
@@ -559,6 +566,12 @@ type DawOptions = {
559
566
  */
560
567
  onViewStateChange?: (state: DawViewState) => void;
561
568
  parseMidi?: ParseMidiFn;
569
+ /**
570
+ * 動作モード({@link DawMode})。
571
+ * 未指定のときは後方互換のため `tracks` の本数から推論する(4本以下→simple / 5本以上→advanced)。
572
+ * 4トラック構成でも1:1取り込みをしたい等、トラック数と意図がずれる場合は明示指定する。
573
+ */
574
+ mode?: DawMode;
562
575
  /** トラック構成。既定は melody/submelody/bass/chord の4本 */
563
576
  tracks?: TrackConfig[];
564
577
  /** ドラムパターン辞書。既定は DRUM_PATTERNS */
@@ -836,6 +849,8 @@ type MidiExtraction = {
836
849
  };
837
850
  /**
838
851
  * MIDIの各トラックを走査し、ノート数などの概要を返す(トラック選択UI用)。
852
+ * ノート数はピアノロールで編集可能なノートのみを数える(ドラム ch10 は除外)。
853
+ * ドラム(ch10)だけで構成されたトラックは編集できないため、結果に含めない。
839
854
  */
840
855
  declare const analyzeMidiTracks: (midi: unknown) => MidiTrackAnalysis[];
841
856
  /** MIDIメタイベントからBPMを取得(無ければ120)。 */
@@ -846,8 +861,13 @@ declare const getMidiBPM: (midi: unknown) => number;
846
861
  */
847
862
  declare const extractMidiPlacements: (midi: unknown, selectedTrackIndices: number[]) => MidiExtraction;
848
863
  /**
849
- * 上級者モード用:MIDIトラックインデックス N DAW トラック trackIds[N] へ直接マッピング。
850
- * 自動分類なし。選択された MIDI トラック K のノートは DAW トラック K に入る。
864
+ * advancedモード用:選択された MIDI トラックを選択順に DAW トラックへ上から詰める
865
+ * (k番目に選んだトラック trackIds[k])。自動分類なし。
866
+ *
867
+ * MIDIの実トラック番号ではなく「選択順の位置」で割り当てるため、先頭のテンポ/コンダクタ
868
+ * トラックや、空・ドラムだけのトラックが混ざっていても DAW レーンがずれない
869
+ * (これらは選択リストに出ない/選ばれないので位置を消費しない)。
870
+ * ドラム(ch10 = channel 9)のノートはスキップする。
851
871
  */
852
872
  declare const extractMidiPlacementsByTrack: (midi: unknown, selectedIndices: number[], trackIds: string[]) => MidiExtraction;
853
873
  type ExportMidiOptions = {
@@ -1266,4 +1286,4 @@ declare const DAW_CSS = "\n@font-face {\n font-family: 'k8x12';\n src: url('ht
1266
1286
  */
1267
1287
  declare const injectStyles: (doc?: Document) => void;
1268
1288
 
1269
- export { type AddNoteOptions, type ApplyChordOptions, type ChordPatternType, type ChordPlacement, type ConsumedSyllable, type CoreEventHandlers, DAW_CSS, DEFAULT_BPM, DEFAULT_GATE, DEFAULT_PAN, DEFAULT_PLAYBACK_VELOCITY, DEFAULT_STEPS_PER_BAR, DEFAULT_VELOCITY, DEFAULT_VOCAL_VOLUME, DRUM_FONT, DRUM_KEYS, DRUM_PATTERNS, type DawInstance, type DawOptions, type DawViewState, type DrumPattern, type DtmStudio, type DtmStudioEngines, type DtmStudioOptions, type ExportMidiOptions, INSTRUMENT_PRESETS, type InstrumentPreset, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KoeVoiceOptions, LinkedList, type LyricSyllable, type LyricTrack, type LyricsConductor, MAX_VOCAL_VOLUME, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MidiExtraction, type MidiNotePlacement, type MidiTrackAnalysis, type MmlMeta, type MmlPlayerInstance, type MmlPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, PITCH_MAP, PREWARM_NOTES, type ParseMMLOptions, type ParseMidiFn, type ParsedMML, type PianoRollInstance, type PianoRollOptions, type PitchToken, type PlayDrumEvent, type PlayNoteEvent, type PlaybackState, type PreviewSoundCallback, type RenderConfig, type Sequencer, type SequencerOptions, type SequencerTrack, type SingingVoices, type SingingVoicesOptions, type StreamPlaybackOptions, type StreamVoiceNote, type StreamVoiceTrack, TRACKS_ADVANCED, TRACKS_SIMPLE, type ToolMode, type TrackConfig, VOICE_IMAGE_KEY, type VoiceModel, type VoiceRegistry, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, buildChordPlacements, buildNameToKeyMapping, collectPitchTokens, createAudioContext, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createSequencer, createSingingVoices, createVoiceRegistry, decomposeToMonophonic, drawGrid, drawHeader, drawKeyboard, drawNotes, drawSelectedNotes, drawSelectionRect, exportMIDI, extractMidiPlacements, extractMidiPlacementsByTrack, fetchSoundFontList, formatMmlMeta, generateRandomPattern, getDrawOffset, getGridCanvas, getGridContext, getGridPosition, getHeaderCanvas, getMidiBPM, getRenderConfig, getXY, icon, init, injectStyles, isChordHeavyTrack, koeUrl, mountDAW, mountMmlPlayer, normalizeLyrics, onClick, panToStereo, parseLyrics, parseMML, parseMmlMeta, setDrawOffset, setupRecorder, shiftNotes, stripLyrics, stripMmlMeta, vocalVolumeToGain };
1289
+ export { type AddNoteOptions, type ApplyChordOptions, type ChordPatternType, type ChordPlacement, type ConsumedSyllable, type CoreEventHandlers, DAW_CSS, DEFAULT_BPM, DEFAULT_GATE, DEFAULT_PAN, DEFAULT_PLAYBACK_VELOCITY, DEFAULT_STEPS_PER_BAR, DEFAULT_VELOCITY, DEFAULT_VOCAL_VOLUME, DRUM_FONT, DRUM_KEYS, DRUM_PATTERNS, type DawInstance, type DawMode, type DawOptions, type DawViewState, type DrumPattern, type DtmStudio, type DtmStudioEngines, type DtmStudioOptions, type ExportMidiOptions, INSTRUMENT_PRESETS, type InstrumentPreset, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KoeVoiceOptions, LinkedList, type LyricSyllable, type LyricTrack, type LyricsConductor, MAX_VOCAL_VOLUME, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MidiExtraction, type MidiNotePlacement, type MidiTrackAnalysis, type MmlMeta, type MmlPlayerInstance, type MmlPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, PITCH_MAP, PREWARM_NOTES, type ParseMMLOptions, type ParseMidiFn, type ParsedMML, type PianoRollInstance, type PianoRollOptions, type PitchToken, type PlayDrumEvent, type PlayNoteEvent, type PlaybackState, type PreviewSoundCallback, type RenderConfig, type Sequencer, type SequencerOptions, type SequencerTrack, type SingingVoices, type SingingVoicesOptions, type StreamPlaybackOptions, type StreamVoiceNote, type StreamVoiceTrack, TRACKS_ADVANCED, TRACKS_SIMPLE, type ToolMode, type TrackConfig, VOICE_IMAGE_KEY, type VoiceModel, type VoiceRegistry, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, buildChordPlacements, buildNameToKeyMapping, collectPitchTokens, createAudioContext, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createSequencer, createSingingVoices, createVoiceRegistry, decomposeToMonophonic, drawGrid, drawHeader, drawKeyboard, drawNotes, drawSelectedNotes, drawSelectionRect, exportMIDI, extractMidiPlacements, extractMidiPlacementsByTrack, fetchSoundFontList, formatMmlMeta, generateRandomPattern, getDrawOffset, getGridCanvas, getGridContext, getGridPosition, getHeaderCanvas, getMidiBPM, getRenderConfig, getXY, icon, init, injectStyles, isChordHeavyTrack, koeUrl, mountDAW, mountMmlPlayer, normalizeLyrics, onClick, panToStereo, parseLyrics, parseMML, parseMmlMeta, setDrawOffset, setupRecorder, shiftNotes, stripLyrics, stripMmlMeta, vocalVolumeToGain };
package/dist/index.js CHANGED
@@ -1322,7 +1322,10 @@ var buildUI = (target, options) => {
1322
1322
  <summary>MIDI / MML \u5165\u529B</summary>
1323
1323
  <div class="dtm-panel-body">
1324
1324
  <div class="dtm-row">
1325
- <span class="dtm-label">MIDI</span>
1325
+ <div style="display: inline-flex; flex-direction: column; align-items: center; gap: 4px; justify-content: center; min-width: 48px;">
1326
+ <span class="dtm-label" style="line-height: 1;">MIDI</span>
1327
+ <button class="dtm-infobtn" data-dtm="midi-info" title="MIDI\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC">${icon("info", 12)}</button>
1328
+ </div>
1326
1329
  <input type="file" class="dtm-input dtm-grow" accept=".mid,.midi" data-dtm="midi-input">
1327
1330
  <button class="dtm-btn dtm-btn--success" data-dtm="midi-load">\u8AAD\u8FBC</button>
1328
1331
  </div>
@@ -1455,6 +1458,7 @@ var buildUI = (target, options) => {
1455
1458
  drumVolumeLabel: sel("drum-volume-label"),
1456
1459
  midiInput: sel("midi-input"),
1457
1460
  midiLoadBtn: sel("midi-load"),
1461
+ midiInfoBtn: sel("midi-info"),
1458
1462
  midiTrackSelection: sel("midi-track-selection"),
1459
1463
  midiPanel: sel("midi-panel"),
1460
1464
  mmlInput: sel("mml-input"),
@@ -2888,7 +2892,7 @@ var analyzeMidiTracks = (midi) => {
2888
2892
  currentTime += event.deltaTime;
2889
2893
  const data = event.data;
2890
2894
  if (event.type === 9 && data && data[1]) {
2891
- notes.push({ pitch: data[0] });
2895
+ notes.push({ pitch: data[0], channel: event.channel ?? 0 });
2892
2896
  } else if (event.type === 8) {
2893
2897
  for (let k = notes.length - 1; k >= 0; k--) {
2894
2898
  if (notes[k].pitch === data[0] && notes[k].end === void 0) {
@@ -2899,11 +2903,13 @@ var analyzeMidiTracks = (midi) => {
2899
2903
  }
2900
2904
  }
2901
2905
  const validNotes = notes.filter((n) => n.end !== void 0);
2906
+ const editableNotes = validNotes.filter((n) => n.channel !== 9);
2907
+ if (validNotes.length > 0 && editableNotes.length === 0) continue;
2902
2908
  result.push({
2903
2909
  index: i,
2904
2910
  name: `Ch${i + 1}`,
2905
- noteCount: validNotes.length,
2906
- selected: validNotes.length > 0
2911
+ noteCount: editableNotes.length,
2912
+ selected: editableNotes.length > 0
2907
2913
  });
2908
2914
  }
2909
2915
  return result;
@@ -3071,13 +3077,11 @@ var extractMidiPlacementsByTrack = (midi, selectedIndices, trackIds) => {
3071
3077
  const bpm = getMidiBPM(midi);
3072
3078
  const ticksPerStep = ticksPerBeat / STEPS_PER_BEAT;
3073
3079
  const placements = [];
3074
- const selectedSet = new Set(selectedIndices);
3075
- for (let midiIdx = 0; midiIdx < track.length; midiIdx++) {
3076
- if (!selectedSet.has(midiIdx)) continue;
3077
- if (midiIdx >= trackIds.length) continue;
3078
- const trackId = trackIds[midiIdx];
3080
+ selectedIndices.forEach((midiIdx, lane) => {
3081
+ if (lane >= trackIds.length) return;
3079
3082
  const trackData = track[midiIdx];
3080
- if (!trackData) continue;
3083
+ if (!trackData) return;
3084
+ const trackId = trackIds[lane];
3081
3085
  const active = [];
3082
3086
  let currentTime = 0;
3083
3087
  for (const event of trackData.event) {
@@ -3112,7 +3116,7 @@ var extractMidiPlacementsByTrack = (midi, selectedIndices, trackIds) => {
3112
3116
  velocity: note.velocity
3113
3117
  });
3114
3118
  }
3115
- }
3119
+ });
3116
3120
  return { placements, bpm };
3117
3121
  };
3118
3122
  var to2byte = (n) => [(n & 65280) >> 8, n & 255];
@@ -3153,6 +3157,7 @@ var exportMIDI = (options) => {
3153
3157
  const midiTracks = [];
3154
3158
  tracks.forEach((track, ch) => {
3155
3159
  if (track.notes.length === 0) return;
3160
+ const channel = ch < 9 ? ch : ch + 1 & 15;
3156
3161
  const events = [];
3157
3162
  for (const n of track.notes) {
3158
3163
  const startTick = Math.round(n.startStep * tickPerStep);
@@ -3162,8 +3167,8 @@ var exportMIDI = (options) => {
3162
3167
  const vel = Math.round(
3163
3168
  (n.velocity ?? DEFAULT_VELOCITY) * (track.volume ?? 100) / 100
3164
3169
  );
3165
- events.push({ t: startTick, m: [144 | ch & 15, n.pitch, vel] });
3166
- events.push({ t: endTick, m: [144 | ch & 15, n.pitch, 0] });
3170
+ events.push({ t: startTick, m: [144 | channel, n.pitch, vel] });
3171
+ events.push({ t: endTick, m: [144 | channel, n.pitch, 0] });
3167
3172
  }
3168
3173
  events.sort((a, b) => a.t - b.t);
3169
3174
  midiTracks.push(events);
@@ -5280,6 +5285,7 @@ var CHORD_INFO_HTML = `
5280
5285
  <h4>1. \u57FA\u672C\u306E\u66F8\u304D\u65B9</h4>
5281
5286
  <p>\u30B3\u30FC\u30C9\u540D\uFF08\u548C\u97F3\u8A18\u53F7\uFF09\u3092\u7E26\u7DDA <code>|</code>\u3001\u30B9\u30DA\u30FC\u30B9\u3001\u307E\u305F\u306F\u30AB\u30F3\u30DE\u3067\u533A\u5207\u3063\u3066\u5165\u529B\u3057\u307E\u3059\u3002\u7E26\u7DDA\u3067\u533A\u5207\u308B\u30681\u5C0F\u7BC0\u3054\u3068\u306E\u914D\u7F6E\u306B\u306A\u308A\u307E\u3059\u3002</p>
5282
5287
  <pre>\u4F8B: C | G | Am | F</pre>
5288
+ <p style="margin-top:4px;"><small>\u30B3\u30FC\u30C9\u9032\u884C\u3092\u81EA\u5206\u3067\u8003\u3048\u308B\u306E\u304C\u96E3\u3057\u3044\u3068\u304D\u306F\u3001\u30B3\u30FC\u30C9\u9032\u884C\u306E\u5171\u6709\u30B5\u30A4\u30C8\uFF08\u4F8B: <a href="https://rechord.cc/scores" target="_blank" rel="noopener">rechord.cc</a>\uFF09\u304B\u3089\u597D\u304D\u306A\u9032\u884C\u3092\u63A2\u3057\u3066\u30B3\u30D4\u30DA\u3059\u308B\u306E\u3082\u624B\u3067\u3059\u3002\u533A\u5207\u308A\u6587\u5B57\uFF08<code>|</code> / \u30B9\u30DA\u30FC\u30B9 / \u30AB\u30F3\u30DE\uFF09\u3060\u3051\u4E0A\u306E\u5F62\u5F0F\u306B\u5408\u308F\u305B\u308C\u3070\u3001\u305D\u306E\u307E\u307E\u4F7F\u3048\u307E\u3059\u3002</small></p>
5283
5289
 
5284
5290
  <h4>2. 1\u5C0F\u7BC0\u306B\u8907\u6570\u30B3\u30FC\u30C9\u3092\u5165\u308C\u308B</h4>
5285
5291
  <p>\u5C0F\u7BC0\u306E\u533A\u5207\u308A\uFF08\u7E26\u7DDA <code>|</code>\uFF09\u306E\u4E2D\u306B\u3001\u30B9\u30DA\u30FC\u30B9\u533A\u5207\u308A\u3067\u30B3\u30FC\u30C9\u3092\u4E26\u3079\u307E\u3059\u3002\u7B49\u9593\u9694\u306B\u914D\u7F6E\u3055\u308C\u307E\u3059\u3002</p>
@@ -5354,6 +5360,38 @@ var MML_INFO_HTML = `
5354
5360
  <p style="margin-top:4px;"><small>\uFF08\u97F3\u6E90\u540D\u306F <code>klatt</code> \u3084 <code>roze</code>, <code>teto</code> \u306A\u3069\u306E\u97F3\u58F0\u30E2\u30C7\u30EB\u3092\u6307\u5B9A\u3067\u304D\u307E\u3059\uFF09</small></p>
5355
5361
  </div>
5356
5362
  `;
5363
+ var MIDI_INFO_HTML = `
5364
+ <div class="dtm-modal-body-content">
5365
+ <h4>1. MIDI\u30D5\u30A1\u30A4\u30EB\u3068\u306F</h4>
5366
+ <p>\u300C\u3069\u306E\u97F3\u3092\u30FB\u3044\u3064\u30FB\u3069\u306E\u304F\u3089\u3044\u306E\u9577\u3055\u3067\u9CF4\u3089\u3059\u304B\u300D\u3092\u8A18\u9332\u3057\u305F\u3001\u6F14\u594F\u30C7\u30FC\u30BF\u306E\u30D5\u30A1\u30A4\u30EB\uFF08\u62E1\u5F35\u5B50 <code>.mid</code> / <code>.midi</code>\uFF09\u3067\u3059\u3002\u97F3\u305D\u306E\u3082\u306E\u3067\u306F\u306A\u304F\u697D\u8B5C\u306B\u8FD1\u3044\u30C7\u30FC\u30BF\u306A\u306E\u3067\u3001\u8AAD\u307F\u8FBC\u3093\u3067\u305D\u306E\u307E\u307E\u7DE8\u96C6\u3067\u304D\u307E\u3059\u3002</p>
5367
+
5368
+ <h4>2. \u8AAD\u307F\u8FBC\u307F\u306E\u3057\u304B\u305F</h4>
5369
+ <ul>
5370
+ <li>\u300C\u30D5\u30A1\u30A4\u30EB\u3092\u9078\u629E\u300D\u304B\u3089 <code>.mid</code> \u30D5\u30A1\u30A4\u30EB\u3092\u9078\u3073\u307E\u3059\u3002</li>
5371
+ <li>\u30D5\u30A1\u30A4\u30EB\u5185\u306E\u30C8\u30E9\u30C3\u30AF\u4E00\u89A7\u304C\u51FA\u308B\u306E\u3067\u3001\u53D6\u308A\u8FBC\u307F\u305F\u3044\u30C8\u30E9\u30C3\u30AF\u3092\u9078\u3073\u307E\u3059\u3002</li>
5372
+ <li>\u300C\u8AAD\u8FBC\u300D\u3092\u62BC\u3059\u3068\u53CD\u6620\u3055\u308C\u307E\u3059\u3002</li>
5373
+ </ul>
5374
+
5375
+ <h4>3. \u30E2\u30FC\u30C9\u306B\u3088\u308B\u53D6\u308A\u8FBC\u307F\u65B9\u306E\u9055\u3044</h4>
5376
+ <ul>
5377
+ <li><strong>SIMPLE</strong>: \u5404\u30C8\u30E9\u30C3\u30AF\u306E\u7279\u5FB4\u304B\u3089\u3001\u30E1\u30ED\u30C7\u30A3\u30FC\u30FB\u30B5\u30D6\u30E1\u30ED\u30FB\u30D9\u30FC\u30B9\u30FB\u4F34\u594F\u306E4\u3064\u306E\u5F79\u5272\u306B\u81EA\u52D5\u3067\u632F\u308A\u5206\u3051\u3089\u308C\u307E\u3059\u3002</li>
5378
+ <li><strong>ADVANCED</strong>: MIDI\u306E\u30C8\u30E9\u30C3\u30AF\u69CB\u6210\u304C\u305D\u306E\u307E\u307E\u53CD\u6620\u3055\u308C\u307E\u3059\uFF081\u5BFE1\uFF09\u3002</li>
5379
+ </ul>
5380
+
5381
+ <h4>4. MIDI\u30D5\u30A1\u30A4\u30EB\u3092\u624B\u306B\u5165\u308C\u308B</h4>
5382
+ <p>\u624B\u5143\u306BMIDI\u304C\u7121\u3044\u3068\u304D\u306F\u3001\u300C<code>\u66F2\u540D midi</code>\u300D\u306A\u3069\u3067\u691C\u7D22\u3059\u308C\u3070\u3001\u7121\u6599\u3067\u914D\u5E03\u3057\u3066\u3044\u308B\u30B5\u30A4\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u3059\u3002</p>
5383
+ <p style="margin-top:4px;"><small>\u307F\u3093\u306A\u304CMIDI\u3092\u6295\u7A3F\u3067\u304D\u308B\u6295\u7A3F\u578B\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0: <a href="http://picotune.me/" target="_blank" rel="noopener">picotune.me</a>\uFF08\u3044\u308D\u3093\u306A\u30B8\u30E3\u30F3\u30EB\u306EMIDI\u3092\u7121\u6599\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3067\u304D\u307E\u3059\u3002\u30B5\u30A4\u30C8\u4E0A\u3067\u306F\u30C1\u30C3\u30D7\u30C1\u30E5\u30FC\u30F3\u98A8\u306B\u518D\u751F\u3055\u308C\u307E\u3059\uFF09</small></p>
5384
+ <p style="margin-top:4px;"><small>\u203B\u691C\u7D22\u3067\u898B\u3064\u304B\u308B\u914D\u5E03\u30B5\u30A4\u30C8\u306F\u3001\u500B\u4EBA\u904B\u55B6\u306E\u3082\u306E\u304B\u3089\u6A29\u5229\u7684\u306B\u30B0\u30EC\u30FC\u306A\u3082\u306E\u307E\u3067\u69D8\u3005\u3067\u3059\u3002\u305D\u306E\u305F\u3081\u3001\u305D\u308C\u3089\u3078\u306E\u76F4\u63A5\u30EA\u30F3\u30AF\u306F\u8F09\u305B\u3066\u3044\u307E\u305B\u3093\u3002\u5229\u7528\u306E\u969B\u306F\u914D\u5E03\u5143\u3084\u6A29\u5229\u95A2\u4FC2\u3092\u3054\u81EA\u8EAB\u3067\u3054\u78BA\u8A8D\u304F\u3060\u3055\u3044\u3002</small></p>
5385
+
5386
+ <h4>5. UST\uFF08UTAU\uFF09\u306E\u6B4C\u8A5E\u3092\u4F7F\u3046</h4>
5387
+ <p>UTAU\u306EUST\u30D5\u30A1\u30A4\u30EB\u304B\u3089\u6B4C\u8A5E\u3060\u3051\u3092\u53D6\u308A\u51FA\u3057\u3066\u3001\u6B4C\u308F\u305B\u308B\u3053\u3068\u3082\u3067\u304D\u307E\u3059\u3002</p>
5388
+ <ul>
5389
+ <li>\u97F3\u7B26: UTAU\u306A\u3069\u3067UST\u3092MIDI\u306B\u66F8\u304D\u51FA\u3057\u3001\u4E0A\u306E\u624B\u9806\u3067\u8AAD\u307F\u8FBC\u307F\u307E\u3059\u3002</li>
5390
+ <li>\u6B4C\u8A5E: \u4E0B\u8A18\u30B5\u30A4\u30C8\u3067UST\u304B\u3089\u6B4C\u8A5E\u30C6\u30AD\u30B9\u30C8\u3092\u629C\u304D\u51FA\u3057\u3001MML/\u6B4C\u8A5E\u5165\u529B\u6B04\u306E <code>@@</code> \u69CB\u6587\u306B\u8CBC\u308A\u4ED8\u3051\u307E\u3059\u3002</li>
5391
+ </ul>
5392
+ <p style="margin-top:4px;"><small>\u6B4C\u8A5E\u306E\u62BD\u51FA: <a href="https://rpgen3.github.io/ust2txt/" target="_blank" rel="noopener">ust2txt</a></small></p>
5393
+ </div>
5394
+ `;
5357
5395
  var BASE_STEP_WIDTH = 0.5;
5358
5396
  var BASE_KEY_HEIGHT = 15;
5359
5397
  var TRACKS_SIMPLE = [
@@ -5512,9 +5550,11 @@ var mountDAW = (target, options = {}) => {
5512
5550
  injectStyles();
5513
5551
  const getAudioTime = options.getAudioTime ?? (() => performance.now() / 1e3);
5514
5552
  const trackConfigs = options.tracks ?? DEFAULT_TRACKS;
5553
+ const mode = options.mode ?? (trackConfigs.length > TRACKS_SIMPLE.length ? "advanced" : "simple");
5554
+ const isAdvanced = mode === "advanced";
5515
5555
  const drumPatterns = options.drumPatterns ?? DRUM_PATTERNS;
5516
5556
  const showMidi = !!options.parseMidi;
5517
- const showChord = true;
5557
+ const showChord = !isAdvanced;
5518
5558
  const refs = buildUI(target, {
5519
5559
  tracks: trackConfigs,
5520
5560
  drumPatternNames: Object.keys(drumPatterns),
@@ -6299,7 +6339,6 @@ var mountDAW = (target, options = {}) => {
6299
6339
  active.core.setVolume(active.volume);
6300
6340
  volLabel.textContent = String(active.volume);
6301
6341
  });
6302
- const isAdvanced = trackStates.length > TRACKS_SIMPLE.length;
6303
6342
  if (isAdvanced || active.config.id !== "chord") {
6304
6343
  const lyricDiv = document.createElement("div");
6305
6344
  lyricDiv.className = "dtm-row";
@@ -6507,16 +6546,16 @@ var mountDAW = (target, options = {}) => {
6507
6546
  updateUndoRedo();
6508
6547
  redrawAll();
6509
6548
  };
6510
- const setToolMode = (mode) => {
6511
- activeToolMode = mode;
6549
+ const setToolMode = (mode2) => {
6550
+ activeToolMode = mode2;
6512
6551
  for (const [btn, m] of [
6513
6552
  [refs.toolPen, "pen"],
6514
6553
  [refs.toolSelect, "select"],
6515
6554
  [refs.toolEraser, "eraser"]
6516
6555
  ]) {
6517
- btn.classList.toggle("dtm-segbtn--active", m === mode);
6556
+ btn.classList.toggle("dtm-segbtn--active", m === mode2);
6518
6557
  }
6519
- if (mode !== "select") {
6558
+ if (mode2 !== "select") {
6520
6559
  selectionRect = null;
6521
6560
  selectedNotes = [];
6522
6561
  }
@@ -6750,7 +6789,6 @@ var mountDAW = (target, options = {}) => {
6750
6789
  const applyMidiSelection = (midi, selectedIndices) => {
6751
6790
  clearAll();
6752
6791
  for (const t of trackStates) t.core.setLoadMode(true);
6753
- const isAdvanced = trackStates.length > TRACKS_SIMPLE.length;
6754
6792
  const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
6755
6793
  midi,
6756
6794
  selectedIndices,
@@ -6948,6 +6986,9 @@ var mountDAW = (target, options = {}) => {
6948
6986
  refs.mmlInfoBtn.addEventListener("click", () => {
6949
6987
  showModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC", MML_INFO_HTML);
6950
6988
  });
6989
+ refs.midiInfoBtn.addEventListener("click", () => {
6990
+ showModal("MIDI\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC", MIDI_INFO_HTML);
6991
+ });
6951
6992
  refs.shiftApplyBtn.addEventListener(
6952
6993
  "click",
6953
6994
  () => overlayDuring(() => {
package/dist/index.mjs CHANGED
@@ -1217,7 +1217,10 @@ var buildUI = (target, options) => {
1217
1217
  <summary>MIDI / MML \u5165\u529B</summary>
1218
1218
  <div class="dtm-panel-body">
1219
1219
  <div class="dtm-row">
1220
- <span class="dtm-label">MIDI</span>
1220
+ <div style="display: inline-flex; flex-direction: column; align-items: center; gap: 4px; justify-content: center; min-width: 48px;">
1221
+ <span class="dtm-label" style="line-height: 1;">MIDI</span>
1222
+ <button class="dtm-infobtn" data-dtm="midi-info" title="MIDI\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC">${icon("info", 12)}</button>
1223
+ </div>
1221
1224
  <input type="file" class="dtm-input dtm-grow" accept=".mid,.midi" data-dtm="midi-input">
1222
1225
  <button class="dtm-btn dtm-btn--success" data-dtm="midi-load">\u8AAD\u8FBC</button>
1223
1226
  </div>
@@ -1350,6 +1353,7 @@ var buildUI = (target, options) => {
1350
1353
  drumVolumeLabel: sel("drum-volume-label"),
1351
1354
  midiInput: sel("midi-input"),
1352
1355
  midiLoadBtn: sel("midi-load"),
1356
+ midiInfoBtn: sel("midi-info"),
1353
1357
  midiTrackSelection: sel("midi-track-selection"),
1354
1358
  midiPanel: sel("midi-panel"),
1355
1359
  mmlInput: sel("mml-input"),
@@ -2783,7 +2787,7 @@ var analyzeMidiTracks = (midi) => {
2783
2787
  currentTime += event.deltaTime;
2784
2788
  const data = event.data;
2785
2789
  if (event.type === 9 && data && data[1]) {
2786
- notes.push({ pitch: data[0] });
2790
+ notes.push({ pitch: data[0], channel: event.channel ?? 0 });
2787
2791
  } else if (event.type === 8) {
2788
2792
  for (let k = notes.length - 1; k >= 0; k--) {
2789
2793
  if (notes[k].pitch === data[0] && notes[k].end === void 0) {
@@ -2794,11 +2798,13 @@ var analyzeMidiTracks = (midi) => {
2794
2798
  }
2795
2799
  }
2796
2800
  const validNotes = notes.filter((n) => n.end !== void 0);
2801
+ const editableNotes = validNotes.filter((n) => n.channel !== 9);
2802
+ if (validNotes.length > 0 && editableNotes.length === 0) continue;
2797
2803
  result.push({
2798
2804
  index: i,
2799
2805
  name: `Ch${i + 1}`,
2800
- noteCount: validNotes.length,
2801
- selected: validNotes.length > 0
2806
+ noteCount: editableNotes.length,
2807
+ selected: editableNotes.length > 0
2802
2808
  });
2803
2809
  }
2804
2810
  return result;
@@ -2966,13 +2972,11 @@ var extractMidiPlacementsByTrack = (midi, selectedIndices, trackIds) => {
2966
2972
  const bpm = getMidiBPM(midi);
2967
2973
  const ticksPerStep = ticksPerBeat / STEPS_PER_BEAT;
2968
2974
  const placements = [];
2969
- const selectedSet = new Set(selectedIndices);
2970
- for (let midiIdx = 0; midiIdx < track.length; midiIdx++) {
2971
- if (!selectedSet.has(midiIdx)) continue;
2972
- if (midiIdx >= trackIds.length) continue;
2973
- const trackId = trackIds[midiIdx];
2975
+ selectedIndices.forEach((midiIdx, lane) => {
2976
+ if (lane >= trackIds.length) return;
2974
2977
  const trackData = track[midiIdx];
2975
- if (!trackData) continue;
2978
+ if (!trackData) return;
2979
+ const trackId = trackIds[lane];
2976
2980
  const active = [];
2977
2981
  let currentTime = 0;
2978
2982
  for (const event of trackData.event) {
@@ -3007,7 +3011,7 @@ var extractMidiPlacementsByTrack = (midi, selectedIndices, trackIds) => {
3007
3011
  velocity: note.velocity
3008
3012
  });
3009
3013
  }
3010
- }
3014
+ });
3011
3015
  return { placements, bpm };
3012
3016
  };
3013
3017
  var to2byte = (n) => [(n & 65280) >> 8, n & 255];
@@ -3048,6 +3052,7 @@ var exportMIDI = (options) => {
3048
3052
  const midiTracks = [];
3049
3053
  tracks.forEach((track, ch) => {
3050
3054
  if (track.notes.length === 0) return;
3055
+ const channel = ch < 9 ? ch : ch + 1 & 15;
3051
3056
  const events = [];
3052
3057
  for (const n of track.notes) {
3053
3058
  const startTick = Math.round(n.startStep * tickPerStep);
@@ -3057,8 +3062,8 @@ var exportMIDI = (options) => {
3057
3062
  const vel = Math.round(
3058
3063
  (n.velocity ?? DEFAULT_VELOCITY) * (track.volume ?? 100) / 100
3059
3064
  );
3060
- events.push({ t: startTick, m: [144 | ch & 15, n.pitch, vel] });
3061
- events.push({ t: endTick, m: [144 | ch & 15, n.pitch, 0] });
3065
+ events.push({ t: startTick, m: [144 | channel, n.pitch, vel] });
3066
+ events.push({ t: endTick, m: [144 | channel, n.pitch, 0] });
3062
3067
  }
3063
3068
  events.sort((a, b) => a.t - b.t);
3064
3069
  midiTracks.push(events);
@@ -5175,6 +5180,7 @@ var CHORD_INFO_HTML = `
5175
5180
  <h4>1. \u57FA\u672C\u306E\u66F8\u304D\u65B9</h4>
5176
5181
  <p>\u30B3\u30FC\u30C9\u540D\uFF08\u548C\u97F3\u8A18\u53F7\uFF09\u3092\u7E26\u7DDA <code>|</code>\u3001\u30B9\u30DA\u30FC\u30B9\u3001\u307E\u305F\u306F\u30AB\u30F3\u30DE\u3067\u533A\u5207\u3063\u3066\u5165\u529B\u3057\u307E\u3059\u3002\u7E26\u7DDA\u3067\u533A\u5207\u308B\u30681\u5C0F\u7BC0\u3054\u3068\u306E\u914D\u7F6E\u306B\u306A\u308A\u307E\u3059\u3002</p>
5177
5182
  <pre>\u4F8B: C | G | Am | F</pre>
5183
+ <p style="margin-top:4px;"><small>\u30B3\u30FC\u30C9\u9032\u884C\u3092\u81EA\u5206\u3067\u8003\u3048\u308B\u306E\u304C\u96E3\u3057\u3044\u3068\u304D\u306F\u3001\u30B3\u30FC\u30C9\u9032\u884C\u306E\u5171\u6709\u30B5\u30A4\u30C8\uFF08\u4F8B: <a href="https://rechord.cc/scores" target="_blank" rel="noopener">rechord.cc</a>\uFF09\u304B\u3089\u597D\u304D\u306A\u9032\u884C\u3092\u63A2\u3057\u3066\u30B3\u30D4\u30DA\u3059\u308B\u306E\u3082\u624B\u3067\u3059\u3002\u533A\u5207\u308A\u6587\u5B57\uFF08<code>|</code> / \u30B9\u30DA\u30FC\u30B9 / \u30AB\u30F3\u30DE\uFF09\u3060\u3051\u4E0A\u306E\u5F62\u5F0F\u306B\u5408\u308F\u305B\u308C\u3070\u3001\u305D\u306E\u307E\u307E\u4F7F\u3048\u307E\u3059\u3002</small></p>
5178
5184
 
5179
5185
  <h4>2. 1\u5C0F\u7BC0\u306B\u8907\u6570\u30B3\u30FC\u30C9\u3092\u5165\u308C\u308B</h4>
5180
5186
  <p>\u5C0F\u7BC0\u306E\u533A\u5207\u308A\uFF08\u7E26\u7DDA <code>|</code>\uFF09\u306E\u4E2D\u306B\u3001\u30B9\u30DA\u30FC\u30B9\u533A\u5207\u308A\u3067\u30B3\u30FC\u30C9\u3092\u4E26\u3079\u307E\u3059\u3002\u7B49\u9593\u9694\u306B\u914D\u7F6E\u3055\u308C\u307E\u3059\u3002</p>
@@ -5249,6 +5255,38 @@ var MML_INFO_HTML = `
5249
5255
  <p style="margin-top:4px;"><small>\uFF08\u97F3\u6E90\u540D\u306F <code>klatt</code> \u3084 <code>roze</code>, <code>teto</code> \u306A\u3069\u306E\u97F3\u58F0\u30E2\u30C7\u30EB\u3092\u6307\u5B9A\u3067\u304D\u307E\u3059\uFF09</small></p>
5250
5256
  </div>
5251
5257
  `;
5258
+ var MIDI_INFO_HTML = `
5259
+ <div class="dtm-modal-body-content">
5260
+ <h4>1. MIDI\u30D5\u30A1\u30A4\u30EB\u3068\u306F</h4>
5261
+ <p>\u300C\u3069\u306E\u97F3\u3092\u30FB\u3044\u3064\u30FB\u3069\u306E\u304F\u3089\u3044\u306E\u9577\u3055\u3067\u9CF4\u3089\u3059\u304B\u300D\u3092\u8A18\u9332\u3057\u305F\u3001\u6F14\u594F\u30C7\u30FC\u30BF\u306E\u30D5\u30A1\u30A4\u30EB\uFF08\u62E1\u5F35\u5B50 <code>.mid</code> / <code>.midi</code>\uFF09\u3067\u3059\u3002\u97F3\u305D\u306E\u3082\u306E\u3067\u306F\u306A\u304F\u697D\u8B5C\u306B\u8FD1\u3044\u30C7\u30FC\u30BF\u306A\u306E\u3067\u3001\u8AAD\u307F\u8FBC\u3093\u3067\u305D\u306E\u307E\u307E\u7DE8\u96C6\u3067\u304D\u307E\u3059\u3002</p>
5262
+
5263
+ <h4>2. \u8AAD\u307F\u8FBC\u307F\u306E\u3057\u304B\u305F</h4>
5264
+ <ul>
5265
+ <li>\u300C\u30D5\u30A1\u30A4\u30EB\u3092\u9078\u629E\u300D\u304B\u3089 <code>.mid</code> \u30D5\u30A1\u30A4\u30EB\u3092\u9078\u3073\u307E\u3059\u3002</li>
5266
+ <li>\u30D5\u30A1\u30A4\u30EB\u5185\u306E\u30C8\u30E9\u30C3\u30AF\u4E00\u89A7\u304C\u51FA\u308B\u306E\u3067\u3001\u53D6\u308A\u8FBC\u307F\u305F\u3044\u30C8\u30E9\u30C3\u30AF\u3092\u9078\u3073\u307E\u3059\u3002</li>
5267
+ <li>\u300C\u8AAD\u8FBC\u300D\u3092\u62BC\u3059\u3068\u53CD\u6620\u3055\u308C\u307E\u3059\u3002</li>
5268
+ </ul>
5269
+
5270
+ <h4>3. \u30E2\u30FC\u30C9\u306B\u3088\u308B\u53D6\u308A\u8FBC\u307F\u65B9\u306E\u9055\u3044</h4>
5271
+ <ul>
5272
+ <li><strong>SIMPLE</strong>: \u5404\u30C8\u30E9\u30C3\u30AF\u306E\u7279\u5FB4\u304B\u3089\u3001\u30E1\u30ED\u30C7\u30A3\u30FC\u30FB\u30B5\u30D6\u30E1\u30ED\u30FB\u30D9\u30FC\u30B9\u30FB\u4F34\u594F\u306E4\u3064\u306E\u5F79\u5272\u306B\u81EA\u52D5\u3067\u632F\u308A\u5206\u3051\u3089\u308C\u307E\u3059\u3002</li>
5273
+ <li><strong>ADVANCED</strong>: MIDI\u306E\u30C8\u30E9\u30C3\u30AF\u69CB\u6210\u304C\u305D\u306E\u307E\u307E\u53CD\u6620\u3055\u308C\u307E\u3059\uFF081\u5BFE1\uFF09\u3002</li>
5274
+ </ul>
5275
+
5276
+ <h4>4. MIDI\u30D5\u30A1\u30A4\u30EB\u3092\u624B\u306B\u5165\u308C\u308B</h4>
5277
+ <p>\u624B\u5143\u306BMIDI\u304C\u7121\u3044\u3068\u304D\u306F\u3001\u300C<code>\u66F2\u540D midi</code>\u300D\u306A\u3069\u3067\u691C\u7D22\u3059\u308C\u3070\u3001\u7121\u6599\u3067\u914D\u5E03\u3057\u3066\u3044\u308B\u30B5\u30A4\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u3059\u3002</p>
5278
+ <p style="margin-top:4px;"><small>\u307F\u3093\u306A\u304CMIDI\u3092\u6295\u7A3F\u3067\u304D\u308B\u6295\u7A3F\u578B\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0: <a href="http://picotune.me/" target="_blank" rel="noopener">picotune.me</a>\uFF08\u3044\u308D\u3093\u306A\u30B8\u30E3\u30F3\u30EB\u306EMIDI\u3092\u7121\u6599\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3067\u304D\u307E\u3059\u3002\u30B5\u30A4\u30C8\u4E0A\u3067\u306F\u30C1\u30C3\u30D7\u30C1\u30E5\u30FC\u30F3\u98A8\u306B\u518D\u751F\u3055\u308C\u307E\u3059\uFF09</small></p>
5279
+ <p style="margin-top:4px;"><small>\u203B\u691C\u7D22\u3067\u898B\u3064\u304B\u308B\u914D\u5E03\u30B5\u30A4\u30C8\u306F\u3001\u500B\u4EBA\u904B\u55B6\u306E\u3082\u306E\u304B\u3089\u6A29\u5229\u7684\u306B\u30B0\u30EC\u30FC\u306A\u3082\u306E\u307E\u3067\u69D8\u3005\u3067\u3059\u3002\u305D\u306E\u305F\u3081\u3001\u305D\u308C\u3089\u3078\u306E\u76F4\u63A5\u30EA\u30F3\u30AF\u306F\u8F09\u305B\u3066\u3044\u307E\u305B\u3093\u3002\u5229\u7528\u306E\u969B\u306F\u914D\u5E03\u5143\u3084\u6A29\u5229\u95A2\u4FC2\u3092\u3054\u81EA\u8EAB\u3067\u3054\u78BA\u8A8D\u304F\u3060\u3055\u3044\u3002</small></p>
5280
+
5281
+ <h4>5. UST\uFF08UTAU\uFF09\u306E\u6B4C\u8A5E\u3092\u4F7F\u3046</h4>
5282
+ <p>UTAU\u306EUST\u30D5\u30A1\u30A4\u30EB\u304B\u3089\u6B4C\u8A5E\u3060\u3051\u3092\u53D6\u308A\u51FA\u3057\u3066\u3001\u6B4C\u308F\u305B\u308B\u3053\u3068\u3082\u3067\u304D\u307E\u3059\u3002</p>
5283
+ <ul>
5284
+ <li>\u97F3\u7B26: UTAU\u306A\u3069\u3067UST\u3092MIDI\u306B\u66F8\u304D\u51FA\u3057\u3001\u4E0A\u306E\u624B\u9806\u3067\u8AAD\u307F\u8FBC\u307F\u307E\u3059\u3002</li>
5285
+ <li>\u6B4C\u8A5E: \u4E0B\u8A18\u30B5\u30A4\u30C8\u3067UST\u304B\u3089\u6B4C\u8A5E\u30C6\u30AD\u30B9\u30C8\u3092\u629C\u304D\u51FA\u3057\u3001MML/\u6B4C\u8A5E\u5165\u529B\u6B04\u306E <code>@@</code> \u69CB\u6587\u306B\u8CBC\u308A\u4ED8\u3051\u307E\u3059\u3002</li>
5286
+ </ul>
5287
+ <p style="margin-top:4px;"><small>\u6B4C\u8A5E\u306E\u62BD\u51FA: <a href="https://rpgen3.github.io/ust2txt/" target="_blank" rel="noopener">ust2txt</a></small></p>
5288
+ </div>
5289
+ `;
5252
5290
  var BASE_STEP_WIDTH = 0.5;
5253
5291
  var BASE_KEY_HEIGHT = 15;
5254
5292
  var TRACKS_SIMPLE = [
@@ -5407,9 +5445,11 @@ var mountDAW = (target, options = {}) => {
5407
5445
  injectStyles();
5408
5446
  const getAudioTime = options.getAudioTime ?? (() => performance.now() / 1e3);
5409
5447
  const trackConfigs = options.tracks ?? DEFAULT_TRACKS;
5448
+ const mode = options.mode ?? (trackConfigs.length > TRACKS_SIMPLE.length ? "advanced" : "simple");
5449
+ const isAdvanced = mode === "advanced";
5410
5450
  const drumPatterns = options.drumPatterns ?? DRUM_PATTERNS;
5411
5451
  const showMidi = !!options.parseMidi;
5412
- const showChord = true;
5452
+ const showChord = !isAdvanced;
5413
5453
  const refs = buildUI(target, {
5414
5454
  tracks: trackConfigs,
5415
5455
  drumPatternNames: Object.keys(drumPatterns),
@@ -6194,7 +6234,6 @@ var mountDAW = (target, options = {}) => {
6194
6234
  active.core.setVolume(active.volume);
6195
6235
  volLabel.textContent = String(active.volume);
6196
6236
  });
6197
- const isAdvanced = trackStates.length > TRACKS_SIMPLE.length;
6198
6237
  if (isAdvanced || active.config.id !== "chord") {
6199
6238
  const lyricDiv = document.createElement("div");
6200
6239
  lyricDiv.className = "dtm-row";
@@ -6402,16 +6441,16 @@ var mountDAW = (target, options = {}) => {
6402
6441
  updateUndoRedo();
6403
6442
  redrawAll();
6404
6443
  };
6405
- const setToolMode = (mode) => {
6406
- activeToolMode = mode;
6444
+ const setToolMode = (mode2) => {
6445
+ activeToolMode = mode2;
6407
6446
  for (const [btn, m] of [
6408
6447
  [refs.toolPen, "pen"],
6409
6448
  [refs.toolSelect, "select"],
6410
6449
  [refs.toolEraser, "eraser"]
6411
6450
  ]) {
6412
- btn.classList.toggle("dtm-segbtn--active", m === mode);
6451
+ btn.classList.toggle("dtm-segbtn--active", m === mode2);
6413
6452
  }
6414
- if (mode !== "select") {
6453
+ if (mode2 !== "select") {
6415
6454
  selectionRect = null;
6416
6455
  selectedNotes = [];
6417
6456
  }
@@ -6645,7 +6684,6 @@ var mountDAW = (target, options = {}) => {
6645
6684
  const applyMidiSelection = (midi, selectedIndices) => {
6646
6685
  clearAll();
6647
6686
  for (const t of trackStates) t.core.setLoadMode(true);
6648
- const isAdvanced = trackStates.length > TRACKS_SIMPLE.length;
6649
6687
  const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
6650
6688
  midi,
6651
6689
  selectedIndices,
@@ -6843,6 +6881,9 @@ var mountDAW = (target, options = {}) => {
6843
6881
  refs.mmlInfoBtn.addEventListener("click", () => {
6844
6882
  showModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC", MML_INFO_HTML);
6845
6883
  });
6884
+ refs.midiInfoBtn.addEventListener("click", () => {
6885
+ showModal("MIDI\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC", MIDI_INFO_HTML);
6886
+ });
6846
6887
  refs.shiftApplyBtn.addEventListener(
6847
6888
  "click",
6848
6889
  () => overlayDuring(() => {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "onjmin",
3
3
  "license": "MIT",
4
4
  "name": "@onjmin/dtm",
5
- "version": "0.1.12",
5
+ "version": "0.1.14",
6
6
  "description": "MMLを中間言語に用いた、モバイルファーストなDAW / ピアノロール打ち込みコンポーネント",
7
7
  "homepage": "https://onjmin.github.io/dtm",
8
8
  "repository": {