@waveform-playlist/spectrogram 9.5.0 → 9.5.1
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 +356 -266
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +354 -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,28 @@ 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(`[waveform-playlist] Spectrogram Web Worker required but unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
4177
|
+
return;
|
|
4149
4178
|
}
|
|
4150
4179
|
}
|
|
4151
4180
|
const clipsNeedingFFT = [];
|
|
@@ -4162,11 +4191,19 @@ var SpectrogramProvider = ({
|
|
|
4162
4191
|
for (const clip of track.clips) {
|
|
4163
4192
|
if (!clip.audioBuffer) continue;
|
|
4164
4193
|
const monoFlag = mono || clip.audioBuffer.numberOfChannels === 1;
|
|
4165
|
-
if (!trackFFTChanged && !hasRegisteredCanvases &&
|
|
4194
|
+
if (!trackFFTChanged && !hasRegisteredCanvases && renderedClipIdsRef.current.has(clip.id)) {
|
|
4195
|
+
const channelDataArrays2 = [];
|
|
4196
|
+
for (let ch = 0; ch < clip.audioBuffer.numberOfChannels; ch++) {
|
|
4197
|
+
channelDataArrays2.push(clip.audioBuffer.getChannelData(ch));
|
|
4198
|
+
}
|
|
4166
4199
|
clipsNeedingDisplayOnly.push({
|
|
4167
4200
|
clipId: clip.id,
|
|
4168
4201
|
trackIndex: i,
|
|
4202
|
+
channelDataArrays: channelDataArrays2,
|
|
4169
4203
|
config: cfg,
|
|
4204
|
+
sampleRate: clip.audioBuffer.sampleRate,
|
|
4205
|
+
offsetSamples: clip.offsetSamples,
|
|
4206
|
+
durationSamples: clip.durationSamples,
|
|
4170
4207
|
clipStartSample: clip.startSample,
|
|
4171
4208
|
monoFlag,
|
|
4172
4209
|
colorMap: cm,
|
|
@@ -4193,68 +4230,38 @@ var SpectrogramProvider = ({
|
|
|
4193
4230
|
}
|
|
4194
4231
|
});
|
|
4195
4232
|
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
4233
|
const getVisibleChunkRange = (channelInfo, clipPixelOffset = 0) => {
|
|
4236
4234
|
const container = scrollContainerRef.current;
|
|
4237
4235
|
if (!container) {
|
|
4238
|
-
return {
|
|
4236
|
+
return {
|
|
4237
|
+
viewportIndices: channelInfo.canvasWidths.map((_, i) => i),
|
|
4238
|
+
bufferIndices: [],
|
|
4239
|
+
remainingIndices: []
|
|
4240
|
+
};
|
|
4239
4241
|
}
|
|
4240
4242
|
const scrollLeft = container.scrollLeft;
|
|
4241
4243
|
const viewportWidth = container.clientWidth;
|
|
4242
|
-
const
|
|
4243
|
-
const
|
|
4244
|
+
const buffer = viewportWidth * 1.5;
|
|
4245
|
+
const bufferStart = Math.max(0, scrollLeft - buffer);
|
|
4246
|
+
const bufferEnd = scrollLeft + viewportWidth + buffer;
|
|
4247
|
+
const viewportIndices = [];
|
|
4248
|
+
const bufferIndices = [];
|
|
4244
4249
|
const remainingIndices = [];
|
|
4245
4250
|
for (let i = 0; i < channelInfo.canvasWidths.length; i++) {
|
|
4246
4251
|
const chunkNumber = extractChunkNumber(channelInfo.canvasIds[i]);
|
|
4247
|
-
const chunkLeft = chunkNumber * import_core.MAX_CANVAS_WIDTH +
|
|
4252
|
+
const chunkLeft = chunkNumber * import_core.MAX_CANVAS_WIDTH + clipPixelOffset;
|
|
4248
4253
|
const chunkRight = chunkLeft + channelInfo.canvasWidths[i];
|
|
4249
4254
|
if (chunkRight > scrollLeft && chunkLeft < scrollLeft + viewportWidth) {
|
|
4250
|
-
|
|
4255
|
+
viewportIndices.push(i);
|
|
4256
|
+
} else if (chunkRight > bufferStart && chunkLeft < bufferEnd) {
|
|
4257
|
+
bufferIndices.push(i);
|
|
4251
4258
|
} else {
|
|
4252
4259
|
remainingIndices.push(i);
|
|
4253
4260
|
}
|
|
4254
4261
|
}
|
|
4255
|
-
return {
|
|
4262
|
+
return { viewportIndices, bufferIndices, remainingIndices };
|
|
4256
4263
|
};
|
|
4257
|
-
const renderChunkSubset = async (api, cacheKey, channelInfo, indices, item, channelIndex) => {
|
|
4264
|
+
const renderChunkSubset = async (api, cacheKey, channelInfo, indices, item, channelIndex, gen) => {
|
|
4258
4265
|
if (indices.length === 0) return;
|
|
4259
4266
|
const canvasIds = indices.map((i) => channelInfo.canvasIds[i]);
|
|
4260
4267
|
const canvasWidths = indices.map((i) => channelInfo.canvasWidths[i]);
|
|
@@ -4264,41 +4271,117 @@ var SpectrogramProvider = ({
|
|
|
4264
4271
|
globalPixelOffsets.push(chunkNumber * import_core.MAX_CANVAS_WIDTH);
|
|
4265
4272
|
}
|
|
4266
4273
|
const colorLUT = getColorMap(item.colorMap);
|
|
4267
|
-
await api.renderChunks(
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4274
|
+
await api.renderChunks(
|
|
4275
|
+
{
|
|
4276
|
+
cacheKey,
|
|
4277
|
+
canvasIds,
|
|
4278
|
+
canvasWidths,
|
|
4279
|
+
globalPixelOffsets,
|
|
4280
|
+
canvasHeight: waveHeight,
|
|
4281
|
+
devicePixelRatio: typeof window !== "undefined" ? window.devicePixelRatio : 1,
|
|
4282
|
+
samplesPerPixel,
|
|
4283
|
+
colorLUT,
|
|
4284
|
+
frequencyScale: item.config.frequencyScale ?? "mel",
|
|
4285
|
+
minFrequency: item.config.minFrequency ?? 0,
|
|
4286
|
+
maxFrequency: item.config.maxFrequency ?? 0,
|
|
4287
|
+
gainDb: item.config.gainDb ?? 20,
|
|
4288
|
+
rangeDb: item.config.rangeDb ?? 80,
|
|
4289
|
+
channelIndex
|
|
4290
|
+
},
|
|
4291
|
+
gen
|
|
4292
|
+
);
|
|
4293
|
+
};
|
|
4294
|
+
const computeFFTForChunks = async (api, channelInfo, indices, item, gen) => {
|
|
4295
|
+
const chunkNumbers = indices.map((i) => extractChunkNumber(channelInfo.canvasIds[i]));
|
|
4296
|
+
const minChunk = Math.min(...chunkNumbers);
|
|
4297
|
+
const maxChunk = Math.max(...chunkNumbers);
|
|
4298
|
+
const maxChunkIdx = indices[chunkNumbers.indexOf(maxChunk)];
|
|
4299
|
+
const lastChunkWidth = channelInfo.canvasWidths[maxChunkIdx];
|
|
4300
|
+
const startPx = minChunk * import_core.MAX_CANVAS_WIDTH;
|
|
4301
|
+
const endPx = maxChunk * import_core.MAX_CANVAS_WIDTH + lastChunkWidth;
|
|
4302
|
+
const windowSize = item.config.fftSize ?? 2048;
|
|
4303
|
+
const rangeStartSample = item.offsetSamples + Math.floor(startPx * samplesPerPixel);
|
|
4304
|
+
const rangeEndSample = Math.min(
|
|
4305
|
+
item.offsetSamples + item.durationSamples,
|
|
4306
|
+
item.offsetSamples + Math.ceil(endPx * samplesPerPixel)
|
|
4307
|
+
);
|
|
4308
|
+
const paddedStart = Math.max(item.offsetSamples, rangeStartSample - windowSize);
|
|
4309
|
+
const paddedEnd = Math.min(
|
|
4310
|
+
item.offsetSamples + item.durationSamples,
|
|
4311
|
+
rangeEndSample + windowSize
|
|
4312
|
+
);
|
|
4313
|
+
const { cacheKey } = await api.computeFFT(
|
|
4314
|
+
{
|
|
4315
|
+
clipId: item.clipId,
|
|
4316
|
+
channelDataArrays: item.channelDataArrays,
|
|
4317
|
+
config: item.config,
|
|
4318
|
+
sampleRate: item.sampleRate,
|
|
4319
|
+
offsetSamples: item.offsetSamples,
|
|
4320
|
+
durationSamples: item.durationSamples,
|
|
4321
|
+
mono: item.monoFlag,
|
|
4322
|
+
sampleRange: { start: paddedStart, end: paddedEnd }
|
|
4323
|
+
},
|
|
4324
|
+
gen
|
|
4325
|
+
);
|
|
4326
|
+
return cacheKey;
|
|
4327
|
+
};
|
|
4328
|
+
const groupContiguousIndices = (channelInfo, indices) => {
|
|
4329
|
+
if (indices.length === 0) return [];
|
|
4330
|
+
const groups = [];
|
|
4331
|
+
let currentGroup = [indices[0]];
|
|
4332
|
+
let prevChunk = extractChunkNumber(channelInfo.canvasIds[indices[0]]);
|
|
4333
|
+
for (let i = 1; i < indices.length; i++) {
|
|
4334
|
+
const chunk = extractChunkNumber(channelInfo.canvasIds[indices[i]]);
|
|
4335
|
+
if (chunk === prevChunk + 1) {
|
|
4336
|
+
currentGroup.push(indices[i]);
|
|
4337
|
+
} else {
|
|
4338
|
+
groups.push(currentGroup);
|
|
4339
|
+
currentGroup = [indices[i]];
|
|
4340
|
+
}
|
|
4341
|
+
prevChunk = chunk;
|
|
4342
|
+
}
|
|
4343
|
+
groups.push(currentGroup);
|
|
4344
|
+
return groups;
|
|
4283
4345
|
};
|
|
4284
4346
|
const computeAsync = async () => {
|
|
4285
4347
|
const abortToken = { aborted: false };
|
|
4286
4348
|
backgroundRenderAbortRef.current = abortToken;
|
|
4287
|
-
const renderBackgroundBatches = async (channelRanges,
|
|
4288
|
-
const
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
}
|
|
4349
|
+
const renderBackgroundBatches = async (channelRanges, item) => {
|
|
4350
|
+
const allGroups = [];
|
|
4351
|
+
if (channelRanges.length > 0) {
|
|
4352
|
+
const { channelInfo, remainingIndices } = channelRanges[0];
|
|
4353
|
+
const groups = groupContiguousIndices(channelInfo, remainingIndices);
|
|
4354
|
+
for (const group of groups) {
|
|
4355
|
+
allGroups.push({
|
|
4356
|
+
group,
|
|
4357
|
+
channelRangeEntries: channelRanges.map(({ ch, channelInfo: ci }) => ({
|
|
4358
|
+
ch,
|
|
4359
|
+
channelInfo: ci
|
|
4360
|
+
}))
|
|
4299
4361
|
});
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
for (const { group, channelRangeEntries } of allGroups) {
|
|
4365
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
|
|
4366
|
+
await new Promise((resolve) => {
|
|
4367
|
+
if (typeof requestIdleCallback === "function") {
|
|
4368
|
+
requestIdleCallback(() => resolve());
|
|
4369
|
+
} else {
|
|
4370
|
+
setTimeout(resolve, 0);
|
|
4371
|
+
}
|
|
4372
|
+
});
|
|
4373
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
|
|
4374
|
+
const { channelInfo: firstChannelInfo } = channelRangeEntries[0];
|
|
4375
|
+
const cacheKey = await computeFFTForChunks(
|
|
4376
|
+
workerApi,
|
|
4377
|
+
firstChannelInfo,
|
|
4378
|
+
group,
|
|
4379
|
+
item,
|
|
4380
|
+
generation
|
|
4381
|
+
);
|
|
4382
|
+
for (const { ch, channelInfo: ci } of channelRangeEntries) {
|
|
4300
4383
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
|
|
4301
|
-
await renderChunkSubset(workerApi, cacheKey,
|
|
4384
|
+
await renderChunkSubset(workerApi, cacheKey, ci, group, item, ch, generation);
|
|
4302
4385
|
}
|
|
4303
4386
|
}
|
|
4304
4387
|
return false;
|
|
@@ -4310,117 +4393,76 @@ var SpectrogramProvider = ({
|
|
|
4310
4393
|
if (clipCanvasInfo && clipCanvasInfo.size > 0) {
|
|
4311
4394
|
const numChannels = item.monoFlag ? 1 : item.channelDataArrays.length;
|
|
4312
4395
|
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
|
-
}
|
|
4396
|
+
const channelRanges = [];
|
|
4397
|
+
for (let ch = 0; ch < numChannels; ch++) {
|
|
4398
|
+
const channelInfo = clipCanvasInfo.get(ch);
|
|
4399
|
+
if (!channelInfo) continue;
|
|
4400
|
+
const range = getVisibleChunkRange(channelInfo, clipPixelOffset);
|
|
4401
|
+
channelRanges.push({ ch, channelInfo, ...range });
|
|
4343
4402
|
}
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
durationSamples: item.durationSamples,
|
|
4353
|
-
mono: item.monoFlag,
|
|
4354
|
-
sampleRange: visibleRange
|
|
4355
|
-
});
|
|
4403
|
+
if (channelRanges.length > 0 && channelRanges[0].viewportIndices.length > 0) {
|
|
4404
|
+
const cacheKey = await computeFFTForChunks(
|
|
4405
|
+
workerApi,
|
|
4406
|
+
channelRanges[0].channelInfo,
|
|
4407
|
+
channelRanges[0].viewportIndices,
|
|
4408
|
+
item,
|
|
4409
|
+
generation
|
|
4410
|
+
);
|
|
4356
4411
|
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);
|
|
4412
|
+
for (const { ch, channelInfo, viewportIndices } of channelRanges) {
|
|
4361
4413
|
await renderChunkSubset(
|
|
4362
4414
|
workerApi,
|
|
4363
|
-
|
|
4415
|
+
cacheKey,
|
|
4364
4416
|
channelInfo,
|
|
4365
|
-
|
|
4417
|
+
viewportIndices,
|
|
4366
4418
|
item,
|
|
4367
|
-
ch
|
|
4419
|
+
ch,
|
|
4420
|
+
generation
|
|
4368
4421
|
);
|
|
4369
4422
|
}
|
|
4370
|
-
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4371
4423
|
}
|
|
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
4424
|
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
|
|
4425
|
+
if (channelRanges.length > 0 && channelRanges[0].bufferIndices.length > 0) {
|
|
4426
|
+
const bufferGroups = groupContiguousIndices(
|
|
4427
|
+
channelRanges[0].channelInfo,
|
|
4428
|
+
channelRanges[0].bufferIndices
|
|
4396
4429
|
);
|
|
4430
|
+
for (const group of bufferGroups) {
|
|
4431
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4432
|
+
const cacheKey = await computeFFTForChunks(
|
|
4433
|
+
workerApi,
|
|
4434
|
+
channelRanges[0].channelInfo,
|
|
4435
|
+
group,
|
|
4436
|
+
item,
|
|
4437
|
+
generation
|
|
4438
|
+
);
|
|
4439
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4440
|
+
for (const { ch, channelInfo } of channelRanges) {
|
|
4441
|
+
await renderChunkSubset(
|
|
4442
|
+
workerApi,
|
|
4443
|
+
cacheKey,
|
|
4444
|
+
channelInfo,
|
|
4445
|
+
group,
|
|
4446
|
+
item,
|
|
4447
|
+
ch,
|
|
4448
|
+
generation
|
|
4449
|
+
);
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4397
4452
|
}
|
|
4453
|
+
renderedClipIdsRef.current.add(item.clipId);
|
|
4398
4454
|
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
|
-
});
|
|
4455
|
+
if (await renderBackgroundBatches(channelRanges, item)) return;
|
|
4415
4456
|
}
|
|
4416
4457
|
} catch (err) {
|
|
4417
|
-
|
|
4458
|
+
if (err instanceof SpectrogramAbortError) return;
|
|
4459
|
+
console.warn(
|
|
4460
|
+
`[waveform-playlist] Spectrogram worker error for clip ${item.clipId}: ${err instanceof Error ? err.message : String(err)}`
|
|
4461
|
+
);
|
|
4418
4462
|
}
|
|
4419
4463
|
}
|
|
4420
4464
|
for (const item of clipsNeedingDisplayOnly) {
|
|
4421
4465
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4422
|
-
const cacheKey = clipCacheKeysRef.current.get(item.clipId);
|
|
4423
|
-
if (!cacheKey) continue;
|
|
4424
4466
|
const clipCanvasInfo = spectrogramCanvasRegistryRef.current.get(item.clipId);
|
|
4425
4467
|
if (!clipCanvasInfo || clipCanvasInfo.size === 0) continue;
|
|
4426
4468
|
try {
|
|
@@ -4429,22 +4471,71 @@ var SpectrogramProvider = ({
|
|
|
4429
4471
|
for (let ch = 0; ch < item.numChannels; ch++) {
|
|
4430
4472
|
const channelInfo = clipCanvasInfo.get(ch);
|
|
4431
4473
|
if (!channelInfo) continue;
|
|
4432
|
-
const
|
|
4433
|
-
|
|
4434
|
-
|
|
4474
|
+
const range = getVisibleChunkRange(channelInfo, clipPixelOffset);
|
|
4475
|
+
channelRanges.push({ ch, channelInfo, ...range });
|
|
4476
|
+
}
|
|
4477
|
+
if (channelRanges.length > 0 && channelRanges[0].viewportIndices.length > 0) {
|
|
4478
|
+
const cacheKey = await computeFFTForChunks(
|
|
4479
|
+
workerApi,
|
|
4480
|
+
channelRanges[0].channelInfo,
|
|
4481
|
+
channelRanges[0].viewportIndices,
|
|
4482
|
+
item,
|
|
4483
|
+
generation
|
|
4484
|
+
);
|
|
4485
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4486
|
+
for (const { ch, channelInfo, viewportIndices } of channelRanges) {
|
|
4487
|
+
await renderChunkSubset(
|
|
4488
|
+
workerApi,
|
|
4489
|
+
cacheKey,
|
|
4490
|
+
channelInfo,
|
|
4491
|
+
viewportIndices,
|
|
4492
|
+
item,
|
|
4493
|
+
ch,
|
|
4494
|
+
generation
|
|
4495
|
+
);
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4499
|
+
if (channelRanges.length > 0 && channelRanges[0].bufferIndices.length > 0) {
|
|
4500
|
+
const bufferGroups = groupContiguousIndices(
|
|
4501
|
+
channelRanges[0].channelInfo,
|
|
4502
|
+
channelRanges[0].bufferIndices
|
|
4435
4503
|
);
|
|
4436
|
-
|
|
4437
|
-
|
|
4504
|
+
for (const group of bufferGroups) {
|
|
4505
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4506
|
+
const cacheKey = await computeFFTForChunks(
|
|
4507
|
+
workerApi,
|
|
4508
|
+
channelRanges[0].channelInfo,
|
|
4509
|
+
group,
|
|
4510
|
+
item,
|
|
4511
|
+
generation
|
|
4512
|
+
);
|
|
4513
|
+
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4514
|
+
for (const { ch, channelInfo } of channelRanges) {
|
|
4515
|
+
await renderChunkSubset(
|
|
4516
|
+
workerApi,
|
|
4517
|
+
cacheKey,
|
|
4518
|
+
channelInfo,
|
|
4519
|
+
group,
|
|
4520
|
+
item,
|
|
4521
|
+
ch,
|
|
4522
|
+
generation
|
|
4523
|
+
);
|
|
4524
|
+
}
|
|
4525
|
+
}
|
|
4438
4526
|
}
|
|
4439
4527
|
if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
|
|
4440
|
-
if (await renderBackgroundBatches(channelRanges,
|
|
4528
|
+
if (await renderBackgroundBatches(channelRanges, item)) return;
|
|
4441
4529
|
} catch (err) {
|
|
4442
|
-
|
|
4530
|
+
if (err instanceof SpectrogramAbortError) return;
|
|
4531
|
+
console.warn(
|
|
4532
|
+
`[waveform-playlist] Spectrogram display re-render error for clip ${item.clipId}: ${err instanceof Error ? err.message : String(err)}`
|
|
4533
|
+
);
|
|
4443
4534
|
}
|
|
4444
4535
|
}
|
|
4445
4536
|
};
|
|
4446
4537
|
computeAsync().catch((err) => {
|
|
4447
|
-
console.error(
|
|
4538
|
+
console.error(`[waveform-playlist] Spectrogram computation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4448
4539
|
});
|
|
4449
4540
|
}, [
|
|
4450
4541
|
tracks,
|
|
@@ -4455,7 +4546,6 @@ var SpectrogramProvider = ({
|
|
|
4455
4546
|
waveHeight,
|
|
4456
4547
|
samplesPerPixel,
|
|
4457
4548
|
spectrogramCanvasVersion,
|
|
4458
|
-
controls,
|
|
4459
4549
|
scrollContainerRef
|
|
4460
4550
|
]);
|
|
4461
4551
|
const setTrackRenderMode = (0, import_react2.useCallback)((trackId, mode) => {
|
|
@@ -4515,7 +4605,6 @@ var SpectrogramProvider = ({
|
|
|
4515
4605
|
);
|
|
4516
4606
|
const value = (0, import_react2.useMemo)(
|
|
4517
4607
|
() => ({
|
|
4518
|
-
spectrogramDataMap,
|
|
4519
4608
|
trackSpectrogramOverrides,
|
|
4520
4609
|
spectrogramWorkerApi: spectrogramWorkerReady ? spectrogramWorkerRef.current : null,
|
|
4521
4610
|
spectrogramConfig,
|
|
@@ -4530,7 +4619,6 @@ var SpectrogramProvider = ({
|
|
|
4530
4619
|
getFrequencyScale
|
|
4531
4620
|
}),
|
|
4532
4621
|
[
|
|
4533
|
-
spectrogramDataMap,
|
|
4534
4622
|
trackSpectrogramOverrides,
|
|
4535
4623
|
spectrogramWorkerReady,
|
|
4536
4624
|
spectrogramConfig,
|
|
@@ -4546,12 +4634,14 @@ var SpectrogramProvider = ({
|
|
|
4546
4634
|
};
|
|
4547
4635
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4548
4636
|
0 && (module.exports = {
|
|
4637
|
+
SpectrogramAbortError,
|
|
4549
4638
|
SpectrogramMenuItems,
|
|
4550
4639
|
SpectrogramProvider,
|
|
4551
4640
|
SpectrogramSettingsModal,
|
|
4552
4641
|
computeSpectrogram,
|
|
4553
4642
|
computeSpectrogramMono,
|
|
4554
4643
|
createSpectrogramWorker,
|
|
4644
|
+
createSpectrogramWorkerPool,
|
|
4555
4645
|
getColorMap,
|
|
4556
4646
|
getFrequencyScale
|
|
4557
4647
|
});
|