UNPKG

react-use-audio-player

Version:

React hook for building custom audio playback controls

1 lines 25.2 kB
{"version":3,"sources":["../src/index.ts","../src/AudioPlayerProvider.tsx","../src/useAudioPlayer.ts","../src/howlCache.ts","../src/HowlStore.ts"],"sourcesContent":["export * from \"./AudioPlayerProvider\"\nexport * from \"./useAudioPlayer\"\nexport * from \"./types\"\n","import { type ComponentProps, createContext, useContext } from \"react\"\nimport { type AudioPlayer, useAudioPlayer } from \"./useAudioPlayer\"\n\nexport const context = createContext<AudioPlayer | null>(null)\n\nexport const useAudioPlayerContext = () => {\n const ctx = useContext(context)\n if (ctx === null) {\n throw new Error(\n \"useAudioPlayerContext must be used within an AudioPlayerProvider\"\n )\n }\n\n return ctx\n}\n\ntype Props = Omit<ComponentProps<typeof context.Provider>, \"value\">\n\nexport function AudioPlayerProvider({ children }: Props) {\n const player = useAudioPlayer()\n\n return <context.Provider value={player}>{children}</context.Provider>\n}\n","import { useCallback, useEffect, useRef, useSyncExternalStore } from \"react\"\nimport type { Howl } from \"howler\"\n\nimport { type Snapshot, HowlStore, defaultState } from \"./HowlStore\"\nimport type { AudioControls, AudioLoadOptions } from \"./types\"\n\nexport type AudioPlayer = AudioControls &\n Snapshot & {\n /** A reference to the underlying Howl object.\n * Use as an escape hatch for behavior not provided by useAudioPlayer. Please refer to Howler [documentation](https://github.com/goldfire/howler.js#documentation)\n * Manipulating the audio directly through the Howl may cause state to desynchronize\n * */\n player: Howl | null\n src: string | null\n /** A way to explicitly load an audio resource */\n load: (...args: [string, AudioLoadOptions | undefined]) => void\n /** Removes event listeners, resets state and unloads the internal Howl object */\n cleanup: () => void\n }\n\nexport function useAudioPlayer(\n src: string,\n options?: AudioLoadOptions\n): Omit<AudioPlayer, \"load\">\nexport function useAudioPlayer(): AudioPlayer\n\n/**\n * @param {string} src - The src path of the audio resource. Changing this will cause a new sound to immediately load\n * @param {AudioLoadOptions} options - Options for the loaded audio including initial properties and configuration. These can later be changed through the API.\n * @return {AudioPlayer} The audio player instance with methods for controlling playback and state.\n */\nexport function useAudioPlayer(src?: string, options?: AudioLoadOptions) {\n const audioRef = useRef(new HowlStore())\n\n // when the src param is used, load a new sound whenever the value changes\n if (src && audioRef.current && src !== audioRef.current.src) {\n audioRef.current.load({\n src,\n format: options?.format,\n html5: options?.html5,\n autoplay: options?.autoplay,\n loop: options?.loop,\n volume: options?.initialVolume,\n mute: options?.initialMute,\n rate: options?.initialRate,\n // event callbacks\n ...options\n })\n }\n\n // need to bind functions back to the howl since they will be called from the context of React\n const state = useSyncExternalStore(\n audioRef.current.subscribe.bind(audioRef.current),\n audioRef.current.getSnapshot.bind(audioRef.current),\n () => defaultState\n )\n\n useEffect(() => {\n // load the sound on mount if the src param is being used\n // this is required for StrictMode when React may remount the hook\n if (src && audioRef.current.src === null) {\n audioRef.current.load({\n src,\n format: options?.format,\n html5: options?.html5,\n autoplay: options?.autoplay,\n loop: options?.loop,\n volume: options?.initialVolume,\n mute: options?.initialMute,\n rate: options?.initialRate,\n // event callbacks\n ...options\n })\n }\n\n // cleans up the sound when hook unmounts\n return () => {\n if (audioRef.current) {\n audioRef.current.destroy()\n }\n }\n }, [])\n\n const load: AudioPlayer[\"load\"] = useCallback((src, options) => {\n audioRef.current.load({\n src,\n format: options?.format,\n html5: options?.html5,\n autoplay: options?.autoplay,\n loop: options?.loop,\n volume: options?.initialVolume,\n mute: options?.initialMute,\n rate: options?.initialRate,\n // event callbacks\n ...options\n })\n }, [])\n\n return {\n ...state,\n player: audioRef.current.howl,\n src: audioRef.current.src,\n load: src ? undefined : load,\n // AudioControls interface\n play: audioRef.current.play.bind(audioRef.current),\n pause: audioRef.current.pause.bind(audioRef.current),\n togglePlayPause: audioRef.current.togglePlayPause.bind(\n audioRef.current\n ),\n stop: audioRef.current.stop.bind(audioRef.current),\n setVolume: audioRef.current.setVolume.bind(audioRef.current),\n fade: audioRef.current.fade.bind(audioRef.current),\n mute: audioRef.current.mute.bind(audioRef.current),\n unmute: audioRef.current.unmute.bind(audioRef.current),\n toggleMute: audioRef.current.toggleMute.bind(audioRef.current),\n setRate: audioRef.current.setRate.bind(audioRef.current),\n seek: audioRef.current.seek.bind(audioRef.current),\n loopOn: audioRef.current.loopOn.bind(audioRef.current),\n loopOff: audioRef.current.loopOff.bind(audioRef.current),\n toggleLoop: audioRef.current.toggleLoop.bind(audioRef.current),\n getPosition: audioRef.current.getPosition.bind(audioRef.current),\n cleanup: audioRef.current.destroy.bind(audioRef.current)\n }\n}\n","import { Howl, type HowlOptions as BaseHowlOptions } from \"howler\"\n\nexport type HowlOptions = BaseHowlOptions & {\n src: string // override src property to only be a single string\n}\n\n/**\n * A cache that tracks all the instances of AudioSources created by the library\n * An instance is cached based on the src attribute it was created with\n *\n * This prevents duplicate instances of audio being created in certain edge cases\n * React StrictMode being one such scenario\n */\nclass HowlCache {\n private _cache: Map<string, Howl> = new Map()\n\n public create(options: HowlOptions): Howl {\n const key = options.src\n if (this._cache.has(key)) {\n return this._cache.get(key)!\n }\n\n const howl = new Howl(options)\n this._cache.set(key, howl)\n return howl\n }\n\n public set(key: string, howl: Howl) {\n this._cache.set(key, howl)\n }\n\n public get(key: string) {\n return this._cache.get(key)\n }\n\n public clear(key: string) {\n this._cache.delete(key)\n }\n\n public destroy(key: string) {\n const howl = this.get(key)\n if (howl) {\n howl.unload()\n this.clear(key)\n }\n }\n\n public reset() {\n this._cache.values().forEach((audio) => audio.unload())\n this._cache.clear()\n }\n}\n\nconst howlCache = new HowlCache()\n\nexport default howlCache\n","import type { Howl } from \"howler\"\nimport howlCache from \"./howlCache\"\nimport type { AudioControls } from \"./types\"\n\ntype Subscriber = () => void\n\ntype CreateOptions = Parameters<typeof howlCache.create>[0]\n\n/**\n * The state (snapshot) managed by the store\n * Mutations to the state will be broadcast to all subscribers.\n */\nexport type Snapshot = {\n /** Indicates whether the audio is in an unloaded state. */\n readonly isUnloaded: boolean\n /** Indicates whether the audio is currently loading. */\n readonly isLoading: boolean\n /** Indicates whether the audio is loaded and ready to play. */\n readonly isReady: boolean\n /** Represents the total duration of the audio in seconds. 0 until a sound is loaded */\n readonly duration: number\n /** The playback rate of the audio. A value of 1 indicates normal playback speed. */\n readonly rate: number\n /** The volume level of the audio, typically between 0 (muted) and 1 (full volume). */\n readonly volume: number\n /** An error message, if an issue occurred with the audio. */\n readonly error?: string\n /** Indicates whether the audio is set to loop after reaching its end. */\n readonly isLooping: boolean\n /** Indicates whether the audio is currently paused. */\n readonly isPaused: boolean\n /** Indicates whether the audio is currently stopped. */\n readonly isStopped: boolean\n /** Indicates whether the audio is currently playing. */\n readonly isPlaying: boolean\n /** Indicates whether the audio is currently muted. */\n readonly isMuted: boolean\n}\n\nexport const defaultState: Snapshot = {\n isUnloaded: true,\n isLoading: false,\n isReady: false,\n isLooping: false,\n isPlaying: false,\n isStopped: false,\n isPaused: false,\n duration: 0,\n rate: 1,\n volume: 1,\n isMuted: false,\n error: undefined\n}\n\nexport class HowlStore implements AudioControls {\n public howl: Howl | null\n public src: string | null\n\n private subscriptions: Set<Subscriber>\n private snapshot: Snapshot\n\n /**\n * Merges changes to the AudioSnapshot with the instnace variable and invokes all subscriber callbacks\n */\n private updateSnapshot(update: Partial<Snapshot>) {\n this.snapshot = {\n ...this.snapshot,\n ...update\n }\n\n this.subscriptions.forEach((cb) => cb())\n }\n\n /**\n * Initiates a snapshot update from a Howl instance\n */\n private updateSnapshotFromHowlState(howl: Howl) {\n this.updateSnapshot({\n ...this.getSnapshotFromHowl(howl)\n })\n }\n\n /**\n * Initializes the store with a new instance of a Howl\n * - creates a new Howl instance\n * - updates the AudioSnapshot instance\n * - sets up Howl event listeners to synchronize AudioSnapshot\n */\n private initHowl(options: CreateOptions) {\n const newHowl = howlCache.create(options)\n this.src = options.src\n this.howl = newHowl\n\n this.updateSnapshot({\n ...this.getSnapshotFromHowl(newHowl),\n // reset error on creation of new Howl\n error: undefined\n })\n\n /*\n Howler places the Howl in a \"loading\" state when HTML5 audio is seeked.\n This is likely done in case the user agent needs to buffer more of the resource.\n However, it may be a bug in Howler that the load event is not emitted following this state change.\n This leaves this hook hanging in an \"isLoading\" state.\n As a temporary (hopefully) workaround we can hijack the HTML5 Audio element from Howler\n and trigger a state update once the Audio element has buffered.\n */\n if (newHowl._html5 && newHowl._sounds[0]?._node) {\n const htmlAudioNode = newHowl._sounds[0]?._node\n htmlAudioNode.addEventListener(\"canplaythrough\", () => {\n this.updateSnapshotFromHowlState(newHowl)\n })\n }\n\n // Howl event listeners and state mutations\n newHowl.on(\"load\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"play\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"end\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"pause\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"stop\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"mute\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"volume\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"rate\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"seek\", () => this.updateSnapshotFromHowlState(newHowl))\n newHowl.on(\"fade\", () => this.updateSnapshotFromHowlState(newHowl))\n\n newHowl.on(\"loaderror\", (_: number, errorCode: unknown) => {\n console.error(`Howl load error: ${errorCode}`)\n this.updateSnapshotFromHowlState(newHowl)\n this.updateSnapshot({\n error: \"Failed to load audio source\"\n })\n })\n\n newHowl.on(\"playerror\", (_: number, errorCode: unknown) => {\n console.error(`Howl playback error: ${errorCode}`)\n this.updateSnapshotFromHowlState(newHowl)\n this.updateSnapshot({\n error: \"Failed to play audio source\"\n })\n })\n }\n\n private getSnapshotFromHowl(howl: Howl): Snapshot {\n if (howl.state() === \"unloaded\") {\n return defaultState\n }\n\n const howlState = howl.state()\n const isPlaying = howl.playing()\n const muteReturn = howl.mute()\n return {\n isUnloaded: howlState === \"unloaded\",\n isLoading: howlState === \"loading\",\n isReady: howlState === \"loaded\",\n isLooping: howl.loop(),\n isPlaying,\n isStopped: !isPlaying && howl.seek() === 0,\n isPaused: !isPlaying && howl.seek() > 0,\n duration: howl.duration(),\n rate: howl.rate(),\n volume: howl.volume(),\n // the Howl#mute method sometimes returns the Howl (i.e. this) instead of the boolean\n isMuted: typeof muteReturn === \"object\" ? false : muteReturn\n }\n }\n\n constructor(options?: CreateOptions) {\n this.howl = null\n this.src = null\n\n this.subscriptions = new Set()\n this.snapshot = defaultState\n\n if (options !== undefined) {\n this.initHowl(options)\n }\n }\n\n public load(options: CreateOptions) {\n if (this.howl !== null) {\n this.destroy()\n }\n\n this.initHowl(options)\n }\n\n public destroy() {\n if (this.src && this.howl) {\n // guarantees that event listeners can no longer be called\n this.howl.off(\"load\")\n this.howl.off(\"play\")\n this.howl.off(\"end\")\n this.howl.off(\"pause\")\n this.howl.off(\"stop\")\n this.howl.off(\"mute\")\n this.howl.off(\"volume\")\n this.howl.off(\"rate\")\n this.howl.off(\"seek\")\n this.howl.off(\"fade\")\n this.howl.off(\"loaderror\")\n this.howl.off(\"playerror\")\n\n howlCache.destroy(this.src)\n\n this.src = null\n this.howl = null\n }\n }\n\n public subscribe(cb: Subscriber) {\n this.subscriptions.add(cb)\n return () => this.subscriptions.delete(cb)\n }\n\n public getSnapshot() {\n return this.snapshot\n }\n\n public play() {\n if (this.howl) {\n // prevents the Howl from spinning up a new \"Sound\" from the loaded audio resource\n if (this.howl.playing()) {\n return\n }\n\n this.howl.play()\n }\n }\n\n public pause() {\n if (this.howl) {\n this.howl.pause()\n }\n }\n\n public togglePlayPause() {\n if (this.snapshot.isPlaying) {\n this.pause()\n } else {\n this.play()\n }\n }\n\n public stop() {\n if (this.howl) {\n this.howl.stop()\n }\n }\n\n public setVolume(vol: number) {\n if (this.howl) {\n this.howl.volume(vol)\n }\n }\n\n public setRate(rate: number) {\n if (this.howl) {\n this.howl.rate(rate)\n }\n }\n\n public loopOn() {\n if (this.howl) {\n this.howl.loop(true)\n // there is no loop even to listen for on Howl so calling the sync operation manually\n this.updateSnapshotFromHowlState(this.howl)\n }\n }\n\n public loopOff() {\n if (this.howl) {\n this.howl.loop(false)\n // there is no loop even to listen for on Howl so calling the sync operation manually\n this.updateSnapshotFromHowlState(this.howl)\n }\n }\n\n public toggleLoop() {\n if (this.snapshot.isLooping) {\n this.loopOff()\n } else {\n this.loopOn()\n }\n }\n\n public mute() {\n if (this.howl) {\n this.howl.mute(true)\n }\n }\n\n public unmute() {\n if (this.howl) {\n this.howl.mute(false)\n }\n }\n\n public toggleMute() {\n if (this.snapshot.isMuted) {\n this.unmute()\n } else {\n this.mute()\n }\n }\n\n public seek(seconds: number) {\n // if audio resource is being streamed then it's duration will be Infinity\n if (this.howl && this.snapshot.duration !== Infinity) {\n this.howl.seek(seconds)\n }\n }\n\n public getPosition(): number {\n if (this.howl) {\n return this.howl.seek()\n }\n\n return 0\n }\n\n public fade(startVolume: number, endVolume: number, durationMs: number) {\n if (this.howl) {\n this.howl.fade(startVolume, endVolume, durationMs)\n }\n }\n}\n"],"mappings":"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","names":["index_exports","__export","AudioPlayerProvider","context","useAudioPlayer","useAudioPlayerContext","__toCommonJS","import_react","import_react","import_howler","HowlCache","options","key","howl","audio","howlCache","howlCache_default","defaultState","HowlStore","update","__spreadValues","cb","howl","options","_a","_b","newHowl","howlCache_default","__spreadProps","_","errorCode","howlState","isPlaying","muteReturn","vol","rate","seconds","startVolume","endVolume","durationMs","useAudioPlayer","src","options","audioRef","HowlStore","__spreadValues","state","defaultState","load","__spreadProps","import_jsx_runtime","context","useAudioPlayerContext","ctx","AudioPlayerProvider","children","player","useAudioPlayer"]}