我正在尝试在Haskell中编写一个交互式,实时的音频合成内容,而我迫切需要"懒惰的数字"来表示时间.
事情就是这样:我的程序基于"信号"的概念,这些信号由"信号处理器"转换.但与Faust或ChucK等其他类似项目不同,我希望使用严格纯粹的功能,并且能够明确地获取时间.
我们的想法是,可以在Haksell中表达纯粹的"懒惰流处理器",并且由于懒惰的评估,它将以交互式,实时的方式工作.
例如,我可以将"midi信号"表示为音符变化事件流:
type Signal = [ (Time, Notes->Notes) ]
Run Code Online (Sandbox Code Playgroud)
这一切在非交互模式下都能很好地工作,但是当我想实时玩它时,我遇到了一个很大的障碍:在任何一个时间点,输出信号都取决于下一次输入的时间事件.所以我的合成引擎实际上会停止直到下一个事件.
让我解释一下:当我的声卡要求输出信号的样本时,懒惰的评估器遍历我的信号处理器的依赖图,并最终要求输入一个输入(midi)信号.但是,让我们说输入信号在本地看起来像这样:
input :: Signal
input = [ ..., (1, noteOn 42), (2, noteOff 42), ... ]
Run Code Online (Sandbox Code Playgroud)
当我需要在1.5时计算输出(音频)信号时,我需要这样的东西:
notesAt :: Signal -> Time -> Notes
notesAt = notesAt' noNotes where
notesAt' n ((st,sf):ss) t
| st > t = n
| otherwise = notesAt' (sf n) ss t
Run Code Online (Sandbox Code Playgroud)
...当我评估"notesAt input 1.5"时,它必须在返回之前计算"2> 1.5".但事件(2,NoteOff 42)将不会再发生0.5秒!所以我的输出依赖于将来会发生的输入事件,从而停止.
我把这种效应称为"矛盾的因果关系".
我已经考虑过如何处理这个问题很长一段时间了,我得出结论,我需要的是某种形式的数字,这将允许我懒洋洋地评估"a> b".让我们说:
bar :: LazyNumber
bar = 1 + bar
foo …Run Code Online (Sandbox Code Playgroud)