簡體   English   中英

具有高速的Java節拍器

[英]Java Metronome with High Speeds

作為一個練習我正在嘗試使用Thread.sleep作為計時器創建一個節拍器,使用JMF作為聲音。 它運作得很好,但由於某種原因,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()的文檔以獲取更多詳細信息。 關於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;
       }
    }
}

使用setBPM設置速度后,可以重復調用generateMetronomeSamples()函數生成的樣本,並使用JavaSound將輸出流式傳輸到揚聲器。 (請參閱JSResources.org以獲得一個好的教程)

一旦你有了工作,你可以用從WAV或AIFF或短音或其他任何東西獲得的聲音來代替刺耳的咔嗒聲。

花點時間看看MIDI吧! - http://www.ibm.com/developerworks/library/it/it-0801art38/http://docs.oracle.com/javase/tutorial/sound/TOC.html 這是與計算機制造的聲音相關的一切的最佳解決方案。

我的假設是,也許其他人可以跳到這里,是線程運行時間是線程調度程序的一時興起。 您無法保證JVM返回該線程需要多長時間。 另外,看到JVM作為機器上的進程運行,並且受操作系統進程調度程序的影響,您至少會看到兩個級別的不可預測性。

就像Jamie Duby所說的那樣,僅僅是因為你告訴線程睡眠1毫秒,並不意味着它會在一毫秒內回調。 唯一的保證是自從你調用Thread.sleep();以來至少已經過了一毫秒。 實際上,處理器無法足夠快地處理代碼以便每毫秒播放一次嗶聲,這就是您看到延遲的原因。 如果你想要一個戲劇性的例子,做一個自制的計時器類並試着讓它計算一分鍾一整分鍾,你會看到計時器關閉了很多。

真正值得回答的人是Max Beikrich,Midi是你能夠產生你想要的輸出的唯一方式。

作為一名音樂家,我有更多的經驗,而不是程序員,但我剛剛完成了一段時間的節拍器應用程序,我把項目擱置了一段時間因為我無法弄清楚為什么我遇到同樣的問題你是。 是的Thread.sleep()可能不可靠,但我設法使用該方法制作一個好的節拍器。

我看到你提到嘗試ExecutorService我不認為使用並發類將解決你的問題。 我猜這是一個系統資源問題,我很確定MIDI是節拍器的方式。 我強迫我的學生用節拍器練習並且我已經使用了很多,我從來不太關心音階的音質,時間更重要,MIDI會比任何其他音頻文件快得多。 我使用了Sound API中的javax.sound.midi庫。 我懷疑這會解決你的問題。

當正常工作時,您可能會注意到您的刻度不均勻,這是由於Thread.sleep()方法不是非常可靠。 我是如何通過使用System.nanoTime()方法而不是System.currentTimeMillis()方法在納秒內完成所有計算來解決這個問題的,只是不要忘記在將休眠時間傳遞給線程之前轉換回毫秒。 sleep()方法。

我不想在這里發布我的節拍器的代碼,以防你想要自己想出來,但如果你想看到它只是給我發電子郵件kevin.bigler3@gmail.com而且我'我很樂意發給你。 祝好運。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM