@capgo/capacitor-native-audio 8.4.3
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/CapgoCapacitorNativeAudio.podspec +16 -0
- package/LICENSE +373 -0
- package/Package.swift +31 -0
- package/README.md +1229 -0
- package/android/build.gradle +89 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/ee/forgr/audio/AudioAsset.java +611 -0
- package/android/src/main/java/ee/forgr/audio/AudioCompletionListener.java +5 -0
- package/android/src/main/java/ee/forgr/audio/AudioDispatcher.java +208 -0
- package/android/src/main/java/ee/forgr/audio/Constant.java +36 -0
- package/android/src/main/java/ee/forgr/audio/HlsAvailabilityChecker.java +84 -0
- package/android/src/main/java/ee/forgr/audio/Logger.java +55 -0
- package/android/src/main/java/ee/forgr/audio/NativeAudio.java +2022 -0
- package/android/src/main/java/ee/forgr/audio/RemoteAudioAsset.java +886 -0
- package/android/src/main/java/ee/forgr/audio/StreamAudioAsset.java +708 -0
- package/android/src/main/res/values/colors.xml +3 -0
- package/android/src/main/res/values/strings.xml +3 -0
- package/android/src/main/res/values/styles.xml +3 -0
- package/dist/docs.json +1470 -0
- package/dist/esm/audio-asset.d.ts +4 -0
- package/dist/esm/audio-asset.js +6 -0
- package/dist/esm/audio-asset.js.map +1 -0
- package/dist/esm/definitions.d.ts +597 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +82 -0
- package/dist/esm/web.js +553 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +571 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +574 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/NativeAudioPlugin/AudioAsset+Fade.swift +157 -0
- package/ios/Sources/NativeAudioPlugin/AudioAsset.swift +403 -0
- package/ios/Sources/NativeAudioPlugin/Constant.swift +52 -0
- package/ios/Sources/NativeAudioPlugin/Logger.swift +43 -0
- package/ios/Sources/NativeAudioPlugin/Plugin.swift +1786 -0
- package/ios/Sources/NativeAudioPlugin/RemoteAudioAsset+Fade.swift +152 -0
- package/ios/Sources/NativeAudioPlugin/RemoteAudioAsset.swift +405 -0
- package/ios/Tests/NativeAudioPluginTests/PluginTests.swift +648 -0
- package/ios/Tests/README.md +39 -0
- package/package.json +101 -0
- package/scripts/configure-dependencies.js +251 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/audio-asset.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst NativeAudio = registerPlugin('NativeAudio', {\n web: () => import('./web').then((m) => new m.NativeAudioWeb()),\n});\nexport * from './definitions';\nexport { NativeAudio };\n//# sourceMappingURL=index.js.map","export class AudioAsset {\n constructor(audio) {\n this.audio = audio;\n }\n}\n//# sourceMappingURL=audio-asset.js.map","import { WebPlugin } from '@capacitor/core';\nimport { AudioAsset } from './audio-asset';\nexport class NativeAudioWeb extends WebPlugin {\n constructor() {\n super();\n this.debugMode = false;\n this.currentTimeIntervals = new Map();\n this.zeroVolume = 0.0001;\n }\n async resume(options) {\n var _a, _b;\n const audio = this.getAudioAsset(options.assetId).audio;\n const data = this.getAudioAssetData(options.assetId);\n const targetVolume = (_b = (_a = data.volumeBeforePause) !== null && _a !== void 0 ? _a : data.volume) !== null && _b !== void 0 ? _b : 1;\n if (options.fadeIn) {\n const fadeDuration = options.fadeInDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;\n this.doFadeIn(audio, fadeDuration, targetVolume);\n }\n else if (audio.volume <= this.zeroVolume) {\n audio.volume = targetVolume;\n this.setGainNodeVolume(audio, targetVolume);\n }\n this.clearFadeOutToStopTimer(options.assetId);\n return this.doResume(options.assetId);\n }\n async doResume(assetId) {\n const audio = this.getAudioAsset(assetId).audio;\n this.startCurrentTimeUpdates(assetId);\n if (audio.paused) {\n return audio.play();\n }\n }\n async pause(options) {\n var _a;\n const audio = this.getAudioAsset(options.assetId).audio;\n this.cancelGainNodeRamp(audio);\n const data = this.getAudioAssetData(options.assetId);\n data.volumeBeforePause = (_a = data.volume) !== null && _a !== void 0 ? _a : audio.volume;\n this.setAudioAssetData(options.assetId, data);\n if (!audio.paused && options.fadeOut) {\n const fadeOutDuration = options.fadeOutDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;\n this.doFadeOut(audio, fadeOutDuration);\n data.fadeOutToStopTimer = window.setTimeout(() => {\n this.doPause(options.assetId).catch(() => {\n // no-op\n });\n }, fadeOutDuration * 1000);\n this.setAudioAssetData(options.assetId, data);\n return;\n }\n return this.doPause(options.assetId);\n }\n async doPause(assetId) {\n const audio = this.getAudioAsset(assetId).audio;\n this.clearFadeOutToStopTimer(assetId);\n this.stopCurrentTimeUpdates(assetId);\n audio.pause();\n }\n async setCurrentTime(options) {\n const audio = this.getAudioAsset(options.assetId).audio;\n audio.currentTime = options.time;\n }\n async getCurrentTime(options) {\n const audio = this.getAudioAsset(options.assetId).audio;\n return { currentTime: audio.currentTime };\n }\n async getDuration(options) {\n const audio = this.getAudioAsset(options.assetId).audio;\n if (Number.isNaN(audio.duration)) {\n throw 'no duration available';\n }\n if (!Number.isFinite(audio.duration)) {\n throw 'duration not available => media resource is streaming';\n }\n return { duration: audio.duration };\n }\n async setDebugMode(options) {\n this.debugMode = options.enabled;\n if (this.debugMode) {\n this.logInfo('Debug mode enabled');\n }\n }\n async configure(options) {\n throw `configure is not supported for web: ${JSON.stringify(options)}`;\n }\n async isPreloaded(options) {\n try {\n return { found: !!this.getAudioAsset(options.assetId) };\n }\n catch (_a) {\n return { found: false };\n }\n }\n async preload(options) {\n var _a;\n this.logInfo(`Preloading audio asset with options: ${JSON.stringify(options)}`);\n if (NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.has(options.assetId)) {\n throw 'AssetId already exists. Unload first if like to change!';\n }\n if (!((_a = options.assetPath) === null || _a === void 0 ? void 0 : _a.length)) {\n throw 'no assetPath provided';\n }\n NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP.set(options.assetId, options);\n await new Promise((resolve, reject) => {\n var _a;\n if (!options.isUrl && !new RegExp('^/?' + NativeAudioWeb.FILE_LOCATION).test(options.assetPath)) {\n const slashPrefix = options.assetPath.startsWith('/') ? '' : '/';\n options.assetPath = `${NativeAudioWeb.FILE_LOCATION}${slashPrefix}${options.assetPath}`;\n }\n const audio = document.createElement('audio');\n audio.id = options.assetId;\n audio.crossOrigin = 'anonymous';\n audio.src = options.assetPath;\n audio.autoplay = false;\n audio.loop = false;\n audio.preload = 'metadata';\n audio.addEventListener('loadedmetadata', () => {\n resolve();\n });\n audio.addEventListener('error', (errEvt) => {\n this.logError(`Error loading audio file: ${options.assetPath}, error: ${String(errEvt)}`);\n reject('Error loading audio file');\n });\n const data = this.getAudioAssetData(options.assetId);\n if (typeof options.volume === 'number') {\n audio.volume = options.volume;\n data.volume = options.volume;\n }\n else {\n data.volume = audio.volume;\n }\n NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.set(options.assetId, new AudioAsset(audio));\n this.setAudioAssetData(options.assetId, data);\n this.setGainNodeVolume(audio, (_a = data.volume) !== null && _a !== void 0 ? _a : 1);\n });\n }\n async playOnce(options) {\n var _a;\n const assetId = `playOnce_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n NativeAudioWeb.playOnceAssets.add(assetId);\n const autoPlay = options.autoPlay !== false;\n const deleteAfterPlay = (_a = options.deleteAfterPlay) !== null && _a !== void 0 ? _a : false;\n try {\n await this.preload({\n assetId,\n assetPath: options.assetPath,\n volume: options.volume,\n isUrl: options.isUrl,\n });\n const cleanupHandler = async () => {\n try {\n await this.unload({ assetId });\n NativeAudioWeb.playOnceAssets.delete(assetId);\n if (deleteAfterPlay) {\n console.warn('[NativeAudio] deleteAfterPlay is not supported on web platform. File deletion is ignored.');\n }\n }\n catch (error) {\n console.error('[NativeAudio] Error during playOnce cleanup:', error);\n }\n };\n if (autoPlay) {\n await this.doPlay({ assetId, volume: options.volume }, false);\n }\n const currentAudio = this.getAudioAsset(assetId).audio;\n currentAudio.addEventListener('ended', () => {\n cleanupHandler().catch((error) => {\n console.error('[NativeAudio] Error during ended cleanup:', error);\n });\n }, { once: true });\n currentAudio.addEventListener('error', () => {\n cleanupHandler().catch((error) => {\n console.error('[NativeAudio] Error during error cleanup:', error);\n });\n }, { once: true });\n return { assetId };\n }\n catch (error) {\n try {\n await this.unload({ assetId });\n NativeAudioWeb.playOnceAssets.delete(assetId);\n }\n catch (_b) {\n // Ignore cleanup errors\n }\n throw error;\n }\n }\n onEnded(assetId) {\n this.logDebug(`Playback ended for assetId: ${assetId}`);\n this.notifyListeners('complete', { assetId });\n }\n async play(options) {\n this.logInfo(`Playing audio asset with options: ${JSON.stringify(options)}`);\n this.clearFadeOutToStopTimer(options.assetId);\n const { delay = 0 } = options;\n if (delay > 0) {\n const data = this.getAudioAssetData(options.assetId);\n data.startTimer = window.setTimeout(() => {\n this.doPlay(options).catch((error) => {\n this.logError(`Delayed play failed: ${String(error)}`);\n });\n data.startTimer = undefined;\n this.setAudioAssetData(options.assetId, data);\n }, delay * 1000);\n this.setAudioAssetData(options.assetId, data);\n return;\n }\n await this.doPlay(options);\n }\n async doPlay(options, recreateAudioElement = true) {\n const { assetId, time = 0 } = options;\n if (!NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP.has(assetId)) {\n throw `no asset for assetId \"${assetId}\" available. Call preload first!`;\n }\n if (recreateAudioElement) {\n const preloadOptions = NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP.get(assetId);\n await this.unload({ assetId });\n await this.preload(preloadOptions);\n }\n const audio = this.getAudioAsset(assetId).audio;\n audio.id = assetId;\n audio.loop = false;\n audio.currentTime = time;\n audio.addEventListener('ended', () => this.onEnded(assetId), {\n once: true,\n });\n const data = this.getAudioAssetData(assetId);\n if (typeof options.volume === 'number') {\n audio.volume = options.volume;\n data.volume = options.volume;\n this.setGainNodeVolume(audio, options.volume);\n }\n else if (typeof data.volume !== 'number') {\n data.volume = audio.volume;\n }\n await audio.play();\n this.startCurrentTimeUpdates(assetId);\n if (options.fadeIn) {\n this.logDebug(`Fading in audio asset with assetId: ${assetId}`);\n const fadeDuration = options.fadeInDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;\n this.doFadeIn(audio, fadeDuration);\n }\n if (options.fadeOut && !Number.isNaN(audio.duration) && Number.isFinite(audio.duration)) {\n this.logDebug(`Scheduling fade out for audio asset with assetId: ${assetId}`);\n const fadeOutDuration = options.fadeOutDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;\n const fadeOutStartTime = options.fadeOutStartTime || audio.duration - fadeOutDuration;\n data.fadeOut = true;\n data.fadeOutStartTime = fadeOutStartTime;\n data.fadeOutDuration = fadeOutDuration;\n }\n this.setAudioAssetData(assetId, data);\n }\n doFadeIn(audio, fadeDuration, targetVolume) {\n var _a;\n const data = this.getAudioAssetData(audio.id);\n this.setGainNodeVolume(audio, this.zeroVolume);\n const fadeToVolume = (_a = targetVolume !== null && targetVolume !== void 0 ? targetVolume : data.volume) !== null && _a !== void 0 ? _a : 1;\n this.linearRampGainNodeVolume(audio, fadeToVolume, fadeDuration);\n data.fadeInTimer = window.setTimeout(() => {\n data.fadeInTimer = undefined;\n this.setAudioAssetData(audio.id, data);\n }, fadeDuration * 1000);\n this.setAudioAssetData(audio.id, data);\n }\n doFadeOut(audio, fadeDuration) {\n this.linearRampGainNodeVolume(audio, this.zeroVolume, fadeDuration);\n }\n async loop(options) {\n this.logInfo(`Looping audio asset with options: ${JSON.stringify(options)}`);\n const audio = this.getAudioAsset(options.assetId).audio;\n this.reset(audio);\n audio.loop = true;\n this.startCurrentTimeUpdates(options.assetId);\n return audio.play();\n }\n async stop(options) {\n this.logInfo(`Stopping audio asset with options: ${JSON.stringify(options)}`);\n const audio = this.getAudioAsset(options.assetId).audio;\n const data = this.getAudioAssetData(options.assetId);\n this.clearFadeOutToStopTimer(options.assetId);\n this.cancelGainNodeRamp(audio);\n if (!audio.paused && options.fadeOut) {\n const fadeDuration = options.fadeOutDuration || NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;\n this.doFadeOut(audio, fadeDuration);\n data.fadeOutToStopTimer = window.setTimeout(() => {\n this.doStop(audio, options);\n }, fadeDuration * 1000);\n this.setAudioAssetData(options.assetId, data);\n return;\n }\n this.doStop(audio, options);\n }\n doStop(audio, options) {\n audio.pause();\n this.onEnded(options.assetId);\n this.reset(audio);\n }\n reset(audio) {\n var _a;\n audio.currentTime = 0;\n for (const [assetId, asset] of NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.entries()) {\n if (asset.audio === audio) {\n this.stopCurrentTimeUpdates(assetId);\n this.clearFadeOutToStopTimer(assetId);\n this.clearStartTimer(assetId);\n this.cancelGainNodeRamp(audio);\n const data = this.getAudioAssetData(assetId);\n const initialVolume = (_a = data.volume) !== null && _a !== void 0 ? _a : 1;\n this.setGainNodeVolume(audio, initialVolume);\n this.setAudioAssetData(assetId, data);\n break;\n }\n }\n }\n clearFadeOutToStopTimer(assetId) {\n const data = this.getAudioAssetData(assetId);\n if (data.fadeOutToStopTimer) {\n clearTimeout(data.fadeOutToStopTimer);\n data.fadeOutToStopTimer = undefined;\n this.setAudioAssetData(assetId, data);\n }\n }\n clearStartTimer(assetId) {\n const data = this.getAudioAssetData(assetId);\n if (data.startTimer) {\n clearTimeout(data.startTimer);\n data.startTimer = undefined;\n this.setAudioAssetData(assetId, data);\n }\n }\n async unload(options) {\n this.logInfo(`Unloading audio asset with options: ${JSON.stringify(options)}`);\n const audio = this.getAudioAsset(options.assetId).audio;\n this.reset(audio);\n NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.delete(options.assetId);\n NativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP.delete(options.assetId);\n NativeAudioWeb.AUDIO_DATA_MAP.delete(options.assetId);\n this.cleanupAudioContext(audio);\n }\n cleanupAudioContext(audio) {\n const gainNode = NativeAudioWeb.GAIN_NODE_MAP.get(audio);\n if (gainNode) {\n gainNode.disconnect();\n NativeAudioWeb.GAIN_NODE_MAP.delete(audio);\n }\n const sourceNode = NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.get(audio);\n if (sourceNode) {\n sourceNode.disconnect();\n NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.delete(audio);\n }\n const audioContext = NativeAudioWeb.AUDIO_CONTEXT_MAP.get(audio);\n if (audioContext) {\n audioContext.close().catch(() => {\n // no-op\n });\n NativeAudioWeb.AUDIO_CONTEXT_MAP.delete(audio);\n }\n }\n async setVolume(options) {\n this.logInfo(`Setting volume for audio asset with options: ${JSON.stringify(options)}`);\n if (typeof (options === null || options === void 0 ? void 0 : options.volume) !== 'number') {\n throw 'no volume provided';\n }\n const { volume, duration = 0 } = options;\n const data = this.getAudioAssetData(options.assetId);\n data.volume = volume;\n this.setAudioAssetData(options.assetId, data);\n const audio = this.getAudioAsset(options.assetId).audio;\n this.cancelGainNodeRamp(audio);\n if (duration > 0) {\n this.exponentialRampGainNodeVolume(audio, volume, duration);\n return;\n }\n audio.volume = volume;\n this.setGainNodeVolume(audio, volume);\n }\n async setRate(options) {\n this.logInfo(`Setting playback rate for audio asset with options: ${JSON.stringify(options)}`);\n if (typeof (options === null || options === void 0 ? void 0 : options.rate) !== 'number') {\n throw 'no rate provided';\n }\n const audio = this.getAudioAsset(options.assetId).audio;\n audio.playbackRate = options.rate;\n }\n async isPlaying(options) {\n const audio = this.getAudioAsset(options.assetId).audio;\n return { isPlaying: !audio.paused };\n }\n async clearCache() {\n this.logWarning('clearCache is not supported for web. No cache to clear.');\n }\n getAudioAsset(assetId) {\n this.checkAssetId(assetId);\n if (!NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.has(assetId)) {\n throw `no asset for assetId \"${assetId}\" available. Call preload first!`;\n }\n return NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID.get(assetId);\n }\n checkAssetId(assetId) {\n if (typeof assetId !== 'string') {\n throw 'assetId must be a string';\n }\n if (!(assetId === null || assetId === void 0 ? void 0 : assetId.length)) {\n throw 'no assetId provided';\n }\n }\n getOrCreateAudioContext(audio) {\n if (NativeAudioWeb.AUDIO_CONTEXT_MAP.has(audio)) {\n return NativeAudioWeb.AUDIO_CONTEXT_MAP.get(audio);\n }\n const audioContext = new AudioContext();\n NativeAudioWeb.AUDIO_CONTEXT_MAP.set(audio, audioContext);\n return audioContext;\n }\n getOrCreateMediaElementSource(audioContext, audio) {\n if (NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.has(audio)) {\n return NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.get(audio);\n }\n const sourceNode = audioContext.createMediaElementSource(audio);\n NativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP.set(audio, sourceNode);\n return sourceNode;\n }\n getOrCreateGainNode(audio, track) {\n const audioContext = this.getOrCreateAudioContext(audio);\n if (NativeAudioWeb.GAIN_NODE_MAP.has(audio)) {\n return NativeAudioWeb.GAIN_NODE_MAP.get(audio);\n }\n const gainNode = audioContext.createGain();\n track.connect(gainNode).connect(audioContext.destination);\n NativeAudioWeb.GAIN_NODE_MAP.set(audio, gainNode);\n return gainNode;\n }\n setGainNodeVolume(audio, volume, time) {\n const audioContext = this.getOrCreateAudioContext(audio);\n const track = this.getOrCreateMediaElementSource(audioContext, audio);\n const gainNode = this.getOrCreateGainNode(audio, track);\n if (time !== undefined) {\n gainNode.gain.setValueAtTime(volume, time);\n }\n else {\n gainNode.gain.setValueAtTime(volume, audioContext.currentTime);\n }\n }\n exponentialRampGainNodeVolume(audio, volume, duration) {\n const audioContext = this.getOrCreateAudioContext(audio);\n const track = this.getOrCreateMediaElementSource(audioContext, audio);\n const gainNode = this.getOrCreateGainNode(audio, track);\n const adjustedVolume = volume < this.zeroVolume ? this.zeroVolume : volume;\n gainNode.gain.exponentialRampToValueAtTime(adjustedVolume, audioContext.currentTime + duration);\n }\n linearRampGainNodeVolume(audio, volume, duration) {\n const audioContext = this.getOrCreateAudioContext(audio);\n const track = this.getOrCreateMediaElementSource(audioContext, audio);\n const gainNode = this.getOrCreateGainNode(audio, track);\n gainNode.gain.linearRampToValueAtTime(volume, audioContext.currentTime + duration);\n }\n cancelGainNodeRamp(audio) {\n const gainNode = NativeAudioWeb.GAIN_NODE_MAP.get(audio);\n if (gainNode) {\n gainNode.gain.cancelScheduledValues(0);\n }\n }\n startCurrentTimeUpdates(assetId) {\n this.stopCurrentTimeUpdates(assetId);\n const audio = this.getAudioAsset(assetId).audio;\n const intervalId = window.setInterval(() => {\n var _a;\n if (!audio.paused) {\n const currentTime = Math.round(audio.currentTime * 10) / 10;\n this.notifyListeners('currentTime', { assetId, currentTime });\n this.logDebug(`Current time update for assetId: ${assetId}, currentTime: ${currentTime}`);\n const data = this.getAudioAssetData(assetId);\n if (data.fadeOut && typeof data.fadeOutStartTime === 'number' && currentTime >= data.fadeOutStartTime) {\n this.cancelGainNodeRamp(audio);\n const fadeOutDuration = (_a = data.fadeOutDuration) !== null && _a !== void 0 ? _a : NativeAudioWeb.DEFAULT_FADE_DURATION_SEC;\n this.doFadeOut(audio, fadeOutDuration);\n data.fadeOut = false;\n this.setAudioAssetData(assetId, data);\n }\n }\n else {\n this.stopCurrentTimeUpdates(assetId);\n }\n }, NativeAudioWeb.CURRENT_TIME_UPDATE_INTERVAL);\n this.currentTimeIntervals.set(assetId, intervalId);\n }\n stopCurrentTimeUpdates(assetId) {\n if (assetId) {\n const intervalId = this.currentTimeIntervals.get(assetId);\n if (intervalId) {\n clearInterval(intervalId);\n this.currentTimeIntervals.delete(assetId);\n }\n return;\n }\n for (const intervalId of this.currentTimeIntervals.values()) {\n clearInterval(intervalId);\n }\n this.currentTimeIntervals.clear();\n }\n getAudioAssetData(assetId) {\n return NativeAudioWeb.AUDIO_DATA_MAP.get(assetId) || {};\n }\n setAudioAssetData(assetId, data) {\n const currentData = NativeAudioWeb.AUDIO_DATA_MAP.get(assetId) || {};\n const newData = Object.assign(Object.assign({}, currentData), data);\n NativeAudioWeb.AUDIO_DATA_MAP.set(assetId, newData);\n }\n logError(message) {\n if (!this.debugMode)\n return;\n console.error(`${NativeAudioWeb.LOG_TAG} Error: ${message}`);\n }\n logWarning(message) {\n if (!this.debugMode)\n return;\n console.warn(`${NativeAudioWeb.LOG_TAG} Warning: ${message}`);\n }\n logInfo(message) {\n if (!this.debugMode)\n return;\n console.info(`${NativeAudioWeb.LOG_TAG} Info: ${message}`);\n }\n logDebug(message) {\n if (!this.debugMode)\n return;\n console.debug(`${NativeAudioWeb.LOG_TAG} Debug: ${message}`);\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n async deinitPlugin() {\n for (const [assetId] of NativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID) {\n await this.unload({ assetId });\n }\n this.stopCurrentTimeUpdates();\n }\n}\nNativeAudioWeb.LOG_TAG = '[NativeAudioWeb]';\nNativeAudioWeb.FILE_LOCATION = '';\nNativeAudioWeb.DEFAULT_FADE_DURATION_SEC = 1;\nNativeAudioWeb.CURRENT_TIME_UPDATE_INTERVAL = 100;\nNativeAudioWeb.AUDIO_PRELOAD_OPTIONS_MAP = new Map();\nNativeAudioWeb.AUDIO_DATA_MAP = new Map();\nNativeAudioWeb.AUDIO_ASSET_BY_ASSET_ID = new Map();\nNativeAudioWeb.AUDIO_CONTEXT_MAP = new Map();\nNativeAudioWeb.MEDIA_ELEMENT_SOURCE_MAP = new Map();\nNativeAudioWeb.GAIN_NODE_MAP = new Map();\nNativeAudioWeb.playOnceAssets = new Set();\nconst NativeAudio = new NativeAudioWeb();\nexport { NativeAudio };\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
|
|
3
|
+
extension AudioAsset {
|
|
4
|
+
|
|
5
|
+
func dispatchComplete() {
|
|
6
|
+
if dispatchedCompleteMap[assetId] == true {
|
|
7
|
+
return
|
|
8
|
+
}
|
|
9
|
+
owner?.notifyListeners("complete", data: ["assetId": assetId])
|
|
10
|
+
dispatchedCompleteMap[assetId] = true
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func cancelFade() {
|
|
14
|
+
fadeTask?.cancel()
|
|
15
|
+
fadeTask = nil
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fileprivate func performLocalFadeOutPauseOnMain(audio: AVAudioPlayer, beforePause: ((TimeInterval, TimeInterval) -> Void)?) {
|
|
19
|
+
let elapsed = audio.currentTime
|
|
20
|
+
let duration = audio.duration.isFinite ? audio.duration : 0
|
|
21
|
+
beforePause?(elapsed, duration)
|
|
22
|
+
audio.pause()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fileprivate func scheduleLocalFadeOutPauseOnMain(audio: AVAudioPlayer, beforePause: ((TimeInterval, TimeInterval) -> Void)?) {
|
|
26
|
+
DispatchQueue.main.async { [weak self] in
|
|
27
|
+
guard let self else { return }
|
|
28
|
+
self.performLocalFadeOutPauseOnMain(audio: audio, beforePause: beforePause)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Same main-thread pause and `beforePause(elapsed, duration)` as fade-out-to-pause when no fade runs (e.g. volume already zero).
|
|
33
|
+
internal func schedulePauseWithPositionRecording(audio: AVAudioPlayer, beforePause: ((TimeInterval, TimeInterval) -> Void)?) {
|
|
34
|
+
scheduleLocalFadeOutPauseOnMain(audio: audio, beforePause: beforePause)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fileprivate func scheduleLocalFadeOutStopOnMain(audio: AVAudioPlayer, beforeStop: ((TimeInterval, TimeInterval) -> Void)?) {
|
|
38
|
+
DispatchQueue.main.async { [weak self] in
|
|
39
|
+
guard let self else { return }
|
|
40
|
+
self.performLocalFadeOutStopOnMain(audio: audio, beforeStop: beforeStop)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fileprivate func performLocalFadeOutStopOnMain(audio: AVAudioPlayer, beforeStop: ((TimeInterval, TimeInterval) -> Void)?) {
|
|
45
|
+
let elapsed = audio.currentTime
|
|
46
|
+
let duration = audio.duration.isFinite ? audio.duration : 0
|
|
47
|
+
beforeStop?(elapsed, duration)
|
|
48
|
+
audio.stop()
|
|
49
|
+
dispatchComplete()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func fadeIn(audio: AVAudioPlayer, fadeInDuration: TimeInterval, targetVolume: Float) {
|
|
53
|
+
cancelFade()
|
|
54
|
+
let steps = Int(fadeInDuration / TimeInterval(fadeDelaySecs))
|
|
55
|
+
guard steps > 0 else { return }
|
|
56
|
+
let fadeStep = targetVolume / Float(steps)
|
|
57
|
+
var currentVolume: Float = 0
|
|
58
|
+
|
|
59
|
+
var task: DispatchWorkItem?
|
|
60
|
+
task = DispatchWorkItem { [weak self] in
|
|
61
|
+
guard let self else { return }
|
|
62
|
+
for _ in 0..<steps {
|
|
63
|
+
guard let task, !task.isCancelled, self.isPlaying(), audio.isPlaying else { return }
|
|
64
|
+
currentVolume += fadeStep
|
|
65
|
+
DispatchQueue.main.async {
|
|
66
|
+
audio.volume = min(max(currentVolume, 0), targetVolume)
|
|
67
|
+
}
|
|
68
|
+
Thread.sleep(forTimeInterval: TimeInterval(self.fadeDelaySecs))
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
fadeTask = task
|
|
72
|
+
if let task {
|
|
73
|
+
fadeQueue.async(execute: task)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// - Parameter beforeStop: Called on the main queue immediately before `stop()` when `toPause` is false,
|
|
78
|
+
/// so the plugin can refresh Now Playing (rate 0) at the final elapsed time.
|
|
79
|
+
/// - Parameter beforePause: Called on the main queue immediately before `pause()` when `toPause` is true,
|
|
80
|
+
/// so the plugin can persist `timeBeforePause` and update Now Playing at the actual stop position.
|
|
81
|
+
/// Kept last so call sites can use a trailing closure for fade-out-to-pause.
|
|
82
|
+
func fadeOut(
|
|
83
|
+
audio: AVAudioPlayer,
|
|
84
|
+
fadeOutDuration: TimeInterval,
|
|
85
|
+
toPause: Bool = false,
|
|
86
|
+
beforeStop: ((TimeInterval, TimeInterval) -> Void)? = nil,
|
|
87
|
+
beforePause: ((TimeInterval, TimeInterval) -> Void)? = nil
|
|
88
|
+
) {
|
|
89
|
+
cancelFade()
|
|
90
|
+
let steps = max(0, Int(fadeOutDuration / TimeInterval(fadeDelaySecs)))
|
|
91
|
+
guard steps > 0 else {
|
|
92
|
+
if toPause {
|
|
93
|
+
scheduleLocalFadeOutPauseOnMain(audio: audio, beforePause: beforePause)
|
|
94
|
+
} else {
|
|
95
|
+
scheduleLocalFadeOutStopOnMain(audio: audio, beforeStop: beforeStop)
|
|
96
|
+
}
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
var currentVolume = audio.volume
|
|
100
|
+
let fadeStep = currentVolume / Float(steps)
|
|
101
|
+
|
|
102
|
+
var task: DispatchWorkItem?
|
|
103
|
+
task = DispatchWorkItem { [weak self] in
|
|
104
|
+
guard let self else { return }
|
|
105
|
+
for _ in 0..<steps {
|
|
106
|
+
guard let task, !task.isCancelled else { return }
|
|
107
|
+
guard self.isPlaying(), audio.isPlaying else { break }
|
|
108
|
+
currentVolume -= fadeStep
|
|
109
|
+
DispatchQueue.main.async {
|
|
110
|
+
audio.volume = max(currentVolume, 0)
|
|
111
|
+
}
|
|
112
|
+
Thread.sleep(forTimeInterval: TimeInterval(self.fadeDelaySecs))
|
|
113
|
+
}
|
|
114
|
+
guard let task, !task.isCancelled else { return }
|
|
115
|
+
DispatchQueue.main.async { [weak self] in
|
|
116
|
+
guard let self else { return }
|
|
117
|
+
if toPause {
|
|
118
|
+
self.performLocalFadeOutPauseOnMain(audio: audio, beforePause: beforePause)
|
|
119
|
+
} else {
|
|
120
|
+
self.performLocalFadeOutStopOnMain(audio: audio, beforeStop: beforeStop)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
fadeTask = task
|
|
125
|
+
if let task {
|
|
126
|
+
fadeQueue.async(execute: task)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
func fadeTo(audio: AVAudioPlayer, fadeDuration: TimeInterval, targetVolume: Float) {
|
|
131
|
+
cancelFade()
|
|
132
|
+
let steps = Int(fadeDuration / TimeInterval(fadeDelaySecs))
|
|
133
|
+
guard steps > 0 else { return }
|
|
134
|
+
|
|
135
|
+
let minVolume = zeroVolume
|
|
136
|
+
var currentVolume = max(audio.volume, minVolume)
|
|
137
|
+
let safeTargetVolume = max(targetVolume, minVolume)
|
|
138
|
+
let ratio = pow(safeTargetVolume / currentVolume, 1.0 / Float(steps))
|
|
139
|
+
|
|
140
|
+
var task: DispatchWorkItem?
|
|
141
|
+
task = DispatchWorkItem { [weak self] in
|
|
142
|
+
guard let self else { return }
|
|
143
|
+
for _ in 0..<steps {
|
|
144
|
+
guard let task, !task.isCancelled, self.isPlaying(), audio.isPlaying else { return }
|
|
145
|
+
currentVolume *= ratio
|
|
146
|
+
DispatchQueue.main.async {
|
|
147
|
+
audio.volume = min(max(currentVolume, minVolume), self.maxVolume)
|
|
148
|
+
}
|
|
149
|
+
Thread.sleep(forTimeInterval: TimeInterval(self.fadeDelaySecs))
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
fadeTask = task
|
|
153
|
+
if let task {
|
|
154
|
+
fadeQueue.async(execute: task)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
@preconcurrency import AVFoundation
|
|
2
|
+
|
|
3
|
+
// swiftlint:disable:next type_body_length
|
|
4
|
+
public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
5
|
+
|
|
6
|
+
var channels: [AVAudioPlayer] = []
|
|
7
|
+
var playIndex: Int = 0
|
|
8
|
+
var assetId: String = ""
|
|
9
|
+
var initialVolume: Float = 1.0
|
|
10
|
+
let zeroVolume: Float = 0.001
|
|
11
|
+
let maxVolume: Float = 1.0
|
|
12
|
+
weak var owner: NativeAudio?
|
|
13
|
+
var onComplete: (() -> Void)?
|
|
14
|
+
|
|
15
|
+
let fadeDelaySecs: Float = 0.08
|
|
16
|
+
private var currentTimeTimer: Timer?
|
|
17
|
+
internal var fadeTimer: Timer?
|
|
18
|
+
var fadeTask: DispatchWorkItem?
|
|
19
|
+
let fadeQueue: DispatchQueue = DispatchQueue(label: "com.audioasset.fadeQueue")
|
|
20
|
+
var dispatchedCompleteMap: [String: Bool] = [:]
|
|
21
|
+
|
|
22
|
+
private var logger = Logger(logTag: "AudioAsset")
|
|
23
|
+
|
|
24
|
+
init(owner: NativeAudio, withAssetId assetId: String, withPath path: String, withChannels channels: Int?, withVolume volume: Float?) {
|
|
25
|
+
self.owner = owner
|
|
26
|
+
self.assetId = assetId
|
|
27
|
+
self.channels = []
|
|
28
|
+
self.initialVolume = min(max(volume ?? Constant.DefaultVolume, Constant.MinVolume), Constant.MaxVolume)
|
|
29
|
+
|
|
30
|
+
super.init()
|
|
31
|
+
|
|
32
|
+
guard let encodedPath = path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
|
|
33
|
+
logger.error("Failed to encode path: %@", String(describing: path))
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let pathUrl = URL(string: encodedPath) ?? URL(fileURLWithPath: encodedPath)
|
|
38
|
+
let channelCount = min(max(channels ?? 1, 1), Constant.MaxChannels)
|
|
39
|
+
|
|
40
|
+
let setupBlock = { [weak self] in
|
|
41
|
+
guard let self else { return }
|
|
42
|
+
for _ in 0..<channelCount {
|
|
43
|
+
do {
|
|
44
|
+
let player = try AVAudioPlayer(contentsOf: pathUrl)
|
|
45
|
+
player.delegate = self
|
|
46
|
+
player.enableRate = true
|
|
47
|
+
player.volume = self.initialVolume
|
|
48
|
+
player.rate = 1.0
|
|
49
|
+
player.prepareToPlay()
|
|
50
|
+
self.channels.append(player)
|
|
51
|
+
} catch {
|
|
52
|
+
self.logger.error("Error loading audio file: %@", error.localizedDescription)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if owner.isRunningTests {
|
|
58
|
+
setupBlock()
|
|
59
|
+
} else {
|
|
60
|
+
owner.executeOnAudioQueue(setupBlock)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Backward-compatible initializer signature
|
|
65
|
+
init(owner: NativeAudio, withAssetId assetId: String, withPath path: String, withChannels channels: Int?, withVolume volume: Float?, withFadeDelay _: Float?) {
|
|
66
|
+
self.owner = owner
|
|
67
|
+
self.assetId = assetId
|
|
68
|
+
self.channels = []
|
|
69
|
+
self.initialVolume = min(max(volume ?? Constant.DefaultVolume, Constant.MinVolume), Constant.MaxVolume)
|
|
70
|
+
super.init()
|
|
71
|
+
|
|
72
|
+
guard let encodedPath = path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
|
|
73
|
+
logger.error("Failed to encode path: %@", String(describing: path))
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let pathUrl = URL(string: encodedPath) ?? URL(fileURLWithPath: encodedPath)
|
|
78
|
+
let channelCount = min(max(channels ?? 1, 1), Constant.MaxChannels)
|
|
79
|
+
let setupBlock = { [weak self] in
|
|
80
|
+
guard let self else { return }
|
|
81
|
+
for _ in 0..<channelCount {
|
|
82
|
+
do {
|
|
83
|
+
let player = try AVAudioPlayer(contentsOf: pathUrl)
|
|
84
|
+
player.delegate = self
|
|
85
|
+
player.enableRate = true
|
|
86
|
+
player.volume = self.initialVolume
|
|
87
|
+
player.rate = 1.0
|
|
88
|
+
player.prepareToPlay()
|
|
89
|
+
self.channels.append(player)
|
|
90
|
+
} catch {
|
|
91
|
+
self.logger.error("Error loading audio file: %@", error.localizedDescription)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if owner.isRunningTests {
|
|
96
|
+
setupBlock()
|
|
97
|
+
} else {
|
|
98
|
+
owner.executeOnAudioQueue(setupBlock)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
deinit {
|
|
103
|
+
currentTimeTimer?.invalidate()
|
|
104
|
+
currentTimeTimer = nil
|
|
105
|
+
fadeTimer?.invalidate()
|
|
106
|
+
fadeTimer = nil
|
|
107
|
+
cancelFade()
|
|
108
|
+
for player in channels where player.isPlaying {
|
|
109
|
+
player.stop()
|
|
110
|
+
}
|
|
111
|
+
channels = []
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func getCurrentTime() -> TimeInterval {
|
|
115
|
+
var result: TimeInterval = 0
|
|
116
|
+
owner?.readOnAudioQueue {
|
|
117
|
+
guard !self.channels.isEmpty, self.playIndex < self.channels.count else { return }
|
|
118
|
+
result = self.channels[self.playIndex].currentTime
|
|
119
|
+
}
|
|
120
|
+
return result
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func setCurrentTime(time: TimeInterval, completion: (() -> Void)? = nil) {
|
|
124
|
+
guard let owner else {
|
|
125
|
+
completion?()
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
owner.executeOnAudioQueue { [weak self] in
|
|
129
|
+
guard let self else {
|
|
130
|
+
completion?()
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
guard !channels.isEmpty, playIndex < channels.count else {
|
|
134
|
+
completion?()
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
let player = channels[playIndex]
|
|
138
|
+
let validTime = min(max(time, 0), player.duration)
|
|
139
|
+
player.currentTime = validTime
|
|
140
|
+
completion?()
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
func getDuration() -> TimeInterval {
|
|
145
|
+
var result: TimeInterval = 0
|
|
146
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
147
|
+
guard let self else { return }
|
|
148
|
+
guard !channels.isEmpty, playIndex < channels.count else { return }
|
|
149
|
+
result = channels[playIndex].duration
|
|
150
|
+
}
|
|
151
|
+
return result
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
func play(time: TimeInterval, volume: Float? = nil) {
|
|
155
|
+
stopCurrentTimeUpdates()
|
|
156
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
157
|
+
guard let self else { return }
|
|
158
|
+
guard !channels.isEmpty else { return }
|
|
159
|
+
if playIndex >= channels.count {
|
|
160
|
+
playIndex = 0
|
|
161
|
+
}
|
|
162
|
+
owner?.activateSession()
|
|
163
|
+
cancelFade()
|
|
164
|
+
|
|
165
|
+
let player = channels[playIndex]
|
|
166
|
+
let validTime = min(max(time, 0), player.duration)
|
|
167
|
+
player.currentTime = validTime
|
|
168
|
+
player.numberOfLoops = 0
|
|
169
|
+
player.volume = volume ?? initialVolume
|
|
170
|
+
player.play()
|
|
171
|
+
|
|
172
|
+
playIndex = (playIndex + 1) % channels.count
|
|
173
|
+
startCurrentTimeUpdates()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Backward-compatible signature
|
|
178
|
+
func play(time: TimeInterval, delay: TimeInterval) {
|
|
179
|
+
let validDelay = max(delay, 0)
|
|
180
|
+
if validDelay > 0 {
|
|
181
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + validDelay) { [weak self] in
|
|
182
|
+
self?.play(time: time, volume: nil)
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
play(time: time, volume: nil)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
func playWithFade(time: TimeInterval) {
|
|
190
|
+
playWithFade(time: time, volume: nil, fadeInDuration: TimeInterval(Constant.DefaultFadeDuration))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
func playWithFade(time: TimeInterval, volume: Float?, fadeInDuration: TimeInterval) {
|
|
194
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
195
|
+
guard let self else { return }
|
|
196
|
+
guard !channels.isEmpty else { return }
|
|
197
|
+
if playIndex >= channels.count {
|
|
198
|
+
playIndex = 0
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let player = channels[playIndex]
|
|
202
|
+
player.currentTime = time
|
|
203
|
+
player.numberOfLoops = 0
|
|
204
|
+
player.volume = 0
|
|
205
|
+
player.play()
|
|
206
|
+
playIndex = (playIndex + 1) % channels.count
|
|
207
|
+
startCurrentTimeUpdates()
|
|
208
|
+
fadeIn(audio: player, fadeInDuration: fadeInDuration, targetVolume: volume ?? initialVolume)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func pause() {
|
|
213
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
214
|
+
guard let self else { return }
|
|
215
|
+
guard !channels.isEmpty, playIndex < channels.count else { return }
|
|
216
|
+
cancelFade()
|
|
217
|
+
channels[playIndex].pause()
|
|
218
|
+
stopCurrentTimeUpdates()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
func resume() {
|
|
223
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
224
|
+
guard let self else { return }
|
|
225
|
+
guard !channels.isEmpty, playIndex < channels.count else { return }
|
|
226
|
+
let player = channels[playIndex]
|
|
227
|
+
player.play()
|
|
228
|
+
startCurrentTimeUpdates()
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
func stop() {
|
|
233
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
234
|
+
guard let self else { return }
|
|
235
|
+
cancelFade()
|
|
236
|
+
stopCurrentTimeUpdates()
|
|
237
|
+
for player in channels {
|
|
238
|
+
if player.isPlaying {
|
|
239
|
+
player.stop()
|
|
240
|
+
}
|
|
241
|
+
player.currentTime = 0
|
|
242
|
+
player.numberOfLoops = 0
|
|
243
|
+
}
|
|
244
|
+
playIndex = 0
|
|
245
|
+
dispatchComplete()
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func stopWithFade() {
|
|
250
|
+
stopWithFade(fadeOutDuration: TimeInterval(Constant.DefaultFadeDuration), toPause: false)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
func stopWithFade(fadeOutDuration: TimeInterval, toPause: Bool = false) {
|
|
254
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
255
|
+
guard let self else { return }
|
|
256
|
+
guard !channels.isEmpty, playIndex < channels.count else {
|
|
257
|
+
if !toPause { stop() }
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
let player = channels[playIndex]
|
|
261
|
+
if player.isPlaying {
|
|
262
|
+
if toPause {
|
|
263
|
+
if player.volume > 0 {
|
|
264
|
+
fadeOut(audio: player, fadeOutDuration: fadeOutDuration, toPause: true, beforePause: { [weak self] elapsed, duration in
|
|
265
|
+
guard let self, let owner = self.owner else { return }
|
|
266
|
+
owner.recordPausePositionAfterFade(assetId: self.assetId, elapsedTime: elapsed, duration: duration)
|
|
267
|
+
})
|
|
268
|
+
} else {
|
|
269
|
+
cancelFade()
|
|
270
|
+
schedulePauseWithPositionRecording(audio: player) { [weak self] elapsed, duration in
|
|
271
|
+
guard let self, let owner = self.owner else { return }
|
|
272
|
+
owner.recordPausePositionAfterFade(assetId: self.assetId, elapsedTime: elapsed, duration: duration)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
} else if player.volume > 0 {
|
|
276
|
+
fadeOut(audio: player, fadeOutDuration: fadeOutDuration, toPause: false, beforeStop: { [weak self] elapsed, duration in
|
|
277
|
+
guard let self, let owner = self.owner else { return }
|
|
278
|
+
owner.recordStoppedPlaybackStateAfterFade(assetId: self.assetId, elapsedTime: elapsed, duration: duration)
|
|
279
|
+
})
|
|
280
|
+
} else {
|
|
281
|
+
let elapsed = player.currentTime
|
|
282
|
+
let duration = player.duration.isFinite ? player.duration : 0
|
|
283
|
+
stop()
|
|
284
|
+
owner?.recordStoppedPlaybackStateAfterFade(assetId: assetId, elapsedTime: elapsed, duration: duration)
|
|
285
|
+
}
|
|
286
|
+
} else if !toPause {
|
|
287
|
+
stop()
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
func loop() {
|
|
293
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
294
|
+
guard let self else { return }
|
|
295
|
+
cancelFade()
|
|
296
|
+
stop()
|
|
297
|
+
guard !channels.isEmpty, playIndex < channels.count else { return }
|
|
298
|
+
let player = channels[playIndex]
|
|
299
|
+
player.delegate = self
|
|
300
|
+
player.numberOfLoops = -1
|
|
301
|
+
player.play()
|
|
302
|
+
playIndex = (playIndex + 1) % channels.count
|
|
303
|
+
startCurrentTimeUpdates()
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
func unload() {
|
|
308
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
309
|
+
guard let self else { return }
|
|
310
|
+
cancelFade()
|
|
311
|
+
stop()
|
|
312
|
+
stopCurrentTimeUpdates()
|
|
313
|
+
channels = []
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
func setVolume(volume: NSNumber) {
|
|
318
|
+
setVolume(volume: volume, fadeDuration: 0)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
func setVolume(volume: NSNumber, fadeDuration: Double) {
|
|
322
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
323
|
+
guard let self else { return }
|
|
324
|
+
cancelFade()
|
|
325
|
+
let validVolume = min(max(volume.floatValue, Constant.MinVolume), Constant.MaxVolume)
|
|
326
|
+
for player in channels {
|
|
327
|
+
if player.isPlaying && fadeDuration > 0 {
|
|
328
|
+
fadeTo(audio: player, fadeDuration: fadeDuration, targetVolume: validVolume)
|
|
329
|
+
} else {
|
|
330
|
+
player.volume = validVolume
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
func setRate(rate: NSNumber) {
|
|
337
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
338
|
+
guard let self else { return }
|
|
339
|
+
let validRate = min(max(rate.floatValue, Constant.MinRate), Constant.MaxRate)
|
|
340
|
+
for player in channels {
|
|
341
|
+
player.rate = validRate
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
func isPlaying() -> Bool {
|
|
347
|
+
var result = false
|
|
348
|
+
owner?.readOnAudioQueue {
|
|
349
|
+
result = self.channels.contains(where: { $0.isPlaying })
|
|
350
|
+
}
|
|
351
|
+
return result
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
internal func shouldStopCurrentTimeUpdatesWhenNotPlaying() -> Bool {
|
|
355
|
+
true
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
|
359
|
+
owner?.executeOnAudioQueue { [weak self] in
|
|
360
|
+
guard let self else { return }
|
|
361
|
+
dispatchComplete()
|
|
362
|
+
onComplete?()
|
|
363
|
+
owner?.audioPlayerDidFinishPlaying(player, successfully: flag)
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
func playerDecodeError(player: AVAudioPlayer, error: NSError?) {
|
|
368
|
+
if let error {
|
|
369
|
+
logger.error("AudioAsset decode error: %@", error.localizedDescription)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
internal func startCurrentTimeUpdates() {
|
|
374
|
+
stopCurrentTimeUpdates()
|
|
375
|
+
dispatchedCompleteMap[assetId] = false
|
|
376
|
+
DispatchQueue.main.async { [weak self] in
|
|
377
|
+
guard let self else { return }
|
|
378
|
+
let timer = Timer(timeInterval: 0.1, repeats: true) { [weak self] _ in
|
|
379
|
+
guard let self, let owner else {
|
|
380
|
+
self?.stopCurrentTimeUpdates()
|
|
381
|
+
return
|
|
382
|
+
}
|
|
383
|
+
if self.isPlaying() {
|
|
384
|
+
owner.notifyCurrentTime(self)
|
|
385
|
+
} else if self.shouldStopCurrentTimeUpdatesWhenNotPlaying() {
|
|
386
|
+
self.stopCurrentTimeUpdates()
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
self.currentTimeTimer = timer
|
|
390
|
+
RunLoop.current.add(timer, forMode: .common)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
internal func stopCurrentTimeUpdates() {
|
|
395
|
+
DispatchQueue.main.async { [weak self] in
|
|
396
|
+
guard let self else { return }
|
|
397
|
+
logger.debug("Stop current time updates")
|
|
398
|
+
self.currentTimeTimer?.invalidate()
|
|
399
|
+
self.currentTimeTimer = nil
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
}
|