@jswork/howler-audio-manager 1.0.1 → 1.0.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/README.md +64 -0
- package/dist/index.cjs.js +137 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.esm.js +146 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +166 -3
- package/dist/index.umd.js.map +1 -1
- package/package.json +46 -31
- package/src/index.ts +2 -0
- package/src/manager.ts +4 -0
- package/src/react.tsx +98 -0
package/README.md
CHANGED
|
@@ -63,6 +63,70 @@ audioManager.playUrl('https://cdn.example.com/voice/lesson-01.mp3', {
|
|
|
63
63
|
audioManager.destroy();
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
## React Hooks
|
|
67
|
+
|
|
68
|
+
在 React 项目中通过 Provider + hooks 接入(`react` 为可选 peer 依赖,非 React 项目使用说明见文末)。
|
|
69
|
+
|
|
70
|
+
```shell
|
|
71
|
+
pnpm add @jswork/howler-audio-manager howler react
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { AudioManagerProvider, useAudio, useAudioEvent, useGlobalVolume, usePreload } from '@jswork/howler-audio-manager';
|
|
76
|
+
|
|
77
|
+
const manager = new AudioManager<SoundEffect>();
|
|
78
|
+
manager.loadEffects(SOUND_CONFIG);
|
|
79
|
+
|
|
80
|
+
export function App() {
|
|
81
|
+
return (
|
|
82
|
+
<AudioManagerProvider manager={manager}>
|
|
83
|
+
<PlayButton />
|
|
84
|
+
</AudioManagerProvider>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function PlayButton() {
|
|
89
|
+
// 1. 获取 manager 实例
|
|
90
|
+
const audio = useAudio<SoundEffect>();
|
|
91
|
+
|
|
92
|
+
// 2. 全局音量(双向绑定)
|
|
93
|
+
const [volume, setVolume] = useGlobalVolume();
|
|
94
|
+
|
|
95
|
+
// 3. 订阅事件总线(自动随组件卸载退订,无闭包陷阱)
|
|
96
|
+
useAudioEvent('end', (payload) => {
|
|
97
|
+
console.log('playback ended', payload);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// 4. 预加载 URL 音频
|
|
101
|
+
const preload = usePreload(['https://cdn.example.com/voice/lesson-01.mp3']);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div>
|
|
105
|
+
<button onClick={() => audio.playEffect(SoundEffect.Click)}>click</button>
|
|
106
|
+
<input
|
|
107
|
+
type="range"
|
|
108
|
+
min={0}
|
|
109
|
+
max={1}
|
|
110
|
+
step={0.01}
|
|
111
|
+
value={volume}
|
|
112
|
+
onChange={(e) => setVolume(Number(e.target.value))}
|
|
113
|
+
/>
|
|
114
|
+
<p>preload: {preload.status}</p>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| API | 签名 | 说明 |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| `AudioManagerProvider` | `({ manager, children }) => JSX` | Context Provider,向下传递 `AudioManager` 实例;实例生命周期由调用方管理(不随 Provider 卸载销毁)。 |
|
|
123
|
+
| `useAudio` | `<K>() => AudioManager<K>` | 取 Provider 传入的实例;不在 Provider 内调用会抛出明确异常。 |
|
|
124
|
+
| `useAudioEvent` | `<K>(event: AudioEventType, handler: (payload) => void) => void` | 订阅全局事件;handler 永远是最新的(无 stale closure),卸载自动调用 `destroy()`。 |
|
|
125
|
+
| `useGlobalVolume` | `() => [number, (v: number) => void]` | 读取/设置全局音量(初始值来自 manager 的 `defaultVolume`)。 |
|
|
126
|
+
| `usePreload` | `(urls: string[]) => { status: 'idle' \| 'loading' \| 'loaded' \| 'error'; error: unknown }` | 批量预加载 URL 音频,状态随加载结果更新;`urls` 内容变化会重新触发。 |
|
|
127
|
+
|
|
128
|
+
> **非 React 项目注意**:hooks 从主入口导出,`react` 为 optional peerDependency。若你的打包器因找不到 `react` 报错,安装 `react`(任意 `>=16.8` 版本,仅作为打包占位)即可,不会影响现有 `AudioManager` 用法。
|
|
129
|
+
|
|
66
130
|
## API Reference
|
|
67
131
|
|
|
68
132
|
### Constructor
|
package/dist/index.cjs.js
CHANGED
|
@@ -4,6 +4,9 @@ function _array_like_to_array(arr, len) {
|
|
|
4
4
|
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
5
5
|
return arr2;
|
|
6
6
|
}
|
|
7
|
+
function _array_with_holes(arr) {
|
|
8
|
+
if (Array.isArray(arr)) return arr;
|
|
9
|
+
}
|
|
7
10
|
function _array_without_holes(arr) {
|
|
8
11
|
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
9
12
|
}
|
|
@@ -29,9 +32,39 @@ function _iterable_to_array(iter) {
|
|
|
29
32
|
return Array.from(iter);
|
|
30
33
|
}
|
|
31
34
|
}
|
|
35
|
+
function _iterable_to_array_limit(arr, i) {
|
|
36
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
37
|
+
if (_i == null) return;
|
|
38
|
+
var _arr = [];
|
|
39
|
+
var _n = true;
|
|
40
|
+
var _d = false;
|
|
41
|
+
var _s, _e;
|
|
42
|
+
try {
|
|
43
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
44
|
+
_arr.push(_s.value);
|
|
45
|
+
if (i && _arr.length === i) break;
|
|
46
|
+
}
|
|
47
|
+
} catch (err) {
|
|
48
|
+
_d = true;
|
|
49
|
+
_e = err;
|
|
50
|
+
} finally{
|
|
51
|
+
try {
|
|
52
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
53
|
+
} finally{
|
|
54
|
+
if (_d) throw _e;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return _arr;
|
|
58
|
+
}
|
|
59
|
+
function _non_iterable_rest() {
|
|
60
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
61
|
+
}
|
|
32
62
|
function _non_iterable_spread() {
|
|
33
63
|
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
34
64
|
}
|
|
65
|
+
function _sliced_to_array(arr, i) {
|
|
66
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
67
|
+
}
|
|
35
68
|
function _to_consumable_array(arr) {
|
|
36
69
|
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
37
70
|
}
|
|
@@ -85,8 +118,8 @@ function _optionalChain(ops) {
|
|
|
85
118
|
* name: @jswork/howler-audio-manager
|
|
86
119
|
* description: Howler.js audio manager, enum-driven sounds for H5.
|
|
87
120
|
* homepage: https://js.work
|
|
88
|
-
* version: 1.0.
|
|
89
|
-
* date: 2026-10-20
|
|
121
|
+
* version: 1.0.2
|
|
122
|
+
* date: 2026-10-20 20:03:06
|
|
90
123
|
* license: MIT
|
|
91
124
|
*/
|
|
92
125
|
// src/manager.ts
|
|
@@ -568,6 +601,12 @@ var AudioManager = /*#__PURE__*/ function() {
|
|
|
568
601
|
}
|
|
569
602
|
}
|
|
570
603
|
},
|
|
604
|
+
{
|
|
605
|
+
key: "getGlobalVolume",
|
|
606
|
+
value: function getGlobalVolume() {
|
|
607
|
+
return this.globalVolume;
|
|
608
|
+
}
|
|
609
|
+
},
|
|
571
610
|
{
|
|
572
611
|
key: "setGlobalVolume",
|
|
573
612
|
value: function setGlobalVolume(volume) {
|
|
@@ -607,6 +646,102 @@ var AudioManager = /*#__PURE__*/ function() {
|
|
|
607
646
|
]);
|
|
608
647
|
return AudioManager;
|
|
609
648
|
}();
|
|
649
|
+
// src/react.tsx
|
|
650
|
+
var _react = require('react');
|
|
651
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
652
|
+
var AudioManagerContext = _react.createContext.call(void 0, null);
|
|
653
|
+
function AudioManagerProvider(param) {
|
|
654
|
+
var manager = param.manager, children = param.children;
|
|
655
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AudioManagerContext.Provider, {
|
|
656
|
+
value: manager,
|
|
657
|
+
children: children
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
function useAudio() {
|
|
661
|
+
var manager = _react.useContext.call(void 0, AudioManagerContext);
|
|
662
|
+
if (!manager) {
|
|
663
|
+
throw new Error("useAudio must be used within an <AudioManagerProvider>.");
|
|
664
|
+
}
|
|
665
|
+
return manager;
|
|
666
|
+
}
|
|
667
|
+
function useAudioEvent(event, handler) {
|
|
668
|
+
var manager = useAudio();
|
|
669
|
+
var handlerRef = _react.useRef.call(void 0, handler);
|
|
670
|
+
handlerRef.current = handler;
|
|
671
|
+
_react.useEffect.call(void 0, function() {
|
|
672
|
+
var subscription = manager.on(event, function(payload) {
|
|
673
|
+
return handlerRef.current(payload);
|
|
674
|
+
});
|
|
675
|
+
return function() {
|
|
676
|
+
return subscription.destroy();
|
|
677
|
+
};
|
|
678
|
+
}, [
|
|
679
|
+
manager,
|
|
680
|
+
event
|
|
681
|
+
]);
|
|
682
|
+
}
|
|
683
|
+
function useGlobalVolume() {
|
|
684
|
+
var manager = useAudio();
|
|
685
|
+
var _react_useState_call = _sliced_to_array(_react.useState.call(void 0, function() {
|
|
686
|
+
return manager.getGlobalVolume();
|
|
687
|
+
}), 2), volume = _react_useState_call[0], setVolumeState = _react_useState_call[1];
|
|
688
|
+
var setVolume = _react.useCallback.call(void 0, function(next) {
|
|
689
|
+
manager.setGlobalVolume(next);
|
|
690
|
+
setVolumeState(next);
|
|
691
|
+
}, [
|
|
692
|
+
manager
|
|
693
|
+
]);
|
|
694
|
+
return [
|
|
695
|
+
volume,
|
|
696
|
+
setVolume
|
|
697
|
+
];
|
|
698
|
+
}
|
|
699
|
+
function usePreload(urls) {
|
|
700
|
+
var manager = useAudio();
|
|
701
|
+
var urlsKey = JSON.stringify(urls);
|
|
702
|
+
var _react_useState_call = _sliced_to_array(_react.useState.call(void 0, {
|
|
703
|
+
status: "idle",
|
|
704
|
+
error: null
|
|
705
|
+
}), 2), state = _react_useState_call[0], setState = _react_useState_call[1];
|
|
706
|
+
_react.useEffect.call(void 0, function() {
|
|
707
|
+
var list = JSON.parse(urlsKey);
|
|
708
|
+
if (list.length === 0) {
|
|
709
|
+
setState({
|
|
710
|
+
status: "idle",
|
|
711
|
+
error: null
|
|
712
|
+
});
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
var cancelled = false;
|
|
716
|
+
setState({
|
|
717
|
+
status: "loading",
|
|
718
|
+
error: null
|
|
719
|
+
});
|
|
720
|
+
manager.preloadUrls(list).then(function() {
|
|
721
|
+
if (!cancelled) setState({
|
|
722
|
+
status: "loaded",
|
|
723
|
+
error: null
|
|
724
|
+
});
|
|
725
|
+
}, function(error) {
|
|
726
|
+
if (!cancelled) setState({
|
|
727
|
+
status: "error",
|
|
728
|
+
error: error
|
|
729
|
+
});
|
|
730
|
+
});
|
|
731
|
+
return function() {
|
|
732
|
+
cancelled = true;
|
|
733
|
+
};
|
|
734
|
+
}, [
|
|
735
|
+
manager,
|
|
736
|
+
urlsKey
|
|
737
|
+
]);
|
|
738
|
+
return state;
|
|
739
|
+
}
|
|
610
740
|
exports.AudioManager = AudioManager;
|
|
741
|
+
exports.AudioManagerProvider = AudioManagerProvider;
|
|
611
742
|
exports.default = AudioManager;
|
|
743
|
+
exports.useAudio = useAudio;
|
|
744
|
+
exports.useAudioEvent = useAudioEvent;
|
|
745
|
+
exports.useGlobalVolume = useGlobalVolume;
|
|
746
|
+
exports.usePreload = usePreload;
|
|
612
747
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/afei/github/howler-audio-manager/dist/index.cjs.js","../src/manager.ts"],"names":["Object","defineProperty","exports","value","_nullishCoalesce","lhs","rhsFn","_optionalChain","ops","lastAccessLHS","undefined","i","length","op","fn","args","call","_howler","require","AudioManager","options","inject","effectCache","Map","urlAudioCache","preloadPromises","activeUrlHowl","globalVolume","listeners","HowlConstructor","_","HowlClass","Howl","HowlerInstance","_2","HowlerObj","Howler","_3","defaultVolume","setGlobalVolume","hasEffect","key","has","loadEffect","url","get","unload","howl","src","preload","on","emit","type","_id","error","set","loadEffects","map","keys","forEach","typedKey","isNaN","Number","playEffect","console","warn","String","playing","stop","_4","volume","off","once","soundId","_5","onStart","_6","_7","_8","onEnd","_9","_10","_11","onStop","_12","_13","onError","_14","play","stopEffect","removeEffect","delete","preloadUrl","existing","state","Promise","resolve","pending","createUrlHowl","promise","reject","settle","then","preloadUrls","urls","all","playUrl","isSingleton","_15","singleton","_16","loop","_17","_18","_19","_20","_21","_22","_23","_24","_25","_26","_27","stopUrl","event","handler","Set","add","destroy","payload","err","mute","muted","stopAll","clear","default"],"mappings":"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","sourcesContent":["\"use strict\";Object.defineProperty(exports, \"__esModule\", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/*!\n * name: @jswork/howler-audio-manager\n * description: Howler.js audio manager, enum-driven sounds for H5.\n * homepage: https://js.work\n * version: 1.0.1\n * date: 2026-10-20 18:54:06\n * license: MIT\n */\n\n\n// src/manager.ts\nvar _howler = require('howler');\nvar AudioManager = class {\n constructor(options, inject) {\n this.effectCache = /* @__PURE__ */ new Map();\n this.urlAudioCache = /* @__PURE__ */ new Map();\n this.preloadPromises = /* @__PURE__ */ new Map();\n this.activeUrlHowl = null;\n this.globalVolume = 1;\n this.listeners = /* @__PURE__ */ new Map();\n this.HowlConstructor = _nullishCoalesce(_optionalChain([inject, 'optionalAccess', _ => _.HowlClass]), () => ( _howler.Howl));\n this.HowlerInstance = _nullishCoalesce(_optionalChain([inject, 'optionalAccess', _2 => _2.HowlerObj]), () => ( _howler.Howler));\n if (typeof _optionalChain([options, 'optionalAccess', _3 => _3.defaultVolume]) === \"number\") {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n hasEffect(key) {\n return this.effectCache.has(key);\n }\n loadEffect(key, url) {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key).unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"effect\", key, url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.emit(\"error\", { type: \"effect\", key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n loadEffects(map) {\n Object.keys(map).forEach((key) => {\n const typedKey = isNaN(Number(key)) ? key : Number(key);\n this.loadEffect(typedKey, map[key]);\n });\n }\n playEffect(key, options) {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return void 0;\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (typeof _optionalChain([options, 'optionalAccess', _4 => _4.volume]) === \"number\") {\n howl.volume(options.volume);\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n _optionalChain([options, 'optionalAccess', _5 => _5.onStart, 'optionalCall', _6 => _6(soundId)]);\n this.emit(\"play\", { type: \"effect\", key, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof _optionalChain([options, 'optionalAccess', _7 => _7.volume]) === \"number\") {\n howl.volume(1);\n }\n _optionalChain([options, 'optionalAccess', _8 => _8.onEnd, 'optionalCall', _9 => _9(soundId)]);\n this.emit(\"end\", { type: \"effect\", key, soundId });\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof _optionalChain([options, 'optionalAccess', _10 => _10.volume]) === \"number\") {\n howl.volume(1);\n }\n _optionalChain([options, 'optionalAccess', _11 => _11.onStop, 'optionalCall', _12 => _12(soundId)]);\n this.emit(\"stop\", { type: \"effect\", key, soundId });\n });\n howl.once(\"playerror\", (soundId, error) => {\n _optionalChain([options, 'optionalAccess', _13 => _13.onError, 'optionalCall', _14 => _14(soundId, error)]);\n this.emit(\"error\", { type: \"effect\", key, soundId, error });\n });\n return howl.play();\n }\n stopEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n removeEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n preloadUrl(url) {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === \"loaded\") {\n return Promise.resolve();\n }\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n const howl = existing && existing.state() !== \"unloaded\" ? existing : this.createUrlHowl(url);\n const promise = new Promise((resolve, reject) => {\n howl.once(\"load\", () => {\n resolve();\n });\n howl.once(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n return promise;\n }\n preloadUrls(urls) {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n createUrlHowl(url) {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"url\", url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n this.emit(\"error\", { type: \"url\", url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n playUrl(url, options) {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n const isSingleton = _optionalChain([options, 'optionalAccess', _15 => _15.singleton]) !== false;\n if (isSingleton && this.activeUrlHowl && this.activeUrlHowl !== howl && this.activeUrlHowl.playing()) {\n this.activeUrlHowl.stop();\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (_optionalChain([options, 'optionalAccess', _16 => _16.loop]) !== void 0) {\n howl.loop(options.loop);\n }\n if (typeof _optionalChain([options, 'optionalAccess', _17 => _17.volume]) === \"number\") {\n howl.volume(options.volume);\n }\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n _optionalChain([options, 'optionalAccess', _18 => _18.onStart, 'optionalCall', _19 => _19(soundId)]);\n this.emit(\"play\", { type: \"url\", url, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof _optionalChain([options, 'optionalAccess', _20 => _20.volume]) === \"number\") {\n howl.volume(1);\n }\n _optionalChain([options, 'optionalAccess', _21 => _21.onEnd, 'optionalCall', _22 => _22(soundId)]);\n this.emit(\"end\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof _optionalChain([options, 'optionalAccess', _23 => _23.volume]) === \"number\") {\n howl.volume(1);\n }\n _optionalChain([options, 'optionalAccess', _24 => _24.onStop, 'optionalCall', _25 => _25(soundId)]);\n this.emit(\"stop\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"playerror\", (soundId, error) => {\n _optionalChain([options, 'optionalAccess', _26 => _26.onError, 'optionalCall', _27 => _27(soundId, error)]);\n this.emit(\"error\", { type: \"url\", url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n return howl.play();\n }\n stopUrl(url) {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n on(event, handler) {\n const set = _nullishCoalesce(this.listeners.get(event), () => ( /* @__PURE__ */ new Set()));\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n emit(event, payload) {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n setGlobalVolume(volume) {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n mute(muted) {\n this.HowlerInstance.mute(muted);\n }\n stopAll() {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n destroy() {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n};\n\n\n\nexports.AudioManager = AudioManager; exports.default = AudioManager;\n","import { Howl, Howler } from 'howler';\nimport type {\n AudioManagerOptions,\n PlayOptions,\n PlayUrlOptions,\n AudioEventType,\n AudioEventPayload,\n EventSubscription,\n SoundResourceMap\n} from './types';\n\nexport class AudioManager<K extends string | number = string> {\n private effectCache = new Map<K, Howl>();\n private urlAudioCache = new Map<string, Howl>();\n private preloadPromises = new Map<string, Promise<void>>();\n private activeUrlHowl: Howl | null = null;\n private globalVolume = 1.0;\n private listeners = new Map<AudioEventType, Set<(payload: AudioEventPayload<K>) => void>>();\n\n private HowlConstructor: typeof Howl;\n private HowlerInstance: typeof Howler;\n\n constructor(\n options?: AudioManagerOptions,\n inject?: { HowlClass?: typeof Howl; HowlerObj?: typeof Howler }\n ) {\n this.HowlConstructor = inject?.HowlClass ?? Howl;\n this.HowlerInstance = inject?.HowlerObj ?? Howler;\n if (typeof options?.defaultVolume === 'number') {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n\n hasEffect(key: K): boolean {\n return this.effectCache.has(key);\n }\n\n loadEffect(key: K, url: string): void {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key)!.unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'effect', key, url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.emit('error', { type: 'effect', key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n\n loadEffects(map: SoundResourceMap<K>): void {\n Object.keys(map).forEach((key) => {\n const typedKey = (isNaN(Number(key)) ? key : Number(key)) as K;\n this.loadEffect(typedKey, map[key as any]);\n });\n }\n\n playEffect(key: K, options?: PlayOptions): number | undefined {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return undefined;\n }\n\n if (howl.playing()) {\n howl.stop();\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'effect', key, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'effect', key, soundId });\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'effect', key, soundId });\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'effect', key, soundId, error });\n });\n\n return howl.play();\n }\n\n stopEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n\n removeEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n\n preloadUrl(url: string): Promise<void> {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === 'loaded') {\n return Promise.resolve();\n }\n\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n\n // A howl in a terminal failed state (unloaded after error) must be replaced\n // so a fresh instance can retry the load.\n const howl = existing && existing.state() !== 'unloaded' ? existing : this.createUrlHowl(url);\n\n const promise = new Promise<void>((resolve, reject) => {\n howl.once('load', () => {\n resolve();\n });\n\n howl.once('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n\n return promise;\n }\n\n preloadUrls(urls: string[]): Promise<void[]> {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n\n private createUrlHowl(url: string): Howl {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'url', url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n this.emit('error', { type: 'url', url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n\n playUrl(url: string, options?: PlayUrlOptions): number {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n\n // Singleton mutex: stop currently active URL audio if singleton !== false\n const isSingleton = options?.singleton !== false;\n if (\n isSingleton &&\n this.activeUrlHowl &&\n this.activeUrlHowl !== howl &&\n this.activeUrlHowl.playing()\n ) {\n this.activeUrlHowl.stop();\n }\n\n // Overlap prevention on the same audio instance\n if (howl.playing()) {\n howl.stop();\n }\n\n if (options?.loop !== undefined) {\n howl.loop(options.loop);\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'url', url, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'url', url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n return howl.play();\n }\n\n stopUrl(url: string): void {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n\n on(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): EventSubscription {\n const set = this.listeners.get(event) ?? new Set<(payload: AudioEventPayload<K>) => void>();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n\n protected emit(event: AudioEventType, payload: AudioEventPayload<K>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n\n setGlobalVolume(volume: number): void {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n\n mute(muted: boolean): void {\n this.HowlerInstance.mute(muted);\n }\n\n stopAll(): void {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n\n destroy(): void {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/Users/afei/github/howler-audio-manager/dist/index.cjs.js","../src/manager.ts","../src/react.tsx"],"names":["Object","defineProperty","exports","value","_nullishCoalesce","lhs","rhsFn","_optionalChain","ops","lastAccessLHS","undefined","i","length","op","fn","args","call","_howler","require","AudioManager","options","inject","effectCache","Map","urlAudioCache","preloadPromises","activeUrlHowl","globalVolume","listeners","HowlConstructor","_","HowlClass","Howl","HowlerInstance","_2","HowlerObj","Howler","_3","defaultVolume","setGlobalVolume","hasEffect","key","has","loadEffect","url","get","unload","howl","src","preload","on","emit","type","_id","error","set","loadEffects","map","keys","forEach","typedKey","isNaN","Number","playEffect","console","warn","String","playing","stop","_4","volume","off","once","soundId","_5","onStart","_6","_7","_8","onEnd","_9","_10","_11","onStop","_12","_13","onError","_14","play","stopEffect","removeEffect","delete","preloadUrl","existing","state","Promise","resolve","pending","createUrlHowl","promise","reject","settle","then","preloadUrls","urls","all","playUrl","isSingleton","_15","singleton","_16","loop","_17","_18","_19","_20","_21","_22","_23","_24","_25","_26","_27","stopUrl","event","handler","Set","add","destroy","payload","err","getGlobalVolume","mute","muted","stopAll","clear","_react","_jsxruntime","AudioManagerContext","createContext","AudioManagerProvider","manager","children","jsx","Provider","useAudio","useContext","Error","useAudioEvent","handlerRef","useRef","current","useEffect","subscription","useGlobalVolume","useState","setVolumeState","setVolume","useCallback","next","usePreload","urlsKey","JSON","stringify","status","setState","list","parse","cancelled","default"],"mappings":"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","sourcesContent":["\"use strict\";Object.defineProperty(exports, \"__esModule\", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/*!\n * name: @jswork/howler-audio-manager\n * description: Howler.js audio manager, enum-driven sounds for H5.\n * homepage: https://js.work\n * version: 1.0.2\n * date: 2026-10-20 20:03:06\n * license: MIT\n */\n\n\n// src/manager.ts\nvar _howler = require('howler');\nvar AudioManager = class {\n constructor(options, inject) {\n this.effectCache = /* @__PURE__ */ new Map();\n this.urlAudioCache = /* @__PURE__ */ new Map();\n this.preloadPromises = /* @__PURE__ */ new Map();\n this.activeUrlHowl = null;\n this.globalVolume = 1;\n this.listeners = /* @__PURE__ */ new Map();\n this.HowlConstructor = _nullishCoalesce(_optionalChain([inject, 'optionalAccess', _ => _.HowlClass]), () => ( _howler.Howl));\n this.HowlerInstance = _nullishCoalesce(_optionalChain([inject, 'optionalAccess', _2 => _2.HowlerObj]), () => ( _howler.Howler));\n if (typeof _optionalChain([options, 'optionalAccess', _3 => _3.defaultVolume]) === \"number\") {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n hasEffect(key) {\n return this.effectCache.has(key);\n }\n loadEffect(key, url) {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key).unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"effect\", key, url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.emit(\"error\", { type: \"effect\", key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n loadEffects(map) {\n Object.keys(map).forEach((key) => {\n const typedKey = isNaN(Number(key)) ? key : Number(key);\n this.loadEffect(typedKey, map[key]);\n });\n }\n playEffect(key, options) {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return void 0;\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (typeof _optionalChain([options, 'optionalAccess', _4 => _4.volume]) === \"number\") {\n howl.volume(options.volume);\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n _optionalChain([options, 'optionalAccess', _5 => _5.onStart, 'optionalCall', _6 => _6(soundId)]);\n this.emit(\"play\", { type: \"effect\", key, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof _optionalChain([options, 'optionalAccess', _7 => _7.volume]) === \"number\") {\n howl.volume(1);\n }\n _optionalChain([options, 'optionalAccess', _8 => _8.onEnd, 'optionalCall', _9 => _9(soundId)]);\n this.emit(\"end\", { type: \"effect\", key, soundId });\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof _optionalChain([options, 'optionalAccess', _10 => _10.volume]) === \"number\") {\n howl.volume(1);\n }\n _optionalChain([options, 'optionalAccess', _11 => _11.onStop, 'optionalCall', _12 => _12(soundId)]);\n this.emit(\"stop\", { type: \"effect\", key, soundId });\n });\n howl.once(\"playerror\", (soundId, error) => {\n _optionalChain([options, 'optionalAccess', _13 => _13.onError, 'optionalCall', _14 => _14(soundId, error)]);\n this.emit(\"error\", { type: \"effect\", key, soundId, error });\n });\n return howl.play();\n }\n stopEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n removeEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n preloadUrl(url) {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === \"loaded\") {\n return Promise.resolve();\n }\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n const howl = existing && existing.state() !== \"unloaded\" ? existing : this.createUrlHowl(url);\n const promise = new Promise((resolve, reject) => {\n howl.once(\"load\", () => {\n resolve();\n });\n howl.once(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n return promise;\n }\n preloadUrls(urls) {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n createUrlHowl(url) {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"url\", url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n this.emit(\"error\", { type: \"url\", url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n playUrl(url, options) {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n const isSingleton = _optionalChain([options, 'optionalAccess', _15 => _15.singleton]) !== false;\n if (isSingleton && this.activeUrlHowl && this.activeUrlHowl !== howl && this.activeUrlHowl.playing()) {\n this.activeUrlHowl.stop();\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (_optionalChain([options, 'optionalAccess', _16 => _16.loop]) !== void 0) {\n howl.loop(options.loop);\n }\n if (typeof _optionalChain([options, 'optionalAccess', _17 => _17.volume]) === \"number\") {\n howl.volume(options.volume);\n }\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n _optionalChain([options, 'optionalAccess', _18 => _18.onStart, 'optionalCall', _19 => _19(soundId)]);\n this.emit(\"play\", { type: \"url\", url, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof _optionalChain([options, 'optionalAccess', _20 => _20.volume]) === \"number\") {\n howl.volume(1);\n }\n _optionalChain([options, 'optionalAccess', _21 => _21.onEnd, 'optionalCall', _22 => _22(soundId)]);\n this.emit(\"end\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof _optionalChain([options, 'optionalAccess', _23 => _23.volume]) === \"number\") {\n howl.volume(1);\n }\n _optionalChain([options, 'optionalAccess', _24 => _24.onStop, 'optionalCall', _25 => _25(soundId)]);\n this.emit(\"stop\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"playerror\", (soundId, error) => {\n _optionalChain([options, 'optionalAccess', _26 => _26.onError, 'optionalCall', _27 => _27(soundId, error)]);\n this.emit(\"error\", { type: \"url\", url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n return howl.play();\n }\n stopUrl(url) {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n on(event, handler) {\n const set = _nullishCoalesce(this.listeners.get(event), () => ( /* @__PURE__ */ new Set()));\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n emit(event, payload) {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n getGlobalVolume() {\n return this.globalVolume;\n }\n setGlobalVolume(volume) {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n mute(muted) {\n this.HowlerInstance.mute(muted);\n }\n stopAll() {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n destroy() {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n};\n\n// src/react.tsx\nvar _react = require('react');\nvar _jsxruntime = require('react/jsx-runtime');\nvar AudioManagerContext = _react.createContext.call(void 0, null);\nfunction AudioManagerProvider({\n manager,\n children\n}) {\n return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AudioManagerContext.Provider, { value: manager, children });\n}\nfunction useAudio() {\n const manager = _react.useContext.call(void 0, AudioManagerContext);\n if (!manager) {\n throw new Error(\"useAudio must be used within an <AudioManagerProvider>.\");\n }\n return manager;\n}\nfunction useAudioEvent(event, handler) {\n const manager = useAudio();\n const handlerRef = _react.useRef.call(void 0, handler);\n handlerRef.current = handler;\n _react.useEffect.call(void 0, () => {\n const subscription = manager.on(event, (payload) => handlerRef.current(payload));\n return () => subscription.destroy();\n }, [manager, event]);\n}\nfunction useGlobalVolume() {\n const manager = useAudio();\n const [volume, setVolumeState] = _react.useState.call(void 0, () => manager.getGlobalVolume());\n const setVolume = _react.useCallback.call(void 0, \n (next) => {\n manager.setGlobalVolume(next);\n setVolumeState(next);\n },\n [manager]\n );\n return [volume, setVolume];\n}\nfunction usePreload(urls) {\n const manager = useAudio();\n const urlsKey = JSON.stringify(urls);\n const [state, setState] = _react.useState.call(void 0, { status: \"idle\", error: null });\n _react.useEffect.call(void 0, () => {\n const list = JSON.parse(urlsKey);\n if (list.length === 0) {\n setState({ status: \"idle\", error: null });\n return;\n }\n let cancelled = false;\n setState({ status: \"loading\", error: null });\n manager.preloadUrls(list).then(\n () => {\n if (!cancelled) setState({ status: \"loaded\", error: null });\n },\n (error) => {\n if (!cancelled) setState({ status: \"error\", error });\n }\n );\n return () => {\n cancelled = true;\n };\n }, [manager, urlsKey]);\n return state;\n}\n\n\n\n\n\n\n\n\nexports.AudioManager = AudioManager; exports.AudioManagerProvider = AudioManagerProvider; exports.default = AudioManager; exports.useAudio = useAudio; exports.useAudioEvent = useAudioEvent; exports.useGlobalVolume = useGlobalVolume; exports.usePreload = usePreload;\n","import { Howl, Howler } from 'howler';\nimport type {\n AudioManagerOptions,\n PlayOptions,\n PlayUrlOptions,\n AudioEventType,\n AudioEventPayload,\n EventSubscription,\n SoundResourceMap\n} from './types';\n\nexport class AudioManager<K extends string | number = string> {\n private effectCache = new Map<K, Howl>();\n private urlAudioCache = new Map<string, Howl>();\n private preloadPromises = new Map<string, Promise<void>>();\n private activeUrlHowl: Howl | null = null;\n private globalVolume = 1.0;\n private listeners = new Map<AudioEventType, Set<(payload: AudioEventPayload<K>) => void>>();\n\n private HowlConstructor: typeof Howl;\n private HowlerInstance: typeof Howler;\n\n constructor(\n options?: AudioManagerOptions,\n inject?: { HowlClass?: typeof Howl; HowlerObj?: typeof Howler }\n ) {\n this.HowlConstructor = inject?.HowlClass ?? Howl;\n this.HowlerInstance = inject?.HowlerObj ?? Howler;\n if (typeof options?.defaultVolume === 'number') {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n\n hasEffect(key: K): boolean {\n return this.effectCache.has(key);\n }\n\n loadEffect(key: K, url: string): void {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key)!.unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'effect', key, url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.emit('error', { type: 'effect', key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n\n loadEffects(map: SoundResourceMap<K>): void {\n Object.keys(map).forEach((key) => {\n const typedKey = (isNaN(Number(key)) ? key : Number(key)) as K;\n this.loadEffect(typedKey, map[key as any]);\n });\n }\n\n playEffect(key: K, options?: PlayOptions): number | undefined {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return undefined;\n }\n\n if (howl.playing()) {\n howl.stop();\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'effect', key, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'effect', key, soundId });\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'effect', key, soundId });\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'effect', key, soundId, error });\n });\n\n return howl.play();\n }\n\n stopEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n\n removeEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n\n preloadUrl(url: string): Promise<void> {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === 'loaded') {\n return Promise.resolve();\n }\n\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n\n // A howl in a terminal failed state (unloaded after error) must be replaced\n // so a fresh instance can retry the load.\n const howl = existing && existing.state() !== 'unloaded' ? existing : this.createUrlHowl(url);\n\n const promise = new Promise<void>((resolve, reject) => {\n howl.once('load', () => {\n resolve();\n });\n\n howl.once('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n\n return promise;\n }\n\n preloadUrls(urls: string[]): Promise<void[]> {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n\n private createUrlHowl(url: string): Howl {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'url', url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n this.emit('error', { type: 'url', url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n\n playUrl(url: string, options?: PlayUrlOptions): number {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n\n // Singleton mutex: stop currently active URL audio if singleton !== false\n const isSingleton = options?.singleton !== false;\n if (\n isSingleton &&\n this.activeUrlHowl &&\n this.activeUrlHowl !== howl &&\n this.activeUrlHowl.playing()\n ) {\n this.activeUrlHowl.stop();\n }\n\n // Overlap prevention on the same audio instance\n if (howl.playing()) {\n howl.stop();\n }\n\n if (options?.loop !== undefined) {\n howl.loop(options.loop);\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'url', url, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'url', url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n return howl.play();\n }\n\n stopUrl(url: string): void {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n\n on(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): EventSubscription {\n const set = this.listeners.get(event) ?? new Set<(payload: AudioEventPayload<K>) => void>();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n\n protected emit(event: AudioEventType, payload: AudioEventPayload<K>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n\n getGlobalVolume(): number {\n return this.globalVolume;\n }\n\n setGlobalVolume(volume: number): void {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n\n mute(muted: boolean): void {\n this.HowlerInstance.mute(muted);\n }\n\n stopAll(): void {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n\n destroy(): void {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n}\n","import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { AudioManager } from './manager';\nimport type { AudioEventType, AudioEventPayload } from './types';\n\nconst AudioManagerContext = createContext<AudioManager<any> | null>(null);\n\nexport interface AudioManagerProviderProps<K extends string | number = string> {\n manager: AudioManager<K>;\n children?: ReactNode;\n}\n\nexport function AudioManagerProvider<K extends string | number = string>({\n manager,\n children\n}: AudioManagerProviderProps<K>) {\n return (\n <AudioManagerContext.Provider value={manager}>\n {children}\n </AudioManagerContext.Provider>\n );\n}\n\nexport function useAudio<K extends string | number = string>(): AudioManager<K> {\n const manager = useContext(AudioManagerContext);\n if (!manager) {\n throw new Error('useAudio must be used within an <AudioManagerProvider>.');\n }\n return manager as unknown as AudioManager<K>;\n}\n\nexport function useAudioEvent<K extends string | number = string>(\n event: AudioEventType,\n handler: (payload: AudioEventPayload<K>) => void\n): void {\n const manager = useAudio<K>();\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const subscription = manager.on(event, (payload) => handlerRef.current(payload));\n return () => subscription.destroy();\n }, [manager, event]);\n}\n\nexport function useGlobalVolume(): [number, (volume: number) => void] {\n const manager = useAudio();\n const [volume, setVolumeState] = useState(() => manager.getGlobalVolume());\n\n const setVolume = useCallback(\n (next: number) => {\n manager.setGlobalVolume(next);\n setVolumeState(next);\n },\n [manager]\n );\n\n return [volume, setVolume];\n}\n\nexport interface PreloadState {\n status: 'idle' | 'loading' | 'loaded' | 'error';\n error: unknown;\n}\n\nexport function usePreload(urls: string[]): PreloadState {\n const manager = useAudio();\n const urlsKey = JSON.stringify(urls);\n const [state, setState] = useState<PreloadState>({ status: 'idle', error: null });\n\n useEffect(() => {\n const list: string[] = JSON.parse(urlsKey);\n if (list.length === 0) {\n setState({ status: 'idle', error: null });\n return;\n }\n\n let cancelled = false;\n setState({ status: 'loading', error: null });\n\n manager.preloadUrls(list).then(\n () => {\n if (!cancelled) setState({ status: 'loaded', error: null });\n },\n (error) => {\n if (!cancelled) setState({ status: 'error', error });\n }\n );\n\n return () => {\n cancelled = true;\n };\n }, [manager, urlsKey]);\n\n return state;\n}\n\n\n"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Howl, Howler } from 'howler';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
2
4
|
|
|
3
5
|
interface AudioManagerOptions {
|
|
4
6
|
/** Default global volume (0.0 to 1.0), defaults to 1.0 */
|
|
@@ -61,10 +63,25 @@ declare class AudioManager<K extends string | number = string> {
|
|
|
61
63
|
stopUrl(url: string): void;
|
|
62
64
|
on(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): EventSubscription;
|
|
63
65
|
protected emit(event: AudioEventType, payload: AudioEventPayload<K>): void;
|
|
66
|
+
getGlobalVolume(): number;
|
|
64
67
|
setGlobalVolume(volume: number): void;
|
|
65
68
|
mute(muted: boolean): void;
|
|
66
69
|
stopAll(): void;
|
|
67
70
|
destroy(): void;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
interface AudioManagerProviderProps<K extends string | number = string> {
|
|
74
|
+
manager: AudioManager<K>;
|
|
75
|
+
children?: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
declare function AudioManagerProvider<K extends string | number = string>({ manager, children }: AudioManagerProviderProps<K>): react.JSX.Element;
|
|
78
|
+
declare function useAudio<K extends string | number = string>(): AudioManager<K>;
|
|
79
|
+
declare function useAudioEvent<K extends string | number = string>(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): void;
|
|
80
|
+
declare function useGlobalVolume(): [number, (volume: number) => void];
|
|
81
|
+
interface PreloadState {
|
|
82
|
+
status: 'idle' | 'loading' | 'loaded' | 'error';
|
|
83
|
+
error: unknown;
|
|
84
|
+
}
|
|
85
|
+
declare function usePreload(urls: string[]): PreloadState;
|
|
86
|
+
|
|
87
|
+
export { type AudioEventPayload, type AudioEventType, AudioManager, type AudioManagerOptions, AudioManagerProvider, type AudioManagerProviderProps, type EventSubscription, type PlayOptions, type PlayUrlOptions, type PreloadState, type SoundResourceMap, AudioManager as default, useAudio, useAudioEvent, useGlobalVolume, usePreload };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Howl, Howler } from 'howler';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
2
4
|
|
|
3
5
|
interface AudioManagerOptions {
|
|
4
6
|
/** Default global volume (0.0 to 1.0), defaults to 1.0 */
|
|
@@ -61,12 +63,27 @@ declare class AudioManager<K extends string | number = string> {
|
|
|
61
63
|
stopUrl(url: string): void;
|
|
62
64
|
on(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): EventSubscription;
|
|
63
65
|
protected emit(event: AudioEventType, payload: AudioEventPayload<K>): void;
|
|
66
|
+
getGlobalVolume(): number;
|
|
64
67
|
setGlobalVolume(volume: number): void;
|
|
65
68
|
mute(muted: boolean): void;
|
|
66
69
|
stopAll(): void;
|
|
67
70
|
destroy(): void;
|
|
68
71
|
}
|
|
69
72
|
|
|
73
|
+
interface AudioManagerProviderProps<K extends string | number = string> {
|
|
74
|
+
manager: AudioManager<K>;
|
|
75
|
+
children?: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
declare function AudioManagerProvider<K extends string | number = string>({ manager, children }: AudioManagerProviderProps<K>): react.JSX.Element;
|
|
78
|
+
declare function useAudio<K extends string | number = string>(): AudioManager<K>;
|
|
79
|
+
declare function useAudioEvent<K extends string | number = string>(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): void;
|
|
80
|
+
declare function useGlobalVolume(): [number, (volume: number) => void];
|
|
81
|
+
interface PreloadState {
|
|
82
|
+
status: 'idle' | 'loading' | 'loaded' | 'error';
|
|
83
|
+
error: unknown;
|
|
84
|
+
}
|
|
85
|
+
declare function usePreload(urls: string[]): PreloadState;
|
|
86
|
+
|
|
70
87
|
// @ts-ignore
|
|
71
88
|
export = AudioManager;
|
|
72
|
-
export { type AudioEventPayload, type AudioEventType, AudioManager, type AudioManagerOptions, type EventSubscription, type PlayOptions, type PlayUrlOptions, type SoundResourceMap };
|
|
89
|
+
export { type AudioEventPayload, type AudioEventType, AudioManager, type AudioManagerOptions, AudioManagerProvider, type AudioManagerProviderProps, type EventSubscription, type PlayOptions, type PlayUrlOptions, type PreloadState, type SoundResourceMap, useAudio, useAudioEvent, useGlobalVolume, usePreload };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
function _array_like_to_array(arr, len) {
|
|
2
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
3
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
4
|
+
return arr2;
|
|
5
|
+
}
|
|
6
|
+
function _array_with_holes(arr) {
|
|
7
|
+
if (Array.isArray(arr)) return arr;
|
|
8
|
+
}
|
|
1
9
|
function _class_call_check(instance, Constructor) {
|
|
2
10
|
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
|
|
3
11
|
}
|
|
@@ -15,12 +23,50 @@ function _create_class(Constructor, protoProps, staticProps) {
|
|
|
15
23
|
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
16
24
|
return Constructor;
|
|
17
25
|
}
|
|
26
|
+
function _iterable_to_array_limit(arr, i) {
|
|
27
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
28
|
+
if (_i == null) return;
|
|
29
|
+
var _arr = [];
|
|
30
|
+
var _n = true;
|
|
31
|
+
var _d = false;
|
|
32
|
+
var _s, _e;
|
|
33
|
+
try {
|
|
34
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
35
|
+
_arr.push(_s.value);
|
|
36
|
+
if (i && _arr.length === i) break;
|
|
37
|
+
}
|
|
38
|
+
} catch (err) {
|
|
39
|
+
_d = true;
|
|
40
|
+
_e = err;
|
|
41
|
+
} finally{
|
|
42
|
+
try {
|
|
43
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
44
|
+
} finally{
|
|
45
|
+
if (_d) throw _e;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return _arr;
|
|
49
|
+
}
|
|
50
|
+
function _non_iterable_rest() {
|
|
51
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
52
|
+
}
|
|
53
|
+
function _sliced_to_array(arr, i) {
|
|
54
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
55
|
+
}
|
|
56
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
57
|
+
if (!o) return;
|
|
58
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
59
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
60
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
61
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
62
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
63
|
+
}
|
|
18
64
|
/*!
|
|
19
65
|
* name: @jswork/howler-audio-manager
|
|
20
66
|
* description: Howler.js audio manager, enum-driven sounds for H5.
|
|
21
67
|
* homepage: https://js.work
|
|
22
|
-
* version: 1.0.
|
|
23
|
-
* date: 2026-10-20
|
|
68
|
+
* version: 1.0.2
|
|
69
|
+
* date: 2026-10-20 20:03:06
|
|
24
70
|
* license: MIT
|
|
25
71
|
*/ // src/manager.ts
|
|
26
72
|
import { Howl, Howler } from "howler";
|
|
@@ -360,6 +406,12 @@ var AudioManager = /*#__PURE__*/ function() {
|
|
|
360
406
|
}
|
|
361
407
|
}
|
|
362
408
|
},
|
|
409
|
+
{
|
|
410
|
+
key: "getGlobalVolume",
|
|
411
|
+
value: function getGlobalVolume() {
|
|
412
|
+
return this.globalVolume;
|
|
413
|
+
}
|
|
414
|
+
},
|
|
363
415
|
{
|
|
364
416
|
key: "setGlobalVolume",
|
|
365
417
|
value: function setGlobalVolume(volume) {
|
|
@@ -399,5 +451,96 @@ var AudioManager = /*#__PURE__*/ function() {
|
|
|
399
451
|
]);
|
|
400
452
|
return AudioManager;
|
|
401
453
|
}();
|
|
402
|
-
|
|
454
|
+
// src/react.tsx
|
|
455
|
+
import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
456
|
+
import { jsx } from "react/jsx-runtime";
|
|
457
|
+
var AudioManagerContext = createContext(null);
|
|
458
|
+
function AudioManagerProvider(param) {
|
|
459
|
+
var manager = param.manager, children = param.children;
|
|
460
|
+
return /* @__PURE__ */ jsx(AudioManagerContext.Provider, {
|
|
461
|
+
value: manager,
|
|
462
|
+
children: children
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
function useAudio() {
|
|
466
|
+
var manager = useContext(AudioManagerContext);
|
|
467
|
+
if (!manager) {
|
|
468
|
+
throw new Error("useAudio must be used within an <AudioManagerProvider>.");
|
|
469
|
+
}
|
|
470
|
+
return manager;
|
|
471
|
+
}
|
|
472
|
+
function useAudioEvent(event, handler) {
|
|
473
|
+
var manager = useAudio();
|
|
474
|
+
var handlerRef = useRef(handler);
|
|
475
|
+
handlerRef.current = handler;
|
|
476
|
+
useEffect(function() {
|
|
477
|
+
var subscription = manager.on(event, function(payload) {
|
|
478
|
+
return handlerRef.current(payload);
|
|
479
|
+
});
|
|
480
|
+
return function() {
|
|
481
|
+
return subscription.destroy();
|
|
482
|
+
};
|
|
483
|
+
}, [
|
|
484
|
+
manager,
|
|
485
|
+
event
|
|
486
|
+
]);
|
|
487
|
+
}
|
|
488
|
+
function useGlobalVolume() {
|
|
489
|
+
var manager = useAudio();
|
|
490
|
+
var _useState = _sliced_to_array(useState(function() {
|
|
491
|
+
return manager.getGlobalVolume();
|
|
492
|
+
}), 2), volume = _useState[0], setVolumeState = _useState[1];
|
|
493
|
+
var setVolume = useCallback(function(next) {
|
|
494
|
+
manager.setGlobalVolume(next);
|
|
495
|
+
setVolumeState(next);
|
|
496
|
+
}, [
|
|
497
|
+
manager
|
|
498
|
+
]);
|
|
499
|
+
return [
|
|
500
|
+
volume,
|
|
501
|
+
setVolume
|
|
502
|
+
];
|
|
503
|
+
}
|
|
504
|
+
function usePreload(urls) {
|
|
505
|
+
var manager = useAudio();
|
|
506
|
+
var urlsKey = JSON.stringify(urls);
|
|
507
|
+
var _useState = _sliced_to_array(useState({
|
|
508
|
+
status: "idle",
|
|
509
|
+
error: null
|
|
510
|
+
}), 2), state = _useState[0], setState = _useState[1];
|
|
511
|
+
useEffect(function() {
|
|
512
|
+
var list = JSON.parse(urlsKey);
|
|
513
|
+
if (list.length === 0) {
|
|
514
|
+
setState({
|
|
515
|
+
status: "idle",
|
|
516
|
+
error: null
|
|
517
|
+
});
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
var cancelled = false;
|
|
521
|
+
setState({
|
|
522
|
+
status: "loading",
|
|
523
|
+
error: null
|
|
524
|
+
});
|
|
525
|
+
manager.preloadUrls(list).then(function() {
|
|
526
|
+
if (!cancelled) setState({
|
|
527
|
+
status: "loaded",
|
|
528
|
+
error: null
|
|
529
|
+
});
|
|
530
|
+
}, function(error) {
|
|
531
|
+
if (!cancelled) setState({
|
|
532
|
+
status: "error",
|
|
533
|
+
error: error
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
return function() {
|
|
537
|
+
cancelled = true;
|
|
538
|
+
};
|
|
539
|
+
}, [
|
|
540
|
+
manager,
|
|
541
|
+
urlsKey
|
|
542
|
+
]);
|
|
543
|
+
return state;
|
|
544
|
+
}
|
|
545
|
+
export { AudioManager, AudioManagerProvider, AudioManager as default, useAudio, useAudioEvent, useGlobalVolume, usePreload };
|
|
403
546
|
//# sourceMappingURL=index.esm.js.map
|