hxaudevds.cpp

来自「symbian 下的helix player源代码」· C++ 代码 · 共 753 行 · 第 1/2 页

CPP
753
字号
/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: hxaudevds.cpp,v 1.13.2.3 2004/07/09 02:01:47 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 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 (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (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.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * 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

#include "hlxosstr.h"

#ifndef TEXT
#define TEXT(w)  OS_STRING(w)
#endif

#ifndef _WINDOWS
#define TCHAR  char
#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 = TEXT("Helix DSWnd");
const TCHAR* szWindowClass = TEXT("Helix DSWndClass");
const TCHAR* kDSWaitEvent = TEXT("HelixDirectSoundNotifyWait");
const TCHAR* kDSDestroyMessage = TEXT("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( TEXT("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, TEXT("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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?