『雲仙』 オーディオ・フレームワーク

mbed用のオーディオ・フレームワーク『雲仙』を開発、公開しました。 『雲仙』はI2S回線を利用したオーディオ入出力のフレームワークで、別途開発したオーディオ・コーデック・コントローラ『島原』と組み合わせることで以下のような機能を提供します。

  • アナログ・オーディオを入力し、mbedで信号処理を行い、アナログ・オーディオを出力する
  • 単精度浮動小数点型によるインターフェース
  • サンプル周波数32kHz, 44.1kHz, 48kHz, 96kHz に対応
  • サンプル・バイ・サンプル処理、ブロック処理の双方に対応
  • 信号処理コールバックおよび信号処理初期化コールバックを提供
  • 信号処理と並行してmbed-SDKが提供するAPIを実行可能
  • mbed / mbed RTOSに対応
  • UMB-ADAU1361Aと数本の線で結線することで、すぐに使うことが出来る
  • 以下のmbedプラットホームに対応

以下、雲仙の利用方法について説明します。

なお、現時点でLPC4088版についてはオーディオ信号処理中にI2Cを動かすと、雑音が入ることがわかっています。これは空中配線のためグラウンドが不安定なことに起因すると考えられますが、正確な原因は突き止められていません。ご注意ください。また、筆者の手元のLPC4088 QuickStart Boardは動作が極めて不安定で、LEDチカチカも満足に動かすことが難しい状態です。プログラムの検証が十分ではないことをご了承ください。

雲仙の始め方

雲仙を利用して信号処理をはじめるには、ハードウェアをそろえ、サンプル・アプリケーションをインポートします。

ハードウェアを入手する

雲仙は特定のオーディオ・コーデックに依存していません。I2Sマスターとして動作するステレオI2SコーデックLSIであれば、どのようなものでも使うことが出来ます。しかし、『島原』オーディオ・コーデック・コントローラーは、オーディオ・コーデック基板UMB-ADAU1361Aに対応しているため、最初はこの基板を使うといいでしょう。

金子システムのオーディオ・コーデック基板 UMB-ADAU1361Aはアナログ・デバイセズのステレオ・コーデックADAU1361を使用した基板です。この基板はステレオ・ミニジャックのライン入力、出力に加えて、同じくステレオ・ミニジャックのヘッドホン出力を持っています。また、ブレッドボードで使いやすいピンは位置になっているため、実験をするのに便利な基板です。

サンプル・アプリケーションからはじめる

雲仙を使う上で一番簡単な方法は、サンプル・アプリケーションをインポートしてしまうことです。こうすることで、使用する上で必要な手順を覚えなくてもフレームワークを使うことができます。 次の三つのサンプル・アプリケーションを用意しています。

Import programunzen_sample_lpcxpresso_4337

LPCXpresso 4337 + UMB-AUAD1361A を使用したオーディオ・サンプル。

Import programunzen_sample_LPC4088_quickstart

Not well confirmed

Import programunzen_sample_nucleo_f746

Sample program of the mbed Audio Framework for the Nucleo F746ZG

いずれもライブラリが異なるだけで、やっている事は入力から出力へそのままデータを流すTalkThroughと呼ばれる処理です。なお、NucleoをUI基板と一緒に使う方は、以下のスケルトンから始めると良いでしょう。

Import programskeleton_unzen_nucleo_f746

Sample program of the mbed Audio Framework for the Nucleo F746ZG

結線

LPCXpresso 4337

以下にLPCXpresso 4337とUMB-ADAU1361Aの結線を示します。筆者はブレッドボード用のワイヤで接続しました。

/media/uploads/shorie/lpcxpressor4088-umb-adau1316a_OGXc6EK.png

実際に配線したボードの写真を以下に示します。

/media/uploads/shorie/imgp2170.jpg

LPC4088 Quick Start board

以下にLPCXpresso 4337とUMB-ADAU1361Aの結線を示します。筆者はブレッドボードにQuick Start Boardを挿して配線しました。

/media/uploads/shorie/lpc4088-umb-adau1361a.png

Nucleo F746ZG

以下にNucleo F746ZGとUMB-ADAU1361Aの結線を示します。SDA/SCLは外部抵抗でプルアップしなければならないことに注意してください。

/media/uploads/shorie/unzen_-----.png

実際に配線したボードの写真を以下に示します。

/media/uploads/shorie/img_20160619_150554.jpg

桐壺基板

上記UMB-ADAU1361AとNUCLEO_F746ZGを接続するための基板「 桐壺 」の設計情報を公開します。

この基板はKiCad 4.0.2 で設計し、出力したgerberデータをスイッチサイエンスの基板製造サービスで製造し、動作を確認しています(写真)。なお、実際に製造して確認したものに対して、本ページのURLをシルク情報として追加しています。そのため、私が製造したものと全く同じではありません。万に一つのことがありますので、基板を外注に出す方は、くれぐれも事前の注意を払ってください。また、この基板は実験用に作成したものであり、民生、産業、医療などの用途に耐えるような設計ではありません。どのような理由であれ、この基板によって発生した問題については責任を取りません。

なお、基板に実装されている抵抗はI2Cのプルアップ用です。私は適当に4.7kΩを使っていますが、より厳密な抵抗を使い方は値を変更して使ってください。 /media/uploads/shorie/imgp2211.jpg

オーディオ入出力

UMB-ADAU1361Aのオーディオ入出力は2.5mm径のステレオ・ミニジャックです。筆者は実験する際、ウォークマンから直接LINE入力に信号を入力しています。

また、出力信号はLINE OUTとHP OUTがあります。いずれにも出力は出ますので、好きな方を使ってください。『島原』コーデック・コントローラのAPIを使うと、CODEC ICのアナログ回路でゲイン・コントロールをすることもできます。APIのゲインの単位は[dB]です。

サンプルを動かす

結線が終わったら、サンプルアプリケーションをコンパイルしてmbedにダウンロードしてください。正常に動作している場合にはLEDが点滅します。

この状態でLINE INにオーディオ信号を入れると、HP OutとLine Outからオーディオ信号が出力されます。ループの中でゲインを変えているので、音量は刻々と変ります。

オーディオ・信号の処理はすべて割り込みハンドラの中で行われています。そのため、main()関数の内部ではmbedのAPIを使うことが出来ます。例えばこのサンプル・アプリケーションでは、I2C API とDigitalOutを使用しています。また、開発中はシリアル出力APIも使用しました。

信号処理を行う

信号処理は、信号処理コールバック関数の中に記述します。このコールバック関数は雲仙の Framework::start() メソッド実行時にフレームワークに登録され、以後I2S受信データが貯まる度に呼び出されます。サンプル・アプリケーションの信号処理コールバック関数はprocess_callback()です。名前はアプリケーションによって変えてかまいません。

信号処理コールバック関数

    // customer signal processing call back.
void process_callback(
            float rx_left_buffer[],     // array of the left input samples
            float rx_right_buffer[],    // array of the right input samples
            float tx_left_buffer[],     // place to write the left output samples
            float tx_right_buffer[],    // place to write the left output samples
            unsigned int block_size     // block size [sample]
            )
{
        // Sample processing
    for ( int i=0; i<block_size; i++)   // for all sample
    {
        tx_left_buffer[i] = rx_left_buffer[i];      // copy from input to output
        tx_right_buffer[i] = rx_right_buffer[i];
        
    }
}

信号処理コールバック関数は入力デーバッファ配列を2本(左右チャンネル用)、出力データバッファ配列を2本(左右チャンネル用)ブロック・サイズ指定用変数を一つ、引数として受け取ります。

ブロック・サイズ指定用変数 block_size は、一回のコールバック関数呼び出しで、何サンプルを処理すべきか知らせます。最小値は1で、この値はデフォルトでは1です。block_sizeを変更したい場合には、set_block_size() メソッドを呼び出して設定してください。ただし、satart() メソッド実行前に設定する必要があります。

block_size == 1 のとき、入出力のデータ数はそれぞれ左右1データずつとなります。

信号処理コールバック関数に与えられる信号は浮動小数点型であり、+/-1の範囲に収まっています。信号処理の結果を出力(送信)データとして与える場合には、必ず+/-の範囲に収めてください。これは信号処理プログラマの責任です。

なお、信号処理コールバックは、main()関数ではなく、割り込みコンテキストで実行されます。また、block_size > 1 の場合、処理途中で割り込まれることもあり得ます。信号処理コールバック関数の処理は、その呼び出し終期よりも短い時間で終了する必要があります。雲仙フレームワークはこの処理が所定の時間内に終わったか否かを確認しません。また、所定の時間内に終わらない場合に何が起きるかは不定です。

信号処理の初期化を行う

信号処理コールバック内部でフィルタを使うときなど、それらを初期化したいことがあります。初期化関数はmain()の中で自分で呼んでもかまわないのですが、初期化コールバックに記述することで目的がはっきりし、かつ正しいタイミングで呼び出すことができます。呼び出しはフレームワークが行います。

サンプル・アプリケーションの信号処理コールバック関数はinit_callback()です。名前はアプリケーションによって変えてかまいません。

処理化コールバック

    // customer signal processing initialization call back.
void init_callback(
            unsigned int block_size     // block size [sample]
            )
{
        // place initialization code here
}

サンプルの初期化コールバックは空ですが、ここに好きな初期化コードを書けば、そのコードはフレームワークの実行開始時に一度だけ、割り込みが発生する前に呼ばれます。

CPU負荷を計測しよう

デバッグ用コールバック機能を使えば、割り込みや信号処理プロセスの実行タイミングおよびその時間を知ることができます。この機能を使うサンプルアプリケーションを作りました。説明も書きましたのでご活用ください。

Import programunzen_sample_lpcxpresso_4337_callbacks

Sample application to explain how to use the debug call back of the Unzen Framework

参考リンク

  • ukifune Nucleo F746ZG用UI基板浮舟と、そのライブラリ
  • skeleton_unzen_nucleo_f746 Nucleo F746ZGおよびUI基板を使う場合のスケルトンプログラム。F746を使う方はここから読み始めると良いでしょう。


Please log in to post comments.