我正在尝试编写一个 Android 应用程序,除其他外,它还可以用作节拍器。这需要能够以准确的时间周期性地播放各种声音,但现在我正在努力让一个声音工作。训练有素的听众很容易检测到超过 2-3 毫秒的错误。这是我的播放主题:
protected void onCreate(Bundle savedInstanceState) {
// stuff
int minBufferSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
}
public void startThread(View view){
mThread = new Thread (new Runnable() {
public void run(){
Log.d(TAG, "Starting met at tempo: " + mTempo + " Interval: " + (60000 / mTempo) );
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
InputStream in = getResources().openRawResource(R.raw.chirp);
byte [] output = getBytesFromStream(in);
Log.d(TAG, "Output length: " + output.length);
mAudioTrack.play();
while (mRunning) {
mAudioTrack.write(output, 0, output.length);
try {
Thread.sleep(60000 / mTempo);
} catch (InterruptedException e) {
}
}
mAudioTrack.release();
}
});
mThread.start();
}
R.raw.chirp 是在 Audacity 中生成的正弦波。 1000hz 以 44100hz 采样 100ms,所以它应该是大约 4410 个样本。在 Nexus 4 上进行测试,速度设置为 200bpm(即 chirp 应该每 300ms 播放一次)声音看起来不错,但为了更好的测量,我通过线路输入将输出记录到 Audacity 中。我注意到两个奇怪的地方:
1) 唧唧声似乎在 298-299 毫秒之间的某个时间段内播放。这在很大程度上是一致的,但是 1-2 毫秒将速度更改为 201bpm,这足以使其与引用节拍器不同步。
2) 第二个奇怪的现象并不是始终可重复的,但是在看似随机数量的线性调频之后,会有一个延迟整整 20 毫秒的线性调频。
第二个问题我可以想当然地解释掉;也许是线程/优先级问题?然而,第一个问题让我感到困惑。我想如果有的话,唧唧声应该迟到,而不是早。我相信这些问题是相关的,而且我也开始认为我没有正确理解正在发生的事情并且正在做一些非常基本的错误(这是我第一次使用任何类型的音频代码。)
如有任何帮助,我们将不胜感激。
最佳答案
除了目前使用的时序源不佳之外,还有一个更大的问题,即 Android 在请求播放的时间和播放实际发生的时间之间仅提供相对松散且变化很大的耦合。
要解决这个问题,请连续播放声音 - 大部分是静音 - 并在所需的时间混合所需的点击。不要使用任何普通的软件可访问系统时钟来确定时间,而是使用先前写入的样本计数,这样您的时序将与数模转换器采样时钟有固定的关系。
关于android - 在Android上准确播放周期性声音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17800776/