VolumeInXXX.cpp
上传用户:oldpeter23
上传日期:2013-01-09
资源大小:1111k
文件大小:17k
源码类别:

IP电话/视频会议

开发平台:

Visual C++

  1. //NetTalk
  2. /*------------------------------------------------------------------------------*
  3.  =============================
  4.    模块名称: AudioPlay.cpp
  5.  =============================
  6.  
  7.  [版权]
  8.  
  9.    Alex Chmut
  10.                                               
  11. *------------------------------------------------------------------------------*/
  12. #include <Windows.h>
  13. #include "VolumeInXXX.h"
  14. /////////////////////////////////////////////////////////////////////////////
  15. //  Defines
  16. #define BAD_DWORD (DWORD)-1
  17. #define WND_CLASS_NAME "Input Volume Msg Wnd Class"
  18. #define WND_NAME "Input Volume Msg Wnd"
  19. /////////////////////////////////////////////////////////////////////////////
  20. //  Globals
  21. PCVolumeInXXX g_pThis = NULL;
  22. ////////////////////////////////////////////////////////////
  23. //{{{ Audio specific functions
  24. #define AUDFREQ 22050 // Frequency
  25. #define AUDCHANNELS 1 // Number of channels
  26. #define AUDBITSMPL 16 // Number of bits per sample
  27. inline
  28. void SetDeviceType( WAVEFORMATEX* pwfe )
  29. {
  30. memset( pwfe, 0, sizeof(WAVEFORMATEX) );
  31. WORD  nBlockAlign = (AUDCHANNELS*AUDBITSMPL)/8;
  32. DWORD nSamplesPerSec = AUDFREQ;
  33. pwfe->wFormatTag = WAVE_FORMAT_PCM;
  34. pwfe->nChannels = AUDCHANNELS;
  35. pwfe->nBlockAlign = nBlockAlign;
  36. pwfe->nSamplesPerSec = nSamplesPerSec;
  37. pwfe->wBitsPerSample = AUDBITSMPL;
  38. pwfe->nAvgBytesPerSec = nSamplesPerSec*nBlockAlign;
  39. }
  40. //}}} Audio specific functions
  41. /////////////////////////////////////////////////////////////////////////////
  42. //  Implementation
  43. //////////////
  44. bool CALLBACK EnumInputLineProc( UINT uLineIndex, MIXERLINE* pLineInfo, DWORD dwUserValue )
  45. {
  46. if ( pLineInfo->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE )
  47. {
  48. *((UINT*)dwUserValue) = uLineIndex;
  49. return false;
  50. }
  51. return true;
  52. }
  53. /*------------------------------------------------------------------------------*/
  54. CVolumeInXXX::CVolumeInXXX()
  55. : m_bOK(false),
  56. m_bInitialized(false),
  57. m_bAvailable(false),
  58. m_uMixerID(0L),
  59. m_dwMixerHandle(0L),
  60. m_hWnd(NULL),
  61. m_uMicrophoneSourceLineIndex(BAD_DWORD),
  62. m_dwMinimalVolume(BAD_DWORD),
  63. m_dwMaximalVolume(BAD_DWORD),
  64. m_pfUserSink(NULL),
  65. m_dwUserValue(0L)
  66. {
  67. if ( m_bOK = Init() )
  68. {
  69. g_pThis = this;
  70. UINT i=-1;
  71. EnumerateInputLines(EnumInputLineProc,(DWORD)&i);
  72. if(i!=(UINT)-1)
  73. {
  74. if ( !Initialize( i ) )
  75. {
  76. Done();
  77. g_pThis = NULL;
  78. }
  79. }
  80. }
  81. }
  82. /*------------------------------------------------------------------------------*/
  83. CVolumeInXXX::CVolumeInXXX( UINT uLineIndex )
  84. : m_bOK(false),
  85. m_bInitialized(false),
  86. m_bAvailable(false),
  87. m_uMixerID(0L),
  88. m_dwMixerHandle(0L),
  89. m_hWnd(NULL),
  90. m_uMicrophoneSourceLineIndex(BAD_DWORD),
  91. m_dwMinimalVolume(BAD_DWORD),
  92. m_dwMaximalVolume(BAD_DWORD),
  93. m_pfUserSink(NULL),
  94. m_dwUserValue(0L)
  95. {
  96. if ( m_bOK = Init() )
  97. {
  98. g_pThis = this;
  99. if ( !Initialize( uLineIndex ) )
  100. {
  101. Done();
  102. g_pThis = NULL;
  103. }
  104. }
  105. }
  106. /*------------------------------------------------------------------------------*/
  107. //////////////
  108. CVolumeInXXX::~CVolumeInXXX()
  109. {
  110. if ( m_bOK )
  111. Done();
  112. g_pThis = NULL;
  113. }
  114. /*------------------------------------------------------------------------------*/
  115. //////////////
  116. bool CVolumeInXXX::Init()
  117. {
  118. if ( !mixerGetNumDevs() )
  119. return false;
  120. // Getting Mixer ID
  121. HWAVEIN hwaveIn;
  122. MMRESULT mmResult;
  123. WAVEFORMATEX WaveFmt;
  124. SetDeviceType( &WaveFmt );
  125. mmResult = waveInOpen( &hwaveIn, WAVE_MAPPER, &WaveFmt, 0L, 0L, CALLBACK_NULL );
  126. if ( mmResult != MMSYSERR_NOERROR )
  127. {
  128. return false;
  129. } else {
  130. mmResult = mixerGetID( (HMIXEROBJ)hwaveIn, &m_uMixerID, MIXER_OBJECTF_HWAVEIN );
  131. waveInClose( hwaveIn );
  132. if ( mmResult != MMSYSERR_NOERROR )
  133. {
  134. return false;
  135. }
  136. }
  137. // Exposing Window to Mixer
  138. WNDCLASSEX wcx;
  139. memset( &wcx, 0, sizeof(WNDCLASSEX) );
  140. wcx.cbSize = sizeof(WNDCLASSEX);
  141. wcx.lpszClassName = WND_CLASS_NAME;
  142. wcx.lpfnWndProc = (WNDPROC)MixerWndProc;
  143. ::RegisterClassEx(&wcx);
  144. m_hWnd = CreateWindow( WND_CLASS_NAME,
  145. WND_NAME,
  146. WS_POPUP | WS_DISABLED,
  147. 0, 0, 0, 0,
  148. NULL, NULL, NULL, NULL );
  149. if ( !m_hWnd )
  150. {
  151. return false;
  152. }
  153. ::ShowWindow(m_hWnd, SW_HIDE);
  154. mmResult = mixerOpen( (LPHMIXER)&m_dwMixerHandle, m_uMixerID, (DWORD)m_hWnd, 0L, CALLBACK_WINDOW );
  155. if ( mmResult != MMSYSERR_NOERROR )
  156. {
  157. ::DestroyWindow( m_hWnd );
  158. return false;
  159. }
  160. return true;
  161. }
  162. /*------------------------------------------------------------------------------*/
  163. //////////////
  164. void CVolumeInXXX::Done()
  165. {
  166. if ( mixerClose( (HMIXER)m_dwMixerHandle ) != MMSYSERR_NOERROR )
  167. {
  168. }
  169. ::DestroyWindow( m_hWnd );
  170. m_bInitialized = false;
  171. m_bOK = false;
  172. }
  173. /*------------------------------------------------------------------------------*/
  174. //////////////
  175. void CVolumeInXXX::OnControlChanged( DWORD dwControlID )
  176. {
  177. if ( m_dwVolumeControlID == dwControlID )
  178. {
  179. DWORD dwVolume = GetCurrentVolume();
  180. if ( (dwVolume!=BAD_DWORD) && (m_pfUserSink) )
  181. {
  182. (*m_pfUserSink)( dwVolume, m_dwUserValue );
  183. }
  184. }
  185. }
  186. /*------------------------------------------------------------------------------*/
  187. //////////////
  188. bool CVolumeInXXX::Initialize( UINT uLineIndex )
  189. {
  190. MMRESULT mmResult;
  191. if ( !m_bOK )
  192. return false;
  193. MIXERLINE MixerLine;
  194. memset( &MixerLine, 0, sizeof(MIXERLINE) );
  195. MixerLine.cbStruct = sizeof(MIXERLINE);
  196. MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
  197. mmResult = mixerGetLineInfo( (HMIXEROBJ)m_dwMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
  198. if ( mmResult != MMSYSERR_NOERROR )
  199. {
  200. return false;
  201. }
  202. MIXERCONTROL Control;
  203. memset( &Control, 0, sizeof(MIXERCONTROL) );
  204. Control.cbStruct = sizeof(MIXERCONTROL);
  205. MIXERLINECONTROLS LineControls;
  206. memset( &LineControls, 0, sizeof(MIXERLINECONTROLS) );
  207. LineControls.cbStruct = sizeof(MIXERLINECONTROLS);
  208. MIXERLINE Line;
  209. memset( &Line, 0, sizeof(MIXERLINE) );
  210. Line.cbStruct = sizeof(MIXERLINE);
  211. if ( ( uLineIndex < MixerLine.cConnections ) )
  212. {
  213. Line.dwDestination = MixerLine.dwDestination;
  214. Line.dwSource = uLineIndex;
  215. mmResult = mixerGetLineInfo( (HMIXEROBJ)m_dwMixerHandle, &Line, MIXER_GETLINEINFOF_SOURCE );
  216. if ( mmResult != MMSYSERR_NOERROR )
  217. {
  218. return false;
  219. }
  220. LineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  221. LineControls.dwLineID = Line.dwLineID;
  222. LineControls.cControls = 1;
  223. LineControls.cbmxctrl = sizeof(MIXERCONTROL);
  224. LineControls.pamxctrl = &Control;
  225. mmResult = mixerGetLineControls( (HMIXEROBJ)m_dwMixerHandle, &LineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE );
  226. if ( mmResult == MMSYSERR_NOERROR )
  227. {
  228. if ( !(Control.fdwControl & MIXERCONTROL_CONTROLF_DISABLED) )
  229. {
  230. m_bAvailable = true;
  231. } else {
  232. }
  233. } else {
  234. }
  235. } else {
  236. return false;
  237. }
  238. // Retrieving Microphone Source Line
  239. for ( UINT uLine = 0; uLine < MixerLine.cConnections; uLine++ )
  240. {
  241. MIXERLINE MicrophoneLine;
  242. memset( &MicrophoneLine, 0, sizeof(MIXERLINE) );
  243. MicrophoneLine.cbStruct = sizeof(MIXERLINE);
  244. MicrophoneLine.dwDestination = MixerLine.dwDestination;
  245. MicrophoneLine.dwSource = uLine;
  246. mmResult = mixerGetLineInfo( (HMIXEROBJ)m_dwMixerHandle, &MicrophoneLine, MIXER_GETLINEINFOF_SOURCE );
  247. if ( mmResult == MMSYSERR_NOERROR )
  248. {
  249. if ( MicrophoneLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE )
  250. {
  251. m_uMicrophoneSourceLineIndex = MicrophoneLine.dwSource;
  252. break;
  253. }
  254. }
  255. }
  256. if ( m_uMicrophoneSourceLineIndex == BAD_DWORD )
  257. {
  258. }
  259. m_uSourceLineIndex = uLineIndex;
  260. m_nChannelCount = Line.cChannels;
  261. m_dwLineID = LineControls.dwLineID;
  262. m_dwVolumeControlID = Control.dwControlID;
  263. m_dwMinimalVolume = Control.Bounds.dwMinimum;
  264. m_dwMaximalVolume = Control.Bounds.dwMaximum;
  265. m_dwVolumeStep = Control.Metrics.cSteps;
  266. m_bInitialized = true;
  267. return true;
  268. }
  269. /*------------------------------------------------------------------------------*/
  270. //////////////////////////////////////////////
  271. bool CVolumeInXXX::GetMicrophoneSourceLineIndex( UINT* puLineIndex )
  272. {
  273. if ( !puLineIndex || !m_bInitialized || (m_uMicrophoneSourceLineIndex==BAD_DWORD) )
  274. return false;
  275. *puLineIndex = m_uMicrophoneSourceLineIndex;
  276. return true;
  277. }
  278. /*------------------------------------------------------------------------------*/
  279. //////////////////////////////////////////////
  280. // IVolume interface
  281. //////////////
  282. bool CVolumeInXXX::IsAvailable()
  283. {
  284. return m_bAvailable;
  285. }
  286. /*------------------------------------------------------------------------------*/
  287. //////////////
  288. void CVolumeInXXX::Enable()
  289. {
  290. if ( !m_bInitialized )
  291. return;
  292. bool bAnyEnabled = false;
  293. MMRESULT mmResult;
  294. MIXERLINE lineDestination;
  295. memset( &lineDestination, 0, sizeof(MIXERLINE) );
  296. lineDestination.cbStruct = sizeof(MIXERLINE);
  297. lineDestination.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
  298. mmResult = mixerGetLineInfo( (HMIXEROBJ)m_dwMixerHandle, &lineDestination, MIXER_GETLINEINFOF_COMPONENTTYPE );
  299. if ( mmResult != MMSYSERR_NOERROR )
  300. {
  301. return;
  302. }
  303. // Getting all line's controls
  304. int nControlCount = lineDestination.cControls;
  305. int nChannelCount = lineDestination.cChannels;
  306. MIXERLINECONTROLS LineControls;
  307. memset( &LineControls, 0, sizeof(MIXERLINECONTROLS) );
  308. MIXERCONTROL* aControls = (MIXERCONTROL*)malloc( nControlCount * sizeof(MIXERCONTROL) );
  309. if ( !aControls )
  310. {
  311. return;
  312. }
  313. memset( &aControls[0], 0, sizeof(nControlCount * sizeof(MIXERCONTROL)) );
  314. for ( int i = 0; i < nControlCount; i++ )
  315. {
  316. aControls[i].cbStruct = sizeof(MIXERCONTROL);
  317. }
  318. LineControls.cbStruct = sizeof(MIXERLINECONTROLS);
  319. LineControls.dwLineID = lineDestination.dwLineID;
  320. LineControls.cControls = nControlCount;
  321. LineControls.cbmxctrl = sizeof(MIXERCONTROL);
  322. LineControls.pamxctrl = &aControls[0];
  323. mmResult = mixerGetLineControls( (HMIXEROBJ)m_dwMixerHandle, &LineControls, MIXER_GETLINECONTROLSF_ALL );
  324. if ( mmResult == MMSYSERR_NOERROR )
  325. {
  326. for ( i = 0; i < nControlCount; i++ )
  327. {
  328. if ( aControls[i].dwControlType & MIXERCONTROL_CT_UNITS_BOOLEAN )
  329. {
  330. MIXERCONTROLDETAILS_BOOLEAN* aDetails = NULL;
  331. int nMultipleItems = aControls[i].cMultipleItems;
  332. int nChannels = nChannelCount;
  333. // MIXERCONTROLDETAILS
  334. MIXERCONTROLDETAILS ControlDetails;
  335. memset( &ControlDetails, 0, sizeof(MIXERCONTROLDETAILS) );
  336. ControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  337. ControlDetails.dwControlID = aControls[i].dwControlID;
  338. if ( aControls[i].fdwControl & MIXERCONTROL_CONTROLF_UNIFORM )
  339. {
  340. nChannels = 1;
  341. }
  342. if ( aControls[i].fdwControl & MIXERCONTROL_CONTROLF_MULTIPLE )
  343. {
  344. nMultipleItems = aControls[i].cMultipleItems;
  345. aDetails = (MIXERCONTROLDETAILS_BOOLEAN*)malloc(nMultipleItems*nChannels*sizeof(MIXERCONTROLDETAILS_BOOLEAN));
  346. if ( !aDetails )
  347. {
  348. continue;
  349. }
  350. for ( int nItem = 0; nItem < nMultipleItems; nItem++ )
  351. {
  352. LONG lValue = FALSE;
  353. if ( nItem == (int)m_uSourceLineIndex )
  354. lValue = TRUE;
  355. for ( int nChannel = 0; nChannel < nChannels; nChannel++ )
  356. {
  357. aDetails[nItem+nChannel].fValue = lValue;
  358. }
  359. }
  360. } else {
  361. nMultipleItems = 0;
  362. aDetails = (MIXERCONTROLDETAILS_BOOLEAN*)malloc(nChannels*sizeof(MIXERCONTROLDETAILS_BOOLEAN));
  363. if ( !aDetails )
  364. {
  365. continue;
  366. }
  367. for ( int nChannel = 0; nChannel < nChannels; nChannel++ )
  368. {
  369. aDetails[nChannel].fValue = (LONG)TRUE;
  370. }
  371. }
  372. ControlDetails.cChannels = nChannels;
  373. ControlDetails.cMultipleItems = nMultipleItems;
  374. ControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  375. ControlDetails.paDetails = &aDetails[0];
  376. mmResult = mixerSetControlDetails( (HMIXEROBJ)m_dwMixerHandle, &ControlDetails, 0L );
  377. if ( mmResult == MMSYSERR_NOERROR )
  378. {
  379. bAnyEnabled = true;
  380. }
  381. free( aDetails );
  382. }
  383. }
  384. } else {
  385. }
  386. free( aControls );
  387. if ( !bAnyEnabled )
  388. {
  389. }
  390. }
  391. /*------------------------------------------------------------------------------*/
  392. //////////////
  393. void CVolumeInXXX::Disable()
  394. {
  395. }
  396. /*------------------------------------------------------------------------------*/
  397. //////////////
  398. DWORD CVolumeInXXX::GetVolumeMetric()
  399. {
  400. if ( !m_bAvailable )
  401. return BAD_DWORD;
  402. return m_dwVolumeStep;
  403. }
  404. /*------------------------------------------------------------------------------*/
  405. //////////////
  406. DWORD CVolumeInXXX::GetMinimalVolume()
  407. {
  408. if ( !m_bAvailable )
  409. return BAD_DWORD;
  410. return m_dwMinimalVolume;
  411. }
  412. /*------------------------------------------------------------------------------*/
  413. //////////////
  414. DWORD CVolumeInXXX::GetMaximalVolume()
  415. {
  416. if ( !m_bAvailable )
  417. return BAD_DWORD;
  418. return m_dwMaximalVolume;
  419. }
  420. /*------------------------------------------------------------------------------*/
  421. //////////////
  422. DWORD CVolumeInXXX::GetCurrentVolume()
  423. {
  424. if ( !m_bAvailable )
  425. return BAD_DWORD;
  426. MIXERCONTROLDETAILS_UNSIGNED* aDetails = (MIXERCONTROLDETAILS_UNSIGNED*)malloc(m_nChannelCount*sizeof(MIXERCONTROLDETAILS_UNSIGNED));
  427. if ( !aDetails )
  428. return BAD_DWORD;
  429. MIXERCONTROLDETAILS ControlDetails;
  430. memset( &ControlDetails, 0, sizeof(MIXERCONTROLDETAILS) );
  431. ControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  432. ControlDetails.dwControlID = m_dwVolumeControlID;
  433. ControlDetails.cChannels = m_nChannelCount;
  434. ControlDetails.cMultipleItems = 0;
  435. ControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  436. ControlDetails.paDetails = &aDetails[0];
  437. MMRESULT mmResult = mixerGetControlDetails( (HMIXEROBJ)m_dwMixerHandle, &ControlDetails, MIXER_GETCONTROLDETAILSF_VALUE );
  438. DWORD dw = aDetails[0].dwValue;
  439. free( aDetails );
  440. if ( mmResult != MMSYSERR_NOERROR )
  441. {
  442. return BAD_DWORD;
  443. }
  444. return dw;
  445. }
  446. /*------------------------------------------------------------------------------*/
  447. //////////////
  448. void CVolumeInXXX::SetCurrentVolume( DWORD dwValue )
  449. {
  450. if ( !m_bAvailable || (dwValue<m_dwMinimalVolume) || (dwValue>m_dwMaximalVolume) )
  451. return;
  452. MIXERCONTROLDETAILS_UNSIGNED* aDetails = (MIXERCONTROLDETAILS_UNSIGNED*)malloc(m_nChannelCount*sizeof(MIXERCONTROLDETAILS_UNSIGNED));
  453. if ( !aDetails )
  454. return;
  455. for ( int i = 0; i < m_nChannelCount; i++ )
  456. {
  457. aDetails[i].dwValue = dwValue;
  458. }
  459. MIXERCONTROLDETAILS ControlDetails;
  460. memset( &ControlDetails, 0, sizeof(MIXERCONTROLDETAILS) );
  461. ControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  462. ControlDetails.dwControlID = m_dwVolumeControlID;
  463. ControlDetails.cChannels = m_nChannelCount;
  464. ControlDetails.cMultipleItems = 0;
  465. ControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  466. ControlDetails.paDetails = &aDetails[0];
  467. MMRESULT mmResult = mixerSetControlDetails( (HMIXEROBJ)m_dwMixerHandle, &ControlDetails, MIXER_SETCONTROLDETAILSF_VALUE );
  468. free( aDetails );
  469. if ( mmResult != MMSYSERR_NOERROR )
  470. {
  471. }
  472. }
  473. /*------------------------------------------------------------------------------*/
  474. //////////////
  475. void CVolumeInXXX::RegisterNotificationSink( PONMICVOULUMECHANGE pfUserSink, DWORD dwUserValue )
  476. {
  477. m_pfUserSink = pfUserSink;
  478. m_dwUserValue = dwUserValue;
  479. }
  480. /*------------------------------------------------------------------------------*/
  481. ////////////////////////////////////////////////////////////////////////
  482. LRESULT CALLBACK CVolumeInXXX::MixerWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  483. {
  484. if ( uMsg == MM_MIXM_CONTROL_CHANGE )
  485. {
  486. if ( g_pThis )
  487. {
  488. g_pThis->OnControlChanged( (DWORD)lParam );
  489. }
  490. }
  491. return ::DefWindowProc( hwnd, uMsg, wParam, lParam);
  492. }
  493. /*------------------------------------------------------------------------------*/
  494. ////////////////////////////////////////////////////////////////////////
  495. bool CVolumeInXXX::EnumerateInputLines( PINPUTLINEPROC pUserCallback, DWORD dwUserValue )
  496. {
  497. if ( !pUserCallback )
  498. return false;
  499. MMRESULT mmResult;
  500. HWAVEIN hwaveIn;
  501. WAVEFORMATEX WaveFmt;
  502. SetDeviceType( &WaveFmt );
  503. mmResult = waveInOpen( &hwaveIn, WAVE_MAPPER, &WaveFmt, 0L, 0L, CALLBACK_NULL );
  504. if ( mmResult != MMSYSERR_NOERROR )
  505. {
  506. return false;
  507. }
  508. UINT uMixerID;
  509. DWORD dwMixerHandle;
  510. mmResult = mixerGetID( (HMIXEROBJ)hwaveIn, &uMixerID, MIXER_OBJECTF_HWAVEIN );
  511. waveInClose( hwaveIn );
  512. if ( mmResult != MMSYSERR_NOERROR )
  513. {
  514. return false;
  515. }
  516. mmResult = mixerOpen( (LPHMIXER)&dwMixerHandle, uMixerID, 0L, 0L, 0L );
  517. if ( mmResult != MMSYSERR_NOERROR )
  518. {
  519. mixerClose( (HMIXER)dwMixerHandle );
  520. return false;
  521. }
  522. MIXERLINE MixerLine;
  523. memset( &MixerLine, 0, sizeof(MIXERLINE) );
  524. MixerLine.cbStruct = sizeof(MIXERLINE);
  525. MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
  526. mmResult = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
  527. if ( mmResult != MMSYSERR_NOERROR )
  528. {
  529. mixerClose( (HMIXER)dwMixerHandle );
  530. return false;
  531. }
  532. MIXERLINE Line;
  533. for ( UINT uLineIndex = 0; uLineIndex < MixerLine.cConnections; uLineIndex++ )
  534. {
  535. memset( &Line, 0, sizeof(MIXERLINE) );
  536. Line.cbStruct = sizeof(MIXERLINE);
  537. Line.dwDestination = MixerLine.dwDestination;
  538. Line.dwSource = uLineIndex;
  539. mmResult = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &Line, MIXER_GETLINEINFOF_SOURCE );
  540. if ( mmResult != MMSYSERR_NOERROR )
  541. {
  542. mixerClose( (HMIXER)dwMixerHandle );
  543. return false;
  544. }
  545. if ( !((*pUserCallback)( uLineIndex, &Line, dwUserValue )) )
  546. {
  547. break;
  548. }
  549. }
  550. mixerClose( (HMIXER)dwMixerHandle );
  551. return true;
  552. }