2012年4月29日日曜日

kz_h8writeとkz_xmodemの2つのソフトウェアを、Mac OSとLinuxとWIndowsの3つのプラットフォームで動作確認する

XMODEM for KOZOS (12ステップで作る 組込みOS自作入門 KOZOS用ユティリティ kz_xmodem)を実装したことで、kz_h8writeとあわせてKOZOS用ユティリティが2つになりました。

ダウンロードはプロジェクトページからどうぞ。
http://sourceforge.jp/projects/kz-h8write/
http://sourceforge.jp/projects/kz-xmodem/

kz_h8writeとkz_xmodemは、Mac OS、Linux、Windowsで動作するように実装してあります。
ただ、あまり積極的にテストしていなかったので、2つのツールが揃ったところでまとめてテストしました。
  • それぞれのバイナリは2012/04/29現在のコードから生成したものを使用しています。
  • Mac OSには10.7.3、LinuxにはUbuntu 11.04、WindowsにはWindows 7を使っています。
  • 各環境で特別な手順を踏まずに手に入るビルド環境を使っています。
OSkz_h8writekz_xmodem
Mac OS
Linux
Windows

2種類のソフトウェアを3つの環境で動作させるわけですから意外に手間がかかります。
クロスプラットフォームともなると自動テストというわけにもいきません。
ターゲットボードにあるスイッチを切り替えなければならないとなればなおさらです。

それでも自分で確認しておくと安心度が違います。
これからもこういった動作確認の報告はしていきたいところです。

2012年4月28日土曜日

XMODEM for KOZOS (12ステップで作る 組込みOS自作入門 KOZOS用ユティリティ kz_xmodem)

「KOZOSってなぁに?」という方は本家のホームーページをご覧下さい。

kz_xmodem

12ステップで作る組込みOS自作入門(KOZOS)の第8ステップ以降では、ROMにブートローダ(kzload)を書き込み、ブートローダ経由でOSRAMに流し込んで動作させるようになっています。転送プロトコルにはXMODEMを採用していますが、複数のプラットフォーム上に存在する汎用ツールの多くが、何ステップかの操作をした後でようやく転送が開始されるような仕組みになっています。また、環境やタイミングに依存して、汎用ツールとKOZOSブートローダの組み合わせでうまく転送できない事もあるようです。
kz_xmodemは、上記の「複数の操作を段階的に行なう事の煩雑さ」や「転送に失敗する事の面倒さ」などを取り除く事を目的に、設計実装されたKOZOS専用XMODEMプログラムです。

kz_xmodemの特徴

l  KOZOSブートローダの動作に合わせて設計したKOZOS専用XMODEMプログラム。
l  LinuxWindowsMac OSに対応。
l  KOZOSブートローダに対して内部でload状態に自動遷移。
l  ターゲットをリセットしてkz_xmodemを実行するだけで転送完了。
l  コマンドプログラムなのでMakefileからの自動実行なども可能。
l  MITライセンスを採用し、商用、非商用を問わず自由に再利用可能。

汎用ツールとkz_xmodemの比較

汎用ツールの場合

汎用ツールの場合、操作は大まかにわけて3段階必要です。
まず、KOZOSブートローダをload状態にします。

次にXMODEM転送の為にファイルを指定して転送を開始します。
ツールによっては、ファイル名の入力などに手間取ると期待したような動作にならない事があるようです。うーん。ここは気合いを入れて操作したいところではありません。

転送が正常に終了したらKOZOSブートローダのプロンプトに戻ってrunコマンドを実行します。


上記のように汎用ツールの場合、ツールの中の画面をいちいち行ったり来たりしなければなりません。OSの動作確認をしたくてうずうずしている時に、色々な操作が伴いとても面倒です。

kz_xmodemの場合

kz_xmodemを使うと、シンプルな2つのステップでOSの動作確認まで進めます。
kz_xmodemに、書き込み対象ファイル名と使用するシリアルポート名を与えて実行します。

書き込みが完了したらKOZOSブートローダにシリアルコンソールを使って接続します。
後はrunコマンドを実行するだけ。

kz_xmodemはコマンドツールですから、Makefileでビルド後に呼ぶようにすれば自動ダウンロードが可能です。要するに、ビルドした後でOSの動作確認に至るまでがmake一発で一気にできてしまうのです。従来のようにわざわざ別のツールを立ち上げてからアレコレ操作する必要はありません。

プロジェクトページ

プロジェクトはsourceforgeで管理しています。 http://sourceforge.jp/projects/kz-xmodem/

2012年4月8日日曜日

WAVファイルを手軽にきちんと扱いたい!

WAVファイルを手軽にきちんと扱いたい!

WAVファイルを手軽にきちんと扱いたくなったので仕様や巷の実装を調べていました。

WAVファイルはチャンク構造になっていて、内部データ表現を自由に選ぶ事ができます。
自由に選ぶ事が出来ると言う事は、それなりにきちんと設計実装したプログラムでなければ正しく扱えない事を意味します。


手元にあった書籍(名前は出しませんが)や巷の実装を見ると、随分と厳しい暗黙の前提条件が用いられていて、色々なWAVファイルをあまり正しく読める事を期待できないなぁという感じでした。

また、データ保持の方式が「スタックにずんっと置く」ような実装だったり、「ファイルサイズに応じて超巨大なメモリをヒープから取る」ような実装だったりと、いくら巨大なメモリがPCに搭載されている昨今とは言え、何だかなぁ~という感じです。

そこで、巷の実装例を少し紹介した後で、設計実装したWAVファイルライブラリを御紹介します。

巷の実装を見てみる

今回設計したライブラリの紹介の前に、巷の実装を見てみましょう。
実装をそのまま掲載する事はできないので、ちょっと修正してあります。

巷の実装例(とある書籍のサンプルコード)

この実装は、出現するチャンク構造の順序に暗黙の前提条件が使用されている例です。

  fp = fopen(file_name, "rb");
  fread(riff_chunk_ID, 1, 4, fp);
  fread(&riff_chunk_size, 4, 1, fp);
  fread(riff_form_type, 1, 4, fp);
  fread(fmt_chunk_ID, 1, 4, fp);
  fread(&fmt_chunk_size, 4, 1, fp);
  fread(&fmt_wave_format_type, 2, 1, fp);
  fread(&fmt_channel, 2, 1, fp);
  fread(&fmt_samples_per_sec, 4, 1, fp);
  fread(&fmt_bytes_per_sec, 4, 1, fp);
  fread(&fmt_block_size, 2, 1, fp);
  fread(&fmt_bits_per_sample, 2, 1, fp);
  fread(data_chunk_ID, 1, 4, fp);
  fread(&data_chunk_size, 4, 1, fp);

この実装の場合、出現するチャンク構造の順序が異なるだけで全く正しい処理ができません。
そして、この後の処理が以下のようになっています。

  pcm->fs = fmt_samples_per_sec;
  pcm->bits = fmt_bits_per_sample;
  pcm->length = data_chunk_size / 2;
  pcm->s = calloc(pcm->length, sizeof(double));

data_chunk_sizeに期待するチャンク構造のチャンク・データ・サイズが格納されているとは限りません。そしてその値をそのまま使ってcallocしているので危ないです。

このコードは落第点です。
でも音を扱うという書籍のサンプル実装です。

この実装の危ない点は、間違っていても処理がどんどん進んでしまう事です。
そして、間違っている場合、何がどう間違っているのか検知していないので、APIの外から見た場合にどうしようもありません。

巷の適当に実装されたコードは意外にこういうのばかりであまり再利用には向いていません。
仮に入門者がこのコードを見て育った時に、「いつこの設計実装レベルから抜け出せるのだろう?」と考えると少し心配になってしまいます。

Tiny WAV I/O Moduleプロジェクト

Tiny WAV I/O Moudleプロジェクトとは、小規模組み込みシステムで使用可能なWAVファイルライブラリを作ろうという目的で作られたプロジェクトです。

今回実装したライブラリは、その設計の前段にあたるもので、「どういったインターフェースなら使いやすいかなぁ」というのを検討する為のもの。libcが使用可能な環境でのみ使用可能です。成果物はTiny WAV I/O Moduleプロジェクトのlibc-basedに追加しました。

まぁ、ざっくり言うとlibc-basedは「パソコンで使えるお手軽WAVライブラリ」ですね。

wavfileモジュールの特徴

以下に今回設計実装したお手軽WAVライブラリ、wavfileモジュールの特徴を示します。

  • 省メモリ設計。(ライブラリ側では巨大なメモリを要求しない。)
  • ファイル形式に依らず0.0から1.0で正規化されたデータ入出力インターフェースを採用。
  • 複数チャネルデータに対応。
  • ヘッダの実装詳細を把握しなくても使える。
データをどこに配置するのかについてはアプリケーション層が決めたい事の一つです。
巨大なメモリを勝手にアロケーションするようなライブラリは使いづらくて仕方ありません。
よってwavfileモジュールでは、これらに関知しないようにインターフェースを設計しました。

また、WAVファイルの処理を実装する時に意外に面倒なのが、データ形式の違いです。

今回のwavfileモジュールでは、ファイルのデータ形式によらず0.0から1.0で正規化されたデータ入出力インターフェースを採用しました。これによって、「8ビット形式のファイルは0から255で1バイトだよね。」とか「16ビット形式のファイルは-32768から32767で2バイトだよね。」とか考えなくて済みます。とにかくサンプルを1つ得るインターフェースを呼ぶだけで良いのです。

typedef struct {
    uint16_t num_channels;
    double channel_data[WAVFILE_MAXIMUM_CHANNELS];
} wavfile_data_t;

wavfile_read_dataを呼ぶと上記の構造体にサンプルデータが格納されて返ってきます。
channel_dataの中身は先の規格化された値が入っている事になります。

wavfileモジュールのインターフェース

wavfileモジュールを使うために必要なインターフェースは、リード用、ライト用と合わせてもたったの6つです。

WAVFILE *wavfile_open(const char *filename, WavFileMode mode, WavFileResult *result);
WavFileResult wavfile_read_info(WAVFILE *p, wavfile_info_t *info);
WavFileResult wavfile_read_data(WAVFILE *p, wavfile_data_t *data);
WavFileResult wavfile_write_info(WAVFILE *p, const wavfile_info_t *info);
WavFileResult wavfile_write_data(WAVFILE *p, const wavfile_data_t *data);
WavFileResult wavfile_close(WAVFILE *p);

基本思想

infoとdataの2段階で簡単に実現しちゃうよ!というのが本ライブラリの基本思想です。

読み込み
  • wavfile_openにファイル名とWavFileModeReadを与えてオープン。
  • wavfile_read_infoでヘッダ情報を読み込む。
  • wavfile_read_dataでデータを読み込む。 (必要に応じて繰り返す)
  • wavfile_closeでクローズ。
書き込み
  • wavfile_openにファイル名とWavFileModeWriteを与えてオープン。
  • ヘッダ情報を設定する。
  • wavfile_write_infoでヘッダ情報を書き込む。
  • データを設定する。
  • wavfile_write_dataでデータを書き込む。(必要に応じて繰り返す)
  • wavfile_closeでクローズ。
どんな風に使えるの?

単にデータを読みたい場合

よくあるやりたい仕事の1つが、とにかくデータを読んでみたい!というものです。
Tiny WAV I/O Moduleのlibc-based実装を使えば簡単に実現できてしまいます。

WavFileResult result;
wavfile_info_t info;
wavfile_data_t data;
WAVFILE *wf = wavfile_open("YourWavFileName.WAV", WavFileModeRead, &result);
if (wf != NULL) {
    wavfile_read_info(wf, &info);
    while (1) {
        wavfile_read_data(wf, &data);
        if (data.num_channels == 0) {
            // 読むべきデータが無くなったらチャネル数に0を返してくる。
            break;
        }
        // ここでデータを確認すれば良い。
    }
    wavfile_close(wf);
}

ソースコード

ソースコードは、以下からダウンロード可能です。
http://pt.sourceforge.jp/projects/tinywavio/

ライセンスはMITです。

今後の計画

最近は色々と忙しくて基板設計まで手が回らないので、今年は中途半端に拘るのを諦め、外販されているモジュールを活用してシステム物をやりたいなぁと計画しています。

実は、金子システム株式会社さんからUMB-SSM2603なるものが販売されています。


そうなのです。
このモジュールはACB-BF592と組み合わせてもミニサイズなのです。


動作も高速なので、実は今回のlibc-basedな実装もそのまま動かせたりして?!なんて甘い考えを持っていたりします。ファイルシステム周辺は何らかの抽象化層に乗せようかなぁ。

とにかく組み込み装置としてエレガントにWAVを扱えるようしていこうと考えています。