【问题标题】:Accessing assets from C++ plugin through Flutter通过 Flutter 从 C++ 插件访问资产
【发布时间】:2020-06-10 03:40:46
【问题描述】:

由于延迟低,我正在尝试将 Google Oboe 用于 3D 音频处理应用。该应用程序将有一个 C++ 后端,它负责处理,前端由 Flutter 完成。我正在运行几个测试,看看它是否会工作,但我在将资产从 Flutter 加载到双簧管时遇到问题。我检查了双簧管repo 中的示例 RhythmGame,使用 Java 完成,但无法安静地找到直接从 Dart 到 C++ 的方法。 前后端的连接是通过dart::ffi

这是我迄今为止尝试过的。根据 Richard Heap here 发布的示例,我将 noise 变量从正弦波更改为 wav 文件中歌曲的一小段:

class _MyAppState extends State<MyApp> {
  final stream = OboeStream();

  var noise = Float32List(512);
  Timer t;

  @override
  void initState() {
    super.initState();
    // for (var i = 0; i < noise.length; i++) {
    //   noise[i] = sin(8 * pi * i / noise.length);
    // }

    _loadSound();
  }

  void _loadSound() async {
    final ByteData data = await rootBundle.load('assets/song_cut.wav');
    noise = data.buffer.asFloat32List();
  }

(...)

那么 Dart 中的这个函数调用原生库的 Dart 包装器:

void start() {
    stream.start();
    var interval = (512000 / stream.getSampleRate()).floor() + 1;
    t = Timer.periodic(Duration(milliseconds: interval), (_) {
      stream.write(noise);
     });
  }

Dart 中的包装是:

void write(Float32List original) {
    var length = original.length;
    var copy = allocate<Float>(count: length)
        ..asTypedList(length).setAll(0, original);

    FfiGoogleOboe()._streamWrite(_nativeInstance, copy, length);
    free(copy);
  }

_streamWrite 是 C++ 中的原生函数:

EXTERNC void stream_write(void* ptr, void* data, int32_t size) {
    auto stream = static_cast<OboeFfiStream*>(ptr);
    auto dataToWrite = static_cast<float*>(data);
    stream->write(dataToWrite, size);
}
void OboeFfiStream::write(float *data, int32_t size) {
    managedStream->write(data, size, 1000000);
}

现在我可以听到这首歌,但它的失真太大了。尝试使用正弦时,我也能听到它,但它也有一些失真。我还没有在双簧管中使用回调模式,因为我想先尝试一下。

【问题讨论】:

  • youtu.be/X8JD8hHkBMc 特别是从 23:00 开始
  • Richard,我几天前看过你的视频(顺便说一句,谢谢你,这是我能找到的唯一一个使用 ffi 的好例子,干得好),但是我'我现在想做的是用我从 pubspec.yaml 中的资产加载的 mp3 文件替换正弦声音
  • 您可以通过添加更多细节来改进您的问题。您正在尝试做什么,如何,您尝试了什么,什么不起作用。显示您的代码。

标签: c++ flutter dart oboe dart-ffi


【解决方案1】:

1 - 您的 WAV 文件是什么格式的?它是 32 位浮点数吗?不要忘记 WAV 文件有一个标题,所以你应该丢弃前几十个字节(直到 data 段)。确保您开始在浮点边界上读取音频数据(如果标题不是,它可能不是 4 的倍数)。如有必要,只需使用十六进制编辑器来确定浮点数据的偏移量并从那里开始读取。或者,截断标题并将您的资产重命名为 song_cut.raw。 Audacity 应该能够生成无标题的原始音频文件。

2 - 您的音频剪辑是以什么采样率录制的?这是否与设备的采样率相匹配? (请注意,iOS 设备通常为 44.1k,但 Android 设备通常为 48k。在 macOS 上使用 Android 模拟器时,谁知道报告的采样率会是多少!如果您的速率不匹配,预计音高失真 - 或使用重采样器. 我认为 Oboe 有一个。或者,与谈话相关的示例 repo 包含一个你可以使用的。)

3 - 请注意,计时器间隔已微调(出于演示目的)以声卡速率传送 512 个样本所需的大致时间。这对于演示可能没问题,但不适用于现实生活。此外,您的 wav 文件中可能没有正好 512 个样本。将您的音频循环调整为 512 个样本,或调整 512000 常量以匹配循环中的样本数。

4a - 您还没有使用回调方法,但您可能应该尽快使用。我成功的一种方法是使用无锁循环缓冲区。双簧管回调尝试清空缓冲区,而 Dart 计时器例程尝试填充它。缓冲区越大,下溢的可能性越小,但延迟越差。

4b - 理想的解决方案是将 Oboe 回调调用到 Dart 中,但我还没有找到一种方法,因为 C->Dart 调用必须在 Dart 主线程上,但 Oboe 回调是肯定在高优先级 IO 线程上。

【讨论】:

  • 你能以任何方式调试你在 C++ 包中所做的事情吗?我从双簧管那里得到了一些我无法理解的运行时错误,一步一步的调试会很棒
猜你喜欢
  • 2015-03-10
  • 1970-01-01
  • 1970-01-01
  • 2019-07-21
  • 1970-01-01
  • 1970-01-01
  • 2018-04-21
  • 1970-01-01
  • 2019-09-13
相关资源
最近更新 更多