Android:Heart Rate BPM声音播放

时间:2014-09-01 08:05:40

标签: android audio soundpool

对于我的应用程序,我需要复制心率监测器的哔哔声,也就是说,它每分钟播放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,但这感觉就像作弊而不是解决实际问题。

1 个答案:

答案 0 :(得分:0)

这个问题在性质上类似于许多有关节拍器实施之前的问题。

准确实现这一目标的唯一方法是渲染自己的PCM流。由于系统(墙上)时钟与音频时钟不同步,因此无法可靠地使用它来插入周期性事件 - 没有一种可行的方法(例如,使用定时器睡眠专用线程)都是准确的,因为它们都是依靠准确的调度和/或运行循环不会拥挤。

而是在每N个样本渲染到PCM流之后插入事件,其中N是周期和采样率的函数。