C++ 在Flatter应用程序中使用Android NDK中的AssetManager类
我一直在尝试在我的Flitter应用程序中使用Android NDK类来访问音频文件。以下是双簧管存储库中的示例,我了解到他们从Java获取C++ 在Flatter应用程序中使用Android NDK中的AssetManager类,c++,flutter,android-ndk,oboe,dart-ffi,C++,Flutter,Android Ndk,Oboe,Dart Ffi,我一直在尝试在我的Flitter应用程序中使用Android NDK类来访问音频文件。以下是双簧管存储库中的示例,我了解到他们从Java获取AssetManager,如下所示: JNIEXPORT void JNICALL Java_com_google_oboe_sample_rhythmgame_MainActivity_native_1onStart(JNIEnv *env, jobject instance,
AssetManager
,如下所示:
JNIEXPORT void JNICALL
Java_com_google_oboe_sample_rhythmgame_MainActivity_native_1onStart(JNIEnv *env, jobject instance,
jobject jAssetManager) {
AAssetManager *assetManager = AAssetManager_fromJava(env, jAssetManager);
if (assetManager == nullptr) {
LOGE("Could not obtain the AAssetManager");
return;
}
game = std::make_unique<Game>(*assetManager);
game->start();
}
下面是该函数的省道包装器:
EXTERNC void *engine_create(void) {
AAssetManager *assetManager = AAssetManager_fromJava(env, jAssetManager); // ERROR: How do I get these?
if (assetManager == nullptr) {
LOGE("Could not obtain the AAssetManager");
return nullptr;
}
return new DSPAudioEngine(*assetManager);
}
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:flutter/services.dart';
typedef oboe_engine_init = Pointer<Void> Function();
typedef OboeEngineInit = Pointer<Void> Function();
class FfiGoogleOboe {
static const MethodChannel _channel =
const MethodChannel('ffi_google_oboe');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
static FfiGoogleOboe _instance;
factory FfiGoogleOboe() {
if (_instance == null) {
_instance = FfiGoogleOboe._();
}
return _instance;
}
OboeEngineInit _engineInit;
FfiGoogleOboe._() {
final oboeLib = DynamicLibrary.open('libffi_google_oboe.so');
_engineInit = oboeLib
.lookup<NativeFunction<oboe_engine_init>>('engine_create')
.asFunction();
}
}
最后,这里是来自双簧管的人员如何使用JNI和Java处理它:
package com.google.oboe.sample.rhythmgame;
import android.content.Context;
import android.content.res.AssetManager;
import androidx.appcompat.app.AppCompatActivity;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.view.WindowManager;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setDefaultStreamValues(this);
}
protected void onResume(){
super.onResume();
native_onStart(getAssets());
}
protected void onPause(){
super.onPause();
native_onStop();
}
static void setDefaultStreamValues(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int defaultSampleRate = Integer.parseInt(sampleRateStr);
String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);
native_setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst);
}
}
private native void native_onStart(AssetManager assetManager);
private native void native_onStop();
private static native void native_setDefaultStreamValues(int defaultSampleRate,
int defaultFramesPerBurst);
}
基本上,您需要从插件的Kotlin文件传递ASESTMAMER引用到C++库。这个答案解释了如何使Kotlin文件调用C++代码: 您需要使用methodChannel调用来触发此操作。您可以从
flatterpluginbinding.applicationContext.assets
获取onAttachedToEngine方法中的AssetManager引用
这是一个示例FLASH插件,它读取C++库中的一个资产:
我认为,您最初使用RootBundle加载资产,然后使用ffi将字节传递给C的方法要容易得多。我得出了相同的结论。我想消除读取错误文件的可能性(特别是如果文件像mp3一样被压缩),并且由于我在Dart中找不到库来从
.wav
或.mp3
获取PCM数据,我想以双簧管为例。但是,我没有找到一种方法来处理颤动,所以我现在要做的是把一个字节缓冲区向下传递到C++,使用<代码> ROOT Cube ,然后从那里解码。基本上,我试图重新实现方法:(AAssetManager和assetManager、大小镜头、音频属性*输出属性)使用float*buffer
而不是AAssetManager&assetManager
。我指的方法是在文件第31行。从Dart中的WAV获取PCM很容易,mp3不是很多,因此您可能希望使用AMediaCodec,并使用资产字节支持的自定义数据源。请参阅:并且您希望使用uint_8 buffer作为t他输入,而不是浮动,因为这是您将从资产中读取并使用ffi传递的内容。
package com.google.oboe.sample.rhythmgame;
import android.content.Context;
import android.content.res.AssetManager;
import androidx.appcompat.app.AppCompatActivity;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.view.WindowManager;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setDefaultStreamValues(this);
}
protected void onResume(){
super.onResume();
native_onStart(getAssets());
}
protected void onPause(){
super.onPause();
native_onStop();
}
static void setDefaultStreamValues(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int defaultSampleRate = Integer.parseInt(sampleRateStr);
String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);
native_setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst);
}
}
private native void native_onStart(AssetManager assetManager);
private native void native_onStop();
private static native void native_setDefaultStreamValues(int defaultSampleRate,
int defaultFramesPerBurst);
}