Vous êtes sur la page 1sur 9

Android Sound Tips

06/01/2011

ANDROID SOUND TIPS


1. Structure of Android sound framework

Figure 1: Android sound framework

In Android 2.2 and older, you can only use java class to play sound (from Android 2.3, you can access directly ALSA from native application). But, the real heart of Android sound mechanism is AudioFlinger, which is not published. Understanding AudioFlinger and under-mechanism of Android sound allows us to answer many questions about sound developing.

Duong Hoang Thanh

Page 1

Android Sound Tips

06/01/2011

Figure 2: AudioFlinger class model

Android sound mechanism is client-server. Your application will be client while AudioFlinger is on server side. So, there is no way to touch or modify AudioFlinger. You just communicate with it via provided clients, especially AudioTrack.

Duong Hoang Thanh

Page 2

Android Sound Tips

06/01/2011

Figure 3: Android sound mechanism is client-server

2. Choose your player We develop Android sound via players provided in java/android/media: AudioTrack: raw player that can play binary data (static or streaming) but in wav format only. Powerful but not easy to use. Most of other players use AudioTrack to communicate with AudioFlinger. MediaPlayer: can play wide range of sound formats (ogg, mp3) and give us the most control with very clearly states. The main trouble is that it is quite slow and cannot play raw data or wav format.

Duong Hoang Thanh

Page 3

Android Sound Tips

06/01/2011

Figure 4: States of Media Player

SoundPool: extremely fast and simple. But it cannot play large sound file because it decodes everything into raw data before playing. You may not imagine that SoundPool uses MediaPlayer to decode sound files and uses AudioTrack to play them. Another backward of SoundPool is that it lacks of sound control and you have no information about sound states. ASyncPlayer: Plays a series of audio URIs, but does all the hard work on another thread so that any slowness with preparing or loading doesn't block the calling thread. But it is too simple with only play and stop method.
Page 4

Duong Hoang Thanh

Android Sound Tips JetPlayer: nice player, but for Jet file only.

06/01/2011

Depend on requirement of your project, you should choose the most suitable player. If you do not have too many sounds playing at same time or sound files are large, media player is suitable. If your project need various sound effects those are short and fast triggered, SoundPool may help. If you want to use wav format or pack sound files into a data package, AudioTrack is the only choice. In fact, we usually use both MediaPlayer (to play background music or large sound files) and SoundPool (to play sound effects). Beware that they are not friendly and their confliction may let your game loses sounds and crashes. So, use them with careful. 3. Problems with sound a. Sound missing AudioFlinger has an AudioMixer that can maximum support 32 sound tracks on each playback thread. So if there are too many sounds to play at the same time, you will receive No more track name error message and some sounds may be ignored.

Figure 5: Each playback thread can support 32 sound tracks.

Another reason of sound missing is that you use SoundPool and play sound right after load it. SoundPool has an underlying thread that takes care of loading and playing sound. So, load method may return before the sound is completely decoded and if you start it at that time, you will receive Sample is not ready. b. Game lagging MediaPlayer is slow and using them regularly may lag game. SoundPool will fix this problem. But SoundPool has a limitation of memory and cannot load large or too many files.
Duong Hoang Thanh Page 5

Android Sound Tips

06/01/2011

c. Lost all sounds and crash As previously mentioned, mixing MediaPlayer with SoundPool may cause missing all sounds and then crash. When creating SoundPool, you must indicate the maximum number of channels. Because of the limitation of AudioMixer, if this number is greater than 32, SoundPool will reduce it to 32. But SoundPool really doesnt know how many sound tracks have been kept by MediaPlayer and others and causes No more track name error. After that, sound will be lost one by one. So, you must limit both number of MediaPlayers instances and number of SoundPool channels. I suggest 2 MediaPlayer (1 for BGM, 1 for VFX) and about 20 SoundPool channels. d. Sound muting When receive a call or user switch to silent mode, you need to mute your music and sounds. Of course, AudioManager.setStreamMute() will do that. But sometime you cannot enable or mute it again. That is because the mute requests for a given stream are cumulative: the AudioManager can receive several mute requests from one or more clients and the stream will be un-muted only when the same number of un-mute requests is received. As the guide from SDK, applications MUST un-mute a muted stream in onPause() and mute is again in onResume() if appropriate. But I suggest setting mute and un-mute right after your application gains or lose focus to prevent delaying. e. 3D sound No player support 3D sound and you have to implement it by yourself. With monosound mode, you need adjust the volume by distance from emitter to listener position. With stereo-mode (when user using headphone), you must add more calculations such as emitter and listener directions, angles and distinguish left and right volume. To me, mono-sound mode is enough while stereo-mode are costing a lot of calculations. 4. Case study The following HelloWorld project will help you studying sound problems. Modify MAX_SOUND_POOL_CHANNEL and MAX_MEDIA_PLAYER constants to see what happens. package com.example.hello; import java.io.IOException; import android.app.Activity; import android.media.AudioManager; import android.media.MediaPlayer;
Duong Hoang Thanh Page 6

Android Sound Tips import android.media.SoundPool; import android.os.Bundle; public class HelloWorld extends Activity { public static final int MAX_SOUND_POOL_CHANNEL = 29; public static final int MAX_MEDIA_PLAYER = 1; private SoundPool mSoundPool; private int[] mSoundList; private int[] mStreamList; private HelloWorld mThis; private MediaPlayer[] mMediaPlayerList; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mThis = this; } @Override protected void onStart() { super.onStart(); mSoundList = new int[32]; mStreamList = new int[32];

06/01/2011

mSoundPool = new SoundPool(MAX_SOUND_POOL_CHANNEL, AudioManager.STREAM_MUSIC, 0); mMediaPlayerList = new MediaPlayer[MAX_MEDIA_PLAYER]; for (int i = 0; i < MAX_MEDIA_PLAYER; i++) { mMediaPlayerList[i] = MediaPlayer.create(getBaseContext(), R.raw.m_boss_carnage); try { mMediaPlayerList[i].prepare(); mMediaPlayerList[i].setLooping(true); mMediaPlayerList[i].start(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) {
Duong Hoang Thanh Page 7

Android Sound Tips // TODO Auto-generated catch block e.printStackTrace(); } }

06/01/2011

for (int i = 0; i < mSoundList.length; i++) { System.out.println("Load sound number " + i); mSoundList[i] = mSoundPool.load(getBaseContext(), R.raw.sfx_kick_impact_1, 1); } for (int i = 0; i < mSoundList.length; i++) { System.out.println("Play sound number " + i); mSoundPool.play(mSoundList[i], 1.0f, 1.0f, 1, -1, 1.0f); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int i = 0; i < mSoundList.length; i++) { System.out.println("Re-Play sound number " + i); mSoundPool.play(mSoundList[i], 1.0f, 1.0f, 1, -1, 1.0f); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } Thread thread = new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub try {
Duong Hoang Thanh Page 8

Android Sound Tips

06/01/2011 Thread.sleep(60000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for (int i = 0; i < mStreamList.length; i++) { System.out.println("Stop sound number " + i); mSoundPool.stop(i); } mSoundPool.release(); for (int i = 0; i < mMediaPlayerList.length; i++) { try { mMediaPlayerList[i].stop(); mMediaPlayerList[i].release(); } catch (Exception e) { e.printStackTrace(); } } mThis.finish(); mThis = null; System.exit(0); }

}); thread.start(); } @Override protected void onStop() { super.onStop(); } }

Duong Hoang Thanh

Page 9

Vous aimerez peut-être aussi