/** @brief オーディオストリーム @file WinAudioStream.cpp @version 0.9 @date 2013 1/7 @author Fumi.Iseki @attention Original Sample is KinectAudioStream.cpp (Kinect Developer Toolkit) */ #include "WinBaseLib.h" #include "WinAudioStream.h" #include // StringCch #include // INT_MAX #ifndef _USE_MATH_DEFINES #define _USE_MATH_DEFINES #endif #include using namespace jbxl; using namespace jbxwl; CWinAudioStream::CWinAudioStream(IMediaObject* pObj) { m_cRef = 1; m_pMediaObj = pObj; m_writeBuffer = NULL; m_readBuffer = NULL; m_readBufferSize = 0; m_readBufferCount = 0; m_readBufferIndex = 0; // m_hStopEvent = NULL; m_hDataReady = NULL; // m_captureThread = NULL; m_hWave = NULL; m_waveHeaders = NULL; m_outputBufferNum = 0; m_outputBufferIndex = 0; m_streamData = init_Buffer(); if (m_pMediaObj!=NULL) m_pMediaObj->AddRef(); InitializeCriticalSection(&m_lock); } CWinAudioStream::~CWinAudioStream() { DEBUG_INFO("DESTRUCTOR: CWinAudioStream: START\n"); // closeOutput(); stopCapture(); DeleteCriticalSection(&m_lock); DEBUG_INFO("DESTRUCTOR: CWinAudioStream: END\n"); } BOOL CWinAudioStream::startCapture(WAVEFORMATEX* fmt) { if (m_pMediaObj==NULL && fmt==NULL) return FALSE; m_hStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); m_hDataReady = CreateEvent(NULL, FALSE, FALSE, NULL); m_writeBuffer = NULL; m_readBufferCount = 0; // WAVEFORMATEX format; if (fmt!=NULL) { format = *fmt; } else { DMO_MEDIA_TYPE media; m_pMediaObj->GetOutputCurrentType(0, &media); format = *(WAVEFORMATEX*)media.pbFormat; } // make Stack m_readBufferSize = format.nSamplesPerSec*format.nBlockAlign; for (UINT i=0; im_bAutoDelete = FALSE; m_captureThread->ResumeThread(); ::Sleep(10); return TRUE; } void CWinAudioStream::stopCapture(void) { if (m_hStopEvent!=NULL) { SetEvent(m_hStopEvent); // if (m_captureThread!=NULL) { // スレッドが終了するまで待つ WaitForSingleObject(m_captureThread->m_hThread, INFINITE); CloseHandle(m_captureThread->m_hThread); delete m_captureThread; m_captureThread = NULL; } CloseHandle(m_hStopEvent); m_hStopEvent = NULL; } // if (m_hDataReady!=NULL) { SetEvent(m_hDataReady); CloseHandle(m_hDataReady); m_hDataReady = NULL; } // Back2BufferStackAll(); // delete Stack while(!m_bufferStack.empty()) { CWinMediaBuffer* pMediaBuf = m_bufferStack.top(); delete pMediaBuf; m_bufferStack.pop(); } // free_Buffer(&m_streamData); return; } BOOL CWinAudioStream::openOutput(ULONG bufcount, WAVEFORMATEX* fmt) { if (m_pMediaObj==NULL && fmt==NULL) return FALSE; // WAVEFORMATEX format; if (fmt!=NULL) { format = *fmt; } else { DMO_MEDIA_TYPE media; m_pMediaObj->GetOutputCurrentType(0, &media); format = *(WAVEFORMATEX*)media.pbFormat; } // MMRESULT ret = ::waveOutOpen(&m_hWave, WAVE_MAPPER, &format, NULL, NULL, 0); if (ret!=MMSYSERR_NOERROR) return FALSE; m_outputBufferNum = bufcount; ULONG bufsz = sizeof(WAVEHDR)*m_outputBufferNum; m_waveHeaders = (WAVEHDR*)malloc(bufsz); memset(m_waveHeaders, 0, bufsz); for (ULONG i=0; i0 && IsCapturing()) { ReadOneBuffer((BYTE**)&pData, &bytesPendingToRead); if (m_readBuffer==NULL) WaitForSingleObject(m_hDataReady, INFINITE); } ULONG bytesRead = cbBuffer - bytesPendingToRead; // 読み込んだデータ長 m_readBufferCount += bytesRead; *pcbRead = bytesRead; return S_OK; } STDMETHODIMP CWinAudioStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition ) { if (plibNewPosition!=NULL) plibNewPosition->QuadPart = m_readBufferCount + dlibMove.QuadPart; return S_OK; } STDMETHODIMP CWinAudioStream::Write(const void* pbuf, ULONG bufsz, ULONG* dummy) { if (pbuf==NULL || bufsz==0 || bufsz>MaxOutputBuffers) return E_INVALIDARG; WAVEHDR* header = &m_waveHeaders[m_outputBufferIndex]; if (!(header->dwFlags & WHDR_DONE)) return E_FAIL ; MMRESULT ret = ::waveOutUnprepareHeader(m_hWave, header, sizeof(WAVEHDR)); if (ret!=MMSYSERR_NOERROR) return E_FAIL; header->dwBufferLength = (DWORD)bufsz; header->dwFlags = 0; memcpy(header->lpData, pbuf, bufsz); ret = ::waveOutPrepareHeader(m_hWave, header, sizeof(WAVEHDR)); if (ret!=MMSYSERR_NOERROR) { header->dwFlags = WHDR_DONE; return E_FAIL; } ret = ::waveOutWrite(m_hWave, header, sizeof(WAVEHDR)); if (ret!=MMSYSERR_NOERROR) { header->dwFlags = WHDR_DONE; return E_FAIL; } m_outputBufferIndex = (m_outputBufferIndex + 1) % m_outputBufferNum; return S_OK; } //////////////////////////////////////////////////////////////////////////////////// // Private CWinAudioStream methods CWinMediaBuffer* CWinAudioStream::GetWriteBuffer() { CWinMediaBuffer* pMediaBuf = NULL; EnterCriticalSection(&m_lock); if (m_bufferStack.size()>0) { pMediaBuf = m_bufferStack.top(); m_bufferStack.pop(); pMediaBuf->SetLength(0); } else if (m_bufferQueue.size()>0) { pMediaBuf = m_bufferQueue.front(); m_bufferQueue.pop(); pMediaBuf->SetLength(0); } LeaveCriticalSection(&m_lock); return pMediaBuf; } void CWinAudioStream::Back2BufferStack(CWinMediaBuffer* pMediaBuf) { if (pMediaBuf!=NULL) { EnterCriticalSection(&m_lock); // pMediaBuf->SetLength(0); m_bufferStack.push(pMediaBuf); // LeaveCriticalSection(&m_lock); } } void CWinAudioStream::Back2BufferStackAll() { EnterCriticalSection(&m_lock); while (m_bufferQueue.size()>0) { CWinMediaBuffer* pMediaBuf = m_bufferQueue.front(); m_bufferQueue.pop(); Back2BufferStack(pMediaBuf); } if (m_readBuffer!=NULL) Back2BufferStack(m_readBuffer); m_readBufferIndex = 0; m_readBuffer = NULL; // LeaveCriticalSection(&m_lock); } void CWinAudioStream::QueueCapturedData(BYTE* pData, UINT cbData) { if (cbData<=0) return; if (m_writeBuffer==NULL) m_writeBuffer = GetWriteBuffer(); if (m_writeBuffer==NULL) return; // BYTE* pWriteData = NULL; DWORD cbWriteData = 0; DWORD cbMaxLength = 0; // m_writeBuffer->GetBufferAndLength(&pWriteData, &cbWriteData); m_writeBuffer->GetMaxLength(&cbMaxLength); if (cbWriteData+cbDataSetLength(cbWriteData + cbData); } else { QueueCapturedBuffer(m_writeBuffer); // m_writeBuffer = GetWriteBuffer(); m_writeBuffer->GetBufferAndLength(&pWriteData, &cbWriteData); // memcpy(pWriteData, pData, cbData); m_writeBuffer->SetLength(cbData); } } void CWinAudioStream::QueueCapturedBuffer(CWinMediaBuffer* pMediaBuf) { EnterCriticalSection(&m_lock); m_bufferQueue.push(pMediaBuf); SetEvent(m_hDataReady); LeaveCriticalSection(&m_lock); } void CWinAudioStream::ReadOneBuffer(BYTE** ppbData, ULONG* pcbData) { EnterCriticalSection(&m_lock); // if (m_readBuffer==NULL) { if(m_bufferQueue.size()!=0) { m_readBuffer = m_bufferQueue.front(); m_bufferQueue.pop(); } } // if (m_readBuffer!=NULL) { //Copy as much data as we can or need BYTE* pData = NULL; DWORD dwDataLength = 0; m_readBuffer->GetBufferAndLength(&pData, &dwDataLength); ULONG cbToCopy = min(dwDataLength-m_readBufferIndex, *pcbData); memcpy(*ppbData, pData+m_readBufferIndex, cbToCopy); *ppbData = (*ppbData) + cbToCopy; *pcbData = (*pcbData) - cbToCopy; m_readBufferIndex += cbToCopy; // if (m_readBufferIndex>=dwDataLength) { Back2BufferStack(m_readBuffer); m_readBuffer = NULL; m_readBufferIndex = 0; if (m_bufferQueue.size()!=0) { m_readBuffer = m_bufferQueue.front(); m_bufferQueue.pop(); } } } LeaveCriticalSection(&m_lock); } UINT CWinAudioStream::CaptureThread(LPVOID pParam) { CWinAudioStream* pthis = (CWinAudioStream*)pParam; return pthis->CaptureThread(); } UINT CWinAudioStream::CaptureThread(void) { HANDLE mmHandle = NULL; DWORD mmTaskIndex = 0; HRESULT hr = S_OK; bool bContinue = true; BYTE* pbOutputBuffer = NULL; CWinMediaBuffer outputBuffer(m_readBufferSize); DMO_OUTPUT_DATA_BUFFER OutputBufferStruct = {0}; OutputBufferStruct.pBuffer = (IMediaBuffer*)&outputBuffer; DWORD dwStatus = 0; ULONG cbProduced = 0; // mmHandle = AvSetMmThreadCharacteristics(_T("Audio"), &mmTaskIndex); while(bContinue) { // if (WaitForSingleObject(m_hStopEvent, 0)==WAIT_OBJECT_0) { bContinue = false; continue; } do { outputBuffer.clear(); OutputBufferStruct.dwStatus = 0; hr = m_pMediaObj->ProcessOutput(0, 1, &OutputBufferStruct, &dwStatus); if (FAILED(hr)) { bContinue = false; break; } else if (hr==S_FALSE) { cbProduced = 0; } else { outputBuffer.GetBufferAndLength(&pbOutputBuffer, &cbProduced); } // Queue にデータを保存 if (cbProduced>0) { QueueCapturedData(pbOutputBuffer, cbProduced); //Write(pbOutputBuffer, cbProduced, NULL); // for TEST } } while (OutputBufferStruct.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE); // Sleep(10); } // SetEvent(m_hDataReady); AvRevertMmThreadCharacteristics(mmHandle); // if (FAILED(hr)) return FALSE; return TRUE; }