2011-05-16 58 views
10

我正在開發節拍器應用程序。用戶可以在運行時選擇bpm,我的應用程序將相應地播放「tick」聲音。 「嘀嗒」是一個單一的節拍器「拍」(MP3)。我試着用Handler和MediaPlayer來實現它,但節拍器並不精確。 所以我考慮改變整個方法:當用戶選擇一個新的bpm值時,我通過每N毫秒重複刻度音X次合成一個新的聲音,然後循環播放此運行時創建的聲音。 這是一個有效的選擇?它如何在Android中實現?每N毫秒播放聲音

+1

循環將再次造成不準確的延遲。因此,您需要使用長時間的節拍器聲音,時間越長越好,以避免或至少最小化循環中播放之間的延遲。然後,通過將當前時間與運行時間進行比較並推進播放流以補償偏移,使用某種方法來應對引入的延遲。 – 2011-05-16 15:53:44

+1

實際上,我通過AudioTrack實現了這一點,並且您所談論的循環延遲根本不明顯。與真正的節拍器相比,它大約爲0.5 bpm,因此它可以接受 – Raffaele 2011-05-30 18:03:28

+1

您可以向我展示一些代碼,您是如何做到這一點的?我其實很感興趣! – MartijnG 2012-05-24 22:02:41

回答

4

循環合成聲音的替代方法似乎是現在的最佳選擇。關於谷歌I/O 2013音頻的很棒的會議,名爲High Performance Audio,我當然會建議您關注一下系統如何工作以及開發人員在處理音頻延遲時會遇到什麼問題。在視頻的17:00左右,有一個圖表顯示jitter與回調。在不存在的完美世界中(哦,真的?),對於所有預定的音頻回調,抖動將爲零。但情況並非如此,因爲圖表中的數據是使用未指定的ICS設備製作的,並且肯定會出現比此更糟的情況,因爲抖動高達35毫秒甚至更大。

因此,作爲一個節拍器是一個精密工具,這些抖動並不好,預定的播放方法應該放在一邊。我甚至用AudioTrack合成了一個合理的節拍器。

希望它可以幫助^^

+3

對不起,如果這個比較晚,你可以在你使用'AudioTrack'成功實現一個節拍器的地方發佈你的代碼嗎? – 2014-09-01 03:10:12

+1

Here:http://masterex.github.io/archive/2012/05/28/android-audio-synthesis.html – 2016-03-25 10:57:34

1

您可以嘗試使用TimerTask計劃執行固定費率執行Timer

Timer和TimerTask都是Android SDK(和Java SE)的一部分。由於前一事件的執行時間,執行不會延遲。

Timer timer = new Timer("MetronomeTimer", true); 
TimerTask tone = new TimerTask(){ 
    @Override 
    public void run(){ 
     //Play sound 
    } 
}; 
timer.scheduleAtFixedRate(tone, 500, 500); //120 BPM. Executes every 500 ms. 

然後,您可以在需要更改BPM時取消TimerTask。

tone.cancel(); 
tone = new TimerTask(){...} 
timer.scheduleAtFixedRate(tone, 1000, 1000); //60 BPM. Executes every 1000 ms. 

可滿足您的要求(從您的意見)另一種可能性是紡線和檢查System.nanoTime()和增量睡覺,但是當你靠近醒來旋轉。

long delayNanos = 500000000; 
long wakeup = System.nanoTime() + delayNanos; //Half second from right now 
long now; 

while(!done){ 
    now = System.nanoTime(); 

    //If we are less than 50 milliseconds from wake up. Spin away. 
    if(now <= wakeup - 50000000){ 
      //Sleep in very small increments, so we don't spin unrestricted. 

      Thread.sleep(10); 
    } 
    if(now >= wakeup){ 
      //Play sound 
      wakeup += delayNanos; 
    } 
} 
+2

在Java風格(Timer + TimerTask)和Android之一(Handler + Message)中,線程實現不適合執行此任務。在手機上的時機是絕對不好的 – Raffaele 2011-05-16 18:11:28

+0

爲了避免關於等待的粒度問題,可能會有一個線程繼續前進(使用Thread.yield()),不斷檢查時間並喚醒另一個線程。可能是一個解決方案。 – 2011-05-16 18:14:38

+0

@chris已經嘗試過了。不解決問題 – Raffaele 2011-05-16 18:22:21

0

當這種打法的聲音稱爲

mSoundManager.playSound(1); 

Android的等待,直到通話結束,然後調用

mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200); 

但是,如果你扭轉這些來電,您可能會發現時間更準確。

mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200); 
mSoundManager.playSound(1); 

你不能在你的聲音究竟服用的時間來播放相同的量,所以告訴處理後先算好一點。然而,仍然不理想。