react-native-audio-api
Version:
react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification
207 lines (206 loc) • 6.69 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useAudioSourceLoader = useAudioSourceLoader;
var _react = require("react");
var _reactNative = require("react-native");
var _AudioFileUtils = require("../core/AudioFileUtils");
var _errors = require("../errors");
var _specs = require("../specs");
var _utils = require("../utils");
var _remoteHttpSource = require("../utils/remoteHttpSource");
var _AudioFileSourceNode = require("./AudioFileSourceNode");
var _utils2 = require("./utils");
function useAudioSourceLoader({
context,
path,
source,
preloadMode,
forceDownload,
loop,
playbackRate,
preservesPitch,
autoPlay,
effectiveVolumeRef,
onLoadStart,
onLoad,
onError,
onEnded,
onAutoPlay
}) {
const [ready, setReady] = (0, _react.useState)(false);
const [duration, setDuration] = (0, _react.useState)(0);
const fileSourceRef = (0, _react.useRef)(null);
const sourceRef = (0, _react.useRef)(null);
const isFetchingCancelled = (0, _react.useRef)(false);
const fullDataFetched = (0, _react.useRef)(false);
const onLoadStartRef = (0, _react.useRef)(onLoadStart);
onLoadStartRef.current = onLoadStart;
const onLoadRef = (0, _react.useRef)(onLoad);
onLoadRef.current = onLoad;
const onErrorRef = (0, _react.useRef)(onError);
onErrorRef.current = onError;
const onEndedRef = (0, _react.useRef)(onEnded);
onEndedRef.current = onEnded;
const onAutoPlayRef = (0, _react.useRef)(onAutoPlay);
onAutoPlayRef.current = onAutoPlay;
const playbackRateRef = (0, _react.useRef)(playbackRate);
playbackRateRef.current = playbackRate;
const preservesPitchRef = (0, _react.useRef)(preservesPitch);
preservesPitchRef.current = preservesPitch;
const disposeSource = (0, _react.useCallback)(() => {
fileSourceRef.current?.stopPositionTracking();
fileSourceRef.current?.dispose();
sourceRef.current = null;
fullDataFetched.current = false;
}, []);
const spawnFileSource = (0, _react.useCallback)(() => {
const nextSource = sourceRef.current;
if (!context || !nextSource) {
return false;
}
fileSourceRef.current?.dispose();
const initialVolume = effectiveVolumeRef.current;
const initialPlaybackRate = playbackRateRef.current;
const initialPreservesPitch = preservesPitchRef.current;
const node = context.context.createFileSource({
source: nextSource,
headers: (0, _utils2.getSourceHeaders)(source),
loop,
volume: initialVolume,
playbackRate: initialPlaybackRate,
preservesPitch: initialPreservesPitch
});
if (!node) {
onErrorRef.current(new _errors.NotSupportedError('This file format requires FFmpeg build'));
return false;
}
const fileSource = new _AudioFileSourceNode.AudioFileSourceNode(context, node);
const {
duration: nextDuration
} = fileSource.attach({
loop,
onEnded: () => {
onEndedRef.current(nextDuration);
spawnFileSource();
}
});
fileSource.setVolume(initialVolume);
fileSource.setPlaybackRate(initialPlaybackRate);
fileSource.setPreservesPitch(initialPreservesPitch);
fileSourceRef.current = fileSource;
setDuration(nextDuration);
onLoadRef.current();
if (autoPlay) {
onAutoPlayRef.current();
}
return true;
}, [autoPlay, context, effectiveVolumeRef, loop, source]);
const loadPlaybackSource = (0, _react.useCallback)(async () => {
if (!path) {
return false;
}
isFetchingCancelled.current = false;
setReady(false);
onLoadStartRef.current();
const headers = (0, _utils2.getSourceHeaders)(source);
try {
if ((0, _remoteHttpSource.isRemoteHttpUrl)(path)) {
sourceRef.current = await (0, _remoteHttpSource.loadRemoteHttpSource)(path, headers, forceDownload);
} else if (_reactNative.Platform.OS === 'android' && !__DEV__ && !path.startsWith('file://')) {
const base64Payload = await _specs.NativeAudioAPIModule.readAndroidReleaseAssetBytesAsBase64(path);
sourceRef.current = (0, _utils.base64ToArrayBuffer)(base64Payload);
} else if (path.startsWith('file://')) {
sourceRef.current = path.replace('file://', '');
} else {
sourceRef.current = path;
}
fullDataFetched.current = true;
if (!isFetchingCancelled.current && spawnFileSource()) {
setReady(true);
return true;
}
return false;
} catch (error) {
if (!isFetchingCancelled.current) {
onErrorRef.current(error);
}
setReady(false);
return false;
}
}, [forceDownload, path, source, spawnFileSource]);
const probeMetadataOnly = (0, _react.useCallback)(async () => {
if (!(0, _remoteHttpSource.isRemoteHttpUrl)(path)) {
setReady(true);
return;
}
isFetchingCancelled.current = false;
setReady(false);
onLoadStartRef.current();
const headers = (0, _utils2.getSourceHeaders)(source);
try {
const probedDuration = await (0, _AudioFileUtils.probeDuration)(path, context?.sampleRate, headers);
if (probedDuration && !isFetchingCancelled.current) {
setDuration(probedDuration);
}
setReady(true);
} catch (error) {
if (!isFetchingCancelled.current) {
onErrorRef.current(error);
}
setReady(false);
}
}, [context?.sampleRate, path, source]);
const loadForPlayback = (0, _react.useCallback)(async () => {
if (fullDataFetched.current) {
return fileSourceRef.current != null;
}
return loadPlaybackSource();
}, [loadPlaybackSource]);
(0, _react.useEffect)(() => {
isFetchingCancelled.current = false;
fullDataFetched.current = false;
if (!path) {
setReady(false);
setDuration(0);
disposeSource();
return () => {
isFetchingCancelled.current = true;
disposeSource();
};
}
if (preloadMode === 'none') {
setReady(true);
return () => {
isFetchingCancelled.current = true;
disposeSource();
};
}
if (preloadMode === 'metadata') {
if (!(0, _utils.isFfmpegEnabled)()) {
setReady(true);
} else {
probeMetadataOnly();
}
return () => {
isFetchingCancelled.current = true;
disposeSource();
};
}
loadPlaybackSource();
return () => {
isFetchingCancelled.current = true;
disposeSource();
};
}, [disposeSource, loadPlaybackSource, path, preloadMode, probeMetadataOnly]);
return {
fileSourceRef,
ready,
duration,
setDuration,
loadForPlayback,
disposeSource
};
}
//# sourceMappingURL=useAudioSourceLoader.js.map