@onjmin/dtm 0.1.39 → 0.1.40
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.js +138 -35
- package/dist/index.mjs +138 -35
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7699,7 +7699,6 @@ var mountDAW = (target, options = {}) => {
|
|
|
7699
7699
|
stepWidth: BASE_STEP_WIDTH * 2
|
|
7700
7700
|
// zoom100% 相当
|
|
7701
7701
|
};
|
|
7702
|
-
const leftPaddingSteps = renderConfig.stepsPerBar * 16;
|
|
7703
7702
|
let zoomX = 100;
|
|
7704
7703
|
let zoomY = 100;
|
|
7705
7704
|
let bpm = options.defaultBpm ?? DEFAULT_BPM;
|
|
@@ -7713,8 +7712,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
7713
7712
|
let snapGridSteps = 12;
|
|
7714
7713
|
const gridLineSteps = 48;
|
|
7715
7714
|
let currentOffsetX = 0;
|
|
7716
|
-
const _initPitch = options.initialScrollPitch;
|
|
7717
|
-
let currentOffsetY =
|
|
7715
|
+
const _initPitch = options.initialScrollPitch ?? 48;
|
|
7716
|
+
let currentOffsetY = (renderConfig.keyCount - 1 - _initPitch) * renderConfig.keyHeight - 215;
|
|
7718
7717
|
let playStartStep = 0;
|
|
7719
7718
|
let isSolo = false;
|
|
7720
7719
|
let lyricTrackIndices = /* @__PURE__ */ new Set();
|
|
@@ -7722,6 +7721,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7722
7721
|
let pausedPlayStep = 0;
|
|
7723
7722
|
let currentPlayStep = 0;
|
|
7724
7723
|
let ready = false;
|
|
7724
|
+
let isLoading = false;
|
|
7725
7725
|
let selectedNotes = [];
|
|
7726
7726
|
let selectionRect = null;
|
|
7727
7727
|
let copiedNotes = [];
|
|
@@ -7741,7 +7741,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7741
7741
|
};
|
|
7742
7742
|
if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
|
|
7743
7743
|
lyricsDebounceTimer = setTimeout(() => {
|
|
7744
|
-
options.onLyricsChange(trackId, data);
|
|
7744
|
+
options.onLyricsChange?.(trackId, data);
|
|
7745
7745
|
lyricsDebounceTimer = null;
|
|
7746
7746
|
}, 300);
|
|
7747
7747
|
};
|
|
@@ -7821,14 +7821,28 @@ var mountDAW = (target, options = {}) => {
|
|
|
7821
7821
|
const getActive = () => trackStates.find((t) => t.config.id === activeTrackId) ?? trackStates[0];
|
|
7822
7822
|
let showModal;
|
|
7823
7823
|
const getMaxNoteStep = () => {
|
|
7824
|
-
let
|
|
7824
|
+
let maxEndStep = 0;
|
|
7825
7825
|
for (const t of trackStates) {
|
|
7826
7826
|
for (const n of t.core.getNotes()) {
|
|
7827
7827
|
const end = n.startStep + n.durationSteps;
|
|
7828
|
-
if (end >
|
|
7828
|
+
if (end > maxEndStep) {
|
|
7829
|
+
maxEndStep = end;
|
|
7830
|
+
}
|
|
7829
7831
|
}
|
|
7830
7832
|
}
|
|
7831
|
-
|
|
7833
|
+
if (maxEndStep === 0) {
|
|
7834
|
+
return renderConfig.stepsPerBar * 4;
|
|
7835
|
+
}
|
|
7836
|
+
const lastNoteMeasure = Math.floor(
|
|
7837
|
+
(maxEndStep - 1) / renderConfig.stepsPerBar
|
|
7838
|
+
);
|
|
7839
|
+
return (lastNoteMeasure + 2) * renderConfig.stepsPerBar;
|
|
7840
|
+
};
|
|
7841
|
+
const getMaxOffsetX = () => {
|
|
7842
|
+
const canvas = getGridCanvas();
|
|
7843
|
+
const maxNoteStep = getMaxNoteStep();
|
|
7844
|
+
const totalContentWidth = maxNoteStep * renderConfig.stepWidth;
|
|
7845
|
+
return Math.max(0, totalContentWidth - canvas.width);
|
|
7832
7846
|
};
|
|
7833
7847
|
const getMaxOffsetY = () => {
|
|
7834
7848
|
const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
|
|
@@ -7908,19 +7922,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
7908
7922
|
};
|
|
7909
7923
|
const updateScrollbars = () => {
|
|
7910
7924
|
const canvas = getGridCanvas();
|
|
7911
|
-
const
|
|
7912
|
-
const leftPaddingWidth = leftPaddingSteps * renderConfig.stepWidth;
|
|
7913
|
-
const totalContentWidth = maxNoteStep * renderConfig.stepWidth;
|
|
7914
|
-
const maxOffsetX = totalContentWidth - canvas.width + leftPaddingWidth;
|
|
7925
|
+
const maxOffsetX = getMaxOffsetX();
|
|
7915
7926
|
const sbW = refs.hScroll.clientWidth;
|
|
7916
7927
|
if (maxOffsetX <= 0) {
|
|
7917
7928
|
refs.hScrollThumb.style.width = "100%";
|
|
7918
7929
|
refs.hScrollThumb.style.left = "0";
|
|
7919
7930
|
} else {
|
|
7920
|
-
const
|
|
7921
|
-
|
|
7922
|
-
canvas.width / (totalContentWidth + leftPaddingWidth) * sbW
|
|
7923
|
-
);
|
|
7931
|
+
const totalContentWidth = getMaxNoteStep() * renderConfig.stepWidth;
|
|
7932
|
+
const thumbW = Math.max(40, canvas.width / totalContentWidth * sbW);
|
|
7924
7933
|
const ratio = currentOffsetX / maxOffsetX;
|
|
7925
7934
|
refs.hScrollThumb.style.width = `${thumbW}px`;
|
|
7926
7935
|
refs.hScrollThumb.style.left = `${clamp3(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
|
|
@@ -7974,11 +7983,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7974
7983
|
draggingV = false;
|
|
7975
7984
|
});
|
|
7976
7985
|
const moveH = (clientX) => {
|
|
7977
|
-
const
|
|
7978
|
-
const maxNoteStep = getMaxNoteStep();
|
|
7979
|
-
const leftPaddingWidth = leftPaddingSteps * renderConfig.stepWidth;
|
|
7980
|
-
const totalContentWidth = maxNoteStep * renderConfig.stepWidth;
|
|
7981
|
-
const maxOffsetX = totalContentWidth - canvas.width + leftPaddingWidth;
|
|
7986
|
+
const maxOffsetX = getMaxOffsetX();
|
|
7982
7987
|
if (maxOffsetX <= 0) return;
|
|
7983
7988
|
const rect = refs.hScroll.getBoundingClientRect();
|
|
7984
7989
|
const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
|
|
@@ -8011,9 +8016,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
8011
8016
|
let selectedOriginal = [];
|
|
8012
8017
|
let lastMultiPreviewPitch = null;
|
|
8013
8018
|
const playPreview = (pitch) => {
|
|
8019
|
+
if (isLoading) return;
|
|
8014
8020
|
options.onResumeAudio?.();
|
|
8015
8021
|
const active = getActive();
|
|
8016
|
-
dispatchNote(active.config.id, pitch, active.volume, 100, 0, 0.
|
|
8022
|
+
dispatchNote(active.config.id, pitch, active.volume, 100, 0, 0.5);
|
|
8017
8023
|
};
|
|
8018
8024
|
const findActiveNoteAt = (x, y, margin = 0) => {
|
|
8019
8025
|
const active = getActive();
|
|
@@ -8286,7 +8292,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
8286
8292
|
0,
|
|
8287
8293
|
getMaxOffsetY()
|
|
8288
8294
|
);
|
|
8289
|
-
currentOffsetX =
|
|
8295
|
+
currentOffsetX = clamp3(
|
|
8296
|
+
currentOffsetX + event.deltaX,
|
|
8297
|
+
0,
|
|
8298
|
+
getMaxOffsetX()
|
|
8299
|
+
);
|
|
8290
8300
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8291
8301
|
redrawAll();
|
|
8292
8302
|
},
|
|
@@ -8321,9 +8331,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
8321
8331
|
const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
|
|
8322
8332
|
renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
|
|
8323
8333
|
refs.zoomXLabel.textContent = `${zoomX}%`;
|
|
8324
|
-
currentOffsetX =
|
|
8334
|
+
currentOffsetX = clamp3(
|
|
8335
|
+
centerStep * renderConfig.stepWidth - canvas.width / 2,
|
|
8325
8336
|
0,
|
|
8326
|
-
|
|
8337
|
+
getMaxOffsetX()
|
|
8327
8338
|
);
|
|
8328
8339
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8329
8340
|
redrawAll();
|
|
@@ -8382,7 +8393,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
8382
8393
|
const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
|
|
8383
8394
|
if (currentPlayStep > threshold) {
|
|
8384
8395
|
const visibleBars = Math.round(visibleSteps / renderConfig.stepsPerBar);
|
|
8385
|
-
currentOffsetX
|
|
8396
|
+
currentOffsetX = clamp3(
|
|
8397
|
+
currentOffsetX + visibleBars * renderConfig.stepsPerBar * renderConfig.stepWidth,
|
|
8398
|
+
0,
|
|
8399
|
+
getMaxOffsetX()
|
|
8400
|
+
);
|
|
8386
8401
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8387
8402
|
}
|
|
8388
8403
|
redrawAll();
|
|
@@ -8452,9 +8467,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
8452
8467
|
}
|
|
8453
8468
|
if (playbackState !== "paused") {
|
|
8454
8469
|
const canvas = getGridCanvas();
|
|
8455
|
-
currentOffsetX =
|
|
8470
|
+
currentOffsetX = clamp3(
|
|
8471
|
+
playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
|
|
8456
8472
|
0,
|
|
8457
|
-
|
|
8473
|
+
getMaxOffsetX()
|
|
8458
8474
|
);
|
|
8459
8475
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8460
8476
|
}
|
|
@@ -9037,7 +9053,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9037
9053
|
if (firstPitch !== null) {
|
|
9038
9054
|
centerPitch(firstPitch);
|
|
9039
9055
|
} else {
|
|
9040
|
-
|
|
9056
|
+
centerPitch(48);
|
|
9041
9057
|
}
|
|
9042
9058
|
redrawAll();
|
|
9043
9059
|
updateTrackPanel();
|
|
@@ -9116,7 +9132,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9116
9132
|
if (firstPitch !== null) {
|
|
9117
9133
|
centerPitch(firstPitch);
|
|
9118
9134
|
} else {
|
|
9119
|
-
|
|
9135
|
+
centerPitch(48);
|
|
9120
9136
|
}
|
|
9121
9137
|
redrawAll();
|
|
9122
9138
|
updateTrackPanel();
|
|
@@ -9586,6 +9602,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9586
9602
|
document.addEventListener("pointermove", onPointerMove);
|
|
9587
9603
|
document.addEventListener("pointerup", onPointerUp);
|
|
9588
9604
|
const setLoading = (loading) => {
|
|
9605
|
+
isLoading = loading;
|
|
9589
9606
|
refs.topbar.classList.toggle("is-loading", loading);
|
|
9590
9607
|
};
|
|
9591
9608
|
const getCurrentPlayStep = () => {
|
|
@@ -9613,9 +9630,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
9613
9630
|
currentPlayStep = step;
|
|
9614
9631
|
playbackState = "paused";
|
|
9615
9632
|
const canvas = getGridCanvas();
|
|
9616
|
-
currentOffsetX =
|
|
9633
|
+
currentOffsetX = clamp3(
|
|
9634
|
+
step * renderConfig.stepWidth - canvas.width * 0.5,
|
|
9617
9635
|
0,
|
|
9618
|
-
|
|
9636
|
+
getMaxOffsetX()
|
|
9619
9637
|
);
|
|
9620
9638
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
9621
9639
|
updateTransport();
|
|
@@ -10373,11 +10391,11 @@ var SoundFont = class _SoundFont {
|
|
|
10373
10391
|
const { buffer, _param } = zone;
|
|
10374
10392
|
if (!buffer || !_param) return;
|
|
10375
10393
|
src.buffer = buffer;
|
|
10376
|
-
g.gain.setValueAtTime(volume, ctx.currentTime);
|
|
10377
10394
|
src.playbackRate.setValueAtTime(_param.playbackRate, 0);
|
|
10378
10395
|
Object.assign(src, _param.src);
|
|
10379
10396
|
const _duration = duration + _SoundFont.afterTime;
|
|
10380
10397
|
const end = _when + (isDrum ? buffer.duration : src.loop ? _duration : Math.min(_duration, _param.max));
|
|
10398
|
+
g.gain.setValueAtTime(volume, ctx.currentTime);
|
|
10381
10399
|
if (!isDrum) g.gain.linearRampToValueAtTime(0, end);
|
|
10382
10400
|
src.connect(g).connect(destination);
|
|
10383
10401
|
src.start(_when);
|
|
@@ -10424,14 +10442,99 @@ var adjustZone = async (ctx, zone) => {
|
|
|
10424
10442
|
a[i] = n / 65536;
|
|
10425
10443
|
}
|
|
10426
10444
|
} else if (zone.file) {
|
|
10427
|
-
const
|
|
10445
|
+
const bytes = Uint8Array.from(atob(zone.file), (c) => c.charCodeAt(0));
|
|
10446
|
+
const buf = bytes.buffer;
|
|
10428
10447
|
if (ctx.state === "interrupted") {
|
|
10429
10448
|
try {
|
|
10430
10449
|
await ctx.resume();
|
|
10431
10450
|
} catch {
|
|
10432
10451
|
}
|
|
10433
10452
|
}
|
|
10434
|
-
|
|
10453
|
+
try {
|
|
10454
|
+
zone.buffer = await ctx.decodeAudioData(buf);
|
|
10455
|
+
} catch (e) {
|
|
10456
|
+
console.error(
|
|
10457
|
+
`[zone.file format] keyRange: ${zone.keyRangeLow}-${zone.keyRangeHigh} - Decode failed:`,
|
|
10458
|
+
e
|
|
10459
|
+
);
|
|
10460
|
+
throw e;
|
|
10461
|
+
}
|
|
10462
|
+
}
|
|
10463
|
+
if (zone.buffer && zone.loopStart >= 1 && zone.loopStart < zone.loopEnd) {
|
|
10464
|
+
const loopLenSeconds = (zone.loopEnd - zone.loopStart) / zone.sampleRate;
|
|
10465
|
+
if (loopLenSeconds < 0.03) {
|
|
10466
|
+
const oldBuf = zone.buffer;
|
|
10467
|
+
const sampleRate = oldBuf.sampleRate;
|
|
10468
|
+
const rateRatio = sampleRate / zone.sampleRate;
|
|
10469
|
+
const loopStartFrame = Math.round(zone.loopStart * rateRatio);
|
|
10470
|
+
const loopEndFrame = Math.round(zone.loopEnd * rateRatio);
|
|
10471
|
+
const loopLengthFrame = loopEndFrame - loopStartFrame;
|
|
10472
|
+
if (loopLengthFrame > 0) {
|
|
10473
|
+
const minLoopLenFrame = Math.round(0.2 * sampleRate);
|
|
10474
|
+
const repeatCount = Math.ceil(minLoopLenFrame / loopLengthFrame);
|
|
10475
|
+
const attackLength = Math.min(loopStartFrame, oldBuf.length);
|
|
10476
|
+
const releaseLength = Math.max(0, oldBuf.length - loopEndFrame);
|
|
10477
|
+
const newLength = attackLength + loopLengthFrame * repeatCount + releaseLength;
|
|
10478
|
+
let totalPeak = 0;
|
|
10479
|
+
let loopPeak = 0;
|
|
10480
|
+
if (oldBuf.numberOfChannels > 0) {
|
|
10481
|
+
const ch0 = oldBuf.getChannelData(0);
|
|
10482
|
+
for (let i = 0; i < ch0.length; i++) {
|
|
10483
|
+
const abs = Math.abs(ch0[i]);
|
|
10484
|
+
if (abs > totalPeak) totalPeak = abs;
|
|
10485
|
+
if (i >= loopStartFrame && i < loopEndFrame) {
|
|
10486
|
+
if (abs > loopPeak) loopPeak = abs;
|
|
10487
|
+
}
|
|
10488
|
+
}
|
|
10489
|
+
}
|
|
10490
|
+
let gainMultiplier = 1;
|
|
10491
|
+
if (loopPeak > 0 && totalPeak > 0 && loopPeak < totalPeak * 0.8) {
|
|
10492
|
+
gainMultiplier = totalPeak * 0.75 / loopPeak;
|
|
10493
|
+
if (gainMultiplier > 20) gainMultiplier = 20;
|
|
10494
|
+
}
|
|
10495
|
+
try {
|
|
10496
|
+
const newBuf = ctx.createBuffer(
|
|
10497
|
+
oldBuf.numberOfChannels,
|
|
10498
|
+
newLength,
|
|
10499
|
+
sampleRate
|
|
10500
|
+
);
|
|
10501
|
+
for (let ch = 0; ch < oldBuf.numberOfChannels; ch++) {
|
|
10502
|
+
const oldData = oldBuf.getChannelData(ch);
|
|
10503
|
+
const newData = newBuf.getChannelData(ch);
|
|
10504
|
+
newData.set(oldData.subarray(0, attackLength), 0);
|
|
10505
|
+
let offset = attackLength;
|
|
10506
|
+
const loopData = oldData.subarray(loopStartFrame, loopEndFrame);
|
|
10507
|
+
const normalizedLoopData = new Float32Array(loopLengthFrame);
|
|
10508
|
+
for (let i = 0; i < loopLengthFrame; i++) {
|
|
10509
|
+
normalizedLoopData[i] = loopData[i] * gainMultiplier;
|
|
10510
|
+
}
|
|
10511
|
+
for (let r = 0; r < repeatCount; r++) {
|
|
10512
|
+
newData.set(normalizedLoopData, offset);
|
|
10513
|
+
offset += loopLengthFrame;
|
|
10514
|
+
}
|
|
10515
|
+
if (releaseLength > 0 && loopEndFrame < oldBuf.length) {
|
|
10516
|
+
const releaseData = oldData.subarray(loopEndFrame);
|
|
10517
|
+
if (gainMultiplier !== 1) {
|
|
10518
|
+
const normalizedRelease = new Float32Array(releaseLength);
|
|
10519
|
+
for (let i = 0; i < releaseLength; i++) {
|
|
10520
|
+
normalizedRelease[i] = releaseData[i] * gainMultiplier;
|
|
10521
|
+
}
|
|
10522
|
+
newData.set(normalizedRelease, offset);
|
|
10523
|
+
} else {
|
|
10524
|
+
newData.set(releaseData, offset);
|
|
10525
|
+
}
|
|
10526
|
+
}
|
|
10527
|
+
}
|
|
10528
|
+
zone.buffer = newBuf;
|
|
10529
|
+
zone.loopEnd = zone.loopStart + (zone.loopEnd - zone.loopStart) * repeatCount;
|
|
10530
|
+
} catch (e) {
|
|
10531
|
+
console.warn(
|
|
10532
|
+
"[SoundFont.loopExtension] Failed to extend loop buffer:",
|
|
10533
|
+
e
|
|
10534
|
+
);
|
|
10535
|
+
}
|
|
10536
|
+
}
|
|
10537
|
+
}
|
|
10435
10538
|
}
|
|
10436
10539
|
for (const [k, v] of [
|
|
10437
10540
|
["loopStart", 0],
|
|
@@ -10592,7 +10695,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
10592
10695
|
presetUI: true,
|
|
10593
10696
|
...options.features
|
|
10594
10697
|
};
|
|
10595
|
-
const audioCtx = options.audioContext ?? new AudioContext();
|
|
10698
|
+
const audioCtx = options.audioContext ?? new AudioContext({ sampleRate: 44100 });
|
|
10596
10699
|
const masterGain = audioCtx.createGain();
|
|
10597
10700
|
masterGain.gain.value = options.masterVolume ?? 1;
|
|
10598
10701
|
masterGain.connect(audioCtx.destination);
|
package/dist/index.mjs
CHANGED
|
@@ -7588,7 +7588,6 @@ var mountDAW = (target, options = {}) => {
|
|
|
7588
7588
|
stepWidth: BASE_STEP_WIDTH * 2
|
|
7589
7589
|
// zoom100% 相当
|
|
7590
7590
|
};
|
|
7591
|
-
const leftPaddingSteps = renderConfig.stepsPerBar * 16;
|
|
7592
7591
|
let zoomX = 100;
|
|
7593
7592
|
let zoomY = 100;
|
|
7594
7593
|
let bpm = options.defaultBpm ?? DEFAULT_BPM;
|
|
@@ -7602,8 +7601,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
7602
7601
|
let snapGridSteps = 12;
|
|
7603
7602
|
const gridLineSteps = 48;
|
|
7604
7603
|
let currentOffsetX = 0;
|
|
7605
|
-
const _initPitch = options.initialScrollPitch;
|
|
7606
|
-
let currentOffsetY =
|
|
7604
|
+
const _initPitch = options.initialScrollPitch ?? 48;
|
|
7605
|
+
let currentOffsetY = (renderConfig.keyCount - 1 - _initPitch) * renderConfig.keyHeight - 215;
|
|
7607
7606
|
let playStartStep = 0;
|
|
7608
7607
|
let isSolo = false;
|
|
7609
7608
|
let lyricTrackIndices = /* @__PURE__ */ new Set();
|
|
@@ -7611,6 +7610,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7611
7610
|
let pausedPlayStep = 0;
|
|
7612
7611
|
let currentPlayStep = 0;
|
|
7613
7612
|
let ready = false;
|
|
7613
|
+
let isLoading = false;
|
|
7614
7614
|
let selectedNotes = [];
|
|
7615
7615
|
let selectionRect = null;
|
|
7616
7616
|
let copiedNotes = [];
|
|
@@ -7630,7 +7630,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7630
7630
|
};
|
|
7631
7631
|
if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
|
|
7632
7632
|
lyricsDebounceTimer = setTimeout(() => {
|
|
7633
|
-
options.onLyricsChange(trackId, data);
|
|
7633
|
+
options.onLyricsChange?.(trackId, data);
|
|
7634
7634
|
lyricsDebounceTimer = null;
|
|
7635
7635
|
}, 300);
|
|
7636
7636
|
};
|
|
@@ -7710,14 +7710,28 @@ var mountDAW = (target, options = {}) => {
|
|
|
7710
7710
|
const getActive = () => trackStates.find((t) => t.config.id === activeTrackId) ?? trackStates[0];
|
|
7711
7711
|
let showModal;
|
|
7712
7712
|
const getMaxNoteStep = () => {
|
|
7713
|
-
let
|
|
7713
|
+
let maxEndStep = 0;
|
|
7714
7714
|
for (const t of trackStates) {
|
|
7715
7715
|
for (const n of t.core.getNotes()) {
|
|
7716
7716
|
const end = n.startStep + n.durationSteps;
|
|
7717
|
-
if (end >
|
|
7717
|
+
if (end > maxEndStep) {
|
|
7718
|
+
maxEndStep = end;
|
|
7719
|
+
}
|
|
7718
7720
|
}
|
|
7719
7721
|
}
|
|
7720
|
-
|
|
7722
|
+
if (maxEndStep === 0) {
|
|
7723
|
+
return renderConfig.stepsPerBar * 4;
|
|
7724
|
+
}
|
|
7725
|
+
const lastNoteMeasure = Math.floor(
|
|
7726
|
+
(maxEndStep - 1) / renderConfig.stepsPerBar
|
|
7727
|
+
);
|
|
7728
|
+
return (lastNoteMeasure + 2) * renderConfig.stepsPerBar;
|
|
7729
|
+
};
|
|
7730
|
+
const getMaxOffsetX = () => {
|
|
7731
|
+
const canvas = getGridCanvas();
|
|
7732
|
+
const maxNoteStep = getMaxNoteStep();
|
|
7733
|
+
const totalContentWidth = maxNoteStep * renderConfig.stepWidth;
|
|
7734
|
+
return Math.max(0, totalContentWidth - canvas.width);
|
|
7721
7735
|
};
|
|
7722
7736
|
const getMaxOffsetY = () => {
|
|
7723
7737
|
const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
|
|
@@ -7797,19 +7811,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
7797
7811
|
};
|
|
7798
7812
|
const updateScrollbars = () => {
|
|
7799
7813
|
const canvas = getGridCanvas();
|
|
7800
|
-
const
|
|
7801
|
-
const leftPaddingWidth = leftPaddingSteps * renderConfig.stepWidth;
|
|
7802
|
-
const totalContentWidth = maxNoteStep * renderConfig.stepWidth;
|
|
7803
|
-
const maxOffsetX = totalContentWidth - canvas.width + leftPaddingWidth;
|
|
7814
|
+
const maxOffsetX = getMaxOffsetX();
|
|
7804
7815
|
const sbW = refs.hScroll.clientWidth;
|
|
7805
7816
|
if (maxOffsetX <= 0) {
|
|
7806
7817
|
refs.hScrollThumb.style.width = "100%";
|
|
7807
7818
|
refs.hScrollThumb.style.left = "0";
|
|
7808
7819
|
} else {
|
|
7809
|
-
const
|
|
7810
|
-
|
|
7811
|
-
canvas.width / (totalContentWidth + leftPaddingWidth) * sbW
|
|
7812
|
-
);
|
|
7820
|
+
const totalContentWidth = getMaxNoteStep() * renderConfig.stepWidth;
|
|
7821
|
+
const thumbW = Math.max(40, canvas.width / totalContentWidth * sbW);
|
|
7813
7822
|
const ratio = currentOffsetX / maxOffsetX;
|
|
7814
7823
|
refs.hScrollThumb.style.width = `${thumbW}px`;
|
|
7815
7824
|
refs.hScrollThumb.style.left = `${clamp3(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
|
|
@@ -7863,11 +7872,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
7863
7872
|
draggingV = false;
|
|
7864
7873
|
});
|
|
7865
7874
|
const moveH = (clientX) => {
|
|
7866
|
-
const
|
|
7867
|
-
const maxNoteStep = getMaxNoteStep();
|
|
7868
|
-
const leftPaddingWidth = leftPaddingSteps * renderConfig.stepWidth;
|
|
7869
|
-
const totalContentWidth = maxNoteStep * renderConfig.stepWidth;
|
|
7870
|
-
const maxOffsetX = totalContentWidth - canvas.width + leftPaddingWidth;
|
|
7875
|
+
const maxOffsetX = getMaxOffsetX();
|
|
7871
7876
|
if (maxOffsetX <= 0) return;
|
|
7872
7877
|
const rect = refs.hScroll.getBoundingClientRect();
|
|
7873
7878
|
const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
|
|
@@ -7900,9 +7905,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
7900
7905
|
let selectedOriginal = [];
|
|
7901
7906
|
let lastMultiPreviewPitch = null;
|
|
7902
7907
|
const playPreview = (pitch) => {
|
|
7908
|
+
if (isLoading) return;
|
|
7903
7909
|
options.onResumeAudio?.();
|
|
7904
7910
|
const active = getActive();
|
|
7905
|
-
dispatchNote(active.config.id, pitch, active.volume, 100, 0, 0.
|
|
7911
|
+
dispatchNote(active.config.id, pitch, active.volume, 100, 0, 0.5);
|
|
7906
7912
|
};
|
|
7907
7913
|
const findActiveNoteAt = (x, y, margin = 0) => {
|
|
7908
7914
|
const active = getActive();
|
|
@@ -8175,7 +8181,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
8175
8181
|
0,
|
|
8176
8182
|
getMaxOffsetY()
|
|
8177
8183
|
);
|
|
8178
|
-
currentOffsetX =
|
|
8184
|
+
currentOffsetX = clamp3(
|
|
8185
|
+
currentOffsetX + event.deltaX,
|
|
8186
|
+
0,
|
|
8187
|
+
getMaxOffsetX()
|
|
8188
|
+
);
|
|
8179
8189
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8180
8190
|
redrawAll();
|
|
8181
8191
|
},
|
|
@@ -8210,9 +8220,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
8210
8220
|
const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
|
|
8211
8221
|
renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
|
|
8212
8222
|
refs.zoomXLabel.textContent = `${zoomX}%`;
|
|
8213
|
-
currentOffsetX =
|
|
8223
|
+
currentOffsetX = clamp3(
|
|
8224
|
+
centerStep * renderConfig.stepWidth - canvas.width / 2,
|
|
8214
8225
|
0,
|
|
8215
|
-
|
|
8226
|
+
getMaxOffsetX()
|
|
8216
8227
|
);
|
|
8217
8228
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8218
8229
|
redrawAll();
|
|
@@ -8271,7 +8282,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
8271
8282
|
const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
|
|
8272
8283
|
if (currentPlayStep > threshold) {
|
|
8273
8284
|
const visibleBars = Math.round(visibleSteps / renderConfig.stepsPerBar);
|
|
8274
|
-
currentOffsetX
|
|
8285
|
+
currentOffsetX = clamp3(
|
|
8286
|
+
currentOffsetX + visibleBars * renderConfig.stepsPerBar * renderConfig.stepWidth,
|
|
8287
|
+
0,
|
|
8288
|
+
getMaxOffsetX()
|
|
8289
|
+
);
|
|
8275
8290
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8276
8291
|
}
|
|
8277
8292
|
redrawAll();
|
|
@@ -8341,9 +8356,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
8341
8356
|
}
|
|
8342
8357
|
if (playbackState !== "paused") {
|
|
8343
8358
|
const canvas = getGridCanvas();
|
|
8344
|
-
currentOffsetX =
|
|
8359
|
+
currentOffsetX = clamp3(
|
|
8360
|
+
playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
|
|
8345
8361
|
0,
|
|
8346
|
-
|
|
8362
|
+
getMaxOffsetX()
|
|
8347
8363
|
);
|
|
8348
8364
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
8349
8365
|
}
|
|
@@ -8926,7 +8942,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
8926
8942
|
if (firstPitch !== null) {
|
|
8927
8943
|
centerPitch(firstPitch);
|
|
8928
8944
|
} else {
|
|
8929
|
-
|
|
8945
|
+
centerPitch(48);
|
|
8930
8946
|
}
|
|
8931
8947
|
redrawAll();
|
|
8932
8948
|
updateTrackPanel();
|
|
@@ -9005,7 +9021,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9005
9021
|
if (firstPitch !== null) {
|
|
9006
9022
|
centerPitch(firstPitch);
|
|
9007
9023
|
} else {
|
|
9008
|
-
|
|
9024
|
+
centerPitch(48);
|
|
9009
9025
|
}
|
|
9010
9026
|
redrawAll();
|
|
9011
9027
|
updateTrackPanel();
|
|
@@ -9475,6 +9491,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
9475
9491
|
document.addEventListener("pointermove", onPointerMove);
|
|
9476
9492
|
document.addEventListener("pointerup", onPointerUp);
|
|
9477
9493
|
const setLoading = (loading) => {
|
|
9494
|
+
isLoading = loading;
|
|
9478
9495
|
refs.topbar.classList.toggle("is-loading", loading);
|
|
9479
9496
|
};
|
|
9480
9497
|
const getCurrentPlayStep = () => {
|
|
@@ -9502,9 +9519,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
9502
9519
|
currentPlayStep = step;
|
|
9503
9520
|
playbackState = "paused";
|
|
9504
9521
|
const canvas = getGridCanvas();
|
|
9505
|
-
currentOffsetX =
|
|
9522
|
+
currentOffsetX = clamp3(
|
|
9523
|
+
step * renderConfig.stepWidth - canvas.width * 0.5,
|
|
9506
9524
|
0,
|
|
9507
|
-
|
|
9525
|
+
getMaxOffsetX()
|
|
9508
9526
|
);
|
|
9509
9527
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
9510
9528
|
updateTransport();
|
|
@@ -10262,11 +10280,11 @@ var SoundFont = class _SoundFont {
|
|
|
10262
10280
|
const { buffer, _param } = zone;
|
|
10263
10281
|
if (!buffer || !_param) return;
|
|
10264
10282
|
src.buffer = buffer;
|
|
10265
|
-
g.gain.setValueAtTime(volume, ctx.currentTime);
|
|
10266
10283
|
src.playbackRate.setValueAtTime(_param.playbackRate, 0);
|
|
10267
10284
|
Object.assign(src, _param.src);
|
|
10268
10285
|
const _duration = duration + _SoundFont.afterTime;
|
|
10269
10286
|
const end = _when + (isDrum ? buffer.duration : src.loop ? _duration : Math.min(_duration, _param.max));
|
|
10287
|
+
g.gain.setValueAtTime(volume, ctx.currentTime);
|
|
10270
10288
|
if (!isDrum) g.gain.linearRampToValueAtTime(0, end);
|
|
10271
10289
|
src.connect(g).connect(destination);
|
|
10272
10290
|
src.start(_when);
|
|
@@ -10313,14 +10331,99 @@ var adjustZone = async (ctx, zone) => {
|
|
|
10313
10331
|
a[i] = n / 65536;
|
|
10314
10332
|
}
|
|
10315
10333
|
} else if (zone.file) {
|
|
10316
|
-
const
|
|
10334
|
+
const bytes = Uint8Array.from(atob(zone.file), (c) => c.charCodeAt(0));
|
|
10335
|
+
const buf = bytes.buffer;
|
|
10317
10336
|
if (ctx.state === "interrupted") {
|
|
10318
10337
|
try {
|
|
10319
10338
|
await ctx.resume();
|
|
10320
10339
|
} catch {
|
|
10321
10340
|
}
|
|
10322
10341
|
}
|
|
10323
|
-
|
|
10342
|
+
try {
|
|
10343
|
+
zone.buffer = await ctx.decodeAudioData(buf);
|
|
10344
|
+
} catch (e) {
|
|
10345
|
+
console.error(
|
|
10346
|
+
`[zone.file format] keyRange: ${zone.keyRangeLow}-${zone.keyRangeHigh} - Decode failed:`,
|
|
10347
|
+
e
|
|
10348
|
+
);
|
|
10349
|
+
throw e;
|
|
10350
|
+
}
|
|
10351
|
+
}
|
|
10352
|
+
if (zone.buffer && zone.loopStart >= 1 && zone.loopStart < zone.loopEnd) {
|
|
10353
|
+
const loopLenSeconds = (zone.loopEnd - zone.loopStart) / zone.sampleRate;
|
|
10354
|
+
if (loopLenSeconds < 0.03) {
|
|
10355
|
+
const oldBuf = zone.buffer;
|
|
10356
|
+
const sampleRate = oldBuf.sampleRate;
|
|
10357
|
+
const rateRatio = sampleRate / zone.sampleRate;
|
|
10358
|
+
const loopStartFrame = Math.round(zone.loopStart * rateRatio);
|
|
10359
|
+
const loopEndFrame = Math.round(zone.loopEnd * rateRatio);
|
|
10360
|
+
const loopLengthFrame = loopEndFrame - loopStartFrame;
|
|
10361
|
+
if (loopLengthFrame > 0) {
|
|
10362
|
+
const minLoopLenFrame = Math.round(0.2 * sampleRate);
|
|
10363
|
+
const repeatCount = Math.ceil(minLoopLenFrame / loopLengthFrame);
|
|
10364
|
+
const attackLength = Math.min(loopStartFrame, oldBuf.length);
|
|
10365
|
+
const releaseLength = Math.max(0, oldBuf.length - loopEndFrame);
|
|
10366
|
+
const newLength = attackLength + loopLengthFrame * repeatCount + releaseLength;
|
|
10367
|
+
let totalPeak = 0;
|
|
10368
|
+
let loopPeak = 0;
|
|
10369
|
+
if (oldBuf.numberOfChannels > 0) {
|
|
10370
|
+
const ch0 = oldBuf.getChannelData(0);
|
|
10371
|
+
for (let i = 0; i < ch0.length; i++) {
|
|
10372
|
+
const abs = Math.abs(ch0[i]);
|
|
10373
|
+
if (abs > totalPeak) totalPeak = abs;
|
|
10374
|
+
if (i >= loopStartFrame && i < loopEndFrame) {
|
|
10375
|
+
if (abs > loopPeak) loopPeak = abs;
|
|
10376
|
+
}
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
let gainMultiplier = 1;
|
|
10380
|
+
if (loopPeak > 0 && totalPeak > 0 && loopPeak < totalPeak * 0.8) {
|
|
10381
|
+
gainMultiplier = totalPeak * 0.75 / loopPeak;
|
|
10382
|
+
if (gainMultiplier > 20) gainMultiplier = 20;
|
|
10383
|
+
}
|
|
10384
|
+
try {
|
|
10385
|
+
const newBuf = ctx.createBuffer(
|
|
10386
|
+
oldBuf.numberOfChannels,
|
|
10387
|
+
newLength,
|
|
10388
|
+
sampleRate
|
|
10389
|
+
);
|
|
10390
|
+
for (let ch = 0; ch < oldBuf.numberOfChannels; ch++) {
|
|
10391
|
+
const oldData = oldBuf.getChannelData(ch);
|
|
10392
|
+
const newData = newBuf.getChannelData(ch);
|
|
10393
|
+
newData.set(oldData.subarray(0, attackLength), 0);
|
|
10394
|
+
let offset = attackLength;
|
|
10395
|
+
const loopData = oldData.subarray(loopStartFrame, loopEndFrame);
|
|
10396
|
+
const normalizedLoopData = new Float32Array(loopLengthFrame);
|
|
10397
|
+
for (let i = 0; i < loopLengthFrame; i++) {
|
|
10398
|
+
normalizedLoopData[i] = loopData[i] * gainMultiplier;
|
|
10399
|
+
}
|
|
10400
|
+
for (let r = 0; r < repeatCount; r++) {
|
|
10401
|
+
newData.set(normalizedLoopData, offset);
|
|
10402
|
+
offset += loopLengthFrame;
|
|
10403
|
+
}
|
|
10404
|
+
if (releaseLength > 0 && loopEndFrame < oldBuf.length) {
|
|
10405
|
+
const releaseData = oldData.subarray(loopEndFrame);
|
|
10406
|
+
if (gainMultiplier !== 1) {
|
|
10407
|
+
const normalizedRelease = new Float32Array(releaseLength);
|
|
10408
|
+
for (let i = 0; i < releaseLength; i++) {
|
|
10409
|
+
normalizedRelease[i] = releaseData[i] * gainMultiplier;
|
|
10410
|
+
}
|
|
10411
|
+
newData.set(normalizedRelease, offset);
|
|
10412
|
+
} else {
|
|
10413
|
+
newData.set(releaseData, offset);
|
|
10414
|
+
}
|
|
10415
|
+
}
|
|
10416
|
+
}
|
|
10417
|
+
zone.buffer = newBuf;
|
|
10418
|
+
zone.loopEnd = zone.loopStart + (zone.loopEnd - zone.loopStart) * repeatCount;
|
|
10419
|
+
} catch (e) {
|
|
10420
|
+
console.warn(
|
|
10421
|
+
"[SoundFont.loopExtension] Failed to extend loop buffer:",
|
|
10422
|
+
e
|
|
10423
|
+
);
|
|
10424
|
+
}
|
|
10425
|
+
}
|
|
10426
|
+
}
|
|
10324
10427
|
}
|
|
10325
10428
|
for (const [k, v] of [
|
|
10326
10429
|
["loopStart", 0],
|
|
@@ -10480,7 +10583,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
10480
10583
|
presetUI: true,
|
|
10481
10584
|
...options.features
|
|
10482
10585
|
};
|
|
10483
|
-
const audioCtx = options.audioContext ?? new AudioContext();
|
|
10586
|
+
const audioCtx = options.audioContext ?? new AudioContext({ sampleRate: 44100 });
|
|
10484
10587
|
const masterGain = audioCtx.createGain();
|
|
10485
10588
|
masterGain.gain.value = options.masterVolume ?? 1;
|
|
10486
10589
|
masterGain.connect(audioCtx.destination);
|
package/package.json
CHANGED