LECTURE 3 /dev/dsp の機能を調べる

○ はじめに

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_REVISIONSNDCTL_DSP_GETCAPS を使った ioctl() によるアクセス のバージョン番号
      DSP_CAP_DUPLEX 録音再生を同時に行える(全二重)
      DSP_CAP_REALTIMESNDCTL_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 の説明は以上です。自分が使っているサウンドカードとドライバがどの様な機能を持っているのか、このプログラムで調べてみましょう! ( 調べたからどうというわけでもないですが。。。 )