16.78MHz -5ページ目

boost::asioで通信する

音を出して映像を出したら次は通信。OSの無いマイコンでは果てしなく難しいTCP/IP通信も、Linuxの動くマイコンではBoostASIO(非同期入出力)を使うことであっという間に実現出来てしまう。
いつも通りサンプルコード
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <locale.h>

// 文字列の中の特定の文字より後ろを削除する
void eraseBack( std::string &_string, const char *_split_chars ) {
for( ; *_split_chars; _split_chars++ ) {
std::string::size_type pos = _string.find( *_split_chars, 0 );
if( pos != std::string::npos )
_string.erase( pos, _string.size() - pos );
}
}

// 環境変数で設定されているロケールを取得する
void getLocale(
std::string &_locale
) {
_locale.assign( setlocale( LC_ALL, "" ) );
eraseBack( _locale, "_.@" );
}

// HTTPリクエストを送る
void sendHTTPRequest(
boost::asio::ip::tcp::iostream &_stream,
const std::string &_locale,
const std::string &_host
) {
_stream << "GET / HTTP/1.1\r\n";
_stream << "Accept: text/html\r\n";
_stream << "Accept-Language: " << _locale << "\r\n";
_stream << "User-Agent: Mozilla/5.0 (Compatible;)\r\n";
_stream << "Host: " << _host << "\r\n";
_stream << "\r\n";
_stream << std::flush;
}

int main(int argc, char* argv[]) {
// 引数が無かったり多すぎたりしたら異常終了
if (argc != 2) {
std::cout << "Invalid argument." << std::endl;
abort();
}

// ホスト名とロケールを用意して
const std::string host( argv[ 1 ] );
std::string locale;
getLocale( locale );

// 80番ポートへの接続を作り
boost::asio::ip::tcp::iostream stream( host, "http" );

// 接続できなかったら異常終了
if( !stream ) {
std::cout << "Unable to connect to " << host << std::endl;
abort();
}

// HTTPリクエストを送る
sendHTTPRequest( stream, locale, host );

// 接続先がコネクションを閉じるまで受信したデータを標準出力に書き出す
std::string result;
while( getline( stream, result ) ) {
std::cout << result << std::endl;
}
}

Boostのライブラリ名は環境によってやや異なるので本当はautoconfマクロ等を使って探した方が良いが、面倒なのでここではgcc-4.3を使っている場合の名前で直接g++を呼ぶことにする。
$ g++ asio_sample.cpp -lboost_system-gcc43-mt -o asio_sample.cpp
$ ./asio_sample ameblo.jp
HTTP/1.1 200 OK
Date: Fri, 04 Dec 2009 17:49:25 GMT
Server: Apache
Set-Cookie: JSESSIONID=1CBAD2DAD73DE2066BDFA3D0AC378331; Path=/
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Expires: Tue, 02 Dec 2008 09:05:21 JST
Transfer-Encoding: chunked
Content-Type: text/html;charset=UTF-8

1ff8
...

要するに、C++のプログラマにはお馴染みのSTL iostreamと同じノリで送受信する。
boost::asio::ip::tcp::iostream stream( host, "http" );
ストリームのコンストラクタの第2引数はポート番号を指定する。ポート番号は数値で指定しても良いし、このサンプルのようにwell-knownポートのサービス名で指定しても良いが、いずれの場合も文字列で指定する点に注意

SDLで描く

16.78MHz-sdl_sample音を出したら次は画面に映像を表示したくなるもの。Linux環境でグラフィカルなプログラムを書く方法はいくつかあるが、中でも最もシンプルなのがSDLを使う方法だ。SDLはウィジットのような高度なGUIを一切提供しておらず、開発者はSDLが用意したフレームバッファを読み書きすることで表示内容を変更する。
まずはサンプルコード
#include <stdlib.h>
#include <math.h>

#include <errno.h>

#include <SDL/SDL.h>

// 現在時刻を返す
double getTime() {
return SDL_GetTicks() * 0.001;
}

// 指定された時刻までスリープ
void waitUntil( double _until ) {
double diff = _until - SDL_GetTicks() * 0.001;
if( diff > 0.0f )
SDL_Delay( diff * 1000 );
}

// このプログラムでは16x16を1タイルとしてタイルブリットを行う
// タイルの更新フラグを作る
void initUpdateList(
SDL_Surface *_screen,
uint8_t **_udlist
) {
*_udlist = malloc( sizeof( uint8_t ) * _screen->w * _screen->h / 256 );
}

// タイルの更新フラグを捨てる
void finalUpdateList(
uint8_t *_udlist
) {
free( _udlist );
}

// タイルの更新フラグを全て下げる
void clearUpdateList(
SDL_Surface *_screen,
uint8_t *_udlist
) {
int x, y;
for( y = 0; y != _screen->h / 16; y++ )
for( x = 0; x != _screen->w / 16; x++ )
_udlist[ y * _screen->w / 16 + x ] = 0u;
}

// タイルの更新フラグを立てる
void setUpdateList(
SDL_Surface *_screen,
uint8_t *_udlist,
unsigned int _x, unsigned int _y
) {
_x /= 16;
_y /= 16;
_udlist[ _y * _screen->w / 16 + _x ] = 1u;
}

// 更新の必要な全てのタイルの更新を要求する
void update(
SDL_Surface *_screen,
uint8_t *_udlist
) {
int x, y;
for( y = 0; y != _screen->h / 16; y++ )
for( x = 0; x != _screen->w / 16; x++ )
if( _udlist[ y * _screen->w / 16 + x ] )
SDL_UpdateRect( _screen, x * 16, y * 16, 16, 16 );
}

// 点を描く
void drawPixel(
SDL_Surface *_screen,
uint8_t *_udlist,
unsigned int _x, unsigned int _y,
uint32_t _color
) {
uint32_t native_color =
SDL_MapRGB(
_screen->format,
( _color >> 16 ) & 0xFF,
( _color >> 8 ) & 0xFF,
_color & 0xFF
);
setUpdateList( _screen, _udlist, _x, _y );
// 256色だったら
if( _screen->format->BytesPerPixel == 1 )
*( (uint8_t*)_screen->pixels + _y * _screen->pitch + _x ) =
(uint8_t)native_color;
// 16bitだったら
else if( _screen->format->BytesPerPixel == 2 )
*( (uint16_t*)_screen->pixels + _y * _screen->pitch / 2 + _x ) =
(uint16_t)native_color;
// 24bitだったら
else if( _screen->format->BytesPerPixel == 3 ) {
// 24bitの型というものは無いのでリトルエンディアンとビッグエンディアンで分けて処理
if( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
*( (uint8_t*)_screen->pixels + _y * _screen->pitch + _x * 3 ) =
(uint8_t)native_color;
*( (uint8_t*)_screen->pixels + _y * _screen->pitch + _x * 3 + 1 ) =
(uint8_t)( native_color >> 8 );
*( (uint8_t*)_screen->pixels + _y * _screen->pitch + _x * 3 + 2 ) =
(uint8_t)( native_color >> 16 );
}
else {
*( (uint8_t*)_screen->pixels + _y * _screen->pitch + _x * 3 + 2 ) =
(uint8_t)native_color;
*( (uint8_t*)_screen->pixels + _y * _screen->pitch + _x * 3 + 1 ) =
(uint8_t)( native_color >> 8 );
*( (uint8_t*)_screen->pixels + _y * _screen->pitch + _x * 3 ) =
(uint8_t)( native_color >> 16 );
}
}
// 32bitだったら
else if( _screen->format->BytesPerPixel == 4 )
*( (uint32_t*)_screen->pixels + _y * _screen->pitch / 4 + _x ) =
(uint32_t)native_color;
else {
printf( "Unknown pixel format.\n" );
abort();
}
}

// 線を描く
void drawLine(
SDL_Surface *_screen,
uint8_t *_udlist,
unsigned int _begin_x, unsigned int _begin_y,
unsigned int _end_x, unsigned int _end_y,
uint32_t _color
) {
// 傾きを求めて
float tangent = ( (float)_end_y - (float)_begin_y ) / ( (float)_end_x - (float)_begin_x );
// 傾きが1未満だったら
if( fabsf( tangent ) < 1.0f ) {
// 切片を求めて
float intercept = (float)_end_y - tangent * (float)_end_x;
int current_x;
if( _end_x < _begin_x ) {
unsigned int temp = _end_x;
_end_x = _begin_x;
_begin_x = temp;
}
// x軸方向のピクセル数分だけ点を描く
for( current_x = _begin_x; current_x <= _end_x; current_x++ )
drawPixel( _screen, _udlist, current_x, tangent * current_x + intercept, _color );
}
// 傾きが1以上だったら
else {
// 傾きをひっくり返して x = tangent * y + intercept の形にして
tangent = 1.0f / tangent;
// 切片をもとめて
float intercept = (float)_end_x - tangent * (float)_end_y;
int current_y;
if( _end_y < _begin_y ) {
unsigned int temp = _end_y;
_end_y = _begin_y;
_begin_y = temp;
}
// y軸方向のピクセル数分だけ点を描く
for( current_y = _begin_y; current_y <= _end_y; current_y++ )
drawPixel( _screen, _udlist, current_y * tangent + intercept, current_y, _color );
}
}

// 解像度に依存せずに直線を描く
// 画面の左下が(-1.0,-1.0)、右上が(1.0,1.0)
void drawLineIndep(
SDL_Surface *_screen,
uint8_t *_udlist,
float _begin_x, float _begin_y, float _begin_z,
float _end_x, float _end_y, float _end_z,
uint32_t _color,
float *_matrix
) {
float translated_begin[ 4 ] = {
_matrix[ 0 ] * _begin_x + _matrix[ 1 ] * _begin_y +
_matrix[ 2 ] * _begin_z + _matrix[ 3 ],
_matrix[ 4 ] * _begin_x + _matrix[ 5 ] * _begin_y +
_matrix[ 6 ] * _begin_z + _matrix[ 7 ],
_matrix[ 8 ] * _begin_x + _matrix[ 9 ] * _begin_y +
_matrix[ 10 ] * _begin_z + _matrix[ 11 ],
_matrix[ 12 ] * _begin_x + _matrix[ 13 ] * _begin_y +
_matrix[ 14 ] * _begin_z + _matrix[ 15 ],
};
translated_begin[ 0 ] /= translated_begin[ 3 ];
translated_begin[ 1 ] /= translated_begin[ 3 ];
float translated_end[ 4 ] = {
_matrix[ 0 ] * _end_x + _matrix[ 1 ] * _end_y +
_matrix[ 2 ] * _end_z + _matrix[ 3 ],
_matrix[ 4 ] * _end_x + _matrix[ 5 ] * _end_y +
_matrix[ 6 ] * _end_z + _matrix[ 7 ],
_matrix[ 8 ] * _end_x + _matrix[ 9 ] * _end_y +
_matrix[ 10 ] * _end_z + _matrix[ 11 ],
_matrix[ 12 ] * _end_x + _matrix[ 13 ] * _end_y +
_matrix[ 14 ] * _end_z + _matrix[ 15 ],
};
translated_end[ 0 ] /= translated_end[ 3 ];
translated_end[ 1 ] /= translated_end[ 3 ];

if(
fabsf( translated_begin[ 0 ] ) < 1.0f && fabsf( translated_begin[ 1 ] ) < 1.0f &&
fabsf( translated_end[ 0 ] ) < 1.0f && fabsf( translated_end[ 1 ] ) < 1.0f
) {
unsigned int rast_begin[ 2 ] = {
( translated_begin[ 0 ] + 1.0f ) / 2 * _screen->w,
_screen->h - ( translated_begin[ 1 ] + 1.0f ) / 2 * _screen->h
};
unsigned int rast_end[ 2 ] = {
( translated_end[ 0 ] + 1.0f ) / 2 * _screen->w,
_screen->h - ( translated_end[ 1 ] + 1.0f ) / 2 * _screen->h
};
drawLine( _screen, _udlist, rast_begin[ 0 ], rast_begin[ 1 ], rast_end[ 0 ], rast_end[ 1 ], _color );
}
}

// 適当な3Dモデルを描く
void drawObjectIndep(
SDL_Surface *_screen,
uint8_t *_udlist,
uint32_t _color,
float *_matrix
) {
drawLineIndep( _screen, _udlist, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, _color, _matrix );
drawLineIndep( _screen, _udlist, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, _color, _matrix );
drawLineIndep( _screen, _udlist, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, _color, _matrix );
drawLineIndep( _screen, _udlist, -1.0, 0.0, 0.0, 0.0, -1.0, 0.0, _color, _matrix );
drawLineIndep( _screen, _udlist, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, _color, _matrix );
drawLineIndep( _screen, _udlist, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, _color, _matrix );
drawLineIndep( _screen, _udlist, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, _color, _matrix );
drawLineIndep( _screen, _udlist, 1.0, 0.0, 0.0, 0.0, -1.0, 0.0, _color, _matrix );
drawLineIndep( _screen, _udlist, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, _color, _matrix );
drawLineIndep( _screen, _udlist, 0.0, 0.0, -1.0, 0.0, -1.0, 0.0, _color, _matrix );
drawLineIndep( _screen, _udlist, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, _color, _matrix );
drawLineIndep( _screen, _udlist, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, _color, _matrix );
}

// 行列を単位行列に初期化する
void identMatrix(
float *_matrix
) {
const static float ident[ 16 ] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
memcpy( _matrix, ident, sizeof( float ) * 16 );
}

#define M( m, x, y ) ( m[ x + y * 4 ] )

// 2つの行列を掛けて結果を左辺に代入する
void multMatrix(
float *_left,
float *_right
) {
float temp[ 16 ];
memcpy( temp, _left, sizeof( float ) * 16 );
unsigned int x, y;
for( y = 0; y != 4; y++ )
for( x = 0; x != 4; x++ ) {
M( _left, x, y ) =
M( temp, 0, x ) * M( _right, y, 0 ) +
M( temp, 1, x ) * M( _right, y, 1 ) +
M( temp, 2, x ) * M( _right, y, 2 ) +
M( temp, 3, x ) * M( _right, y, 3 );
}
}

// OpenGL glRotate互換の回転
void rotateMatrix(
float *_matrix,
float _angle,
float _x,
float _y,
float _z
) {
float cos = cosf( _angle );
float sin = sinf( _angle );
float rotate[ 16 ] = {
_x * _x * ( 1 - cos ) + cos, _x * _y * ( 1 - cos ) - _z * sin, _x * _z * ( 1 - cos ) + _y * sin, 0.0f,
_y * _x * ( 1 - cos ) + _z * sin, _y * _y * ( 1 - cos ) + cos, _y * _z * ( 1 - cos ) - _x * sin, 0.0f,
_z * _x * ( 1 - cos ) - _y * sin, _z * _y * ( 1 - cos ) + _x * sin, _z * _z * ( 1 - cos ) + cos, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
multMatrix( _matrix, rotate );
}

// OpenGL glScale互換の拡大縮小
void scaleMatrix(
float *_matrix,
float _x,
float _y,
float _z
) {
float scale[ 16 ] = {
_x, 0.0f, 0.0f, 0.0f,
0.0f, _y, 0.0f, 0.0f,
0.0f, 0.0f, _z, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
multMatrix( _matrix, scale );
}

// フレームバッファを書き換えるためにロックする
void lockSurface( SDL_Surface *_screen ) {
if( SDL_MUSTLOCK( _screen ) ) {
if( SDL_LockSurface( _screen ) ) {
printf( "Unable to lock screen: %s\n", SDL_GetError() );
abort();
}
}
}

// フレームバッファのロックを解除する
void unlockSurface( SDL_Surface *_screen ) {
if( SDL_MUSTLOCK( _screen ) ) {
SDL_UnlockSurface( _screen );
}
}

int main() {
// SDLを初期化
if ( SDL_Init( SDL_INIT_VIDEO ) ) {
printf( "Unable to init SDL: %s\n", SDL_GetError() );
abort();
}
// フレームバッファを1つ作成
SDL_Surface *screen = SDL_SetVideoMode(1024, 768, 32, SDL_SWSURFACE );
if( !screen ) {
printf( "Unable to set screen size to 640x480: %s\n", SDL_GetError() );
abort();
}
uint8_t *udlist;
float matrix[ 16 ];
double system_time = getTime();
int cycle_count;
// タイルの更新フラグを初期化
initUpdateList( screen, &udlist );
clearUpdateList( screen, udlist );
// 60フレームで5秒間
for( cycle_count = 0; cycle_count != 60 * 5; cycle_count++ ) {
// 同次座標を初期化
identMatrix( matrix );
// ピッチ回転 20度
rotateMatrix( matrix, M_PI / 9.0f, 1.0f, 0.0f, 0.0f );
// ヨー回転 経過時間 * 180度
rotateMatrix( matrix, M_PI * cycle_count / 120.0f, 0.0f, 1.0f, 0.0f );
// 縮小 80% (ついでにアスペクト比修正)
scaleMatrix( matrix, 0.8f * 768 / 1024, 0.8f, 0.8f );
// フレームバッファをロック
lockSurface( screen );
// 3Dモデルを描画
drawObjectIndep( screen, udlist, 0xFF0000, matrix );
// フレームバッファのロックを解除
unlockSurface( screen );
// フレームバッファを更新
update( screen, udlist );
// タイルの更新フラグを消去
clearUpdateList( screen, udlist );
// 1/60秒スリープ
waitUntil( system_time + 0.016667 );
system_time = getTime();
// フレームバッファをロック
lockSurface( screen );
// 先ほどと同じモデルを黒で描く(消去する)
drawObjectIndep( screen, udlist, 0x000000, matrix );
// フレームバッファをアンロック
unlockSurface( screen );
}
// タイルの更新フラグを破棄
finalUpdateList( udlist );
}

$ gcc sdl_sample.c -lSDL -lm -o sdl_sample
$ ./sdl_sample

ワイヤーフレームの8面体が出てきて回転したら成功。重要な所をつまんでいくと、
  if ( SDL_Init( SDL_INIT_VIDEO ) ) {
SDLの他の全ての関数を呼ぶ前にまずSDLを初期化する。引数としてSDLのどの機能を使う予定かを指定する。今回は描画まわりだけを使うつもりなのでSDL_INIT_VIDEOだけを指定する。
  SDL_Surface *screen = SDL_SetVideoMode(1024, 768, 32, SDL_SWSURFACE );
1024x768ピクセルで1ピクセルあたり32bitのソフトウェア(ハードウェア上に直接確保されていなくても構わない)フレームバッファを作る
    if( SDL_LockSurface( _screen ) ) {
フレームバッファはよそのスレッドから同時に書き換えられたり、書き換え中に描画されてしまったりしないように書き換える前にロックするのがマナー。SDL_Surface::pixelsがフレームバッファへのポインタなので、ここからフレームバッファの内容を書き換え
    SDL_UnlockSurface( _screen );
ロックを解除して、
        SDL_UpdateRect( _screen, x * 16, y * 16, 16, 16 );
更新が必要な矩形範囲の更新を要求すると新しいフレームバッファの内容が画面に表示される。
ちなみにこのプログラムでは書いていないが、本当はSDL_FreeSurfaceでフレームバッファを破棄して、SDL_Quitで終了する必要がある。
SDLにはこの他にもイベントハンドラや入力デバイスの処理、サウンド、ポータブルスレッドといった機能が備わっている。詳しいことはドキュメントを参照。

LeopardBoard

ブログのアクセス数が急に増えたと思ったら先日のHawkBoardのネタここからリンクされたのが原因だったようだ。ありがとうございます。ありがとうございます。で、リンク先の日記LeopardBoardなるものの存在を知ったので軽く調べてみた。

LeopardBoardはやっぱり名前から想像が付く通りBeagleBoardやHawkBoardと同じ、テキサスインスツルメンツのバックアップを受けてコミュニティベースで開発されているシングルボードコンピュータで、同社の映像処理に特化したSoC、DaVinci TMS320DM355カメラや音声入出力、SDカードスロット等を付けた、いわばデジカメ自作キット。価格はLeopardBoard単品$84VGAカメラ付きで$99、カメラボード単品は2M3M5Mそれぞれ$35、$40、$45。

このカメラはもしかしてOMAP3のカメラインターフェースと互換があるんじゃないかと思ったが、ピンの本数からして別物だった。Gumstix Overoのカメラコネクタに繋ぐのは難しそうだ。

ALSAの叩き方

Linuxの走る高性能なSoCが増えて、今まではその都度内蔵フラッシュメモリにバイナリを転送して実行、みたいなことをしていたマイコン屋さんもLinuxシステムに触れる機会が増えたんじゃないだろうか。Linuxが動いているマシンではユーザ空間のプロセスは基本的にハードウェアを直接触ることは出来ない。あぁ、今までは音色を出すのにPWMをつつくだけで良かったのに、とげんなりしてしまうかもしれない。しかし、LinuxのオーディオアーキテクチャALSAから音を出すのは実はそれと同じくらい簡単だ。ここでは、高度な機能は抜きにしてLinuxシステム上で動くプログラムから音を出す方法を紹介する
まずはサンプルコード
#include <stdlib.h>
#include <stdint.h>
#include <math.h>

#include <time.h>

#include <alsa/asoundlib.h>

// 現在時刻を返す
double getTime() {
struct timespec time_spec;
clock_getres( CLOCK_REALTIME, &time_spec );
return (double)( time_spec.tv_sec ) + (double)( time_spec.tv_nsec ) * 0.000001;
}

// 指定された時刻までスリープ
void waitUntil( double _until ) {
struct timespec until;
until.tv_sec = (time_t)( _until );
until.tv_nsec = (long)( ( _until - (time_t)( _until ) ) * 1000000.0 );
clock_nanosleep( CLOCK_REALTIME, TIMER_ABSTIME, &until, NULL );
}

// ここから本体
int main() {
// 0番目のサウンドデバイス
const static char device[] = "hw:0";
// 符号付き16bit
const static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
// snd_pcm_readi/snd_pcm_writeiを使って読み書きする
const static snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED;
// 再生周波数 48kHz
const static unsigned int sampling_rate = 48000;
// モノラル
const static unsigned int channels = 1;
// ALSAがサウンドカードの都合に合わせて勝手に再生周波数を変更することを許す
const static unsigned int soft_resample = 1;
// 20ミリ秒分ALSAのバッファに蓄える
const static unsigned int latency = 20000;
// ラ(440Hz)
const static unsigned int tone_freq = 440;
// 5秒間流す
const static unsigned int length = 5;

snd_pcm_t *pcm;
// 再生用PCMストリームを開く
if( snd_pcm_open( &pcm, device, SND_PCM_STREAM_PLAYBACK, 0 ) ) {
printf( "Unable to open." );
abort();
}

// 再生周波数、フォーマット、バッファのサイズ等を指定する
if(
snd_pcm_set_params(
pcm, format, access,
channels, sampling_rate, soft_resample, latency
)
) {
printf( "Unable to set format." );
snd_pcm_close( pcm );
abort();
}

// 再生するためのサイン波を作っておく
const unsigned int buffer_size = sampling_rate / tone_freq;
int16_t buffer[ buffer_size ];
int buffer_iter;
for( buffer_iter = 0; buffer_iter != buffer_size; buffer_iter++ )
buffer[ buffer_iter ] = sin( 2.0 * M_PI * buffer_iter / buffer_size ) * 32767;

double system_time = getTime();
unsigned int beep_counter = 0;
for( beep_counter = 0; beep_counter != tone_freq * length; beep_counter++ ) {
// とりあえず1周期分だけ書き出す
int write_result = snd_pcm_writei ( pcm, ( const void* )buffer, buffer_size );
if( write_result < 0 ) {
// バッファアンダーラン等が発生してストリームが停止した時は回復を試みる
if( snd_pcm_recover( pcm, write_result, 0 ) < 0 ) {
printf( "Unable to recover this stream." );
snd_pcm_close( pcm );
abort();
}
}
// 少し待ってからまた1周期分書き出す
waitUntil( system_time + (double)buffer_size / 48000.0f );
system_time = getTime();
}
// 終わったらストリームを閉じる
snd_pcm_close( pcm );
}

ALSAを使うのでコンパイルする時はリンカに-lasoundを渡してやる必要がある。
$ gcc alsa_sample.c -lasound -lm -o alsa_sample
$ ./alsa_sample
(ポーーーーーーーーーーーー)

まずsnd_pcm_openでストリームを開き、snd_pcm_set_paramsで設定をして、snd_pcm_writeiで波形を書き出して、snd_pcm_closeでストリームを閉じる。他にも便利な機能が沢山用意されているが基本的にこれだけで音は鳴る。ALSAのレイテンシは大きくすればするほど高負荷時でも音が途切れにくくなるが、遅れて音が出るようになる最近のALSAにはソフトウェアミキサーが備わっているので、ハードウェアが同時に複数の音を鳴らせるような作りになっていなくても(オンボードサウンドは大抵これ)複数のストリームを開くことが出来る。ソフトウェアミキシングの品質はあまり良くないがそれが気にならない場合はミキシングはALSAに任せてしまうのが楽。

Mobile ITX

Mobile-ITX CPU Module-Hand-1Mini-ITX(17cm)、Nano-ITX(12cm)、Pico-ITX(10cm)といったやたら小さいマザーボードの規格を作ってきたVIAから新しいマザーボードの規格が発表された
新しく発表されたMobile-ITXは、縦横6cmより小さい。
VIAは来年にも実際に同社のx86互換プロセッサを搭載したMobile-ITX製品をリリースするつもりのようだ。工業向けにこういう製品が今まで無かったわけではないが、おそらく普通のPCパーツ屋に並ぶことになるであろうこのVIAのマザーボードはPC Linuxに慣れ親しんだユーザが組み込みLinuxへの第一歩を踏み出すのに丁度いいデバイスになるだろう。

ちなみに、端子が全く出ていないように見えるのは目の錯覚ではなくI/Oは全部裏側のコネクタからまとめて出ている。一般的なコネクタの形状に変換するI/Oボードが別で必要になるので注意。