React Native’de ses dosyaları nasıl oynatılır?
Bu yazımda küçük bir uygulama geliştirerek ses dosyalarını oynatacağız. Öncelikle projemizi oluşturalım:
npx react-native init SampleRNSound --template react-native-template-typescript
cd SampleRNSound
npx react-native run-ios
Ses ile ilgili birçok kütüphane bulunuyor. react-native-sound kütüphanesi en sade şekilde bu işi gerçekleştirdiği için onu kullanacağız. Şimdi ses kütüphanesini yükleyelim ve ilgili pod’ları indirelim.
yarn add react-native-sound
pod install --project-directory=ios/
Ses dosyalarının temini
Ses dosyalarını freesound.org sitesinden temin edebilirsiniz. Dikkat etmeniz gereken kısım ise, ses dosyasının açıklamalar kısmında yer alan lisans bilgisidir. Creative Commons lisanslı sesleri uygulamanızda Yasal bildirimler başlığı altında atıfta bulunarak kullanabilirsiniz.
Bu yazıda kullanılacak sesleri, Downloads dizini içerisinde sounds dizini oluşturarak içerisine indirelim ve dosya ismini soldaki kelime ile değiştirelim. Örn: rain için rain.wav haline gelsin.
- rain: https://freesound.org/people/InspectorJ/sounds/401277/
- forest: https://freesound.org/people/JayHu/sounds/506103/
- waves: https://freesound.org/people/florianreichelt/sounds/450755/
- wind: https://freesound.org/people/InspectorJ/sounds/405561/
- stream: https://freesound.org/people/mystiscool/sounds/7137/
- night: https://freesound.org/people/hargissssound/sounds/319372/
- fireplace: https://freesound.org/people/leosalom/sounds/234288/
Lame CLI aracı ile wav dosyalarının mp3'e çevrilmesi
Wav dosyaları büyük oldukları için uygulamanızda epey bir yer kaplayacaktır. Bu nedenle lame CLI aracı ile dönüştürebilirsiniz. lame aracını yüklemek için:
brew install lame
İndirdiğimiz dosyalar üzerinde işlem yapmak için terminalde sounds dizinine gidelim ve aşağıdaki komutu çalıştıralım:
for i in *.wav; do lame -b 320 -h "${i}" "${i%.wav}.mp3"; done
Ardından dizindeki orijinal wav dosyalarını silelim:
rm *.wav
Dizindeki ses dosyalarının tamamı bu haliyle dahi 33MB yer kaplıyor. Aşağıdaki komut ile görebilirsiniz:
du -h .
Daha da azaltmak için ses dosyalarını ffmpeg aracı ile kırpabiliriz. Kesilen dosyalar için trimmed adında ayrı bir dizin oluşturalım.
mkdir trimmed
ffmpeg komutu ile mp3 dosyalarının ilk 10 saniyesini kırpalım ve trimmed dizinine kopyalayalım:
for i in *.mp3; do ffmpeg -i "${i}" -to 10 -c copy "trimmed/${i}"; done
Yeniden dizin boyutuna baktığımızda 2.5 MB olduğunu göreceğiz. Bu boyut bizim için yeterlidir:
Ses dosyalarının projeye eklenmesi
Ses dosyalarının projede kullanımı için native projede eklememiz gerekiyor. İki ayrı platform için aşağıdaki yolu izleyebilirsiniz
- Android: Ses dosyalarının tamamı android/app/src/main/res/raw dizini içerisine direkt olarak eklenmelidir. raw dizini içerisinde iç içe başka dizinler oluşturularak eklenirse, proje ilgili ses dosyalarını görmeyebilir.
- iOS: XCode açılarak projeye sağ tıklanıp Add Files to SampleRNSound seçilerek eklenir.
Xcode’un açılması ve dosyaların eklenmesi aşağıdaki gibidir:
open ios/SampleRNSound.xcworkspace
Dosyalar eklendikten sonra react native projesini VSCode ile açalım ve App.tsx dosyasının içine gidip aşağıdaki gibi değiştirelim:
import React from 'react';
import Sound from 'react-native-sound';
const App = () => { const soundFile = 'rain.mp3';
Sound.setCategory('Playback'); const sound = new Sound(soundFile, Sound.MAIN_BUNDLE, soundLoadCallback); function soundLoadCallback(error: any) {
if (error) {
console.warn('Error: ' + JSON.stringify(error));
return;
}
console.log('Sound file loaded.');
playSound();
} const playSound = () => {
console.log('Sound playing...');
sound.play(soundFinished);
}; const soundFinished = (success: boolean) => {
if (success) {
console.log('Sound finished.');
} else {
console.warn('Playback failed due to audio decoding errors.');
}
};
return <></>;
};
export default App;
Buradaki kod sayesinde rain.mp3 dosyası bitene kadar oynatılacak ve bittikten sonra da “Sound finished.” şeklinde terminal çıktısı verecektir. Bu kod ile bir ses dosyasını çalabiliyorsak birçok ses dosyasını da çalabiliriz demektir. Bunun için arayüz çalışmasına geçelim.
İkonların temin edilmesi
İkonları flaticon’dan temin ettim. Siz de aşağıdaki linkten ilgili koleksiyonu indirebilirsiniz:
Not: İkonları kullanacağınız zaman telif bilgisini eklemeyi unutmayınız.
İndirilen ikonlar için src/img dizini oluşturalım ve içerisine atalım. Kolaylık olması adına ikon isimlerini ses dosyaları ile uyacak şekilde değiştirelim (Örn rain.mp3 için rain.png):
İkon dosyalarının uygulama içerisinde dinamik olarak kullanılabilmesi için require fonksiyonu ile önceden uygulama içerisine import edilmesi gereklidir. Bu nedenle img dizininin içerisinde _images.tsx dosyasını oluşturalım:
const images = {
fireplace: require('./fireplace.png'),
forest: require('./forest.png'),
night: require('./night.png'),
rain: require('./rain.png'),
stream: require('./stream.png'),
waves: require('./waves.png'),
wind: require('./wind.png'),
pause: require('./pause.png'),
};export default images;
Şimdi bu ikonlar için bir genel bir buton bileşeni yapalım ve basıldığında ilgili ses dosyası çalınsın. Bu buton bileşeninin tutulması için src/components dizinini oluşturalım ve SoundButton.tsx dosyasını ekleyelim:
import React, {useState} from 'react';
import Sound from 'react-native-sound';
import images from '../img/_images';
import {
TouchableOpacity,
Image,
View,
ImageBackground,
StyleSheet,
} from 'react-native';
type Props = {
sound: string;
image: any;
};export const SoundButton: React.FC<Props> = (props) => {
const soundFile = props.sound + '.mp3';
const [isPlaying, setIsPlaying] = useState(false);
Sound.setCategory('Playback');
const [sound] = useState<Sound>(
new Sound(soundFile, Sound.MAIN_BUNDLE, soundLoadCallback),
); function soundLoadCallback(error: any) {
if (error) {
console.warn('Error: ' + JSON.stringify(error));
return;
}
console.log('Sound file loaded.');
}
const toggleSound = () => {
console.log('Sound playing: ' + sound.isPlaying());
if (isPlaying) {
stopSound();
} else {
playSound();
}
};const stopSound = () => {
sound.stop(() => {
setIsPlaying(false);
console.log(`Stopped ${soundFile}.`);
});
};const playSound = () => {
console.log(`Playing ${soundFile}...`);
setIsPlaying(true);
sound.play(soundFinished);
};const soundFinished = (success: boolean) => {
if (success) {
console.log(`${soundFile} finished.`);
playSound();
} else {
console.warn('Playback failed due to audio decoding errors.');
}
};
return (
<TouchableOpacity onPress={toggleSound} style={styles.container}>
<ImageBackground source={props.image} style={styles.image}>
{isPlaying && (
<View style={styles.overlay}>
<Image source={images.pause} style={styles.pause} />
</View>
)}
</ImageBackground>
</TouchableOpacity>
);
};const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
padding: 20,
},
image: {
width: 150,
height: 150,
},
overlay: {
backgroundColor: '#ffffff99',
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
},
pause: {
width: '30%',
height: '30%',
},
});
export default SoundButton;
SoundButton bileşenini, sound ve image adında iki prop değeri alacak şekilde ayarladık. Bu sayede çalınacak ses ve görseli belirleyebileceğiz. Ayrıca o an çalınıp çalınmadığı bilgisini isPlaying gibi bir state değişkeninde tutmak iyi olacaktır. Bu sayede pause ikonunu görüntüleyebilir, ses bittiğinde tekrar oynatabiliriz.
Butonları görüntüleyen App.tsx de aşağıdaki gibi olacaktır:
import React from 'react';
import {SafeAreaView, ScrollView, View, StyleSheet} from 'react-native';
import SoundButton from './src/components/SoundButton';
import images from './src/img/_images';
const App = () => {
console.log(images.rain);
return (
<SafeAreaView>
<ScrollView style={styles.scrollView}>
<View style={styles.row}>
<SoundButton sound="rain" image={images.rain} />
<SoundButton sound="forest" image={images.forest} />
</View>
<View style={styles.row}>
<SoundButton sound="waves" image={images.waves} />
<SoundButton sound="wind" image={images.wind} />
</View>
<View style={styles.row}>
<SoundButton sound="stream" image={images.stream} />
<SoundButton sound="night" image={images.night} />
</View>
<SoundButton sound="fireplace" image={images.fireplace} />
</ScrollView>
</SafeAreaView>
);
};const styles = StyleSheet.create({
scrollView: {
paddingTop: 20,
},
row: {
flexDirection: 'row',
},
});
export default App;
Sonuç olarak
react-native-sound kütüphanesi ile ses dosyalarını açmak oldukça basit. Bu kütüphanenin arka planda ses oynatmama gibi önemli bir eksiği de var fakat şimdilik işimizi görüyor. Diğer ses kütüphanelerini ile ilgili yazıyı inceleyebilirsiniz.
Projenin bitmiş halini react-native-sound-sample reposunda bulabilirsiniz. Bu yazı hakkında soru ve görüşlerinizi aşağıdaki yorumlar kısmından yazabilirsiniz. Bana destek vermek için alkış simgesine tıklayabilirsiniz. Sonraki yazımda görüşmek üzere…