/////////////////////////////////////////////////////////////////////////////
// Copyright (C) 1998 by J�rg K�nig
// All rights reserved
//
// This file is part of the completely free tetris clone "CGTetris".
//
// This is free software.
// You may redistribute it by any means providing it is not sold for profit
// without the authors written consent.
//
// No warrantee of any kind, expressed or implied, is included with this
// software; use at your own risk, responsibility for damages (if any) to
// anyone resulting from the use of this software rests entirely with the
// user.
//
// Send bug reports, bug fixes, enhancements, requests, flames, etc., and
// I'll try to keep a version up to date. I can be reached as follows:
// J.Koenig@adg.de (company site)
// Joerg.Koenig@rhein-neckar.de (private site)
/////////////////////////////////////////////////////////////////////////////
// DirectSound.cpp: implementation of the CDirectSound class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "DirectSound.h"
// The following macro is defined since DirectX 5, but will work with
// older versions too.
#ifndef DSBLOCK_ENTIREBUFFER
#define DSBLOCK_ENTIREBUFFER 0x00000002
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
static void DSError( HRESULT hRes ) {
switch(hRes) {
case DS_OK: TRACE0("NO ERROR\n"); break;
case DSERR_ALLOCATED: TRACE0("ALLOCATED\n"); break;
case DSERR_INVALIDPARAM: TRACE0("INVALIDPARAM\n"); break;
case DSERR_OUTOFMEMORY: TRACE0("OUTOFMEMORY\n"); break;
case DSERR_UNSUPPORTED: TRACE0("UNSUPPORTED\n"); break;
case DSERR_NOAGGREGATION: TRACE0("NOAGGREGATION\n"); break;
case DSERR_UNINITIALIZED: TRACE0("UNINITIALIZED\n"); break;
case DSERR_BADFORMAT: TRACE0("BADFORMAT\n"); break;
case DSERR_ALREADYINITIALIZED: TRACE0("ALREADYINITIALIZED\n"); break;
case DSERR_BUFFERLOST: TRACE0("BUFFERLOST\n"); break;
case DSERR_CONTROLUNAVAIL: TRACE0("CONTROLUNAVAIL\n"); break;
case DSERR_GENERIC: TRACE0("GENERIC\n"); break;
case DSERR_INVALIDCALL: TRACE0("INVALIDCALL\n"); break;
case DSERR_OTHERAPPHASPRIO: TRACE0("OTHERAPPHASPRIO\n"); break;
case DSERR_PRIOLEVELNEEDED: TRACE0("PRIOLEVELNEEDED\n"); break;
default: TRACE1("%lu\n",hRes);break;
}
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
LPDIRECTSOUND CDirectSound::m_lpDirectSound;
DWORD CDirectSound::m_dwInstances;
CDirectSound::CDirectSound()
{
m_lpDirectSound = 0;
m_pDsb = 0;
m_pTheSound = 0;
m_dwTheSound = 0;
m_bEnabled = TRUE;
++m_dwInstances;
}
CDirectSound::~CDirectSound()
{
if( m_pDsb )
m_pDsb->Release();
if( !--m_dwInstances && m_lpDirectSound ) {
m_lpDirectSound->Release();
m_lpDirectSound = 0;
}
}
BOOL CDirectSound::Create(LPCTSTR pszResource, CWnd * pWnd)
{
//////////////////////////////////////////////////////////////////
// load resource
HINSTANCE hApp = ::GetModuleHandle(0);
ASSERT(hApp);
HRSRC hResInfo = ::FindResource(hApp, pszResource, TEXT("WAVE"));
if(hResInfo == 0)
return FALSE;
HGLOBAL hRes = ::LoadResource(hApp, hResInfo);
if(hRes == 0)
return FALSE;
LPVOID pTheSound = ::LockResource(hRes);
if(pTheSound == 0)
return FALSE;
return Create(pTheSound, pWnd);
}
BOOL CDirectSound :: Create(LPVOID pSoundData, CWnd * pWnd) {
if(pWnd == 0)
pWnd = AfxGetApp()->GetMainWnd();
ASSERT(pWnd != 0);
ASSERT(::IsWindow(pWnd->GetSafeHwnd()));
ASSERT(pSoundData != 0);
//////////////////////////////////////////////////////////////////
// create direct sound object
if( m_lpDirectSound == 0 ) {
// Someone might use sounds for starting apps. This may cause
// DirectSoundCreate() to fail because the driver is used by
// anyone else. So wait a little before starting with the work ...
HRESULT hRes = DS_OK;
short nRes = 0;
do {
if( nRes )
::Sleep(500);
hRes = ::DirectSoundCreate(0, &m_lpDirectSound, 0);
++nRes;
} while( nRes < 10 && (hRes == DSERR_ALLOCATED || hRes == DSERR_NODRIVER) );
if( hRes != DS_OK )
return FALSE;
m_lpDirectSound->SetCooperativeLevel(pWnd->GetSafeHwnd(), DSSCL_NORMAL);
}
ASSERT(m_lpDirectSound != 0);
WAVEFORMATEX * pcmwf;
if( ! GetWaveData(pSoundData, pcmwf, m_pTheSound, m_dwTheSound) ||
! CreateSoundBuffer(pcmwf) ||
! SetSoundData(m_pTheSound, m_dwTheSound) )
return FALSE;
return TRUE;
}
BOOL CDirectSound :: GetWaveData(void * pRes, WAVEFORMATEX * & pWaveHeader, void * & pbWaveData, DWORD & cbWaveSize) {
pWaveHeader = 0;
pbWaveData = 0;
cbWaveSize = 0;
DWORD * pdw = (DWORD *)pRes;
DWORD dwRiff = *pdw++;
DWORD dwLength = *pdw++;
DWORD dwType = *pdw++;
if( dwRiff != mmioFOURCC('R', 'I', 'F', 'F') )
return FALSE; // not even RIFF
if( dwType != mmioFOURCC('W', 'A', 'V', 'E') )
return FALSE; // not a WAV
DWORD * pdwEnd = (DWORD *)((BYTE *)pdw + dwLength-4);
while( pdw < pdwEnd ) {
dwType = *pdw++;
dwLength = *pdw++;
switch( dwType ) {
case mmioFOURCC('f', 'm', 't', ' '):
if( !pWaveHeader ) {
if( dwLength < sizeof(WAVEFORMAT) )
return FALSE; // not a WAV
pWaveHeader = (WAVEFORMATEX *)pdw;
if( pbWaveData && cbWaveSize )
return TRUE;
}
break;
case mmioFOURCC('d', 'a', 't', 'a'):
pbWaveData = LPVOID(pdw);
cbWaveSize = dwLength;
if( pWaveHeader )
return TRUE;
break;
}
pdw = (DWORD *)((BYTE *)pdw + ((dwLength+1)&~1));
}
return FALSE;
}
BOOL CDirectSound::CreateSoundBuffer(WAVEFORMATEX * pcmwf)
{
DSBUFFERDESC dsbdesc;
// Set up DSBUFFERDESC structure.
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); // Zero it out.
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
// Need no controls (pan, volume, frequency).
dsbdesc.dwFlags = DSBCAPS_STATIC; // assumes that the sound is played often
dsbdesc.dwBufferBytes = m_dwTheSound;
dsbdesc.lpwfxFormat = pcmwf; // Create buffer.
HRESULT hRes;
if( DS_OK != (hRes = m_lpDirectSound->CreateSoundBuffer(&dsbdesc, &m_pDsb, 0)) ) {
// Failed.
DSError(hRes);
m_pDsb = 0;
return FALSE;
}
return TRUE;
}
BOOL CDirectSound::SetSoundData(void * pSoundData, DWORD dwSoundSize) {
LPVOID lpvPtr1;
DWORD dwBytes1;
// Obtain write pointer.
HRESULT hr = m_pDsb->Lock(0, 0, &lpvPtr1, &dwBytes1, 0, 0, DSBLOCK_ENTIREBUFFER);
// If DSERR_BUFFERLOST is returned, restore and retry lock.
if(DSERR_BUFFERLOST == hr) {
m_pDsb->Restore();
hr = m_pDsb->Lock(0, 0, &lpvPtr1, &dwBytes1, 0, 0, DSBLOCK_ENTIREBUFFER);
}
if(DS_OK == hr) {
// Write to pointers.
::CopyMemory(lpvPtr1, pSoundData, dwBytes1);
// Release the data back to DirectSound.
hr = m_pDsb->Unlock(lpvPtr1, dwBytes1, 0, 0);
if(DS_OK == hr)
return TRUE;
}
// Lock, Unlock, or Restore failed.
return FALSE;
}
void CDirectSound::Play(DWORD dwStartPosition, BOOL bLoop)
{
if( ! IsValid() || ! IsEnabled() )
return; // no chance to play the sound ...
if( dwStartPosition > m_dwTheSound )
dwStartPosition = m_dwTheSound;
m_pDsb->SetCurrentPosition(dwStartPosition);
if( DSERR_BUFFERLOST == m_pDsb->Play(0, 0, bLoop ? DSBPLAY_LOOPING : 0) ) {
// another application had stolen our buffer
// Note that a "Restore()" is not enough, because
// the sound data is invalid after Restore().
SetSoundData(m_pTheSound, m_dwTheSound);
// Try playing again
m_pDsb->Play(0, 0, bLoop ? DSBPLAY_LOOPING : 0);
}
}
void CDirectSound::Stop()
{
if( IsValid() )
m_pDsb->Stop();
}
void CDirectSound::Pause()
{
Stop();
}
void CDirectSound::Continue()
{
if( IsValid() ) {
DWORD dw