如何在不复制的情况下将本机void指针传递给Dart Isolate



我正在为Dart公开一个音频库(C库(。要触发音频引擎,它需要几个初始化步骤(UI不阻塞(,然后通过执行功能触发音频处理,即阻塞(音频处理是一项繁重的任务(。这就是为什么我来读有关达特分离株的文章。

我的第一个想法是,我只需要在隔离中调用性能方法,但这似乎不可能,因为perform函数将引擎状态作为第一个参数——这个引擎状态是一个不透明的指针(dart:ffi中的指针(。当试图通过计算函数将引擎状态传递给新的隔离区时,Dart VM返回一个错误——它无法将C指针传递给隔离。

我找不到将这些数据传递给隔离区的方法,我想这是由于主隔离区和我正在创建的隔离区的内存不同。

因此,我可能应该在隔离中管理整个发动机状态,这意味着:

  • 创建引擎状态
  • 用一些选项(字符串(初始化它
  • 触发执行功能
  • 运行时控制音频

我找不到任何关于如何在隔离中执行此操作的示例,而是从主线程/隔离中触发的。也不知道如何管理隔离内存(保持引擎状态并使用它(。我当然可以做

下面是我想做的一个非孤立的例子:

Pointer<Void> engineState = createEngineState();
initEngine(engineState, parametersString);
startEngine(engineState);
perform(engineState);

在运行时,由UI操作触发(如滑块值更改或按钮点击(:

setEngineControl(engineState, valueToSet);
double controleValue = getEngineControl(engineState);

引擎状态可以封装在一个类中,我认为这在这里并不重要。无论是类还是不透明的数据类型,我都找不到如何管理和保持这种状态,以及从主线程执行触发器(隔离处理(。知道吗?

提前,谢谢。

附言:我在写作时注意到,我的问题/解释可能不准确,我不得不说我有点迷失在这里,因为我从未使用过飞镖隔离物。如果缺少一些信息,请告诉我。

4月24日编辑:它似乎是在隔离中创建和管理对象状态。但主要问题并没有得到解决。由于perform方法在未完成时实际上是阻塞的,因此无法在隔离中接收消息。我首先想到的一个选项是使用performBlock方法,它只执行一个音频样本块。像这样:

while(performBlock(engineState)) {
// listen messages, and do something
}

但这似乎不起作用,过程仍然被阻止,直到音频表演结束。即使这个循环是在隔离中的异步方法中调用的,它也会阻塞,并且不会读取任何消息。

我现在考虑将主隔离中管理的Pointer<Void>传递给另一个工人的可能性(仅用于执行方法(,然后能够从主隔离中触发一些控制方法。

隔离Dart包提供了一个注册表子库来管理一些共享内存。但在隔离区之间传递void指针仍然是不可能的。

[错误:flutter/lib/ui/ui _dart_state.cc(157(]未处理的异常:无效参数:本地对象(来自dart:ffi((如指针和结构(不能在隔离之间传递。

有人遇到过这种情况吗?

可以获取此Pointer指向的地址作为数字,并根据该地址构造新的Pointer(请参见Pointer.addressPointer.fromAddress()(。由于数字可以在隔离区之间自由传递,因此可以用于在它们之间传递本机指针。

例如,在您的情况下,可以这样做(我使用Flutter的计算使示例更简单,但显然也可以显式使用Send/ReceivePort(

// Callback to be used in a backround isolate.
// Returns address of the new engine.
int initEngine(String parameters) {
Pointer<Void> engineState = createEngineState();
initEngine(engineState, parameters);
startEngine(engineState);
return engineState.address;
}
// Callback to be used in a backround isolate.
// Does whichever processing is needed using the given engine.
void processWithEngine(int engineStateAddress) {
final engineState = Pointer<Void>.fromAddress(engineStateAddress);
process(engineState);
}
void main() {
// Initialize the engine in a background isolate.
final address = compute(initEngine, "parameters");
final engineState = Pointer<Void>.fromAddress(address);
// Do some heavy computation in a background isolate using the engine.
compute(processWithEngine, engineState.address);
}

我最终在音频循环内部处理回调。

while(performAudio())
{
tasks.forEach((String key, List<int> value) {
double val = getCallback(key);
value.forEach((int element) {
callbackPort.send([element, val]);
});
});
}

其中"val"是要发送到回调的内容。int"value"的列表是回调索引的列表。

假设您的音频循环以512个样本的矢量大小执行,那么在每处理512个音频样本后,您将能够通过回调,这意味着每秒48000/512次(假设采样率为48000(。这种方法不是最好的方法,但它有效,不过我仍然要看看它是否在非常密集的处理环境中有效。在这里,它被认为用于实时音频,但它也可以用于音频渲染。

您可以在此处查看完整代码:https://framagit.org/johannphilippe/csounddart/-/blob/master/lib/csoundnative.dart

最新更新