Solarisで音を出す
[1] 主筆嬢 06/02/19 16:08
Solarisで音を出してみたくなった。
やり方教えろ。
[2] nabiki_t 06/02/19 16:08
相変わらず高圧的な奴だな。しかも、音が出したくなったとか、訳わかんねぇし。
まぁ、とりあえず音を出したければ叩けばいい。なにがしかの音は出るだろ。
[3] 主筆嬢 06/02/19 16:09
殺すぞ。
プログラムで音声データを再生する必要があるんだ。ちゃんと教えろ。
[4] nabiki_t 06/02/19 16:10
だから、/dev/audioを叩けばいいと言っている。
manページでも見ろよ。
[5] 主筆嬢 06/02/19 16:12
さっきは(筐体を)叩けと言ってたじゃん。/dev/audioなんていつ言った。
それに、manページ全部英語で読みにくい。和訳しろ。
[6] nabiki_t 06/02/19 16:13
それぐらい、おまえがやれ。
でもまぁいい。ほれ。出血大サービスだ。
[7] 主筆嬢 06/02/19 16:15
ふーん。
でもやっぱり判りにくいんだけど。日本語ぶっ飛んでるし。
解説しろ。
[8] nabiki_t 06/02/19 16:17
わがままな奴だな。前々から判ってはいた事だけどさ。
まぁいいか。最初から教えてやる。感謝しろ。
[9] nabiki_t 06/02/19 16:18
まずはデバイスファイルのオープンからだ。
#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;
}
|
とりあえず、open()システムコールで/dev/audioを開いただけだ。
[10] 主筆嬢 06/02/19 16:18
この時点からしてすでに動かないんだけど。
実行させると"It failed to open /dev/audio"って表示されるんだけど。
とりあえず今は、Blade100にtelnetでつないで実験しようとしてるんだけど。何がいけないの?
[11] nabiki_t 06/02/19 16:19
Blade100はいいんだけど、telnetがいけない。
サウンドのデバイスは、telnetでアクセスするとroot権限がないと使えないようになっている。マシンにローカルでアクセスすると(つまり、マシンに直結したキーボードとディスプレイを使えば)、一般ユーザでも使うことができるようになる。(X端でつないだ場合どうなるかは知らない。)
まぁ、デバイスの特性を考えれば当然だけど。
もしどうしても、telnetでログインした状態で一般ユーザから使えるようにしたければ、デバイスファイルのパーミッションを変更してあげればいい。
俺の環境(Sun Blade 100)だと、シンボリックリンクが「/dev/audio」->「/dev/sound/0」->「/devices/pci@1f,0/sound@8:sound,audio」と続いていて、最後の奴のパーミッションが「crw-------」となっているために、一般ユーザからではアクセスできなくなっている。だからこの最後の奴のパーミッションを変更すれば、一般ユーザでも使えるようになる。
だけどそんなことをしたら、よからぬ考えを持ってる奴がネットワーク経由でアクセスしてきて、突如大音響でろくでもない音楽を流される、という危険性が生じるがな。
[12] 主筆嬢 06/02/19 16:20
つまり、Blade100にマウスとキーボードとディスプレイを接続していないおまえが悪いって事だね。
[13] nabiki_t 06/02/19 16:21
どうしても俺を悪者にしたいようだな、こいつ。
まぁいいや。デバイスファイルを開いたら、次はデバイスの状態を取得してみる。
#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;
}
|
[14] nabiki_t 06/02/19 16:22
デバイスの設定を取得するには、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
|
もちろん詳細な値は、上記のプログラムを実行したときの設定内容によって変わってくるけどな。
[15] 広告 06/02/19 16:24
[16] nabiki_t 06/02/19 16:25
今度は設定内容を変更してみる。
特に、Blade100のデフォルト値では、再生時の音量が191になってるから、このまま音を出すと近所から苦情が来る。
それに、エンコードがμ-Lawというのも扱いにくいから、ここはPCMに変更しておく。
・・・>>13のmain関数の直前までは同じ
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;
}
|
[17] nabiki_t 06/02/19 16:27
デバイスの設定を行うには、AUDIO_SETINFO ioctlを行えばいい。audio_info構造体に適当な値を設定してioctlしてやるだけだ。
ついでに言うと、AUDIO_SETINFOの場合でも引数に指定した構造体の値は一部変更される可能性がある。
[18] nabiki_t 06/02/19 16:31
とりあえずこれで音を出す環境が整ったから、ここでなんか音を出してみることにする。
音を出すにはwrite()システムコールで/dev/audioにデータを書き込んやればいいだけだ。ただ問題は、どんなデータを書き込めばいいのか、ということなのだが。
[19] nabiki_t 06/02/19 16:32
>>15で設定したPCM・44.1KHz・16bit・ステレオなら、下記のようになる。

- 一つの「サンプル」は符号付き16bitの値
- 「サンプル」がチャンネル数分集まって「サンプルフレーム」を形成する。左から右の順にデータが格納される。
- 「サンプルフレーム」が「サンプリング周波数」個集まって、一秒分のデータになる。
[20] nabiki_t 06/02/19 16:34
以上を踏まえて、左側から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の内蔵スピーカーはモノラルで、左右の音の違いが確認できないから、一応ヘッドフォンに出力するようにした。
[21] 主筆嬢 06/02/19 16:36
一応、左に低音、右に高音が三秒ぐらい出力されるね。
でも、これほんとに400Hzと1000Hzなの?
[22] nabiki_t 06/02/19 16:37
ぶっちゃけて言うと、確認してないから知らない。
なんかそういう、オシロスコープみたいのがあれば確認できるのだろうが、あいにくと持っていないし。
とりあえず音が出りゃいいんだから、いいんじゃねぇのか?
[23] 主筆嬢 06/02/19 16:38
いい加減な奴。
[24] nabiki_t 06/02/19 16:40
おまえには言われたくない。
[25] nabiki_t 06/02/19 16:41
まぁいいか。
とりあえず後はmanページでも見ておけ。いろいろと細かいことが書いてあるみたいだから。
ついでに、AUファイルのフォーマットの解説も見ておけ。簡単だから。
[26] 主筆嬢 06/02/19 16:43
わかった。
それと後、今回はBlade100を使ったけど、いつものFire v250ではどうなるの?
[27] nabiki_t 06/02/19 16:45
どうなるのって、おまえ、あれにはサウンドのデバイスがついてないだろうが。/dev/audioのデバイスファイルからして存在しないし。
[28] 主筆嬢 06/02/19 16:47
じゃ、あのマシンでは絶対に音を出すことはできないの?
[29] nabiki_t 06/02/19 16:48
まぁ、サーバ用マシンだからな。でも、全く不可能というわけではない。
一応Solaris10はUSBオーディオに対応してるから、USBスピーカーを買ってきてくっつけてやれば、音を出すことはできる。
でも、いくつか注意することがある。
まず、USBスピーカーに音声入力の機能がついていない場合は、open()システムコールでO_RDRWのモードで開こうとすると失敗する。当然だが。
それと、普通USBスピーカーにはヘッドフォンジャックはついていない。だから、>>20のコードで出力先ポートをヘッドフォンにしようとすると失敗する。
あとはまぁ、USBスピーカーが接続されたり取り外されたりするのに合わせて、/dev/audioファイルが作成されたり消されたりするって事ぐらいか。
[30] 主筆嬢 06/02/19 16:49
ふーん
とにかく一応、音は出るんだ。
[31] nabiki_t 06/02/19 16:50
もっとも、マシン自体の轟音がうるさいから、音を出すことに何か意義があるとも思えないけどね。
|