繁体   English   中英

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

[英]How to play sounds at accurate periods of time across different devices in Android

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

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

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

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

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

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

我似乎无法在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);
}

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

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

这里有一些代码来说明我在说什么。

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

    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以持续播放相同的声音。 我这样写的是能够按顺序播放不同的声音。

我在开发使用声音和类似内容的应用程序时遇到了很多问题。 我不建议您使用SoundPool,因为它受到bug影响,并且还要注意SoundPool的循环声音在4.3及更高版本的设备上不起作用,请参阅AOSP - 问题跟踪器中的这个未解决的问题 我认为解决方案是本地化并使用OpenSL ES类似的库。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM