Computer(IT)/Visual Studio C++

fatal error LNK1170 Release Mode(Solve)

약탄치킨 2009. 2. 25. 19:34
반응형

wavein_simple.h,wavein_simple.cpp

wavein_simple.h
//---------------------------------------------------------------------


#ifndef ___WAVEIN_SIMPLE_H_INCLUDED___
#define ___WAVEIN_SIMPLE_H_INCLUDED___

#include <windows.h>
#include <mmsystem.h>
#include <vector>
#include "sync_simple.h"

using namespace std;

#define WAIT_SIG 0
#define CONTINUE_SIG 1
#define EXIT_SIG 2

 

//---------------------------- CLASS -------------------------------------------------------------

// See CWaveINSimple::Start(IReceiver *pReceiver) below.
// Instances of any class extending "IReceiver" will be able to receive raw (PCM)
// sound from an instance of the CWaveINSimple and process sound via own
// implementation of the "ReceiveBuffer" method.

class IReceiver {
public:
 virtual void ReceiveBuffer(LPSTR lpData, DWORD dwBytesRecorded) = 0;
};

class CWaveINSimple;
class CMixer;
///////////////////////////////////////////////////////////////////////////
// Implementation of the Mixer's Line
class CMixerLine {
 friend class CMixer;
private:
 MIXERLINE m_MxLine;
 HMIXER m_MixerHandle;

 // Yep, constructor and destructor are declared private, since only
 // instances of CMixer can create CMixerLine objects (one CMixer can
 // have zero or more CMixerLine's, see CMixer::GetLines()).
 //
 // Constructor receives parameters exactly as they are passed by
 // the CMixer::InitLines(), which is called from CMixer::Open().
 // In fact, MixerHandle and pMxLine (pointer to MIXERLINE which
 // is filled in CMixer::InitLines()) is all we need to know about
 // Mixer's Line.
 CMixerLine(HMIXER MixerHandle, MIXERLINE *pMxLine);
 ~CMixerLine() {};

public:

 // Raturns Line's name.
 const TCHAR *GetName() const { return this->m_MxLine.szName; };

 // Un-mute Mixer's Line. I would say this is a useless method, since
 // Mixer Lines for the WaveIN devices doesn't support Mute/Un-mute, at
 // least for the sounds cards I worked with. However, according to
 // http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B159753,
 // there may be such sound cards. Anyway, method doesn't throw any
 // exceptions or return any errors if Mixer's Line doesn't support un-muting.
 void UnMute();

 // Set the Mixer's Line volume, nVolumeLevel should be within 0..100,
 // think of it like a percentage.
 void SetVolume(UINT nVolumeLevel);

 // Select the Mixer's Line. Regarding recording Lines, Method selects
 // Mixer's Line as a recording source (same as we would do this
 // manually via "sndvol32 /r").
 // Code for this method is almost an exact copy from the
 // http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B159753.
 // I couldn't find a better one.
 void Select();
};
///////////////////////////////////////////////////////////////////////////
// Implementation of the Mixer. Via Mixer we have access to Mixer's Lines.
// Another particularity is that each Wave device (WaveIN in this case) has
// one Mixer.
class CMixer {
 friend class CWaveINSimple;

private:
 UINT m_nWaveDeviceID;

 // Handle to Mixer for WAVE Device
 HMIXER m_MixerHandle;
 QMutex m_qLocalMutex;

 // Here where Mixer keeps its Lines.
 vector<CMixerLine*> m_arrMixerLines;

 // Naturally, Mixer is a property of the Wave device (WaveIN in this case).
 // So, we can't just create (and destroy) a CMixer object from nothing (well,
 // we can but this design says so). See CWaveINSimple::OpenMixer() for more details.
 CMixer(UINT nWaveDeviceID);
 ~CMixer();

 // This method initialize Mixer's Lines collection. It is
 // called within CMixer::Open().
 void InitLines();

public:
 // Opens the MIXER and call CMixer::InitLines().
 void Open();

 // Method closes the MIXER and clears the Lines collection.
 void Close();

 // Returns MIXER's WAVEIN lines,
 // call Open() before requesting lines (except cases when
 // Mixer is returned by CWaveINSimple::OpenMixer()).
 const vector<CMixerLine*>& GetLines() { return this->m_arrMixerLines; };

 // Returns a MIXER's WAVEIN line by line's name,
 // call Open() before requesting the line (except cases when
 // Mixer is returned by CWaveINSimple::OpenMixer()).
 CMixerLine& GetLine(const TCHAR *pLineName);
};
///////////////////////////////////////////////////////////////////////////
// Via implementation of the CWaveINSimple we get access to the WaveIN
// devices, or, better saying, to the Wave input devices (capable of
// recording sound).
class CWaveINSimple {
private:
 // Static collection where all WaveIN devices, present in the system,
 // are saved. See CWaveINSimple::GetDevices()
 static vector<CWaveINSimple*> m_arrWaveINDevices;
 static QMutex m_qGlobalMutex;
 static volatile bool m_isDeviceListLoaded;

 // This is thread's routine procedure to which the Windows Low Level
 // WAVE API passes messages regarding digital audio recording (such
 // as MM_WIM_DATA, MM_WIM_OPEN, and MM_WIM_CLOSE). Mind that "waveInOpen"
 // (in CWaveINSimple::_Start()) is called with CALLBACK_THREAD.
 // Thread is created inside the CWaveINSimple::_Start() call.
 static DWORD WINAPI waveInProc(LPVOID arg);

 // WaveIN Device's ID. It is used as input parameter for the CMixer
 // constructor. With this ID we can access Mixer without actually opening
 // the WaveIN device.
 UINT m_nWaveDeviceID;

 // Structure to keep basic details about WaveIN device.
 WAVEINCAPS m_wic;

 // Handle to the WaveIN Device.
 HWAVEIN m_WaveInHandle;

 // Structure to keep sound's quality settings. Also used when opening WaveIN
 // device, see CWaveINSimple::_Start().
 WAVEFORMATEX m_waveFormat;

 // Two WAVEHDR's are used for recording (ie, double-buffering).
 WAVEHDR m_WaveHeader[2];

 // Pointer to a IReceiver object, passed via
 // CWaveINSimple::Start(IReceiver *pReceiver), that will be responsible for
 // further processing of the sound data.
 IReceiver *m_Receiver;
 CMixer m_Mixer;
 QMutex m_qLocalMutex;

 // These class' attributes are used for communication with thread's routine.
 volatile int m_SIG;
 volatile unsigned char m_BuffersDone;

 // Constructor and destructor are declared private (due design). So, there
 // is no way to instantiate CWaveINSimple objects directly. To obtain a
 // CWaveINSimple object, use CWaveINSimple::GetDevices() or CWaveINSimple::GetDevice()
 // (see below). CWaveINSimple objects are destroyed via CWaveINSimple::CleanUp().
 CWaveINSimple(UINT nWaveDeviceID, WAVEINCAPS *pWIC);
 ~CWaveINSimple();

 void Close(int iLevel);

 // This method starts recording sound from the WaveIN device. Passed object (derivate from
 // IReceiver) will be responsible for further processing of the sound data.
 void _Start(IReceiver *pReceiver);

 // This method stops recording.
 void _Stop();

public:
 // This static method returns a collection of the WaveIN devices (capable of recording),
 // present in the system.
 static const vector<CWaveINSimple*>& GetDevices();

 // This static method returns an instance of the WaveIN device (capable of recording),
 // by device's name.
 static CWaveINSimple& GetDevice(const TCHAR *pDeviceName);

 // This static method performs general clean-up, once everything is finished and
 // no more recording is needed.
 static void CleanUp();

 // Wrapper of the _Start() method, for the multithreading version.
 // This is the actual starter.
 void Start(IReceiver *pReceiver);

 // Wrapper of the _Stop() method, for the multithreading version
 // This is the actual stopper.
 void Stop();

 // Returns name of the Device
 const TCHAR *GetName() const { return this->m_wic.szPname; };

 // This method returns and opens Mixer associated with the Device.
 CMixer& OpenMixer();


};

//---------------------------- IMPLEMENTATION ----------------------------------------------------

vector<CWaveINSimple*> CWaveINSimple::m_arrWaveINDevices;
QMutex CWaveINSimple::m_qGlobalMutex;
volatile bool CWaveINSimple::m_isDeviceListLoaded = false;

#endif

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


wavein_simple.cpp
//--------------------------------------------------------------------
#include "stdafx.h"
#include "wavein_simple.h"


///////////////////////////////////////////////////////////////////////////
CMixerLine::CMixerLine(HMIXER MixerHandle, MIXERLINE *pMxLine) {
 m_MixerHandle = MixerHandle;
 memcpy(&m_MxLine, pMxLine, sizeof(MIXERLINE));
}

void CMixerLine::Select() {
 DWORD i;
 MIXERLINE mxl;
 BOOL bOneItemOnly = FALSE;

 // Get the line info for the WaveIN destination line
 mxl.cbStruct = sizeof(MIXERLINE);
 mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
 mixerGetLineInfo((HMIXEROBJ) this->m_MixerHandle, &mxl, MIXER_GETLINEINFOF_COMPONENTTYPE);

 // Find a LIST control, if any, for the WaveIN line
 LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL) malloc(mxl.cControls * sizeof(MIXERCONTROL));
 if (pmxctrl != NULL) {
  MIXERLINECONTROLS mxlctrl = {sizeof(mxlctrl), mxl.dwLineID, 0, mxl.cControls,
   sizeof(MIXERCONTROL), pmxctrl};
  mixerGetLineControls((HMIXEROBJ)m_MixerHandle, &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.
  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
   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));

   if (plisttext != NULL) {
    MIXERCONTROLDETAILS mxcd = {sizeof(mxcd), pmxctrl[i].dwControlID,
     cChannels, (HWND)cMultipleItems, sizeof(MIXERCONTROLDETAILS_LISTTEXT), (LPVOID) plisttext};
    mixerGetControlDetails((HMIXEROBJ) this->m_MixerHandle, &mxcd, MIXER_GETCONTROLDETAILSF_LISTTEXT);

    // Now get the value for each item
    LPMIXERCONTROLDETAILS_BOOLEAN plistbool = (LPMIXERCONTROLDETAILS_BOOLEAN)
     malloc(cChannels * cMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN));

    if (plistbool != NULL) {

     mxcd.cbDetails = sizeof MIXERCONTROLDETAILS_BOOLEAN;
     mxcd.paDetails = plistbool;
     mixerGetControlDetails((HMIXEROBJ)m_MixerHandle, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE);

     // Select the class' line item
     for (DWORD j=0; j<cMultipleItems; j = j + cChannels) {
      if (0 == lstrcmp(plisttext[j].szName, GetName())) {
       // 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)m_MixerHandle, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE);

     free(plistbool);
    }

    free(plisttext);
   }
  }

  free(pmxctrl);
 }
}

void CMixerLine::SetVolume(UINT nVolumeLevel) {
 MIXERCONTROL MxCtrl;
 MIXERLINECONTROLS MxLCtrl;
 MIXERCONTROLDETAILS_UNSIGNED uValue[2];
 MIXERCONTROLDETAILS MxControlDetails;
 UINT nVolume;

 if (nVolumeLevel > 100) nVolume = 100;
 else nVolume = nVolumeLevel;

 // Find volume control, if any, of the line
 MxLCtrl.cbStruct = sizeof(MIXERLINECONTROLS);
 MxLCtrl.dwLineID = m_MxLine.dwLineID;
 MxLCtrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
 MxLCtrl.cControls = 1;
 MxLCtrl.cbmxctrl = sizeof(MIXERCONTROL);
 MxLCtrl.pamxctrl = &MxCtrl;

 if (!mixerGetLineControls((HMIXEROBJ)m_MixerHandle, &MxLCtrl, MIXER_GETLINECONTROLSF_ONEBYTYPE)) {
  // Found, so proceed
  MxControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  MxControlDetails.dwControlID = MxCtrl.dwControlID;
  MxControlDetails.cChannels = m_MxLine.cChannels;

  if (MxControlDetails.cChannels > 2) MxControlDetails.cChannels = 2;
  if (MIXERCONTROL_CONTROLF_UNIFORM &  MxCtrl.fdwControl) MxControlDetails.cChannels = 1;

  MxControlDetails.cMultipleItems = 0;
  MxControlDetails.hwndOwner = (HWND) 0;
  MxControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  MxControlDetails.paDetails = uValue;

  nVolume = MxCtrl.Bounds.dwMinimum + (UINT) (((MxCtrl.Bounds.dwMaximum - MxCtrl.Bounds.dwMinimum) * nVolume)/100);
  // Set volume (for both channels)
  uValue[0].dwValue = uValue[1].dwValue = nVolume;
  mixerSetControlDetails((HMIXEROBJ) m_MixerHandle, &MxControlDetails, MIXER_SETCONTROLDETAILSF_VALUE);
 }
}

void CMixerLine::UnMute() {
 MIXERCONTROL MxCtrl;
 MIXERLINECONTROLS MxLCtrl;
 MIXERCONTROLDETAILS_BOOLEAN bValue[2];
 MIXERCONTROLDETAILS MxControlDetails;

 // Find mute control, if any, of the line
 MxLCtrl.cbStruct = sizeof(MIXERLINECONTROLS);
 MxLCtrl.dwLineID = m_MxLine.dwLineID;
 MxLCtrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
 MxLCtrl.cControls = 1;
 MxLCtrl.cbmxctrl = sizeof(MIXERCONTROL);
 MxLCtrl.pamxctrl = &MxCtrl;

 if (!mixerGetLineControls((HMIXEROBJ)m_MixerHandle, &MxLCtrl, MIXER_GETLINECONTROLSF_ONEBYTYPE)) {
  // Found, so proceed
  MxControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  MxControlDetails.dwControlID = MxCtrl.dwControlID;
  MxControlDetails.cChannels = m_MxLine.cChannels;

  if (MxControlDetails.cChannels > 2) MxControlDetails.cChannels = 2;
  if (MIXERCONTROL_CONTROLF_UNIFORM &  MxCtrl.fdwControl) MxControlDetails.cChannels = 1;

  MxControlDetails.cMultipleItems = 0;
  MxControlDetails.hwndOwner = (HWND) 0;
  MxControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  MxControlDetails.paDetails = bValue;

  // Unmute the line (for both channels)
  bValue[0].fValue = bValue[1].fValue = 0;
  mixerSetControlDetails((HMIXEROBJ)m_MixerHandle, &MxControlDetails, MIXER_SETCONTROLDETAILSF_VALUE);
 }
}
///////////////////////////////////////////////////////////////////////////

CMixerLine& CMixer::GetLine(const TCHAR *pLineName) {
 vector<CMixerLine*>::iterator itPos = m_arrMixerLines.begin();
 CMixerLine* pMixerLine = NULL;

 if (pLineName != NULL) {
  m_qLocalMutex.Lock();
  for (; itPos < m_arrMixerLines.end(); itPos++) {
   if (::lstrcmp(((CMixerLine*) *itPos)->GetName(), pLineName) == 0) {
    pMixerLine = *itPos;
    break;
   }
  }
  m_qLocalMutex.Unlock();
 }

 if (pMixerLine == NULL) throw "Mixer's line not found.";
 return *pMixerLine;
}

void CMixer::InitLines() {
 DWORD dwMixLines, i;
 CMixerLine *pMixerLine;
 MIXERLINE mxLine;
 MMRESULT err;

 mxLine.cbStruct = sizeof(MIXERLINE);
 mxLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;

 err = mixerGetLineInfo((HMIXEROBJ)m_MixerHandle, &mxLine, MIXER_GETLINEINFOF_COMPONENTTYPE);
 if (err) {
  // Device doesn't have a WAVE recording control
  return;
 }

 dwMixLines = mxLine.cConnections;

 for (i = 0; i < dwMixLines; i++) {
  mxLine.cbStruct = sizeof(MIXERLINE);
  mxLine.dwSource = i;

  err = mixerGetLineInfo((HMIXEROBJ) this->m_MixerHandle, &mxLine, MIXER_GETLINEINFOF_SOURCE);
  if (!err) {
   // Not interested in MIDI IN lines
   if (mxLine.dwComponentType != MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER) {
    // Create Mixer's Line object and add to the Lines Collection.
    pMixerLine = new CMixerLine(this->m_MixerHandle, &mxLine);
    m_arrMixerLines.push_back(pMixerLine);
   }
  }
 }

}

void CMixer::Open() {
 MMRESULT err;

 m_qLocalMutex.Lock();

 if (m_MixerHandle == NULL) {
  err = mixerOpen(&m_MixerHandle, m_nWaveDeviceID, 0, 0, MIXER_OBJECTF_WAVEIN);
  if (err) {
   m_MixerHandle = NULL;
   m_qLocalMutex.Unlock();
   throw "Can't open Mixer Device.";
  }
  InitLines();
 }

 m_qLocalMutex.Unlock();
}

void CMixer::Close() {
 m_qLocalMutex.Lock();

 if (m_MixerHandle != NULL) {

  vector<CMixerLine*>::iterator itPos = m_arrMixerLines.begin();
  for (; itPos < m_arrMixerLines.end(); itPos++) {
   delete *itPos;
  }

  m_arrMixerLines.clear();
  mixerClose(m_MixerHandle);
  m_MixerHandle = NULL;
 }

 m_qLocalMutex.Unlock();
}

CMixer::~CMixer() {
 Close();
}

CMixer::CMixer(UINT nWaveDeviceID): m_qLocalMutex() {
 m_nWaveDeviceID = nWaveDeviceID;
 m_MixerHandle = NULL;
}
///////////////////////////////////////////////////////////////////////////

CMixer& CWaveINSimple::OpenMixer() {
 m_Mixer.Open();
 return m_Mixer;
}

CWaveINSimple& CWaveINSimple::GetDevice(const TCHAR *pDeviceName) {
 
 CWaveINSimple* wINSimple = NULL;
 UINT i;

 GetDevices();

 if (pDeviceName != NULL) {
  m_qGlobalMutex.Lock();
  for (i = 0; i < m_arrWaveINDevices.size(); i++) {
   if ((::lstrcmp(m_arrWaveINDevices[i]->GetName(), pDeviceName)) == 0) {
    wINSimple = m_arrWaveINDevices[i];
    break;
   }
  }
  m_qGlobalMutex.Unlock();
 }

 if (wINSimple == NULL) throw "Device not found.";
 return *wINSimple;
 
}

CWaveINSimple::~CWaveINSimple() {
 
 this->m_qLocalMutex.Lock();
 this->_Stop();
 if (this->m_WaveHeader[0].lpData != NULL) VirtualFree(this->m_WaveHeader[0].lpData, 0, MEM_RELEASE);
 this->m_qLocalMutex.Unlock();
 
}

void CWaveINSimple::Close(int iLevel) {
 
 switch(iLevel) {
 case 1:
  waveInUnprepareHeader(this->m_WaveInHandle, &this->m_WaveHeader[1], sizeof(WAVEHDR));
 case 2:
  waveInUnprepareHeader(this->m_WaveInHandle, &this->m_WaveHeader[0], sizeof(WAVEHDR));
 case 3:
  // Close the WaveIN device.
  while ((waveInClose(this->m_WaveInHandle)) != MMSYSERR_NOERROR) ::Sleep(1);
 case 4:
  this->m_WaveInHandle = NULL;
  this->m_Receiver = NULL;
 }
 
}

// Wrapper for the multithreading version
void CWaveINSimple::Stop() {
 
 this->m_qLocalMutex.Lock();
 this->_Stop();
 this->m_qLocalMutex.Unlock();
 
}

void CWaveINSimple::_Stop() {
 
 if (this->m_WaveInHandle != NULL) {
  // Say to Thread that stop recording is requested.
  this->m_SIG = EXIT_SIG;

  // Stop recording and tell the driver to unqueue/return all of
  // our WAVEHDRs (via MM_WIM_DONE). The driver will return any
  // partially filled buffer that was currently recording.
  waveInReset(this->m_WaveInHandle);

  // Wait for the recording Thread to receive the MM_WIM_DONE for
  // each queued WAVEHDRs.
  while (this->m_BuffersDone < 2) ::Sleep(1);
  this->Close(1);

  this->m_WaveHeader[1].dwFlags = this->m_WaveHeader[0].dwFlags = 0;
 }
 
}

// Wrapper for the multithreading version
void CWaveINSimple::Start(IReceiver *pReceiver) {
 
 const char *message = NULL;

 this->m_qLocalMutex.Lock();

 try {
  this->_Start(pReceiver);
 }
 catch (const char *msg) {
  message = msg;
 }

 this->m_qLocalMutex.Unlock();
 if (message != NULL) throw message;
 
}

void CWaveINSimple::_Start(IReceiver *pReceiver) {
 
 HANDLE waveInThread;
 LPTHREAD_START_ROUTINE pStartRoutine = &CWaveINSimple::waveInProc;
 DWORD dwThreadID;
 MMRESULT err;

 if (this->m_WaveInHandle == NULL) {
  this->m_Receiver = pReceiver;
  this->m_SIG = WAIT_SIG;

  // Create the Thread that will receive incoming "blocks" of digital audio data
  // (sent from the driver). The main procedure of this thread is
  // CWaveINSimple::waveInProc(). We need to get the threadID and pass that
  // to waveInOpen().
  waveInThread = CreateThread(NULL, 0, pStartRoutine, (PVOID) this, 0, &dwThreadID);
  if (!waveInThread) {
   this->m_Receiver = NULL;
   throw "Can't create WAVE recording thread.";
  }
  CloseHandle(waveInThread);

  // Open the WaveIN Device, specifying Thread's ID as a callback.
  err = waveInOpen(&this->m_WaveInHandle, this->m_nWaveDeviceID, &this->m_waveFormat, dwThreadID, 0, CALLBACK_THREAD);
  if (err) {
   // Open failed, say to Thread to stop.
   this->m_SIG = EXIT_SIG;
   this->Close(4);
   throw "Can't open WaveIN Device.";
  }
  this->m_SIG = CONTINUE_SIG;

  // Allocate, prepare, and queue two buffers that the driver can
  // use to record blocks of audio data. Alloc buffers only one
  // time and use them until object is deleted.
  // Double-buffering is used.
  if (this->m_WaveHeader[0].lpData == NULL) {
   this->m_WaveHeader[0].lpData = (char *)VirtualAlloc(0, this->m_WaveHeader[0].dwBufferLength * 2, MEM_COMMIT, PAGE_READWRITE);
   if (this->m_WaveHeader[0].lpData == NULL) {
    this->Close(3);
    throw "Can't allocate memory for WAVE buffer.";
   }
  }
  this->m_WaveHeader[1].lpData = this->m_WaveHeader[0].lpData + this->m_WaveHeader[0].dwBufferLength;

  err = waveInPrepareHeader(this->m_WaveInHandle, &this->m_WaveHeader[0], sizeof(WAVEHDR));
  if (err) {
   this->Close(3);
   throw "Error preparing WAVEHDR 1.";
  }

  err = waveInPrepareHeader(this->m_WaveInHandle, &this->m_WaveHeader[1], sizeof(WAVEHDR));
  if (err) {
   this->Close(2);
   throw "Error preparing WAVEHDR 2.";
  }

  err = waveInAddBuffer(this->m_WaveInHandle, &this->m_WaveHeader[0], sizeof(WAVEHDR));
  if (err) {
   this->Close(1);
   throw "Error queueing WAVEHDR 1.";
  }

  err = waveInAddBuffer(this->m_WaveInHandle, &this->m_WaveHeader[1], sizeof(WAVEHDR));
  if (err) {
   this->m_BuffersDone = 1;
   this->Stop();
   throw "Error queueing WAVEHDR 2.";
  }

  // Start recording. Thread will now be receiving audio data.
  err = waveInStart(this->m_WaveInHandle);
  if (err) {
   this->Stop();
   throw "Error starting record.";
  }
 }
 
}

CWaveINSimple::CWaveINSimple(UINT nWaveDeviceID, WAVEINCAPS *pWIC): m_Mixer(nWaveDeviceID), m_qLocalMutex() {
 
 this->m_nWaveDeviceID = nWaveDeviceID;
 memcpy(&this->m_wic, pWIC, sizeof(WAVEINCAPS));
 this->m_WaveInHandle = NULL;
 this->m_Receiver = NULL;

 //Initialize the WAVEFORMATEX for 16-bit, 44KHz, stereo.
 ZeroMemory(&this->m_waveFormat, sizeof(WAVEFORMATEX));
 this->m_waveFormat.wFormatTag = WAVE_FORMAT_PCM;
 this->m_waveFormat.nChannels = 2;
 this->m_waveFormat.nSamplesPerSec = 44100;
 this->m_waveFormat.wBitsPerSample = 16;
 this->m_waveFormat.nBlockAlign = this->m_waveFormat.nChannels * (this->m_waveFormat.wBitsPerSample/8);
 this->m_waveFormat.nAvgBytesPerSec = this->m_waveFormat.nSamplesPerSec * this->m_waveFormat.nBlockAlign;
 this->m_waveFormat.cbSize = 0;

 //Initialize the sound buffers, but don't allocate memory yet.
 ZeroMemory(this->m_WaveHeader, sizeof(WAVEHDR) * 2);
 this->m_WaveHeader[1].dwBufferLength = this->m_WaveHeader[0].dwBufferLength = this->m_waveFormat.nAvgBytesPerSec << 1;
 
}

DWORD WINAPI CWaveINSimple::waveInProc(LPVOID arg) {
 
 MSG  msg;
 CWaveINSimple *_this = (CWaveINSimple *) arg;

 if (_this == NULL) return(0);

 while (_this->m_SIG == WAIT_SIG) ::Sleep(1);
 if (_this->m_SIG == EXIT_SIG) {
  // Call to waveInOpen failed.
  return(0);
 }

 while (GetMessage(&msg, 0, 0, 0) == 1)  {
  switch (msg.message) {
   // A buffer has been filled by the driver
   case MM_WIM_DATA:
    // msg.lParam contains a pointer to the WAVEHDR structure
    // for the filled buffer.
    if ((((WAVEHDR *)msg.lParam)->dwBytesRecorded) && (_this->m_Receiver)) {

     // Send buffer to the m_Receiver (instance of the IReceiver)
     // for further processing
     _this->m_Receiver->ReceiveBuffer(((WAVEHDR *)msg.lParam)->lpData,
      ((WAVEHDR *)msg.lParam)->dwBytesRecorded);
    }

    // Still recording?
    if (_this->m_SIG != EXIT_SIG) {
     // Yes. Then requeue this buffer so the driver can
     // use it for another block of audio data.
     waveInAddBuffer(_this->m_WaveInHandle, (WAVEHDR *)msg.lParam, sizeof(WAVEHDR));
    }
    else {
     // No, so another WAVEHDR has been returned after
     // recording has stopped. When we get all of them back,
     // m_BuffersDone will be equal to how many WAVEHDRs
     // we queued.
     ++_this->m_BuffersDone;
    }
    break;

    // Main thread is opening the WAVE device.
   case MM_WIM_OPEN:
    _this->m_BuffersDone = 0;
    break;

    // Main thread is closing the WAVE device.
   case MM_WIM_CLOSE:
    return(0);
    break;
  }
 }
 
 return(0);
}

const vector<CWaveINSimple*>& CWaveINSimple::GetDevices() {
 
 UINT nInDev, i;
 WAVEINCAPS wic;
 CWaveINSimple *pWaveIn;

 m_qGlobalMutex.Lock();

 if (!m_isDeviceListLoaded) {
  nInDev = waveInGetNumDevs();

  for (i = 0; i < nInDev; i++) {
   if (!waveInGetDevCaps(i, &wic, sizeof(WAVEINCAPS))) {

    // We are only interested in devices supporting
    // 44.1 kHz, stereo, 16-bit, stereo input
    if (wic.dwFormats & WAVE_FORMAT_4S16) {
     pWaveIn = new CWaveINSimple(i, &wic);
     m_arrWaveINDevices.push_back(pWaveIn);
    }
   }
  }

  m_isDeviceListLoaded = true;
 }

 m_qGlobalMutex.Unlock();

 return m_arrWaveINDevices;

}

void CWaveINSimple::CleanUp() {
 m_qGlobalMutex.Lock();

 vector<CWaveINSimple*>::iterator itPos = m_arrWaveINDevices.begin();
 for (; itPos < m_arrWaveINDevices.end(); itPos++) {
  delete *itPos;
 }

 m_arrWaveINDevices.clear();
 m_isDeviceListLoaded = false;

 m_qGlobalMutex.Unlock();
}

반응형