@onjmin/dtm 1.0.1 → 1.0.2
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.js +64 -3
- package/dist/index.mjs +64 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9916,6 +9916,36 @@ var SoundFont = class _SoundFont {
|
|
|
9916
9916
|
static afterTime = 0.5;
|
|
9917
9917
|
static fonts = /* @__PURE__ */ new Map();
|
|
9918
9918
|
static ch = -1;
|
|
9919
|
+
// ── ヒューマナイズ(疑似ラウンドロビン)──
|
|
9920
|
+
// サンプルを複数持たない代わりに、発音のたびピッチと音量をごく僅かにランダムへ
|
|
9921
|
+
// ブレさせて「毎回寸分違わず同じ波形」という機械的な均一さを崩す。
|
|
9922
|
+
/** 発音ごとのピッチ微揺らぎ幅(セント、±この値の範囲で一様乱数)。 */
|
|
9923
|
+
static humanizeDetuneCents = 4;
|
|
9924
|
+
/** 発音ごとの音量微揺らぎ幅(比率、0.06 = ±6%の範囲で一様乱数)。 */
|
|
9925
|
+
static humanizeGainRatio = 0.06;
|
|
9926
|
+
// ── ベロシティ→明るさ連動フィルタ ──
|
|
9927
|
+
// volume は音量にしか効かず音色(明るさ)が変わらないのを補うため、
|
|
9928
|
+
// ローパスのカットオフを volume に連動させる(弱いほど丸く、強いほど明るく)。
|
|
9929
|
+
/** volume=0 のときのローパスカットオフ(Hz)。 */
|
|
9930
|
+
static brightnessMinHz = 1200;
|
|
9931
|
+
/** volume=1 のときのローパスカットオフ(Hz)。サンプルの帯域を実質塞がない値。 */
|
|
9932
|
+
static brightnessMaxHz = 18e3;
|
|
9933
|
+
/** ベロシティ→明るさフィルタのQ値。共鳴が付かないButterworth特性。 */
|
|
9934
|
+
static brightnessQ = Math.SQRT1_2;
|
|
9935
|
+
// ── ロングトーン用ビブラート(ピッチLFO) ──
|
|
9936
|
+
// 伸ばす音が完全に静止していると不自然に聞こえるため、一定長以上持続する
|
|
9937
|
+
// 音符にだけ、発音直後フェードインしつつ穏やかなピッチ揺れを掛ける。
|
|
9938
|
+
// 閾値・速さは歌声合成側の vibrato.ts (VIBRATO_MIN_SEC/VIBRATO_RATE_HZ) と
|
|
9939
|
+
// 同じ考え方だが、MIDI楽器はここでは独立した定数として持つ
|
|
9940
|
+
// (koeのCurveInput契約に縛られない汎用のWeb Audio LFOのため)。
|
|
9941
|
+
/** ロングトーン判定の最短秒数。これ未満の音符にはビブラートを掛けない。 */
|
|
9942
|
+
static longToneThresholdSec = 0.35;
|
|
9943
|
+
/** ロングトーンビブラートの速さ(Hz)。 */
|
|
9944
|
+
static vibratoRateHz = 5.5;
|
|
9945
|
+
/** ロングトーンビブラートの深さ(セント)。歌声用(±35セント)より控えめ。 */
|
|
9946
|
+
static vibratoDepthCents = 12;
|
|
9947
|
+
/** ロングトーンビブラートのフェードイン秒数。発音直後は素直な音程を保つ。 */
|
|
9948
|
+
static vibratoFadeSec = 0.2;
|
|
9919
9949
|
static toURL(fontName) {
|
|
9920
9950
|
return `https://surikov.github.io/webaudiofontdata/sound/${fontName}.js`;
|
|
9921
9951
|
}
|
|
@@ -9966,6 +9996,7 @@ var SoundFont = class _SoundFont {
|
|
|
9966
9996
|
const zone = zones.get(pitch);
|
|
9967
9997
|
if (!zone) return;
|
|
9968
9998
|
const src = ctx.createBufferSource();
|
|
9999
|
+
const filter = ctx.createBiquadFilter();
|
|
9969
10000
|
const g = ctx.createGain();
|
|
9970
10001
|
const _when = when + ctx.currentTime;
|
|
9971
10002
|
const { buffer, _param } = zone;
|
|
@@ -9973,23 +10004,53 @@ var SoundFont = class _SoundFont {
|
|
|
9973
10004
|
src.buffer = buffer;
|
|
9974
10005
|
src.playbackRate.setValueAtTime(_param.playbackRate, 0);
|
|
9975
10006
|
Object.assign(src, _param.src);
|
|
10007
|
+
const humanizeCents = (Math.random() * 2 - 1) * _SoundFont.humanizeDetuneCents;
|
|
10008
|
+
const humanizeGainMul = 1 + (Math.random() * 2 - 1) * _SoundFont.humanizeGainRatio;
|
|
10009
|
+
src.detune.setValueAtTime(humanizeCents, 0);
|
|
10010
|
+
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);
|
|
9976
10018
|
g.gain.setValueAtTime(0, ctx.currentTime);
|
|
9977
10019
|
const attackTime = 5e-3;
|
|
9978
10020
|
const startGainTime = Math.max(ctx.currentTime, _when);
|
|
9979
10021
|
g.gain.setValueAtTime(0, startGainTime);
|
|
9980
|
-
g.gain.linearRampToValueAtTime(
|
|
10022
|
+
g.gain.linearRampToValueAtTime(effectiveVolume, startGainTime + attackTime);
|
|
9981
10023
|
const _duration = duration + _SoundFont.afterTime;
|
|
9982
10024
|
const end = _when + (isDrum ? buffer.duration : src.loop ? _duration : Math.min(_duration, _param.max));
|
|
9983
10025
|
if (!isDrum) {
|
|
9984
|
-
g.gain.setValueAtTime(
|
|
10026
|
+
g.gain.setValueAtTime(effectiveVolume, startGainTime + attackTime);
|
|
9985
10027
|
g.gain.linearRampToValueAtTime(0, end);
|
|
9986
10028
|
}
|
|
9987
|
-
src.connect(g).connect(destination);
|
|
10029
|
+
src.connect(filter).connect(g).connect(destination);
|
|
10030
|
+
let vibratoOsc;
|
|
10031
|
+
let vibratoDepth;
|
|
10032
|
+
if (!isDrum && duration >= _SoundFont.longToneThresholdSec) {
|
|
10033
|
+
vibratoOsc = ctx.createOscillator();
|
|
10034
|
+
vibratoOsc.type = "sine";
|
|
10035
|
+
vibratoOsc.frequency.setValueAtTime(_SoundFont.vibratoRateHz, 0);
|
|
10036
|
+
vibratoDepth = ctx.createGain();
|
|
10037
|
+
vibratoDepth.gain.setValueAtTime(0, startGainTime);
|
|
10038
|
+
vibratoDepth.gain.linearRampToValueAtTime(
|
|
10039
|
+
_SoundFont.vibratoDepthCents,
|
|
10040
|
+
startGainTime + _SoundFont.vibratoFadeSec
|
|
10041
|
+
);
|
|
10042
|
+
vibratoOsc.connect(vibratoDepth).connect(src.detune);
|
|
10043
|
+
vibratoOsc.start(_when);
|
|
10044
|
+
vibratoOsc.stop(end);
|
|
10045
|
+
}
|
|
9988
10046
|
src.start(_when);
|
|
9989
10047
|
src.stop(end);
|
|
9990
10048
|
src.onended = () => {
|
|
9991
10049
|
src.disconnect();
|
|
10050
|
+
filter.disconnect();
|
|
9992
10051
|
g.disconnect();
|
|
10052
|
+
vibratoOsc?.disconnect();
|
|
10053
|
+
vibratoDepth?.disconnect();
|
|
9993
10054
|
};
|
|
9994
10055
|
}
|
|
9995
10056
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -9783,6 +9783,36 @@ var SoundFont = class _SoundFont {
|
|
|
9783
9783
|
static afterTime = 0.5;
|
|
9784
9784
|
static fonts = /* @__PURE__ */ new Map();
|
|
9785
9785
|
static ch = -1;
|
|
9786
|
+
// ── ヒューマナイズ(疑似ラウンドロビン)──
|
|
9787
|
+
// サンプルを複数持たない代わりに、発音のたびピッチと音量をごく僅かにランダムへ
|
|
9788
|
+
// ブレさせて「毎回寸分違わず同じ波形」という機械的な均一さを崩す。
|
|
9789
|
+
/** 発音ごとのピッチ微揺らぎ幅(セント、±この値の範囲で一様乱数)。 */
|
|
9790
|
+
static humanizeDetuneCents = 4;
|
|
9791
|
+
/** 発音ごとの音量微揺らぎ幅(比率、0.06 = ±6%の範囲で一様乱数)。 */
|
|
9792
|
+
static humanizeGainRatio = 0.06;
|
|
9793
|
+
// ── ベロシティ→明るさ連動フィルタ ──
|
|
9794
|
+
// volume は音量にしか効かず音色(明るさ)が変わらないのを補うため、
|
|
9795
|
+
// ローパスのカットオフを volume に連動させる(弱いほど丸く、強いほど明るく)。
|
|
9796
|
+
/** volume=0 のときのローパスカットオフ(Hz)。 */
|
|
9797
|
+
static brightnessMinHz = 1200;
|
|
9798
|
+
/** volume=1 のときのローパスカットオフ(Hz)。サンプルの帯域を実質塞がない値。 */
|
|
9799
|
+
static brightnessMaxHz = 18e3;
|
|
9800
|
+
/** ベロシティ→明るさフィルタのQ値。共鳴が付かないButterworth特性。 */
|
|
9801
|
+
static brightnessQ = Math.SQRT1_2;
|
|
9802
|
+
// ── ロングトーン用ビブラート(ピッチLFO) ──
|
|
9803
|
+
// 伸ばす音が完全に静止していると不自然に聞こえるため、一定長以上持続する
|
|
9804
|
+
// 音符にだけ、発音直後フェードインしつつ穏やかなピッチ揺れを掛ける。
|
|
9805
|
+
// 閾値・速さは歌声合成側の vibrato.ts (VIBRATO_MIN_SEC/VIBRATO_RATE_HZ) と
|
|
9806
|
+
// 同じ考え方だが、MIDI楽器はここでは独立した定数として持つ
|
|
9807
|
+
// (koeのCurveInput契約に縛られない汎用のWeb Audio LFOのため)。
|
|
9808
|
+
/** ロングトーン判定の最短秒数。これ未満の音符にはビブラートを掛けない。 */
|
|
9809
|
+
static longToneThresholdSec = 0.35;
|
|
9810
|
+
/** ロングトーンビブラートの速さ(Hz)。 */
|
|
9811
|
+
static vibratoRateHz = 5.5;
|
|
9812
|
+
/** ロングトーンビブラートの深さ(セント)。歌声用(±35セント)より控えめ。 */
|
|
9813
|
+
static vibratoDepthCents = 12;
|
|
9814
|
+
/** ロングトーンビブラートのフェードイン秒数。発音直後は素直な音程を保つ。 */
|
|
9815
|
+
static vibratoFadeSec = 0.2;
|
|
9786
9816
|
static toURL(fontName) {
|
|
9787
9817
|
return `https://surikov.github.io/webaudiofontdata/sound/${fontName}.js`;
|
|
9788
9818
|
}
|
|
@@ -9833,6 +9863,7 @@ var SoundFont = class _SoundFont {
|
|
|
9833
9863
|
const zone = zones.get(pitch);
|
|
9834
9864
|
if (!zone) return;
|
|
9835
9865
|
const src = ctx.createBufferSource();
|
|
9866
|
+
const filter = ctx.createBiquadFilter();
|
|
9836
9867
|
const g = ctx.createGain();
|
|
9837
9868
|
const _when = when + ctx.currentTime;
|
|
9838
9869
|
const { buffer, _param } = zone;
|
|
@@ -9840,23 +9871,53 @@ var SoundFont = class _SoundFont {
|
|
|
9840
9871
|
src.buffer = buffer;
|
|
9841
9872
|
src.playbackRate.setValueAtTime(_param.playbackRate, 0);
|
|
9842
9873
|
Object.assign(src, _param.src);
|
|
9874
|
+
const humanizeCents = (Math.random() * 2 - 1) * _SoundFont.humanizeDetuneCents;
|
|
9875
|
+
const humanizeGainMul = 1 + (Math.random() * 2 - 1) * _SoundFont.humanizeGainRatio;
|
|
9876
|
+
src.detune.setValueAtTime(humanizeCents, 0);
|
|
9877
|
+
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);
|
|
9843
9885
|
g.gain.setValueAtTime(0, ctx.currentTime);
|
|
9844
9886
|
const attackTime = 5e-3;
|
|
9845
9887
|
const startGainTime = Math.max(ctx.currentTime, _when);
|
|
9846
9888
|
g.gain.setValueAtTime(0, startGainTime);
|
|
9847
|
-
g.gain.linearRampToValueAtTime(
|
|
9889
|
+
g.gain.linearRampToValueAtTime(effectiveVolume, startGainTime + attackTime);
|
|
9848
9890
|
const _duration = duration + _SoundFont.afterTime;
|
|
9849
9891
|
const end = _when + (isDrum ? buffer.duration : src.loop ? _duration : Math.min(_duration, _param.max));
|
|
9850
9892
|
if (!isDrum) {
|
|
9851
|
-
g.gain.setValueAtTime(
|
|
9893
|
+
g.gain.setValueAtTime(effectiveVolume, startGainTime + attackTime);
|
|
9852
9894
|
g.gain.linearRampToValueAtTime(0, end);
|
|
9853
9895
|
}
|
|
9854
|
-
src.connect(g).connect(destination);
|
|
9896
|
+
src.connect(filter).connect(g).connect(destination);
|
|
9897
|
+
let vibratoOsc;
|
|
9898
|
+
let vibratoDepth;
|
|
9899
|
+
if (!isDrum && duration >= _SoundFont.longToneThresholdSec) {
|
|
9900
|
+
vibratoOsc = ctx.createOscillator();
|
|
9901
|
+
vibratoOsc.type = "sine";
|
|
9902
|
+
vibratoOsc.frequency.setValueAtTime(_SoundFont.vibratoRateHz, 0);
|
|
9903
|
+
vibratoDepth = ctx.createGain();
|
|
9904
|
+
vibratoDepth.gain.setValueAtTime(0, startGainTime);
|
|
9905
|
+
vibratoDepth.gain.linearRampToValueAtTime(
|
|
9906
|
+
_SoundFont.vibratoDepthCents,
|
|
9907
|
+
startGainTime + _SoundFont.vibratoFadeSec
|
|
9908
|
+
);
|
|
9909
|
+
vibratoOsc.connect(vibratoDepth).connect(src.detune);
|
|
9910
|
+
vibratoOsc.start(_when);
|
|
9911
|
+
vibratoOsc.stop(end);
|
|
9912
|
+
}
|
|
9855
9913
|
src.start(_when);
|
|
9856
9914
|
src.stop(end);
|
|
9857
9915
|
src.onended = () => {
|
|
9858
9916
|
src.disconnect();
|
|
9917
|
+
filter.disconnect();
|
|
9859
9918
|
g.disconnect();
|
|
9919
|
+
vibratoOsc?.disconnect();
|
|
9920
|
+
vibratoDepth?.disconnect();
|
|
9860
9921
|
};
|
|
9861
9922
|
}
|
|
9862
9923
|
};
|
package/package.json
CHANGED