@onjmin/dtm 0.1.96 → 0.1.99
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 +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +157 -10
- package/dist/index.mjs +155 -10
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -942,6 +942,12 @@ type DawOptions = {
|
|
|
942
942
|
onPlayDrum?: (e: PlayDrumEvent) => void;
|
|
943
943
|
/** 初回ユーザー操作時に呼ばれる(AudioContextのresume等に使う) */
|
|
944
944
|
onResumeAudio?: () => void | Promise<void>;
|
|
945
|
+
/**
|
|
946
|
+
* 「MIDI / MML 出力」パネルの「WAV書き出し」ボタン押下時に呼ばれる。
|
|
947
|
+
* 未指定ならボタン自体を表示しない(ライブラリは音を出さないため、書き出し処理は
|
|
948
|
+
* AudioContextを持つ利用側 = createDtmStudio 等が担う)。
|
|
949
|
+
*/
|
|
950
|
+
onExportWav?: () => void | Promise<void>;
|
|
945
951
|
/** 再生の基準クロック秒。既定 performance.now()/1000。利用側は audioCtx.currentTime を返す */
|
|
946
952
|
getAudioTime?: () => number;
|
|
947
953
|
/**
|
|
@@ -2295,6 +2301,15 @@ type DtmStudio = {
|
|
|
2295
2301
|
* MediaRecorder のストリーム合成(canvas + audio)に使えます。
|
|
2296
2302
|
*/
|
|
2297
2303
|
getAudioStreamTrack: () => MediaStreamTrack;
|
|
2304
|
+
/**
|
|
2305
|
+
* WAV書き出し用の録音を開始する(マスタバス最終段=リバーブ/ディレイ/コンプ/
|
|
2306
|
+
* リミッター/フェード適用後のPCMをそのままタップする)。
|
|
2307
|
+
* 呼び出し側で再生(daw.play() 等)を開始し、曲が終わったら stopWavRecording を呼ぶ想定。
|
|
2308
|
+
* 実際の再生と同じ経路を録るため等速(曲の長さ分だけ実時間がかかる)。
|
|
2309
|
+
*/
|
|
2310
|
+
startWavRecording: () => void;
|
|
2311
|
+
/** startWavRecording で開始した録音を止め、16bit PCM WAVのBlobを返す。 */
|
|
2312
|
+
stopWavRecording: () => Promise<Blob>;
|
|
2298
2313
|
/** 歌声合成ヘルパ(klatt + koe音源)。 */
|
|
2299
2314
|
singingVoices: SingingVoices;
|
|
2300
2315
|
/** 編集UI(mountDAW)を音・歌声込みでマウントする。 */
|
|
@@ -2425,4 +2440,17 @@ declare const createSynth: (ctx: AudioContext, destination?: AudioNode, tone?: S
|
|
|
2425
2440
|
|
|
2426
2441
|
declare const VOICE_IMAGES: Record<string, string>;
|
|
2427
2442
|
|
|
2428
|
-
|
|
2443
|
+
/**
|
|
2444
|
+
* PCM(Float32、チャンネル別)から 16bit PCM WAV の Blob を作る、依存なしの小さなエンコーダ。
|
|
2445
|
+
* 追加ライブラリなしで完結させたいので RIFF/WAVE ヘッダを手書きしている。
|
|
2446
|
+
*/
|
|
2447
|
+
/**
|
|
2448
|
+
* チャンネルごとの Float32Array(同じ長さ)から 16bit PCM WAV の Blob を生成する。
|
|
2449
|
+
* @param channels 各チャンネルのサンプル列(例: [left, right])
|
|
2450
|
+
* @param sampleRate サンプリングレート(Hz)
|
|
2451
|
+
*/
|
|
2452
|
+
declare const encodeWavPCM16: (channels: Float32Array[], sampleRate: number) => Blob;
|
|
2453
|
+
/** Float32Array のチャンクを1本へ連結する。 */
|
|
2454
|
+
declare const concatFloat32: (chunks: Float32Array[]) => Float32Array;
|
|
2455
|
+
|
|
2456
|
+
export { type AddNoteOptions, type AnyDrumPattern, type ApplyChordOptions, type ChordPatternType, type ChordPlacement, type ChordPlayerInstance, type ConsumedSyllable, type CoreEventHandlers, type CustomVocalDef, 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 DawMode, type DawOptions, type DawViewState, type DrumPattern, type DrumPatternDef, type DtmStudio, type DtmStudioEngines, type DtmStudioOptions, type ExportMidiOptions, type FadeScheduleParams, GM_INSTRUMENT_NAMES, INSTRUMENT_PRESETS, type InstrumentPreset, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KoeVoiceOptions, LinkedList, type LoopConfig, type LoopPoint, type LyricSyllable, type LyricSyncData, type LyricTrack, type LyricsConductor, MAX_VOCAL_VOLUME, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MidiExtraction, type MidiNotePlacement, MidiSearchClient, type MidiSearchConfig, type MidiTrackAnalysis, type MmlMeta, type MmlPlayback, type MmlPlayerInstance, type MmlPlayerOptions, type ModeSwitchInstance, type ModeSwitchOptions, type MountChordPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, type NoteData, type NoteRemove, type OctaveUnisonMode, PITCH_MAP, PREWARM_NOTES, type ParseMMLOptions, type ParseMidiFn, type ParsedMML, type PianoRollInstance, type PianoRollOptions, type PicotuneSearchParams, type PicotuneSong, type PitchToken, type PlayChordsOptions, type PlayDrumEvent, type PlayMmlOptions, type PlayNoteEvent, type PlayNoteOptions, type PlayPlacementsOptions, type PlaySingingMmlOptions, type PlaybackCue, type PlaybackState, type PresetSelectInstance, type PresetSelectOptions, type PreviewSoundCallback, type RenderConfig, type Sequencer, type SequencerOptions, type SequencerTrack, type SingingVoices, type SingingVoicesOptions, type StreamPlaybackOptions, type StreamVoiceNote, type StreamVoiceTrack, type Synth, TRACKS_ADVANCED, TRACKS_SIMPLE, type ToolMode, type TrackConfig, VIBRATO_MIN_SEC, VOICE_IMAGES, VOICE_IMAGE_KEY, type VoiceExpression, type VoiceModel, type VoiceRegistry, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, buildChordPlacements, buildDrumPatternJson, buildNameToKeyMapping, collectPitchTokens, concatFloat32, createAudioContext, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createSequencer, createSingingVoices, createSynth, createVoiceRegistry, decodeMml, decomposeToMonophonic, drawGrid, drawHeader, drawKeyboard, drawNotes, drawSelectedNotes, drawSelectionRect, encodeMml, encodeWavPCM16, exportMIDI, extractDrumPatternFromNotes, extractMidiDrumPattern, extractMidiPlacements, extractMidiPlacementsByTrack, formatMmlMeta, freqFromPitch, generateRandomPattern, getDrawOffset, getDrumPatternKeys, getGridCanvas, getGridContext, getGridPosition, getHeaderCanvas, getMidiBPM, getRenderConfig, getXY, icon, init, injectStyles, isChordHeavyTrack, isPlausibleMidiTranscription, isValidHttpUrl, koeUrl, mountChordPlayer, mountDAW, mountMmlPlayer, normalizeDrumPatterns, normalizeLyrics, onClick, panToStereo, parseCustomVocals, parseLyrics, parseMML, parseMmlMeta, playChords, playMML, playNote, playPlacements, playSingingMML, resolveDrumPattern, resolveLoopPoint, setBackgroundActive, setDrawOffset, shiftNotes, showLoadingOverlay, stripCustomVocals, stripLyrics, stripMmlMeta, transposeNotes, vocalVolumeToGain };
|
package/dist/index.d.ts
CHANGED
|
@@ -942,6 +942,12 @@ type DawOptions = {
|
|
|
942
942
|
onPlayDrum?: (e: PlayDrumEvent) => void;
|
|
943
943
|
/** 初回ユーザー操作時に呼ばれる(AudioContextのresume等に使う) */
|
|
944
944
|
onResumeAudio?: () => void | Promise<void>;
|
|
945
|
+
/**
|
|
946
|
+
* 「MIDI / MML 出力」パネルの「WAV書き出し」ボタン押下時に呼ばれる。
|
|
947
|
+
* 未指定ならボタン自体を表示しない(ライブラリは音を出さないため、書き出し処理は
|
|
948
|
+
* AudioContextを持つ利用側 = createDtmStudio 等が担う)。
|
|
949
|
+
*/
|
|
950
|
+
onExportWav?: () => void | Promise<void>;
|
|
945
951
|
/** 再生の基準クロック秒。既定 performance.now()/1000。利用側は audioCtx.currentTime を返す */
|
|
946
952
|
getAudioTime?: () => number;
|
|
947
953
|
/**
|
|
@@ -2295,6 +2301,15 @@ type DtmStudio = {
|
|
|
2295
2301
|
* MediaRecorder のストリーム合成(canvas + audio)に使えます。
|
|
2296
2302
|
*/
|
|
2297
2303
|
getAudioStreamTrack: () => MediaStreamTrack;
|
|
2304
|
+
/**
|
|
2305
|
+
* WAV書き出し用の録音を開始する(マスタバス最終段=リバーブ/ディレイ/コンプ/
|
|
2306
|
+
* リミッター/フェード適用後のPCMをそのままタップする)。
|
|
2307
|
+
* 呼び出し側で再生(daw.play() 等)を開始し、曲が終わったら stopWavRecording を呼ぶ想定。
|
|
2308
|
+
* 実際の再生と同じ経路を録るため等速(曲の長さ分だけ実時間がかかる)。
|
|
2309
|
+
*/
|
|
2310
|
+
startWavRecording: () => void;
|
|
2311
|
+
/** startWavRecording で開始した録音を止め、16bit PCM WAVのBlobを返す。 */
|
|
2312
|
+
stopWavRecording: () => Promise<Blob>;
|
|
2298
2313
|
/** 歌声合成ヘルパ(klatt + koe音源)。 */
|
|
2299
2314
|
singingVoices: SingingVoices;
|
|
2300
2315
|
/** 編集UI(mountDAW)を音・歌声込みでマウントする。 */
|
|
@@ -2425,4 +2440,17 @@ declare const createSynth: (ctx: AudioContext, destination?: AudioNode, tone?: S
|
|
|
2425
2440
|
|
|
2426
2441
|
declare const VOICE_IMAGES: Record<string, string>;
|
|
2427
2442
|
|
|
2428
|
-
|
|
2443
|
+
/**
|
|
2444
|
+
* PCM(Float32、チャンネル別)から 16bit PCM WAV の Blob を作る、依存なしの小さなエンコーダ。
|
|
2445
|
+
* 追加ライブラリなしで完結させたいので RIFF/WAVE ヘッダを手書きしている。
|
|
2446
|
+
*/
|
|
2447
|
+
/**
|
|
2448
|
+
* チャンネルごとの Float32Array(同じ長さ)から 16bit PCM WAV の Blob を生成する。
|
|
2449
|
+
* @param channels 各チャンネルのサンプル列(例: [left, right])
|
|
2450
|
+
* @param sampleRate サンプリングレート(Hz)
|
|
2451
|
+
*/
|
|
2452
|
+
declare const encodeWavPCM16: (channels: Float32Array[], sampleRate: number) => Blob;
|
|
2453
|
+
/** Float32Array のチャンクを1本へ連結する。 */
|
|
2454
|
+
declare const concatFloat32: (chunks: Float32Array[]) => Float32Array;
|
|
2455
|
+
|
|
2456
|
+
export { type AddNoteOptions, type AnyDrumPattern, type ApplyChordOptions, type ChordPatternType, type ChordPlacement, type ChordPlayerInstance, type ConsumedSyllable, type CoreEventHandlers, type CustomVocalDef, 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 DawMode, type DawOptions, type DawViewState, type DrumPattern, type DrumPatternDef, type DtmStudio, type DtmStudioEngines, type DtmStudioOptions, type ExportMidiOptions, type FadeScheduleParams, GM_INSTRUMENT_NAMES, INSTRUMENT_PRESETS, type InstrumentPreset, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KoeVoiceOptions, LinkedList, type LoopConfig, type LoopPoint, type LyricSyllable, type LyricSyncData, type LyricTrack, type LyricsConductor, MAX_VOCAL_VOLUME, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MidiExtraction, type MidiNotePlacement, MidiSearchClient, type MidiSearchConfig, type MidiTrackAnalysis, type MmlMeta, type MmlPlayback, type MmlPlayerInstance, type MmlPlayerOptions, type ModeSwitchInstance, type ModeSwitchOptions, type MountChordPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, type NoteData, type NoteRemove, type OctaveUnisonMode, PITCH_MAP, PREWARM_NOTES, type ParseMMLOptions, type ParseMidiFn, type ParsedMML, type PianoRollInstance, type PianoRollOptions, type PicotuneSearchParams, type PicotuneSong, type PitchToken, type PlayChordsOptions, type PlayDrumEvent, type PlayMmlOptions, type PlayNoteEvent, type PlayNoteOptions, type PlayPlacementsOptions, type PlaySingingMmlOptions, type PlaybackCue, type PlaybackState, type PresetSelectInstance, type PresetSelectOptions, type PreviewSoundCallback, type RenderConfig, type Sequencer, type SequencerOptions, type SequencerTrack, type SingingVoices, type SingingVoicesOptions, type StreamPlaybackOptions, type StreamVoiceNote, type StreamVoiceTrack, type Synth, TRACKS_ADVANCED, TRACKS_SIMPLE, type ToolMode, type TrackConfig, VIBRATO_MIN_SEC, VOICE_IMAGES, VOICE_IMAGE_KEY, type VoiceExpression, type VoiceModel, type VoiceRegistry, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, buildChordPlacements, buildDrumPatternJson, buildNameToKeyMapping, collectPitchTokens, concatFloat32, createAudioContext, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createSequencer, createSingingVoices, createSynth, createVoiceRegistry, decodeMml, decomposeToMonophonic, drawGrid, drawHeader, drawKeyboard, drawNotes, drawSelectedNotes, drawSelectionRect, encodeMml, encodeWavPCM16, exportMIDI, extractDrumPatternFromNotes, extractMidiDrumPattern, extractMidiPlacements, extractMidiPlacementsByTrack, formatMmlMeta, freqFromPitch, generateRandomPattern, getDrawOffset, getDrumPatternKeys, getGridCanvas, getGridContext, getGridPosition, getHeaderCanvas, getMidiBPM, getRenderConfig, getXY, icon, init, injectStyles, isChordHeavyTrack, isPlausibleMidiTranscription, isValidHttpUrl, koeUrl, mountChordPlayer, mountDAW, mountMmlPlayer, normalizeDrumPatterns, normalizeLyrics, onClick, panToStereo, parseCustomVocals, parseLyrics, parseMML, parseMmlMeta, playChords, playMML, playNote, playPlacements, playSingingMML, resolveDrumPattern, resolveLoopPoint, setBackgroundActive, setDrawOffset, shiftNotes, showLoadingOverlay, stripCustomVocals, stripLyrics, stripMmlMeta, transposeNotes, vocalVolumeToGain };
|
package/dist/index.js
CHANGED
|
@@ -56,6 +56,7 @@ __export(index_exports, {
|
|
|
56
56
|
buildDrumPatternJson: () => buildDrumPatternJson,
|
|
57
57
|
buildNameToKeyMapping: () => buildNameToKeyMapping,
|
|
58
58
|
collectPitchTokens: () => collectPitchTokens,
|
|
59
|
+
concatFloat32: () => concatFloat32,
|
|
59
60
|
createAudioContext: () => createAudioContext,
|
|
60
61
|
createDtmStudio: () => createDtmStudio,
|
|
61
62
|
createKlattVoice: () => createKlattVoice,
|
|
@@ -75,6 +76,7 @@ __export(index_exports, {
|
|
|
75
76
|
drawSelectedNotes: () => drawSelectedNotes,
|
|
76
77
|
drawSelectionRect: () => drawSelectionRect,
|
|
77
78
|
encodeMml: () => encodeMml,
|
|
79
|
+
encodeWavPCM16: () => encodeWavPCM16,
|
|
78
80
|
exportMIDI: () => exportMIDI,
|
|
79
81
|
extractDrumPatternFromNotes: () => extractDrumPatternFromNotes,
|
|
80
82
|
extractMidiDrumPattern: () => extractMidiDrumPattern,
|
|
@@ -11752,6 +11754,7 @@ var buildUI = (target, options) => {
|
|
|
11752
11754
|
<div class="dtm-row">
|
|
11753
11755
|
<button class="dtm-btn dtm-btn--accent" data-dtm="export-midi">MIDI\u51FA\u529B</button>
|
|
11754
11756
|
<button class="dtm-btn dtm-btn--success" data-dtm="generate-mml">MML\u751F\u6210</button>
|
|
11757
|
+
<button class="dtm-btn dtm-btn--primary dtm-hidden" data-dtm="export-wav">WAV\u66F8\u304D\u51FA\u3057</button>
|
|
11755
11758
|
</div>
|
|
11756
11759
|
<div class="dtm-row">
|
|
11757
11760
|
<button class="dtm-btn dtm-btn--primary" data-dtm="drum-json-export" title="\u8AAD\u307F\u8FBC\u3093\u3060MIDI\u306E\u30C9\u30E9\u30E0\u5B9A\u7FA9\u3092JSON\u51FA\u529B">\u30C9\u30E9\u30E0JSON\u51FA\u529B</button>
|
|
@@ -11895,6 +11898,7 @@ var buildUI = (target, options) => {
|
|
|
11895
11898
|
macroHarmonic: sel("macro-harmonic"),
|
|
11896
11899
|
macroMono: sel("macro-mono"),
|
|
11897
11900
|
exportMidiBtn: sel("export-midi"),
|
|
11901
|
+
exportWavBtn: sel("export-wav"),
|
|
11898
11902
|
drumJsonExportBtn: sel("drum-json-export"),
|
|
11899
11903
|
drumJsonOutput: sel("drum-json-output"),
|
|
11900
11904
|
drumJsonStatus: sel("drum-json-status"),
|
|
@@ -13749,7 +13753,6 @@ var AUTO_MASTER_INFO_HTML = `
|
|
|
13749
13753
|
<li>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6 Decay: <strong>\u73FE\u5728\u306EBPM\u306B\u5FDC\u3058\u3066\u81EA\u52D5\u8A08\u7B97</strong>\uFF08\u901F\u3044\u66F2\u307B\u3069\u77ED\u304F0.9\u79D2\u5BC4\u308A\u3001\u9045\u3044\u66F2\u307B\u3069\u9577\u304F3.0\u79D2\u5BC4\u308A\uFF09</li>
|
|
13750
13754
|
<li>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6 Pre Delay: 20ms\uFF08\u539F\u97F3\u306E\u8F2A\u90ED\u3092\u4FDD\u3063\u305F\u307E\u307E\u5965\u884C\u304D\u3092\u8DB3\u3059\u5B9A\u756A\u5024\uFF09</li>
|
|
13751
13755
|
<li>\u30D5\u30A7\u30FC\u30C9\u30A2\u30A6\u30C8: \u672A\u8A2D\u5B9A\uFF080\u79D2\uFF09\u306E\u3068\u304D\u3060\u30511.5\u79D2\u3092\u88DC\u3046\uFF08\u66F2\u306E\u7D42\u308F\u308A\u3092\u3076\u3064\u5207\u308A\u306B\u3057\u306A\u3044\uFF09\u3002\u65E2\u306B0\u79D2\u4EE5\u5916\u306E\u5024\u304C\u8A2D\u5B9A\u6E08\u307F\u306A\u3089\u4E0A\u66F8\u304D\u3057\u306A\u3044</li>
|
|
13752
|
-
<li>\u6B4C\u8A5E\u306E\u3042\u308B\u30C8\u30E9\u30C3\u30AF: \u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8ON</li>
|
|
13753
13756
|
</ul>
|
|
13754
13757
|
<p style="margin-top:4px;"><small>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6Mix\u3092\u5BC6\u5EA6\u3067\u6C7A\u3081\u308B\u306E\u306F\u3001\u300C\u97F3\u6570\u306E\u591A\u3044\u539A\u3044\u30A2\u30EC\u30F3\u30B8\u306B\u6B8B\u97FF\u3092\u91CD\u306D\u3059\u304E\u308B\u3068\u6FC1\u308B\u3001\u97F3\u6570\u306E\u5C11\u306A\u3044\u9699\u9593\u306E\u591A\u3044\u7DE8\u6210\u306B\u306F\u6B8B\u97FF\u3067\u7A7A\u9593\u3092\u57CB\u3081\u308B\u3068\u5FC3\u5730\u3088\u3044\u300D\u3068\u3044\u3046\u30DF\u30AD\u30B7\u30F3\u30B0\u306E\u5B9A\u77F3\u306B\u57FA\u3065\u304D\u307E\u3059\u3002</small></p>
|
|
13755
13758
|
<h4>\u697D\u5668\u30FB\u97F3\u91CF\u30FB\u5F79\u5272\u3054\u3068\u306E\u30DF\u30C3\u30AF\u30B9\u306E\u81EA\u52D5\u5272\u308A\u5F53\u3066</h4>
|
|
@@ -13764,10 +13767,10 @@ var AUTO_MASTER_INFO_HTML = `
|
|
|
13764
13767
|
<h4>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB\u306E\u81EA\u52D5\u5224\u5B9A</h4>
|
|
13765
13768
|
<p>\u6B4C\u8A5E\u306E\u3042\u308B\u30C8\u30E9\u30C3\u30AF\u304C\u8907\u6570\u3042\u308B\u5834\u5408\uFF08\u30CF\u30E2\u30EA\u30FB\u30B3\u30FC\u30E9\u30B9\u30FB\u639B\u3051\u58F0\u7B49\uFF09\u3001\u767A\u97F3\u6642\u9593\u304C\u6700\u3082\u9577\u3044\u30C8\u30E9\u30C3\u30AF\u3092\u300C\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB\u300D\u3068\u307F\u306A\u3057\u3001\u4ED6\u3068\u306F\u9055\u3046\u6271\u3044\u306B\u3057\u307E\u3059\u3002</p>
|
|
13766
13769
|
<ul>
|
|
13767
|
-
<li>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB: \u97F3\u5727\u5F37\u531630%\u30FB\u30EA\
|
|
13768
|
-
<li>\u305D\u308C\u4EE5\u5916\u306E\u30DC\u30FC\u30AB\u30EB: \u97F3\u5727\u5F37\u531625%\u30FB\u30EA\u30D0\u30FC\u30D6\u9001\
|
|
13770
|
+
<li>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB: \u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8ON\u30FB\u58F0\u91CF\u3092\u65E2\u5B9A\u306E1.1\u500D\uFF08\u524D\u306B\u51FA\u3059\uFF09\u30FB\u97F3\u5727\u5F37\u531630%\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45100%\uFF08\u72ED\u3081\u30FB\u4E2D\u592E\u5BC4\u308A\u3067\u30AF\u30EA\u30A2\u306B\uFF09\u30FBEQ\u4F4E\u57DF-3dB\uFF08\u30E9\u30F3\u30D6\u30EB/\u3053\u3082\u308A\u9664\u53BB\uFF09\u30FBEQ\u9AD8\u57DF+2dB\uFF08\u629C\u3051\u30FB\u30D7\u30EC\u30BC\u30F3\u30B9\uFF09\u30FB\u30C7\u30A3\u30EC\u30A4\u9001\u308A15%\uFF08\u8A9E\u5C3E\u306B\u8EFD\u3044\u30B9\u30E9\u30C3\u30D7\uFF09\u30FB\u30EA\u30D0\u30FC\u30D6\u9001\u308A25%\u30FB\u5B9A\u4F4D\u306F\u4E2D\u592E\u56FA\u5B9A</li>
|
|
13771
|
+
<li>\u305D\u308C\u4EE5\u5916\u306E\u30DC\u30FC\u30AB\u30EB\uFF08\u30CF\u30E2\u30EA\u30FB\u30B3\u30FC\u30E9\u30B9\u7B49\uFF09: \u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8OFF\u30FB\u58F0\u91CF\u3092\u65E2\u5B9A\u306E0.85\u500D\u3092\u57FA\u6E96\u306B\u3001\u526F\u30DC\u30FC\u30AB\u30EB\u306E\u672C\u6570\u304C\u591A\u3044\u307B\u3069\u3055\u3089\u306B\u7D5E\u308B\uFF08\u91CD\u306A\u3063\u3066\u97F3\u5727\u304C\u7A4D\u307F\u4E0A\u304C\u308B\u5206\u3092\u7B49\u30D1\u30EF\u30FC\u5247\u3067\u76F8\u6BBA\uFF09\u30FB\u97F3\u5727\u5F37\u531625%\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45120%\uFF08\u5E83\u3052\u3066\u300C\u58C1\u300D\u306E\u3088\u3046\u306A\u539A\u307F\u3092\u51FA\u3059\uFF09\u30FBEQ\u4F4E\u57DF-3dB\u30FBEQ\u9AD8\u57DF\u306F\u7121\u5909\u5316\uFF08\u660E\u308B\u304F\u3057\u3059\u304E\u308B\u3068\u524D\u306B\u51FA\u3066\u3057\u307E\u3046\u305F\u3081\uFF09\u30FB\u30C7\u30A3\u30EC\u30A4\u9001\u308A0%\uFF08\u639B\u3051\u308B\u3068\u8F2A\u90ED\u304C\u307C\u3084\u3051\u3066\u56E3\u5B50\u306B\u306A\u308B\u305F\u3081\uFF09\u30FB\u30EA\u30D0\u30FC\u30D6\u9001\u308A45%\uFF08\u5965\u3078\u99B4\u67D3\u307E\u305B\u308B\uFF09\u30FB\u5B9A\u4F4D\u306F\u5DE6\u53F3\u3078\u4EA4\u4E92\u306B\u632F\u3063\u3066\u30C0\u30D6\u30EA\u30F3\u30B0\u611F\u3092\u51FA\u3059</li>
|
|
13769
13772
|
</ul>
|
|
13770
|
-
<p style="margin-top:4px;"><small>\u300C\u524D\u306B\u51FA\u3057\u305F\u3044\u97F3\u306F\u30EA\u30D0\u30FC\u30D6\u5C11\u306A\u3081\u3001\u5965\u306B\u7F6E\u304D\u305F\u3044\u97F3\u306F\u30EA\u30D0\u30FC\u30D6\u591A\u3081\u300D\u3068\u3044\u3046\u5B9A\u77F3\u306B\u57FA\u3065\u3044\u3066\u3044\u307E\u3059\u3002\u300C\u3044\u3044\u611F\u3058\u306E\u521D\u671F\u5024\u300D\u3092\u4E00\u62EC\u3067\u5F53\u3066\u308B\u3060\u3051\u3067\u3001\u66F2\u3084\u597D\u307F\u306B\u5FDC\u3058\u305F\u5FAE\u8ABF\u6574\u307E\u3067\u306F\u884C\u3044\u307E\u305B\u3093\u3002\u65E2\u5B58\u306E\u8A2D\u5B9A\u306F\u4E0A\u66F8\u304D\u3055\u308C\u308B\u306E\u3067\u3001\u6C17\u306B\u5165\u3089\u306A\u3051\u308C\u3070\u5404\u30B9\u30E9\u30A4\u30C0\u30FC\u304B\u3089\u500B\u5225\u306B\u623B\u3057\u3066\u304F\u3060\u3055\u3044\u3002</small></p>
|
|
13773
|
+
<p style="margin-top:4px;"><small>\u300C\u524D\u306B\u51FA\u3057\u305F\u3044\u97F3\u306F\u58F0\u91CF\u5927\u304D\u3081\u30FB\u72ED\u3081\u30FB\u30EA\u30D0\u30FC\u30D6\u5C11\u306A\u3081\u3001\u5965\u306B\u7F6E\u304D\u305F\u3044\u97F3\u306F\u58F0\u91CF\u63A7\u3048\u3081\u30FB\u5E83\u3081\u30FB\u30EA\u30D0\u30FC\u30D6\u591A\u3081\u300D\u3068\u3044\u3046\u5B9A\u77F3\u306B\u57FA\u3065\u3044\u3066\u3044\u307E\u3059\u3002EQ\u306E\u4F4E\u57DF\u30AB\u30C3\u30C8\u306F\u30DC\u30FC\u30AB\u30EB\u3067\u307B\u307C\u666E\u904D\u7684\u306A\u5B9A\u77F3\uFF08\u5B9F\u8CEA\u7684\u306A\u30CF\u30A4\u30D1\u30B9\u30D5\u30A3\u30EB\u30BF\u306E\u4EE3\u7528\uFF09\u3067\u3059\u3002\u30D3\u30D6\u30E9\u30FC\u30C8\u3092\u30CF\u30E2\u30EA\u30FB\u30B3\u30FC\u30E9\u30B9\u3078\u639B\u3051\u306A\u3044\u306E\u306F\u3001\u8907\u6570\u306E\u58F0\u304C\u540C\u6642\u306B\u63FA\u308C\u308B\u3068\u58F0\u3054\u3068\u306E\u4F4D\u76F8\u30FB\u901F\u3055\u306E\u30BA\u30EC\u3067\u548C\u97F3\u304C\u300C\u3046\u306D\u308B\u300D\u300C\u6FC1\u308B\u300D\u305F\u3081\u3067\u3001\u5408\u5531\u3084\u30B9\u30C8\u30EA\u30F3\u30B0\u30B9\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u594F\u6CD5\u3068\u540C\u3058\u304F\u3001\u30CF\u30E2\u30EA/\u30B3\u30FC\u30E9\u30B9\u306F\u30B9\u30C8\u30EC\u30FC\u30C8\u30C8\u30FC\u30F3\u306B\u3057\u3066\u97F3\u7A0B\u306E\u8EF8\u3092\u5B89\u5B9A\u3055\u305B\u308B\u65B9\u304C\u99B4\u67D3\u307F\u307E\u3059\u3002\u30AA\u30AF\u30BF\u30FC\u30D6\u30E6\u30CB\u30BE\u30F3\uFF081\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0A/\u4E0B\u3092\u91CD\u306D\u3066\u539A\u307F\u3092\u51FA\u3059\u52A0\u5DE5\uFF09\u306F\u3053\u3053\u3067\u306F\u81EA\u52D5\u9069\u7528\u3057\u307E\u305B\u3093 \u2014 \u58F0\u8CEA\u3092\u5927\u304D\u304F\u5909\u3048\u308B\u6F14\u51FA\u76EE\u7684\u306E\u52A0\u5DE5\u3067\u3042\u308A\u3001\u66F2\u306B\u3088\u3063\u3066\u5408\u3046/\u5408\u308F\u306A\u3044\u304C\u5206\u304B\u308C\u308B\u5275\u4F5C\u4E0A\u306E\u9078\u629E\uFF08\u30D0\u30E9\u30FC\u30C9\u3067\u306F\u4E0D\u81EA\u7136\u306B\u306A\u308A\u3084\u3059\u3044\u7B49\uFF09\u3067\u3001\u30DF\u30C3\u30AF\u30B9\u306E\u662F\u6B63\u3068\u306F\u6027\u8CEA\u304C\u9055\u3046\u305F\u3081\u3067\u3059\u3002\u4F7F\u3044\u305F\u3044\u5834\u5408\u306F\u5404\u30DC\u30FC\u30AB\u30EB\u30C8\u30E9\u30C3\u30AF\u306E\u8A2D\u5B9A\u6B04\u304B\u3089\u500B\u5225\u306B\u9078\u3093\u3067\u304F\u3060\u3055\u3044\u3002\u300C\u3044\u3044\u611F\u3058\u306E\u521D\u671F\u5024\u300D\u3092\u4E00\u62EC\u3067\u5F53\u3066\u308B\u3060\u3051\u3067\u3001\u66F2\u3084\u597D\u307F\u306B\u5FDC\u3058\u305F\u5FAE\u8ABF\u6574\u307E\u3067\u306F\u884C\u3044\u307E\u305B\u3093\u3002\u65E2\u5B58\u306E\u8A2D\u5B9A\u306F\u4E0A\u66F8\u304D\u3055\u308C\u308B\u306E\u3067\u3001\u6C17\u306B\u5165\u3089\u306A\u3051\u308C\u3070\u5404\u30B9\u30E9\u30A4\u30C0\u30FC\u304B\u3089\u500B\u5225\u306B\u623B\u3057\u3066\u304F\u3060\u3055\u3044\u3002</small></p>
|
|
13771
13774
|
</div>
|
|
13772
13775
|
`;
|
|
13773
13776
|
var MIDI_INFO_HTML = `
|
|
@@ -16997,6 +17000,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16997
17000
|
}
|
|
16998
17001
|
let mainVocalId = null;
|
|
16999
17002
|
let mainVocalScore = -1;
|
|
17003
|
+
let nonMainVocalCount = 0;
|
|
17000
17004
|
for (const t of trackStates) {
|
|
17001
17005
|
if (!t.lyricModel) continue;
|
|
17002
17006
|
const score = t.core.getNotes().reduce((sum, n) => sum + n.durationSteps, 0);
|
|
@@ -17005,6 +17009,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
17005
17009
|
mainVocalId = t.config.id;
|
|
17006
17010
|
}
|
|
17007
17011
|
}
|
|
17012
|
+
for (const t of trackStates) {
|
|
17013
|
+
if (t.lyricModel && t.config.id !== mainVocalId) nonMainVocalCount++;
|
|
17014
|
+
}
|
|
17008
17015
|
const autoPreset = INSTRUMENT_PRESETS[currentInstrument] ?? INSTRUMENT_PRESETS.piano;
|
|
17009
17016
|
const roleByTrackId = /* @__PURE__ */ new Map();
|
|
17010
17017
|
for (const t of trackStates) {
|
|
@@ -17046,28 +17053,43 @@ var mountDAW = (target, options = {}) => {
|
|
|
17046
17053
|
t.trackPan = pan;
|
|
17047
17054
|
options.onTrackPanChange?.(t.config.id, pan);
|
|
17048
17055
|
const comp = t.lyricModel ? isMainVocal ? 30 : 25 : roleMix?.compression ?? 35;
|
|
17049
|
-
const width = t.lyricModel ?
|
|
17056
|
+
const width = t.lyricModel ? isMainVocal ? 100 : 120 : roleMix?.width ?? 115;
|
|
17050
17057
|
t.trackCompression = comp;
|
|
17051
17058
|
t.trackWidth = width;
|
|
17052
17059
|
options.onTrackCompressionChange?.(t.config.id, comp);
|
|
17053
17060
|
options.onTrackWidthChange?.(t.config.id, width);
|
|
17054
|
-
if (
|
|
17055
|
-
|
|
17056
|
-
t.
|
|
17057
|
-
options.
|
|
17061
|
+
if (t.lyricModel) {
|
|
17062
|
+
t.trackEqLow = -3;
|
|
17063
|
+
t.trackEqHigh = isMainVocal ? 2 : 0;
|
|
17064
|
+
options.onTrackEqLowChange?.(t.config.id, t.trackEqLow);
|
|
17065
|
+
options.onTrackEqHighChange?.(t.config.id, t.trackEqHigh);
|
|
17066
|
+
} else {
|
|
17058
17067
|
const eqLow = roleMix?.eqLow ?? 0;
|
|
17059
17068
|
const eqHigh = roleMix?.eqHigh ?? 0;
|
|
17060
17069
|
t.trackEqLow = eqLow;
|
|
17061
17070
|
t.trackEqHigh = eqHigh;
|
|
17062
17071
|
options.onTrackEqLowChange?.(t.config.id, eqLow);
|
|
17063
17072
|
options.onTrackEqHighChange?.(t.config.id, eqHigh);
|
|
17073
|
+
}
|
|
17074
|
+
if (!t.lyricModel) {
|
|
17075
|
+
const reverbSend = roleMix?.reverbSend ?? 15;
|
|
17076
|
+
t.trackReverbSend = reverbSend;
|
|
17077
|
+
options.onTrackReverbSendChange?.(t.config.id, reverbSend);
|
|
17064
17078
|
const delaySend = roleMix?.delaySend ?? 0;
|
|
17065
17079
|
t.trackDelaySend = delaySend;
|
|
17066
17080
|
options.onTrackDelaySendChange?.(t.config.id, delaySend);
|
|
17067
17081
|
}
|
|
17068
17082
|
if (t.lyricModel) {
|
|
17069
|
-
t.vocalVibrato =
|
|
17083
|
+
t.vocalVibrato = isMainVocal;
|
|
17070
17084
|
t.vocalReverb = isMainVocal ? 25 : 45;
|
|
17085
|
+
t.vocalDelay = isMainVocal ? 15 : 0;
|
|
17086
|
+
t.vocalVolume = isMainVocal ? Math.round(DEFAULT_VOCAL_VOLUME * 1.1) : clamp5(
|
|
17087
|
+
Math.round(
|
|
17088
|
+
DEFAULT_VOCAL_VOLUME * 0.85 / Math.sqrt(Math.max(1, nonMainVocalCount))
|
|
17089
|
+
),
|
|
17090
|
+
60,
|
|
17091
|
+
MAX_VOCAL_VOLUME
|
|
17092
|
+
);
|
|
17071
17093
|
fireLyricsChange(t);
|
|
17072
17094
|
}
|
|
17073
17095
|
}
|
|
@@ -17135,6 +17157,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
17135
17157
|
a.click();
|
|
17136
17158
|
URL.revokeObjectURL(url2);
|
|
17137
17159
|
});
|
|
17160
|
+
if (options.onExportWav) {
|
|
17161
|
+
refs.exportWavBtn.classList.remove("dtm-hidden");
|
|
17162
|
+
refs.exportWavBtn.addEventListener("click", () => {
|
|
17163
|
+
void options.onExportWav?.();
|
|
17164
|
+
});
|
|
17165
|
+
}
|
|
17138
17166
|
const copy = (text, btn) => {
|
|
17139
17167
|
navigator.clipboard?.writeText(text);
|
|
17140
17168
|
btn.classList.add("dtm-btn--success");
|
|
@@ -19011,6 +19039,64 @@ var SoundFont_list = new class {
|
|
|
19011
19039
|
}
|
|
19012
19040
|
}();
|
|
19013
19041
|
|
|
19042
|
+
// src/wav-export.ts
|
|
19043
|
+
var floatTo16BitPCM = (view, offset, input) => {
|
|
19044
|
+
for (let i2 = 0; i2 < input.length; i2++, offset += 2) {
|
|
19045
|
+
const s = Math.max(-1, Math.min(1, input[i2]));
|
|
19046
|
+
view.setInt16(offset, s < 0 ? s * 32768 : s * 32767, true);
|
|
19047
|
+
}
|
|
19048
|
+
};
|
|
19049
|
+
var writeString = (view, offset, str) => {
|
|
19050
|
+
for (let i2 = 0; i2 < str.length; i2++) {
|
|
19051
|
+
view.setUint8(offset + i2, str.charCodeAt(i2));
|
|
19052
|
+
}
|
|
19053
|
+
};
|
|
19054
|
+
var encodeWavPCM16 = (channels, sampleRate) => {
|
|
19055
|
+
const numChannels = channels.length;
|
|
19056
|
+
const numFrames = channels[0]?.length ?? 0;
|
|
19057
|
+
const bytesPerSample = 2;
|
|
19058
|
+
const blockAlign = numChannels * bytesPerSample;
|
|
19059
|
+
const dataSize = numFrames * blockAlign;
|
|
19060
|
+
const buffer = new ArrayBuffer(44 + dataSize);
|
|
19061
|
+
const view = new DataView(buffer);
|
|
19062
|
+
writeString(view, 0, "RIFF");
|
|
19063
|
+
view.setUint32(4, 36 + dataSize, true);
|
|
19064
|
+
writeString(view, 8, "WAVE");
|
|
19065
|
+
writeString(view, 12, "fmt ");
|
|
19066
|
+
view.setUint32(16, 16, true);
|
|
19067
|
+
view.setUint16(20, 1, true);
|
|
19068
|
+
view.setUint16(22, numChannels, true);
|
|
19069
|
+
view.setUint32(24, sampleRate, true);
|
|
19070
|
+
view.setUint32(28, sampleRate * blockAlign, true);
|
|
19071
|
+
view.setUint16(32, blockAlign, true);
|
|
19072
|
+
view.setUint16(34, bytesPerSample * 8, true);
|
|
19073
|
+
writeString(view, 36, "data");
|
|
19074
|
+
view.setUint32(40, dataSize, true);
|
|
19075
|
+
if (numChannels === 1) {
|
|
19076
|
+
floatTo16BitPCM(view, 44, channels[0]);
|
|
19077
|
+
} else {
|
|
19078
|
+
let offset = 44;
|
|
19079
|
+
for (let i2 = 0; i2 < numFrames; i2++) {
|
|
19080
|
+
for (let c = 0; c < numChannels; c++) {
|
|
19081
|
+
const s = Math.max(-1, Math.min(1, channels[c][i2]));
|
|
19082
|
+
view.setInt16(offset, s < 0 ? s * 32768 : s * 32767, true);
|
|
19083
|
+
offset += 2;
|
|
19084
|
+
}
|
|
19085
|
+
}
|
|
19086
|
+
}
|
|
19087
|
+
return new Blob([buffer], { type: "audio/wav" });
|
|
19088
|
+
};
|
|
19089
|
+
var concatFloat32 = (chunks) => {
|
|
19090
|
+
const total = chunks.reduce((sum, c) => sum + c.length, 0);
|
|
19091
|
+
const out = new Float32Array(total);
|
|
19092
|
+
let offset = 0;
|
|
19093
|
+
for (const c of chunks) {
|
|
19094
|
+
out.set(c, offset);
|
|
19095
|
+
offset += c.length;
|
|
19096
|
+
}
|
|
19097
|
+
return out;
|
|
19098
|
+
};
|
|
19099
|
+
|
|
19014
19100
|
// src/studio.ts
|
|
19015
19101
|
var import_meta = {};
|
|
19016
19102
|
var SOUNDFONT_NAME2 = "FluidR3_GM_sf2_file";
|
|
@@ -19488,6 +19574,28 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19488
19574
|
},
|
|
19489
19575
|
onPlayNote: playNote4,
|
|
19490
19576
|
onPlayDrum: playDrum,
|
|
19577
|
+
onExportWav: async () => {
|
|
19578
|
+
if (!daw) return;
|
|
19579
|
+
daw.stop();
|
|
19580
|
+
await resumeAudio();
|
|
19581
|
+
startWavRecording();
|
|
19582
|
+
await daw.play();
|
|
19583
|
+
await new Promise((resolve) => {
|
|
19584
|
+
const iv = setInterval(() => {
|
|
19585
|
+
if (daw.getPlaybackState() !== "playing") {
|
|
19586
|
+
clearInterval(iv);
|
|
19587
|
+
resolve();
|
|
19588
|
+
}
|
|
19589
|
+
}, 200);
|
|
19590
|
+
});
|
|
19591
|
+
const blob2 = await stopWavRecording();
|
|
19592
|
+
const url2 = URL.createObjectURL(blob2);
|
|
19593
|
+
const a = document.createElement("a");
|
|
19594
|
+
a.href = url2;
|
|
19595
|
+
a.download = "dtm.wav";
|
|
19596
|
+
a.click();
|
|
19597
|
+
URL.revokeObjectURL(url2);
|
|
19598
|
+
},
|
|
19491
19599
|
singingVoices,
|
|
19492
19600
|
parseMidi,
|
|
19493
19601
|
onInstrumentChange: handleInstrumentChange,
|
|
@@ -20075,11 +20183,48 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20075
20183
|
const getAudioStreamTrack = () => {
|
|
20076
20184
|
return createMediaStreamDestination().stream.getAudioTracks()[0];
|
|
20077
20185
|
};
|
|
20186
|
+
let wavTap = null;
|
|
20187
|
+
const startWavRecording = () => {
|
|
20188
|
+
if (wavTap) return;
|
|
20189
|
+
const bufferSize = 4096;
|
|
20190
|
+
const processor = audioCtx.createScriptProcessor(bufferSize, 2, 2);
|
|
20191
|
+
const chunksL = [];
|
|
20192
|
+
const chunksR = [];
|
|
20193
|
+
processor.onaudioprocess = (e) => {
|
|
20194
|
+
chunksL.push(new Float32Array(e.inputBuffer.getChannelData(0)));
|
|
20195
|
+
chunksR.push(
|
|
20196
|
+
new Float32Array(
|
|
20197
|
+
e.inputBuffer.getChannelData(
|
|
20198
|
+
e.inputBuffer.numberOfChannels > 1 ? 1 : 0
|
|
20199
|
+
)
|
|
20200
|
+
)
|
|
20201
|
+
);
|
|
20202
|
+
};
|
|
20203
|
+
const silentSink = audioCtx.createGain();
|
|
20204
|
+
silentSink.gain.value = 0;
|
|
20205
|
+
fadeGain.connect(processor);
|
|
20206
|
+
processor.connect(silentSink);
|
|
20207
|
+
silentSink.connect(audioCtx.destination);
|
|
20208
|
+
wavTap = { processor, silentSink, chunksL, chunksR };
|
|
20209
|
+
};
|
|
20210
|
+
const stopWavRecording = async () => {
|
|
20211
|
+
if (!wavTap) throw new Error("[dtm] startWavRecording \u304C\u547C\u3070\u308C\u3066\u3044\u307E\u305B\u3093");
|
|
20212
|
+
const { processor, silentSink, chunksL, chunksR } = wavTap;
|
|
20213
|
+
fadeGain.disconnect(processor);
|
|
20214
|
+
processor.disconnect(silentSink);
|
|
20215
|
+
silentSink.disconnect(audioCtx.destination);
|
|
20216
|
+
wavTap = null;
|
|
20217
|
+
const left = concatFloat32(chunksL);
|
|
20218
|
+
const right = concatFloat32(chunksR);
|
|
20219
|
+
return encodeWavPCM16([left, right], audioCtx.sampleRate);
|
|
20220
|
+
};
|
|
20078
20221
|
return {
|
|
20079
20222
|
audioContext: audioCtx,
|
|
20080
20223
|
masterGain,
|
|
20081
20224
|
createMediaStreamDestination,
|
|
20082
20225
|
getAudioStreamTrack,
|
|
20226
|
+
startWavRecording,
|
|
20227
|
+
stopWavRecording,
|
|
20083
20228
|
singingVoices,
|
|
20084
20229
|
mountEditor,
|
|
20085
20230
|
mountPlayer,
|
|
@@ -20142,6 +20287,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20142
20287
|
buildDrumPatternJson,
|
|
20143
20288
|
buildNameToKeyMapping,
|
|
20144
20289
|
collectPitchTokens,
|
|
20290
|
+
concatFloat32,
|
|
20145
20291
|
createAudioContext,
|
|
20146
20292
|
createDtmStudio,
|
|
20147
20293
|
createKlattVoice,
|
|
@@ -20161,6 +20307,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20161
20307
|
drawSelectedNotes,
|
|
20162
20308
|
drawSelectionRect,
|
|
20163
20309
|
encodeMml,
|
|
20310
|
+
encodeWavPCM16,
|
|
20164
20311
|
exportMIDI,
|
|
20165
20312
|
extractDrumPatternFromNotes,
|
|
20166
20313
|
extractMidiDrumPattern,
|
package/dist/index.mjs
CHANGED
|
@@ -11621,6 +11621,7 @@ var buildUI = (target, options) => {
|
|
|
11621
11621
|
<div class="dtm-row">
|
|
11622
11622
|
<button class="dtm-btn dtm-btn--accent" data-dtm="export-midi">MIDI\u51FA\u529B</button>
|
|
11623
11623
|
<button class="dtm-btn dtm-btn--success" data-dtm="generate-mml">MML\u751F\u6210</button>
|
|
11624
|
+
<button class="dtm-btn dtm-btn--primary dtm-hidden" data-dtm="export-wav">WAV\u66F8\u304D\u51FA\u3057</button>
|
|
11624
11625
|
</div>
|
|
11625
11626
|
<div class="dtm-row">
|
|
11626
11627
|
<button class="dtm-btn dtm-btn--primary" data-dtm="drum-json-export" title="\u8AAD\u307F\u8FBC\u3093\u3060MIDI\u306E\u30C9\u30E9\u30E0\u5B9A\u7FA9\u3092JSON\u51FA\u529B">\u30C9\u30E9\u30E0JSON\u51FA\u529B</button>
|
|
@@ -11764,6 +11765,7 @@ var buildUI = (target, options) => {
|
|
|
11764
11765
|
macroHarmonic: sel("macro-harmonic"),
|
|
11765
11766
|
macroMono: sel("macro-mono"),
|
|
11766
11767
|
exportMidiBtn: sel("export-midi"),
|
|
11768
|
+
exportWavBtn: sel("export-wav"),
|
|
11767
11769
|
drumJsonExportBtn: sel("drum-json-export"),
|
|
11768
11770
|
drumJsonOutput: sel("drum-json-output"),
|
|
11769
11771
|
drumJsonStatus: sel("drum-json-status"),
|
|
@@ -13618,7 +13620,6 @@ var AUTO_MASTER_INFO_HTML = `
|
|
|
13618
13620
|
<li>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6 Decay: <strong>\u73FE\u5728\u306EBPM\u306B\u5FDC\u3058\u3066\u81EA\u52D5\u8A08\u7B97</strong>\uFF08\u901F\u3044\u66F2\u307B\u3069\u77ED\u304F0.9\u79D2\u5BC4\u308A\u3001\u9045\u3044\u66F2\u307B\u3069\u9577\u304F3.0\u79D2\u5BC4\u308A\uFF09</li>
|
|
13619
13621
|
<li>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6 Pre Delay: 20ms\uFF08\u539F\u97F3\u306E\u8F2A\u90ED\u3092\u4FDD\u3063\u305F\u307E\u307E\u5965\u884C\u304D\u3092\u8DB3\u3059\u5B9A\u756A\u5024\uFF09</li>
|
|
13620
13622
|
<li>\u30D5\u30A7\u30FC\u30C9\u30A2\u30A6\u30C8: \u672A\u8A2D\u5B9A\uFF080\u79D2\uFF09\u306E\u3068\u304D\u3060\u30511.5\u79D2\u3092\u88DC\u3046\uFF08\u66F2\u306E\u7D42\u308F\u308A\u3092\u3076\u3064\u5207\u308A\u306B\u3057\u306A\u3044\uFF09\u3002\u65E2\u306B0\u79D2\u4EE5\u5916\u306E\u5024\u304C\u8A2D\u5B9A\u6E08\u307F\u306A\u3089\u4E0A\u66F8\u304D\u3057\u306A\u3044</li>
|
|
13621
|
-
<li>\u6B4C\u8A5E\u306E\u3042\u308B\u30C8\u30E9\u30C3\u30AF: \u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8ON</li>
|
|
13622
13623
|
</ul>
|
|
13623
13624
|
<p style="margin-top:4px;"><small>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6Mix\u3092\u5BC6\u5EA6\u3067\u6C7A\u3081\u308B\u306E\u306F\u3001\u300C\u97F3\u6570\u306E\u591A\u3044\u539A\u3044\u30A2\u30EC\u30F3\u30B8\u306B\u6B8B\u97FF\u3092\u91CD\u306D\u3059\u304E\u308B\u3068\u6FC1\u308B\u3001\u97F3\u6570\u306E\u5C11\u306A\u3044\u9699\u9593\u306E\u591A\u3044\u7DE8\u6210\u306B\u306F\u6B8B\u97FF\u3067\u7A7A\u9593\u3092\u57CB\u3081\u308B\u3068\u5FC3\u5730\u3088\u3044\u300D\u3068\u3044\u3046\u30DF\u30AD\u30B7\u30F3\u30B0\u306E\u5B9A\u77F3\u306B\u57FA\u3065\u304D\u307E\u3059\u3002</small></p>
|
|
13624
13625
|
<h4>\u697D\u5668\u30FB\u97F3\u91CF\u30FB\u5F79\u5272\u3054\u3068\u306E\u30DF\u30C3\u30AF\u30B9\u306E\u81EA\u52D5\u5272\u308A\u5F53\u3066</h4>
|
|
@@ -13633,10 +13634,10 @@ var AUTO_MASTER_INFO_HTML = `
|
|
|
13633
13634
|
<h4>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB\u306E\u81EA\u52D5\u5224\u5B9A</h4>
|
|
13634
13635
|
<p>\u6B4C\u8A5E\u306E\u3042\u308B\u30C8\u30E9\u30C3\u30AF\u304C\u8907\u6570\u3042\u308B\u5834\u5408\uFF08\u30CF\u30E2\u30EA\u30FB\u30B3\u30FC\u30E9\u30B9\u30FB\u639B\u3051\u58F0\u7B49\uFF09\u3001\u767A\u97F3\u6642\u9593\u304C\u6700\u3082\u9577\u3044\u30C8\u30E9\u30C3\u30AF\u3092\u300C\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB\u300D\u3068\u307F\u306A\u3057\u3001\u4ED6\u3068\u306F\u9055\u3046\u6271\u3044\u306B\u3057\u307E\u3059\u3002</p>
|
|
13635
13636
|
<ul>
|
|
13636
|
-
<li>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB: \u97F3\u5727\u5F37\u531630%\u30FB\u30EA\
|
|
13637
|
-
<li>\u305D\u308C\u4EE5\u5916\u306E\u30DC\u30FC\u30AB\u30EB: \u97F3\u5727\u5F37\u531625%\u30FB\u30EA\u30D0\u30FC\u30D6\u9001\
|
|
13637
|
+
<li>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB: \u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8ON\u30FB\u58F0\u91CF\u3092\u65E2\u5B9A\u306E1.1\u500D\uFF08\u524D\u306B\u51FA\u3059\uFF09\u30FB\u97F3\u5727\u5F37\u531630%\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45100%\uFF08\u72ED\u3081\u30FB\u4E2D\u592E\u5BC4\u308A\u3067\u30AF\u30EA\u30A2\u306B\uFF09\u30FBEQ\u4F4E\u57DF-3dB\uFF08\u30E9\u30F3\u30D6\u30EB/\u3053\u3082\u308A\u9664\u53BB\uFF09\u30FBEQ\u9AD8\u57DF+2dB\uFF08\u629C\u3051\u30FB\u30D7\u30EC\u30BC\u30F3\u30B9\uFF09\u30FB\u30C7\u30A3\u30EC\u30A4\u9001\u308A15%\uFF08\u8A9E\u5C3E\u306B\u8EFD\u3044\u30B9\u30E9\u30C3\u30D7\uFF09\u30FB\u30EA\u30D0\u30FC\u30D6\u9001\u308A25%\u30FB\u5B9A\u4F4D\u306F\u4E2D\u592E\u56FA\u5B9A</li>
|
|
13638
|
+
<li>\u305D\u308C\u4EE5\u5916\u306E\u30DC\u30FC\u30AB\u30EB\uFF08\u30CF\u30E2\u30EA\u30FB\u30B3\u30FC\u30E9\u30B9\u7B49\uFF09: \u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8OFF\u30FB\u58F0\u91CF\u3092\u65E2\u5B9A\u306E0.85\u500D\u3092\u57FA\u6E96\u306B\u3001\u526F\u30DC\u30FC\u30AB\u30EB\u306E\u672C\u6570\u304C\u591A\u3044\u307B\u3069\u3055\u3089\u306B\u7D5E\u308B\uFF08\u91CD\u306A\u3063\u3066\u97F3\u5727\u304C\u7A4D\u307F\u4E0A\u304C\u308B\u5206\u3092\u7B49\u30D1\u30EF\u30FC\u5247\u3067\u76F8\u6BBA\uFF09\u30FB\u97F3\u5727\u5F37\u531625%\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45120%\uFF08\u5E83\u3052\u3066\u300C\u58C1\u300D\u306E\u3088\u3046\u306A\u539A\u307F\u3092\u51FA\u3059\uFF09\u30FBEQ\u4F4E\u57DF-3dB\u30FBEQ\u9AD8\u57DF\u306F\u7121\u5909\u5316\uFF08\u660E\u308B\u304F\u3057\u3059\u304E\u308B\u3068\u524D\u306B\u51FA\u3066\u3057\u307E\u3046\u305F\u3081\uFF09\u30FB\u30C7\u30A3\u30EC\u30A4\u9001\u308A0%\uFF08\u639B\u3051\u308B\u3068\u8F2A\u90ED\u304C\u307C\u3084\u3051\u3066\u56E3\u5B50\u306B\u306A\u308B\u305F\u3081\uFF09\u30FB\u30EA\u30D0\u30FC\u30D6\u9001\u308A45%\uFF08\u5965\u3078\u99B4\u67D3\u307E\u305B\u308B\uFF09\u30FB\u5B9A\u4F4D\u306F\u5DE6\u53F3\u3078\u4EA4\u4E92\u306B\u632F\u3063\u3066\u30C0\u30D6\u30EA\u30F3\u30B0\u611F\u3092\u51FA\u3059</li>
|
|
13638
13639
|
</ul>
|
|
13639
|
-
<p style="margin-top:4px;"><small>\u300C\u524D\u306B\u51FA\u3057\u305F\u3044\u97F3\u306F\u30EA\u30D0\u30FC\u30D6\u5C11\u306A\u3081\u3001\u5965\u306B\u7F6E\u304D\u305F\u3044\u97F3\u306F\u30EA\u30D0\u30FC\u30D6\u591A\u3081\u300D\u3068\u3044\u3046\u5B9A\u77F3\u306B\u57FA\u3065\u3044\u3066\u3044\u307E\u3059\u3002\u300C\u3044\u3044\u611F\u3058\u306E\u521D\u671F\u5024\u300D\u3092\u4E00\u62EC\u3067\u5F53\u3066\u308B\u3060\u3051\u3067\u3001\u66F2\u3084\u597D\u307F\u306B\u5FDC\u3058\u305F\u5FAE\u8ABF\u6574\u307E\u3067\u306F\u884C\u3044\u307E\u305B\u3093\u3002\u65E2\u5B58\u306E\u8A2D\u5B9A\u306F\u4E0A\u66F8\u304D\u3055\u308C\u308B\u306E\u3067\u3001\u6C17\u306B\u5165\u3089\u306A\u3051\u308C\u3070\u5404\u30B9\u30E9\u30A4\u30C0\u30FC\u304B\u3089\u500B\u5225\u306B\u623B\u3057\u3066\u304F\u3060\u3055\u3044\u3002</small></p>
|
|
13640
|
+
<p style="margin-top:4px;"><small>\u300C\u524D\u306B\u51FA\u3057\u305F\u3044\u97F3\u306F\u58F0\u91CF\u5927\u304D\u3081\u30FB\u72ED\u3081\u30FB\u30EA\u30D0\u30FC\u30D6\u5C11\u306A\u3081\u3001\u5965\u306B\u7F6E\u304D\u305F\u3044\u97F3\u306F\u58F0\u91CF\u63A7\u3048\u3081\u30FB\u5E83\u3081\u30FB\u30EA\u30D0\u30FC\u30D6\u591A\u3081\u300D\u3068\u3044\u3046\u5B9A\u77F3\u306B\u57FA\u3065\u3044\u3066\u3044\u307E\u3059\u3002EQ\u306E\u4F4E\u57DF\u30AB\u30C3\u30C8\u306F\u30DC\u30FC\u30AB\u30EB\u3067\u307B\u307C\u666E\u904D\u7684\u306A\u5B9A\u77F3\uFF08\u5B9F\u8CEA\u7684\u306A\u30CF\u30A4\u30D1\u30B9\u30D5\u30A3\u30EB\u30BF\u306E\u4EE3\u7528\uFF09\u3067\u3059\u3002\u30D3\u30D6\u30E9\u30FC\u30C8\u3092\u30CF\u30E2\u30EA\u30FB\u30B3\u30FC\u30E9\u30B9\u3078\u639B\u3051\u306A\u3044\u306E\u306F\u3001\u8907\u6570\u306E\u58F0\u304C\u540C\u6642\u306B\u63FA\u308C\u308B\u3068\u58F0\u3054\u3068\u306E\u4F4D\u76F8\u30FB\u901F\u3055\u306E\u30BA\u30EC\u3067\u548C\u97F3\u304C\u300C\u3046\u306D\u308B\u300D\u300C\u6FC1\u308B\u300D\u305F\u3081\u3067\u3001\u5408\u5531\u3084\u30B9\u30C8\u30EA\u30F3\u30B0\u30B9\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u594F\u6CD5\u3068\u540C\u3058\u304F\u3001\u30CF\u30E2\u30EA/\u30B3\u30FC\u30E9\u30B9\u306F\u30B9\u30C8\u30EC\u30FC\u30C8\u30C8\u30FC\u30F3\u306B\u3057\u3066\u97F3\u7A0B\u306E\u8EF8\u3092\u5B89\u5B9A\u3055\u305B\u308B\u65B9\u304C\u99B4\u67D3\u307F\u307E\u3059\u3002\u30AA\u30AF\u30BF\u30FC\u30D6\u30E6\u30CB\u30BE\u30F3\uFF081\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0A/\u4E0B\u3092\u91CD\u306D\u3066\u539A\u307F\u3092\u51FA\u3059\u52A0\u5DE5\uFF09\u306F\u3053\u3053\u3067\u306F\u81EA\u52D5\u9069\u7528\u3057\u307E\u305B\u3093 \u2014 \u58F0\u8CEA\u3092\u5927\u304D\u304F\u5909\u3048\u308B\u6F14\u51FA\u76EE\u7684\u306E\u52A0\u5DE5\u3067\u3042\u308A\u3001\u66F2\u306B\u3088\u3063\u3066\u5408\u3046/\u5408\u308F\u306A\u3044\u304C\u5206\u304B\u308C\u308B\u5275\u4F5C\u4E0A\u306E\u9078\u629E\uFF08\u30D0\u30E9\u30FC\u30C9\u3067\u306F\u4E0D\u81EA\u7136\u306B\u306A\u308A\u3084\u3059\u3044\u7B49\uFF09\u3067\u3001\u30DF\u30C3\u30AF\u30B9\u306E\u662F\u6B63\u3068\u306F\u6027\u8CEA\u304C\u9055\u3046\u305F\u3081\u3067\u3059\u3002\u4F7F\u3044\u305F\u3044\u5834\u5408\u306F\u5404\u30DC\u30FC\u30AB\u30EB\u30C8\u30E9\u30C3\u30AF\u306E\u8A2D\u5B9A\u6B04\u304B\u3089\u500B\u5225\u306B\u9078\u3093\u3067\u304F\u3060\u3055\u3044\u3002\u300C\u3044\u3044\u611F\u3058\u306E\u521D\u671F\u5024\u300D\u3092\u4E00\u62EC\u3067\u5F53\u3066\u308B\u3060\u3051\u3067\u3001\u66F2\u3084\u597D\u307F\u306B\u5FDC\u3058\u305F\u5FAE\u8ABF\u6574\u307E\u3067\u306F\u884C\u3044\u307E\u305B\u3093\u3002\u65E2\u5B58\u306E\u8A2D\u5B9A\u306F\u4E0A\u66F8\u304D\u3055\u308C\u308B\u306E\u3067\u3001\u6C17\u306B\u5165\u3089\u306A\u3051\u308C\u3070\u5404\u30B9\u30E9\u30A4\u30C0\u30FC\u304B\u3089\u500B\u5225\u306B\u623B\u3057\u3066\u304F\u3060\u3055\u3044\u3002</small></p>
|
|
13640
13641
|
</div>
|
|
13641
13642
|
`;
|
|
13642
13643
|
var MIDI_INFO_HTML = `
|
|
@@ -16866,6 +16867,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16866
16867
|
}
|
|
16867
16868
|
let mainVocalId = null;
|
|
16868
16869
|
let mainVocalScore = -1;
|
|
16870
|
+
let nonMainVocalCount = 0;
|
|
16869
16871
|
for (const t of trackStates) {
|
|
16870
16872
|
if (!t.lyricModel) continue;
|
|
16871
16873
|
const score = t.core.getNotes().reduce((sum, n) => sum + n.durationSteps, 0);
|
|
@@ -16874,6 +16876,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
16874
16876
|
mainVocalId = t.config.id;
|
|
16875
16877
|
}
|
|
16876
16878
|
}
|
|
16879
|
+
for (const t of trackStates) {
|
|
16880
|
+
if (t.lyricModel && t.config.id !== mainVocalId) nonMainVocalCount++;
|
|
16881
|
+
}
|
|
16877
16882
|
const autoPreset = INSTRUMENT_PRESETS[currentInstrument] ?? INSTRUMENT_PRESETS.piano;
|
|
16878
16883
|
const roleByTrackId = /* @__PURE__ */ new Map();
|
|
16879
16884
|
for (const t of trackStates) {
|
|
@@ -16915,28 +16920,43 @@ var mountDAW = (target, options = {}) => {
|
|
|
16915
16920
|
t.trackPan = pan;
|
|
16916
16921
|
options.onTrackPanChange?.(t.config.id, pan);
|
|
16917
16922
|
const comp = t.lyricModel ? isMainVocal ? 30 : 25 : roleMix?.compression ?? 35;
|
|
16918
|
-
const width = t.lyricModel ?
|
|
16923
|
+
const width = t.lyricModel ? isMainVocal ? 100 : 120 : roleMix?.width ?? 115;
|
|
16919
16924
|
t.trackCompression = comp;
|
|
16920
16925
|
t.trackWidth = width;
|
|
16921
16926
|
options.onTrackCompressionChange?.(t.config.id, comp);
|
|
16922
16927
|
options.onTrackWidthChange?.(t.config.id, width);
|
|
16923
|
-
if (
|
|
16924
|
-
|
|
16925
|
-
t.
|
|
16926
|
-
options.
|
|
16928
|
+
if (t.lyricModel) {
|
|
16929
|
+
t.trackEqLow = -3;
|
|
16930
|
+
t.trackEqHigh = isMainVocal ? 2 : 0;
|
|
16931
|
+
options.onTrackEqLowChange?.(t.config.id, t.trackEqLow);
|
|
16932
|
+
options.onTrackEqHighChange?.(t.config.id, t.trackEqHigh);
|
|
16933
|
+
} else {
|
|
16927
16934
|
const eqLow = roleMix?.eqLow ?? 0;
|
|
16928
16935
|
const eqHigh = roleMix?.eqHigh ?? 0;
|
|
16929
16936
|
t.trackEqLow = eqLow;
|
|
16930
16937
|
t.trackEqHigh = eqHigh;
|
|
16931
16938
|
options.onTrackEqLowChange?.(t.config.id, eqLow);
|
|
16932
16939
|
options.onTrackEqHighChange?.(t.config.id, eqHigh);
|
|
16940
|
+
}
|
|
16941
|
+
if (!t.lyricModel) {
|
|
16942
|
+
const reverbSend = roleMix?.reverbSend ?? 15;
|
|
16943
|
+
t.trackReverbSend = reverbSend;
|
|
16944
|
+
options.onTrackReverbSendChange?.(t.config.id, reverbSend);
|
|
16933
16945
|
const delaySend = roleMix?.delaySend ?? 0;
|
|
16934
16946
|
t.trackDelaySend = delaySend;
|
|
16935
16947
|
options.onTrackDelaySendChange?.(t.config.id, delaySend);
|
|
16936
16948
|
}
|
|
16937
16949
|
if (t.lyricModel) {
|
|
16938
|
-
t.vocalVibrato =
|
|
16950
|
+
t.vocalVibrato = isMainVocal;
|
|
16939
16951
|
t.vocalReverb = isMainVocal ? 25 : 45;
|
|
16952
|
+
t.vocalDelay = isMainVocal ? 15 : 0;
|
|
16953
|
+
t.vocalVolume = isMainVocal ? Math.round(DEFAULT_VOCAL_VOLUME * 1.1) : clamp5(
|
|
16954
|
+
Math.round(
|
|
16955
|
+
DEFAULT_VOCAL_VOLUME * 0.85 / Math.sqrt(Math.max(1, nonMainVocalCount))
|
|
16956
|
+
),
|
|
16957
|
+
60,
|
|
16958
|
+
MAX_VOCAL_VOLUME
|
|
16959
|
+
);
|
|
16940
16960
|
fireLyricsChange(t);
|
|
16941
16961
|
}
|
|
16942
16962
|
}
|
|
@@ -17004,6 +17024,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
17004
17024
|
a.click();
|
|
17005
17025
|
URL.revokeObjectURL(url2);
|
|
17006
17026
|
});
|
|
17027
|
+
if (options.onExportWav) {
|
|
17028
|
+
refs.exportWavBtn.classList.remove("dtm-hidden");
|
|
17029
|
+
refs.exportWavBtn.addEventListener("click", () => {
|
|
17030
|
+
void options.onExportWav?.();
|
|
17031
|
+
});
|
|
17032
|
+
}
|
|
17007
17033
|
const copy = (text, btn) => {
|
|
17008
17034
|
navigator.clipboard?.writeText(text);
|
|
17009
17035
|
btn.classList.add("dtm-btn--success");
|
|
@@ -18880,6 +18906,64 @@ var SoundFont_list = new class {
|
|
|
18880
18906
|
}
|
|
18881
18907
|
}();
|
|
18882
18908
|
|
|
18909
|
+
// src/wav-export.ts
|
|
18910
|
+
var floatTo16BitPCM = (view, offset, input) => {
|
|
18911
|
+
for (let i2 = 0; i2 < input.length; i2++, offset += 2) {
|
|
18912
|
+
const s = Math.max(-1, Math.min(1, input[i2]));
|
|
18913
|
+
view.setInt16(offset, s < 0 ? s * 32768 : s * 32767, true);
|
|
18914
|
+
}
|
|
18915
|
+
};
|
|
18916
|
+
var writeString = (view, offset, str) => {
|
|
18917
|
+
for (let i2 = 0; i2 < str.length; i2++) {
|
|
18918
|
+
view.setUint8(offset + i2, str.charCodeAt(i2));
|
|
18919
|
+
}
|
|
18920
|
+
};
|
|
18921
|
+
var encodeWavPCM16 = (channels, sampleRate) => {
|
|
18922
|
+
const numChannels = channels.length;
|
|
18923
|
+
const numFrames = channels[0]?.length ?? 0;
|
|
18924
|
+
const bytesPerSample = 2;
|
|
18925
|
+
const blockAlign = numChannels * bytesPerSample;
|
|
18926
|
+
const dataSize = numFrames * blockAlign;
|
|
18927
|
+
const buffer = new ArrayBuffer(44 + dataSize);
|
|
18928
|
+
const view = new DataView(buffer);
|
|
18929
|
+
writeString(view, 0, "RIFF");
|
|
18930
|
+
view.setUint32(4, 36 + dataSize, true);
|
|
18931
|
+
writeString(view, 8, "WAVE");
|
|
18932
|
+
writeString(view, 12, "fmt ");
|
|
18933
|
+
view.setUint32(16, 16, true);
|
|
18934
|
+
view.setUint16(20, 1, true);
|
|
18935
|
+
view.setUint16(22, numChannels, true);
|
|
18936
|
+
view.setUint32(24, sampleRate, true);
|
|
18937
|
+
view.setUint32(28, sampleRate * blockAlign, true);
|
|
18938
|
+
view.setUint16(32, blockAlign, true);
|
|
18939
|
+
view.setUint16(34, bytesPerSample * 8, true);
|
|
18940
|
+
writeString(view, 36, "data");
|
|
18941
|
+
view.setUint32(40, dataSize, true);
|
|
18942
|
+
if (numChannels === 1) {
|
|
18943
|
+
floatTo16BitPCM(view, 44, channels[0]);
|
|
18944
|
+
} else {
|
|
18945
|
+
let offset = 44;
|
|
18946
|
+
for (let i2 = 0; i2 < numFrames; i2++) {
|
|
18947
|
+
for (let c = 0; c < numChannels; c++) {
|
|
18948
|
+
const s = Math.max(-1, Math.min(1, channels[c][i2]));
|
|
18949
|
+
view.setInt16(offset, s < 0 ? s * 32768 : s * 32767, true);
|
|
18950
|
+
offset += 2;
|
|
18951
|
+
}
|
|
18952
|
+
}
|
|
18953
|
+
}
|
|
18954
|
+
return new Blob([buffer], { type: "audio/wav" });
|
|
18955
|
+
};
|
|
18956
|
+
var concatFloat32 = (chunks) => {
|
|
18957
|
+
const total = chunks.reduce((sum, c) => sum + c.length, 0);
|
|
18958
|
+
const out = new Float32Array(total);
|
|
18959
|
+
let offset = 0;
|
|
18960
|
+
for (const c of chunks) {
|
|
18961
|
+
out.set(c, offset);
|
|
18962
|
+
offset += c.length;
|
|
18963
|
+
}
|
|
18964
|
+
return out;
|
|
18965
|
+
};
|
|
18966
|
+
|
|
18883
18967
|
// src/studio.ts
|
|
18884
18968
|
var SOUNDFONT_NAME2 = "FluidR3_GM_sf2_file";
|
|
18885
18969
|
var TRACK_ROLES = [
|
|
@@ -19356,6 +19440,28 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19356
19440
|
},
|
|
19357
19441
|
onPlayNote: playNote4,
|
|
19358
19442
|
onPlayDrum: playDrum,
|
|
19443
|
+
onExportWav: async () => {
|
|
19444
|
+
if (!daw) return;
|
|
19445
|
+
daw.stop();
|
|
19446
|
+
await resumeAudio();
|
|
19447
|
+
startWavRecording();
|
|
19448
|
+
await daw.play();
|
|
19449
|
+
await new Promise((resolve) => {
|
|
19450
|
+
const iv = setInterval(() => {
|
|
19451
|
+
if (daw.getPlaybackState() !== "playing") {
|
|
19452
|
+
clearInterval(iv);
|
|
19453
|
+
resolve();
|
|
19454
|
+
}
|
|
19455
|
+
}, 200);
|
|
19456
|
+
});
|
|
19457
|
+
const blob2 = await stopWavRecording();
|
|
19458
|
+
const url2 = URL.createObjectURL(blob2);
|
|
19459
|
+
const a = document.createElement("a");
|
|
19460
|
+
a.href = url2;
|
|
19461
|
+
a.download = "dtm.wav";
|
|
19462
|
+
a.click();
|
|
19463
|
+
URL.revokeObjectURL(url2);
|
|
19464
|
+
},
|
|
19359
19465
|
singingVoices,
|
|
19360
19466
|
parseMidi,
|
|
19361
19467
|
onInstrumentChange: handleInstrumentChange,
|
|
@@ -19943,11 +20049,48 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19943
20049
|
const getAudioStreamTrack = () => {
|
|
19944
20050
|
return createMediaStreamDestination().stream.getAudioTracks()[0];
|
|
19945
20051
|
};
|
|
20052
|
+
let wavTap = null;
|
|
20053
|
+
const startWavRecording = () => {
|
|
20054
|
+
if (wavTap) return;
|
|
20055
|
+
const bufferSize = 4096;
|
|
20056
|
+
const processor = audioCtx.createScriptProcessor(bufferSize, 2, 2);
|
|
20057
|
+
const chunksL = [];
|
|
20058
|
+
const chunksR = [];
|
|
20059
|
+
processor.onaudioprocess = (e) => {
|
|
20060
|
+
chunksL.push(new Float32Array(e.inputBuffer.getChannelData(0)));
|
|
20061
|
+
chunksR.push(
|
|
20062
|
+
new Float32Array(
|
|
20063
|
+
e.inputBuffer.getChannelData(
|
|
20064
|
+
e.inputBuffer.numberOfChannels > 1 ? 1 : 0
|
|
20065
|
+
)
|
|
20066
|
+
)
|
|
20067
|
+
);
|
|
20068
|
+
};
|
|
20069
|
+
const silentSink = audioCtx.createGain();
|
|
20070
|
+
silentSink.gain.value = 0;
|
|
20071
|
+
fadeGain.connect(processor);
|
|
20072
|
+
processor.connect(silentSink);
|
|
20073
|
+
silentSink.connect(audioCtx.destination);
|
|
20074
|
+
wavTap = { processor, silentSink, chunksL, chunksR };
|
|
20075
|
+
};
|
|
20076
|
+
const stopWavRecording = async () => {
|
|
20077
|
+
if (!wavTap) throw new Error("[dtm] startWavRecording \u304C\u547C\u3070\u308C\u3066\u3044\u307E\u305B\u3093");
|
|
20078
|
+
const { processor, silentSink, chunksL, chunksR } = wavTap;
|
|
20079
|
+
fadeGain.disconnect(processor);
|
|
20080
|
+
processor.disconnect(silentSink);
|
|
20081
|
+
silentSink.disconnect(audioCtx.destination);
|
|
20082
|
+
wavTap = null;
|
|
20083
|
+
const left = concatFloat32(chunksL);
|
|
20084
|
+
const right = concatFloat32(chunksR);
|
|
20085
|
+
return encodeWavPCM16([left, right], audioCtx.sampleRate);
|
|
20086
|
+
};
|
|
19946
20087
|
return {
|
|
19947
20088
|
audioContext: audioCtx,
|
|
19948
20089
|
masterGain,
|
|
19949
20090
|
createMediaStreamDestination,
|
|
19950
20091
|
getAudioStreamTrack,
|
|
20092
|
+
startWavRecording,
|
|
20093
|
+
stopWavRecording,
|
|
19951
20094
|
singingVoices,
|
|
19952
20095
|
mountEditor,
|
|
19953
20096
|
mountPlayer,
|
|
@@ -20009,6 +20152,7 @@ export {
|
|
|
20009
20152
|
buildDrumPatternJson,
|
|
20010
20153
|
buildNameToKeyMapping,
|
|
20011
20154
|
collectPitchTokens,
|
|
20155
|
+
concatFloat32,
|
|
20012
20156
|
createAudioContext,
|
|
20013
20157
|
createDtmStudio,
|
|
20014
20158
|
createKlattVoice,
|
|
@@ -20028,6 +20172,7 @@ export {
|
|
|
20028
20172
|
drawSelectedNotes,
|
|
20029
20173
|
drawSelectionRect,
|
|
20030
20174
|
encodeMml,
|
|
20175
|
+
encodeWavPCM16,
|
|
20031
20176
|
exportMIDI,
|
|
20032
20177
|
extractDrumPatternFromNotes,
|
|
20033
20178
|
extractMidiDrumPattern,
|
package/package.json
CHANGED