ゼロ、前に書かれています
私が開発した最初の OpenSL ES は Android でした。しかし最近、Hongmeng のドキュメントを読んだところ、その基盤となるライブラリが OpenSL ES もサポートしていることに気付き、興味を持ちました。Hongmengのネイティブ開発について簡単に理解した後、開発を開始しました。移植プロセス中に、実際には Android プログラマーにとって非常に友好的であることがわかりました。また、Android から Hongmeng への移植には大きな変化はありません。この記事では、OpenSL ES 開発を出発点として、Hongmeng がどのように基本的なネイティブ開発を行っているかについて主に説明します。デモは、完全な録音と再生の録音機能を実現するレコーダーを実装することです。
1. Hongmeng ネイティブ アプリを作成する
Hongmeng の開発ツールは AndroidStudio と非常によく似ており、やはり同じ会社のオープンソースなので使い方はほぼ同じで、エントリーコストも高くありません。ネイティブ アプリの作成は、AndroidStuio の場合と似ています. プロジェクト テンプレートを作成するインターフェイスで [ネイティブ C++] を選択し、プロジェクトの種類、名前、パスなどの一連の構成を完了した後、最初のネイティブ C++ を作成します。初めての場合
ネイティブプロジェクトを初めて作成する場合は、開発キットのダウンロードを求められる場合がありますので、設定に移動して、これら3つのオプションにチェックを入れてインストールしたかどうかを確認してください.主な理由は、3番目のオプションがネイティブ プロジェクトの構造は Android に似ています
. メイン パスには通常のプロジェクトよりも 1 つ多い cpp があり, CMakeList と helloworld を返す CPP ファイルが含まれています. これには JNI の内容が含まれます. JNI に慣れていない場合は、最初に私の以前のブログ投稿JNI 共通開発スキルを読んでください。ここでは、CMakeList の記述方法と JNI のプロセス仕様については触れません。
2. OpenSL 開発を開始する
2.1. 基盤となるライブラリの紹介
OpenSLESRecord
ここでは主に OpenSL ES ライブラリと Hongmeng ログ ライブラリを紹介します.ログ ライブラリはデフォルトでインポートされています.新しい C++ クラスを作成し,すべての関数はその中に実装されています.デフォルトで作成された hello.CPP を通じて呼び出されます.
CMakeList は OpenSLESRecord ライブラリを宣言し、それを OpenSL ES とリンクします。そうしないと、OpenSL ES ライブラリが見つかりません。
add_library( # Sets the name of the library.
OpenSLESRecord
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
OpenSLESRecord.cpp)
target_link_libraries( # Specifies the target library.
OpenSLESRecord
# Links the target library to the log library
# included in the NDK.
OpenSLES
libhilog_ndk.z.so)
2.2. オーディオ録音の背景
オーディオのいくつかの簡単な概念について話す
- PCM
とは中国語でパルス符号変調といい、人間の言葉で言えば、オーディオのアナログをデジタルに変換し、マイクで集音した電気信号をバイトデータに変換するという意味です。録音ファイルは xxx.pcm です。しかし、通常のプレイヤーが PCM を再生できない点が 1 つあります。PCM は純粋なオーディオ データであり、オーディオ フォーマット情報がなく、デコードできないため、自分で書き込むか、それを使用して再生するしかありませんが、音声フォーマットも指定してAdobe Audition
再生。 - サンプリング形式
一般的なサンプリング形式は、8 ビット、16 ビット、24 ビット、32 ビットです。デジタルで表現できる音声情報の範囲で、例えば当社のオーディオアナログの最高電圧は5V、サンプリングフォーマットは8ビットで、アナログ信号を最大255等分し、電圧をそれぞれの等しい部分の真ん中にエラーがあります。したがって、サンプリング形式のビット数が多いほど、誤差が小さくなり、精度が高くなります。 - 一般的な
ものは、私たちが通常理解しているモノフォニックおよびデュアルチャンネルであり、3.1、5.1 - チャンネルレイアウトは
チャンネル出力の方向であり、一般的なものは左耳、右耳、左右の耳です - サンプリング レート
サンプリング レートは、デバイスが 1 秒あたりに音声信号を収集する速度です. 一般的なサンプリング レートは 8000、16000、44100、96000 などです. たとえば、サンプリング レートは 8000 で、音声信号がサンプリングされることを意味します毎秒8000回。 - ビットレートと
は、単位時間あたりに送信されるビット数(ビット)のことで、単位はbit/sで、計算式采样格式* 声道数 * 采样率
は
(采样格式* 声道数 * 采样率 * 时间 / 8)bytes
以下は、私の録音の PCM 形式で、この情報を保存するために SLDataFormat_PCM 構造体が使用されます。SLDataFormat_PCM slDataFormatPcm = { SL_DATAFORMAT_PCM, //输出PCM格式的数据 (SLuint32) 2, //输出的声道数量 SL_SAMPLINGRATE_44_1, //输出的采样频率,这里是44100Hz SL_PCMSAMPLEFORMAT_FIXED_16, //输出的采样格式,这里是16bit SL_PCMSAMPLEFORMAT_FIXED_16, //一般来说,跟随上一个参数 SL_SPEAKER_FRONT_LEFT |SL_SPEAKER_FRONT_RIGHT, //双声道配置,如果单声道可以用 SL_SPEAKER_FRONT_CENTER SL_BYTEORDER_LITTLEENDIAN //PCM数据的大小端排列 };
2.2. レコーディングの開発プロセス
実際、上記のプロセスを拡大すると、実際には、オブジェクトの作成 -> オブジェクトのインスタンス化 -> インターフェースの取得がプロセスとして考えられます。以下は、最初の 3 つのステップのコードです。
SLresult sLresult= slCreateEngine(&engineObject,
0,
NULL,
0,
NULL,
0);
LOGE("slCreateEngine...");
if (SL_RESULT_SUCCESS != sLresult){
return;
}
sLresult=(*engineObject)->Realize(engineObject, 0);
LOGE("slCreateEngine Realize...");
if (SL_RESULT_SUCCESS != sLresult){
return;
}
sLresult=(*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&audioEngine);
LOGE("slCreateEngine GetInterface...");
if (SL_RESULT_SUCCESS != sLresult){
return;
}
Android 開発は C 言語の観点から理解するのが難しいと言う場合は、Java の観点から見てみましょう (もちろん、これは Java 開発の一般的な考え方を示すためのものです。完全にそうではありません)。sum の概念を置き換えますC的结构体
。そのメソッド構造を見てみましょう. OpenSL ES のヘッダー ファイルから、この記事で使用されている 3 つのメソッドを例として取り上げます。Java的对象
其实这就像是一个建造者模式代码,创建对象像是把需要构建的参数传进去,实例化对象像是调用build,我们到这一步才算是拿到了对象。而这个对象的属性里又有其它子模块对象,分别有各自的功能,我们这里是根据ID拿到这个对象的。我们前三步的目的就是为了拿到引擎接口,因为它有实际的方法。
......
SLresult (*CreateAudioPlayer) (
SLEngineItf self,
SLObjectItf * pPlayer,
SLDataSource *pAudioSrc,
SLDataSink *pAudioSnk,
SLuint32 numInterfaces,
const SLInterfaceID * pInterfaceIds,
const SLboolean * pInterfaceRequired
);
SLresult (*CreateAudioRecorder) (
SLEngineItf self,
SLObjectItf * pRecorder,
SLDataSource *pAudioSrc,
SLDataSink *pAudioSnk,
SLuint32 numInterfaces,
const SLInterfaceID * pInterfaceIds,
const SLboolean * pInterfaceRequired
);
SLresult (*CreateOutputMix) (
SLEngineItf self,
SLObjectItf * pMix,
SLuint32 numInterfaces,
const SLInterfaceID * pInterfaceIds,
const SLboolean * pInterfaceRequired
);
......
最初の 3 つのステップを理解したら、次の 3 つのステップは同じです. 最終的な目標は、記録インターフェイスを取得し、そのメソッドを呼び出し、キャッシュ キューを設定し、キャッシュ キューのコールバックを設定することです.データ処理はこのコールバック メソッドで行われ、ここでファイルに書き込みます。記録メソッドを呼び出すのが最善であり、すべて問題ありません。
2.3. プレイバックの開発プロセス
録音プロセスと比較して、再生プロセスではリバーブを作成する必要があります。これは、実際にはプレーヤーが必要とするためです。リバーブを作成するプロセスも理解できます。
2.4 まとめ
ここで述べているのはあくまで一般的な処理です. これらの大規模な処理では, いくつかのパラメータを渡す必要があります. これらのパラメータをどのように設定するかは, 基本的に入力デバイス ID, 出力デバイス ID, PCM 形式などの固定パラメータです. 詳しい説明は割愛しますが、興味のある方はgithubで私のデモを見てください.デモパスは記事の最後に載せます.
3. 上位層アプリケーション開発
3.1. 許可申請
- 録音許可(マイク許可)
- ファイルの読み取りと書き込みのアクセス許可
のうち、ファイルの記録と書き込みのアクセス許可は動的に適用する必要があり、
必要なアクセス許可は config.json で宣言されています。
"reqPermissions": [
{
"name": "ohos.permission.MICROPHONE"
},
{
"name": "ohos.permission.WRITE_USER_STORAGE"
},
{
"name": "ohos.permission.READ_USER_STORAGE"
}
]
Java レイヤーが動的にアクセス許可を要求する
private void requestPermission() {
String[] permissions = {
SystemPermission.WRITE_USER_STORAGE, SystemPermission.MICROPHONE
};
requestPermissionsFromUser(Arrays.stream(permissions)
.filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED).toArray(String[]::new), 0);
}
3.2. インターフェースの書き方
録音の一時停止と再生のための 3 つのボタン
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<Button
ohos:background_element="#0099cc"
ohos:text_color="#ffffff"
ohos:id="$+id:start_record_button"
ohos:height="match_content"
ohos:width="match_parent"
ohos:start_padding="14vp"
ohos:end_padding="0vp"
ohos:padding="10vp"
ohos:text="开始录音"
ohos:text_size="16vp"
ohos:top_margin="30vp"/>
<Button
ohos:background_element="#0099cc"
ohos:text_color="#ffffff"
ohos:id="$+id:stop_record_button"
ohos:height="match_content"
ohos:width="match_parent"
ohos:start_padding="14vp"
ohos:end_padding="0vp"
ohos:padding="10vp"
ohos:text="停止录音"
ohos:text_size="16vp"
ohos:top_margin="10vp"/>
<Button
ohos:background_element="#0099cc"
ohos:text_color="#ffffff"
ohos:id="$+id:play_record_button"
ohos:height="match_content"
ohos:width="match_parent"
ohos:start_padding="14vp"
ohos:end_padding="0vp"
ohos:padding="10vp"
ohos:text="播放录音"
ohos:text_size="16vp"
ohos:top_margin="10vp"/>
</DirectionalLayout>
Javaレイヤーは引き続き単語数を構成しません.理解できない場合は、まずHongmengの公式ドキュメントを参照してください.
4.まとめ
Android 側で OpenSL ES を開発する方法を知っていれば、Hongmeng 側で開発することは実際には難しくありません。そのため、Android または iOS から OpenSL ES を開始することもできます。これらには関連するドキュメントが多数あるためです。ネイティブ側の主なわずかに異なる点は、Android 側には独自の BufferQueue がありますが、Hongmeng 側にはこのヘッダー ファイルがないため、Hongmeng 側にはこのヘッダー ファイルがありませんが、大きな問題ではありませんSLES/OpenSLES_Android.h
。組み込みの BufferQueue- を使用しますSLBufferQueueItf
。上位層は Android 側とは異なりますが、プロセスは同じであり、対応する実装方法があります。主な理由は、署名を実機で実行するのが少し面倒なことと、アプリケーションを登録するために開発者プラットフォームに移動する必要があることです。Hongmeng の公式バージョン 2.0 の IDE のネイティブ開発にはまだバグがあり、不可解な爆発と警告があり、問題なく動作しますが、ベータ版 3.0 に変更した後は、そのような問題はありませんでした。以下は私が書いた録音デモです. もし興味があれば, それをクローンして再生し, それを基に開発してみてください. 例えば, PCMファイルをwavファイルに変換してください.直接再生します。役立つと思われる場合は、小さな星を指すこともできます。誰もが議論し、批判し、修正することを歓迎します。
Hongmeng 環境の OpenSL ES 開発 GitHub アドレス