Soundpool样本未准备好。

24

我有一个.wav文件,想在整个游戏中使用它,目前我将其加载到游戏中每个活动的onCreate()中。

 soundCount = soundpool.load(this,R.raw.count, 1);

活动开始后声音将会播放。

  soundpool.play(soundCount, 0.9f, 0.9f, 1, -1, 1f);

问题在于有时我会遇到错误 "sample x not ready"。 是否有可能在游戏启动时加载.wav文件并将其保留在内存中,以便稍后在整个游戏中使用? 或者是否有可能等待1-2秒钟,直到声音加载完成?

8个回答

38

您需要通过使用SoundPool.setOnLoadCompleteListener添加监听器来等待其完成。


7
很遗憾,setOnLoadCompleteListener在API 8之前不可用 :( 除了使用setOnLoadCompleteListener之外,是否还有其他公认的解决方案可以解决这个问题?/谢谢 - SteD

5

由于我的项目兼容Android 1.5,无法使用setOnLoadCompleteListener函数。因此,我决定延迟播放声音来解决问题。

以下是我的源代码:

    playSound_Delayed(soundId, 100);

// (..)

private void playSound_Delayed (final int soundId, final long millisec) {
    // TIMER
    final Handler mHandler = new Handler();
    final Runnable mDelayedTimeTask = new Runnable() {
    int counter = 0;
    public void run() {
        counter++;

        if (counter == 1) {
            boolean ret = mHandler.postDelayed(this, millisec); 
            if (ret==false) Log.w("playSound_Delayed", "mHandler.postAtTime FAILED!");
       } else {
           playSound(soundId);
       }
    }
    };
    mDelayedTimeTask.run();
}

1
不一定是直接相关的问题,但你可能想使用Log.w替换System.err.println,以便在LogCat中显示它。 - superjos
1
System.err.println实际上会在logcat中显示System.err标签。但我同意Log.w更加清晰。 - entropy

4
如果可能的话,您应该使用setOnLoadCompleteListener。如果不行,可以在调用“play”时将其包装在while循环中。例如:
int waitLimit = 1000;
int waitCounter = 0;
int throttle = 10;
while(soundPool.play(soundId, 1.f, 1.f, 1, 0, 1.f) == 0 && waitCounter < waitLimit)
 {waitCounter++; SystemClock.sleep(throttle);}

此方法会在10ms间隔内重试“play”操作1000次。当然,应该在非UI线程上运行,这仍然不是最理想的解决方案,但比等待任意时间并期望池准备好要强一些。


2
private SoundPool soundPool;
private int my_sound;
boolean loaded = false;

// 在构造函数中

soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
my_sound = soundPool.load(this, R.raw.blast_sound, 1);

soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
    public void onLoadComplete(SoundPool soundPool, int sampleId,int status) {
       loaded = true;
    }
});

// 然后在你想播放声音的地方,输入以下内容

if (loaded) {
soundPool.play(my_sound,  0.9f, 0.9f, 1, 0, 1f);
}

2

我用简单的do-while循环解决了这个问题。如果方法play()成功,它将返回非零的streamID,如果失败,则返回零。因此,只需要检查返回值即可。

int streamID = -1;
do {
    streamID = soundPool.play(soundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
} while(streamID==0);

2
这对您肯定有用!
public void playWavFile() {

    Thread streamThread = new Thread(new Runnable() {           

        @Override
        public void run() {                 
            SoundPool soundPool;
            final int wav;
            String path = "/mnt/sdcard/AudioRecorder/record.wav";
            soundPool = new SoundPool(5,AudioManager.STREAM_MUSIC, 0);
            wav = soundPool.load(path, 1);
            soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
                @Override
                public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
                    // TODO Auto-generated method stub
                    soundPool.play(wav,100, 100, 0, 0, 1f);
                }
            });

        }        
    });

    streamThread.start();
}   

2

SoundPool库使用MediaPlayer服务将音频解码为原始的16位PCM单声道或立体声流,我们简称为流预处理,这需要一些时间,具体取决于音频文件的大小。如果在此过程结束之前尝试播放声音,则会出现“样本x未准备就绪”的错误。

有两种解决方案:

  1. 实现setOnLoadCompleteListener
  2. 等待任意长度的时间,以便准备工作完成

然后再播放它。请注意,这种情况下没有异常抛出,应用程序也不会因为try-catch而崩溃。


1
这听起来很疯狂,但不要立即播放声音。给你的应用程序几秒钟时间来初始化SoundPool。在我的应用程序中,这正是问题所在;我添加了一个假的“加载”屏幕,大约3秒钟,然后一切都正常了。(我甚至不需要预加载声音。)

这就是我正在做的,但我发现它真的很烦人。游戏只需一秒钟即可加载。加载屏幕仅为声音而存在。 - W.K.S
@W.K.S 我也是。我有一个想法,可以把加载屏幕变成一个小游戏,或者在其中加入一些互动作为“彩蛋”,尽管它只可见几秒钟。 - ashes999

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接