miriad-viz 0.4.0 → 0.4.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.
|
@@ -2102,6 +2102,103 @@ var textStyle2 = {
|
|
|
2102
2102
|
textAlign: "center",
|
|
2103
2103
|
maxWidth: "100%"
|
|
2104
2104
|
};
|
|
2105
|
+
|
|
2106
|
+
// src/viewer/audio-position.ts
|
|
2107
|
+
function progressToAudioPosition(progress, timingFile) {
|
|
2108
|
+
if (timingFile.lines.length === 0) return null;
|
|
2109
|
+
if (progress < 0 || progress > 1) return null;
|
|
2110
|
+
for (let i = 0; i < timingFile.lines.length; i++) {
|
|
2111
|
+
const line = timingFile.lines[i];
|
|
2112
|
+
if (progress >= line.progressStart && progress <= line.progressEnd) {
|
|
2113
|
+
const progressRange = line.progressEnd - line.progressStart;
|
|
2114
|
+
if (progressRange <= 0) {
|
|
2115
|
+
return {
|
|
2116
|
+
clipId: line.id,
|
|
2117
|
+
offsetSec: 0,
|
|
2118
|
+
audioFile: line.audioFile,
|
|
2119
|
+
lineIndex: i
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
const fraction = (progress - line.progressStart) / progressRange;
|
|
2123
|
+
const offsetSec = fraction * line.durationSec;
|
|
2124
|
+
return {
|
|
2125
|
+
clipId: line.id,
|
|
2126
|
+
offsetSec,
|
|
2127
|
+
audioFile: line.audioFile,
|
|
2128
|
+
lineIndex: i
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
return null;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
// src/viewer/useAudioPlayback.ts
|
|
2136
|
+
var SEEK_TOLERANCE_SEC = 0.15;
|
|
2137
|
+
function useAudioPlayback({
|
|
2138
|
+
progress,
|
|
2139
|
+
playing,
|
|
2140
|
+
timingFile,
|
|
2141
|
+
audioBaseUrl
|
|
2142
|
+
}) {
|
|
2143
|
+
const audioCache = react.useRef(/* @__PURE__ */ new Map());
|
|
2144
|
+
const activeClipRef = react.useRef(null);
|
|
2145
|
+
const baseUrl = audioBaseUrl.endsWith("/") ? audioBaseUrl : `${audioBaseUrl}/`;
|
|
2146
|
+
const getAudio = react.useCallback(
|
|
2147
|
+
(clipId, audioFile) => {
|
|
2148
|
+
const cached = audioCache.current.get(clipId);
|
|
2149
|
+
if (cached) return cached;
|
|
2150
|
+
const audio = new Audio(`${baseUrl}${audioFile}`);
|
|
2151
|
+
audio.preload = "auto";
|
|
2152
|
+
audioCache.current.set(clipId, audio);
|
|
2153
|
+
return audio;
|
|
2154
|
+
},
|
|
2155
|
+
[baseUrl]
|
|
2156
|
+
);
|
|
2157
|
+
const stopAll = react.useCallback(() => {
|
|
2158
|
+
for (const audio of audioCache.current.values()) {
|
|
2159
|
+
audio.pause();
|
|
2160
|
+
}
|
|
2161
|
+
activeClipRef.current = null;
|
|
2162
|
+
}, []);
|
|
2163
|
+
react.useEffect(() => {
|
|
2164
|
+
return () => {
|
|
2165
|
+
for (const audio of audioCache.current.values()) {
|
|
2166
|
+
audio.pause();
|
|
2167
|
+
audio.src = "";
|
|
2168
|
+
}
|
|
2169
|
+
audioCache.current.clear();
|
|
2170
|
+
};
|
|
2171
|
+
}, []);
|
|
2172
|
+
react.useEffect(() => {
|
|
2173
|
+
if (!timingFile || !playing) {
|
|
2174
|
+
stopAll();
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
const position = progressToAudioPosition(progress, timingFile);
|
|
2178
|
+
if (!position) {
|
|
2179
|
+
stopAll();
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2182
|
+
const { clipId, offsetSec, audioFile } = position;
|
|
2183
|
+
const audio = getAudio(clipId, audioFile);
|
|
2184
|
+
if (activeClipRef.current !== clipId) {
|
|
2185
|
+
stopAll();
|
|
2186
|
+
activeClipRef.current = clipId;
|
|
2187
|
+
audio.currentTime = offsetSec;
|
|
2188
|
+
audio.play().catch(() => {
|
|
2189
|
+
});
|
|
2190
|
+
} else {
|
|
2191
|
+
const drift = Math.abs(audio.currentTime - offsetSec);
|
|
2192
|
+
if (drift > SEEK_TOLERANCE_SEC) {
|
|
2193
|
+
audio.currentTime = offsetSec;
|
|
2194
|
+
}
|
|
2195
|
+
if (audio.paused) {
|
|
2196
|
+
audio.play().catch(() => {
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
}, [progress, playing, timingFile, getAudio, stopAll]);
|
|
2201
|
+
}
|
|
2105
2202
|
var FALLBACK_DURATION = 120;
|
|
2106
2203
|
function usePlayback(durationSeconds = FALLBACK_DURATION) {
|
|
2107
2204
|
const [progress, setProgress] = react.useState(0);
|
|
@@ -2236,9 +2333,12 @@ function SceneContents({ devMode, progress }) {
|
|
|
2236
2333
|
] });
|
|
2237
2334
|
}
|
|
2238
2335
|
var VIZ_DATA_URL = "./data/viz-data.json";
|
|
2336
|
+
var TIMING_URL = "./data/timing.json";
|
|
2337
|
+
var AUDIO_BASE_URL = "./audio/";
|
|
2239
2338
|
var activeTestScene = getTestScene();
|
|
2240
2339
|
function App() {
|
|
2241
2340
|
const [vizData, setVizData] = react.useState(null);
|
|
2341
|
+
const [timingFile, setTimingFile] = react.useState(null);
|
|
2242
2342
|
const [error, setError] = react.useState(null);
|
|
2243
2343
|
const [devMode, setDevMode] = react.useState(getInitialDevMode);
|
|
2244
2344
|
const viewportOverride = useViewportOverride();
|
|
@@ -2324,6 +2424,20 @@ function App() {
|
|
|
2324
2424
|
chunkLJG3H4FA_cjs.useVizStore.getState().setDataSummary(extractDataSummary(enriched));
|
|
2325
2425
|
}).catch((err) => setError(err instanceof Error ? err.message : String(err)));
|
|
2326
2426
|
}, []);
|
|
2427
|
+
react.useEffect(() => {
|
|
2428
|
+
if (activeTestScene) return;
|
|
2429
|
+
fetch(TIMING_URL).then((res) => {
|
|
2430
|
+
if (!res.ok) return null;
|
|
2431
|
+
return res.json();
|
|
2432
|
+
}).then((data4) => setTimingFile(data4)).catch(() => {
|
|
2433
|
+
});
|
|
2434
|
+
}, []);
|
|
2435
|
+
useAudioPlayback({
|
|
2436
|
+
progress: playback.progress,
|
|
2437
|
+
playing: playback.playing,
|
|
2438
|
+
timingFile,
|
|
2439
|
+
audioBaseUrl: AUDIO_BASE_URL
|
|
2440
|
+
});
|
|
2327
2441
|
react.useMemo(() => {
|
|
2328
2442
|
chunkLJG3H4FA_cjs.useVizStore.getState().setPlaying(playback.playing);
|
|
2329
2443
|
}, [playback.playing]);
|
|
@@ -2522,103 +2636,6 @@ var errorStyle = {
|
|
|
2522
2636
|
fontFamily: "monospace"
|
|
2523
2637
|
};
|
|
2524
2638
|
|
|
2525
|
-
// src/viewer/audio-position.ts
|
|
2526
|
-
function progressToAudioPosition(progress, timingFile) {
|
|
2527
|
-
if (timingFile.lines.length === 0) return null;
|
|
2528
|
-
if (progress < 0 || progress > 1) return null;
|
|
2529
|
-
for (let i = 0; i < timingFile.lines.length; i++) {
|
|
2530
|
-
const line = timingFile.lines[i];
|
|
2531
|
-
if (progress >= line.progressStart && progress <= line.progressEnd) {
|
|
2532
|
-
const progressRange = line.progressEnd - line.progressStart;
|
|
2533
|
-
if (progressRange <= 0) {
|
|
2534
|
-
return {
|
|
2535
|
-
clipId: line.id,
|
|
2536
|
-
offsetSec: 0,
|
|
2537
|
-
audioFile: line.audioFile,
|
|
2538
|
-
lineIndex: i
|
|
2539
|
-
};
|
|
2540
|
-
}
|
|
2541
|
-
const fraction = (progress - line.progressStart) / progressRange;
|
|
2542
|
-
const offsetSec = fraction * line.durationSec;
|
|
2543
|
-
return {
|
|
2544
|
-
clipId: line.id,
|
|
2545
|
-
offsetSec,
|
|
2546
|
-
audioFile: line.audioFile,
|
|
2547
|
-
lineIndex: i
|
|
2548
|
-
};
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
return null;
|
|
2552
|
-
}
|
|
2553
|
-
|
|
2554
|
-
// src/viewer/useAudioPlayback.ts
|
|
2555
|
-
var SEEK_TOLERANCE_SEC = 0.15;
|
|
2556
|
-
function useAudioPlayback({
|
|
2557
|
-
progress,
|
|
2558
|
-
playing,
|
|
2559
|
-
timingFile,
|
|
2560
|
-
audioBaseUrl
|
|
2561
|
-
}) {
|
|
2562
|
-
const audioCache = react.useRef(/* @__PURE__ */ new Map());
|
|
2563
|
-
const activeClipRef = react.useRef(null);
|
|
2564
|
-
const baseUrl = audioBaseUrl.endsWith("/") ? audioBaseUrl : `${audioBaseUrl}/`;
|
|
2565
|
-
const getAudio = react.useCallback(
|
|
2566
|
-
(clipId, audioFile) => {
|
|
2567
|
-
const cached = audioCache.current.get(clipId);
|
|
2568
|
-
if (cached) return cached;
|
|
2569
|
-
const audio = new Audio(`${baseUrl}${audioFile}`);
|
|
2570
|
-
audio.preload = "auto";
|
|
2571
|
-
audioCache.current.set(clipId, audio);
|
|
2572
|
-
return audio;
|
|
2573
|
-
},
|
|
2574
|
-
[baseUrl]
|
|
2575
|
-
);
|
|
2576
|
-
const stopAll = react.useCallback(() => {
|
|
2577
|
-
for (const audio of audioCache.current.values()) {
|
|
2578
|
-
audio.pause();
|
|
2579
|
-
}
|
|
2580
|
-
activeClipRef.current = null;
|
|
2581
|
-
}, []);
|
|
2582
|
-
react.useEffect(() => {
|
|
2583
|
-
return () => {
|
|
2584
|
-
for (const audio of audioCache.current.values()) {
|
|
2585
|
-
audio.pause();
|
|
2586
|
-
audio.src = "";
|
|
2587
|
-
}
|
|
2588
|
-
audioCache.current.clear();
|
|
2589
|
-
};
|
|
2590
|
-
}, []);
|
|
2591
|
-
react.useEffect(() => {
|
|
2592
|
-
if (!timingFile || !playing) {
|
|
2593
|
-
stopAll();
|
|
2594
|
-
return;
|
|
2595
|
-
}
|
|
2596
|
-
const position = progressToAudioPosition(progress, timingFile);
|
|
2597
|
-
if (!position) {
|
|
2598
|
-
stopAll();
|
|
2599
|
-
return;
|
|
2600
|
-
}
|
|
2601
|
-
const { clipId, offsetSec, audioFile } = position;
|
|
2602
|
-
const audio = getAudio(clipId, audioFile);
|
|
2603
|
-
if (activeClipRef.current !== clipId) {
|
|
2604
|
-
stopAll();
|
|
2605
|
-
activeClipRef.current = clipId;
|
|
2606
|
-
audio.currentTime = offsetSec;
|
|
2607
|
-
audio.play().catch(() => {
|
|
2608
|
-
});
|
|
2609
|
-
} else {
|
|
2610
|
-
const drift = Math.abs(audio.currentTime - offsetSec);
|
|
2611
|
-
if (drift > SEEK_TOLERANCE_SEC) {
|
|
2612
|
-
audio.currentTime = offsetSec;
|
|
2613
|
-
}
|
|
2614
|
-
if (audio.paused) {
|
|
2615
|
-
audio.play().catch(() => {
|
|
2616
|
-
});
|
|
2617
|
-
}
|
|
2618
|
-
}
|
|
2619
|
-
}, [progress, playing, timingFile, getAudio, stopAll]);
|
|
2620
|
-
}
|
|
2621
|
-
|
|
2622
2639
|
Object.defineProperty(exports, "SIM_HOURS_PER_SECOND", {
|
|
2623
2640
|
enumerable: true,
|
|
2624
2641
|
get: function () { return chunkIHAAQH6X_cjs.SIM_HOURS_PER_SECOND; }
|