I'm using react-native-sound library to play audio in my app. I would like to add option to control speed time of listening.
in my AudioPlayer component I have:
const [music, setMusic] = useState(null)
const [isPlaying, setPlaying] = useState(false)
const [duration, setDuration] = useState(0)
const [currentTime, setCurrentTime] = useState(0)
useEffect(() => {
const audio = new Sound(decodeURI(props.track.url), null, (err) => {
if (err) {
return
}
})
Sound.setActive(true)
Sound.setCategory('Playback', true)
Sound.setMode('Default')
setMusic(audio)
return function cleanup() {
Sound.setActive(false)
audio.release()
}
}, [props.track])
useEffect(() => {
const interval = setInterval(() => {
if (music && duration <= 0) {
setDuration(music.getDuration())
}
if (music && isPlaying) {
music.getCurrentTime((seconds: number) => {
setCurrentTime(seconds)
})
}
}, 100)
return () => clearInterval(interval)
})
const onPlayPausePress = async () => {
if (music.isPlaying()) {
music.pause()
setPlaying(false)
} else {
music.play(success => {
setPlaying(false)
// setCurrentTime(0)
// music.setCurrentTime((0))
})
setPlaying(true)
}
}
const manageSpeedTime = (speed) => {
if (music.isPlaying()) {
music.pause()
setPlaying(false)
music.setSpeed(speed)
music.getCurrentTime((seconds: number) => {
setCurrentTime(seconds)
})
music.setCurrentTime(currentTime)
await music.play(success => {
setPlaying(false)
})
setPlaying(true)
}
}
And later in my code:
<Slider
style={{ width: '55%', height: 40}}
minimumValue={0}
maximumValue={duration}
minimumTrackTintColor={props.audioStylesProps.sliderRunColor}
maximumTrackTintColor={props.audioStylesProps.sliderStartColor}
thumbTintColor={props.audioStylesProps.sliderCircleColor}
value={currentTime}
onValueChange={onSliderChange}
/>
<View style={{flexDirection: "column"}}>
<Button title={"1x"} onPress={() => {manageSpeedTime(1)}} color={"red"}/>
<Button title={"1.25x"} onPress={() => {manageSpeedTime(1.25)}} color={"red"}/>
<Button title={"2x"} onPress={() => {manageSpeedTime(2)}} color={"red"}/>
</View>
My problem is, when I speed time x2 it works fine, but when I want come back to normal speed, I got delay. For example, Im listening with x2 speed and at 40seconds I change speed to 1x, instead of starting from 40sec, my slider goes back to around 34-36 seconds and starts playing music at 40. My idea was to stop music when I change speed time, set speed, set current time and start playing, but looks like music.seetCurrentTime(currentTime) in manageSpeedTime doesn't work. Could somebody help my with solving this issue?
Related
function ManageData({props, navigation}) {
const [details, setDetails] = useState({
dataList: [],
loading: true,
offset: 1,
totalRecords: 0,
search: '',
});
useEffect(() => {
getData();
}, []);
const getData = async () => {
try {
// console.log('search',details.search);
var params = {};
params = {
'pagination[page]': details.offset,
'pagination[perpage]': 10,
};
if(details?.search?.length > 0){
params['query[search]'] = details?.search;
params['pagination[pages]'] = 30;
params['pagination[total]'] = 293;
}else{
params['query'] = ""
}
const result = await getPayeeDetails(session, params);
// console.log('result',result?.data?.data?.length);
if (result?.data?.data?.length > 0) {
setDetails(prev => ({
...prev,
offset: prev.offset + 1,
dataList: [...prev.dataList, ...result.data.data],
loading: false,
totalRecords: result.data.recordsFiltered,
}));
}
} catch (error) {
console.log('getPayeesError', error);
}
};
const loadMore = () => {
try {
if (details.dataList.length != details.totalRecords) {
setDetails(prev => ({
...prev,
loading: true,
}));
getData();
}
} catch (error) {
console.log('LoadMoreError', error);
}
};
const searchHandler=(data)=>{
try{
console.log('clearData',data);
setDetails(prev => ({
...prev,
dataList:[],
offset:1,
search: data == 'RESET'?"":data,
}));
getData();
}catch(error){
console.log("SearchError",error)
}
}
return (
<BackDropContainer
searchHandler={searchHandler}>
<View style={{backgroundColor: 'white', flex: 1}}>
<FlatList
style={{marginTop: '4%'}}
data={details?.dataList}
renderItem={({item}) => (
<TouchableOpacity onPress={() => showDialog(item)}>
<Item data={item} />
</TouchableOpacity>
)}
onEndReached={loadMore}
keyExtractor={(item, index) => index}
/>
</View>
</BackDropContainer>
);
}
I have a flatlist with searchview in my React Native application. Each time user scrolls to the end of flatlist the loadmore function will be called and also the offset value is increased as 1 to fetch next page from API.
Every time the API results array of 10 data from API so the flatlist will be loaded 10 by 10 for each scroll. When I type some data in searchview the searchHandler function will be called, and there I want to reset the offset as 1 and also need to send typed data to the API.
The issue is searched data and offset is not sending with API whenever I try to search the data. State is not updating properly when searching data.
Note: The data which is types has to be sent along with API whenever user search something.
I am new in react-native, in my application I am generating QRCode by one library and it working but in android it is taking time to show on UI, when I set that component to hook to show on UI then it stuck for while and every thing getting non-responsive. After some time it shows and everything work well.
So how can put that setWalletQR in background so that I can show loader until it show to UI?
Here is my code where I am generating the QR in InteractionManager to show
const PozReceive = ({ onClose }: ReceiveProps) => {
const [walletQR, setWalletQR] = useState<ConentQR>(null);
const generateWalletQrCode = () => {
const interactionPromise = InteractionManager.runAfterInteractions(() => {
const qrCode = ConentQR(user?.walletAddress || '', walletImg, 50);
setWalletQR(qrCode);
});
return () => interactionPromise.cancel();
};
useEffect(() => {
if (!pouchQR) {
generatePouchQrCode();
}
}, []);
return (
<Modal
coverScreen={true}
isVisible={true}
onBackdropPress={onClose}
onBackButtonPress={onClose}
backdropColor={Colors.DARK_PURPLE}
backdropOpacity={0.7}
style={styles.modal}>
<>
<BlurView
style={styles.blurView}
blurType="dark"
blurAmount={20}
reducedTransparencyFallbackColor="white"
/>
<VStack style={[styles.modalContainer]}>
{!walletQR ? (
<Image style={styles.qrLoader} source={loaderGif} />
) : (
walletQR
)}
</VStack>
</>
</Modal>
);
};
and here is QR code generator code :-
const ContentQR = (
content: string,
logo: Image.propTypes.source,
logoSize: number,
backgroundColor: string = 'transparent',
) => {
return (
<QRCode
color={Colors.DARK_PURPLE}
content={content}
codeStyle={'dot'}
outerEyeStyle={'diamond'}
logo={logo}
logoSize={logoSize}
backgroundColor={backgroundColor}
/>
);
};
Someone please help me I getting stuck here for while.
You can introduce a variable isLoading and render the loader based on this variable instead of qr value.
const PozReceive = ({ onClose }: ReceiveProps) => {
const [walletQR, setWalletQR] = useState<ConentQR>(null);
const [isLoading, setIsLoading] = useState<Boolean>(false);
const generateWalletQrCode = () => {
setIsLoading(true)
const interactionPromise = InteractionManager.runAfterInteractions(() => {
const qrCode = ConentQR(user?.walletAddress || '', walletImg, 50);
setWalletQR(qrCode);
setIsLoading(false)
});
return () => interactionPromise.cancel();
};
....
<VStack style={[styles.modalContainer]}>
{isLoading && <Image style={styles.qrLoader} source={loaderGif} />}
{!isLoaing && walletQR && walletQR}
</VStack>
When using the RNSound library, I ran into a problem - I can't pause the sound.
initialize :
Sound.setCategory("Playback");
let whoosh = new Sound("complite.mp3", Sound.MAIN_BUNDLE, (error) => {
if (error) {
console.log("failed to load the sound", error);
return;
}
})
and use like this:
<Button
onPress={() => {
if (!start) {
whoosh.play();
const myTimer = setInterval(() => {
setCounter((counter) => counter - 1);
}, 1000);
setTimer(myTimer);
setStart((start) => !start);
} else {
whoosh.pause();
clearInterval(timer);
setCounter(null);
setStart((start) => !start);
}
}}
the first time the button is pressed, the sound is played. On the second press, nothing happens and music is playing. On the third press, the same melody runs in parallel for the second time. As far as I understand, each time I click on the button, I refer to a new instance of Sound. Help with a solution please.
p.s. - i need solution with functional component, not a class. Thanks.
Declare the Sound instance outside of your component scope. You don't need to create a new instance of Sound everytime. Refer my sample.
Sound.setCategory('Playback');
var whoosh = new Sound('beep.mp3', Sound.MAIN_BUNDLE, error => {
if (error) {
console.log('failed to load the sound', error);
return;
}
// loaded successfully
console.log(
'duration in seconds: ' +
whoosh.getDuration() +
'number of channels: ' +
whoosh.getNumberOfChannels(),
);
});
const App: () => Node = () => {
const [start, setStart] = useState(false);
const [counter, setCounter] = useState(0);
const [timer, setTimer] = useState(null);
return (
<Button
onPress={() => {
if (!start) {
whoosh.play();
const myTimer = setInterval(() => {
setCounter(counter => counter - 1);
}, 1000);
setTimer(myTimer);
setStart(start => !start);
} else {
whoosh.pause();
clearInterval(timer);
setCounter(null);
setStart(start => !start);
}
}}
title="Click me"
/>
);
};
Let me know how it goes.
I used such libraries as react-native-sound and react-native-audio-recorder-player in order to play tracks from an array. I tried mapping the array but it plays just the first track.
Is there any method that I can use to play all tracks from the array one after another?
const[id, setId] = useState(1)
const sound = new Sound(('/data/user/0/com.hay/cache/' + id + '.mp3'), Sound.MAIN_BUNDLE)
const playA = () => {
sound.play(success=> setId(id+1))
}
return (
<View style={{flex: 1, backgroundColor: '#FFEDCB', justifyContent: 'center'}}>
<View style={{alignSelf: 'center'}} >
<Image source={globo} style={{width: 340, height: 484}} />
<View style={{ position: 'absolute', top: 403 }}>
<View style={{flexDirection: 'row',position: 'absolute', right: -120 }}>
<TouchableOpacity onPress={()=>playA() }
style={styles.iconHeart}><Icon name={ 'play-circle-outline'}
size={60} color='#F8C56A' /></TouchableOpacity>
</View></View></View></View>
);
};
I'll give you my hook to play some audios which is actually not using react-native-sound nor react-native-audio-recorder-player, but I guess this would help you a bit.
Basically when you play sound you'll get Promises. So what you have to do is to loop over the Array of audio and resolve the Promises one after another like this.
audios.reduce((p, audio) => p.then(() => audioPlay(audio)), Promise.resolve());
The code below is the full version of my hook.
import { useState } from 'react';
import { Player } from '#react-native-community/audio-toolkit';
export const useAudioPlay = ({ audioDatas = [] }) => {
const [isPlaying, setIsPlayIng] = useState(false);
const [currentAudio, setCurrentAudio] = useState();
const audios = audioDatas.map((audioData) => new Player(audioData));
const audioPlay = async (audio) => {
setCurrentAudio(audio);
setIsPlayIng(true);
await new Promise((resolve) =>
audio.play().on('ended', () =>
setTimeout(() => {
if (audio === audios[audios.length - 1]) {
setIsPlayIng(false);
}
resolve();
}, 500)
)
);
};
const stopAudio = () => {
if (currentAudio) {
currentAudio.stop();
setIsPlayIng(false);
}
};
/*
* this meybe what you want.
*/
const playAudios = () => {
if (isPlaying) {
stopAudio();
return;
}
audios.reduce((p, audio) => p.then(() => audioPlay(audio)), Promise.resolve());
};
return { playAudios, stopAudio, isPlaying };
};
Use this callback in react-native-sound
play(onEnd?: (success: boolean) => void): void
Example
Sound.play((success)=> success && nextSong(withID))
Use useEffect in order to play next song.
useEffect(() => {
playA()
}, [id]
);
const LastSongID = 10 // Add Logic for LastSong
const playA = () => {
sound.play((success) => {
if (success && id !== LastSongID) setId(id + 1);
});
};
It should look like this:
useEffect( () => {
let sound = new Sound(('/data/user/0/com.hay/cache/' + id + '.mp3'), Sound.MAIN_BUNDLE, (error) => {
if (error) {
console.log('failed to load the sound', error);
} else {
sound.play((success) => setId(id+1));
}
});
}, [id] )
library used react-native progress
for making an progress bar
import * as Progress from 'react-native-progress';
<Progress.Bar progress={0.3} width={200} />
use this library react-native progress
initial value set to 0
const [progressBarValue, setProgressBarValue] = useState(0)
useEffect(() => {
const intervalId = setInterval(() => {
setProgressBarValue((prev) => {
if (prev >= 1.2) {
setCongratulations('Congratulations')
clearInterval(intervalId);
return 1.120;
} else {
return prev + 0.01;
}
});
}, 1000);
return () => clearInterval(intervalId);
}, []);
render code
<Progress.Bar progress={progressBarValue} width={width-50} color={'rgba(221,196,145, 1)'}/>