Воспроизводить звук каждые N миллисекунд

Я разрабатываю приложение метронома. Пользователь может выбрать во время выполнения bpm, и мое приложение будет воспроизводить звук "тика". "Тик" - это один метроном "выстрел" (mp3). Я попытался реализовать его с помощью Handler и MediaPlayer, но метроном не совсем точен. Поэтому я подумал об изменении целого подхода: когда пользователь выбирает новое значение bpm, я синтезирую новый звук, повторяя звук тика X раз в каждые N миллисекунд, а затем перебираю звук, созданный во время исполнения. Является ли это подходящей альтернативой? Как это можно реализовать в Android?

Ответ 1

Альтернатива петли через синтезированный звук кажется лучшим выбором на данный момент. Была отличная сессия об аудио в Google I/O 2013 под названием High Performance Audio, которую я бы, конечно, советовал, наблюдая за более глубоким пониманием того, как работа системы и с какими проблемами сталкиваются разработчики при работе с задержкой звука. Примерно в 17:00 видео есть график, показывающий jitter по сравнению с обратными вызовами. В идеальном мире, который не существует (на самом деле?), Дрожание будет равно нулю для всех запланированных обратных вызовов звука. Но это не так, потому что есть дрожания, достигающие 35 миллисекунд или даже больше, поскольку данные на графике были сделаны с использованием неуказанного устройства ICS, и, безусловно, есть худшие сценарии.

Итак, поскольку метроном - это инструмент точности, и эти неудобства вообще не хороши, подход к плановому воспроизведению должен быть оставлен в стороне. Я даже выполнил разумную работу метронома с синтезированным звуком, используя AudioTrack.

Надеюсь, что это поможет ^^

Ответ 2

Вы можете попытаться использовать TimerTask для выполнения фиксированной скорости на Timer.

Таймер и TimerTask являются частью Android SDK (и Java SE). Выполнения не задерживаются из-за времени выполнения предыдущего события.

Timer timer = new Timer("MetronomeTimer", true);
TimerTask tone = new TimerTask(){
     @Override
     public void run(){
         //Play sound
     }
};
timer.scheduleAtFixedRate(tone, 500, 500); //120 BPM. Executes every 500 ms.

Затем вы можете отменить TimerTask, когда вам нужно изменить BPM.

tone.cancel();
tone = new TimerTask(){...}
timer.scheduleAtFixedRate(tone, 1000, 1000); //60 BPM. Executes every 1000 ms.

Другая возможность, которая может соответствовать вашим требованиям (из ваших комментариев), - это прясть потока и проверка System.nanoTime() и спящий с шагом, но вращение, когда вы приближаетесь к пробуждению.

long delayNanos = 500000000;
long wakeup = System.nanoTime() + delayNanos; //Half second from right now
long now;

while(!done){
     now = System.nanoTime();

     //If we are less than 50 milliseconds from wake up. Spin away. 
     if(now <= wakeup - 50000000){
          //Sleep in very small increments, so we don't spin unrestricted.

          Thread.sleep(10); 
     }
     if(now >= wakeup){
           //Play sound
           wakeup += delayNanos;
     }
}

Ответ 3

Когда этот звук воспроизведения называется

mSoundManager.playSound(1);

Android ждет, пока этот вызов не будет завершен, затем вы вызываете

mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200);

однако, если вы отмените эти вызовы, вы можете обнаружить, что время более точное.

mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200);
mSoundManager.playSound(1);

Вы не можете рассчитывать на то, что ваш звук занимает ровно столько же времени, чтобы играть, поэтому говорить, что обработчик для отправки сначала немного лучше. Тем не менее, он не идеален.