@onjmin/dtm 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -2086,6 +2086,8 @@ type SoundFontInstance = {
2086
2086
  destination: AudioNode;
2087
2087
  pitch: number;
2088
2088
  volume: number;
2089
+ /** 元ノートのvelocity(0-127)。音量ではなく音色の明るさにだけ効く。 */
2090
+ velocity?: number;
2089
2091
  when: number;
2090
2092
  duration: number;
2091
2093
  }) => void;
package/dist/index.d.ts CHANGED
@@ -2086,6 +2086,8 @@ type SoundFontInstance = {
2086
2086
  destination: AudioNode;
2087
2087
  pitch: number;
2088
2088
  volume: number;
2089
+ /** 元ノートのvelocity(0-127)。音量ではなく音色の明るさにだけ効く。 */
2090
+ velocity?: number;
2089
2091
  when: number;
2090
2092
  duration: number;
2091
2093
  }) => void;
package/dist/index.js CHANGED
@@ -9924,14 +9924,23 @@ var SoundFont = class _SoundFont {
9924
9924
  /** 発音ごとの音量微揺らぎ幅(比率、0.06 = ±6%の範囲で一様乱数)。 */
9925
9925
  static humanizeGainRatio = 0.06;
9926
9926
  // ── ベロシティ→明るさ連動フィルタ ──
9927
- // volume は音量にしか効かず音色(明るさ)が変わらないのを補うため、
9928
- // ローパスのカットオフを volume に連動させる(弱いほど丸く、強いほど明るく)。
9929
- /** volume=0 のときのローパスカットオフ(Hz)。 */
9930
- static brightnessMinHz = 1200;
9931
- /** volume=1 のときのローパスカットオフ(Hz)。サンプルの帯域を実質塞がない値。 */
9932
- static brightnessMaxHz = 18e3;
9927
+ // velocity は音量にしか効かず音色(明るさ)が変わらないのを補うため、
9928
+ // ローパスのカットオフを velocity に連動させる(弱いほど丸く、強いほど明るく)。
9929
+ // 連動元は必ず velocity であって volume ではない。volume には
9930
+ // 「トラック音量フェーダー×velocity」が既に畳み込まれている(sequencer.ts参照)ため、
9931
+ // volume を使うとフェーダーを下げただけで音色まで暗くなり、音量がゲインと音色へ
9932
+ // 二重に効いてしまう。音量フェーダーは音色を変えてはならない。
9933
+ /** velocity=0 のときのローパスカットオフ(Hz)。 */
9934
+ static brightnessMinHz = 2400;
9935
+ /** velocity=127 のときのローパスカットオフ(Hz)。サンプルの帯域を実質塞がない値。 */
9936
+ static brightnessMaxHz = 2e4;
9933
9937
  /** ベロシティ→明るさフィルタのQ値。共鳴が付かないButterworth特性。 */
9934
9938
  static brightnessQ = Math.SQRT1_2;
9939
+ /**
9940
+ * この明るさ比率以上ならフィルタ自体を挿入しない。最強ベロシティの音は改修前と
9941
+ * 完全に同一の信号経路を通り、余計な音痩せや位相変化が起きないようにするため。
9942
+ */
9943
+ static brightnessBypassAbove = 0.98;
9935
9944
  // ── ロングトーン用ビブラート(ピッチLFO) ──
9936
9945
  // 伸ばす音が完全に静止していると不自然に聞こえるため、一定長以上持続する
9937
9946
  // 音符にだけ、発音直後フェードインしつつ穏やかなピッチ揺れを掛ける。
@@ -9986,6 +9995,7 @@ var SoundFont = class _SoundFont {
9986
9995
  destination,
9987
9996
  pitch = 60,
9988
9997
  volume = 1,
9998
+ velocity,
9989
9999
  when = 0,
9990
10000
  duration = 1
9991
10001
  } = {}) {
@@ -9996,7 +10006,6 @@ var SoundFont = class _SoundFont {
9996
10006
  const zone = zones.get(pitch);
9997
10007
  if (!zone) return;
9998
10008
  const src = ctx.createBufferSource();
9999
- const filter = ctx.createBiquadFilter();
10000
10009
  const g = ctx.createGain();
10001
10010
  const _when = when + ctx.currentTime;
10002
10011
  const { buffer, _param } = zone;
@@ -10008,13 +10017,16 @@ var SoundFont = class _SoundFont {
10008
10017
  const humanizeGainMul = 1 + (Math.random() * 2 - 1) * _SoundFont.humanizeGainRatio;
10009
10018
  src.detune.setValueAtTime(humanizeCents, 0);
10010
10019
  const effectiveVolume = volume * humanizeGainMul;
10011
- filter.type = "lowpass";
10012
- const brightness = Math.max(0, Math.min(1, volume));
10013
- filter.frequency.setValueAtTime(
10014
- _SoundFont.brightnessMinHz + (_SoundFont.brightnessMaxHz - _SoundFont.brightnessMinHz) * brightness,
10015
- 0
10016
- );
10017
- filter.Q.setValueAtTime(_SoundFont.brightnessQ, 0);
10020
+ const brightness = velocity === void 0 ? 1 : Math.max(0, Math.min(1, velocity / 127));
10021
+ const filter = brightness >= _SoundFont.brightnessBypassAbove ? void 0 : ctx.createBiquadFilter();
10022
+ if (filter) {
10023
+ filter.type = "lowpass";
10024
+ filter.frequency.setValueAtTime(
10025
+ _SoundFont.brightnessMinHz + (_SoundFont.brightnessMaxHz - _SoundFont.brightnessMinHz) * brightness,
10026
+ 0
10027
+ );
10028
+ filter.Q.setValueAtTime(_SoundFont.brightnessQ, 0);
10029
+ }
10018
10030
  g.gain.setValueAtTime(0, ctx.currentTime);
10019
10031
  const attackTime = 5e-3;
10020
10032
  const startGainTime = Math.max(ctx.currentTime, _when);
@@ -10026,7 +10038,9 @@ var SoundFont = class _SoundFont {
10026
10038
  g.gain.setValueAtTime(effectiveVolume, startGainTime + attackTime);
10027
10039
  g.gain.linearRampToValueAtTime(0, end);
10028
10040
  }
10029
- src.connect(filter).connect(g).connect(destination);
10041
+ if (filter) src.connect(filter).connect(g);
10042
+ else src.connect(g);
10043
+ g.connect(destination);
10030
10044
  let vibratoOsc;
10031
10045
  let vibratoDepth;
10032
10046
  if (!isDrum && duration >= _SoundFont.longToneThresholdSec) {
@@ -10047,7 +10061,7 @@ var SoundFont = class _SoundFont {
10047
10061
  src.stop(end);
10048
10062
  src.onended = () => {
10049
10063
  src.disconnect();
10050
- filter.disconnect();
10064
+ filter?.disconnect();
10051
10065
  g.disconnect();
10052
10066
  vibratoOsc?.disconnect();
10053
10067
  vibratoDepth?.disconnect();
@@ -11141,6 +11155,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
11141
11155
  };
11142
11156
  const wafPlayDynamic = ({
11143
11157
  pitch,
11158
+ velocity,
11144
11159
  volume,
11145
11160
  when,
11146
11161
  duration,
@@ -11154,6 +11169,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
11154
11169
  destination,
11155
11170
  pitch,
11156
11171
  volume: volume * 0.85,
11172
+ velocity,
11157
11173
  when,
11158
11174
  duration
11159
11175
  });
@@ -19612,6 +19628,7 @@ var createDtmStudio = async (options = {}) => {
19612
19628
  destination: getChannelStrip(e.trackId).input,
19613
19629
  pitch: e.pitch,
19614
19630
  volume: e.volume,
19631
+ velocity: e.velocity,
19615
19632
  when: e.when,
19616
19633
  duration: e.duration
19617
19634
  });
@@ -19928,6 +19945,7 @@ var createDtmStudio = async (options = {}) => {
19928
19945
  destination: getChannelStrip(e.trackId).input,
19929
19946
  pitch: e.pitch,
19930
19947
  volume: e.volume,
19948
+ velocity: e.velocity,
19931
19949
  when: e.when,
19932
19950
  duration: e.duration
19933
19951
  });
@@ -20014,6 +20032,7 @@ var createDtmStudio = async (options = {}) => {
20014
20032
  destination: getChannelStrip(e.trackId).input,
20015
20033
  pitch: e.pitch,
20016
20034
  volume: e.volume,
20035
+ velocity: e.velocity,
20017
20036
  when: e.when,
20018
20037
  duration: e.duration
20019
20038
  });
@@ -20093,6 +20112,7 @@ var createDtmStudio = async (options = {}) => {
20093
20112
  destination: getChannelStrip(e.trackId).input,
20094
20113
  pitch: e.pitch,
20095
20114
  volume: e.volume,
20115
+ velocity: e.velocity,
20096
20116
  when: e.when,
20097
20117
  duration: e.duration
20098
20118
  });
@@ -20132,6 +20152,7 @@ var createDtmStudio = async (options = {}) => {
20132
20152
  destination: getChannelStrip(e.trackId).input,
20133
20153
  pitch: e.pitch,
20134
20154
  volume: e.volume,
20155
+ velocity: e.velocity,
20135
20156
  when: e.when,
20136
20157
  duration: e.duration
20137
20158
  });
@@ -20188,6 +20209,7 @@ var createDtmStudio = async (options = {}) => {
20188
20209
  destination: getChannelStrip("chord").input,
20189
20210
  pitch: e.pitch,
20190
20211
  volume: e.volume,
20212
+ velocity: e.velocity,
20191
20213
  when: e.when,
20192
20214
  duration: e.duration
20193
20215
  });
package/dist/index.mjs CHANGED
@@ -9791,14 +9791,23 @@ var SoundFont = class _SoundFont {
9791
9791
  /** 発音ごとの音量微揺らぎ幅(比率、0.06 = ±6%の範囲で一様乱数)。 */
9792
9792
  static humanizeGainRatio = 0.06;
9793
9793
  // ── ベロシティ→明るさ連動フィルタ ──
9794
- // volume は音量にしか効かず音色(明るさ)が変わらないのを補うため、
9795
- // ローパスのカットオフを volume に連動させる(弱いほど丸く、強いほど明るく)。
9796
- /** volume=0 のときのローパスカットオフ(Hz)。 */
9797
- static brightnessMinHz = 1200;
9798
- /** volume=1 のときのローパスカットオフ(Hz)。サンプルの帯域を実質塞がない値。 */
9799
- static brightnessMaxHz = 18e3;
9794
+ // velocity は音量にしか効かず音色(明るさ)が変わらないのを補うため、
9795
+ // ローパスのカットオフを velocity に連動させる(弱いほど丸く、強いほど明るく)。
9796
+ // 連動元は必ず velocity であって volume ではない。volume には
9797
+ // 「トラック音量フェーダー×velocity」が既に畳み込まれている(sequencer.ts参照)ため、
9798
+ // volume を使うとフェーダーを下げただけで音色まで暗くなり、音量がゲインと音色へ
9799
+ // 二重に効いてしまう。音量フェーダーは音色を変えてはならない。
9800
+ /** velocity=0 のときのローパスカットオフ(Hz)。 */
9801
+ static brightnessMinHz = 2400;
9802
+ /** velocity=127 のときのローパスカットオフ(Hz)。サンプルの帯域を実質塞がない値。 */
9803
+ static brightnessMaxHz = 2e4;
9800
9804
  /** ベロシティ→明るさフィルタのQ値。共鳴が付かないButterworth特性。 */
9801
9805
  static brightnessQ = Math.SQRT1_2;
9806
+ /**
9807
+ * この明るさ比率以上ならフィルタ自体を挿入しない。最強ベロシティの音は改修前と
9808
+ * 完全に同一の信号経路を通り、余計な音痩せや位相変化が起きないようにするため。
9809
+ */
9810
+ static brightnessBypassAbove = 0.98;
9802
9811
  // ── ロングトーン用ビブラート(ピッチLFO) ──
9803
9812
  // 伸ばす音が完全に静止していると不自然に聞こえるため、一定長以上持続する
9804
9813
  // 音符にだけ、発音直後フェードインしつつ穏やかなピッチ揺れを掛ける。
@@ -9853,6 +9862,7 @@ var SoundFont = class _SoundFont {
9853
9862
  destination,
9854
9863
  pitch = 60,
9855
9864
  volume = 1,
9865
+ velocity,
9856
9866
  when = 0,
9857
9867
  duration = 1
9858
9868
  } = {}) {
@@ -9863,7 +9873,6 @@ var SoundFont = class _SoundFont {
9863
9873
  const zone = zones.get(pitch);
9864
9874
  if (!zone) return;
9865
9875
  const src = ctx.createBufferSource();
9866
- const filter = ctx.createBiquadFilter();
9867
9876
  const g = ctx.createGain();
9868
9877
  const _when = when + ctx.currentTime;
9869
9878
  const { buffer, _param } = zone;
@@ -9875,13 +9884,16 @@ var SoundFont = class _SoundFont {
9875
9884
  const humanizeGainMul = 1 + (Math.random() * 2 - 1) * _SoundFont.humanizeGainRatio;
9876
9885
  src.detune.setValueAtTime(humanizeCents, 0);
9877
9886
  const effectiveVolume = volume * humanizeGainMul;
9878
- filter.type = "lowpass";
9879
- const brightness = Math.max(0, Math.min(1, volume));
9880
- filter.frequency.setValueAtTime(
9881
- _SoundFont.brightnessMinHz + (_SoundFont.brightnessMaxHz - _SoundFont.brightnessMinHz) * brightness,
9882
- 0
9883
- );
9884
- filter.Q.setValueAtTime(_SoundFont.brightnessQ, 0);
9887
+ const brightness = velocity === void 0 ? 1 : Math.max(0, Math.min(1, velocity / 127));
9888
+ const filter = brightness >= _SoundFont.brightnessBypassAbove ? void 0 : ctx.createBiquadFilter();
9889
+ if (filter) {
9890
+ filter.type = "lowpass";
9891
+ filter.frequency.setValueAtTime(
9892
+ _SoundFont.brightnessMinHz + (_SoundFont.brightnessMaxHz - _SoundFont.brightnessMinHz) * brightness,
9893
+ 0
9894
+ );
9895
+ filter.Q.setValueAtTime(_SoundFont.brightnessQ, 0);
9896
+ }
9885
9897
  g.gain.setValueAtTime(0, ctx.currentTime);
9886
9898
  const attackTime = 5e-3;
9887
9899
  const startGainTime = Math.max(ctx.currentTime, _when);
@@ -9893,7 +9905,9 @@ var SoundFont = class _SoundFont {
9893
9905
  g.gain.setValueAtTime(effectiveVolume, startGainTime + attackTime);
9894
9906
  g.gain.linearRampToValueAtTime(0, end);
9895
9907
  }
9896
- src.connect(filter).connect(g).connect(destination);
9908
+ if (filter) src.connect(filter).connect(g);
9909
+ else src.connect(g);
9910
+ g.connect(destination);
9897
9911
  let vibratoOsc;
9898
9912
  let vibratoDepth;
9899
9913
  if (!isDrum && duration >= _SoundFont.longToneThresholdSec) {
@@ -9914,7 +9928,7 @@ var SoundFont = class _SoundFont {
9914
9928
  src.stop(end);
9915
9929
  src.onended = () => {
9916
9930
  src.disconnect();
9917
- filter.disconnect();
9931
+ filter?.disconnect();
9918
9932
  g.disconnect();
9919
9933
  vibratoOsc?.disconnect();
9920
9934
  vibratoDepth?.disconnect();
@@ -11008,6 +11022,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
11008
11022
  };
11009
11023
  const wafPlayDynamic = ({
11010
11024
  pitch,
11025
+ velocity,
11011
11026
  volume,
11012
11027
  when,
11013
11028
  duration,
@@ -11021,6 +11036,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
11021
11036
  destination,
11022
11037
  pitch,
11023
11038
  volume: volume * 0.85,
11039
+ velocity,
11024
11040
  when,
11025
11041
  duration
11026
11042
  });
@@ -19478,6 +19494,7 @@ var createDtmStudio = async (options = {}) => {
19478
19494
  destination: getChannelStrip(e.trackId).input,
19479
19495
  pitch: e.pitch,
19480
19496
  volume: e.volume,
19497
+ velocity: e.velocity,
19481
19498
  when: e.when,
19482
19499
  duration: e.duration
19483
19500
  });
@@ -19794,6 +19811,7 @@ var createDtmStudio = async (options = {}) => {
19794
19811
  destination: getChannelStrip(e.trackId).input,
19795
19812
  pitch: e.pitch,
19796
19813
  volume: e.volume,
19814
+ velocity: e.velocity,
19797
19815
  when: e.when,
19798
19816
  duration: e.duration
19799
19817
  });
@@ -19880,6 +19898,7 @@ var createDtmStudio = async (options = {}) => {
19880
19898
  destination: getChannelStrip(e.trackId).input,
19881
19899
  pitch: e.pitch,
19882
19900
  volume: e.volume,
19901
+ velocity: e.velocity,
19883
19902
  when: e.when,
19884
19903
  duration: e.duration
19885
19904
  });
@@ -19959,6 +19978,7 @@ var createDtmStudio = async (options = {}) => {
19959
19978
  destination: getChannelStrip(e.trackId).input,
19960
19979
  pitch: e.pitch,
19961
19980
  volume: e.volume,
19981
+ velocity: e.velocity,
19962
19982
  when: e.when,
19963
19983
  duration: e.duration
19964
19984
  });
@@ -19998,6 +20018,7 @@ var createDtmStudio = async (options = {}) => {
19998
20018
  destination: getChannelStrip(e.trackId).input,
19999
20019
  pitch: e.pitch,
20000
20020
  volume: e.volume,
20021
+ velocity: e.velocity,
20001
20022
  when: e.when,
20002
20023
  duration: e.duration
20003
20024
  });
@@ -20054,6 +20075,7 @@ var createDtmStudio = async (options = {}) => {
20054
20075
  destination: getChannelStrip("chord").input,
20055
20076
  pitch: e.pitch,
20056
20077
  volume: e.volume,
20078
+ velocity: e.velocity,
20057
20079
  when: e.when,
20058
20080
  duration: e.duration
20059
20081
  });
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "onjmin",
3
3
  "license": "MIT",
4
4
  "name": "@onjmin/dtm",
5
- "version": "1.0.2",
5
+ "version": "1.0.3",
6
6
  "description": "MMLを中間言語に用いた、モバイルファーストなDAW / ピアノロール打ち込みコンポーネント",
7
7
  "homepage": "https://onjmin.github.io/dtm",
8
8
  "repository": {