@capgo/native-audio 8.2.11 → 8.2.13
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 +147 -34
- package/android/build.gradle +1 -1
- package/android/src/main/java/ee/forgr/audio/AudioAsset.java +352 -74
- package/android/src/main/java/ee/forgr/audio/AudioDispatcher.java +24 -3
- package/android/src/main/java/ee/forgr/audio/Constant.java +9 -1
- package/android/src/main/java/ee/forgr/audio/Logger.java +55 -0
- package/android/src/main/java/ee/forgr/audio/NativeAudio.java +336 -57
- package/android/src/main/java/ee/forgr/audio/RemoteAudioAsset.java +307 -98
- package/android/src/main/java/ee/forgr/audio/StreamAudioAsset.java +285 -96
- package/dist/docs.json +307 -41
- package/dist/esm/definitions.d.ts +116 -38
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +52 -41
- package/dist/esm/web.js +386 -41
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +386 -41
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +386 -41
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NativeAudioPlugin/AudioAsset+Fade.swift +104 -0
- package/ios/Sources/NativeAudioPlugin/AudioAsset.swift +168 -324
- package/ios/Sources/NativeAudioPlugin/Constant.swift +17 -4
- package/ios/Sources/NativeAudioPlugin/Logger.swift +43 -0
- package/ios/Sources/NativeAudioPlugin/Plugin.swift +176 -87
- package/ios/Sources/NativeAudioPlugin/RemoteAudioAsset+Fade.swift +110 -0
- package/ios/Sources/NativeAudioPlugin/RemoteAudioAsset.swift +117 -273
- package/ios/Tests/NativeAudioPluginTests/PluginTests.swift +47 -72
- package/package.json +1 -1
package/dist/esm/web.js
CHANGED
|
@@ -3,21 +3,62 @@ import { AudioAsset } from './audio-asset';
|
|
|
3
3
|
export class NativeAudioWeb extends WebPlugin {
|
|
4
4
|
constructor() {
|
|
5
5
|
super();
|
|
6
|
+
this.debugMode = false;
|
|
7
|
+
this.currentTimeIntervals = new Map();
|
|
8
|
+
this.zeroVolume = 0.0001;
|
|
6
9
|
}
|
|
7
10
|
async resume(options) {
|
|
11
|
+
var _a, _b;
|
|
8
12
|
const audio = this.getAudioAsset(options.assetId).audio;
|
|
13
|
+
const data = this.getAudioAssetData(options.assetId);
|
|
14
|
+
const targetVolume = (_b = (_a = data.volumeBeforePause) !== null && _a !== void 0 ? _a : data.volume) !== null && _b !== void 0 ? _b : 1;
|
|
15
|
+
if (options.fadeIn) {
|
|
16
|
+
const fadeDuration = options.fadeInDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;
|
|
17
|
+
this.doFadeIn(audio, fadeDuration, targetVolume);
|
|
18
|
+
}
|
|
19
|
+
else if (audio.volume <= this.zeroVolume) {
|
|
20
|
+
audio.volume = targetVolume;
|
|
21
|
+
this.setGainNodeVolume(audio, targetVolume);
|
|
22
|
+
}
|
|
23
|
+
this.clearFadeOutToStopTimer(options.assetId);
|
|
24
|
+
return this.doResume(options.assetId);
|
|
25
|
+
}
|
|
26
|
+
async doResume(assetId) {
|
|
27
|
+
const audio = this.getAudioAsset(assetId).audio;
|
|
28
|
+
this.startCurrentTimeUpdates(assetId);
|
|
9
29
|
if (audio.paused) {
|
|
10
30
|
return audio.play();
|
|
11
31
|
}
|
|
12
32
|
}
|
|
13
33
|
async pause(options) {
|
|
34
|
+
var _a;
|
|
14
35
|
const audio = this.getAudioAsset(options.assetId).audio;
|
|
15
|
-
|
|
36
|
+
this.cancelGainNodeRamp(audio);
|
|
37
|
+
const data = this.getAudioAssetData(options.assetId);
|
|
38
|
+
data.volumeBeforePause = (_a = data.volume) !== null && _a !== void 0 ? _a : audio.volume;
|
|
39
|
+
this.setAudioAssetData(options.assetId, data);
|
|
40
|
+
if (!audio.paused && options.fadeOut) {
|
|
41
|
+
const fadeOutDuration = options.fadeOutDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;
|
|
42
|
+
this.doFadeOut(audio, fadeOutDuration);
|
|
43
|
+
data.fadeOutToStopTimer = window.setTimeout(() => {
|
|
44
|
+
this.doPause(options.assetId).catch(() => {
|
|
45
|
+
// no-op
|
|
46
|
+
});
|
|
47
|
+
}, fadeOutDuration * 1000);
|
|
48
|
+
this.setAudioAssetData(options.assetId, data);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
return this.doPause(options.assetId);
|
|
52
|
+
}
|
|
53
|
+
async doPause(assetId) {
|
|
54
|
+
const audio = this.getAudioAsset(assetId).audio;
|
|
55
|
+
this.clearFadeOutToStopTimer(assetId);
|
|
56
|
+
this.stopCurrentTimeUpdates(assetId);
|
|
57
|
+
audio.pause();
|
|
16
58
|
}
|
|
17
59
|
async setCurrentTime(options) {
|
|
18
60
|
const audio = this.getAudioAsset(options.assetId).audio;
|
|
19
61
|
audio.currentTime = options.time;
|
|
20
|
-
return;
|
|
21
62
|
}
|
|
22
63
|
async getCurrentTime(options) {
|
|
23
64
|
const audio = this.getAudioAsset(options.assetId).audio;
|
|
@@ -33,6 +74,12 @@ export class NativeAudioWeb extends WebPlugin {
|
|
|
33
74
|
}
|
|
34
75
|
return { duration: audio.duration };
|
|
35
76
|
}
|
|
77
|
+
async setDebugMode(options) {
|
|
78
|
+
this.debugMode = options.enabled;
|
|
79
|
+
if (this.debugMode) {
|
|
80
|
+
this.logInfo('Debug mode enabled');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
36
83
|
async configure(options) {
|
|
37
84
|
throw `configure is not supported for web: ${JSON.stringify(options)}`;
|
|
38
85
|
}
|
|
@@ -40,55 +87,70 @@ export class NativeAudioWeb extends WebPlugin {
|
|
|
40
87
|
try {
|
|
41
88
|
return { found: !!this.getAudioAsset(options.assetId) };
|
|
42
89
|
}
|
|
43
|
-
catch (
|
|
90
|
+
catch (_a) {
|
|
44
91
|
return { found: false };
|
|
45
92
|
}
|
|
46
93
|
}
|
|
47
94
|
async preload(options) {
|
|
48
95
|
var _a;
|
|
96
|
+
this.logInfo(`Preloading audio asset with options: ${JSON.stringify(options)}`);
|
|
49
97
|
if (NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.has(options.assetId)) {
|
|
50
98
|
throw 'AssetId already exists. Unload first if like to change!';
|
|
51
99
|
}
|
|
52
100
|
if (!((_a = options.assetPath) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
53
101
|
throw 'no assetPath provided';
|
|
54
102
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
audio.
|
|
65
|
-
|
|
66
|
-
|
|
103
|
+
NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP.set(options.assetId, options);
|
|
104
|
+
await new Promise((resolve, reject) => {
|
|
105
|
+
var _a;
|
|
106
|
+
if (!options.isUrl && !new RegExp('^/?' + NativeAudioWeb.FILE_LOCATION).test(options.assetPath)) {
|
|
107
|
+
const slashPrefix = options.assetPath.startsWith('/') ? '' : '/';
|
|
108
|
+
options.assetPath = `${NativeAudioWeb.FILE_LOCATION}${slashPrefix}${options.assetPath}`;
|
|
109
|
+
}
|
|
110
|
+
const audio = document.createElement('audio');
|
|
111
|
+
audio.id = options.assetId;
|
|
112
|
+
audio.crossOrigin = 'anonymous';
|
|
113
|
+
audio.src = options.assetPath;
|
|
114
|
+
audio.autoplay = false;
|
|
115
|
+
audio.loop = false;
|
|
116
|
+
audio.preload = 'metadata';
|
|
117
|
+
audio.addEventListener('loadedmetadata', () => {
|
|
118
|
+
resolve();
|
|
119
|
+
});
|
|
120
|
+
audio.addEventListener('error', (errEvt) => {
|
|
121
|
+
this.logError(`Error loading audio file: ${options.assetPath}, error: ${String(errEvt)}`);
|
|
122
|
+
reject('Error loading audio file');
|
|
123
|
+
});
|
|
124
|
+
const data = this.getAudioAssetData(options.assetId);
|
|
125
|
+
if (typeof options.volume === 'number') {
|
|
126
|
+
audio.volume = options.volume;
|
|
127
|
+
data.volume = options.volume;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
data.volume = audio.volume;
|
|
131
|
+
}
|
|
132
|
+
NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.set(options.assetId, new AudioAsset(audio));
|
|
133
|
+
this.setAudioAssetData(options.assetId, data);
|
|
134
|
+
this.setGainNodeVolume(audio, (_a = data.volume) !== null && _a !== void 0 ? _a : 1);
|
|
135
|
+
});
|
|
67
136
|
}
|
|
68
137
|
async playOnce(options) {
|
|
69
138
|
var _a;
|
|
70
|
-
// Generate a unique temporary asset ID
|
|
71
139
|
const assetId = `playOnce_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
72
140
|
NativeAudioWeb.playOnceAssets.add(assetId);
|
|
73
|
-
const autoPlay = options.autoPlay !== false;
|
|
141
|
+
const autoPlay = options.autoPlay !== false;
|
|
74
142
|
const deleteAfterPlay = (_a = options.deleteAfterPlay) !== null && _a !== void 0 ? _a : false;
|
|
75
143
|
try {
|
|
76
|
-
// Preload the asset
|
|
77
144
|
await this.preload({
|
|
78
145
|
assetId,
|
|
79
146
|
assetPath: options.assetPath,
|
|
80
147
|
volume: options.volume,
|
|
81
148
|
isUrl: options.isUrl,
|
|
82
149
|
});
|
|
83
|
-
// Set up automatic cleanup on completion
|
|
84
|
-
const audio = this.getAudioAsset(assetId).audio;
|
|
85
150
|
const cleanupHandler = async () => {
|
|
86
151
|
try {
|
|
87
|
-
// Unload the asset
|
|
88
152
|
await this.unload({ assetId });
|
|
89
153
|
NativeAudioWeb.playOnceAssets.delete(assetId);
|
|
90
|
-
// Delete file if requested (Web can't actually delete files from disk)
|
|
91
|
-
// This is a no-op on web, but we keep the interface consistent
|
|
92
154
|
if (deleteAfterPlay) {
|
|
93
155
|
console.warn('[NativeAudio] deleteAfterPlay is not supported on web platform. File deletion is ignored.');
|
|
94
156
|
}
|
|
@@ -97,21 +159,23 @@ export class NativeAudioWeb extends WebPlugin {
|
|
|
97
159
|
console.error('[NativeAudio] Error during playOnce cleanup:', error);
|
|
98
160
|
}
|
|
99
161
|
};
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
162
|
+
if (autoPlay) {
|
|
163
|
+
await this.doPlay({ assetId, volume: options.volume }, false);
|
|
164
|
+
}
|
|
165
|
+
const currentAudio = this.getAudioAsset(assetId).audio;
|
|
166
|
+
currentAudio.addEventListener('ended', () => {
|
|
167
|
+
cleanupHandler().catch((error) => {
|
|
168
|
+
console.error('[NativeAudio] Error during ended cleanup:', error);
|
|
169
|
+
});
|
|
170
|
+
}, { once: true });
|
|
171
|
+
currentAudio.addEventListener('error', () => {
|
|
103
172
|
cleanupHandler().catch((error) => {
|
|
104
173
|
console.error('[NativeAudio] Error during error cleanup:', error);
|
|
105
174
|
});
|
|
106
175
|
}, { once: true });
|
|
107
|
-
// Auto-play if requested
|
|
108
|
-
if (autoPlay) {
|
|
109
|
-
await this.play({ assetId });
|
|
110
|
-
}
|
|
111
176
|
return { assetId };
|
|
112
177
|
}
|
|
113
178
|
catch (error) {
|
|
114
|
-
// Cleanup on failure
|
|
115
179
|
try {
|
|
116
180
|
await this.unload({ assetId });
|
|
117
181
|
NativeAudioWeb.playOnceAssets.delete(assetId);
|
|
@@ -123,43 +187,196 @@ export class NativeAudioWeb extends WebPlugin {
|
|
|
123
187
|
}
|
|
124
188
|
}
|
|
125
189
|
onEnded(assetId) {
|
|
190
|
+
this.logDebug(`Playback ended for assetId: ${assetId}`);
|
|
126
191
|
this.notifyListeners('complete', { assetId });
|
|
127
192
|
}
|
|
128
193
|
async play(options) {
|
|
194
|
+
this.logInfo(`Playing audio asset with options: ${JSON.stringify(options)}`);
|
|
195
|
+
this.clearFadeOutToStopTimer(options.assetId);
|
|
196
|
+
const { delay = 0 } = options;
|
|
197
|
+
if (delay > 0) {
|
|
198
|
+
const data = this.getAudioAssetData(options.assetId);
|
|
199
|
+
data.startTimer = window.setTimeout(() => {
|
|
200
|
+
this.doPlay(options).catch((error) => {
|
|
201
|
+
this.logError(`Delayed play failed: ${String(error)}`);
|
|
202
|
+
});
|
|
203
|
+
data.startTimer = undefined;
|
|
204
|
+
this.setAudioAssetData(options.assetId, data);
|
|
205
|
+
}, delay * 1000);
|
|
206
|
+
this.setAudioAssetData(options.assetId, data);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
await this.doPlay(options);
|
|
210
|
+
}
|
|
211
|
+
async doPlay(options, recreateAudioElement = true) {
|
|
129
212
|
const { assetId, time = 0 } = options;
|
|
213
|
+
if (!NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP.has(assetId)) {
|
|
214
|
+
throw `no asset for assetId "${assetId}" available. Call preload first!`;
|
|
215
|
+
}
|
|
216
|
+
if (recreateAudioElement) {
|
|
217
|
+
const preloadOptions = NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP.get(assetId);
|
|
218
|
+
await this.unload({ assetId });
|
|
219
|
+
await this.preload(preloadOptions);
|
|
220
|
+
}
|
|
130
221
|
const audio = this.getAudioAsset(assetId).audio;
|
|
131
|
-
|
|
222
|
+
audio.id = assetId;
|
|
132
223
|
audio.loop = false;
|
|
133
224
|
audio.currentTime = time;
|
|
134
225
|
audio.addEventListener('ended', () => this.onEnded(assetId), {
|
|
135
226
|
once: true,
|
|
136
227
|
});
|
|
137
|
-
|
|
228
|
+
const data = this.getAudioAssetData(assetId);
|
|
229
|
+
if (typeof options.volume === 'number') {
|
|
230
|
+
audio.volume = options.volume;
|
|
231
|
+
data.volume = options.volume;
|
|
232
|
+
this.setGainNodeVolume(audio, options.volume);
|
|
233
|
+
}
|
|
234
|
+
else if (typeof data.volume !== 'number') {
|
|
235
|
+
data.volume = audio.volume;
|
|
236
|
+
}
|
|
237
|
+
await audio.play();
|
|
238
|
+
this.startCurrentTimeUpdates(assetId);
|
|
239
|
+
if (options.fadeIn) {
|
|
240
|
+
this.logDebug(`Fading in audio asset with assetId: ${assetId}`);
|
|
241
|
+
const fadeDuration = options.fadeInDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;
|
|
242
|
+
this.doFadeIn(audio, fadeDuration);
|
|
243
|
+
}
|
|
244
|
+
if (options.fadeOut && !Number.isNaN(audio.duration) && Number.isFinite(audio.duration)) {
|
|
245
|
+
this.logDebug(`Scheduling fade out for audio asset with assetId: ${assetId}`);
|
|
246
|
+
const fadeOutDuration = options.fadeOutDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;
|
|
247
|
+
const fadeOutStartTime = options.fadeOutStartTime || audio.duration - fadeOutDuration;
|
|
248
|
+
data.fadeOut = true;
|
|
249
|
+
data.fadeOutStartTime = fadeOutStartTime;
|
|
250
|
+
data.fadeOutDuration = fadeOutDuration;
|
|
251
|
+
}
|
|
252
|
+
this.setAudioAssetData(assetId, data);
|
|
253
|
+
}
|
|
254
|
+
doFadeIn(audio, fadeDuration, targetVolume) {
|
|
255
|
+
var _a;
|
|
256
|
+
const data = this.getAudioAssetData(audio.id);
|
|
257
|
+
this.setGainNodeVolume(audio, this.zeroVolume);
|
|
258
|
+
const fadeToVolume = (_a = targetVolume !== null && targetVolume !== void 0 ? targetVolume : data.volume) !== null && _a !== void 0 ? _a : 1;
|
|
259
|
+
this.linearRampGainNodeVolume(audio, fadeToVolume, fadeDuration);
|
|
260
|
+
data.fadeInTimer = window.setTimeout(() => {
|
|
261
|
+
data.fadeInTimer = undefined;
|
|
262
|
+
this.setAudioAssetData(audio.id, data);
|
|
263
|
+
}, fadeDuration * 1000);
|
|
264
|
+
this.setAudioAssetData(audio.id, data);
|
|
265
|
+
}
|
|
266
|
+
doFadeOut(audio, fadeDuration) {
|
|
267
|
+
this.linearRampGainNodeVolume(audio, this.zeroVolume, fadeDuration);
|
|
138
268
|
}
|
|
139
269
|
async loop(options) {
|
|
270
|
+
this.logInfo(`Looping audio asset with options: ${JSON.stringify(options)}`);
|
|
140
271
|
const audio = this.getAudioAsset(options.assetId).audio;
|
|
141
|
-
|
|
272
|
+
this.reset(audio);
|
|
142
273
|
audio.loop = true;
|
|
274
|
+
this.startCurrentTimeUpdates(options.assetId);
|
|
143
275
|
return audio.play();
|
|
144
276
|
}
|
|
145
277
|
async stop(options) {
|
|
278
|
+
this.logInfo(`Stopping audio asset with options: ${JSON.stringify(options)}`);
|
|
146
279
|
const audio = this.getAudioAsset(options.assetId).audio;
|
|
280
|
+
const data = this.getAudioAssetData(options.assetId);
|
|
281
|
+
this.clearFadeOutToStopTimer(options.assetId);
|
|
282
|
+
this.cancelGainNodeRamp(audio);
|
|
283
|
+
if (!audio.paused && options.fadeOut) {
|
|
284
|
+
const fadeDuration = options.fadeOutDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;
|
|
285
|
+
this.doFadeOut(audio, fadeDuration);
|
|
286
|
+
data.fadeOutToStopTimer = window.setTimeout(() => {
|
|
287
|
+
this.doStop(audio, options);
|
|
288
|
+
}, fadeDuration * 1000);
|
|
289
|
+
this.setAudioAssetData(options.assetId, data);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.doStop(audio, options);
|
|
293
|
+
}
|
|
294
|
+
doStop(audio, options) {
|
|
147
295
|
audio.pause();
|
|
148
|
-
|
|
296
|
+
this.onEnded(options.assetId);
|
|
297
|
+
this.reset(audio);
|
|
298
|
+
}
|
|
299
|
+
reset(audio) {
|
|
300
|
+
var _a;
|
|
149
301
|
audio.currentTime = 0;
|
|
302
|
+
for (const [assetId, asset] of NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.entries()) {
|
|
303
|
+
if (asset.audio === audio) {
|
|
304
|
+
this.stopCurrentTimeUpdates(assetId);
|
|
305
|
+
this.clearFadeOutToStopTimer(assetId);
|
|
306
|
+
this.clearStartTimer(assetId);
|
|
307
|
+
this.cancelGainNodeRamp(audio);
|
|
308
|
+
const data = this.getAudioAssetData(assetId);
|
|
309
|
+
const initialVolume = (_a = data.volume) !== null && _a !== void 0 ? _a : 1;
|
|
310
|
+
this.setGainNodeVolume(audio, initialVolume);
|
|
311
|
+
this.setAudioAssetData(assetId, data);
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
clearFadeOutToStopTimer(assetId) {
|
|
317
|
+
const data = this.getAudioAssetData(assetId);
|
|
318
|
+
if (data.fadeOutToStopTimer) {
|
|
319
|
+
clearTimeout(data.fadeOutToStopTimer);
|
|
320
|
+
data.fadeOutToStopTimer = undefined;
|
|
321
|
+
this.setAudioAssetData(assetId, data);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
clearStartTimer(assetId) {
|
|
325
|
+
const data = this.getAudioAssetData(assetId);
|
|
326
|
+
if (data.startTimer) {
|
|
327
|
+
clearTimeout(data.startTimer);
|
|
328
|
+
data.startTimer = undefined;
|
|
329
|
+
this.setAudioAssetData(assetId, data);
|
|
330
|
+
}
|
|
150
331
|
}
|
|
151
332
|
async unload(options) {
|
|
152
|
-
|
|
333
|
+
this.logInfo(`Unloading audio asset with options: ${JSON.stringify(options)}`);
|
|
334
|
+
const audio = this.getAudioAsset(options.assetId).audio;
|
|
335
|
+
this.reset(audio);
|
|
153
336
|
NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.delete(options.assetId);
|
|
337
|
+
NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP.delete(options.assetId);
|
|
338
|
+
NativeAudioWeb.AUDIO_DATA_MAP.delete(options.assetId);
|
|
339
|
+
this.cleanupAudioContext(audio);
|
|
340
|
+
}
|
|
341
|
+
cleanupAudioContext(audio) {
|
|
342
|
+
const gainNode = NativeAudioWeb.GAIN_NODE_MAP.get(audio);
|
|
343
|
+
if (gainNode) {
|
|
344
|
+
gainNode.disconnect();
|
|
345
|
+
NativeAudioWeb.GAIN_NODE_MAP.delete(audio);
|
|
346
|
+
}
|
|
347
|
+
const sourceNode = NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.get(audio);
|
|
348
|
+
if (sourceNode) {
|
|
349
|
+
sourceNode.disconnect();
|
|
350
|
+
NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.delete(audio);
|
|
351
|
+
}
|
|
352
|
+
const audioContext = NativeAudioWeb.AUDIO_CONTEXT_MAP.get(audio);
|
|
353
|
+
if (audioContext) {
|
|
354
|
+
audioContext.close().catch(() => {
|
|
355
|
+
// no-op
|
|
356
|
+
});
|
|
357
|
+
NativeAudioWeb.AUDIO_CONTEXT_MAP.delete(audio);
|
|
358
|
+
}
|
|
154
359
|
}
|
|
155
360
|
async setVolume(options) {
|
|
361
|
+
this.logInfo(`Setting volume for audio asset with options: ${JSON.stringify(options)}`);
|
|
156
362
|
if (typeof (options === null || options === void 0 ? void 0 : options.volume) !== 'number') {
|
|
157
363
|
throw 'no volume provided';
|
|
158
364
|
}
|
|
365
|
+
const { volume, duration = 0 } = options;
|
|
366
|
+
const data = this.getAudioAssetData(options.assetId);
|
|
367
|
+
data.volume = volume;
|
|
368
|
+
this.setAudioAssetData(options.assetId, data);
|
|
159
369
|
const audio = this.getAudioAsset(options.assetId).audio;
|
|
160
|
-
audio
|
|
370
|
+
this.cancelGainNodeRamp(audio);
|
|
371
|
+
if (duration > 0) {
|
|
372
|
+
this.exponentialRampGainNodeVolume(audio, volume, duration);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
audio.volume = volume;
|
|
376
|
+
this.setGainNodeVolume(audio, volume);
|
|
161
377
|
}
|
|
162
378
|
async setRate(options) {
|
|
379
|
+
this.logInfo(`Setting playback rate for audio asset with options: ${JSON.stringify(options)}`);
|
|
163
380
|
if (typeof (options === null || options === void 0 ? void 0 : options.rate) !== 'number') {
|
|
164
381
|
throw 'no rate provided';
|
|
165
382
|
}
|
|
@@ -171,8 +388,7 @@ export class NativeAudioWeb extends WebPlugin {
|
|
|
171
388
|
return { isPlaying: !audio.paused };
|
|
172
389
|
}
|
|
173
390
|
async clearCache() {
|
|
174
|
-
|
|
175
|
-
return;
|
|
391
|
+
this.logWarning('clearCache is not supported for web. No cache to clear.');
|
|
176
392
|
}
|
|
177
393
|
getAudioAsset(assetId) {
|
|
178
394
|
this.checkAssetId(assetId);
|
|
@@ -189,19 +405,148 @@ export class NativeAudioWeb extends WebPlugin {
|
|
|
189
405
|
throw 'no assetId provided';
|
|
190
406
|
}
|
|
191
407
|
}
|
|
408
|
+
getOrCreateAudioContext(audio) {
|
|
409
|
+
if (NativeAudioWeb.AUDIO_CONTEXT_MAP.has(audio)) {
|
|
410
|
+
return NativeAudioWeb.AUDIO_CONTEXT_MAP.get(audio);
|
|
411
|
+
}
|
|
412
|
+
const audioContext = new AudioContext();
|
|
413
|
+
NativeAudioWeb.AUDIO_CONTEXT_MAP.set(audio, audioContext);
|
|
414
|
+
return audioContext;
|
|
415
|
+
}
|
|
416
|
+
getOrCreateMediaElementSource(audioContext, audio) {
|
|
417
|
+
if (NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.has(audio)) {
|
|
418
|
+
return NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.get(audio);
|
|
419
|
+
}
|
|
420
|
+
const sourceNode = audioContext.createMediaElementSource(audio);
|
|
421
|
+
NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.set(audio, sourceNode);
|
|
422
|
+
return sourceNode;
|
|
423
|
+
}
|
|
424
|
+
getOrCreateGainNode(audio, track) {
|
|
425
|
+
const audioContext = this.getOrCreateAudioContext(audio);
|
|
426
|
+
if (NativeAudioWeb.GAIN_NODE_MAP.has(audio)) {
|
|
427
|
+
return NativeAudioWeb.GAIN_NODE_MAP.get(audio);
|
|
428
|
+
}
|
|
429
|
+
const gainNode = audioContext.createGain();
|
|
430
|
+
track.connect(gainNode).connect(audioContext.destination);
|
|
431
|
+
NativeAudioWeb.GAIN_NODE_MAP.set(audio, gainNode);
|
|
432
|
+
return gainNode;
|
|
433
|
+
}
|
|
434
|
+
setGainNodeVolume(audio, volume, time) {
|
|
435
|
+
const audioContext = this.getOrCreateAudioContext(audio);
|
|
436
|
+
const track = this.getOrCreateMediaElementSource(audioContext, audio);
|
|
437
|
+
const gainNode = this.getOrCreateGainNode(audio, track);
|
|
438
|
+
if (time !== undefined) {
|
|
439
|
+
gainNode.gain.setValueAtTime(volume, time);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
gainNode.gain.setValueAtTime(volume, audioContext.currentTime);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
exponentialRampGainNodeVolume(audio, volume, duration) {
|
|
446
|
+
const audioContext = this.getOrCreateAudioContext(audio);
|
|
447
|
+
const track = this.getOrCreateMediaElementSource(audioContext, audio);
|
|
448
|
+
const gainNode = this.getOrCreateGainNode(audio, track);
|
|
449
|
+
const adjustedVolume = volume < this.zeroVolume ? this.zeroVolume : volume;
|
|
450
|
+
gainNode.gain.exponentialRampToValueAtTime(adjustedVolume, audioContext.currentTime + duration);
|
|
451
|
+
}
|
|
452
|
+
linearRampGainNodeVolume(audio, volume, duration) {
|
|
453
|
+
const audioContext = this.getOrCreateAudioContext(audio);
|
|
454
|
+
const track = this.getOrCreateMediaElementSource(audioContext, audio);
|
|
455
|
+
const gainNode = this.getOrCreateGainNode(audio, track);
|
|
456
|
+
gainNode.gain.linearRampToValueAtTime(volume, audioContext.currentTime + duration);
|
|
457
|
+
}
|
|
458
|
+
cancelGainNodeRamp(audio) {
|
|
459
|
+
const gainNode = NativeAudioWeb.GAIN_NODE_MAP.get(audio);
|
|
460
|
+
if (gainNode) {
|
|
461
|
+
gainNode.gain.cancelScheduledValues(0);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
startCurrentTimeUpdates(assetId) {
|
|
465
|
+
this.stopCurrentTimeUpdates(assetId);
|
|
466
|
+
const audio = this.getAudioAsset(assetId).audio;
|
|
467
|
+
const intervalId = window.setInterval(() => {
|
|
468
|
+
var _a;
|
|
469
|
+
if (!audio.paused) {
|
|
470
|
+
const currentTime = Math.round(audio.currentTime * 10) / 10;
|
|
471
|
+
this.notifyListeners('currentTime', { assetId, currentTime });
|
|
472
|
+
this.logDebug(`Current time update for assetId: ${assetId}, currentTime: ${currentTime}`);
|
|
473
|
+
const data = this.getAudioAssetData(assetId);
|
|
474
|
+
if (data.fadeOut && typeof data.fadeOutStartTime === 'number' && currentTime >= data.fadeOutStartTime) {
|
|
475
|
+
this.cancelGainNodeRamp(audio);
|
|
476
|
+
const fadeOutDuration = (_a = data.fadeOutDuration) !== null && _a !== void 0 ? _a : NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;
|
|
477
|
+
this.doFadeOut(audio, fadeOutDuration);
|
|
478
|
+
data.fadeOut = false;
|
|
479
|
+
this.setAudioAssetData(assetId, data);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
this.stopCurrentTimeUpdates(assetId);
|
|
484
|
+
}
|
|
485
|
+
}, NativeAudioWeb.CURRENT_TIME_UPDATE_INTERVAL);
|
|
486
|
+
this.currentTimeIntervals.set(assetId, intervalId);
|
|
487
|
+
}
|
|
488
|
+
stopCurrentTimeUpdates(assetId) {
|
|
489
|
+
if (assetId) {
|
|
490
|
+
const intervalId = this.currentTimeIntervals.get(assetId);
|
|
491
|
+
if (intervalId) {
|
|
492
|
+
clearInterval(intervalId);
|
|
493
|
+
this.currentTimeIntervals.delete(assetId);
|
|
494
|
+
}
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
for (const intervalId of this.currentTimeIntervals.values()) {
|
|
498
|
+
clearInterval(intervalId);
|
|
499
|
+
}
|
|
500
|
+
this.currentTimeIntervals.clear();
|
|
501
|
+
}
|
|
502
|
+
getAudioAssetData(assetId) {
|
|
503
|
+
return NativeAudioWeb.AUDIO_DATA_MAP.get(assetId) || {};
|
|
504
|
+
}
|
|
505
|
+
setAudioAssetData(assetId, data) {
|
|
506
|
+
const currentData = NativeAudioWeb.AUDIO_DATA_MAP.get(assetId) || {};
|
|
507
|
+
const newData = Object.assign(Object.assign({}, currentData), data);
|
|
508
|
+
NativeAudioWeb.AUDIO_DATA_MAP.set(assetId, newData);
|
|
509
|
+
}
|
|
510
|
+
logError(message) {
|
|
511
|
+
if (!this.debugMode)
|
|
512
|
+
return;
|
|
513
|
+
console.error(`${NativeAudioWeb.LOG_TAG} Error: ${message}`);
|
|
514
|
+
}
|
|
515
|
+
logWarning(message) {
|
|
516
|
+
if (!this.debugMode)
|
|
517
|
+
return;
|
|
518
|
+
console.warn(`${NativeAudioWeb.LOG_TAG} Warning: ${message}`);
|
|
519
|
+
}
|
|
520
|
+
logInfo(message) {
|
|
521
|
+
if (!this.debugMode)
|
|
522
|
+
return;
|
|
523
|
+
console.info(`${NativeAudioWeb.LOG_TAG} Info: ${message}`);
|
|
524
|
+
}
|
|
525
|
+
logDebug(message) {
|
|
526
|
+
if (!this.debugMode)
|
|
527
|
+
return;
|
|
528
|
+
console.debug(`${NativeAudioWeb.LOG_TAG} Debug: ${message}`);
|
|
529
|
+
}
|
|
192
530
|
async getPluginVersion() {
|
|
193
531
|
return { version: 'web' };
|
|
194
532
|
}
|
|
195
533
|
async deinitPlugin() {
|
|
196
|
-
// Stop and unload all audio assets
|
|
197
534
|
for (const [assetId] of NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID) {
|
|
198
535
|
await this.unload({ assetId });
|
|
199
536
|
}
|
|
200
|
-
|
|
537
|
+
this.stopCurrentTimeUpdates();
|
|
201
538
|
}
|
|
202
539
|
}
|
|
540
|
+
NativeAudioWeb.LOG_TAG = '[NativeAudioWeb]';
|
|
203
541
|
NativeAudioWeb.FILE_LOCATION = '';
|
|
542
|
+
NativeAudioWeb.DEFAULT_FADE_DURATION_SEC = 1;
|
|
543
|
+
NativeAudioWeb.CURRENT_TIME_UPDATE_INTERVAL = 100;
|
|
544
|
+
NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP = new Map();
|
|
545
|
+
NativeAudioWeb.AUDIO_DATA_MAP = new Map();
|
|
204
546
|
NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID = new Map();
|
|
547
|
+
NativeAudioWeb.AUDIO_CONTEXT_MAP = new Map();
|
|
548
|
+
NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP = new Map();
|
|
549
|
+
NativeAudioWeb.GAIN_NODE_MAP = new Map();
|
|
205
550
|
NativeAudioWeb.playOnceAssets = new Set();
|
|
206
551
|
const NativeAudio = new NativeAudioWeb();
|
|
207
552
|
export { NativeAudio };
|