Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/audio/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 将时间建模为惰性数字_Haskell_Audio_Real Time_Lazy Evaluation_Interactive - Fatal编程技术网

Haskell 将时间建模为惰性数字

Haskell 将时间建模为惰性数字,haskell,audio,real-time,lazy-evaluation,interactive,Haskell,Audio,Real Time,Lazy Evaluation,Interactive,我想用Haskell写一个交互式实时音频合成的东西, 我迫切需要“懒惰的数字”来代表时间 事情是这样的:我的程序是基于“信号”和这些信号的概念 由“信号处理器”转换。但与其他类似项目不同,如 Faust或ChucK,我希望使用严格的纯函数,但是 明确访问时间 其思想是,可以在中表达纯粹的“惰性流处理器” Haksell和由于懒惰的评估,这将在交互环境中工作, 实时时尚 例如,我可以将“midi信号”表示为音符流 活动: 在非交互模式下,这一切都很好,但当我想 实时使用它,我遇到了一个大障碍:在任

我想用Haskell写一个交互式实时音频合成的东西, 我迫切需要“懒惰的数字”来代表时间

事情是这样的:我的程序是基于“信号”和这些信号的概念 由“信号处理器”转换。但与其他类似项目不同,如 Faust或ChucK,我希望使用严格的纯函数,但是 明确访问时间

其思想是,可以在中表达纯粹的“惰性流处理器” Haksell和由于懒惰的评估,这将在交互环境中工作, 实时时尚

例如,我可以将“midi信号”表示为音符流 活动:

在非交互模式下,这一切都很好,但当我想 实时使用它,我遇到了一个大障碍:在任何一个时间点, 输出信号取决于下一个输入事件的时间。所以 我的合成引擎实际上会停止,直到下一个事件

让我解释一下:当我的声卡要求我的输出信号样本时, 惰性计算器遍历我的信号处理器和处理器的依赖关系图 最终要求输入一段(midi)信号。但让我们说 本地输入信号如下所示:

input :: Signal
input = [ ..., (1, noteOn 42), (2, noteOff 42), ... ]
notesAt :: Signal -> Time -> Notes
notesAt = notesAt' noNotes where
    notesAt' n ((st,sf):ss) t
            | st > t = n
            | otherwise = notesAt' (sf n) ss t
当我需要在时间1.5计算输出(音频)信号时,我需要 大概是这样的:

input :: Signal
input = [ ..., (1, noteOn 42), (2, noteOff 42), ... ]
notesAt :: Signal -> Time -> Notes
notesAt = notesAt' noNotes where
    notesAt' n ((st,sf):ss) t
            | st > t = n
            | otherwise = notesAt' (sf n) ss t
。。。当我评估“NotesatInput1.5”时,它必须计算 返回前“2>1.5”。但这一事件(注42)不会发生 再过0.5秒!所以我的输出依赖于一个输入事件 将在未来发生并因此停止

我称这种效应为“自相矛盾的因果关系”

我已经思考了一段时间如何处理这个问题,我已经 得出结论,我需要的是某种形式的数字 将允许我懒散地评估“a>b”。比如说:

bar :: LazyNumber
bar = 1 + bar

foo :: Bool
foo = bar > 100
。。。然后我希望“foo”的值为真

请注意,您可以使用Peano数字来实现这一点,它实际上是有效的

但为了提高效率,我想将我的数字表示为:

data LazyNumber = MoreThan Double | Exactly Double
。。。这需要是可变的才能工作,即使每个函数 懒散的数字(例如“>”)将是纯

在这一点上,我有点迷路了。所以问题是:这可能吗 在交互式系统中实现表示时间的有效延迟数 实时应用程序

编辑 有人指出我所做的有一个名字:函数式反应式编程。Edward Amsden的论文“函数式反应式编程综述”是一篇很好的介绍。以下是摘录:

大多数FRP实施,包括迄今为止的所有信号功能实施,都屈服于事件的持续重新评估 由于系统连续对FRP表达式进行重新采样以进行输出的“基于拉动”实现而未出现。这个 关于反应式(第3.1节和第4.4节)的工作旨在解决经典FRP的这一问题,但将这项工作扩展到信号功能已被证明是可行的 尚未探索,且操作简单,发生时间短 比较依赖于程序员的检查,而且可能很难进行 证明身份以保持引用的透明度

这似乎是问题的核心:我的“虚拟事件”方法和DarkOtter的建议属于“事件未发生的持续重新评估”类别

作为一个天真的程序员,我说“让我们使用懒惰的数字,让我们让foo/bar示例工作”/我挥手。同时,我来看看YampaSynth


而且,在我看来,像我试图做的那样,使数字相对于线性时间“懒惰”与使(实数)数字相对于精度(c.f.)而言“懒惰”密切相关。我的意思是,我们希望从严格的纯上下文中使用可变对象(事件时间的下限与实数的间隔),给定一定的规则以确保我们“保持引用的透明度”。更多的手工操作,对不起。

您可以做一些类似的事情来实现(大致)最大延迟,我想这可能已经在一些FRP程序中完成了。我认为这个想法与你的建议类似,有一种类型:

data Improving n = Greater n (Improving n) | Exact n
您可以为此定义各种方便的实例,如comonad,但您要说的关键是,您必须有某种方法,在任何IO进程等待下一个midi事件发生时,它都会立即生成一对,并延迟承诺时间和事件。事件仍然只有在实际事件发生时才可用,但时间应该被篡改,以便在某个最大延迟后,它的一部分始终可用。也就是说,它等待100毫秒,然后如果事件发生,则延迟thunk变为(大于100毫秒(thunk)),下一个thunk随后以相同的方式运行。这允许您按照自己的意愿懒洋洋地进行交叉

我在旧版本的FRP库中看到过类似的操作,使用MVAR和unsafeDupablePerformIO的组合。其思想是,您有一个MVar,IO monad等待线程将其推入以发出值的信号,并且您输入的thunk使用unsafeDupablePerformIO从MVar读取(它应该是线程安全的,并且是幂等的,所以我认为这样做应该是安全的)

然后,如果等待的线程认为它太长,您只需创建另一个MVar并为下一位创建相应的thunk,然后将您的(更大(100ms)(thunk))值推到旧的值中,这允许在惰性部分继续求值

它并不完美,但它应该意味着你只需要等待,比如说100毫秒,而不是500毫秒

如果您不想混淆时间表示,我想您可以始终将midi事件流设置为(时间,可能是事件)流,然后确保生成插入事件的内容至少每x ms插入一次

编辑: 我给她举了一个简单的例子
takeB_ 10 <-< notes
query deadline = takeWhileD isJust <-< mapU (\() -> deadline) <-< notes