libFLAC 続き

malloc, calloc, realloc, fclose が残っていましたので除去しました.
一応PCで動いているみたいですが ROM と RAM の消費量は下記となりました.

0x3870 .text                    libflac.a(stream_decoder.o)
0x1158 .text                    libflac.a(bitreader.o)
0x0b24 .text                    libflac.a(format.o)
0x0a4c .text                    libflac.a(fixed.o)
0x09a4 .text.qsort              libg.a(lib_a-qsort.o)
0x0960 .text                    libflac.a(lpc.o)
0x0578 .text.__ieee754_log      libm.a(lib_a-e_log.o)
0x1100 .rodata                  libflac.a(crc.o)
0x063c .rodata.log.str1.4       libm.a(lib_a-w_log.o)
0x2be8 .bss                     libflac.a(stream_decoder.o)
0x102c .bss                     libflac.a(bitreader.o)
0x0428 .data.impure_data        libg.a(lib_a-impure.o)
0x0200 .bss                     libflac.a(format.o)

allocate_output_(), private_->output, private_->residual

output と residual は int32_t の配列で下記の RAM を必要とします.

sizeof(int32_t) * channels * blocksize * 2
  • sizeof(int32_t) は最終の decode された data が 16bits であっても decode の過程で上位 16bits は利用しているので int16_t に変更はできませんでした.
  • channels は CDDA であれば 2 固定です.
  • blocksize は encode 時のパラメータで変更可能です.
  • 最後の *2 は output と residual それぞれで2つです.

前回では根拠なく blocksize を 2352 にしましたが、これですと 0x9300 bytes も必要になりますので 2352 / 4 に変更しました. 588 を超える値で encode された flac data は利用できない制限ができてしまいました.

output は絶対にいるにしろ residual を 1 channel だけ使うようにできればもっと RAM の確保に余裕が持てそうです.

libFLAC/lpc.c

libFLAC/stream_decoder.c の read_subframe_lpc_() で decoder->private_->local_lpc_restore_signal(_16bit|_64bit)? がありますが x86_64 と i686 の CPU 向けの最適化で利用しているみたいなので関連する関数ポインタは使わずに、 FLAC__lpc_restore_signal() のみを利用します. FLAC__lpc_restore_signal_64bits() は CDDA ではいらないみたいなのでなくしました.

FLAC__lpc_restore_signal() は redisual と output を利用している部分なので residual が減らせるか調査した方がいいと思います.

metadata各種

METADATA_BLOCK_STREAMINFO は固定長、必須データですので問題ありません.
他の metadata は可変長データが含まれており、そのたびに malloc 系で RAM を確保します. METADATA_BLOCK_SEEKTABLE を除き、 可変長 metadata は音楽データの decode 前に動的確保され、 callback 関数の呼び出しの後に動的解放を行います.
先述の private_->output, private_->residual は排他の関係だと思うのでこの静的確保 RAM を必要量に応じて動的配分するということにしました.

METADATA_BLOCK_SEEKTABLE はずっと確保されているのですが、なくても動くと書かれているので利用しないようにソースを調整しました. 動かしてみて seek の時間が遅すぎるなら調整が必要です. 標準の encoder では単純に 10 秒間隔でいれているのでデータ量をこの用途ではもう少し減らした方がいいです.

残りは METADATA_BLOCK_PICTURE ですが、これは非常に難しいです. 問題を列挙します.

  • private_->output で確保した領域は画像を入れる場所としては小さい
  • png/jpeg などを使う場合は別途画像ライブラリで展開するため RAM が足りなすぎる
  • 別途の画像ライブラリはいまのところ何も考えていない
  • もし VRAM 用のベタデータを割り振るにしろ必ず RAM に展開する仕組みなのでやはり RAM が足りない
  • picture は UI でほしい

画像データの最小転送量は decode するなら block size だけ展開し、decode した data は VRAM 用の stream に流し込む仕組みでないと.. 難しいですね.

各種 metadata は利用できないように設定できますので今のところ画像は触れないということにしています.

FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size (長い)

safe_realloc_() を呼ぶ場所がありました. debug print をいれたら 0x100 * 2 bytes が必要で、呼ばれるのは1度だけでしたので、それだけを確保しました. それが適切なのかよくわかりません.

その他

libFLAC 側でファイルを管理してもらう FLAC__stream_decoder_init_FILE() と関連する関数や decoder->private_ の変数はすべてなくしました. ここに fclose が残っていてこれが malloc/free を利用しているようでした.

decoder->private_->last_frame.subframe は(今のところ)重要ではないのに消費量が多いのでなくして header と foot だけにしました.

今のところの評価

libflac に必要な RAM は 0x3e14 bytes なので、 MCU の SAMD20 の最低 1/4 を占めることになります. mruby/C も最低 1/4 なのでこの2つが最終的に2/3を使うのであればギリギリ大丈夫なのでは... というところです.