@onjmin/dtm 0.1.98 → 0.1.100
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 +130 -1
- package/dist/index.mjs +128 -1
- 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,
|
|
@@ -2808,7 +2810,7 @@ var KOE_VOICEBANKS = {
|
|
|
2808
2810
|
mgroid: "MGRoid_\u539F\u97F3\u8A2D\u5B9A\u6E08\u307F.koe",
|
|
2809
2811
|
motroid: "MOTRoid\u5B8C\u5168\u7248V2.koe",
|
|
2810
2812
|
nynroid: "NYNRoidver1.4.koe",
|
|
2811
|
-
uc: "\u84C4\u97F3\u30AD\u30EA\u30B3\
|
|
2813
|
+
uc: "\u84C4\u97F3\u30AD\u30EA\u30B3 \uFF08beta1.0\uFF09.koe"
|
|
2812
2814
|
};
|
|
2813
2815
|
var KOE_VOICEBANK_LABELS = {
|
|
2814
2816
|
tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093",
|
|
@@ -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"),
|
|
@@ -17153,6 +17157,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
17153
17157
|
a.click();
|
|
17154
17158
|
URL.revokeObjectURL(url2);
|
|
17155
17159
|
});
|
|
17160
|
+
if (options.onExportWav) {
|
|
17161
|
+
refs.exportWavBtn.classList.remove("dtm-hidden");
|
|
17162
|
+
refs.exportWavBtn.addEventListener("click", () => {
|
|
17163
|
+
void options.onExportWav?.();
|
|
17164
|
+
});
|
|
17165
|
+
}
|
|
17156
17166
|
const copy = (text, btn) => {
|
|
17157
17167
|
navigator.clipboard?.writeText(text);
|
|
17158
17168
|
btn.classList.add("dtm-btn--success");
|
|
@@ -19029,6 +19039,64 @@ var SoundFont_list = new class {
|
|
|
19029
19039
|
}
|
|
19030
19040
|
}();
|
|
19031
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
|
+
|
|
19032
19100
|
// src/studio.ts
|
|
19033
19101
|
var import_meta = {};
|
|
19034
19102
|
var SOUNDFONT_NAME2 = "FluidR3_GM_sf2_file";
|
|
@@ -19506,6 +19574,28 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19506
19574
|
},
|
|
19507
19575
|
onPlayNote: playNote4,
|
|
19508
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
|
+
},
|
|
19509
19599
|
singingVoices,
|
|
19510
19600
|
parseMidi,
|
|
19511
19601
|
onInstrumentChange: handleInstrumentChange,
|
|
@@ -20093,11 +20183,48 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20093
20183
|
const getAudioStreamTrack = () => {
|
|
20094
20184
|
return createMediaStreamDestination().stream.getAudioTracks()[0];
|
|
20095
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
|
+
};
|
|
20096
20221
|
return {
|
|
20097
20222
|
audioContext: audioCtx,
|
|
20098
20223
|
masterGain,
|
|
20099
20224
|
createMediaStreamDestination,
|
|
20100
20225
|
getAudioStreamTrack,
|
|
20226
|
+
startWavRecording,
|
|
20227
|
+
stopWavRecording,
|
|
20101
20228
|
singingVoices,
|
|
20102
20229
|
mountEditor,
|
|
20103
20230
|
mountPlayer,
|
|
@@ -20160,6 +20287,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20160
20287
|
buildDrumPatternJson,
|
|
20161
20288
|
buildNameToKeyMapping,
|
|
20162
20289
|
collectPitchTokens,
|
|
20290
|
+
concatFloat32,
|
|
20163
20291
|
createAudioContext,
|
|
20164
20292
|
createDtmStudio,
|
|
20165
20293
|
createKlattVoice,
|
|
@@ -20179,6 +20307,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20179
20307
|
drawSelectedNotes,
|
|
20180
20308
|
drawSelectionRect,
|
|
20181
20309
|
encodeMml,
|
|
20310
|
+
encodeWavPCM16,
|
|
20182
20311
|
exportMIDI,
|
|
20183
20312
|
extractDrumPatternFromNotes,
|
|
20184
20313
|
extractMidiDrumPattern,
|
package/dist/index.mjs
CHANGED
|
@@ -2677,7 +2677,7 @@ var KOE_VOICEBANKS = {
|
|
|
2677
2677
|
mgroid: "MGRoid_\u539F\u97F3\u8A2D\u5B9A\u6E08\u307F.koe",
|
|
2678
2678
|
motroid: "MOTRoid\u5B8C\u5168\u7248V2.koe",
|
|
2679
2679
|
nynroid: "NYNRoidver1.4.koe",
|
|
2680
|
-
uc: "\u84C4\u97F3\u30AD\u30EA\u30B3\
|
|
2680
|
+
uc: "\u84C4\u97F3\u30AD\u30EA\u30B3 \uFF08beta1.0\uFF09.koe"
|
|
2681
2681
|
};
|
|
2682
2682
|
var KOE_VOICEBANK_LABELS = {
|
|
2683
2683
|
tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093",
|
|
@@ -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"),
|
|
@@ -17022,6 +17024,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
17022
17024
|
a.click();
|
|
17023
17025
|
URL.revokeObjectURL(url2);
|
|
17024
17026
|
});
|
|
17027
|
+
if (options.onExportWav) {
|
|
17028
|
+
refs.exportWavBtn.classList.remove("dtm-hidden");
|
|
17029
|
+
refs.exportWavBtn.addEventListener("click", () => {
|
|
17030
|
+
void options.onExportWav?.();
|
|
17031
|
+
});
|
|
17032
|
+
}
|
|
17025
17033
|
const copy = (text, btn) => {
|
|
17026
17034
|
navigator.clipboard?.writeText(text);
|
|
17027
17035
|
btn.classList.add("dtm-btn--success");
|
|
@@ -18898,6 +18906,64 @@ var SoundFont_list = new class {
|
|
|
18898
18906
|
}
|
|
18899
18907
|
}();
|
|
18900
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
|
+
|
|
18901
18967
|
// src/studio.ts
|
|
18902
18968
|
var SOUNDFONT_NAME2 = "FluidR3_GM_sf2_file";
|
|
18903
18969
|
var TRACK_ROLES = [
|
|
@@ -19374,6 +19440,28 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19374
19440
|
},
|
|
19375
19441
|
onPlayNote: playNote4,
|
|
19376
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
|
+
},
|
|
19377
19465
|
singingVoices,
|
|
19378
19466
|
parseMidi,
|
|
19379
19467
|
onInstrumentChange: handleInstrumentChange,
|
|
@@ -19961,11 +20049,48 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19961
20049
|
const getAudioStreamTrack = () => {
|
|
19962
20050
|
return createMediaStreamDestination().stream.getAudioTracks()[0];
|
|
19963
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
|
+
};
|
|
19964
20087
|
return {
|
|
19965
20088
|
audioContext: audioCtx,
|
|
19966
20089
|
masterGain,
|
|
19967
20090
|
createMediaStreamDestination,
|
|
19968
20091
|
getAudioStreamTrack,
|
|
20092
|
+
startWavRecording,
|
|
20093
|
+
stopWavRecording,
|
|
19969
20094
|
singingVoices,
|
|
19970
20095
|
mountEditor,
|
|
19971
20096
|
mountPlayer,
|
|
@@ -20027,6 +20152,7 @@ export {
|
|
|
20027
20152
|
buildDrumPatternJson,
|
|
20028
20153
|
buildNameToKeyMapping,
|
|
20029
20154
|
collectPitchTokens,
|
|
20155
|
+
concatFloat32,
|
|
20030
20156
|
createAudioContext,
|
|
20031
20157
|
createDtmStudio,
|
|
20032
20158
|
createKlattVoice,
|
|
@@ -20046,6 +20172,7 @@ export {
|
|
|
20046
20172
|
drawSelectedNotes,
|
|
20047
20173
|
drawSelectionRect,
|
|
20048
20174
|
encodeMml,
|
|
20175
|
+
encodeWavPCM16,
|
|
20049
20176
|
exportMIDI,
|
|
20050
20177
|
extractDrumPatternFromNotes,
|
|
20051
20178
|
extractMidiDrumPattern,
|
package/package.json
CHANGED