LECTURE 6 /dev/mixer の機能を調べる

○ はじめに

/dev/mixer はその名の通りミキサー機能を担当するデバイスです。入出力音源のボリュームの上げ下げを行ったり、入力音源を選択したりするには /dev/mixer を操作する必要があります ( ちなみにここで紹介する操作を /dev/dsp に対して行っても正しく動作します。/dev/dsp を使用している際にわざわざ /dev/mixer を別に open() する必要はありません )。

ここでは、/dev/mixer からいろいろな情報を参照する方法について説明していきます。/dev/mixer に対してパラメータを設定する方法については、次の LECTURE で説明します。

○ サンプルプログラム

/dev/mixer で扱うことのできるチャンネル情報、およびサポートしている機能情報を取得して表示するサンプルプログラム mixer_test.c です。はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。

gcc -o mixer_test mixer_test.c

としてコンパイルします。コンパイルオプションは各自の環境などに応じて修正して下さい。なお、自分の環境 ( AU8820B2 チップのサウンドカード + Vortex of Sound 提供の Linux® 用ドライバ。LECTURE 3 の dsptest.c の時とは変わりました(^_^) ) で実行すると以下の表示になりました。

列の意味は左から

  1. チャンネル名称
  2. チャンネルがサポートされているか
  3. 現在のチャンネルのボリュームレベル
  4. チャンネルが録音源として使用できるか
  5. 現在の録音源として選択されているか
  6. ステレオをサポートしているか

となっています。


Channel  Support Channel Rec    Current Stereo
Name             Level   Source Input         
-------- ------- ------- ------ ------- ------
vol      ○      80%,80% ○     ×      ○
bass     ×              ×     ×      ×
treble   ×              ×     ×      ×
synth    ×              ×     ×      ×
pcm      ○      80%,80% ×     ×      ○
speaker  ○      80%     ×     ×      ×
line     ○      80%,80% ○     ×      ○
mic      ○      80%     ○     ○      ×
cd       ○      80%,80% ○     ×      ○
mix      ×              ×     ×      ×
pcm2     ×              ×     ×      ×
rec      ○      80%,80% ×     ×      ○
igain    ×              ×     ×      ×
ogain    ○      80%     ×     ×      ×
line1    ○      80%,80% ○     ×      ○
line2    ×              ×     ×      ×
line3    ×              ×     ×      ×
dig1     ×              ×     ×      ×
dig2     ×              ×     ×      ×
dig3     ×              ×     ×      ×
phin     ○      80%     ○     ×      ×
phout    ×              ○     ×      ○
video    ○      80%,80% ○     ×      ○
radio    ×              ×     ×      ×
monitor  ×              ×     ×      ×

### Rec Sources are EXCLUSIVE     ###

--- mixer_test.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	static int mixer_getinfo( int fd );
    8	static int mixer_getcaps( int fd );
    9	
   10	int 
   11	main( void )
   12	{
   13		int fd;
   14	
   15		if ( ( fd = open( "/dev/mixer", O_RDONLY ) ) == -1 ) {
   16			perror( "open()" );
   17			return 1;
   18		}
   19	
   20		/* チャンネル情報を取得 */
   21		if ( mixer_getinfo( fd ) == -1 ) {
   22			perror( "mixer_getinfo() failed" );
   23			close( fd );
   24			return 1;
   25		}
   26	
   27		/* ミキサー機能情報(あいまいですが)を取得 */
   28		if ( mixer_getcaps( fd ) == -1 ) {
   29			perror( "mixer_getcaps() failed" );
   30			close( fd );
   31			return 1;
   32		}
   33	
   34		close( fd );
   35		return 0;
   36	}
   37	    
   38	
   39	/*
   40	 * /dev/mixer からチャンネル情報を取得
   41	 */
   42	static int 
   43	mixer_getinfo( int fd )
   44	{
   45		int devmask, recmask;
   46		int recsrc;
   47		int stereodevs;
   48		int level;
   49		int i;
   50	
   51		/* soundcard.h で定義されているチャンネル名称 */
   52		const char* sound_device_names[] = SOUND_DEVICE_NAMES;
   53	
   54	
   55		/* ミキサーがサポートしているチャンネル情報を取得 */
   56		if ( ioctl( fd, SOUND_MIXER_READ_DEVMASK, &devmask ) == -1 ) {
   57			perror( "ioctl( SOUND_MIXER_READ_DEVMASK )" );
   58			return -1;
   59		}
   60	
   61		/* 録音源として使えるチャンネル情報を取得 */
   62		if ( ioctl( fd, SOUND_MIXER_READ_RECMASK, &recmask ) == -1 ) {
   63			perror( "ioctl( SOUND_MIXER_READ_RECMASK )" );
   64			return -1;
   65		}
   66	
   67		/* 現在録音源として選択されているチャンネル情報を取得 */
   68		if ( ioctl( fd, SOUND_MIXER_READ_RECSRC, &recsrc ) == -1 ) {
   69			perror( "ioctl( SOUND_MIXER_READ_RECSRC )" );
   70			return -1;
   71		}
   72	
   73		/* チャンネルがステレオをサポートしているかどうかを取得 */
   74		if ( ioctl( fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs ) == -1 ) {
   75			perror( "ioctl( SOUND_MIXER_READ_STEREODEVS )" );
   76			return -1;
   77		}
   78	
   79		printf("\n" );
   80		printf("Channel  Support Channel Rec    Current Stereo\n");  
   81		printf("Name             Level   Source Input         \n");
   82		printf("-------- ------- ------- ------ ------- ------\n");
   83	            
   84	
   85		/* 定義されているチャンネルの数だけループ */
   86		for ( i = 0; i < SOUND_MIXER_NRDEVICES; i ++ ) {
   87			/* チャンネル名称 */
   88			printf( "%-8s ", sound_device_names[i] );
   89	
   90			/* チャンネルのサポート状況 */
   91			( devmask & ( 1 << i ) ) ? 
   92				printf( "○      " ) : printf( "×      " );
   93	
   94	
   95			if ( ( devmask & ( 1 << i ) ) ) {
   96				/* チャンネルのボリュームを取得 */
   97				if ( ioctl( fd, MIXER_READ( i ), &level ) == -1 ) {
   98					perror( "ioctl(MIXER_READ())" );
   99					return -1; 
  100				}
  101	
  102				if ( stereodevs & ( 1 << i ) ) {
  103					/* ステレオの場合は下位8ビットが左、上位8ビットが右 */
  104					printf( "%d%%,%d%% ", 
  105						level & 0xff, ( level & 0xff00 ) >> 8 );
  106				}
  107				else {
  108					/* モノラル */
  109					printf( "%d%%     ", level & 0xff );
  110				}
  111			}
  112			else {
  113				printf( "        " );
  114			}
  115	
  116	
  117			/* 録音源として使えるか、現在の録音源か、ステレオか  */
  118			( recmask & ( 1 << i ) )    ? 
  119				printf( "○     " )  : printf( "×     " );
  120			
  121			( recsrc  & ( 1 << i ) )    ? 
  122				printf( "○      " ) : printf( "×      " );
  123	
  124			( stereodevs & ( 1 << i ) ) ? 
  125				printf( "○" )   : printf( "×" );
  126	
  127			printf( "\n" );
  128		}
  129	
  130		return 0;
  131	}
  132	
  133	
  134	/*
  135	 * サウンドカードのミキサー機能についての情報を取得。
  136	 * 本プログラム作成時点では、録音源の複数同時使用可否情報のみ。
  137	 */
  138	static int 
  139	mixer_getcaps( int fd )
  140	{
  141		int caps;
  142	
  143		/* サウンドカードのミキサー機能情報を取得 */
  144		if ( ioctl( fd, SOUND_MIXER_READ_CAPS, &caps ) == -1 ) {
  145			perror( "SOUND_MIXER_READ_CAPS" );
  146			return -1;
  147		}
  148	
  149		printf( "\n" );
  150	
  151	
  152		/* 録音源を複数同時に使用可能か */
  153		( caps & SOUND_CAP_EXCL_INPUT ) ? 
  154			printf( "### Rec Sources are EXCLUSIVE     ###\n" ) :
  155			printf( "### Rec Sources are NOT EXCLUSIVE ###\n" );
  156	
  157		printf( "\n" );
  158	
  159		return 0;
  160	}
  161	
  162	/* End of mixer_test.c */


○ サンプルプログラムの説明

では、サンプルプログラムについて説明していきます。

  • 10 〜 36 行目 ( main() )

    main() 関数では、各種の情報を取得するための自作関数を実行しているだけです。 自作関数についてこれから説明していきます。

  • 39 〜 131 行目 ( mixer_getinfo() )

    mixer_getinfo() は、/dev/mixer がサポートしている入出力チャンネル、 録音源として使えるチャンネル、現在の録音源、チャンネルがステレオ音を扱えるかどうか などの情報を取得するための関数です。

    • 51 〜 52 行目

      SOUND_DEVICE_NAMES は、ミキサーで扱うことのできるチャンネルの名称の配列として <linux/soundcard.h> で定義されています。また、似たような内容で SOUND_DEVICE_LABELS というマクロも定義されています。

        #define SOUND_DEVICE_LABELS  {"Vol  ", "Bass", "Treb1", ... 省略 }
        #define SOUND_DEVICE_NAMES   {"vol", "bass", "treble", ... 省略 }
      
      
      ちなみに何のために分かれているのか良く分かりません m(__)m。

      このマクロによって得られる配列の要素数は、SOUND_MIXER_NRDEVICES として定義されています。これらは後程チャンネル名称を表示する際に使用します。 なお、下記の定数が各チャンネルを表す添字に対応しています。

      下記のチャンネルの中には僕も良く分かっていないものもあります。 が、一応僕の環境下( <linux/soundcard.h> 内の SOUND_VERSION によると OSS 3.8.2 の様です )でサウンドシステムとしてサポートされているものの一覧として 書いておきます。

      定数名チャンネル
      SOUND_MIXER_VOLUME メインボリューム
      SOUND_MIXER_BASS 低音域
      SOUND_MIXER_TREBLE 高音域
      SOUND_MIXER_SYNTH FM シンセサイザ
      SOUND_MIXER_PCM D/A 変換器
      SOUND_MIXER_SPEAKER PC スピーカ出力
      SOUND_MIXER_LINE ライン入力
      SOUND_MIXER_MIC マイク入力
      SOUND_MIXER_CD CD 入力
      SOUND_MIXER_IMIX 録音源からの再生ボリューム
      SOUND_MIXER_ALTPCM 第 2 D/A 変換器
      SOUND_MIXER_RECLEV メイン録音レベル
      SOUND_MIXER_IGAIN 入力ゲイン
      SOUND_MIXER_OGAIN 出力ゲイン
      SOUND_MIXER_LINE1 カード専用入力源 1
      SOUND_MIXER_LINE2 カード専用入力源 2
      SOUND_MIXER_LINE3 カード専用入力源 3
      SOUND_MIXER_DIGITAL1デジタル入力 1
      SOUND_MIXER_DIGITAL2デジタル入力 2
      SOUND_MIXER_DIGITAL3デジタル入力 3
      SOUND_MIXER_PHONEIN PHONE 入力
      SOUND_MIXER_PHONEOUTPHONE 出力
      SOUND_MIXER_VIDEO ビデオ / TV (音声) 入力
      SOUND_MIXER_RADIO ラジオ入力
      SOUND_MIXER_MONITOR モニターのボリューム

      最後の "モニター" って何でしょう?(分かりません m(__)m)

    • 55 〜 59 行目

      /dev/mixer がどのチャンネルをサポートしているのかについての情報を取得しています。 ioctl() の第 2 引数に SOUND_MIXER_READ_DEVMASK もしくは MIXER_READ( SOUND_MIXER_DEVMASK ) を設定します ( SOUND_MIXER_READ_DEVMASK は MIXER_READ( SOUND_MIXER_DEVMASK ) のエイリアス )。

      ioctl() を実行すると、第 3 引数に設定した変数にサポートされているチャンネルの ビットマスクが設定されます。

        int mask;
        ioctl( fd, SOUND_MIXER_READ_DEVMASK, &mask );
      
      

      このビットマスクと下記のチャンネル毎のマスクを表す定数の AND を行った結果が 1 であれば、そのチャンネルはサポートされているということになります。

      定数名チャンネルSOUND_MIXER_...を使った別表現
      SOUND_MASK_VOLUME メインボリューム ( 1 << SOUND_MIXER_VOLUME )
      SOUND_MASK_BASS 低音域 ( 1 << SOUND_MIXER_BASS )
      SOUND_MASK_TREBLE 高音域 ( 1 << SOUND_MIXER_TREBLE )
      SOUND_MASK_SYNTH FM シンセサイザ ( 1 << SOUND_MIXER_SYNTH )
      SOUND_MASK_PCM D/A 変換器 ( 1 << SOUND_MIXER_PCM )
      SOUND_MASK_SPEAKER PC スピーカ出力 ( 1 << SOUND_MIXER_SPEAKER )
      SOUND_MASK_LINE ライン入力 ( 1 << SOUND_MIXER_LINE )
      SOUND_MASK_MIC マイク入力 ( 1 << SOUND_MIXER_MIC )
      SOUND_MASK_CD CD 入力 ( 1 << SOUND_MIXER_CD )
      SOUND_MASK_IMIX 録音源からの再生ボリューム ( 1 << SOUND_MIXER_IMIX )
      SOUND_MASK_ALTPCM 第 2 D/A 変換器 ( 1 << SOUND_MIXER_ALTPCM )
      SOUND_MASK_RECLEV メイン録音レベル ( 1 << SOUND_MIXER_RECLEV )
      SOUND_MASK_IGAIN 入力ゲイン ( 1 << SOUND_MIXER_IGAIN )
      SOUND_MASK_OGAIN 出力ゲイン ( 1 << SOUND_MIXER_OGAIN )
      SOUND_MASK_LINE1 カード専用入力源 1 ( 1 << SOUND_MIXER_LINE1 )
      SOUND_MASK_LINE2 カード専用入力源 2 ( 1 << SOUND_MIXER_LINE2 )
      SOUND_MASK_LINE3 カード専用入力源 3 ( 1 << SOUND_MIXER_LINE3 )
      SOUND_MASK_DIGITAL1デジタル入力 1 ( 1 << SOUND_MIXER_DIGITAL1 )
      SOUND_MASK_DIGITAL2デジタル入力 2 ( 1 << SOUND_MIXER_DIGITAL2 )
      SOUND_MASK_DIGITAL3デジタル入力 3 ( 1 << SOUND_MIXER_DIGITAL3 )
      SOUND_MASK_PHONEIN PHONE 入力 ( 1 << SOUND_MIXER_PHONEIN )
      SOUND_MASK_PHONEOUTPHONE 出力 ( 1 << SOUND_MIXER_PHONEOUT )
      SOUND_MASK_VIDEO ビデオ / TV (音声) 入力 ( 1 << SOUND_MIXER_RADIO )
      SOUND_MASK_RADIO ラジオ入力 ( 1 << SOUND_MIXER_VIDEO )
      SOUND_MASK_MONITOR モニターのボリューム ( 1 << SOUND_MIXER_MONITOR )

    • 61 〜 65 行目

      /dev/mixer がサポートしているチャンネルのうち、どのチャンネルが録音源として 使用できるのかについての情報を取得しています。ioctl() の第 2 引数に SOUND_MIXER_READ_RECMASK もしくは MIXER_READ( SOUND_MIXER_RECMASK ) を設定します ( SOUND_MIXER_READ_RECMASK は MIXER_READ( SOUND_MIXER_RECMASK ) のエイリアス )。

      ioctl() を実行すると、第 3 引数に設定した変数に、録音源として使用できる チャンネルのビットマスクが設定されます。

        int mask;
        ioctl( fd, SOUND_MIXER_READ_RECMASK, &mask );
      
      

      このビットマスクと先程のマスク定数の AND を行った結果が 1 であれば、そのチャンネルは録音源として使えるということになります。

    • 67 〜 71 行目

      現在録音源として選択されているチャンネルはどれなのかについての情報を取得しています。 ioctl() の第 2 引数に SOUND_MIXER_READ_RECSRC もしくは MIXER_READ( SOUND_MIXER_RECSRC ) を設定します ( SOUND_MIXER_READ_RECSRC は MIXER_READ( SOUND_MIXER_RECSRC ) のエイリアス )。

      ioctl() を実行すると、第 3 引数に設定した変数に現在録音源として選択されている チャンネルのビットマスクが設定されます。

        int mask;
        ioctl( fd, SOUND_MIXER_READ_RECSRC, &mask );
      
      

      このビットマスクと先程のマスク定数の AND を行った結果が 1 であれば、そのチャンネルは録音源として選択されているということになります。

    • 73 〜 77 行目

      各チャンネルがステレオをサポートしているかどうかについての情報を取得しています。 ioctl() の第 2 引数に SOUND_MIXER_READ_STEREODEVS もしくは MIXER_READ( SOUND_MIXER_STEREODEVS ) を設定します ( SOUND_MIXER_READ_STEREODEVS は MIXER_READ( SOUND_MIXER_STEREODEVS ) のエイリアス )。

      ioctl() を実行すると、第 3 引数に設定した変数にステレオをサポートしている チャンネルのビットマスクが設定されます。

        int mask;
        ioctl( fd, SOUND_MIXER_READ_STEREODEVS, &mask );
      
      

      このビットマスクと先程のマスク定数の AND を行った結果が 1 であれば、そのチャンネルはステレオをサポートしているということになります。

    • 79 〜 128 行目

      ここまでに取得した情報の表示処理を行っています。ほとんどの箇所は 得られたビットマスクをチェックしているだけですが、96 〜 100 行目ではサポートしているチャンネルのゲインレベルを取得するために新たな ioctl() を発行しています。

      ioctl() の第 2 引数にゲインレベルを読みたいチャンネルに応じた定数を設定します。 以下にその一覧を示します。

      定数名チャンネルMIXER_READ を使った表現
      SOUND_MIXER_READ_VOLUME メインボリューム MIXER_READ( SOUND_MIXER_VOLUME )
      SOUND_MIXER_READ_BASS 低音域 MIXER_READ( SOUND_MIXER_BASS )
      SOUND_MIXER_READ_TREBLE 高音域 MIXER_READ( SOUND_MIXER_TREBLE )
      SOUND_MIXER_READ_SYNTH FM シンセサイザ MIXER_READ( SOUND_MIXER_SYNTH )
      SOUND_MIXER_READ_PCM D/A 変換器 MIXER_READ( SOUND_MIXER_PCM )
      SOUND_MIXER_READ_SPEAKER PC スピーカ出力 MIXER_READ( SOUND_MIXER_SPEAKER )
      SOUND_MIXER_READ_LINE ライン入力 MIXER_READ( SOUND_MIXER_LINE )
      SOUND_MIXER_READ_MIC マイク入力 MIXER_READ( SOUND_MIXER_MIC )
      SOUND_MIXER_READ_CD CD 入力 MIXER_READ( SOUND_MIXER_CD )
      SOUND_MIXER_READ_IMIX 録音源からの再生ボリューム MIXER_READ( SOUND_MIXER_IMIX )
      SOUND_MIXER_READ_ALTPCM 第 2 D/A 変換器 MIXER_READ( SOUND_MIXER_ALTPCM )
      SOUND_MIXER_READ_RECLEV メイン録音レベル MIXER_READ( SOUND_MIXER_RECLEV )
      SOUND_MIXER_READ_IGAIN 入力ゲイン MIXER_READ( SOUND_MIXER_IGAIN )
      SOUND_MIXER_READ_OGAIN 出力ゲイン MIXER_READ( SOUND_MIXER_OGAIN )
      SOUND_MIXER_READ_LINE1 カード専用入力源 1 MIXER_READ( SOUND_MIXER_LINE1 )
      SOUND_MIXER_READ_LINE2 カード専用入力源 2 MIXER_READ( SOUND_MIXER_LINE2 )
      SOUND_MIXER_READ_LINE3 カード専用入力源 3 MIXER_READ( SOUND_MIXER_LINE3 )

      この他のチャンネルに関しては、僕の環境下では定数がありませんでした。

      ioctl() を実行すると、第 3 引数に設定した変数に指定したチャンネルの ゲインレベルが設定されます。

        int level;
        ioctl( fd, SOUND_MIXER_READ_XXX, &level );
      
      

      チャンネルがステレオをサポートする場合は、level の下位 8 ビットが左チャンネルの ゲインレベル、上位 8 ビットが右チャンネルのゲインレベルになります。モノラルの場合は 下位 8 ビットがゲインレベルになります。いずれの場合も、ゲインレベルは 0 〜 100 の値となります( 102 〜 110 行参照 )。

  • 134 〜 160 行目 ( mixer_getcaps() )

    mixer_getcaps() は、mixer_getinfo() で得られるチャンネル毎の情報の他の ミキサー機能に関する情報を取得する関数です。

    • 143 〜 147 行目

      ミキサー機能の情報を ioctl() で取得しています。ioctl() の第 2 引数に SOUND_MIXER_READ_CAPS もしくは MIXER_READ( SOUND_MIXER_CAPS ) を設定します ( SOUND_MIXER_READ_CAPS は MIXER_READ( SOUND_MIXER_CAPS ) のエイリアス )。

      ioctl() を実行すると、 第 3 引数に設定した変数に機能を表すビットマスクが設定されます。

        int caps;
        ioctl( fd, SOUND_MIXER_READ_CAPS, &caps );
      
      

      OSS 3.8.2 では、SOUND_MIXER_READ_CAPS で調べられる機能は一つのみの様です。 複数の録音源を同時に選択可能かどうかというもので、SOUND_CAP_EXCL_INPUT と cap の AND を計算した結果、ビットが立っていれば一度に一つの録音源しか 使用できません。ビットが立っていなければ、 複数の録音源を同時に選択することができます ( 152 〜 155 行参照 )。


mixer_test.c の説明は以上です。dsptest.c と同様に、自分が使っているサウンドカードとドライバがどの様な機能を持っているのかこのプログラムで表示させてみましょうー。