react-native-audio-api
Version:
react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification
243 lines (242 loc) • 8.59 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeGestureHandler = require("react-native-gesture-handler");
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
var _AudioTagContext = require("../AudioTagContext");
var _audioControlUtils = require("./audioControlUtils");
var _play = _interopRequireDefault(require("./icons/play.png"));
var _pause = _interopRequireDefault(require("./icons/pause.png"));
var _speaker = _interopRequireDefault(require("./icons/speaker.png"));
var _speakerX = _interopRequireDefault(require("./icons/speaker-x.png"));
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const TRACK_BAR_HEIGHT = 12;
const TRACK_BAR_HEIGHT_PRESSED = 18;
const TRACK_BAR_ANIM_MS = 150;
const SCRUB_PAN_MIN_DISTANCE = 8;
const AudioControls = () => {
const {
ready,
play,
pause,
seekToTime,
playbackState,
muted,
setMuted,
currentTime,
duration
} = (0, _AudioTagContext.useAudioTagContext)();
const progressTrackAnim = (0, _audioControlUtils.useExpandableTrackHeight)(TRACK_BAR_HEIGHT, TRACK_BAR_HEIGHT_PRESSED, TRACK_BAR_ANIM_MS);
const [scrubTime, setScrubTime] = (0, _react.useState)(null);
const progressTrackRef = (0, _reactNativeReanimated.useAnimatedRef)();
const progressMetricsWidth = (0, _reactNativeReanimated.useSharedValue)(0);
const durationRef = (0, _react.useRef)(duration);
durationRef.current = duration;
const onStart = (0, _react.useCallback)(x => {
progressTrackAnim.expand();
const d = durationRef.current;
progressTrackRef.current?.measureInWindow((_left, _y, width, _h) => {
progressMetricsWidth.value = width;
setScrubTime((0, _audioControlUtils.timeFromLocationX)(x, width, d));
});
}, [progressTrackAnim, progressMetricsWidth, setScrubTime, progressTrackRef]);
const onUpdate = (0, _react.useCallback)(x => {
const d = durationRef.current;
const w = progressMetricsWidth.value;
setScrubTime((0, _audioControlUtils.timeFromLocationX)(x, w, d));
}, [progressMetricsWidth]);
const seekTo = (0, _react.useCallback)(x => {
const d = durationRef.current;
const w = progressMetricsWidth.value;
const t = (0, _audioControlUtils.timeFromLocationX)(x, w, d);
seekToTime(t);
}, [progressMetricsWidth, seekToTime]);
const onEnd = (0, _react.useCallback)(x => {
seekTo(x);
progressTrackAnim.collapse();
setScrubTime(null);
}, [progressTrackAnim, seekTo]);
const onCancel = (0, _react.useCallback)(() => {
progressTrackAnim.collapse();
setScrubTime(null);
}, [progressTrackAnim]);
const onTapSeek = (0, _react.useCallback)(x => {
onEnd(x);
}, [onEnd]);
const scrubGesture = (0, _react.useMemo)(() => {
const panGesture = _reactNativeGestureHandler.Gesture.Pan().runOnJS(true).minDistance(SCRUB_PAN_MIN_DISTANCE).onStart(e => {
onStart(e.x);
}).onUpdate(e => {
onUpdate(e.x);
}).onEnd(e => {
onEnd(e.x);
}).onFinalize((_e, success) => {
if (!success) {
onCancel();
}
});
const tapGesture = _reactNativeGestureHandler.Gesture.Tap().runOnJS(true).maxDistance(14).onEnd((e, success) => {
if (success) {
onTapSeek(e.x);
}
});
return _reactNativeGestureHandler.Gesture.Race(panGesture, tapGesture);
}, [onStart, onUpdate, onEnd, onCancel, onTapSeek]);
const onPlayPausePress = (0, _react.useCallback)(() => {
if (playbackState === 'playing') {
pause();
} else {
play();
}
}, [playbackState, pause, play]);
const onProgressTrackLayout = (0, _react.useCallback)(() => {
progressTrackRef.current?.measureInWindow((_left, _y, width, _h) => {
progressMetricsWidth.value = width;
});
}, [progressMetricsWidth, progressTrackRef]);
if (!ready) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: styles.container,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
color: "#333",
size: "small"
})
});
}
const displayTime = scrubTime ?? currentTime;
const progress = duration > 0 ? displayTime / duration : 0;
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: styles.container,
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.topRow,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
style: styles.playPause,
onPress: onPlayPausePress,
children: playbackState === 'playing' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
source: _pause.default,
style: {
width: 24,
height: 24
}
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
source: _play.default,
style: {
width: 24,
height: 24
}
})
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
style: styles.timeText,
children: [(0, _audioControlUtils.formatTime)(displayTime), " / ", (0, _audioControlUtils.formatTime)(duration)]
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
gesture: scrubGesture,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
ref: progressTrackRef,
onLayout: onProgressTrackLayout,
style: styles.progressTrack,
collapsable: false,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
style: [styles.trackInner, progressTrackAnim.animatedStyle],
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: [styles.trackFill, {
width: `${progress * 100}%`
}]
})
})
})
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
style: styles.volumeIcon,
onPress: () => setMuted(!muted),
children: muted ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
source: _speakerX.default,
style: {
width: 24,
height: 24
}
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
source: _speaker.default,
style: {
width: 24,
height: 24
}
})
})]
})
});
};
const styles = _reactNative.StyleSheet.create({
container: {
flexDirection: 'column',
alignSelf: 'stretch',
minWidth: 200,
paddingVertical: 10,
paddingHorizontal: 12,
backgroundColor: '#f5f5f5',
borderRadius: 8,
borderWidth: 1,
borderColor: '#333',
..._reactNative.Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2
},
shadowOpacity: 0.15,
shadowRadius: 4
},
android: {
elevation: 4
}
})
},
topRow: {
flexDirection: 'row',
alignItems: 'center'
},
playPause: {
padding: 4,
marginRight: 12
},
timeText: {
color: '#000',
fontSize: 12,
marginRight: 10,
minWidth: 48
},
progressTrack: {
flex: 1,
minWidth: 40,
justifyContent: 'center',
marginRight: 10
},
trackInner: {
alignSelf: 'stretch',
backgroundColor: '#ccc',
overflow: 'hidden'
},
trackFill: {
height: '100%',
backgroundColor: '#000'
},
volumeIcon: {
padding: 4,
marginRight: 12
},
loadingRow: {
flexDirection: 'row',
alignItems: 'center'
},
loadingText: {
color: '#333',
fontSize: 14
}
});
var _default = exports.default = AudioControls;
//# sourceMappingURL=AudioControls.js.map