Skip to content

Commit

Permalink
Implemented ALC_EXT_DISCONNECT support.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Apr 8, 2018
1 parent d29e638 commit 51d79a0
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 9 deletions.
89 changes: 85 additions & 4 deletions mojoal.c
Expand Up @@ -49,6 +49,11 @@
#define AL_FORMAT_STEREO_FLOAT32 0x10011
#endif

/* ALC_EXT_DISCONNECTED support... */
#ifndef ALC_CONNECTED
#define ALC_CONNECTED 0x313
#endif


/*
The locking strategy for this OpenAL implementation is complicated.
Expand Down Expand Up @@ -301,6 +306,7 @@ struct ALCdevice_struct
char *name;
ALCenum error;
ALCboolean iscapture;
ALCboolean connected;
SDL_AudioDeviceID sdldevice;

ALint channels;
Expand Down Expand Up @@ -352,7 +358,8 @@ static ALCenum null_device_error = ALC_NO_ERROR;
/* we don't have any device-specific extensions. */
#define ALC_EXTENSION_ITEMS \
ALC_EXTENSION_ITEM(ALC_ENUMERATION_EXT) \
ALC_EXTENSION_ITEM(ALC_EXT_CAPTURE)
ALC_EXTENSION_ITEM(ALC_EXT_CAPTURE) \
ALC_EXTENSION_ITEM(ALC_EXT_DISCONNECT)

#define AL_EXTENSION_ITEMS \
AL_EXTENSION_ITEM(AL_EXT_FLOAT32)
Expand Down Expand Up @@ -396,6 +403,7 @@ ALCdevice *alcOpenDevice(const ALCchar *devicename)
/* we don't open an SDL audio device until the first context is
created, so we can attempt to match audio formats. */

dev->connected = ALC_TRUE;
dev->iscapture = ALC_FALSE;
return dev;
}
Expand Down Expand Up @@ -677,6 +685,42 @@ static void mix_context(ALCcontext *ctx, float *stream, int len)
}


/* Disconnected devices move all PLAYING sources to STOPPED, making their buffer queues processed. */
static void mix_disconnected_context(ALCcontext *ctx)
{
ALsizei i;
FIXME("keep a small array of playing sources so we don't have to walk the whole array");
for (i = 0; i < OPENAL_MAX_SOURCES; i++) {
ALsource *src = &ctx->sources[i];
if (SDL_AtomicGet(&src->allocated) != 1) {
continue; /* not in use */
}

obtain_newly_queued_buffers(&src->buffer_queue);

if (src->state == AL_PLAYING) {
src->state = AL_STOPPED;
if (src->type == AL_STREAMING) { /* mark buffers processed. */
while (src->buffer_queue.head) {
void *ptr;
BufferQueueItem *item = src->buffer_queue.head;
src->buffer_queue.head = item->next;
SDL_AtomicAdd(&src->buffer_queue.num_items, -1);

/* Move it to the processed queue for alSourceUnqueueBuffers() to pick up. */
do {
ptr = SDL_AtomicGetPtr(&src->buffer_queue_processed.just_queued);
SDL_AtomicSetPtr(&item->next, ptr);
} while (!SDL_AtomicCASPtr(&src->buffer_queue_processed.just_queued, ptr, item));

SDL_AtomicAdd(&src->buffer_queue_processed.num_items, 1);
}
src->buffer_queue.tail = NULL;
}
}
}
}

/* We process all unsuspended ALC contexts during this call, mixing their
output to (stream). SDL then plays this mixed audio to the hardware. */
static void SDLCALL playback_device_callback(void *userdata, Uint8 *stream, int len)
Expand All @@ -686,9 +730,20 @@ static void SDLCALL playback_device_callback(void *userdata, Uint8 *stream, int

SDL_memset(stream, '\0', len);

if (device->connected) {
if (SDL_GetAudioDeviceStatus(device->sdldevice) == SDL_AUDIO_STOPPED) {
device->connected = ALC_FALSE;
}
}


for (ctx = device->playback.contexts; ctx != NULL; ctx = ctx->next) {
if (SDL_AtomicGet(&ctx->processing)) {
mix_context(ctx, (float *) stream, len);
if (device->connected) {
mix_context(ctx, (float *) stream, len);
} else {
mix_disconnected_context(ctx);
}
}
}
}
Expand All @@ -707,6 +762,11 @@ ALCcontext* alcCreateContext(ALCdevice *device, const ALCint* attrlist)
return NULL;
}

if (!device->connected) {
set_alc_error(device, ALC_INVALID_DEVICE);
return NULL;
}

if (attrlist != NULL) {
ALCint attr;
while ((attr = attrlist[attrcount++]) != 0) {
Expand Down Expand Up @@ -933,6 +993,7 @@ ALCenum alcGetEnumValue(ALCdevice *device, const ALCchar *enumname)
ENUM_TEST(ALC_CAPTURE_SAMPLES);
ENUM_TEST(ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
ENUM_TEST(ALC_ALL_DEVICES_SPECIFIER);
ENUM_TEST(ALC_CONNECTED);
#undef ENUM_TEST

set_alc_error(device, ALC_INVALID_VALUE);
Expand Down Expand Up @@ -1055,6 +1116,10 @@ void alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *val
SDL_UnlockAudioDevice(device->sdldevice);
return;

case ALC_CONNECTED:
*values = (ALCint) device->connected ? ALC_TRUE : ALC_FALSE;
return;

case ALC_ATTRIBUTES_SIZE:
case ALC_ALL_ATTRIBUTES:
if (!device || device->iscapture) {
Expand Down Expand Up @@ -1108,7 +1173,16 @@ static void SDLCALL capture_device_callback(void *userdata, Uint8 *stream, int l
{
ALCdevice *device = (ALCdevice *) userdata;
SDL_assert(device->iscapture);
ring_buffer_put(&device->capture.ring, stream, (ALCsizei) len);

if (device->connected) {
if (SDL_GetAudioDeviceStatus(device->sdldevice) == SDL_AUDIO_STOPPED) {
device->connected = ALC_FALSE;
}
}

if (device->connected) {
ring_buffer_put(&device->capture.ring, stream, (ALCsizei) len);
}
}

ALCdevice* alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize)
Expand Down Expand Up @@ -1137,6 +1211,7 @@ ALCdevice* alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, AL
desired.callback = capture_device_callback;
desired.userdata = device;

device->connected = ALC_TRUE;
device->iscapture = ALC_TRUE;

if (!devicename) {
Expand Down Expand Up @@ -2406,7 +2481,12 @@ static void source_play(ALCcontext *ctx, const ALuint name)
} else if (src->state != AL_PAUSED) {
src->offset = 0;
}
src->state = AL_PLAYING;
if (ctx->device->connected) {
src->state = AL_PLAYING;
} else {
FIXME("buffer queue needs to promote to processed");
src->state = AL_STOPPED; /* disconnected devices promote directly to STOPPED */
}
}
}

Expand All @@ -2415,6 +2495,7 @@ static void source_stop(ALCcontext *ctx, const ALuint name)
ALsource *src = get_source(ctx, name);
if (src) {
FIXME("this needs a lock");
FIXME("buffer queue needs to promote to processed");
if (src->state != AL_INITIAL) {
src->state = AL_STOPPED;
}
Expand Down
29 changes: 26 additions & 3 deletions tests/testcapture.c
Expand Up @@ -41,6 +41,8 @@ int main(int argc, char **argv)
#define freq 44100
#define total_samples freq * 5
static cfmt buf[total_samples];
ALenum alc_connected = 0;
ALCint connected = ALC_TRUE;
ALCdevice *device;
ALCdevice *capture;
ALCcontext *context;
Expand Down Expand Up @@ -72,6 +74,10 @@ int main(int argc, char **argv)
return 4;
}

if (alcIsExtensionPresent(capture, "ALC_EXT_DISCONNECT")) {
alc_connected = alcGetEnumValue(capture, "ALC_CONNECTED");
}

printf("recording...\n");
alcCaptureStart(capture);
check_openal_alc_error(capture, "alcCaptureStart");
Expand All @@ -80,10 +86,17 @@ int main(int argc, char **argv)
SDL_Delay(100);
alcGetIntegerv(capture, ALC_CAPTURE_SAMPLES, 1, &samples);
check_openal_alc_error(capture, "alcGetIntegerv");
} while (samples < total_samples);

if (alc_connected != 0) {
alcGetIntegerv(capture, alc_connected, 1, &connected);
check_openal_alc_error(capture, "alcGetIntegerv");
}
} while (connected && (samples < total_samples));

if (!connected) {
printf("(Uhoh, recording device was disconnected! Carrying on...)\n");
}

alcCaptureSamples(capture, buf, total_samples);
alcCaptureSamples(capture, buf, samples);
check_openal_alc_error(capture, "alcCaptureSamples");
alcCaptureStop(capture);
check_openal_alc_error(capture, "alcCaptureStop");
Expand All @@ -110,6 +123,16 @@ int main(int argc, char **argv)
check_openal_error("alGetSourceiv");
} while (state == AL_PLAYING);

if (alcIsExtensionPresent(device, "ALC_EXT_DISCONNECT")) {
alc_connected = alcGetEnumValue(device, "ALC_CONNECTED");
check_openal_alc_error(device, "alcGetEnumValue");
alcGetIntegerv(device, alc_connected, 1, &connected);
check_openal_alc_error(device, "alcGetIntegerv");
if (!connected) {
printf("(Uhoh, playback device was disconnected!)\n");
}
}

printf("Cleaning up...\n");

alDeleteSources(1, &sid);
Expand Down
18 changes: 16 additions & 2 deletions tests/testqueueing.c
Expand Up @@ -64,7 +64,7 @@ static int queueBuffer(const ALuint sid, const ALuint bid, const ALenum alfmt, c
return 1;
}

static void queuewav(const char *fname)
static void queuewav(ALCdevice *device, const char *fname)
{
SDL_AudioSpec spec;
ALenum alfmt = AL_NONE;
Expand All @@ -75,6 +75,7 @@ static void queuewav(const char *fname)
ALuint sid = 0;
ALuint buffers[32];
ALsizei i;
ALenum alc_connected = 0;

if (!SDL_LoadWAV(fname, &spec, &buf, &buflen)) {
printf("Loading '%s' failed! %s\n", fname, SDL_GetError());
Expand All @@ -87,6 +88,10 @@ static void queuewav(const char *fname)

check_openal_error("startup");

if (alcIsExtensionPresent(device, "ALC_EXT_DISCONNECT")) {
alc_connected = alcGetEnumValue(device, "ALC_CONNECTED");
}

printf("Now queueing '%s'...\n", fname);

alGenSources(1, &sid);
Expand Down Expand Up @@ -148,6 +153,15 @@ static void queuewav(const char *fname)
processed--;
}

if (alc_connected != 0) {
ALCint connected = 0;
alcGetIntegerv(device, alc_connected, 1, &connected);
if (!connected) {
printf("Device is apparently disconnected!\n");
failed = 1;
}
}

if (!failed) {
ALint state = 0;
alGetSourceiv(sid, AL_SOURCE_STATE, &state);
Expand Down Expand Up @@ -214,7 +228,7 @@ int main(int argc, char **argv)
alcMakeContextCurrent(context);

for (i = 1; i < argc; i++) {
queuewav(argv[i]);
queuewav(device, argv[i]);
}

alcMakeContextCurrent(NULL);
Expand Down

0 comments on commit 51d79a0

Please sign in to comment.