📹

【Android】独自Audio入力をサポートした CameraXフォークの公開

2024/12/19に公開

はじめに

FairyDevicesの Kabocha と申します。
Androidアプリ開発でカメラ機能を使う際に、CameraX を利用していますか?
録画や写真撮影、画像解析など、一般的なユースケースであれば、非常に楽ができ、使わない手がない素晴らしいライブラリです。

(原文引用)
CameraX は、アプリにカメラ機能を簡単に追加できるようにする Jetpack サポート ライブラリです。互換性の修正や回避策などが多数含まれており、さまざまなデバイスで一貫した開発エクスペリエンスを提供します。
developer.android.com より

しかし、CameraX には、まだ改善の余地がある部分も存在します。
例えば、CameraX VideoCapture におけるマイク周り は、カメラやエンコーダー周りと比べるとやや力不足と言えます。

CameraX では、マイクの設定は、内部実装に閉じられており、自由にカスタマイズすることはできません。
(2024年12月時点で)2年半以上前から、Audioの入力ソースを変更できるようにしてほしいというリクエストは、未解決となっています。

例えば、ノイズを除去しながら録画したり、細かい音声調整を行う場合は、
CameraXのバージョンごとに考慮しつつ、リフレクションを使うか、CameraXを使わずに、Camera2 APIから実装せざるを得ませんでした。

なにができるのか?

百聞は一見にしかずですので、まずは、なにができるのかをご紹介します。
Androidアプリで、このような動画音声を 編集なし でリアルタイムで実現できます。

通常の CameraX を使った例

https://www.youtube.com/watch?v=16Ez18n8q0g
発話と再生している効果音が入っています。

CameraXフォーク + VAD(Voice Activity Detection)を使用した例

https://www.youtube.com/watch?v=JN6A-vEd91M
発話をしているときだけ効果音と発話が入り、発話をしていない時は無音になります。

CameraXフォーク + リアルタイムノイズ除去を使った例

https://www.youtube.com/watch?v=-B3_QqQqqoo
ほぼ効果音は入らず、発話のみが入ります。

動画編集無しでこの違いが出ます。このような音声処理をCameraXの機能を使いながら実現できるのが、本プロジェクトで実現したことです。

何故 CameraXをフォークしたのか

弊社では、THINKLET という5つのマイクを搭載したAndroidベースのOS搭載の独自デバイスを開発・提供・販売しています。
THINKLETは、Android OSベースで動作しますので、CameraXを利用することができます。
しかし、CameraX を利用する場合、一度に使えるマイクは 1 つか 2 つに制限されており、搭載しているマイクの性能を最大限に引き出すことができませんでした。
そこで、私たちは、マイク周りの機能を外部化し、より柔軟なマイク制御を実現する べく、CameraX の一部をフォークしました。

フォークによって実現したこと

今回のフォークでは、内部実装されている 音声入力機能を外部から注入可能にする ことを実現しました。
具体的なソースコードは、以下のリポジトリで公開しています。

実際の呼び出しとしては、以下のようになります。

+ val mic: ThinkletMic = YourMic()
  val record = Recorder.Builder()
    .setExecutor(recorderExecutor)
    .setQualitySelector(QualitySelector.from(Quality.FHD))
+   .setThinkletMic(mic)
    .build()

上記例の YourMic クラスは、以下のような設定を指定可能です。

  • サンプリングレート(16kHz, 48Khzなど)
  • チャンネル数(1chモノラル あるいは、2chステレオ)
  • AudioSource (AudioSource.CAMCORDER, AudioSource.MIC など)

また、CameraXのエンコーダーの処理に渡す ByteBuffer のコールバックを受け、別の音声に差し替え もできます。

override fun read(byteBuffer: ByteBuffer, bufferSize: Int): Int {
    val readSize = super.read(byteBuffer, BUFFER_SIZE)

    if (readSize > 0) {
        val data = ByteArray(readSize)
        byteBuffer.rewind()
        byteBuffer.get(data)

        // 読み取った音に細工をする
        val audioData = data.customized()
        byteBuffer.flip()
        byteBuffer.put(audioData)
    }
    return readSize
}

THINKLET向けライブラリ

このフォークしたCameraXに合わせ、THINKLET向けに実装した5つのマイクを使う ThinkletMic.FiveChや、THINKLET向けのSDKとして公開している 音声処理 を使用する ThinkletMics.Xfe をソースコードも含め、プラグインライブラリとして提供しています。

Androidアプリ向けサンプル実装

このフォークしたCameraXは、Androidでももちろん動作します。
Androidでも動作する例として、VAD(Voice Activity Detection)を使用した 人の声を検知したときだけ録画に音声を渡す 実装例を公開しています。
どのような動画になるかは、なにができるのか? の動画をご確認ください。

今後の展望

私たちは、このフォークしたプロジェクトを実際に使用し、プロダクトを作っています。
私たちも、新しいCameraXの機能を使っていきたいので、定期的にCameraX アップデートに合わせ、リリース予定です。

謝辞

本プロジェクトの調査および実装にあたり、多大なるご協力をいただきました Tamaki Hidetsugu / Ralph様 に、心より感謝申し上げます。
特に、技術的な助言や具体的な問題解決へのアプローチにおいて多大な貢献をいただき、本プロジェクトを円滑に進めることができました。

関連リンク

参考リンク

Github

フェアリーデバイセズ公式

Discussion