@onjmin/dtm 0.1.25 → 0.1.27
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/README.md +1 -2
- package/dist/index.d.mts +4 -16
- package/dist/index.d.ts +4 -16
- package/dist/index.js +90 -117
- package/dist/index.mjs +90 -116
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7,34 +7,6 @@ function createAudioContext() {
|
|
|
7
7
|
drumGainNode.connect(audioCtx.destination);
|
|
8
8
|
return { audioCtx, gainNode, drumGainNode };
|
|
9
9
|
}
|
|
10
|
-
function setupRecorder(audioCtx, gainNode, drumGainNode) {
|
|
11
|
-
let isRecording = false;
|
|
12
|
-
let recordedData = [[], []];
|
|
13
|
-
const recorderProcessor = audioCtx.createScriptProcessor(4096, 2, 2);
|
|
14
|
-
recorderProcessor.onaudioprocess = (e) => {
|
|
15
|
-
if (!isRecording) return;
|
|
16
|
-
const left = e.inputBuffer.getChannelData(0);
|
|
17
|
-
const right = e.inputBuffer.getChannelData(1);
|
|
18
|
-
recordedData[0].push(left.slice());
|
|
19
|
-
recordedData[1].push(right.slice());
|
|
20
|
-
};
|
|
21
|
-
gainNode.connect(recorderProcessor);
|
|
22
|
-
drumGainNode.connect(recorderProcessor);
|
|
23
|
-
recorderProcessor.connect(audioCtx.destination);
|
|
24
|
-
return {
|
|
25
|
-
startRecording: () => {
|
|
26
|
-
isRecording = true;
|
|
27
|
-
},
|
|
28
|
-
stopRecording: () => {
|
|
29
|
-
isRecording = false;
|
|
30
|
-
},
|
|
31
|
-
getRecordedData: () => recordedData,
|
|
32
|
-
isRecording: () => isRecording,
|
|
33
|
-
clearRecordedData: () => {
|
|
34
|
-
recordedData = [[], []];
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
10
|
async function fetchSoundFontList(ttl) {
|
|
39
11
|
const res = await fetch(`https://rpgen3.github.io/soundfont/list/${ttl}.txt`);
|
|
40
12
|
const str = await res.text();
|
|
@@ -1131,8 +1103,9 @@ var buildUI = (target, options) => {
|
|
|
1131
1103
|
target.innerHTML = `
|
|
1132
1104
|
<div class="dtm-daw" data-dtm="root">
|
|
1133
1105
|
<div class="dtm-topbar" data-dtm="transport">
|
|
1106
|
+
<button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
|
|
1134
1107
|
<button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
|
|
1135
|
-
<button class="dtm-iconbtn
|
|
1108
|
+
<button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
|
|
1136
1109
|
<label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
|
|
1137
1110
|
<span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
|
|
1138
1111
|
<span class="dtm-grow"></span>
|
|
@@ -1297,10 +1270,12 @@ var buildUI = (target, options) => {
|
|
|
1297
1270
|
</div>
|
|
1298
1271
|
<div class="dtm-output dtm-hidden" data-dtm="output-container">
|
|
1299
1272
|
<p class="dtm-label" data-dtm="output-status"></p>
|
|
1273
|
+
<div class="dtm-output-label">\u6539\u884C\u3042\u308A\u7248</div>
|
|
1300
1274
|
<div class="dtm-output-row">
|
|
1301
1275
|
<pre><code data-dtm="output-full"></code></pre>
|
|
1302
1276
|
<button class="dtm-btn dtm-btn--primary dtm-btn--icon" data-dtm="copy-full" title="\u30B3\u30D4\u30FC">${icon("copy")}</button>
|
|
1303
1277
|
</div>
|
|
1278
|
+
<div class="dtm-output-label">\uFF11\u884C\u7248</div>
|
|
1304
1279
|
<div class="dtm-output-row">
|
|
1305
1280
|
<pre><code data-dtm="output-mini"></code></pre>
|
|
1306
1281
|
<button class="dtm-btn dtm-btn--primary dtm-btn--icon" data-dtm="copy-mini" title="\u30B3\u30D4\u30FC">${icon("copy")}</button>
|
|
@@ -1328,7 +1303,8 @@ var buildUI = (target, options) => {
|
|
|
1328
1303
|
topbar: sel("transport"),
|
|
1329
1304
|
topbarLoading: sel("topbar-loading"),
|
|
1330
1305
|
playBtn: sel("play"),
|
|
1331
|
-
|
|
1306
|
+
prevBarBtn: sel("prev-bar"),
|
|
1307
|
+
nextBarBtn: sel("next-bar"),
|
|
1332
1308
|
soloCheckbox: sel("solo"),
|
|
1333
1309
|
toolPen: sel("tool-pen"),
|
|
1334
1310
|
toolSelect: sel("tool-select"),
|
|
@@ -2633,7 +2609,7 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
2633
2609
|
return model;
|
|
2634
2610
|
};
|
|
2635
2611
|
var PREWARM_NOTES = 3;
|
|
2636
|
-
var STREAM_LOOKAHEAD_SEC =
|
|
2612
|
+
var STREAM_LOOKAHEAD_SEC = 6;
|
|
2637
2613
|
var STREAM_POLL_MS = 100;
|
|
2638
2614
|
var FALLBACK_MODEL = "klatt";
|
|
2639
2615
|
var createSingingVoices = (ctx, destination, options = {}) => {
|
|
@@ -2697,7 +2673,7 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2697
2673
|
if (!m?.renderToCache) continue;
|
|
2698
2674
|
let n = 0;
|
|
2699
2675
|
forEachSungNote(track, (note, prevVowel) => {
|
|
2700
|
-
if (n >= count) return;
|
|
2676
|
+
if (n >= count && note.startSec >= STREAM_LOOKAHEAD_SEC) return;
|
|
2701
2677
|
n++;
|
|
2702
2678
|
promises.push(
|
|
2703
2679
|
m.renderToCache?.(
|
|
@@ -2730,14 +2706,18 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2730
2706
|
if (opts?.isAudible && !opts.isAudible(track)) continue;
|
|
2731
2707
|
const t0 = anchorTime + note.startSec;
|
|
2732
2708
|
if (model.renderToCache && model.scheduleCached) {
|
|
2733
|
-
const
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2709
|
+
const renderToCache = model.renderToCache;
|
|
2710
|
+
const scheduleCached = model.scheduleCached;
|
|
2711
|
+
void (async () => {
|
|
2712
|
+
const key = await renderToCache(
|
|
2713
|
+
note.syllable,
|
|
2714
|
+
prevVowel,
|
|
2715
|
+
note.pitch,
|
|
2716
|
+
note.durationSec * 1e3
|
|
2717
|
+
);
|
|
2718
|
+
if (session !== streamSession) return;
|
|
2719
|
+
if (key) scheduleCached(key, t0, peak, track.pan);
|
|
2720
|
+
})();
|
|
2741
2721
|
} else {
|
|
2742
2722
|
const when = t0 - ctx.currentTime;
|
|
2743
2723
|
model(note.syllable, {
|
|
@@ -4146,7 +4126,7 @@ var PITCH_MAP2 = {
|
|
|
4146
4126
|
b: 11
|
|
4147
4127
|
};
|
|
4148
4128
|
var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
4149
|
-
var META_DIRECTIVE = /#(inst|drum|volume|mode)=([\w-]+)/gi;
|
|
4129
|
+
var META_DIRECTIVE = /#(inst|drum|volume|drumvolume|mode)=([\w-]+)/gi;
|
|
4150
4130
|
var parseMmlMeta = (mml) => {
|
|
4151
4131
|
const meta = {};
|
|
4152
4132
|
for (const m of mml.matchAll(META_DIRECTIVE)) {
|
|
@@ -4156,6 +4136,9 @@ var parseMmlMeta = (mml) => {
|
|
|
4156
4136
|
else if (key === "volume") {
|
|
4157
4137
|
const v = Number.parseInt(m[2], 10);
|
|
4158
4138
|
if (!Number.isNaN(v)) meta.volume = v;
|
|
4139
|
+
} else if (key === "drumvolume") {
|
|
4140
|
+
const dv = Number.parseInt(m[2], 10);
|
|
4141
|
+
if (!Number.isNaN(dv)) meta.drumVolume = dv;
|
|
4159
4142
|
} else if (key === "mode") {
|
|
4160
4143
|
if (m[2] === "simple" || m[2] === "advanced") {
|
|
4161
4144
|
meta.mode = m[2];
|
|
@@ -4170,6 +4153,8 @@ var formatMmlMeta = (meta) => {
|
|
|
4170
4153
|
if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
|
|
4171
4154
|
if (meta.drum) parts.push(`#drum=${meta.drum}`);
|
|
4172
4155
|
if (meta.volume !== void 0) parts.push(`#volume=${meta.volume}`);
|
|
4156
|
+
if (meta.drumVolume !== void 0)
|
|
4157
|
+
parts.push(`#drumvolume=${meta.drumVolume}`);
|
|
4173
4158
|
if (meta.mode) parts.push(`#mode=${meta.mode}`);
|
|
4174
4159
|
return parts.join(" ");
|
|
4175
4160
|
};
|
|
@@ -4891,7 +4876,7 @@ var DAW_CSS = `
|
|
|
4891
4876
|
top: 0;
|
|
4892
4877
|
z-index: 20;
|
|
4893
4878
|
display: flex;
|
|
4894
|
-
flex-wrap:
|
|
4879
|
+
flex-wrap: nowrap;
|
|
4895
4880
|
align-items: center;
|
|
4896
4881
|
gap: var(--dtm-gap);
|
|
4897
4882
|
padding: 6px;
|
|
@@ -4901,6 +4886,17 @@ var DAW_CSS = `
|
|
|
4901
4886
|
inset 0 0 0 2px var(--c-black),
|
|
4902
4887
|
0 0 0 2px var(--dtm-success),
|
|
4903
4888
|
4px 4px 0 var(--c-black);
|
|
4889
|
+
overflow-x: auto;
|
|
4890
|
+
scrollbar-width: none;
|
|
4891
|
+
}
|
|
4892
|
+
.dtm-topbar::-webkit-scrollbar {
|
|
4893
|
+
display: none;
|
|
4894
|
+
}
|
|
4895
|
+
.dtm-topbar > * {
|
|
4896
|
+
flex-shrink: 0;
|
|
4897
|
+
}
|
|
4898
|
+
.dtm-topbar > .dtm-grow {
|
|
4899
|
+
flex-shrink: 1;
|
|
4904
4900
|
}
|
|
4905
4901
|
|
|
4906
4902
|
/* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */
|
|
@@ -5251,6 +5247,15 @@ var DAW_CSS = `
|
|
|
5251
5247
|
line-height: 1.8;
|
|
5252
5248
|
color: var(--dtm-success);
|
|
5253
5249
|
}
|
|
5250
|
+
.dtm-output-label {
|
|
5251
|
+
font-size: 11px;
|
|
5252
|
+
color: var(--dtm-muted);
|
|
5253
|
+
font-family: var(--dtm-font);
|
|
5254
|
+
margin-top: 10px;
|
|
5255
|
+
}
|
|
5256
|
+
.dtm-output-label:first-of-type {
|
|
5257
|
+
margin-top: 0;
|
|
5258
|
+
}
|
|
5254
5259
|
.dtm-output-row { display: flex; gap: 8px; align-items: flex-start; margin-top: 6px; }
|
|
5255
5260
|
.dtm-output-row pre { flex: 1; }
|
|
5256
5261
|
|
|
@@ -6094,6 +6099,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6094
6099
|
const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
|
|
6095
6100
|
const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
|
|
6096
6101
|
const trackVolume = meta.volume ?? options.volume ?? 100;
|
|
6102
|
+
const drumVolume = meta.drumVolume ?? 80;
|
|
6097
6103
|
const colors = options.trackColors ?? DEFAULT_TRACK_COLORS;
|
|
6098
6104
|
const useSynth = options.synth ?? !options.onPlayNote;
|
|
6099
6105
|
const secondsPerStep = 60 / bpm / STEPS_PER_BEAT3;
|
|
@@ -6710,9 +6716,9 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6710
6716
|
}
|
|
6711
6717
|
target.appendChild(root);
|
|
6712
6718
|
let consentOverlayEl = null;
|
|
6713
|
-
const checkConsentAndShow = () => {
|
|
6719
|
+
const checkConsentAndShow = (onAgree) => {
|
|
6714
6720
|
try {
|
|
6715
|
-
if (options.skipConsent) return;
|
|
6721
|
+
if (options.skipConsent) return false;
|
|
6716
6722
|
const unagreed = termsModels.filter((model) => {
|
|
6717
6723
|
if (agreedModelsInSession.has(model)) return false;
|
|
6718
6724
|
try {
|
|
@@ -6726,7 +6732,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6726
6732
|
return true;
|
|
6727
6733
|
}
|
|
6728
6734
|
});
|
|
6729
|
-
if (unagreed.length === 0) return;
|
|
6735
|
+
if (unagreed.length === 0) return false;
|
|
6730
6736
|
const consentOverlay = doc.createElement("div");
|
|
6731
6737
|
consentOverlay.className = "dtm-consent-overlay";
|
|
6732
6738
|
const modal = doc.createElement("div");
|
|
@@ -6769,17 +6775,19 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6769
6775
|
}
|
|
6770
6776
|
consentOverlay.remove();
|
|
6771
6777
|
consentOverlayEl = null;
|
|
6778
|
+
if (onAgree) onAgree();
|
|
6772
6779
|
};
|
|
6773
6780
|
footer.appendChild(btn);
|
|
6774
6781
|
modal.append(header, body2, footer);
|
|
6775
6782
|
consentOverlay.appendChild(modal);
|
|
6776
6783
|
doc.body.appendChild(consentOverlay);
|
|
6777
6784
|
consentOverlayEl = consentOverlay;
|
|
6785
|
+
return true;
|
|
6778
6786
|
} catch (err2) {
|
|
6779
6787
|
console.error("[dtm-player] Error in checkConsentAndShow:", err2);
|
|
6788
|
+
return false;
|
|
6780
6789
|
}
|
|
6781
6790
|
};
|
|
6782
|
-
checkConsentAndShow();
|
|
6783
6791
|
const autoScroll = (lane, el) => {
|
|
6784
6792
|
if (el.offsetWidth === 0 || lane.clientWidth === 0) return;
|
|
6785
6793
|
const elementCenter = el.offsetLeft + el.offsetWidth / 2;
|
|
@@ -6838,7 +6846,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6838
6846
|
if (useSynth) ensureSynth().playNote(e);
|
|
6839
6847
|
},
|
|
6840
6848
|
onPlayDrum: (e) => {
|
|
6841
|
-
const velocity = e.velocity * (trackVolume / 100);
|
|
6849
|
+
const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100);
|
|
6842
6850
|
options.onPlayDrum?.({ ...e, velocity });
|
|
6843
6851
|
if (useSynth) ensureSynth().playDrum({ ...e, velocity });
|
|
6844
6852
|
},
|
|
@@ -6912,6 +6920,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
6912
6920
|
};
|
|
6913
6921
|
const play = () => {
|
|
6914
6922
|
if (playing || trackIndices.length === 0) return;
|
|
6923
|
+
if (checkConsentAndShow(() => play())) return;
|
|
6915
6924
|
if (activePlayer && activePlayer !== instance) activePlayer.stop();
|
|
6916
6925
|
activePlayer = instance;
|
|
6917
6926
|
setPlayingUI(true);
|
|
@@ -7940,7 +7949,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7940
7949
|
};
|
|
7941
7950
|
const updateTransport = () => {
|
|
7942
7951
|
const playing = playbackState === "playing";
|
|
7943
|
-
const label = playing ? "\
|
|
7952
|
+
const label = playing ? "\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
|
|
7944
7953
|
refs.playBtn.innerHTML = `${icon(playing ? "pause" : "play")}<span>${label}</span>`;
|
|
7945
7954
|
refs.playBtn.classList.toggle("dtm-play--stop", playing);
|
|
7946
7955
|
};
|
|
@@ -8208,6 +8217,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
8208
8217
|
instrument: currentInstrument || void 0,
|
|
8209
8218
|
drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
|
|
8210
8219
|
volume: masterVolume,
|
|
8220
|
+
drumVolume,
|
|
8211
8221
|
mode
|
|
8212
8222
|
});
|
|
8213
8223
|
if (refs.decomposeChordToggle.checked) {
|
|
@@ -8359,6 +8369,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
8359
8369
|
refs.masterVolume.value = String(meta.volume);
|
|
8360
8370
|
refs.masterVolumeLabel.textContent = `${meta.volume}%`;
|
|
8361
8371
|
}
|
|
8372
|
+
if (meta.drumVolume !== void 0) {
|
|
8373
|
+
drumVolume = meta.drumVolume;
|
|
8374
|
+
refs.drumVolume.value = String(meta.drumVolume);
|
|
8375
|
+
refs.drumVolumeLabel.textContent = `${meta.drumVolume}%`;
|
|
8376
|
+
}
|
|
8362
8377
|
for (const t of trackStates) {
|
|
8363
8378
|
t.lyrics = "";
|
|
8364
8379
|
t.lyricModel = "";
|
|
@@ -8512,8 +8527,17 @@ var mountDAW = (target, options = {}) => {
|
|
|
8512
8527
|
const wireEvents = () => {
|
|
8513
8528
|
refs.playBtn.addEventListener("click", togglePlay);
|
|
8514
8529
|
refs.playBtn.disabled = false;
|
|
8515
|
-
refs.
|
|
8516
|
-
|
|
8530
|
+
refs.prevBarBtn.addEventListener("click", () => {
|
|
8531
|
+
const targetStep = Math.max(
|
|
8532
|
+
0,
|
|
8533
|
+
Math.floor((getCurrentPlayStep() - 1) / renderConfig.stepsPerBar) * renderConfig.stepsPerBar
|
|
8534
|
+
);
|
|
8535
|
+
jumpTo(targetStep);
|
|
8536
|
+
});
|
|
8537
|
+
refs.nextBarBtn.addEventListener("click", () => {
|
|
8538
|
+
const targetStep = Math.floor(getCurrentPlayStep() / renderConfig.stepsPerBar + 1) * renderConfig.stepsPerBar;
|
|
8539
|
+
jumpTo(targetStep);
|
|
8540
|
+
});
|
|
8517
8541
|
refs.soloCheckbox.addEventListener("change", () => {
|
|
8518
8542
|
isSolo = refs.soloCheckbox.checked;
|
|
8519
8543
|
});
|
|
@@ -8876,6 +8900,20 @@ var mountDAW = (target, options = {}) => {
|
|
|
8876
8900
|
if (playbackState === "paused") return pausedPlayStep;
|
|
8877
8901
|
return playStartStep;
|
|
8878
8902
|
};
|
|
8903
|
+
const jumpTo = async (step) => {
|
|
8904
|
+
const wasPlaying = playbackState === "playing";
|
|
8905
|
+
if (wasPlaying) {
|
|
8906
|
+
sequencer.stop();
|
|
8907
|
+
options.singingVoices?.stopStream();
|
|
8908
|
+
playStartStep = step;
|
|
8909
|
+
pausedPlayStep = step;
|
|
8910
|
+
currentPlayStep = step;
|
|
8911
|
+
playbackState = "paused";
|
|
8912
|
+
await play();
|
|
8913
|
+
} else {
|
|
8914
|
+
forcePauseAt(step);
|
|
8915
|
+
}
|
|
8916
|
+
};
|
|
8879
8917
|
const forcePauseAt = (step) => {
|
|
8880
8918
|
playStartStep = step;
|
|
8881
8919
|
pausedPlayStep = step;
|
|
@@ -8950,6 +8988,7 @@ var playMML = (mml, options = {}) => {
|
|
|
8950
8988
|
const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
|
|
8951
8989
|
const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
|
|
8952
8990
|
let masterVolume = meta.volume ?? options.volume ?? 100;
|
|
8991
|
+
let drumVolume = meta.drumVolume ?? 80;
|
|
8953
8992
|
const trackIndices = [...new Set(placements.map((p) => p.trackIndex))].sort(
|
|
8954
8993
|
(a, b) => a - b
|
|
8955
8994
|
);
|
|
@@ -8986,7 +9025,7 @@ var playMML = (mml, options = {}) => {
|
|
|
8986
9025
|
synth?.playNote(e);
|
|
8987
9026
|
},
|
|
8988
9027
|
onPlayDrum: (e) => {
|
|
8989
|
-
const velocity = e.velocity * (masterVolume / 100);
|
|
9028
|
+
const velocity = e.velocity * (drumVolume / 100) * (masterVolume / 100);
|
|
8990
9029
|
options.onPlayDrum?.({ ...e, velocity });
|
|
8991
9030
|
synth?.playDrum({ ...e, velocity });
|
|
8992
9031
|
},
|
|
@@ -9544,7 +9583,6 @@ var importFrom = async (url2, name) => {
|
|
|
9544
9583
|
var createDtmStudio = async (options = {}) => {
|
|
9545
9584
|
const cdn = { ...DEFAULT_CDN, ...options.cdn };
|
|
9546
9585
|
const features = {
|
|
9547
|
-
recorder: true,
|
|
9548
9586
|
midi: true,
|
|
9549
9587
|
chord: true,
|
|
9550
9588
|
presetUI: true,
|
|
@@ -9583,68 +9621,6 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9583
9621
|
const singingVoices = createSingingVoices(audioCtx, masterGain, {
|
|
9584
9622
|
voiceWorkerUrl
|
|
9585
9623
|
});
|
|
9586
|
-
const recorder = features.recorder ? setupRecorder(audioCtx, masterGain, drumGain) : null;
|
|
9587
|
-
const downloadWav = () => {
|
|
9588
|
-
if (!recorder) return;
|
|
9589
|
-
const recordedData = recorder.getRecordedData();
|
|
9590
|
-
const ch = recordedData.length;
|
|
9591
|
-
const len = recordedData[0].length;
|
|
9592
|
-
if (len === 0) return;
|
|
9593
|
-
const bufSize = recordedData[0][0].length;
|
|
9594
|
-
const wave = new Float32Array(ch * len * bufSize);
|
|
9595
|
-
let idx = 0;
|
|
9596
|
-
for (let i = 0; i < len; i++)
|
|
9597
|
-
for (let j = 0; j < bufSize; j++)
|
|
9598
|
-
for (let k = 0; k < ch; k++) wave[idx++] = recordedData[k][i][j];
|
|
9599
|
-
const sampleRate = audioCtx.sampleRate;
|
|
9600
|
-
const channels = 2;
|
|
9601
|
-
const bitRate = 16;
|
|
9602
|
-
const step = bitRate / 8;
|
|
9603
|
-
const blockSize = channels * step;
|
|
9604
|
-
const byteLen = wave.length * step;
|
|
9605
|
-
const view = new DataView(new ArrayBuffer(44 + byteLen));
|
|
9606
|
-
const ws = (off2, s) => {
|
|
9607
|
-
for (let i = 0; i < s.length; i++)
|
|
9608
|
-
view.setUint8(off2 + i, s.charCodeAt(i));
|
|
9609
|
-
};
|
|
9610
|
-
ws(0, "RIFF");
|
|
9611
|
-
view.setUint32(4, 32 + byteLen, true);
|
|
9612
|
-
ws(8, "WAVE");
|
|
9613
|
-
ws(12, "fmt ");
|
|
9614
|
-
view.setUint32(16, 16, true);
|
|
9615
|
-
view.setUint16(20, 1, true);
|
|
9616
|
-
view.setUint16(22, channels, true);
|
|
9617
|
-
view.setUint32(24, sampleRate, true);
|
|
9618
|
-
view.setUint32(28, sampleRate * blockSize, true);
|
|
9619
|
-
view.setUint16(32, blockSize, true);
|
|
9620
|
-
view.setUint16(34, bitRate, true);
|
|
9621
|
-
ws(36, "data");
|
|
9622
|
-
view.setUint32(40, byteLen, true);
|
|
9623
|
-
const clamp4 = (n, a2, b) => Math.max(a2, Math.min(b, n));
|
|
9624
|
-
let off = 44;
|
|
9625
|
-
for (let i = 0; i < wave.length; i++, off += step)
|
|
9626
|
-
view.setInt16(
|
|
9627
|
-
off,
|
|
9628
|
-
clamp4(Math.round(wave[i] * 32768), -32768, 32767),
|
|
9629
|
-
true
|
|
9630
|
-
);
|
|
9631
|
-
const blob2 = new Blob([view], { type: "audio/wav" });
|
|
9632
|
-
const url2 = URL.createObjectURL(blob2);
|
|
9633
|
-
const a = document.createElement("a");
|
|
9634
|
-
a.href = url2;
|
|
9635
|
-
a.download = "record.wav";
|
|
9636
|
-
a.click();
|
|
9637
|
-
URL.revokeObjectURL(url2);
|
|
9638
|
-
};
|
|
9639
|
-
const onToggleRecord = recorder ? () => {
|
|
9640
|
-
if (recorder.isRecording()) {
|
|
9641
|
-
recorder.stopRecording();
|
|
9642
|
-
downloadWav();
|
|
9643
|
-
} else {
|
|
9644
|
-
recorder.clearRecordedData();
|
|
9645
|
-
recorder.startRecording();
|
|
9646
|
-
}
|
|
9647
|
-
} : void 0;
|
|
9648
9624
|
const listReady = new Promise((resolve) => {
|
|
9649
9625
|
SoundFont_list.init();
|
|
9650
9626
|
SoundFont_list.onload(() => resolve());
|
|
@@ -9851,7 +9827,6 @@ var createDtmStudio = async (options = {}) => {
|
|
|
9851
9827
|
onPlayDrum: playDrum,
|
|
9852
9828
|
singingVoices,
|
|
9853
9829
|
parseMidi,
|
|
9854
|
-
onToggleRecord,
|
|
9855
9830
|
onInstrumentChange: handleInstrumentChange,
|
|
9856
9831
|
...dawOverrides
|
|
9857
9832
|
};
|
|
@@ -10142,7 +10117,6 @@ export {
|
|
|
10142
10117
|
playMML,
|
|
10143
10118
|
playSingingMML,
|
|
10144
10119
|
setDrawOffset,
|
|
10145
|
-
setupRecorder,
|
|
10146
10120
|
shiftNotes,
|
|
10147
10121
|
stripLyrics,
|
|
10148
10122
|
stripMmlMeta,
|
package/package.json
CHANGED