一般正常的录音都是对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;
}
{
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;
}
另外,因为混音输出是各种声音的在声卡的输出,所以在录音前最好把所有输入源设为禁音,这样可以保证质量.
并将其它的输出源的音量设为最小(这个是否有影响没有测试)