@onjmin/dtm 0.1.28 → 0.1.29
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 +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +77 -4
- package/dist/index.mjs +77 -4
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -309,6 +309,17 @@ type StreamPlaybackOptions = {
|
|
|
309
309
|
* 引数には遅れたノート情報と遅延秒数が渡されます。
|
|
310
310
|
*/
|
|
311
311
|
onLateSkip?: (note: StreamVoiceNote, delay: number) => void;
|
|
312
|
+
/**
|
|
313
|
+
* 合成が間に合わず、代替メロディ(楽器)を鳴らす際のコールバック。
|
|
314
|
+
* 引数には対象トラック、ノート情報、および発音すべき絶対時刻(AudioContextクロック秒)が渡されます。
|
|
315
|
+
*/
|
|
316
|
+
onFallbackPlayNote?: (track: StreamVoiceTrack, note: StreamVoiceNote, t0: number) => void;
|
|
317
|
+
/**
|
|
318
|
+
* 遅延判定の閾値(秒)。発音予定時刻よりこの秒数前に合成完了していない場合、
|
|
319
|
+
* 遅延するとみなし代替メロディに切り替えます。
|
|
320
|
+
* 省略時は 0.15 秒(150ms)です。
|
|
321
|
+
*/
|
|
322
|
+
fallbackThresholdSec?: number;
|
|
312
323
|
};
|
|
313
324
|
/**
|
|
314
325
|
* 歌唱モデルをまとめて管理し、koeデモ式の「先読みストリーミング合成」で歌わせる高レベルヘルパ。
|
package/dist/index.d.ts
CHANGED
|
@@ -309,6 +309,17 @@ type StreamPlaybackOptions = {
|
|
|
309
309
|
* 引数には遅れたノート情報と遅延秒数が渡されます。
|
|
310
310
|
*/
|
|
311
311
|
onLateSkip?: (note: StreamVoiceNote, delay: number) => void;
|
|
312
|
+
/**
|
|
313
|
+
* 合成が間に合わず、代替メロディ(楽器)を鳴らす際のコールバック。
|
|
314
|
+
* 引数には対象トラック、ノート情報、および発音すべき絶対時刻(AudioContextクロック秒)が渡されます。
|
|
315
|
+
*/
|
|
316
|
+
onFallbackPlayNote?: (track: StreamVoiceTrack, note: StreamVoiceNote, t0: number) => void;
|
|
317
|
+
/**
|
|
318
|
+
* 遅延判定の閾値(秒)。発音予定時刻よりこの秒数前に合成完了していない場合、
|
|
319
|
+
* 遅延するとみなし代替メロディに切り替えます。
|
|
320
|
+
* 省略時は 0.15 秒(150ms)です。
|
|
321
|
+
*/
|
|
322
|
+
fallbackThresholdSec?: number;
|
|
312
323
|
};
|
|
313
324
|
/**
|
|
314
325
|
* 歌唱モデルをまとめて管理し、koeデモ式の「先読みストリーミング合成」で歌わせる高レベルヘルパ。
|
package/dist/index.js
CHANGED
|
@@ -2826,13 +2826,44 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2826
2826
|
if (model.renderToCache && model.scheduleCached) {
|
|
2827
2827
|
const renderToCache = model.renderToCache;
|
|
2828
2828
|
const scheduleCached = model.scheduleCached;
|
|
2829
|
+
const threshold = opts?.fallbackThresholdSec ?? 0.15;
|
|
2829
2830
|
void (async () => {
|
|
2830
|
-
|
|
2831
|
+
let isTimeout = false;
|
|
2832
|
+
let timeoutId;
|
|
2833
|
+
const renderPromise = renderToCache(
|
|
2831
2834
|
note.syllable,
|
|
2832
2835
|
prevVowel,
|
|
2833
2836
|
note.pitch,
|
|
2834
2837
|
note.durationSec * 1e3
|
|
2838
|
+
).then((key2) => {
|
|
2839
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2840
|
+
return { key: key2, type: "success" };
|
|
2841
|
+
});
|
|
2842
|
+
const timeoutLimitTime = t0 - threshold;
|
|
2843
|
+
const timeoutDelayMs = (timeoutLimitTime - ctx.currentTime) * 1e3;
|
|
2844
|
+
const timeoutPromise = new Promise(
|
|
2845
|
+
(resolve) => {
|
|
2846
|
+
if (timeoutDelayMs <= 0) {
|
|
2847
|
+
isTimeout = true;
|
|
2848
|
+
resolve({ key: null, type: "timeout" });
|
|
2849
|
+
} else {
|
|
2850
|
+
timeoutId = setTimeout(() => {
|
|
2851
|
+
isTimeout = true;
|
|
2852
|
+
resolve({ key: null, type: "timeout" });
|
|
2853
|
+
}, timeoutDelayMs);
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2835
2856
|
);
|
|
2857
|
+
const result = await Promise.race([renderPromise, timeoutPromise]);
|
|
2858
|
+
if (session !== streamSession) return;
|
|
2859
|
+
if (result.type === "timeout") {
|
|
2860
|
+
console.warn(
|
|
2861
|
+
`[dtm] Synthesizer expected late skip: ${note.syllable.kana} at ${note.startSec}s. Fallback to instrument.`
|
|
2862
|
+
);
|
|
2863
|
+
opts?.onFallbackPlayNote?.(track, note, t0);
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
const key = result.key;
|
|
2836
2867
|
if (session !== streamSession) return;
|
|
2837
2868
|
if (key) {
|
|
2838
2869
|
const delay = ctx.currentTime - t0;
|
|
@@ -2840,9 +2871,10 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2840
2871
|
scheduleCached(key, t0, peak, track.pan);
|
|
2841
2872
|
} else {
|
|
2842
2873
|
console.warn(
|
|
2843
|
-
`[dtm] Synthesizer late skip: ${note.syllable.kana} at ${note.startSec}s (delayed by ${delay.toFixed(3)}s)`
|
|
2874
|
+
`[dtm] Synthesizer late skip (safety): ${note.syllable.kana} at ${note.startSec}s (delayed by ${delay.toFixed(3)}s)`
|
|
2844
2875
|
);
|
|
2845
2876
|
opts?.onLateSkip?.(note, delay);
|
|
2877
|
+
opts?.onFallbackPlayNote?.(track, note, t0);
|
|
2846
2878
|
}
|
|
2847
2879
|
}
|
|
2848
2880
|
})();
|
|
@@ -7130,10 +7162,31 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
7130
7162
|
if (streaming && !skipSinging) {
|
|
7131
7163
|
ensureVoices().startStream(tracks, seq.getStartTime(), {
|
|
7132
7164
|
isAudible: (t) => !mutedTracks.has(Number(t.id)),
|
|
7133
|
-
|
|
7165
|
+
onFallbackPlayNote: (track, note, t0) => {
|
|
7134
7166
|
showPlayerMessage(
|
|
7135
7167
|
"\u97F3\u58F0\u5408\u6210\u304C\u9593\u306B\u5408\u308F\u306A\u3044\u305F\u3081\u3001\u4E00\u90E8\u306E\u767A\u97F3\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F"
|
|
7136
7168
|
);
|
|
7169
|
+
const trackIdx = Number(track.id);
|
|
7170
|
+
if (mutedTracks.has(trackIdx)) return;
|
|
7171
|
+
const lt = lyricTracks.get(trackIdx);
|
|
7172
|
+
if (!lt) return;
|
|
7173
|
+
const relativeWhen = t0 - ensureCtx().currentTime;
|
|
7174
|
+
if (relativeWhen > 0) {
|
|
7175
|
+
const vol = (lt.volume ?? DEFAULT_VOCAL_VOLUME) / 100 * (trackVolume / 100);
|
|
7176
|
+
const e = {
|
|
7177
|
+
trackId: track.id ?? "",
|
|
7178
|
+
pitch: note.pitch,
|
|
7179
|
+
velocity: 100,
|
|
7180
|
+
volume: vol,
|
|
7181
|
+
when: relativeWhen,
|
|
7182
|
+
duration: note.durationSec,
|
|
7183
|
+
pan: track.pan
|
|
7184
|
+
};
|
|
7185
|
+
options.onPlayNote?.(e);
|
|
7186
|
+
if (useSynth) ensureSynth().playNote(e);
|
|
7187
|
+
}
|
|
7188
|
+
},
|
|
7189
|
+
onLateSkip: () => {
|
|
7137
7190
|
}
|
|
7138
7191
|
});
|
|
7139
7192
|
}
|
|
@@ -8143,7 +8196,27 @@ var mountDAW = (target, options = {}) => {
|
|
|
8143
8196
|
sequencer.start(fromStep);
|
|
8144
8197
|
if (streaming && voices) {
|
|
8145
8198
|
voices.startStream(streamTracks, sequencer.getStartTime(), {
|
|
8146
|
-
isAudible: (t) => !isSolo || t.id === activeTrackId
|
|
8199
|
+
isAudible: (t) => !isSolo || t.id === activeTrackId,
|
|
8200
|
+
onFallbackPlayNote: (track, note, t0) => {
|
|
8201
|
+
if (isSolo && track.id !== activeTrackId) return;
|
|
8202
|
+
const relativeWhen = t0 - getAudioTime();
|
|
8203
|
+
if (relativeWhen > 0) {
|
|
8204
|
+
const trackState = trackStates.find(
|
|
8205
|
+
(t) => t.config.id === track.id
|
|
8206
|
+
);
|
|
8207
|
+
const trackVol = trackState ? trackState.volume : 80;
|
|
8208
|
+
const volume = trackVol / 100 * (masterVolume / 100);
|
|
8209
|
+
options.onPlayNote?.({
|
|
8210
|
+
trackId: track.id ?? "",
|
|
8211
|
+
pitch: note.pitch,
|
|
8212
|
+
velocity: 100,
|
|
8213
|
+
volume,
|
|
8214
|
+
when: relativeWhen,
|
|
8215
|
+
duration: note.durationSec,
|
|
8216
|
+
pan: track.pan
|
|
8217
|
+
});
|
|
8218
|
+
}
|
|
8219
|
+
}
|
|
8147
8220
|
});
|
|
8148
8221
|
}
|
|
8149
8222
|
updateTransport();
|
package/dist/index.mjs
CHANGED
|
@@ -2718,13 +2718,44 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2718
2718
|
if (model.renderToCache && model.scheduleCached) {
|
|
2719
2719
|
const renderToCache = model.renderToCache;
|
|
2720
2720
|
const scheduleCached = model.scheduleCached;
|
|
2721
|
+
const threshold = opts?.fallbackThresholdSec ?? 0.15;
|
|
2721
2722
|
void (async () => {
|
|
2722
|
-
|
|
2723
|
+
let isTimeout = false;
|
|
2724
|
+
let timeoutId;
|
|
2725
|
+
const renderPromise = renderToCache(
|
|
2723
2726
|
note.syllable,
|
|
2724
2727
|
prevVowel,
|
|
2725
2728
|
note.pitch,
|
|
2726
2729
|
note.durationSec * 1e3
|
|
2730
|
+
).then((key2) => {
|
|
2731
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2732
|
+
return { key: key2, type: "success" };
|
|
2733
|
+
});
|
|
2734
|
+
const timeoutLimitTime = t0 - threshold;
|
|
2735
|
+
const timeoutDelayMs = (timeoutLimitTime - ctx.currentTime) * 1e3;
|
|
2736
|
+
const timeoutPromise = new Promise(
|
|
2737
|
+
(resolve) => {
|
|
2738
|
+
if (timeoutDelayMs <= 0) {
|
|
2739
|
+
isTimeout = true;
|
|
2740
|
+
resolve({ key: null, type: "timeout" });
|
|
2741
|
+
} else {
|
|
2742
|
+
timeoutId = setTimeout(() => {
|
|
2743
|
+
isTimeout = true;
|
|
2744
|
+
resolve({ key: null, type: "timeout" });
|
|
2745
|
+
}, timeoutDelayMs);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2727
2748
|
);
|
|
2749
|
+
const result = await Promise.race([renderPromise, timeoutPromise]);
|
|
2750
|
+
if (session !== streamSession) return;
|
|
2751
|
+
if (result.type === "timeout") {
|
|
2752
|
+
console.warn(
|
|
2753
|
+
`[dtm] Synthesizer expected late skip: ${note.syllable.kana} at ${note.startSec}s. Fallback to instrument.`
|
|
2754
|
+
);
|
|
2755
|
+
opts?.onFallbackPlayNote?.(track, note, t0);
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
const key = result.key;
|
|
2728
2759
|
if (session !== streamSession) return;
|
|
2729
2760
|
if (key) {
|
|
2730
2761
|
const delay = ctx.currentTime - t0;
|
|
@@ -2732,9 +2763,10 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2732
2763
|
scheduleCached(key, t0, peak, track.pan);
|
|
2733
2764
|
} else {
|
|
2734
2765
|
console.warn(
|
|
2735
|
-
`[dtm] Synthesizer late skip: ${note.syllable.kana} at ${note.startSec}s (delayed by ${delay.toFixed(3)}s)`
|
|
2766
|
+
`[dtm] Synthesizer late skip (safety): ${note.syllable.kana} at ${note.startSec}s (delayed by ${delay.toFixed(3)}s)`
|
|
2736
2767
|
);
|
|
2737
2768
|
opts?.onLateSkip?.(note, delay);
|
|
2769
|
+
opts?.onFallbackPlayNote?.(track, note, t0);
|
|
2738
2770
|
}
|
|
2739
2771
|
}
|
|
2740
2772
|
})();
|
|
@@ -7022,10 +7054,31 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
7022
7054
|
if (streaming && !skipSinging) {
|
|
7023
7055
|
ensureVoices().startStream(tracks, seq.getStartTime(), {
|
|
7024
7056
|
isAudible: (t) => !mutedTracks.has(Number(t.id)),
|
|
7025
|
-
|
|
7057
|
+
onFallbackPlayNote: (track, note, t0) => {
|
|
7026
7058
|
showPlayerMessage(
|
|
7027
7059
|
"\u97F3\u58F0\u5408\u6210\u304C\u9593\u306B\u5408\u308F\u306A\u3044\u305F\u3081\u3001\u4E00\u90E8\u306E\u767A\u97F3\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F"
|
|
7028
7060
|
);
|
|
7061
|
+
const trackIdx = Number(track.id);
|
|
7062
|
+
if (mutedTracks.has(trackIdx)) return;
|
|
7063
|
+
const lt = lyricTracks.get(trackIdx);
|
|
7064
|
+
if (!lt) return;
|
|
7065
|
+
const relativeWhen = t0 - ensureCtx().currentTime;
|
|
7066
|
+
if (relativeWhen > 0) {
|
|
7067
|
+
const vol = (lt.volume ?? DEFAULT_VOCAL_VOLUME) / 100 * (trackVolume / 100);
|
|
7068
|
+
const e = {
|
|
7069
|
+
trackId: track.id ?? "",
|
|
7070
|
+
pitch: note.pitch,
|
|
7071
|
+
velocity: 100,
|
|
7072
|
+
volume: vol,
|
|
7073
|
+
when: relativeWhen,
|
|
7074
|
+
duration: note.durationSec,
|
|
7075
|
+
pan: track.pan
|
|
7076
|
+
};
|
|
7077
|
+
options.onPlayNote?.(e);
|
|
7078
|
+
if (useSynth) ensureSynth().playNote(e);
|
|
7079
|
+
}
|
|
7080
|
+
},
|
|
7081
|
+
onLateSkip: () => {
|
|
7029
7082
|
}
|
|
7030
7083
|
});
|
|
7031
7084
|
}
|
|
@@ -8035,7 +8088,27 @@ var mountDAW = (target, options = {}) => {
|
|
|
8035
8088
|
sequencer.start(fromStep);
|
|
8036
8089
|
if (streaming && voices) {
|
|
8037
8090
|
voices.startStream(streamTracks, sequencer.getStartTime(), {
|
|
8038
|
-
isAudible: (t) => !isSolo || t.id === activeTrackId
|
|
8091
|
+
isAudible: (t) => !isSolo || t.id === activeTrackId,
|
|
8092
|
+
onFallbackPlayNote: (track, note, t0) => {
|
|
8093
|
+
if (isSolo && track.id !== activeTrackId) return;
|
|
8094
|
+
const relativeWhen = t0 - getAudioTime();
|
|
8095
|
+
if (relativeWhen > 0) {
|
|
8096
|
+
const trackState = trackStates.find(
|
|
8097
|
+
(t) => t.config.id === track.id
|
|
8098
|
+
);
|
|
8099
|
+
const trackVol = trackState ? trackState.volume : 80;
|
|
8100
|
+
const volume = trackVol / 100 * (masterVolume / 100);
|
|
8101
|
+
options.onPlayNote?.({
|
|
8102
|
+
trackId: track.id ?? "",
|
|
8103
|
+
pitch: note.pitch,
|
|
8104
|
+
velocity: 100,
|
|
8105
|
+
volume,
|
|
8106
|
+
when: relativeWhen,
|
|
8107
|
+
duration: note.durationSec,
|
|
8108
|
+
pan: track.pan
|
|
8109
|
+
});
|
|
8110
|
+
}
|
|
8111
|
+
}
|
|
8039
8112
|
});
|
|
8040
8113
|
}
|
|
8041
8114
|
updateTransport();
|
package/package.json
CHANGED