Воспроизведение звуковых эффектов в Android с помощью SoundPool

1- Введение

Статья основана на:
  • Android Studio 1.5

2- SoundPool & AudioManager

Для начала, я создам ситуацию, вы создаете игру, игра проигрывает такие звуки как выстрелы, бомбы, это звуковые эффекты в игре. Android предоставляет вам класс  SoundPool, похожий на хранилице звуков и готовый проигрывать их при запросе.

SoundPool содержит набор музыки, звуков из файла музыки в приложении или из файла в системе,.. SoundPool помогает проигрывать разные звуки одновременно. 

SourcePool использует сервис  MediaPlayer, чтобы сделать звуки.
Вы можете настроить  SoundPool проигрывать звуки при определенных  stream (поток). Есть следующие виды stream:
Вид потока звуков Описание
AudioManager.STREAM_ALARM Аудио звуков сирены
AudioManager.STREAM DTMF Аудио DTMF тонов
AudioManager.STREAM_MUSIC Аудио для перепроигрывания звуков
AudioManager.STREAM_NOTIFICATION Аудио звука уведомления
AudioManager.STREAM_RING Аудио звука звона телефона
AudioManager.STREAM_SYSTEM Аудио системных звуков
AudioManager.STREAM_VOICE_CALL Аудио телефонного вызова
AudioManager позволяет вам поменять громкость на разных потоках (stream) звуков. Например вы слушаете песню на устройстве, когда играете в игры, вы можете поменять громкость звуков игры не влияя на громкость песни.
Другой вопрос это какое устройство производит звук.

Используя  STREAM_MUSIC звук будет произведен через одно звуковое устройтсво (телефонный динамик, наушники, динамик bluetooth и др.) соединенные с телефоном.

Используя  STREAM_RING  звук будет произведен через все звуковые устройтсва соединенные с телефоном. Это поведение может отличаться на каждом устройстве.

SoundPool API:

Метод Описание
int play(int soundID, float leftVolume, float rightVolume,
            int priority, int loop, float rate)
Воспроизведение источника звука, с возвращением ID нового воспроизведенного потока (streamID).
void pause(int streamID)  Пауза звукового потока с  ID streamID.
void stop(int streamID) Остановка звукового потока с streamID.
void setVolume(int streamID, float leftVolume, float rightVolume) Установка громкости звукового потока  с ID streamID
void setLoop(int streamID, int loop) Установка номер цикла для звукового потока с ID streamID.

3- Пример с SoundPool

Создать новый project SoundPoolDemo.
Создать папку  raw, которая является подпапкой  res. Скопировать 2 файла звука в папку  raw, здесь я скопировал 2 звуковых файла: звук разрушения ( destroy.wav) и звук выстрелов (gun.wav). Заметьте, вы можете использовать звуковой файл формата mp3 или wav.
Интерфейс приложения.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="org.o7planning.soundpooldemo.MainActivity">

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Destroy"
        android:id="@+id/button_destroy"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginLeft="40dp"
        android:layout_marginStart="40dp"
        android:layout_marginTop="54dp"
        android:onClick="playSoundDestroy" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Gun"
        android:id="@+id/button_gun"
        android:layout_alignTop="@+id/button_destroy"
        android:layout_toRightOf="@+id/button_destroy"
        android:layout_toEndOf="@+id/button_destroy"
        android:layout_marginLeft="74dp"
        android:layout_marginStart="74dp"
        android:onClick="playSoundGun" />
    
</RelativeLayout>
MainActivity.java
package org.o7planning.soundpooldemo;

import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {


   private SoundPool soundPool;

   private AudioManager audioManager;

   // Maximumn sound stream.
   private static final int MAX_STREAMS = 5;

   // Stream type.
   private static final int streamType = AudioManager.STREAM_MUSIC;

   private boolean loaded;

   private int soundIdDestroy;
   private int soundIdGun;
   private float volume;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       // AudioManager audio settings for adjusting the volume
       audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);

       // Current volumn Index of particular stream type.
       float currentVolumeIndex = (float) audioManager.getStreamVolume(streamType);

       // Get the maximum volume index for a particular stream type.
       float maxVolumeIndex  = (float) audioManager.getStreamMaxVolume(streamType);

       // Volumn (0 --> 1)
       this.volume = currentVolumeIndex / maxVolumeIndex;

       // Suggests an audio stream whose volume should be changed by
       // the hardware volume controls.
       this.setVolumeControlStream(streamType);

       // For Android SDK >= 21
       if (Build.VERSION.SDK_INT >= 21 ) {

           AudioAttributes audioAttrib = new AudioAttributes.Builder()
                   .setUsage(AudioAttributes.USAGE_GAME)
                   .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                   .build();

           SoundPool.Builder builder= new SoundPool.Builder();
           builder.setAudioAttributes(audioAttrib).setMaxStreams(MAX_STREAMS);

           this.soundPool = builder.build();
       }
       // for Android SDK < 21
       else {
           // SoundPool(int maxStreams, int streamType, int srcQuality)
           this.soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);
       }

       // When Sound Pool load complete.
       this.soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
           @Override
           public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
               loaded = true;
           }
       });

       // Load sound file (destroy.wav) into SoundPool.
       this.soundIdDestroy = this.soundPool.load(this, R.raw.destroy,1);

       // Load sound file (gun.wav) into SoundPool.
       this.soundIdGun = this.soundPool.load(this, R.raw.gun,1);

   }



   // When users click on the button "Gun"
   public void playSoundGun(View view)  {
     if(loaded)  {
         float leftVolumn = volume;
         float rightVolumn = volume;
         // Play sound of gunfire. Returns the ID of the new stream.
         int streamId = this.soundPool.play(this.soundIdGun,leftVolumn, rightVolumn, 1, 0, 1f);
     }
   }

   // When users click on the button "Destroy"
   public void playSoundDestroy(View view)  {
       if(loaded)  {
           float leftVolumn = volume;
           float rightVolumn = volume;

           // Play sound objects destroyed. Returns the ID of the new stream.
           int streamId = this.soundPool.play(this.soundIdDestroy,leftVolumn, rightVolumn, 1, 0, 1f);
       }
   }

}
Запуск приложения: