|
○ はじめに
LECTURE 1 では、/dev/dsp に対して書き込みを行うことでサウンドを再生する方法を説明しました。その際、サウンドフォーマットは AFMT_S16_LE を指定しましたが実際に設定された値は確認していません。使用しているドライバ ( およびサウンドカード ) が AFMT_S16_LE というフォーマットをサポートしていないかもしれません。
実行時に対処する場合は ioctl() の第 3 引数の値を調べれば良いですが、サウンドフォーマット以外の様々な機能について、/dev/dsp がどのような機能をサポートしているのかあらかじめ知ることができると便利です。
ここでは、/dev/dsp に設定されているパラメータや、サポートしている機能を調べる方法について説明します。
○ サンプルプログラム
/dev/dsp に設定されているパラメータ、およびサポートしている機能情報を取得して表示するサンプルプログラム dsptest.c です。はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。
gcc -o dsptest dsptest.c
としてコンパイルします。コンパイルオプションは各自の環境などに応じて修正して下さい。sound.c では数学関数を使用していたため -lm オプションを必要としましたが、dsptest.c では不要です。なお、自分の環境 ( Sound Blaster Live! + カーネル 2.2.20 付属のドライバ ) で実行すると以下の表示になりました。
Sampling rate : 8000 Hz Channels : 1 Sample size : 16 bits Block size : 2048 bytes Supported formats signed 16 bit, little-endian ( default ) DSP capabilities revision : 0 duplex : ○ realtime : ○ batch : × coprocessor : ○ trigger : ○ mmap : ○
--- dsptest.c ---
1 #include <fcntl.h> 2 #include <linux/soundcard.h> 3 #include <stdio.h> 4 #include <sys/ioctl.h> 5 #include <unistd.h> 6 7 8 static int dsp_parameters( int fd ); 9 static int dsp_supported_format( int fd ); 10 static int dsp_capabilities( int fd ); 11 12 13 int 14 main( void ) 15 { 16 int fd; 17 18 if ( ( fd = open( "/dev/dsp", O_RDONLY ) ) == -1 ) { 19 perror( "open()" ); 20 return 1; 21 } 22 23 /* /dev/dsp のデフォルトパラメータを取得 */ 24 if ( dsp_parameters( fd ) == -1 ) { 25 perror( "dsp_parameters() failed\n" ); 26 close( fd ); 27 return 1; 28 } 29 30 /* /dev/dsp でサポートされているフォーマット情報を取得 */ 31 if ( dsp_supported_format( fd ) == -1 ) { 32 perror( "supported_format() failed\n" ); 33 close( fd ); 34 return 1; 35 } 36 37 /* /dev/dsp でサポートされている DSP 機能情報を取得 */ 38 if ( dsp_capabilities( fd ) == -1 ) { 39 perror( "dsp_capabilities() failed\n" ); 40 close( fd ); 41 return 1; 42 } 43 44 close( fd ); 45 return 0; 46 } 47 48 49 /* 50 * /dev/dsp のデフォルトパラメータを取得 51 */ 52 static int 53 dsp_parameters( int fd ) 54 { 55 int rate; 56 int channels; 57 int bits; 58 int blksize; 59 60 61 /* サンプリングレート値を取得 */ 62 if ( ioctl( fd, SOUND_PCM_READ_RATE, &rate ) == -1 ) { 63 perror( "ioctl( SOUND_PCM_READ_RATE )" ); 64 return -1; 65 } 66 67 /* チャンネル数を取得 */ 68 if ( ioctl( fd, SOUND_PCM_READ_CHANNELS, &channels ) == -1 ) { 69 perror( "ioctl( SOUND_PCM_READ_CHANNELS )" ); 70 return -1; 71 } 72 73 /* 量子化ビット数を取得 */ 74 if ( ioctl( fd, SOUND_PCM_READ_BITS, &bits ) == -1 ) { 75 perror( "ioctl( SOUND_PCM_READ_BITS )" ); 76 return -1; 77 } 78 79 /* ドライバが内部に持っているバッファのブロックサイズを取得 */ 80 if ( ioctl( fd, SNDCTL_DSP_GETBLKSIZE, &blksize ) == -1 ) { 81 perror( "ioctl( SNDCTL_DSP_GETBLKSIZE )" ); 82 return -1; 83 } 84 85 printf( "\n" ); 86 printf( "Sampling rate : %d Hz\n", rate ); 87 printf( "Channels : %d\n", channels ); 88 printf( "Sample size : %d bits\n", bits ); 89 printf( "Block size : %d bytes\n", blksize ); 90 printf( "\n" ); 91 92 return 0; 93 } 94 95 96 /* 97 * /dev/dsp のデフォルトのサウンドフォーマット、および 98 * サポートされているサウンドフォーマットを調べる。 99 */ 100 static int 101 dsp_supported_format( int fd ) 102 { 103 /* format_table に設定されているサウンドフォーマットの数 */ 104 #define FORMAT_NUM 8 105 106 int format_table[ FORMAT_NUM ] = { 107 AFMT_MU_LAW, /* mu-law */ 108 AFMT_A_LAW, /* A-law */ 109 AFMT_U8, /* unsigned 8 bit */ 110 AFMT_S8, /* signed 8 bit */ 111 AFMT_S16_LE, /* signed 16 bit, little-endian */ 112 AFMT_S16_BE, /* signed 16 bit, big-endian */ 113 AFMT_U16_LE, /* unsigned 16 bit, little-endian */ 114 AFMT_U16_BE /* unsigned 16 bit, big-endian */ 115 }; 116 117 char* format_msgtbl[ FORMAT_NUM ] = { 118 " mu-law", 119 " A-law", 120 " unsigned 8 bit", 121 " signed 8 bit", 122 " signed 16 bit, little-endian", 123 " signed 16 bit, big-endian", 124 " unsigned 16 bit, little-endian", 125 " unsigned 16 bit, big-endian" 126 }; 127 128 int formats, default_format; 129 int i; 130 131 /* デフォルトフォーマット問い合わせ用の値を設定 */ 132 default_format = AFMT_QUERY; 133 134 /* デフォルトのサウンドフォーマットを取得 */ 135 if ( ioctl( fd, SOUND_PCM_SETFMT, &default_format ) == -1 ) { 136 perror( "ioctl( SOUND_PCM_SETFMT )" ); 137 return -1; 138 } 139 140 /* サポートされているフォーマットを取得 */ 141 if ( ioctl( fd, SOUND_PCM_GETFMTS, &formats ) == -1 ) { 142 perror( "ioctl( SOUND_PCM_GETFMTS )" ); 143 return -1; 144 } 145 146 printf( "Supported formats\n" ); 147 148 /* 取得したフォーマット情報を解析して表示する */ 149 for ( i = 0; i < FORMAT_NUM; i ++ ) { 150 if ( formats & format_table[i] ) { 151 printf( "%s", format_msgtbl[i] ); 152 153 ( default_format == format_table[i] ) ? 154 printf( " ( default )\n" ) : printf( "\n" ); 155 } 156 } 157 158 return 0; 159 } 160 161 162 /* 163 * /dev/dsp でサポートされている DSP 機能について調べる 164 */ 165 static int 166 dsp_capabilities( int fd ) 167 { 168 /* caps_table に設定されている機能の数 */ 169 #define CAPS_NUM 7 170 171 int caps_table[ CAPS_NUM ] = { 172 DSP_CAP_REVISION, /* バージョン */ 173 DSP_CAP_DUPLEX, /* 録音再生を同時に行える */ 174 DSP_CAP_REALTIME, /* SNDCTL_DSP_GET[IO]PTR を使って、再生中の 175 バッファ位置を正確に知ることが出来る */ 176 DSP_CAP_BATCH, /* 録音再生にローカルストーレッジを使う */ 177 DSP_CAP_COPROC, /* codec ではなく DSP チップが存在している */ 178 DSP_CAP_TRIGGER, /* 録音再生の triggering が行える */ 179 DSP_CAP_MMAP /* 録音再生にハードウェアのバッファを使える */ 180 }; 181 182 char* caps_msgtbl[ CAPS_NUM ] = { 183 " revision : ", 184 " duplex : ", 185 " realtime : ", 186 " batch : ", 187 " coprocessor : ", 188 " trigger : ", 189 " mmap : " 190 }; 191 192 char* msgtbl[2] = { "○", "×" }; 193 int i, caps; 194 195 /* DSP 機能情報を取得 */ 196 if ( ioctl( fd, SNDCTL_DSP_GETCAPS, &caps ) == -1 ) { 197 perror( "ioctl( SNDCTL_DSP_GETCAPS )" ); 198 return -1; 199 } 200 201 printf( "\nDSP capabilities\n" ); 202 203 /* 取得した機能情報を解析して表示する */ 204 for ( i = 0; i < CAPS_NUM; i ++ ) { 205 printf( "%s", caps_msgtbl[i] ); 206 207 /* リビジョンだけ他のと取り出し方が異なる */ 208 if ( i == 0 ) { 209 printf( "%d\n", ( caps & DSP_CAP_REVISION ) ); 210 } 211 else { 212 ( ( caps & caps_table[i] ) != 0 ) ? 213 printf( "%s\n", msgtbl[0] ): 214 printf( "%s\n", msgtbl[1] ); 215 } 216 } 217 218 printf( "\n" ); 219 return 0; 220 } 221 222 223 /* End of dsptest.c */
○ サンプルプログラムの説明
では、サンプルプログラムについて説明していきます。
- 13 〜 46 行目 ( main() )
main() 関数では、各種の情報を取得するための自作関数を実行しているだけです。 自作関数についてこれから説明していきます。
- 49 〜 93 行目 ( dsp_parameters() )
dsp_parameters() は、/dev/dsp に現在設定されているサンプリングレート、チャンネル数、 量子化ビット数、ドライバ内部のバッファサイズを取得するための関数です。
- 61 〜 65 行目
現在設定されているサンプリングレートを取得しています。ioctl() の第 2 引数に SOUND_PCM_READ_RATE を設定して実行すると、第 3 引数に指定した変数に 現在のサンプリングレート値が設定されます。
int rate; ioctl( fd, SOUND_PCM_READ_RATE, &rate );
- 67 〜 71 行目
現在設定されているチャンネル数を取得しています。ioctl() の第 2 引数に SOUND_PCM_READ_CHANNELS を設定して実行すると、第 3 引数に指定した変数に現在の チャンネル数が設定されます。
int channels; ioctl( fd, SOUND_PCM_READ_CHANNELS, &channels );
- 73 〜 77 行目
現在設定されている量子化ビット数を取得しています。ioctl() の第 2 引数に SOUND_PCM_READ_BITS を設定して実行すると、第 3 引数に指定した変数に現在の 量子化ビット数が設定されます。
int bits; ioctl( fd, SOUND_PCM_READ_BITS, &bits );
ここまでの SOUND_PCM_READ_RATE、SOUND_PCM_READ_CHANNELS、 SOUND_PCM_READ_BITS は、 はじめに#参考情報 で紹介している Vortex of Sound から落として来た au88x-1.1.2.tar.gz ではサポートされていませんでした( これ以降のバージョンではサポートされるかも?)。 ユーザ空間から値を設定する方はサポートしているのに、 何故取得する方をサポートしていないのか、個人的に謎です。。。
なお、この場合 ioctl() がエラーとなって dsptest は終了しました。
- 79 〜 83 行目
サウンドドライバがデータの転送に使用するバッファサイズを取得しています。 ioctl() の第 2 引数に SNDCTL_DSP_GETBLKSIZE を設定して実行すると、 第 3 引数に指定した変数にバッファサイズが設定されます。
int blksize; ioctl( fd, SNDCTL_DSP_GETBLKSIZE, &blksize );
- 96 〜 159 行目 ( dsp_supported_format() )
dsp_supported_format() は、/dev/dsp がサポートしているサウンドフォーマットを調べるための関数です。LECTURE 1 で紹介した 8 個のフォーマットについて、それぞれサポートしているかどうかを調べています。 soundcard.h を見るとその他のフォーマットも記載されていますが dsptest.c ではチェックしていません。
本ページを書き上げた後に気付いたのですが、モジュールによるのかもしれませんが /dev/dsp を O_RDONLY で開いているか O_WRONLY で開いているかによってサポートしている フォーマットが異なる場合がある様です。
- 134 〜 138 行目
ここではまず、デフォルトとして設定されているサウンドフォーマットを取得しています。 ioctl() の第 2 引数に SOUND_PCM_SETFMT もしくは SNDCTL_DSP_SETFMT を設定します ( SOUND_PCM_SETFMT は SNDCTL_DSP_SETFMT のエイリアス )。そして、 第 3 引数に AFMT_QUERY を設定した変数のアドレスを指定して ioctl() を実行すると、 第 3 引数にデフォルトのサウンドフォーマットが設定されます。
int default_format = AFMT_QUERY; ioctl( fd, SOUND_PCM_SETFMT, &default_format );
- 140 〜 144 行目
/dev/dsp がサポートしているサウンドフォーマットを取得しています。ioctl() の第 2 引数に SOUND_PCM_GETFMTS もしくは SNDCTL_DSP_GETFMTS を設定します ( SOUND_PCM_GETFMTS は SNDCTL_DSP_GETFMTS のエイリアス )。ioctl() を実行すると、 第 3 引数に指定した変数にサポートされているサウンドフォーマットのビットマスク が設定されます。
int format; ioctl( fd, SOUND_PCM_GETFMTS, &format );
このビットマスクと各サウンドフォーマット ( AFMT_U8 等 ) のビット毎の AND 演算 を行った結果が 1 であれば、 そのフォーマットはサポートされているということになります。
- 162 〜 220 行目 ( dsp_capabilities() )
dsp_capabilities() は、/dev/dsp でサポートされている DSP 機能情報を取得します。
- 171 〜 180 行目
サポートの有無を調べる DSP 機能を指定しています。それぞれの内容について、以下に 記します。
定数名 説明 DSP_CAP_REVISION SNDCTL_DSP_GETCAPS を使った ioctl() によるアクセス のバージョン番号 DSP_CAP_DUPLEX 録音再生を同時に行える(全二重) DSP_CAP_REALTIME SNDCTL_DSP_GETIPTR、もしくは SNDCTL_DSP_GETOPTR を使って、現在の出力バッファにおける位置を正確に 知ることができる DSP_CAP_BATCH 録音再生用のバッファとして、ローカルストーレッジを 使用する DSP_CAP_COPROC ソフトウェアによる CODEC ではなく、DSP チップを ハードウェアとして持っている DSP_CAP_TRIGGER 録音再生のトリガー機能を使うことが出来る DSP_CAP_MMAP ハードウェアレベルの録音再生用バッファに直接 アクセスすることが出来る
- 195 〜 199 行目
DSP 機能情報を取得しています。iotcl() の第 2 引数に SNDCTL_DSP_GETCAPS を設定して 実行すると、第 3 引数に DSP 機能のビットマスクが設定されます。
int caps; ioctl( fd, SNDCTL_DSP_GETCAPS, &caps );
このビットマスクと各 DSP 機能 ( DSP_CAP_DUPLEX 等 ) のビット毎の AND 演算を行った 結果が 1 であれば、その DSP 機能はサポートされているということになります。ただし DSP_CAP_REVISION は特別で、AND 演算の結果そのものが SNDCTL_DSP_GETCAPS を使った ioctl() によるアクセスのバージョン番号となります。
以下 2002/8/20 追記DSP_CAP_DUPLEX に関しては、SNDCTL_DSP_GETCAPS コマンドを発行する前に下記の ioctl() コマンドを発行するべきであるという記述を OSS プログラマーズガイドで発見しました。
ioctl( fd, SNDCTL_DSP_SETDUPLEX, 0 );
全二重モードがサポートされていればこのコマンドで全二重モードに移行するので、 その後で SNDCTL_DSP_GETCAPS を使って調べるべきだということでしょう。が、 いくつか調べたドライバのコードでは、SNDCTL_DSP_SETDUPLEX コマンドの実行が 成功するものはもともと SNDCTL_DSP_GETCAPS 時に DSP_CAP_DUPLEX を設定して返す様に なっているみたいです。もちろん全てのドライバのコードを見たわけではないので カードとドライバ次第ということにはなりますが、 個人的には現状で本当に必要なのか疑問。。。
追記ここまで
dsptest.c の説明は以上です。自分が使っているサウンドカードとドライバがどの様な機能を持っているのか、このプログラムで調べてみましょう! ( 調べたからどうというわけでもないですが。。。 )