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