UNPKG

audio-hooks

Version:

A React hooks library for managing audio playback with advanced controls and features.

511 lines 18.6 kB
// src/react/fns/index.ts function _array_like_to_array(arr, len) { if (len == null || len > arr.length) len = arr.length; for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i]; return arr2; } function _array_with_holes(arr) { if (Array.isArray(arr)) return arr; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _async_to_generator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _iterable_to_array_limit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){ _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally{ try { if (!_n && _i["return"] != null) _i["return"](); } finally{ if (_d) throw _e; } } return _arr; } function _non_iterable_rest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _sliced_to_array(arr, i) { return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest(); } function _unsupported_iterable_to_array(o, minLen) { if (!o) return; if (typeof o === "string") return _array_like_to_array(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen); } function _ts_generator(thisArg, body) { var f, y, t, g, _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function(v) { return step([ n, v ]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while(_)try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [ op[0] & 2, t.value ]; switch(op[0]){ case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [ 0 ]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [ 6, e ]; y = 0; } finally{ f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var randomPlayingIndex = function(index, length) { var random = Math.floor(Math.random() * (length - index + 1)) + index; if (random === index) { return randomPlayingIndex(index, length); } return random; }; // src/react/index.ts import { useState, useRef, useCallback, useMemo } from "react"; import { useGain, useAudioContext } from "web-audio-hooks/react"; import { AudioProvider } from "web-audio-hooks/react"; var MODE = { Shuffle: "Shuffle", SingleOnce: "SingleOnce", SingleLoop: "SingleLoop", SequentialOnce: "SequentialOnce", SequentialLoop: "SequentialLoop" }; var MODES = [ "Shuffle", "SingleOnce", "SingleLoop", "SequentialOnce", "SequentialLoop" ]; var useAudioList = function(urls) { var onEnded = (arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}).onEnded; var context = useAudioContext(); var audioRef = useRef(null); var audioPoolRef = useRef([]); var sourceRef = useRef(null); var gainNode = useGain(0.5); var _useState = _sliced_to_array(useState(MODE.SequentialOnce), 2), playMode = _useState[0], setPlayMode = _useState[1]; var _useState1 = _sliced_to_array(useState(-1), 2), playingIndex = _useState1[0], setPlayingIndex = _useState1[1]; var _useState2 = _sliced_to_array(useState(urls), 2), audioUrls = _useState2[0], setAudioList = _useState2[1]; var _useState3 = _sliced_to_array(useState(false), 2), playing = _useState3[0], setPlaying = _useState3[1]; var _useState4 = _sliced_to_array(useState(0), 2), duration = _useState4[0], setDuration = _useState4[1]; var _useState5 = _sliced_to_array(useState(0.5), 2), volume = _useState5[0], setVolume = _useState5[1]; var _useState6 = _sliced_to_array(useState(0), 2), currentTime = _useState6[0], setCurrentTime = _useState6[1]; var _useState7 = _sliced_to_array(useState(1), 2), playbackRate = _useState7[0], setPlaybackRate = _useState7[1]; var state = useMemo(function() { return { audios: audioUrls, volume: volume, playing: playing, duration: duration, currentTime: currentTime, playbackRate: playbackRate, playMode: playMode, playingIndex: playingIndex }; }, [ audioUrls, volume, playing, duration, currentTime, playbackRate, playMode, playingIndex ]); var getAudio = function(url) { var available = audioPoolRef.current.find(function(a) { return a.ended; }); if (available && url) { available.src = url; return available; } return new Audio(url); }; var initAudio = useCallback(function(url) { if (!context || !url || !gainNode) return; if (sourceRef.current) { var _audioRef_current; (_audioRef_current = audioRef.current) === null || _audioRef_current === void 0 ? void 0 : _audioRef_current.pause(); sourceRef.current.disconnect(); } audioRef.current = getAudio(url); var audio = audioRef.current; audio.playbackRate = playbackRate; audio.crossOrigin = "anonymous"; sourceRef.current = context.createMediaElementSource(audio); sourceRef.current.connect(gainNode); gainNode.connect(context.destination); var updateTime = function() { setCurrentTime(audio.currentTime); }; var updateMetadata = function() { setDuration(audio.duration); }; var onAudioEnded = function() { onEnded === null || onEnded === void 0 ? void 0 : onEnded(); if (playMode === "SingleLoop") { audio.currentTime = 0; audio.play(); } else if (playMode === "SingleOnce") { var _audioRef_current; setPlaying(false); (_audioRef_current = audioRef.current) === null || _audioRef_current === void 0 ? void 0 : _audioRef_current.pause(); } else { var nextIndex = getNextIndex(true); if (nextIndex !== -1) { playTrack(nextIndex); } else if (playMode === "SequentialOnce") { var _audioRef_current1; setPlaying(false); (_audioRef_current1 = audioRef.current) === null || _audioRef_current1 === void 0 ? void 0 : _audioRef_current1.pause(); } } audioPoolRef.current.push(audio); }; var onRateChange = function() { controls.setPlaybackRate(audio.playbackRate); }; audio.addEventListener("ratechange", onRateChange); audio.addEventListener("timeupdate", updateTime); audio.addEventListener("loadedmetadata", updateMetadata); audio.addEventListener("ended", onAudioEnded); return function() { var _sourceRef_current; audio.pause(); audioPoolRef.current.forEach(function(a) { return a.pause(); }); audioPoolRef.current = []; (_sourceRef_current = sourceRef.current) === null || _sourceRef_current === void 0 ? void 0 : _sourceRef_current.disconnect(); audio.removeEventListener("ratechange", onRateChange); audio.removeEventListener("timeupdate", updateTime); audio.removeEventListener("loadedmetadata", updateMetadata); audio.removeEventListener("ended", onAudioEnded); }; }, [ context, gainNode, playbackRate, playMode ]); var playTrack = useCallback(/*#__PURE__*/ function() { var _ref = _async_to_generator(function(index) { var _audioRef_current, _audioRef_current1, err; return _ts_generator(this, function(_state) { switch(_state.label){ case 0: if (index < 0 || index >= audioUrls.length) { console.warn("Invalid index: ".concat(index)); if (playMode === "SequentialOnce") { ; setPlaying(false); (_audioRef_current = audioRef.current) === null || _audioRef_current === void 0 ? void 0 : _audioRef_current.pause(); } return [ 2 ]; } setPlayingIndex(index); setPlaying(false); initAudio(audioUrls[index]); _state.label = 1; case 1: _state.trys.push([ 1, 5, , 6 ]); if (!((context === null || context === void 0 ? void 0 : context.state) === "suspended")) return [ 3, 3 ]; return [ 4, context.resume() ]; case 2: _state.sent(); _state.label = 3; case 3: return [ 4, (_audioRef_current1 = audioRef.current) === null || _audioRef_current1 === void 0 ? void 0 : _audioRef_current1.play() ]; case 4: _state.sent(); setPlaying(true); return [ 3, 6 ]; case 5: err = _state.sent(); console.error("播放失败:", err); return [ 3, 6 ]; case 6: return [ 2 ]; } }); }); return function(index) { return _ref.apply(this, arguments); }; }(), [ context, audioUrls, initAudio, playMode ]); var getPrevIndex = function() { if (audioUrls.length === 0) return -1; if (playMode === "Shuffle") { return randomPlayingIndex(playingIndex, audioUrls.length); } if (playingIndex <= 0) { return audioUrls.length - 1; } return playingIndex - 1; }; var getNextIndex = function() { var isFromEndedEvent = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false; if (audioUrls.length === 0) return -1; if (audioUrls.length > 0 && playingIndex === -1) { return 0; } if (playMode === "Shuffle") { return randomPlayingIndex(playingIndex, audioUrls.length); } if (playMode === "SingleOnce") { return -1; } if (playingIndex >= audioUrls.length - 1) { if (playMode === "SequentialLoop") { return 0; } else if (playMode === "SequentialOnce" && isFromEndedEvent) { return -1; } } return playingIndex + 1; }; var playPrevTrack = function() { playTrack(getPrevIndex()); }; var playNextTrack = function() { playTrack(getNextIndex()); }; var controls = { play: /*#__PURE__*/ _async_to_generator(function() { return _ts_generator(this, function(_state) { switch(_state.label){ case 0: if (!audioRef.current) return [ 3, 2 ]; return [ 4, audioRef.current.play() ]; case 1: _state.sent(); setPlaying(true); return [ 3, 3 ]; case 2: if (audioUrls.length > 0) { playTrack(0); } _state.label = 3; case 3: return [ 2 ]; } }); }), pause: function() { var _audioRef_current; (_audioRef_current = audioRef.current) === null || _audioRef_current === void 0 ? void 0 : _audioRef_current.pause(); setPlaying(false); }, togglePlay: function() { playing ? controls.pause() : controls.play(); }, playTrack: playTrack, setAudioList: setAudioList, prev: playPrevTrack, next: playNextTrack, seek: function(time) { if (audioRef.current) { audioRef.current.currentTime = time; } }, fastForward: function() { var ms = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 5e3; if (audioRef.current) { var newTime = Math.min(audioRef.current.currentTime + ms / 1e3, audioRef.current.duration || 0); audioRef.current.currentTime = newTime; } }, rewind: function() { var ms = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 5e3; if (audioRef.current) { var newTime = Math.max(audioRef.current.currentTime - ms / 1e3, 0); audioRef.current.currentTime = newTime; } }, setVolume: function(volume2) { setVolume(volume2); if (gainNode) { gainNode.gain.setValueAtTime(volume2, (context === null || context === void 0 ? void 0 : context.currentTime) || 0); } }, nextPlayMode: function(_mode) { var currentIndex = MODES.indexOf(playMode); var nextIndex = (currentIndex + 1) % MODES.length; var mode = _mode || MODES[nextIndex]; setPlayMode(mode); }, setPlaybackRate: function(rate) { rate = Math.min(Math.max(rate, 0.5), 3); setPlaybackRate(rate); if (audioRef.current) { audioRef.current.playbackRate = rate; } }, switchAudio: setPlayingIndex }; return { state: state, controls: controls }; }; export { AudioProvider, MODE, useAudioList }; //# sourceMappingURL=index.js.map