@onjmin/dtm 0.1.5 → 0.1.8
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 +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +40 -32
- package/dist/index.mjs +40 -32
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -263,6 +263,11 @@ type StreamVoiceNote = {
|
|
|
263
263
|
};
|
|
264
264
|
/** ストリーミング再生する歌詞トラック1本。 */
|
|
265
265
|
type StreamVoiceTrack = {
|
|
266
|
+
/**
|
|
267
|
+
* 呼び出し側がソロ/ミュート判定に使う識別子(演奏トラックの config.id 等)。
|
|
268
|
+
* {@link StreamPlaybackOptions.isAudible} で参照する。省略時は常に可聴。
|
|
269
|
+
*/
|
|
270
|
+
id?: string;
|
|
266
271
|
/** 歌唱モデル名(koe音源キーワード or "klatt")。 */
|
|
267
272
|
model: string;
|
|
268
273
|
/** 最終ゲイン(声量×マスタ等を適用済み。1=等倍)。 */
|
|
@@ -272,6 +277,17 @@ type StreamVoiceTrack = {
|
|
|
272
277
|
/** 発音順(startSec昇順)の歌唱ノート列。 */
|
|
273
278
|
notes: StreamVoiceNote[];
|
|
274
279
|
};
|
|
280
|
+
/** {@link SingingVoices.startStream} の任意オプション。 */
|
|
281
|
+
type StreamPlaybackOptions = {
|
|
282
|
+
/**
|
|
283
|
+
* そのトラックを今この瞬間に発音してよいか(ソロ/ミュート判定)。
|
|
284
|
+
* 各ノートを合成・スケジュールする直前にライブで評価するため、再生中に
|
|
285
|
+
* ソロを切り替えると先読み地平(最大 {@link STREAM_LOOKAHEAD_SEC} 秒)以降のノートへ反映される。
|
|
286
|
+
* 既にスケジュール済みのノートは鳴り切る(楽器側のミュート挙動と同じ)。
|
|
287
|
+
* 省略時は全トラック可聴。
|
|
288
|
+
*/
|
|
289
|
+
isAudible?: (track: StreamVoiceTrack) => boolean;
|
|
290
|
+
};
|
|
275
291
|
/**
|
|
276
292
|
* 歌唱モデルをまとめて管理し、koeデモ式の「先読みストリーミング合成」で歌わせる高レベルヘルパ。
|
|
277
293
|
*
|
|
@@ -298,7 +314,7 @@ type SingingVoices = {
|
|
|
298
314
|
* AudioContextクロック秒(=シーケンサの開始時刻と一致させること)。
|
|
299
315
|
* 即座に return し、合成は裏で先回り進行する。
|
|
300
316
|
*/
|
|
301
|
-
startStream: (tracks: StreamVoiceTrack[], anchorTime: number) => void;
|
|
317
|
+
startStream: (tracks: StreamVoiceTrack[], anchorTime: number, opts?: StreamPlaybackOptions) => void;
|
|
302
318
|
/** 進行中のストリームを中断し、スケジュール済みの発音をすべて止める(停止・一時停止・シーク)。 */
|
|
303
319
|
stopStream: () => void;
|
|
304
320
|
/** ストリーム停止+各モデルの内部状態を初期化する。 */
|
|
@@ -1266,4 +1282,4 @@ declare const DAW_CSS = "\n@font-face {\n font-family: 'k8x12';\n src: url('ht
|
|
|
1266
1282
|
*/
|
|
1267
1283
|
declare const injectStyles: (doc?: Document) => void;
|
|
1268
1284
|
|
|
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, type MidiExtraction, type MidiNotePlacement, type MidiTrackAnalysis, type MmlMeta, type MmlPlayerInstance, type MmlPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, PITCH_MAP, PREWARM_NOTES, type ParseChordFn, type ParseChordsFn, 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 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 };
|
|
1285
|
+
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, type MidiExtraction, type MidiNotePlacement, type MidiTrackAnalysis, type MmlMeta, type MmlPlayerInstance, type MmlPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, PITCH_MAP, PREWARM_NOTES, type ParseChordFn, type ParseChordsFn, 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
|
@@ -263,6 +263,11 @@ type StreamVoiceNote = {
|
|
|
263
263
|
};
|
|
264
264
|
/** ストリーミング再生する歌詞トラック1本。 */
|
|
265
265
|
type StreamVoiceTrack = {
|
|
266
|
+
/**
|
|
267
|
+
* 呼び出し側がソロ/ミュート判定に使う識別子(演奏トラックの config.id 等)。
|
|
268
|
+
* {@link StreamPlaybackOptions.isAudible} で参照する。省略時は常に可聴。
|
|
269
|
+
*/
|
|
270
|
+
id?: string;
|
|
266
271
|
/** 歌唱モデル名(koe音源キーワード or "klatt")。 */
|
|
267
272
|
model: string;
|
|
268
273
|
/** 最終ゲイン(声量×マスタ等を適用済み。1=等倍)。 */
|
|
@@ -272,6 +277,17 @@ type StreamVoiceTrack = {
|
|
|
272
277
|
/** 発音順(startSec昇順)の歌唱ノート列。 */
|
|
273
278
|
notes: StreamVoiceNote[];
|
|
274
279
|
};
|
|
280
|
+
/** {@link SingingVoices.startStream} の任意オプション。 */
|
|
281
|
+
type StreamPlaybackOptions = {
|
|
282
|
+
/**
|
|
283
|
+
* そのトラックを今この瞬間に発音してよいか(ソロ/ミュート判定)。
|
|
284
|
+
* 各ノートを合成・スケジュールする直前にライブで評価するため、再生中に
|
|
285
|
+
* ソロを切り替えると先読み地平(最大 {@link STREAM_LOOKAHEAD_SEC} 秒)以降のノートへ反映される。
|
|
286
|
+
* 既にスケジュール済みのノートは鳴り切る(楽器側のミュート挙動と同じ)。
|
|
287
|
+
* 省略時は全トラック可聴。
|
|
288
|
+
*/
|
|
289
|
+
isAudible?: (track: StreamVoiceTrack) => boolean;
|
|
290
|
+
};
|
|
275
291
|
/**
|
|
276
292
|
* 歌唱モデルをまとめて管理し、koeデモ式の「先読みストリーミング合成」で歌わせる高レベルヘルパ。
|
|
277
293
|
*
|
|
@@ -298,7 +314,7 @@ type SingingVoices = {
|
|
|
298
314
|
* AudioContextクロック秒(=シーケンサの開始時刻と一致させること)。
|
|
299
315
|
* 即座に return し、合成は裏で先回り進行する。
|
|
300
316
|
*/
|
|
301
|
-
startStream: (tracks: StreamVoiceTrack[], anchorTime: number) => void;
|
|
317
|
+
startStream: (tracks: StreamVoiceTrack[], anchorTime: number, opts?: StreamPlaybackOptions) => void;
|
|
302
318
|
/** 進行中のストリームを中断し、スケジュール済みの発音をすべて止める(停止・一時停止・シーク)。 */
|
|
303
319
|
stopStream: () => void;
|
|
304
320
|
/** ストリーム停止+各モデルの内部状態を初期化する。 */
|
|
@@ -1266,4 +1282,4 @@ declare const DAW_CSS = "\n@font-face {\n font-family: 'k8x12';\n src: url('ht
|
|
|
1266
1282
|
*/
|
|
1267
1283
|
declare const injectStyles: (doc?: Document) => void;
|
|
1268
1284
|
|
|
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, type MidiExtraction, type MidiNotePlacement, type MidiTrackAnalysis, type MmlMeta, type MmlPlayerInstance, type MmlPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, PITCH_MAP, PREWARM_NOTES, type ParseChordFn, type ParseChordsFn, 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 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 };
|
|
1285
|
+
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, type MidiExtraction, type MidiNotePlacement, type MidiTrackAnalysis, type MmlMeta, type MmlPlayerInstance, type MmlPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, PITCH_MAP, PREWARM_NOTES, type ParseChordFn, type ParseChordsFn, 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
|
@@ -1789,42 +1789,33 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
1789
1789
|
}
|
|
1790
1790
|
await Promise.all(promises);
|
|
1791
1791
|
};
|
|
1792
|
-
const startStream = (tracks, anchorTime) => {
|
|
1792
|
+
const startStream = (tracks, anchorTime, opts) => {
|
|
1793
1793
|
const session = ++streamSession;
|
|
1794
|
-
const
|
|
1795
|
-
for (const track of tracks) {
|
|
1794
|
+
const runTrack = async (track) => {
|
|
1796
1795
|
const model = loaded.get(track.model.toLowerCase());
|
|
1797
|
-
if (!model)
|
|
1796
|
+
if (!model) return;
|
|
1797
|
+
const items = [];
|
|
1798
1798
|
forEachSungNote(track, (note, prevVowel) => {
|
|
1799
|
-
items.push({
|
|
1800
|
-
model,
|
|
1801
|
-
note,
|
|
1802
|
-
prevVowel,
|
|
1803
|
-
volume: track.volume,
|
|
1804
|
-
pan: track.pan
|
|
1805
|
-
});
|
|
1799
|
+
items.push({ note, prevVowel });
|
|
1806
1800
|
});
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
void (async () => {
|
|
1810
|
-
for (const item of items) {
|
|
1801
|
+
const peak = Math.max(1e-4, track.volume);
|
|
1802
|
+
for (const { note, prevVowel } of items) {
|
|
1811
1803
|
if (session !== streamSession) return;
|
|
1812
|
-
while (
|
|
1804
|
+
while (note.startSec - (ctx.currentTime - anchorTime) > STREAM_LOOKAHEAD_SEC) {
|
|
1813
1805
|
await new Promise((resolve) => setTimeout(resolve, STREAM_POLL_MS));
|
|
1814
1806
|
if (session !== streamSession) return;
|
|
1815
1807
|
}
|
|
1816
|
-
|
|
1817
|
-
const
|
|
1818
|
-
const { model, note } = item;
|
|
1808
|
+
if (opts?.isAudible && !opts.isAudible(track)) continue;
|
|
1809
|
+
const t0 = anchorTime + note.startSec;
|
|
1819
1810
|
if (model.renderToCache && model.scheduleCached) {
|
|
1820
1811
|
const key = await model.renderToCache(
|
|
1821
1812
|
note.syllable,
|
|
1822
|
-
|
|
1813
|
+
prevVowel,
|
|
1823
1814
|
note.pitch,
|
|
1824
1815
|
note.durationSec * 1e3
|
|
1825
1816
|
);
|
|
1826
1817
|
if (session !== streamSession) return;
|
|
1827
|
-
if (key) model.scheduleCached(key, t0, peak,
|
|
1818
|
+
if (key) model.scheduleCached(key, t0, peak, track.pan);
|
|
1828
1819
|
} else {
|
|
1829
1820
|
const when = t0 - ctx.currentTime;
|
|
1830
1821
|
model(note.syllable, {
|
|
@@ -1834,12 +1825,13 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
1834
1825
|
volume: peak,
|
|
1835
1826
|
when,
|
|
1836
1827
|
duration: note.durationSec,
|
|
1837
|
-
pan:
|
|
1828
|
+
pan: track.pan
|
|
1838
1829
|
});
|
|
1830
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1839
1831
|
}
|
|
1840
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1841
1832
|
}
|
|
1842
|
-
}
|
|
1833
|
+
};
|
|
1834
|
+
for (const track of tracks) void runTrack(track);
|
|
1843
1835
|
};
|
|
1844
1836
|
const stopStream = () => {
|
|
1845
1837
|
streamSession++;
|
|
@@ -3379,13 +3371,10 @@ var createSequencer = (options) => {
|
|
|
3379
3371
|
}
|
|
3380
3372
|
while (nowIndex < timeline.length) {
|
|
3381
3373
|
const ev = timeline[nowIndex];
|
|
3382
|
-
if (soloId && ev.trackId !== soloId) {
|
|
3383
|
-
nowIndex++;
|
|
3384
|
-
continue;
|
|
3385
|
-
}
|
|
3386
3374
|
const _when = ev.when - time;
|
|
3387
3375
|
if (_when > PLAN_TIME) break;
|
|
3388
3376
|
nowIndex++;
|
|
3377
|
+
if (soloId && ev.trackId !== soloId) continue;
|
|
3389
3378
|
const velocityVolume = ev.velocity / 127;
|
|
3390
3379
|
const currentVolume = (trackVolumeMap.get(ev.trackId) ?? ev.volume * 100) / 100;
|
|
3391
3380
|
options.onPlayNote({
|
|
@@ -5080,6 +5069,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
5080
5069
|
});
|
|
5081
5070
|
}
|
|
5082
5071
|
return {
|
|
5072
|
+
id: trackState?.config.id,
|
|
5083
5073
|
model: lt.model,
|
|
5084
5074
|
volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (masterVolume / 100),
|
|
5085
5075
|
pan: panToStereo(lt.pan ?? DEFAULT_PAN),
|
|
@@ -5110,7 +5100,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
5110
5100
|
playbackState = "playing";
|
|
5111
5101
|
sequencer.start(fromStep);
|
|
5112
5102
|
if (streaming && voices) {
|
|
5113
|
-
voices.startStream(streamTracks, sequencer.getStartTime()
|
|
5103
|
+
voices.startStream(streamTracks, sequencer.getStartTime(), {
|
|
5104
|
+
isAudible: (t) => !isSolo || t.id === activeTrackId
|
|
5105
|
+
});
|
|
5114
5106
|
}
|
|
5115
5107
|
updateTransport();
|
|
5116
5108
|
};
|
|
@@ -7145,8 +7137,10 @@ var createDtmStudio = async (options = {}) => {
|
|
|
7145
7137
|
...dawOverrides
|
|
7146
7138
|
};
|
|
7147
7139
|
const wantPresetUI = presetUI ?? features.presetUI;
|
|
7140
|
+
let select = null;
|
|
7148
7141
|
if (wantPresetUI) {
|
|
7149
|
-
|
|
7142
|
+
editorPresetSelects.get(target)?.remove();
|
|
7143
|
+
select = target.ownerDocument.createElement("select");
|
|
7150
7144
|
select.className = "dtm-studio-preset";
|
|
7151
7145
|
for (const [key, p] of Object.entries(INSTRUMENT_PRESETS)) {
|
|
7152
7146
|
const opt = target.ownerDocument.createElement("option");
|
|
@@ -7158,6 +7152,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
7158
7152
|
target.appendChild(select);
|
|
7159
7153
|
editorPresetSelects.set(target, select);
|
|
7160
7154
|
select.addEventListener("change", async () => {
|
|
7155
|
+
if (!select) return;
|
|
7161
7156
|
daw.setInstrument(select.value);
|
|
7162
7157
|
await loadPreset(select.value, trackIds);
|
|
7163
7158
|
});
|
|
@@ -7167,7 +7162,15 @@ var createDtmStudio = async (options = {}) => {
|
|
|
7167
7162
|
const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
|
|
7168
7163
|
daw.setInstrument(presetKey);
|
|
7169
7164
|
void loadPreset(presetKey, trackIds);
|
|
7170
|
-
|
|
7165
|
+
const destroy = () => {
|
|
7166
|
+
daw.destroy();
|
|
7167
|
+
select?.remove();
|
|
7168
|
+
if (editorPresetSelects.get(target) === select)
|
|
7169
|
+
editorPresetSelects.delete(target);
|
|
7170
|
+
const i = mountedEditors.indexOf(daw);
|
|
7171
|
+
if (i >= 0) mountedEditors.splice(i, 1);
|
|
7172
|
+
};
|
|
7173
|
+
return { ...daw, destroy };
|
|
7171
7174
|
};
|
|
7172
7175
|
const mountPlayer = (target, mml, opts = {}) => {
|
|
7173
7176
|
const meta = parseMML(mml, {}).meta ?? {};
|
|
@@ -7183,7 +7186,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
7183
7186
|
...opts
|
|
7184
7187
|
});
|
|
7185
7188
|
mountedPlayers.push(player);
|
|
7186
|
-
|
|
7189
|
+
const destroy = () => {
|
|
7190
|
+
player.destroy();
|
|
7191
|
+
const i = mountedPlayers.indexOf(player);
|
|
7192
|
+
if (i >= 0) mountedPlayers.splice(i, 1);
|
|
7193
|
+
};
|
|
7194
|
+
return { ...player, destroy };
|
|
7187
7195
|
};
|
|
7188
7196
|
const dispose = () => {
|
|
7189
7197
|
for (const p of mountedPlayers) p.destroy();
|
package/dist/index.mjs
CHANGED
|
@@ -1685,42 +1685,33 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
1685
1685
|
}
|
|
1686
1686
|
await Promise.all(promises);
|
|
1687
1687
|
};
|
|
1688
|
-
const startStream = (tracks, anchorTime) => {
|
|
1688
|
+
const startStream = (tracks, anchorTime, opts) => {
|
|
1689
1689
|
const session = ++streamSession;
|
|
1690
|
-
const
|
|
1691
|
-
for (const track of tracks) {
|
|
1690
|
+
const runTrack = async (track) => {
|
|
1692
1691
|
const model = loaded.get(track.model.toLowerCase());
|
|
1693
|
-
if (!model)
|
|
1692
|
+
if (!model) return;
|
|
1693
|
+
const items = [];
|
|
1694
1694
|
forEachSungNote(track, (note, prevVowel) => {
|
|
1695
|
-
items.push({
|
|
1696
|
-
model,
|
|
1697
|
-
note,
|
|
1698
|
-
prevVowel,
|
|
1699
|
-
volume: track.volume,
|
|
1700
|
-
pan: track.pan
|
|
1701
|
-
});
|
|
1695
|
+
items.push({ note, prevVowel });
|
|
1702
1696
|
});
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
void (async () => {
|
|
1706
|
-
for (const item of items) {
|
|
1697
|
+
const peak = Math.max(1e-4, track.volume);
|
|
1698
|
+
for (const { note, prevVowel } of items) {
|
|
1707
1699
|
if (session !== streamSession) return;
|
|
1708
|
-
while (
|
|
1700
|
+
while (note.startSec - (ctx.currentTime - anchorTime) > STREAM_LOOKAHEAD_SEC) {
|
|
1709
1701
|
await new Promise((resolve) => setTimeout(resolve, STREAM_POLL_MS));
|
|
1710
1702
|
if (session !== streamSession) return;
|
|
1711
1703
|
}
|
|
1712
|
-
|
|
1713
|
-
const
|
|
1714
|
-
const { model, note } = item;
|
|
1704
|
+
if (opts?.isAudible && !opts.isAudible(track)) continue;
|
|
1705
|
+
const t0 = anchorTime + note.startSec;
|
|
1715
1706
|
if (model.renderToCache && model.scheduleCached) {
|
|
1716
1707
|
const key = await model.renderToCache(
|
|
1717
1708
|
note.syllable,
|
|
1718
|
-
|
|
1709
|
+
prevVowel,
|
|
1719
1710
|
note.pitch,
|
|
1720
1711
|
note.durationSec * 1e3
|
|
1721
1712
|
);
|
|
1722
1713
|
if (session !== streamSession) return;
|
|
1723
|
-
if (key) model.scheduleCached(key, t0, peak,
|
|
1714
|
+
if (key) model.scheduleCached(key, t0, peak, track.pan);
|
|
1724
1715
|
} else {
|
|
1725
1716
|
const when = t0 - ctx.currentTime;
|
|
1726
1717
|
model(note.syllable, {
|
|
@@ -1730,12 +1721,13 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
1730
1721
|
volume: peak,
|
|
1731
1722
|
when,
|
|
1732
1723
|
duration: note.durationSec,
|
|
1733
|
-
pan:
|
|
1724
|
+
pan: track.pan
|
|
1734
1725
|
});
|
|
1726
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1735
1727
|
}
|
|
1736
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1737
1728
|
}
|
|
1738
|
-
}
|
|
1729
|
+
};
|
|
1730
|
+
for (const track of tracks) void runTrack(track);
|
|
1739
1731
|
};
|
|
1740
1732
|
const stopStream = () => {
|
|
1741
1733
|
streamSession++;
|
|
@@ -3275,13 +3267,10 @@ var createSequencer = (options) => {
|
|
|
3275
3267
|
}
|
|
3276
3268
|
while (nowIndex < timeline.length) {
|
|
3277
3269
|
const ev = timeline[nowIndex];
|
|
3278
|
-
if (soloId && ev.trackId !== soloId) {
|
|
3279
|
-
nowIndex++;
|
|
3280
|
-
continue;
|
|
3281
|
-
}
|
|
3282
3270
|
const _when = ev.when - time;
|
|
3283
3271
|
if (_when > PLAN_TIME) break;
|
|
3284
3272
|
nowIndex++;
|
|
3273
|
+
if (soloId && ev.trackId !== soloId) continue;
|
|
3285
3274
|
const velocityVolume = ev.velocity / 127;
|
|
3286
3275
|
const currentVolume = (trackVolumeMap.get(ev.trackId) ?? ev.volume * 100) / 100;
|
|
3287
3276
|
options.onPlayNote({
|
|
@@ -4976,6 +4965,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
4976
4965
|
});
|
|
4977
4966
|
}
|
|
4978
4967
|
return {
|
|
4968
|
+
id: trackState?.config.id,
|
|
4979
4969
|
model: lt.model,
|
|
4980
4970
|
volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (masterVolume / 100),
|
|
4981
4971
|
pan: panToStereo(lt.pan ?? DEFAULT_PAN),
|
|
@@ -5006,7 +4996,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
5006
4996
|
playbackState = "playing";
|
|
5007
4997
|
sequencer.start(fromStep);
|
|
5008
4998
|
if (streaming && voices) {
|
|
5009
|
-
voices.startStream(streamTracks, sequencer.getStartTime()
|
|
4999
|
+
voices.startStream(streamTracks, sequencer.getStartTime(), {
|
|
5000
|
+
isAudible: (t) => !isSolo || t.id === activeTrackId
|
|
5001
|
+
});
|
|
5010
5002
|
}
|
|
5011
5003
|
updateTransport();
|
|
5012
5004
|
};
|
|
@@ -7040,8 +7032,10 @@ var createDtmStudio = async (options = {}) => {
|
|
|
7040
7032
|
...dawOverrides
|
|
7041
7033
|
};
|
|
7042
7034
|
const wantPresetUI = presetUI ?? features.presetUI;
|
|
7035
|
+
let select = null;
|
|
7043
7036
|
if (wantPresetUI) {
|
|
7044
|
-
|
|
7037
|
+
editorPresetSelects.get(target)?.remove();
|
|
7038
|
+
select = target.ownerDocument.createElement("select");
|
|
7045
7039
|
select.className = "dtm-studio-preset";
|
|
7046
7040
|
for (const [key, p] of Object.entries(INSTRUMENT_PRESETS)) {
|
|
7047
7041
|
const opt = target.ownerDocument.createElement("option");
|
|
@@ -7053,6 +7047,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
7053
7047
|
target.appendChild(select);
|
|
7054
7048
|
editorPresetSelects.set(target, select);
|
|
7055
7049
|
select.addEventListener("change", async () => {
|
|
7050
|
+
if (!select) return;
|
|
7056
7051
|
daw.setInstrument(select.value);
|
|
7057
7052
|
await loadPreset(select.value, trackIds);
|
|
7058
7053
|
});
|
|
@@ -7062,7 +7057,15 @@ var createDtmStudio = async (options = {}) => {
|
|
|
7062
7057
|
const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
|
|
7063
7058
|
daw.setInstrument(presetKey);
|
|
7064
7059
|
void loadPreset(presetKey, trackIds);
|
|
7065
|
-
|
|
7060
|
+
const destroy = () => {
|
|
7061
|
+
daw.destroy();
|
|
7062
|
+
select?.remove();
|
|
7063
|
+
if (editorPresetSelects.get(target) === select)
|
|
7064
|
+
editorPresetSelects.delete(target);
|
|
7065
|
+
const i = mountedEditors.indexOf(daw);
|
|
7066
|
+
if (i >= 0) mountedEditors.splice(i, 1);
|
|
7067
|
+
};
|
|
7068
|
+
return { ...daw, destroy };
|
|
7066
7069
|
};
|
|
7067
7070
|
const mountPlayer = (target, mml, opts = {}) => {
|
|
7068
7071
|
const meta = parseMML(mml, {}).meta ?? {};
|
|
@@ -7078,7 +7081,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
7078
7081
|
...opts
|
|
7079
7082
|
});
|
|
7080
7083
|
mountedPlayers.push(player);
|
|
7081
|
-
|
|
7084
|
+
const destroy = () => {
|
|
7085
|
+
player.destroy();
|
|
7086
|
+
const i = mountedPlayers.indexOf(player);
|
|
7087
|
+
if (i >= 0) mountedPlayers.splice(i, 1);
|
|
7088
|
+
};
|
|
7089
|
+
return { ...player, destroy };
|
|
7082
7090
|
};
|
|
7083
7091
|
const dispose = () => {
|
|
7084
7092
|
for (const p of mountedPlayers) p.destroy();
|
package/package.json
CHANGED