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);
}