MPEG Audio Layer 3 atau yang lebih dikenal dengan MP3, adalah format kompresi audio yang paling populer digunakan di seluruh dunia. Dengan dukungan terhadap MP3, Android menyediakan akses ke beragam file audio yang tersedia di Internet maupun toko musik.
AMR (Adaptive Multi-Rate)
Format kompresi ini sangat populer digunakan pada aplikasi panggilan suara ponsel. AMR dirancang untuk voice encoding, format kompresi ini tidak cocok untuk jenis audio kompleks seperti musik. Format kompresi ini biasanya disimpan sebagai file .amr atau .3gp.Ogg Vorbis
Ogg Vorbis (.ogg) adalah format kompresi audio open source dan bebas paten. Kualitasnya sebanding dengan format komersial seperti MP3 maupun AAC. Ogg Vorbis saat ini dikelola oleh Xiph.org.PCM (Pulse Code Modulation)
PCM adalah format audio yang populer di PC terutama Windows. Format audio ini biasanya format tidak terkompresi seperti WAVE (.wav). PCM menyimpan data audio sebagai sekumpulan data amplitudo yang berubah terhadap waktu. Android mendukung PCM melalui file WAV.Layanan bawaan untuk memainkan file audio
Android menyediakan aplikasi bawaan untuk memainkan file audio. Untuk menjalankan aplikasi tersebut, Anda hanya perlu menciptakan Intent ACTION_VIEW. Intent ini butuh data lokasi file audio (Uri) dan tipe MIME file audio yang hendak dimainkan. Ketika kode pada Listing 1 dieksekusi, Android akan menjalankan aplikasi yang mampu menangani file audio dengan MIME “audio/mp3”, biasanya adalah aplikasi Music.Listing 1
1 2 3 4 5 6 | //file musik diasumsikan File sdcard=Environment. getExternalStorageDirectory(); File fileAudio=new File(sdcard.getPath() +"/smoothjazz.mp3"); Uri uriAudioFile=Uri.fromFile(fileAudio); Intent an_intent = new Intent( android.content.Intent.ACTION_VIEW); an_intent.setDataAndType(audioFileUri, "audio/mp3"); startActivity(an_intent); |
Audio playback dengan MediaPlayer
Cara memainkan file audio di atas adalah memanfaatkan aplikasi lain. Ini tentu tidak cocok bila Anda hendak membuat aplikasi multimedia player sendiri. Untuk hal tersebut, Android menyediakan kelas MediaPlayer yang menyediakan fungsionalitas audio playback yang lengkap. Kelas ini dapat Anda pergunakan dengan mengimpor paket android.media.MediaPlayer umumnya digunakan untuk memainkan data audio yang berukuran besar dengan cara streaming yakni membaca data sedikit demi sedikit lalu memainkan data tersebut.
Ada beberapa cara kita dapat menciptakan MediaPlayer, yakni dari resource, dari Uri, dari file yang ada penyimpanan lokal (local storage) atau dari jaringan (URL).
Memuat audio pada MediaPlayer
Untuk menggunakan file audio yang diletakkan di resource, Anda cukup meletakkan file audio tersebut di direktori res/raw. Perbarui isi jendela Package Explorer dengan menekan F5 maka plugin Eclipse akan membuatnya dapat diakses lewat kelas R. Jika Anda menyalin file audio bernama smoothjazz.mp3 ke direktori res/raw, maka kode pada Listing 2 adalah cara menciptakan instance MediaPlayer dari data fie audio di resource. Yang harus dipastikan adalah ektensi pada nama file tidak disertakan sebagai pengenal sehingga jika Anda memiliki file dengan nama sama namun ekstensi berbeda, misalkan smoothjazz.mp3 dan smoothjazz.ogg, hal ini menyebabkan kerancuan. Solusinya adalah dengan menamai ulang file, misalnya menjadi smoothjazz-mp3.mp3 dan smoothjazz-ogg.ogg.Listing 2
1 2 3 | MediaPlayer mplayer = MediaPlayer.create( context,R.raw.smoothjazz); |
Memuat audio dari penyimpanan lokal
File audio yang tersimpan di memori lokal seperti SD card dapat dimainkan dengan terlebih dahulu mengatur sumber data yang digunakan MediaPlayer. Metode setDataSource() disediakan untuk maksud ini. Ada tiga bentuk metode setDataSource(), yakni setDataSource(String path), setDataSource(Context context, Uri uri) dan setDataSource(FileDescriptor fd).Listing 3
1 2 3 4 5 | MediaPlayer mplayer = new MediaPlayer(); mplayer.setDataSource(“/mnt/sdcard/smootjazz.mp3?); mplayer.prepare(); |
Jika Anda ingin aplikasi tetap responsif saat proses persiapan. Anda bisa menggunakan prepareAsync(). Metode prepareAsync() digunakan untuk menyiapkan data audio secara asinkron. Kendalir alir program akan segera keluar dari metode prepareAsync() tanpa menunggu proses persiapan selesai.
Untuk memastikan kapan proses persiapan selesai, Anda harus memberitahukan fungsi callback yang akan dipanggil ketika proses persiapan selesai melalui metode setOnPreparedListener() milik MediaPlayer. Metode ini mengharapkan instance interface MediaPlayer.onPreparedListener. Metode pada interface ini adalah onPrepared(). Metode onPrepared() ini akan dipanggil bila audio siap dimainkan.
Memuat audio dari Uri
Untuk memuat audio dari Uri Anda bisa memodifikasi Listing 3 dengan menggunakan metode setDataSource(Context context, Uri uri)Kendali audio playback pada MediaPlayer
Kendali audio playback disediakan oleh MediaPlayer melalui metode start(), stop(), pause() dan seekTo().Mulai playback
Metode start() (Listing 4) akan memicu aksi audio playback dan mengubah status MediaPlayer menjadi Started bila pemanggilan start() sukses. Status Started ini dapat diuji dengan metode isPlaying() yang akan mengembalikan nilai boolean true atau false. Pemanggilan start() berulang-ulang ketika berada dalam status Started tidak berdampak apa-apa.Listing 4
1 | mplayer.start(); |
Menghentikan sementara
Playback bisa dihentikan sementara dengan metode pause(). Ketika pause() sukses dijalankan, status akan berubah dari Started ke Paused. Metode pause() dikerjakan secara asinkron yakni akan keluar tanpa menunggu status Started berubah menjadi Paused. Untuk audio yang dimainkan secara streaming dari jaringan, waktu yang dibutuhkan untuk mencapai status Paused mungkin cukup panjang.Listing 5
1 | mplayer.pause(); |
Melanjutkan playback
Playback yang telah dihentikan sementara dapat dilanjutkan dengan memanggil start() pada saat MediaPlayer dalam status Paused. Pemanggilan start() selanjutnya akan menyebabkan transisi status Paused menjadi Started.Bila posisi kursor playback tidak diubah sejak dihentikan sementara, maka start() akan mulai dari posisi kursor playback terakhir ketika pause() dijalankan.
Menghentikan playback
Audio playback dihentikan dengan memanggil stop(). Bila stop() sukses, status Started akan diubah menjadi Stopped. Perubahan status dari Started ke Stopped mungkin tidak terjadi seketika.Listing 6
1 | mplayer.stop() |
Mengatur posisi kursor playback
Ketika status adalah Prepared, Starterd, Paused atau PlaybackCompleterd. posisi playback bisa diubah dengan seekTo(). Metode ini mengharapkan parameter bertipe integer yakni waktu dalam milidetik dihitung dari awal (Listing 7).Listing 7
1 2 3 4 5 | //ubah posisi playback //20 detik dari awal mplayer.seekTo(20000); |
Memainkan audio berulang-ulang
Metode setLooping() Anda perlukan jika Anda ingin memainkan musik yang diulang-ulang ketika telah selesai (Listing 8). Metode ini mengharapkan sebuah parameter bertipe boolean yang bila diisi true akan menyebabkan audio playback diulang terus menerus.Listing 8
1 | mplayer.setLooping(true); |
Anda perlu membuat implementasi metode OnCompletionListener.onCompletion(). Dalam metode onCompletion(), Anda hitung jumlah pengulangan yang terjadi dan bila masih kurang, Anda memanggil start() untuk memulai audio playback dari awal.
Kendali volume pada MediaPlayer
Volume MediaPlayer dapat diatur dengan setVolume() (Listing 9). Metode ini mengharapkan dua parameter bertipe float yakni volume speaker kiri dan volume speaker kanan.Listing 9
1 | mplayer.setVolume(leftvol,rightvol); |
Audio playback dengan SoundPool
SoundPool adalah kelas yang mengelola dan memainkan data audio bertipe PCM 16-bit mono atau stereo tidak termampatkan. SoundPool dirancang untuk memainkan audio berdurasi pendek dengan latensi rendah. Tingkat latensi yang rendah ini dicapai karena SoundPool mengasumsikan semua data audio yang ditanganinya adalah data berformat PCM tidak termampatkan. Data audio PCM tersebut selanjutnya disimpan di memori sehingga bisa diakses dengan cepat tanpa perlu dekompresi. Untuk format audio lain, SoundPool akan menggunakan MediaPlayer untuk melakukan dekompresi format tersebut ke format PCM ketika proses memuat data ke memori. Dengan demikian, aplikasi dapat menyimpan data audio termampatkan seperti MP3 atau Ogg yang saat runtime dimuat ke memori sebagai data PCMSoundPool dapat digunakan untuk memainkan beberapa data audio secara bersamaan. Ketika menciptakan instance kelas ini, Anda akan diminta menentukan jumlah stream maksimum yang akan digunakan. Nilai ini adalah banyaknya audio yang dapat dimainkan secara bersamaan pada satu saat.
Jika data audio yang Anda mainkan bersamaan lebih banyak dari jumlah stream maksimum. SoundPool akan menghentikan playback audio yang sedang dimainkan berdasarkan prioritas dan kapan waktu mulai dimainkan. Hal ini guna memberi ruang bagi audio baru untuk dimainkan.
Semakin banyak jumlah stream SoundPool, semakin berat beban CPU untuk mencampur audio-audio yang dimainkan bersamaan. Untuk itu, Anda sebaiknya meminimalkan jumlah stream maksimum sesuai kebutuhan saja.
Dengan fitur seperti latensi rendah dan kemampuan memainkan beberapa audio secara bersama, SoundPool sangat cocok untuk digunakan dalam aplikasi game. Listing 10 berisi contoh bagaimana menciptakan instance SoundPool dengan maksimal jumlah stream 6, tipe stream untuk musik dan kualitas default.
Listing 10
1 2 3 4 5 | SoundPool gPool=new SoundPool(6, AudioManager.STREAM_MUSIC, 0); |
Memuat audio pada SoundPool
Kelas SoundPool menyediakan metode load() untuk memuat data audio ke memori. Metode ini terdiri atas empat variasi, namun yang kita akan pergunakan hanya dua, yakni: load(String path, int priority), load(Context context, int resid, int priority). Metode load() pertama memuat data audio dari file yang tersimpan di penyimpanan lokal dan load() yang kedua memuat data audio dari resource. Untuk memuat data audio dari Uri, Anda bisa menggunakan load() pertama.Metode load() mengembalikan nilai integer berisi pengenal audio yang nantinya dapat digunakan untuk mengacu pada data audio yang baru dimuat. Anda harus menyimpan pengenal ini karena semua fungsionalitas kendali playback menggunakannya.
Parameter terakhir load() adalah prioritas audio tersebut. Prioritas digunakan untuk menentukan bagaimana audio akan dihapus dari audio track ketika audio track sudah penuh terpakai dan ada audio lain yang hendak dimainkan. Menurut dokumentasi Android SDK, parameter ini belum berpengaruh apa-apa dan menyarankan menggunakan nilai 1 agar kompatibel dengan rilis Android di masa datang. Listing 11 berisi contoh
Listing 11
1 | gSoundID=gPool.load(pFilename, 1); |
Kendali audio playback pada SoundPool
Metode play() milik SoundPool digunakan untuk memulai playback (Listing 12). Parameter metode ini berturut-turut adalah pengenal audio yang dikembalikan oleh load() bertipe integer, volume kiri dan kanan bertipe float (0.0=volume minimum hingga 1.0=volume maksimum), prioritas audio bertipe integer, jumlah pengulangan bertipe integer (0=tidak diulang, -1=diulang terus menerus) dan playback rate.Playback rate mengatur kecepatan playback. Nilai defaultnya adalah 1.0 dan berkisar 0.5 hingga 2.0. Bila kurang dari 1.0, suara akan menjadi lambat dan berat (Anda bisa bayangkan suara Darth Vader pada film Star Wars). Bila lebih dari 1.0, suara menjadi cepat dan tinggi (terdengar seperti suara pada karakter Alvin and the chipmunks).
Metode play() mengembalikan nilai bertipe integer yang merupakan pengenal stream yang sedang dimainkan. Anda menggunakan pengenal stream ini untuk mengendalikan playback lebih lanjut.
Listing 12
1 2 3 4 5 6 7 8 9 10 11 | gStreamID=gPool.play(gSoundID, gLeftVolume, gRightVolume, gPriority, gLoop, gRate); |
Menghentikan sementara playback
Metode pause() mengharapkan sebuah parameter bertipe integer berisi pengenal stream yang akan dihentikan sementara (Listing 13).Listing 13
1 | gPool.pause(gStreamID); |
Melanjutkan playback
Playback dilanjutkan dengan memanggil resume() dan melewatkan pengenal stream (Listing 14).Listing 14
1 | gPool.resume(gStreamID); |
Menghentikan playback
Playback dihentikan dengan stop() dan melewatkan pengenal stream (Listing 15).Listing 15
1 | gPool.stop(gStreamID); |
Merancang sistem audio untuk game
Kita akan membungkus MediaPlayer dan SoundPool menjadi sistem audio yang cocok untuk digunakan dalam aplikasi game. Pada aplikasi game, umumnya developer hanya peduli pada audio playback, terutama untuk memainkan musik latar dan efek-efek suara dalam game.Musik latar pada dasarnya adalah data audio biasa, namun berdurasinya lebih panjang, sehingga untuk memainkannya lebih cocok menggunakan teknik streaming untuk meminimalkan kebutuhan buffer sementara. Musik latar ini kadang dimainkan secara berulang-ulang selama game berlangsung.
Efek suara adalah data audio berdurasi pendek. Suara dentuman meriam, suara desing peluru, suara hembusan angin dan lain-lain adalah contoh efek suara. Efek suara kadang dimainkan sebagai reaksi atas aksi yang dikerjakan oleh pemain. Untuk efek suara seperti ini, waktu latency (waktu yang dilampaui sejak proses playback dimulai hingga terdengar di speaker) harus cukup rendah. Jika latency terlalu tinggi, maka visualisasi game bisa jadi tidak singkron dengan audio. Ketika pemain menekan tombol untuk menembakkan peluru, Anda tentu ingin efek suara desing peluru terdengar bersamaan dengan penekanan tombol tersebut, bukan beberapa detik setelahnya.
Antar muka dasar sistem audio
Sistem audio yang kita bangun akan diturunkan dari dua buah interface dasar yang menentukan antar muka pemrograman yang harus disediakan oleh kelas yang mengimplementasi interface tersebut. Interface pertama adalah IAudio (Listing 12), adalah interface bagi enkapsulasi data audio baik musik latar maupun efek suara. Interface kedua adalah IAudioManager (Listing 13). Sesuai namanya ia adalah interface pengelola IAudio.IAudio
Interface ini menyediakan antar muka yang harus diimplementasi oleh kelas turunan meliputi:- Metode untuk memuat data audio dari file berdasar yang nama file, Uri atau resource.
- Metode kendali audio playback
- Metode pengaturan volume.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | package juhara.jhr_engine.audio; import java.io.IOException; import android.net.Uri; /** * Interface dasar management audio clip * @version 1.0 * @author Zamrony P. Juhara * */ public interface IAudio { public void loadFromFile( final String pFilename) throws IOException; public void loadFromUri( final Uri an_uri) throws IOException; public void loadFromResource( final int resId) throws IOException; public void play(); public void pause(); public void resume(); public void stop(); public void release(); public void reset(); public boolean isPlaying(); public void setVolume(); public void setVolume(float leftvol, float rightvol); public void setLeftVolume(float vol); public void setRightVolume(float vol); public float getLeftVolume(); public float getRightVolume(); } |
IAudioManager
Audio manager menyediakan antar muka pengelolaan IAudio meliputi metode untuk menambah, menghapus dan mendapatkan instance interface IAudio dan membebaskan audio engine internal (Listing 13).Listing 13
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package juhara.jhr_engine.audio; import android.content.Context; import juhara.jhr_engine.audio.IAudio; public interface IAudioManager
{ public void add( final AudioClass pAudio); public AudioClass get( final int indx); public void remove(final int indx); public void clear(); public int size(); public void release(); public void reset(); public Context getContext(); } |
Kelas dasar audio
Kelas Audio (Listing 14) adalah kelas abstrak dan merupakan implementasi interface IAudio. Sebagian besar metode pada interface IAudio dibiarkan belum diimplementasi karena tidak relevan. Sebagian besar fungsionalitas yang disediakan kelas ini adalah metode pengaturan volume, prioritas playback, sample rate dan jumlah pengulangan playback.Listing 14
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | package juhara.jhr_engine.audio; import juhara.jhr_engine.audio.IAudio; public abstract class Audio implements IAudio { public static final int LOOP_FOREVER=-1; public static final int NO_LOOP=0; protected final IAudioManager gManager; private float gLeftVolume=1.0f, gRightVolume=1.0f, gRate=1.0f; private int gPriority=0, gLoopCount=0; public Audio( IAudioManager aManager) { gManager=aManager; } @Override public float getLeftVolume() { return gLeftVolume; } @Override public float getRightVolume() { return gRightVolume; } @Override public void setLeftVolume(final float aLeftVolume) { this.gLeftVolume = aLeftVolume; setVolume(); } @Override public void setRightVolume(final float aRightVolume) { this.gRightVolume = aRightVolume; setVolume(); } protected IAudioManager getManager() { return this.gManager; } public float getRate() { return gRate; } public void setRate(final float aRate) { gRate = aRate; } public int getPriority() { return gPriority; } public void setPriority(final int aPriority) { gPriority = aPriority; } public int getLoop() { return gLoopCount; } public void setLoop(final int aLoopCount) { gLoopCount = aLoopCount; } @Override public void setVolume() { setVolume(gLeftVolume, gRightVolume); } } |
Manajemen audio dasar
Kelas BasicAudioManager (Listing 15) mengelola daftar instance Audio dan merupakan kelas implementasi interface IAudioManager. Daftar instance kelas Audio disimpan dalam sebuah instance ArrayList. Sebenarnya kelas BasicAudioManager ini hanyalah pembungkus instance ArrayList dengan tambahan metode untuk mereset dan membebaskan instance Audio yang tersimpan di ArrayList.Listing 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | package juhara.jhr_engine.audio; import java.util.ArrayList; import android.content.Context; import juhara.jhr_engine.audio.IAudio; import juhara.jhr_engine.audio.IAudioManager; public abstract class BasicAudioManager
implements IAudioManager { private final ArrayList gAudioList = new ArrayList private final Context gAppContext; public BasicAudioManager(final Context aContext) { gAppContext=aContext; } @Override public void add(final AudioClass pAudio) { gAudioList.add(pAudio); } @Override public AudioClass get(final int indx) { return gAudioList.get(indx); } @Override public void reset() { for(int i=0; i
|
Sistem audio musik latar
Instance pembungkus fungsionalitas audio playback untuk musik latar akan memanfaatkan MediaPlayer karena kita butuh streaming untuk memainkan musik latar yang cenderung berdurasi panjang. Latency bukan prioritas penting karena musik latar dapat dimainkan terlambat beberapa saat tanpa pengaruh besar pada gameplay.Kelas StreamSound akan diturunkan dari kelas Audio dan melengkapi implementasi semua metode abstrak termasuk kendali audio playback seperti play(), pause(), resume() dan stop() dan proses memuat data audio dari file, uri atau resource.
Manajemen musik latar
Sistem audio untuk musik latar terbagi atas kelas StreamSoundManager dan StreamSound yang masing-masing bertugas sebagai pengelola instance StreamSound dan pembungkus audio playback untuk musik latar.Sistem audio efek suara
Efek suara berdurasi pendek akan dibungkus dalam kelas SoundFX. Untuk efek suara, kita akan menggunakan SoundPool. Semua instance SoundFX akan menggunakan instance SoundPool yang sama. Instance SoundPool sendiri dikelola oleh pengelola SoundFX yakni kelas SoundFXManager.Fungsionalitas yang disediakan SoundFX adalah proses memuat data audio, kendali playback dan pengaturan volume serta prioritas.