6

I get this error hundreds of times when I run in Debug, it doesn't seem to affect the program, but how do I get rid of it?

I know it can be traced back to the SoundPool based on other posts

09-15 09:03:09.190: ERROR/AudioCache(34): Heap size overflow! req size: 1052672, max size: 1048576

GideonKain
  • 744
  • 1
  • 12
  • 27

3 Answers3

12

SoundPool is hard code the buffer size as 1M for all loaded file. So you probably will get 'heap size overflow' error when you load too much files into SoundPool. I also have this issue on a game project that will load game sound FX into SoundPool and the solution is as follow:

  1. Play long/large background music in MediaPlayer.
  2. Play short sound files in multi SoundPool instances to prevent the heap error. The sample code for multi SoundPools:


 /**
  * Multi SoundPool to prevent memory error.
  */
public class SoundPools {

  private static final String TAG = "SoundPools";

  private static final int MAX_STREAMS_PER_POOL = 15;

  private List<SoundPoolContainer> containers;

  public SoundPools() {
    containers = Collections.synchronizedList(new ArrayList<SoundPoolContainer>());
  }

  public void loadSound(Context context, String id, String file) {
    Log.d(TAG, "SouldPools load sound " + file);
    try {
      for (SoundPoolContainer container : containers) {
        if (container.contains(id)) {
          return;
        }
      }
      for (SoundPoolContainer container : containers) {
        if (!container.isFull()) {
          container.load(context, id, file);
          return;
        }
      }
      SoundPoolContainer container = new SoundPoolContainer();
      containers.add(container);
      container.load(context, id, file);
    } catch (Exception e) {
      Log.w(TAG, "Load sound error", e);
    }
  }

  public void playSound(Context context, String id, String file) {
    Log.d(TAG, "SouldPools play sound " + file);
    try {
      for (SoundPoolContainer container : containers) {
        if (container.contains(id)) {
          container.play(context, id, file);
          return;
        }
      }
      for (SoundPoolContainer container : containers) {
        if (!container.isFull()) {
          container.play(context, id, file);
          return;
        }
      }
      SoundPoolContainer container = new SoundPoolContainer();
      containers.add(container);

      container.play(context, id, file);
    } catch (Exception e) {
      Log.w(TAG, "Play sound error", e);
    }
  }

  public void onPause() {
    for (SoundPoolContainer container : containers) {
      container.onPause();
    }
  }

  public void onResume() {
    for (SoundPoolContainer container : containers) {
      container.onResume();
    }
  }

  private static class SoundPoolContainer {
    SoundPool soundPool;
    Map<String, Integer> soundMap;
    AtomicInteger size;

    public SoundPoolContainer() {
      this.soundPool = new SoundPool(MAX_STREAMS_PER_POOL, android.media.AudioManager.STREAM_MUSIC, 0);
      this.soundMap = new ConcurrentHashMap<String, Integer>(MAX_STREAMS_PER_POOL);
      this.size = new AtomicInteger(0);
    }

    public void load(Context context, String id, String file) {
      try {
        this.size.incrementAndGet();
        soundMap.put(id, soundPool.load(context.getAssets().openFd(file), 1));
      } catch (Exception e) {
        this.size.decrementAndGet();
        Log.w(TAG, "Load sound error", e);
      }
    }

    public void play(Context context, String id, String file) {
      android.media.AudioManager audioManager = (android.media.AudioManager) context
          .getSystemService(Context.AUDIO_SERVICE);
      final int streamVolume = audioManager.getStreamVolume(android.media.AudioManager.STREAM_MUSIC);
      Integer soundId = soundMap.get(id);
      if (soundId == null) {
        soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
          @Override
          public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
            soundPool.play(sampleId, streamVolume, streamVolume, 1, 0, 1f);
          }
        });
        try {
          this.size.incrementAndGet();
          soundPool.load(context.getAssets().openFd(file), 1);
        } catch (IOException e) {
          this.size.decrementAndGet();
          Log.w(TAG, "Load/Play sound error", e);
        }
      } else {
        try {
          soundPool.play(soundId, streamVolume, streamVolume, 1, 0, 1f);
        } catch (Exception e) {
          Log.w(TAG, "Play sound error", e);
        }
      }
    }

    public boolean contains(String id) {
      return soundMap.containsKey(id);
    }

    public boolean isFull() {
      return this.size.get() >= MAX_STREAMS_PER_POOL;
    }

    public void onPause() {
      try {
        soundPool.autoPause();
      } catch (Exception e) {
        Log.w(TAG, "Pause SoundPool error", e);
      }
    }

    public void onResume() {
      try {
        soundPool.autoResume();
      } catch (Exception e) {
        Log.w(TAG, "Resume SoundPool error", e);
      }
    }
  }

}

andyao
  • 121
  • 1
  • 7
0

Maybe your media resource is too big for playing with soundpool. Soundpool only plays short sound effects. On the Soundpool API there is no specification about the maximum of a resource for playing with soundpool, but if You use it, play just short sound effects like, for example, a short explosion in a shooter game. If this is Your problem, You should use MediaPlayer for playing your sounds.

Opiatefuchs
  • 9,800
  • 2
  • 36
  • 49
0

It is not possible for this error to be about heap size, because there aren't any device with such a small heap 1052672 ~ 1Mb., but about SoundPool buffer size. SoundPool has hardcoded limit of 1Mb of raw PCM_16 data in memory. This limit applies for every sound you are loading into memory, and not for sounds in sum per one SoundPool. So the most voted answer about SoundPools is very wrong!

Let me introduce SoundPoolCompat which uses AudioTrack under the hood and have custom bufferSize. All data within specified buffer will be loaded into memory and played with small latency like SoundPool does. All data that exceed that bufferSize will be loaded on demand (which adds latency, similar to MediaPlayer). Api is very similart to SoundPool, also it is added a feature to load sounds from Uri (for example gdrive). And there is playOnce method, all resources will be unloaded after file is played.

implementation 'com.olekdia:sound-pool:3.0.2'

https://gitlab.com/olekdia/common/libraries/sound-pool

Oleksandr Albul
  • 1,611
  • 1
  • 23
  • 31