반응형
ed) waveIN_simple.h
#ifndef ___WAVEIN_SIMPLE_H_INCLUDED___ #define ___WAVEIN_SIMPLE_H_INCLUDED___ #include#include #include 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 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 & 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 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 & 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::m_arrWaveINDevices; QMutex CWaveINSimple::m_qGlobalMutex; volatile bool CWaveINSimple::m_isDeviceListLoaded = false; /////////////////////////////////////////////////////////////////////////// CMixerLine::CMixerLine(HMIXER MixerHandle, MIXERLINE *pMxLine) { this->m_MixerHandle = MixerHandle; memcpy(&this->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) this->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) this->m_MixerHandle, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE); // Select the class' line item for (DWORD j=0; j 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) this->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 = this->m_MxLine.dwLineID; MxLCtrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; MxLCtrl.cControls = 1; MxLCtrl.cbmxctrl = sizeof(MIXERCONTROL); MxLCtrl.pamxctrl = &MxCtrl; if (!mixerGetLineControls((HMIXEROBJ) this->m_MixerHandle, &MxLCtrl, MIXER_GETLINECONTROLSF_ONEBYTYPE)) { // Found, so proceed MxControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); MxControlDetails.dwControlID = MxCtrl.dwControlID; MxControlDetails.cChannels = this->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) this->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 = this->m_MxLine.dwLineID; MxLCtrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; MxLCtrl.cControls = 1; MxLCtrl.cbmxctrl = sizeof(MIXERCONTROL); MxLCtrl.pamxctrl = &MxCtrl; if (!mixerGetLineControls((HMIXEROBJ) this->m_MixerHandle, &MxLCtrl, MIXER_GETLINECONTROLSF_ONEBYTYPE)) { // Found, so proceed MxControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); MxControlDetails.dwControlID = MxCtrl.dwControlID; MxControlDetails.cChannels = this->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) this->m_MixerHandle, &MxControlDetails, MIXER_SETCONTROLDETAILSF_VALUE); } } /////////////////////////////////////////////////////////////////////////// CMixerLine& CMixer::GetLine(const TCHAR *pLineName) { vector ::iterator itPos = this->m_arrMixerLines.begin(); CMixerLine* pMixerLine = NULL; if (pLineName != NULL) { this->m_qLocalMutex.Lock(); for (; itPos < this->m_arrMixerLines.end(); itPos++) { if (::lstrcmp(((CMixerLine*) *itPos)->GetName(), pLineName) == 0) { pMixerLine = *itPos; break; } } this->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) this->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); this->m_arrMixerLines.push_back(pMixerLine); } } } } void CMixer::Open() { MMRESULT err; this->m_qLocalMutex.Lock(); if (this->m_MixerHandle == NULL) { err = mixerOpen(&this->m_MixerHandle, this->m_nWaveDeviceID, 0, 0, MIXER_OBJECTF_WAVEIN); if (err) { this->m_MixerHandle = NULL; this->m_qLocalMutex.Unlock(); throw "Can't open Mixer Device."; } this->InitLines(); } this->m_qLocalMutex.Unlock(); } void CMixer::Close() { this->m_qLocalMutex.Lock(); if (this->m_MixerHandle != NULL) { vector ::iterator itPos = this->m_arrMixerLines.begin(); for (; itPos < this->m_arrMixerLines.end(); itPos++) { delete *itPos; } this->m_arrMixerLines.clear(); mixerClose(this->m_MixerHandle); this->m_MixerHandle = NULL; } this->m_qLocalMutex.Unlock(); } CMixer::~CMixer() { this->Close(); } CMixer::CMixer(UINT nWaveDeviceID): m_qLocalMutex() { this->m_nWaveDeviceID = nWaveDeviceID; this->m_MixerHandle = NULL; } /////////////////////////////////////////////////////////////////////////// CMixer& CWaveINSimple::OpenMixer() { this->m_Mixer.Open(); return this->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::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 ::iterator itPos = m_arrWaveINDevices.begin(); for (; itPos < m_arrWaveINDevices.end(); itPos++) { delete *itPos; } m_arrWaveINDevices.clear(); m_isDeviceListLoaded = false; m_qGlobalMutex.Unlock(); } #endif
반응형