戻る
Last Update 2003/04/30
WindowsオーディオミキサーAPIを使う
 アプリケーションから直接 Windows の音量などのサウンドデバイスに関する操作をするにはミキサーAPIを使います。ここではミキサー API について、完全ではありませんが解説していきます。
 関数や構造体については SDK の記載のまま書いているのでお使いの開発環境に置き換えてください。Delphi をお使いの方は拙作 Mixer コンポーネントが参考になるかと思います。

 使用する関数や構造体の詳細については MSDN Audio Mixer Reference などを参照してください。

目次
ミキサーの構造
ミキサー構成要素の調査
コントロールの操作
左右バランス
コールバック処理
リストコントロールを操作する
マスター音量だけを操作する

付録



ミキサーの構造
図.1 デバイスとライン
図.2 ラインとコントロールの関係
 ミキサーの構造は大まかに書くと図.1の様になっています。一般的にサウンドカード1つが1つのデバイスに相当します(デバイスのことを単にミキサーと呼ぶ場合もあります)。複数のサウンドカードが搭載されていれば当然デバイスも複数存在します。デバイスはラインで構成されており、ラインは出口(ディスティネーションライン)と入り口(ソースライン)の2種類があります。図.1に示されるようにディスティネーションラインにはソースラインがぶらさがっているような構造になっています。ソースラインから入ってきた信号が他のソースと混ざり合ってディスティネーションラインから出て行くわけです。一般的なサウンドカードにはディスティネーションラインとして「スピーカ出力」と「録音入力」を備えています。場合によってはこれ以外に「音声入力」を備えている事もあります。
 図.1には「音量」に相当する要素がありません。わかりやすくするため省いてあるのですが、図.2に示すようにラインが「音量」などを所有しています。「音量」「ミュート」「トーン調整」などの操作対象をコントロールと呼びます。コントロールには他にも数多くの種類がありますが、ここでは主に「音量」コントロールに着目して進めていきます。
 「音量」コントロールはすべてのラインに備えられてると思うかもしれませんが、そうではありません。実際のところほとんどのラインには備えられているのですが保証はありません。他の種類のコントロールについても同様で、あるだろうと予想することはできますが実際に調べてみないとわかりません。これらはデバイスドライバが決めることであって、”存在しない”という事態も考慮する必要があります。ラインについても同様で、どのようなラインがデバイスに存在するのか調査する必要があります。
ミキサー構成要素の調査
 ミキサーの構成要素は次のような順番で調査していきます。
デバイス
 ↓
ディスティネーションライン
 ↓
ソースライン
 ↓
コントロール
上記の手順をもう少し具体的に書くと次のようになります。
  1. デバイス数を取得
  2. デバイス情報を取得
  3. ディスティネーションライン数を取得
  4. デバイスをオープン
  5. ディスティネーションライン情報を取得
  6. ソースライン数を取得
  7. ソースライン情報を取得
  8. ラインIDとコントロール数を取得
  9. コントロール情報を取得
  10. デバイスをクローズ
それぞれの段階でその数だけループすればすべてのコントロールについて列挙できることになります。
 実際に「音量」を操作するには、9. で得られたコントロール情報をもとにして値を取得するので、得られた情報をリストや配列に保持しておく必要があります。それらについては後述します。

1. デバイス数を取得
 デバイス数の取得には mixerGetNumDevs を使います。引数はありません。戻り値がデバイス数です。サウンドデバイスを搭載していない可能性もあるので 0 が返ってくる事もありえます。API の引数や戻り値などについて最低限必要な事しか解説しないので MSDN なども合わせて参照してください。

2. デバイス情報を取得
 デバイス情報の取得には mixerGetDevCaps を使います。引数 uMxId にはデバイス番号を指定します。1番目の場合は 0 で、1. で得た数 -1 まで有効です。pmxcaps には MIXERCAPS 構造体を、cbmxcaps には MIXERCAPS 構造体のサイズを指定します。MIXERCAPS 構造体はあらかじめ 0 で初期化しておきます。他の関数で使う構造体についても、特に記述が無い場合は 0 で初期化する必要があると考えてください。また、ポインタを渡す必要がある箇所でも明示していないので、そのへんはお使いの処理系に合わせて自己で判断してください。
 関数を実行すると、MIXERCAPS 構造体の szPname メンバは取得したサウンドデバイスの名前が入っています。

3. ディスティネーションライン数を取得
 デバイスに接続されているディスティネーションラインの数は 2. で取得した MIXERCPAS 構造体の cDestinations メンバが示しています。

4. デバイスをオープン
 ミキサーデバイスのオープンには mixerOpen を使います。引数 phmx はミキサーハンドルを受け取るためのものです。uMxId は 2. の uMxId と同じ値を指定します。dwCallback と dwInstance には 0 を指定します。fdwOpen はフラグです。 uMxId には mixerGetNumDevs の値を使うので MIXER_OBJECTF_MIXER を指定します。

5. ディスティネーションライン情報を取得
 ディスティネーションライン情報の取得には mixerGetLineInfo を使います。引数 hmxobj には 4. で得たミキサーハンドルを指定します。pmxl には MIXERLINE 構造体を指定します。MIXERLINE 構造体の cbStruct メンバには MIXERLINE 構造体のサイズを入れ、dwDestination には取得したいディスティネーションラインの番号を入れておきます。1番目は 0 です。引数 fdwInfo にはディスティネーションライン番号を元にして取得するので MIXER_GETLINEINFOF_DESTINATION を MIXER_OBJECTF_HMIXER と併せて指定します。

6. ソースライン数を取得
 ディスティネーションラインに接続されているソースライン数は 5. で取得した MIXERLINE 構造体の cConnections メンバが示してます。

7. ソースライン情報を取得
 ソースライン情報の取得には 5. で使ったのと同じ mixerGetLineInfo を使います。引数 hmxobj には 5. と同じようにミキサーハンドルを指定します。MIXERLINE 構造体のメンバは、cbStruct に構造体自身のサイズ、dwDestination に 5. で指定したものと同じディスティネーションライン番号、dwSource には取得したいソースライン番号(1番目は 0)を入れておきます。引数 fdwInfo は、今度はソースライン番号からソースライン情報を取得するので MIXER_GETLINEINFOF_SOURCE と MIXER_OBJECTF_HMIXER を指定します。

8. ラインIDとコントロール数を取得
 ラインIDは 7. で取得した MIXERLINE 構造体の dwLineID メンバが、コントロール数は cControls メンバが示しています。

9. コントロール情報を取得
 ようやくコントロール情報を取得する段階までやってきました。コントロール情報の取得には mixerGetLineControls を使用します。この関数は目的の種類のコントロールを1つだけ取得したり、接続されているすべてのコントロールを取得することができます。ここでは後々使えるようにすべて取得することにします。
 引数 hmxobj には今までと同じようにミキサーハンドルを指定します。pmxlc には MIXERLINECONTROLS 構造体を指定します。cbStruct メンバには構造体自身のサイズ、dwLineID には 8. のラインID、cControls には 8. のコントロール数、cbmxctrl には MIXERCONTROL 構造体のサイズを入れます。そして、pamxctrl メンバには MIXERCONTROL 構造体の配列を指定します。配列の長さは取得するコントロールの数です。ここではすべてのコントロールについて取得するのでコントロール数分の長さの配列を用意します。ここで注意することは、MIXERLINECONTROLS の cbmxctrl メンバには MIXERCONTROL 構造体の1つ分のサイズを入れることです。最後の引数 fdwControls フラグにはすべてのコントロールについて取得するので MIXER_GETLINECONTROLSF_ALL と MIXER_OBJECTF_HMIXER を指定します。
 関数を実行すると、指定した MIXERCONTROL 構造体の配列にコントロール情報が詰まっています。

10. デバイスをクローズ
 最後にミキサデバイスをクローズしておく必要があります。クローズには mixerClose を使います。引数 hmx には 4. で取得したミキサーハンドルを指定します。クローズするタイミングは調査後すぐにでも、アプリケーション終了時でもかまいません。アプリケーション終了時までオープンしたままならば、コントロールを操作する際にこの時オープンしたハンドルを使い回せます。

 これでコントロール情報をすべて取得することができました。やることは数を取得して掘り下げていく事の繰り返しなのですが、関数自体の使い方が多岐にわたっているので MSDN などのドキュメントを見てもわかりづらいです。
コントロールの操作
 コントロールの操作には mixerGetControlDetails と mixerSetControlDetails を使います。前者は状態を取得するために、後者は状態を設定するために使います。両者とも引数は同じです。
 音量を取得する場合について考えてみます。引数 hmxobj にはミキサーデバイスハンドルを指定します。mixerOpen で取得できるハンドルのことです。このハンドルはあらかじめ調査の段階で取得しておいたハンドルでもかまいませんし、この際に mixerOpen してもかまいません。
 第二引数 pmxcd には MIXERCONTROLDETAILS 構造体を指定します。この構造体には次のメンバがあります。

MIXERCONTROLDETAILS
cbStruct自身のサイズ
dwControlID操作したいコントロールのID
cChannelsコントロールに必要なチャンネル数
cMultipleItemsチャンネルあたりの項目数
cbDetailsMIXERCONTROLDETAILS_UNSIGNED 構造体のサイズ
paDetailsMIXERCONTROLDETAILS_UNSIGNED 構造体の配列
 第三引数 fdwDetails フラグには、取得する場合は MIXER_GETCONTROLDETAILSF_VALUE と MIXER_OBJECTF_HMIXER を、設定する場合は MIXER_SETCONTROLDETAILSF_VALUE と MIXER_OBJECTF_HMIXER を指定します。

 第二引数で指定する MIXERCONTROLDETAILS 構造体について解説します。後ろのメンバから見ていきます。

paDetails
 paDetails には取得また設定したい値を入れるための構造体の配列を渡します。今回は音量なので MIXERCONTROLDETAILS_UNSIGNED を使います。どの構造体を使うかは MIXERCONTROL 構造体の dwControlType をもとに決定します。次にそのリストを示します。

MIXERCONTROLDETAILS_BOOLEAN を使う必要のあるコントロール
(MIXERCONTROL.dwControlType の値)
MIXERCONTROL_CONTROLTYPE_BOOLEANMETER
MIXERCONTROL_CONTROLTYPE_BOOLEAN
MIXERCONTROL_CONTROLTYPE_BUTTON
MIXERCONTROL_CONTROLTYPE_LOUDNESS
MIXERCONTROL_CONTROLTYPE_MONO
MIXERCONTROL_CONTROLTYPE_MUTE
MIXERCONTROL_CONTROLTYPE_ONOFF
MIXERCONTROL_CONTROLTYPE_STEREOENH
MIXERCONTROL_CONTROLTYPE_MIXER
MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT
MIXERCONTROL_CONTROLTYPE_MUX
MIXERCONTROL_CONTROLTYPE_SINGLESELECT

MIXERCONTROLDETAILS_SIGNED を使う必要のあるコントロール
MIXERCONTROL_CONTROLTYPE_PEAKMETER
MIXERCONTROL_CONTROLTYPE_SIGNEDMETER
MIXERCONTROL_CONTROLTYPE_SIGNED
MIXERCONTROL_CONTROLTYPE_DECIBELS
MIXERCONTROL_CONTROLTYPE_PAN
MIXERCONTROL_CONTROLTYPE_QSOUNDPAN
MIXERCONTROL_CONTROLTYPE_SLIDER

MIXERCONTROLDETAILS_UNSIGNED を使う必要のあるコントロール
MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER
MIXERCONTROL_CONTROLTYPE_UNSIGNED
MIXERCONTROL_CONTROLTYPE_BASS
MIXERCONTROL_CONTROLTYPE_EQUALIZER
MIXERCONTROL_CONTROLTYPE_FADER
MIXERCONTROL_CONTROLTYPE_TREBLE
MIXERCONTROL_CONTROLTYPE_VOLUME
MIXERCONTROL_CONTROLTYPE_MICROTIME
MIXERCONTROL_CONTROLTYPE_MILLITIME
MIXERCONTROL_CONTROLTYPE_PERCENT

MIXERCONTROLDETAILS_LISTTEXT を使う必要のあるコントロール
MIXERCONTROL_CONTROLTYPE_EQUALIZER
MIXERCONTROL_CONTROLTYPE_MIXER
MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT
MIXERCONTROL_CONTROLTYPE_MUX
MIXERCONTROL_CONTROLTYPE_SINGLESELECT

「音量」コントロールは dwControlType が MIXERCONTROL_CONTROLTYPE_VOLUME なので、MIXERCONTROLDETAILS_UNSIGNED 構造体の配列を指定すれば良いということになります。必要とされる配列の長さについては後述します。

cbDetails
 cbDetails には paDetails に指定する構造体のサイズを入れます。配列すべてのサイズではなくて構造体1つ分のサイズです。

cMultipleItems
 cMultipleItems メンバには、MIXERCONTROL 構造体の fdwControl フラグに MIXERCONTROL_CONTROLF_MULTIPLE が含まれる場合のみ MIXERCONTROL 構造体の cMultipleItems メンバの値を指定します。

cChannels
 cChannels にはこのコントロールに必要なチャンネル数です。一見 MIXERLINE 構造体の cChannels メンバをそのまま指定すれば良いように感じますが、そうではありません。コントロールによって必要なチャンネル数が異なるからです。これには MIXERCONTROL 構造体の fdwControl フラグを参照します。もし fdwControl フラグに MIXERCONTROL_CONTROLF_UNIFORM が含まれている場合はチャンネル数に関係なく 1 であることを意味します。そうでない場合はラインのチャンネル数(MIXERLINE.cChannels)です。例えば、「ミュート」、「スイッチ」コントロールやモノラルラインには多くの場合 MIXERCONTROL_CONTROLF_UNIFORM が指定されています。「ミュート」コントロールについては例外で、ドライバによっては MIXERCONTROL_CONTROLF_UNIFORM を返さないものがあります。その場合、そのままラインのチャンネル数を指定するとエラーが返るかアクセス違反が起きるので「ミュート」コントロールについては 1 と決め打ちする必要があります(Windows 標準ボリュームコントロールでも 1 に決め打ちしてるようです)。

dwControlID
 dwControlID には操作したいコントロールのIDを指定します。MIXERCONTROL 構造体の dwControlID を指定します。

cbStruct
 cbStruct には構造体自身のサイズを入れます。

 paDetails メンバに指定する配列の長さはどうやって決めれば良いのでしょうか。これは cChannels * cMultipleItems で得られます。Multiple なコントロールでない場合は cMultipleItems は 0 なので cChannels が長さになります。つまり、「音量」コントロールの場合は Multiple なコントロールではないので cChannels の長さが必要になります。「イコライザ」コントロールの場合は Multiple なコントロールなので cChannels * cMultipleItems の長さが必要になります(実際にはイコライザは UNIFORM なコントロールの場合がほとんどなので 1 * cMultipleItems になるでしょう)。

 これで準備は整いました。mixerGetControlDetails 関数が成功すると MIXERCONTROLDETAILS_UNSIGNED の配列にはそれぞれのチャンネルの音量が入っています。値は32ビット整数ですが、有効な値は 0〜65535 です。値を設定する場合は MIXERCONTROLDETAILS_UNSIGNED の配列にそれぞれのチャンネルの値を入れて mixerSetControlDetails 関数を実行します。

 コントロールを操作するにはコントロール情報(MIXERCONTROL 構造体)以外にもいくつかの値が必要になってきます。MIXERCONTROLDETAILS 構造体についてもう一度整理してみましょう。
MIXERCONTROLDETAILS
cbStruct自身のサイズ
dwControlIDMIXERCONTROL.dwControlID
cChannelsMIXERLINE.cChannels、もしくは 1(MIXERCONTROL.fdwControl を参照)
cMultipleItemsMIXERCONTROL.cMultipleItems、もしくは 0(MIXERCONTROL.fdwControl を参照)
cbDetailsMIXERCONTROLDETAILS_XXXXX 構造体のサイズ
paDetailsMIXERCONTROLDETAILS_XXXXX 構造体の配列(MIXERCONTROL.dwControlType を参照)
したがって、コントロール操作に最低限必要な値は、 ということになります。これらの値を調査の段階でリストなり配列なりに保持しておき、コントロールを操作する段階で使うと効率的です。
左右バランス
 Windows 標準のボリュームコントロールにはステレオラインならば左右バランスの調整ができます。しかしそのような種類のミキサーコントロールはありません。左右バランスはそれぞれのチャンネルの音量から計算して得ます。Windows のボリュームコントロールで音量を 0 にして終了し、もういちどボリュームコントロールを起動してみてください。0 にしたラインは左右バランスが中央に戻っています。これは 左:0、右:0 なため中央ということになるわけです。ステレオなラインでの音量は、音量の大きい方のチャンネルの値を表示します。そして、左右チャンネルの値の比から左右バランスを計算し、その値を記録しておきます。記録しておかないと音量をセットする際に左右バランスがわからなくなってしまいます。
コールバック処理
 コールバック処理を行えば、他のアプリケーションがミキサーコントロールなどに対して操作を行ったかどうかを知ることが出来ます。コールバックといってもコールバック関数を使うのではなくメッセージを受け取ります。
 ミキサーデバイスをオープンする際に mixerOpen 関数の dwCallback 引数にウィンドウハンドルを渡すと、以後オープンしたミキサーデバイスに変更があった場合に2種類のメッセージが送られてくるようになります。コントロールに変更があった場合には MM_MIXM_CONTROL_CHANGE が、ライン状態に変更があった場合には MM_MIXM_LINE_CHANGE が送られてきます。それぞれのメッセージのパラメータは次のようになっています。

MM_MIXM_CONTROL_CHANGE
wParam:ミキサーデバイスハンドル
lParam:コントロール ID
MM_MIXM_LINE_CHANGE
wParam:ミキサーデバイスハンドル
lParam:ライン ID
調査の段階で記録しておいた情報の中に、該当するコントロールまたはラインがあれば、それに関する更新を行えばよいということになります。

 2種類のメッセージは使い分けるべきなのでしょうが、実際のところ MM_MIXM_CONTROL_CHANGE しか使いません。ラインメッセージはドライバによって送ってきたりこなかったりで扱いづらいです。両方のメッセージが続けて送られてくることもあります。MM_MIXM_CONTROL_CHANGE のみの使用で問題ないでしょう。
リストコントロールを操作する
 リストコントロールの操作はちょっと複雑です。リストコントロールとは MIXERCONTROLDETAILS_LISTTEXT を使う必要のあるコントロールの事です(先述のリストを参照)。一般的なサウンドカードでは MUX(録音ソース選択) がリストコントロールです。Windows 標準のボリュームコントロールでは、録音ソース選択はあたかもそれぞれのラインにそのようなコントロールが存在するように感じられますが、実際には表示されていない MUX コントロールが存在し、標準ボリュームコントロールではそれをあたかもラインに機能があるように表示しているわけです。
 リストコントロールは名前の通りリストを保持しています。MIXERCONTROLDETAILS_LISTTEXT 構造体には2つのパラメータ(dwParam1、dwParams2)と1つの文字列(szName)が含まれます。MUX コントロールでは dwParam1 にライン ID が入っています。szName メンバはそのラインの名前です。MUX コントロールは排他的選択なコントロールですから、リスト中のどれか1つだけが選択状態になります。選択されいている項目を知るには MIXERCONTROLDETAILS_BOOLEAN 構造体を使います。mixerGetControlDetails で値を取得すると、MIXERCONTROLDETAILS_BOOLEAN 配列のいずれかが TRUE(1) になっており、おなじインデックスのリスト項目が選択されているラインを示しています。選択されているラインを変更するには MIXERCONTROLDETAILS_BOOLEAN 配列の該当するインデックスの要素を TRUE(他はFALSE)にして mixerSetControlDetails を使います。
 リストを取得する場合は mixerGetControlDetails 関数の 引数 fdwDetails に MIXER_GETCONTROLDETAILSF_LISTTEXT を指定します。選択状態の取得・設定の場合は音量の時のように MIXER_GETCONTROLDETAILSF_VALUE を指定します。
マスター音量だけを操作する
 上述ではどのようなコントロールを操作する場合でも対応できる方法を示しましたが、単一のコントロールだけを操作したい場合はずっとシンプルにできます。例えば、マスター音量だけを操作する場合について述べます。
 単一のコントロールをだけを操作したい場合はすべてについて調べていく必要はありません。関数の引数を変更することで目的の情報だけを得ることが出来ます。コントロール情報を取得するまでの流れは上述と同じですが、mixerGetLineInfo と mixerGetLineControls の使い方に違いがあります。

mixerGetLineInfo
 引数 pmxl の MIXERLINE 構造体を次のようにします。
 dwComponentType メンバにはラインの種類を指定します。マスターラインを取得したいので MIXERLINE_COMPONENTTYPE_DST_SPEAKERS になります。cbStruct は構造体のサイズを入れ、その他のメンバは 0 で初期化します。
 引数 fdwInfo には種類を指定して取得するので MIXER_GETLINEINFOF_COMPONENTTYPE と MIXER_OBJECTF_HMIXER を指定します。

mixerGetLineControls
 引数 pmxlc の MIXERLINECONTROLS 構造体を次のようにします。
 dwLineID メンバには mixerGetLineInfo で取得したライン ID を指定します。dwControlType メンバにはコントロールの種類を指定します。音量コントロールを取得したいので MIXERCONTROL_CONTROLTYPE_VOLUME を指定します。cControls メンバには 1 を指定します。cbmxctrl メンバには MIXERCONTROL 構造体のサイズを入れ、pamxctrl メンバに MIXERCONTROL 構造体の変数を指定します。cbStruct メンバには自身のサイズを入れます。その他のメンバは 0 で初期化します。
 引数 fdwControls には種類を指定して取得するので MIXER_GETLINECONTROLSF_ONEBYTYPE と MIXER_OBJECTF_HMIXER を指定します。

 上述のように2つの関数を使うとコントロール情報が取得できるので、この情報をもとにコントロール操作を行います。

付録 (BBSに寄せられた質問等)
録音Ctl文字質問 oneeven [2004/10/11(Mon) 22:30] (206)

webmaster Vivas
Copyright(C)1999, Vivas. All rights reserved.