Edited at

RXマイコン、FreeRTOS、FatFs、で MP3、WAV の再生


はじめに

前回、FreeRTOS を使い、FatFs をスレッドセーフで動かす実験を行いました。

今回はそれの応用で、MP3、WAV などの音楽ファイルを再生してみました。


  • 自作の RX64M ボード

  • GR-KAEDE

  • RX65N Envition kit (RTK5)

での動作を確認しました。

MP3 コーデックは mad ライブラリを使っています。

また、ID3 タグの表示(コンソール、シリアル出力)に対応しています。


ハードウェアーの準備

RX64M、RX65N、などには、2 チャネルの 12 ビット D/A が内臓されており、意外なほどノイズが少なく音が良いです。

※M5-STACK などでは、D/A の品質は良くない話を聞くのですが、RX マイコン内臓の D/A は非常に高品質だと思います。

※ 4 ビット捨てているのだけど、意外と 16 ビットの D/A 出力と音を聞き比べても、大きな違いを感じません(これは個人差があると思いますが・・)

D/A 出力は、以下のようになっています。

CPU
DA0(Left)
DA1(Right)

RX64M
P03
P05

RX65N
P03
P05

※GR-KAEDE では、DA0 は LED4 に接続しています。

※RX65N Envition kit では、P05(DA1) が SW5 に接続しています。

D/A 出力は 3.3V フルスケール、GND 電位は中点電圧(1.65V)なので、アンプなどに接続する場合は、多少の工夫をする必要があります。

自分は、R/Cによるローパスフィルターとインピーダンス変換、電圧オフセットを行う為、オペアンプによる回路を使っています。



※この回路は RL7 の PWM 変調用の物ですが、代用しています。

※この出力にアンプを繋ぐ場合、電源のGND電位には注意が必要です、基本的に別電源でGND電位が全く異なる場合にしか使えません。


ソフトウェアーの構成

前回、指定ファイルを間欠的(25ミリ秒毎に1024バイト)に読み込み、そのサイズを表示するだけのシンプルなものでした。

今回は、そこに、MP3 や WAV のデコードと再生を行うプログラムを組み込みました。

オーディオ再生では、TPU に設定した周期で、DMA を起動して、DA0、DA1 のレジスタに、波形値を送っています。

波形値はリングバッファにして管理し、バッファが空いたら、デコードした波形データを必要な分コピーしています。

「TPU」は RX64M などに内臓されている高分解のタイマーで、一般的なサンプリング周期、48KHz や 44.1KHz の間隔を作ります。

※ただ、44.1KHz は、タイマーの分解能と、CPU の発振周期の関係で、割り切れない為、正確ではありませんが十分と思います。

※48KHz は綺麗に割り切れので正確です。

オーディオ再生に必要なコンテキストは以下のようなもので、C++ テンプレートクラスで管理しています。

    volatile uint32_t   wpos_;

/// DMAC 終了割り込み
class dmac_term_task {
public:
void operator() () {
device::DMAC0::DMCNT.DTE = 1; // DMA を再スタート
wpos_ = 0;
}
};

typedef device::dmac_mgr<device::DMAC0, dmac_term_task> DMAC_MGR;
DMAC_MGR dmac_mgr_;

uint32_t get_wave_pos_() { return (dmac_mgr_.get_count() & 0x3ff) ^ 0x3ff; }

typedef device::R12DA DAC;
typedef device::dac_out<DAC> DAC_OUT;
DAC_OUT dac_out_;

typedef utils::sound_out<8192, 1024> SOUND_OUT;
SOUND_OUT sound_out_;


  • DMA はリピートモードで動かして、それが完了した場合に再スタートして、再生が途切れないようにしています。

  • リングバッファ(SOUND_OUT)は、8192ワードで、出力バッファ(DMA 用)は 1024 バイトとなっています。

  • 8192 ワードは、デコーダーがデコードして貯めた、音と、DMA により逐次消費される量が追い越す事はないような値です。

  • 元々は、シングルタスク(16ミリ毎)で動作させる事を念頭に設定された大きさなので、RTOS で動かす場合(今回は10ミリ秒)は、もっと小さく出来ると思います。

  • MP3 の場合は、フレーム単位(1152)のデコードなので、その関係も考慮する必要があります。

    {  // DMAC マネージャー開始

uint8_t intr_level = 4;
bool cpu_intr = true;
auto ret = dmac_mgr_.start(tpu0_.get_intr_vec(), DMAC_MGR::trans_type::SP_DN_32,
reinterpret_cast<uint32_t>(sound_out_.get_wave()), DAC::DADR0.address(),
sound_out_.size(), intr_level, cpu_intr);
if(!ret) {
utils::format("DMAC Not start...\n");
}
}


  • DA0、DA1 のレジスターは連続する32ビットのレジスターとしてアクセス出来るので、32ビット転送を使っています。

     ※ DMAC_MGR::trans_type::SP_DN_32

    class tpu_task {

public:
void operator() () {
uint32_t tmp = wpos_;
++wpos_;
if((tmp ^ wpos_) & 64) {
sound_out_.service(64);
}
}
};

typedef device::tpu_io<device::TPU0, tpu_task> TPU0;
TPU0 tpu0_;


  • TPU の定義は上記のようになっていて、64ワード進む毎に隙間を埋めるようにしています。

  • 64ワード(16ビットステレオなら256バイト)をリングバッファから取り出して、DMA 用のバッファに詰めていきます。

  • tpu_task は、ファンクタで、TPU の割り込みから呼ばれます。

  • テンプレートのパラメータとして定義してあるので、コンパイル後は内部に埋め込まれて、最適化されます。

  • 周期が比較的短い割り込みなどでは有効な方法だと思います。


動作の様子

コンソール出力例

# cd ロミオとシンデレラ

# dir
7300590 Oct 1 2011 23:16 01 キャットフード.mp3
6566445 Oct 1 2011 23:16 02 茜コントラスト.mp3
6306857 Oct 1 2011 23:16 03 エトランゼ.mp3
7627078 Oct 1 2011 23:16 04 Mermaid.mp3
6273652 Oct 1 2011 23:16 05 ZERO.mp3
6600923 Oct 1 2011 23:16 06 Paradise Cage.mp3
5727593 Oct 1 2011 23:16 07 六月病.mp3
6222142 Oct 1 2011 23:16 08 ローレライ.mp3
7056714 Oct 1 2011 23:16 09 ロミオとシンデレラ.mp3
6740736 Oct 1 2011 23:17 10 Red Garden -type M-.mp3
6816588 Oct 1 2011 23:17 11 曲がり角.mp3
7385846 Oct 1 2011 23:17 12 飴か夢.mp3
7604026 Oct 1 2011 23:17 13 ストラップ.mp3
2869353 Oct 1 2011 23:17 14 [Secret Track].mp3
8648 Jul 11 2016 07:48 AlbumArtSmall.jpg
46768 Jul 11 2016 07:48 Folder.jpg
184832 Jun 26 2018 17:00 Thumbs.db
Total 17 files
# play *
# ID3v2: Ver: 0200, Flag: 00 (356606)
V2.2: 'JPG'
Album: 'ロミオとシンデレラ'
Title: 'キャットフード'
Artist: 'doriko Feat. 初音ミク'
Year: 2010
Disc:
Track: 1/14
Sample Rate: 44100
00:04:49ID3v2: Ver: 0200, Flag: 00 (356606)
V2.2: 'JPG'
Album: 'ロミオとシンデレラ'
Title: '茜コントラスト'
Artist: 'doriko Feat. 初音ミク'
Year: 2010
Disc:
Track: 2/14
Sample Rate: 44100
00:04:18ID3v2: Ver: 0200, Flag: 00 (356571)
V2.2: 'JPG'
Album: 'ロミオとシンデレラ'
Title: 'エトランゼ'
Artist: 'doriko Feat. 初音ミク'
Year: 2010
Disc:
Track: 3/14
Sample Rate: 44100

※再生中に、dir 等のコマンドを実行出来ます。


最後に



RX65N Envition kit



DIY RX64M ボード

ソースコードは GitHub にあります。

FreeRTOS は使ってみると、非常に応用範囲が広く、簡単に扱える事が判りました。

元々、シングルタスクで動いていた物ですが、何の問題(タスクのスタックサイズを大きくした程度)もなく移行でき、調整も殆どいりませんでした。

これから、ネットワークスタックなどに応用してみようと思います。