他人の空似

2016 年 5 月 18 日

音量ミキサーのアプリ別音量を設定するサンプル

Filed under: 未分類 — 中の人 @ 10:10 PM

タスクバーアイコンの音量から音量ミキサーを開いたところにあるアプリ別のアレです。
ライブラリやデバイスの都合で音量を調整できないことがありますが、それを無理にでもなんとかする際に便利です。

ではいつものようにコード本体からどうぞ。
http://resemblances.click3.org/product_list/index.cgi/detail/88
http://wordpress.click3.org/garakuta/volum_mixer_example.zip
動作としてはWindows付属の効果音を鳴らしっぱなしにしつつ、音量をひたすら上下させるだけのアプリになります。
また、サンプルの機能をそのままライブラリ化したものもあるので別途貼っておきます。
http://resemblances.click3.org/product_list/index.cgi/detail/87
http://wordpress.click3.org/garakuta/volume_manager.zip

前提

音量調整にはCOMを使用しています。
特別COMに関する説明はしませんので、COMとは何かを知らない場合は理解が難しい可能性があります。
またインスタンスの解放処理など本質的ではないコードも除けてあるので、サンプルそのままでの使用は非推奨です。
自身のコードに組み込む場合はvolume_managerの方を参照してください。

全体の流れ

非同期で効果音をループ再生
=>音声デバイスを取得
=>そのデバイスのセッション管理インスタンスを取得
=>セッションを列挙
=>セッションのプロセスIDを取得、自身のプロセスIDと一致する物を探索
=>音量を調整
となります。

非同期で効果音をループ再生

::PlaySoundW(L"C:\\Windows\\Media\\ringout.wav", nullptr, SND_ASYNC | SND_FILENAME | SND_LOOP);

単にPlaySoundしているだけです。

音声デバイスを取得

IMMDeviceEnumerator *deviceEnumerator;
::CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
IMMDevice *device;
deviceEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia, &device);

CoCreateInstanceのあたりは説明不要ですね。
GetDefaultAudioEndpointは名前の通りデフォルトのデバイスを取得するもので、eRenderはスピーカなどの出力、eMultimediaは何らかのコンテンツ再生用を意味しています。

そのデバイスのセッション管理インスタンスを取得

IAudioSessionManager2 *sessionManager;
device->Activate(__uuidof(IAudioSessionManager2), CLSCTX_INPROC_SERVER, nullptr, reinterpret_cast<void **>(&sessionManager));

これも特に説明はいらないですね。
単にActivateメソッドを呼んでいるだけです。

なお、ここでいうセッションは「アプリの音量セッション」とでも呼ぶべきもので、音量出力APIを利用した後で現在も生きているプロセスに紐づく音量管理セッションのことです。
音量再生系APIを呼んだ後でなくては生成されないので注意が必要です。

セッションを列挙

IAudioSessionEnumerator *sessionEnumerator;
sessionManager->GetSessionEnumerator(&sessionEnumerator);
unsigned int count;
sessionEnumerator->GetCount(reinterpret_cast<int *>(&count));
for (unsigned int i = 0; i < count; i++) {
   IAudioSessionControl *session1;
   sessionEnumerator->GetSession(i, &session1);
   IAudioSessionControl2 *session;
   session1->QueryInterface(&session);
   ...
}

GetSessionEnumeratorの返り値がセッションの個数取得とセッション取得メソッドがあるので、それをforで回して取得しています。
IAudioSessionControlにはプロセスIDのようなプロセスと紐づけられる情報がないのでIAudioSessionControl2にQueryInterfaceしています。

セッションのプロセスIDを取得、自身のプロセスIDと一致する物を探索

DWORD processId;
session->GetProcessId(&processId);
if (processId != ::GetCurrentProcessId()) {
   continue;
}

これも説明はいらないですね、みたままです。

音量を調整

ISimpleAudioVolume *audioVolume;
session->QueryInterface(&audioVolume);
unsigned int volume = 0;
while (true) {
   if (volume <= 100) {
      audioVolume->SetMasterVolume(static_cast<float>(volume) / 100, nullptr);
   } else if(volume < 200) {
      audioVolume->SetMasterVolume(static_cast<float>(200-volume) / 100, nullptr);
   } else {
      volume = 0;
   }
   ::Sleep(10);
   volume++;
}

ISimpleAudioVolumeへQueryInterfaceし、あとは10msごとにSetMasterVolumeする無限ループなだけです。
ここにはないですがGetMasterVolumeやSetMute/GetMuteといったメソッドも存在します。

上手くいけばここで音声が上下に波打つようにして流れ続けるはずです。

おわりに

必要最低限程度ですがこれでアプリ別音量の設定ができます。
ここには書いていませんが少し呼び方を変えれば対象デバイスを指定して同様の処理を行ったりマイク音量の調整といったことも可能です。
あとは必要に応じて該当するインターフェースのドキュメントを参照してください。

Powered by WordPress