"use strict"; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; const common_vendor = require("../../common/vendor.js"); class AudioPool { constructor(src, size, volume = 1) { __publicField(this, "list", []); __publicField(this, "cursor", 0); __publicField(this, "warmedUp", false); __publicField(this, "targetVolume", 1); this.src = src; this.size = size; this.targetVolume = volume; } create() { for (let i = 0; i < this.size; i++) { const ctx = common_vendor.index.createInnerAudioContext(); ctx.src = this.src; ctx.obeyMuteSwitch = false; ctx.autoplay = false; ctx.volume = this.targetVolume; this.list.push(ctx); } } setVolume(volume) { this.targetVolume = Math.max(0, Math.min(1, volume)); this.list.forEach((ctx) => { try { ctx.volume = this.targetVolume; } catch (_) { } }); } /** * 预热:短暂播放,让音频文件下载/解码到内存 * 真正首次 play 不会再卡冷启动 */ warmUp() { if (this.warmedUp) return; if (this.list.length === 0) this.create(); this.list.forEach((ctx) => { try { ctx.volume = 0; ctx.play(); setTimeout(() => { try { ctx.stop(); ctx.volume = this.targetVolume; } catch (_) { } }, 60); } catch (_) { } }); this.warmedUp = true; } play() { if (this.list.length === 0) { this.create(); this.warmUp(); } const ctx = this.list[this.cursor]; this.cursor = (this.cursor + 1) % this.list.length; try { this.list.forEach((c) => { try { c.stop(); } catch (_) { } }); ctx.play(); } catch (_) { try { ctx.play(); } catch (__) { } } } destroy() { this.list.forEach((ctx) => { var _a; try { (_a = ctx.destroy) == null ? void 0 : _a.call(ctx); } catch (_) { } }); this.list = []; this.warmedUp = false; } } const DEFAULT_CLICK_SRC = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/20260526105128557ef8669.mp3"; function useMetronome(options = {}) { const { initialBpm = 80, bpmMin = 40, bpmMax = 240, clickSrc = DEFAULT_CLICK_SRC, accentSrc, poolSize = 4, volume = 1, silent = false, onBeat } = options; const bpm = common_vendor.ref(initialBpm); const isPlaying = common_vendor.ref(false); const beatIndex = common_vendor.ref(0); const isAccent = common_vendor.ref(false); const accentEveryRef = common_vendor.ref(options.accentEvery ?? 4); const intervalMs = common_vendor.computed(() => 6e4 / bpm.value); let clickPool = null; let accentPool = null; let timer = null; let nextTickAt = 0; const ensureAudio = () => { if (silent) return; if (!clickPool) { clickPool = new AudioPool(clickSrc, poolSize, volume); clickPool.warmUp(); } if (accentSrc && !accentPool) { accentPool = new AudioPool(accentSrc, Math.max(2, Math.ceil(poolSize / 2)), volume); accentPool.warmUp(); } }; const playSound = (accent) => { if (accent && accentPool) { accentPool.play(); } else if (clickPool) { clickPool.play(); } }; const tick = () => { if (!isPlaying.value) return; const every = Math.max(1, accentEveryRef.value); const accent = beatIndex.value % every === 0; isAccent.value = accent; playSound(accent); onBeat == null ? void 0 : onBeat(beatIndex.value, accent); beatIndex.value++; nextTickAt += intervalMs.value; const nextDelay = Math.max(0, nextTickAt - Date.now()); timer = setTimeout(tick, nextDelay); }; const start = () => { if (isPlaying.value) return; ensureAudio(); isPlaying.value = true; beatIndex.value = 0; nextTickAt = Date.now(); tick(); }; const stop = () => { isPlaying.value = false; if (timer) { clearTimeout(timer); timer = null; } }; const setBpm = (val) => { bpm.value = Math.max(bpmMin, Math.min(bpmMax, Math.round(val))); }; const setAccentEvery = (n) => { accentEveryRef.value = Math.max(1, Math.min(16, Math.round(n))); beatIndex.value = 0; }; const updateAudioSrc = (newClickSrc, newAccentSrc) => { const wasPlaying = isPlaying.value; if (wasPlaying) stop(); clickPool == null ? void 0 : clickPool.destroy(); accentPool == null ? void 0 : accentPool.destroy(); clickPool = null; accentPool = null; clickPool = new AudioPool(newClickSrc, poolSize); if (newAccentSrc) { accentPool = new AudioPool(newAccentSrc, Math.max(2, Math.ceil(poolSize / 2))); } const previewCtx = common_vendor.index.createInnerAudioContext(); previewCtx.src = newClickSrc; previewCtx.obeyMuteSwitch = false; previewCtx.volume = 0.4; try { previewCtx.play(); setTimeout(() => { var _a; try { (_a = previewCtx.destroy) == null ? void 0 : _a.call(previewCtx); } catch (_) { } }, 500); } catch (_) { } if (wasPlaying) start(); }; const preload = () => { try { common_vendor.index.setInnerAudioOption({ obeyMuteSwitch: false, mixWithOther: true }); } catch (_) { } ensureAudio(); }; common_vendor.onUnmounted(() => { stop(); clickPool == null ? void 0 : clickPool.destroy(); accentPool == null ? void 0 : accentPool.destroy(); clickPool = null; accentPool = null; }); return { bpm, isPlaying, beatIndex, isAccent, intervalMs, accentEvery: accentEveryRef, start, stop, setBpm, setAccentEvery, updateAudioSrc, preload }; } exports.useMetronome = useMetronome; //# sourceMappingURL=../../../.sourcemap/mp-weixin/training/hooks/useMetronome.js.map