5.15. Воспроизведение звуков

5.15. Воспроизведение звуков

В ОС Андроид для воспроизведения звуков есть два класса: SoundPool и MediaPlayer. В данном примере рассматривается использование класса SoundPool. Разница между этими двумя классами в том, что класс SoundPool служит для воспроизведения маленьких звуковых файлов, а через MediaPlayer можно воспроизводить файлы какого-угодно размера, поэтому в примере ниже через класс SoundPool приводится пример воспроизведения звуковых эффектов, которые по мере необходимости могут быть добавлены в ваше приложение.


Создайте новый проект на основе макета Empty Activity. Добавьте в папку layout новый файл spinner_item.xml и напишите туда следующий код:

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="Sample Text"
    android:textSize="16sp"
    android:textColor="#000"
    android:padding="8dp">
</TextView>

 Нажмите правой кнопкой мыши на папку app и выберите New -> Folder -> Assets Folder.

Скачайте файлы по ссылкам ниже и добавьте их в папку assets:

https://drive.google.com/open?id=1_z6fUppglAA4h0hZZm5ShBGGqH5Ku0Am

https://drive.google.com/open?id=1mOwYdFSQeqaHM9eEcQs-DzJhNUxqJI_v

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


Нажмите правой кнопкой мыши на папку drawable, выберите New -> Vector Asset. Найдите и добавьте файлы под названиями: stop и play.

Теперь в файле activity_main.xml необходимо написать следующий код:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Spinner
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:id="@+id/sfx"
        android:layout_margin="16dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        />

    <LinearLayout
        android:id="@+id/buttonsContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent">

        <ImageButton
            android:id="@+id/playMusic"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:onClick="playMusic"
            android:src="@drawable/ic_play_arrow_black_24dp" />

        <ImageButton
            android:id="@+id/stopMusic"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:onClick="stopMusic"
            android:src="@drawable/ic_stop_black_24dp" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 Макеты готовы теперь откройте файл MainActivity.java и напишите там следующий код:

public class MainActivity extends AppCompatActivity {
    // Переменная для хранения ссылки на проигрываемый звуковой эффект
    SoundPool sp;
    // Переменная для хранения идентификатора файла получаемого через загрузку файла в SoundPool
    int fileId;
    // После проигрывания файла необходимо сохранить идентификатор потока, для возможности приостановки проигрывания в дальнейшем
    int musicStream;
    ArrayAdapter<String> adapter;
    // Массив для хранения названия файлов в папке assets
    List<String> names = Arrays.asList("zing.mp3", "sfx2.wav");
    // Список для выбора необходимого звукового эффекта
    Spinner spinner;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        spinner = findViewById(R.id.sfx);
        // Адаптер для хранения списка эффектов
        adapter = new ArrayAdapter<>(this, R.layout.spinner_item, names);
        spinner.setAdapter(adapter);

        // Начиная с версии 5 ОС Андроид были добавлены изменения в класс SoundPool, поэтому надо проверить текущую версию ОС и если текущая версия выше версии 5 чьё кодовое название Lollipop, тогда использовать новый вариант иначе использовать старый вариант использования класса SoundPool
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // Нужно в первую очередь определить следующие атрибуты для класса SoundPool
            AudioAttributes attributes = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .build();
            // Здесь указать максимальное число потоков через функцию setMaxStreams и передачей целого числа для указания количества потоков и установить атрибуты через функцию setAudioAttributes
            sp = new SoundPool.Builder()
                    .setMaxStreams(1)
                    .setAudioAttributes(attributes)
                    .build();

        } else {
            // Для версий ОС Андроид ниже 5 создание объекта класса SoundPool легче, нужно лишь в конструкторе передать необходимые значения, максимальное число потоков, тип потока и качество звука
            sp = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
        }

        // При выборе какого-нибудь эффекта из списка выбрать его из папки assets и присвоить идентификатор переменной fileId
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                try {
                    // Для получения доступа к файлам хранимым в папке assets необходимо получить к ним доступ через контекст.
                    AssetManager assetManager = MainActivity.this.getAssets();
                    // Создать объект класса AssetFileDescriptor для хранения ссылки на запрашиваемый дескриптор файла
                    AssetFileDescriptor descriptor;

                    // Через экземпляр класса AssetManager теперь необходимо получить дескриптор файла по его названию. Название получается из массива через индекс, индекс получается через функцию spinner.getSelectedItemPosition
                    descriptor = assetManager.openFd(names.get(spinner.getSelectedItemPosition()));
                    fileId = sp.load(descriptor, 0);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });

    }

    // При клике на кнопку запустить воспроизведение. Обработчик установлен через атрибут onClick в файле макета.
    public void playMusic(View v) {
        musicStream = sp.play(fileId, 1, 1, 0, 0, 1);
    }
    // При клике на кнопку остановить воспроизведение
    public void stopMusic(View v) {
        sp.stop(musicStream);
    }
}

 Запустите приложение и посмотрите на результат, вернее послушайте результат.