がれすたさんのDIY日記

電子回路、Python、組み込みシステム開発、自作エフェクターを語るblog

MIMXRT10xxでwavファイルを組み込む方法



今回は組み込みでbinファイルだったり、wavファイルを組み込む方法を紹介しようと思います。
RT1020と書いていますがSTM32とかでも同様なことができます。

環境は以下の通り

  • MCUXpresso IDE v10.3.0 [Build 2200] [2018-12-03]
  • SDK v2.5(RT1020 EVK)
  • MIMXRT1020-EVK: i.MX RT1020 Evaluation Kit

やることは

これができると何ができるのか?
エフェクターで言えば

  1. モデルのパラメータ情報(大容量)をもっておける
  2. キャビネットIRを読み込んだりできる
  3. パーカッション音源を保存しておける

近年のエフェクターの場合ノンパラメトリックで情報を持っている場合もあると思うので信号処理のブロック処理データ数によってはかなり大きいデータになる。
あとは近年はやっているIRデータはwavで配られていることも多いのでそれにも使えると思う。

バイナリファイル(wav,bin,...etc)を読み込む方法について

実際にやって行くわけだがバイナリファイルをリンクする方法は

  • c arrayに変換して貼り付ける
  • オブジェクトファイルとして持ってobjcopyで結合する
  • asm命令(incbin)で読み込む

の3つがある。
それぞれ利点があってobjcopyなんかだとオプションでリンクきれるのでデバッグ時には結構楽。
今回行うincbinでは全体ではなく範囲で切り取ったりするのが可能なので一番柔軟な命令かもしれない。

プロジェクト作成

今回は音の再生まで行うのでSAIペリフェラルを用います。
まずはsai_interrupt_transferを読み込みます。
プロジェクト作成やインポートについて知りたい方はこの記事を参考にしてください
gsmcustomeffects.hatenablog.com

サンプルはsai_interrupt_transferを利用

f:id:gsmcustomeffects:20190205032837p:plain
サンプルのインポート

defineマクロの定義

プロジェクトが作成出来たらasmを書いていきたいのだがCファイルに直接は書けないのでインラインasmを利用して埋め込む
それをdefineマクロで一気に書くとこうなる。(ほとんどChanさんのまま

#define	IMPORT_BIN(sect, file, sym , ofs) asm (\
		".section " #sect "\n"                  /* Change section */\
		".balign 4\n"                           /* Word alignment */\
		".global " #sym "\n"                    /* Export the object address to other modules */\
		#sym ":\n"                              /* Define the object label */\
		".incbin \""file"\","#ofs" \n"                /* Import the file */\
		".global _sizeof_" #sym "\n"            /* Export the object size to other modules */\
		".set _sizeof_" #sym ", . - " #sym "\n" /* Define the object size */\
		".balign 4\n"                           /* Word alignment */\
		".section \".text\"\n")                 /* Restore section */

細かい説明はgnu incbinとかarm incbinとかでググると公式ドキュメントが出てくるのでそちらに譲ることとする。

実際の使い方はこうなる

IMPORT_BIN(".rodata","../source_transfer/ss.wav",FooBin,40);
extern const char FooBin[], _sizeof_FooBin[];

ファイルポインタとファイルサイズ(byte)を同時に取得可能なマクロとなっている(Chanさんすごい・・・
ofsに関しては40を設定していて最初の40byteを読み飛ばすための記述。
wavの場合最初の数バイトはファイル情報が入っているのでそれを音声に乗っけないための処置(正しくはヘッダー全部見ないとわかんない
というよりバイナリダンプしてdataチャンク探してそこから先読めばいい

次にwavをインポート

f:id:gsmcustomeffects:20190205052408p:plain
wavの配置はソースと同様のディレクト

次に再生に関する記述を行っていく。
基本的にはサンプルコードが初期化してくれるのであとはオーディオフォーマットの設定をする。

f:id:gsmcustomeffects:20190205040950p:plain
フォーマットの設定

あくまで例だが自分の場合はこうした。
ちなみにハイレゾの場合24bitのデータがパッキングされるのでそのままでは使えないのでインターリーブして0埋めしてuint32_tにキャストしてLRにパッキングしないといけない

次に再生ポインタの変更
元のプログラムではmusic.hからデータを読み出すものポインタの読み込み先がmusic.hのデータ配列になってる。
それをこのように変更する。

f:id:gsmcustomeffects:20190205045021p:plain
データ読み込み先の変更


ここまで来たらあとはコンパイルしてみよう

f:id:gsmcustomeffects:20190205050023p:plain
wavを含むビルド結果

生データを含むため5MBにもなってしまっている。
MIMXRT1020-EVK: i.MX RT1020 Evaluation Kitの場合QuadSPIflashで既存ストレージが8MBあるので問題ないが通常のマイコンではこの手法はあんまし推奨できないかもしれないと感じる。

再生について

このボードの場合初期化の時点でWM8960のヘッドホンアンプのコンフィグもやってくれるのでこのまま実行すればいい。


まとめ

今回は生データを組み込むための手法を紹介し一例としてwavをリンクしてみた。
上記でも触れているが実行ファイル自体が肥大化するのでデバッグの時の書き込み時間がくそ長くなる。そのためコメントアウトなどして対策は必要
改善策としてはSDカードなどのストレージを用意してlittlefsなんかのファイルシステムで読んであげてデコードするとか?
あとwavごとにフォーマットが違うので先頭ヘッダーから情報抜きだしてそれ相応の設定をするコード追記は必要。(それっぽいデコーダーの実装)

実用的に考えると?

MIMXRT10xx全般に言えることで基本的には密結合RAMにコード配置して信号処理を回すことになるんだけどそうなるとセカンダリブートローダーが必要になる。
その場合メインのアプリケーションプロジェクトとブートローダープロジェクトの二つができる。
アプリのほうは最大でも256KB程度(内蔵RAMの最大値に依存)におさめないといけないのでデータ系のリンクをここにするのはナンセンスである(rodataセクションはどちらにせよFlashに行くため)
メインアプリと分けると読み込んだbinなりwavのポインタを取得しないといけないのでなんかしら対策は必要なはず・・・・

スキームとしては以下のような感じなんだけど最後のほうわからん・・

  1. ブート方法がQuadSPI経由だとしてブートローダはXIPで起動する。
  2. ブートローダのコード自体でincbinしてアプリケーションコード部分だけRAM転送してPC書き換えて起動。
  3. incbinで呼んだファイルのポインタを取得する方法がわからん

別にデメリットばかりでもないよ・・・

フォーマットが決まった短いファイルをいっぱい持っておきたい(例えばキャビネットIRの場合)ケースもある。

f:id:gsmcustomeffects:20190205051824p:plain
キャビネットIRの読み込み例

あとはあんまし書き換えないであろうreverb IRとかそういうのもアリですね。