具有高速的Java节拍器

Mac*_*cks 5 java audio jmf

作为一个练习我正在尝试使用Thread.sleep作为计时器创建一个节拍器,使用JMF作为声音.它运作得很好,但由于某种原因,JMF似乎只播放每分钟最多207次的声音.

来自我的节拍器课程:

public void play() {
    soundPlayer.play();
    waitPulse();        
    play();
}
Run Code Online (Sandbox Code Playgroud)

从我的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();
    }   
}
Run Code Online (Sandbox Code Playgroud)

我已经将SoundPlayer.play()作为一个线程来测试它是否会产生影响,但事实并非如此.我可以很容易地将速度改变到大约207bpm,但即使我将我的计时器设置为1000bpm,声音的播放速度也不会超过207bpm.

我把System.out.println("Click");我的ThreadPlayer.run()放在里面来检查我的循环是否正常工作 - 确实如此.

这个问题似乎与我对JMF的实施有关.我很确定有一个简单的解决方案,任何人都可以帮助我吗?

非常感谢你的帮助!:)

Bjo*_*che 9

关于Thread.sleep()不可靠的答案是正确的:你不能指望它完全返回你指定的时间.事实上,我很惊讶你的节拍器可以使用,特别是当你的系统负载不足时.阅读Thread.sleep()的文档以获取更多详细信息.关于MIDI的Max Beikirch的答案是一个很好的建议: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;
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用setBPM设置速度后,可以重复调用generateMetronomeSamples()函数生成的样本,并使用JavaSound将输出流式传输到扬声器.(请参阅JSResources.org以获得一个好的教程)

一旦你有了这个工作,你就可以用从WAV或AIFF或短音或其他任何东西获得的声音来代替刺耳的咔嗒声.