如何在Android中的不同设备上以准确的时间段播放声音

时间:2013-09-25 16:27:49

标签: android soundpool

我正在开发Android游戏,但我遇到了一个非常烦人,难以发现的错误。问题在于,当您使用SoundPool播放声音时,您实际上可以循环播放您正在播放的任何声音。在这种情况下,问题是“跑步”声音;当主角运行时,这个声音会很快地连续执行(大约每400ms个)。

现在,当在常规(不那么强大)设备上播放声音时,例如三星SII,声音每500ms播放一次 - 但是,如果我在另一台设备上运行相同的代码(比方说,三星SIV,三星SIII),声音会播放两倍甚至三倍。

看起来设备硬件规格越强大,它的播放速度就越快。在某些设备上,它播放速度非常快,几乎可以听到一个稳定的连续声音。我一直在寻找在声音播放之间的时间段设置特定比率的技术,但它不能正常工作,问题仍然存在。有没有人知道如何修复它,使用SoundPoolMediaPlayer或Android上的任何其他声音控制API?

5 个答案:

答案 0 :(得分:1)

仅仅循环或使用常规间隔来处理类似脚步声的问题是,您可能会将声音和视觉效果分离。如果您的声音延迟或加速,或者您的视觉效果被延迟或加速,则您必须动态且自动地调整该延迟。你已经有了这个问题

更好的解决方案是在触发声音的精确事件上放置一个触发器(在这种情况下,脚被放下),然后播放声音。这也意味着如果您有多个声音源(如多个足迹),则无需以正确的间隔手动启动声音。

答案 1 :(得分:0)

您可以使用AudioTrack来播放连续的PCM数据流,因为您可以确定声音之间的时间间隔传递流。首次启动声音时可能会有一点延迟,但这取决于最小缓冲区大小,我认为这取决于Android版本和设备。在我的galaxy s2 android 4.1上它大概是20ms。
如果你认为这可以是一个选项,我可以发布一些代码

答案 2 :(得分:0)

我无法在Galaxy Nexus和Nexus S上复制这个问题,这是否意味着我修复了它?或者也许您可以通过以下方式展示您的不同之处:

SoundPool soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
Integer sound1 = soundPool.load(this, R.raw.file1, 1);
Integer sound2 = soundPool.load(this, R.raw.file2, 1);
playSound(sound1);

public void playSound(int sound) {
  AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
  float volume = mgr.getStreamVolume(AudioManager.STREAM_MUSIC)
               / mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);  
  soundPool.play(sound, volume, volume, 1, -1, 1.0f);
}

答案 3 :(得分:0)

如果问题是你想要控制离散声音之间的间隔,最简单的方法是使用处理程序。

基本上你开始一个声音播放,这是一个异步过程。然后使用处理程序安排消息在将来的某个时间播放下一个声音。要做到正确需要一些反复试验,但是你可以保证声音会在每个设备上的前一​​个声音之后的同一时间间隔开始。

以下是一些代码来说明我在说什么。

以下是您可以使用的处理程序实现:

    handler = new Handler() {

        /* (non-Javadoc)
         * @see android.os.Handler#handleMessage(android.os.Message)
         */
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == NEXT_ITEM_MSG) {
                playNextSound();
            }
            else if (msg.what == SEQUENCE_COMPLETE_MSG) {
                // notify a listener
                listener.onSoundComplete()
            }
        }
    };

然后你可以这样写playNextSound:

private void playNextSound() {
    if (mRunning) {
        // Get the first item
        SoundSequenceItem item = currentSequence.getNextSequenceItem();
        if (item == null) {
            Message msg = handler.obtainMessage(SEQUENCE_COMPLETE_MSG);
            handler.sendMessage(msg);
            return;
        }
        // Play the sound
        int iSoundResId = item.getSoundResId();
        if (iSoundResId != -1) {
            player.playSoundNow(soundResId);
        }

        // schedule a message to advance to next item after duration
        Message msg = handler.obtainMessage(NEXT_ITEM_MSG);
        handler.sendMessageDelayed(msg, item.getDuration());
    }
}

并且您的SoundSequenceItem可能只是一个具有声音文件资源ID和持续时间的简单类。如果你想在角色移动时继续播放声音,你可以这样做:

public void onSoundComplete() {
    if (character.isRunning()) {
        currentSequence.addSequenceItem(new SoundSequenceItem(R.id.footsteps,500);
        playNextSound();
    }
}

或者您可以修改playNextSound以持续播放相同的声音。我这样编写的是能够按顺序播放不同的声音。

答案 4 :(得分:0)

我在开发使用声音和类似内容的应用程序时遇到了很多问题。我不建议您使用SoundPool,因为它受到bug影响,并且还要注意使用SoundPool的循环声音不适用于4.3及更高版本的设备,请参阅this open issue, at AOSP - Issue tracker。 我认为解决方案是本地化并使用OpenSL ES类似的库。