gusucode.com > 用于语音视频聊天的类源码程序 > 用于语音视频聊天的类源码程序/TAudio.cpp
#include <vcl.h> //--------------------------------------------------------------------------- #pragma hdrstop #include "TAudio.h" #include <algorithm> using namespace std; //--------------------------------------------------------------------------- //void CALLBACK WaveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2); //void CALLBACK WaveOutProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); extern TStrings *DebugMsg; //--------------------------------------------------------------------------- #pragma package(smart_init) TAudio::TAudio(TOnWaveIn OnWaveIn,bool StartIn) { Active=false; FOnWaveIn=OnWaveIn; phIn=NULL; phOut=NULL; for(int i=0;i<WaveBufCount;i++) InWaveHdr[i].lpData=NULL; for(int i=0;i<WaveBufCount;i++) OutWaveHdr[i].lpData=NULL; WaveWnd = AllocateHWnd(WaveInOutProc); //-------------保证以下出错后仍能正常运行 ACMConvert=new TACMConvert; if(!ACMConvert->Init()) { ShowMessage("初始化音频压缩模块失败"); return; } pcmWaveFormat=ACMConvert->pwfPCM; //初始化输入 MMRESULT mmr=waveInOpen(&phIn,WAVE_MAPPER,pcmWaveFormat,(unsigned long)WaveWnd,0,CALLBACK_WINDOW); if(MMSYSERR_NOERROR!=mmr) return; // throw Exception("waveInOpen Error "+IntToStr(mmr)); for(int i=0;i<WaveBufCount;i++) { InWaveHdr[i].lpData=new char[WaveBufLen]; InWaveHdr[i].dwBufferLength=WaveBufLen; InWaveHdr[i].dwBytesRecorded=0; InWaveHdr[i].dwLoops=0; InWaveHdr[i].dwFlags=0; if(MMSYSERR_NOERROR!=waveInPrepareHeader(phIn,&InWaveHdr[i],sizeof(WAVEHDR))) { delete[] InWaveHdr[i].lpData; InWaveHdr[i].lpData=NULL; return; } // throw Exception("waveInPrepareHeader Error "); if(MMSYSERR_NOERROR!=waveInAddBuffer(phIn,&InWaveHdr[i],sizeof(WAVEHDR))) return; // throw Exception("waveInAddBuffer Error "); } UnMute(); //取消静音 SelectMicrophone(); //将麦克风选中为输入设备 SetMaxInVolume(); ////设置最大输入音量 if(StartIn) waveInStart(phIn); //初始化输出 if(MMSYSERR_NOERROR!=waveOutOpen(&phOut,WAVE_MAPPER,pcmWaveFormat,(unsigned long)WaveWnd,0,CALLBACK_WINDOW)!=MMSYSERR_NOERROR) return; // throw Exception("waveOutOpen Error "); for(int i=0;i<WaveBufCount;i++) { OutWaveHdr[i].lpData=new char[WaveBufLen]; OutWaveHdr[i].dwBufferLength=WaveBufLen; OutWaveHdr[i].dwBytesRecorded=0; OutWaveHdr[i].dwLoops=0; OutWaveHdr[i].dwFlags=0; OutWaveHdr[i].dwUser=0; //没有使用的缓冲 if(MMSYSERR_NOERROR!=waveOutPrepareHeader(phOut,&OutWaveHdr[i],sizeof(WAVEHDR))) { delete[] OutWaveHdr[i].lpData; OutWaveHdr[i].lpData=NULL; return; } // throw Exception("waveOutPrepareHeader Error "); } OutIndex=0; // SetOutVolume(0.7); // SetInVolume(0.5); Active=true; } TAudio::~TAudio() { DeallocateHWnd(WaveWnd); if(phIn) { waveInReset(phIn); waveInStop(phIn); for(int i=0;i<WaveBufCount;i++) if(InWaveHdr[i].lpData) { waveInUnprepareHeader(phIn,&InWaveHdr[i],sizeof(WAVEHDR)); delete[] InWaveHdr[i].lpData; } waveInClose(phIn); } if(phOut) {waveOutReset(phOut); waveOutBreakLoop(phOut); for(int i=0;i<WaveBufCount;i++) if(OutWaveHdr[i].lpData) { waveOutUnprepareHeader(phOut,&OutWaveHdr[i],sizeof(WAVEHDR)); delete[] OutWaveHdr[i].lpData; } waveOutClose(phOut); } delete ACMConvert; } void TAudio::SetOutVolume(float Vol) { //声量 0x0000 - 0xFFFF //低 WORD 左声道 如果是单声道以低WORD为准 //高 WORD 右声道 unsigned int Volume=Vol * 0xFFFF; Volume=Volume | (Volume <<16); waveOutSetVolume(phOut,Volume); } void TAudio::OutAddBuffer(BYTE *Data,int DataLen) { //解压 if(!ACMConvert->UnCompress(Data,DataLen)) return; // DebugMsg->Add("解压至 "+IntToStr(ACMConvert->UncmpLen)); //寻找没有使用的缓冲 bool HasBuf=false; int P=-1; for(int i=OutIndex;i<WaveBufCount;i++) if(OutWaveHdr[i].dwUser==0) { P=i; break; } else HasBuf=true; if(P==-1) for(int i=0;i<OutIndex;i++) if(OutWaveHdr[i].dwUser==0) //没有使用的缓冲 { P=i; break; } else HasBuf=true; if(P==-1) //没有空闲缓冲就冲掉最老的 { P=OutIndex; OutIndex++; if(OutIndex >= WaveBufCount) OutIndex=0; waveOutWrite(phOut,&OutWaveHdr[OutIndex],sizeof(WAVEHDR)); } WAVEHDR *pWaveHdr=&OutWaveHdr[P]; pWaveHdr->dwUser=1; pWaveHdr->dwBufferLength=min(ACMConvert->UncmpLen,WaveBufLen); memcpy(pWaveHdr->lpData,ACMConvert->UncmpData,pWaveHdr->dwBufferLength); if(!HasBuf) //没有播放缓冲 { OutIndex=P; waveOutWrite(phOut,&OutWaveHdr[OutIndex],sizeof(WAVEHDR)); } } /* 由于在 delete Audio; 时不能完全停止 WaveInProc 所以改用 CALLBACK_WINDOW void CALLBACK WaveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2) { if(uMsg!=MM_WIM_DATA) return; TAudio *Audio=(TAudio *)dwInstance; LPWAVEHDR pWaveHdr=LPWAVEHDR(dwParam1); int DataLen=pWaveHdr->dwBytesRecorded; if(Audio->ACMConvert->Compress(pWaveHdr->lpData,DataLen)) {// DebugMsg->Add(IntToStr(pWaveHdr->dwBytesRecorded)+" 压缩至 "+IntToStr(DataLen)); Audio->FOnWaveIn(pWaveHdr->lpData,DataLen); } waveInAddBuffer(Audio->phIn,pWaveHdr,sizeof(WAVEHDR)); } void CALLBACK WaveOutProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { if(uMsg!=WOM_DONE) return; TAudio *Audio=(TAudio *)dwInstance; LPWAVEHDR pWaveHdr=LPWAVEHDR(dwParam1); pWaveHdr->dwUser=0; int P=-1; for(int i=Audio->OutIndex+1;i<WaveBufCount;i++) if(Audio->OutWaveHdr[i].dwUser==1) { P=i; break; } if(P==-1) for(int i=0;i<Audio->OutIndex;i++) if(Audio->OutWaveHdr[i].dwUser==1) { P=i; break; } if(P!=-1) { Audio->OutIndex=P; waveOutWrite(Audio->phOut,&Audio->OutWaveHdr[Audio->OutIndex],sizeof(WAVEHDR)); } } */ void TAudio::StopIn() { waveInStop(phIn); } void __fastcall TAudio::WaveInOutProc(TMessage &Message) { LPWAVEHDR pWaveHdr=LPWAVEHDR(Message.LParam); switch(Message.Msg) { case MM_WIM_DATA : //输入 { int DataLen=pWaveHdr->dwBytesRecorded; if(ACMConvert->Compress(pWaveHdr->lpData,DataLen)) FOnWaveIn(pWaveHdr->lpData,DataLen); waveInAddBuffer(phIn,pWaveHdr,sizeof(WAVEHDR)); } break; case WOM_DONE : //输出 { pWaveHdr->dwUser=0; int P=-1; for(int i=OutIndex+1;i<WaveBufCount;i++) if(OutWaveHdr[i].dwUser==1) { P=i; break; } if(P==-1) for(int i=0;i<OutIndex;i++) if(OutWaveHdr[i].dwUser==1) { P=i; break; } if(P!=-1) { OutIndex=P; waveOutWrite(phOut,&OutWaveHdr[OutIndex],sizeof(WAVEHDR)); } } break; } } //取消静音 void UnMute() { // Open the mixer device HMIXER hmx; mixerOpen(&hmx, 0, 0, 0, 0); // Get the line info for the wave in destination line MIXERLINE mxl; mxl.cbStruct = sizeof(mxl); mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; mixerGetLineInfo((HMIXEROBJ)hmx, &mxl,MIXER_GETLINEINFOF_COMPONENTTYPE); // Now find the microphone source line connected to this wave in // destination DWORD cConnections = mxl.cConnections; for(DWORD j=0; j<cConnections; j++) { mxl.dwSource = j; mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_SOURCE); if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == mxl.dwComponentType) break; } // Find a mute control, if any, of the microphone line // LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL)malloc(sizeof(MIXERCONTROL)); // MIXERLINECONTROLS mxlctrl = {sizeof(MIXERLINECONTROLS), mxl.dwLineID, // MIXERCONTROL_CONTROLTYPE_MUTE, 1, sizeof(MIXERCONTROL), pmxctrl}; MIXERCONTROL mxctrl; MIXERLINECONTROLS mxlctrls; mxlctrls.cbStruct = sizeof(MIXERLINECONTROLS); mxlctrls.dwLineID = mxl.dwLineID; mxlctrls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mxlctrls.cControls = 1; mxlctrls.cbmxctrl = sizeof(MIXERCONTROL); mxlctrls.pamxctrl = &mxctrl; if(!mixerGetLineControls((HMIXEROBJ) hmx, &mxlctrls,MIXER_GETLINECONTROLSF_ONEBYTYPE)) { // Found, so proceed DWORD cChannels = mxl.cChannels; if (MIXERCONTROL_CONTROLF_UNIFORM & mxctrl.fdwControl) cChannels = 1; LPMIXERCONTROLDETAILS_BOOLEAN pbool =(LPMIXERCONTROLDETAILS_BOOLEAN) malloc(cChannels * sizeof(MIXERCONTROLDETAILS_BOOLEAN)); MIXERCONTROLDETAILS mxcd = {sizeof(mxcd), mxctrl.dwControlID,cChannels, (HWND)0, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID) pbool}; mixerGetControlDetails((HMIXEROBJ)hmx, &mxcd,MIXER_SETCONTROLDETAILSF_VALUE); // Unmute the microphone line (for both channels) pbool[0].fValue = pbool[cChannels - 1].fValue = 0; mixerSetControlDetails((HMIXEROBJ)hmx, &mxcd,MIXER_SETCONTROLDETAILSF_VALUE); free(pbool); } mixerClose(hmx); } //将麦克风选中为输入设备 void SelectMicrophone() { // Open the mixer device HMIXER hmx; mixerOpen(&hmx, 0, 0, 0, 0); // Get the line info for the wave in destination line MIXERLINE mxl; mxl.cbStruct = sizeof(mxl); mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; mixerGetLineInfo((HMIXEROBJ)hmx, &mxl,MIXER_GETLINEINFOF_COMPONENTTYPE); // Find a LIST control, if any, for the wave in line LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL)malloc(mxl.cControls * sizeof(MIXERCONTROL)); MIXERLINECONTROLS mxlctrl = {sizeof mxlctrl, mxl.dwLineID, 0, mxl.cControls, sizeof(MIXERCONTROL), pmxctrl}; mixerGetLineControls((HMIXEROBJ) hmx, &mxlctrl,MIXER_GETLINECONTROLSF_ALL); // Now walk through each control to find a type of LIST control. This // can be either Mux, Single-select, Mixer or Multiple-select. DWORD i; for(i=0; i < mxl.cControls; i++) if (MIXERCONTROL_CT_CLASS_LIST == (pmxctrl[i].dwControlType & MIXERCONTROL_CT_CLASS_MASK)) break; if (i < mxl.cControls) { // Found a LIST control // Check if the LIST control is a Mux or Single-select type BOOL bOneItemOnly = FALSE; switch (pmxctrl[i].dwControlType) { case MIXERCONTROL_CONTROLTYPE_MUX: case MIXERCONTROL_CONTROLTYPE_SINGLESELECT: bOneItemOnly = TRUE; } DWORD cChannels = mxl.cChannels, cMultipleItems = 0; if (MIXERCONTROL_CONTROLF_UNIFORM & pmxctrl[i].fdwControl) cChannels = 1; if (MIXERCONTROL_CONTROLF_MULTIPLE & pmxctrl[i].fdwControl) cMultipleItems = pmxctrl[i].cMultipleItems; // Get the text description of each item LPMIXERCONTROLDETAILS_LISTTEXT plisttext =(LPMIXERCONTROLDETAILS_LISTTEXT) malloc(cChannels * cMultipleItems * sizeof(MIXERCONTROLDETAILS_LISTTEXT)); MIXERCONTROLDETAILS mxcd = {sizeof(mxcd), pmxctrl[i].dwControlID,cChannels, (HWND)cMultipleItems, sizeof(MIXERCONTROLDETAILS_LISTTEXT),(LPVOID) plisttext}; mixerGetControlDetails((HMIXEROBJ)hmx, &mxcd,MIXER_GETCONTROLDETAILSF_LISTTEXT); // Now get the value for each item LPMIXERCONTROLDETAILS_BOOLEAN plistbool =(LPMIXERCONTROLDETAILS_BOOLEAN) malloc(cChannels * cMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN)); mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); mxcd.paDetails = plistbool; mixerGetControlDetails((HMIXEROBJ)hmx, &mxcd,MIXER_GETCONTROLDETAILSF_VALUE); // Select the "Microphone" item for (DWORD j=0; j<cMultipleItems; j = j + cChannels) if (0 == strcmp(plisttext[j].szName, "Microphone")) // Select it for both left and right channels plistbool[j].fValue = plistbool[j+ cChannels - 1].fValue = 1; else if (bOneItemOnly) // Mux or Single-select allows only one item to be selected // so clear other items as necessary plistbool[j].fValue = plistbool[j+ cChannels - 1].fValue = 0; // Now actually set the new values in mixerSetControlDetails((HMIXEROBJ)hmx, &mxcd,MIXER_GETCONTROLDETAILSF_VALUE); free(pmxctrl); free(plisttext); free(plistbool); } else free(pmxctrl); mixerClose(hmx); } //设置最大输入音量 void SetMaxInVolume() { // Open the mixer device HMIXER hmx; mixerOpen(&hmx, 0, 0, 0, 0); // Get the line info for the wave in destination line MIXERLINE mxl; mxl.cbStruct = sizeof(mxl); mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; mixerGetLineInfo((HMIXEROBJ)hmx, &mxl,MIXER_GETLINEINFOF_COMPONENTTYPE); // Now find the microphone source line connected to this wave in // destination DWORD cConnections = mxl.cConnections; for(DWORD j=0; j<cConnections; j++) { mxl.dwSource = j; mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_SOURCE); if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == mxl.dwComponentType) break; } // Find a volume control, if any, of the microphone line // LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL)malloc(sizeof(MIXERCONTROL)); // MIXERLINECONTROLS mxlctrl = {sizeof mxlctrl, mxl.dwLineID, // MIXERCONTROL_CONTROLTYPE_VOLUME, 1, sizeof(MIXERCONTROL), pmxctrl}; MIXERCONTROL mxctrl; MIXERLINECONTROLS mxlctrls; mxlctrls.cbStruct = sizeof(MIXERLINECONTROLS); mxlctrls.dwLineID = mxl.dwLineID; mxlctrls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mxlctrls.cControls = 1; mxlctrls.cbmxctrl = sizeof(MIXERCONTROL); mxlctrls.pamxctrl = &mxctrl; if(!mixerGetLineControls((HMIXEROBJ) hmx, &mxlctrls,MIXER_GETLINECONTROLSF_ONEBYTYPE)) { // Found! DWORD cChannels = mxl.cChannels; if (MIXERCONTROL_CONTROLF_UNIFORM & mxctrl.fdwControl) cChannels = 1; LPMIXERCONTROLDETAILS_UNSIGNED pUnsigned =(LPMIXERCONTROLDETAILS_UNSIGNED) malloc(cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED)); MIXERCONTROLDETAILS mxcd = {sizeof(mxcd), mxctrl.dwControlID,cChannels, (HWND)0, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID) pUnsigned}; mixerGetControlDetails((HMIXEROBJ)hmx, &mxcd,MIXER_SETCONTROLDETAILSF_VALUE); // Set the volume to the middle (for both channels as needed) pUnsigned[0].dwValue = pUnsigned[cChannels - 1].dwValue =mxctrl.Bounds.dwMaximum; // pmxctrl->Bounds.dwMinimum+(pmxctrl->Bounds.dwMaximum-pmxctrl->Bounds.dwMinimum)*Vol; mixerSetControlDetails((HMIXEROBJ)hmx, &mxcd,MIXER_SETCONTROLDETAILSF_VALUE); free(pUnsigned); } mixerClose(hmx); }