7

我目前正在从事一个个人项目:在 Flash 中创建一个用于实时音频合成的库。简而言之:将波发生器、滤波器、混频器等相互连接并为声卡提供原始(实时)数据的工具。像 max/msp 或 Reaktor 这样的东西。

我已经有一些工作的东西,但我想知道我写的基本设置是否正确。我不想以后遇到问题,迫使我改变我的应用程序的核心(尽管这总是会发生)。

基本上,我现在所做的是从链的末端开始,在(原始)声音数据“输出”(到声卡)的地方。为此,我需要将字节块(ByteArrays)写入一个对象,并且为了得到那个块,我会询问任何连接到我的“Sound Out”模块的模块给我他的块。该模块对连接到他的输入的模块执行相同的请求,并且一直发生直到到达链的开始。

这是正确的方法吗?我可以想象如果有反馈回路,或者如果有另一个没有输出的模块会遇到问题:如果我要在某个地方连接一个频谱分析仪,那将是链中的死胡同(一个没有输出的模块,只有一个输入)。在我目前的设置中,这样的模块不起作用,因为我只从声音输出模块开始计算。

有没有人有这样的编程经验?我会对有关正确方法的一些想法非常感兴趣。(为了清楚起见:我不是在寻找特定的 Flash 实现,这就是为什么我没有在 flash 或 actionscript 下标记这个问题)

4

2 回答 2

1

不久前我做了类似的事情,我使用了与您相同的方法 - 从虚拟线路输出开始,然后将信号追踪到顶部。我是按样本做的,而不是按缓冲区做的;如果我今天要编写相同的应用程序,我可能会选择 per-buffer,因为我怀疑它的性能会更好。

光谱仪被设计成一个插入模块,也就是说,它只有在输入和输出都连接的情况下才能工作,并且它将输入不变地传递到输出。

为了处理反馈,我有一个特殊的帮助模块,它引入了 1 个样本的延迟,并且每个周期只会获取一次输入。

另外,我认为使用浮点数进行所有内部处理,因此将浮点数数组作为缓冲区,这将比字节数组容易得多,并且它会为您节省在整数和浮点数之间进行转换的额外工作。

于 2010-11-30T18:29:44.003 回答
0

在以后的版本中,您的网络的不同部分可能有不同的数据包速率。

一个示例是,如果您将其扩展为将数据传输到磁盘或从磁盘传输数据。另一个例子是低数据速率控制变量,例如一个控制回声延迟,稍后可能会成为您网络的一部分。您可能不想以与处理音频数据包相同的频率处理控制变量,但它们仍然是“实时的”并且是功能网络的一部分。例如,它们可能需要平滑以避免突然的转换。

只要您以相同的速率调用所有函数,并且所有函数基本上都在花费恒定时间,那么您的数据提取方法就可以正常工作。在拉取数据和推送之间几乎没有选择余地。对于播放音频来说,拉动更自然一些,而对于录制来说,推动更自然一些,但两者都可以工作并最终对底层音频处理函数进行相同的调用。

  • 对于光谱仪,您遇到了多个数据接收器的问题,但这不是问题。从真正的接收器中引入一个虚拟链接。虚拟链接可能会导致对未兑现的数据的请求。只要 dummy link 知道它是一个 dummy 并且不关心缺少数据,一切都会好起来的。这是一种将多个接收器或源减少到一个的标准技术。

  • 使用这种网络,您不希望在一次完整更新中进行两次相同的计算。例如,如果您混合了信号的高通和低通版本,您不想对原始信号进行两次评估。您必须执行一些操作,例如为每个缓冲区记录一个计时器滴答值,并在您看到当前滴答值已经存在时停止拉取传播。同样的机制也将保护您免受评估中的反馈循环

因此,您关心的这两个问题很容易在您当前的框架内解决。

在网络的不同部分存在不同数据包速率的速率匹配是当前方法的问题所在。如果您正在将音频写入磁盘,那么为了提高效率,您将希望不经常写入大块。您不希望在这些写入期间阻止对更频繁的小型音频输入和输出处理数据包的服务。单靠单一的利率拉动或推动策略是不够的。

只是接受在某些时候您可能需要一种比单一费率网络更复杂的更新方式。发生这种情况时,您将需要针对正在运行的不同速率的线程,或者您将编写自己的简单调度程序,可能就像在 n 中调用一次不太频繁评估的函数一样简单,以使速率匹配。您无需为此提前计划。您的音频功能几乎肯定已经将确保其输入缓冲区准备好其他功能的责任委派给其他功能,并且只有那些其他功能需要更改,而不是音频功能本身。

在这个阶段我建议的一件事是要小心集中音频缓冲区分配,注意缓冲区就像栅栏一样。它们不属于音频功能,它们位于音频功能之间。集中缓冲区分配将使追溯修改网络不同部分的不同速率的更新策略变得容易。

于 2010-11-30T22:15:06.417 回答