@magmacrunch/adenosine-audio 0.2.0

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.
@@ -0,0 +1,4 @@
1
+ export declare function getCtx(): AudioContext;
2
+ export declare function resumeCtx(): Promise<void>;
3
+ export declare function closeCtx(): void;
4
+ //# sourceMappingURL=audio-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-context.d.ts","sourceRoot":"","sources":["../src/audio-context.ts"],"names":[],"mappings":"AAEA,wBAAgB,MAAM,IAAI,YAAY,CAKrC;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAK/C;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAK/B"}
@@ -0,0 +1,9 @@
1
+ import type { AudioManifest } from './types.js';
2
+ import { loadMusic as _loadMusic, pauseMusic as _pauseMusic, stopMusic as _stopMusic, setMusicVolume as _setMusicVolume, setMusicMuted as _setMusicMuted, isMusicMuted as _isMusicMuted, isMusicPlaying as _isMusicPlaying, toggleMusicMute as _toggleMusicMute, onVisibilityChange } from './music.js';
3
+ import { loadSfx as _loadSfx, playSfx as _playSfx, setSfxVolume as _setSfxVolume, setSfxGlobalVolume as _setSfxGlobalVolume, setSfxMuted as _setSfxMuted, isSfxMuted as _isSfxMuted, toggleSfxMute as _toggleSfxMute } from './sfx.js';
4
+ export declare function init(manifest: AudioManifest): Promise<void>;
5
+ export declare function playMusic(fadeIn?: number): Promise<void>;
6
+ export declare function destroy(): void;
7
+ export { _loadMusic as loadMusic, _pauseMusic as pauseMusic, _stopMusic as stopMusic, _setMusicVolume as setMusicVolume, _setMusicMuted as setMusicMuted, _isMusicMuted as isMusicMuted, _isMusicPlaying as isMusicPlaying, _toggleMusicMute as toggleMusicMute, _loadSfx as loadSfx, _playSfx as playSfx, _setSfxVolume as setSfxVolume, _setSfxGlobalVolume as setSfxGlobalVolume, _setSfxMuted as setSfxMuted, _isSfxMuted as isSfxMuted, _toggleSfxMute as toggleSfxMute, onVisibilityChange as handleVisibility, };
8
+ export type { AudioManifest, MusicConfig, SfxConfig, VisibilityOptions } from './types.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,YAAY,CAAC;AACnE,OAAO,EACL,SAAS,IAAI,UAAU,EAEvB,UAAU,IAAI,WAAW,EACzB,SAAS,IAAI,UAAU,EACvB,cAAc,IAAI,eAAe,EACjC,aAAa,IAAI,cAAc,EAC/B,YAAY,IAAI,aAAa,EAC7B,cAAc,IAAI,eAAe,EACjC,eAAe,IAAI,gBAAgB,EACnC,kBAAkB,EAEnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,OAAO,IAAI,QAAQ,EACnB,OAAO,IAAI,QAAQ,EACnB,YAAY,IAAI,aAAa,EAC7B,kBAAkB,IAAI,mBAAmB,EACzC,WAAW,IAAI,YAAY,EAC3B,UAAU,IAAI,WAAW,EACzB,aAAa,IAAI,cAAc,EAEhC,MAAM,UAAU,CAAC;AAKlB,wBAAsB,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBjE;AAED,wBAAsB,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9D;AAED,wBAAgB,OAAO,IAAI,IAAI,CAI9B;AAED,OAAO,EACL,UAAU,IAAI,SAAS,EACvB,WAAW,IAAI,UAAU,EACzB,UAAU,IAAI,SAAS,EACvB,eAAe,IAAI,cAAc,EACjC,cAAc,IAAI,aAAa,EAC/B,aAAa,IAAI,YAAY,EAC7B,eAAe,IAAI,cAAc,EACjC,gBAAgB,IAAI,eAAe,EACnC,QAAQ,IAAI,OAAO,EACnB,QAAQ,IAAI,OAAO,EACnB,aAAa,IAAI,YAAY,EAC7B,mBAAmB,IAAI,kBAAkB,EACzC,YAAY,IAAI,WAAW,EAC3B,WAAW,IAAI,UAAU,EACzB,cAAc,IAAI,aAAa,EAC/B,kBAAkB,IAAI,gBAAgB,GACvC,CAAC;AAEF,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ var AdAudio = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ destroy: () => destroy,
25
+ handleVisibility: () => onVisibilityChange,
26
+ init: () => init,
27
+ isMusicMuted: () => isMusicMuted,
28
+ isMusicPlaying: () => isMusicPlaying,
29
+ isSfxMuted: () => isSfxMuted,
30
+ loadMusic: () => loadMusic,
31
+ loadSfx: () => loadSfx,
32
+ pauseMusic: () => pauseMusic,
33
+ playMusic: () => playMusic2,
34
+ playSfx: () => playSfx,
35
+ setMusicMuted: () => setMusicMuted,
36
+ setMusicVolume: () => setMusicVolume,
37
+ setSfxGlobalVolume: () => setSfxGlobalVolume,
38
+ setSfxMuted: () => setSfxMuted,
39
+ setSfxVolume: () => setSfxVolume,
40
+ stopMusic: () => stopMusic,
41
+ toggleMusicMute: () => toggleMusicMute,
42
+ toggleSfxMute: () => toggleSfxMute
43
+ });
44
+
45
+ // src/audio-context.ts
46
+ var ctx = null;
47
+ function getCtx() {
48
+ if (!ctx) {
49
+ ctx = new (window.AudioContext || window.webkitAudioContext)();
50
+ }
51
+ return ctx;
52
+ }
53
+ async function resumeCtx() {
54
+ const c = getCtx();
55
+ if (c.state === "suspended") {
56
+ await c.resume();
57
+ }
58
+ }
59
+ function closeCtx() {
60
+ if (ctx && ctx.state !== "closed") {
61
+ ctx.close();
62
+ }
63
+ ctx = null;
64
+ }
65
+
66
+ // src/music.ts
67
+ var musicBuffer = null;
68
+ var musicSource = null;
69
+ var musicGain = null;
70
+ var musicStarted = false;
71
+ var musicMuted = false;
72
+ var musicVolume = 0.3;
73
+ var visibilityHandler = null;
74
+ async function loadMusic(url, opts) {
75
+ musicVolume = opts?.volume ?? 0.3;
76
+ const res = await fetch(url);
77
+ const arrayBuf = await res.arrayBuffer();
78
+ musicBuffer = await getCtx().decodeAudioData(arrayBuf);
79
+ }
80
+ async function playMusic(fadeIn = 2) {
81
+ if (!musicBuffer || musicStarted) return;
82
+ await resumeCtx();
83
+ const ctx2 = getCtx();
84
+ musicGain = ctx2.createGain();
85
+ musicGain.connect(ctx2.destination);
86
+ musicGain.gain.setValueAtTime(0, ctx2.currentTime);
87
+ musicSource = ctx2.createBufferSource();
88
+ musicSource.buffer = musicBuffer;
89
+ musicSource.loop = true;
90
+ musicSource.connect(musicGain);
91
+ musicSource.start(0);
92
+ const target = musicMuted ? 0 : musicVolume;
93
+ musicGain.gain.linearRampToValueAtTime(target, ctx2.currentTime + fadeIn);
94
+ musicStarted = true;
95
+ }
96
+ function pauseMusic() {
97
+ if (musicSource) {
98
+ musicSource.onended = null;
99
+ musicSource.stop();
100
+ musicSource = null;
101
+ }
102
+ musicStarted = false;
103
+ }
104
+ function stopMusic() {
105
+ pauseMusic();
106
+ if (musicGain) {
107
+ musicGain.disconnect();
108
+ musicGain = null;
109
+ }
110
+ }
111
+ function setMusicVolume(volume, rampTime = 0.5) {
112
+ musicVolume = volume;
113
+ if (musicGain && !musicMuted) {
114
+ const ctx2 = getCtx();
115
+ musicGain.gain.cancelScheduledValues(ctx2.currentTime);
116
+ musicGain.gain.setValueAtTime(musicGain.gain.value, ctx2.currentTime);
117
+ musicGain.gain.linearRampToValueAtTime(volume, ctx2.currentTime + rampTime);
118
+ }
119
+ }
120
+ function setMusicMuted(muted, rampTime = 0.5) {
121
+ musicMuted = muted;
122
+ if (musicGain) {
123
+ const ctx2 = getCtx();
124
+ musicGain.gain.cancelScheduledValues(ctx2.currentTime);
125
+ if (rampTime <= 0) {
126
+ musicGain.gain.setValueAtTime(muted ? 0 : musicVolume, ctx2.currentTime);
127
+ } else {
128
+ musicGain.gain.setValueAtTime(musicGain.gain.value, ctx2.currentTime);
129
+ musicGain.gain.linearRampToValueAtTime(muted ? 0 : musicVolume, ctx2.currentTime + rampTime);
130
+ }
131
+ }
132
+ }
133
+ function isMusicMuted() {
134
+ return musicMuted;
135
+ }
136
+ function toggleMusicMute() {
137
+ setMusicMuted(!musicMuted);
138
+ return musicMuted;
139
+ }
140
+ function isMusicPlaying() {
141
+ return musicStarted;
142
+ }
143
+ function onVisibilityChange(pause) {
144
+ if (visibilityHandler) {
145
+ document.removeEventListener("visibilitychange", visibilityHandler);
146
+ }
147
+ visibilityHandler = () => {
148
+ if (document.hidden && pause && musicStarted) {
149
+ pauseMusic();
150
+ } else if (!document.hidden && pause && musicBuffer) {
151
+ playMusic(0);
152
+ }
153
+ };
154
+ document.addEventListener("visibilitychange", visibilityHandler);
155
+ }
156
+ function destroyMusic() {
157
+ stopMusic();
158
+ musicBuffer = null;
159
+ musicStarted = false;
160
+ musicMuted = false;
161
+ if (visibilityHandler) {
162
+ document.removeEventListener("visibilitychange", visibilityHandler);
163
+ visibilityHandler = null;
164
+ }
165
+ }
166
+
167
+ // src/sfx.ts
168
+ var sfxMap = /* @__PURE__ */ new Map();
169
+ var sfxMuted = false;
170
+ var sfxGlobalVolume = 1;
171
+ async function loadSfx(name, url, opts) {
172
+ const res = await fetch(url);
173
+ const arrayBuf = await res.arrayBuffer();
174
+ const buffer = await getCtx().decodeAudioData(arrayBuf);
175
+ sfxMap.set(name, {
176
+ buffer,
177
+ volume: opts?.volume ?? 0.5,
178
+ pool: [],
179
+ poolSize: opts?.pool ?? 1
180
+ });
181
+ }
182
+ async function playSfx(name, opts) {
183
+ if (sfxMuted) return;
184
+ const entry = sfxMap.get(name);
185
+ if (!entry) return;
186
+ await resumeCtx();
187
+ const ctx2 = getCtx();
188
+ const gain = ctx2.createGain();
189
+ const vol = (opts?.volume ?? entry.volume) * sfxGlobalVolume;
190
+ gain.gain.setValueAtTime(vol, ctx2.currentTime);
191
+ gain.connect(ctx2.destination);
192
+ const source = ctx2.createBufferSource();
193
+ source.buffer = entry.buffer;
194
+ source.connect(gain);
195
+ source.start(0);
196
+ source.onended = () => {
197
+ gain.disconnect();
198
+ const idx = entry.pool.indexOf(source);
199
+ if (idx !== -1) entry.pool.splice(idx, 1);
200
+ };
201
+ if (entry.pool.length < entry.poolSize) {
202
+ entry.pool.push(source);
203
+ }
204
+ }
205
+ function setSfxVolume(name, volume) {
206
+ const entry = sfxMap.get(name);
207
+ if (entry) entry.volume = volume;
208
+ }
209
+ function setSfxGlobalVolume(volume) {
210
+ sfxGlobalVolume = volume;
211
+ }
212
+ function setSfxMuted(muted) {
213
+ sfxMuted = muted;
214
+ }
215
+ function isSfxMuted() {
216
+ return sfxMuted;
217
+ }
218
+ function toggleSfxMute() {
219
+ sfxMuted = !sfxMuted;
220
+ return sfxMuted;
221
+ }
222
+ function destroySfx() {
223
+ sfxMap.clear();
224
+ sfxMuted = false;
225
+ sfxGlobalVolume = 1;
226
+ }
227
+
228
+ // src/index.ts
229
+ var musicFadeIn = 2;
230
+ async function init(manifest) {
231
+ if (manifest.music) {
232
+ await loadMusic(manifest.music.url, {
233
+ volume: manifest.music.volume,
234
+ fadeIn: manifest.music.fadeIn
235
+ });
236
+ musicFadeIn = manifest.music.fadeIn ?? 2;
237
+ }
238
+ if (manifest.sfx) {
239
+ const entries = Object.entries(manifest.sfx);
240
+ await Promise.all(
241
+ entries.map(([name, cfg]) => loadSfx(name, cfg.url, { volume: cfg.volume, pool: cfg.pool }))
242
+ );
243
+ }
244
+ if (manifest.autoVisibility !== false) {
245
+ onVisibilityChange(true);
246
+ }
247
+ }
248
+ async function playMusic2(fadeIn) {
249
+ await playMusic(fadeIn ?? musicFadeIn);
250
+ }
251
+ function destroy() {
252
+ destroyMusic();
253
+ destroySfx();
254
+ closeCtx();
255
+ }
256
+ return __toCommonJS(index_exports);
257
+ })();
258
+ //# sourceMappingURL=index.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/audio-context.ts","../src/music.ts","../src/sfx.ts"],"sourcesContent":["import type { AudioManifest, VisibilityOptions } from './types.js';\nimport {\n loadMusic as _loadMusic,\n playMusic as _playMusic,\n pauseMusic as _pauseMusic,\n stopMusic as _stopMusic,\n setMusicVolume as _setMusicVolume,\n setMusicMuted as _setMusicMuted,\n isMusicMuted as _isMusicMuted,\n isMusicPlaying as _isMusicPlaying,\n toggleMusicMute as _toggleMusicMute,\n onVisibilityChange,\n destroyMusic,\n} from './music.js';\nimport {\n loadSfx as _loadSfx,\n playSfx as _playSfx,\n setSfxVolume as _setSfxVolume,\n setSfxGlobalVolume as _setSfxGlobalVolume,\n setSfxMuted as _setSfxMuted,\n isSfxMuted as _isSfxMuted,\n toggleSfxMute as _toggleSfxMute,\n destroySfx,\n} from './sfx.js';\nimport { closeCtx } from './audio-context.js';\n\nlet musicFadeIn = 2.0;\n\nexport async function init(manifest: AudioManifest): Promise<void> {\n if (manifest.music) {\n await _loadMusic(manifest.music.url, {\n volume: manifest.music.volume,\n fadeIn: manifest.music.fadeIn,\n });\n musicFadeIn = manifest.music.fadeIn ?? 2.0;\n }\n\n if (manifest.sfx) {\n const entries = Object.entries(manifest.sfx);\n await Promise.all(\n entries.map(([name, cfg]) => _loadSfx(name, cfg.url, { volume: cfg.volume, pool: cfg.pool }))\n );\n }\n\n if (manifest.autoVisibility !== false) {\n onVisibilityChange(true);\n }\n}\n\nexport async function playMusic(fadeIn?: number): Promise<void> {\n await _playMusic(fadeIn ?? musicFadeIn);\n}\n\nexport function destroy(): void {\n destroyMusic();\n destroySfx();\n closeCtx();\n}\n\nexport {\n _loadMusic as loadMusic,\n _pauseMusic as pauseMusic,\n _stopMusic as stopMusic,\n _setMusicVolume as setMusicVolume,\n _setMusicMuted as setMusicMuted,\n _isMusicMuted as isMusicMuted,\n _isMusicPlaying as isMusicPlaying,\n _toggleMusicMute as toggleMusicMute,\n _loadSfx as loadSfx,\n _playSfx as playSfx,\n _setSfxVolume as setSfxVolume,\n _setSfxGlobalVolume as setSfxGlobalVolume,\n _setSfxMuted as setSfxMuted,\n _isSfxMuted as isSfxMuted,\n _toggleSfxMute as toggleSfxMute,\n onVisibilityChange as handleVisibility,\n};\n\nexport type { AudioManifest, MusicConfig, SfxConfig, VisibilityOptions } from './types.js';\n","let ctx: AudioContext | null = null;\n\nexport function getCtx(): AudioContext {\n if (!ctx) {\n ctx = new (window.AudioContext || (window as any).webkitAudioContext)();\n }\n return ctx;\n}\n\nexport async function resumeCtx(): Promise<void> {\n const c = getCtx();\n if (c.state === 'suspended') {\n await c.resume();\n }\n}\n\nexport function closeCtx(): void {\n if (ctx && ctx.state !== 'closed') {\n ctx.close();\n }\n ctx = null;\n}\n","import { getCtx, resumeCtx } from './audio-context.js';\n\nlet musicBuffer: AudioBuffer | null = null;\nlet musicSource: AudioBufferSourceNode | null = null;\nlet musicGain: GainNode | null = null;\nlet musicStarted = false;\nlet musicMuted = false;\nlet musicVolume = 0.3;\nlet visibilityHandler: (() => void) | null = null;\n\nexport async function loadMusic(url: string, opts?: { volume?: number; fadeIn?: number }): Promise<void> {\n musicVolume = opts?.volume ?? 0.3;\n const res = await fetch(url);\n const arrayBuf = await res.arrayBuffer();\n musicBuffer = await getCtx().decodeAudioData(arrayBuf);\n}\n\nexport async function playMusic(fadeIn = 2.0): Promise<void> {\n if (!musicBuffer || musicStarted) return;\n await resumeCtx();\n\n const ctx = getCtx();\n musicGain = ctx.createGain();\n musicGain.connect(ctx.destination);\n musicGain.gain.setValueAtTime(0, ctx.currentTime);\n\n musicSource = ctx.createBufferSource();\n musicSource.buffer = musicBuffer;\n musicSource.loop = true;\n musicSource.connect(musicGain);\n musicSource.start(0);\n\n const target = musicMuted ? 0 : musicVolume;\n musicGain.gain.linearRampToValueAtTime(target, ctx.currentTime + fadeIn);\n musicStarted = true;\n}\n\nexport function pauseMusic(): void {\n if (musicSource) {\n musicSource.onended = null;\n musicSource.stop();\n musicSource = null;\n }\n musicStarted = false;\n}\n\nexport function stopMusic(): void {\n pauseMusic();\n if (musicGain) {\n musicGain.disconnect();\n musicGain = null;\n }\n}\n\nexport function setMusicVolume(volume: number, rampTime = 0.5): void {\n musicVolume = volume;\n if (musicGain && !musicMuted) {\n const ctx = getCtx();\n musicGain.gain.cancelScheduledValues(ctx.currentTime);\n musicGain.gain.setValueAtTime(musicGain.gain.value, ctx.currentTime);\n musicGain.gain.linearRampToValueAtTime(volume, ctx.currentTime + rampTime);\n }\n}\n\nexport function setMusicMuted(muted: boolean, rampTime = 0.5): void {\n musicMuted = muted;\n if (musicGain) {\n const ctx = getCtx();\n musicGain.gain.cancelScheduledValues(ctx.currentTime);\n if (rampTime <= 0) {\n musicGain.gain.setValueAtTime(muted ? 0 : musicVolume, ctx.currentTime);\n } else {\n musicGain.gain.setValueAtTime(musicGain.gain.value, ctx.currentTime);\n musicGain.gain.linearRampToValueAtTime(muted ? 0 : musicVolume, ctx.currentTime + rampTime);\n }\n }\n}\n\nexport function isMusicMuted(): boolean {\n return musicMuted;\n}\n\nexport function toggleMusicMute(): boolean {\n setMusicMuted(!musicMuted);\n return musicMuted;\n}\n\nexport function isMusicPlaying(): boolean {\n return musicStarted;\n}\n\nexport function onVisibilityChange(pause: boolean): void {\n if (visibilityHandler) {\n document.removeEventListener('visibilitychange', visibilityHandler);\n }\n visibilityHandler = () => {\n if (document.hidden && pause && musicStarted) {\n pauseMusic();\n } else if (!document.hidden && pause && musicBuffer) {\n playMusic(0);\n }\n };\n document.addEventListener('visibilitychange', visibilityHandler);\n}\n\nexport function destroyMusic(): void {\n stopMusic();\n musicBuffer = null;\n musicStarted = false;\n musicMuted = false;\n if (visibilityHandler) {\n document.removeEventListener('visibilitychange', visibilityHandler);\n visibilityHandler = null;\n }\n}\n","import { getCtx, resumeCtx } from './audio-context.js';\n\ninterface SfxEntry {\n buffer: AudioBuffer;\n volume: number;\n pool: AudioBufferSourceNode[];\n poolSize: number;\n}\n\nconst sfxMap = new Map<string, SfxEntry>();\nlet sfxMuted = false;\nlet sfxGlobalVolume = 1.0;\n\nexport async function loadSfx(name: string, url: string, opts?: { volume?: number; pool?: number }): Promise<void> {\n const res = await fetch(url);\n const arrayBuf = await res.arrayBuffer();\n const buffer = await getCtx().decodeAudioData(arrayBuf);\n sfxMap.set(name, {\n buffer,\n volume: opts?.volume ?? 0.5,\n pool: [],\n poolSize: opts?.pool ?? 1,\n });\n}\n\nexport async function playSfx(name: string, opts?: { volume?: number }): Promise<void> {\n if (sfxMuted) return;\n const entry = sfxMap.get(name);\n if (!entry) return;\n await resumeCtx();\n\n const ctx = getCtx();\n const gain = ctx.createGain();\n const vol = (opts?.volume ?? entry.volume) * sfxGlobalVolume;\n gain.gain.setValueAtTime(vol, ctx.currentTime);\n gain.connect(ctx.destination);\n\n const source = ctx.createBufferSource();\n source.buffer = entry.buffer;\n source.connect(gain);\n source.start(0);\n\n source.onended = () => {\n gain.disconnect();\n const idx = entry.pool.indexOf(source);\n if (idx !== -1) entry.pool.splice(idx, 1);\n };\n\n if (entry.pool.length < entry.poolSize) {\n entry.pool.push(source);\n }\n}\n\nexport function setSfxVolume(name: string, volume: number): void {\n const entry = sfxMap.get(name);\n if (entry) entry.volume = volume;\n}\n\nexport function setSfxGlobalVolume(volume: number): void {\n sfxGlobalVolume = volume;\n}\n\nexport function setSfxMuted(muted: boolean): void {\n sfxMuted = muted;\n}\n\nexport function isSfxMuted(): boolean {\n return sfxMuted;\n}\n\nexport function toggleSfxMute(): boolean {\n sfxMuted = !sfxMuted;\n return sfxMuted;\n}\n\nexport function destroySfx(): void {\n sfxMap.clear();\n sfxMuted = false;\n sfxGlobalVolume = 1.0;\n}\n"],"mappings":"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","names":["playMusic","ctx","ctx","playMusic"]}
package/dist/index.js ADDED
@@ -0,0 +1,233 @@
1
+ // src/audio-context.ts
2
+ var ctx = null;
3
+ function getCtx() {
4
+ if (!ctx) {
5
+ ctx = new (window.AudioContext || window.webkitAudioContext)();
6
+ }
7
+ return ctx;
8
+ }
9
+ async function resumeCtx() {
10
+ const c = getCtx();
11
+ if (c.state === "suspended") {
12
+ await c.resume();
13
+ }
14
+ }
15
+ function closeCtx() {
16
+ if (ctx && ctx.state !== "closed") {
17
+ ctx.close();
18
+ }
19
+ ctx = null;
20
+ }
21
+
22
+ // src/music.ts
23
+ var musicBuffer = null;
24
+ var musicSource = null;
25
+ var musicGain = null;
26
+ var musicStarted = false;
27
+ var musicMuted = false;
28
+ var musicVolume = 0.3;
29
+ var visibilityHandler = null;
30
+ async function loadMusic(url, opts) {
31
+ musicVolume = opts?.volume ?? 0.3;
32
+ const res = await fetch(url);
33
+ const arrayBuf = await res.arrayBuffer();
34
+ musicBuffer = await getCtx().decodeAudioData(arrayBuf);
35
+ }
36
+ async function playMusic(fadeIn = 2) {
37
+ if (!musicBuffer || musicStarted) return;
38
+ await resumeCtx();
39
+ const ctx2 = getCtx();
40
+ musicGain = ctx2.createGain();
41
+ musicGain.connect(ctx2.destination);
42
+ musicGain.gain.setValueAtTime(0, ctx2.currentTime);
43
+ musicSource = ctx2.createBufferSource();
44
+ musicSource.buffer = musicBuffer;
45
+ musicSource.loop = true;
46
+ musicSource.connect(musicGain);
47
+ musicSource.start(0);
48
+ const target = musicMuted ? 0 : musicVolume;
49
+ musicGain.gain.linearRampToValueAtTime(target, ctx2.currentTime + fadeIn);
50
+ musicStarted = true;
51
+ }
52
+ function pauseMusic() {
53
+ if (musicSource) {
54
+ musicSource.onended = null;
55
+ musicSource.stop();
56
+ musicSource = null;
57
+ }
58
+ musicStarted = false;
59
+ }
60
+ function stopMusic() {
61
+ pauseMusic();
62
+ if (musicGain) {
63
+ musicGain.disconnect();
64
+ musicGain = null;
65
+ }
66
+ }
67
+ function setMusicVolume(volume, rampTime = 0.5) {
68
+ musicVolume = volume;
69
+ if (musicGain && !musicMuted) {
70
+ const ctx2 = getCtx();
71
+ musicGain.gain.cancelScheduledValues(ctx2.currentTime);
72
+ musicGain.gain.setValueAtTime(musicGain.gain.value, ctx2.currentTime);
73
+ musicGain.gain.linearRampToValueAtTime(volume, ctx2.currentTime + rampTime);
74
+ }
75
+ }
76
+ function setMusicMuted(muted, rampTime = 0.5) {
77
+ musicMuted = muted;
78
+ if (musicGain) {
79
+ const ctx2 = getCtx();
80
+ musicGain.gain.cancelScheduledValues(ctx2.currentTime);
81
+ if (rampTime <= 0) {
82
+ musicGain.gain.setValueAtTime(muted ? 0 : musicVolume, ctx2.currentTime);
83
+ } else {
84
+ musicGain.gain.setValueAtTime(musicGain.gain.value, ctx2.currentTime);
85
+ musicGain.gain.linearRampToValueAtTime(muted ? 0 : musicVolume, ctx2.currentTime + rampTime);
86
+ }
87
+ }
88
+ }
89
+ function isMusicMuted() {
90
+ return musicMuted;
91
+ }
92
+ function toggleMusicMute() {
93
+ setMusicMuted(!musicMuted);
94
+ return musicMuted;
95
+ }
96
+ function isMusicPlaying() {
97
+ return musicStarted;
98
+ }
99
+ function onVisibilityChange(pause) {
100
+ if (visibilityHandler) {
101
+ document.removeEventListener("visibilitychange", visibilityHandler);
102
+ }
103
+ visibilityHandler = () => {
104
+ if (document.hidden && pause && musicStarted) {
105
+ pauseMusic();
106
+ } else if (!document.hidden && pause && musicBuffer) {
107
+ playMusic(0);
108
+ }
109
+ };
110
+ document.addEventListener("visibilitychange", visibilityHandler);
111
+ }
112
+ function destroyMusic() {
113
+ stopMusic();
114
+ musicBuffer = null;
115
+ musicStarted = false;
116
+ musicMuted = false;
117
+ if (visibilityHandler) {
118
+ document.removeEventListener("visibilitychange", visibilityHandler);
119
+ visibilityHandler = null;
120
+ }
121
+ }
122
+
123
+ // src/sfx.ts
124
+ var sfxMap = /* @__PURE__ */ new Map();
125
+ var sfxMuted = false;
126
+ var sfxGlobalVolume = 1;
127
+ async function loadSfx(name, url, opts) {
128
+ const res = await fetch(url);
129
+ const arrayBuf = await res.arrayBuffer();
130
+ const buffer = await getCtx().decodeAudioData(arrayBuf);
131
+ sfxMap.set(name, {
132
+ buffer,
133
+ volume: opts?.volume ?? 0.5,
134
+ pool: [],
135
+ poolSize: opts?.pool ?? 1
136
+ });
137
+ }
138
+ async function playSfx(name, opts) {
139
+ if (sfxMuted) return;
140
+ const entry = sfxMap.get(name);
141
+ if (!entry) return;
142
+ await resumeCtx();
143
+ const ctx2 = getCtx();
144
+ const gain = ctx2.createGain();
145
+ const vol = (opts?.volume ?? entry.volume) * sfxGlobalVolume;
146
+ gain.gain.setValueAtTime(vol, ctx2.currentTime);
147
+ gain.connect(ctx2.destination);
148
+ const source = ctx2.createBufferSource();
149
+ source.buffer = entry.buffer;
150
+ source.connect(gain);
151
+ source.start(0);
152
+ source.onended = () => {
153
+ gain.disconnect();
154
+ const idx = entry.pool.indexOf(source);
155
+ if (idx !== -1) entry.pool.splice(idx, 1);
156
+ };
157
+ if (entry.pool.length < entry.poolSize) {
158
+ entry.pool.push(source);
159
+ }
160
+ }
161
+ function setSfxVolume(name, volume) {
162
+ const entry = sfxMap.get(name);
163
+ if (entry) entry.volume = volume;
164
+ }
165
+ function setSfxGlobalVolume(volume) {
166
+ sfxGlobalVolume = volume;
167
+ }
168
+ function setSfxMuted(muted) {
169
+ sfxMuted = muted;
170
+ }
171
+ function isSfxMuted() {
172
+ return sfxMuted;
173
+ }
174
+ function toggleSfxMute() {
175
+ sfxMuted = !sfxMuted;
176
+ return sfxMuted;
177
+ }
178
+ function destroySfx() {
179
+ sfxMap.clear();
180
+ sfxMuted = false;
181
+ sfxGlobalVolume = 1;
182
+ }
183
+
184
+ // src/index.ts
185
+ var musicFadeIn = 2;
186
+ async function init(manifest) {
187
+ if (manifest.music) {
188
+ await loadMusic(manifest.music.url, {
189
+ volume: manifest.music.volume,
190
+ fadeIn: manifest.music.fadeIn
191
+ });
192
+ musicFadeIn = manifest.music.fadeIn ?? 2;
193
+ }
194
+ if (manifest.sfx) {
195
+ const entries = Object.entries(manifest.sfx);
196
+ await Promise.all(
197
+ entries.map(([name, cfg]) => loadSfx(name, cfg.url, { volume: cfg.volume, pool: cfg.pool }))
198
+ );
199
+ }
200
+ if (manifest.autoVisibility !== false) {
201
+ onVisibilityChange(true);
202
+ }
203
+ }
204
+ async function playMusic2(fadeIn) {
205
+ await playMusic(fadeIn ?? musicFadeIn);
206
+ }
207
+ function destroy() {
208
+ destroyMusic();
209
+ destroySfx();
210
+ closeCtx();
211
+ }
212
+ export {
213
+ destroy,
214
+ onVisibilityChange as handleVisibility,
215
+ init,
216
+ isMusicMuted,
217
+ isMusicPlaying,
218
+ isSfxMuted,
219
+ loadMusic,
220
+ loadSfx,
221
+ pauseMusic,
222
+ playMusic2 as playMusic,
223
+ playSfx,
224
+ setMusicMuted,
225
+ setMusicVolume,
226
+ setSfxGlobalVolume,
227
+ setSfxMuted,
228
+ setSfxVolume,
229
+ stopMusic,
230
+ toggleMusicMute,
231
+ toggleSfxMute
232
+ };
233
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/audio-context.ts","../src/music.ts","../src/sfx.ts","../src/index.ts"],"sourcesContent":["let ctx: AudioContext | null = null;\n\nexport function getCtx(): AudioContext {\n if (!ctx) {\n ctx = new (window.AudioContext || (window as any).webkitAudioContext)();\n }\n return ctx;\n}\n\nexport async function resumeCtx(): Promise<void> {\n const c = getCtx();\n if (c.state === 'suspended') {\n await c.resume();\n }\n}\n\nexport function closeCtx(): void {\n if (ctx && ctx.state !== 'closed') {\n ctx.close();\n }\n ctx = null;\n}\n","import { getCtx, resumeCtx } from './audio-context.js';\n\nlet musicBuffer: AudioBuffer | null = null;\nlet musicSource: AudioBufferSourceNode | null = null;\nlet musicGain: GainNode | null = null;\nlet musicStarted = false;\nlet musicMuted = false;\nlet musicVolume = 0.3;\nlet visibilityHandler: (() => void) | null = null;\n\nexport async function loadMusic(url: string, opts?: { volume?: number; fadeIn?: number }): Promise<void> {\n musicVolume = opts?.volume ?? 0.3;\n const res = await fetch(url);\n const arrayBuf = await res.arrayBuffer();\n musicBuffer = await getCtx().decodeAudioData(arrayBuf);\n}\n\nexport async function playMusic(fadeIn = 2.0): Promise<void> {\n if (!musicBuffer || musicStarted) return;\n await resumeCtx();\n\n const ctx = getCtx();\n musicGain = ctx.createGain();\n musicGain.connect(ctx.destination);\n musicGain.gain.setValueAtTime(0, ctx.currentTime);\n\n musicSource = ctx.createBufferSource();\n musicSource.buffer = musicBuffer;\n musicSource.loop = true;\n musicSource.connect(musicGain);\n musicSource.start(0);\n\n const target = musicMuted ? 0 : musicVolume;\n musicGain.gain.linearRampToValueAtTime(target, ctx.currentTime + fadeIn);\n musicStarted = true;\n}\n\nexport function pauseMusic(): void {\n if (musicSource) {\n musicSource.onended = null;\n musicSource.stop();\n musicSource = null;\n }\n musicStarted = false;\n}\n\nexport function stopMusic(): void {\n pauseMusic();\n if (musicGain) {\n musicGain.disconnect();\n musicGain = null;\n }\n}\n\nexport function setMusicVolume(volume: number, rampTime = 0.5): void {\n musicVolume = volume;\n if (musicGain && !musicMuted) {\n const ctx = getCtx();\n musicGain.gain.cancelScheduledValues(ctx.currentTime);\n musicGain.gain.setValueAtTime(musicGain.gain.value, ctx.currentTime);\n musicGain.gain.linearRampToValueAtTime(volume, ctx.currentTime + rampTime);\n }\n}\n\nexport function setMusicMuted(muted: boolean, rampTime = 0.5): void {\n musicMuted = muted;\n if (musicGain) {\n const ctx = getCtx();\n musicGain.gain.cancelScheduledValues(ctx.currentTime);\n if (rampTime <= 0) {\n musicGain.gain.setValueAtTime(muted ? 0 : musicVolume, ctx.currentTime);\n } else {\n musicGain.gain.setValueAtTime(musicGain.gain.value, ctx.currentTime);\n musicGain.gain.linearRampToValueAtTime(muted ? 0 : musicVolume, ctx.currentTime + rampTime);\n }\n }\n}\n\nexport function isMusicMuted(): boolean {\n return musicMuted;\n}\n\nexport function toggleMusicMute(): boolean {\n setMusicMuted(!musicMuted);\n return musicMuted;\n}\n\nexport function isMusicPlaying(): boolean {\n return musicStarted;\n}\n\nexport function onVisibilityChange(pause: boolean): void {\n if (visibilityHandler) {\n document.removeEventListener('visibilitychange', visibilityHandler);\n }\n visibilityHandler = () => {\n if (document.hidden && pause && musicStarted) {\n pauseMusic();\n } else if (!document.hidden && pause && musicBuffer) {\n playMusic(0);\n }\n };\n document.addEventListener('visibilitychange', visibilityHandler);\n}\n\nexport function destroyMusic(): void {\n stopMusic();\n musicBuffer = null;\n musicStarted = false;\n musicMuted = false;\n if (visibilityHandler) {\n document.removeEventListener('visibilitychange', visibilityHandler);\n visibilityHandler = null;\n }\n}\n","import { getCtx, resumeCtx } from './audio-context.js';\n\ninterface SfxEntry {\n buffer: AudioBuffer;\n volume: number;\n pool: AudioBufferSourceNode[];\n poolSize: number;\n}\n\nconst sfxMap = new Map<string, SfxEntry>();\nlet sfxMuted = false;\nlet sfxGlobalVolume = 1.0;\n\nexport async function loadSfx(name: string, url: string, opts?: { volume?: number; pool?: number }): Promise<void> {\n const res = await fetch(url);\n const arrayBuf = await res.arrayBuffer();\n const buffer = await getCtx().decodeAudioData(arrayBuf);\n sfxMap.set(name, {\n buffer,\n volume: opts?.volume ?? 0.5,\n pool: [],\n poolSize: opts?.pool ?? 1,\n });\n}\n\nexport async function playSfx(name: string, opts?: { volume?: number }): Promise<void> {\n if (sfxMuted) return;\n const entry = sfxMap.get(name);\n if (!entry) return;\n await resumeCtx();\n\n const ctx = getCtx();\n const gain = ctx.createGain();\n const vol = (opts?.volume ?? entry.volume) * sfxGlobalVolume;\n gain.gain.setValueAtTime(vol, ctx.currentTime);\n gain.connect(ctx.destination);\n\n const source = ctx.createBufferSource();\n source.buffer = entry.buffer;\n source.connect(gain);\n source.start(0);\n\n source.onended = () => {\n gain.disconnect();\n const idx = entry.pool.indexOf(source);\n if (idx !== -1) entry.pool.splice(idx, 1);\n };\n\n if (entry.pool.length < entry.poolSize) {\n entry.pool.push(source);\n }\n}\n\nexport function setSfxVolume(name: string, volume: number): void {\n const entry = sfxMap.get(name);\n if (entry) entry.volume = volume;\n}\n\nexport function setSfxGlobalVolume(volume: number): void {\n sfxGlobalVolume = volume;\n}\n\nexport function setSfxMuted(muted: boolean): void {\n sfxMuted = muted;\n}\n\nexport function isSfxMuted(): boolean {\n return sfxMuted;\n}\n\nexport function toggleSfxMute(): boolean {\n sfxMuted = !sfxMuted;\n return sfxMuted;\n}\n\nexport function destroySfx(): void {\n sfxMap.clear();\n sfxMuted = false;\n sfxGlobalVolume = 1.0;\n}\n","import type { AudioManifest, VisibilityOptions } from './types.js';\nimport {\n loadMusic as _loadMusic,\n playMusic as _playMusic,\n pauseMusic as _pauseMusic,\n stopMusic as _stopMusic,\n setMusicVolume as _setMusicVolume,\n setMusicMuted as _setMusicMuted,\n isMusicMuted as _isMusicMuted,\n isMusicPlaying as _isMusicPlaying,\n toggleMusicMute as _toggleMusicMute,\n onVisibilityChange,\n destroyMusic,\n} from './music.js';\nimport {\n loadSfx as _loadSfx,\n playSfx as _playSfx,\n setSfxVolume as _setSfxVolume,\n setSfxGlobalVolume as _setSfxGlobalVolume,\n setSfxMuted as _setSfxMuted,\n isSfxMuted as _isSfxMuted,\n toggleSfxMute as _toggleSfxMute,\n destroySfx,\n} from './sfx.js';\nimport { closeCtx } from './audio-context.js';\n\nlet musicFadeIn = 2.0;\n\nexport async function init(manifest: AudioManifest): Promise<void> {\n if (manifest.music) {\n await _loadMusic(manifest.music.url, {\n volume: manifest.music.volume,\n fadeIn: manifest.music.fadeIn,\n });\n musicFadeIn = manifest.music.fadeIn ?? 2.0;\n }\n\n if (manifest.sfx) {\n const entries = Object.entries(manifest.sfx);\n await Promise.all(\n entries.map(([name, cfg]) => _loadSfx(name, cfg.url, { volume: cfg.volume, pool: cfg.pool }))\n );\n }\n\n if (manifest.autoVisibility !== false) {\n onVisibilityChange(true);\n }\n}\n\nexport async function playMusic(fadeIn?: number): Promise<void> {\n await _playMusic(fadeIn ?? musicFadeIn);\n}\n\nexport function destroy(): void {\n destroyMusic();\n destroySfx();\n closeCtx();\n}\n\nexport {\n _loadMusic as loadMusic,\n _pauseMusic as pauseMusic,\n _stopMusic as stopMusic,\n _setMusicVolume as setMusicVolume,\n _setMusicMuted as setMusicMuted,\n _isMusicMuted as isMusicMuted,\n _isMusicPlaying as isMusicPlaying,\n _toggleMusicMute as toggleMusicMute,\n _loadSfx as loadSfx,\n _playSfx as playSfx,\n _setSfxVolume as setSfxVolume,\n _setSfxGlobalVolume as setSfxGlobalVolume,\n _setSfxMuted as setSfxMuted,\n _isSfxMuted as isSfxMuted,\n _toggleSfxMute as toggleSfxMute,\n onVisibilityChange as handleVisibility,\n};\n\nexport type { AudioManifest, MusicConfig, SfxConfig, VisibilityOptions } from './types.js';\n"],"mappings":"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","names":["ctx","ctx","playMusic"]}
@@ -0,0 +1,15 @@
1
+ export declare function loadMusic(url: string, opts?: {
2
+ volume?: number;
3
+ fadeIn?: number;
4
+ }): Promise<void>;
5
+ export declare function playMusic(fadeIn?: number): Promise<void>;
6
+ export declare function pauseMusic(): void;
7
+ export declare function stopMusic(): void;
8
+ export declare function setMusicVolume(volume: number, rampTime?: number): void;
9
+ export declare function setMusicMuted(muted: boolean, rampTime?: number): void;
10
+ export declare function isMusicMuted(): boolean;
11
+ export declare function toggleMusicMute(): boolean;
12
+ export declare function isMusicPlaying(): boolean;
13
+ export declare function onVisibilityChange(pause: boolean): void;
14
+ export declare function destroyMusic(): void;
15
+ //# sourceMappingURL=music.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"music.d.ts","sourceRoot":"","sources":["../src/music.ts"],"names":[],"mappings":"AAUA,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvG;AAED,wBAAsB,SAAS,CAAC,MAAM,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB3D;AAED,wBAAgB,UAAU,IAAI,IAAI,CAOjC;AAED,wBAAgB,SAAS,IAAI,IAAI,CAMhC;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,SAAM,GAAG,IAAI,CAQnE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAM,GAAG,IAAI,CAYlE;AAED,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAGzC;AAED,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAYvD;AAED,wBAAgB,YAAY,IAAI,IAAI,CASnC"}
package/dist/sfx.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare function loadSfx(name: string, url: string, opts?: {
2
+ volume?: number;
3
+ pool?: number;
4
+ }): Promise<void>;
5
+ export declare function playSfx(name: string, opts?: {
6
+ volume?: number;
7
+ }): Promise<void>;
8
+ export declare function setSfxVolume(name: string, volume: number): void;
9
+ export declare function setSfxGlobalVolume(volume: number): void;
10
+ export declare function setSfxMuted(muted: boolean): void;
11
+ export declare function isSfxMuted(): boolean;
12
+ export declare function toggleSfxMute(): boolean;
13
+ export declare function destroySfx(): void;
14
+ //# sourceMappingURL=sfx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sfx.d.ts","sourceRoot":"","sources":["../src/sfx.ts"],"names":[],"mappings":"AAaA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAUjH;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BrF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAG/D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEhD;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAgB,aAAa,IAAI,OAAO,CAGvC;AAED,wBAAgB,UAAU,IAAI,IAAI,CAIjC"}
@@ -0,0 +1,20 @@
1
+ export interface MusicConfig {
2
+ url: string;
3
+ volume?: number;
4
+ fadeIn?: number;
5
+ }
6
+ export interface SfxConfig {
7
+ url: string;
8
+ volume?: number;
9
+ pool?: number;
10
+ }
11
+ export interface AudioManifest {
12
+ music?: MusicConfig;
13
+ sfx?: Record<string, SfxConfig>;
14
+ autoVisibility?: boolean;
15
+ }
16
+ export interface VisibilityOptions {
17
+ pauseMusic?: boolean;
18
+ muteSfxOnHidden?: boolean;
19
+ }
20
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@magmacrunch/adenosine-audio",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "description": "Web Audio API engine for game music and sound effects",
6
+ "license": "LGPL-2.1",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/magmacrunchmedia/adenosine",
10
+ "directory": "packages/audio"
11
+ },
12
+ "author": "magmacrunch media",
13
+ "homepage": "https://github.com/magmacrunchmedia/adenosine#readme",
14
+ "main": "dist/index.js",
15
+ "types": "dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ }
21
+ },
22
+ "files": ["dist"],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "scripts": {
27
+ "build": "tsup && tsc --emitDeclarationOnly --declaration --outDir dist",
28
+ "typecheck": "tsc --noEmit",
29
+ "test": "vitest run"
30
+ },
31
+ "devDependencies": {
32
+ "tsup": "^8.5.1",
33
+ "typescript": "^7.0.2",
34
+ "vitest": "^3.2.1"
35
+ }
36
+ }