Professional Documents
Culture Documents
#define USE_ERROR
//#define USE_TRACE
#ifndef WIN32_EXTRA_LEAN
#define WIN32_EXTRA_LEAN
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <mmsystem.h>
#include "Ports.h"
#define PORT_CONTROL_TYPE_BOOLEAN 1
#define PORT_CONTROL_TYPE_SIGNED 2
#define PORT_CONTROL_TYPE_UNSIGNED 3
//#define PORT_CONTROL_TYPE_UNSIGNED_DB 4
#define PORT_CONTROL_TYPE_FAKE_VOLUME 5
#define PORT_CONTROL_TYPE_FAKE_BALANCE 6
#define PORT_CONTROL_TYPE_MUX 5
#define PORT_CONTROL_TYPE_MIXER 6
INT32 PORT_GetPortMixerCount() {
return (INT32) mixerGetNumDevs();
}
#ifdef USE_TRACE
#endif // USE_TRACE
// internal utility functions
// returns TRUE if there are more than MIXER/MUX controls in this line
// if controls is non-NULL, it will be filled with the info
int lineHasControls(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls) {
MIXERLINECONTROLS localControls;
int ret = FALSE;
UINT i;
localControls.pamxctrl = NULL;
if (controls == NULL) {
controls = &localControls;
}
if (getControlInfo(handle, line, controls)) {
for (i = 0; !ret && (i < controls->cControls); i++) {
switch (controls->pamxctrl[i].dwControlType & MIXERCONTROL_CT_CLASS_MASK) {
case MIXERCONTROL_CT_CLASS_FADER : // fall through
case MIXERCONTROL_CT_CLASS_SLIDER : // fall through
case MIXERCONTROL_CT_CLASS_SWITCH : ret = TRUE;
}
}
}
if (localControls.pamxctrl) {
free(localControls.pamxctrl);
localControls.pamxctrl = NULL;
}
return ret;
}
TRACE0("PORT_Open\n");
mmres = mixerOpen((LPHMIXER) &handle, mixerIndex, 0, 0, MIXER_OBJECTF_MIXER);
if (mmres != MMSYSERR_NOERROR) {
return NULL;
}
controls.pamxctrl = NULL;
if (getControlInfo(handle, line, &controls)) {
for (i = 0; i < controls.cControls; i++) {
switch (controls.pamxctrl[i].dwControlType & MIXERCONTROL_CT_CLASS_MASK) {
case MIXERCONTROL_CT_CLASS_FADER : // fall through
case MIXERCONTROL_CT_CLASS_SLIDER : // fall through
case MIXERCONTROL_CT_CLASS_SWITCH : // fall through
case MIXERCONTROL_CT_CLASS_LIST : ret++; break;
}
if ((controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
|| (controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)) {
ret += controls.pamxctrl[i].cMultipleItems;
if (muxCount) {
(*muxCount) += controls.pamxctrl[i].cMultipleItems;
}
}
else if ((controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
&& (line->cChannels == 2)) {
ret++; // for FAKE volume/balance pairs
}
}
}
if (controls.pamxctrl) {
free(controls.pamxctrl);
controls.pamxctrl = NULL;
}
return ret;
}
void createMuxControl(PortInfo* info, PortControlCreator* creator, MIXERLINE* dstLine, DWORD srcLineID, void**
controlObjects, int* controlCount) {
MIXERLINECONTROLS controlInfos;
MIXERCONTROLDETAILS* details;
MIXERCONTROLDETAILS_LISTTEXT* listTextDetails = NULL;
UINT listTextDetailCount = 0;
PortControlID* controlID;
UINT i, c;
int m;
TRACE0(">createMuxControl\n");
// go through all controls of dstline
controlInfos.pamxctrl = NULL;
if (getControlInfo(info->handle, dstLine, &controlInfos)) {
for (i = 0; i < controlInfos.cControls; i++) {
if (((controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
|| (controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX))
&& (controlInfos.pamxctrl[i].cMultipleItems > 0)) {
if (info->usedControlIDs >= info->maxControlCount) {
ERROR1("not enough free controlIDs !! maxControlIDs = %d\n", info->maxControlCount);
break;
}
// get the details for this mux control
controlID = &(info->controlIDs[info->usedControlIDs]);
controlID->portInfo = info;
if (controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER) {
controlID->controlType = PORT_CONTROL_TYPE_MIXER;
} else {
controlID->controlType = PORT_CONTROL_TYPE_MUX;
}
details = &(controlID->details);
details->cbStruct = sizeof(MIXERCONTROLDETAILS);
details->dwControlID = controlInfos.pamxctrl[i].dwControlID;
details->cChannels = 1;
details->cMultipleItems = controlInfos.pamxctrl[i].cMultipleItems;
details->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
if (!listTextDetails || (listTextDetailCount < (details->cMultipleItems * details->cChannels))) {
// need to allocate new listTextDetails
if (listTextDetails) {
free(listTextDetails);
listTextDetails = NULL;
}
listTextDetailCount = details->cMultipleItems * details->cChannels;
listTextDetails = (MIXERCONTROLDETAILS_LISTTEXT*) malloc(listTextDetailCount *
sizeof(MIXERCONTROLDETAILS_LISTTEXT));
if (!listTextDetails) {
ERROR0("createMuxControl: unable to allocate listTextDetails!\n");
if (controlInfos.pamxctrl) {
free(controlInfos.pamxctrl);
controlInfos.pamxctrl = NULL;
}
TRACE0("<createMuxControl ERROR\n");
return;
}
}
details->paDetails = listTextDetails;
if (mixerGetControlDetails((HMIXEROBJ) info->handle, details, MIXER_GETCONTROLDETAILSF_LISTTEXT |
MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) {
ERROR0("createMuxControl: unable to get control details!\n");
continue;
}
// prevent freeing this data
details->paDetails = NULL;
// go through all mux items. If the line matches, then add a BOOLEAN select control
for (c = 0; c < details->cMultipleItems; c++) {
if (listTextDetails[c].dwParam1 == srcLineID) {
// we have found the line in the MUX lines.
controlID->muxIndex = c;
details->cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
// now look if any other controlID was already part of this MUX line
for (m = 0; m < info->usedControlIDs; m++) {
if (info->controlIDs[m].details.dwControlID == details->dwControlID) {
// reuse the MUX Data
TRACE2("Reusing paDetails=%p of controlID[%d]\n", info-
>controlIDs[m].details.paDetails, m);
details->paDetails = info->controlIDs[m].details.paDetails;
break;
}
}
if (!details->paDetails) {
// first time this MUX control is used, allocate some of the muxData
details->paDetails = &(info->muxData[info->usedMuxData]);
TRACE2("Setting paDetails=%p to muxData[%d] \n", details->paDetails, info-
>usedMuxData);
info->usedMuxData += details->cMultipleItems;
}
// finally this line can be added
controlObjects[*controlCount] = (creator->newBooleanControl)(creator, controlID,
CONTROL_TYPE_SELECT);
(*controlCount)++;
info->usedControlIDs++;
break;
}
}
}
}
}
if (listTextDetails) {
free(listTextDetails);
listTextDetails = NULL;
}
if (controlInfos.pamxctrl) {
free(controlInfos.pamxctrl);
controlInfos.pamxctrl = NULL;
}
TRACE0("<createMuxControl\n");
}
void createLineControls(PortInfo* info, PortControlCreator* creator, MIXERLINE* line, void** controlObjects, int*
controlCount) {
MIXERLINECONTROLS controlInfos;
MIXERCONTROL* mixerControl;
UINT i;
INT32 type;
void addCompoundControl(PortInfo* info, PortControlCreator* creator, char* name, void** controlObjects, int*
controlCount) {
void* compControl;
TRACE0(">addAllControl\n");
// go through all controls and add them to the vector
for (i = 0; i < *controlCount; i++) {
(creator->addControl)(creator, controlObjects[i]);
}
*controlCount = 0;
TRACE0("<addAllControl\n");
}
switch (controlID->controlType) {
case PORT_CONTROL_TYPE_MUX:
if (!value) {
// cannot unselect a MUX line
return;
}
if (!getControlValue(controlID)) {
return;
}
bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails;
for (i = 0; i < controlID->details.cMultipleItems; i++) {
bools[i].fValue = (i == (UINT) controlID->muxIndex)?TRUE:FALSE;
}
break;
case PORT_CONTROL_TYPE_MIXER:
if (!getControlValue(controlID)) {
return;
}
bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails;
bools[controlID->muxIndex].fValue = (value?TRUE:FALSE);
break;
case PORT_CONTROL_TYPE_BOOLEAN:
controlID->boolValue.fValue = (value?TRUE:FALSE);
break;
default:
ERROR1("PORT_SetIntValue: wrong controlType=%d !\n", controlID->controlType);
return;
}
setControlValue(controlID);
}
/*
* sets the unsigned values for left and right volume according to
* the given volume (0...1) and balance (-1..0..+1)
*/
void setFakeVolume(PortControlID* controlID, float vol, float bal) {
vol = vol * (controlID->max - controlID->min);
if (bal < 0.0f) {
controlID->unsignedValue[0].dwValue = (UINT) (vol + 0.5f) + controlID->min;
controlID->unsignedValue[1].dwValue = (UINT) ((vol * (bal + 1.0f)) + 0.5f) + controlID->min;
} else {
controlID->unsignedValue[1].dwValue = (UINT) (vol + 0.5f) + controlID->min;
controlID->unsignedValue[0].dwValue = (UINT) ((vol * (1.0f - bal)) + 0.5f) + controlID->min;
}
}
#endif // USE_PORTS
-----------------------
1410
1411
1412
/*
* Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
#define USE_ERROR
#define USE_TRACE
#ifndef WIN32_EXTRA_LEAN
#define WIN32_EXTRA_LEAN
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <mmsystem.h>
#include <string.h>
#ifdef USE_DEBUG_SILENCING
#define DEBUG_SILENCING0(p) TRACE0(p)
#define DEBUG_SILENCING1(p1,p2) TRACE1(p1,p2)
#define DEBUG_SILENCING2(p1,p2,p3) TRACE2(p1,p2,p3)
#else
#define DEBUG_SILENCING0(p)
#define DEBUG_SILENCING1(p1,p2)
#define DEBUG_SILENCING2(p1,p2,p3)
#endif
typedef struct {
INT32 mixerIndex;
BOOL isSource;
/* either LPDIRECTSOUND or LPDIRECTSOUNDCAPTURE */
void* dev;
/* how many instances use the dev */
INT32 refCount;
GUID guid;
} DS_AudioDeviceCache;
BOOL DS_lockCache() {
/* dummy implementation for now, Java does locking */
return TRUE;
}
void DS_unlockCache() {
/* dummy implementation for now */
}
typedef struct {
INT32 currMixerIndex;
BOOL isSource;
} DS_RefreshCacheStruct;
INT32 DAUDIO_GetDirectAudioDeviceCount() {
DS_RefreshCacheStruct rs;
INT32 oldCount;
INT32 cacheIndex;
if (!DS_lockCache()) {
return 0;
}
if (g_lastCacheRefreshTime == 0
|| (UINT64) timeGetTime() > (UINT64) (g_lastCacheRefreshTime + WAIT_BETWEEN_CACHE_REFRESH_MILLIS)) {
/* first, initialize any old cache items */
for (cacheIndex = 0; cacheIndex < g_cacheCount; cacheIndex++) {
g_audioDeviceCache[cacheIndex].mixerIndex = -1;
}
/* enumerate all devices and either add them to the device cache,
* or refresh the mixer number
*/
rs.currMixerIndex = 0;
rs.isSource = TRUE;
DirectSoundEnumerate((LPDSENUMCALLBACK) DS_RefreshCacheEnum, &rs);
/* if we only got the Primary Sound Driver (GUID=NULL),
* then there aren't any playback devices installed */
if (rs.currMixerIndex == 1) {
cacheIndex = findCacheItemByGUID(NULL, TRUE);
if (cacheIndex == 0) {
rs.currMixerIndex = 0;
g_audioDeviceCache[0].mixerIndex = -1;
TRACE0("Removing stale Primary Sound Driver from list.\n");
}
}
oldCount = rs.currMixerIndex;
rs.isSource = FALSE;
DirectSoundCaptureEnumerate((LPDSENUMCALLBACK) DS_RefreshCacheEnum, &rs);
/* if we only got the Primary Sound Capture Driver (GUID=NULL),
* then there aren't any capture devices installed */
if ((rs.currMixerIndex - oldCount) == 1) {
cacheIndex = findCacheItemByGUID(NULL, FALSE);
if (cacheIndex != -1) {
rs.currMixerIndex = oldCount;
g_audioDeviceCache[cacheIndex].mixerIndex = -1;
TRACE0("Removing stale Primary Sound Capture Driver from list.\n");
}
}
g_mixerCount = rs.currMixerIndex;
if (!DS_lockCache()) {
return FALSE;
}
/*desc->vendor;
desc->version;*/
DS_unlockCache();
return (desc->maxSimulLines == -1)?TRUE:FALSE;
}
//static UINT32 sampleRateArray[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 56000, 88000, 96000,
172000, 192000 };
static INT32 sampleRateArray[] = { -1 };
static INT32 channelsArray[] = { 1, 2};
static INT32 bitsArray[] = { 8, 16};
/* sanity */
if (deviceID >= g_cacheCount) {
return;
}
if ((g_audioDeviceCache[deviceID].isSource && !isSource)
|| (!g_audioDeviceCache[deviceID].isSource && isSource)) {
/* only support Playback or Capture */
return;
}
typedef struct {
int deviceID;
/* for convenience */
BOOL isSource;
/* the secondary buffer (Playback) */
LPDIRECTSOUNDBUFFER playBuffer;
/* the secondary buffer (Capture) */
LPDIRECTSOUNDCAPTUREBUFFER captureBuffer;
UINT64 framePos;
/* where to write into the buffer.
* -1 if at current position (Playback)
* For Capture, this is the read position
*/
int writePos;
BOOL underrun;
} DS_Info;
case DSERR_CONTROLUNAVAIL:
return "DSERR_CONTROLUNAVAIL";
case DSERR_INVALIDPARAM:
return "DSERR_INVALIDPARAM";
case DSERR_INVALIDCALL:
return "DSERR_INVALIDCALL";
case DSERR_GENERIC:
return "DSERR_GENERIC";
case DSERR_PRIOLEVELNEEDED:
return "DSERR_PRIOLEVELNEEDED";
case DSERR_OUTOFMEMORY:
return "DSERR_OUTOFMEMORY";
case DSERR_BADFORMAT:
return "DSERR_BADFORMAT";
case DSERR_UNSUPPORTED:
return "DSERR_UNSUPPORTED";
case DSERR_NODRIVER:
return "DSERR_NODRIVER";
case DSERR_ALREADYINITIALIZED:
return "DSERR_ALREADYINITIALIZED";
case DSERR_NOAGGREGATION:
return "DSERR_NOAGGREGATION";
case DSERR_BUFFERLOST:
return "DSERR_BUFFERLOST";
case DSERR_OTHERAPPHASPRIO:
return "DSERR_OTHERAPPHASPRIO";
case DSERR_UNINITIALIZED:
return "DSERR_UNINITIALIZED";
default:
return "Unknown HRESULT";
}
}
/*
** data/routines for starting DS buffers by separate thread
** (joint into DS_StartBufferHelper class)
** see cr6372428: playback fails after exiting from thread that has started it
** due IDirectSoundBuffer8::Play() description:
** http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c
** /directx/htm/idirectsoundbuffer8play.asp
** (remark section): If the application is multithreaded, the thread that plays
** the buffer must continue to exist as long as the buffer is playing.
** Buffers created on WDM drivers stop playing when the thread is terminated.
** IDirectSoundCaptureBuffer8::Start() has the same remark:
** http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c
** /directx/htm/idirectsoundcapturebuffer8start.asp
*/
class DS_StartBufferHelper {
public:
/* starts DirectSound buffer (playback or capture) */
static HRESULT StartBuffer(DS_Info* info);
/* checks for initialization success */
static inline BOOL isInitialized() { return data.threadHandle != NULL; }
protected:
DS_StartBufferHelper() {} // no need to create an instance
/* data class */
class Data {
public:
Data();
~Data();
// public data to access from parent class
CRITICAL_SECTION crit_sect;
volatile HANDLE threadHandle;
volatile HANDLE startEvent;
volatile HANDLE startedEvent;
volatile DS_Info* line2Start;
volatile HRESULT startResult;
} static data;
/* StartThread function */
static DWORD WINAPI __stdcall ThreadProc(void *param);
};
DS_StartBufferHelper::Data::Data() {
threadHandle = NULL;
::InitializeCriticalSection(&crit_sect);
startEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
startedEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
if (startEvent != NULL && startedEvent != NULL)
threadHandle = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
}
DS_StartBufferHelper::Data::~Data() {
::EnterCriticalSection(&crit_sect);
if (threadHandle != NULL) {
// terminate thread
line2Start = NULL;
::SetEvent(startEvent);
::CloseHandle(threadHandle);
threadHandle = NULL;
}
::LeaveCriticalSection(&crit_sect);
// won't delete startEvent/startedEvent/crit_sect
// - Windows will do during process shutdown
}
/* adds 2 positions
*/
inline int DS_addPos(DS_Info* info, int pos1, int pos2) {
int result = pos1 + pos2;
while (result >= info->dsBufferSizeInBytes)
result -= info->dsBufferSizeInBytes;
return result;
}
if (g_audioDeviceCache[deviceID].refCount) {
g_audioDeviceCache[deviceID].refCount--;
}
if (g_audioDeviceCache[deviceID].refCount == 0) {
if (g_audioDeviceCache[deviceID].dev != NULL) {
if (g_audioDeviceCache[deviceID].isSource) {
DEV_PLAY(deviceID)->Release();
} else {
DEV_CAPTURE(deviceID)->Release();
}
g_audioDeviceCache[deviceID].dev = NULL;
}
}
}
#ifndef _WAVEFORMATEXTENSIBLE_
#define _WAVEFORMATEXTENSIBLE_
typedef struct {
WAVEFORMATEX Format;
union {
WORD wValidBitsPerSample; /* bits of precision */
WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
WORD wReserved; /* If neither applies, set to zero. */
} Samples;
DWORD dwChannelMask; /* which channels are */
/* present in stream */
GUID SubFormat;
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
#endif // !_WAVEFORMATEXTENSIBLE_
#if !defined(WAVE_FORMAT_EXTENSIBLE)
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
#if !defined(DEFINE_WAVEFORMATEX_GUID)
#define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
#endif
#ifndef STATIC_KSDATAFORMAT_SUBTYPE_PCM
#define STATIC_KSDATAFORMAT_SUBTYPE_PCM\
DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)
#endif
if (info->isSource) {
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
| DSBCAPS_GLOBALFOCUS;
dsbdesc.dwBufferBytes = info->dsBufferSizeInBytes;
dsbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;
res = DEV_PLAY(info->deviceID)->CreateSoundBuffer
(&dsbdesc, (LPDIRECTSOUNDBUFFER*) &buffer, NULL);
} else {
memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
dscbdesc.dwFlags = 0;
dscbdesc.dwBufferBytes = info->dsBufferSizeInBytes;
dscbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;
res = DEV_CAPTURE(info->deviceID)->CreateCaptureBuffer
(&dscbdesc, (LPDIRECTSOUNDCAPTUREBUFFER*) &buffer, NULL);
}
if (FAILED(res)) {
ERROR1("DS_createSoundBuffer: ERROR: Failed to create sound buffer: %s", TranslateDSError(res));
return NULL;
}
return buffer;
}
DS_Info* info;
void* buffer;
TRACE0("> DAUDIO_Open\n");
info->deviceID = deviceID;
info->isSource = isSource;
info->bitsPerSample = sampleSizeInBits;
info->frameSize = frameSize;
info->framePos = 0;
info->started = FALSE;
info->underrun = FALSE;
if (!DS_addDeviceRef(deviceID)) {
DS_removeDeviceRef(deviceID);
free(info);
return NULL;
}
buffer = DS_createSoundBuffer(info,
sampleRate,
sampleSizeInBits,
channels,
bufferSizeInBytes);
if (!buffer) {
DS_removeDeviceRef(deviceID);
free(info);
return NULL;
}
if (info->isSource) {
info->playBuffer = (LPDIRECTSOUNDBUFFER) buffer;
} else {
info->captureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) buffer;
}
DS_clearBuffer(info, FALSE /* entire buffer */);
TRACE0("> DAUDIO_Start\n");
if (info->isSource) {
res = info->playBuffer->GetStatus(&status);
if (res == DS_OK) {
if (status & DSBSTATUS_LOOPING) {
ERROR0("DAUDIO_Start: ERROR: Already started!");
return TRUE;
}
TRACE0("> DAUDIO_Stop\n");
info->started = FALSE;
if (info->isSource) {
info->playBuffer->Stop();
} else {
info->captureBuffer->Stop();
}
TRACE0("< DAUDIO_Stop\n");
return TRUE;
}
TRACE0("DAUDIO_Close\n");
if (info != NULL) {
DS_destroySoundBuffer(info);
DS_removeDeviceRef(info->deviceID);
free(info);
}
}
if (info->isSource) {
if (FAILED(info->playBuffer->GetCurrentPosition(playCursor, writeCursor))) {
ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");
return 0;
}
int processing = DS_getDistance(info, (int)*playCursor, (int)*writeCursor);
// workaround: sometimes DirectSound report writeCursor is less (for several bytes) then playCursor
if (processing > info->dsBufferSizeInBytes / 2) {
*writeCursor = *playCursor;
processing = 0;
}
TRACE3(" playCursor=%d, writeCursor=%d, info->writePos=%d\n",
*playCursor, *writeCursor, info->writePos);
*bufferSize = info->bufferSizeInBytes;
if (fromPlayCursor) {
*bufferSize += processing;
}
DS_CheckUnderrun(info, *playCursor, *writeCursor);
if (info->writePos == -1 || (info->underrun && !fromPlayCursor)) {
/* always full buffer if at beginning */
available = *bufferSize;
} else {
int currWriteAhead = DS_getDistance(info, fromPlayCursor ? (int)*playCursor : (int)*writeCursor,
info->writePos);
if (currWriteAhead > *bufferSize) {
if (info->underrun) {
// playCursor surpassed writePos - no valid data, whole buffer available
available = *bufferSize;
} else {
// the case may occur after stop(), when writeCursor jumps back to playCursor
// so "actual" buffer size has grown
*bufferSize = currWriteAhead;
available = 0;
}
} else {
available = *bufferSize - currWriteAhead;
}
}
} else {
if (FAILED(info->captureBuffer->GetCurrentPosition(playCursor, writeCursor))) {
ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");
return 0;
}
*bufferSize = info->bufferSizeInBytes;
if (fromPlayCursor) {
*bufferSize += DS_getDistance(info, (int)*playCursor, (int)*writeCursor);
}
TRACE4(" captureCursor=%d, readCursor=%d, info->readPos=%d refBufferSize=%d\n",
*playCursor, *writeCursor, info->writePos, *bufferSize);
if (info->writePos == -1) {
/* always empty buffer if at beginning */
info->writePos = (int) (*writeCursor);
}
if (fromPlayCursor) {
available = ((int) (*playCursor) - info->writePos);
} else {
available = ((int) (*writeCursor) - info->writePos);
}
if (available < 0) {
available += info->dsBufferSizeInBytes;
}
if (!fromPlayCursor && available > info->bufferSizeInBytes) {
/* overflow */
ERROR2("DS_GetAvailable: ERROR: overflow detected: "
"DirectSoundBufferSize=%d, bufferSize=%d, ",
info->dsBufferSizeInBytes, info->bufferSizeInBytes);
ERROR3("captureCursor=%d, readCursor=%d, info->readPos=%d\n",
*playCursor, *writeCursor, info->writePos);
/* advance read position, to allow exactly one buffer worth of data */
newReadPos = (int) (*writeCursor) - info->bufferSizeInBytes;
if (newReadPos < 0) {
newReadPos += info->dsBufferSizeInBytes;
}
info->writePos = newReadPos;
available = info->bufferSizeInBytes;
}
}
available = (available / info->frameSize) * info->frameSize;
info->writePos = thisWritePos;
/* update position
* must be AFTER updating writePos,
* so that getSvailable doesn't return too little,
* so that getFramePos doesn't jump
*/
info->framePos += (byteSize / info->frameSize);
/* decrease silenced bytes */
if (info->silencedBytes > byteSize) {
info->silencedBytes -= byteSize;
} else {
info->silencedBytes = 0;
}
break;
} /* while */
// returns -1 on error
int DAUDIO_Read(void* id, char* data, int byteSize) {
DS_Info* info = (DS_Info*) id;
int available;
int thisReadPos;
DWORD captureCursor, readCursor;
HRESULT res;
void* buffer1, *buffer2;
DWORD buffer1len, buffer2len;
int bufferSize;
/* update position
* must be BEFORE updating readPos,
* so that getAvailable doesn't return too much,
* so that getFramePos doesn't jump
*/
info->framePos += (byteSize / info->frameSize);
info->writePos = thisReadPos;
}
}
TRACE0("DAUDIO_Flush\n");
if (info->isSource) {
info->playBuffer->Stop();
DS_clearBuffer(info, FALSE /* entire buffer */);
} else {
DWORD captureCursor, readCursor;
/* set the read pointer to the current read position */
if (FAILED(info->captureBuffer->GetCurrentPosition(&captureCursor, &readCursor))) {
ERROR0("DAUDIO_Flush: ERROR: Failed to get current position.");
return FALSE;
}
DS_clearBuffer(info, FALSE /* entire buffer */);
/* SHOULD set to *captureCursor*,
* but that would be detected as overflow
* in a subsequent GetAvailable() call.
*/
info->writePos = (int) readCursor;
}
return TRUE;
}
#endif // USE_DAUDIO