一般正常的录音都是对Line in进行录音,但有些需要对line out进行录音,因为有些音源是没有line in的.

如即时聊天时录取对方的语音.但这些声音都要经过声卡播放,所以它们的音源就是声卡输出.

对于声卡的输出,在录音控制中有两个,就是Mono mix和stereo mix,当然最好是选取stereo mix,效果更好.

编程打开录音设备时,是不能控制打开的设备上的输出还是输入通道的.所在要在打开设备前就设置好.

用Mixer的API可以对输出源进行牧举,不过不同的设备的定义不同,对于SoundMax能够正确区别stereo mix和Mono mix的区别,但AC 97的声卡它都是相似的输出源.所以只能多次地优选列举.代码逻辑不是完美,但只能如此.否则只能让用户手工选择,致少要按6次mouse才行.

以下是自己定义的结构,在第一次没有找到正确的输出源时将所有输出源保的index,swLineID和name保存起来.下面

在结构数组中再优选,而不要再对设备牧举

typedef struct ControlEnumData{
INT index;
CString szName;
DWORD dwLineID;
} CTRL_ENUM_DATA,* LPCTRL_ENUM_DATA; 
BOOLXXX::setOutSource()
{
UINTdeviceCount 
  = 
  ::mixerGetNumDevs();
 
  if 
  (deviceCount 
  < 
   
  1 
  ){
MessageBox( 
  " 
  没有找到混音设备,请手工选择录音设备的混音输出! 
  " 
  );
 
  return 
  TRUE;
}
BOOLfound 
  = 
  FALSE;

 
  for 
  (UINTdevID 
  = 
   
  0 
  ;devID 
  < 
  deviceCount;devID 
  ++ 
  ){
HMIXERhMixer;
 
  if 
  (mixerOpen( 
  & 
  hMixer,devID,(DWORD) 
  0 
  , 
  0 
  ,CALLBACK_WINDOW) 
  != 
  MMSYSERR_NOERROR)
{
::mixerClose(hMixer);
 
  continue 
  ;
}

MIXERLINEline;
line.cbStruct 
  = 
   
  sizeof 
  (MIXERLINE);
 
  // 
  注意我们要查找的是录音设备.所以dwComponentType应该是MIXERLINE_COMPONENTTYPE_DST_WAVEIN; 
  
 
  line.dwComponentType 
  = 
  MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
 
  if 
  (::mixerGetLineInfo((HMIXEROBJ)hMixer, 
  & 
  line,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETLINEINFOF_COMPONENTTYPE) 
  != 
  MMSYSERR_NOERROR){
::mixerClose(hMixer);
 
  continue 
  ;
}
DWORDdwConnections 
  = 
  line.cConnections;
DWORDselectIndex 
  = 
   
  - 
  1 
  ;
MIXERLINElinesub;
CTRL_ENUM_DATAenumData[ 
  8 
  ];
 
  int 
  enumIndex 
  = 
   
  0 
  ;
 
  // 
  然后看录音设备上共一连结了几个线路 
  
 
   
  for 
  (DWORDi 
  = 
   
  0 
  ;i 
  < 
  dwConnections;i 
  ++ 
  ){ 
  // 
  列举出波形混音输出源的索引并最声音设置最大 
  
 
  linesub.cbStruct 
  = 
   
  sizeof 
  (MIXERLINE);
linesub.dwSource 
  = 
  i;
linesub.dwDestination 
  = 
  line.dwDestination;
 
  if 
  (mixerGetLineInfo((HMIXEROBJ)hMixer, 
  & 
  linesub,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETLINEINFOF_SOURCE) 
  == 
  MMSYSERR_NOERROR){
 
  if 
  (linesub.dwComponentType 
  == 
  MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
 
  // 
  这是stereomix的输出源.如果能明确找到,就直接定位到这个index上.
 
  // 
  并把这个设备选中并把音量调到最大. 
  
 
  MIXERCONTROLmxc;
MIXERLINECONTROLSmxlc;
mxlc.cbStruct 
  = 
   
  sizeof 
  (MIXERLINECONTROLS);
mxlc.dwLineID 
  = 
  linesub.dwLineID;
DWORDdwControlType 
  = 
  MIXERCONTROL_CONTROLTYPE_VOLUME;
mxlc.dwControlType 
  = 
  dwControlType;
mxlc.cControls 
  = 
   
  1 
  ;
mxlc.cbmxctrl 
  = 
   
  sizeof 
  (MIXERCONTROL);
mxlc.pamxctrl 
  = 
   
  & 
  mxc;
 
  if 
  (::mixerGetLineControls((HMIXEROBJ)hMixer, 
  & 
  mxlc,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETLINECONTROLSF_ONEBYTYPE) 
  == 
  MMSYSERR_NOERROR)
{
MIXERCONTROLDETAILS_BOOLEANvumVal[ 
  1 
  ];
MIXERCONTROLDETAILSmxcd;
mxcd.cbStruct 
  = 
   
  sizeof 
  (MIXERCONTROLDETAILS);
mxcd.dwControlID 
  = 
  mxc.dwControlID; 
  // 
  在上面的&mxc得到 
  
 
  mxcd.cChannels 
  = 
   
  1 
  ;
mxcd.cMultipleItems 
  = 
  mxc.cMultipleItems;
mxcd.cbDetails 
  = 
   
  sizeof 
  (MIXERCONTROLDETAILS_BOOLEAN);
mxcd.paDetails 
  = 
   
  & 
  vumVal;
vumVal[ 
  0 
  ].fValue 
  = 
   
  65535 
  ;
 
  // 
  将设备音量调至最大 
  
 
  ::mixerSetControlDetails((HMIXEROBJ)hMixer, 
  & 
  mxcd,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETCONTROLDETAILSF_VALUE);

}
selectIndex 
  = 
  i;
 
  break 
  ; 
  // 
  如果明确找到MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT,break出去 
  
 
  
}
 
  if 
  (linesub.dwComponentType 
  == 
  MIXERLINE_COMPONENTTYPE_SRC_ANALOG){
 
  // 
  否则是MIXERLINE_COMPONENTTYPE_SRC_ANALOG的话,保存起来 
  
 
  enumData[enumIndex].index 
  = 
  i;
enumData[enumIndex].dwLineID 
  = 
  linesub.dwLineID;
enumData[enumIndex 
  ++ 
  ].szName 
  = 
  linesub.szName;
}
}

}
 
  if 
  (selectIndex 
  == 
   
  - 
  1 
   
  && 
  enumIndex 
  < 
   
  1 
  ){
::mixerClose(hMixer);
 
  continue 
  ;
}
 
  // 
  既没有找到MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT,也没有找到MIXERLINE_COMPONENTTYPE_SRC_ANALOG. 
  
 
  
DWORDvumLineID; 
  // 
  这个ID用于下面对找到的输出源调整音量 
  
 
   
  if 
  (selectIndex 
  == 
   
  - 
  1 
  ){ 
  // 
  第一次优选,找STEREO 
  
 
   
  for 
  (INTi 
  = 
   
  0 
  ;i 
  < 
  enumIndex;i 
  ++ 
  ){
 
  if 
  (enumData[i].szName.MakeUpper().Find( 
  " 
  STEREO 
  " 
  ) 
  != 
   
  - 
  1 
  )
{
selectIndex 
  = 
  enumData[i].index;
vumLineID 
  = 
  enumData[i].dwLineID;
 
  break 
  ;
}
}
}
 
  if 
  (selectIndex 
  == 
   
  - 
  1 
  ){ 
  // 
  第二次优选,找MONO 
  
 
   
  for 
  (INTi 
  = 
   
  0 
  ;i 
  < 
  enumIndex;i 
  ++ 
  ){
 
  if 
  (enumData[i].szName.MakeUpper().Find( 
  " 
  MONO 
  " 
  ) 
  != 
   
  - 
  1 
  )
{
selectIndex 
  = 
  enumData[i].index;
vumLineID 
  = 
  enumData[i].dwLineID;
 
  break 
  ;
}
}
}
 
  if 
  (selectIndex 
  == 
   
  - 
  1 
  ){ 
  // 
  都没有就默认第一个吧.无奈:( 
  
 
  selectIndex 
  = 
  enumData[ 
  0 
  ].index;
vumLineID 
  = 
  enumData[ 
  0 
  ].dwLineID;
}
 
  if 
  (selectIndex 
  != 
   
  - 
  1 
  ){
 
  // 
  此时肯selectIndex肯定不为-1,加了条件是想在{}内重新定义mxc,mxlc
 
  // 
  来设置音量,不和下面的设置选中的mxc,mxlc冲突. 
  
 
  MIXERCONTROLmxc;
MIXERLINECONTROLSmxlc;
mxlc.cbStruct 
  = 
   
  sizeof 
  (MIXERLINECONTROLS);
mxlc.dwLineID 
  = 
  vumLineID;
DWORDdwControlType 
  = 
  MIXERCONTROL_CONTROLTYPE_VOLUME;
mxlc.dwControlType 
  = 
  dwControlType;
mxlc.cControls 
  = 
   
  1 
  ;
mxlc.cbmxctrl 
  = 
   
  sizeof 
  (MIXERCONTROL);
mxlc.pamxctrl 
  = 
   
  & 
  mxc;
 
  if 
  (::mixerGetLineControls((HMIXEROBJ)hMixer, 
  & 
  mxlc,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETLINECONTROLSF_ONEBYTYPE) 
  == 
  MMSYSERR_NOERROR)
{
MIXERCONTROLDETAILS_BOOLEANvumVal[ 
  1 
  ];
MIXERCONTROLDETAILSmxcd;
mxcd.cbStruct 
  = 
   
  sizeof 
  (MIXERCONTROLDETAILS);
mxcd.dwControlID 
  = 
  mxc.dwControlID;
mxcd.cChannels 
  = 
   
  1 
  ;
mxcd.cMultipleItems 
  = 
  mxc.cMultipleItems;
mxcd.cbDetails 
  = 
   
  sizeof 
  (MIXERCONTROLDETAILS_BOOLEAN);
mxcd.paDetails 
  = 
   
  & 
  vumVal;
vumVal[ 
  0 
  ].fValue 
  = 
   
  65535 
  ;
 
  // 
  将设备音量调至最大 
  
 
  ::mixerSetControlDetails((HMIXEROBJ)hMixer, 
  & 
  mxcd,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETCONTROLDETAILSF_VALUE);

}


}
 
  // 
  得到了index就能正确定位该输出源并将其选中,这里调试的时候浪费了很多时间,网上的代码
 
  // 
  都是错误的.录音通道列表和控制面板中录音属性中顺序是一致的,但是对其选中的控件的顺序
 
  // 
  是反的.这一点我调试了好久才发现,没有找到相关文档.KAO,网上可以看到的文章都是错误的. 
  
 
  
found 
  = 
  TRUE;
MIXERCONTROLmxc;
MIXERLINECONTROLSmxlc;
mxlc.cbStruct 
  = 
   
  sizeof 
  (MIXERLINECONTROLS);
mxlc.dwLineID 
  = 
  line.dwLineID;
 
  // 
  注意不能在linesub中查找MIXERCONTROL_CONTROLTYPE_MUX,因为"选中"互斥控件是整个录音控制的
 
  // 
  即MIXERLINE_COMPONENTTYPE_DST_WAVEIN,不是波形混音输出源所独有的. 
  
 
  DWORDdwControlType 
  = 
  MIXERCONTROL_CONTROLTYPE_MUX;
mxlc.dwControlType 
  = 
  dwControlType;
mxlc.cControls 
  = 
   
  1 
  ;
mxlc.cbmxctrl 
  = 
   
  sizeof 
  (MIXERCONTROL);
mxlc.pamxctrl 
  = 
   
  & 
  mxc;
 
  if 
  (::mixerGetLineControls((HMIXEROBJ)hMixer, 
  & 
  mxlc,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETLINECONTROLSF_ONEBYTYPE) 
  != 
  MMSYSERR_NOERROR)
{
MessageBox( 
  " 
  无法选中波形混音输出,请从控制面板中手工设置混音输出为录音设备! 
  " 
  );
 
  return 
  FALSE;
}

MIXERCONTROLDETAILS_BOOLEANvumVal[ 
  8 
  ];
MIXERCONTROLDETAILSmxcd;
mxcd.cbStruct 
  = 
   
  sizeof 
  (MIXERCONTROLDETAILS);
mxcd.dwControlID 
  = 
  mxc.dwControlID;
mxcd.cChannels 
  = 
   
  1 
  ;
mxcd.cMultipleItems 
  = 
  mxc.cMultipleItems;
mxcd.cbDetails 
  = 
   
  sizeof 
  (MIXERCONTROLDETAILS_BOOLEAN);
mxcd.paDetails 
  =& 
  vumVal;

 
  if 
  (::mixerGetControlDetails((HMIXEROBJ)hMixer, 
  & 
  mxcd,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETCONTROLDETAILSF_VALUE) 
  != 
  MMSYSERR_NOERROR)
{
MessageBox( 
  " 
  无法选中波形混音输出,请从控制面板中手工设置混音输出为录音设备! 
  " 
  );
 
  return 
  FALSE;
}
 
  for 
  (DWORDi 
  = 
   
  0 
  ;i 
  < 
  mxc.cMultipleItems;i 
  ++ 
  ){
vumVal[i].fValue 
  = 
  FALSE;
}
vumVal[mxc.cMultipleItems 
  - 
  selectIndex 
  - 
  1 
  ].fValue 
  = 
  TRUE;
 
  // 
  选中控件顺序和设备顺序相反,TMD 
  
 
   
  if 
  (::mixerSetControlDetails((HMIXEROBJ)hMixer, 
  & 
  mxcd,
MIXER_OBJECTF_HMIXER 
  | 
  MIXER_GETCONTROLDETAILSF_VALUE) 
  != 
  MMSYSERR_NOERROR)
{
MessageBox( 
  " 
  无法选中波形混音输出,请从控制面板中手工设置混音输出为录音设备! 
  " 
  );
 
  return 
  FALSE;
}
::mixerClose(hMixer);
 
  break 
  ; 
  // 
  如果到这里已经正确设置了,break出去. 
  
 
  }
 
  if 
  ( 
  ! 
  found){
MessageBox( 
  " 
  没有找到录音设备的混音输出,请手工选择! 
  " 
  );
}
 
  return 
  TRUE;
}


另外,因为混音输出是各种声音的在声卡的输出,所以在录音前最好把所有输入源设为禁音,这样可以保证质量.

并将其它的输出源的音量设为最小(这个是否有影响没有测试)