mirror of
https://github.com/AppleWin/AppleWin.git
synced 2024-12-29 08:30:04 +00:00
744 lines
18 KiB
C++
744 lines
18 KiB
C++
/*
|
|
AppleWin : An Apple //e emulator for Windows
|
|
|
|
Copyright (C) 1994-1996, Michael O'Brien
|
|
Copyright (C) 1999-2001, Oliver Schmidt
|
|
Copyright (C) 2002-2005, Tom Charlesworth
|
|
Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski
|
|
|
|
AppleWin is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
AppleWin is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with AppleWin; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* Description: Core sound related functionality
|
|
*
|
|
* Author: Tom Charlesworth
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "SoundCore.h"
|
|
#include "Core.h"
|
|
#include "Interface.h"
|
|
#include "Log.h"
|
|
#include "Speaker.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#define MAX_SOUND_DEVICES 10
|
|
|
|
static std::string sound_devices[MAX_SOUND_DEVICES];
|
|
static GUID sound_device_guid[MAX_SOUND_DEVICES];
|
|
static int num_sound_devices = 0;
|
|
|
|
static LPDIRECTSOUND g_lpDS = NULL;
|
|
|
|
//-------------------------------------
|
|
|
|
// Used for muting & fading:
|
|
|
|
static const UINT uMAX_VOICES = NUM_SLOTS * 2 + 1 + 1; // 8x (2x SSI263) + spkr + MockingboardCardManager
|
|
static UINT g_uNumVoices = 0;
|
|
static VOICE* g_pVoices[uMAX_VOICES] = {NULL};
|
|
|
|
static VOICE* g_pSpeakerVoice = NULL;
|
|
|
|
//-------------------------------------
|
|
|
|
bool g_bDSAvailable = false;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// NB. Also similar is done by: MockingboardCardManager::Destroy()
|
|
// - which is called from WM_DESTROY (when both restarting VM & exiting the app)
|
|
|
|
VOICE::~VOICE(void)
|
|
{
|
|
if (lpDSBvoice)
|
|
{
|
|
DSVoiceStop(this);
|
|
DSReleaseSoundBuffer(this);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static BOOL CALLBACK DSEnumProc(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext)
|
|
{
|
|
int i = num_sound_devices;
|
|
if(i == MAX_SOUND_DEVICES)
|
|
return TRUE;
|
|
if(lpGUID != NULL)
|
|
memcpy(&sound_device_guid[i], lpGUID, sizeof (GUID));
|
|
else
|
|
memset(&sound_device_guid[i], 0, sizeof(GUID));
|
|
sound_devices[i] = lpszDesc;
|
|
|
|
if(g_fh) fprintf(g_fh, "%d: %s - %s\n",i,lpszDesc,lpszDrvName);
|
|
|
|
num_sound_devices++;
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef _DEBUG
|
|
static const char *DirectSound_ErrorText (HRESULT error)
|
|
{
|
|
switch( error )
|
|
{
|
|
case DSERR_ALLOCATED:
|
|
return "Allocated";
|
|
case DSERR_CONTROLUNAVAIL:
|
|
return "Control Unavailable";
|
|
case DSERR_INVALIDPARAM:
|
|
return "Invalid Parameter";
|
|
case DSERR_INVALIDCALL:
|
|
return "Invalid Call";
|
|
case DSERR_GENERIC:
|
|
return "Generic";
|
|
case DSERR_PRIOLEVELNEEDED:
|
|
return "Priority Level Needed";
|
|
case DSERR_OUTOFMEMORY:
|
|
return "Out of Memory";
|
|
case DSERR_BADFORMAT:
|
|
return "Bad Format";
|
|
case DSERR_UNSUPPORTED:
|
|
return "Unsupported";
|
|
case DSERR_NODRIVER:
|
|
return "No Driver";
|
|
case DSERR_ALREADYINITIALIZED:
|
|
return "Already Initialized";
|
|
case DSERR_NOAGGREGATION:
|
|
return "No Aggregation";
|
|
case DSERR_BUFFERLOST:
|
|
return "Buffer Lost";
|
|
case DSERR_OTHERAPPHASPRIO:
|
|
return "Other Application Has Priority";
|
|
case DSERR_UNINITIALIZED:
|
|
return "Uninitialized";
|
|
case DSERR_NOINTERFACE:
|
|
return "No Interface";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT DSGetLock(LPDIRECTSOUNDBUFFER pVoice, DWORD dwOffset, DWORD dwBytes,
|
|
SHORT** ppDSLockedBuffer0, DWORD* pdwDSLockedBufferSize0,
|
|
SHORT** ppDSLockedBuffer1, DWORD* pdwDSLockedBufferSize1)
|
|
{
|
|
DWORD nStatus;
|
|
HRESULT hr = pVoice->GetStatus(&nStatus);
|
|
if(hr != DS_OK)
|
|
return hr;
|
|
|
|
if(nStatus & DSBSTATUS_BUFFERLOST)
|
|
{
|
|
do
|
|
{
|
|
hr = pVoice->Restore();
|
|
if(hr == DSERR_BUFFERLOST)
|
|
Sleep(10);
|
|
}
|
|
while(hr != DS_OK);
|
|
}
|
|
|
|
// Get write only pointer(s) to sound buffer
|
|
if(dwBytes == 0)
|
|
{
|
|
if(FAILED(hr = pVoice->Lock(0, 0,
|
|
(void**)ppDSLockedBuffer0, pdwDSLockedBufferSize0,
|
|
(void**)ppDSLockedBuffer1, pdwDSLockedBufferSize1,
|
|
DSBLOCK_ENTIREBUFFER)))
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
if(FAILED(hr = pVoice->Lock(dwOffset, dwBytes,
|
|
(void**)ppDSLockedBuffer0, pdwDSLockedBufferSize0,
|
|
(void**)ppDSLockedBuffer1, pdwDSLockedBufferSize1,
|
|
0)))
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT DSGetSoundBuffer(VOICE* pVoice, DWORD dwFlags, DWORD dwBufferSize, DWORD nSampleRate, int nChannels, const char* pszDevName)
|
|
{
|
|
pVoice->name = pszDevName;
|
|
|
|
WAVEFORMATEX wavfmt;
|
|
DSBUFFERDESC dsbdesc;
|
|
|
|
wavfmt.wFormatTag = WAVE_FORMAT_PCM;
|
|
wavfmt.nChannels = nChannels;
|
|
wavfmt.nSamplesPerSec = nSampleRate;
|
|
wavfmt.wBitsPerSample = 16;
|
|
wavfmt.nBlockAlign = wavfmt.nChannels==1 ? 2 : 4;
|
|
wavfmt.nAvgBytesPerSec = wavfmt.nBlockAlign * wavfmt.nSamplesPerSec;
|
|
|
|
memset (&dsbdesc, 0, sizeof (dsbdesc));
|
|
dsbdesc.dwSize = sizeof (dsbdesc);
|
|
dsbdesc.dwBufferBytes = dwBufferSize;
|
|
dsbdesc.lpwfxFormat = &wavfmt;
|
|
dsbdesc.dwFlags = dwFlags | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS;
|
|
|
|
// Are buffers released when g_lpDS OR pVoice->lpDSBvoice is released?
|
|
// . From DirectX doc:
|
|
// "Buffer objects are owned by the device object that created them. When the
|
|
// device object is released, all buffers created by that object are also released..."
|
|
HRESULT hr = g_lpDS->CreateSoundBuffer(&dsbdesc, &pVoice->lpDSBvoice, NULL);
|
|
if(FAILED(hr))
|
|
return hr;
|
|
|
|
//
|
|
|
|
_ASSERT(g_uNumVoices < uMAX_VOICES);
|
|
if(g_uNumVoices < uMAX_VOICES)
|
|
g_pVoices[g_uNumVoices++] = pVoice;
|
|
|
|
if(pVoice->bIsSpeaker)
|
|
g_pSpeakerVoice = pVoice;
|
|
|
|
return hr;
|
|
}
|
|
|
|
void DSReleaseSoundBuffer(VOICE* pVoice)
|
|
{
|
|
if(pVoice->bIsSpeaker)
|
|
g_pSpeakerVoice = NULL;
|
|
|
|
for(UINT i=0; i<g_uNumVoices; i++)
|
|
{
|
|
if(g_pVoices[i] == pVoice)
|
|
{
|
|
g_pVoices[i] = g_pVoices[g_uNumVoices-1];
|
|
g_pVoices[g_uNumVoices-1] = NULL;
|
|
g_uNumVoices--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAFE_RELEASE(pVoice->lpDSBvoice);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool DSVoiceStop(PVOICE Voice)
|
|
{
|
|
#ifdef NO_DIRECT_X
|
|
return false;
|
|
#else
|
|
_ASSERT(Voice->lpDSBvoice);
|
|
HRESULT hr = Voice->lpDSBvoice->Stop();
|
|
if(FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "%s: DSStop failed (%08X)\n", Voice->name.c_str(), hr);
|
|
return false;
|
|
}
|
|
|
|
Voice->bActive = false;
|
|
return true;
|
|
#endif // NO_DIRECT_X
|
|
}
|
|
|
|
// Use this to Play()
|
|
bool DSZeroVoiceBuffer(PVOICE Voice, DWORD dwBufferSize)
|
|
{
|
|
#ifdef NO_DIRECT_X
|
|
return false;
|
|
#else
|
|
|
|
DWORD dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer
|
|
SHORT* pDSLockedBuffer;
|
|
|
|
if (!DSVoiceStop(Voice))
|
|
return false;
|
|
|
|
HRESULT hr = DSGetLock(Voice->lpDSBvoice, 0, 0, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, 0);
|
|
if(FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "%s: DSGetLock failed (%08X)\n", Voice->name.c_str(), hr);
|
|
return false;
|
|
}
|
|
|
|
_ASSERT(dwDSLockedBufferSize == dwBufferSize);
|
|
memset(pDSLockedBuffer, 0x00, dwDSLockedBufferSize);
|
|
|
|
hr = Voice->lpDSBvoice->Unlock((void*)pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0);
|
|
if(FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "%s: DSUnlock failed (%08X)\n", Voice->name.c_str(), hr);
|
|
return false;
|
|
}
|
|
|
|
hr = Voice->lpDSBvoice->Play(0,0,DSBPLAY_LOOPING);
|
|
if(FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "%s: DSPlay failed (%08X)\n", Voice->name.c_str(), hr);
|
|
return false;
|
|
}
|
|
|
|
Voice->bActive = true;
|
|
return true;
|
|
#endif // NO_DIRECT_X
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool DSZeroVoiceWritableBuffer(PVOICE Voice, DWORD dwBufferSize)
|
|
{
|
|
DWORD dwDSLockedBufferSize0=0, dwDSLockedBufferSize1=0;
|
|
SHORT *pDSLockedBuffer0, *pDSLockedBuffer1;
|
|
|
|
|
|
HRESULT hr = DSGetLock(Voice->lpDSBvoice,
|
|
0, dwBufferSize,
|
|
&pDSLockedBuffer0, &dwDSLockedBufferSize0,
|
|
&pDSLockedBuffer1, &dwDSLockedBufferSize1);
|
|
if(FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "%s: DSGetLock failed (%08X)\n", Voice->name.c_str(), hr);
|
|
return false;
|
|
}
|
|
|
|
memset(pDSLockedBuffer0, 0x00, dwDSLockedBufferSize0);
|
|
if(pDSLockedBuffer1)
|
|
memset(pDSLockedBuffer1, 0x00, dwDSLockedBufferSize1);
|
|
|
|
hr = Voice->lpDSBvoice->Unlock((void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
|
|
(void*)pDSLockedBuffer1, dwDSLockedBufferSize1);
|
|
if(FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "%s: DSUnlock failed (%08X)\n", Voice->name.c_str(), hr);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static bool g_bTimerActive = false;
|
|
static eFADE g_FadeType = FADE_NONE;
|
|
static UINT_PTR g_nTimerID = 0;
|
|
|
|
//-------------------------------------
|
|
|
|
static VOID CALLBACK SoundCore_TimerFunc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
|
|
|
|
static bool SoundCore_StartTimer()
|
|
{
|
|
if(g_bTimerActive)
|
|
return true;
|
|
|
|
g_nTimerID = SetTimer(NULL, 0, 1, SoundCore_TimerFunc); // 1ms interval
|
|
if(g_nTimerID == 0)
|
|
{
|
|
fprintf(stderr, "Error creating timer\n");
|
|
_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
g_bTimerActive = true;
|
|
return true;
|
|
}
|
|
|
|
static void SoundCore_StopTimer()
|
|
{
|
|
if(!g_bTimerActive)
|
|
return;
|
|
|
|
if(KillTimer(NULL, g_nTimerID) == FALSE)
|
|
{
|
|
fprintf(stderr, "Error killing timer\n");
|
|
_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
g_bTimerActive = false;
|
|
}
|
|
|
|
bool SoundCore_GetTimerState()
|
|
{
|
|
return g_bTimerActive;
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
// [OLD: Used to fade volume in/out]
|
|
// FADE_OUT : Just keep filling speaker soundbuffer with same value
|
|
// FADE_IN : Switch to FADE_NONE & StopTimer()
|
|
|
|
static VOID CALLBACK SoundCore_TimerFunc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
|
|
{
|
|
if((g_pSpeakerVoice == NULL) || (g_pSpeakerVoice->bActive == false))
|
|
g_FadeType = FADE_NONE;
|
|
|
|
// Timer expired
|
|
if(g_FadeType == FADE_NONE)
|
|
{
|
|
SoundCore_StopTimer();
|
|
return;
|
|
}
|
|
|
|
//
|
|
|
|
#if 1
|
|
if(g_FadeType == FADE_IN)
|
|
g_FadeType = FADE_NONE;
|
|
else
|
|
SpkrUpdate_Timer();
|
|
#else
|
|
const LONG nFadeUnit_Fast = (DSBVOLUME_MAX - DSBVOLUME_MIN) / 10;
|
|
const LONG nFadeUnit_Slow = (DSBVOLUME_MAX - DSBVOLUME_MIN) / 1000; // Less noisy for 'silence'
|
|
|
|
LONG nFadeUnit = g_pSpeakerVoice->bRecentlyActive ? nFadeUnit_Fast : nFadeUnit_Slow;
|
|
LONG nFadeVolume = g_pSpeakerVoice->nFadeVolume;
|
|
|
|
if(g_FadeType == FADE_IN)
|
|
{
|
|
if(nFadeVolume == g_pSpeakerVoice->nVolume)
|
|
{
|
|
g_FadeType = FADE_NONE;
|
|
SoundCore_StopTimer();
|
|
return;
|
|
}
|
|
|
|
nFadeVolume += nFadeUnit;
|
|
|
|
if(nFadeVolume > g_pSpeakerVoice->nVolume)
|
|
nFadeVolume = g_pSpeakerVoice->nVolume;
|
|
}
|
|
else // FADE_OUT
|
|
{
|
|
if(nFadeVolume == DSBVOLUME_MIN)
|
|
{
|
|
g_FadeType = FADE_NONE;
|
|
SoundCore_StopTimer();
|
|
return;
|
|
}
|
|
|
|
nFadeVolume -= nFadeUnit;
|
|
|
|
if(nFadeVolume < DSBVOLUME_MIN)
|
|
nFadeVolume = DSBVOLUME_MIN;
|
|
}
|
|
|
|
g_pSpeakerVoice->nFadeVolume = nFadeVolume;
|
|
g_pSpeakerVoice->lpDSBvoice->SetVolume(nFadeVolume);
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SoundCore_SetFade(eFADE FadeType)
|
|
{
|
|
static AppMode_e nLastMode = MODE_UNDEFINED;
|
|
|
|
if(g_nAppMode == MODE_DEBUG)
|
|
return;
|
|
|
|
// Fade in/out for speaker, the others are unmuted/muted here
|
|
if(FadeType != FADE_NONE)
|
|
{
|
|
for(UINT i=0; i<g_uNumVoices; i++)
|
|
{
|
|
// Note: Kludge for fading speaker if curr/last g_nAppMode is/was MODE_LOGO:
|
|
// . Bug in DirectSound? SpeakerVoice.lpDSBvoice->SetVolume() doesn't work without this!
|
|
// . See SoundCore_TweakVolumes() - could be this?
|
|
if((g_pVoices[i]->bIsSpeaker) && (g_nAppMode != MODE_LOGO) && (nLastMode != MODE_LOGO))
|
|
{
|
|
g_pVoices[i]->lpDSBvoice->GetVolume(&g_pVoices[i]->nFadeVolume);
|
|
g_FadeType = FadeType;
|
|
SoundCore_StartTimer();
|
|
}
|
|
else if(FadeType == FADE_OUT)
|
|
{
|
|
g_pVoices[i]->lpDSBvoice->SetVolume(DSBVOLUME_MIN);
|
|
g_pVoices[i]->bMute = true;
|
|
}
|
|
else // FADE_IN
|
|
{
|
|
g_pVoices[i]->lpDSBvoice->SetVolume(g_pVoices[i]->nVolume);
|
|
g_pVoices[i]->bMute = false;
|
|
}
|
|
}
|
|
}
|
|
else // FadeType == FADE_NONE
|
|
{
|
|
if( (g_FadeType != FADE_NONE) && // Currently fading-in/out
|
|
(g_pSpeakerVoice && g_pSpeakerVoice->bActive) )
|
|
{
|
|
g_FadeType = FADE_NONE; // TimerFunc will call StopTimer()
|
|
g_pSpeakerVoice->lpDSBvoice->SetVolume(g_pSpeakerVoice->nVolume);
|
|
}
|
|
}
|
|
|
|
nLastMode = g_nAppMode;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// If AppleWin started by double-clicking a .dsk, then our window won't have focus when volumes are set (so gets ignored).
|
|
// Subsequent setting (to the same volume) will get ignored, as DirectSound thinks that volume is already set.
|
|
|
|
void SoundCore_TweakVolumes()
|
|
{
|
|
for (UINT i=0; i<g_uNumVoices; i++)
|
|
{
|
|
g_pVoices[i]->lpDSBvoice->SetVolume(g_pVoices[i]->nVolume-1);
|
|
g_pVoices[i]->lpDSBvoice->SetVolume(g_pVoices[i]->nVolume);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static UINT g_uDSInitRefCount = 0;
|
|
|
|
bool DSInit()
|
|
{
|
|
if(g_bDSAvailable)
|
|
{
|
|
g_uDSInitRefCount++;
|
|
return true; // Already initialised successfully
|
|
}
|
|
|
|
num_sound_devices = 0;
|
|
HRESULT hr = DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumProc, NULL);
|
|
if(FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "DSEnumerate failed (%08X)\n",hr);
|
|
return false;
|
|
}
|
|
|
|
if(g_fh)
|
|
{
|
|
fprintf(g_fh, "Number of sound devices = %d\n",num_sound_devices);
|
|
}
|
|
|
|
bool bCreatedOK = false;
|
|
for(int x=0; x<num_sound_devices; x++)
|
|
{
|
|
hr = DirectSoundCreate(&sound_device_guid[x], &g_lpDS, NULL);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "DSCreate succeeded for sound device #%d\n",x);
|
|
bCreatedOK = true;
|
|
break;
|
|
}
|
|
|
|
if(g_fh) fprintf(g_fh, "DSCreate failed for sound device #%d (%08X)\n",x,hr);
|
|
}
|
|
if(!bCreatedOK)
|
|
{
|
|
if(g_fh) fprintf(g_fh, "DSCreate failed for all sound devices\n");
|
|
return false;
|
|
}
|
|
|
|
HWND hwnd = GetFrame().g_hFrameWindow;
|
|
_ASSERT(hwnd);
|
|
hr = g_lpDS->SetCooperativeLevel(hwnd, DSSCL_NORMAL);
|
|
if (FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "SetCooperativeLevel failed (%08X)\n",hr);
|
|
return false;
|
|
}
|
|
|
|
DSCAPS DSCaps;
|
|
memset(&DSCaps, 0, sizeof(DSCAPS));
|
|
DSCaps.dwSize = sizeof(DSCAPS);
|
|
hr = g_lpDS->GetCaps(&DSCaps);
|
|
if(FAILED(hr))
|
|
{
|
|
if(g_fh) fprintf(g_fh, "GetCaps failed (%08X)\n",hr);
|
|
// Not fatal: so continue...
|
|
}
|
|
|
|
g_bDSAvailable = true;
|
|
|
|
g_uDSInitRefCount = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void DSUninit()
|
|
{
|
|
if(!g_bDSAvailable)
|
|
return;
|
|
|
|
_ASSERT(g_uDSInitRefCount);
|
|
|
|
if(g_uDSInitRefCount == 0)
|
|
return;
|
|
|
|
g_uDSInitRefCount--;
|
|
|
|
if(g_uDSInitRefCount)
|
|
return;
|
|
|
|
//
|
|
|
|
_ASSERT(g_uNumVoices == 0);
|
|
|
|
SAFE_RELEASE(g_lpDS);
|
|
g_bDSAvailable = false;
|
|
|
|
SoundCore_StopTimer();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
LONG NewVolume(DWORD dwVolume, DWORD dwVolumeMax)
|
|
{
|
|
float fVol = (float) dwVolume / (float) dwVolumeMax; // 0.0=Max, 1.0=Min
|
|
|
|
return (LONG) ((float) DSBVOLUME_MIN * fVol);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
static int g_nErrorInc = 20; // Old: 1
|
|
static int g_nErrorMax = 200; // Old: 50
|
|
|
|
int SoundCore_GetErrorInc()
|
|
{
|
|
return g_nErrorInc;
|
|
}
|
|
|
|
void SoundCore_SetErrorInc(const int nErrorInc)
|
|
{
|
|
g_nErrorInc = nErrorInc < g_nErrorMax ? nErrorInc : g_nErrorMax;
|
|
if(g_fh) fprintf(g_fh, "Speaker/MB Error Inc = %d\n", g_nErrorInc);
|
|
}
|
|
|
|
int SoundCore_GetErrorMax()
|
|
{
|
|
return g_nErrorMax;
|
|
}
|
|
|
|
void SoundCore_SetErrorMax(const int nErrorMax)
|
|
{
|
|
g_nErrorMax = nErrorMax < MAX_SAMPLES ? nErrorMax : MAX_SAMPLES;
|
|
if(g_fh) fprintf(g_fh, "Speaker/MB Error Max = %d\n", g_nErrorMax);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
// Use DWORD_PTR according to IReferenceClock from <strmif.h>.
|
|
static DWORD_PTR g_pdwAdviseCookie = 0; // Not really used as pointer.
|
|
static IReferenceClock *g_pRefClock = NULL;
|
|
static HANDLE g_hSemaphore = NULL;
|
|
static bool g_bRefClockTimerActive = false;
|
|
static DWORD g_dwLastUsecPeriod = 0;
|
|
|
|
|
|
bool SysClk_InitTimer()
|
|
{
|
|
g_hSemaphore = CreateSemaphore(NULL, 0, 1, NULL); // Max count = 1
|
|
if (g_hSemaphore == NULL)
|
|
{
|
|
fprintf(stderr, "Error creating semaphore\n");
|
|
return false;
|
|
}
|
|
|
|
if (CoCreateInstance(CLSID_SystemClock, NULL, CLSCTX_INPROC,
|
|
IID_IReferenceClock, (LPVOID*)&g_pRefClock) != S_OK)
|
|
{
|
|
fprintf(stderr, "Error initialising COM\n");
|
|
return false; // Fails for Win95!
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SysClk_UninitTimer()
|
|
{
|
|
SysClk_StopTimer();
|
|
|
|
SAFE_RELEASE(g_pRefClock);
|
|
|
|
if (CloseHandle(g_hSemaphore) == 0)
|
|
fprintf(stderr, "Error closing semaphore handle\n");
|
|
}
|
|
|
|
//
|
|
|
|
void SysClk_WaitTimer()
|
|
{
|
|
if(!g_bRefClockTimerActive)
|
|
return;
|
|
|
|
WaitForSingleObject(g_hSemaphore, INFINITE);
|
|
}
|
|
|
|
//
|
|
|
|
void SysClk_StartTimerUsec(DWORD dwUsecPeriod)
|
|
{
|
|
if(g_bRefClockTimerActive && (g_dwLastUsecPeriod == dwUsecPeriod))
|
|
return;
|
|
|
|
SysClk_StopTimer();
|
|
|
|
REFERENCE_TIME rtPeriod = (REFERENCE_TIME) (dwUsecPeriod * 10); // In units of 100ns
|
|
REFERENCE_TIME rtNow;
|
|
|
|
HRESULT hr = g_pRefClock->GetTime(&rtNow);
|
|
// S_FALSE : Returned time is the same as the previous value
|
|
|
|
if ((hr != S_OK) && (hr != S_FALSE))
|
|
{
|
|
_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
// IReferenceClock from <strmif.h> (origin <axcore.idl>) is "oddly" defined to use HSEMAPHORE.
|
|
static_assert(sizeof(HSEMAPHORE) == sizeof(HANDLE), "must be same size");
|
|
if (g_pRefClock->AdvisePeriodic(rtNow, rtPeriod, (HSEMAPHORE)g_hSemaphore, &g_pdwAdviseCookie) != S_OK)
|
|
{
|
|
fprintf(stderr, "Error creating timer\n");
|
|
_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
g_dwLastUsecPeriod = dwUsecPeriod;
|
|
g_bRefClockTimerActive = true;
|
|
}
|
|
|
|
void SysClk_StopTimer()
|
|
{
|
|
if(!g_bRefClockTimerActive)
|
|
return;
|
|
|
|
if (g_pRefClock->Unadvise(g_pdwAdviseCookie) != S_OK)
|
|
{
|
|
fprintf(stderr, "Error deleting timer\n");
|
|
_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
g_bRefClockTimerActive = false;
|
|
}
|