简体   繁体   English

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

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

I'm developing a game in Android and I came across a very annoying, hard-to-find bug. 我正在Android中开发游戏,我遇到了一个非常烦人,难以找到的错误。 The issue is that when you are using SoundPool to play your sounds, you can actually loop whatever sound you are playing. 问题在于,当您使用SoundPool播放声音时,您实际上可以循环播放您正在播放的任何声音。 In this case, the issue is the "running steps" sound; 在这种情况下,问题是“跑步”声音; this sound gets executed quite fast and continually (around every 400ms ) when the main character is running. 当主角运行时,这个声音会很快地连续执行(大约每400ms )。

Now when playing the sound on a regular (not so powerful) device eg Samsung SII, the sound is played every 500ms - however, if I run the very same code on another device (let's say, Samsung SIV, Samsung SIII), the sound plays twice or even three times faster. 现在,当在普通(不那么强大)设备上播放声音时,例如三星SII,声音每隔500ms播放一次 - 但是,如果我在另一台设备上运行相同的代码(比方说,三星SIV,三星SIII),声音播放速度提高两倍甚至三倍。

It seems like the more powerful the device hardware specs are, the faster it plays. 看起来设备硬件规格越强大,它的播放速度就越快。 On some devices, it plays so fast that you almost hear one solid continuous sound. 在某些设备上,它播放速度非常快,几乎可以听到一个稳定的连续声音。 I've been looking for techniques to set a specific ratio on the time period between sound plays, but it doesn't work properly and the issue remains. 我一直在寻找在声音播放之间的时间段设置特定比率的技术,但它不能正常工作,问题仍然存在。 Does anyone know how to fix it, either using SoundPool , MediaPlayer , or any other sound-controlling API on Android? 有没有人知道如何修复它,使用SoundPoolMediaPlayer或Android上的任何其他声音控制API?

The problem with just looping or using a regular interval for something like footsteps is that you have a possible decoupling of sound and visuals. 仅仅循环或使用常规间隔来处理类似脚步声的问题就是您可能会将声音和视觉效果分离。 If your sound gets delays or sped up, or your visuals get delayed or sped up, you would have to adjust for that delay dynamically and automatically. 如果您的声音延迟或加速,或者您的视觉效果被延迟或加速,则您必须动态且自动地调整该延迟。 You already have that issue right here 你已经有了这个问题

A better solution would be to place a trigger on the exact event which should trigger the sound (in this case, the foot being placed down), which then plays the sound. 一个更好的解决方案是在触发声音的确切事件上放置一个触发器(在这种情况下,脚被放下),然后播放声音。 This also means that if you have multiple sources of the sound (like multiple footsteps), you don't have to manually start the sound with the right interval. 这也意味着如果您有多个声源(如多个足迹),则无需以正确的间隔手动启动声音。

You could use an AudioTrack to play a continuous stream of PCM data, since you would pass a stream you could be sure about the interval between sounds. 您可以使用AudioTrack播放连续的PCM数据流,因为您可以传递一个流,您可以确定声音之间的间隔。 the downside could be a little delay when first starting the sound but it depends on the minimum buffer size, and it depends, I think, on android version and device. 首次启动声音时可能会有一点延迟,但这取决于最小缓冲区大小,我认为这取决于Android版本和设备。 On my galaxy s2 android 4.1 it was about 20ms. 在我的galaxy s2 android 4.1上大概是20ms。
if you think this could be an option I can post some code 如果您认为这可以是一个选项,我可以发布一些代码

I can't seem to replicate the issue on Galaxy Nexus and Nexus S, does that mean I fixed it? 我似乎无法在Galaxy Nexus和Nexus S上复制这个问题,这是否意味着我修复了它? Or maybe you could show what you're doing differently from this: 或者也许你可以通过以下方式展示你的不同之处:

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);
}

If the problem is that you want to control the interval between the discrete sounds, The easiest way to do this is with a handler. 如果问题是你想要控制离散声音之间的间隔,最简单的方法是使用处理程序。

Basically you start a sound playing which is an asynchronous process. 基本上你开始一个声音播放,这是一个异步过程。 Then you use a handler to schedule a message to play the next sound sometime in the future. 然后使用处理程序安排消息在将来的某个时间播放下一个声音。 It will take some trial and error to get it right, but you will be guaranteed that the sound will start at the same interval after the previous sound on every device. 要做到正确需要一些反复试验,但是你可以保证声音会在每个设备上的前一​​个声音之后的相同间隔开始。

Here is some code to illustrate what I am talking about. 这里有一些代码来说明我在说什么。

Here is a handler implementation you could use: 这是您可以使用的处理程序实现:

    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()
            }
        }
    };

Then you could write playNextSound like this: 然后你可以像这样写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());
    }
}

and your SoundSequenceItem could just be a simple class that has a sound file resource id and a duration. 并且您的SoundSequenceItem可能只是一个具有声音文件资源ID和持续时间的简单类。 If you want to keep playing the sound while the character is moving you could do something like this: 如果你想在角色移动时继续播放声音,你可以这样做:

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

Or you could modify playNextSound to continually play the same sound. 或者您可以修改playNextSound以持续播放相同的声音。 Mine is written this way to be able to play different sounds in sequence. 我这样写的是能够按顺序播放不同的声音。

I have had a lot of problems developing apps which used sounds and stuff like that. 我在开发使用声音和类似内容的应用程序时遇到了很多问题。 I would not suggest you to use SoundPool since it is bug-affected, and also be aware that looping sounds with SoundPool won't work on devices which are 4.3 and higher, see this open issue, at AOSP - Issue tracker . 我不建议您使用SoundPool,因为它受到bug影响,并且还要注意SoundPool的循环声音在4.3及更高版本的设备上不起作用,请参阅AOSP - 问题跟踪器中的这个未解决的问题 I think that the solution is to go native and use OpenSL ES o similar libraries. 我认为解决方案是本地化并使用OpenSL ES类似的库。

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

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