@onjmin/dtm 0.1.58 → 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 CHANGED
@@ -45,6 +45,34 @@ studio.mountChordPlayer(
45
45
  );
46
46
  ```
47
47
 
48
+ ## マスター音量の調整
49
+
50
+ ライブラリの再生音量は 0-100 のパーセンテージで調整できます。
51
+
52
+ - `createDtmStudio` / `mountDAW` / `mountEditor` などの DAW 系 API では `masterVolume` を使います。
53
+ - ヘッドレス再生 API(`playMML` / `playChords` / `mountChordPlayer`)では `volume` を使います。
54
+ - 再生中に音量を切り替えたい場合は各インスタンスの `setVolume()` を呼び出します。
55
+
56
+ ```ts
57
+ const studio = await createDtmStudio();
58
+ const daw = studio.mountEditor(editorEl, {
59
+ initialMML: "@0 t120 o5 l8 ccggaag4 ffeeddc4",
60
+ masterVolume: 60,
61
+ });
62
+ daw.setVolume(40);
63
+
64
+ const bgm = playMML("@0 t120 o5 l8 ccggaag4 ffeeddc4", {
65
+ loop: true,
66
+ volume: 70,
67
+ });
68
+ bgm.setVolume(50);
69
+
70
+ const chordPlayer = studio.mountChordPlayer(chordEl, "| C | G | Am | F |", {
71
+ volume: 80,
72
+ });
73
+ chordPlayer.setVolume(65);
74
+ ```
75
+
48
76
  ---
49
77
 
50
78
  ## モード(`simple` / `advanced`)
package/dist/index.d.mts CHANGED
@@ -46,6 +46,8 @@ type ChordPlayerInstance = {
46
46
  toggleMetronome: () => void;
47
47
  /** 再生中か否かを取得する */
48
48
  isPlaying: () => boolean;
49
+ /** マスタ音量を 0-100 で変更する。 */
50
+ setVolume: (volume: number) => void;
49
51
  };
50
52
  /**
51
53
  * コード進行再生専用プレイヤーを target 要素にマウントする。
@@ -733,6 +735,10 @@ type DawOptions = {
733
735
  initialActiveTrack?: string;
734
736
  /** 初期スクロールで中央に表示するMIDIピッチ番号(0–127)。未指定なら既定位置。 */
735
737
  initialScrollPitch?: number;
738
+ /** マスタ音量 0-100。既定50。 */
739
+ masterVolume?: number;
740
+ /** ドラム音量 0-100。既定80。 */
741
+ drumVolume?: number;
736
742
  parseMidi?: ParseMidiFn;
737
743
  /**
738
744
  * 動作モード({@link DawMode})。
@@ -804,6 +810,12 @@ type DawInstance = {
804
810
  applyPatch: (trackId: string, added: NoteData[], removed: NoteRemove[]) => void;
805
811
  /** リモートから受信した歌詞・UTAU設定を適用する。 */
806
812
  applyLyrics: (trackId: string, data: LyricSyncData) => void;
813
+ /** マスタ音量を 0-100 で変更する。 */
814
+ setMasterVolume: (volume: number) => void;
815
+ /** マスタ音量を 0-100 で変更する(`setMasterVolume` のエイリアス)。 */
816
+ setVolume: (volume: number) => void;
817
+ /** ドラム音量を 0-100 で変更する。 */
818
+ setDrumVolume: (volume: number) => void;
807
819
  /** リモートから受信したトラック個別楽器を適用する(`onTrackInstrumentChange` は発火しない)。 */
808
820
  applyTrackInstrument: (trackIndex: number, instrumentName: string) => void;
809
821
  /**
@@ -1456,6 +1468,8 @@ type MmlPlayerOptions = {
1456
1468
  onResumeAudio?: () => void | Promise<void>;
1457
1469
  /** 内蔵の簡易square-wave synthを使うか。既定は onPlayNote 未指定なら true */
1458
1470
  synth?: boolean;
1471
+ /** マスタ音量 0-100。既定100。 */
1472
+ masterVolume?: number;
1459
1473
  /** BPM未検出時のフォールバック。既定120 */
1460
1474
  defaultBpm?: number;
1461
1475
  /** 各トラックの 0-100 ボリューム。既定100 */
@@ -1487,6 +1501,7 @@ type MmlPlayerInstance = {
1487
1501
  play: () => void;
1488
1502
  stop: () => void;
1489
1503
  isPlaying: () => boolean;
1504
+ setVolume: (volume: number) => void;
1490
1505
  destroy: () => void;
1491
1506
  };
1492
1507
  declare const encodeMml: (mml: string) => Promise<string>;
package/dist/index.d.ts CHANGED
@@ -46,6 +46,8 @@ type ChordPlayerInstance = {
46
46
  toggleMetronome: () => void;
47
47
  /** 再生中か否かを取得する */
48
48
  isPlaying: () => boolean;
49
+ /** マスタ音量を 0-100 で変更する。 */
50
+ setVolume: (volume: number) => void;
49
51
  };
50
52
  /**
51
53
  * コード進行再生専用プレイヤーを target 要素にマウントする。
@@ -733,6 +735,10 @@ type DawOptions = {
733
735
  initialActiveTrack?: string;
734
736
  /** 初期スクロールで中央に表示するMIDIピッチ番号(0–127)。未指定なら既定位置。 */
735
737
  initialScrollPitch?: number;
738
+ /** マスタ音量 0-100。既定50。 */
739
+ masterVolume?: number;
740
+ /** ドラム音量 0-100。既定80。 */
741
+ drumVolume?: number;
736
742
  parseMidi?: ParseMidiFn;
737
743
  /**
738
744
  * 動作モード({@link DawMode})。
@@ -804,6 +810,12 @@ type DawInstance = {
804
810
  applyPatch: (trackId: string, added: NoteData[], removed: NoteRemove[]) => void;
805
811
  /** リモートから受信した歌詞・UTAU設定を適用する。 */
806
812
  applyLyrics: (trackId: string, data: LyricSyncData) => void;
813
+ /** マスタ音量を 0-100 で変更する。 */
814
+ setMasterVolume: (volume: number) => void;
815
+ /** マスタ音量を 0-100 で変更する(`setMasterVolume` のエイリアス)。 */
816
+ setVolume: (volume: number) => void;
817
+ /** ドラム音量を 0-100 で変更する。 */
818
+ setDrumVolume: (volume: number) => void;
807
819
  /** リモートから受信したトラック個別楽器を適用する(`onTrackInstrumentChange` は発火しない)。 */
808
820
  applyTrackInstrument: (trackIndex: number, instrumentName: string) => void;
809
821
  /**
@@ -1456,6 +1468,8 @@ type MmlPlayerOptions = {
1456
1468
  onResumeAudio?: () => void | Promise<void>;
1457
1469
  /** 内蔵の簡易square-wave synthを使うか。既定は onPlayNote 未指定なら true */
1458
1470
  synth?: boolean;
1471
+ /** マスタ音量 0-100。既定100。 */
1472
+ masterVolume?: number;
1459
1473
  /** BPM未検出時のフォールバック。既定120 */
1460
1474
  defaultBpm?: number;
1461
1475
  /** 各トラックの 0-100 ボリューム。既定100 */
@@ -1487,6 +1501,7 @@ type MmlPlayerInstance = {
1487
1501
  play: () => void;
1488
1502
  stop: () => void;
1489
1503
  isPlaying: () => boolean;
1504
+ setVolume: (volume: number) => void;
1490
1505
  destroy: () => void;
1491
1506
  };
1492
1507
  declare const encodeMml: (mml: string) => Promise<string>;
package/dist/index.js CHANGED
@@ -5539,6 +5539,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5539
5539
  const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
5540
5540
  const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
5541
5541
  const trackVolume = meta.volume ?? options.volume ?? 100;
5542
+ let masterVolume = options.masterVolume ?? 100;
5542
5543
  const drumVolume = meta.drumVolume ?? 80;
5543
5544
  const colors = options.trackColors ?? DEFAULT_TRACK_COLORS;
5544
5545
  const useSynth = options.synth ?? !options.onPlayNote;
@@ -6318,11 +6319,12 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6318
6319
  const em = emojiByTrack.get(trackIdx);
6319
6320
  if (em) jumpEmojiAt(em, e.when);
6320
6321
  if (lyricTracks.has(trackIdx) && !skipSinging) return;
6321
- options.onPlayNote?.(e);
6322
- if (useSynth) ensureSynth().playNote(e);
6322
+ const volume = e.volume * (masterVolume / 100);
6323
+ options.onPlayNote?.({ ...e, volume });
6324
+ if (useSynth) ensureSynth().playNote({ ...e, volume });
6323
6325
  },
6324
6326
  onPlayDrum: (e) => {
6325
- const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100);
6327
+ const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100) * (masterVolume / 100);
6326
6328
  options.onPlayDrum?.({ ...e, velocity });
6327
6329
  if (useSynth) ensureSynth().playDrum({ ...e, velocity });
6328
6330
  },
@@ -6346,6 +6348,9 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6346
6348
  if (activePlayer === instance) activePlayer = null;
6347
6349
  options.onStop?.();
6348
6350
  };
6351
+ const setVolume = (volume) => {
6352
+ masterVolume = volume;
6353
+ };
6349
6354
  const buildStreamTracks = () => [...lyricTracks.entries()].map(([index, lt]) => {
6350
6355
  const seqTrack = seqTracks.find((t) => Number(t.id) === index);
6351
6356
  const sorted = [...seqTrack?.notes ?? []].sort(
@@ -6367,7 +6372,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6367
6372
  return {
6368
6373
  id: String(index),
6369
6374
  model: lt.model,
6370
- volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (trackVolume / 100),
6375
+ volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (trackVolume / 100) * (masterVolume / 100),
6371
6376
  pan: panToStereo(lt.pan ?? DEFAULT_PAN),
6372
6377
  notes
6373
6378
  };
@@ -6481,6 +6486,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6481
6486
  play,
6482
6487
  stop,
6483
6488
  isPlaying: () => playing,
6489
+ setVolume,
6484
6490
  destroy
6485
6491
  };
6486
6492
  return instance;
@@ -6975,6 +6981,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
6975
6981
  let activeIndex = -1;
6976
6982
  let activePlayback = null;
6977
6983
  let isPlaying = false;
6984
+ let masterVolume = options.volume ?? 80;
6978
6985
  let isLoading = false;
6979
6986
  let loadAbortId = 0;
6980
6987
  let loopEnabled = false;
@@ -7334,7 +7341,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
7334
7341
  container2.innerHTML = "";
7335
7342
  modalSamplePlayer = mountChordPlayer(container2, sampleChords, {
7336
7343
  audioContext: options.audioContext ?? ownCtx ?? void 0,
7337
- volume: options.volume ?? 100,
7344
+ volume: masterVolume,
7338
7345
  bpm: options.bpm ?? 120,
7339
7346
  studio: options.studio,
7340
7347
  // 再帰的なモーダル生成を防ぐ。
@@ -7675,7 +7682,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
7675
7682
  };
7676
7683
  const playInternal = (startIdx, ctx, cutGain) => {
7677
7684
  const C32 = 48;
7678
- const volume = options.volume ?? 80;
7685
+ const volume = masterVolume;
7679
7686
  const placements = [];
7680
7687
  for (const event of chordEvents) {
7681
7688
  if (event.when < playOffsetSec) continue;
@@ -7832,6 +7839,10 @@ var mountChordPlayer = (target, chords, options = {}) => {
7832
7839
  }
7833
7840
  }
7834
7841
  };
7842
+ const setVolume = (volume) => {
7843
+ masterVolume = Math.max(0, Math.min(100, volume));
7844
+ activePlayback?.setVolume(masterVolume);
7845
+ };
7835
7846
  const destroy = () => {
7836
7847
  stop();
7837
7848
  closeInfoModal();
@@ -7961,6 +7972,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
7961
7972
  element: container,
7962
7973
  play,
7963
7974
  stop,
7975
+ setVolume,
7964
7976
  destroy,
7965
7977
  toggleMetronome,
7966
7978
  isPlaying: () => isPlaying
@@ -9994,6 +10006,10 @@ var mountDAW = (target, options = {}) => {
9994
10006
  showChord,
9995
10007
  showMidiSearch
9996
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}%`;
9997
10013
  const renderConfig = {
9998
10014
  stepsPerBar: 192,
9999
10015
  keyCount: 128,
@@ -10005,8 +10021,8 @@ var mountDAW = (target, options = {}) => {
10005
10021
  let zoomX = 100;
10006
10022
  let zoomY = 100;
10007
10023
  let bpm = options.defaultBpm ?? DEFAULT_BPM;
10008
- let masterVolume = 50;
10009
- let drumVolume = 80;
10024
+ let masterVolume = options.masterVolume ?? 50;
10025
+ let drumVolume = options.drumVolume ?? 80;
10010
10026
  let currentDrumPattern = refs.drumSelect.value;
10011
10027
  let currentInstrument = "";
10012
10028
  let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
@@ -12764,6 +12780,21 @@ var mountDAW = (target, options = {}) => {
12764
12780
  getCurrentPlayStep,
12765
12781
  forcePauseAt,
12766
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
+ },
12767
12798
  applyPatch: (trackId, added, removed) => {
12768
12799
  const track = trackStates.find((t) => t.config.id === trackId);
12769
12800
  if (!track) return;
package/dist/index.mjs CHANGED
@@ -5417,6 +5417,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5417
5417
  const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
5418
5418
  const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
5419
5419
  const trackVolume = meta.volume ?? options.volume ?? 100;
5420
+ let masterVolume = options.masterVolume ?? 100;
5420
5421
  const drumVolume = meta.drumVolume ?? 80;
5421
5422
  const colors = options.trackColors ?? DEFAULT_TRACK_COLORS;
5422
5423
  const useSynth = options.synth ?? !options.onPlayNote;
@@ -6196,11 +6197,12 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6196
6197
  const em = emojiByTrack.get(trackIdx);
6197
6198
  if (em) jumpEmojiAt(em, e.when);
6198
6199
  if (lyricTracks.has(trackIdx) && !skipSinging) return;
6199
- options.onPlayNote?.(e);
6200
- if (useSynth) ensureSynth().playNote(e);
6200
+ const volume = e.volume * (masterVolume / 100);
6201
+ options.onPlayNote?.({ ...e, volume });
6202
+ if (useSynth) ensureSynth().playNote({ ...e, volume });
6201
6203
  },
6202
6204
  onPlayDrum: (e) => {
6203
- const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100);
6205
+ const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100) * (masterVolume / 100);
6204
6206
  options.onPlayDrum?.({ ...e, velocity });
6205
6207
  if (useSynth) ensureSynth().playDrum({ ...e, velocity });
6206
6208
  },
@@ -6224,6 +6226,9 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6224
6226
  if (activePlayer === instance) activePlayer = null;
6225
6227
  options.onStop?.();
6226
6228
  };
6229
+ const setVolume = (volume) => {
6230
+ masterVolume = volume;
6231
+ };
6227
6232
  const buildStreamTracks = () => [...lyricTracks.entries()].map(([index, lt]) => {
6228
6233
  const seqTrack = seqTracks.find((t) => Number(t.id) === index);
6229
6234
  const sorted = [...seqTrack?.notes ?? []].sort(
@@ -6245,7 +6250,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6245
6250
  return {
6246
6251
  id: String(index),
6247
6252
  model: lt.model,
6248
- volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (trackVolume / 100),
6253
+ volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (trackVolume / 100) * (masterVolume / 100),
6249
6254
  pan: panToStereo(lt.pan ?? DEFAULT_PAN),
6250
6255
  notes
6251
6256
  };
@@ -6359,6 +6364,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6359
6364
  play,
6360
6365
  stop,
6361
6366
  isPlaying: () => playing,
6367
+ setVolume,
6362
6368
  destroy
6363
6369
  };
6364
6370
  return instance;
@@ -6853,6 +6859,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
6853
6859
  let activeIndex = -1;
6854
6860
  let activePlayback = null;
6855
6861
  let isPlaying = false;
6862
+ let masterVolume = options.volume ?? 80;
6856
6863
  let isLoading = false;
6857
6864
  let loadAbortId = 0;
6858
6865
  let loopEnabled = false;
@@ -7212,7 +7219,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
7212
7219
  container2.innerHTML = "";
7213
7220
  modalSamplePlayer = mountChordPlayer(container2, sampleChords, {
7214
7221
  audioContext: options.audioContext ?? ownCtx ?? void 0,
7215
- volume: options.volume ?? 100,
7222
+ volume: masterVolume,
7216
7223
  bpm: options.bpm ?? 120,
7217
7224
  studio: options.studio,
7218
7225
  // 再帰的なモーダル生成を防ぐ。
@@ -7553,7 +7560,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
7553
7560
  };
7554
7561
  const playInternal = (startIdx, ctx, cutGain) => {
7555
7562
  const C32 = 48;
7556
- const volume = options.volume ?? 80;
7563
+ const volume = masterVolume;
7557
7564
  const placements = [];
7558
7565
  for (const event of chordEvents) {
7559
7566
  if (event.when < playOffsetSec) continue;
@@ -7710,6 +7717,10 @@ var mountChordPlayer = (target, chords, options = {}) => {
7710
7717
  }
7711
7718
  }
7712
7719
  };
7720
+ const setVolume = (volume) => {
7721
+ masterVolume = Math.max(0, Math.min(100, volume));
7722
+ activePlayback?.setVolume(masterVolume);
7723
+ };
7713
7724
  const destroy = () => {
7714
7725
  stop();
7715
7726
  closeInfoModal();
@@ -7839,6 +7850,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
7839
7850
  element: container,
7840
7851
  play,
7841
7852
  stop,
7853
+ setVolume,
7842
7854
  destroy,
7843
7855
  toggleMetronome,
7844
7856
  isPlaying: () => isPlaying
@@ -9872,6 +9884,10 @@ var mountDAW = (target, options = {}) => {
9872
9884
  showChord,
9873
9885
  showMidiSearch
9874
9886
  });
9887
+ refs.masterVolume.value = String(options.masterVolume ?? 50);
9888
+ refs.masterVolumeLabel.textContent = `${options.masterVolume ?? 50}%`;
9889
+ refs.drumVolume.value = String(options.drumVolume ?? 80);
9890
+ refs.drumVolumeLabel.textContent = `${options.drumVolume ?? 80}%`;
9875
9891
  const renderConfig = {
9876
9892
  stepsPerBar: 192,
9877
9893
  keyCount: 128,
@@ -9883,8 +9899,8 @@ var mountDAW = (target, options = {}) => {
9883
9899
  let zoomX = 100;
9884
9900
  let zoomY = 100;
9885
9901
  let bpm = options.defaultBpm ?? DEFAULT_BPM;
9886
- let masterVolume = 50;
9887
- let drumVolume = 80;
9902
+ let masterVolume = options.masterVolume ?? 50;
9903
+ let drumVolume = options.drumVolume ?? 80;
9888
9904
  let currentDrumPattern = refs.drumSelect.value;
9889
9905
  let currentInstrument = "";
9890
9906
  let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
@@ -12642,6 +12658,21 @@ var mountDAW = (target, options = {}) => {
12642
12658
  getCurrentPlayStep,
12643
12659
  forcePauseAt,
12644
12660
  setLoading,
12661
+ setMasterVolume: (volume) => {
12662
+ masterVolume = clamp3(volume, 0, 100);
12663
+ refs.masterVolume.value = String(masterVolume);
12664
+ refs.masterVolumeLabel.textContent = `${masterVolume}%`;
12665
+ },
12666
+ setVolume: (volume) => {
12667
+ masterVolume = clamp3(volume, 0, 100);
12668
+ refs.masterVolume.value = String(masterVolume);
12669
+ refs.masterVolumeLabel.textContent = `${masterVolume}%`;
12670
+ },
12671
+ setDrumVolume: (volume) => {
12672
+ drumVolume = clamp3(volume, 0, 100);
12673
+ refs.drumVolume.value = String(drumVolume);
12674
+ refs.drumVolumeLabel.textContent = `${drumVolume}%`;
12675
+ },
12645
12676
  applyPatch: (trackId, added, removed) => {
12646
12677
  const track = trackStates.find((t) => t.config.id === trackId);
12647
12678
  if (!track) return;
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.58",
5
+ "version": "0.1.59",
6
6
  "description": "MMLを中間言語に用いた、モバイルファーストなDAW / ピアノロール打ち込みコンポーネント",
7
7
  "homepage": "https://onjmin.github.io/dtm",
8
8
  "repository": {