4

我正在为 Dart 公开一个音频库(C 库)。要触发音频引擎,它需要一些初始化步骤(UI 非阻塞),然后使用执行函数触发音频处理,该函数是阻塞的(音频处理是一项繁重的任务)。这就是为什么我来阅读有关 Dart 隔离的原因。

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

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

所以,我可能应该在隔离中管理整个引擎状态,这意味着:

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

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

这是我想要做的一个非孤立的例子:

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

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

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

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

提前,谢谢。

PS:我注意到,在写作时,我的问题/解释可能不准确,我不得不说我在这里有点迷路,因为我从未使用过 Dart Isolates。请告诉我是否缺少某些信息。

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

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

但这似乎不起作用,在音频性能完成之前,进程仍然被阻塞。即使在隔离中的异步方法中调用此循环,它也会阻塞,并且不会读取任何消息。

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

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

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

有没有人遇到过这种情况?

4

2 回答 2

4

可以获取 thisPointer指向的地址作为数字,并Pointer从该地址构造一个新地址(参见Pointer.addressPointer.fromAddress())。由于数字可以在隔离之间自由传递,这可以用来在它们之间传递本地指针。

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

// 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);
}
于 2020-11-22T03:03:48.737 回答
1

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

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

于 2021-04-15T11:56:26.993 回答