📄 hxaudevds.cpp
字号:
/* ***** BEGIN LICENSE BLOCK *****
* Version: RCSL 1.0/RPSL 1.0
*
* Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
*
* The contents of this file, and the files included with this file, are
* subject to the current version of the RealNetworks Public Source License
* Version 1.0 (the "RPSL") available at
* http://www.helixcommunity.org/content/rpsl unless you have licensed
* the file under the RealNetworks Community Source License Version 1.0
* (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
* in which case the RCSL will apply. You may also obtain the license terms
* directly from RealNetworks. You may not use this file except in
* compliance with the RPSL or, if you have a valid RCSL with RealNetworks
* applicable to this file, the RCSL. Please see the applicable RPSL or
* RCSL for the rights, obligations and limitations governing use of the
* contents of the file.
*
* This file is part of the Helix DNA Technology. RealNetworks is the
* developer of the Original Code and owns the copyrights in the portions
* it created.
*
* This file, and the files included with this file, is distributed and made
* available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
*
* Technology Compatibility Kit Test Suite(s) Location:
* http://www.helixcommunity.org/content/tck
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
#include "hxtypes.h"
#if defined( _WINDOWS ) || defined( _WIN32 )
#include <windows.h>
#include <tchar.h>
#include <mmsystem.h>
#include "mmreg.h"
#endif /*defined( _WINDOWS ) || defined( _WIN32 )*/
#include <stdio.h>
#include <string.h>
#include "hxresult.h"
#include "cbqueue.h"
#include "cpqueue.h"
#include "hxslist.h"
#include "hxcom.h"
#include "hxengin.h"
#include "ihxpckts.h"
#include "hxausvc.h"
#include "auderrs.h"
#include "math.h"
#include "hxaudev.h"
#include "tsconvrt.h"
#include "hxaudevds.h"
extern HINSTANCE g_hInstance;
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif
typedef HRESULT (WINAPI* FPDIRECTSOUNDCREATE)(LPGUID lpGuid, LPDIRECTSOUND * ppDS, IUnknown FAR * pUnkOuter);
static LRESULT CALLBACK HXDSWndProc(HWND, UINT, WPARAM, LPARAM);
UINT CHXAudioDeviceDS::zm_uDestroyMessage = 0;
const UINT32 kExitThreadWaitTime = 3000; // ms
#define HXMSG_TIMESYNC WM_USER+501
const TCHAR* szTitle = _T("Helix DSWnd");
const TCHAR* szWindowClass = _T("Helix DSWndClass");
const TCHAR* kDSWaitEvent = _T("HelixDirectSoundNotifyWait");
const TCHAR* kDSDestroyMessage = _T("HX_DestroyDSWindowInternal");
const int BUFFER_TIME = 8;
extern BOOL RMEnableLogging();
extern void RMDSLog(const char* pFormatString, ...);
#define RMDS_LOG RMDSLog
CHXAudioDeviceDS::CHXAudioDeviceDS():
m_ulLastPlayCursor(0)
, m_ulLastWriteCursor(0)
, m_ulCurrPlayTime(0)
, m_ulCurrLoopTime(0)
, m_pDSDev(NULL)
, m_pPrimaryBuffer(NULL)
, m_pSecondaryBuffer(NULL)
, m_hwnd(NULL)
, m_pAudioPtrStart(NULL)
, m_hSoundDll(NULL)
, m_ulLoops(0)
, m_ulLoopTime(0)
, m_hDSNotifyEvent(NULL)
, m_hWaitThread(NULL)
, m_nBlocksPerBuffer(0)
, m_bExitThread(FALSE)
, m_ulOriginalThreadId(0)
{
// Create a unique message for destroying the audio window
if (!zm_uDestroyMessage)
{
zm_uDestroyMessage = RegisterWindowMessage(kDSDestroyMessage);
}
#ifdef _WINCE
WNDCLASS wcex;
#else
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.hIconSm = NULL;
#endif
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)HXDSWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = g_hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
#ifdef _WINCE
RegisterClass(&wcex);
#else
RegisterClassEx(&wcex);
#endif
#ifdef _WINCE
m_hwnd = ::CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPED|WS_BORDER|WS_CAPTION|WS_SYSMENU,
-5000, -5000, 1, 1, NULL, NULL, g_hInstance, NULL );
#else
m_hwnd = ::CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPEDWINDOW,
-5000, -5000, 1, 1, NULL, NULL, g_hInstance, NULL );
#endif
m_ulOriginalThreadId = GetCurrentThreadId();
m_hSoundDll = ::LoadLibrary(_T("dsound.dll"));
}
CHXAudioDeviceDS::~CHXAudioDeviceDS()
{
if (m_hSoundDll)
{
FreeLibrary(m_hSoundDll);
m_hSoundDll = NULL;
}
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;
}
}
/*
* Set the format of the primary buffer, if possible. On WDM drivers, this has
* no effect -- the kernel mixer determines that format.
*/
HX_RESULT CHXAudioDeviceDS::SetPrimaryBufferFormat()
{
HX_RESULT res = HXR_OK ;
DSBUFFERDESC bufferDesc;
::memset(&bufferDesc, 0, sizeof(DSBUFFERDESC));
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.lpwfxFormat = 0 ;
bufferDesc.dwBufferBytes = 0 ;
bufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER ;
/* close the primary buffer if we had one open before. */
HX_RELEASE(m_pPrimaryBuffer) ;
/* try to open with WAVE_FORMAT_EXTENSIBLE */
res = m_pDSDev->CreateSoundBuffer(&bufferDesc, &m_pPrimaryBuffer, NULL);
if (res == DS_OK)
{
res = !DS_OK ;
if (m_WaveFormat.Format.nChannels > 2)
{
res = m_pPrimaryBuffer->SetFormat(&m_WaveFormat.Format) ;
}
if (res != DS_OK)
{
/* if that fails, try to open with WAVE_FORMAT_PCM */
m_WaveFormat.Format.wFormatTag = WAVE_FORMAT_PCM ;
res = m_pPrimaryBuffer->SetFormat(&m_WaveFormat.Format) ;
}
}
return res ;
}
/*
* IHXAudioDevice override methods
*/
HX_RESULT CHXAudioDeviceDS::_Imp_Open(const HXAudioFormat* pFormat)
{
HX_RESULT theErr = HXR_FAIL;
if(!m_hwnd || !m_hSoundDll)
return theErr;
// close open resources
_Imp_Close() ;
/* Get the IDirectSound interface */
FPDIRECTSOUNDCREATE fpCreateDS = (FPDIRECTSOUNDCREATE) ::GetProcAddress(m_hSoundDll, _T("DirectSoundCreate"));
if(!fpCreateDS)
return theErr;
theErr = fpCreateDS(NULL, &m_pDSDev, NULL);
if (FAILED(theErr))
return theErr;
/* set the cooperative level. Because we want control over the format of the
primary buffer (16 bit, multichannel!), we need DSSCL_PRIORITY. */
m_pDSDev->SetCooperativeLevel(m_hwnd, DSSCL_PRIORITY );
/* fill out the wave format structure */
::memset(&m_WaveFormat, 0, sizeof(m_WaveFormat));
m_WaveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
m_WaveFormat.Format.nChannels = pFormat->uChannels;
m_WaveFormat.Format.nSamplesPerSec = pFormat->ulSamplesPerSec;
m_WaveFormat.Format.wBitsPerSample = pFormat->uBitsPerSample;
m_WaveFormat.Format.nBlockAlign = pFormat->uBitsPerSample/8 * pFormat->uChannels;
m_WaveFormat.Format.nAvgBytesPerSec = m_WaveFormat.Format.nBlockAlign * pFormat->ulSamplesPerSec ;
m_WaveFormat.Format.cbSize = 22;
m_WaveFormat.Samples.wValidBitsPerSample = pFormat->uBitsPerSample;
m_WaveFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM ;
m_WaveFormat.dwChannelMask = defaultChannelMapping(pFormat->uChannels) ;
/* set the format of the primary buffer. This will fail on WDM systems (because
the kernel mixer termines the primary buffer format), but is important on
non-WDM systems.
This might change the m_WaveFormat structure from a WAVE_FORMAT_EXTENSIBLE
to a WAVEFORMATEX.
Ignore the result.
*/
SetPrimaryBufferFormat() ;
/* Now open a secondary buffer. */
DSBUFFERDESC bufferDesc;
::memset(&bufferDesc, 0, sizeof(DSBUFFERDESC));
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.lpwfxFormat = &m_WaveFormat.Format;
// Manipulate the buffer size so that is is an exact multiple of the block size.
// This will ensure that our write positions on the buffer are the same in every loop.
// We need to do this so that we have fixed playback notification positions marking the end each write block.
m_nBlocksPerBuffer = (m_WaveFormat.Format.nAvgBytesPerSec*BUFFER_TIME)/pFormat->uMaxBlockSize;
m_ulTotalBuffer = pFormat->uMaxBlockSize*m_nBlocksPerBuffer;
m_ulLoopTime = (double)m_ulTotalBuffer / (double)m_WaveFormat.Format.nAvgBytesPerSec;
bufferDesc.dwBufferBytes = m_ulTotalBuffer ;
bufferDesc.dwFlags =
DSBCAPS_CTRLVOLUME | // so we can control the volume
DSBCAPS_GETCURRENTPOSITION2 | // finer position reports
DSBCAPS_CTRLPOSITIONNOTIFY | // have them reported here
DSBCAPS_GLOBALFOCUS | // take control!
DSBCAPS_STICKYFOCUS ;
/* Again, try with WAVE_FORMAT_EXTENSIBLE first, but only if multichannel. */
theErr = !DS_OK ;
if (m_WaveFormat.Format.nChannels > 2)
{
m_WaveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
theErr = m_pDSDev->CreateSoundBuffer(&bufferDesc, &m_pSecondaryBuffer, NULL);
}
if (theErr != DS_OK)
{
/* and if that fails, try WAVEFORMATEX */
m_WaveFormat.Format.wFormatTag = WAVE_FORMAT_PCM;
theErr = m_pDSDev->CreateSoundBuffer(&bufferDesc, &m_pSecondaryBuffer, NULL);
}
/* call it a day and count our blessings. */
switch (theErr)
{
case DS_OK:
theErr = HXR_OK;
break;
case DSERR_OUTOFMEMORY:
theErr = HXR_OUTOFMEMORY;
break;
default:
theErr = HXR_FAIL;
break;
}
if (SUCCEEDED(theErr) && m_pSecondaryBuffer)
{
m_eState = E_DEV_OPENED;
KillThreadAndEvent();
SetWindowLong(m_hwnd, GWL_USERDATA, (LONG)this);
// Create the event to be signalled on playback position notifications and the thread to wait for those events to be signalled.
m_hDSNotifyEvent = CreateEvent(NULL, TRUE, FALSE, kDSWaitEvent);
// now set the notification positions for direct sound playback.
IDirectSoundNotify* pNotify = NULL;
m_pSecondaryBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&pNotify);
if(pNotify && m_hDSNotifyEvent)
{
DSBPOSITIONNOTIFY* aPositionNotify = new DSBPOSITIONNOTIFY[m_nBlocksPerBuffer];
if(aPositionNotify)
{
for(int i = 0; i < m_nBlocksPerBuffer; i++)
{
aPositionNotify[i].dwOffset = i * pFormat->uMaxBlockSize;
aPositionNotify[i].hEventNotify = m_hDSNotifyEvent;
}
}
pNotify->SetNotificationPositions(m_nBlocksPerBuffer, aPositionNotify);
delete[] aPositionNotify;
DWORD dwWaitThreadID(0);
m_hWaitThread = CreateThread(NULL, 0, EventThreadProc, (LPVOID)this, 0, &dwWaitThreadID);
}
HX_RELEASE(pNotify);
m_pSecondaryBuffer->SetVolume(DSBVOLUME_MAX);
m_pSecondaryBuffer->SetCurrentPosition(0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -