#include "precompile.h" #include "audiomicspk.h" #include "audiomicspk3.h" #include "audiocontext.h" #include #include "audiolog.h" #include "./other/delaybuf.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CLOCK_PERIOD 30 #define MAX_STR_LEN 512 #define AUDIO_CLOCK 8000 #define MAX_DELAY 100 #define MS_REF_TICK 10000 // play use portaudio typedef struct tagAUDIO_DEVICE_INFO { char szDeviceName[MAX_STR_LEN]; char szDeviceID[MAX_STR_LEN]; } AUDIO_DEVICE_INFO, *PAUDIO_DEVICE_INFO; static HRESULT GetDeviceNum(EDataFlow eDataFlow, UINT *uDevCount) { IMMDeviceEnumerator *pEnumerator = NULL; IMMDeviceCollection *pEndpoints = NULL; HRESULT hr; hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&pEnumerator); if (FAILED(hr)) goto on_error; hr = pEnumerator->lpVtbl->EnumAudioEndpoints(pEnumerator, eDataFlow, DEVICE_STATE_ACTIVE, (void**)&pEndpoints); if (FAILED(hr)) goto on_error; hr = pEndpoints->lpVtbl->GetCount(pEndpoints, uDevCount); if (FAILED(hr)) goto on_error; on_error: if (pEndpoints) pEndpoints->lpVtbl->Release(pEndpoints); if (pEnumerator) pEnumerator->lpVtbl->Release(pEnumerator); return hr; } static HRESULT EnumDevice(EDataFlow eDataFlow, UINT uNumElements, AUDIO_DEVICE_INFO *pDevicInfo) { IMMDeviceEnumerator *pEnumerator = NULL; IMMDeviceCollection *pEndpoints = NULL; HRESULT hr; UINT uCount; UINT uIdx; hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&pEnumerator); if (FAILED(hr)) goto on_error; hr = pEnumerator->lpVtbl->EnumAudioEndpoints(pEnumerator, eDataFlow, DEVICE_STATE_ACTIVE, (void**)&pEndpoints); if (FAILED(hr)) goto on_error; hr = pEndpoints->lpVtbl->GetCount(pEndpoints, &uCount); if (FAILED(hr)) goto on_error; ZeroMemory(pDevicInfo, sizeof(AUDIO_DEVICE_INFO)*uNumElements); for (uIdx = 0; uIdx < uCount && uIdx < uNumElements; ++uIdx) { IMMDevice *pDevice = NULL; IPropertyStore *pPS = NULL; WCHAR* pszDeviceId = NULL; PROPVARIANT value; PropVariantInit(&value); pEndpoints->lpVtbl->Item(pEndpoints, uIdx, &pDevice); pDevice->lpVtbl->GetId(pDevice, &pszDeviceId); pDevice->lpVtbl->OpenPropertyStore(pDevice, STGM_READ, &pPS); pPS->lpVtbl->GetValue(pPS, &PKEY_Device_FriendlyName, &value); WideCharToMultiByte(CP_ACP, 0, pszDeviceId, -1, pDevicInfo[uIdx].szDeviceID, MAX_STR_LEN-1, NULL, NULL); WideCharToMultiByte(CP_ACP, 0, value.pwszVal, -1, pDevicInfo[uIdx].szDeviceName, MAX_STR_LEN-1, NULL, NULL); PropVariantClear(&value); CoTaskMemFree(pszDeviceId); pPS->lpVtbl->Release(pPS); pDevice->lpVtbl->Release(pDevice); } on_error: if (pEndpoints) pEndpoints->lpVtbl->Release(pEndpoints); if (pEnumerator) pEnumerator->lpVtbl->Release(pEnumerator); return hr; } static HRESULT DeviceBindTo( EDataFlow eDataFlow, // eCapture/eRender INT iDevIdx, // Device Index. -1 - default device. IAudioClient **ppAudioClient, // pointer pointer to IAudioClient interface IAudioEndpointVolume **ppEndpointVolume ) { IMMDeviceEnumerator *pEnumerator = NULL; IMMDeviceCollection *pEndpoints = NULL; IMMDevice *pDevice = NULL; HRESULT hr; hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&pEnumerator); if (FAILED(hr)) goto on_error; if (iDevIdx < 0) { hr = pEnumerator->lpVtbl->GetDefaultAudioEndpoint(pEnumerator, eDataFlow, eConsole, &pDevice); if (FAILED(hr)) goto on_error; } else { hr = pEnumerator->lpVtbl->EnumAudioEndpoints(pEnumerator, eDataFlow, DEVICE_STATE_ACTIVE, (void**)&pEndpoints); if (FAILED(hr)) goto on_error; hr = pEndpoints->lpVtbl->Item(pEndpoints, iDevIdx, &pDevice); if (FAILED(hr)) goto on_error; } hr = pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)ppAudioClient); if (FAILED(hr)) goto on_error; if (ppEndpointVolume) { hr = pDevice->lpVtbl->Activate(pDevice, &IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, NULL, (void**)ppEndpointVolume); if (FAILED(hr)) { if (*ppAudioClient) { (*ppAudioClient)->lpVtbl->Release(*ppAudioClient); *ppAudioClient = NULL; } goto on_error; } } on_error: if (pDevice) { pDevice->lpVtbl->Release(pDevice); } if (pEndpoints) { pEndpoints->lpVtbl->Release(pEndpoints); } if (pEnumerator) { pEnumerator->lpVtbl->Release(pEnumerator); } return hr; } static int get_device_index(int indev, const char *key) { EDataFlow df = indev ? eCapture : eRender; UINT i; UINT n; AUDIO_DEVICE_INFO *pInfo = NULL; GetDeviceNum(df, &n); pInfo = malloc(sizeof(AUDIO_DEVICE_INFO) * n); EnumDevice(df, n, pInfo); for (i = 0; i < n; ++i) { if (strstr(pInfo[i].szDeviceName, key)) { free(pInfo); return i; } } free(pInfo); return -1; } static int get_device_index_poartaudio(int indev, const char *key) { int i; int n = Pa_GetDeviceCount(); for (i = 0; i < n; ++i) { const PaDeviceInfo* pInfo = Pa_GetDeviceInfo(i); if (indev) { if (pInfo->maxInputChannels && strstr(pInfo->name, key)) return i; } else { if (pInfo->maxOutputChannels && strstr(pInfo->name, key)) return i; } } return -1; } static HRESULT CreateVoiceCaptureDMO( int spk_dev_id, int rec_dev_id, BOOL bNS, BOOL bAGC, IMediaObject *ppDMO) { HRESULT hr = S_OK; return hr; } typedef struct IMediaBufferImpl { struct IMediaBufferVtbl *lpVtbl; struct IMediaBufferVtbl vtbl; BYTE *m_pData; ULONG m_ulSize; ULONG m_ulData; ULONG m_cRef; }IMediaBufferImpl; static HRESULT STDMETHODCALLTYPE QueryInterface(IMediaBuffer * This, REFIID riid, void** ppvObject); static ULONG STDMETHODCALLTYPE AddRef(IMediaBuffer * This); static ULONG STDMETHODCALLTYPE Release(IMediaBuffer * This); static HRESULT STDMETHODCALLTYPE SetLength(IMediaBuffer * This, DWORD ulLength); static HRESULT STDMETHODCALLTYPE GetMaxLength(IMediaBuffer * This, DWORD *pcbMaxLength); static HRESULT STDMETHODCALLTYPE GetBufferAndLength(IMediaBuffer * This, BYTE **ppBuffer, DWORD *pcbLength); static void destroy_media_buffer(IMediaBufferImpl *pImpl) { free(pImpl->m_pData); free(pImpl); } static IMediaBuffer *create_media_buffer(ULONG len) { IMediaBufferImpl *pImpl = (IMediaBufferImpl*)malloc(sizeof(IMediaBufferImpl)); if (pImpl) { pImpl->lpVtbl = &pImpl->vtbl; pImpl->lpVtbl->QueryInterface = &QueryInterface; pImpl->lpVtbl->AddRef = &AddRef; pImpl->lpVtbl->Release = &Release; pImpl->lpVtbl->SetLength = &SetLength; pImpl->lpVtbl->GetMaxLength = &GetMaxLength; pImpl->lpVtbl->GetBufferAndLength = &GetBufferAndLength; pImpl->m_pData = (BYTE*)malloc(len); pImpl->m_ulData = 0; pImpl->m_ulSize = len; pImpl->m_cRef = 1; } return (IMediaBuffer*)pImpl; } static HRESULT STDMETHODCALLTYPE QueryInterface(IMediaBuffer * This, REFIID riid, void** ppvObject) { if (This == NULL || riid == NULL || ppvObject == NULL || *ppvObject == NULL) return E_POINTER; if (IsEqualIID(riid, &IID_IUnknown)) { *ppvObject = (void*)This; return S_OK; } else if (IsEqualIID(riid, &IID_IMediaBuffer)) { *ppvObject = (void*)This; return S_OK; } return E_NOTIMPL; } static ULONG STDMETHODCALLTYPE AddRef(IMediaBuffer * This) { IMediaBufferImpl *pImpl = (IMediaBufferImpl *)This; return InterlockedIncrement(&pImpl->m_cRef); } static ULONG STDMETHODCALLTYPE Release(IMediaBuffer * This) { IMediaBufferImpl *pImpl = (IMediaBufferImpl *)This; ULONG lRet = InterlockedDecrement(&pImpl->m_cRef); if (lRet == 0) { destroy_media_buffer(pImpl); } return lRet; } static HRESULT STDMETHODCALLTYPE SetLength(IMediaBuffer * This, DWORD ulLength) { IMediaBufferImpl *pImpl = (IMediaBufferImpl *)This; pImpl->m_ulData = ulLength; return NOERROR; } static HRESULT STDMETHODCALLTYPE GetMaxLength(IMediaBuffer * This, DWORD *pcbMaxLength) { IMediaBufferImpl *pImpl = (IMediaBufferImpl *)This; *pcbMaxLength = pImpl->m_ulSize; return NOERROR; } static HRESULT STDMETHODCALLTYPE GetBufferAndLength(IMediaBuffer * This, BYTE **ppBuffer, DWORD *pcbLength) { IMediaBufferImpl *pImpl = (IMediaBufferImpl *)This; if (ppBuffer) *ppBuffer = pImpl->m_pData; if (pcbLength) *pcbLength = pImpl->m_ulData; return NOERROR; } static int load_data(audiomicspk3_t *micspk, BYTE *pData, UINT32 nCount, UINT32 *pReturnedCount) { UINT32 frame_samples = AUDIO_CLOCK * FRAME_TIME / 1000; UINT32 frame_offset = 0; while (nCount >= frame_samples) { delay_buf_get(micspk->ply_dbuf, (SHORT*)(pData+frame_offset*2)); frame_offset += frame_samples; nCount -= frame_samples; } *pReturnedCount = frame_offset; return 0; } static int save_data(audiomicspk3_t *micspk, IMediaBuffer *pBuffer) { UINT32 frame_samples = AUDIO_CLOCK * FRAME_TIME / 1000; UINT32 frame_offset = 0; BYTE *pData; DWORD dwLength; pBuffer->lpVtbl->GetBufferAndLength(pBuffer, &pData, &dwLength); //AUDIO_LOG_INFO("micspk2 rec samples:%d", dwLength>>1); while (dwLength >= frame_samples*2) { delay_buf_put(micspk->rec_dbuf, (SHORT*)(pData+frame_offset*2)); frame_offset += frame_samples; dwLength -= frame_samples*2; } return 0; } static int StreamCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { audiomicspk3_t *micspk = (audiomicspk3_t*)userData; apr_status_t status; if (output) { unsigned nsamples_req = frameCount; if (micspk->ply_buf_cnt == 0 && nsamples_req == micspk->frame_samples) { delay_buf_get(micspk->ply_dbuf, (short*)output); } else { if (micspk->ply_buf_cnt > 0) { memcpy(output, micspk->ply_buf, micspk->ply_buf_cnt<<1); output = (short*)output + micspk->ply_buf_cnt; micspk->ply_buf_cnt = 0; nsamples_req -= micspk->ply_buf_cnt; } while (nsamples_req > 0) { if (nsamples_req >= micspk->frame_samples) { delay_buf_get(micspk->ply_dbuf, (short*)output); output = (short*)output + micspk->frame_samples; nsamples_req -= micspk->frame_samples; } else { delay_buf_get(micspk->ply_dbuf, micspk->ply_buf); micspk->ply_buf_cnt = micspk->frame_samples; memcpy(output, micspk->ply_buf, nsamples_req<<1); output = (short*)output + nsamples_req; memmove(micspk->ply_buf, micspk->ply_buf+nsamples_req, (micspk->frame_samples-nsamples_req)<<1); nsamples_req = 0; } } } } return paContinue; } static int initialize_spk(audiomicspk3_t *micspk) { UINT32 frame_samples = AUDIO_CLOCK * FRAME_TIME / 1000; const PaDeviceInfo *info; PaStreamParameters outParam; PaError paError; int ply_dev_id = micspk->ply_dev_id_portaudio; if (ply_dev_id == -1) { ply_dev_id = Pa_GetDefaultOutputDevice(); if (ply_dev_id == paNoDevice) { AUDIO_LOG_ERROR("audiomicspk create error, cannot find output device"); return APR_EGENERAL; } } info = Pa_GetDeviceInfo(ply_dev_id); outParam.device = ply_dev_id; outParam.channelCount = 1; outParam.sampleFormat = paInt16; outParam.suggestedLatency = info->defaultLowOutputLatency; outParam.hostApiSpecificStreamInfo = NULL; if (Pa_IsFormatSupported(NULL, &outParam, AUDIO_CLOCK) != paNoError) { AUDIO_LOG_ERROR("audiomicspk create error, cannot open audio output device"); return APR_EGENERAL; } paError = Pa_OpenStream(&micspk->ply_stream, NULL, &outParam, AUDIO_CLOCK, frame_samples, paClipOff, &StreamCallback, micspk); if (paError != 0) { AUDIO_LOG_ERROR("audiomicspk create error, Pa_OpenStream function failed in dir! "); return APR_EGENERAL; } paError = Pa_StartStream(micspk->ply_stream); if (paError != 0) { AUDIO_LOG_ERROR("Pa_StartStream function failed in dir! "); Pa_CloseStream(micspk->ply_stream); micspk->ply_stream = NULL; return APR_EGENERAL; } return 0; } static int initialize_rec(audiomicspk3_t *micspk) { IMediaObject *pDMO = NULL; IPropertyStore *pPS = NULL; HRESULT hr; UINT32 frame_samples = AUDIO_CLOCK * FRAME_TIME / 1000; hr = CoCreateInstance(&CLSID_CWMAudioAEC, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void**)&pDMO); if (SUCCEEDED(hr)) { hr = pDMO->lpVtbl->QueryInterface(pDMO, &IID_IPropertyStore, (void**)&pPS); } if (FAILED(hr)) { if (pDMO) { pDMO->lpVtbl->Release(pDMO); } return -1; } // sys mode { PROPVARIANT pvSysMode; PropVariantInit(&pvSysMode); pvSysMode.vt = VT_I4; pvSysMode.lVal = micspk->opt & AMS2_OPT_AEC ? SINGLE_CHANNEL_AEC : SINGLE_CHANNEL_NSAGC; //pvSysMode.lVal = micspk->opt & AMS2_OPT_AEC ? OPTIBEAM_ARRAY_AND_AEC : SINGLE_CHANNEL_NSAGC; pPS->lpVtbl->SetValue(pPS, &MFPKEY_WMAAECMA_SYSTEM_MODE, &pvSysMode); PropVariantClear(&pvSysMode); } // feature mode { PROPVARIANT pvFeatMode; PropVariantInit(&pvFeatMode); pvFeatMode.vt = VT_BOOL; pvFeatMode.boolVal = VARIANT_TRUE; pPS->lpVtbl->SetValue(pPS, &MFPKEY_WMAAECMA_FEATURE_MODE, &pvFeatMode); PropVariantClear(&pvFeatMode); } // set device index { PROPVARIANT pvDeviceId; PropVariantInit(&pvDeviceId); pvDeviceId.vt = VT_I4; pvDeviceId.lVal = (unsigned long)(micspk->ply_dev_id<<16) + (unsigned long)(0x0000ffff & micspk->rec_dev_id); pPS->lpVtbl->SetValue(pPS, &MFPKEY_WMAAECMA_DEVICE_INDEXES, &pvDeviceId); PropVariantClear(&pvDeviceId); } // echo length #if 1 if (micspk->opt & AMS2_OPT_AEC) { PROPVARIANT pvEchoLength; PropVariantInit(&pvEchoLength); pvEchoLength.vt = VT_I4; pvEchoLength.lVal = 128; pPS->lpVtbl->SetValue(pPS, &MFPKEY_WMAAECMA_FEATR_ECHO_LENGTH, &pvEchoLength); PropVariantClear(&pvEchoLength); } #endif // echo supress times #if 1 if (micspk->opt & AMS2_OPT_AEC) { PROPVARIANT pvAESTimes; PropVariantInit(&pvAESTimes); pvAESTimes.vt = VT_I4; pvAESTimes.lVal = 1; // 0, 1, 2 pPS->lpVtbl->SetValue(pPS, &MFPKEY_WMAAECMA_FEATR_AES, &pvAESTimes); PropVariantClear(&pvAESTimes); } #endif // retreive TS if (micspk->opt & AMS2_OPT_AEC) { PROPVARIANT pvTS; PropVariantInit(&pvTS); pvTS.vt = VT_BOOL; pvTS.boolVal = VARIANT_TRUE; pPS->lpVtbl->SetValue(pPS, &MFPKEY_WMAAECMA_RETRIEVE_TS_STATS, &pvTS); PropVariantClear(&pvTS); } // noise suppression { PROPVARIANT pvNoiseSup; PropVariantInit(&pvNoiseSup); pvNoiseSup.vt = VT_I4; pvNoiseSup.lVal = micspk->opt & AMS2_OPT_NS; pPS->lpVtbl->SetValue(pPS, &MFPKEY_WMAAECMA_FEATR_NS, &pvNoiseSup); PropVariantClear(&pvNoiseSup); } // Turn on/off AGC { PROPVARIANT pvAGC; PropVariantInit(&pvAGC); pvAGC.vt = VT_BOOL; pvAGC.boolVal = micspk->opt & AMS2_OPT_AGC ? VARIANT_TRUE : VARIANT_FALSE; pPS->lpVtbl->SetValue(pPS, &MFPKEY_WMAAECMA_FEATR_AGC, &pvAGC); PropVariantClear(&pvAGC); } // set output format { DMO_MEDIA_TYPE mt = {0}; WAVEFORMATEX wfxOut = {WAVE_FORMAT_PCM, 1, 8000, 16000, 2, 16, 0}; MoInitMediaType(&mt, sizeof(WAVEFORMATEX)); mt.majortype = MEDIATYPE_Audio; mt.subtype = MEDIASUBTYPE_PCM; mt.lSampleSize = 0; //mt.lSampleSize = 2; mt.bFixedSizeSamples = TRUE; mt.bTemporalCompression = FALSE; mt.formattype = FORMAT_WaveFormatEx; memcpy(mt.pbFormat, &wfxOut, sizeof(WAVEFORMATEX)); pDMO->lpVtbl->SetOutputType(pDMO, 0, &mt, 0); micspk->rec_buf_size = wfxOut.nSamplesPerSec * wfxOut.wBitsPerSample / 8; MoFreeMediaType(&mt); } delay_buf_create(AUDIO_CLOCK, frame_samples, 1, MAX_DELAY, 0, (delay_buf**)&micspk->rec_dbuf); pDMO->lpVtbl->AllocateStreamingResources(pDMO); pPS->lpVtbl->Release(pPS); micspk->rec_dmo = pDMO; return 0; } static void uninitialize_spk(audiomicspk3_t *micspk) { if (micspk->ply_stream) { Pa_AbortStream(micspk->ply_stream); Pa_CloseStream(micspk->ply_stream); } if (micspk->ply_dbuf) { delay_buf_destroy(micspk->ply_dbuf); micspk->ply_dbuf = NULL; } } static void uninitialize_rec(audiomicspk3_t *micspk) { IMediaObject *pDMO = (IMediaObject *)micspk->rec_dmo; if (pDMO) { pDMO->lpVtbl->Release(pDMO); } if (micspk->rec_dbuf) { delay_buf_destroy(micspk->rec_dbuf); micspk->rec_dbuf = NULL; } micspk->rec_dmo = NULL; } static void on_clock_rec(audiomicspk3_t *micspk) { IMediaObject *pDMO = (IMediaObject*)micspk->rec_dmo; DWORD dwStatus = 0; HRESULT hr; DWORD cOutputBufLen = micspk->rec_buf_size; DMO_OUTPUT_DATA_BUFFER bufferStruct = {0}; do { bufferStruct.pBuffer = create_media_buffer(cOutputBufLen); bufferStruct.dwStatus = 0; hr = pDMO->lpVtbl->ProcessOutput(pDMO, 0, 1, &bufferStruct, &dwStatus); if (SUCCEEDED(hr)) { if (hr == S_FALSE) { //.... } else { save_data(micspk, bufferStruct.pBuffer); } bufferStruct.pBuffer->lpVtbl->Release(bufferStruct.pBuffer); } else { bufferStruct.pBuffer->lpVtbl->Release(bufferStruct.pBuffer); break; } } while (bufferStruct.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE); } static unsigned int __stdcall work_proc(void *arg) { audiomicspk3_t *micspk = (audiomicspk3_t *)arg; int rc; // set a higher priority because of audio is very sensitive about timing SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); CoInitialize(NULL); // // record need play because of AEC, so // record <---> record and play // play <---> play // record and play <---> record and play // if (micspk->opt & AMS_OPT_RECORD) { rc = initialize_spk(micspk); if (rc != 0) goto on_error; rc = initialize_rec(micspk); if (rc != 0) goto on_error; } else if (micspk->opt & AMS_OPT_PLAY) { rc = initialize_spk(micspk); if (rc != 0) goto on_error; } micspk->evt_exit = CreateEventA(NULL, FALSE, FALSE, NULL); for (;;) { DWORD dwRet = WaitForSingleObject(micspk->evt_exit, CLOCK_PERIOD); if (dwRet == WAIT_OBJECT_0) { // exit break; } else { // timeout if (micspk->opt & AMS_OPT_RECORD) { on_clock_rec(micspk); } else if (micspk->opt & AMS_OPT_PLAY) { //on_clock_spk(micspk); } } } on_error: if (micspk->opt & AMS_OPT_RECORD) { uninitialize_spk(micspk); uninitialize_rec(micspk); } else if (micspk->opt & AMS_OPT_PLAY) { uninitialize_spk(micspk); } CoUninitialize(); return 0; } static apr_status_t read_frame(void *self, audioframe_t *frame) { audiomicspk3_t *micspk = CONTAINING_RECORD(self, audiomicspk3_t, base); UINT32 frame_samples = AUDIO_CLOCK * FRAME_TIME / 1000; frame->size = 2*frame_samples; frame->dtmf = 0; delay_buf_get(micspk->rec_dbuf, (short*)frame->buffer); return APR_SUCCESS; } static apr_status_t write_frame(void *self, const audioframe_t *frame) { audiomicspk3_t *micspk = CONTAINING_RECORD(self, audiomicspk3_t, base); delay_buf_put(micspk->ply_dbuf, (short*)frame->buffer); return APR_SUCCESS; } static audiostream_vtbl_t g_stream_vtbl = { &read_frame, &write_frame, }; apr_status_t audiomicspk3_create(apr_pool_t *pool, audioengine_t *engine, int opt, int clock, const char *rec_dev_key, const char *ply_dev_key, audiomicspk3_t **p_micspk) { int rec_dev_id = -1; int ply_dev_id = -1; int ply_dev_id_portaudio = -1; audiomicspk3_t *micspk; unsigned long frame_samples; ply_dev_id_portaudio = get_device_index_poartaudio(0, ply_dev_key); ply_dev_id = get_device_index(0, ply_dev_key); rec_dev_id = get_device_index(1, rec_dev_key); if (rec_dev_id < 0 || ply_dev_id < 0 || ply_dev_id_portaudio < 0) return APR_EGENERAL; //opt |= AMS2_OPT_AGC | AMS2_OPT_NS; micspk = apr_palloc(pool, sizeof(audiomicspk3_t)); memset(micspk, 0, sizeof(audiomicspk3_t)); frame_samples = FRAME_TIME * clock / 1000; micspk->rec_dev_id = rec_dev_id; micspk->ply_dev_id = ply_dev_id; micspk->ply_dev_id_portaudio = ply_dev_id_portaudio; micspk->opt = opt; micspk->frame_samples = frame_samples; audiostream_init(engine, &g_stream_vtbl, &micspk->base); micspk->base.direction = 0; if (opt & AMS_OPT_PLAY) { micspk->base.direction |= STREAM_DIR_WRITE; delay_buf_create(clock, frame_samples, 1, MAX_DELAY, 0, (delay_buf**)&micspk->ply_dbuf); micspk->ply_buf = (short*)apr_palloc(pool, frame_samples<<1); micspk->ply_buf_cnt = 0; } if (opt & AMS_OPT_RECORD) { micspk->base.direction |= STREAM_DIR_READ; delay_buf_create(clock, frame_samples, 1, MAX_DELAY, 0, (delay_buf**)&micspk->rec_dbuf); } micspk->worker_thread = (HANDLE)_beginthreadex(NULL, 0, &work_proc, micspk, 0, NULL); if (micspk->worker_thread) { BOOL bThreadExited = FALSE; do { DWORD dwRet = WaitForSingleObject(micspk->worker_thread, 10); if (dwRet == WAIT_TIMEOUT) { volatile HANDLE hExit = micspk->evt_exit; if (hExit) break; } else if (dwRet == WAIT_OBJECT_0) { bThreadExited = TRUE; } } while (!bThreadExited); if (bThreadExited) { audiomicspk3_destroy(micspk); return APR_EGENERAL; } } else { audiomicspk3_destroy(micspk); return APR_EGENERAL; } *p_micspk = micspk; return APR_SUCCESS; } void audiomicspk3_destroy(audiomicspk3_t *micspk) { if (micspk->evt_exit) { SetEvent(micspk->evt_exit); WaitForSingleObject(micspk->worker_thread, INFINITE); CloseHandle(micspk->worker_thread); CloseHandle(micspk->evt_exit); } }