You are on page 1of 50

/*

* Copyright 2002-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 "Ports.h"

#if USE_PORTS == TRUE

typedef struct tag_PortControlID PortControlID;

typedef struct tag_PortInfo {


// Windows API stuff
HMIXER handle;
INT32 mixerIndex;
int dstLineCount; // how many MIXERLINE structs in dstMixerLine
MIXERLINE* dstLines;
int srcLineCount; // how many MIXERLINE structs in srcMixerLine
MIXERLINE* srcLines; // contains all the Source Lines of dstLines
// Java Sound mapping
int targetPortCount; // one port per dstLine (playback)
int sourcePortCount; // only WAVEIN; one port maps to one srcLine
LPMIXERLINE* ports; // points into dstLines and dstLines. Starts with Target Ports (Playback)
int maxControlCount; // upper bound of number of controls
int usedControlIDs; // number of items already filled in controlIDs
PortControlID* controlIDs; // the control IDs themselves
int usedMuxData;
MIXERCONTROLDETAILS_BOOLEAN* muxData;
} PortInfo;

#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

typedef struct tag_PortControlID {


PortInfo* portInfo;
INT32 controlType; // one of PORT_CONTROL_TYPE_XX
INT32 min;
INT32 max;
MIXERCONTROLDETAILS details;
union {
MIXERCONTROLDETAILS_BOOLEAN boolValue;
MIXERCONTROLDETAILS_SIGNED signedValue;
MIXERCONTROLDETAILS_UNSIGNED unsignedValue[2];
INT32 muxIndex;
};
} PortControlID;

int getControlInfo(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls);

INT32 PORT_GetPortMixerCount() {
return (INT32) mixerGetNumDevs();
}

#ifdef USE_TRACE

char* getLineFlags(DWORD flags) {


static char ret[100];
ret[0]=0;
if (flags & MIXERLINE_LINEF_ACTIVE) {
strcat(ret, "ACTIVE ");
flags ^= MIXERLINE_LINEF_ACTIVE;
}
if (flags & MIXERLINE_LINEF_DISCONNECTED) {
strcat(ret, "DISCONNECTED ");
flags ^= MIXERLINE_LINEF_DISCONNECTED;
}
if (flags & MIXERLINE_LINEF_SOURCE) {
strcat(ret, "SOURCE ");
flags ^= MIXERLINE_LINEF_SOURCE;
}
if (flags!=0) {
UINT_PTR r = (UINT_PTR) ret;
r += strlen(ret);
sprintf((char*) r, "%d", flags);
}
return ret;
}

char* getComponentType(int componentType) {


switch (componentType) {
case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES: return "DST_HEADPHONES";
case MIXERLINE_COMPONENTTYPE_DST_LINE: return "DST_LINE";
case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: return "DST_SPEAKERS";
case MIXERLINE_COMPONENTTYPE_DST_DIGITAL: return "DST_DIGITAL";
case MIXERLINE_COMPONENTTYPE_DST_MONITOR: return "DST_MONITOR";
case MIXERLINE_COMPONENTTYPE_DST_TELEPHONE: return "DST_TELEPHONE";
case MIXERLINE_COMPONENTTYPE_DST_UNDEFINED: return "DST_UNDEFINED";
case MIXERLINE_COMPONENTTYPE_DST_VOICEIN: return "DST_VOICEIN";
case MIXERLINE_COMPONENTTYPE_DST_WAVEIN: return "DST_WAVEIN";

case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: return "SRC_COMPACTDISC";


case MIXERLINE_COMPONENTTYPE_SRC_LINE: return "SRC_LINE";
case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: return "SRC_MICROPHONE";
case MIXERLINE_COMPONENTTYPE_SRC_ANALOG: return "SRC_ANALOG";
case MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY: return "SRC_AUXILIARY";
case MIXERLINE_COMPONENTTYPE_SRC_DIGITAL: return "SRC_DIGITAL";
case MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER: return "SRC_PCSPEAKER";
case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER: return "SRC_SYNTHESIZER";
case MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE: return "SRC_TELEPHONE";
case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED: return "SRC_UNDEFINED";
case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT: return "SRC_WAVEOUT";
}
return "";
}

void printMixerLine(MIXERLINE* mixerLine) {


TRACE2("MIXERLINE destination=%d, source=%d, ", mixerLine->dwDestination, mixerLine->dwSource);
TRACE3("channels=%d, connections=%d, controls=%d, ", mixerLine->cChannels, mixerLine->cConnections,
mixerLine->cControls);
TRACE3("\"%s\", fdwLine=%s, componentType=%s\n", mixerLine->szName, getLineFlags(mixerLine->fdwLine),
getComponentType(mixerLine->dwComponentType));
}

char* getControlClass(int controlType) {


switch (controlType & MIXERCONTROL_CT_CLASS_MASK) {
case MIXERCONTROL_CT_CLASS_CUSTOM : return "CLASS_CUSTOM";
case MIXERCONTROL_CT_CLASS_FADER : return "CLASS_FADER ";
case MIXERCONTROL_CT_CLASS_LIST : return "CLASS_LIST ";
case MIXERCONTROL_CT_CLASS_METER : return "CLASS_METER ";
case MIXERCONTROL_CT_CLASS_NUMBER : return "CLASS_NUMBER";
case MIXERCONTROL_CT_CLASS_SLIDER : return "CLASS_SLIDER";
case MIXERCONTROL_CT_CLASS_SWITCH : return "CLASS_SWITCH";
case MIXERCONTROL_CT_CLASS_TIME : return "CLASS_TIME ";
}
return "unknown class";
}

char* getControlType(int controlType) {


switch (controlType) {
case MIXERCONTROL_CONTROLTYPE_CUSTOM : return "CUSTOM ";
case MIXERCONTROL_CONTROLTYPE_BASS : return "BASS ";
case MIXERCONTROL_CONTROLTYPE_EQUALIZER : return "EQUALIZER ";
case MIXERCONTROL_CONTROLTYPE_FADER : return "FADER ";
case MIXERCONTROL_CONTROLTYPE_TREBLE : return "TREBLE ";
case MIXERCONTROL_CONTROLTYPE_VOLUME : return "VOLUME ";
case MIXERCONTROL_CONTROLTYPE_MIXER : return "MIXER ";
case MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT : return "MULTIPLESELECT ";
case MIXERCONTROL_CONTROLTYPE_MUX : return "MUX ";
case MIXERCONTROL_CONTROLTYPE_SINGLESELECT : return "SINGLESELECT ";
case MIXERCONTROL_CONTROLTYPE_BOOLEANMETER : return "BOOLEANMETER ";
case MIXERCONTROL_CONTROLTYPE_PEAKMETER : return "PEAKMETER ";
case MIXERCONTROL_CONTROLTYPE_SIGNEDMETER : return "SIGNEDMETER ";
case MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER : return "UNSIGNEDMETER ";
case MIXERCONTROL_CONTROLTYPE_DECIBELS : return "DECIBELS ";
case MIXERCONTROL_CONTROLTYPE_PERCENT : return "PERCENT ";
case MIXERCONTROL_CONTROLTYPE_SIGNED : return "SIGNED ";
case MIXERCONTROL_CONTROLTYPE_UNSIGNED : return "UNSIGNED ";
case MIXERCONTROL_CONTROLTYPE_PAN : return "PAN ";
case MIXERCONTROL_CONTROLTYPE_QSOUNDPAN : return "QSOUNDPAN ";
case MIXERCONTROL_CONTROLTYPE_SLIDER : return "SLIDER ";
case MIXERCONTROL_CONTROLTYPE_BOOLEAN : return "BOOLEAN ";
case MIXERCONTROL_CONTROLTYPE_BUTTON : return "BUTTON ";
case MIXERCONTROL_CONTROLTYPE_LOUDNESS : return "LOUDNESS ";
case MIXERCONTROL_CONTROLTYPE_MONO : return "MONO ";
case MIXERCONTROL_CONTROLTYPE_MUTE : return "MUTE ";
case MIXERCONTROL_CONTROLTYPE_ONOFF : return "ONOFF ";
case MIXERCONTROL_CONTROLTYPE_STEREOENH : return "STEREOENH ";
case MIXERCONTROL_CONTROLTYPE_MICROTIME : return "MICROTIME ";
case MIXERCONTROL_CONTROLTYPE_MILLITIME : return "MILLITIME ";
}
return "unknown";
}

char* getControlState(DWORD controlState) {


static char ret[100];
ret[0]=0;
if (controlState & MIXERCONTROL_CONTROLF_DISABLED) {
strcat(ret, "DISABLED ");
controlState ^= MIXERCONTROL_CONTROLF_DISABLED;
}
if (controlState & MIXERCONTROL_CONTROLF_MULTIPLE) {
strcat(ret, "MULTIPLE ");
controlState ^= MIXERCONTROL_CONTROLF_MULTIPLE;
}
if (controlState & MIXERCONTROL_CONTROLF_UNIFORM) {
strcat(ret, "UNIFORM ");
controlState ^= MIXERCONTROL_CONTROLF_UNIFORM;
}
if (controlState!=0) {
UINT_PTR r = (UINT_PTR) ret;
r += strlen(ret);
sprintf((char*) r, "%d", controlState);
}
return ret;
}

void printControl(MIXERCONTROL* control) {


TRACE3(" %s: dwControlType=%s/%s, ", control->szName, getControlClass(control->dwControlType),
getControlType(control->dwControlType));
TRACE3("multpleItems=%d, state=%d, %s\n", control->cMultipleItems, control->fdwControl,
getControlState(control->fdwControl));
}

void printMixerLineControls(HMIXER handle, MIXERLINE* mixerLine) {


MIXERLINECONTROLS controls;
DWORD i;
TRACE1(" Controls for %s:\n", mixerLine->szName);
if (getControlInfo(handle, mixerLine, &controls)) {
for (i = 0; i < controls.cControls; i++) {
printControl(&controls.pamxctrl[i]);
}
if (controls.pamxctrl) {
free(controls.pamxctrl);
controls.pamxctrl = NULL;
}
}
}

void printInfo(PortInfo* info) {


TRACE5(" PortInfo %p: handle=%p, mixerIndex=%d, dstLineCount=%d, dstLines=%p, ", info, (void*) info->handle,
info->mixerIndex, info->dstLineCount, info->dstLines);
TRACE5("srcLineCount=%d, srcLines=%p, targetPortCount=%d, sourcePortCount=%d, ports=%p, ", info-
>srcLineCount, info->srcLines, info->targetPortCount, info->sourcePortCount, info->ports);
TRACE3("maxControlCount=%d, usedControlIDs=%d, controlIDs=%p \n", info->maxControlCount, info-
>usedControlIDs, info->controlIDs);
TRACE2("usedMuxData=%d, muxData=%p, controlIDs=%p \n", info->usedMuxData, info->muxData);
}

#endif // USE_TRACE
// internal utility functions

int getMixerLineByDestination(HMIXER handle, DWORD dstIndex, MIXERLINE* mixerLine) {


mixerLine->cbStruct = sizeof(MIXERLINE);
mixerLine->dwDestination = dstIndex;
if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine,
MIXER_GETLINEINFOF_DESTINATION | MIXER_OBJECTF_HMIXER
) == MMSYSERR_NOERROR) {
return TRUE;
}
mixerLine->cControls = 0;
mixerLine->cConnections = 0;
return FALSE;
}

int getMixerLineByType(HMIXER handle, DWORD linetype, MIXERLINE* mixerLine) {


mixerLine->cbStruct = sizeof(MIXERLINE);
mixerLine->dwComponentType = linetype;
if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine,
MIXER_GETLINEINFOF_COMPONENTTYPE | MIXER_OBJECTF_HMIXER
) == MMSYSERR_NOERROR) {
return TRUE;
}
mixerLine->cControls = 0;
mixerLine->cConnections = 0;
return FALSE;
}

int getMixerLineBySource(HMIXER handle, DWORD dstIndex, DWORD srcIndex, MIXERLINE* mixerLine) {


mixerLine->cbStruct = sizeof(MIXERLINE);
mixerLine->dwDestination = dstIndex;
mixerLine->dwSource = srcIndex;
if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine,
MIXER_GETLINEINFOF_SOURCE | MIXER_OBJECTF_HMIXER
) == MMSYSERR_NOERROR) {
return TRUE;
}
mixerLine->cControls = 0;
mixerLine->cConnections = 0;
return FALSE;
}

int getControlInfo(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls) {


int ret = FALSE;

//TRACE2(">getControlInfo for line %s with %d controls\n", line->szName, line->cControls);


controls->pamxctrl = NULL;
if (line->cControls > 0) {
// line points to the requested line.
// Reserve memory for the control infos
controls->cbStruct = sizeof(MIXERLINECONTROLS);
controls->dwLineID = line->dwLineID;
controls->cControls = line->cControls;
controls->cbmxctrl = sizeof(MIXERCONTROL);
controls->pamxctrl = (MIXERCONTROL*) malloc(sizeof(MIXERCONTROL) * line->cControls);
if (controls->pamxctrl) {
//TRACE0(" calling mixerGetLineControls\n");
ret = mixerGetLineControls((HMIXEROBJ) handle, controls,
MIXER_GETLINECONTROLSF_ALL | MIXER_OBJECTF_HMIXER) == MMSYSERR_NOERROR;
}
}
if (!ret) {
if (controls->pamxctrl) {
free(controls->pamxctrl);
controls->pamxctrl = NULL;
}
}
//TRACE0("<getControlInfo \n");
return ret;
}

// 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;
}

///// implemented functions of Ports.h

INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* description) {


MIXERCAPS mixerCaps;
if (mixerGetDevCaps(mixerIndex, &mixerCaps, sizeof(MIXERCAPS)) == MMSYSERR_NOERROR) {
strncpy(description->name, mixerCaps.szPname, PORT_STRING_LENGTH-1);
description->name[PORT_STRING_LENGTH-1] = 0;
sprintf(description->version, "%d.%d", (mixerCaps.vDriverVersion & 0xFF00) >> 8, mixerCaps.vDriverVersion
& 0xFF);
strncpy(description->description, "Port Mixer", PORT_STRING_LENGTH-1);
return TRUE;
}
return FALSE;
}

int getDestinationCount(HMIXER handle) {


int ret = 0;
MIXERCAPS mixerCaps;

if (mixerGetDevCaps((UINT_PTR) handle, &mixerCaps, sizeof(MIXERCAPS)) == MMSYSERR_NOERROR) {


ret = mixerCaps.cDestinations;
}
return ret;
}

void* PORT_Open(INT32 mixerIndex) {


PortInfo* info = NULL;
MMRESULT mmres;
HMIXER handle;
MIXERLINE* waveInLine;
int success = FALSE;
int src, dst, srcIndex, waveInHasControls;
int dstCount;

TRACE0("PORT_Open\n");
mmres = mixerOpen((LPHMIXER) &handle, mixerIndex, 0, 0, MIXER_OBJECTF_MIXER);
if (mmres != MMSYSERR_NOERROR) {
return NULL;
}

info = (PortInfo*) malloc(sizeof(PortInfo));


if (info != NULL) {
success = TRUE;
memset(info, 0, sizeof(PortInfo));
info->handle = handle;
info->mixerIndex = mixerIndex;
waveInLine = NULL;
waveInHasControls = FALSE;
// number of destinations
dstCount = getDestinationCount(handle);
if (dstCount) {
info->dstLines = (MIXERLINE*) malloc(dstCount * sizeof(MIXERLINE));
success = (info->dstLines != NULL);
}
if (success && info->dstLines) {
// go through all destinations and fill the structures in PortInfo
for (dst = 0; dst < dstCount; dst++) {
if (getMixerLineByDestination(handle, dst, &info->dstLines[info->dstLineCount])) {
info->srcLineCount += info->dstLines[info->dstLineCount].cConnections;
if (info->dstLines[info->dstLineCount].dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN
&& !waveInLine) {
waveInLine = &info->dstLines[info->dstLineCount];
info->sourcePortCount = waveInLine->cConnections;
if (lineHasControls(handle, waveInLine, NULL)) {
// add a single port for all the controls that do not show in the MUX/MIXER controls
info->sourcePortCount++;
waveInHasControls = TRUE;
}
} else {
info->targetPortCount++;
}
info->dstLineCount++;
}
}
}
if (info->srcLineCount) {
info->srcLines = (MIXERLINE*) malloc(info->srcLineCount * sizeof(MIXERLINE));
success = (info->srcLines != NULL);
}
if (success && info->srcLines) {
// go through all destinations and fill the source line structures in PortInfo
srcIndex = 0;
for (dst = 0; dst < info->dstLineCount; dst++) {
// remember the srcIndex for mapping the srcLines to this destination line
info->dstLines[dst].dwUser = srcIndex;
for (src = 0; src < (int) info->dstLines[dst].cConnections; src++) {
getMixerLineBySource(handle, dst, src, &info->srcLines[srcIndex++]);
}
}
}
// now create the mapping to Java Sound
if ((info->targetPortCount + info->sourcePortCount) > 0) {
info->ports = (LPMIXERLINE*) malloc((info->targetPortCount + info->sourcePortCount) *
sizeof(LPMIXERLINE));
success = (info->ports != NULL);
}
if (success && info->ports) {
// first add the target MIXERLINEs to the array
srcIndex = 0;
for (dst = 0; dst < info->dstLineCount; dst++) {
if (waveInLine != &info->dstLines[dst]) {
info->ports[srcIndex++] = &info->dstLines[dst];
}
}
if (srcIndex != info->targetPortCount) {
ERROR2("srcIndex=%d is NOT targetPortCount=%d !\n", srcIndex, info->targetPortCount);
}
//srcIndex = info->targetPortCount; // should be automatic!
if (waveInLine) {
// if the recording destination line has controls, add the line
if (waveInHasControls) {
info->ports[srcIndex++] = waveInLine;
}
for (src = 0; src < (int) waveInLine->cConnections; src++) {
info->ports[srcIndex++] = &info->srcLines[src + waveInLine->dwUser];
}
}
if (srcIndex != (info->targetPortCount + info->sourcePortCount)) {
ERROR2("srcIndex=%d is NOT PortCount=%d !\n", srcIndex, (info->targetPortCount + info-
>sourcePortCount));
}
}
}
if (!success) {
if (handle != NULL) {
mixerClose(handle);
}
PORT_Close((void*) info);
info = NULL;
}
return info;
}

void PORT_Close(void* id) {


TRACE0("PORT_Close\n");
if (id != NULL) {
PortInfo* info = (PortInfo*) id;
if (info->handle) {
mixerClose(info->handle);
info->handle = NULL;
}
if (info->dstLines) {
free(info->dstLines);
info->dstLines = NULL;
}
if (info->srcLines) {
free(info->srcLines);
info->srcLines=NULL;
}
if (info->ports) {
free(info->ports);
info->ports = NULL;
}
if (info->controlIDs) {
free(info->controlIDs);
info->controlIDs = NULL;
}
if (info->muxData) {
free(info->muxData);
info->muxData = NULL;
}
free(info);
}
}

INT32 PORT_GetPortCount(void* id) {


int ret = 0;
PortInfo* info = (PortInfo*) id;
if (info != NULL) {
ret = info->targetPortCount + info->sourcePortCount;
}
return ret;
}

int componentType2type(DWORD componentType) {


int ret = 0;
if (componentType >= MIXERLINE_COMPONENTTYPE_DST_FIRST && componentType <= MIXERLINE_COMPONENTTYPE_DST_LAST)
{
ret = PORT_DST_UNKNOWN;
}
else if (componentType >= MIXERLINE_COMPONENTTYPE_SRC_FIRST && componentType <=
MIXERLINE_COMPONENTTYPE_SRC_LAST) {
ret = PORT_SRC_UNKNOWN;
}
// handle special cases
switch (componentType) {
case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES: ret = PORT_DST_HEADPHONE; break;
case MIXERLINE_COMPONENTTYPE_DST_LINE: ret = PORT_DST_LINE_OUT; break;
case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: ret = PORT_DST_SPEAKER; break;
case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: ret = PORT_SRC_COMPACT_DISC; break;
case MIXERLINE_COMPONENTTYPE_SRC_LINE: ret = PORT_SRC_LINE_IN; break;
case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: ret = PORT_SRC_MICROPHONE; break;
}
return ret;
}

INT32 PORT_GetPortType(void* id, INT32 portIndex) {


MIXERLINE* line;
PortInfo* info = (PortInfo*) id;
if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) {
line = info->ports[portIndex];
if (line) {
return componentType2type(line->dwComponentType);
}
}
return 0;
}

INT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) {


MIXERLINE* line;
PortInfo* info = (PortInfo*) id;

if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) {


line = info->ports[portIndex];
if (line) {
strncpy(name, line->szName, len-1);
name[len-1] = 0;
return TRUE;
}
}
return FALSE;
}

int getControlCount(HMIXER handle, MIXERLINE* line, INT32* muxCount) {


MIXERLINECONTROLS controls;
int ret = 0;
UINT i;

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;
}

MIXERLINE* findDestLine(PortInfo* info, DWORD dwDestination) {


int i;
TRACE0(">findDestLine\n");
for (i = 0; i < info->dstLineCount; i++) {
if (info->dstLines[i].dwDestination == dwDestination) {
TRACE0("<findDestLine\n");
return &(info->dstLines[i]);
}
}
TRACE0("<findDestLine NULL\n");
return NULL;
}

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 createPortControl(PortInfo* info, PortControlCreator* creator, MIXERCONTROL* mixerControl,


INT32 type, void** controlObjects, int* controlCount) {
PortControlID* controlID;
void* newControl = NULL;
char* typeName = mixerControl->szName;
float min;
TRACE0(">createPortControl\n");

// fill the ControlID structure and add this control


if (info->usedControlIDs >= info->maxControlCount) {
ERROR1("not enough free controlIDs !! maxControlIDs = %d\n", info->maxControlCount);
return;
}
controlID = &(info->controlIDs[info->usedControlIDs]);
controlID->portInfo = info;
controlID->controlType = type;
controlID->details.cbStruct = sizeof(MIXERCONTROLDETAILS);
controlID->details.dwControlID = mixerControl->dwControlID;
controlID->details.cChannels = 1; // uniform
controlID->details.cMultipleItems = 0;
switch (type) {
case PORT_CONTROL_TYPE_BOOLEAN:
TRACE0(" PORT_CONTROL_TYPE_BOOLEAN\n");
controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
controlID->details.paDetails = &(controlID->boolValue);
if (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) {
typeName = CONTROL_TYPE_MUTE;
}
newControl = (creator->newBooleanControl)(creator, controlID, typeName);
break;
case PORT_CONTROL_TYPE_SIGNED:
TRACE0(" PORT_CONTROL_TYPE_SIGNED\n");
controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_SIGNED);
controlID->details.paDetails = &(controlID->signedValue);
controlID->min = (INT32) mixerControl->Bounds.lMinimum;
controlID->max = (INT32) mixerControl->Bounds.lMaximum;
if (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_PAN) {
typeName = CONTROL_TYPE_PAN;
}
newControl = (creator->newFloatControl)(creator, controlID, typeName,
-1.0f, 1.0f, 2.0f / (controlID->max - controlID->min + 1), "");
break;
case PORT_CONTROL_TYPE_FAKE_VOLUME: // fall through
case PORT_CONTROL_TYPE_FAKE_BALANCE: // fall through
case PORT_CONTROL_TYPE_UNSIGNED:
TRACE0(" PORT_CONTROL_TYPE_UNSIGNED\n");
controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
controlID->details.paDetails = &(controlID->unsignedValue[0]);
controlID->min = (INT32) mixerControl->Bounds.dwMinimum;
controlID->max = (INT32) mixerControl->Bounds.dwMaximum;
min = 0.0f;
if ((type == PORT_CONTROL_TYPE_FAKE_VOLUME)
|| (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)) {
typeName = CONTROL_TYPE_VOLUME;
}
if (type == PORT_CONTROL_TYPE_FAKE_BALANCE) {
typeName = CONTROL_TYPE_BALANCE;
min = -1.0f;
}
if ((type == PORT_CONTROL_TYPE_FAKE_VOLUME)
|| (type == PORT_CONTROL_TYPE_FAKE_BALANCE)) {
controlID->details.cChannels = 2;
}
TRACE0(" ....PORT_CONTROL_TYPE_UNSIGNED\n");
newControl = (creator->newFloatControl)(creator, controlID, typeName,
min, 1.0f, 1.0f / (controlID->max - controlID->min + 1), "");
break;
default:
ERROR1("createPortControl: unknown type %d !", type);
break;
}
if (newControl) {
controlObjects[*controlCount] = newControl;
(*controlCount)++;
info->usedControlIDs++;
}
TRACE0("<createPortControl\n");
}

void createLineControls(PortInfo* info, PortControlCreator* creator, MIXERLINE* line, void** controlObjects, int*
controlCount) {
MIXERLINECONTROLS controlInfos;
MIXERCONTROL* mixerControl;
UINT i;
INT32 type;

TRACE1(">createLineControls for line %s\n", line->szName);


// go through all controls of line
controlInfos.pamxctrl = NULL;
if (getControlInfo(info->handle, line, &controlInfos)) {
for (i = 0; i < controlInfos.cControls; i++) {
TRACE1(" %d\n", i);
mixerControl = &(controlInfos.pamxctrl[i]);
type = 0;
switch (mixerControl->dwControlType) {
case MIXERCONTROL_CONTROLTYPE_BOOLEAN : // fall through
case MIXERCONTROL_CONTROLTYPE_BUTTON : // fall through
case MIXERCONTROL_CONTROLTYPE_LOUDNESS : // fall through
case MIXERCONTROL_CONTROLTYPE_MONO : // fall through
case MIXERCONTROL_CONTROLTYPE_MUTE : // fall through
case MIXERCONTROL_CONTROLTYPE_ONOFF : // fall through
case MIXERCONTROL_CONTROLTYPE_STEREOENH: type = PORT_CONTROL_TYPE_BOOLEAN; break;

case MIXERCONTROL_CONTROLTYPE_PAN : // fall through


case MIXERCONTROL_CONTROLTYPE_QSOUNDPAN: // fall through
case MIXERCONTROL_CONTROLTYPE_SLIDER : type = PORT_CONTROL_TYPE_SIGNED; break;

case MIXERCONTROL_CONTROLTYPE_BASS : // fall through


//case MIXERCONTROL_CONTROLTYPE_EQUALIZER: // fall through
case MIXERCONTROL_CONTROLTYPE_FADER : // fall through
case MIXERCONTROL_CONTROLTYPE_TREBLE : type = PORT_CONTROL_TYPE_UNSIGNED; break;
case MIXERCONTROL_CONTROLTYPE_VOLUME :
type = PORT_CONTROL_TYPE_UNSIGNED;
if (line->cChannels == 2 && ((mixerControl->fdwControl & MIXERCONTROL_CONTROLF_UNIFORM) ==
0)) {
type = PORT_CONTROL_TYPE_FAKE_VOLUME;
}
break;
}
if (type != 0) {
createPortControl(info, creator, mixerControl, type, controlObjects, controlCount);
// create fake balance for fake volume
if (type == PORT_CONTROL_TYPE_FAKE_VOLUME) {
createPortControl(info, creator, mixerControl, PORT_CONTROL_TYPE_FAKE_BALANCE,
controlObjects, controlCount);
}
}
}
}
if (controlInfos.pamxctrl) {
free(controlInfos.pamxctrl);
controlInfos.pamxctrl = NULL;
}
TRACE0("<createLineControls\n");
}

void addCompoundControl(PortInfo* info, PortControlCreator* creator, char* name, void** controlObjects, int*
controlCount) {
void* compControl;

TRACE1(">addCompoundControl %d controls\n", *controlCount);


if (*controlCount) {
// create compound control and add it to the vector
compControl = (creator->newCompoundControl)(creator, name, controlObjects, *controlCount);
if (compControl) {
TRACE1(" addCompoundControl: calling addControl %p\n", compControl);
(creator->addControl)(creator, compControl);
}
*controlCount = 0;
}
TRACE0("<addCompoundControl\n");
}

void addAllControls(PortInfo* info, PortControlCreator* creator, void** controlObjects, int* controlCount) {


int i = 0;

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");
}

void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {


MIXERLINE* line;
PortInfo* info = (PortInfo*) id;
int portCount = PORT_GetPortCount(id);
void** controls = NULL;
int controlCount;
UINT i;

TRACE4(">PORT_GetControls(id=%p, portIndex=%d). controlIDs=%p, maxControlCount=%d\n", id, portIndex, info-


>controlIDs, info->maxControlCount);
if ((portIndex >= 0) && (portIndex < portCount)) {
line = info->ports[portIndex];
if (line) {
// if the memory isn't reserved for the control structures, allocate it
if (!info->controlIDs) {
int i, maxCount = 0, muxCount = 0;
TRACE0("getControl: allocate mem\n");
// get a maximum number of controls
// first for all destination lines
for (i = 0; i < info->dstLineCount; i++) {
MIXERLINE* thisLine = &(info->dstLines[i]);
maxCount += getControlCount(info->handle, thisLine, &muxCount);
}
// then all source lines
for (i = 0; i < info->srcLineCount; i++) {
MIXERLINE* thisLine = &(info->srcLines[i]);
maxCount += getControlCount(info->handle, thisLine, &muxCount);
}
info->maxControlCount = maxCount;
if (maxCount > 0) {
info->controlIDs = (PortControlID*) malloc(sizeof(PortControlID) * maxCount);
} else {
// no ports: nothing to do !
return;
}
TRACE2("Creating muxData for %d elements and %d controlIDs.\n", muxCount, maxCount);
if (muxCount > 0) {
info->muxData = (MIXERCONTROLDETAILS_BOOLEAN*) malloc(sizeof(MIXERCONTROLDETAILS_BOOLEAN) *
muxCount);
}
if (!info->controlIDs || (muxCount && !info->muxData)) {
ERROR3("PORT_GetControls: info->controlIDs=%p, muxCount=%d, info->muxData=%p !!\n", info-
>controlIDs, muxCount, info->muxData);
return;
}
}
if (info->maxControlCount == 0) {
return;
}
controls = (void*) malloc(info->maxControlCount * sizeof(void*));
if (!controls) {
ERROR0("PORT_GetControls: couldn't allocate controls!\n");
return;
}

// add controls of this line


controlCount = 0;
// if this line is part of MUX, add the respective BOOLEANCONTROL as a control
if ((line->fdwLine & MIXERLINE_LINEF_SOURCE) == MIXERLINE_LINEF_SOURCE) {
MIXERLINE* dstLine = findDestLine(info, line->dwDestination);
TRACE0("Port_getControls: this is a source line\n");
if (dstLine) {
// selection controls (implemented as Mute control)
createMuxControl(info, creator, dstLine, line->dwLineID, controls, &controlCount);
}
// then add all controls in one compound control
createLineControls(info, creator, line, controls, &controlCount);
addCompoundControl(info, creator, line->szName, controls, &controlCount);
} else {
TRACE0("getControl: this is a dest line\n");
// if this is a destination line, add its controls
createLineControls(info, creator, line, controls, &controlCount);
addAllControls(info, creator, controls, &controlCount);
// then add all controls of its source lines as one compound control
for (i = 0; i < line->cConnections; i++) {
// then add all controls
MIXERLINE* srcLine = &(info->srcLines[line->dwUser + i]);
TRACE1("PORT_getControls: add source line %d\n", i);
createLineControls(info, creator, srcLine, controls, &controlCount);
addCompoundControl(info, creator, srcLine->szName, controls, &controlCount);
}
}
}
}
if (controls) {
free(controls);
}
TRACE0("< PORT_getControls\n");
}

int getControlValue(PortControlID* controlID) {


if (mixerGetControlDetails((HMIXEROBJ) controlID->portInfo->handle, &(controlID->details),
MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) {
ERROR0("getControlValue: unable to get control details!\n");
//ERROR3(" cbStruct=%d, dwControlID=%d, cChannels=%d, ", controlID->details.cbStruct, controlID-
>details.dwControlID, controlID->details.cChannels);
//ERROR2(" cMultipleItems=%d, cbDetails=%d\n", controlID->details.cMultipleItems, controlID-
>details.cbDetails);
return FALSE;
}
return TRUE;
}

int setControlValue(PortControlID* controlID) {


if (mixerSetControlDetails((HMIXEROBJ) controlID->portInfo->handle, &(controlID->details),
MIXER_SETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) {
ERROR0("setControlValue: unable to set control details!\n");
//ERROR3(" cbStruct=%d, dwControlID=%d, cChannels=%d, ", controlID->details.cbStruct, controlID-
>details.dwControlID, controlID->details.cChannels);
//ERROR2(" cMultipleItems=%d, cbDetails=%d\n", controlID->details.cMultipleItems, controlID-
>details.cbDetails);
return FALSE;
}
return TRUE;
}

INT32 PORT_GetIntValue(void* controlIDV) {


PortControlID* controlID = (PortControlID*) controlIDV;
MIXERCONTROLDETAILS_BOOLEAN* bools;
int ret = 0;
if (getControlValue(controlID)) {
switch (controlID->controlType) {
case PORT_CONTROL_TYPE_MUX: // fall through
case PORT_CONTROL_TYPE_MIXER:
bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails;
ret = (bools[controlID->muxIndex].fValue)?TRUE:FALSE;
break;
case PORT_CONTROL_TYPE_BOOLEAN:
ret = (controlID->boolValue.fValue)?TRUE:FALSE;
break;
default: ERROR1("PORT_GetIntValue: wrong controlType=%d !\n", controlID->controlType);
}
}
return ret;
}
void PORT_SetIntValue(void* controlIDV, INT32 value) {
PortControlID* controlID = (PortControlID*) controlIDV;
MIXERCONTROLDETAILS_BOOLEAN* bools;
UINT i;

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);
}

float getFakeBalance(PortControlID* controlID) {


float volL, volR;
float range = (float) (controlID->max - controlID->min);
// pan is the ratio of left and right
volL = (((float) (controlID->unsignedValue[0].dwValue - controlID->min)) / range);
volR = (((float) (controlID->unsignedValue[1].dwValue - controlID->min)) / range);
if (volL > volR) {
return -1.0f + (volR / volL);
}
else if (volR > volL) {
return 1.0f - (volL / volR);
}
return 0.0f;
}

float getFakeVolume(PortControlID* controlID) {


// volume is the greater value of both
UINT vol = controlID->unsignedValue[0].dwValue;
if (controlID->unsignedValue[1].dwValue > vol) {
vol = controlID->unsignedValue[1].dwValue;
}
return (((float) (vol - controlID->min)) / (controlID->max - controlID->min));
}

/*
* 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;
}
}

float PORT_GetFloatValue(void* controlIDV) {


PortControlID* controlID = (PortControlID*) controlIDV;
float ret = 0.0f;
float range = (float) (controlID->max - controlID->min);
if (getControlValue(controlID)) {
switch (controlID->controlType) {
case PORT_CONTROL_TYPE_SIGNED:
ret = ((float) controlID->signedValue.lValue) / controlID->max;
break;
case PORT_CONTROL_TYPE_UNSIGNED:
ret = (((float) (controlID->unsignedValue[0].dwValue - controlID->min)) / range);
break;
case PORT_CONTROL_TYPE_FAKE_VOLUME:
ret = getFakeVolume(controlID);
break;
case PORT_CONTROL_TYPE_FAKE_BALANCE:
ret = getFakeBalance(controlID);
break;
default: ERROR1("PORT_GetFloatValue: wrong controlType=%d !\n", controlID->controlType);
}
}
return ret;
}

void PORT_SetFloatValue(void* controlIDV, float value) {


PortControlID* controlID = (PortControlID*) controlIDV;
float range = (float) (controlID->max - controlID->min);
switch (controlID->controlType) {
case PORT_CONTROL_TYPE_SIGNED:
controlID->signedValue.lValue = (INT32) ((value * controlID->max) + 0.5f);
break;
case PORT_CONTROL_TYPE_UNSIGNED:
controlID->unsignedValue[0].dwValue = (INT32) ((value * range) + 0.5f) + controlID->min;
break;
case PORT_CONTROL_TYPE_FAKE_VOLUME:
if (!getControlValue(controlID)) {
return;
}
setFakeVolume(controlID, value, getFakeBalance(controlID));
break;
case PORT_CONTROL_TYPE_FAKE_BALANCE:
if (!getControlValue(controlID)) {
return;
}
setFakeVolume(controlID, getFakeVolume(controlID), value);
break;
default:
ERROR1("PORT_SetFloatValue: wrong controlType=%d !\n", controlID->controlType);
return;
}
setControlValue(controlID);
}

#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

/* define this for the silencing/servicing code. Requires USE_TRACE */


//#define USE_DEBUG_SILENCING

#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>

/* include DirectSound headers */


#include <dsound.h>

/* include Java Sound specific headers as C code */


#ifdef __cplusplus
extern "C" {
#endif
#include "DirectAudio.h"
#ifdef __cplusplus
}
#endif

#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

#if USE_DAUDIO == TRUE

/* half a minute to wait before device list is re-read */


#define WAIT_BETWEEN_CACHE_REFRESH_MILLIS 30000

/* maximum number of supported devices, playback+capture */


#define MAX_DS_DEVICES 60

typedef struct {
INT32 mixerIndex;
BOOL isSource;
/* either LPDIRECTSOUND or LPDIRECTSOUNDCAPTURE */
void* dev;
/* how many instances use the dev */
INT32 refCount;
GUID guid;
} DS_AudioDeviceCache;

static DS_AudioDeviceCache g_audioDeviceCache[MAX_DS_DEVICES];


static INT32 g_cacheCount = 0;
static UINT64 g_lastCacheRefreshTime = 0;
static INT32 g_mixerCount = 0;

BOOL DS_lockCache() {
/* dummy implementation for now, Java does locking */
return TRUE;
}

void DS_unlockCache() {
/* dummy implementation for now */
}

static GUID CLSID_DAUDIO_Zero = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

BOOL isEqualGUID(LPGUID lpGuid1, LPGUID lpGuid2) {


if (lpGuid1 == NULL || lpGuid2 == NULL) {
if (lpGuid1 == lpGuid2) {
return TRUE;
}
if (lpGuid1 == NULL) {
lpGuid1 = (LPGUID) (&CLSID_DAUDIO_Zero);
} else {
lpGuid2 = (LPGUID) (&CLSID_DAUDIO_Zero);
}
}
return memcmp(lpGuid1, lpGuid2, sizeof(GUID)) == 0;
}

INT32 findCacheItemByGUID(LPGUID lpGuid, BOOL isSource) {


int i;
for (i = 0; i < g_cacheCount; i++) {
if (isSource == g_audioDeviceCache[i].isSource
&& isEqualGUID(lpGuid, &(g_audioDeviceCache[i].guid))) {
return i;
}
}
return -1;
}

INT32 findCacheItemByMixerIndex(INT32 mixerIndex) {


int i;
for (i = 0; i < g_cacheCount; i++) {
if (g_audioDeviceCache[i].mixerIndex == mixerIndex) {
return i;
}
}
return -1;
}

typedef struct {
INT32 currMixerIndex;
BOOL isSource;
} DS_RefreshCacheStruct;

BOOL CALLBACK DS_RefreshCacheEnum(LPGUID lpGuid,


LPCSTR lpstrDescription,
LPCSTR lpstrModule,
DS_RefreshCacheStruct* rs) {
INT32 cacheIndex = findCacheItemByGUID(lpGuid, rs->isSource);
/*TRACE3("Enumerating %d: %s (%s)\n", cacheIndex, lpstrDescription, lpstrModule);*/
if (cacheIndex == -1) {
/* add this device */
if (g_cacheCount < MAX_DS_DEVICES-1) {
g_audioDeviceCache[g_cacheCount].mixerIndex = rs->currMixerIndex;
g_audioDeviceCache[g_cacheCount].isSource = rs->isSource;
g_audioDeviceCache[g_cacheCount].dev = NULL;
g_audioDeviceCache[g_cacheCount].refCount = 0;
if (lpGuid == NULL) {
memset(&(g_audioDeviceCache[g_cacheCount].guid), 0, sizeof(GUID));
} else {
memcpy(&(g_audioDeviceCache[g_cacheCount].guid), lpGuid, sizeof(GUID));
}
g_cacheCount++;
rs->currMixerIndex++;
} else {
/* failure case: more than MAX_DS_DEVICES available... */
}
} else {
/* device already exists in cache... update mixer number */
g_audioDeviceCache[cacheIndex].mixerIndex = rs->currMixerIndex;
rs->currMixerIndex++;
}
/* continue enumeration */
return TRUE;
}

///// implemented functions of DirectAudio.h

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;

g_lastCacheRefreshTime = (UINT64) timeGetTime();


}
DS_unlockCache();
/*TRACE1("DirectSound: %d installed devices\n", g_mixerCount);*/
return g_mixerCount;
}

BOOL CALLBACK DS_GetDescEnum(LPGUID lpGuid,


LPCSTR lpstrDescription,
LPCSTR lpstrModule,
DirectAudioDeviceDescription* desc) {

INT32 cacheIndex = findCacheItemByGUID(lpGuid, g_audioDeviceCache[desc->deviceID].isSource);


if (cacheIndex == desc->deviceID) {
strncpy(desc->name, lpstrDescription, DAUDIO_STRING_LENGTH);
//strncpy(desc->description, lpstrModule, DAUDIO_STRING_LENGTH);
desc->maxSimulLines = -1;
/* do not continue enumeration */
return FALSE;
}
return TRUE;
}

INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription* desc) {

if (!DS_lockCache()) {
return FALSE;
}

/* set the deviceID field to the cache index */


desc->deviceID = findCacheItemByMixerIndex(mixerIndex);
if (desc->deviceID < 0) {
DS_unlockCache();
return FALSE;
}
desc->maxSimulLines = 0;
if (g_audioDeviceCache[desc->deviceID].isSource) {
DirectSoundEnumerate((LPDSENUMCALLBACK) DS_GetDescEnum, desc);
strncpy(desc->description, "DirectSound Playback", DAUDIO_STRING_LENGTH);
} else {
DirectSoundCaptureEnumerate((LPDSENUMCALLBACK) DS_GetDescEnum, desc);
strncpy(desc->description, "DirectSound Capture", DAUDIO_STRING_LENGTH);
}

/*desc->vendor;
desc->version;*/

DS_unlockCache();
return (desc->maxSimulLines == -1)?TRUE:FALSE;
}

/* multi-channel info: http://www.microsoft.com/whdc/hwdev/tech/audio/multichaud.mspx */

//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};

#define SAMPLERATE_COUNT sizeof(sampleRateArray)/sizeof(INT32)


#define CHANNELS_COUNT sizeof(channelsArray)/sizeof(INT32)
#define BITS_COUNT sizeof(bitsArray)/sizeof(INT32)

void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {

int rateIndex, channelIndex, bitIndex;

/* no need to lock, since deviceID identifies the device sufficiently */

/* sanity */
if (deviceID >= g_cacheCount) {
return;
}
if ((g_audioDeviceCache[deviceID].isSource && !isSource)
|| (!g_audioDeviceCache[deviceID].isSource && isSource)) {
/* only support Playback or Capture */
return;
}

for (rateIndex = 0; rateIndex < SAMPLERATE_COUNT; rateIndex++) {


for (channelIndex = 0; channelIndex < CHANNELS_COUNT; channelIndex++) {
for (bitIndex = 0; bitIndex < BITS_COUNT; bitIndex++) {
DAUDIO_AddAudioFormat(creator, bitsArray[bitIndex],
((bitsArray[bitIndex] + 7) / 8) * channelsArray[channelIndex],
channelsArray[channelIndex],
(float) sampleRateArray[rateIndex],
DAUDIO_PCM,
(bitsArray[bitIndex]==8)?FALSE:TRUE, /* signed */
(bitsArray[bitIndex]==8)?FALSE:
#ifndef _LITTLE_ENDIAN
TRUE /* big endian */
#else
FALSE /* little endian */
#endif
);
}
}
}
}

typedef struct {
int deviceID;
/* for convenience */
BOOL isSource;
/* the secondary buffer (Playback) */
LPDIRECTSOUNDBUFFER playBuffer;
/* the secondary buffer (Capture) */
LPDIRECTSOUNDCAPTUREBUFFER captureBuffer;

/* size of the directsound buffer, usually 2 seconds */


int dsBufferSizeInBytes;
/* size of the read/write-ahead, as specified by Java */
int bufferSizeInBytes;
int bitsPerSample;
int frameSize; // storage size in Bytes

UINT64 framePos;
/* where to write into the buffer.
* -1 if at current position (Playback)
* For Capture, this is the read position
*/
int writePos;

/* if start() had been called */


BOOL started;

/* how many bytes there is silence from current write position */


int silencedBytes;

BOOL underrun;

} DS_Info;

LPSTR TranslateDSError(HRESULT hr) {


switch(hr) {
case DSERR_ALLOCATED:
return "DSERR_ALLOCATED";

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);
};

/* StartBufferHelper class implementation


*/
DS_StartBufferHelper::Data DS_StartBufferHelper::data;

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
}

DWORD WINAPI __stdcall DS_StartBufferHelper::ThreadProc(void *param)


{
while (1) {
// wait for something to do
::WaitForSingleObject(data.startEvent, INFINITE);
if (data.line2Start == NULL) {
// (data.line2Start == NULL) is a signal to terminate thread
break;
}
if (data.line2Start->isSource) {
data.startResult =
data.line2Start->playBuffer->Play(0, 0, DSCBSTART_LOOPING);
} else {
data.startResult =
data.line2Start->captureBuffer->Start(DSCBSTART_LOOPING);
}
::SetEvent(data.startedEvent);
}
return 0;
}

HRESULT DS_StartBufferHelper::StartBuffer(DS_Info* info) {


HRESULT hr;
::EnterCriticalSection(&data.crit_sect);
if (!isInitialized()) {
::LeaveCriticalSection(&data.crit_sect);
return E_FAIL;
}
data.line2Start = info;
::SetEvent(data.startEvent);
::WaitForSingleObject(data.startedEvent, INFINITE);
hr = data.startResult;
::LeaveCriticalSection(&data.crit_sect);
return hr;
}

/* helper routines for DS buffer positions */


/* returns distance from pos1 to pos2
*/
inline int DS_getDistance(DS_Info* info, int pos1, int pos2) {
int distance = pos2 - pos1;
while (distance < 0)
distance += info->dsBufferSizeInBytes;
return distance;
}

/* 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;
}

BOOL DS_addDeviceRef(INT32 deviceID) {


HWND ownerWindow;
HRESULT res = DS_OK;
LPDIRECTSOUND devPlay;
LPDIRECTSOUNDCAPTURE devCapture;
LPGUID lpGuid = NULL;
if (g_audioDeviceCache[deviceID].dev == NULL) {
/* Create DirectSound */
TRACE1("Creating DirectSound object for device %d\n", deviceID);
lpGuid = &(g_audioDeviceCache[deviceID].guid);
if (isEqualGUID(lpGuid, NULL)) {
lpGuid = NULL;
}
if (g_audioDeviceCache[deviceID].isSource) {
res = DirectSoundCreate(lpGuid, &devPlay, NULL);
g_audioDeviceCache[deviceID].dev = (void*) devPlay;
} else {
res = DirectSoundCaptureCreate(lpGuid, &devCapture, NULL);
g_audioDeviceCache[deviceID].dev = (void*) devCapture;
}
g_audioDeviceCache[deviceID].refCount = 0;
if (FAILED(res)) {
ERROR1("DAUDIO_Open: ERROR: Failed to create DirectSound: %s", TranslateDSError(res));
g_audioDeviceCache[deviceID].dev = NULL;
return FALSE;
}
if (g_audioDeviceCache[deviceID].isSource) {
ownerWindow = GetForegroundWindow();
if (ownerWindow == NULL) {
ownerWindow = GetDesktopWindow();
}
TRACE0("DAUDIO_Open: Setting cooperative level\n");
res = devPlay->SetCooperativeLevel(ownerWindow, DSSCL_NORMAL);
if (FAILED(res)) {
ERROR1("DAUDIO_Open: ERROR: Failed to set cooperative level: %s", TranslateDSError(res));
return FALSE;
}
}
}
g_audioDeviceCache[deviceID].refCount++;
return TRUE;
}

#define DEV_PLAY(devID) ((LPDIRECTSOUND) g_audioDeviceCache[devID].dev)


#define DEV_CAPTURE(devID) ((LPDIRECTSOUNDCAPTURE) g_audioDeviceCache[devID].dev)

void DS_removeDeviceRef(INT32 deviceID) {

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

void createWaveFormat(WAVEFORMATEXTENSIBLE* format,


int sampleRate,
int channels,
int bits,
int significantBits) {
GUID subtypePCM = {STATIC_KSDATAFORMAT_SUBTYPE_PCM};
format->Format.nSamplesPerSec = (DWORD)sampleRate;
format->Format.nChannels = (WORD) channels;
/* do not support useless padding, like 24-bit samples stored in 32-bit containers */
format->Format.wBitsPerSample = (WORD) ((bits + 7) & 0xFFF8);

if (channels <= 2 && bits <= 16) {


format->Format.wFormatTag = WAVE_FORMAT_PCM;
format->Format.cbSize = 0;
} else {
format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format->Format.cbSize = 22;
format->Samples.wValidBitsPerSample = bits;
/* no way to specify speaker locations */
format->dwChannelMask = 0xFFFFFFFF;
format->SubFormat = subtypePCM;
}
format->Format.nBlockAlign = (WORD)((format->Format.wBitsPerSample * format->Format.nChannels) / 8);
format->Format.nAvgBytesPerSec = format->Format.nSamplesPerSec * format->Format.nBlockAlign;
}

/* fill buffer with silence


*/
void DS_clearBuffer(DS_Info* info, BOOL fromWritePos) {
UBYTE* pb1=NULL, *pb2=NULL;
DWORD cb1=0, cb2=0;
DWORD flags = 0;
int start, count;
TRACE1("> DS_clearBuffer for device %d\n", info->deviceID);
if (info->isSource) {
if (fromWritePos) {
DWORD playCursor, writeCursor;
int end;
if (FAILED(info->playBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {
ERROR0(" DS_clearBuffer: ERROR: Failed to get current position.");
TRACE0("< DS_clearbuffer\n");
return;
}
DEBUG_SILENCING2(" DS_clearBuffer: DS playPos=%d myWritePos=%d", (int) playCursor, (int) info-
>writePos);
if (info->writePos >= 0) {
start = info->writePos + info->silencedBytes;
} else {
start = writeCursor + info->silencedBytes;
//flags |= DSBLOCK_FROMWRITECURSOR;
}
while (start >= info->dsBufferSizeInBytes) {
start -= info->dsBufferSizeInBytes;
}

// fix for bug 6251460 (REGRESSION: short sounds do not play)


// for unknown reason with hardware DS buffer playCursor sometimes
// jumps back for little interval (mostly 2-8 bytes) (writeCursor moves forward as usual)
// The issue happens right after start playing and for short sounds only (less then DS buffer,
// when whole sound written into the buffer and remaining space filled by silence)
// the case doesn't produce any audible aftifacts so just catch it to prevent filling
// whole buffer by silence.
if (((int)playCursor <= start && start < (int)writeCursor)
|| (writeCursor < playCursor // buffer bound is between playCursor & writeCursor
&& (start < (int)writeCursor || (int)playCursor <= start))) {
return;
}

count = info->dsBufferSizeInBytes - info->silencedBytes;


// why / 4?
//if (count > info->dsBufferSizeInBytes / 4) {
// count = info->dsBufferSizeInBytes / 4;
//}
end = start + count;
if ((int) playCursor < start) {
playCursor += (DWORD) info->dsBufferSizeInBytes;
}
if (start <= (int) playCursor && end > (int) playCursor) {
/* at maximum, silence until play cursor */
count = (int) playCursor - start;
#ifdef USE_TRACE
if ((int) playCursor >= info->dsBufferSizeInBytes) playCursor -= (DWORD) info-
>dsBufferSizeInBytes;
TRACE3("\n DS_clearBuffer: Start Writing from %d, "
"would overwrite playCursor=%d, so reduce count to %d\n",
start, playCursor, count);
#endif
}
DEBUG_SILENCING2(" clearing buffer from %d, count=%d. ", (int)start, (int) count);
if (count <= 0) {
DEBUG_SILENCING0("\n");
TRACE1("< DS_clearBuffer: no need to clear, silencedBytes=%d\n", info->silencedBytes);
return;
}
} else {
start = 0;
count = info->dsBufferSizeInBytes;
flags |= DSBLOCK_ENTIREBUFFER;
}
if (FAILED(info->playBuffer->Lock(start,
count,
(LPVOID*) &pb1, &cb1,
(LPVOID*) &pb2, &cb2, flags))) {
ERROR0("\n DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");
TRACE0("< DS_clearbuffer\n");
return;
}
} else {
if (FAILED(info->captureBuffer->Lock(0,
info->dsBufferSizeInBytes,
(LPVOID*) &pb1, &cb1,
(LPVOID*) &pb2, &cb2, DSCBLOCK_ENTIREBUFFER))) {
ERROR0(" DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");
TRACE0("< DS_clearbuffer\n");
return;
}
}
if (pb1!=NULL) {
memset(pb1, (info->bitsPerSample == 8)?128:0, cb1);
}
if (pb2!=NULL) {
memset(pb2, (info->bitsPerSample == 8)?128:0, cb2);
}
if (info->isSource) {
info->playBuffer->Unlock( pb1, cb1, pb2, cb2 );
if (!fromWritePos) {
/* doesn't matter where to start writing next time */
info->writePos = -1;
info->silencedBytes = info->dsBufferSizeInBytes;
} else {
info->silencedBytes += (cb1+cb2);
if (info->silencedBytes > info->dsBufferSizeInBytes) {
ERROR1(" DS_clearbuffer: ERROR: silencedBytes=%d exceeds buffer size!\n",
info->silencedBytes);
info->silencedBytes = info->dsBufferSizeInBytes;
}
}
DEBUG_SILENCING2(" silencedBytes=%d, my writePos=%d\n", (int)info->silencedBytes, (int)info->writePos);
} else {
info->captureBuffer->Unlock( pb1, cb1, pb2, cb2 );
}
TRACE0("< DS_clearbuffer\n");
}

/* returns pointer to buffer */


void* DS_createSoundBuffer(DS_Info* info,
float sampleRate,
int sampleSizeInBits,
int channels,
int bufferSizeInBytes) {
DSBUFFERDESC dsbdesc;
DSCBUFFERDESC dscbdesc;
HRESULT res;
WAVEFORMATEXTENSIBLE format;
void* buffer;

TRACE1("Creating secondary buffer for device %d\n", info->deviceID);


createWaveFormat(&format,
(int) sampleRate,
channels,
info->frameSize / channels * 8,
sampleSizeInBits);

/* 2 second secondary buffer */


info->dsBufferSizeInBytes = 2 * ((int) sampleRate) * info->frameSize;

if (bufferSizeInBytes > info->dsBufferSizeInBytes / 2) {


bufferSizeInBytes = info->dsBufferSizeInBytes / 2;
}
bufferSizeInBytes = (bufferSizeInBytes / info->frameSize) * info->frameSize;
info->bufferSizeInBytes = bufferSizeInBytes;

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;
}

void DS_destroySoundBuffer(DS_Info* info) {


if (info->playBuffer != NULL) {
info->playBuffer->Release();
info->playBuffer = NULL;
}
if (info->captureBuffer != NULL) {
info->captureBuffer->Release();
info->captureBuffer = NULL;
}
}

void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,


int encoding, float sampleRate, int sampleSizeInBits,
int frameSize, int channels,
int isSigned, int isBigEndian, int bufferSizeInBytes) {

DS_Info* info;
void* buffer;

TRACE0("> DAUDIO_Open\n");

/* some sanity checks */


if (deviceID >= g_cacheCount) {
ERROR1("DAUDIO_Open: ERROR: cannot open the device with deviceID=%d!\n", deviceID);
return NULL;
}
if ((g_audioDeviceCache[deviceID].isSource && !isSource)
|| (!g_audioDeviceCache[deviceID].isSource && isSource)) {
/* only support Playback or Capture */
ERROR0("DAUDIO_Open: ERROR: Cache is corrupt: cannot open the device in specified isSource mode!\n");
return NULL;
}
if (encoding != DAUDIO_PCM) {
ERROR1("DAUDIO_Open: ERROR: cannot open the device with encoding=%d!\n", encoding);
return NULL;
}
if (sampleSizeInBits > 8 &&
#ifdef _LITTLE_ENDIAN
isBigEndian
#else
!isBigEndian
#endif
) {
ERROR1("DAUDIO_Open: ERROR: wrong endianness: isBigEndian==%d!\n", isBigEndian);
return NULL;
}
if (sampleSizeInBits == 8 && isSigned) {
ERROR0("DAUDIO_Open: ERROR: wrong signed'ness: with 8 bits, data must be unsigned!\n");
return NULL;
}
if (!DS_StartBufferHelper::isInitialized()) {
ERROR0("DAUDIO_Open: ERROR: StartBufferHelper initialization was failed!\n");
return NULL;
}

info = (DS_Info*) malloc(sizeof(DS_Info));


if (!info) {
ERROR0("DAUDIO_Open: ERROR: Out of memory\n");
return NULL;
}
memset(info, 0, sizeof(DS_Info));

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 */);

/* use writepos of device */


if (info->isSource) {
info->writePos = -1;
} else {
info->writePos = 0;
}

TRACE0("< DAUDIO_Open: Opened device successfully.\n");


return (void*) info;
}

int DAUDIO_Start(void* id, int isSource) {


DS_Info* info = (DS_Info*) id;
HRESULT res = DS_OK;
DWORD status;

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;
}

/* only start buffer if already something written to it */


if (info->writePos >= 0) {
res = DS_StartBufferHelper::StartBuffer(info);
if (res == DSERR_BUFFERLOST) {
res = info->playBuffer->Restore();
if (res == DS_OK) {
DS_clearBuffer(info, FALSE /* entire buffer */);
/* write() will trigger actual device start */
}
} else {
/* make sure that we will have silence after
the currently valid audio data */
DS_clearBuffer(info, TRUE /* from write position */);
}
}
}
} else {
if (info->captureBuffer->GetStatus(&status) == DS_OK) {
if (status & DSCBSTATUS_LOOPING) {
ERROR0("DAUDIO_Start: ERROR: Already started!");
return TRUE;
}
}
res = DS_StartBufferHelper::StartBuffer(info);
}
if (FAILED(res)) {
ERROR1("DAUDIO_Start: ERROR: Failed to start: %s", TranslateDSError(res));
return FALSE;
}
info->started = TRUE;
return TRUE;
}

int DAUDIO_Stop(void* id, int isSource) {


DS_Info* info = (DS_Info*) id;

TRACE0("> DAUDIO_Stop\n");

info->started = FALSE;
if (info->isSource) {
info->playBuffer->Stop();
} else {
info->captureBuffer->Stop();
}

TRACE0("< DAUDIO_Stop\n");
return TRUE;
}

void DAUDIO_Close(void* id, int isSource) {


DS_Info* info = (DS_Info*) id;

TRACE0("DAUDIO_Close\n");

if (info != NULL) {
DS_destroySoundBuffer(info);
DS_removeDeviceRef(info->deviceID);
free(info);
}
}

/* Check buffer for underrun


* This method is only meaningful for Output devices (write devices).
*/
void DS_CheckUnderrun(DS_Info* info, DWORD playCursor, DWORD writeCursor) {
TRACE5("DS_CheckUnderrun: playCursor=%d, writeCursor=%d, "
"info->writePos=%d silencedBytes=%d dsBufferSizeInBytes=%d\n",
(int) playCursor, (int) writeCursor, (int) info->writePos,
(int) info->silencedBytes, (int) info->dsBufferSizeInBytes);
if (info->underrun || info->writePos < 0) return;
int writeAhead = DS_getDistance(info, writeCursor, info->writePos);
if (writeAhead > info->bufferSizeInBytes) {
// this may occur after Stop(), when writeCursor decreases (real valid data size > bufferSizeInBytes)
// But the case can occur only when we have more then info->bufferSizeInBytes valid bytes
// (and less then (info->dsBufferSizeInBytes - info->bufferSizeInBytes) silenced bytes)
// If we already have a lot of silencedBytes after valid data (written by
// DAUDIO_StillDraining() or DAUDIO_Service()) then it's underrun
if (info->silencedBytes >= info->dsBufferSizeInBytes - info->bufferSizeInBytes) {
// underrun!
ERROR0("DS_CheckUnderrun: ERROR: underrun detected!\n");
info->underrun = TRUE;
}
}
}

/* For source (playback) line:


* (a) if (fromPlayCursor == FALSE), returns number of bytes available
* for writing: bufferSize - (info->writePos - writeCursor);
* (b) if (fromPlayCursor == TRUE), playCursor is used instead writeCursor
* and returned value can be used for play position calculation (see also
* note about bufferSize)
* For destination (capture) line:
* (c) if (fromPlayCursor == FALSE), returns number of bytes available
* for reading from the buffer: readCursor - info->writePos;
* (d) if (fromPlayCursor == TRUE), captureCursor is used instead readCursor
* and returned value can be used for capture position calculation (see
* note about bufferSize)
* bufferSize parameter are filled by "actual" buffer size:
* if (fromPlayCursor == FALSE), bufferSize = info->bufferSizeInBytes
* otherwise it increase by number of bytes currently processed by DirectSound
* (writeCursor - playCursor) or (captureCursor - readCursor)
*/
int DS_GetAvailable(DS_Info* info,
DWORD* playCursor, DWORD* writeCursor,
int* bufferSize, BOOL fromPlayCursor) {
int available;
int newReadPos;

TRACE2("DS_GetAvailable: fromPlayCursor=%d, deviceID=%d\n", fromPlayCursor, info->deviceID);


if (!info->playBuffer && !info->captureBuffer) {
ERROR0("DS_GetAvailable: ERROR: buffer not yet created");
return 0;
}

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;

TRACE1("DS_available: Returning %d available bytes\n", (int) available);


return available;
}

// returns -1 on error, otherwise bytes written


int DAUDIO_Write(void* id, char* data, int byteSize) {
DS_Info* info = (DS_Info*) id;
int available;
int thisWritePos;
DWORD playCursor, writeCursor;
HRESULT res;
void* buffer1, *buffer2;
DWORD buffer1len, buffer2len;
BOOL needRestart = FALSE;
int bufferLostTrials = 2;
int bufferSize;

TRACE1("> DAUDIO_Write %d bytes\n", byteSize);

while (--bufferLostTrials > 0) {


available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, FALSE /* fromPlayCursor */);
if (byteSize > available) byteSize = available;
if (byteSize == 0) break;
thisWritePos = info->writePos;
if (thisWritePos == -1 || info->underrun) {
// play from current write cursor after flush, etc.
needRestart = TRUE;
thisWritePos = writeCursor;
info->underrun = FALSE;
}
DEBUG_SILENCING2("DAUDIO_Write: writing from %d, count=%d\n", (int) thisWritePos, (int) byteSize);
res = info->playBuffer->Lock(thisWritePos, byteSize,
(LPVOID *) &buffer1, &buffer1len,
(LPVOID *) &buffer2, &buffer2len,
0);
if (res != DS_OK) {
/* some DS failure */
if (res == DSERR_BUFFERLOST) {
ERROR0("DAUDIO_write: ERROR: Restoring lost Buffer.");
if (info->playBuffer->Restore() == DS_OK) {
DS_clearBuffer(info, FALSE /* entire buffer */);
info->writePos = -1;
/* try again */
continue;
}
}
/* can't recover from error */
byteSize = 0;
break;
}
/* buffer could be locked successfully */
/* first fill first buffer */
if (buffer1) {
memcpy(buffer1, data, buffer1len);
data = (char*) (((UINT_PTR) data) + buffer1len);
} else buffer1len = 0;
if (buffer2) {
memcpy(buffer2, data, buffer2len);
} else buffer2len = 0;
byteSize = buffer1len + buffer2len;

/* update next write pos */


thisWritePos += byteSize;
while (thisWritePos >= info->dsBufferSizeInBytes) {
thisWritePos -= info->dsBufferSizeInBytes;
}
/* commit data to directsound */
info->playBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);

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 */

/* start the device, if necessary */


if (info->started && needRestart && (info->writePos >= 0)) {
DS_StartBufferHelper::StartBuffer(info);
}

TRACE1("< DAUDIO_Write: returning %d bytes.\n", byteSize);


return byteSize;
}

// 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;

TRACE1("> DAUDIO_Read %d bytes\n", byteSize);

available = DS_GetAvailable(info, &captureCursor, &readCursor, &bufferSize, FALSE /* fromCaptureCursor? */);


if (byteSize > available) byteSize = available;
if (byteSize > 0) {
thisReadPos = info->writePos;
if (thisReadPos == -1) {
/* from beginning */
thisReadPos = 0;
}
res = info->captureBuffer->Lock(thisReadPos, byteSize,
(LPVOID *) &buffer1, &buffer1len,
(LPVOID *) &buffer2, &buffer2len,
0);
if (res != DS_OK) {
/* can't recover from error */
byteSize = 0;
} else {
/* buffer could be locked successfully */
/* first fill first buffer */
if (buffer1) {
memcpy(data, buffer1, buffer1len);
data = (char*) (((UINT_PTR) data) + buffer1len);
} else buffer1len = 0;
if (buffer2) {
memcpy(data, buffer2, buffer2len);
} else buffer2len = 0;
byteSize = buffer1len + buffer2len;

/* update next read pos */


thisReadPos = DS_addPos(info, thisReadPos, byteSize);
/* commit data to directsound */
info->captureBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);

/* 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;
}
}

TRACE1("< DAUDIO_Read: returning %d bytes.\n", byteSize);


return byteSize;
}

int DAUDIO_GetBufferSize(void* id, int isSource) {


DS_Info* info = (DS_Info*) id;
return info->bufferSizeInBytes;
}

int DAUDIO_StillDraining(void* id, int isSource) {


DS_Info* info = (DS_Info*) id;
BOOL draining = FALSE;
int available, bufferSize;
DWORD playCursor, writeCursor;

DS_clearBuffer(info, TRUE /* from write position */);


available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, TRUE /* fromPlayCursor */);
draining = (available < bufferSize);

TRACE3("DAUDIO_StillDraining: available=%d silencedBytes=%d Still draining: %s\n",


available, info->silencedBytes, draining?"TRUE":"FALSE");
return draining;
}

int DAUDIO_Flush(void* id, int isSource) {


DS_Info* info = (DS_Info*) id;

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;
}

int DAUDIO_GetAvailable(void* id, int isSource) {


DS_Info* info = (DS_Info*) id;
DWORD playCursor, writeCursor;
int ret, bufferSize;

ret = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ FALSE);

TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);


return ret;
}

INT64 estimatePositionFromAvail(DS_Info* info, INT64 javaBytePos, int bufferSize, int availInBytes) {


// estimate the current position with the buffer size and
// the available bytes to read or write in the buffer.
// not an elegant solution - bytePos will stop on xruns,
// and in race conditions it may jump backwards
// Advantage is that it is indeed based on the samples that go through
// the system (rather than time-based methods)
if (info->isSource) {
// javaBytePos is the position that is reached when the current
// buffer is played completely
return (INT64) (javaBytePos - bufferSize + availInBytes);
} else {
// javaBytePos is the position that was when the current buffer was empty
return (INT64) (javaBytePos + availInBytes);
}
}

INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {


DS_Info* info = (DS_Info*) id;
int available, bufferSize;
DWORD playCursor, writeCursor;
INT64 result = javaBytePos;

available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ TRUE);


result = estimatePositionFromAvail(info, javaBytePos, bufferSize, available);
return result;
}

void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {


/* save to ignore, since GetBytePosition
* takes the javaBytePos param into account
*/
}

int DAUDIO_RequiresServicing(void* id, int isSource) {


// need servicing on for SourceDataLines
return isSource?TRUE:FALSE;
}

void DAUDIO_Service(void* id, int isSource) {


DS_Info* info = (DS_Info*) id;
if (isSource) {
if (info->silencedBytes < info->dsBufferSizeInBytes) {
// clear buffer
TRACE0("DAUDIO_Service\n");
DS_clearBuffer(info, TRUE /* from write position */);
}
if (info->writePos >= 0
&& info->started
&& !info->underrun
&& info->silencedBytes >= info->dsBufferSizeInBytes) {
// if we're currently playing, and the entire buffer is silenced...
// then we are underrunning!
info->underrun = TRUE;
ERROR0("DAUDIO_Service: ERROR: DirectSound: underrun detected!\n");
}
}
}

#endif // USE_DAUDIO

You might also like