Как воспроизводить звуки в определенные промежутки времени на разных устройствах в Android

Я разрабатываю игру для Android и наткнулся на очень раздражающую ошибку, которую трудно найти. Проблема в том, что когда вы используете SoundPool для воспроизведения своих звуков, вы можете зациклить любой воспроизводимый звук. В данном случае проблема заключается в звуке «бегущих шагов»; этот звук воспроизводится довольно быстро и непрерывно (примерно каждые 400ms), когда главный герой бежит.

Теперь при воспроизведении звука на обычном (не таком мощном) устройстве, т.е. Samsung SII, звук воспроизводится каждые 500ms - однако, если я запускаю тот же самый код на другом устройстве (скажем, Samsung SIV, Samsung SIII), звук воспроизводится в два, а то и в три раза быстрее.

Похоже, что чем мощнее аппаратное обеспечение устройства, тем быстрее оно воспроизводится. На некоторых устройствах он воспроизводится так быстро, что вы почти слышите один сплошной непрерывный звук. Я искал методы для установки определенного соотношения в период времени между воспроизведением звука, но он не работает должным образом, и проблема остается. Кто-нибудь знает, как это исправить, используя SoundPool, MediaPlayer или любой другой API управления звуком на Android?


person Martin Cazares    schedule 25.09.2013    source источник
comment
Вы нашли решение? если да то поделитесь!!   -  person lelloman    schedule 30.09.2013
comment
Я еще не искал его, если вы +1 вопрос, возможно, больше людей обратят на него внимание...   -  person Martin Cazares    schedule 30.09.2013
comment
Я не могу проголосовать больше одного раза   -  person lelloman    schedule 30.09.2013
comment
Вы можете поместить незацикленный звук внутри runnable и настроить запуск runnable через определенный интервал времени. Эта статья объясняет это: blog.nelsondev.net/?p=207   -  person Matt Logan    schedule 08.10.2013
comment
Грязным способом было бы сделать ваш звук длиннее (добавить отступы) и просто зациклить его.   -  person Sherif elKhatib    schedule 11.10.2013


Ответы (5)


Вы можете использовать AudioTrack для воспроизведения непрерывного потока данных PCM, поскольку вы бы пропустили поток, вы могли бы быть уверены в интервале между звуками. Недостатком может быть небольшая задержка при первом запуске звука, но это зависит от минимального размера буфера, и это зависит, я думаю, от версии Android и устройства. На моем Galaxy S2 Android 4.1 это было около 20 мс.
Если вы думаете, что это может быть вариантом, я могу опубликовать код.

person lelloman    schedule 25.09.2013

Проблема с простым зацикливанием или использованием регулярных интервалов для чего-то вроде шагов заключается в том, что у вас есть возможное разделение звука и визуальных эффектов. Если ваш звук задерживается или ускоряется, или ваши визуальные эффекты задерживаются или ускоряются, вам придется динамически и автоматически корректировать эту задержку. У вас уже есть эта проблема прямо здесь

Лучшим решением было бы разместить триггер на точном событии, которое должно вызвать звук (в данном случае, при опускании ноги), который затем воспроизведет звук. Это также означает, что если у вас есть несколько источников звука (например, несколько шагов), вам не нужно вручную запускать звук с правильным интервалом.

person Nzall    schedule 15.10.2013
comment
У этого подхода есть огромный пробел, поскольку не все случаи могут учитывать одно событие каждый раз, когда мне нужен звук, например. автоматическое ружье, это одно событие для срабатывания, и звук должен повторяться через определенные промежутки времени, пока палец не будет отпущен из ружья... - person Martin Cazares; 15.10.2013
comment
Если у вас есть звук автоматического оружия, зацикливание лучше, да, но если вы не делаете игру с автозапуском или приключенческую игру в стиле Myst, зацикливание звука — плохая идея. Лучше связать его с шагами, потому что таким образом звук всегда связан с шагами. Я не говорил зацикливать это все время. - person Nzall; 16.10.2013

Мне не удается воспроизвести проблему на 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);
}
person dlamblin    schedule 16.10.2013
comment
Ничего, я именно так и делаю, протестируйте на старом аппарате, и вы заметите огромную разницу в промежутке между одним звуком и следующим, у него будет большая задержка между собой, вот точно такой же код у меня. .. - person Martin Cazares; 16.10.2013

Если проблема в том, что вы хотите контролировать интервал между дискретными звуками, проще всего это сделать с помощью обработчика.

По сути, вы запускаете воспроизведение звука, которое является асинхронным процессом. Затем вы используете обработчик, чтобы запланировать сообщение для воспроизведения следующего звука когда-нибудь в будущем. Потребуются пробы и ошибки, чтобы сделать это правильно, но вы будете уверены, что звук будет начинаться с тем же интервалом после предыдущего звука на каждом устройстве.

Вот некоторый код, иллюстрирующий то, о чем я говорю.

Вот реализация обработчика, которую вы могли бы использовать:

    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 может быть просто простым классом, который имеет идентификатор ресурса звукового файла и продолжительность. Если вы хотите продолжать воспроизводить звук во время движения персонажа, вы можете сделать что-то вроде этого:

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

Или вы можете изменить playNextSound, чтобы постоянно воспроизводить один и тот же звук. Мой написан таким образом, чтобы иметь возможность последовательно воспроизводить разные звуки.

person museofwater    schedule 16.10.2013
comment
Я думал о нескольких способах сделать это вручную, таких как ThreadPools/Scheduler/Monitor и даже обработчики, однако я ищу собственный вызов в качестве ответа, делать это вручную не будет проблемой, я просто что-то ищу существующий в API - person Martin Cazares; 16.10.2013

У меня было много проблем с разработкой приложений, которые использовали звуки и тому подобное. Я бы не советовал вам использовать SoundPool, поскольку в нем есть ошибки, а также имейте в виду, что зацикливание звуков с помощью SoundPool не будет работать на устройствах версии 4.3 и выше, см. эта открытая проблема в AOSP — Система отслеживания ошибок. Я думаю, что решение состоит в том, чтобы стать родным и использовать OpenSL ES или аналогичные библиотеки.

person fiipi    schedule 30.12.2013