|
|
|
|
|
Windows関連
スクリーンセーバー作成法
半透明ウインドウの性能
bootfont.bin
キャビネット形式
ウインドウスタイルをいじる
Java製ソフトをServiceに登録する
イベントログにメッセージを出力する
コントロールパネルにアイコンを追加する
スクリプトによる拡張1
スクリプトによる拡張2
ガジェットの作成
大容量メモリ
メモリ搭載量の下限に挑む
スパースファイルにする
Solaris関連
OpenGL
Solaris設定
ディレクトリの読み込み
主筆プラグイン開発
マルチスレッドでの開発
door
音を出す
Blade100の正しい虐め方
パッケージの作成
画像入出力
BMPファイル
ICOファイル
ANIファイル
JPEGファイル
減色アルゴリズム
減色アルゴリズムの並列化
その他アルゴリズムなど
自由軸回転
Base64
文字列操作
CPU利用率の取得
正規表現ライブラリ
メタボールを作る
メタボールを作る2
正規表現とNFA・DFA
C言語の構文解析
液晶ディスプレイを解体してみた
|
Solarisで音を出す
2006年2月29日公開
Sunのマシン、とりあえずBlade100で音声を再生することについて考えてみる。
参考文献
文献も何も、/dev/audioのmanページを見れば、必要なことは全部書かれている。
だが英語である。
訳してみる
訳してみた。
それでもやっぱり判りにくいから、使い方について簡単に説明してみる。
デバイスファイルのオープン
まずは/dev/audioをopen()システムコールで開く必要がある。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd;
// 開く
fd = open( "/dev/audio", O_RDWR );
if ( fd < 0 ) {
// 失敗した
fprintf( stderr, "It failed to open /dev/audio\n" );
return 0;
}
// 閉じる
close( fd );
return 0;
}
|
マシンに直接繋いだキーボードとディスプレイで操作している限り、特に何も考えることなく一般ユーザ権限で/dev/audioを開くことができるはずである。
ただし、telnetで接続して操作している場合は、アクセス権が無くて開くことができない可能性がある。すなわち、/dev/audioを開くのにroot権限が必要になる。どうやら、リモートで接続されて盗聴などのよからぬ事をされるのを防止するという理由があるらしい。
どうしてもtelnetで接続した状態で一般ユーザから/dev/audioを使えるようにしたければ、デバイスファイルのパーミッションを変更する必要がある。
俺の環境では「/dev/audio」は「/dev/sound/0」へのシンボリックリンクになっており、「/dev/sound/0」は「/devices/pci@1f,0/sound@8:sound,audio」へのシンボリックリンクになっている。でもって、この最後の「/devices/pci@1f,0/sound@8:sound,audio」のパーミッションが「crw-------」となっているために、一般ユーザからは使えないようになっている。
だから、この「/devices/pci@1f,0/sound@8:sound,audio」というファイルのパーミッションを変更してやればいいということになる。とりあえず、全開にしておけば間違いはない。
デバイスの状態を取得する
デバイスファイルを開いたら、次はデバイスの状態を取得してみる。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
// audio_prinfo構造体の中身を表示する
void foo( const struct audio_prinfo *p )
{
printf( "sample_rate = %d\n", p->sample_rate );
printf( "channels = %d\n", p->channels );
printf( "precision = %d\n", p->precision );
printf( "encoding = %d : ", p->encoding );
switch ( p->encoding ) {
case AUDIO_ENCODING_ULAW:
printf( "AUDIO_ENCODING_ULAW" );
break;
case AUDIO_ENCODING_ALAW:
printf( "AUDIO_ENCODING_ALAW" );
break;
case AUDIO_ENCODING_LINEAR:
printf( "AUDIO_ENCODING_LINEAR" );
break;
}
printf( "\n" );
printf( "gain = %d\n", p->gain );
printf( "port = %d\n", p->port );
printf( "buffer_size = %d\n", p->buffer_size );
// 後は省略
}
// デバイスの状態を読み込み表示する
struct audio_info read_info( int fd )
{
struct audio_info info;
// 情報を取得する
if ( ioctl( fd, AUDIO_GETINFO, &info ) < 0 ) {
// 失敗
fprintf( stderr, "It failed to get audio info\n" );
return info;
}
printf( "Succseed to get info\n" );
printf( "--- record ---\n" );
foo( &info.record );
printf( "--- play ---\n" );
foo( &info.play );
printf( "\n" );
printf( "monitor_gain = %d\n", info.monitor_gain );
printf( "output_muted = %d\n", info.output_muted );
return info;
}
int main( int argc, char *argv[] )
{
int fd;
// 開く
fd = open( "/dev/audio", O_RDWR );
if ( fd < 0 ) {
// 失敗した
fprintf( stderr, "It failed to open /dev/audio\n" );
return 0;
}
// デバイスの情報を表示する
read_info( fd );
// 閉じる
close( fd );
return 0;
}
|
デバイスの状態の取得は、上記プログラムの真ん中ぐらいにある、ioctl()関数で行っている。その上にあるfooという関数は、単に構造体の中身を整理して画面に出力する処理を行っているだけである。また、main関数では/dev/audioを開いてread_info関数を呼び出しているだけである。
つまり、デバイスの設定を取得するには、AUDIO_GETINFOのioctlを行えばいいということになる。引数はaudio_info構造体の変数のアドレスである。
呼び出しに成功すると、引数に指定した構造体にデバイスの現在の設定が取得される。
Succseed to get info
--- record ---
sample_rate = 8000
channels = 1
precision = 8
encoding = 1 : AUDIO_ENCODING_ULAW
gain = 127
port = 1
buffer_size = 8192
--- play ---
sample_rate = 8000
channels = 1
precision = 8
encoding = 1 : AUDIO_ENCODING_ULAW
gain = 191
port = 3
buffer_size = 0
monitor_gain = 0
output_muted = 0
|
当然、上記の値は実行したときの環境によって変わってくる。
デバイスの状態を変更する
今度はデバイスの状態を変更してみる。例えば、音声を再生するときの音量を変更してみる。また、再生する音声のエンコード方式をPCMに変更する。
// <<< read_info関数は上のプログラムと同じ >>>
int main( int argc, char *argv[] )
{
int fd;
struct audio_info info;
// 開く
fd = open( "/dev/audio", O_RDWR );
if ( fd < 0 ) {
// 失敗した
fprintf( stderr, "It failed to open /dev/audio\n" );
return 0;
}
// デバイスの情報を表示する
info = read_info( fd );
// 44.1KHz・16bit・PCM・ステレオに設定する
info.play.sample_rate = 44100;
info.play.channels = 2;
info.play.precision = 16;
info.play.encoding = AUDIO_ENCODING_LINEAR;
info.play.gain = 10;
if ( ioctl( fd, AUDIO_SETINFO, &info ) < 0 )
fprintf( stderr, "It failed to set audio info\n" );
// 閉じる
close( fd );
return 0;
}
|
デバイスの設定を変更するには、AUDIO_SETINFOのioctlを行えばいい。audio_info構造体に適当な値を設定してioctlしてやるだけである。
なお、AUDIO_SETINFOの場合でも引数に指定した構造体の値は一部変更される可能性がある。
音声の再生
デバイスの状態を変更し、とりあえずは音声を再生する環境を整えることができたので、この辺で音を出してみることにする。
音を出すには、とりあえず/dev/audioにwrite()システムコールでデータを書き込めばいいのだが、問題はどんなデータを書き込むのかということである。
上で設定した「PCM・44.1KHz・16bit・ステレオ」なら、下記のようになる。

- 一つの「サンプル」は符号付き16bitの値
- 「サンプル」がチャンネル数分集まって「サンプルフレーム」を形成する。左から右の順にデータが格納される。
- 「サンプルフレーム」が「サンプリング周波数」個集まって、一秒分のデータになる。
以上を踏まえて、左側から400Hz、右側から1000Hzの音を出してみる。
#include <stdio.h>
#include <sys/audio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stropts.h>
#include <math.h>
void play( int fd )
{
// 符号付き16bit
// 44.1KHz・ステレオで一秒分のバッファ
short buf[ 44100 * 2 ];
int i;
// バッファに再生するデータを格納する
for ( i = 0; i < 44100; i++ ) {
double w = (double)i / 44100.0 * 6.28318;
// 左側
buf[i*2+0] = (short)( sin(w*400.0)*30000.0 );
// 右側
buf[i*2+1] = (short)( sin(w*1000.0)*30000.0 );
}
// 3秒分バッファに書き込む
for ( i = 0; i < 3; i++ )
write( fd, buf, sizeof( buf ) );
}
int main()
{
int fd;
struct audio_info info;
// デバイスファイルを開く
fd = open( "/dev/audio", O_RDWR );
if ( fd < 0 ) {
// 失敗
fprintf( stderr, "Failed to open file\n" );
return 0;
}
// 現在の状態を取得
if ( ioctl( fd, AUDIO_GETINFO, &info ) < 0 ) {
// 失敗
fprintf( stderr, "Failed to get audio info\n" );
goto ERR_EXIT;
}
// 44.1KHz・16bit・ステレオ・PCMに設定する
info.play.sample_rate = 44100;
info.play.channels = 2;
info.play.precision = 16;
info.play.encoding = AUDIO_ENCODING_LINEAR;
info.play.gain = 10;
info.play.port = AUDIO_HEADPHONE;
if ( ioctl( fd, AUDIO_SETINFO, &info ) < 0 ) {
fprintf( stderr, "Failed to set audio info\n" );
goto ERR_EXIT;
}
// 音を出す
play( fd );
ERR_EXIT:
// 閉じる
close ( fd );
return 0;
}
|
Blade100の内蔵スピーカーはモノラルで、左右の音の違いが確認できないから、一応ヘッドフォンに出力するようにしている。
AUファイル
話は変わるが、一応Solarisでは音声を記録するためのファイルフォーマットが決まっている。
それについてもmanページがあるが、例のごとく英語だから、非合法に和訳してみた。
なお詳しくは知らんが、どうやら適当な.auファイルを/dev/audioにリダイレクトしてやれば、それだけで音声を再生することはできるとか、できないとか、聞いたことがあるような気がする。もっとも、俺はやったことはないが。
|
|
|