Skip to content

Commit

Permalink
More ALC_EXT_disconnect work:
Browse files Browse the repository at this point in the history
- Catch CoreAudio notification of device list changes instead of querying
  CoreAudio in every ALC_CONNECTED query.
- Flush playing sources on disconnect, per latest extension specs.
- Other cleanups and major design changes.  :)
  • Loading branch information
icculus committed Sep 7, 2005
1 parent a416fb2 commit 5355bf3
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 36 deletions.
139 changes: 104 additions & 35 deletions osx/alContext.c
Expand Up @@ -47,6 +47,10 @@ static ALvoid __alcStopCaptureIO(ALdevice *device);
static ALubyte *__alcDetermineDeviceNameList(ALboolean isOutput);
#endif

static ALvoid __alcMixContext(ALcontext *ctx, UInt8 *_dst,
UInt32 frames, UInt32 framesize,
ALboolean justStopPlaying);


// minor pthread wrapper...
#define __alCreateLock(lockptr) pthread_mutex_init(lockptr, NULL)
Expand All @@ -55,6 +59,10 @@ static ALubyte *__alcDetermineDeviceNameList(ALboolean isOutput);
#define __alUngrabLock(lockptr) pthread_mutex_unlock(lockptr)


static ALuint __alcDevCount = 0;
static ALdevice *__alcDevices[16];


static inline ALvoid __alGrabContext(ALcontext *ctx)
{
__alGrabLock(&ctx->contextLock);
Expand Down Expand Up @@ -114,28 +122,6 @@ ALvoid __alSetError( ALenum err )
} // __alSetError


static ALboolean __alcDeviceIsConnected(ALdevice *dev)
{
UInt32 isAlive = 1;
UInt32 size = sizeof (isAlive);
OSStatus error = 0;

if (!dev->isConnected) // once disconnected, we never "reconnect".
return AL_FALSE;

error = AudioDeviceGetProperty(dev->device, 0, dev->isInputDevice,
kAudioDevicePropertyDeviceIsAlive,
&size, &isAlive);

if (error == kAudioHardwareBadDeviceError)
return AL_FALSE; // device was unplugged.

if ((error == kAudioHardwareNoError) && (!isAlive))
return AL_FALSE; // device died in some other way.

return AL_TRUE;
} // __alcDeviceIsConnected

#if HAVE_PRAGMA_EXPORT
#pragma export on
#endif
Expand Down Expand Up @@ -243,7 +229,7 @@ ALCAPI ALvoid ALCAPIENTRY alcGetIntegerv(ALCdevice *device,ALenum param,ALsizei
if (size < sizeof (ALint))
__alcSetError((ALdevice *) dev, ALC_INVALID_VALUE);
else
*data = __alcDeviceIsConnected(dev);
*data = dev->isConnected;
break;
#endif

Expand Down Expand Up @@ -776,6 +762,67 @@ ALboolean __alDetectVectorUnit(ALvoid)
} // __alDetectVectorUnit


#if SUPPORTS_ALC_EXT_DISCONNECT
// !!! FIXME: this might be dumb. Checking the updated list of device IDs
// !!! FIXME: might be smarter.
static ALboolean __alcDeviceIsConnected(ALdevice *dev)
{
UInt32 isAlive = 1;
UInt32 size = sizeof (isAlive);
OSStatus error = 0;

error = AudioDeviceGetProperty(dev->device, 0, dev->isInputDevice,
kAudioDevicePropertyDeviceIsAlive,
&size, &isAlive);

if (error == kAudioHardwareBadDeviceError)
return AL_FALSE; // device was unplugged.

if ((error == kAudioHardwareNoError) && (!isAlive))
return AL_FALSE; // device died in some other way.

return AL_TRUE;
} // __alcDeviceIsConnected


static void __alcHandleDisconnect(ALdevice *dev)
{
ALcontext **ctxs;
__alGrabDevice(dev);

dev->isConnected = AL_FALSE;

if (!dev->isInputDevice)
{
// flush any playing sources.
for (ctxs = dev->createdContexts; *ctxs != NULL; ctxs++)
__alcMixContext(*ctxs, NULL, 0, 0, AL_TRUE);
} // if

__alUngrabDevice(dev);
} // __alcHandleDisconnect


static OSStatus __alcDeviceDisconnectNotify(AudioHardwarePropertyID inPropertyID,
void *inClientData)
{
if (inPropertyID == kAudioHardwarePropertyDevices)
{
int i;
// !!! FIXME: Grab some sort of universal lock here...
for (i = 0; i < __alcDevCount; i++)
{
ALdevice *dev = __alcDevices[i];
if ((dev->isConnected) && (!__alcDeviceIsConnected(dev)))
__alcHandleDisconnect(dev);
} // for
} // if

return(0); // docs say always return zero.
} // __alcDeviceDisconnectNotify
#endif


static ALboolean __alcDoFirstInit(ALvoid)
{
static ALboolean __alcAlreadyDidFirstInit = AL_FALSE;
Expand All @@ -791,6 +838,11 @@ static ALboolean __alcDoFirstInit(ALvoid)

__alcAlreadyDidFirstInit = AL_TRUE;
__alCalculateExtensions(AL_TRUE);

#if SUPPORTS_ALC_EXT_DISCONNECT
AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices,
__alcDeviceDisconnectNotify, NULL);
#endif
} // if

return(AL_TRUE);
Expand Down Expand Up @@ -1040,7 +1092,8 @@ static ALboolean __alcDetermineDeviceID(const ALubyte *deviceName,


static ALvoid __alcMixContext(ALcontext *ctx, UInt8 *_dst,
UInt32 frames, UInt32 framesize)
UInt32 frames, UInt32 framesize,
ALboolean justStopPlaying)
{
register ALsource *src;
register ALsource **playingSources;
Expand All @@ -1065,7 +1118,10 @@ static ALvoid __alcMixContext(ALcontext *ctx, UInt8 *_dst,
if (!src->inUse) // someone deleted a playing source!
src->state = AL_STOPPED;

if (src->state == AL_PLAYING)
else if ((src->state == AL_PLAYING) && (justStopPlaying))
src->state = AL_STOPPED; // just flush the buffer queue.

else if (src->state == AL_PLAYING)
{
register int stallcount = 0;
while (srcframes)
Expand Down Expand Up @@ -1183,9 +1239,6 @@ static OSStatus __alcCaptureDevice(AudioDeviceID inDevice, const AudioTimeStamp
assert(dev->speakers == 0);
assert(dev->capture.started);

if (!dev->isConnected) // device was unplugged? Just do nothing.
return kAudioHardwareNoError;

if (!inInputData)
return kAudioHardwareNoError;

Expand Down Expand Up @@ -1301,12 +1354,6 @@ static OSStatus __alcMixDevice(AudioDeviceID inDevice, const AudioTimeStamp* i
UInt32 frames = outOutputData->mBuffers[0].mDataByteSize;
UInt32 framesize;

if (!dev->isConnected) // device was unplugged? Just write silence.
{
memset(outDataPtr, '\0', outOutputData->mBuffers[0].mDataByteSize);
return kAudioHardwareNoError;
} // if

__alGrabDevice(dev); // potentially long lock...

framesize = (sizeof (Float32) * dev->streamFormat.mChannelsPerFrame);
Expand All @@ -1321,7 +1368,7 @@ static OSStatus __alcMixDevice(AudioDeviceID inDevice, const AudioTimeStamp* i
// swallow the cost of a mutex lock and function call to find out
// we don't really have to mix this context.
if ((!ctx->suspended) && (ctx->playingSources[0] != NULL))
__alcMixContext(ctx, outDataPtr, frames, framesize);
__alcMixContext(ctx, outDataPtr, frames, framesize, AL_FALSE);
} // for

__alUngrabDevice(dev);
Expand All @@ -1343,6 +1390,9 @@ static ALCdevice* __alcOpenDeviceInternal(const ALubyte *deviceName,
ALdevice *retval = NULL;
AudioStreamBasicDescription *streamFormats;

if (__alcDevCount >= AL_MAXDEVICES)
return(NULL);

#if SUPPORTS_ALC_EXT_CAPTURE
if (isInput)
ioproc = __alcCaptureDevice;
Expand Down Expand Up @@ -1408,7 +1458,9 @@ static ALCdevice* __alcOpenDeviceInternal(const ALubyte *deviceName,
if (retval == NULL)
return(NULL);

#if SUPPORTS_ALC_EXT_DISCONNECT
retval->isConnected = AL_TRUE;
#endif

__alCreateLock(&retval->deviceLock);

Expand All @@ -1434,6 +1486,9 @@ static ALCdevice* __alcOpenDeviceInternal(const ALubyte *deviceName,
return(NULL);
} // if

// !!! FIXME: Grab some sort of universal lock here...
__alcDevices[__alcDevCount++] = retval;

if (isOutput)
AudioDeviceStart(device, ioproc);

Expand Down Expand Up @@ -1462,6 +1517,7 @@ ALCAPI ALvoid ALCAPIENTRY alcCloseDevice( ALCdevice *device )
__alcSetError(dev, ALC_INVALID);
else
{
int i;
AudioDeviceIOProc ioproc = __alcMixDevice;
#if SUPPORTS_ALC_EXT_CAPTURE
if (dev->isInputDevice)
Expand All @@ -1475,6 +1531,19 @@ ALCAPI ALvoid ALCAPIENTRY alcCloseDevice( ALCdevice *device )
// !!! FIXME: start a new run, but it will not be running at this point.

__alDestroyLock(&dev->deviceLock);

// !!! FIXME: Grab some sort of universal lock here...
for (i = 0; i < __alcDevCount; i++)
{
if (__alcDevices[i] == dev)
{
for (__alcDevCount--; i < __alcDevCount; i++)
__alcDevices[i] = __alcDevices[i+1];
__alcDevices[i] = NULL;
break;
} // if
} // for

free(dev);
} // else
} // alcCloseDevice
Expand Down
7 changes: 6 additions & 1 deletion osx/alInternal.h
Expand Up @@ -70,6 +70,7 @@ typedef pthread_mutex_t ALlock;
#define AL_MAXBUFFERQUEUE 128
#define AL_MAXSOURCES 1024 * 5
#define AL_MAXCONTEXTS 4
#define AL_MAXDEVICES 16
#define AL_MAXSPEAKERS 8

// zero if altivec-aligned.
Expand Down Expand Up @@ -363,8 +364,9 @@ typedef struct ALdevice_struct
AudioStreamBasicDescription streamFormat;
#endif

#if SUPPORTS_ALC_EXT_CAPTURE
ALboolean isInputDevice;

#if SUPPORTS_ALC_EXT_CAPTURE
struct // put this in a struct for the sake of the namespace...
{
ALboolean started;
Expand All @@ -377,7 +379,10 @@ typedef struct ALdevice_struct
} capture;
#endif

#if SUPPORTS_ALC_EXT_DISCONNECT
ALboolean isConnected;
#endif

ALenum errorCode;
ALlock deviceLock;
SpeakerConfigType speakerConfig;
Expand Down
5 changes: 5 additions & 0 deletions osx/alSource.c
Expand Up @@ -680,6 +680,11 @@ static inline ALvoid __alSourcePlay_locked(ALcontext *ctx, ALsource *src)
{
if (src != NULL)
{
// !!! FIXME: if ALC_EXT_disconnect is supported, and the device
// !!! FIXME: was disconnected, we need to just push all queued
// !!! FIXME: buffers to PROCESSED and push this source to STOPPED.
// !!! FIXME: (assuming the spec settles on that....)

if (src->state != AL_PAUSED)
{
ALsource **srcs;
Expand Down

0 comments on commit 5355bf3

Please sign in to comment.