LECTURE 7 /dev/mixer を操作する

○ はじめに

LECTURE 6 /dev/mixer の機能を調べる では、/dev/mixer からいろいろな情報を参照する方法について説明しました。ここでは、/dev/mixer に対してパラメータを設定する方法について説明します。具体的には、指定したチャンネルのボリューム ( ゲインレベル ) 設定、および録音源として指定するチャンネルの設定方法について説明します。

○ サンプルプログラム

/dev/mixer を操作して、指定したチャンネルのボリューム ( ゲインレベル ) を設定する、あるいは指定したチャンネルを録音源として設定するサンプルプログラム mixer_set.c です。はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。

gcc -o mixer_set mixer_set.c

としてコンパイルします。コンパイルオプションは各自の環境などに応じて修正して下さい。

引数を何も与えないで実行すると、次の様な表示が出ます。

usage : mixer_set vol <channel name> <left volume> <right volume>
    or  mixer_set vol <channel name> <volume>
    or  mixer_set rec <channel name>

第 2 引数を vol としている上 2 つの行は、channel で指定したチャンネルのボリューム ( ゲインレベル ) を設定します。1 番上の行はステレオチャンネルに対して左右のゲインレベルを設定する場合、2 番目の行はモノラルチャンネルに対してゲインレベルを設定する場合です。1 番下の行は、channel で指定したチャンネルを録音源として設定します。

channel には LECTURE 6 /dev/mixer の機能を調べる で作成した mixer_test を実行した際に、Channel Name の列に表示される名称を使用することができます。ゲインレベルは 0 〜 100 の数値を指定して下さい。mixer_set を実行した後は、mixer_test で設定した内容が反映されているかどうか確認することができます。

--- mixer_set.c ---

    1	#include <fcntl.h>
    2	#include <linux/soundcard.h>
    3	#include <stdio.h>
    4	#include <stdlib.h>
    5	#include <string.h>
    6	#include <sys/ioctl.h>
    7	#include <unistd.h>
    8	
    9	
   10	int 
   11	main( int argc, char** argv )
   12	{
   13		int fd;
   14		int channel, level, mask;
   15		int i;
   16	
   17		/* soundcard.h で定義されているチャンネル名称 */
   18		const char* sound_device_names[] = SOUND_DEVICE_NAMES;
   19	
   20		if ( argc < 3 || argc > 5 ) {
   21			printf( "usage : mixer_set vol <channel name> <left volume>"
   22				" <right volume>\n" );
   23			printf( "    or  mixer_set vol <channel name> <volume>\n" );
   24			printf( "    or  mixer_set rec <channel name>\n" );
   25			return 1;
   26		}
   27	
   28	
   29		/* 定義されているチャンネルの数だけループ */
   30		for ( i = 0; i < SOUND_MIXER_NRDEVICES; i ++ ) {
   31			if ( strcmp( argv[2], sound_device_names[i] ) == 0 ) {
   32				channel = i;
   33				break;
   34			}
   35		}
   36	    
   37		/* 指定したデバイス名が定義されていない */
   38		if ( i == SOUND_MIXER_NRDEVICES ) {
   39			printf( "specified channel is not defined\n" );
   40			return 1;
   41		}
   42	
   43		/* write() で書き込むわけじゃないのでこれで良い */
   44		if ( ( fd = open( "/dev/mixer", O_RDONLY ) ) == -1 ) {
   45			perror( "open" );
   46			return 1;
   47		}
   48	
   49		/* ボリュームの設定 */
   50		if ( strcmp( argv[1], "vol" ) == 0 ) {
   51			/* ボリュームのエンコード */
   52			if ( argc == 4 ) {
   53				level =   atoi( argv[3] );
   54			}
   55			else if ( argc == 5 ) {
   56				level = ( atoi( argv[4] ) << 8 ) | atoi( argv[3] );
   57			}
   58	
   59			/* デバイスにボリュームを設定する */
   60			if ( ioctl( fd, MIXER_WRITE( channel ), &level ) == -1 ) {
   61				perror( "ioctl(MIXER_WRITE())" );
   62				close( fd );
   63				return 1;
   64			}
   65		}
   66		else if ( strcmp( argv[1], "rec" ) == 0 ) {
   67			/* 録音源の設定 */
   68			mask = ( 1 << channel );
   69	
   70			if ( ioctl( fd, SOUND_MIXER_WRITE_RECSRC, &mask ) == -1 ) {
   71				perror( "ioctl(SOUND_MIXER_WRITE_RECSRC)" );
   72				close( fd );
   73				return 1;
   74			}
   75		}
   76	
   77		close( fd );
   78		return 0;
   79	}
   80	
   81	/* End of mixer_set.c */


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

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

  • 17 〜 18 行目

    前回と同じ様に、ミキサーで扱うことのできるチャンネルの名称の配列として <linux/soundcard.h> で定義されている SOUND_DEVICE_NAMES を char* の配列として利用できる様にしています。

  • 29 〜 41 行目

    コマンドラインの第 2 引数で指定されたチャンネル名称とSOUND_DEVICE_NAMES の内容を比較して、 一致するものがあるかどうか探しています。一致する名称が無い場合は終了します。 一致する名称があった場合は、その要素番号を後で使用するために保存しておきます。

  • 43 〜 47 行目

    指定されたチャンネル名称が存在することを確認してからミキサーデバイスを open() しています。 write() で書き込むわけではないので、O_RDONLY ( 読み込み専用 ) で開いています。

  • 49 〜 65 行目

    コマンドラインの第 3 引数に vol が指定された場合にゲインレベルの設定を行っています。 モノラルチャンネル用には、指定されたゲインレベルをそのまま使用します。 ステレオチャンネルに対して設定する場合は、右チャンネル用のレベルを上位 8 ビットに、 左チャンネル用のレベルを下位 8 ビットにエンコードします。

    ioctl() の第 2 引数にゲインレベルを設定したいチャンネルに応じた定数を設定します。 mixer_set.c では、チャンネル名称を検索した際に保存しておいた要素番号を MIXER_WRITE() マクロに指定しています。この要素番号は、チャンネル名称に対応した定数として <linux/soundcard.h> で定義されています。また、MIXER_WRITE( ... ) のマクロそのものも 定数として定義されています。 以下にその一覧を示します。

    定数名チャンネルMIXER_WRITE を使った表現
    SOUND_MIXER_WRITE_VOLUME メインボリューム MIXER_WRITE( SOUND_MIXER_VOLUME )
    SOUND_MIXER_WRITE_BASS 低音域 MIXER_WRITE( SOUND_MIXER_BASS )
    SOUND_MIXER_WRITE_TREBLE 高音域 MIXER_WRITE( SOUND_MIXER_TREBLE )
    SOUND_MIXER_WRITE_SYNTH FM シンセサイザ MIXER_WRITE( SOUND_MIXER_SYNTH )
    SOUND_MIXER_WRITE_PCM D/A 変換器 MIXER_WRITE( SOUND_MIXER_PCM )
    SOUND_MIXER_WRITE_SPEAKER PC スピーカ出力 MIXER_WRITE( SOUND_MIXER_SPEAKER )
    SOUND_MIXER_WRITE_LINE ライン入力 MIXER_WRITE( SOUND_MIXER_LINE )
    SOUND_MIXER_WRITE_MIC マイク入力 MIXER_WRITE( SOUND_MIXER_MIC )
    SOUND_MIXER_WRITE_CD CD 入力 MIXER_WRITE( SOUND_MIXER_CD )
    SOUND_MIXER_WRITE_IMIX 録音源からの再生ボリューム MIXER_WRITE( SOUND_MIXER_IMIX )
    SOUND_MIXER_WRITE_ALTPCM 第 2 D/A 変換器 MIXER_WRITE( SOUND_MIXER_ALTPCM )
    SOUND_MIXER_WRITE_RECLEV メイン録音レベル MIXER_WRITE( SOUND_MIXER_RECLEV )
    SOUND_MIXER_WRITE_IGAIN 入力ゲイン MIXER_WRITE( SOUND_MIXER_IGAIN )
    SOUND_MIXER_WRITE_OGAIN 出力ゲイン MIXER_WRITE( SOUND_MIXER_OGAIN )
    SOUND_MIXER_WRITE_LINE1 カード専用入力源 1 MIXER_WRITE( SOUND_MIXER_LINE1 )
    SOUND_MIXER_WRITE_LINE2 カード専用入力源 2 MIXER_WRITE( SOUND_MIXER_LINE2 )
    SOUND_MIXER_WRITE_LINE3 カード専用入力源 3 MIXER_WRITE( SOUND_MIXER_LINE3 )

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

    第 3 引数に設定したいゲインレベルをエンコードした変数のポインタを指定して実行すると、 指定したチャンネルに対してゲインレベルが設定されます。また、 変更後の値が第 3 引数の変数に設定されます。

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

  • 66 〜 75 行目

    コマンドラインの第 2 引数に rec が指定された場合に録音源の設定を行っています。 チャンネル名称を検索した時に保存しておいた要素番号に対応するビットを 1 に設定したビットマスクを作成します。このビットマスクにもやはり定数名が定義されています。 LECTURE 6 /dev/mixer の機能を調べる の 55 〜 59 行目の説明でも同じものを記述していますが、再度下記に載せます。

    定数名チャンネル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 )

    ioctl() の第 2 引数に SOUND_MIXER_WRITE_RECSRC もしくは MIXER_WRITE( SOUND_MIXER_RECSRC ) を設定します ( SOUND_MIXER_WRITE_RECSRC は MIXER_WRITE( SOUND_MIXER_RECSRC ) のエイリアス )。

    第 3 引数に先程作成したビットマスク変数へのポインタを設定して ioctl() を実行すると、 指定したチャンネルが録音源として設定されます ( 実際に使用できなくても設定できるかもしれません )。 実行後には実際に設定されたチャンネルを表すビットマスクが、第 3 引数に設定される。。。 と思います ( m(__)m 前回説明した ioctl( SOUND_MIXER_READ_RECSRC ) を使えば分かりますよねっ ! )

    なお、全てのビットを 0 にしたままで ioctl() を実行した場合、マイク入力が有効になる様です。


mixer_set.c の説明は以上です。OSS API 自体の説明は今回までで終りです。次回からは API の使い方ではなく、音で遊んでいきたいと思います。できる限り早く。。。m(__)m