@waveform-playlist/spectrogram 9.5.0 → 9.5.2
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 +16 -25
- package/dist/index.d.ts +16 -25
- package/dist/index.js +360 -266
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +358 -266
- package/dist/index.mjs.map +1 -1
- package/dist/worker/spectrogram.worker.mjs +190 -159
- package/dist/worker/spectrogram.worker.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -30,12 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
SpectrogramAbortError: () => SpectrogramAbortError,
|
|
33
34
|
SpectrogramMenuItems: () => SpectrogramMenuItems,
|
|
34
35
|
SpectrogramProvider: () => SpectrogramProvider,
|
|
35
36
|
SpectrogramSettingsModal: () => SpectrogramSettingsModal,
|
|
36
37
|
computeSpectrogram: () => computeSpectrogram,
|
|
37
38
|
computeSpectrogramMono: () => computeSpectrogramMono,
|
|
38
39
|
createSpectrogramWorker: () => createSpectrogramWorker,
|
|
40
|
+
createSpectrogramWorkerPool: () => createSpectrogramWorkerPool,
|
|
39
41
|
getColorMap: () => getColorMap,
|
|
40
42
|
getFrequencyScale: () => getFrequencyScale
|
|
41
43
|
});
|
|
@@ -3819,6 +3821,12 @@ var SpectrogramSettingsModal = ({
|
|
|
3819
3821
|
};
|
|
3820
3822
|
|
|
3821
3823
|
// src/worker/createSpectrogramWorker.ts
|
|
3824
|
+
var SpectrogramAbortError = class extends Error {
|
|
3825
|
+
constructor() {
|
|
3826
|
+
super("aborted");
|
|
3827
|
+
this.name = "SpectrogramAbortError";
|
|
3828
|
+
}
|
|
3829
|
+
};
|
|
3822
3830
|
function addPending(map, id, resolve, reject) {
|
|
3823
3831
|
map.set(id, { resolve, reject });
|
|
3824
3832
|
}
|
|
@@ -3836,15 +3844,15 @@ function createSpectrogramWorker(worker) {
|
|
|
3836
3844
|
case "error":
|
|
3837
3845
|
entry.reject(new Error(msg.error));
|
|
3838
3846
|
break;
|
|
3847
|
+
case "aborted":
|
|
3848
|
+
entry.reject(new SpectrogramAbortError());
|
|
3849
|
+
break;
|
|
3839
3850
|
case "cache-key":
|
|
3840
3851
|
entry.resolve({ cacheKey: msg.cacheKey });
|
|
3841
3852
|
break;
|
|
3842
3853
|
case "done":
|
|
3843
3854
|
entry.resolve(void 0);
|
|
3844
3855
|
break;
|
|
3845
|
-
case "spectrograms":
|
|
3846
|
-
entry.resolve(msg.spectrograms);
|
|
3847
|
-
break;
|
|
3848
3856
|
}
|
|
3849
3857
|
} else if (msg.id) {
|
|
3850
3858
|
console.warn(`[spectrogram] Received response for unknown message ID: ${msg.id}`);
|
|
@@ -3858,28 +3866,7 @@ function createSpectrogramWorker(worker) {
|
|
|
3858
3866
|
pending.clear();
|
|
3859
3867
|
};
|
|
3860
3868
|
return {
|
|
3861
|
-
|
|
3862
|
-
if (terminated) return Promise.reject(new Error("Worker terminated"));
|
|
3863
|
-
const id = String(++idCounter);
|
|
3864
|
-
return new Promise((resolve, reject) => {
|
|
3865
|
-
addPending(pending, id, resolve, reject);
|
|
3866
|
-
const transferableArrays = params.channelDataArrays.map((arr) => arr.slice());
|
|
3867
|
-
const transferables = transferableArrays.map((arr) => arr.buffer);
|
|
3868
|
-
worker.postMessage(
|
|
3869
|
-
{
|
|
3870
|
-
id,
|
|
3871
|
-
channelDataArrays: transferableArrays,
|
|
3872
|
-
config: params.config,
|
|
3873
|
-
sampleRate: params.sampleRate,
|
|
3874
|
-
offsetSamples: params.offsetSamples,
|
|
3875
|
-
durationSamples: params.durationSamples,
|
|
3876
|
-
mono: params.mono
|
|
3877
|
-
},
|
|
3878
|
-
transferables
|
|
3879
|
-
);
|
|
3880
|
-
});
|
|
3881
|
-
},
|
|
3882
|
-
computeFFT(params) {
|
|
3869
|
+
computeFFT(params, generation = 0) {
|
|
3883
3870
|
if (terminated) return Promise.reject(new Error("Worker terminated"));
|
|
3884
3871
|
const id = String(++idCounter);
|
|
3885
3872
|
return new Promise((resolve, reject) => {
|
|
@@ -3891,6 +3878,7 @@ function createSpectrogramWorker(worker) {
|
|
|
3891
3878
|
{
|
|
3892
3879
|
type: "compute-fft",
|
|
3893
3880
|
id,
|
|
3881
|
+
generation,
|
|
3894
3882
|
clipId: params.clipId,
|
|
3895
3883
|
channelDataArrays: transferableArrays,
|
|
3896
3884
|
config: params.config,
|
|
@@ -3898,13 +3886,14 @@ function createSpectrogramWorker(worker) {
|
|
|
3898
3886
|
offsetSamples: params.offsetSamples,
|
|
3899
3887
|
durationSamples: params.durationSamples,
|
|
3900
3888
|
mono: params.mono,
|
|
3901
|
-
...params.sampleRange ? { sampleRange: params.sampleRange } : {}
|
|
3889
|
+
...params.sampleRange ? { sampleRange: params.sampleRange } : {},
|
|
3890
|
+
...params.channelFilter !== void 0 ? { channelFilter: params.channelFilter } : {}
|
|
3902
3891
|
},
|
|
3903
3892
|
transferables
|
|
3904
3893
|
);
|
|
3905
3894
|
});
|
|
3906
3895
|
},
|
|
3907
|
-
renderChunks(params) {
|
|
3896
|
+
renderChunks(params, generation = 0) {
|
|
3908
3897
|
if (terminated) return Promise.reject(new Error("Worker terminated"));
|
|
3909
3898
|
const id = String(++idCounter);
|
|
3910
3899
|
return new Promise((resolve, reject) => {
|
|
@@ -3912,6 +3901,7 @@ function createSpectrogramWorker(worker) {
|
|
|
3912
3901
|
worker.postMessage({
|
|
3913
3902
|
type: "render-chunks",
|
|
3914
3903
|
id,
|
|
3904
|
+
generation,
|
|
3915
3905
|
cacheKey: params.cacheKey,
|
|
3916
3906
|
canvasIds: params.canvasIds,
|
|
3917
3907
|
canvasWidths: params.canvasWidths,
|
|
@@ -3929,6 +3919,10 @@ function createSpectrogramWorker(worker) {
|
|
|
3929
3919
|
});
|
|
3930
3920
|
});
|
|
3931
3921
|
},
|
|
3922
|
+
abortGeneration(generation) {
|
|
3923
|
+
if (terminated) return;
|
|
3924
|
+
worker.postMessage({ type: "abort-generation", generation });
|
|
3925
|
+
},
|
|
3932
3926
|
registerCanvas(canvasId, canvas) {
|
|
3933
3927
|
worker.postMessage({ type: "register-canvas", canvasId, canvas }, [canvas]);
|
|
3934
3928
|
},
|
|
@@ -3948,29 +3942,6 @@ function createSpectrogramWorker(worker) {
|
|
|
3948
3942
|
worker.postMessage({ type: "unregister-audio-data", clipId });
|
|
3949
3943
|
registeredClipIds.delete(clipId);
|
|
3950
3944
|
},
|
|
3951
|
-
computeAndRender(params) {
|
|
3952
|
-
if (terminated) return Promise.reject(new Error("Worker terminated"));
|
|
3953
|
-
const id = String(++idCounter);
|
|
3954
|
-
return new Promise((resolve, reject) => {
|
|
3955
|
-
addPending(pending, id, resolve, reject);
|
|
3956
|
-
const transferableArrays = params.channelDataArrays.map((arr) => arr.slice());
|
|
3957
|
-
const transferables = transferableArrays.map((arr) => arr.buffer);
|
|
3958
|
-
worker.postMessage(
|
|
3959
|
-
{
|
|
3960
|
-
type: "compute-render",
|
|
3961
|
-
id,
|
|
3962
|
-
channelDataArrays: transferableArrays,
|
|
3963
|
-
config: params.config,
|
|
3964
|
-
sampleRate: params.sampleRate,
|
|
3965
|
-
offsetSamples: params.offsetSamples,
|
|
3966
|
-
durationSamples: params.durationSamples,
|
|
3967
|
-
mono: params.mono,
|
|
3968
|
-
render: params.render
|
|
3969
|
-
},
|
|
3970
|
-
transferables
|
|
3971
|
-
);
|
|
3972
|
-
});
|
|
3973
|
-
},
|
|
3974
3945
|
terminate() {
|
|
3975
3946
|
terminated = true;
|
|
3976
3947
|
worker.terminate();
|
|
@@ -3982,6 +3953,76 @@ function createSpectrogramWorker(worker) {
|
|
|
3982
3953
|
};
|
|
3983
3954
|
}
|
|
3984
3955
|
|
|
3956
|
+
// src/worker/createSpectrogramWorkerPool.ts
|
|
3957
|
+
function parseChannelFromCanvasId(canvasId) {
|
|
3958
|
+
const match = canvasId.match(/-ch(\d+)-/);
|
|
3959
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
3960
|
+
}
|
|
3961
|
+
function defaultPoolSize() {
|
|
3962
|
+
return 2;
|
|
3963
|
+
}
|
|
3964
|
+
function createSpectrogramWorkerPool(createWorker, poolSize = defaultPoolSize()) {
|
|
3965
|
+
const workers = [];
|
|
3966
|
+
try {
|
|
3967
|
+
for (let i = 0; i < poolSize; i++) {
|
|
3968
|
+
workers.push(createSpectrogramWorker(createWorker()));
|
|
3969
|
+
}
|
|
3970
|
+
} catch (err) {
|
|
3971
|
+
for (const w of workers) {
|
|
3972
|
+
w.terminate();
|
|
3973
|
+
}
|
|
3974
|
+
throw err;
|
|
3975
|
+
}
|
|
3976
|
+
function getWorkerForChannel(channelIndex) {
|
|
3977
|
+
return workers[channelIndex % workers.length];
|
|
3978
|
+
}
|
|
3979
|
+
return {
|
|
3980
|
+
computeFFT(params, generation = 0) {
|
|
3981
|
+
if (params.mono) {
|
|
3982
|
+
return workers[0].computeFFT(params, generation);
|
|
3983
|
+
}
|
|
3984
|
+
const channelCount = params.channelDataArrays.length;
|
|
3985
|
+
const activeWorkers = workers.slice(0, channelCount);
|
|
3986
|
+
const promises = activeWorkers.map(
|
|
3987
|
+
(w, i) => w.computeFFT({ ...params, channelFilter: i }, generation)
|
|
3988
|
+
);
|
|
3989
|
+
return Promise.all(promises).then((results) => results[0]);
|
|
3990
|
+
},
|
|
3991
|
+
renderChunks(params, generation = 0) {
|
|
3992
|
+
const worker = getWorkerForChannel(params.channelIndex);
|
|
3993
|
+
return worker.renderChunks({ ...params, channelIndex: 0 }, generation);
|
|
3994
|
+
},
|
|
3995
|
+
abortGeneration(generation) {
|
|
3996
|
+
for (const w of workers) {
|
|
3997
|
+
w.abortGeneration(generation);
|
|
3998
|
+
}
|
|
3999
|
+
},
|
|
4000
|
+
registerCanvas(canvasId, canvas) {
|
|
4001
|
+
const ch = parseChannelFromCanvasId(canvasId);
|
|
4002
|
+
getWorkerForChannel(ch).registerCanvas(canvasId, canvas);
|
|
4003
|
+
},
|
|
4004
|
+
unregisterCanvas(canvasId) {
|
|
4005
|
+
const ch = parseChannelFromCanvasId(canvasId);
|
|
4006
|
+
getWorkerForChannel(ch).unregisterCanvas(canvasId);
|
|
4007
|
+
},
|
|
4008
|
+
registerAudioData(clipId, channelDataArrays, sampleRate) {
|
|
4009
|
+
for (const w of workers) {
|
|
4010
|
+
w.registerAudioData(clipId, channelDataArrays, sampleRate);
|
|
4011
|
+
}
|
|
4012
|
+
},
|
|
4013
|
+
unregisterAudioData(clipId) {
|
|
4014
|
+
for (const w of workers) {
|
|
4015
|
+
w.unregisterAudioData(clipId);
|
|
4016
|
+
}
|
|
4017
|
+
},
|
|
4018
|
+
terminate() {
|
|
4019
|
+
for (const w of workers) {
|
|
4020
|
+
w.terminate();
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
};
|
|
4024
|
+
}
|
|
4025
|
+
|
|
3985
4026
|
// src/SpectrogramProvider.tsx
|
|
3986
4027
|
var import_react2 = require("react");
|
|
3987
4028
|
var import_core = require("@waveform-playlist/core");
|
|
@@ -4000,13 +4041,11 @@ function extractChunkNumber(canvasId) {
|
|
|
4000
4041
|
var SpectrogramProvider = ({
|
|
4001
4042
|
config: spectrogramConfig,
|
|
4002
4043
|
colorMap: spectrogramColorMap,
|
|
4044
|
+
workerPoolSize,
|
|
4003
4045
|
children
|
|
4004
4046
|
}) => {
|
|
4005
|
-
const { tracks, waveHeight, samplesPerPixel, isReady, mono
|
|
4047
|
+
const { tracks, waveHeight, samplesPerPixel, isReady, mono } = (0, import_browser2.usePlaylistData)();
|
|
4006
4048
|
const { scrollContainerRef } = (0, import_browser2.usePlaylistControls)();
|
|
4007
|
-
const [spectrogramDataMap, setSpectrogramDataMap] = (0, import_react2.useState)(
|
|
4008
|
-
/* @__PURE__ */ new Map()
|
|
4009
|
-
);
|
|
4010
4049
|
const [trackSpectrogramOverrides, setTrackSpectrogramOverrides] = (0, import_react2.useState)(/* @__PURE__ */ new Map());
|
|
4011
4050
|
const spectrogramCanvasRegistryRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
4012
4051
|
const [spectrogramCanvasVersion, setSpectrogramCanvasVersion] = (0, import_react2.useState)(0);
|
|
@@ -4016,7 +4055,7 @@ var SpectrogramProvider = ({
|
|
|
4016
4055
|
const spectrogramGenerationRef = (0, import_react2.useRef)(0);
|
|
4017
4056
|
const prevCanvasVersionRef = (0, import_react2.useRef)(0);
|
|
4018
4057
|
const [spectrogramWorkerReady, setSpectrogramWorkerReady] = (0, import_react2.useState)(false);
|
|
4019
|
-
const
|
|
4058
|
+
const renderedClipIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
4020
4059
|
const backgroundRenderAbortRef = (0, import_react2.useRef)(null);
|
|
4021
4060
|
const registeredAudioClipIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
4022
4061
|
(0, import_react2.useEffect)(() => {
|
|
@@ -4030,15 +4069,19 @@ var SpectrogramProvider = ({
|
|
|
4030
4069
|
let workerApi = spectrogramWorkerRef.current;
|
|
4031
4070
|
if (!workerApi) {
|
|
4032
4071
|
try {
|
|
4033
|
-
|
|
4034
|
-
new
|
|
4035
|
-
|
|
4072
|
+
workerApi = createSpectrogramWorkerPool(
|
|
4073
|
+
() => new Worker(
|
|
4074
|
+
new URL("@waveform-playlist/spectrogram/worker/spectrogram.worker", import_meta.url),
|
|
4075
|
+
{ type: "module" }
|
|
4076
|
+
),
|
|
4077
|
+
workerPoolSize
|
|
4036
4078
|
);
|
|
4037
|
-
workerApi = createSpectrogramWorker(rawWorker);
|
|
4038
4079
|
spectrogramWorkerRef.current = workerApi;
|
|
4039
4080
|
setSpectrogramWorkerReady(true);
|
|
4040
|
-
} catch {
|
|
4041
|
-
console.warn(
|
|
4081
|
+
} catch (err) {
|
|
4082
|
+
console.warn(
|
|
4083
|
+
`[waveform-playlist] Spectrogram Web Worker unavailable for pre-transfer: ${err instanceof Error ? err.message : String(err)}`
|
|
4084
|
+
);
|
|
4042
4085
|
return;
|
|
4043
4086
|
}
|
|
4044
4087
|
}
|
|
@@ -4110,42 +4153,30 @@ var SpectrogramProvider = ({
|
|
|
4110
4153
|
prevSpectrogramConfigRef.current = currentKeys;
|
|
4111
4154
|
prevSpectrogramFFTKeyRef.current = currentFFTKeys;
|
|
4112
4155
|
}
|
|
4113
|
-
if (configChanged) {
|
|
4114
|
-
setSpectrogramDataMap((prevMap) => {
|
|
4115
|
-
const activeClipIds = /* @__PURE__ */ new Set();
|
|
4116
|
-
for (const track of tracks) {
|
|
4117
|
-
const mode = trackSpectrogramOverrides.get(track.id)?.renderMode ?? track.renderMode ?? "waveform";
|
|
4118
|
-
if (mode === "spectrogram" || mode === "both") {
|
|
4119
|
-
for (const clip of track.clips) {
|
|
4120
|
-
activeClipIds.add(clip.id);
|
|
4121
|
-
}
|
|
4122
|
-
}
|
|
4123
|
-
}
|
|
4124
|
-
const newMap = new Map(prevMap);
|
|
4125
|
-
for (const clipId of newMap.keys()) {
|
|
4126
|
-
if (!activeClipIds.has(clipId)) {
|
|
4127
|
-
newMap.delete(clipId);
|
|
4128
|
-
}
|
|
4129
|
-
}
|
|
4130
|
-
return newMap;
|
|
4131
|
-
});
|
|
4132
|
-
}
|
|
4133
4156
|
if (backgroundRenderAbortRef.current) {
|
|
4134
4157
|
backgroundRenderAbortRef.current.aborted = true;
|
|
4135
4158
|
}
|
|
4136
4159
|
const generation = ++spectrogramGenerationRef.current;
|
|
4160
|
+
if (spectrogramWorkerRef.current) {
|
|
4161
|
+
spectrogramWorkerRef.current.abortGeneration(generation);
|
|
4162
|
+
}
|
|
4137
4163
|
let workerApi = spectrogramWorkerRef.current;
|
|
4138
4164
|
if (!workerApi) {
|
|
4139
4165
|
try {
|
|
4140
|
-
|
|
4141
|
-
new
|
|
4142
|
-
|
|
4166
|
+
workerApi = createSpectrogramWorkerPool(
|
|
4167
|
+
() => new Worker(
|
|
4168
|
+
new URL("@waveform-playlist/spectrogram/worker/spectrogram.worker", import_meta.url),
|
|
4169
|
+
{ type: "module" }
|
|
4170
|
+
),
|
|
4171
|
+
workerPoolSize
|
|
4143
4172
|
);
|
|
4144
|
-
workerApi = createSpectrogramWorker(rawWorker);
|
|
4145
4173
|
spectrogramWorkerRef.current = workerApi;
|
|
4146
4174
|
setSpectrogramWorkerReady(true);
|
|
4147
|
-
} catch {
|
|
4148
|
-
console.
|
|
4175
|
+
} catch (err) {
|
|
4176
|
+
console.error(
|
|
4177
|
+
`[waveform-playlist] Spectrogram Web Worker required but unavailable: ${err instanceof Error ? err.message : String(err)}`
|
|
4178
|
+
);
|
|
4179
|
+
return;
|
|
4149
4180
|
}
|
|
4150
4181
|
}
|
|
4151
4182
|
const clipsNeedingFFT = [];
|
|
@@ -4162,11 +4193,19 @@ var SpectrogramProvider = ({
|
|
|
4162
4193
|
for (const clip of track.clips) {
|
|
4163
4194
|
if (!clip.audioBuffer) continue;
|
|
4164
4195
|
const monoFlag = mono || clip.audioBuffer.numberOfChannels === 1;
|
|
4165
|
-
if (!trackFFTChanged && !hasRegisteredCanvases &&
|
|
4196
|
+
if (!trackFFTChanged && !hasRegisteredCanvases && renderedClipIdsRef.current.has(clip.id)) {
|
|
4197
|
+
const channelDataArrays2 = [];
|
|
4198
|
+
for (let ch = 0; ch < clip.audioBuffer.numberOfChannels; ch++) {
|
|
4199
|
+
channelDataArrays2.push(clip.audioBuffer.getChannelData(ch));
|
|
4200
|
+
}
|
|
4166
4201
|
clipsNeedingDisplayOnly.push({
|
|
4167
4202
|
clipId: clip.id,
|
|
4168
4203
|
trackIndex: i,
|
|
4204
|
+
channelDataArrays: channelDataArrays2,
|
|
4169
4205
|
config: cfg,
|
|
4206
|
+
sampleRate: clip.audioBuffer.sampleRate,
|
|
4207
|
+
offsetSamples: clip.offsetSamples,
|
|
4208
|
+
durationSamples: clip.durationSamples,
|
|
4170
4209
|
clipStartSample: clip.startSample,
|
|
4171
4210
|
monoFlag,
|
|
4172
4211
|
colorMap: cm,
|
|
@@ -4193,68 +4232,38 @@ var SpectrogramProvider = ({
|
|
|
4193
4232
|
}
|
|
4194
4233
|
});
|
|
4195
4234
|
if (clipsNeedingFFT.length === 0 && clipsNeedingDisplayOnly.length === 0) return;
|
|
4196
|
-
if (!workerApi) {
|
|
4197
|
-
try {
|
|
4198
|
-
setSpectrogramDataMap((prevMap) => {
|
|
4199
|
-
const newMap = new Map(prevMap);
|
|
4200
|
-
for (const item of clipsNeedingFFT) {
|
|
4201
|
-
const clip = tracks.flatMap((t) => t.clips).find((c) => c.id === item.clipId);
|
|
4202
|
-
if (!clip?.audioBuffer) continue;
|
|
4203
|
-
const channelSpectrograms = [];
|
|
4204
|
-
if (item.monoFlag) {
|
|
4205
|
-
channelSpectrograms.push(
|
|
4206
|
-
computeSpectrogramMono(
|
|
4207
|
-
clip.audioBuffer,
|
|
4208
|
-
item.config,
|
|
4209
|
-
item.offsetSamples,
|
|
4210
|
-
item.durationSamples
|
|
4211
|
-
)
|
|
4212
|
-
);
|
|
4213
|
-
} else {
|
|
4214
|
-
for (let ch = 0; ch < clip.audioBuffer.numberOfChannels; ch++) {
|
|
4215
|
-
channelSpectrograms.push(
|
|
4216
|
-
computeSpectrogram(
|
|
4217
|
-
clip.audioBuffer,
|
|
4218
|
-
item.config,
|
|
4219
|
-
item.offsetSamples,
|
|
4220
|
-
item.durationSamples,
|
|
4221
|
-
ch
|
|
4222
|
-
)
|
|
4223
|
-
);
|
|
4224
|
-
}
|
|
4225
|
-
}
|
|
4226
|
-
newMap.set(item.clipId, channelSpectrograms);
|
|
4227
|
-
}
|
|
4228
|
-
return newMap;
|
|
4229
|
-
});
|
|
4230
|
-
} catch (err) {
|
|
4231
|
-
console.error("[waveform-playlist] Synchronous spectrogram computation failed:", err);
|
|
4232
|
-
}
|
|
4233
|
-
return;
|
|
4234
|
-
}
|
|
4235
4235
|
const getVisibleChunkRange = (channelInfo, clipPixelOffset = 0) => {
|
|
4236
4236
|
const container = scrollContainerRef.current;
|
|
4237
4237
|
if (!container) {
|
|
4238
|
-
return {
|
|
4238
|
+
return {
|
|
4239
|
+
viewportIndices: channelInfo.canvasWidths.map((_, i) => i),
|
|
4240
|
+
bufferIndices: [],
|
|
4241
|
+
remainingIndices: []
|
|
4242
|
+
};
|
|
4239
4243
|
}
|
|
4240
4244
|
const scrollLeft = container.scrollLeft;
|
|
4241
4245
|
const viewportWidth = container.clientWidth;
|
|
4242
|
-
const
|
|
4243
|
-
const
|
|
4246
|
+
const buffer = viewportWidth * 1.5;
|
|
4247
|
+
const bufferStart = Math.max(0, scrollLeft - buffer);
|
|
4248
|
+
const bufferEnd = scrollLeft + viewportWidth + buffer;
|
|
4249
|
+
const viewportIndices = [];
|
|
4250
|
+
const bufferIndices = [];
|
|
4244
4251
|
const remainingIndices = [];
|
|
4245
4252
|
for (let i = 0; i < channelInfo.canvasWidths.length; i++) {
|
|
4246
4253
|
const chunkNumber = extractChunkNumber(channelInfo.canvasIds[i]);
|
|
4247
|
-
const chunkLeft = chunkNumber * import_core.MAX_CANVAS_WIDTH +
|
|
4254
|
+
const chunkLeft = chunkNumber * import_core.MAX_CANVAS_WIDTH + clipPixelOffset;
|
|
4248
4255
|
const chunkRight = chunkLeft + channelInfo.canvasWidths[i];
|
|
4249
4256
|
if (chunkRight > scrollLeft && chunkLeft < scrollLeft + viewportWidth) {
|
|
4250
|
-
|
|
4257
|
+
viewportIndices.push(i);
|
|
4258
|
+
} else if (chunkRight > bufferStart && chunkLeft < bufferEnd) {
|
|
4259
|
+
bufferIndices.push(i);
|
|
4251
4260
|
} else {
|
|
4252
4261
|
remainingIndices.push(i);
|
|
4253
4262
|
}
|
|
4254
4263
|
}
|
|
4255
|
-
return {
|
|
4264
|
+
return { viewportIndices, bufferIndices, remainingIndices };
|
|
4256
4265
|
};
|
|
4257
|
-
const renderChunkSubset = async (api, cacheKey, channelInfo, indices, item, channelIndex) => {
|
|
4266
|
+
const renderChunkSubset = async (api, cacheKey, channelInfo, indices, item, channelIndex, gen) => {
|
|
4258
4267
|
if (indices.length === 0) return;
|
|
4259
4268
|
const canvasIds = indices.map((i) => channelInfo.canvasIds[i]);
|
|
4260
4269
|
const canvasWidths = indices.map((i) => channelInfo.canvasWidths[i]);
|
|
@@ -4264,41 +4273,117 @@ var SpectrogramProvider = ({
|
|
|
4264
4273
|
globalPixelOffsets.push(chunkNumber * import_core.MAX_CANVAS_WIDTH);
|
|
4265
4274
|
}
|
|
4266
4275
|
const colorLUT = getColorMap(item.colorMap);
|
|
4267
|
-
await api.renderChunks(
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4276
|
+
await api.renderChunks(
|
|
4277
|
+
{
|
|
4278
|
+
cacheKey,
|
|
4279
|
+
canvasIds,
|
|
4280
|
+
canvasWidths,
|
|
4281
|
+
globalPixelOffsets,
|
|
4282
|
+
canvasHeight: waveHeight,
|
|
4283
|
+
devicePixelRatio: typeof window !== "undefined" ? window.devicePixelRatio : 1,
|
|
4284
|
+
samplesPerPixel,
|
|
4285
|
+
colorLUT,
|
|
4286
|
+
frequencyScale: item.config.frequencyScale ?? "mel",
|
|
4287
|
+
minFrequency: item.config.minFrequency ?? 0,
|
|
4288
|
+
maxFrequency: item.config.maxFrequency ?? 0,
|
|
4289
|
+
gainDb: item.config.gainDb ?? 20,
|
|
4290
|
+
rangeDb: item.config.rangeDb ?? 80,
|
|
4291
|
+
channelIndex
|
|
4292
|
+
},
|
|
4293
|
+
gen
|
|
4294
|
+
);
|
|
4295
|
+
};
|
|
4296
|
+
const computeFFTForChunks = async (api, channelInfo, indices, item, gen) => {
|
|
4297
|
+
const chunkNumbers = indices.map((i) => extractChunkNumber(channelInfo.canvasIds[i]));
|
|
4298
|
+
const minChunk = Math.min(...chunkNumbers);
|
|
4299
|
+
const maxChunk = Math.max(...chunkNumbers);
|
|
4300
|
+
const maxChunkIdx = indices[chunkNumbers.indexOf(maxChunk)];
|
|
4301
|
+
const lastChunkWidth = channelInfo.canvasWidths[maxChunkIdx];
|
|
4302
|
+
const startPx = minChunk * import_core.MAX_CANVAS_WIDTH;
|
|
4303
|
+
const endPx = maxChunk * import_core.MAX_CANVAS_WIDTH + lastChunkWidth;
|
|
4304
|
+
const windowSize = item.config.fftSize ?? 2048;
|
|
4305
|
+
const rangeStartSample = item.offsetSamples + Math.floor(startPx * samplesPerPixel);
|
|
4306
|
+
const rangeEndSample = Math.min(
|
|
4307
|
+
item.offsetSamples + item.durationSamples,
|
|
4308
|
+
item.offsetSamples + Math.ceil(endPx * samplesPerPixel)
|
|
4309
|
+
);
|
|
4310
|
+
const paddedStart = Math.max(item.offsetSamples, rangeStartSample - windowSize);
|
|
4311
|
+
const paddedEnd = Math.min(
|
|
4312
|
+
item.offsetSamples + item.durationSamples,
|
|
4313
|
+
rangeEndSample + windowSize
|
|
4314
|
+
);
|
|
4315
|
+
const { cacheKey } = await api.computeFFT(
|
|
4316
|
+
{
|
|
4317
|
+
clipId: item.clipId,
|
|
4318
|
+
channelDataArrays: item.channelDataArrays,
|
|
4319
|
+
config: item.config,
|
|
4320
|
+
sampleRate: item.sampleRate,
|
|
4321
|
+
offsetSamples: item.offsetSamples,
|
|
4322
|
+
durationSamples: item.durationSamples,
|
|
4323
|
+
mono: item.monoFlag,
|
|
4324
|
+
sampleRange: { start: paddedStart, end: paddedEnd }
|
|
4325
|
+
},
|
|
4326
|
+
gen
|
|
4327
|
+
);
|
|
4328
|
+
return cacheKey;
|
|
4329
|
+
};
|
|
4330
|
+
const groupContiguousIndices = (channelInfo, indices) => {
|
|
4331
|
+
if (indices.length === 0) return [];
|
|
4332
|
+
const groups = [];
|
|
4333
|
+
let currentGroup = [indices[0]];
|
|
4334
|
+
let prevChunk = extractChunkNumber(channelInfo.canvasIds[indices[0]]);
|
|
4335
|
+
for (let i = 1; i < indices.length; i++) {
|
|
4336
|
+
const chunk = extractChunkNumber(channelInfo.canvasIds[indices[i]]);
|
|
4337
|
+
if (chunk === prevChunk + 1) {
|
|
4338
|
+
currentGroup.push(indices[i]);
|
|
4339
|
+
} else {
|
|
4340
|
+
groups.push(currentGroup);
|
|
4341
|
+
currentGroup = [indices[i]];
|
|
4342
|
+
}
|
|
4343
|
+
prevChunk = chunk;
|
|
4344
|
+
}
|
|
4345
|
+
groups.push(currentGroup);
|
|
4346
|
+
return groups;
|
|
4283
4347
|
};
|
|
4284
4348
|
const computeAsync = async () => {
|
|
4285
4349
|
const abortToken = { aborted: false };
|
|
4286
4350
|
backgroundRenderAbortRef.current = abortToken;
|
|
4287
|
-
const renderBackgroundBatches = async (channelRanges,
|
|
4288
|
-
const
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
}
|
|
4351
|
+
const renderBackgroundBatches = async (channelRanges, item) => {
|
|
4352
|
+
const allGroups = [];
|
|
4353
|
+
if (channelRanges.length > 0) {
|
|
4354
|
+
const { channelInfo, remainingIndices } = channelRanges[0];
|
|
4355
|
+
const groups = groupContiguousIndices(channelInfo, remainingIndices);
|
|
4356
|
+
for (const group of groups) {
|
|
4357
|
+
allGroups.push({
|
|
4358
|
+
group,
|
|
4359
|
+
channelRangeEntries: channelRanges.map(({ ch, channelInfo: ci }) => ({
|
|
4360
|
+
ch,
|
|
4361
|
+
channelInfo: ci
|
|
4362
|
+
}))
|
|
4299
4363
|
});
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
for (const { group, channelRangeEntries } of allGroups) {
|
|
4367
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
|
|
4368
|
+
await new Promise((resolve) => {
|
|
4369
|
+
if (typeof requestIdleCallback === "function") {
|
|
4370
|
+
requestIdleCallback(() => resolve());
|
|
4371
|
+
} else {
|
|
4372
|
+
setTimeout(resolve, 0);
|
|
4373
|
+
}
|
|
4374
|
+
});
|
|
4375
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
|
|
4376
|
+
const { channelInfo: firstChannelInfo } = channelRangeEntries[0];
|
|
4377
|
+
const cacheKey = await computeFFTForChunks(
|
|
4378
|
+
workerApi,
|
|
4379
|
+
firstChannelInfo,
|
|
4380
|
+
group,
|
|
4381
|
+
item,
|
|
4382
|
+
generation
|
|
4383
|
+
);
|
|
4384
|
+
for (const { ch, channelInfo: ci } of channelRangeEntries) {
|
|
4300
4385
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
|
|
4301
|
-
await renderChunkSubset(workerApi, cacheKey,
|
|
4386
|
+
await renderChunkSubset(workerApi, cacheKey, ci, group, item, ch, generation);
|
|
4302
4387
|
}
|
|
4303
4388
|
}
|
|
4304
4389
|
return false;
|
|
@@ -4310,117 +4395,76 @@ var SpectrogramProvider = ({
|
|
|
4310
4395
|
if (clipCanvasInfo && clipCanvasInfo.size > 0) {
|
|
4311
4396
|
const numChannels = item.monoFlag ? 1 : item.channelDataArrays.length;
|
|
4312
4397
|
const clipPixelOffset = Math.floor(item.clipStartSample / samplesPerPixel);
|
|
4313
|
-
const
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
const
|
|
4318
|
-
|
|
4319
|
-
const controlWidth = controls.show ? controls.width : 0;
|
|
4320
|
-
const vpStartPx = Math.max(0, scrollLeft - controlWidth);
|
|
4321
|
-
const vpEndPx = vpStartPx + viewportWidth;
|
|
4322
|
-
const clipStartPx = clipPixelOffset;
|
|
4323
|
-
const clipEndPx = clipStartPx + Math.ceil(item.durationSamples / samplesPerPixel);
|
|
4324
|
-
const overlapStartPx = Math.max(vpStartPx, clipStartPx);
|
|
4325
|
-
const overlapEndPx = Math.min(vpEndPx, clipEndPx);
|
|
4326
|
-
if (overlapEndPx > overlapStartPx) {
|
|
4327
|
-
const localStartPx = overlapStartPx - clipStartPx;
|
|
4328
|
-
const localEndPx = overlapEndPx - clipStartPx;
|
|
4329
|
-
const visStartSample = item.offsetSamples + Math.floor(localStartPx * samplesPerPixel);
|
|
4330
|
-
const visEndSample = Math.min(
|
|
4331
|
-
item.offsetSamples + item.durationSamples,
|
|
4332
|
-
item.offsetSamples + Math.ceil(localEndPx * samplesPerPixel)
|
|
4333
|
-
);
|
|
4334
|
-
const paddedStart = Math.max(item.offsetSamples, visStartSample - windowSize);
|
|
4335
|
-
const paddedEnd = Math.min(
|
|
4336
|
-
item.offsetSamples + item.durationSamples,
|
|
4337
|
-
visEndSample + windowSize
|
|
4338
|
-
);
|
|
4339
|
-
if (paddedEnd - paddedStart < item.durationSamples * 0.8) {
|
|
4340
|
-
visibleRange = { start: paddedStart, end: paddedEnd };
|
|
4341
|
-
}
|
|
4342
|
-
}
|
|
4398
|
+
const channelRanges = [];
|
|
4399
|
+
for (let ch = 0; ch < numChannels; ch++) {
|
|
4400
|
+
const channelInfo = clipCanvasInfo.get(ch);
|
|
4401
|
+
if (!channelInfo) continue;
|
|
4402
|
+
const range = getVisibleChunkRange(channelInfo, clipPixelOffset);
|
|
4403
|
+
channelRanges.push({ ch, channelInfo, ...range });
|
|
4343
4404
|
}
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
durationSamples: item.durationSamples,
|
|
4353
|
-
mono: item.monoFlag,
|
|
4354
|
-
sampleRange: visibleRange
|
|
4355
|
-
});
|
|
4405
|
+
if (channelRanges.length > 0 && channelRanges[0].viewportIndices.length > 0) {
|
|
4406
|
+
const cacheKey = await computeFFTForChunks(
|
|
4407
|
+
workerApi,
|
|
4408
|
+
channelRanges[0].channelInfo,
|
|
4409
|
+
channelRanges[0].viewportIndices,
|
|
4410
|
+
item,
|
|
4411
|
+
generation
|
|
4412
|
+
);
|
|
4356
4413
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4357
|
-
for (
|
|
4358
|
-
const channelInfo = clipCanvasInfo.get(ch);
|
|
4359
|
-
if (!channelInfo) continue;
|
|
4360
|
-
const { visibleIndices } = getVisibleChunkRange(channelInfo, clipPixelOffset);
|
|
4414
|
+
for (const { ch, channelInfo, viewportIndices } of channelRanges) {
|
|
4361
4415
|
await renderChunkSubset(
|
|
4362
4416
|
workerApi,
|
|
4363
|
-
|
|
4417
|
+
cacheKey,
|
|
4364
4418
|
channelInfo,
|
|
4365
|
-
|
|
4419
|
+
viewportIndices,
|
|
4366
4420
|
item,
|
|
4367
|
-
ch
|
|
4421
|
+
ch,
|
|
4422
|
+
generation
|
|
4368
4423
|
);
|
|
4369
4424
|
}
|
|
4370
|
-
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4371
4425
|
}
|
|
4372
|
-
const { cacheKey } = await workerApi.computeFFT({
|
|
4373
|
-
clipId: item.clipId,
|
|
4374
|
-
channelDataArrays: item.channelDataArrays,
|
|
4375
|
-
config: item.config,
|
|
4376
|
-
sampleRate: item.sampleRate,
|
|
4377
|
-
offsetSamples: item.offsetSamples,
|
|
4378
|
-
durationSamples: item.durationSamples,
|
|
4379
|
-
mono: item.monoFlag
|
|
4380
|
-
});
|
|
4381
4426
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
if (!channelInfo) continue;
|
|
4387
|
-
const range = getVisibleChunkRange(channelInfo, clipPixelOffset);
|
|
4388
|
-
channelRanges.push({ ch, channelInfo, ...range });
|
|
4389
|
-
await renderChunkSubset(
|
|
4390
|
-
workerApi,
|
|
4391
|
-
cacheKey,
|
|
4392
|
-
channelInfo,
|
|
4393
|
-
range.visibleIndices,
|
|
4394
|
-
item,
|
|
4395
|
-
ch
|
|
4427
|
+
if (channelRanges.length > 0 && channelRanges[0].bufferIndices.length > 0) {
|
|
4428
|
+
const bufferGroups = groupContiguousIndices(
|
|
4429
|
+
channelRanges[0].channelInfo,
|
|
4430
|
+
channelRanges[0].bufferIndices
|
|
4396
4431
|
);
|
|
4432
|
+
for (const group of bufferGroups) {
|
|
4433
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4434
|
+
const cacheKey = await computeFFTForChunks(
|
|
4435
|
+
workerApi,
|
|
4436
|
+
channelRanges[0].channelInfo,
|
|
4437
|
+
group,
|
|
4438
|
+
item,
|
|
4439
|
+
generation
|
|
4440
|
+
);
|
|
4441
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4442
|
+
for (const { ch, channelInfo } of channelRanges) {
|
|
4443
|
+
await renderChunkSubset(
|
|
4444
|
+
workerApi,
|
|
4445
|
+
cacheKey,
|
|
4446
|
+
channelInfo,
|
|
4447
|
+
group,
|
|
4448
|
+
item,
|
|
4449
|
+
ch,
|
|
4450
|
+
generation
|
|
4451
|
+
);
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4397
4454
|
}
|
|
4455
|
+
renderedClipIdsRef.current.add(item.clipId);
|
|
4398
4456
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4399
|
-
if (await renderBackgroundBatches(channelRanges,
|
|
4400
|
-
} else {
|
|
4401
|
-
const spectrograms = await workerApi.compute({
|
|
4402
|
-
channelDataArrays: item.channelDataArrays,
|
|
4403
|
-
config: item.config,
|
|
4404
|
-
sampleRate: item.sampleRate,
|
|
4405
|
-
offsetSamples: item.offsetSamples,
|
|
4406
|
-
durationSamples: item.durationSamples,
|
|
4407
|
-
mono: item.monoFlag
|
|
4408
|
-
});
|
|
4409
|
-
if (spectrogramGenerationRef.current !== generation) return;
|
|
4410
|
-
setSpectrogramDataMap((prevMap) => {
|
|
4411
|
-
const newMap = new Map(prevMap);
|
|
4412
|
-
newMap.set(item.clipId, spectrograms);
|
|
4413
|
-
return newMap;
|
|
4414
|
-
});
|
|
4457
|
+
if (await renderBackgroundBatches(channelRanges, item)) return;
|
|
4415
4458
|
}
|
|
4416
4459
|
} catch (err) {
|
|
4417
|
-
|
|
4460
|
+
if (err instanceof SpectrogramAbortError) return;
|
|
4461
|
+
console.warn(
|
|
4462
|
+
`[waveform-playlist] Spectrogram worker error for clip ${item.clipId}: ${err instanceof Error ? err.message : String(err)}`
|
|
4463
|
+
);
|
|
4418
4464
|
}
|
|
4419
4465
|
}
|
|
4420
4466
|
for (const item of clipsNeedingDisplayOnly) {
|
|
4421
4467
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4422
|
-
const cacheKey = clipCacheKeysRef.current.get(item.clipId);
|
|
4423
|
-
if (!cacheKey) continue;
|
|
4424
4468
|
const clipCanvasInfo = spectrogramCanvasRegistryRef.current.get(item.clipId);
|
|
4425
4469
|
if (!clipCanvasInfo || clipCanvasInfo.size === 0) continue;
|
|
4426
4470
|
try {
|
|
@@ -4429,22 +4473,73 @@ var SpectrogramProvider = ({
|
|
|
4429
4473
|
for (let ch = 0; ch < item.numChannels; ch++) {
|
|
4430
4474
|
const channelInfo = clipCanvasInfo.get(ch);
|
|
4431
4475
|
if (!channelInfo) continue;
|
|
4432
|
-
const
|
|
4433
|
-
|
|
4434
|
-
|
|
4476
|
+
const range = getVisibleChunkRange(channelInfo, clipPixelOffset);
|
|
4477
|
+
channelRanges.push({ ch, channelInfo, ...range });
|
|
4478
|
+
}
|
|
4479
|
+
if (channelRanges.length > 0 && channelRanges[0].viewportIndices.length > 0) {
|
|
4480
|
+
const cacheKey = await computeFFTForChunks(
|
|
4481
|
+
workerApi,
|
|
4482
|
+
channelRanges[0].channelInfo,
|
|
4483
|
+
channelRanges[0].viewportIndices,
|
|
4484
|
+
item,
|
|
4485
|
+
generation
|
|
4486
|
+
);
|
|
4487
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4488
|
+
for (const { ch, channelInfo, viewportIndices } of channelRanges) {
|
|
4489
|
+
await renderChunkSubset(
|
|
4490
|
+
workerApi,
|
|
4491
|
+
cacheKey,
|
|
4492
|
+
channelInfo,
|
|
4493
|
+
viewportIndices,
|
|
4494
|
+
item,
|
|
4495
|
+
ch,
|
|
4496
|
+
generation
|
|
4497
|
+
);
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4501
|
+
if (channelRanges.length > 0 && channelRanges[0].bufferIndices.length > 0) {
|
|
4502
|
+
const bufferGroups = groupContiguousIndices(
|
|
4503
|
+
channelRanges[0].channelInfo,
|
|
4504
|
+
channelRanges[0].bufferIndices
|
|
4435
4505
|
);
|
|
4436
|
-
|
|
4437
|
-
|
|
4506
|
+
for (const group of bufferGroups) {
|
|
4507
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4508
|
+
const cacheKey = await computeFFTForChunks(
|
|
4509
|
+
workerApi,
|
|
4510
|
+
channelRanges[0].channelInfo,
|
|
4511
|
+
group,
|
|
4512
|
+
item,
|
|
4513
|
+
generation
|
|
4514
|
+
);
|
|
4515
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4516
|
+
for (const { ch, channelInfo } of channelRanges) {
|
|
4517
|
+
await renderChunkSubset(
|
|
4518
|
+
workerApi,
|
|
4519
|
+
cacheKey,
|
|
4520
|
+
channelInfo,
|
|
4521
|
+
group,
|
|
4522
|
+
item,
|
|
4523
|
+
ch,
|
|
4524
|
+
generation
|
|
4525
|
+
);
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4438
4528
|
}
|
|
4439
4529
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4440
|
-
if (await renderBackgroundBatches(channelRanges,
|
|
4530
|
+
if (await renderBackgroundBatches(channelRanges, item)) return;
|
|
4441
4531
|
} catch (err) {
|
|
4442
|
-
|
|
4532
|
+
if (err instanceof SpectrogramAbortError) return;
|
|
4533
|
+
console.warn(
|
|
4534
|
+
`[waveform-playlist] Spectrogram display re-render error for clip ${item.clipId}: ${err instanceof Error ? err.message : String(err)}`
|
|
4535
|
+
);
|
|
4443
4536
|
}
|
|
4444
4537
|
}
|
|
4445
4538
|
};
|
|
4446
4539
|
computeAsync().catch((err) => {
|
|
4447
|
-
console.error(
|
|
4540
|
+
console.error(
|
|
4541
|
+
`[waveform-playlist] Spectrogram computation failed: ${err instanceof Error ? err.message : String(err)}`
|
|
4542
|
+
);
|
|
4448
4543
|
});
|
|
4449
4544
|
}, [
|
|
4450
4545
|
tracks,
|
|
@@ -4455,7 +4550,6 @@ var SpectrogramProvider = ({
|
|
|
4455
4550
|
waveHeight,
|
|
4456
4551
|
samplesPerPixel,
|
|
4457
4552
|
spectrogramCanvasVersion,
|
|
4458
|
-
controls,
|
|
4459
4553
|
scrollContainerRef
|
|
4460
4554
|
]);
|
|
4461
4555
|
const setTrackRenderMode = (0, import_react2.useCallback)((trackId, mode) => {
|
|
@@ -4515,7 +4609,6 @@ var SpectrogramProvider = ({
|
|
|
4515
4609
|
);
|
|
4516
4610
|
const value = (0, import_react2.useMemo)(
|
|
4517
4611
|
() => ({
|
|
4518
|
-
spectrogramDataMap,
|
|
4519
4612
|
trackSpectrogramOverrides,
|
|
4520
4613
|
spectrogramWorkerApi: spectrogramWorkerReady ? spectrogramWorkerRef.current : null,
|
|
4521
4614
|
spectrogramConfig,
|
|
@@ -4530,7 +4623,6 @@ var SpectrogramProvider = ({
|
|
|
4530
4623
|
getFrequencyScale
|
|
4531
4624
|
}),
|
|
4532
4625
|
[
|
|
4533
|
-
spectrogramDataMap,
|
|
4534
4626
|
trackSpectrogramOverrides,
|
|
4535
4627
|
spectrogramWorkerReady,
|
|
4536
4628
|
spectrogramConfig,
|
|
@@ -4546,12 +4638,14 @@ var SpectrogramProvider = ({
|
|
|
4546
4638
|
};
|
|
4547
4639
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4548
4640
|
0 && (module.exports = {
|
|
4641
|
+
SpectrogramAbortError,
|
|
4549
4642
|
SpectrogramMenuItems,
|
|
4550
4643
|
SpectrogramProvider,
|
|
4551
4644
|
SpectrogramSettingsModal,
|
|
4552
4645
|
computeSpectrogram,
|
|
4553
4646
|
computeSpectrogramMono,
|
|
4554
4647
|
createSpectrogramWorker,
|
|
4648
|
+
createSpectrogramWorkerPool,
|
|
4555
4649
|
getColorMap,
|
|
4556
4650
|
getFrequencyScale
|
|
4557
4651
|
});
|