@kano/stem-daw 0.1.0
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 +253 -0
- package/dist/chat-actions-54Z6URC4.js +7 -0
- package/dist/chat-actions-54Z6URC4.js.map +1 -0
- package/dist/chunk-56PWIP7O.js +1029 -0
- package/dist/chunk-56PWIP7O.js.map +1 -0
- package/dist/chunk-AAVC7KUW.js +145 -0
- package/dist/chunk-AAVC7KUW.js.map +1 -0
- package/dist/chunk-KCOOE2OP.js +1764 -0
- package/dist/chunk-KCOOE2OP.js.map +1 -0
- package/dist/chunk-LO74ZJ4H.js +23923 -0
- package/dist/chunk-LO74ZJ4H.js.map +1 -0
- package/dist/chunk-OFGZURP6.js +247 -0
- package/dist/chunk-OFGZURP6.js.map +1 -0
- package/dist/chunk-OYNES5W3.js +3085 -0
- package/dist/chunk-OYNES5W3.js.map +1 -0
- package/dist/chunk-QQ5NZTHT.js +336 -0
- package/dist/chunk-QQ5NZTHT.js.map +1 -0
- package/dist/chunk-TBXCZFAY.js +13713 -0
- package/dist/chunk-TBXCZFAY.js.map +1 -0
- package/dist/chunk-U44X6QP5.js +281 -0
- package/dist/chunk-U44X6QP5.js.map +1 -0
- package/dist/chunk-UKMELGZL.js +27 -0
- package/dist/chunk-UKMELGZL.js.map +1 -0
- package/dist/components/DAWView.d.ts +19 -0
- package/dist/components/DAWView.js +11 -0
- package/dist/components/DAWView.js.map +1 -0
- package/dist/daw-controller-BjRWcTol.d.ts +339 -0
- package/dist/engine/daw-controller.d.ts +3 -0
- package/dist/engine/daw-controller.js +5 -0
- package/dist/engine/daw-controller.js.map +1 -0
- package/dist/engine/daw-import-stem-fm-config.d.ts +224 -0
- package/dist/engine/daw-import-stem-fm-config.js +7 -0
- package/dist/engine/daw-import-stem-fm-config.js.map +1 -0
- package/dist/fetchStationTracks-SKFT4V3U.js +3 -0
- package/dist/fetchStationTracks-SKFT4V3U.js.map +1 -0
- package/dist/index.d.ts +308 -0
- package/dist/index.js +332 -0
- package/dist/index.js.map +1 -0
- package/dist/interface-DaRj7RkY.d.ts +66 -0
- package/dist/interfaces-5ZlG0Y4Y.d.ts +549 -0
- package/dist/media-session-XTP6PP7Q.js +3 -0
- package/dist/media-session-XTP6PP7Q.js.map +1 -0
- package/dist/note-detection-PPLM7R2H.js +148 -0
- package/dist/note-detection-PPLM7R2H.js.map +1 -0
- package/dist/sampler-audio-B7MBG3YN.js +3 -0
- package/dist/sampler-audio-B7MBG3YN.js.map +1 -0
- package/dist/sampler-store-QPHANXYP.js +3 -0
- package/dist/sampler-store-QPHANXYP.js.map +1 -0
- package/dist/services/track-search-api.d.ts +152 -0
- package/dist/services/track-search-api.js +4 -0
- package/dist/services/track-search-api.js.map +1 -0
- package/dist/store/daw-auth-store.d.ts +31 -0
- package/dist/store/daw-auth-store.js +3 -0
- package/dist/store/daw-auth-store.js.map +1 -0
- package/dist/store/daw-session-store.d.ts +255 -0
- package/dist/store/daw-session-store.js +3 -0
- package/dist/store/daw-session-store.js.map +1 -0
- package/dist/vite/index.d.ts +46 -0
- package/dist/vite/index.js +94 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/workers/analysis-worker.js +379 -0
- package/dist/workers/buffer-player-processor-202602.lavv8e32-ts.js +1 -0
- package/dist/workers/daw-stem-processor.js +228 -0
- package/dist/workers/manifest.json +10 -0
- package/dist/workers/phase-vocoder3.js +920 -0
- package/dist/workers/realtime-pitch-shift-processor.js +2 -0
- package/package.json +151 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// src/daw/utils/sampler-audio.ts
|
|
2
|
+
var CROSSFADE_SAMPLES = 256;
|
|
3
|
+
function fracBarToSample(bar, barMapping, bufLen) {
|
|
4
|
+
if (barMapping.length === 0) return 0;
|
|
5
|
+
const floorBar = Math.floor(bar);
|
|
6
|
+
const frac = bar - floorBar;
|
|
7
|
+
if (floorBar >= barMapping.length) return bufLen;
|
|
8
|
+
const entry = barMapping[Math.max(0, floorBar)];
|
|
9
|
+
return Math.round(entry.sampleStart + frac * entry.duration);
|
|
10
|
+
}
|
|
11
|
+
function extractRegion(source, sampleStart, sampleEnd, ctx) {
|
|
12
|
+
const len = Math.max(0, sampleEnd - sampleStart);
|
|
13
|
+
if (len === 0) return ctx.createBuffer(source.numberOfChannels, 1, source.sampleRate);
|
|
14
|
+
const out = ctx.createBuffer(source.numberOfChannels, len, source.sampleRate);
|
|
15
|
+
for (let ch = 0; ch < source.numberOfChannels; ch++) {
|
|
16
|
+
const src = source.getChannelData(ch);
|
|
17
|
+
const dst = out.getChannelData(ch);
|
|
18
|
+
dst.set(src.subarray(sampleStart, sampleStart + len));
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
function reverseBuffer(buffer) {
|
|
23
|
+
for (let ch = 0; ch < buffer.numberOfChannels; ch++) {
|
|
24
|
+
buffer.getChannelData(ch).reverse();
|
|
25
|
+
}
|
|
26
|
+
return buffer;
|
|
27
|
+
}
|
|
28
|
+
function applyGain(buffer, gain) {
|
|
29
|
+
if (gain === 1) return buffer;
|
|
30
|
+
for (let ch = 0; ch < buffer.numberOfChannels; ch++) {
|
|
31
|
+
const data = buffer.getChannelData(ch);
|
|
32
|
+
for (let i = 0; i < data.length; i++) data[i] *= gain;
|
|
33
|
+
}
|
|
34
|
+
return buffer;
|
|
35
|
+
}
|
|
36
|
+
function extractSliceWithTrim(sourceBuffer, slice, startOffset, endOffset, ctx) {
|
|
37
|
+
const sliceLen = slice.sampleEnd - slice.sampleStart;
|
|
38
|
+
const trimStart = slice.sampleStart + Math.round(sliceLen * startOffset);
|
|
39
|
+
const trimEnd = slice.sampleStart + Math.round(sliceLen * (1 - endOffset));
|
|
40
|
+
const len = Math.max(1, trimEnd - trimStart);
|
|
41
|
+
const out = ctx.createBuffer(sourceBuffer.numberOfChannels, len, sourceBuffer.sampleRate);
|
|
42
|
+
for (let ch = 0; ch < sourceBuffer.numberOfChannels; ch++) {
|
|
43
|
+
const src = sourceBuffer.getChannelData(ch);
|
|
44
|
+
out.getChannelData(ch).set(src.subarray(trimStart, trimStart + len));
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
async function pitchShiftClassic(buffer, semitones) {
|
|
49
|
+
if (semitones === 0) return buffer;
|
|
50
|
+
const rate = Math.pow(2, semitones / 12);
|
|
51
|
+
const newLength = Math.max(1, Math.round(buffer.length / rate));
|
|
52
|
+
const offline = new OfflineAudioContext(buffer.numberOfChannels, newLength, buffer.sampleRate);
|
|
53
|
+
const source = offline.createBufferSource();
|
|
54
|
+
source.buffer = buffer;
|
|
55
|
+
source.playbackRate.value = rate;
|
|
56
|
+
source.connect(offline.destination);
|
|
57
|
+
source.start();
|
|
58
|
+
return offline.startRendering();
|
|
59
|
+
}
|
|
60
|
+
async function pitchShiftFormant(buffer, semitones, _ctx) {
|
|
61
|
+
if (semitones === 0) return buffer;
|
|
62
|
+
return pitchShiftClassic(buffer, semitones);
|
|
63
|
+
}
|
|
64
|
+
async function renderSequenceAsync(sourceBuffer, sequence, slices, ctx, pitchMode = "classic") {
|
|
65
|
+
const sliceMap = new Map(slices.map((s) => [s.id, s]));
|
|
66
|
+
const renderedParts = [];
|
|
67
|
+
let totalSamples = 0;
|
|
68
|
+
for (const entry of sequence) {
|
|
69
|
+
const slice = sliceMap.get(entry.sliceId);
|
|
70
|
+
if (!slice) continue;
|
|
71
|
+
let part = extractSliceWithTrim(
|
|
72
|
+
sourceBuffer,
|
|
73
|
+
slice,
|
|
74
|
+
entry.startOffset ?? 0,
|
|
75
|
+
entry.endOffset ?? 0,
|
|
76
|
+
ctx
|
|
77
|
+
);
|
|
78
|
+
if (entry.reverse) part = reverseBuffer(part);
|
|
79
|
+
if (entry.pitchSemitones && entry.pitchSemitones !== 0) {
|
|
80
|
+
part = pitchMode === "formant" ? await pitchShiftFormant(part, entry.pitchSemitones) : await pitchShiftClassic(part, entry.pitchSemitones);
|
|
81
|
+
}
|
|
82
|
+
if (entry.gain !== void 0 && entry.gain !== 1) {
|
|
83
|
+
part = applyGain(part, entry.gain);
|
|
84
|
+
}
|
|
85
|
+
renderedParts.push(part);
|
|
86
|
+
totalSamples += part.length;
|
|
87
|
+
}
|
|
88
|
+
if (totalSamples === 0) {
|
|
89
|
+
return ctx.createBuffer(sourceBuffer.numberOfChannels, 1, sourceBuffer.sampleRate);
|
|
90
|
+
}
|
|
91
|
+
const output = ctx.createBuffer(sourceBuffer.numberOfChannels, totalSamples, sourceBuffer.sampleRate);
|
|
92
|
+
let writePos = 0;
|
|
93
|
+
for (const part of renderedParts) {
|
|
94
|
+
for (let ch = 0; ch < output.numberOfChannels; ch++) {
|
|
95
|
+
const dst = output.getChannelData(ch);
|
|
96
|
+
const src = part.numberOfChannels > ch ? part.getChannelData(ch) : part.getChannelData(0);
|
|
97
|
+
dst.set(src, writePos);
|
|
98
|
+
}
|
|
99
|
+
writePos += part.length;
|
|
100
|
+
}
|
|
101
|
+
applyCrossfades(output, renderedParts);
|
|
102
|
+
return output;
|
|
103
|
+
}
|
|
104
|
+
function renderSequence(sourceBuffer, sequence, slices, ctx) {
|
|
105
|
+
const hasParams = sequence.some(
|
|
106
|
+
(e) => e.pitchSemitones && e.pitchSemitones !== 0 || e.gain !== void 0 && e.gain !== 1 || e.reverse || e.startOffset && e.startOffset > 0 || e.endOffset && e.endOffset > 0
|
|
107
|
+
);
|
|
108
|
+
if (hasParams) {
|
|
109
|
+
console.warn("[sampler-audio] renderSequence called with per-entry params \u2014 use renderSequenceAsync instead");
|
|
110
|
+
}
|
|
111
|
+
const sliceMap = new Map(slices.map((s) => [s.id, s]));
|
|
112
|
+
let totalSamples = 0;
|
|
113
|
+
for (const entry of sequence) {
|
|
114
|
+
const slice = sliceMap.get(entry.sliceId);
|
|
115
|
+
if (slice) totalSamples += slice.sampleEnd - slice.sampleStart;
|
|
116
|
+
}
|
|
117
|
+
if (totalSamples === 0) {
|
|
118
|
+
return ctx.createBuffer(sourceBuffer.numberOfChannels, 1, sourceBuffer.sampleRate);
|
|
119
|
+
}
|
|
120
|
+
const output = ctx.createBuffer(sourceBuffer.numberOfChannels, totalSamples, sourceBuffer.sampleRate);
|
|
121
|
+
let writePos = 0;
|
|
122
|
+
const parts = [];
|
|
123
|
+
for (const entry of sequence) {
|
|
124
|
+
const slice = sliceMap.get(entry.sliceId);
|
|
125
|
+
if (!slice) continue;
|
|
126
|
+
const len = slice.sampleEnd - slice.sampleStart;
|
|
127
|
+
const part = ctx.createBuffer(sourceBuffer.numberOfChannels, len, sourceBuffer.sampleRate);
|
|
128
|
+
for (let ch = 0; ch < sourceBuffer.numberOfChannels; ch++) {
|
|
129
|
+
const src = sourceBuffer.getChannelData(ch);
|
|
130
|
+
const dst = output.getChannelData(ch);
|
|
131
|
+
dst.set(src.subarray(slice.sampleStart, slice.sampleStart + len), writePos);
|
|
132
|
+
part.getChannelData(ch).set(src.subarray(slice.sampleStart, slice.sampleStart + len));
|
|
133
|
+
}
|
|
134
|
+
parts.push(part);
|
|
135
|
+
writePos += len;
|
|
136
|
+
}
|
|
137
|
+
applyCrossfades(output, parts);
|
|
138
|
+
return output;
|
|
139
|
+
}
|
|
140
|
+
function applyCrossfades(buffer, parts) {
|
|
141
|
+
const fade = Math.min(CROSSFADE_SAMPLES, Math.floor(buffer.length / (parts.length * 4)));
|
|
142
|
+
if (fade < 2) return;
|
|
143
|
+
let pos = 0;
|
|
144
|
+
for (let i = 0; i < parts.length; i++) {
|
|
145
|
+
const len = parts[i].length;
|
|
146
|
+
if (i > 0 && pos > 0) {
|
|
147
|
+
for (let ch = 0; ch < buffer.numberOfChannels; ch++) {
|
|
148
|
+
const data = buffer.getChannelData(ch);
|
|
149
|
+
for (let s = 0; s < fade; s++) {
|
|
150
|
+
const t = s / fade;
|
|
151
|
+
const idx = pos + s;
|
|
152
|
+
if (idx < buffer.length) data[idx] *= t;
|
|
153
|
+
const prevIdx = pos - fade + s;
|
|
154
|
+
if (prevIdx >= 0 && prevIdx < buffer.length) data[prevIdx] *= 1 - (1 - t) * 0.3;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
pos += len;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function renderPianoRollAsync(sourceBuffer, notes, rootPitchMidi, bpm, bars, quantizeGrid, ctx) {
|
|
162
|
+
const stepsPerBeatMap = { "1/4": 1, "1/8": 2, "1/16": 4, "1/32": 8, off: 4 };
|
|
163
|
+
const stepsPerBeat = stepsPerBeatMap[quantizeGrid] ?? 4;
|
|
164
|
+
const totalSteps = bars * 4 * stepsPerBeat;
|
|
165
|
+
const stepDurationSec = 60 / bpm / stepsPerBeat;
|
|
166
|
+
const totalDurationSec = totalSteps * stepDurationSec;
|
|
167
|
+
const totalSamples = Math.ceil(totalDurationSec * sourceBuffer.sampleRate);
|
|
168
|
+
if (notes.length === 0 || totalSamples === 0) {
|
|
169
|
+
return ctx.createBuffer(sourceBuffer.numberOfChannels, 1, sourceBuffer.sampleRate);
|
|
170
|
+
}
|
|
171
|
+
const output = ctx.createBuffer(sourceBuffer.numberOfChannels, totalSamples, sourceBuffer.sampleRate);
|
|
172
|
+
const ATTACK_SEC = 3e-3;
|
|
173
|
+
const RELEASE_SEC = 0.02;
|
|
174
|
+
const tailSamples = Math.ceil(RELEASE_SEC * sourceBuffer.sampleRate);
|
|
175
|
+
for (const note of notes) {
|
|
176
|
+
const semitones = note.pitch - rootPitchMidi;
|
|
177
|
+
const rate = Math.pow(2, semitones / 12);
|
|
178
|
+
const noteDurationSec = note.durationSteps * stepDurationSec;
|
|
179
|
+
const noteStartSample = Math.round(note.startStep * stepDurationSec * sourceBuffer.sampleRate);
|
|
180
|
+
const pitchedSourceLen = Math.max(1, Math.round(sourceBuffer.length / rate));
|
|
181
|
+
const noteLenSamples = Math.max(1, Math.round(noteDurationSec * sourceBuffer.sampleRate));
|
|
182
|
+
const renderLen = Math.max(1, Math.min(pitchedSourceLen, noteLenSamples + tailSamples));
|
|
183
|
+
const offline = new OfflineAudioContext(sourceBuffer.numberOfChannels, renderLen, sourceBuffer.sampleRate);
|
|
184
|
+
const src = offline.createBufferSource();
|
|
185
|
+
src.buffer = sourceBuffer;
|
|
186
|
+
src.playbackRate.value = rate;
|
|
187
|
+
const gainNode = offline.createGain();
|
|
188
|
+
const velGain = note.velocity / 127;
|
|
189
|
+
const renderDurSec = renderLen / sourceBuffer.sampleRate;
|
|
190
|
+
const atk = Math.min(ATTACK_SEC, renderDurSec * 0.25);
|
|
191
|
+
const rel = Math.min(RELEASE_SEC, renderDurSec * 0.5);
|
|
192
|
+
const relStart = Math.max(atk, renderDurSec - rel);
|
|
193
|
+
gainNode.gain.setValueAtTime(0, 0);
|
|
194
|
+
gainNode.gain.linearRampToValueAtTime(velGain, atk);
|
|
195
|
+
gainNode.gain.setValueAtTime(velGain, relStart);
|
|
196
|
+
gainNode.gain.linearRampToValueAtTime(0, renderDurSec);
|
|
197
|
+
src.connect(gainNode);
|
|
198
|
+
gainNode.connect(offline.destination);
|
|
199
|
+
src.start();
|
|
200
|
+
const rendered = await offline.startRendering();
|
|
201
|
+
for (let ch = 0; ch < output.numberOfChannels; ch++) {
|
|
202
|
+
const dst = output.getChannelData(ch);
|
|
203
|
+
const srcData = rendered.numberOfChannels > ch ? rendered.getChannelData(ch) : rendered.getChannelData(0);
|
|
204
|
+
const copyLen = Math.min(srcData.length, totalSamples - noteStartSample);
|
|
205
|
+
for (let i = 0; i < copyLen; i++) {
|
|
206
|
+
dst[noteStartSample + i] += srcData[i];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
let peak = 0;
|
|
211
|
+
for (let ch = 0; ch < output.numberOfChannels; ch++) {
|
|
212
|
+
const data = output.getChannelData(ch);
|
|
213
|
+
for (let i = 0; i < data.length; i++) {
|
|
214
|
+
const abs = Math.abs(data[i]);
|
|
215
|
+
if (abs > peak) peak = abs;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (peak > 1) {
|
|
219
|
+
const scale = 0.95 / peak;
|
|
220
|
+
for (let ch = 0; ch < output.numberOfChannels; ch++) {
|
|
221
|
+
const data = output.getChannelData(ch);
|
|
222
|
+
for (let i = 0; i < data.length; i++) data[i] *= scale;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return output;
|
|
226
|
+
}
|
|
227
|
+
function spliceBuffer(original, insertAtSample, replaceLength, replacement, ctx) {
|
|
228
|
+
const newLen = original.length - replaceLength + replacement.length;
|
|
229
|
+
const out = ctx.createBuffer(original.numberOfChannels, Math.max(newLen, 1), original.sampleRate);
|
|
230
|
+
for (let ch = 0; ch < original.numberOfChannels; ch++) {
|
|
231
|
+
const src = original.getChannelData(ch);
|
|
232
|
+
const dst = out.getChannelData(ch);
|
|
233
|
+
const rep = replacement.numberOfChannels > ch ? replacement.getChannelData(ch) : replacement.getChannelData(0);
|
|
234
|
+
dst.set(src.subarray(0, insertAtSample));
|
|
235
|
+
dst.set(rep, insertAtSample);
|
|
236
|
+
const afterOriginal = insertAtSample + replaceLength;
|
|
237
|
+
const afterNew = insertAtSample + replacement.length;
|
|
238
|
+
if (afterOriginal < src.length) {
|
|
239
|
+
dst.set(src.subarray(afterOriginal), afterNew);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export { extractRegion, fracBarToSample, renderPianoRollAsync, renderSequence, renderSequenceAsync, spliceBuffer };
|
|
246
|
+
//# sourceMappingURL=chunk-OFGZURP6.js.map
|
|
247
|
+
//# sourceMappingURL=chunk-OFGZURP6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daw/utils/sampler-audio.ts"],"names":[],"mappings":"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","file":"chunk-OFGZURP6.js","sourcesContent":["/**\n * Audio processing utilities for the Bar Sampler.\n * Handles buffer extraction, sequence rendering with per-entry pitch/gain/reverse,\n * and crossfades.\n */\n\nimport type { SamplerSlice, SamplerSequenceEntry, PianoRollNote } from '../interfaces';\nimport type { QuantizeGrid } from '../store/sampler-store';\n\nconst CROSSFADE_SAMPLES = 256;\n\n/** Convert a fractional bar position to a sample offset (for beat-level extraction). */\nexport function fracBarToSample(\n bar: number,\n barMapping: { sampleStart: number; duration: number }[],\n bufLen: number,\n): number {\n if (barMapping.length === 0) return 0;\n const floorBar = Math.floor(bar);\n const frac = bar - floorBar;\n if (floorBar >= barMapping.length) return bufLen;\n const entry = barMapping[Math.max(0, floorBar)];\n return Math.round(entry.sampleStart + frac * entry.duration);\n}\n\n/** Extract a sample range from an AudioBuffer into a new AudioBuffer. */\nexport function extractRegion(\n source: AudioBuffer,\n sampleStart: number,\n sampleEnd: number,\n ctx: AudioContext,\n): AudioBuffer {\n const len = Math.max(0, sampleEnd - sampleStart);\n if (len === 0) return ctx.createBuffer(source.numberOfChannels, 1, source.sampleRate);\n const out = ctx.createBuffer(source.numberOfChannels, len, source.sampleRate);\n for (let ch = 0; ch < source.numberOfChannels; ch++) {\n const src = source.getChannelData(ch);\n const dst = out.getChannelData(ch);\n dst.set(src.subarray(sampleStart, sampleStart + len));\n }\n return out;\n}\n\n/** Reverse an AudioBuffer's channel data in-place and return it. */\nfunction reverseBuffer(buffer: AudioBuffer): AudioBuffer {\n for (let ch = 0; ch < buffer.numberOfChannels; ch++) {\n buffer.getChannelData(ch).reverse();\n }\n return buffer;\n}\n\n/** Apply gain to an AudioBuffer's channel data in-place. */\nfunction applyGain(buffer: AudioBuffer, gain: number): AudioBuffer {\n if (gain === 1) return buffer;\n for (let ch = 0; ch < buffer.numberOfChannels; ch++) {\n const data = buffer.getChannelData(ch);\n for (let i = 0; i < data.length; i++) data[i] *= gain;\n }\n return buffer;\n}\n\n/**\n * Extract a slice region from the source buffer, applying trim offsets.\n * startOffset/endOffset are 0..1 fractions within the slice's sample range.\n */\nfunction extractSliceWithTrim(\n sourceBuffer: AudioBuffer,\n slice: SamplerSlice,\n startOffset: number,\n endOffset: number,\n ctx: BaseAudioContext,\n): AudioBuffer {\n const sliceLen = slice.sampleEnd - slice.sampleStart;\n const trimStart = slice.sampleStart + Math.round(sliceLen * startOffset);\n const trimEnd = slice.sampleStart + Math.round(sliceLen * (1 - endOffset));\n const len = Math.max(1, trimEnd - trimStart);\n const out = ctx.createBuffer(sourceBuffer.numberOfChannels, len, sourceBuffer.sampleRate);\n for (let ch = 0; ch < sourceBuffer.numberOfChannels; ch++) {\n const src = sourceBuffer.getChannelData(ch);\n out.getChannelData(ch).set(src.subarray(trimStart, trimStart + len));\n }\n return out;\n}\n\n/** Pitch engine: classic = speed+pitch together (resampler), formant = preserve length */\nexport type SamplerPitchMode = 'classic' | 'formant';\n\n/**\n * Pitch-shift a buffer using playbackRate via OfflineAudioContext.\n * Classic sampler behaviour: pitch and speed change together.\n */\nasync function pitchShiftClassic(\n buffer: AudioBuffer,\n semitones: number,\n): Promise<AudioBuffer> {\n if (semitones === 0) return buffer;\n const rate = Math.pow(2, semitones / 12);\n const newLength = Math.max(1, Math.round(buffer.length / rate));\n const offline = new OfflineAudioContext(buffer.numberOfChannels, newLength, buffer.sampleRate);\n const source = offline.createBufferSource();\n source.buffer = buffer;\n source.playbackRate.value = rate;\n source.connect(offline.destination);\n source.start();\n return offline.startRendering();\n}\n\n/**\n * Pitch-shift preserving duration (formant-preserving).\n * TODO: Use phase-vocoder or Rubberband for true formant preservation.\n * For now falls back to classic for consistency.\n */\nasync function pitchShiftFormant(\n buffer: AudioBuffer,\n semitones: number,\n _ctx: BaseAudioContext,\n): Promise<AudioBuffer> {\n if (semitones === 0) return buffer;\n // Placeholder: use classic until formant-preserving algo is implemented\n return pitchShiftClassic(buffer, semitones);\n}\n\n/**\n * Render an arranged sequence of slices into a single AudioBuffer.\n * Supports per-entry pitch shifting, gain, reverse, and trim offsets.\n * Uses classic (playbackRate) pitch shifting for real-time sampler character.\n */\nexport async function renderSequenceAsync(\n sourceBuffer: AudioBuffer,\n sequence: SamplerSequenceEntry[],\n slices: SamplerSlice[],\n ctx: AudioContext,\n pitchMode: SamplerPitchMode = 'classic',\n): Promise<AudioBuffer> {\n const sliceMap = new Map(slices.map((s) => [s.id, s]));\n\n const renderedParts: AudioBuffer[] = [];\n let totalSamples = 0;\n\n for (const entry of sequence) {\n const slice = sliceMap.get(entry.sliceId);\n if (!slice) continue;\n\n let part = extractSliceWithTrim(\n sourceBuffer, slice,\n entry.startOffset ?? 0,\n entry.endOffset ?? 0,\n ctx,\n );\n\n if (entry.reverse) part = reverseBuffer(part);\n if (entry.pitchSemitones && entry.pitchSemitones !== 0) {\n part = pitchMode === 'formant'\n ? await pitchShiftFormant(part, entry.pitchSemitones, ctx)\n : await pitchShiftClassic(part, entry.pitchSemitones);\n }\n if (entry.gain !== undefined && entry.gain !== 1) {\n part = applyGain(part, entry.gain);\n }\n\n renderedParts.push(part);\n totalSamples += part.length;\n }\n\n if (totalSamples === 0) {\n return ctx.createBuffer(sourceBuffer.numberOfChannels, 1, sourceBuffer.sampleRate);\n }\n\n const output = ctx.createBuffer(sourceBuffer.numberOfChannels, totalSamples, sourceBuffer.sampleRate);\n let writePos = 0;\n for (const part of renderedParts) {\n for (let ch = 0; ch < output.numberOfChannels; ch++) {\n const dst = output.getChannelData(ch);\n const src = part.numberOfChannels > ch ? part.getChannelData(ch) : part.getChannelData(0);\n dst.set(src, writePos);\n }\n writePos += part.length;\n }\n\n applyCrossfades(output, renderedParts);\n return output;\n}\n\n/**\n * Synchronous render for backwards compatibility (no pitch/gain/reverse applied).\n * Used when no entries have per-entry params set.\n */\nexport function renderSequence(\n sourceBuffer: AudioBuffer,\n sequence: SamplerSequenceEntry[],\n slices: SamplerSlice[],\n ctx: AudioContext,\n): AudioBuffer {\n const hasParams = sequence.some((e) =>\n (e.pitchSemitones && e.pitchSemitones !== 0) ||\n (e.gain !== undefined && e.gain !== 1) ||\n e.reverse ||\n (e.startOffset && e.startOffset > 0) ||\n (e.endOffset && e.endOffset > 0)\n );\n\n if (hasParams) {\n console.warn('[sampler-audio] renderSequence called with per-entry params — use renderSequenceAsync instead');\n }\n\n const sliceMap = new Map(slices.map((s) => [s.id, s]));\n\n let totalSamples = 0;\n for (const entry of sequence) {\n const slice = sliceMap.get(entry.sliceId);\n if (slice) totalSamples += slice.sampleEnd - slice.sampleStart;\n }\n\n if (totalSamples === 0) {\n return ctx.createBuffer(sourceBuffer.numberOfChannels, 1, sourceBuffer.sampleRate);\n }\n\n const output = ctx.createBuffer(sourceBuffer.numberOfChannels, totalSamples, sourceBuffer.sampleRate);\n let writePos = 0;\n const parts: AudioBuffer[] = [];\n for (const entry of sequence) {\n const slice = sliceMap.get(entry.sliceId);\n if (!slice) continue;\n const len = slice.sampleEnd - slice.sampleStart;\n const part = ctx.createBuffer(sourceBuffer.numberOfChannels, len, sourceBuffer.sampleRate);\n for (let ch = 0; ch < sourceBuffer.numberOfChannels; ch++) {\n const src = sourceBuffer.getChannelData(ch);\n const dst = output.getChannelData(ch);\n dst.set(src.subarray(slice.sampleStart, slice.sampleStart + len), writePos);\n part.getChannelData(ch).set(src.subarray(slice.sampleStart, slice.sampleStart + len));\n }\n parts.push(part);\n writePos += len;\n }\n\n applyCrossfades(output, parts);\n return output;\n}\n\nfunction applyCrossfades(\n buffer: AudioBuffer,\n parts: AudioBuffer[],\n): void {\n const fade = Math.min(CROSSFADE_SAMPLES, Math.floor(buffer.length / (parts.length * 4)));\n if (fade < 2) return;\n\n let pos = 0;\n for (let i = 0; i < parts.length; i++) {\n const len = parts[i].length;\n\n if (i > 0 && pos > 0) {\n for (let ch = 0; ch < buffer.numberOfChannels; ch++) {\n const data = buffer.getChannelData(ch);\n for (let s = 0; s < fade; s++) {\n const t = s / fade;\n const idx = pos + s;\n if (idx < buffer.length) data[idx] *= t;\n const prevIdx = pos - fade + s;\n if (prevIdx >= 0 && prevIdx < buffer.length) data[prevIdx] *= 1 - (1 - t) * 0.3;\n }\n }\n }\n pos += len;\n }\n}\n\n/**\n * Render piano roll notes into a single AudioBuffer.\n * Each note is a pitch-shifted copy of the source sample (or first slice),\n * placed at its grid-quantized position. Polyphonic — overlapping notes are mixed.\n *\n * @param sourceBuffer The sample audio to pitch-shift\n * @param notes Piano roll note events\n * @param rootPitchMidi Detected root pitch of the source (default 60 = C4)\n * @param bpm Tempo in beats per minute\n * @param bars Number of bars the piano roll spans\n * @param quantizeGrid Grid resolution\n * @param ctx AudioContext for buffer creation\n */\nexport async function renderPianoRollAsync(\n sourceBuffer: AudioBuffer,\n notes: PianoRollNote[],\n rootPitchMidi: number,\n bpm: number,\n bars: number,\n quantizeGrid: QuantizeGrid,\n ctx: AudioContext,\n): Promise<AudioBuffer> {\n const stepsPerBeatMap: Record<string, number> = { '1/4': 1, '1/8': 2, '1/16': 4, '1/32': 8, off: 4 };\n const stepsPerBeat = stepsPerBeatMap[quantizeGrid] ?? 4;\n const totalSteps = bars * 4 * stepsPerBeat;\n const stepDurationSec = 60 / bpm / stepsPerBeat;\n const totalDurationSec = totalSteps * stepDurationSec;\n const totalSamples = Math.ceil(totalDurationSec * sourceBuffer.sampleRate);\n\n if (notes.length === 0 || totalSamples === 0) {\n return ctx.createBuffer(sourceBuffer.numberOfChannels, 1, sourceBuffer.sampleRate);\n }\n\n const output = ctx.createBuffer(sourceBuffer.numberOfChannels, totalSamples, sourceBuffer.sampleRate);\n\n // Short fades around each note remove the clicks / pops caused by\n // hard-cutting the pitch-shifted source mid-waveform.\n const ATTACK_SEC = 0.003;\n const RELEASE_SEC = 0.02;\n const tailSamples = Math.ceil(RELEASE_SEC * sourceBuffer.sampleRate);\n\n for (const note of notes) {\n const semitones = note.pitch - rootPitchMidi;\n const rate = Math.pow(2, semitones / 12);\n const noteDurationSec = note.durationSteps * stepDurationSec;\n const noteStartSample = Math.round(note.startStep * stepDurationSec * sourceBuffer.sampleRate);\n\n const pitchedSourceLen = Math.max(1, Math.round(sourceBuffer.length / rate));\n const noteLenSamples = Math.max(1, Math.round(noteDurationSec * sourceBuffer.sampleRate));\n // Let the note ring slightly past its nominal end so the release fade\n // doesn't eat into the audible portion of the note.\n const renderLen = Math.max(1, Math.min(pitchedSourceLen, noteLenSamples + tailSamples));\n\n const offline = new OfflineAudioContext(sourceBuffer.numberOfChannels, renderLen, sourceBuffer.sampleRate);\n const src = offline.createBufferSource();\n src.buffer = sourceBuffer;\n src.playbackRate.value = rate;\n\n const gainNode = offline.createGain();\n const velGain = note.velocity / 127;\n const renderDurSec = renderLen / sourceBuffer.sampleRate;\n const atk = Math.min(ATTACK_SEC, renderDurSec * 0.25);\n const rel = Math.min(RELEASE_SEC, renderDurSec * 0.5);\n const relStart = Math.max(atk, renderDurSec - rel);\n gainNode.gain.setValueAtTime(0, 0);\n gainNode.gain.linearRampToValueAtTime(velGain, atk);\n gainNode.gain.setValueAtTime(velGain, relStart);\n gainNode.gain.linearRampToValueAtTime(0, renderDurSec);\n\n src.connect(gainNode);\n gainNode.connect(offline.destination);\n src.start();\n\n const rendered = await offline.startRendering();\n\n // Mix into output (additive)\n for (let ch = 0; ch < output.numberOfChannels; ch++) {\n const dst = output.getChannelData(ch);\n const srcData = rendered.numberOfChannels > ch ? rendered.getChannelData(ch) : rendered.getChannelData(0);\n const copyLen = Math.min(srcData.length, totalSamples - noteStartSample);\n for (let i = 0; i < copyLen; i++) {\n dst[noteStartSample + i] += srcData[i];\n }\n }\n }\n\n // Normalize if any clipping\n let peak = 0;\n for (let ch = 0; ch < output.numberOfChannels; ch++) {\n const data = output.getChannelData(ch);\n for (let i = 0; i < data.length; i++) {\n const abs = Math.abs(data[i]);\n if (abs > peak) peak = abs;\n }\n }\n if (peak > 1) {\n const scale = 0.95 / peak;\n for (let ch = 0; ch < output.numberOfChannels; ch++) {\n const data = output.getChannelData(ch);\n for (let i = 0; i < data.length; i++) data[i] *= scale;\n }\n }\n\n return output;\n}\n\n/**\n * Splice a resampled buffer into a full stem buffer at a given sample position.\n * Returns a new AudioBuffer with the resampled content inserted.\n */\nexport function spliceBuffer(\n original: AudioBuffer,\n insertAtSample: number,\n replaceLength: number,\n replacement: AudioBuffer,\n ctx: AudioContext,\n): AudioBuffer {\n const newLen = original.length - replaceLength + replacement.length;\n const out = ctx.createBuffer(original.numberOfChannels, Math.max(newLen, 1), original.sampleRate);\n\n for (let ch = 0; ch < original.numberOfChannels; ch++) {\n const src = original.getChannelData(ch);\n const dst = out.getChannelData(ch);\n const rep = replacement.numberOfChannels > ch ? replacement.getChannelData(ch) : replacement.getChannelData(0);\n\n dst.set(src.subarray(0, insertAtSample));\n dst.set(rep, insertAtSample);\n const afterOriginal = insertAtSample + replaceLength;\n const afterNew = insertAtSample + replacement.length;\n if (afterOriginal < src.length) {\n dst.set(src.subarray(afterOriginal), afterNew);\n }\n }\n return out;\n}\n"]}
|