この記事は前回の「Androidで劣化無しでデジタル録音!「sndcpy」を使う」に関連する記事です。

前回、sndcpyはデフォルトでサンプリング周波数が48kHzに固定されていると書きました。今回は自由にサンプリング周波数を変えられるように改造してみます。他の部分にも手を加えていきます。

なお、この記事は専門的なものを取り扱うので少し難しいかもしれません。ソースコードは https://github.com/rom1v/sndcpy にあります。Javaで書かれており、Android Studioでビルド可能です。

早速改造していきます。

サンプリング周波数の設定はRecordService.javaの43行目で定数として定義されています。

private static final int SAMPLE_RATE = 48000;

定数のため、このままだと宣言後に再代入できないので、finalを消します。

private static int SAMPLE_RATE = 48000;

次に、amコマンドでIntentとして値を渡せるように改造します。

このアプリは、まずアプリの起動時(onCreate())に権限確認をしていて、権限があることを確認してからonActivityResult()の中でサービスを開始しています。その部分がMainActivity.javaの49行目、53行目~60行目となります。

サービスを開始する際はRecordServiceのstart()を呼び出していて、その時にIntentも渡しているので、そこにサンプリング周波数の情報も入れます。

data.putExtra("SAMPLE_RATE", mSampleRate);
RecordService.start(this, data);

RecordServiceのstart()ではIntentからサンプリング周波数の情報を取り出してSAMPLE_RATEに代入します。

private static int SAMPLE_RATE = 48000;
private static int SAMPLE_RATE_REAL = 0;
//...
public static void start(Context context, Intent data) {
        SAMPLE_RATE_REAL = data.getIntExtra("SAMPLE_RATE", 48000);
        SAMPLE_RATE = SAMPLE_RATE_REAL;
        Log.d(TAG, "Current SAMPLE_RATE is " + SAMPLE_RATE + " Hz");
        //...
}

SAMPLE_RATE_REAL に代入しているのは実装上の都合です。この変数はSAMPLE_RATEと同様に定義します。 getIntExtraの第2引数にはデフォルト値が指定できるので、何も指定されなかったときは48kHzにしています。

ここまでやると自由に変えられるようになったはずです。ビルドしてインストールしてみます。

ADBでは

adb shell am start com.rom1v.sndcpy/.MainActivity --ei SAMPLE_RATE 88200

と入力して起動すれば88.2kHzを指定したことになります。

前回の記事の方法でFFmpegのプレイヤーで再生してみれば、うまくいけば音が鳴ります。

ffplay -ar 88200 -ac 2 -f s16le tcp://localhost:28200

バッファサイズも変えられるようにしてみた

実験している途中、サンプリング周波数を上げたことで遅延が増えていることに気づきました。 鑑賞用などであれば問題はないと思いますが、一応バッファサイズを変えられるようにしてみました。

ソースコードは省略しますが、1024*1024バイト(通常)、1024*512バイト(やや小さい)、1024*256バイト(小さい)、1024*128バイト(超小さい)、1024*64バイト(超超小さい)から選べるようにしました。実際には0~4の数値で指定します。

小さいバッファ(1024*256バイト)にしたいときは

adb shell am start com.rom1v.sndcpy/.MainActivity --ei SAMPLE_RATE 88200 --ei BUFFER_SIZE_TYPE 2

という風に入力します。SAMPLE_RATEの値はあくまでも例なので88200以外でも大丈夫です。 Wi-Fiでバッファサイズを減らすときは、十分なリンク速度で高性能なアクセスポイントを使うことを推奨します。

これ以外にも、FFmpegのプレイヤーを使っている場合は-fflags nobufferを引数に指定することで遅延を減らすこともできます。

通知に表示する情報を増やしてみた

改造したsndcpyでは、Logcatに現在の設定を出力しています。

03-22 20:19:43.731 10023 10023 D sndcpy  : Current SAMPLE_RATE is 44100 Hz
03-22 20:19:43.731 10023 10023 D sndcpy  : Current BUFFER_SIZE is 65536 bytes

ですが、いちいちLogcatを見て探すのは気が滅入るので、通知にサンプリング周波数を表示するようにしました。

残る疑問

本当にサンプリング周波数上げて音質上がるんですか?

結論から言うと、端末依存です。例えばsndcpyの設定を96kHzにして96kHzのハイレゾFLACを再生しても、OSのサンプリング周波数が48kHzのままで結局音質は上がりませんでした。サンプリング周波数を上げる方法が見つからなかったのでPowerampから再生しようとしたらsndcpy経由で音が出ませんでした。ミキサーを経由しないダイレクト出力を使っているようです。なので今回ははっきりとした結果が分かりませんでした。

ビット深度は上げられるのか?

ビット深度はデフォルトで16ビットとなっています(RecordService.javaの144行目)。 24ビット、32ビットと変えられるので、試しに指定してみたところ、実験端末のXperia5ではエラーになりました。 端末によって挙動が異なるのかもしれませんが16ビットのままが無難そうなので、そのままにしておきました。

さいごに

ちょっと改造するだけでだいぶ便利になり、オタクっぽい設定もできるようになりました。

今回改造したコードは、元のリポジトリのMITライセンスに従いGitHubで公開しています。
https://github.com/CyberRex0/sndcpy

ビルド済みAPKはリリース一覧から入手できます。(Android 10以降に対応)
https://github.com/CyberRex0/sndcpy/releases