📄 winaudio.cpp
字号:
// We must check the output format and convert to MS if possible
switch (mmTime.wType)
{
case TIME_MS:
{
ulDeviceTime = mmTime.u.ms;
#if defined(_WIN32)
ULONG32 BitsPerSample = (pWFmt->nChannels * pWFmt->wBitsPerSample);
#elif _WIN16
ULONG32 BitsPerSample = (pWFmt->nAvgBytesPerSec * 8 / pWFmt->nSamplesPerSec);
#endif
ULONG32 BitsPerSecond = (BitsPerSample * pWFmt->nSamplesPerSec);
ulDeviceBytesPlayed = (mmTime.u.ms * BitsPerSecond) / 8000;
}
break;
case TIME_SAMPLES:
{
// Convert samples to MS
ulDeviceSamplesPlayed = mmTime.u.sample;
if (m_ulLastDeviceSamplesPlayed > ulDeviceSamplesPlayed &&
((m_ulLastDeviceSamplesPlayed - ulDeviceSamplesPlayed) > MAX_TIMESTAMP_GAP))
{
m_ulDevPosRollOver++;
}
m_ulLastDeviceSamplesPlayed = ulDeviceSamplesPlayed;
m_llDeviceSamplesPlayed = (INT64)ulDeviceSamplesPlayed + (INT64)m_ulDevPosRollOver * (INT64)MAX_UINT32;
ulDeviceTime = (ULONG32)(((float)m_llDeviceSamplesPlayed/(float)pWFmt->nSamplesPerSec)*1000.f);
#if defined(_WIN32)
ULONG32 BitsPerSample = (pWFmt->nChannels * pWFmt->wBitsPerSample);
#elif _WIN16
ULONG32 BitsPerSample = (pWFmt->nAvgBytesPerSec * 8 / pWFmt->nSamplesPerSec);
#endif
ulDeviceBytesPlayed = (ULONG32)(mmTime.u.sample * 8.0f) / BitsPerSample;
}
break;
case TIME_BYTES:
{
// Convert Bytes to MS
ulDeviceBytesPlayed = mmTime.u.cb;
if (m_ulLastDeviceBytesPlayed > ulDeviceBytesPlayed &&
((m_ulLastDeviceBytesPlayed - ulDeviceBytesPlayed) > MAX_TIMESTAMP_GAP))
{
m_ulDevPosRollOver++;
}
m_ulLastDeviceBytesPlayed = ulDeviceBytesPlayed;
m_llDeviceBytesPlayed = (INT64)ulDeviceBytesPlayed + (INT64)m_ulDevPosRollOver * (INT64)MAX_UINT32;
//{FILE* f1 = ::fopen("c:\\audio.txt", "a+"); ::fprintf(f1, "ulDeviceBytesPlayed: %lu\n", ulDeviceBytesPlayed);::fclose(f1);}
#if defined(_WIN32)
ULONG32 BitsPerSample = (pWFmt->nChannels * pWFmt->wBitsPerSample);
#elif _WIN16
ULONG32 BitsPerSample = (pWFmt->nAvgBytesPerSec * 8 / pWFmt->nSamplesPerSec);
#endif
ULONG32 BitsPerSecond = (BitsPerSample * pWFmt->nSamplesPerSec);
ulDeviceTime = (ULONG32)((m_llDeviceBytesPlayed * 8000)/(INT64)BitsPerSecond); }
break;
}
// ?? ulCurrentTime = ulDeviceTime + m_StreamTimeOffset + m_CorrectionOffset;
ulCurrentTime = m_ulCurrentTime = ulDeviceTime;
}
else
{
ulCurrentTime = m_ulCurrentTime = 0;
}
//{FILE* f1 = ::fopen("c:\\audio.txt", "a+"); ::fprintf(f1, "%lu\t%lu\n", HX_GET_TICKCOUNT(), m_ulCurrentTime);::fclose(f1);}
return HXR_OK;
}
/************************************************************************
* Method:
* CAudioOutWindows::WaveOutProc
* Purpose:
* This static member function is called when the wave
* device is finished playing its audio buffer. We use
* it to tell audio services that we're ready for more.
*/
#if defined(_WIN32)
LRESULT __declspec(dllexport) CALLBACK
#else
LRESULT CALLBACK __export
#endif
CAudioOutWindows::WaveOutWndProc(HWND hWnd, // handle of window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter)
{
DWORD dwParam1 = lParam;
CAudioOutWindows* pThis = 0;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* lpCreate = 0;
// Set our this pointer, so our WndProc can find us again
lpCreate = (CREATESTRUCT FAR*) lParam;
pThis = (CAudioOutWindows*) lpCreate->lpCreateParams;
SetWindowLong(hWnd, OFFSET_THIS, (long) pThis);
}
else if (uMsg == WM_NCDESTROY)
{
// remove our this pointer so if somebody calls this function
// again after the window is gone (and the object is gone
// too) we don't try to call a method from the pointer
SetWindowLong(hWnd, OFFSET_THIS, 0L);
}
else
{
pThis = (CAudioOutWindows*) (LPHANDLE)GetWindowLong(hWnd, OFFSET_THIS);
}
if (!pThis)
{
goto exit;
}
switch (uMsg)
{
case MM_WOM_DONE:
HX_ASSERT(zm_bClosed || zm_pCurrentAudioDevice == pThis);
if (!zm_bClosed && zm_pCurrentAudioDevice == pThis)
{
// Only do this on WOM_DONE, note there are other messages sent to this
// callback function, like WOM_OPEN and WOM_CLOSE
//DWORD dwTime1 = timeGetTime();
//OutputDebugString("START:(uMsg == MM_WOM_DONE) in WaveOutThreadProc\r\n");
pThis->m_pMutex->Lock();
if (zm_bClosed)
{
pThis->m_pMutex->Unlock();
goto exit;
}
WAVEHDR* pWAVEHDR;
CWaveHeader* pWaveHeader;
pWAVEHDR = (WAVEHDR*)dwParam1;
pWaveHeader = (CWaveHeader*)pWAVEHDR->dwUser;
/* We may have already removed it from the queue
* while checking for _NumberOfBlocksRemainingToPlay
*/
if ((pWAVEHDR->dwFlags & WHDR_DONE) &&
!pThis->m_UsedBuffersList.IsEmpty() &&
pThis->m_UsedBuffersList.GetHead() == (void*) pWaveHeader)
{
// put it back in the queue
pWaveHeader->m_bAvail = TRUE;
pThis->m_rAvailBuffers.EnQueuePtr(pWaveHeader);
/* This is assuming that we get WOM_DONEs sequentially
* There seems to be a bug in WIN16 where it may return
* WOM_DONEs out of order. Need to be fixed for that
* XXX Rahul
*/
pThis->m_UsedBuffersList.RemoveHead();
}
pThis->m_pMutex->Unlock();
if (pThis &&
!pThis->m_bResetting)
{
// Call the base class's OnTimeSync to let it know that we're done playing
// the current chunk of audio
//OutputDebugString("BEFORE:pThis->OnTimeSync()\r\n");
pThis->OnTimeSync();
}
}
break;
case MM_WOM_OPEN:
break;
case MM_WOM_CLOSE:
break;
default:
break;
}
if (uMsg == zm_uDestroyMessage)
{
LRESULT result = (LRESULT)DestroyWindow(hWnd);
// free the memory used by this class now that our window is destroyed
UnregisterClass(WND_CLASS, g_hInstance);
return result;
}
exit:
return ( DefWindowProc(hWnd, uMsg, wParam, lParam) );
}
UINT16 CAudioOutWindows::_NumberOfBlocksRemainingToPlay(void)
{
m_pMutex->Lock();
UINT16 unRemaining = m_UsedBuffersList.GetCount();
/* There may be some buffers for which we have not receoved WOM_DONEs
* but they have already been played
*/
if (unRemaining > 0)
{
LISTPOSITION ndxLastUsed = NULL;
LISTPOSITION ndxUsed = m_UsedBuffersList.GetHeadPosition();
while (ndxUsed != NULL)
{
ndxLastUsed = ndxUsed;
CWaveHeader* pWaveHeader =
(CWaveHeader*) m_UsedBuffersList.GetNext(ndxUsed);
if (pWaveHeader->m_WAVEHDR.dwFlags & WHDR_DONE)
{
if (unRemaining > 0)
unRemaining--;
// put it back in the queue
pWaveHeader->m_bAvail = TRUE;
m_rAvailBuffers.EnQueuePtr(pWaveHeader);
m_UsedBuffersList.RemoveAt(ndxLastUsed);
}
else
{
/* We assume that subsequent blocks are also not done playing */
break;
}
}
}
m_pMutex->Unlock();
return unRemaining;
}
#if defined(_WIN32) && !defined(_WINCE)
void
CAudioOutWindows::CheckForVolumeSupport()
{
zm_bMixerVolSupportChecked = TRUE;
if (!m_hWnd)
{
Register();
}
// if there is more than 1 mixer, we need to find out which is the current one
// we do this by determining the mixer being used by the current wave device
int startIndex = 0;
if (m_hWave != NULL && mixerGetNumDevs() > 1)
{
UINT nId = -1;
if ((mixerGetID((HMIXEROBJ)m_hWave, &nId, MIXER_OBJECTF_HWAVEOUT) == MMSYSERR_NOERROR) && nId != -1)
{
startIndex = nId;
}
}
for(UINT index = startIndex; index < mixerGetNumDevs(); index++)
{
if(mixerOpen(&m_hMixer, index, (DWORD)m_hWnd, 0, CALLBACK_WINDOW)
== MMSYSERR_NOERROR)
{
MIXERLINE mxLine;
mxLine.cbStruct = sizeof(MIXERLINE);
mxLine.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
if(mixerGetLineInfo((HMIXEROBJ)m_hMixer, &mxLine,
MIXER_GETLINEINFOF_COMPONENTTYPE | MIXER_OBJECTF_HMIXER)
== MMSYSERR_NOERROR)
{
// use waveOutSetVolume for bad device(i.e. crystal audio)
if (zm_audioDevice == HXAUDIO_UNKNOWN && mxLine.Target.szPname)
{
for (int i = 0; i < g_nBadDrivers; i++)
{
if (_tcsstr(mxLine.Target.szPname, g_badDrivers[i]))
{
zm_audioDevice = HXAUDIO_BADDEVICE;
break;
}
}
if (zm_audioDevice == HXAUDIO_UNKNOWN)
{
zm_audioDevice = HXAUDIO_GOODDEVICE;
}
}
if (zm_audioDevice != HXAUDIO_BADDEVICE)
{
MIXERCONTROL mxControl;
mxControl.cbStruct = sizeof(MIXERCONTROL);
MIXERLINECONTROLS mxLineControls;
mxLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
mxLineControls.dwLineID = mxLine.dwLineID;
mxLineControls.cControls = 1;
mxLineControls.cbmxctrl = mxControl.cbStruct;
mxLineControls.pamxctrl = &mxControl;
mxLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME |
MIXERCONTROL_CONTROLF_UNIFORM;
if(mixerGetLineControls((HMIXEROBJ)m_hMixer, &mxLineControls,
MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER)
== MMSYSERR_NOERROR)
{
if(mxLineControls.cControls)
{
zm_bVolSupport = TRUE;
zm_bMixerVolSupport = TRUE;
m_VolumeControlDetails.cbStruct =
sizeof(MIXERCONTROLDETAILS);
m_VolumeControlDetails.dwControlID =
mxControl.dwControlID;
m_VolumeControlDetails.cChannels = 1;
m_VolumeControlDetails.cMultipleItems =
mxControl.cMultipleItems;
}
}
}
}
break;
}
}
}
#endif /*_WIN32*/
HX_RESULT
CAudioOutWindows::Register()
{
WNDCLASS internalClass;
// OutputDebugString("BEFORE CALL TO:Register\r\n");
if (m_hWnd)
{
return HXR_OK;
}
//m_hInst = hInst;
m_hInst = g_hInstance;
if (!m_hInst)
{
#ifdef _DEBUG
MessageBox(NULL, _T("Don't have a valid handle"), NULL, MB_OK);
#endif
return HXR_OUTOFMEMORY;
}
// XXXKM - let's see if we can get the class info first; added this additional
// check due to a strange problem when registering a class that seems to already
// be registered. For some reason, under some circumstance, RegisterClass returns
// NULL and GetLastError returns 0x57 (invalid parameter), however the class
// is already registered
if (!::GetClassInfo(m_hInst, WND_CLASS, &internalClass))
{
// First register our window class
internalClass.style = 0;
internalClass.lpfnWndProc = CAudioOutWindows::WaveOutWndProc;
internalClass.cbClsExtra = 0;
internalClass.cbWndExtra = sizeof( this );
internalClass.hInstance = m_hInst; // Use app's instance
internalClass.hIcon = 0;
internalClass.hCursor = 0;
internalClass.hbrBackground = 0;
internalClass.lpszMenuName = NULL;
internalClass.lpszClassName = WND_CLASS;
#ifdef _WIN16
if (!RegisterClass( &internalClass ))
#else
if (!RegisterClass( &internalClass ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
#endif /* _WIN16 */
{
#ifdef _DEBUG
MessageBox(NULL, _T("Could Not register class"), NULL, MB_OK);
#endif
return(HXR_OUTOFMEMORY);
}
m_bClassRegistered = TRUE;
}
// OutputDebugString("BEFORE CALL TO:CreateWindow\r\n");
// Now create an instance of the window
m_hWnd = CreateWindow( WND_CLASS /*"AudioServicesInternal"*/, _T("Audio Services Internal Messages"),
WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, m_hInst, this);
#if defined(_WIN32)
m_ulOriginalThreadId = GetCurrentThreadId();
#endif
if (!m_hWnd)
{
#ifdef _DEBUG
MessageBox(NULL, _T("Could Not create messageWindow"), NULL, MB_OK);
#endif
return HXR_OUTOFMEMORY;
}
return HXR_OK;
}
void
CAudioOutWindows::UnRegister()
{
// OutputDebugString("BEFORE CALL TO:UnRegister\r\n");
// Ask the window to destroy itself
if (m_hWnd)
{
#if defined(_WIN32)
if (m_ulOriginalThreadId == GetCurrentThreadId())
{
SendMessage(m_hWnd, zm_uDestroyMessage, 0, 0);
}
else
{
PostMessage(m_hWnd, zm_uDestroyMessage, 0, 0);
Sleep(0);
}
#else
SendMessage(m_hWnd, zm_uDestroyMessage, 0, 0);
#endif
m_hWnd = NULL;
}
/*
// Ask the window to destroy itself
if (m_hWnd && SendMessage(m_hWnd, zm_uDestroyMessage, 0, 0))
{
m_hWnd = NULL;
}
// free the memory used by this class now that our window is destroyed
UnregisterClass(WND_CLASS, g_hInstance);
*/
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -