2014-02-15 88 views
2

我試圖開始使用反應香蕉並且想要創建一個簡單的合成器。有很多GUI示例,但我無法將它們應用於音頻。由於音頻API有回調,說「給我n樣本的音頻」我想我應該觸發一個事件每個回調(使用snd部分的newAddHandler返回),其中包含要生成的樣本數量,一個指針應該寫在哪裏和定時信息來協調MIDI事件。傳遞給reactimate的IO操作會將樣本寫入指針。 MIDI事件將同樣從另一個回調中觸發,並且還包含時間信息。使用反應香蕉的Haskell中的音頻合成

然而,這是我卡住的地方。我猜音頻信號應該是一種行爲,但是如何在合適的時間內「運行」一個行爲來獲取樣本?適當的課程取決於兩個音頻回調之間可能發生的MIDI事件。

+0

它不是反應性香蕉,但仍可能是有趣的:關於時間的注意事項在http://hackage.haskell.org/package/live-sequencer-0.0.4/src/src/Event.hs – d8d0d65b3f7cf42

回答

1

要解決這樣的問題,我覺得有用的是採取語義視點:什麼是音頻信號?我可以用什麼類型來表示它?

從本質上講,音頻信號是一個隨時間變化的幅度

Audio = Time -> Double 

這表明表示作爲行爲

type Audio = Behavior Double 

然後,我們可以使用<@>組合子在查詢振幅特定時刻,即任何事件發生時。然而,出於效率原因,音頻數據通常存儲在64字節(或128,256)的中。畢竟,處理過程需要快速,並且使用緊密的內部循環非常重要。這表明對音頻數據作爲一個行爲

type Audio = Behavior (Vector Double) 

其值是音頻數據的64分字節的塊,並且每當改變對應於64個字節的時間段是在建模。

連接到其他API只在語義模型澄清後才能完成。在這種情況下,將行爲中的音頻數據寫入緩衝區似乎是個好主意,緩衝區的內容在外部API調用回調時顯示。


順便說一下,我不知道reactive-banana-0.8是否足夠快,但對樣本級音頻處理有用。它不應該太糟糕,但你可能不得不選擇一個相當大的塊大小。

+0

無視表現並假設Behavior Double,你和John L所說的是一個時鐘機制觸發每個樣本的事件(例如每秒44100次)? – absence

+0

擁有一個時鐘事件每秒觸發X次是一種很好的方式,可以構建每秒X次更改的行爲。 –

+0

太棒了!我開始看到這些作品是如何融合在一起的,但似乎仍然有一些重要的我錯過了。我如何製作時鐘事件?看看可用的函數,我得到的最接近的結果是使用replicate獲得[()],我可以在「生成n個樣本」事件上泄漏,應用,收集並最終生成fmap,但我猜測一個緩衝區中的所有事件都會共享相同的時間戳,即「生成n個採樣」事件發生時的測量時間,而不是t/sampleRate,其中t = [0 ..]。 – absence

2

假設其意圖是做一些生活,我認爲爲每個回調觸發一個事件將是極其有限的。大多數音頻API希望這些回調將很快返回(例如,通常您不會調用malloc或在一箇中阻止IO)。發射FRP事件可能適用於非常簡單的處理,但我認爲如果您嘗試進行更復雜的處理,則會在音頻流中出現輟學現象。我期望一個更可行的方法是自己觸發事件(通過時鐘或響應GUI事件等)並生成音頻緩衝區,並從該緩衝區讀取回調API。我知道一些音頻API(例如portaudio)具有緩衝模式,可以自動處理這些模式。儘管如果你擁有的只是一個回調API,在其上添加一個緩衝區並不難。

+1

謝謝,可能在回調中觸發的事件應該意味着「填充下一個緩衝區」而不是當前緩衝區,然後由前一個回調函數填充的緩衝區可以切換到API以避免停滯(響應系統時鐘的激發事件導致漂移並最終退出)。無論事件發生在哪裏,最大的問題是我的問題第二段中描述的反應性香蕉部分。 – absence

+0

@absence我懷疑你會想要一個類似「Behavior Builder」的類型(例如使用來自最新的'bytestring'的Builder)。您可以累積事件以創建該行爲,並添加音頻輸入,並使輸出回調將其刷新。 –

+0

使用Builder會讓我使用反應香蕉來計算樣本嗎?我想擁有信封,振盪器,濾波器等的行爲(它們基本上都是時間 - >雙精度),我仍然不明白如何從中取樣。 – absence