Question

Freeプログラマー on Tue, 22 May 2018 10:58:03


VisualStudio2010 MFCでソフトを開発しております。 ソフトのターゲットOSは Win7~10です。

ソフトに録音/再生機能が必要になりましたので、mci関係のAPIで開発を進めております。

表題のように再生時の音量調整が上手く行かないのですが、

デバイスタイプをMCI_DEVTYPE_WAVEFORM_AUDIOにして録音したwavファイルを
再生するとき、、
MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE で音量を調整しても効きません。


デバイスタイプを "mpegvideo" にして再生するファイルをOpenすると、音量を変えることが出来る という情報があったのですが、

録音をMCI_DEVTYPE_WAVEFORM_AUDIOでしている為、_"mpegvideo"でOpenするとエラーになります。
また、デバイスタイプを"mpegvideo"して録音用にOpenするとエラーになりました。


デバイスタイプをMCI_DEVTYPE_WAVEFORM_AUDIOにして音量を調整する事は可能でしょうか?

("mpegvideo"でも録音/再生/音量調整できて、PAUSEなどで問題なければばそれでも結構です。 ※デバイスタイプにより一時停止で先頭にSEEKされるという事があるらしいので)


サンプルのソースははしょってますので、このままでは動きませんが、
大まかな流れと考えてください。

// -----------------------------
// 録音

MCI_OPEN_PARMS prm;
prm.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_WAVEFORM_AUDIO;
//prm.lpstrDeviceType = (LPCWSTR) (LPCWSTR)_T("mpegvideo"); MCI_OPENで失敗する
prm.lpstrElementName = _T("");
MCIERROR ret = mciSendCommand( 0, MCI_OPEN, MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT|MCI_WAIT, (DWORD)&prm );
mciSendCommand( prm.wDeviceID , MCI_RECORD , 0 , 0 );

// 時間経過
mciSendCommand( prm.wDeviceID , MCI_STOP , MCI_WAIT , 0);
MCI_SAVE_PARMS msp;
mciSendCommand( prm.wDeviceID , MCI_SAVE ,MCI_WAIT | MCI_SAVE_FILE , (DWORD)&msp);
MCI_GENERIC_PARMS prm;
mciSendCommand( prm.wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)&prm );



// -----------------------------
//再生

MCI_OPEN_PARMS prm;
prm.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_WAVEFORM_AUDIO;
prm.lpstrElementName = 録音したファイルパス;
 mciSendCommand( 0, MCI_OPEN, MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT,(DWORD)&prm );

// 音量調整
MCI_DGV_SETAUDIO_PARMS mci_vol = {0};
mci_vol.dwItem = MCI_DGV_SETAUDIO_VOLUME ;
mci_vol.dwValue = 100; // 0~1000で調節可能
mciSendCommand( prm.wDeviceID, MCI_SETAUDIO, MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE, (DWORD) &mci_vol);

mciSendCommand( prm.wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)&prm );

よろしくお願い致します。


Replies

sygh on Tue, 22 May 2018 15:36:16


waveaudioはmciSendCommand()によるボリューム調節に対応していないようです。デバイス識別名"mpegvideo"を使って対処するか、mciSendString()を使ってコマンド文字列を直接送信する方式で対処する方法もあるようです。

なお、MCIデバイスタイプに整数値ID番号ではなくデバイス識別名の文字列を指定する場合、パラメータオプションMCI_OPEN_TYPE_IDの指定は不要です。"mpegvideo"を使ってオープンするとエラーになったというのは、おそらくMCI_OPEN_TYPE_IDを誤って指定していたからではないですか?

MCI_OPEN command (Windows)

ちなみに、各種MCI関数が返すエラーコードに対応する(ローカライズされた)エラーメッセージを、mciGetErrorString()で取得することができます。エラーが発生した場合、単に「XXXをするとエラーが発生した」ということだけでなく、「具体的にどういうエラーコードやエラーメッセージが返ってきたのか」ということについても述べるようにしてください。

CString GetMciErrorMessage(MCIERROR errorCode)
{
	CString strErrorMessage;
	const int MciMaxMessageLength = 128;
	const int MciMessageBufferLength = MciMaxMessageLength + 2;
	::mciGetErrorString(errorCode, strErrorMessage.GetBuffer(MciMessageBufferLength), MciMessageBufferLength);
	strErrorMessage.ReleaseBuffer();
	return strErrorMessage;
}

void DoMciTest()
{
	MCIERROR errorCode = 0;

	MCI_OPEN_PARMS openParams = {};
	openParams.lpstrElementName = <ファイルパス文字列>;
	//openParams.lpstrDeviceType = reinterpret_cast<LPCTSTR>(MCI_DEVTYPE_WAVEFORM_AUDIO);
	openParams.lpstrDeviceType = _T("mpegvideo");
	//const DWORD_PTR openParam1 = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT;
	const DWORD_PTR openParam1 = MCI_OPEN_TYPE | MCI_OPEN_ELEMENT;
	errorCode = ::mciSendCommand(0, MCI_OPEN, openParam1, reinterpret_cast<DWORD_PTR>(&openParams));
	if (errorCode != 0)
	{
		AfxMessageBox(GetMciErrorMessage(errorCode));
		exit(-1);
	}

	MCI_DGV_SETAUDIO_PARMS dgvSetAudioParams = {};
	dgvSetAudioParams.dwItem = MCI_DGV_SETAUDIO_VOLUME;
	dgvSetAudioParams.dwValue = 10; // ボリューム。
	const DWORD_PTR dgvSetAudioParam1 = MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE;
	errorCode = ::mciSendCommand(openParams.wDeviceID, MCI_SETAUDIO, dgvSetAudioParam1, reinterpret_cast<DWORD_PTR>(&dgvSetAudioParams));
	if (errorCode != 0)
	{
		AfxMessageBox(GetMciErrorMessage(errorCode));
		exit(-1);
	}

	MCI_PLAY_PARMS playParams = {};
	errorCode = ::mciSendCommand(openParams.wDeviceID, MCI_PLAY, 0, reinterpret_cast<DWORD_PTR>(&playParams));
	if (errorCode != 0)
	{
		AfxMessageBox(GetMciErrorMessage(errorCode));
		exit(-1);
	}
	// TODO: デバイスの停止とクローズ。
}

本題とは直接の関係はありませんが、LPCWSTRや_T()マクロの意味を正確に理解されていないようなので、まずMBCSとUnicodeの違いおよびconst修飾子について学習することを推奨します。

また、mciSendCommand()の第4引数の型はDWORDではなくDWORD_PTRです。DWORD_PTR型は32bitプロセスでは32bit幅ですが、64bitプロセスでは64bit幅であり、ポインタ互換のサイズを持つ符号なし整数型です。ポインタを誤ってDWORDに強制キャストしてしまうと、64bit向けにビルドしたプロセスの実行時に不具合を引き起こす原因になります。ポインタ互換の整数型に関しても学習してみてください。そのほか、C++ではC言語形式のキャスト構文を使うべきではありません。Web上の古い情報やサンプルをうのみにするのは避けたほうがいいです。

mciSendCommand function (Windows)

ちなみに「※デバイスタイプにより一時停止で先頭にSEEKされるという事があるらしいので」とやらのソース(情報源)は何ですか?

MIDIに関してはMCI_PAUSEを正常に受け付けないようですが、RIFFフォーマットのオーディオには関係ないはずです。MIDIとRIFFに関しては自分で調べてください。

Freeプログラマー on Wed, 23 May 2018 00:10:18


ご回答ありがとうございました。

"mpegvideo" の再生時の Openに関してはMCI_OPEN_TYPE_IDを外す事でエラーを回避出来ました。

他の情報もありがとうございました。

音声関係は今回初めて触っていますので、また行き詰ったときに質問を上げさせて頂く事になると思います。
その際にはよろしくお願い致します。