我正在尝试将 Google Oboe 用于 3D 音频处理应用,因为它的延迟较低。该应用程序将有一个 C++ 后端来进行处理,前端则使用 Flutter 完成。我正在运行一些测试来看看它是否有效,但我在将 Assets 从 Flutter 加载到 Oboe 时遇到问题。我检查了 Oboe's 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++ 中的 native 函数:
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);
}
现在我可以听到这首歌,但它的失真度太大。当尝试使用正弦时,我也能听到它,但它也有一些失真。我还没有在 Oboe 中使用回调模式,因为我想先尝试一下这是否有效。
最佳答案
1 - 您的 WAV 文件是什么格式?是32位 float 吗?不要忘记 WAV 文件有一个 header ,因此您应该丢弃前几十个字节(直到 data
段)。确保您开始在浮点边界上读取音频数据(如果 header 不是,则该边界可能不是 4 的倍数)。如有必要,只需使用十六进制编辑器来确定 float 据的偏移量并开始读取。或者,截断标题并将您的资源重命名为 song_cut.raw
。 Audacity 应该能够生成无 header 的原始音频文件。
2 - 您的音频剪辑是以什么采样率录制的?这与设备的采样率匹配吗? (请注意,iOS 设备通常为 44.1k,但 Android 设备通常为 48k。在 macOS 上使用 Android 模拟器时,谁知道报告的采样率会是多少!如果您的速率不匹配,则可能会出现音调失真 - 或使用重采样器.我认为 Oboe 有一个。或者,与演讲相关的示例存储库包含一个您可以使用的。)
3 - 请注意,计时器间隔已微调(出于演示目的)到以声卡速率传送 512 个样本所需的大致时间。这对于演示来说可能没问题,但不适合现实生活。另外,您的 wav 文件中可能不包含 512 个样本。将音频循环调整为 512 个样本,或调整 512000 常量以匹配循环中的样本数。
4a - 您尚未使用回调方法,但您可能应该尽快使用。我成功的一种方法是使用无锁循环缓冲区。 Oboe 回调尝试清空缓冲区,而 Dart 计时器例程尝试填充它。缓冲区越大,下溢的可能性越小,但延迟也越差。
4b - 理想的解决方案是将 Oboe 回调调用到 Dart 中,但我还没有找到一种方法来做到这一点,因为 C->Dart 调用必须在主 Dart 线程上,但 Oboe 回调是当然是在高优先级 IO 线程上。
关于c++ - 通过 Flutter 从 C++ 插件访问资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62270389/