- Demo
- Library and installation
- Usage
- Steps
- References
Link youtube: https://www.youtube.com/watch?v=OfUqaofrcMw
- React native sound: https://www.npmjs.com/package/react-native-sound
npm install react-native-sound --save
react-native link react-native-sound
- React native slider: https://www.npmjs.com/package/@react-native-community/slider
npm install @react-native-community/slider --save
- React native vector icons: https://github.com/oblador/react-native-vector-icons
npm install --save react-native-vector-icons
react-native link react-native-vector-icons
- React native vector icons directory (search icon name you want to use): https://oblador.github.io/react-native-vector-icons/
- Copy your audio files into folder /android/app/src/main/res/raw (if not exists, create it)
- Notice that your audio's name in folder raw should be like this: File-based resource names must contain only lowercase a-z, 0-9, or underscore or you will face this error when run app
- Same as Load sound from the app bundle
- Instead of use path like previous 2 types before, we use like this:
path: require('./sounds/Play-Doh-meets-Dora_Carmen-Maria-and-Edu-Espinal.mp3')
- And when we create new sound, the second parameter is the callback, not the basePath
if (currentAudio.isRequired === true) {
const newPlayer = new SoundPlayer(require('./sounds/Play-Doh-meets-Dora_Carmen-Maria-and-Edu-Espinal.mp3'), (error) => callback(error, newPlayer));
...
}
...
- Notice that your audio's name in this case should be like this: File-based resource names must not contain space character or any special characters or you will face this error when run app
- Create type SoundFileType: used for create a list of sounds we will play
type SoundFileType = {
type: 'app-bundle';
name: string;
path: string;
basePath: string;
} | {
type: 'network';
name: string;
path: string;
} | {
type: 'directory';
name: string;
path: NodeRequire;
};
- Create type AudioStatusType: when we play sound, there are many states such as: loading, error, play, pause, next, previous,...
type AudioStatusType = 'loading' | 'success' | 'error' | 'play' | 'pause' | 'next' | 'previous' | 'stop';
- Create interface IUseAudioHelper for next step: parameter of hook useAudioHelper
interface IUseAudioHelper {
listSounds: ISoundFile[];
timeRate?: number;
}
Hook useAudioHelper has all methods, values we need like: play, pause, currentTime,...
const [listSounds, setListSounds] = React.useState(request.listSounds);
const [timeRate, setTimeRate] = React.useState(request.timeRate || 15);
const [status, setStatus] = React.useState<AudioStatusType>('loading');
const [duration, setDuration] = React.useState(0);
const [player, setPlayer] = React.useState<SoundPlayer>(null);
function playWithPlayer(player: SoundPlayer) {
if (player) {
player.play(playComplete);
setStatus('play');
}
}
function initialize() {
setStatus('loading');
if (listSounds.length > 0) {
if (player) {
player.release();
}
const callback = (error, player: SoundPlayer) => {
if (error) {
setStatus('error');
setErrorMessage(error.message);
} else {
setStatus('success');
setErrorMessage('');
}
player.setSpeed(speed);
setDuration(player.getDuration());
play(player);
}
const currentAudio = listSounds[index];
// If the audio is a 'require' then the second parameter must be the callback.
let newPlayer: SoundPlayer = null;
switch(currentAudio.type) {
default: break;
case 'app-bundle':
newPlayer = new SoundPlayer(currentAudio.path, currentAudio.basePath, (error) => callback(error, newPlayer));
break;
case 'network':
newPlayer = new SoundPlayer(currentAudio.path, null, (error) => callback(error, newPlayer));
break;
case 'directory':
newPlayer = new SoundPlayer(currentAudio.path, (error) => callback(error, newPlayer));
break;
}
if (newPlayer) {
setPlayer(newPlayer);
}
}
}
const [currentTime, setCurrentTime] = React.useState(0);
React.useEffect(() => {
const interval = setInterval(() => {
if (player && status === 'play') {
player.getCurrentTime((seconds: number) => {
setCurrentTime(seconds);
})
}
}, 100);
return () => clearInterval(interval);
});
const [speed, setSpeed] = React.useState(1);
React.useEffect(() => {
if (player) {
player.setSpeed(speed);
}
}, [speed]);
const [index, setIndex] = React.useState(0);
React.useEffect(() => {
initialize();
}, [index]);
function playComplete(isEnd: boolean) {
if (isEnd === true) {
next();
}
}
function play() {
if (player) {
player.play(playComplete);
setStatus('play');
}
}
function pause() {
if (player) {
player.pause();
setStatus('pause');
}
}
function stop() {
if (player) {
player.stop();
setStatus('stop');
}
}
function next() {
if (player && index < listSounds.length - 1) {
player.release();
setCurrentTime(0);
setStatus('next');
setIndex(index + 1);
}
}
function previous() {
if (player && index > 0) {
player.release();
setCurrentTime(0);
setStatus('previous');
setIndex(index - 1);
}
}
function seekToTime(seconds: number) {
if (player) {
player.setCurrentTime(seconds);
setCurrentTime(seconds);
}
}
function increaseTime() {
if (player) {
player.getCurrentTime((seconds) => {
if (seconds + timeRate < duration) {
seekToTime(seconds + timeRate)
} else {
seekToTime(duration);
}
});
}
}
function decreaseTime() {
if (player) {
player.getCurrentTime((seconds) => {
if (seconds - timeRate > 0) {
seekToTime(seconds - timeRate);
} else {
seekToTime(0);
}
});
}
}
function formatTimeString(value: number) {
return new Date(value * 1000).toISOString().substr(11, 8)
}
function getDurationString() {
return formatTimeString(duration);
}
function getCurrentTimeString() {
return formatTimeString(currentTime);
}
function getCurrentAudioName() {
return listSounds[index].name;
}
function isDisabledButtonPlay() {
return status === 'loading' || status === 'play';
}
function isDisabledButtonPause() {
return status === 'loading' || status === 'pause' || status === 'stop';
}
function isDisabledButtonStop() {
return status === 'loading' || status === 'stop';
}
function isDisabledButtonNext() {
return status === 'loading' || index === listSounds.length - 1;
}
function isDisabledButtonPrevious() {
return status === 'loading' || index === 0;
}
return {
status,
duration,
currentTime,
play,
pause,
stop,
next,
previous,
increaseTime,
decreaseTime,
durationString: getDurationString(),
currentTimeString: getCurrentTimeString(),
currentAudioName: getCurrentAudioName(),
isDisabledButtonPlay: isDisabledButtonPlay(),
isDisabledButtonPause: isDisabledButtonPause(),
isDisabledButtonStop: isDisabledButtonStop(),
isDisabledButtonNext: isDisabledButtonNext(),
isDisabledButtonPrevious: isDisabledButtonPrevious(),
seekToTime,
timeRate,
speed,
setSpeed,
}
const player = useAudioHelper({
listSounds: [
{ type: 'app-bundle', path: 'blue_dream_cheel.mp3', name: 'Blue Dream - Cheel', basePath: SoundPlayer.MAIN_BUNDLE },
{ type: 'app-bundle', path: 'know_myself_patrick_patrikios.mp3', name: 'Know Myself - Patrick Patrikios', basePath: SoundPlayer.MAIN_BUNDLE },
{ type: 'directory', path: require('./sounds/Play-Doh-meets-Dora_Carmen-Maria-and-Edu-Espinal.mp3'), name: 'Play Doh meets Dora - Carmen Maria and Edu Espinal', },
{ type: 'network', path: 'https://raw.githubusercontent.com/uit2712/RNPlaySound/develop/sounds/Tropic%20-%20Anno%20Domini%20Beats.mp3', name: 'Tropic - Anno Domini Beats', },
],
timeRate: 15,
isLogStatus: true,
});
Link: https://www.npmjs.com/package/react-native-sound
Link: https://github.com/zmxv/react-native-sound-demo
Link: https://github.com/benevbright/react-native-sound-playerview