Para tocar qualquer som no Android, podemos utilizar a classe MediaPlayer. Com ela podemos executar arquivos de áudio que estejam dentro do APK (na pasta assets ou res/raw) ou ainda no sistema de arquivos do aparelho (como SD card por exemplo).
// Carregando audio do diretório res/raw
MediaPlayer player =
MediaPlayer.create(this, R.raw.explosion);
player.start();
// Carregando audio do cartão de memória
MediaPlayer mp = new MediaPlayer();
mp.setDataSource("/sdcard/explosion.mp3");
mp.prepare();
mp.start();
Porém, em um dos aplicativos que desenvolvemos aqui no trabalho, nós precisávamos tocar dois sons simultâneamente: a música do jogo e o efeito sonoro. A classe MediaPlayer não é aconselhável para efeitos sonoros uma vez que eles precisam de um tempo de resposta baixo. Para esse trabalho, podemos utilizar as classes SoundPool e AudioManager (do pacote android.media) que facilitam o trabalho com sons, permitindo tocá-los concorrentemente.
Vejam o exemplo comentado abaixo:
public class SoundManager {
// Total de sons no pool
private static final int MAXSTREAMS = 4;
// Instância única
private static SoundManager instance;
// Pool de sons
private SoundPool mSoundPool;
// AudioManager para controlar o volume do som
private AudioManager mAudioManager;
// Lista com os ids dos sons adicionados
private ArrayList<Integer> mSoundPoolMap;
// Pilha que armazena as transações
// de execução dos sons
private Stack<Integer> mSongsTransactions;
private Context mContext;
// Construtor privado pra implementar o
// Singleton Design Pattern
private SoundManager(Context ct) {
mContext = ct;
mSoundPoolMap = new ArrayList<Integer>();
mSongsTransactions = new Stack<Integer>();
// Criando o pool de sons
mSoundPool = new SoundPool(
MAXSTREAMS, AudioManager.STREAM_MUSIC, 0);
// AudioManager é um serviço de sistema
mAudioManager = (AudioManager)
mContext.getSystemService(
Context.AUDIO_SERVICE);
}
// Método estático para obter a instância única
public static SoundManager getInstance(Context ct) {
if (instance == null){
instance = new SoundManager(ct);
}
return instance;
}
// Adiciona um som ao pool
public void addSound(int soundId) {
mSoundPoolMap.add(
/* Carrega e obtém o id do som no pool
* O segundo parâmetro o id do recurso
* E o terceiro não serve pra nada :) ,
* Mas na documentação diz pra colocar 1 */
mSoundPool.load(mContext, soundId, 1));
}
// Manda tocar um determinado som
public void playSound(int index) {
/* O AudioManager é usado aqui pra obter
* o valor atual do volume do aparelho para
* não tocar o som nem baixo nem alto demais.
* A divisão que é feita aqui é pq o método
* requer um valor entre 0.0 e 1.0. */
float streamVolume =
mAudioManager.getStreamVolume(
AudioManager.STREAM_MUSIC);
streamVolume /=
mAudioManager.getStreamMaxVolume(
AudioManager.STREAM_MUSIC);
/* playId, armazena o id da requisição do som
* a ser tocado. Ele é usado para parar um
* determinado som a qualquer momento. */
int playId = mSoundPool.play(
mSoundPoolMap.get(index), // ID do som
streamVolume, // volume da esquerda
streamVolume, // volume da direita
1, // prioridade
0, // -1 toca repetidamente,
// n = número de repetições)
1 // pitch. 0.5f metade da velocidade
// 1 = normal e 2 = dobro da velocidade
);
// adiciona o id da transação na pilha
mSongsTransactions.push(playId);
}
public void stopSounds() {
// Percorre todos os ids da pilha e manda
// parar todos os sons
while (mSongsTransactions.size() > 0)
mSoundPool.stop(mSongsTransactions.pop());
}
// Libera os recursos alocados
public void cleanup() {
mSoundPool.release();
mSoundPool = null;
mSoundPoolMap.clear();
mSongsTransactions.clear();
mAudioManager.unloadSoundEffects();
}
}
Com a classe acima, podemos executar duas mídias concorrentemente e com um tempo de resposta muito bom. Vejam como utiliza-la abaixo:
public class TesteSomActivity
extends Activity
implements OnClickListener{
SoundManager sm;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Informa que a aplicação controlará o
// volume da media do telefone pelos botões
// de volume do aparelho
setVolumeControlStream(
AudioManager.STREAM_MUSIC);
sm = SoundManager.getInstance(this);
sm.addSound(R.raw.explosao);
sm.addSound(R.raw.musica);
((Button)findViewById(R.id.btnStart1)).
setOnClickListener(this);
((Button)findViewById(R.id.btnStart2)).
setOnClickListener(this);
((Button)findViewById(R.id.btnStop)).
setOnClickListener(this);
}
public void onClick(View v) {
if (v.getId() == R.id.btnStart1){
sm.playSound(0);
} else if (v.getId() == R.id.btnStart2){
sm.playSound(1);
} else if (v.getId() == R.id.btnStop){
sm.stopSounds();
}
}
}
O primeiro botão executará um efeito sonoro de explosão, enquanto que o segundo tocará a música do jogo. O terceiro parará ambos.
Se o aúdio for muito grande, é melhor optar por combinar o MediaPlayer para a música do jogo e SoundPool para os efeitos sonoros.
Para fazer com que sua aplicação controle o volume da mídia através dos botões de volume do aparelho (ou das teclas + e - no emulador) basta colocar a linha abaixo no onCreate da sua Activity:
setVolumeControlStream(AudioManager.STREAM_MUSIC);
Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber
P.S.: Esse exemplo foi baseado em dois posts encontrados por dois colaboradores daqui do CESAR.
Post 1 e
Post 2
9 comentários:
Entre tantas outras alternativas, só essa classe resolveu o problema do som da aplicação.
olá, parabéns pelo post!
Eu usei sua classe e esta dando o seguinte erro no logCat:
04-01 15:29:14.223: WARN/SoundPool(654): sample 1 not READY
Isso está acontecendo usando outras classes que eu vi na net também...
Você sabe me dizer o que tenho que fazer?
Eu preciso executar um som de "bip" a cada segundo. Esse arquivo de som está no formato mp3 dentro da pasta raw e tem 834 Bytes.
Se você puder me ajudar eu te agradeço!
Oi Fernando,
Acho que o problema é que você está tentando usar o som sem que o SoundPool tenha terminado de carregá-lo.
Dá uma olhada nessa discussão:
http://stackoverflow.com/questions/5202510/soundpool-sample-not-ready
4br4ç05,
nglauber
Olá, como eu executo um som em mp3 no segundo exemplo, não consegui fazer isso.
Oi Claudio,
Verifica se o caminho do arquivo está correto. Uma outra coisa pode ser o codec usado pelo MP3 que não seja suportado.
Dá uma olhada nesse link aqui:
http://nglauber.blogspot.com.br/2011/04/android-dicas-2.html
Pode ser que ajude.
4br4ç05,
nglauber
mt show deu certo . Vlw !! Vai dar upgrade no joguinho to criando :D
Tudo certo. Testado e aprovado. Valeu.
Muitos parabéns pelo post.
será que me podes tirar uma dúvida... Como posso fazer para que isto funcione, mas que os ficheiros estejam armazenados num servidor web. é possível?
Oi Andrea,
Como esses arquivos de som teoricamente devem ser pequenos, é melhor você fazer o download e cache dos mesmos antes de executar.
4br4ç05,
nglauber
Postar um comentário