我正在开发一个节拍器应用程序。用户可以在运行时选择bpm,我的应用程序将相应地播放“tick”声音。 “tick”是单个节拍器“shot”(mp3)。我尝试使用Handler和MediaPlayer实现它,但节拍器根本不精确。 所以我想改变整个方法:当用户选择一个新的bpm值时,我通过每隔N毫秒重复一次嘀嗒声X次来合成一个新的声音,然后循环这个运行时创建的声音。 这是一个有效的选择吗?如何在Android中实现?
答案 0 :(得分:6)
循环通过合成声音的替代方案似乎是目前最好的选择。在Google I / O 2013上有一个关于音频的伟大的会话叫做High Performance Audio,我当然建议观看,以便更深入地了解系统如何工作以及开发人员在遇到什么问题时处理音频延迟。大约在视频的17:00,有一个图表显示jitter与回调。在一个不存在的完美世界中(哦真的吗?),对于所有预定的音频回调,抖动将为零。但事实并非如此,因为图表中的数据是使用未指定的ICS设备制造的,因此存在高达35毫秒甚至更高的抖动,并且肯定会出现更糟糕的情况。
因此,由于节拍器是精密工具而且这些抖动并不好,所以应该将预定的播放方法放在一边。我甚至使用AudioTrack
使用合成声音制作了一个相当可靠的节拍器工作。
希望它有所帮助^^
答案 1 :(得分:1)
您可以尝试在TimerTask上使用Timer计划执行固定费率。
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.
然后,当您需要更改BPM时,可以取消TimerTask。
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;
}
}
答案 2 :(得分:0)
当播放此播放声音时
mSoundManager.playSound(1);
Android会等到该通话结束后再拨打
mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200);
但是,如果您撤消这些调用,您可能会发现时间更准确。
mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200);
mSoundManager.playSound(1);
你不能指望你的声音花费相同的时间来播放,所以告诉处理程序首先发布会更好一些。然而,仍然不理想。