作为一种练习,我正在尝试使用Thread.sleep作为计时器和JMF来使用Java创建一个节拍器。效果很好,但是由于某些原因,JMF似乎只能以每分钟207拍的最大速度播放声音。

来自我的节拍器类:

public void play() {
    soundPlayer.play();
    waitPulse();
    play();
}

在我的SoundPlayer类中:
public void play() {
    new Thread(new ThreadPlayer()).start();
}

private class ThreadPlayer implements Runnable {
    public void run() {
        System.out.println("Click");
        player.setMediaTime(new Time(0));
        player.start();
    }
}

我已经使SoundPlayer.play()作为线程来测试它是否会有所作为,但事实并非如此。 我可以轻松地将速度更改为大约207bpm,但是即使将计时器设置为1000bpm,声音的播放速度也不会比大约207bpm快。

我已将System.out.println("Click");放入ThreadPlayer.run()内,以检查我的循环是否正常工作。

问题似乎与我对JMF的实现有关。我很确定有一个简单的解决方案,有人可以帮助我吗?

非常感谢你的帮助! :)

最佳答案

关于Thread.sleep()不可靠的答案是正确的:您不能指望它在指定的时间内完全返回。实际上,令您惊讶的是您的节拍器完全可用,尤其是当您的系统处于负载状态时。有关更多详细信息,请阅读Thread.sleep()的文档。 Max Beikirch关于MIDI的答案是一个很好的建议:MIDI可以很好地处理定时。

但是您会问如何使用音频。诀窍是打开音频流,并在节拍器声之间保持静音,然后将节拍器声插入所需的位置。当您执行此操作时,您的声卡将以恒定速率播放样本(无论它们包含喀嗒声还是无声)。这里的关键是保持音频流打开,并且永远不要关闭它。因此,时钟是音频硬件,而不是系统时钟,这是一个细微但重要的区别。

因此,假设您正在以44100 Hz的频率生成16位单声道采样。这是一项功能,可以按要求的速率产生咔嗒声。请记住,这种喀哒声对扬声器(和您的耳朵)不利,因此,如果您实际使用它,请以较低的音量播放。 (此外,此代码未经测试-只是为了演示该概念)

int interval = 44100; // 1 beat per second, by default
int count = 0;
void setBPM( float bpm ) {
    interval = ( bpm / 60 ) * 44100 ;
}
void generateMetronomeSamples( short[] s ) {
    for( int i=0; i<s.length; ++i ) {
       s = 0;
       ++count;
       if( count == 0 ) {
          s = Short.MAX_VALUE;
       }
       if( count == interval ) {
          count = 0;
       }
    }
}

一旦使用setBPM设置了速度,就可以回放通过重复调用generateMetronomeSamples()函数生成的样本,然后使用JavaSound将输出流传输到扬声器。 (有关良好的教程,请参见JSResources.org)

工作完成后,您可以用从WAV或AIFF或短音等获得的声音来代替刺耳的咔嗒声。

关于高速Java节拍器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14164921/

10-09 04:51