对于我的应用程序,我需要复制心率监测器的哔哔声,也就是说,它每分钟播放N次声音。问题是它似乎滞后了大约5ms。我知道由于操作系统的上下文切换和其他开销,我不能指望实时性能。 BPM为80时,我得到以下日志输出:
MainActivity: Beeping every 946ms
MainActivity: java.lang.InterruptedException
MainActivity: Beep 0: 951
MainActivity: Beep 1: 951
MainActivity: Beep 2: 951
MainActivity: Beep 3: 951
MainActivity: Beep 4: 951
MainActivity: Beep 5: 951
MainActivity: Beep 6: 952
MainActivity: Beep 7: 951
MainActivity: Beep 8: 954
MainActivity: Beep 9: 951
MainActivity: Beep 10: 953
MainActivity: Beep 11: 952
MainActivity: Beep 12: 951
MainActivity: Beep 13: 951
这是我用来发出哔哔声的Thread
:
mBeepThread = new Thread(new Runnable() {
@Override
public void run() {
ArrayList<Long> beepTimes = new ArrayList<Long>(5000);
try {
AssetFileDescriptor afd;
afd = getAssets().openFd("audio/heart_beep.ogg");
SoundPool sp = new SoundPool(10,AudioManager.STREAM_MUSIC,0);
int soundId = sp.load(afd, 1);
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
long duration = mp.getDuration();
long waitTime = (60000/bpmVal) - duration;
mp.release();
Log.i(TAG,"Beeping every "+waitTime+"ms");
while(true) {
beepTimes.add(SystemClock.elapsedRealtime());
sp.play(soundId,1,1,1,0,1);
Thread.sleep(waitTime);
}
} catch (IllegalStateException | IOException | InterruptedException e) {
Log.e(TAG,e.getMessage(),e);
Iterator<Long> it = beepTimes.iterator();
int count = 0;
Long oldTime = it.next();
while(it.hasNext()) {
long newTime = it.next();
long diff = newTime - oldTime;
oldTime = newTime;
Log.i(TAG,String.format("Beep %d: %d",count++,diff));
}
return;
}
}
});
mBeepThread.start();
我能做些什么来让它真实地发挥设定的BPM,或者它实际上每946毫秒播放一次,而另外5个只是制作声音播放和录音的开销?
我知道我可以从waitTime
起飞5ms,但这感觉就像作弊而不是解决实际问题。
答案 0 :(得分:0)
这个问题在性质上类似于许多有关节拍器实施之前的问题。
准确实现这一目标的唯一方法是渲染自己的PCM流。由于系统(墙上)时钟与音频时钟不同步,因此无法可靠地使用它来插入周期性事件 - 没有一种可行的方法(例如,使用定时器睡眠专用线程)都是准确的,因为它们都是依靠准确的调度和/或运行循环不会拥挤。
而是在每N个样本渲染到PCM流之后插入事件,其中N是周期和采样率的函数。