次の方法で共有


レガシ オーディオ アプリケーションのオーディオ イベント

DirectSound、DirectShow、waveOutXxx 関数などのレガシ オーディオ API を使用すると、アプリケーションはオーディオ ストリームのボリューム レベルを取得および設定できます。 アプリケーションでは、これらの API のボリューム制御機能を使用して、アプリケーション ウィンドウにボリューム スライダーを表示できます。

Windows Vista では、システム ボリュームコントロール プログラム Sndvol を使用すると、ユーザーは個々のアプリケーションのオーディオ ボリューム レベルを制御できます。 アプリケーションによって表示されるボリューム スライダーは、Sndvol の対応するボリューム スライダーにリンクする必要があります。 ユーザーがアプリケーション ウィンドウのボリューム スライダーを使用してアプリケーションの音量を調整すると、Sndvol の対応するボリューム スライダーがすぐに移動して、新しいボリューム レベルを示します。 逆に、ユーザーが Sndvol を使用してアプリケーションのボリュームを調整した場合、アプリケーション ウィンドウのボリューム スライダーは、新しいボリューム レベルを示すように移動する必要があります。

Windows Vista では、Sndvol は、IDirectSoundBuffer::SetVolume メソッドまたは waveOutSetVolume 関数の呼び出しによってアプリケーションが行ったボリューム変更をすぐに反映します。 ただし、DirectSound や waveOutXxx 関数などのレガシ オーディオ API では、ユーザーが Sndvol を介してアプリケーション ボリュームを変更したときにアプリケーションに通知する手段はありません。 アプリケーションがボリューム スライダーを表示しても、ボリュームの変更の通知を受け取らない場合、スライダーは Sndvol でユーザーが行った変更を反映できません。 適切な動作を実装するには、アプリケーション デザイナーがレガシ オーディオ API による通知の欠如を何らかの形で補う必要があります。

1 つの解決策として、アプリケーションが定期的にタイマーを設定して、ボリューム レベルを確認して変更されたかどうかを確認するように通知する場合があります。

よりエレガントなソリューションは、アプリケーションがコア オーディオ API のイベント通知機能を使用することです。 特に、アプリケーションは IAudioSessionEventsインターフェイス登録して、ボリュームの変更やその他の種類のオーディオ イベントが発生したときにコールバックを受け取ることができます。 ボリュームが変更されると、ボリューム変更コールバック ルーチンは、変更を反映するようにアプリケーションのボリューム スライダーを直ちに更新できます。

次のコード例は、ボリューム変更やその他のオーディオ イベントの通知を受け取るためにアプリケーションを登録する方法を示しています。

//-----------------------------------------------------------
// Register the application to receive notifications when the
// volume level changes on the default process-specific audio
// session (with session GUID value GUID_NULL) on the audio
// endpoint device with the specified data-flow direction
// (eRender or eCapture) and device role.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hr)  \
              if (FAILED(hr)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

class AudioVolumeEvents
{
    HRESULT _hrStatus;
    IAudioSessionManager *_pManager;
    IAudioSessionControl *_pControl;
    IAudioSessionEvents *_pAudioEvents;
public:
    AudioVolumeEvents(EDataFlow, ERole, IAudioSessionEvents*);
    ~AudioVolumeEvents();
    HRESULT GetStatus() { return _hrStatus; };
};

// Constructor
AudioVolumeEvents::AudioVolumeEvents(EDataFlow flow, ERole role,
                                     IAudioSessionEvents *pAudioEvents)
{
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;

    _hrStatus = S_OK;
    _pManager = NULL;
    _pControl = NULL;
    _pAudioEvents = pAudioEvents;

    if (_pAudioEvents == NULL)
    {
        _hrStatus = E_POINTER;
        return;
    }

    _pAudioEvents->AddRef();

    // Get the enumerator for the audio endpoint devices
    // on this system.
    _hrStatus = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 __uuidof(IMMDeviceEnumerator),
                                 (void**)&pEnumerator);
    EXIT_ON_ERROR(_hrStatus)

    // Get the audio endpoint device with the specified data-flow
    // direction (eRender or eCapture) and device role.
    _hrStatus = pEnumerator->GetDefaultAudioEndpoint(flow, role,
                                                     &pDevice);
    EXIT_ON_ERROR(_hrStatus)

    // Get the session manager for the endpoint device.
    _hrStatus = pDevice->Activate(__uuidof(IAudioSessionManager),
                                  CLSCTX_INPROC_SERVER, NULL,
                                  (void**)&_pManager);
    EXIT_ON_ERROR(_hrStatus)

    // Get the control interface for the process-specific audio
    // session with session GUID = GUID_NULL. This is the session
    // that an audio stream for a DirectSound, DirectShow, waveOut,
    // or PlaySound application stream belongs to by default.
    _hrStatus = _pManager->GetAudioSessionControl(NULL, 0, &_pControl);
    EXIT_ON_ERROR(_hrStatus)

    _hrStatus = _pControl->RegisterAudioSessionNotification(_pAudioEvents);
    EXIT_ON_ERROR(_hrStatus)

Exit:
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
}

// Destructor
AudioVolumeEvents::~AudioVolumeEvents()
{
    if (_pControl != NULL)
    {
        _pControl->UnregisterAudioSessionNotification(_pAudioEvents);
    }
    SAFE_RELEASE(_pManager)
    SAFE_RELEASE(_pControl)
    SAFE_RELEASE(_pAudioEvents)
};

前のコード例では、AudioVolumeEvents という名前のクラスを実装しています。 プログラムの初期化中、オーディオ アプリケーションは AudioVolumeEvents オブジェクトを作成してオーディオ イベント通知を有効にします。 このクラスのコンストラクターは、次の 3 つの入力パラメーターを受け取ります。

コンストラクターは、IMMDeviceEnumerator::GetDefaultAudioEndpoint メソッドに、フローとロールの値を入力パラメーターとして提供します。 このメソッドは、指定されたデータ フロー方向とデバイス ロールを持つオーディオ エンドポイント デバイスをカプセル化する IMMDevice オブジェクトを作成します。

アプリケーションは、pAudioEvents によって指オブジェクトを実装します。 (実装は前のコード例では示されていません。IAudioSessionEvents インターフェイスを実装するコード例については、「オーディオ セッション イベント」を参照してください。このインターフェイスの各メソッドは、特定の種類のオーディオ イベントの通知を受け取ります。 アプリケーションが特定のイベントの種類に関心がない場合、そのイベント型のメソッドは何もしない必要がありますが、S_OKを返します。

IAudioSessionEvents::OnSimpleVolumeChanged メソッドは、ボリューム変更の通知を受け取ります。 通常、このメソッドは、アプリケーションのボリューム スライダーを更新します。

前のコード例では、AudioVolumeEvents クラスのコンストラクターは、セッション GUID 値GUID_NULLで識別されるプロセス固有の オーディオ セッション の通知を登録します。 既定では、DirectSound、DirectShow、waveOutXxx 関数などのレガシ オーディオ API は、ストリームをこのセッションに割り当てます。 ただし、DirectSound または DirectShow アプリケーションでは、オプションとして既定の動作をオーバーライドし、そのストリームをクロスプロセス セッションまたは GUID_NULL 以外の GUID 値で識別されるセッションに割り当てることができます。 (現在、waveOutXxx アプリケーションで同様の方法で既定の動作をオーバーライドするメカニズムは提供されていません)。この動作を持つ DirectShow アプリケーションのコード例については、「DirectShow アプリケーション デバイスの役割」を参照してください。 このようなアプリケーションに対応するために、前のコード例のコンストラクターを変更して、2 つの追加の入力パラメーター (監視対象のセッションがプロセス間またはプロセス固有のセッションかどうかを示すフラグ) を 2 つ受け取ることができます。 これらのパラメーターを、コンストラクターの IAudioSessionManager::GetAudioSessionControl メソッドの呼び出しに渡します。

コンストラクターが IAudioSessionControl::RegisterAudioSessionNotification メソッドを呼び出して通知を登録した後、アプリケーションは、IAudioSessionControl または IAudioSessionManagerインターフェイスが存在する限り、引き続き通知を受け取ります。 前のコード例の AudioVolumeEvents オブジェクトは、デストラクターが呼び出されるまで、これらのインターフェイスへの参照を保持します。 この動作により、AudioVolumeEvents オブジェクトの有効期間中、アプリケーションは引き続き通知を受け取ります。

DirectSound またはレガシ Windows マルチメディア アプリケーションでは、デバイスの役割に基づいてオーディオ デバイスを暗黙的に選択する代わりに、ユーザーがフレンドリ名で識別される使用可能なデバイスの一覧からデバイスを明示的に選択できる場合があります。 この動作をサポートするには、前のコード例を変更して、選択したデバイスのオーディオ イベント通知を生成する必要があります。 2 つの変更が必要です。 まず、入力パラメーターとして エンドポイント ID 文字列を受け入れるようにコンストラクター定義を変更します (コード例のフローパラメーターとロール パラメーターの代わりに)。 この文字列は、選択した DirectSound またはレガシ波形デバイスに対応するオーディオ エンドポイント デバイスを識別します。 次に、IMMDeviceEnumerator::GetDefaultAudioEndpoint メソッドの呼び出しを、IMMDeviceEnumerator::GetDevice メソッドの呼び出しに置き換えます。 GetDevice 呼び出しは、エンドポイント ID 文字列を入力パラメーターとして受け取り、文字列で識別されるエンドポイント デバイスのインスタンスを作成します。

DirectSound デバイスまたはレガシ波形デバイスのエンドポイント ID 文字列を取得する方法は次のとおりです。

まず、デバイスの列挙中に、DirectSound は列挙された各デバイスのエンドポイント ID 文字列を提供します。 デバイスの列挙を開始するために、アプリケーションは、DirectSoundCreate または DirectSoundCaptureCreate 関数 入力パラメーターとしてコールバック関数ポインターを渡します。 コールバック関数の定義は次のとおりです。

BOOL DSEnumCallback(
  LPGUID  lpGuid,
  LPCSTR  lpcstrDescription,
  LPCSTR  lpcstrModule,
  LPVOID  lpContext
);

Windows Vista では、lpcstrModule パラメーターはエンドポイント ID 文字列を指します。 (Windows Server 2003、Windows XP、Windows 2000 を含む以前のバージョンの Windows では、lpcstrModule パラメーターはデバイスのドライバー モジュールの名前を指します)。lpcstrDescription パラメーターは、デバイスのフレンドリ名を含む文字列を指します。 DirectSound デバイス列挙の詳細については、Windows SDK のドキュメントを参照してください。

次に、レガシ波形デバイスのエンドポイント ID 文字列を取得するには、waveOutMessage または waveInMessage 関数を使用して、DRV_QUERYFUNCTIONINSTANCEID メッセージを波形デバイス ドライバーに送信します。 このメッセージの使用方法を示すコード例については、「従来の Windows マルチメディア アプリケーションのデバイス ロール 」を参照してください。

レガシ オーディオ API との相互運用性の