#include "StdAfx.h" #ifdef _USE_VOICECHAT #include "VoiceChatClient.h" #ifdef _DEBUG #define new new(_NORMAL_BLOCK,__FILE__,__LINE__) #endif const float g_fVoiceChatVolumeRate = 2.0f; CVoiceChatClient::CVoiceChatClient(void) { m_pClient = NULL; m_nNumDevice = 0; m_Devices = NULL; m_fVoiceChatVolume = 0.5f; m_hMixer = NULL; m_fMicVolume = 0.5f; #if defined(DOLBY_CLIENT_LOG) logout = NULL; #endif m_fOrigMicVolume = 0.5f; } CVoiceChatClient::~CVoiceChatClient(void) { SAFE_DELETE( m_Devices ); #if defined(DOLBY_CLIENT_LOG) fclose(logout); #endif ICEClient_DestroyIceClient( m_pClient ); RestoreOrigMicConfig(); FinalizeMicControl(); } #if defined(DOLBY_CLIENT_LOG) void CVoiceChatClient::logging(unsigned int level, const char *msg, void *rock) { fprintf((FILE*)rock, "%s\n", msg); fflush((FILE*)rock); } #endif bool CVoiceChatClient::Initialize( const char *pServerIP, unsigned short nServerPort, const char *pUserName, UINT nPrivateID, VOICE_CHAT_CODEC Codec ) { m_pClient = ICEClient_CreateIceClient(); if( !m_pClient ) { return false; } #if defined(DOLBY_CLIENT_LOG) const char* logfile = "DolbyAxonClientLog.txt"; fopen_s(&logout, logfile, "a+"); ICEClient_SetLogging(m_pClient, logging, logout, 3); #endif ICECLIENT_ERROR IceError = ICEClient_Init( m_pClient, ICECLIENT_ENGINE_DOLBYHEADPHONE ); if( IceError != ICECLIENT_ERROR_NONE) { IceError = ICEClient_Init( m_pClient, ICECLIENT_ENGINE_DIRECTX ); if( IceError != ICECLIENT_ERROR_NONE && IceError != ICECLIENT_ERROR_ALREADY_INITED ) { return false; } } IceError = ICEClient_SetCodec( m_pClient, Codec ); if( IceError != ICECLIENT_ERROR_NONE ) { return false; } // 적절한 렌더링 엔진 선택. ICEClient_device OutputDevice; IceError = ICEClient_GetOutputDevice( m_pClient, &OutputDevice ); OutputDevice.enginetype = ICEClient_GetDefaultEngineType( OutputDevice.devtype ); IceError = ICEClient_SetDevice( m_pClient, &OutputDevice, NULL ); //if multigame mode set gameid ICEClient_SetID(m_pClient, nPrivateID, 0); ICEClient_SetServer( m_pClient, pServerIP, nServerPort ); ICEClient_SetName( m_pClient, pUserName ); //ICEClient_SetPrivateID( m_pClient, nPrivateID );. IceError = ICEClient_AutoTick( m_pClient ); if( IceError != ICECLIENT_ERROR_NONE ) { return false; } ICEClient_TalkInto( m_pClient, true, 0 ); // 해도 왜 자기 목소리가 안들리는지.. 뭔가 다른 설정이 필요한건가. //ICEClient_SetEchoSupression( m_pClient, true ); // 초기화되기 전에 한 볼륨셋팅들이 적용되도록, 초기화 후에 설정된 볼륨을 다시 한번 적용시킨다. SetVoiceChatVolume( m_fVoiceChatVolume ); // 마이크컨트롤이 있는지 확인. ICEClient_device InputDevice; IceError = ICEClient_GetCaptureDevice( m_pClient, &InputDevice ); if( IceError == ICECLIENT_ERROR_NONE ) { InitializeMicControl( &InputDevice ); CheckOrigMicConfig(); } // 미리 장치를 검색해둔다.(기본생성은 디폴트를 따르고 중간에 검색된 장치로 바꿀 수 있게 해준다.) m_nNumDevice = 0; ICEClient_GetDeviceList( m_Devices, &m_nNumDevice ); if( !m_nNumDevice ) { SAFE_DELETE( m_Devices ); } else { m_Devices = (ICEClient_device *)malloc( sizeof(ICEClient_device)*m_nNumDevice ); IceError = ICEClient_GetDeviceList( m_Devices, &m_nNumDevice ); if( IceError != ICECLIENT_ERROR_NONE ) SAFE_DELETE( m_Devices ); } return true; } void CVoiceChatClient::Finalize() { CVoiceChatClient *pClient = CVoiceChatClient::GetInstancePtr(); SAFE_DELETE( pClient ); } ICEClient_device *CVoiceChatClient::GetDevice( unsigned int nIndex ) { if( !m_Devices ) return NULL; if( nIndex < m_nNumDevice ) return &m_Devices[nIndex]; return NULL; } bool CVoiceChatClient::SetDevice( unsigned int nIndex, bool bMic ) { if( !m_pClient ) return false; if( !m_Devices ) return false; if( nIndex >= m_nNumDevice ) return false; ICECLIENT_ERROR IceError; if( bMic ) IceError = ICEClient_SetDevice( m_pClient, NULL, &m_Devices[nIndex] ); else IceError = ICEClient_SetDevice( m_pClient, &m_Devices[nIndex], NULL ); return (IceError == ICECLIENT_ERROR_NONE); } bool CVoiceChatClient::GetCaptureDeviceName( std::wstring &wszName ) { if( !m_pClient ) return false; // 마이크컨트롤이 있는지 확인. ICEClient_device InputDevice; if( ICEClient_GetCaptureDevice( m_pClient, &InputDevice ) == ICECLIENT_ERROR_NONE ) { ToWideString( InputDevice.name, wszName ); return true; } return false; } bool CVoiceChatClient::IsExistMic() { if( !m_Devices ) return false; for( unsigned int i = 0; i < m_nNumDevice; ++i ) { if( m_Devices[i].devtype == ICECLIENT_DEVICE_CAPTURE ) return true; } return false; } BYTE CVoiceChatClient::GetSpeaking() { if( !IsExistMic() ) return 0; if( !m_pClient ) return 0; // 말하고 있는 도중의 체크는 아래 함수로 한다. ICEClient_stat stat = ICEClient_GetStats(m_pClient); return stat.talking; } void CVoiceChatClient::SetVoiceChatVolume( float fValue ) { if( !m_pClient ) return; m_fVoiceChatVolume = fValue * g_fVoiceChatVolumeRate; ICEClient_SetVolume( m_pClient, m_fVoiceChatVolume ); } float CVoiceChatClient::GetVoiceChatVolume() { return m_fVoiceChatVolume / g_fVoiceChatVolumeRate; } void CVoiceChatClient::MuteMyMic( bool bMute ) { if( !m_pClient ) return; ICEClient_MuteMic( m_pClient, (bMute ? 1 : 0) ); } void CVoiceChatClient::SetRotation( int nRotation ) { if( !m_pClient ) return; ICEClient_SetRotation( m_pClient, nRotation ); } void CVoiceChatClient::SetVoiceFont( eVoiceFontType eVoiceFont, float fPitch, float fTimbre ) { if( !m_pClient ) return; switch( eVoiceFont ) { case VoiceFontNone: ICEClient_SetVoiceFont( m_pClient, ICECLIENT_VOICEFONT_NONE ); break; case VoiceFontW2M: ICEClient_SetVoiceFont( m_pClient, ICECLIENT_VOICEFONT_PRESET_W2M ); break; case VoiceFontM2W: ICEClient_SetVoiceFont( m_pClient, ICECLIENT_VOICEFONT_PRESET_M2W ); break; case VoiceFontELF: ICEClient_SetVoiceFont( m_pClient, ICECLIENT_VOICEFONT_PRESET_ELF ); break; case VoiceFontCustom: { ICEClient_voicefont CustomVoiceFont; CustomVoiceFont.pitchchange = fPitch; CustomVoiceFont.timbrechange = fTimbre; ICEClient_SetCustomVoiceFont( m_pClient, CustomVoiceFont ); } break; } } void CVoiceChatClient::StartMicTestPhase1() { if( !m_pClient ) return; ICEClient_StartMicTestPhase1( m_pClient ); } void CVoiceChatClient::StartMicTestPhase2( void (*callback)(void*) ) { if( !m_pClient ) return; ICEClient_StartMicTestPhase2( m_pClient, ICECLIENT_MICTEST_LOOPBACK, callback, NULL ); } void CVoiceChatClient::StopMicTest() { if( !m_pClient ) return; ICEClient_StopMicTest( m_pClient ); } bool CVoiceChatClient::InitializeMicControl( ICEClient_device *pCaptureDevice ) { FinalizeMicControl(); HRESULT hr; const int nMaxMixerCount = 20; // 대충 20개까지 장치 검사해본다. bool bFind = false; for( int i = 0; i < nMaxMixerCount; ++i ) { hr = mixerOpen(&m_hMixer, i, 0, 0, 0); if( hr != S_OK ) continue; memset(&m_mxl, 0, sizeof(m_mxl)); m_mxl.cbStruct = sizeof(m_mxl); MIXERCAPS caps; if( mixerGetDevCaps((UINT_PTR)m_hMixer, &caps, sizeof(MIXERCAPS)) != MMSYSERR_NOERROR ) { FinalizeMicControl(); continue; } WCHAR wszBuff[256] = L""; //ZeroMemory(wszBuff, sizeof(WCHAR)*_countof(wszBuff));//sizeof * sizeof의 결과는 대개 정확하지 않습니다. 문자 수 또는 바이트 수를 사용하려고 했습니까? ZeroMemory(wszBuff, sizeof(wszBuff)); MultiByteToWideChar(CP_ACP, 0, pCaptureDevice->name, -1, wszBuff, _countof(wszBuff) ); if( wcsncmp( caps.szPname, wszBuff, wcslen(caps.szPname) ) != 0 ) { // caps.szPname으로 오는게 32크기이므로 널문자 제외한 크기만큼 비교해야한다. FinalizeMicControl(); continue; } int nDest = caps.cDestinations; for( int j = 0; j < nDest; ++j ) { m_mxl.cbStruct = sizeof(m_mxl); m_mxl.dwSource = 0; m_mxl.dwDestination = j; if( mixerGetLineInfo((HMIXEROBJ)m_hMixer, &m_mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_DESTINATION) != MMSYSERR_NOERROR ) { FinalizeMicControl(); break; } if( m_mxl.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN ) break; } int nConnections = m_mxl.cConnections; for( int j = 0; j < nConnections; ++j ) { m_mxl.dwSource = j; if( mixerGetLineInfo((HMIXEROBJ)m_hMixer, &m_mxl, MIXER_GETLINEINFOF_SOURCE) != MMSYSERR_NOERROR ) { FinalizeMicControl(); break; } if( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == m_mxl.dwComponentType ) { // 실제로 마이크 볼륨 조절을 해보니 그래도 못찾는 경우가 있어서, 볼륨 조절 컨트롤까지 직접 찾아본다. MIXERLINECONTROLS mlc = {0,}; MIXERCONTROL mc = {0,}; mlc.cbStruct = sizeof(MIXERLINECONTROLS); mlc.dwLineID = m_mxl.dwLineID; mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mlc.cControls = 1; mlc.pamxctrl = &mc; mlc.cbmxctrl = sizeof(MIXERCONTROL); if( mixerGetLineControls((HMIXEROBJ)m_hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR ) { FinalizeMicControl(); break; } CheckMicBoostControl(); return true; } } FinalizeMicControl(); } return false; } void CVoiceChatClient::FinalizeMicControl() { if( m_hMixer ) { mixerClose(m_hMixer); m_hMixer = NULL; m_bEnableMicBoost = false; } } void CVoiceChatClient::CheckOrigMicConfig() { if( IsExistMicControl() ) m_fOrigMicVolume = GetMicVolume(); //if( IsEnableMicBoost() ) // m_bOrigMicBoost = GetMicBoost(); } void CVoiceChatClient::RestoreOrigMicConfig() { if( IsExistMicControl() ) SetMicVolume( m_fOrigMicVolume ); //if( IsEnableMicBoost() ) // SetMicBoost( m_bOrigMicBoost ); } void CVoiceChatClient::SetMicVolume( float fValue ) { if( !IsExistMicControl() ) return; MIXERLINECONTROLS mlc = {0,}; MIXERCONTROL mc = {0,}; mlc.cbStruct = sizeof(MIXERLINECONTROLS); mlc.dwLineID = m_mxl.dwLineID; mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mlc.cControls = 1; mlc.pamxctrl = &mc; mlc.cbmxctrl = sizeof(MIXERCONTROL); mixerGetLineControls((HMIXEROBJ)m_hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE); MIXERCONTROLDETAILS mxcd = {0,}; MIXERCONTROLDETAILS_UNSIGNED mxdu = {0,}; mxdu.dwValue = DWORD(fValue * 65535); mxcd.cMultipleItems = 0; mxcd.cChannels = 1; mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = mc.dwControlID; mxcd.cbDetails = sizeof(mxdu); mxcd.paDetails = &mxdu; mixerSetControlDetails((HMIXEROBJ)m_hMixer, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE); } float CVoiceChatClient::GetMicVolume() { if( !IsExistMicControl() ) return 0.0f; MIXERLINECONTROLS mlc = {0,}; MIXERCONTROL mc = {0,}; mlc.cbStruct = sizeof(MIXERLINECONTROLS); mlc.dwLineID = m_mxl.dwLineID; mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mlc.cControls = 1; mlc.pamxctrl = &mc; mlc.cbmxctrl = sizeof(MIXERCONTROL); mixerGetLineControls((HMIXEROBJ)m_hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE); MIXERCONTROLDETAILS mxcd = {0,}; MIXERCONTROLDETAILS_UNSIGNED mxdu = {0,}; mxcd.cMultipleItems = 0; mxcd.cChannels = 1; mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = mc.dwControlID; mxcd.cbDetails = sizeof(mxdu); mxcd.paDetails = &mxdu; mixerGetControlDetails((HMIXEROBJ)m_hMixer, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE); // 0~65535의 값이니. return mxdu.dwValue / 65535.0f; } void CVoiceChatClient::CheckMicBoostControl() { MMRESULT mmr; HMIXER hMixer; MIXERCAPS MixerCaps; MIXERLINE MixerLine; HRESULT hr; const int nMaxMixerCount = 20; // 대충 20개까지 장치 검사해본다. for( int i = 0; i < nMaxMixerCount; ++i ) { hr = mixerOpen(&hMixer, i, 0, 0, 0); if( hr != S_OK ) continue; if( mixerGetDevCaps((UINT_PTR)hMixer, &MixerCaps, sizeof(MIXERCAPS)) != MMSYSERR_NOERROR ) { mixerClose( hMixer ); continue; } bool bFindDest = false; memset(&MixerLine, 0, sizeof(MixerLine)); int nDest = MixerCaps.cDestinations; for( int j = 0; j < nDest; ++j ) { MixerLine.cbStruct = sizeof(MixerLine); MixerLine.dwDestination = j; MixerLine.dwSource = 0; mmr = mixerGetLineInfo((HMIXEROBJ)hMixer, &MixerLine, MIXER_GETLINEINFOF_DESTINATION); if( mmr != MMSYSERR_NOERROR ) { mixerClose( hMixer ); continue; } if( MixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS ) { bFindDest = true; break; } } if( !bFindDest ) { mixerClose( hMixer ); continue; } // 소스컨트롤 중에 어느게 실제로 마이크와 연결되어있는지 구분할 방법이 없으므로 모든 증폭컨트롤에 적용하기로 한다. int nConnection = MixerLine.cConnections; DWORD dwDstIndex = MixerLine.dwDestination; for( int j = 0; j < nConnection; ++j ) { MixerLine.cbStruct = sizeof( MIXERLINE ); MixerLine.dwSource = j; MixerLine.dwDestination = dwDstIndex; if( mixerGetLineInfo((HMIXEROBJ)hMixer, &MixerLine, MIXER_GETLINEINFOF_SOURCE) != MMSYSERR_NOERROR ) { continue; } if( (MixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) || (MixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LAST) ) { MIXERCONTROL mxc; MIXERLINECONTROLS mxlc; ZeroMemory(&mxlc, sizeof(MIXERLINECONTROLS)); mxlc.cbStruct = sizeof(MIXERLINECONTROLS); mxlc.dwLineID = MixerLine.dwLineID; mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(MIXERCONTROL); mxlc.pamxctrl = &mxc; mxc.cbStruct = sizeof(MIXERCONTROL); if( mixerGetLineControls((HMIXEROBJ)hMixer, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR ) { // 여기까지 들어왔으면 증폭을 찾을 것이라 판단한다. mixerClose( hMixer ); m_bEnableMicBoost = true; return; } } } mixerClose( hMixer ); } // 예제 소스라 해서 디버깅해보니 제대로 동작하지 않는다. 다음에 정리.. /* HMIXER hMixer; MMRESULT mmr; MIXERCAPS MixerCaps; MIXERLINE MixerLine; int u; bool bFindDest = false; bool bFindCtrl = false; int index = 0; mmr = mixerOpen(&hMixer, index, 0, 0, 0); if (mmr != MMSYSERR_NOERROR) { return; } mmr = mixerGetDevCaps((UINT_PTR)hMixer, &MixerCaps, sizeof(MixerCaps)); if( mmr != MMSYSERR_NOERROR ) { mixerClose( hMixer ); return; } memset(&MixerLine, 0, sizeof(MixerLine)); for( u = 0; u < MixerCaps.cDestinations; u++ ) { MixerLine.cbStruct = sizeof(MixerLine); MixerLine.dwDestination = u; MixerLine.dwSource = 0; mmr = mixerGetLineInfo((HMIXEROBJ)hMixer, &MixerLine, MIXER_GETLINEINFOF_DESTINATION); if( mmr != MMSYSERR_NOERROR ) { mixerClose( hMixer ); return; } if( MixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN ) { bFindDest = true; break; } } if( !bFindDest ) { mixerClose( hMixer ); return; } MIXERLINECONTROLS MixerLineControls; PMIXERCONTROL paMixerControls = new MIXERCONTROL[MixerLine.cControls]; MixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS); MixerLineControls.dwLineID = MixerLine.dwLineID; MixerLineControls.cControls = MixerLine.cControls; MixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); MixerLineControls.pamxctrl = paMixerControls; // Get the controls for the current destination mixer line mmr = mixerGetLineControls((HMIXEROBJ)hMixer, &MixerLineControls, MIXER_GETLINECONTROLSF_ALL); if( mmr != MMSYSERR_NOERROR ) { delete[] paMixerControls; mixerClose( hMixer ); return; } for( u = 0; u < MixerLine.cControls; u++ ) { if ((paMixerControls[u].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX) || (paMixerControls[u].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)) { bFindCtrl = true; break; } } if( !bFindCtrl ) { delete[] paMixerControls; mixerClose( hMixer ); return; } MIXERCONTROLDETAILS MixerControlDetails; PMIXERCONTROLDETAILS_LISTTEXT pMixerControlDetails_Listtext = new MIXERCONTROLDETAILS_LISTTEXT[MixerLine.cChannels * paMixerControls[u].cMultipleItems]; MixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); MixerControlDetails.dwControlID = paMixerControls[u].dwControlID; MixerControlDetails.cChannels = 1;//MixerLine.cChannels; MixerControlDetails.hwndOwner = (HWND)paMixerControls[u].cMultipleItems; MixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); MixerControlDetails.paDetails = pMixerControlDetails_Listtext; mmr = mixerGetControlDetails((HMIXEROBJ)hMixer, &MixerControlDetails, MIXER_GETCONTROLDETAILSF_LISTTEXT); if (mmr != MMSYSERR_NOERROR) { delete[] paMixerControls; delete[] pMixerControlDetails_Listtext; mixerClose( hMixer ); return; } PMIXERCONTROLDETAILS_BOOLEAN pMixerControlDetails_Boolean = new MIXERCONTROLDETAILS_BOOLEAN[MixerLine.cChannels * paMixerControls[u].cMultipleItems]; MixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); MixerControlDetails.dwControlID = paMixerControls[u].dwControlID; MixerControlDetails.cChannels = 1;//MixerLine.cChannels; MixerControlDetails.hwndOwner = (HWND)paMixerControls[u].cMultipleItems; MixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); MixerControlDetails.paDetails = pMixerControlDetails_Boolean; mmr = mixerGetControlDetails((HMIXEROBJ)hMixer, &MixerControlDetails, MIXER_GETCONTROLDETAILSF_VALUE); if( mmr != MMSYSERR_NOERROR ) { delete[] paMixerControls; delete[] pMixerControlDetails_Listtext; delete[] pMixerControlDetails_Boolean; mixerClose( hMixer ); return; } //Set only the microphone or line as input sources for( int v = 0; v < paMixerControls[u].cMultipleItems; v++ ) { pMixerControlDetails_Boolean[v].fValue = FALSE; if( pMixerControlDetails_Listtext[v].dwParam2 == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE ) pMixerControlDetails_Boolean[v].fValue = TRUE; } mmr = mixerSetControlDetails((HMIXEROBJ)hMixer, &MixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE); if( mmr != MMSYSERR_NOERROR ) { delete[] paMixerControls; delete[] pMixerControlDetails_Listtext; delete[] pMixerControlDetails_Boolean; mixerClose( hMixer ); return; } delete[] paMixerControls; delete[] pMixerControlDetails_Listtext; delete[] pMixerControlDetails_Boolean; mixerClose( hMixer ); m_bEnableMicBoost = true; return; */ /* MMRESULT mmr; int u; PMIXERCONTROL paMixerControls; MIXERLINECONTROLS mlc = {0,}; MIXERCONTROL mc = {0,}; paMixerControls = (PMIXERCONTROL)malloc(sizeof(MIXERCONTROL) * m_mxl.cControls); mlc.cbStruct = sizeof(MIXERLINECONTROLS); mlc.dwLineID = m_mxl.dwLineID; mlc.cControls = m_mxl.cControls; mlc.cbmxctrl = sizeof(MIXERCONTROL); mlc.pamxctrl = paMixerControls; mmr = mixerGetLineControls((HMIXEROBJ)m_hMixer, &mlc, MIXER_GETLINECONTROLSF_ALL); for (u = 0; u < (int)m_mxl.cControls; u++) if (paMixerControls[u].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX) //if (NULL != strstr(paMixerControls[u].szName, "Mic Boost")) //if (paMixerControls[u].dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF) break; mlc.cbStruct = sizeof(MIXERLINECONTROLS); mlc.dwControlID = paMixerControls[u].dwControlID; mlc.cbmxctrl = sizeof(MIXERCONTROL); mlc.pamxctrl = &mc; mmr = mixerGetLineControls((HMIXEROBJ)m_hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYID); free(paMixerControls); PMIXERCONTROLDETAILS_LISTTEXT pMixerControlDetails_Listtext; MIXERCONTROLDETAILS MixerControlDetails; pMixerControlDetails_Listtext = (PMIXERCONTROLDETAILS_LISTTEXT)malloc(m_mxl.cChannels * mc.cMultipleItems * sizeof(MIXERCONTROLDETAILS_LISTTEXT)); MixerControlDetails.cbStruct = sizeof(MixerControlDetails); MixerControlDetails.dwControlID = mc.dwControlID; MixerControlDetails.cChannels = m_mxl.cChannels; MixerControlDetails.cMultipleItems = mc.cMultipleItems; MixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); MixerControlDetails.paDetails = pMixerControlDetails_Listtext; mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &MixerControlDetails, MIXER_GETCONTROLDETAILSF_LISTTEXT); for(u = 0; u < (int)mc.cMultipleItems; u++) if (0 == wcscmp(pMixerControlDetails_Listtext[u].szName, L"Microphone")) break; PMIXERCONTROLDETAILS_BOOLEAN pMixerControlDetails_Boolean; pMixerControlDetails_Boolean = (PMIXERCONTROLDETAILS_BOOLEAN)malloc(m_mxl.cChannels * mc.cMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN)); MixerControlDetails.cbStruct = sizeof(MixerControlDetails); MixerControlDetails.dwControlID = mc.dwControlID; MixerControlDetails.cChannels = m_mxl.cChannels; MixerControlDetails.cMultipleItems = mc.cMultipleItems; MixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); MixerControlDetails.paDetails = pMixerControlDetails_Boolean; mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &MixerControlDetails, 0L); pMixerControlDetails_Boolean[u].fValue = true; mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &MixerControlDetails, 0L); free(pMixerControlDetails_Listtext); free(pMixerControlDetails_Boolean); */ } void CVoiceChatClient::SetMicBoost( bool bBoost ) { if( !IsEnableMicBoost() ) return; MMRESULT mmr; HMIXER hMixer; MIXERCAPS MixerCaps; MIXERLINE MixerLine; HRESULT hr; const int nMaxMixerCount = 20; // 대충 20개까지 장치 검사해본다. for( int i = 0; i < nMaxMixerCount; ++i ) { hr = mixerOpen(&hMixer, i, 0, 0, 0); if( hr != S_OK ) continue; if( mixerGetDevCaps((UINT_PTR)hMixer, &MixerCaps, sizeof(MIXERCAPS)) != MMSYSERR_NOERROR ) { mixerClose( hMixer ); continue; } bool bFindDest = false; memset(&MixerLine, 0, sizeof(MixerLine)); int nDest = MixerCaps.cDestinations; for( int j = 0; j < nDest; ++j ) { MixerLine.cbStruct = sizeof(MixerLine); MixerLine.dwDestination = j; MixerLine.dwSource = 0; mmr = mixerGetLineInfo((HMIXEROBJ)hMixer, &MixerLine, MIXER_GETLINEINFOF_DESTINATION); if( mmr != MMSYSERR_NOERROR ) { mixerClose( hMixer ); continue; } if( MixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS ) { bFindDest = true; break; } } if( !bFindDest ) { mixerClose( hMixer ); continue; } // 소스컨트롤 중에 어느게 실제로 마이크와 연결되어있는지 구분할 방법이 없으므로 모든 증폭컨트롤에 적용하기로 한다. int nConnection = MixerLine.cConnections; DWORD dwDstIndex = MixerLine.dwDestination; for( int j = 0; j < nConnection; ++j ) { MixerLine.cbStruct = sizeof( MIXERLINE ); MixerLine.dwSource = j; MixerLine.dwDestination = dwDstIndex; if( mixerGetLineInfo((HMIXEROBJ)hMixer, &MixerLine, MIXER_GETLINEINFOF_SOURCE) != MMSYSERR_NOERROR ) { continue; } if( (MixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) || (MixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LAST) ) { MIXERCONTROL mxc; MIXERLINECONTROLS mxlc; ZeroMemory(&mxlc, sizeof(MIXERLINECONTROLS)); mxlc.cbStruct = sizeof(MIXERLINECONTROLS); mxlc.dwLineID = MixerLine.dwLineID; mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(MIXERCONTROL); mxlc.pamxctrl = &mxc; mxc.cbStruct = sizeof(MIXERCONTROL); if( mixerGetLineControls((HMIXEROBJ)hMixer, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR ) { DWORD dwValue = bBoost ? 1 : 0; MIXERCONTROLDETAILS mxcd; DWORD dwControlID = mxc.dwControlID; DWORD cMultipleItems = mxc.cMultipleItems; // Initialize the MIXERCONTROLDETAILS structure ZeroMemory(&mxcd, sizeof(MIXERCONTROLDETAILS)); mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = dwControlID; //mxc.dwControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = cMultipleItems; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); mxcd.paDetails = &dwValue; //mxcd.paDetails = &mxcdStruct; // Set the values if( mixerSetControlDetails((HMIXEROBJ)hMixer, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR ) { OutputDebug( "VoiceChat Boost Setting Fail!" ); } } } } mixerClose( hMixer ); } } #endif