Статьи

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

Независимо от того, создали ли вы игру и хотите добавить звуковые эффекты или воспроизводить музыку из своего приложения, Android предоставляет вам несколько вариантов, а именно:

  1. Soundpool
  2. MediaPlayer

Общие рекомендации по выбору и использованию: SoundPool лучше всего подходит для коротких звуковых клипов (звуки уведомлений, звуковые эффекты в играх), а MediaPlayer лучше подходит для больших звуковых файлов,  таких как песни.

И мы хотим, чтобы все было так просто. Давайте подробнее рассмотрим эти два варианта, начиная с SoundPool. Скажем, у нас есть три коротких звуковых клипа (.WAV, .MP3. И т. Д.), Которые мы хотели бы включить в наше приложение. Сначала мы поместим их в нашу папку ресурсов приложения, создав подпапку с именем «raw». Затем мы можем получить доступ к трем звукам в нашем приложении:

package com.ts.sounds;

import android.media.SoundPool;
// other imports
// ...
public class OurSoundPlayer{

    public static final int S1 = R.raw.s1;
    public static final int S2 = R.raw.s2;
    public static final int S3 = R.raw.s3;
}

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

     private static SoundPool soundPool;
     private static HashMap soundPoolMap;

     /** Populate the SoundPool*/
     public static void initSounds(Context context) {
         soundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 100);
    soundPoolMap = new HashMap(3);     

    soundPoolMap.put( S1, soundPool.load(context, R.raw.s1, 1) );
    soundPoolMap.put( S2, soundPool.load(context, R.raw.s2, 2) );
    soundPoolMap.put( S3, soundPool.load(context, R.raw.s3, 3) );
     }

В основном мы создавали SoundPool с максимальным количеством одновременных потоков, равным 2, и типа STREAM_MUSIC, что и должно использоваться игровыми приложениями. Предполагается, что последний аргумент для конструктора касается качества преобразователя частоты дискретизации, но, похоже, не оказывает заметного влияния. Теперь мы можем добавить наш метод для воспроизведения звуков:

     /** Play a given sound in the soundPool */
     public static void playSound(Context context, int soundID) {
   if(soundPool == null || soundPoolMap == null){
      initSounds(context);
   }
        float volume = ....// whatever in the range = 0.0 to 1.0

        // play sound with same right and left volume, with a priority of 1, 
        // zero repeats (i.e play once), and a playback rate of 1f
        soundPool.play(soundPoolMap.get(soundID), volume, volume, 1, 0, 1f);
     }

Теперь мы можем воспроизводить звуки, вызывая OurSoundPlayer.playSound (..)  из любого места в нашем приложении, передавая ему контекст и звук, который мы хотим. Выглядит достаточно просто. Однако есть потенциальная проблема с использованием SoundPool, поскольку он может не воспроизводить звук, по крайней мере, не в первый раз, в то время как мы получим следующее сообщение в наших журналах Android:

«образец х не готов»

‘x’ — это soundID в пуле. Все, что мы получаем, это сообщение в журналах. Нет исключений, нет сообщений об ошибках. Android просто не воспроизводит звук. Происходит то, что   мы не можем воспроизводить звуки сразу после запуска процессов загрузки , потому что нужно перебирать список звуков, вызывая соответствующий метод SoundPool.load () . Вся коллекция семплов должна быть загружена в память, чтобы обеспечить более быстрое воспроизведение.

Есть несколько решений этой проблемы:

  1. Разрешите загрузку всех звуков, либо инициализируя пул в начале потока приложения, либо добавляя задержку в коде, например, счетчик ожидания
  2. Если мы используем Android 2.2 (API 8) и выше,  внедрите SoundPool . setOnLoadCompleteListener, чтобы проверить, когда загрузка сделана
  3. Используйте MediaPlayer. Он не использует пул и загружает нужный звук по запросу.

Первый вариант, вероятно, является наиболее распространенным, но наименее желательным, поскольку мы должны определить произвольное время, необходимое для завершения загрузки звука. Второй вариант требует от нас реализации слушателя, чтобы мы знали, когда сможем воспроизвести звук. Последнее решение с MediaPlayer вводит небольшую задержку, но решает проблему относительно простым способом. Вот исправленный   метод OurSoundPlayer.playSound (..) :

  /** Play the sound using android.media.MediaPlayer */
  public static void playSound(Context context, int soundID){
   MediaPlayer mp = MediaPlayer.create(context, soundID);  
   mp.start();
  }

MediaPlayer есть другие методы , чтобы приостановить, сбросить и освободить ресурсы , которые он использует. Как это обычно бывает, при выборе MediaPlayer вместо SoundPool существуют компромиссы. MediaPlayer медленнее, поэтому он не может быть идеальным для игр, но предоставляет ряд обратных вызовов , которые дают нам больший контроль над некоторыми событиями, такими как, например, завершение воспроизведения звука или, например, если произошла ошибка.

С SoundPool у нас нет возможности узнать, когда закончится воспроизведение звука (если мы сами не вызываем метод stop  SoundPool ). Однако мы можем одновременно воспроизводить несколько потоков, контролировать высоту звуков для доплеровских эффектов или эффектов синтеза, и циклическая обработка выходит из коробки (хотя это можно довольно легко реализовать в коде).

SoundPool оптимизирован для воспроизведения небольших звуковых эффектов. Но в то время как MediaPlayer может воспроизводить практически все, будь то локально или по сети, SoundPool ограничен небольшими локальными звуковыми файлами, поэтому, например, воспроизведение песен не разрешается, и это еще один случай, когда Android не дает нам ошибок или исключения, но просто отказывается воспроизводить звук. Добро пожаловать в SoundPool’s Sound of … Silence, который возникает, когда звук еще не загружен, а также когда звуковой файл слишком велик.

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

И MediaPlayer, и SoundPool имеют свои преимущества и недостатки. Использование одного или другого зависит от конкретного контекста, в котором используется приложение. Ничто, конечно, не мешает нам использовать оба в разных частях одного приложения.

Примечание: эта статья основана на личном опыте разработки приложений Android для наиболее распространенных версий Android на рынке (2.2 и 2.3, то есть Froyo и Gingerbread) для Samsung Galaxy .

Из блога Тони .