120 lines
2.8 KiB
TypeScript
120 lines
2.8 KiB
TypeScript
import { AVPlaybackStatus, Audio, InterruptionModeAndroid, InterruptionModeIOS } from 'expo-av';
|
|
import { Sound } from 'expo-av/build/Audio/Sound';
|
|
|
|
const AUDIO_MODE = {
|
|
allowsRecordingIOS: false,
|
|
playsInSilentModeIOS: true,
|
|
staysActiveInBackground: true,
|
|
shouldDuckAndroid: true,
|
|
playThroughEarpieceAndroid: false,
|
|
interruptionModeIOS: InterruptionModeIOS.DoNotMix,
|
|
interruptionModeAndroid: InterruptionModeAndroid.DoNotMix
|
|
};
|
|
|
|
class AudioPlayer {
|
|
private audioQueue: { [uri: string]: Sound };
|
|
private audioPlaying: string;
|
|
|
|
constructor() {
|
|
this.audioQueue = {};
|
|
this.audioPlaying = '';
|
|
}
|
|
|
|
async loadAudio(uri: string): Promise<Sound> {
|
|
if (this.audioQueue[uri]) {
|
|
return this.audioQueue[uri];
|
|
}
|
|
const { sound } = await Audio.Sound.createAsync({ uri }, { androidImplementation: 'MediaPlayer' });
|
|
this.audioQueue[uri] = sound;
|
|
return sound;
|
|
}
|
|
|
|
onPlaybackStatusUpdate(uri: string, status: AVPlaybackStatus, callback: (status: AVPlaybackStatus) => void) {
|
|
if (status) {
|
|
callback(status);
|
|
this.onEnd(uri, status);
|
|
}
|
|
}
|
|
|
|
async onEnd(uri: string, status: AVPlaybackStatus) {
|
|
if (status.isLoaded) {
|
|
if (status.didJustFinish) {
|
|
try {
|
|
await this.audioQueue[uri]?.stopAsync();
|
|
this.audioPlaying = '';
|
|
} catch {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
setOnPlaybackStatusUpdate(uri: string, callback: (status: AVPlaybackStatus) => void): void {
|
|
return this.audioQueue[uri]?.setOnPlaybackStatusUpdate(status => this.onPlaybackStatusUpdate(uri, status, callback));
|
|
}
|
|
|
|
async playAudio(uri: string) {
|
|
if (this.audioPlaying) {
|
|
await this.pauseAudio(this.audioPlaying);
|
|
}
|
|
await Audio.setAudioModeAsync(AUDIO_MODE);
|
|
await this.audioQueue[uri]?.playAsync();
|
|
this.audioPlaying = uri;
|
|
}
|
|
|
|
async pauseAudio(uri: string) {
|
|
await this.audioQueue[uri]?.pauseAsync();
|
|
this.audioPlaying = '';
|
|
}
|
|
|
|
async pauseCurrentAudio() {
|
|
if (this.audioPlaying) {
|
|
await this.pauseAudio(this.audioPlaying);
|
|
}
|
|
}
|
|
|
|
async setPositionAsync(uri: string, time: number) {
|
|
try {
|
|
await this.audioQueue[uri]?.setPositionAsync(time);
|
|
} catch {
|
|
// Do nothing
|
|
}
|
|
}
|
|
|
|
async setRateAsync(uri: string, value = 1.0) {
|
|
await this.audioQueue[uri].setRateAsync(value, true);
|
|
}
|
|
|
|
async unloadAudio(uri: string) {
|
|
await this.audioQueue[uri]?.stopAsync();
|
|
await this.audioQueue[uri]?.unloadAsync();
|
|
delete this.audioQueue[uri];
|
|
this.audioPlaying = '';
|
|
}
|
|
|
|
async unloadCurrentAudio() {
|
|
if (this.audioPlaying) {
|
|
await this.unloadAudio(this.audioPlaying);
|
|
}
|
|
}
|
|
|
|
async unloadAllAudios() {
|
|
const audiosLoaded = Object.values(this.audioQueue);
|
|
try {
|
|
await Promise.all(
|
|
audiosLoaded.map(async audio => {
|
|
await audio?.stopAsync();
|
|
await audio?.unloadAsync();
|
|
})
|
|
);
|
|
} catch {
|
|
// Do nothing
|
|
}
|
|
this.audioPlaying = '';
|
|
this.audioQueue = {};
|
|
}
|
|
}
|
|
|
|
const audioPlayer = new AudioPlayer();
|
|
export default audioPlayer;
|