/* Copyright 2004 XDA-Developers.com * All Rights Reserved * author: itsme@xs4all.nl * * $Header$ * * c:\local\WINCE420\PUBLIC\COMMON\SDK\INC\mmsystem.h * */ #include "audiodev.h" #include "cenk.H" #include "kernelmisc.h" #include #include "stringutils.h" void DumpWH(WAVEHDR *pwh) { debug("pwh=%08lx data=%08lx rec=%08lx len=%08lx flags=%x loops=%x usr=%x\n", pwh, pwh->lpData, pwh->dwBytesRecorded, pwh->dwBufferLength, pwh->dwFlags, pwh->dwLoops, pwh->dwUser); } AudioRecorder::AudioRecorder(int samplesperblock, int blocks, int samplerate) : m_queue(4) { m_samplesperblock= samplesperblock; m_samplerate= samplerate; m_nBlocks= blocks; m_wavehdrptrs.clear(); m_wavehdrs.clear(); m_timestamps.clear(); m_bufferlist.clear(); m_whlist.clear(); m_bRunning= false; } AudioRecorder::~AudioRecorder() { } bool AudioRecorder::CopyInputBuffer( WAVEHDR *pwh) { return true; } void AudioRecorder::HandleSource(DWORD msec) { WAVEHDR *pwh; debug("processing queue\n"); DWORD t0= GetTickCount(); while (m_queue.GetWait(pwh) && GetTickCount()-t0dwUser); } else { debug("WARNING: AudioRecorder::HandleSource stopping, dropped buffer %d\n", pwh->dwUser); } } debug("done processing queue : %d msec\n", GetTickCount()-t0); } void CALLBACK AudioRecorder::waveInProc( HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { AudioRecorder* obj= (AudioRecorder*)dwInstance; obj->HandleWaveCallback(uMsg, (WAVEHDR*)dwParam1); } void AudioRecorder::HandleWaveCallback(UINT uMsg, WAVEHDR* pwh) { switch(uMsg) { case WIM_OPEN: m_bRunning= true; break; case WIM_DATA: m_wavehdrptrs.push_back(pwh); m_wavehdrs.push_back(*pwh); m_timestamps.push_back(GetTickCount()); m_queue.Add(pwh); break; case WIM_CLOSE: m_bRunning= false; break; } } bool AudioRecorder::UnPrepareInputBuffer( WAVEHDR *pwh ) { MMRESULT res= waveInUnprepareHeader( m_hwi, pwh, sizeof( WAVEHDR ) ); if( res!=MMSYSERR_NOERROR ) { debug("ERROR: waveInUnprepareHeader: %08lx\n", res); return false; } return true; } bool AudioRecorder::PrepareAndSubmitBuffer(int buffernr) { m_bufferlist[buffernr].resize(m_samplesperblock); WAVEHDR *pwh= &m_whlist[buffernr]; pwh->dwBytesRecorded= 0; pwh->dwFlags= 0; pwh->dwLoops= 0; pwh->dwUser= buffernr; pwh->dwBufferLength= m_samplesperblock*sizeof(sample_t); //debug("srcAudioInput::PrepareAndSubmitBuffer: vptr=%08lx data=%08lx\n", vectorptr(m_bufferlist[buffernr]), pwh->lpData); pwh->lpData= (char*)vectorptr(m_bufferlist[buffernr]); //debug("inside prepare: "); DumpWH(pwh); DWORD res= waveInPrepareHeader( m_hwi, pwh, sizeof( WAVEHDR ) ); if( res!=MMSYSERR_NOERROR ) { debug("ERROR waveInPrepareHeader: %08lx\n", res); return false; } //debug("inside prepare2: "); DumpWH(pwh); res= waveInAddBuffer(m_hwi, pwh, sizeof( WAVEHDR ) ); if (res!=MMSYSERR_NOERROR ) { debug("ERROR waveInAddBuffer: %08lx\n", res); return false; } //debug("exiting prepare: "); DumpWH(pwh); return true; } bool AudioRecorder::StartAudioInput() { WAVEFORMATEX wfe; wfe.nSamplesPerSec= m_samplerate; wfe.wFormatTag= WAVE_FORMAT_PCM; wfe.nChannels= 1; wfe.wBitsPerSample= 16; wfe.nBlockAlign= (wfe.nChannels*wfe.wBitsPerSample)/8; wfe.nAvgBytesPerSec= m_samplerate*wfe.nBlockAlign; wfe.cbSize= 0; debug("AudioRecorder::StartAudioInput: nrof recording devs: %d\n", waveInGetNumDevs()); // initialize audio input MMRESULT res= waveInOpen( &m_hwi, WAVE_MAPPER, &wfe, (DWORD_PTR)waveInProc, (DWORD)this, CALLBACK_FUNCTION ); if( res!=MMSYSERR_NOERROR ) { debug("ERROR: AudioRecorder::StartAudioInput: waveInOpen: %08lx\n", res); m_hwi= NULL; return false; } res= waveInStop(m_hwi); if (res!=MMSYSERR_NOERROR ) { debug("ERROR: AudioRecorder::StartAudioInput: waveInStop: %08lx\n", res); return false; } m_whlist.resize(m_nBlocks); m_bufferlist.resize(m_nBlocks); for (int i=0 ; idwUser); } else { debug("WARNING: AudioPlayer::HandleSource stopping, dropped buffer %d\n", pwh->dwUser); } } debug("done processing queue : %d msec\n", GetTickCount()-t0); } void CALLBACK AudioPlayer::waveOutProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { AudioPlayer* obj= (AudioPlayer*)dwInstance; obj->HandleWaveCallback(uMsg, (WAVEHDR*)dwParam1); } void AudioPlayer::HandleWaveCallback(UINT uMsg, WAVEHDR* pwh) { switch(uMsg) { case WOM_OPEN: m_bRunning= true; break; case WOM_DONE: m_wavehdrptrs.push_back(pwh); m_wavehdrs.push_back(*pwh); m_timestamps.push_back(GetTickCount()); m_queue.Add(pwh); break; case WOM_CLOSE: m_bRunning= false; break; } } bool AudioPlayer::UnPrepareOutputBuffer( WAVEHDR *pwh ) { MMRESULT res= waveOutUnprepareHeader( m_hwo, pwh, sizeof( WAVEHDR ) ); if( res!=MMSYSERR_NOERROR ) { debug("ERROR: waveOutUnprepareHeader: %08lx\n", res); return false; } return true; } bool AudioPlayer::PrepareAndSubmitBuffer(int buffernr) { m_bufferlist[buffernr].resize(m_samplesperblock); WAVEHDR *pwh= &m_whlist[buffernr]; pwh->dwBytesRecorded= 0; pwh->dwFlags= 0; pwh->dwLoops= 0; pwh->dwUser= buffernr; pwh->dwBufferLength= m_samplesperblock*sizeof(sample_t); pwh->lpData= (char*)vectorptr(m_bufferlist[buffernr]); DWORD res= waveOutPrepareHeader( m_hwo, pwh, sizeof( WAVEHDR ) ); if( res!=MMSYSERR_NOERROR ) { debug("ERROR waveInPrepareHeader: %08lx\n", res); return false; } res= waveOutWrite(m_hwo, pwh, sizeof( WAVEHDR ) ); if (res!=MMSYSERR_NOERROR ) { debug("ERROR waveOutWrite: %08lx\n", res); return false; } return true; } bool AudioPlayer::StartAudioOutput() { WAVEFORMATEX wfe; wfe.nSamplesPerSec= m_samplerate; wfe.wFormatTag= WAVE_FORMAT_PCM; wfe.nChannels= 1; wfe.wBitsPerSample= 16; wfe.nBlockAlign= (wfe.nChannels*wfe.wBitsPerSample)/8; wfe.nAvgBytesPerSec= m_samplerate*wfe.nBlockAlign; wfe.cbSize= 0; debug("AudioPlayer::StartAudioOutput: nrof playing devs: %d\n", waveOutGetNumDevs()); // initialize audio output MMRESULT res= waveOutOpen( &m_hwo, WAVE_MAPPER, &wfe, (DWORD_PTR)waveOutProc, (DWORD)this, CALLBACK_FUNCTION ); if( res!=MMSYSERR_NOERROR ) { debug("ERROR: AudioPlayer::StartAudioOutput: waveOutOpen: %08lx\n", res); m_hwo= NULL; return false; } res= waveOutPause(m_hwo); if (res!=MMSYSERR_NOERROR ) { debug("ERROR: AudioPlayer::StartAudioOutput: waveOutPause: %08lx\n", res); return false; } m_whlist.resize(m_nBlocks); m_bufferlist.resize(m_nBlocks); for (int i=0 ; i ThreadTimeInfoMap; // accessing THREAD->dwKernTime works fine for wce4+wce5 but is not binary compatible with wce3 void GetProcessThreadTimes(DWORD pid, ThreadTimeInfoMap& times) { HDATA *hdata= cvHandle2HDataPtr((HANDLE)pid); PROCESS *pproc= (PROCESS *)hdata->pvObj; times.clear(); THREAD *pth= pproc->pTh; while(pth) { ThreadTimeInfo &info= times[(DWORD)pth->hTh]; info.kernel = pth->dwKernTime; info.user = pth->dwUserTime; info.address = pth->dwStartAddr; info.current = GetTickCount(); pth= pth->pNextInProc; } } DWORD FindProcessId(const std::string& processname) { DWORD dwProc= 0; HANDLE hTH= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS|TH32CS_SNAPNOHEAPS, 0); std::Wstring wprocname= ToWString(processname); PROCESSENTRY32 pe; pe.dwSize= sizeof(PROCESSENTRY32); if (Process32First(hTH, &pe)) { do { if (wcsicmp(wprocname.c_str(), pe.szExeFile)==0) { dwProc= pe.th32ProcessID; break; } } while (Process32Next(hTH, &pe)); } CloseToolhelp32Snapshot(hTH); return dwProc; } DWORD FindMaxPid(const ThreadTimeInfoMap& ti0, const ThreadTimeInfoMap& ti1) { DWORD maxTid=0; DWORD maxKernel=0; for (ThreadTimeInfoMap::const_iterator i0=ti0.begin() ; i0!=ti0.end() ; ++i0) { ThreadTimeInfoMap::const_iterator i1= ti1.find((*i0).first); if (i1==ti1.end()) continue; DWORD diffKernel= (*i1).second.kernel-(*i0).second.kernel; if (diffKernel>maxKernel) { maxKernel= diffKernel; maxTid= (*i0).first; } } return maxTid; } double calcsamplerate(DWORD nSamplesPerBlock, const DwordVector& timestamps) { if (timestamps.size()<=1) { debug("WARNING: .... can't calc sr from %d samples, spd=%d\n", timestamps.size(), nSamplesPerBlock); return 0; } int i; for (i=timestamps.size()-1 ; i>2 && (timestamps[i]-timestamps[i-1])*10 < (timestamps[2]-timestamps[0])/2 ; i--) ; return 1000.0*nSamplesPerBlock*i/(timestamps[i] - timestamps[0]); } void testaudiodev(int nBlocks, DWORD rate, DWORD nSamplesPerBlock, DWORD dwPidDevExe, DWORD dwTestTime) { debug("testing audio input(%d, %d, %d)\n", nBlocks, rate, nSamplesPerBlock); AudioRecorder audioin(nSamplesPerBlock, nBlocks, rate); ThreadTimeInfoMap ti0; GetProcessThreadTimes(dwPidDevExe, ti0); audioin.StartAudioInput(); audioin.HandleSource(dwTestTime); audioin.StopAudioInput(); ThreadTimeInfoMap ti1; GetProcessThreadTimes(dwPidDevExe, ti1); DWORD tidmax= FindMaxPid(ti0, ti1); DWORD measurementtime=0; double pmUser= 0; double pmKernel= 0; if (tidmax) { measurementtime= ti1[tidmax].current-ti0[tidmax].current; if (measurementtime) { pmUser= (ti1[tidmax].user-ti0[tidmax].user)*100.0/measurementtime; pmKernel= (ti1[tidmax].kernel-ti0[tidmax].kernel)*100.0/measurementtime; } } double measuredrate= calcsamplerate(nSamplesPerBlock, audioin.m_timestamps); debug("params[%5d %5d %d] (chunk= %6.2f msec) cpu{ %08lx:%08lx %5.1f%% %5.1f%%} : rate: %5.0f (%5.2f) :\n", rate, nSamplesPerBlock, nBlocks, 1000.0*nSamplesPerBlock / rate, tidmax, ti0[tidmax].address, pmUser, pmKernel, measuredrate, (double)measuredrate/rate); size_t i; debug("dt "); for (i=1 ; i( 12, 8000, 8001,11000,11025,11026, 16000,22000,22050,24000,32000, 44100,48000); //DwordVector testrates= MakeVector( 2, 8000,11025); DWORD dwPidDevExe= FindProcessId("device.exe"); debug("device.exe pid=%08lx\n", dwPidDevExe); // void testaudiodev(int nBlocks, DWORD rate, DWORD nSamplesPerBlock, DWORD dwPidDevExe, DWORD dwTestTime) for (int nBlocks=2 ; nBlocks<5 ; nBlocks++) for (DwordVector::iterator prate=testrates.begin() ; prate!=testrates.end() ; ++prate) for (DWORD nSamplesPerBlock= 5 * *prate/100; nSamplesPerBlock<=40 * (*prate/100) ; nSamplesPerBlock+=5 * *prate/100 ) testaudiodev(nBlocks, *prate, nSamplesPerBlock, dwPidDevExe, 4000); int i,j,k; for (j=880 ; j<1100 ; j++) testaudiodev(3, 8000, j, dwPidDevExe, 1000); for (k=2 ; k<10 ; k++) testaudiodev(k, 8000, 1000, dwPidDevExe, 5000); for (k=2 ; k<10 ; k++) testaudiodev(k, 8000, 940, dwPidDevExe, 5000); /* for (k=3 ; k<8 ; k++) for (j=1 ; j<=40 ; j++) for (i=0 ; i<10 ; i++) testaudiodev(k, 8000, 110*j, dwPidDevExe, 1000); for (k=3 ; k<8 ; k++) for (j=1 ; j<=40 ; j++) for (i=0 ; i<10 ; i++) testaudiodev(k, 11025, 110*j, dwPidDevExe, 1000); */ SetProcPermissions(dwPerm); SetKMode(bMode); debug("finished measuring\n"); return 0; }