C++ Builder CE サウンドプログラミング

1. PowerDVD Ultra 18等で『DLNA TV 二か国語放送主福切り替え』がブロークンなのを対策してみる

これはQiitaに投稿した記事https://qiita.com/docnao/items/d1572cb5f1907e4bddbdの改訂版です。

サブタイトルのような支障(仕様ともいふ)をなんとかしたいわけです。最低限左chの日本語音声と右chの英語音声が混ざっているのが不快ですが、PowerDVD Ultra側では切り替えができません。Windows 11の場合ならば、下記で、

左チャンネルを0にして右を100とか、逆に右チャンネルを0にして左チャンネルを100とかに変更すれば一応右左(例えば英語と日本語)が同時に聞こえるというのは防げます。もっとも定位はセンターからズレますし、音量レベルも下がるのが不都合ですけど、これを手動で行うのはあまりにも面倒なので、プログラムで実現したいわけです。

#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <stdio.h>

int main(int argc, char** argv) {
	HRESULT hr;
	IMMDeviceEnumerator* pEnum = NULL;
	IMMDevice* pEndpoint = NULL;
	IAudioEndpointVolume* pAudioEndVol = NULL;

	hr = CoInitializeEx(0, COINIT_MULTITHREADED);// Coおまじない

	// MMDevice インターフェースを取得

	hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, IID_PPV_ARGS(&pEnum));
	if (FAILED(hr)) {
		CoUninitialize();
		return 1;
	}

	// defaultのオーディオエンドポイントを取得

	hr = pEnum->GetDefaultAudioEndpoint(eRender, eConsole, &pEndpoint);
	if (FAILED(hr)) {
		if (pEnum)
			pEnum->Release();
		CoUninitialize();
		return 2;
	}

	// ボリュームオブジェクトを作成

	hr = pEndpoint->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&pAudioEndVol);
	if (FAILED(hr)) {
		if (pEndpoint)
			pEndpoint->Release();
		if (pEnum)
			pEnum->Release();
		CoUninitialize();
		return 3;
	}

	// 左右のチャンネルの音量をセット

	char* pfile;
	pfile = argv[0] + strlen(argv[0]);
	for (; pfile > argv[0]; pfile--)
	{
		if ((*pfile == '\\') || (*pfile == '/'))
		{
			pfile++;
			break;
		}
	}

	if (!_stricmp(pfile, "left.exe")) {
		pAudioEndVol->SetChannelVolumeLevelScalar(0, 1.0, &GUID_NULL);   // Left
		pAudioEndVol->SetChannelVolumeLevelScalar(1, 0.0, &GUID_NULL);   // right


	}
	else if (!_stricmp(pfile, "right.exe")) {
		pAudioEndVol->SetChannelVolumeLevelScalar(0, 0.0, &GUID_NULL);   // Left
		pAudioEndVol->SetChannelVolumeLevelScalar(1, 1.0, &GUID_NULL);   // right

	}
	else {
		pAudioEndVol->SetChannelVolumeLevelScalar(0, 1.0, &GUID_NULL);   // Left
		pAudioEndVol->SetChannelVolumeLevelScalar(1, 1.0, &GUID_NULL);   // right
	}

	if (pAudioEndVol)
		pAudioEndVol->Release();
	if (pEndpoint)
		pEndpoint->Release();
	if (pEnum)
		pEnum->Release();
	CoUninitialize();
	return 0;
}

これを例えばrestore.cppとしてセーブして。コマンドラインのC Compilerである、bcc32cでコンパイルすればbuild完了で、restore.exeができます。restore.cppが存在するディレクトリで”Open Git Bash here”した後、

jakeb@Orbit-11 MINGW64 ~/OneDrive/Desktop/restore
$ vi restore.cpp

jakeb@Orbit-11 MINGW64 ~/OneDrive/Desktop/restore
$ bcc32c restore.cpp
Embarcadero C++ 7.70 for Win32 Copyright (c) 2012-2024 Embarcadero Technologies, Inc.
restore.cpp:
Turbo Incremental Link 6.99 Copyright (c) 1997-2024 Embarcadero Technologies, Inc.

jakeb@Orbit-11 MINGW64 ~/OneDrive/Desktop/restore
$ ls -alt
total 336
-rw-r--r-- 1 jakeb 197609 196608  1月 24 11:18 restore.tds
drwxr-xr-x 1 jakeb 197609      0  1月 24 11:18 ./
-rwxr-xr-x 1 jakeb 197609  75264  1月 24 11:18 restore.exe*
-rw-r--r-- 1 jakeb 197609   1874  1月 24 11:18 restore.cpp
drwxr-xr-x 1 jakeb 197609      0  1月 24 11:17 ../

jakeb@Orbit-11 MINGW64 ~/OneDrive/Desktop/restore
$ cp restore.exe left.exe

jakeb@Orbit-11 MINGW64 ~/OneDrive/Desktop/restore
$ cp restore.exe right.exe

等として、restore.exeをleft.exeおよびright.exeとしてもCopyしてください。ソースを読めばわかりますが、LinuxというかUnixの基本的手法のargv[0]で挙動を変えるという仕掛けです。標準のUnixコマンドにもそのような仕掛けをもったものがちらほらあります。(かなりのオールドタイマーでないと知らないかも。筆者なんざBSD/386の時代から…..。)

これでright.exeを起動(ダブルクリックでおけ)すれば、

となり、left.exeを起動すれば、

元に戻したければ、restore.exeを起動すれば

と元に戻ります。お試しあれ。なおこれらは100対0にするので、それが不満な場合は、いったん

HRESULT GetChannelVolumeLevelScalar(
  [in]  UINT  nChannel,
  [out] float *pfLevel
);

で現在の値を取り出しておいて、ファイルなどへ書き出しておき、restore.exeで戻す等は可能です。

真面目に定位まで含めて対策するのであれば、元記事にあるようにapo equalizer等で

Copy: L=L R=L

なるスクリプトを末端に置けばいいです。普通の状態に戻すのは、このスクリプトをremoveすればいいです。(意外に面倒ですけど。)

ちなみにVisual StudioでもIDEからなら正常にコンパイルできました。

コメント