aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/backends
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2019-04-07 23:39:04 +0200
committerSven Gothel <[email protected]>2019-04-07 23:39:04 +0200
commit73233ce69919fc19c53ce8663c5b8cc05227f07e (patch)
treef2b6ccc1a14d7c387f33398a44ea4511d7ecb212 /Alc/backends
parent8efa4c7ba5ee8eb399d31a9884e45f743d4625ad (diff)
parent99a55c445211fea77af6ab61cbc6a6ec4fbdc9b9 (diff)
Merge branch 'v1.19' of git://repo.or.cz/openal-soft into v1.19v1.19
Diffstat (limited to 'Alc/backends')
-rw-r--r--Alc/backends/alsa.c306
-rw-r--r--Alc/backends/base.c187
-rw-r--r--Alc/backends/base.h62
-rw-r--r--Alc/backends/coreaudio.c586
-rw-r--r--Alc/backends/dsound.c372
-rw-r--r--Alc/backends/jack.c171
-rw-r--r--Alc/backends/loopback.c13
-rw-r--r--Alc/backends/null.c31
-rw-r--r--Alc/backends/opensl.c985
-rw-r--r--Alc/backends/oss.c541
-rw-r--r--Alc/backends/portaudio.c62
-rw-r--r--Alc/backends/pulseaudio.c414
-rw-r--r--Alc/backends/qsa.c415
-rw-r--r--Alc/backends/sdl2.c288
-rw-r--r--Alc/backends/sndio.c476
-rw-r--r--Alc/backends/solaris.c112
-rw-r--r--Alc/backends/wasapi.c (renamed from Alc/backends/mmdevapi.c)909
-rw-r--r--Alc/backends/wave.c123
-rw-r--r--Alc/backends/winmm.c199
19 files changed, 4147 insertions, 2105 deletions
diff --git a/Alc/backends/alsa.c b/Alc/backends/alsa.c
index 9a443c09..a967fff0 100644
--- a/Alc/backends/alsa.c
+++ b/Alc/backends/alsa.c
@@ -26,6 +26,8 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
@@ -199,15 +201,21 @@ static ALCboolean alsa_load(void)
#ifdef HAVE_DYNLOAD
if(!alsa_handle)
{
+ al_string missing_funcs = AL_STRING_INIT_STATIC();
+
alsa_handle = LoadLib("libasound.so.2");
if(!alsa_handle)
+ {
+ WARN("Failed to load %s\n", "libasound.so.2");
return ALC_FALSE;
+ }
error = ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(alsa_handle, #f); \
if(p##f == NULL) { \
error = ALC_TRUE; \
+ alstr_append_cstr(&missing_funcs, "\n" #f); \
} \
} while(0)
ALSA_FUNCS(LOAD_FUNC);
@@ -215,10 +223,11 @@ static ALCboolean alsa_load(void)
if(error)
{
+ WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(alsa_handle);
alsa_handle = NULL;
- return ALC_FALSE;
}
+ alstr_reset(&missing_funcs);
}
#endif
@@ -237,16 +246,13 @@ static vector_DevMap CaptureDevices;
static void clear_devlist(vector_DevMap *devlist)
{
- DevMap *iter, *end;
-
- iter = VECTOR_ITER_BEGIN(*devlist);
- end = VECTOR_ITER_END(*devlist);
- for(;iter != end;iter++)
- {
- AL_STRING_DEINIT(iter->name);
- AL_STRING_DEINIT(iter->device_name);
- }
- VECTOR_RESIZE(*devlist, 0);
+#define FREE_DEV(i) do { \
+ AL_STRING_DEINIT((i)->name); \
+ AL_STRING_DEINIT((i)->device_name); \
+} while(0)
+ VECTOR_FOR_EACH(DevMap, *devlist, FREE_DEV);
+ VECTOR_RESIZE(*devlist, 0, 0);
+#undef FREE_DEV
}
@@ -272,11 +278,45 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
- al_string_copy_cstr(&entry.name, alsaDevice);
- al_string_copy_cstr(&entry.device_name, GetConfigValue(NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
- "device" : "capture", "default"));
+ alstr_copy_cstr(&entry.name, alsaDevice);
+ alstr_copy_cstr(&entry.device_name, GetConfigValue(
+ NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture", "default"
+ ));
VECTOR_PUSH_BACK(*DeviceList, entry);
+ if(stream == SND_PCM_STREAM_PLAYBACK)
+ {
+ const char *customdevs, *sep, *next;
+ next = GetConfigValue(NULL, "alsa", "custom-devices", "");
+ while((customdevs=next) != NULL && customdevs[0])
+ {
+ next = strchr(customdevs, ';');
+ sep = strchr(customdevs, '=');
+ if(!sep)
+ {
+ al_string spec = AL_STRING_INIT_STATIC();
+ if(next)
+ alstr_copy_range(&spec, customdevs, next++);
+ else
+ alstr_copy_cstr(&spec, customdevs);
+ ERR("Invalid ALSA device specification \"%s\"\n", alstr_get_cstr(spec));
+ alstr_reset(&spec);
+ continue;
+ }
+
+ AL_STRING_INIT(entry.name);
+ AL_STRING_INIT(entry.device_name);
+ alstr_copy_range(&entry.name, customdevs, sep++);
+ if(next)
+ alstr_copy_range(&entry.device_name, sep, next++);
+ else
+ alstr_copy_cstr(&entry.device_name, sep);
+ TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name),
+ alstr_get_cstr(entry.device_name));
+ VECTOR_PUSH_BACK(*DeviceList, entry);
+ }
+ }
+
card = -1;
if((err=snd_card_next(&card)) < 0)
ERR("Failed to find a card: %s\n", snd_strerror(err));
@@ -321,7 +361,8 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
- if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
+ if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0)
+ {
if(err != -ENOENT)
ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
continue;
@@ -333,15 +374,15 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
ConfigValueStr(NULL, "alsa", name, &device_prefix);
snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
- cardname, devname, cardid, dev);
+ cardname, devname, cardid, dev);
snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d",
- device_prefix, cardid, dev);
+ device_prefix, cardid, dev);
TRACE("Got device \"%s\", \"%s\"\n", name, device);
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
- al_string_copy_cstr(&entry.name, name);
- al_string_copy_cstr(&entry.device_name, device);
+ alstr_copy_cstr(&entry.name, name);
+ alstr_copy_cstr(&entry.device_name, device);
VECTOR_PUSH_BACK(*DeviceList, entry);
}
snd_ctl_close(handle);
@@ -397,7 +438,7 @@ typedef struct ALCplaybackAlsa {
ALvoid *buffer;
ALsizei size;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackAlsa;
@@ -405,15 +446,14 @@ static int ALCplaybackAlsa_mixerProc(void *ptr);
static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr);
static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct)
+static void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self);
static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
-static void ALCplaybackAlsa_close(ALCplaybackAlsa *self);
static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self);
+static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
@@ -425,6 +465,19 @@ static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
+
+ self->pcmHandle = NULL;
+ self->buffer = NULL;
+
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
+
+void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self)
+{
+ if(self->pcmHandle)
+ snd_pcm_close(self->pcmHandle);
+ self->pcmHandle = NULL;
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -444,14 +497,14 @@ static int ALCplaybackAlsa_mixerProc(void *ptr)
update_size = device->UpdateSize;
num_updates = device->NumUpdates;
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
int state = verify_state(self->pcmHandle);
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
ALCplaybackAlsa_lock(self);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state));
ALCplaybackAlsa_unlock(self);
break;
}
@@ -534,14 +587,14 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
update_size = device->UpdateSize;
num_updates = device->NumUpdates;
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
int state = verify_state(self->pcmHandle);
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
ALCplaybackAlsa_lock(self);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state));
ALCplaybackAlsa_unlock(self);
break;
}
@@ -588,7 +641,9 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
{
case -EAGAIN:
continue;
+#if ESTRPIPE != EPIPE
case -ESTRPIPE:
+#endif
case -EPIPE:
case -EINTR:
ret = snd_pcm_recover(self->pcmHandle, ret, 1);
@@ -630,12 +685,12 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
if(VECTOR_SIZE(PlaybackDevices) == 0)
probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+ if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
- driver = al_string_get_cstr(iter->device_name);
+ driver = alstr_get_cstr(iter->device_name);
}
else
{
@@ -654,16 +709,11 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
snd_config_update_free_global();
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCplaybackAlsa_close(ALCplaybackAlsa *self)
-{
- snd_pcm_close(self->pcmHandle);
-}
-
static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -677,6 +727,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
unsigned int rate;
const char *funcerr;
int allowmmap;
+ int dir;
int err;
switch(device->FmtType)
@@ -704,7 +755,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
break;
}
- allowmmap = GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "mmap", 1);
+ allowmmap = GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "mmap", 1);
periods = device->NumUpdates;
periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
bufferLen = periodLen * periods;
@@ -748,7 +799,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
}
CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
/* test and set channels (implicitly sets frame bits) */
- if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
+ if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)) < 0)
{
static const enum DevFmtChannels channellist[] = {
DevFmtStereo,
@@ -761,20 +812,24 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
for(k = 0;k < COUNTOF(channellist);k++)
{
- if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
+ if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k], 0)) >= 0)
{
device->FmtChans = channellist[k];
+ device->AmbiOrder = 0;
break;
}
}
}
- CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+ CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
/* set rate (implicitly constrains period/buffer parameters) */
- if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0))
+ if(!GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) ||
+ !(device->Flags&DEVICE_FREQUENCY_REQUEST))
{
if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
ERR("Failed to disable ALSA resampler\n");
}
+ else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0)
+ ERR("Failed to enable ALSA resampler\n");
CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
/* set buffer time (implicitly constrains period/buffer parameters) */
if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
@@ -787,7 +842,9 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_access(hp, &access));
CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
- CHECK(snd_pcm_hw_params_get_periods(hp, &periods, NULL));
+ CHECK(snd_pcm_hw_params_get_periods(hp, &periods, &dir));
+ if(dir != 0)
+ WARN("Inexact period count: %u (%d)\n", periods, dir);
snd_pcm_hw_params_free(hp);
hp = NULL;
@@ -837,7 +894,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
{
- self->buffer = malloc(self->size);
+ self->buffer = al_malloc(16, self->size);
if(!self->buffer)
{
ERR("buffer malloc failed\n");
@@ -855,11 +912,11 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
}
thread_func = ALCplaybackAlsa_mixerProc;
}
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, thread_func, self) != althrd_success)
{
ERR("Could not create playback thread\n");
- free(self->buffer);
+ al_free(self->buffer);
self->buffer = NULL;
return ALC_FALSE;
}
@@ -876,28 +933,33 @@ static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
- free(self->buffer);
+ al_free(self->buffer);
self->buffer = NULL;
}
-static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self)
+static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t delay = 0;
+ ClockLatency ret;
int err;
+ ALCplaybackAlsa_lock(self);
+ ret.ClockTime = GetDeviceClockTime(device);
if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
- return 0;
+ delay = 0;
}
- return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
+ if(delay < 0) delay = 0;
+ ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
+ ALCplaybackAlsa_unlock(self);
+
+ return ret;
}
@@ -910,21 +972,20 @@ typedef struct ALCcaptureAlsa {
ALsizei size;
ALboolean doCapture;
- RingBuffer *ring;
+ ll_ringbuffer_t *ring;
snd_pcm_sframes_t last_avail;
} ALCcaptureAlsa;
static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct)
+static void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self);
static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
-static void ALCcaptureAlsa_close(ALCcaptureAlsa *self);
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
-static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self);
+static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
@@ -936,6 +997,25 @@ static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
+
+ self->pcmHandle = NULL;
+ self->buffer = NULL;
+ self->ring = NULL;
+}
+
+void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self)
+{
+ if(self->pcmHandle)
+ snd_pcm_close(self->pcmHandle);
+ self->pcmHandle = NULL;
+
+ al_free(self->buffer);
+ self->buffer = NULL;
+
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -958,12 +1038,12 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
if(VECTOR_SIZE(CaptureDevices) == 0)
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(CaptureDevices))
+ if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
- driver = al_string_get_cstr(iter->device_name);
+ driver = alstr_get_cstr(iter->device_name);
}
else
{
@@ -1020,7 +1100,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
/* set format (implicitly sets sample bits) */
CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
/* set channels (implicitly sets frame bits) */
- CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+ CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
/* set rate (implicitly constrains period/buffer parameters) */
CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
@@ -1042,24 +1122,19 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
if(needring)
{
- self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType),
- device->UpdateSize*device->NumUpdates);
+ self->ring = ll_ringbuffer_create(
+ device->UpdateSize*device->NumUpdates,
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
+ false
+ );
if(!self->ring)
{
ERR("ring buffer create failed\n");
goto error2;
}
-
- self->size = snd_pcm_frames_to_bytes(self->pcmHandle, periodSizeInFrames);
- self->buffer = malloc(self->size);
- if(!self->buffer)
- {
- ERR("buffer malloc failed\n");
- goto error2;
- }
}
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
@@ -1068,31 +1143,29 @@ error:
if(hp) snd_pcm_hw_params_free(hp);
error2:
- free(self->buffer);
- self->buffer = NULL;
- DestroyRingBuffer(self->ring);
+ ll_ringbuffer_free(self->ring);
self->ring = NULL;
snd_pcm_close(self->pcmHandle);
+ self->pcmHandle = NULL;
return ALC_INVALID_VALUE;
}
-static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
-{
- snd_pcm_close(self->pcmHandle);
- DestroyRingBuffer(self->ring);
-
- free(self->buffer);
- self->buffer = NULL;
-}
-
static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
{
- int err = snd_pcm_start(self->pcmHandle);
+ int err = snd_pcm_prepare(self->pcmHandle);
+ if(err < 0)
+ ERR("prepare failed: %s\n", snd_strerror(err));
+ else
+ {
+ err = snd_pcm_start(self->pcmHandle);
+ if(err < 0)
+ ERR("start failed: %s\n", snd_strerror(err));
+ }
if(err < 0)
{
- ERR("start failed: %s\n", snd_strerror(err));
- aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, "Capture state failure: %s",
+ snd_strerror(err));
return ALC_FALSE;
}
@@ -1117,11 +1190,11 @@ static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
void *ptr;
size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
- ptr = malloc(size);
+ ptr = al_malloc(16, size);
if(ptr)
{
ALCcaptureAlsa_captureSamples(self, ptr, avail);
- free(self->buffer);
+ al_free(self->buffer);
self->buffer = ptr;
self->size = size;
}
@@ -1138,12 +1211,12 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
if(self->ring)
{
- ReadRingBuffer(self->ring, buffer, samples);
+ ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
self->last_avail -= samples;
- while(device->Connected && samples > 0)
+ while(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && samples > 0)
{
snd_pcm_sframes_t amt = 0;
@@ -1163,7 +1236,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
}
else
{
- free(self->buffer);
+ al_free(self->buffer);
self->buffer = NULL;
self->size = 0;
}
@@ -1186,7 +1259,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
break;
}
/* If the amount available is less than what's asked, we lost it
@@ -1211,7 +1284,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t avail = 0;
- if(device->Connected && self->doCapture)
+ if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && self->doCapture)
avail = snd_pcm_avail_update(self->pcmHandle);
if(avail < 0)
{
@@ -1227,7 +1300,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
if(avail < 0)
{
ERR("restore error: %s\n", snd_strerror(avail));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(avail));
}
}
@@ -1241,12 +1314,15 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
while(avail > 0)
{
+ ll_ringbuffer_data_t vec[2];
snd_pcm_sframes_t amt;
- amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
- if(avail < amt) amt = avail;
+ ll_ringbuffer_get_write_vector(self->ring, vec);
+ if(vec[0].len == 0) break;
- amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt);
+ amt = (vec[0].len < (snd_pcm_uframes_t)avail) ?
+ vec[0].len : (snd_pcm_uframes_t)avail;
+ amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt);
if(amt < 0)
{
ERR("read error: %s\n", snd_strerror(amt));
@@ -1263,39 +1339,41 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
break;
}
avail = amt;
continue;
}
- WriteRingBuffer(self->ring, self->buffer, amt);
+ ll_ringbuffer_write_advance(self->ring, amt);
avail -= amt;
}
- return RingBufferSize(self->ring);
+ return ll_ringbuffer_read_space(self->ring);
}
-static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self)
+static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t delay = 0;
+ ClockLatency ret;
int err;
+ ALCcaptureAlsa_lock(self);
+ ret.ClockTime = GetDeviceClockTime(device);
if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
- return 0;
+ delay = 0;
}
- return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
-}
+ if(delay < 0) delay = 0;
+ ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
+ ALCcaptureAlsa_unlock(self);
+ return ret;
+}
-static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
-static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
typedef struct ALCalsaBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
@@ -1334,19 +1412,25 @@ static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
+#define APPEND_OUTNAME(i) do { \
+ if(!alstr_empty((i)->name)) \
+ alstr_append_range(outnames, VECTOR_BEGIN((i)->name), \
+ VECTOR_END((i)->name)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
- VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+ VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
- VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+ VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
}
diff --git a/Alc/backends/base.c b/Alc/backends/base.c
index ebeb31bf..9d8614b1 100644
--- a/Alc/backends/base.c
+++ b/Alc/backends/base.c
@@ -4,17 +4,22 @@
#include <stdlib.h>
#include "alMain.h"
+#include "alu.h"
#include "backends/base.h"
+extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
+extern inline void ALCdevice_Lock(ALCdevice *device);
+extern inline void ALCdevice_Unlock(ALCdevice *device);
+extern inline ClockLatency GetClockLatency(ALCdevice *device);
+
/* Base ALCbackend method implementations. */
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
{
- int ret;
- self->mDevice = device;
- ret = almtx_init(&self->mMutex, almtx_recursive);
+ int ret = almtx_init(&self->mMutex, almtx_recursive);
assert(ret == althrd_success);
+ self->mDevice = device;
}
void ALCbackend_Destruct(ALCbackend *self)
@@ -37,9 +42,27 @@ ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
return 0;
}
-ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self))
+ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
{
- return 0;
+ ALCdevice *device = self->mDevice;
+ ALuint refcount;
+ ClockLatency ret;
+
+ do {
+ while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
+ althrd_yield();
+ ret.ClockTime = GetDeviceClockTime(device);
+ ATOMIC_THREAD_FENCE(almemory_order_acquire);
+ } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
+
+ /* NOTE: The device will generally have about all but one periods filled at
+ * any given time during playback. Without a more accurate measurement from
+ * the output, this is an okay approximation.
+ */
+ ret.Latency = device->UpdateSize * DEVICE_CLOCK_RES / device->Frequency *
+ maxu(device->NumUpdates-1, 1);
+
+ return ret;
}
void ALCbackend_lock(ALCbackend *self)
@@ -59,157 +82,3 @@ void ALCbackend_unlock(ALCbackend *self)
void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
{
}
-
-
-/* Wrappers to use an old-style backend with the new interface. */
-typedef struct PlaybackWrapper {
- DERIVE_FROM_TYPE(ALCbackend);
-
- const BackendFuncs *Funcs;
-} PlaybackWrapper;
-
-static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
-static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
-static void PlaybackWrapper_close(PlaybackWrapper *self);
-static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
-static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
-static void PlaybackWrapper_stop(PlaybackWrapper *self);
-static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALint64, getLatency)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
-DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
-
-static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
-{
- ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
- SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
-
- self->Funcs = funcs;
-}
-
-static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->OpenPlayback(device, name);
-}
-
-static void PlaybackWrapper_close(PlaybackWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->ClosePlayback(device);
-}
-
-static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->ResetPlayback(device);
-}
-
-static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->StartPlayback(device);
-}
-
-static void PlaybackWrapper_stop(PlaybackWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->StopPlayback(device);
-}
-
-
-typedef struct CaptureWrapper {
- DERIVE_FROM_TYPE(ALCbackend);
-
- const BackendFuncs *Funcs;
-} CaptureWrapper;
-
-static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
-static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
-static void CaptureWrapper_close(CaptureWrapper *self);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
-static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
-static void CaptureWrapper_stop(CaptureWrapper *self);
-static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
-static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALint64, getLatency)
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
-DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
-
-static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
-{
- ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
- SET_VTABLE2(CaptureWrapper, ALCbackend, self);
-
- self->Funcs = funcs;
-}
-
-static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->OpenCapture(device, name);
-}
-
-static void CaptureWrapper_close(CaptureWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->CloseCapture(device);
-}
-
-static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->StartCapture(device);
- return ALC_TRUE;
-}
-
-static void CaptureWrapper_stop(CaptureWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->StopCapture(device);
-}
-
-static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->CaptureSamples(device, buffer, samples);
-}
-
-static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->AvailableSamples(device);
-}
-
-
-ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
-{
- if(type == ALCbackend_Playback)
- {
- PlaybackWrapper *backend;
-
- NEW_OBJ(backend, PlaybackWrapper)(device, funcs);
- if(!backend) return NULL;
-
- return STATIC_CAST(ALCbackend, backend);
- }
-
- if(type == ALCbackend_Capture)
- {
- CaptureWrapper *backend;
-
- NEW_OBJ(backend, CaptureWrapper)(device, funcs);
- if(!backend) return NULL;
-
- return STATIC_CAST(ALCbackend, backend);
- }
-
- return NULL;
-}
diff --git a/Alc/backends/base.h b/Alc/backends/base.h
index f6b4b80a..03db56e9 100644
--- a/Alc/backends/base.h
+++ b/Alc/backends/base.h
@@ -3,6 +3,26 @@
#include "alMain.h"
#include "threads.h"
+#include "alstring.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ClockLatency {
+ ALint64 ClockTime;
+ ALint64 Latency;
+} ClockLatency;
+
+/* Helper to get the current clock time from the device's ClockBase, and
+ * SamplesDone converted from the sample rate.
+ */
+inline ALuint64 GetDeviceClockTime(ALCdevice *device)
+{
+ return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES /
+ device->Frequency);
+}
struct ALCbackendVtable;
@@ -20,7 +40,7 @@ void ALCbackend_Destruct(ALCbackend *self);
ALCboolean ALCbackend_reset(ALCbackend *self);
ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
ALCuint ALCbackend_availableSamples(ALCbackend *self);
-ALint64 ALCbackend_getLatency(ALCbackend *self);
+ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
void ALCbackend_lock(ALCbackend *self);
void ALCbackend_unlock(ALCbackend *self);
@@ -28,7 +48,6 @@ struct ALCbackendVtable {
void (*const Destruct)(ALCbackend*);
ALCenum (*const open)(ALCbackend*, const ALCchar*);
- void (*const close)(ALCbackend*);
ALCboolean (*const reset)(ALCbackend*);
ALCboolean (*const start)(ALCbackend*);
@@ -37,7 +56,7 @@ struct ALCbackendVtable {
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
ALCuint (*const availableSamples)(ALCbackend*);
- ALint64 (*const getLatency)(ALCbackend*);
+ ClockLatency (*const getClockLatency)(ALCbackend*);
void (*const lock)(ALCbackend*);
void (*const unlock)(ALCbackend*);
@@ -48,13 +67,12 @@ struct ALCbackendVtable {
#define DEFINE_ALCBACKEND_VTABLE(T) \
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
-DECLARE_THUNK(T, ALCbackend, void, close) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
DECLARE_THUNK(T, ALCbackend, void, stop) \
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
-DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \
+DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency) \
DECLARE_THUNK(T, ALCbackend, void, lock) \
DECLARE_THUNK(T, ALCbackend, void, unlock) \
static void T##_ALCbackend_Delete(void *ptr) \
@@ -64,13 +82,12 @@ static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
T##_ALCbackend_Destruct, \
\
T##_ALCbackend_open, \
- T##_ALCbackend_close, \
T##_ALCbackend_reset, \
T##_ALCbackend_start, \
T##_ALCbackend_stop, \
T##_ALCbackend_captureSamples, \
T##_ALCbackend_availableSamples, \
- T##_ALCbackend_getLatency, \
+ T##_ALCbackend_getClockLatency, \
T##_ALCbackend_lock, \
T##_ALCbackend_unlock, \
\
@@ -99,7 +116,7 @@ struct ALCbackendFactoryVtable {
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
- void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
+ void (*const probe)(ALCbackendFactory *self, enum DevProbe type, al_string *outnames);
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
};
@@ -108,7 +125,7 @@ struct ALCbackendFactoryVtable {
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
-DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
+DECLARE_THUNK2(T, ALCbackendFactory, void, probe, enum DevProbe, al_string*) \
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
\
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
@@ -122,17 +139,40 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
+ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
-ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
+ALCbackendFactory *SndioBackendFactory_getFactory(void);
+ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
+ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
+ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
+ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
-ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
+
+inline void ALCdevice_Lock(ALCdevice *device)
+{ V0(device->Backend,lock)(); }
+
+inline void ALCdevice_Unlock(ALCdevice *device)
+{ V0(device->Backend,unlock)(); }
+
+
+inline ClockLatency GetClockLatency(ALCdevice *device)
+{
+ ClockLatency ret = V0(device->Backend,getClockLatency)();
+ ret.Latency += device->FixedLatency;
+ return ret;
+}
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
#endif /* AL_BACKENDS_BASE_H */
diff --git a/Alc/backends/coreaudio.c b/Alc/backends/coreaudio.c
index 43e881da..adb01fa6 100644
--- a/Alc/backends/coreaudio.c
+++ b/Alc/backends/coreaudio.c
@@ -23,195 +23,145 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <alloca.h>
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
-#include <CoreServices/CoreServices.h>
#include <unistd.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
+#include "backends/base.h"
-typedef struct {
- AudioUnit audioUnit;
-
- ALuint frameSize;
- ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
- AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
- AudioConverterRef audioConverter; // Sample rate converter if needed
- AudioBufferList *bufferList; // Buffer for data coming from the input device
- ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
+static const ALCchar ca_device[] = "CoreAudio Default";
- RingBuffer *ring;
-} ca_data;
-static const ALCchar ca_device[] = "CoreAudio Default";
+typedef struct ALCcoreAudioPlayback {
+ DERIVE_FROM_TYPE(ALCbackend);
+ AudioUnit audioUnit;
-static void destroy_buffer_list(AudioBufferList* list)
-{
- if(list)
- {
- UInt32 i;
- for(i = 0;i < list->mNumberBuffers;i++)
- free(list->mBuffers[i].mData);
- free(list);
- }
-}
+ ALuint frameSize;
+ AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
+} ALCcoreAudioPlayback;
-static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
-{
- AudioBufferList *list;
+static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
+static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self);
+static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name);
+static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self);
+static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self);
+static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self);
+static DECLARE_FORWARD2(ALCcoreAudioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioPlayback)
- list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
- if(list)
- {
- list->mNumberBuffers = 1;
+DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioPlayback);
- list->mBuffers[0].mNumberChannels = channelCount;
- list->mBuffers[0].mDataByteSize = byteSize;
- list->mBuffers[0].mData = malloc(byteSize);
- if(list->mBuffers[0].mData == NULL)
- {
- free(list);
- list = NULL;
- }
- }
- return list;
-}
-static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
+static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device)
{
- ALCdevice *device = (ALCdevice*)inRefCon;
- ca_data *data = (ca_data*)device->ExtraData;
-
- aluMixData(device, ioData->mBuffers[0].mData,
- ioData->mBuffers[0].mDataByteSize / data->frameSize);
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
- return noErr;
+ self->frameSize = 0;
+ memset(&self->format, 0, sizeof(self->format));
}
-static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
- AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
+static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
{
- ALCdevice *device = (ALCdevice*)inUserData;
- ca_data *data = (ca_data*)device->ExtraData;
+ AudioUnitUninitialize(self->audioUnit);
+ AudioComponentInstanceDispose(self->audioUnit);
- // Read from the ring buffer and store temporarily in a large buffer
- ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
-
- // Set the input data
- ioData->mNumberBuffers = 1;
- ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
- ioData->mBuffers[0].mData = data->resampleBuffer;
- ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
-
- return noErr;
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
-static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
- UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- ALCdevice *device = (ALCdevice*)inRefCon;
- ca_data *data = (ca_data*)device->ExtraData;
- AudioUnitRenderActionFlags flags = 0;
- OSStatus err;
- // fill the bufferList with data from the input device
- err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
- if(err != noErr)
- {
- ERR("AudioUnitRender error: %d\n", err);
- return err;
- }
+static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon,
+ AudioUnitRenderActionFlags* UNUSED(ioActionFlags), const AudioTimeStamp* UNUSED(inTimeStamp),
+ UInt32 UNUSED(inBusNumber), UInt32 UNUSED(inNumberFrames), AudioBufferList *ioData)
+{
+ ALCcoreAudioPlayback *self = inRefCon;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
- WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
+ ALCcoreAudioPlayback_lock(self);
+ aluMixData(device, ioData->mBuffers[0].mData,
+ ioData->mBuffers[0].mDataByteSize / self->frameSize);
+ ALCcoreAudioPlayback_unlock(self);
return noErr;
}
-static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
+
+static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
{
- ComponentDescription desc;
- Component comp;
- ca_data *data;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ AudioComponentDescription desc;
+ AudioComponent comp;
OSStatus err;
- if(!deviceName)
- deviceName = ca_device;
- else if(strcmp(deviceName, ca_device) != 0)
+ if(!name)
+ name = ca_device;
+ else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
/* open the default output unit */
desc.componentType = kAudioUnitType_Output;
+#if TARGET_OS_IOS
+ desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#else
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+#endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
- comp = FindNextComponent(NULL, &desc);
+ comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
- ERR("FindNextComponent failed\n");
+ ERR("AudioComponentFindNext failed\n");
return ALC_INVALID_VALUE;
}
- data = calloc(1, sizeof(*data));
-
- err = OpenAComponent(comp, &data->audioUnit);
+ err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
- ERR("OpenAComponent failed\n");
- free(data);
+ ERR("AudioComponentInstanceNew failed\n");
return ALC_INVALID_VALUE;
}
/* init and start the default audio unit... */
- err = AudioUnitInitialize(data->audioUnit);
+ err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
- CloseComponent(data->audioUnit);
- free(data);
+ AudioComponentInstanceDispose(self->audioUnit);
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
- device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ca_close_playback(ALCdevice *device)
+static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
{
- ca_data *data = (ca_data*)device->ExtraData;
-
- AudioUnitUninitialize(data->audioUnit);
- CloseComponent(data->audioUnit);
-
- free(data);
- device->ExtraData = NULL;
-}
-
-static ALCboolean ca_reset_playback(ALCdevice *device)
-{
- ca_data *data = (ca_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioStreamBasicDescription streamFormat;
AURenderCallbackStruct input;
OSStatus err;
UInt32 size;
- err = AudioUnitUninitialize(data->audioUnit);
+ err = AudioUnitUninitialize(self->audioUnit);
if(err != noErr)
ERR("-- AudioUnitUninitialize failed.\n");
/* retrieve default output unit's properties (output side) */
size = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
+ err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
@@ -229,7 +179,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
#endif
/* set default output unit's input side to match output side */
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -238,7 +188,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
if(device->Frequency != streamFormat.mSampleRate)
{
- device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
+ device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates *
streamFormat.mSampleRate /
device->Frequency);
device->Frequency = streamFormat.mSampleRate;
@@ -313,7 +263,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
kLinearPCMFormatFlagIsPacked;
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -321,11 +271,11 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
}
/* setup callback */
- data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
- input.inputProc = ca_callback;
- input.inputProcRefCon = device;
+ self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+ input.inputProc = ALCcoreAudioPlayback_MixerProc;
+ input.inputProcRefCon = self;
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -333,7 +283,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
}
/* init the default audio unit... */
- err = AudioUnitInitialize(data->audioUnit);
+ err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
@@ -343,12 +293,9 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
-static ALCboolean ca_start_playback(ALCdevice *device)
+static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
{
- ca_data *data = (ca_data*)device->ExtraData;
- OSStatus err;
-
- err = AudioOutputUnitStart(data->audioUnit);
+ OSStatus err = AudioOutputUnitStart(self->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
@@ -358,64 +305,196 @@ static ALCboolean ca_start_playback(ALCdevice *device)
return ALC_TRUE;
}
-static void ca_stop_playback(ALCdevice *device)
+static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
+{
+ OSStatus err = AudioOutputUnitStop(self->audioUnit);
+ if(err != noErr)
+ ERR("AudioOutputUnitStop failed\n");
+}
+
+
+
+
+typedef struct ALCcoreAudioCapture {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ AudioUnit audioUnit;
+
+ ALuint frameSize;
+ ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
+ AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
+
+ AudioConverterRef audioConverter; // Sample rate converter if needed
+ AudioBufferList *bufferList; // Buffer for data coming from the input device
+ ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
+
+ ll_ringbuffer_t *ring;
+} ALCcoreAudioCapture;
+
+static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
+static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
+static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
+static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
+static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self);
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
+
+DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
+
+
+static AudioBufferList *allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
+{
+ AudioBufferList *list;
+
+ list = calloc(1, FAM_SIZE(AudioBufferList, mBuffers, 1) + byteSize);
+ if(list)
+ {
+ list->mNumberBuffers = 1;
+
+ list->mBuffers[0].mNumberChannels = channelCount;
+ list->mBuffers[0].mDataByteSize = byteSize;
+ list->mBuffers[0].mData = &list->mBuffers[1];
+ }
+ return list;
+}
+
+static void destroy_buffer_list(AudioBufferList *list)
+{
+ free(list);
+}
+
+
+static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
+
+ self->audioUnit = 0;
+ self->audioConverter = NULL;
+ self->bufferList = NULL;
+ self->resampleBuffer = NULL;
+ self->ring = NULL;
+}
+
+static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
+{
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+
+ free(self->resampleBuffer);
+ self->resampleBuffer = NULL;
+
+ destroy_buffer_list(self->bufferList);
+ self->bufferList = NULL;
+
+ if(self->audioConverter)
+ AudioConverterDispose(self->audioConverter);
+ self->audioConverter = NULL;
+
+ if(self->audioUnit)
+ AudioComponentInstanceDispose(self->audioUnit);
+ self->audioUnit = 0;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon,
+ AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
+ const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber),
+ UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
{
- ca_data *data = (ca_data*)device->ExtraData;
+ ALCcoreAudioCapture *self = inRefCon;
+ AudioUnitRenderActionFlags flags = 0;
OSStatus err;
- err = AudioOutputUnitStop(data->audioUnit);
+ // fill the bufferList with data from the input device
+ err = AudioUnitRender(self->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->bufferList);
if(err != noErr)
- ERR("AudioOutputUnitStop failed\n");
+ {
+ ERR("AudioUnitRender error: %d\n", err);
+ return err;
+ }
+
+ ll_ringbuffer_write(self->ring, self->bufferList->mBuffers[0].mData, inNumberFrames);
+
+ return noErr;
+}
+
+static OSStatus ALCcoreAudioCapture_ConvertCallback(AudioConverterRef UNUSED(inAudioConverter),
+ UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
+ AudioStreamPacketDescription** UNUSED(outDataPacketDescription),
+ void *inUserData)
+{
+ ALCcoreAudioCapture *self = inUserData;
+
+ // Read from the ring buffer and store temporarily in a large buffer
+ ll_ringbuffer_read(self->ring, self->resampleBuffer, *ioNumberDataPackets);
+
+ // Set the input data
+ ioData->mNumberBuffers = 1;
+ ioData->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
+ ioData->mBuffers[0].mData = self->resampleBuffer;
+ ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * self->format.mBytesPerFrame;
+
+ return noErr;
}
-static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
+
+static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name)
{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioStreamBasicDescription requestedFormat; // The application requested format
AudioStreamBasicDescription hardwareFormat; // The hardware format
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
AURenderCallbackStruct input;
- ComponentDescription desc;
- AudioDeviceID inputDevice;
+ AudioComponentDescription desc;
UInt32 outputFrameCount;
UInt32 propertySize;
+ AudioObjectPropertyAddress propertyAddress;
UInt32 enableIO;
- Component comp;
- ca_data *data;
+ AudioComponent comp;
OSStatus err;
- if(!deviceName)
- deviceName = ca_device;
- else if(strcmp(deviceName, ca_device) != 0)
+ if(!name)
+ name = ca_device;
+ else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
desc.componentType = kAudioUnitType_Output;
+#if TARGET_OS_IOS
+ desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#else
desc.componentSubType = kAudioUnitSubType_HALOutput;
+#endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Search for component with given description
- comp = FindNextComponent(NULL, &desc);
+ comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
- ERR("FindNextComponent failed\n");
+ ERR("AudioComponentFindNext failed\n");
return ALC_INVALID_VALUE;
}
- data = calloc(1, sizeof(*data));
- device->ExtraData = data;
-
// Open the component
- err = OpenAComponent(comp, &data->audioUnit);
+ err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
- ERR("OpenAComponent failed\n");
+ ERR("AudioComponentInstanceNew failed\n");
goto error;
}
// Turn off AudioUnit output
enableIO = 0;
- err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -424,22 +503,28 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
// Turn on AudioUnit input
enableIO = 1;
- err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
+#if !TARGET_OS_IOS
// Get the default input device
+ AudioDeviceID inputDevice = kAudioDeviceUnknown;
+
propertySize = sizeof(AudioDeviceID);
- err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
+ propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+
+ err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
if(err != noErr)
{
- ERR("AudioHardwareGetProperty failed\n");
+ ERR("AudioObjectGetPropertyData failed\n");
goto error;
}
-
if(inputDevice == kAudioDeviceUnknown)
{
ERR("No input device found\n");
@@ -447,18 +532,19 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Track the input device
- err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
+#endif
// set capture callback
- input.inputProc = ca_capture_callback;
- input.inputProcRefCon = device;
+ input.inputProc = ALCcoreAudioCapture_RecordProc;
+ input.inputProcRefCon = self;
- err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -466,7 +552,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Initialize the device
- err = AudioUnitInitialize(data->audioUnit);
+ err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
@@ -475,7 +561,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
// Get the hardware format
propertySize = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
+ err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
@@ -522,7 +608,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
goto error;
}
@@ -535,8 +621,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
requestedFormat.mFramesPerPacket = 1;
// save requested format description for later use
- data->format = requestedFormat;
- data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->format = requestedFormat;
+ self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
// Use intermediate format for sample rate conversion (outputFormat)
// Set sample rate to the same as hardware for resampling later
@@ -544,11 +630,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
// Determine sample rate ratio for resampling
- data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
+ self->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
// The output format should be the requested format, but using the hardware sample rate
// This is because the AudioUnit will automatically scale other properties, except for sample rate
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -556,8 +642,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Set the AudioUnit output format frame count
- outputFrameCount = device->UpdateSize * data->sampleRateRatio;
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
+ outputFrameCount = device->UpdateSize * self->sampleRateRatio;
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed: %d\n", err);
@@ -565,7 +651,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Set up sample converter
- err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
+ err = AudioConverterNew(&outputFormat, &requestedFormat, &self->audioConverter);
if(err != noErr)
{
ERR("AudioConverterNew failed: %d\n", err);
@@ -573,92 +659,83 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Create a buffer for use in the resample callback
- data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
+ self->resampleBuffer = malloc(device->UpdateSize * self->frameSize * self->sampleRateRatio);
// Allocate buffer for the AudioUnit output
- data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
- if(data->bufferList == NULL)
+ self->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->frameSize * self->sampleRateRatio);
+ if(self->bufferList == NULL)
goto error;
- data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
- if(data->ring == NULL)
- goto error;
+ self->ring = ll_ringbuffer_create(
+ (size_t)ceil(device->UpdateSize*self->sampleRateRatio*device->NumUpdates),
+ self->frameSize, false
+ );
+ if(!self->ring) goto error;
- al_string_copy_cstr(&device->DeviceName, deviceName);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
error:
- DestroyRingBuffer(data->ring);
- free(data->resampleBuffer);
- destroy_buffer_list(data->bufferList);
-
- if(data->audioConverter)
- AudioConverterDispose(data->audioConverter);
- if(data->audioUnit)
- CloseComponent(data->audioUnit);
-
- free(data);
- device->ExtraData = NULL;
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+ free(self->resampleBuffer);
+ self->resampleBuffer = NULL;
+ destroy_buffer_list(self->bufferList);
+ self->bufferList = NULL;
+
+ if(self->audioConverter)
+ AudioConverterDispose(self->audioConverter);
+ self->audioConverter = NULL;
+ if(self->audioUnit)
+ AudioComponentInstanceDispose(self->audioUnit);
+ self->audioUnit = 0;
return ALC_INVALID_VALUE;
}
-static void ca_close_capture(ALCdevice *device)
-{
- ca_data *data = (ca_data*)device->ExtraData;
-
- DestroyRingBuffer(data->ring);
- free(data->resampleBuffer);
- destroy_buffer_list(data->bufferList);
- AudioConverterDispose(data->audioConverter);
- CloseComponent(data->audioUnit);
-
- free(data);
- device->ExtraData = NULL;
-}
-
-static void ca_start_capture(ALCdevice *device)
+static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
{
- ca_data *data = (ca_data*)device->ExtraData;
- OSStatus err = AudioOutputUnitStart(data->audioUnit);
+ OSStatus err = AudioOutputUnitStart(self->audioUnit);
if(err != noErr)
+ {
ERR("AudioOutputUnitStart failed\n");
+ return ALC_FALSE;
+ }
+ return ALC_TRUE;
}
-static void ca_stop_capture(ALCdevice *device)
+static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
{
- ca_data *data = (ca_data*)device->ExtraData;
- OSStatus err = AudioOutputUnitStop(data->audioUnit);
+ OSStatus err = AudioOutputUnitStop(self->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
-static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
{
- ca_data *data = (ca_data*)device->ExtraData;
- AudioBufferList *list;
+ union {
+ ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)];
+ AudioBufferList list;
+ } audiobuf = { { 0 } };
UInt32 frameCount;
OSStatus err;
// If no samples are requested, just return
- if(samples == 0)
- return ALC_NO_ERROR;
-
- // Allocate a temporary AudioBufferList to use as the return resamples data
- list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
+ if(samples == 0) return ALC_NO_ERROR;
// Point the resampling buffer to the capture buffer
- list->mNumberBuffers = 1;
- list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
- list->mBuffers[0].mDataByteSize = samples * data->frameSize;
- list->mBuffers[0].mData = buffer;
+ audiobuf.list.mNumberBuffers = 1;
+ audiobuf.list.mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
+ audiobuf.list.mBuffers[0].mDataByteSize = samples * self->frameSize;
+ audiobuf.list.mBuffers[0].mData = buffer;
// Resample into another AudioBufferList
frameCount = samples;
- err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
- device, &frameCount, list, NULL);
+ err = AudioConverterFillComplexBuffer(self->audioConverter,
+ ALCcoreAudioCapture_ConvertCallback, self, &frameCount, &audiobuf.list, NULL
+ );
if(err != noErr)
{
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
@@ -667,46 +744,73 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
return ALC_NO_ERROR;
}
-static ALCuint ca_available_samples(ALCdevice *device)
+static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
{
- ca_data *data = device->ExtraData;
- return RingBufferSize(data->ring) / data->sampleRateRatio;
+ return ll_ringbuffer_read_space(self->ring) / self->sampleRateRatio;
}
-static const BackendFuncs ca_funcs = {
- ca_open_playback,
- ca_close_playback,
- ca_reset_playback,
- ca_start_playback,
- ca_stop_playback,
- ca_open_capture,
- ca_close_capture,
- ca_start_capture,
- ca_stop_capture,
- ca_capture_samples,
- ca_available_samples
-};
-
-ALCboolean alc_ca_init(BackendFuncs *func_list)
+typedef struct ALCcoreAudioBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCcoreAudioBackendFactory;
+#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
+
+static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
+static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
+static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
+
+
+ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void)
+{
+ static ALCcoreAudioBackendFactory factory = ALCCOREAUDIOBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory* UNUSED(self))
{
- *func_list = ca_funcs;
return ALC_TRUE;
}
-void alc_ca_deinit(void)
+static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Playback || ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_ca_probe(enum DevProbe type)
+static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(ca_device);
- break;
case CAPTURE_DEVICE_PROBE:
- AppendCaptureDeviceList(ca_device);
+ alstr_append_range(outnames, ca_device, ca_device+sizeof(ca_device));
break;
}
}
+
+static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCcoreAudioPlayback *backend;
+ NEW_OBJ(backend, ALCcoreAudioPlayback)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ ALCcoreAudioCapture *backend;
+ NEW_OBJ(backend, ALCcoreAudioCapture)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
diff --git a/Alc/backends/dsound.c b/Alc/backends/dsound.c
index 4db4b557..c368cffb 100644
--- a/Alc/backends/dsound.c
+++ b/Alc/backends/dsound.c
@@ -34,6 +34,7 @@
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
#include "alstring.h"
@@ -60,7 +61,7 @@
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#define DEVNAME_TAIL " on OpenAL Soft"
+#define DEVNAME_HEAD "OpenAL Soft on "
#ifdef HAVE_DYNLOAD
@@ -123,7 +124,7 @@ static void clear_devlist(vector_DevMap *list)
{
#define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
- VECTOR_RESIZE(*list, 0);
+ VECTOR_RESIZE(*list, 0, 0);
#undef DEINIT_STR
}
@@ -145,19 +146,18 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
{
const DevMap *iter;
- al_string_copy_wcstr(&entry.name, desc);
- if(count == 0)
- al_string_append_cstr(&entry.name, DEVNAME_TAIL);
- else
+ alstr_copy_cstr(&entry.name, DEVNAME_HEAD);
+ alstr_append_wcstr(&entry.name, desc);
+ if(count != 0)
{
char str[64];
- snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
- al_string_append_cstr(&entry.name, str);
+ snprintf(str, sizeof(str), " #%d", count+1);
+ alstr_append_cstr(&entry.name, str);
}
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(*devices)) break;
+ if(iter == VECTOR_END(*devices)) break;
#undef MATCH_ENTRY
count++;
}
@@ -166,7 +166,7 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
hr = StringFromCLSID(guid, &guidstr);
if(SUCCEEDED(hr))
{
- TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
+ TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry.name), guidstr);
CoTaskMemFree(guidstr);
}
@@ -185,22 +185,21 @@ typedef struct ALCdsoundPlayback {
IDirectSoundNotify *Notifies;
HANDLE NotifyEvent;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCdsoundPlayback;
static int ALCdsoundPlayback_mixerProc(void *ptr);
static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, Destruct)
+static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self);
static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name);
-static void ALCdsoundPlayback_close(ALCdsoundPlayback *self);
static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self);
static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
@@ -212,6 +211,35 @@ static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *devi
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self);
+
+ self->DS = NULL;
+ self->PrimaryBuffer = NULL;
+ self->Buffer = NULL;
+ self->Notifies = NULL;
+ self->NotifyEvent = NULL;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
+
+static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self)
+{
+ if(self->Notifies)
+ IDirectSoundNotify_Release(self->Notifies);
+ self->Notifies = NULL;
+ if(self->Buffer)
+ IDirectSoundBuffer_Release(self->Buffer);
+ self->Buffer = NULL;
+ if(self->PrimaryBuffer != NULL)
+ IDirectSoundBuffer_Release(self->PrimaryBuffer);
+ self->PrimaryBuffer = NULL;
+
+ if(self->DS)
+ IDirectSound_Release(self->DS);
+ self->DS = NULL;
+ if(self->NotifyEvent)
+ CloseHandle(self->NotifyEvent);
+ self->NotifyEvent = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -240,16 +268,17 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Failed to get buffer caps: 0x%lx\n", err);
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failure retrieving playback buffer info: 0x%lx", err);
ALCdevice_Unlock(device);
return 1;
}
- FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
FragSize = device->UpdateSize * FrameSize;
IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL);
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
// Get current play cursor
IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL);
@@ -264,7 +293,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Failed to play buffer: 0x%lx\n", err);
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failure starting playback: 0x%lx", err);
ALCdevice_Unlock(device);
return 1;
}
@@ -300,8 +329,10 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
if(SUCCEEDED(err))
{
// If we have an active context, mix data directly into output buffer otherwise fill with silence
+ ALCdevice_Lock(device);
aluMixData(device, WritePtr1, WriteCnt1/FrameSize);
aluMixData(device, WritePtr2, WriteCnt2/FrameSize);
+ ALCdevice_Unlock(device);
// Unlock output buffer only when successfully locked
IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
@@ -310,7 +341,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Buffer lock error: %#lx\n", err);
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to lock output buffer: 0x%lx", err);
ALCdevice_Unlock(device);
return 1;
}
@@ -342,23 +373,23 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
{
- deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
+ deviceName = alstr_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
guid = &VECTOR_FRONT(PlaybackDevices).guid;
}
else
{
const DevMap *iter;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+ if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
guid = &iter->guid;
}
hr = DS_OK;
- self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL)
hr = E_FAIL;
@@ -380,29 +411,11 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
+ alstr_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
}
-static void ALCdsoundPlayback_close(ALCdsoundPlayback *self)
-{
- if(self->Notifies)
- IDirectSoundNotify_Release(self->Notifies);
- self->Notifies = NULL;
- if(self->Buffer)
- IDirectSoundBuffer_Release(self->Buffer);
- self->Buffer = NULL;
- if(self->PrimaryBuffer != NULL)
- IDirectSoundBuffer_Release(self->PrimaryBuffer);
- self->PrimaryBuffer = NULL;
-
- IDirectSound_Release(self->DS);
- self->DS = NULL;
- CloseHandle(self->NotifyEvent);
- self->NotifyEvent = NULL;
-}
-
static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -473,7 +486,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
case DevFmtMono:
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -526,7 +539,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
retry_open:
hr = S_OK;
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+ OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
OutputType.Format.nSamplesPerSec = device->Frequency;
@@ -625,7 +638,7 @@ retry_open:
static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self)
{
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
@@ -636,10 +649,8 @@ static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
IDirectSoundBuffer_Stop(self->Buffer);
@@ -654,19 +665,19 @@ typedef struct ALCdsoundCapture {
IDirectSoundCaptureBuffer *DSCbuffer;
DWORD BufferBytes;
DWORD Cursor;
- RingBuffer *Ring;
+
+ ll_ringbuffer_t *Ring;
} ALCdsoundCapture;
static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, Destruct)
+static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self);
static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name);
-static void ALCdsoundCapture_close(ALCdsoundCapture *self);
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
static void ALCdsoundCapture_stop(ALCdsoundCapture *self);
static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
-static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
@@ -677,6 +688,29 @@ static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCdsoundCapture, ALCbackend, self);
+
+ self->DSC = NULL;
+ self->DSCbuffer = NULL;
+ self->Ring = NULL;
+}
+
+static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self)
+{
+ ll_ringbuffer_free(self->Ring);
+ self->Ring = NULL;
+
+ if(self->DSCbuffer != NULL)
+ {
+ IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
+ IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
+ self->DSCbuffer = NULL;
+ }
+
+ if(self->DSC)
+ IDirectSoundCapture_Release(self->DSC);
+ self->DSC = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -702,17 +736,17 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
{
- deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name);
+ deviceName = alstr_get_cstr(VECTOR_FRONT(CaptureDevices).name);
guid = &VECTOR_FRONT(CaptureDevices).guid;
}
else
{
const DevMap *iter;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(CaptureDevices))
+ if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
guid = &iter->guid;
}
@@ -732,99 +766,98 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
break;
}
- //DirectSoundCapture Init code
- hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
- if(SUCCEEDED(hr))
+ memset(&InputType, 0, sizeof(InputType));
+ switch(device->FmtChans)
{
- memset(&InputType, 0, sizeof(InputType));
-
- switch(device->FmtChans)
- {
- case DevFmtMono:
- InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
- break;
- case DevFmtStereo:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT;
- break;
- case DevFmtQuad:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX51:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX51Rear:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX61:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_CENTER |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX71:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtBFormat3D:
- break;
- }
+ case DevFmtMono:
+ InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
+ break;
+ case DevFmtStereo:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT;
+ break;
+ case DevFmtQuad:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT;
+ break;
+ case DevFmtX51:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ case DevFmtX51Rear:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT;
+ break;
+ case DevFmtX61:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_CENTER |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ case DevFmtX71:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ case DevFmtAmbi3D:
+ WARN("%s capture not supported\n", DevFmtChannelsString(device->FmtChans));
+ return ALC_INVALID_ENUM;
+ }
- InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
- InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
- InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
- InputType.Format.nSamplesPerSec = device->Frequency;
- InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
- InputType.Format.cbSize = 0;
+ InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
+ InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
+ InputType.Format.nSamplesPerSec = device->Frequency;
+ InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
+ InputType.Format.cbSize = 0;
+ InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
+ if(device->FmtType == DevFmtFloat)
+ InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ else
+ InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
- {
- InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
- if(device->FmtType == DevFmtFloat)
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- }
+ if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
+ {
+ InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+ }
- samples = device->UpdateSize * device->NumUpdates;
- samples = maxu(samples, 100 * device->Frequency / 1000);
+ samples = device->UpdateSize * device->NumUpdates;
+ samples = maxu(samples, 100 * device->Frequency / 1000);
- memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
- DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
- DSCBDescription.dwFlags = 0;
- DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
- DSCBDescription.lpwfxFormat = &InputType.Format;
+ memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
+ DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
+ DSCBDescription.dwFlags = 0;
+ DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
+ DSCBDescription.lpwfxFormat = &InputType.Format;
+ //DirectSoundCapture Init code
+ hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
+ if(SUCCEEDED(hr))
hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
- }
if(SUCCEEDED(hr))
{
- self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
+ self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates,
+ InputType.Format.nBlockAlign, false);
if(self->Ring == NULL)
hr = DSERR_OUTOFMEMORY;
}
@@ -833,7 +866,7 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
{
ERR("Device init failed: 0x%08lx\n", hr);
- DestroyRingBuffer(self->Ring);
+ ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->DSCbuffer != NULL)
IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
@@ -848,27 +881,11 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
self->BufferBytes = DSCBDescription.dwBufferBytes;
SetDefaultWFXChannelOrder(device);
- al_string_copy_cstr(&device->DeviceName, deviceName);
+ alstr_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
}
-static void ALCdsoundCapture_close(ALCdsoundCapture *self)
-{
- DestroyRingBuffer(self->Ring);
- self->Ring = NULL;
-
- if(self->DSCbuffer != NULL)
- {
- IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
- IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
- self->DSCbuffer = NULL;
- }
-
- IDirectSoundCapture_Release(self->DSC);
- self->DSC = NULL;
-}
-
static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
{
HRESULT hr;
@@ -877,7 +894,8 @@ static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("start failed: 0x%08lx\n", hr);
- aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
+ "Failure starting capture: 0x%lx", hr);
return ALC_FALSE;
}
@@ -892,13 +910,14 @@ static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("stop failed: 0x%08lx\n", hr);
- aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
+ "Failure stopping capture: 0x%lx", hr);
}
}
static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
{
- ReadRingBuffer(self->Ring, buffer, samples);
+ ll_ringbuffer_read(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
@@ -911,10 +930,10 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
DWORD FrameSize;
HRESULT hr;
- if(!device->Connected)
+ if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
goto done;
- FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
BufferBytes = self->BufferBytes;
LastCursor = self->Cursor;
@@ -930,9 +949,9 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
}
if(SUCCEEDED(hr))
{
- WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
+ ll_ringbuffer_write(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
if(ReadPtr2 != NULL)
- WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
+ ll_ringbuffer_write(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer,
ReadPtr1, ReadCnt1,
ReadPtr2, ReadCnt2);
@@ -942,19 +961,14 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("update failed: 0x%08lx\n", hr);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failure retrieving capture data: 0x%lx", hr);
}
done:
- return RingBufferSize(self->Ring);
+ return (ALCuint)ll_ringbuffer_read_space(self->Ring);
}
-static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
-static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
-
typedef struct ALCdsoundBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCdsoundBackendFactory;
@@ -965,7 +979,7 @@ ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self);
static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self);
static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type);
-static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
+static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
@@ -1009,7 +1023,7 @@ static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory*
return ALC_FALSE;
}
-static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
HRESULT hr, hrcom;
@@ -1017,12 +1031,17 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
hrcom = CoInitialize(NULL);
switch(type)
{
+#define APPEND_OUTNAME(e) do { \
+ if(!alstr_empty((e)->name)) \
+ alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
+ VECTOR_END((e)->name)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
clear_devlist(&PlaybackDevices);
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
if(FAILED(hr))
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
- VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+ VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
@@ -1030,8 +1049,9 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
if(FAILED(hr))
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
- VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+ VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
if(SUCCEEDED(hrcom))
CoUninitialize();
diff --git a/Alc/backends/jack.c b/Alc/backends/jack.c
index 69d1277a..fdbe93f2 100644
--- a/Alc/backends/jack.c
+++ b/Alc/backends/jack.c
@@ -26,6 +26,8 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
@@ -54,6 +56,7 @@ static const ALCchar jackDevice[] = "JACK Default";
MAGIC(jack_get_ports); \
MAGIC(jack_free); \
MAGIC(jack_get_sample_rate); \
+ MAGIC(jack_set_error_function); \
MAGIC(jack_set_process_callback); \
MAGIC(jack_set_buffer_size_callback); \
MAGIC(jack_set_buffer_size); \
@@ -62,6 +65,7 @@ static const ALCchar jackDevice[] = "JACK Default";
static void *jack_handle;
#define MAKE_FUNC(f) static __typeof(f) * p##f
JACK_FUNCS(MAKE_FUNC);
+static __typeof(jack_error_callback) * pjack_error_callback;
#undef MAKE_FUNC
#define jack_client_open pjack_client_open
@@ -78,10 +82,12 @@ JACK_FUNCS(MAKE_FUNC);
#define jack_get_ports pjack_get_ports
#define jack_free pjack_free
#define jack_get_sample_rate pjack_get_sample_rate
+#define jack_set_error_function pjack_set_error_function
#define jack_set_process_callback pjack_set_process_callback
#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
#define jack_set_buffer_size pjack_set_buffer_size
#define jack_get_buffer_size pjack_get_buffer_size
+#define jack_error_callback (*pjack_error_callback)
#endif
@@ -94,26 +100,42 @@ static ALCboolean jack_load(void)
#ifdef HAVE_DYNLOAD
if(!jack_handle)
{
- jack_handle = LoadLib("libjack.so.0");
+ al_string missing_funcs = AL_STRING_INIT_STATIC();
+
+#ifdef _WIN32
+#define JACKLIB "libjack.dll"
+#else
+#define JACKLIB "libjack.so.0"
+#endif
+ jack_handle = LoadLib(JACKLIB);
if(!jack_handle)
+ {
+ WARN("Failed to load %s\n", JACKLIB);
return ALC_FALSE;
+ }
error = ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(jack_handle, #f); \
if(p##f == NULL) { \
error = ALC_TRUE; \
+ alstr_append_cstr(&missing_funcs, "\n" #f); \
} \
} while(0)
JACK_FUNCS(LOAD_FUNC);
#undef LOAD_FUNC
+ /* Optional symbols. These don't exist in all versions of JACK. */
+#define LOAD_SYM(f) p##f = GetSymbol(jack_handle, #f)
+ LOAD_SYM(jack_error_callback);
+#undef LOAD_SYM
if(error)
{
+ WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(jack_handle);
jack_handle = NULL;
- return ALC_FALSE;
}
+ alstr_reset(&missing_funcs);
}
#endif
@@ -128,9 +150,9 @@ typedef struct ALCjackPlayback {
jack_port_t *Port[MAX_OUTPUT_CHANNELS];
ll_ringbuffer_t *Ring;
- alcnd_t Cond;
+ alsem_t Sem;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCjackPlayback;
@@ -142,15 +164,14 @@ static int ALCjackPlayback_mixerProc(void *arg);
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
static void ALCjackPlayback_Destruct(ALCjackPlayback *self);
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
-static void ALCjackPlayback_close(ALCjackPlayback *self);
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
static void ALCjackPlayback_stop(ALCjackPlayback *self);
static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self);
-static void ALCjackPlayback_lock(ALCjackPlayback *self);
-static void ALCjackPlayback_unlock(ALCjackPlayback *self);
+static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
+static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
@@ -163,14 +184,14 @@ static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
- alcnd_init(&self->Cond);
+ alsem_init(&self->Sem, 0);
self->Client = NULL;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
self->Port[i] = NULL;
self->Ring = NULL;
- self->killNow = 1;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
@@ -189,7 +210,7 @@ static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
self->Client = NULL;
}
- alcnd_destroy(&self->Cond);
+ alsem_destroy(&self->Sem);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -204,19 +225,23 @@ static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
ALCjackPlayback_lock(self);
device->UpdateSize = numframes;
device->NumUpdates = 2;
- TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
bufsize = device->UpdateSize;
- if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
+ if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
- bufsize += device->UpdateSize;
+ device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize;
+
+ TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
ll_ringbuffer_free(self->Ring);
- self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
+ self->Ring = ll_ringbuffer_create(bufsize,
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
+ true
+ );
if(!self->Ring)
{
ERR("Failed to reallocate ringbuffer\n");
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to reallocate %u-sample buffer", bufsize);
}
ALCjackPlayback_unlock(self);
return 0;
@@ -230,7 +255,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
ll_ringbuffer_data_t data[2];
jack_nframes_t total = 0;
jack_nframes_t todo;
- ALuint i, c, numchans;
+ ALsizei i, c, numchans;
ll_ringbuffer_get_read_vector(self->Ring, data);
@@ -241,8 +266,9 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
todo = minu(numframes, data[0].len);
for(c = 0;c < numchans;c++)
{
- for(i = 0;i < todo;i++)
- out[c][i] = ((ALfloat*)data[0].buf)[i*numchans + c];
+ const ALfloat *restrict in = ((ALfloat*)data[0].buf) + c;
+ for(i = 0;(jack_nframes_t)i < todo;i++)
+ out[c][i] = in[i*numchans];
out[c] += todo;
}
total += todo;
@@ -252,22 +278,23 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
{
for(c = 0;c < numchans;c++)
{
- for(i = 0;i < todo;i++)
- out[c][i] = ((ALfloat*)data[1].buf)[i*numchans + c];
+ const ALfloat *restrict in = ((ALfloat*)data[1].buf) + c;
+ for(i = 0;(jack_nframes_t)i < todo;i++)
+ out[c][i] = in[i*numchans];
out[c] += todo;
}
total += todo;
}
ll_ringbuffer_read_advance(self->Ring, total);
- alcnd_signal(&self->Cond);
+ alsem_post(&self->Sem);
if(numframes > total)
{
todo = numframes-total;
for(c = 0;c < numchans;c++)
{
- for(i = 0;i < todo;i++)
+ for(i = 0;(jack_nframes_t)i < todo;i++)
out[c][i] = 0.0f;
}
}
@@ -285,27 +312,16 @@ static int ALCjackPlayback_mixerProc(void *arg)
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
ALCjackPlayback_lock(self);
- while(!self->killNow && device->Connected)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
ALuint todo, len1, len2;
- /* NOTE: Unfortunately, there is an unavoidable race condition here.
- * It's possible for the process() method to run, updating the read
- * pointer and signaling the condition variable, in between the mixer
- * loop checking the write size and waiting for the condition variable.
- * This will cause the mixer loop to wait until the *next* process()
- * invocation, most likely writing silence for it.
- *
- * However, this should only happen if the mixer is running behind
- * anyway (as ideally we'll be asleep in alcnd_wait by the time the
- * process() method is invoked), so this behavior is not unwarranted.
- * It's unfortunate since it'll be wasting time sleeping that could be
- * used to catch up, but there's no way around it without blocking in
- * the process() method.
- */
if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
{
- alcnd_wait(&self->Cond, &STATIC_CAST(ALCbackend,self)->mMutex);
+ ALCjackPlayback_unlock(self);
+ alsem_wait(&self->Sem);
+ ALCjackPlayback_lock(self);
continue;
}
@@ -355,29 +371,15 @@ static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCjackPlayback_close(ALCjackPlayback *self)
-{
- ALuint i;
-
- for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
- {
- if(self->Port[i])
- jack_port_unregister(self->Client, self->Port[i]);
- self->Port[i] = NULL;
- }
- jack_client_close(self->Client);
- self->Client = NULL;
-}
-
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- ALuint numchans, i;
+ ALsizei numchans, i;
ALuint bufsize;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
@@ -388,23 +390,21 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
}
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
- * ready for when requested. Note that one period's worth of audio in the
- * ring buffer will always be left unfilled because one element of the ring
- * buffer will not be writeable, and we only write in period-sized chunks.
+ * ready for when requested.
*/
device->Frequency = jack_get_sample_rate(self->Client);
device->UpdateSize = jack_get_buffer_size(self->Client);
device->NumUpdates = 2;
bufsize = device->UpdateSize;
- if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
+ if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
- bufsize += device->UpdateSize;
+ device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize;
/* Force 32-bit float output. */
device->FmtType = DevFmtFloat;
- numchans = ChannelsFromDevFmt(device->FmtChans);
+ numchans = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
for(i = 0;i < numchans;i++)
{
char name[64];
@@ -433,7 +433,10 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
}
ll_ringbuffer_free(self->Ring);
- self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
+ self->Ring = ll_ringbuffer_create(bufsize,
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
+ true
+ );
if(!self->Ring)
{
ERR("Failed to allocate ringbuffer\n");
@@ -448,7 +451,7 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
{
const char **ports;
- ALuint i;
+ ALsizei i;
if(jack_activate(self->Client))
{
@@ -475,7 +478,7 @@ static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
}
jack_free(ports);
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success)
{
jack_deactivate(self->Client);
@@ -489,47 +492,36 @@ static void ALCjackPlayback_stop(ALCjackPlayback *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
- self->killNow = 1;
- /* Lock the backend to ensure we don't flag the mixer to die and signal the
- * mixer to wake up in between it checking the flag and going to sleep and
- * wait for a wakeup (potentially leading to it never waking back up to see
- * the flag). */
- ALCjackPlayback_lock(self);
- ALCjackPlayback_unlock(self);
- alcnd_signal(&self->Cond);
+ alsem_post(&self->Sem);
althrd_join(self->thread, &res);
jack_deactivate(self->Client);
}
-static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self)
+static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- ALint64 latency;
+ ClockLatency ret;
ALCjackPlayback_lock(self);
- latency = ll_ringbuffer_read_space(self->Ring);
+ ret.ClockTime = GetDeviceClockTime(device);
+ ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
+ device->Frequency;
ALCjackPlayback_unlock(self);
- return latency * 1000000000 / device->Frequency;
+ return ret;
}
-static void ALCjackPlayback_lock(ALCjackPlayback *self)
+static void jack_msg_handler(const char *message)
{
- almtx_lock(&STATIC_CAST(ALCbackend,self)->mMutex);
+ WARN("%s\n", message);
}
-static void ALCjackPlayback_unlock(ALCjackPlayback *self)
-{
- almtx_unlock(&STATIC_CAST(ALCbackend,self)->mMutex);
-}
-
-
typedef struct ALCjackBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCjackBackendFactory;
@@ -537,6 +529,7 @@ typedef struct ALCjackBackendFactory {
static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
{
+ void (*old_error_cb)(const char*);
jack_client_t *client;
jack_status_t status;
@@ -545,7 +538,11 @@ static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self)
if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
ClientOptions |= JackNoStartServer;
+
+ old_error_cb = (&jack_error_callback ? jack_error_callback : NULL);
+ jack_set_error_function(jack_msg_handler);
client = jack_client_open("alsoft", ClientOptions, &status, NULL);
+ jack_set_error_function(old_error_cb);
if(client == NULL)
{
WARN("jack_client_open() failed, 0x%02x\n", status);
@@ -574,12 +571,12 @@ static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(jackDevice);
+ alstr_append_range(outnames, jackDevice, jackDevice+sizeof(jackDevice));
break;
case CAPTURE_DEVICE_PROBE:
diff --git a/Alc/backends/loopback.c b/Alc/backends/loopback.c
index 3e577f78..e9940086 100644
--- a/Alc/backends/loopback.c
+++ b/Alc/backends/loopback.c
@@ -35,13 +35,12 @@ typedef struct ALCloopback {
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
-static void ALCloopback_close(ALCloopback *self);
static ALCboolean ALCloopback_reset(ALCloopback *self);
static ALCboolean ALCloopback_start(ALCloopback *self);
static void ALCloopback_stop(ALCloopback *self);
static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
@@ -59,14 +58,10 @@ static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCloopback_close(ALCloopback* UNUSED(self))
-{
-}
-
static ALCboolean ALCloopback_reset(ALCloopback *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
@@ -92,7 +87,7 @@ ALCbackendFactory *ALCloopbackFactory_getFactory(void);
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
-static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
+static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
@@ -115,7 +110,7 @@ static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(sel
return ALC_FALSE;
}
-static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
+static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
{
}
diff --git a/Alc/backends/null.c b/Alc/backends/null.c
index 99729c0a..d1c110e8 100644
--- a/Alc/backends/null.c
+++ b/Alc/backends/null.c
@@ -36,7 +36,7 @@
typedef struct ALCnullBackend {
DERIVE_FROM_TYPE(ALCbackend);
- volatile int killNow;
+ ATOMIC(int) killNow;
althrd_t thread;
} ALCnullBackend;
@@ -45,13 +45,12 @@ static int ALCnullBackend_mixerProc(void *ptr);
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
-static void ALCnullBackend_close(ALCnullBackend *self);
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
static void ALCnullBackend_stop(ALCnullBackend *self);
static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
@@ -66,6 +65,8 @@ static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
+
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
@@ -87,7 +88,8 @@ static int ALCnullBackend_mixerProc(void *ptr)
ERR("Failed to get starting time\n");
return 1;
}
- while(!self->killNow && device->Connected)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
@@ -109,7 +111,9 @@ static int ALCnullBackend_mixerProc(void *ptr)
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
+ ALCnullBackend_lock(self);
aluMixData(device, NULL, device->UpdateSize);
+ ALCnullBackend_unlock(self);
done += device->UpdateSize;
}
}
@@ -128,15 +132,11 @@ static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
return ALC_INVALID_VALUE;
device = STATIC_CAST(ALCbackend, self)->mDevice;
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
-{
-}
-
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
@@ -145,7 +145,7 @@ static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
{
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
@@ -155,10 +155,8 @@ static void ALCnullBackend_stop(ALCnullBackend *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
}
@@ -173,7 +171,7 @@ ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
-static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
@@ -197,14 +195,13 @@ static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(nullDevice);
- break;
case CAPTURE_DEVICE_PROBE:
+ alstr_append_range(outnames, nullDevice, nullDevice+sizeof(nullDevice));
break;
}
}
diff --git a/Alc/backends/opensl.c b/Alc/backends/opensl.c
index 7b8fdb25..d8ae001b 100644
--- a/Alc/backends/opensl.c
+++ b/Alc/backends/opensl.c
@@ -22,38 +22,25 @@
#include "config.h"
#include <stdlib.h>
+#include <jni.h>
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
+#include <SLES/OpenSLES_AndroidConfiguration.h>
/* Helper macros */
#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
-typedef struct {
- /* engine interfaces */
- SLObjectItf engineObject;
- SLEngineItf engine;
-
- /* output mix interfaces */
- SLObjectItf outputMix;
-
- /* buffer queue player interfaces */
- SLObjectItf bufferQueueObject;
-
- void *buffer;
- ALuint bufferSize;
- ALuint curBuffer;
-
- ALuint frameSize;
-} osl_data;
-
-
static const ALCchar opensl_device[] = "OpenSL";
@@ -79,10 +66,31 @@ static SLuint32 GetChannelMask(enum DevFmtChannels chans)
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
- case DevFmtBFormat3D: break;
+ case DevFmtAmbi3D:
+ break;
+ }
+ return 0;
+}
+
+#ifdef SL_DATAFORMAT_PCM_EX
+static SLuint32 GetTypeRepresentation(enum DevFmtType type)
+{
+ switch(type)
+ {
+ case DevFmtUByte:
+ case DevFmtUShort:
+ case DevFmtUInt:
+ return SL_PCM_REPRESENTATION_UNSIGNED_INT;
+ case DevFmtByte:
+ case DevFmtShort:
+ case DevFmtInt:
+ return SL_PCM_REPRESENTATION_SIGNED_INT;
+ case DevFmtFloat:
+ return SL_PCM_REPRESENTATION_FLOAT;
}
return 0;
}
+#endif
static const char *res_str(SLresult result)
{
@@ -123,311 +131,940 @@ static const char *res_str(SLresult result)
ERR("%s: %s\n", (s), res_str((x))); \
} while(0)
+
+typedef struct ALCopenslPlayback {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ /* engine interfaces */
+ SLObjectItf mEngineObj;
+ SLEngineItf mEngine;
+
+ /* output mix interfaces */
+ SLObjectItf mOutputMix;
+
+ /* buffer queue player interfaces */
+ SLObjectItf mBufferQueueObj;
+
+ ll_ringbuffer_t *mRing;
+ alsem_t mSem;
+
+ ALsizei mFrameSize;
+
+ ATOMIC(ALenum) mKillNow;
+ althrd_t mThread;
+} ALCopenslPlayback;
+
+static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf bq, void *context);
+static int ALCopenslPlayback_mixerProc(void *arg);
+
+static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device);
+static void ALCopenslPlayback_Destruct(ALCopenslPlayback *self);
+static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name);
+static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self);
+static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self);
+static void ALCopenslPlayback_stop(ALCopenslPlayback *self);
+static DECLARE_FORWARD2(ALCopenslPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self);
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCopenslPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCopenslPlayback);
+
+
+static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCopenslPlayback, ALCbackend, self);
+
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+ self->mOutputMix = NULL;
+ self->mBufferQueueObj = NULL;
+
+ self->mRing = NULL;
+ alsem_init(&self->mSem, 0);
+
+ self->mFrameSize = 0;
+
+ ATOMIC_INIT(&self->mKillNow, AL_FALSE);
+}
+
+static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self)
+{
+ if(self->mBufferQueueObj != NULL)
+ VCALL0(self->mBufferQueueObj,Destroy)();
+ self->mBufferQueueObj = NULL;
+
+ if(self->mOutputMix)
+ VCALL0(self->mOutputMix,Destroy)();
+ self->mOutputMix = NULL;
+
+ if(self->mEngineObj)
+ VCALL0(self->mEngineObj,Destroy)();
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+
+ ll_ringbuffer_free(self->mRing);
+ self->mRing = NULL;
+
+ alsem_destroy(&self->mSem);
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
/* this callback handler is called every time a buffer finishes playing */
-static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
+static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf UNUSED(bq), void *context)
+{
+ ALCopenslPlayback *self = context;
+
+ /* A note on the ringbuffer usage: The buffer queue seems to hold on to the
+ * pointer passed to the Enqueue method, rather than copying the audio.
+ * Consequently, the ringbuffer contains the audio that is currently queued
+ * and waiting to play. This process() callback is called when a buffer is
+ * finished, so we simply move the read pointer up to indicate the space is
+ * available for writing again, and wake up the mixer thread to mix and
+ * queue more audio.
+ */
+ ll_ringbuffer_read_advance(self->mRing, 1);
+
+ alsem_post(&self->mSem);
+}
+
+
+static int ALCopenslPlayback_mixerProc(void *arg)
{
- ALCdevice *Device = context;
- osl_data *data = Device->ExtraData;
- ALvoid *buf;
+ ALCopenslPlayback *self = arg;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ ll_ringbuffer_data_t data[2];
+ SLPlayItf player;
SLresult result;
- buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize;
- aluMixData(Device, buf, data->bufferSize/data->frameSize);
+ SetRTPriority();
+ althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- result = VCALL(bq,Enqueue)(buf, data->bufferSize);
- PRINTERR(result, "bq->Enqueue");
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
+ PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
+ }
- data->curBuffer = (data->curBuffer+1) % Device->NumUpdates;
+ ALCopenslPlayback_lock(self);
+ if(SL_RESULT_SUCCESS != result)
+ aluHandleDisconnect(device, "Failed to get playback buffer: 0x%08x", result);
+
+ while(SL_RESULT_SUCCESS == result &&
+ !ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
+ {
+ size_t todo;
+
+ if(ll_ringbuffer_write_space(self->mRing) == 0)
+ {
+ SLuint32 state = 0;
+
+ result = VCALL(player,GetPlayState)(&state);
+ PRINTERR(result, "player->GetPlayState");
+ if(SL_RESULT_SUCCESS == result && state != SL_PLAYSTATE_PLAYING)
+ {
+ result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
+ PRINTERR(result, "player->SetPlayState");
+ }
+ if(SL_RESULT_SUCCESS != result)
+ {
+ aluHandleDisconnect(device, "Failed to start platback: 0x%08x", result);
+ break;
+ }
+
+ if(ll_ringbuffer_write_space(self->mRing) == 0)
+ {
+ ALCopenslPlayback_unlock(self);
+ alsem_wait(&self->mSem);
+ ALCopenslPlayback_lock(self);
+ continue;
+ }
+ }
+
+ ll_ringbuffer_get_write_vector(self->mRing, data);
+
+ aluMixData(device, data[0].buf, data[0].len*device->UpdateSize);
+ if(data[1].len > 0)
+ aluMixData(device, data[1].buf, data[1].len*device->UpdateSize);
+
+ todo = data[0].len+data[1].len;
+ ll_ringbuffer_write_advance(self->mRing, todo);
+
+ for(size_t i = 0;i < todo;i++)
+ {
+ if(!data[0].len)
+ {
+ data[0] = data[1];
+ data[1].buf = NULL;
+ data[1].len = 0;
+ }
+
+ result = VCALL(bufferQueue,Enqueue)(data[0].buf, device->UpdateSize*self->mFrameSize);
+ PRINTERR(result, "bufferQueue->Enqueue");
+ if(SL_RESULT_SUCCESS != result)
+ {
+ aluHandleDisconnect(device, "Failed to queue audio: 0x%08x", result);
+ break;
+ }
+
+ data[0].len--;
+ data[0].buf += device->UpdateSize*self->mFrameSize;
+ }
+ }
+ ALCopenslPlayback_unlock(self);
+
+ return 0;
}
-static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName)
+static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name)
{
- osl_data *data = NULL;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
SLresult result;
- if(!deviceName)
- deviceName = opensl_device;
- else if(strcmp(deviceName, opensl_device) != 0)
+ if(!name)
+ name = opensl_device;
+ else if(strcmp(name, opensl_device) != 0)
return ALC_INVALID_VALUE;
- data = calloc(1, sizeof(*data));
- if(!data)
- return ALC_OUT_OF_MEMORY;
-
// create engine
- result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL);
+ result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL);
PRINTERR(result, "slCreateEngine");
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE);
+ result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "engine->Realize");
}
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine);
+ result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine);
PRINTERR(result, "engine->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL);
+ result = VCALL(self->mEngine,CreateOutputMix)(&self->mOutputMix, 0, NULL, NULL);
PRINTERR(result, "engine->CreateOutputMix");
}
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE);
+ result = VCALL(self->mOutputMix,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "outputMix->Realize");
}
if(SL_RESULT_SUCCESS != result)
{
- if(data->outputMix != NULL)
- VCALL0(data->outputMix,Destroy)();
- data->outputMix = NULL;
+ if(self->mOutputMix != NULL)
+ VCALL0(self->mOutputMix,Destroy)();
+ self->mOutputMix = NULL;
- if(data->engineObject != NULL)
- VCALL0(data->engineObject,Destroy)();
- data->engineObject = NULL;
- data->engine = NULL;
+ if(self->mEngineObj != NULL)
+ VCALL0(self->mEngineObj,Destroy)();
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
- free(data);
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&Device->DeviceName, deviceName);
- Device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-
-static void opensl_close_playback(ALCdevice *Device)
-{
- osl_data *data = Device->ExtraData;
-
- if(data->bufferQueueObject != NULL)
- VCALL0(data->bufferQueueObject,Destroy)();
- data->bufferQueueObject = NULL;
-
- VCALL0(data->outputMix,Destroy)();
- data->outputMix = NULL;
-
- VCALL0(data->engineObject,Destroy)();
- data->engineObject = NULL;
- data->engine = NULL;
-
- free(data);
- Device->ExtraData = NULL;
-}
-
-static ALCboolean opensl_reset_playback(ALCdevice *Device)
+static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
{
- osl_data *data = Device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
SLDataLocator_OutputMix loc_outmix;
- SLDataFormat_PCM format_pcm;
SLDataSource audioSrc;
SLDataSink audioSnk;
- SLInterfaceID id;
- SLboolean req;
+ ALuint sampleRate;
+ SLInterfaceID ids[2];
+ SLboolean reqs[2];
SLresult result;
+ if(self->mBufferQueueObj != NULL)
+ VCALL0(self->mBufferQueueObj,Destroy)();
+ self->mBufferQueueObj = NULL;
+
+ ll_ringbuffer_free(self->mRing);
+ self->mRing = NULL;
- Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency;
- Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2;
- Device->NumUpdates = 2;
+ sampleRate = device->Frequency;
+#if 0
+ if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
+ {
+ /* FIXME: Disabled until I figure out how to get the Context needed for
+ * the getSystemService call.
+ */
+ JNIEnv *env = Android_GetJNIEnv();
+ jobject jctx = Android_GetContext();
+
+ /* Get necessary stuff for using java.lang.Integer,
+ * android.content.Context, and android.media.AudioManager.
+ */
+ jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
+ jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
+ "parseInt", "(Ljava/lang/String;)I"
+ );
+ TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
+
+ jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
+ jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
+ "AUDIO_SERVICE", "Ljava/lang/String;"
+ );
+ jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
+ "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
+ );
+ TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
+ ctx_cls, ctx_audsvc, ctx_getSysSvc);
+
+ jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
+ jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
+ "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
+ );
+ jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
+ "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
+ );
+ TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
+ audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
+
+ const char *strchars;
+ jstring strobj;
+
+ /* Now make the calls. */
+ //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
+ strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
+ jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
+ strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
+ TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
+ JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
+
+ //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
+ strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
+ jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
+ strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
+ TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
+ JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
+
+ //int sampleRate = Integer.parseInt(srateStr);
+ sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
+
+ strchars = JCALL(env,GetStringUTFChars)(srateStr, NULL);
+ TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
+ JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
+
+ if(!sampleRate) sampleRate = device->Frequency;
+ else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
+ }
+#endif
- Device->Frequency = 44100;
- Device->FmtChans = DevFmtStereo;
- Device->FmtType = DevFmtShort;
+ if(sampleRate != device->Frequency)
+ {
+ device->NumUpdates = (device->NumUpdates*sampleRate + (device->Frequency>>1)) /
+ device->Frequency;
+ device->NumUpdates = maxu(device->NumUpdates, 2);
+ device->Frequency = sampleRate;
+ }
- SetDefaultWFXChannelOrder(Device);
+ device->FmtChans = DevFmtStereo;
+ device->FmtType = DevFmtShort;
+ SetDefaultWFXChannelOrder(device);
+ self->mFrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
- req = SL_BOOLEAN_TRUE;
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
- loc_bufq.numBuffers = Device->NumUpdates;
-
+ loc_bufq.numBuffers = device->NumUpdates;
+
+#ifdef SL_DATAFORMAT_PCM_EX
+ SLDataFormat_PCM_EX format_pcm;
+ format_pcm.formatType = SL_DATAFORMAT_PCM_EX;
+ format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ format_pcm.sampleRate = device->Frequency * 1000;
+ format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ format_pcm.containerSize = format_pcm.bitsPerSample;
+ format_pcm.channelMask = GetChannelMask(device->FmtChans);
+ format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+ SL_BYTEORDER_BIGENDIAN;
+ format_pcm.representation = GetTypeRepresentation(device->FmtType);
+#else
+ SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM;
- format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans);
- format_pcm.samplesPerSec = Device->Frequency * 1000;
- format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
+ format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ format_pcm.samplesPerSec = device->Frequency * 1000;
+ format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(Device->FmtChans);
+ format_pcm.channelMask = GetChannelMask(device->FmtChans);
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN;
+#endif
audioSrc.pLocator = &loc_bufq;
audioSrc.pFormat = &format_pcm;
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
- loc_outmix.outputMix = data->outputMix;
+ loc_outmix.outputMix = self->mOutputMix;
audioSnk.pLocator = &loc_outmix;
audioSnk.pFormat = NULL;
- if(data->bufferQueueObject != NULL)
- VCALL0(data->bufferQueueObject,Destroy)();
- data->bufferQueueObject = NULL;
+ ids[0] = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
+ reqs[0] = SL_BOOLEAN_TRUE;
+ ids[1] = SL_IID_ANDROIDCONFIGURATION;
+ reqs[1] = SL_BOOLEAN_FALSE;
- result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req);
+ result = VCALL(self->mEngine,CreateAudioPlayer)(&self->mBufferQueueObj,
+ &audioSrc, &audioSnk, COUNTOF(ids), ids, reqs
+ );
PRINTERR(result, "engine->CreateAudioPlayer");
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE);
+ /* Set the stream type to "media" (games, music, etc), if possible. */
+ SLAndroidConfigurationItf config;
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
+ PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDCONFIGURATION");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
+ result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE,
+ &streamType, sizeof(streamType)
+ );
+ PRINTERR(result, "config->SetConfiguration");
+ }
+
+ /* Clear any error since this was optional. */
+ result = SL_RESULT_SUCCESS;
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(self->mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "bufferQueue->Realize");
}
+ if(SL_RESULT_SUCCESS == result)
+ {
+ self->mRing = ll_ringbuffer_create(device->NumUpdates,
+ self->mFrameSize*device->UpdateSize, true
+ );
+ if(!self->mRing)
+ {
+ ERR("Out of memory allocating ring buffer %ux%u %u\n", device->UpdateSize,
+ device->NumUpdates, self->mFrameSize);
+ result = SL_RESULT_MEMORY_FAILURE;
+ }
+ }
if(SL_RESULT_SUCCESS != result)
{
- if(data->bufferQueueObject != NULL)
- VCALL0(data->bufferQueueObject,Destroy)();
- data->bufferQueueObject = NULL;
+ if(self->mBufferQueueObj != NULL)
+ VCALL0(self->mBufferQueueObj,Destroy)();
+ self->mBufferQueueObj = NULL;
+
+ return ALC_FALSE;
+ }
+
+ return ALC_TRUE;
+}
+
+static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
+{
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ SLresult result;
+
+ ll_ringbuffer_reset(self->mRing);
+
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "bufferQueue->GetInterface");
+ if(SL_RESULT_SUCCESS != result)
+ return ALC_FALSE;
+
+ result = VCALL(bufferQueue,RegisterCallback)(ALCopenslPlayback_process, self);
+ PRINTERR(result, "bufferQueue->RegisterCallback");
+ if(SL_RESULT_SUCCESS != result)
+ return ALC_FALSE;
+ ATOMIC_STORE_SEQ(&self->mKillNow, AL_FALSE);
+ if(althrd_create(&self->mThread, ALCopenslPlayback_mixerProc, self) != althrd_success)
+ {
+ ERR("Failed to start mixer thread\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
-static ALCboolean opensl_start_playback(ALCdevice *Device)
+
+static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
{
- osl_data *data = Device->ExtraData;
SLAndroidSimpleBufferQueueItf bufferQueue;
SLPlayItf player;
SLresult result;
- ALuint i;
+ int res;
+
+ if(ATOMIC_EXCHANGE_SEQ(&self->mKillNow, AL_TRUE))
+ return;
+
+ alsem_post(&self->mSem);
+ althrd_join(self->mThread, &res);
- result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device);
+ result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
+ PRINTERR(result, "player->SetPlayState");
+ }
+
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "bufferQueue->GetInterface");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL0(bufferQueue,Clear)();
+ PRINTERR(result, "bufferQueue->Clear");
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(bufferQueue,RegisterCallback)(NULL, NULL);
PRINTERR(result, "bufferQueue->RegisterCallback");
}
if(SL_RESULT_SUCCESS == result)
{
- data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
- data->bufferSize = Device->UpdateSize * data->frameSize;
- data->buffer = calloc(Device->NumUpdates, data->bufferSize);
- if(!data->buffer)
- {
- result = SL_RESULT_MEMORY_FAILURE;
- PRINTERR(result, "calloc");
- }
+ SLAndroidSimpleBufferQueueState state;
+ do {
+ althrd_yield();
+ result = VCALL(bufferQueue,GetState)(&state);
+ } while(SL_RESULT_SUCCESS == result && state.count > 0);
+ PRINTERR(result, "bufferQueue->GetState");
+ }
+}
+
+static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ClockLatency ret;
+
+ ALCopenslPlayback_lock(self);
+ ret.ClockTime = GetDeviceClockTime(device);
+ ret.Latency = ll_ringbuffer_read_space(self->mRing)*device->UpdateSize *
+ DEVICE_CLOCK_RES / device->Frequency;
+ ALCopenslPlayback_unlock(self);
+
+ return ret;
+}
+
+
+typedef struct ALCopenslCapture {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ /* engine interfaces */
+ SLObjectItf mEngineObj;
+ SLEngineItf mEngine;
+
+ /* recording interfaces */
+ SLObjectItf mRecordObj;
+
+ ll_ringbuffer_t *mRing;
+ ALCuint mSplOffset;
+
+ ALsizei mFrameSize;
+} ALCopenslCapture;
+
+static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf bq, void *context);
+
+static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device);
+static void ALCopenslCapture_Destruct(ALCopenslCapture *self);
+static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name);
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self);
+static void ALCopenslCapture_stop(ALCopenslCapture *self);
+static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self);
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCopenslCapture)
+DEFINE_ALCBACKEND_VTABLE(ALCopenslCapture);
+
+
+static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf UNUSED(bq), void *context)
+{
+ ALCopenslCapture *self = context;
+ /* A new chunk has been written into the ring buffer, advance it. */
+ ll_ringbuffer_write_advance(self->mRing, 1);
+}
+
+
+static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCopenslCapture, ALCbackend, self);
+
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+
+ self->mRecordObj = NULL;
+
+ self->mRing = NULL;
+ self->mSplOffset = 0;
+
+ self->mFrameSize = 0;
+}
+
+static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
+{
+ if(self->mRecordObj != NULL)
+ VCALL0(self->mRecordObj,Destroy)();
+ self->mRecordObj = NULL;
+
+ if(self->mEngineObj != NULL)
+ VCALL0(self->mEngineObj,Destroy)();
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+
+ ll_ringbuffer_free(self->mRing);
+ self->mRing = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ SLDataLocator_AndroidSimpleBufferQueue loc_bq;
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ SLDataLocator_IODevice loc_dev;
+ SLDataSource audioSrc;
+ SLDataSink audioSnk;
+ SLresult result;
+
+ if(!name)
+ name = opensl_device;
+ else if(strcmp(name, opensl_device) != 0)
+ return ALC_INVALID_VALUE;
+
+ result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL);
+ PRINTERR(result, "slCreateEngine");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE);
+ PRINTERR(result, "engine->Realize");
}
- /* enqueue the first buffer to kick off the callbacks */
- for(i = 0;i < Device->NumUpdates;i++)
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine);
+ PRINTERR(result, "engine->GetInterface");
+ }
+ if(SL_RESULT_SUCCESS == result)
{
+ /* Ensure the total length is at least 100ms */
+ ALsizei length = maxi(device->NumUpdates * device->UpdateSize,
+ device->Frequency / 10);
+ /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
+ ALsizei update_len = clampi(device->NumUpdates*device->UpdateSize / 3,
+ device->Frequency / 100,
+ device->Frequency / 100 * 5);
+
+ device->UpdateSize = update_len;
+ device->NumUpdates = (length+update_len-1) / update_len;
+
+ self->mFrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+ }
+ loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
+ loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
+ loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
+ loc_dev.device = NULL;
+
+ audioSrc.pLocator = &loc_dev;
+ audioSrc.pFormat = NULL;
+
+ loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
+ loc_bq.numBuffers = device->NumUpdates;
+
+#ifdef SL_DATAFORMAT_PCM_EX
+ SLDataFormat_PCM_EX format_pcm;
+ format_pcm.formatType = SL_DATAFORMAT_PCM_EX;
+ format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ format_pcm.sampleRate = device->Frequency * 1000;
+ format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ format_pcm.containerSize = format_pcm.bitsPerSample;
+ format_pcm.channelMask = GetChannelMask(device->FmtChans);
+ format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+ SL_BYTEORDER_BIGENDIAN;
+ format_pcm.representation = GetTypeRepresentation(device->FmtType);
+#else
+ SLDataFormat_PCM format_pcm;
+ format_pcm.formatType = SL_DATAFORMAT_PCM;
+ format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ format_pcm.samplesPerSec = device->Frequency * 1000;
+ format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ format_pcm.containerSize = format_pcm.bitsPerSample;
+ format_pcm.channelMask = GetChannelMask(device->FmtChans);
+ format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+ SL_BYTEORDER_BIGENDIAN;
+#endif
+
+ audioSnk.pLocator = &loc_bq;
+ audioSnk.pFormat = &format_pcm;
+
+ if(SL_RESULT_SUCCESS == result)
+ {
+ const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
+ const SLboolean reqs[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
+
+ result = VCALL(self->mEngine,CreateAudioRecorder)(&self->mRecordObj,
+ &audioSrc, &audioSnk, COUNTOF(ids), ids, reqs
+ );
+ PRINTERR(result, "engine->CreateAudioRecorder");
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ /* Set the record preset to "generic", if possible. */
+ SLAndroidConfigurationItf config;
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
+ PRINTERR(result, "recordObj->GetInterface SL_IID_ANDROIDCONFIGURATION");
if(SL_RESULT_SUCCESS == result)
{
- ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize;
- result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize);
- PRINTERR(result, "bufferQueue->Enqueue");
+ SLuint32 preset = SL_ANDROID_RECORDING_PRESET_GENERIC;
+ result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET,
+ &preset, sizeof(preset)
+ );
+ PRINTERR(result, "config->SetConfiguration");
}
+
+ /* Clear any error since this was optional. */
+ result = SL_RESULT_SUCCESS;
}
- data->curBuffer = 0;
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
- PRINTERR(result, "bufferQueue->GetInterface");
+ result = VCALL(self->mRecordObj,Realize)(SL_BOOLEAN_FALSE);
+ PRINTERR(result, "recordObj->Realize");
}
+
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
- PRINTERR(result, "player->SetPlayState");
+ self->mRing = ll_ringbuffer_create(device->NumUpdates,
+ device->UpdateSize*self->mFrameSize, false
+ );
+
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "recordObj->GetInterface");
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(bufferQueue,RegisterCallback)(ALCopenslCapture_process, self);
+ PRINTERR(result, "bufferQueue->RegisterCallback");
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ ALsizei chunk_size = device->UpdateSize * self->mFrameSize;
+ ll_ringbuffer_data_t data[2];
+ size_t i;
+
+ ll_ringbuffer_get_write_vector(self->mRing, data);
+ for(i = 0;i < data[0].len && SL_RESULT_SUCCESS == result;i++)
+ {
+ result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size);
+ PRINTERR(result, "bufferQueue->Enqueue");
+ }
+ for(i = 0;i < data[1].len && SL_RESULT_SUCCESS == result;i++)
+ {
+ result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size);
+ PRINTERR(result, "bufferQueue->Enqueue");
+ }
}
if(SL_RESULT_SUCCESS != result)
{
- if(data->bufferQueueObject != NULL)
- VCALL0(data->bufferQueueObject,Destroy)();
- data->bufferQueueObject = NULL;
+ if(self->mRecordObj != NULL)
+ VCALL0(self->mRecordObj,Destroy)();
+ self->mRecordObj = NULL;
+
+ if(self->mEngineObj != NULL)
+ VCALL0(self->mEngineObj,Destroy)();
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+
+ return ALC_INVALID_VALUE;
+ }
+
+ alstr_copy_cstr(&device->DeviceName, name);
+
+ return ALC_NO_ERROR;
+}
- free(data->buffer);
- data->buffer = NULL;
- data->bufferSize = 0;
+static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self)
+{
+ SLRecordItf record;
+ SLresult result;
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_RECORD, &record);
+ PRINTERR(result, "recordObj->GetInterface");
+
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(record,SetRecordState)(SL_RECORDSTATE_RECORDING);
+ PRINTERR(result, "record->SetRecordState");
+ }
+
+ if(SL_RESULT_SUCCESS != result)
+ {
+ ALCopenslCapture_lock(self);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
+ "Failed to start capture: 0x%08x", result);
+ ALCopenslCapture_unlock(self);
return ALC_FALSE;
}
return ALC_TRUE;
}
-
-static void opensl_stop_playback(ALCdevice *Device)
+static void ALCopenslCapture_stop(ALCopenslCapture *self)
{
- osl_data *data = Device->ExtraData;
- SLPlayItf player;
- SLAndroidSimpleBufferQueueItf bufferQueue;
+ SLRecordItf record;
SLresult result;
- result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
- PRINTERR(result, "bufferQueue->GetInterface");
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_RECORD, &record);
+ PRINTERR(result, "recordObj->GetInterface");
+
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
- PRINTERR(result, "player->SetPlayState");
+ result = VCALL(record,SetRecordState)(SL_RECORDSTATE_PAUSED);
+ PRINTERR(result, "record->SetRecordState");
}
+}
- result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
- PRINTERR(result, "bufferQueue->GetInterface");
- if(SL_RESULT_SUCCESS == result)
+static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ALsizei chunk_size = device->UpdateSize * self->mFrameSize;
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ ll_ringbuffer_data_t data[2];
+ SLresult result;
+ ALCuint i;
+
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "recordObj->GetInterface");
+
+ /* Read the desired samples from the ring buffer then advance its read
+ * pointer.
+ */
+ ll_ringbuffer_get_read_vector(self->mRing, data);
+ for(i = 0;i < samples;)
{
- result = VCALL0(bufferQueue,Clear)();
- PRINTERR(result, "bufferQueue->Clear");
+ ALCuint rem = minu(samples - i, device->UpdateSize - self->mSplOffset);
+ memcpy((ALCbyte*)buffer + i*self->mFrameSize,
+ data[0].buf + self->mSplOffset*self->mFrameSize,
+ rem * self->mFrameSize);
+
+ self->mSplOffset += rem;
+ if(self->mSplOffset == device->UpdateSize)
+ {
+ /* Finished a chunk, reset the offset and advance the read pointer. */
+ self->mSplOffset = 0;
+
+ ll_ringbuffer_read_advance(self->mRing, 1);
+ result = VCALL(bufferQueue,Enqueue)(data[0].buf, chunk_size);
+ PRINTERR(result, "bufferQueue->Enqueue");
+ if(SL_RESULT_SUCCESS != result) break;
+
+ data[0].len--;
+ if(!data[0].len)
+ data[0] = data[1];
+ else
+ data[0].buf += chunk_size;
+ }
+
+ i += rem;
}
- if(SL_RESULT_SUCCESS == result)
+
+ if(SL_RESULT_SUCCESS != result)
{
- SLAndroidSimpleBufferQueueState state;
- do {
- althrd_yield();
- result = VCALL(bufferQueue,GetState)(&state);
- } while(SL_RESULT_SUCCESS == result && state.count > 0);
- PRINTERR(result, "bufferQueue->GetState");
+ ALCopenslCapture_lock(self);
+ aluHandleDisconnect(device, "Failed to update capture buffer: 0x%08x", result);
+ ALCopenslCapture_unlock(self);
+ return ALC_INVALID_DEVICE;
}
- free(data->buffer);
- data->buffer = NULL;
- data->bufferSize = 0;
+ return ALC_NO_ERROR;
}
+static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return ll_ringbuffer_read_space(self->mRing) * device->UpdateSize;
+}
-static const BackendFuncs opensl_funcs = {
- opensl_open_playback,
- opensl_close_playback,
- opensl_reset_playback,
- opensl_start_playback,
- opensl_stop_playback,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
-};
+typedef struct ALCopenslBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCopenslBackendFactory;
+#define ALCOPENSLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCopenslBackendFactory, ALCbackendFactory) } }
-ALCboolean alc_opensl_init(BackendFuncs *func_list)
+static ALCboolean ALCopenslBackendFactory_init(ALCopenslBackendFactory* UNUSED(self))
{
- *func_list = opensl_funcs;
return ALC_TRUE;
}
-void alc_opensl_deinit(void)
+static void ALCopenslBackendFactory_deinit(ALCopenslBackendFactory* UNUSED(self))
+{
+}
+
+static ALCboolean ALCopenslBackendFactory_querySupport(ALCopenslBackendFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_opensl_probe(enum DevProbe type)
+static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(opensl_device);
- break;
case CAPTURE_DEVICE_PROBE:
+ alstr_append_range(outnames, opensl_device, opensl_device+sizeof(opensl_device));
break;
}
}
+
+static ALCbackend* ALCopenslBackendFactory_createBackend(ALCopenslBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCopenslPlayback *backend;
+ NEW_OBJ(backend, ALCopenslPlayback)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ ALCopenslCapture *backend;
+ NEW_OBJ(backend, ALCopenslCapture)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCopenslBackendFactory);
+
+
+ALCbackendFactory *ALCopenslBackendFactory_getFactory(void)
+{
+ static ALCopenslBackendFactory factory = ALCOPENSLBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
diff --git a/Alc/backends/oss.c b/Alc/backends/oss.c
index dce42e21..71faad25 100644
--- a/Alc/backends/oss.c
+++ b/Alc/backends/oss.c
@@ -22,10 +22,12 @@
#include <sys/ioctl.h>
#include <sys/types.h>
+#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
@@ -33,6 +35,8 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
@@ -51,11 +55,176 @@
#define SOUND_MIXER_WRITE MIXER_WRITE
#endif
+#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
+#define ALC_OSS_COMPAT
+#endif
+#ifndef SNDCTL_AUDIOINFO
+#define ALC_OSS_COMPAT
+#endif
+
+/*
+ * FreeBSD strongly discourages the use of specific devices,
+ * such as those returned in oss_audioinfo.devnode
+ */
+#ifdef __FreeBSD__
+#define ALC_OSS_DEVNODE_TRUC
+#endif
+
+struct oss_device {
+ const ALCchar *handle;
+ const char *path;
+ struct oss_device *next;
+};
+
+static struct oss_device oss_playback = {
+ "OSS Default",
+ "/dev/dsp",
+ NULL
+};
+
+static struct oss_device oss_capture = {
+ "OSS Default",
+ "/dev/dsp",
+ NULL
+};
+
+#ifdef ALC_OSS_COMPAT
+
+#define DSP_CAP_OUTPUT 0x00020000
+#define DSP_CAP_INPUT 0x00010000
+static void ALCossListPopulate(struct oss_device *UNUSED(devlist), int UNUSED(type_flag))
+{
+}
+
+#else
+
+#ifndef HAVE_STRNLEN
+static size_t strnlen(const char *str, size_t maxlen)
+{
+ const char *end = memchr(str, 0, maxlen);
+ if(!end) return maxlen;
+ return end - str;
+}
+#endif
+
+static void ALCossListAppend(struct oss_device *list, const char *handle, size_t hlen, const char *path, size_t plen)
+{
+ struct oss_device *next;
+ struct oss_device *last;
+ size_t i;
+
+ /* skip the first item "OSS Default" */
+ last = list;
+ next = list->next;
+#ifdef ALC_OSS_DEVNODE_TRUC
+ for(i = 0;i < plen;i++)
+ {
+ if(path[i] == '.')
+ {
+ if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
+ hlen = hlen + i - plen;
+ plen = i;
+ }
+ }
+#else
+ (void)i;
+#endif
+ if(handle[0] == '\0')
+ {
+ handle = path;
+ hlen = plen;
+ }
+
+ while(next != NULL)
+ {
+ if(strncmp(next->path, path, plen) == 0)
+ return;
+ last = next;
+ next = next->next;
+ }
+
+ next = (struct oss_device*)malloc(sizeof(struct oss_device) + hlen + plen + 2);
+ next->handle = (char*)(next + 1);
+ next->path = next->handle + hlen + 1;
+ next->next = NULL;
+ last->next = next;
+
+ strncpy((char*)next->handle, handle, hlen);
+ ((char*)next->handle)[hlen] = '\0';
+ strncpy((char*)next->path, path, plen);
+ ((char*)next->path)[plen] = '\0';
-static const ALCchar oss_device[] = "OSS Default";
+ TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path);
+}
+
+static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
+{
+ struct oss_sysinfo si;
+ struct oss_audioinfo ai;
+ int fd, i;
+
+ if((fd=open("/dev/mixer", O_RDONLY)) < 0)
+ {
+ TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
+ return;
+ }
+ if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
+ {
+ TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
+ goto done;
+ }
+ for(i = 0;i < si.numaudios;i++)
+ {
+ const char *handle;
+ size_t len;
-static const char *oss_driver = "/dev/dsp";
-static const char *oss_capture = "/dev/dsp";
+ ai.dev = i;
+ if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
+ {
+ ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
+ continue;
+ }
+ if(ai.devnode[0] == '\0')
+ continue;
+
+ if(ai.handle[0] != '\0')
+ {
+ len = strnlen(ai.handle, sizeof(ai.handle));
+ handle = ai.handle;
+ }
+ else
+ {
+ len = strnlen(ai.name, sizeof(ai.name));
+ handle = ai.name;
+ }
+ if((ai.caps&type_flag))
+ ALCossListAppend(devlist, handle, len, ai.devnode,
+ strnlen(ai.devnode, sizeof(ai.devnode)));
+ }
+
+done:
+ close(fd);
+}
+
+#endif
+
+static void ALCossListFree(struct oss_device *list)
+{
+ struct oss_device *cur;
+ if(list == NULL)
+ return;
+
+ /* skip the first item "OSS Default" */
+ cur = list->next;
+ list->next = NULL;
+
+ while(cur != NULL)
+ {
+ struct oss_device *next = cur->next;
+ free(cur);
+ cur = next;
+ }
+}
static int log2i(ALCuint x)
{
@@ -68,7 +237,6 @@ static int log2i(ALCuint x)
return y;
}
-
typedef struct ALCplaybackOSS {
DERIVE_FROM_TYPE(ALCbackend);
@@ -77,22 +245,21 @@ typedef struct ALCplaybackOSS {
ALubyte *mix_data;
int data_size;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackOSS;
static int ALCplaybackOSS_mixerProc(void *ptr);
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
+static void ALCplaybackOSS_Destruct(ALCplaybackOSS *self);
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
-static void ALCplaybackOSS_close(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
@@ -103,42 +270,66 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
{
ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- ALint frameSize;
+ struct timeval timeout;
+ ALubyte *write_ptr;
+ ALint frame_size;
+ ALint to_write;
ssize_t wrote;
+ fd_set wfds;
+ int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- while(!self->killNow && device->Connected)
+ ALCplaybackOSS_lock(self);
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
- ALint len = self->data_size;
- ALubyte *WritePtr = self->mix_data;
+ FD_ZERO(&wfds);
+ FD_SET(self->fd, &wfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ ALCplaybackOSS_unlock(self);
+ sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
+ ALCplaybackOSS_lock(self);
+ if(sret < 0)
+ {
+ if(errno == EINTR)
+ continue;
+ ERR("select failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
+ break;
+ }
+ else if(sret == 0)
+ {
+ WARN("select timeout\n");
+ continue;
+ }
- aluMixData(device, WritePtr, len/frameSize);
- while(len > 0 && !self->killNow)
+ write_ptr = self->mix_data;
+ to_write = self->data_size;
+ aluMixData(device, write_ptr, to_write/frame_size);
+ while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
{
- wrote = write(self->fd, WritePtr, len);
+ wrote = write(self->fd, write_ptr, to_write);
if(wrote < 0)
{
- if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
- {
- ERR("write failed: %s\n", strerror(errno));
- ALCplaybackOSS_lock(self);
- aluHandleDisconnect(device);
- ALCplaybackOSS_unlock(self);
- break;
- }
-
- al_nssleep(1000000);
- continue;
+ if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ ERR("write failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed writing playback samples: %s",
+ strerror(errno));
+ break;
}
- len -= wrote;
- WritePtr += wrote;
+ to_write -= wrote;
+ write_ptr += wrote;
}
}
+ ALCplaybackOSS_unlock(self);
return 0;
}
@@ -148,37 +339,59 @@ static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
+
+ self->fd = -1;
+ ATOMIC_INIT(&self->killNow, AL_FALSE);
+}
+
+static void ALCplaybackOSS_Destruct(ALCplaybackOSS *self)
+{
+ if(self->fd != -1)
+ close(self->fd);
+ self->fd = -1;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
{
+ struct oss_device *dev = &oss_playback;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- if(!name)
- name = oss_device;
- else if(strcmp(name, oss_device) != 0)
- return ALC_INVALID_VALUE;
-
- self->killNow = 0;
+ if(!name || strcmp(name, dev->handle) == 0)
+ name = dev->handle;
+ else
+ {
+ if(!dev->next)
+ {
+ ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
+ dev = &oss_playback;
+ }
+ while(dev != NULL)
+ {
+ if (strcmp(dev->handle, name) == 0)
+ break;
+ dev = dev->next;
+ }
+ if(dev == NULL)
+ {
+ WARN("Could not find \"%s\" in device list\n", name);
+ return ALC_INVALID_VALUE;
+ }
+ }
- self->fd = open(oss_driver, O_WRONLY);
+ self->fd = open(dev->path, O_WRONLY);
if(self->fd == -1)
{
- ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
+ ERR("Could not open %s: %s\n", dev->path, strerror(errno));
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCplaybackOSS_close(ALCplaybackOSS *self)
-{
- close(self->fd);
- self->fd = -1;
-}
-
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -212,18 +425,11 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
}
periods = device->NumUpdates;
- numChannels = ChannelsFromDevFmt(device->FmtChans);
- frameSize = numChannels * BytesFromDevFmt(device->FmtType);
-
+ numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
ossSpeed = device->Frequency;
- log2FragmentSize = log2i(device->UpdateSize * frameSize);
-
- /* according to the OSS spec, 16 bytes are the minimum */
- if (log2FragmentSize < 4)
- log2FragmentSize = 4;
- /* Subtract one period since the temp mixing buffer counts as one. Still
- * need at least two on the card, though. */
- if(periods > 2) periods--;
+ frameSize = numChannels * BytesFromDevFmt(device->FmtType);
+ /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
+ log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4);
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
@@ -245,7 +451,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
}
#undef CHECKERR
- if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
+ if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
return ALC_FALSE;
@@ -261,7 +467,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
device->Frequency = ossSpeed;
device->UpdateSize = info.fragsize / frameSize;
- device->NumUpdates = info.fragments + 1;
+ device->NumUpdates = info.fragments;
SetDefaultChannelOrder(device);
@@ -272,10 +478,12 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
self->mix_data = calloc(1, self->data_size);
- self->killNow = 0;
+ ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
{
free(self->mix_data);
@@ -290,10 +498,8 @@ static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
@@ -309,28 +515,23 @@ typedef struct ALCcaptureOSS {
int fd;
- ALubyte *read_data;
- int data_size;
-
- RingBuffer *ring;
- int doCapture;
+ ll_ringbuffer_t *ring;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCcaptureOSS;
static int ALCcaptureOSS_recordProc(void *ptr);
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
+static void ALCcaptureOSS_Destruct(ALCcaptureOSS *self);
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
-static void ALCcaptureOSS_close(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
-static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
@@ -341,32 +542,55 @@ static int ALCcaptureOSS_recordProc(void *ptr)
{
ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- int frameSize;
- int amt;
+ struct timeval timeout;
+ int frame_size;
+ fd_set rfds;
+ ssize_t amt;
+ int sret;
SetRTPriority();
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- while(!self->killNow)
+ while(!ATOMIC_LOAD_SEQ(&self->killNow))
{
- amt = read(self->fd, self->read_data, self->data_size);
- if(amt < 0)
+ ll_ringbuffer_data_t vec[2];
+
+ FD_ZERO(&rfds);
+ FD_SET(self->fd, &rfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ sret = select(self->fd+1, &rfds, NULL, NULL, &timeout);
+ if(sret < 0)
{
- ERR("read failed: %s\n", strerror(errno));
- ALCcaptureOSS_lock(self);
- aluHandleDisconnect(device);
- ALCcaptureOSS_unlock(self);
+ if(errno == EINTR)
+ continue;
+ ERR("select failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed to check capture samples: %s", strerror(errno));
break;
}
- if(amt == 0)
+ else if(sret == 0)
{
- al_nssleep(1000000);
+ WARN("select timeout\n");
continue;
}
- if(self->doCapture)
- WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
+
+ ll_ringbuffer_get_write_vector(self->ring, vec);
+ if(vec[0].len > 0)
+ {
+ amt = read(self->fd, vec[0].buf, vec[0].len*frame_size);
+ if(amt < 0)
+ {
+ ERR("read failed: %s\n", strerror(errno));
+ ALCcaptureOSS_lock(self);
+ aluHandleDisconnect(device, "Failed reading capture samples: %s", strerror(errno));
+ ALCcaptureOSS_unlock(self);
+ break;
+ }
+ ll_ringbuffer_write_advance(self->ring, amt/frame_size);
+ }
}
return 0;
@@ -377,11 +601,27 @@ static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
+
+ self->fd = -1;
+ self->ring = NULL;
+ ATOMIC_INIT(&self->killNow, AL_FALSE);
+}
+
+static void ALCcaptureOSS_Destruct(ALCcaptureOSS *self)
+{
+ if(self->fd != -1)
+ close(self->fd);
+ self->fd = -1;
+
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ struct oss_device *dev = &oss_capture;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
@@ -392,15 +632,32 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
int ossSpeed;
char *err;
- if(!name)
- name = oss_device;
- else if(strcmp(name, oss_device) != 0)
- return ALC_INVALID_VALUE;
+ if(!name || strcmp(name, dev->handle) == 0)
+ name = dev->handle;
+ else
+ {
+ if(!dev->next)
+ {
+ ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
+ dev = &oss_capture;
+ }
+ while(dev != NULL)
+ {
+ if (strcmp(dev->handle, name) == 0)
+ break;
+ dev = dev->next;
+ }
+ if(dev == NULL)
+ {
+ WARN("Could not find \"%s\" in device list\n", name);
+ return ALC_INVALID_VALUE;
+ }
+ }
- self->fd = open(oss_capture, O_RDONLY);
+ self->fd = open(dev->path, O_RDONLY);
if(self->fd == -1)
{
- ERR("Could not open %s: %s\n", oss_capture, strerror(errno));
+ ERR("Could not open %s: %s\n", dev->path, strerror(errno));
return ALC_INVALID_VALUE;
}
@@ -424,7 +681,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
}
periods = 4;
- numChannels = ChannelsFromDevFmt(device->FmtChans);
+ numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
@@ -454,7 +711,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
}
#undef CHECKERR
- if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
+ if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
close(self->fd);
@@ -472,7 +729,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
return ALC_INVALID_VALUE;
}
- self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
+ self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, frameSize, false);
if(!self->ring)
{
ERR("Ring buffer create failed\n");
@@ -481,60 +738,41 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
return ALC_OUT_OF_MEMORY;
}
- self->data_size = info.fragsize;
- self->read_data = calloc(1, self->data_size);
-
- self->killNow = 0;
- if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
- {
- device->ExtraData = NULL;
- close(self->fd);
- self->fd = -1;
- return ALC_OUT_OF_MEMORY;
- }
-
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCcaptureOSS_close(ALCcaptureOSS *self)
-{
- int res;
-
- self->killNow = 1;
- althrd_join(self->thread, &res);
-
- close(self->fd);
- self->fd = -1;
-
- DestroyRingBuffer(self->ring);
- self->ring = NULL;
-
- free(self->read_data);
- self->read_data = NULL;
-}
-
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
{
- self->doCapture = 1;
+ ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
+ if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
+ return ALC_FALSE;
return ALC_TRUE;
}
static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
{
- self->doCapture = 0;
+ int res;
+
+ if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
+ return;
+
+ althrd_join(self->thread, &res);
+
+ if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
+ ERR("Error resetting device: %s\n", strerror(errno));
}
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
{
- ReadRingBuffer(self->ring, buffer, samples);
+ ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
{
- return RingBufferSize(self->ring);
+ return ll_ringbuffer_read_space(self->ring);
}
@@ -546,9 +784,9 @@ typedef struct ALCossBackendFactory {
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
-static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit)
+static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
-static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
+static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
@@ -562,12 +800,19 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void)
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
{
- ConfigValueStr(NULL, "oss", "device", &oss_driver);
- ConfigValueStr(NULL, "oss", "capture", &oss_capture);
+ ConfigValueStr(NULL, "oss", "device", &oss_playback.path);
+ ConfigValueStr(NULL, "oss", "capture", &oss_capture.path);
return ALC_TRUE;
}
+void ALCossBackendFactory_deinit(ALCossBackendFactory* UNUSED(self))
+{
+ ALCossListFree(&oss_playback);
+ ALCossListFree(&oss_capture);
+}
+
+
ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
@@ -575,29 +820,31 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
return ALC_FALSE;
}
-void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
+void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
+ struct oss_device *cur = NULL;
switch(type)
{
case ALL_DEVICE_PROBE:
- {
-#ifdef HAVE_STAT
- struct stat buf;
- if(stat(oss_driver, &buf) == 0)
-#endif
- AppendAllDevicesList(oss_device);
- }
- break;
+ ALCossListFree(&oss_playback);
+ ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
+ cur = &oss_playback;
+ break;
case CAPTURE_DEVICE_PROBE:
- {
+ ALCossListFree(&oss_capture);
+ ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
+ cur = &oss_capture;
+ break;
+ }
+ while(cur != NULL)
+ {
#ifdef HAVE_STAT
- struct stat buf;
- if(stat(oss_capture, &buf) == 0)
+ struct stat buf;
+ if(stat(cur->path, &buf) == 0)
#endif
- AppendCaptureDeviceList(oss_device);
- }
- break;
+ alstr_append_range(outnames, cur->handle, cur->handle+strlen(cur->handle)+1);
+ cur = cur->next;
}
}
diff --git a/Alc/backends/portaudio.c b/Alc/backends/portaudio.c
index f45833c6..6a6cfa31 100644
--- a/Alc/backends/portaudio.c
+++ b/Alc/backends/portaudio.c
@@ -26,6 +26,8 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
#include "compat.h"
#include "backends/base.h"
@@ -139,13 +141,12 @@ static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBu
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
static void ALCportPlayback_Destruct(ALCportPlayback *self);
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
-static void ALCportPlayback_close(ALCportPlayback *self);
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
static void ALCportPlayback_stop(ALCportPlayback *self);
static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
@@ -163,8 +164,9 @@ static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
static void ALCportPlayback_Destruct(ALCportPlayback *self)
{
- if(self->stream)
- Pa_CloseStream(self->stream);
+ PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
+ if(err != paNoError)
+ ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
self->stream = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
@@ -177,7 +179,9 @@ static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *
{
ALCportPlayback *self = userData;
+ ALCportPlayback_lock(self);
aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
+ ALCportPlayback_unlock(self);
return 0;
}
@@ -243,20 +247,12 @@ retry_open:
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCportPlayback_close(ALCportPlayback *self)
-{
- PaError err = Pa_CloseStream(self->stream);
- if(err != paNoError)
- ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
- self->stream = NULL;
-}
-
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -334,13 +330,12 @@ static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuff
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
static void ALCportCapture_Destruct(ALCportCapture *self);
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
-static void ALCportCapture_close(ALCportCapture *self);
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCportCapture_start(ALCportCapture *self);
static void ALCportCapture_stop(ALCportCapture *self);
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
-static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
@@ -354,16 +349,17 @@ static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
SET_VTABLE2(ALCportCapture, ALCbackend, self);
self->stream = NULL;
+ self->ring = NULL;
}
static void ALCportCapture_Destruct(ALCportCapture *self)
{
- if(self->stream)
- Pa_CloseStream(self->stream);
+ PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
+ if(err != paNoError)
+ ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
self->stream = NULL;
- if(self->ring)
- ll_ringbuffer_free(self->ring);
+ ll_ringbuffer_free(self->ring);
self->ring = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
@@ -397,9 +393,9 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
- frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- self->ring = ll_ringbuffer_create(samples, frame_size);
+ self->ring = ll_ringbuffer_create(samples, frame_size, false);
if(self->ring == NULL) return ALC_INVALID_VALUE;
self->params.device = -1;
@@ -431,7 +427,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
- self->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
+ self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
err = Pa_OpenStream(&self->stream, &self->params, NULL,
device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
@@ -443,22 +439,11 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCportCapture_close(ALCportCapture *self)
-{
- PaError err = Pa_CloseStream(self->stream);
- if(err != paNoError)
- ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
- self->stream = NULL;
-
- ll_ringbuffer_free(self->ring);
- self->ring = NULL;
-}
-
static ALCboolean ALCportCapture_start(ALCportCapture *self)
{
@@ -499,9 +484,8 @@ typedef struct ALCportBackendFactory {
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
-static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
+static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
@@ -533,15 +517,13 @@ static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(pa_device);
- break;
case CAPTURE_DEVICE_PROBE:
- AppendCaptureDeviceList(pa_device);
+ alstr_append_range(outnames, pa_device, pa_device+sizeof(pa_device));
break;
}
}
diff --git a/Alc/backends/pulseaudio.c b/Alc/backends/pulseaudio.c
index 9ad04a71..b34d7abc 100644
--- a/Alc/backends/pulseaudio.c
+++ b/Alc/backends/pulseaudio.c
@@ -25,6 +25,7 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
#include "threads.h"
#include "compat.h"
@@ -182,6 +183,8 @@ static ALCboolean pulse_load(void)
#ifdef HAVE_DYNLOAD
if(!pa_handle)
{
+ al_string missing_funcs = AL_STRING_INIT_STATIC();
+
#ifdef _WIN32
#define PALIB "libpulse-0.dll"
#elif defined(__APPLE__) && defined(__MACH__)
@@ -191,12 +194,16 @@ static ALCboolean pulse_load(void)
#endif
pa_handle = LoadLib(PALIB);
if(!pa_handle)
+ {
+ WARN("Failed to load %s\n", PALIB);
return ALC_FALSE;
+ }
#define LOAD_FUNC(x) do { \
p##x = GetSymbol(pa_handle, #x); \
if(!(p##x)) { \
ret = ALC_FALSE; \
+ alstr_append_cstr(&missing_funcs, "\n" #x); \
} \
} while(0)
LOAD_FUNC(pa_context_unref);
@@ -270,9 +277,11 @@ static ALCboolean pulse_load(void)
if(ret == ALC_FALSE)
{
+ WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(pa_handle);
pa_handle = NULL;
}
+ alstr_reset(&missing_funcs);
}
#endif /* HAVE_DYNLOAD */
return ret;
@@ -325,18 +334,20 @@ static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop)
static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
{
const char *name = "OpenAL Soft";
- char path_name[PATH_MAX];
+ al_string binname = AL_STRING_INIT_STATIC();
pa_context_state_t state;
pa_context *context;
int err;
- if(pa_get_binary_name(path_name, sizeof(path_name)))
- name = pa_path_get_filename(path_name);
+ GetProcBinary(NULL, &binname);
+ if(!alstr_empty(binname))
+ name = alstr_get_cstr(binname);
context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);
if(!context)
{
ERR("pa_context_new() failed\n");
+ alstr_reset(&binname);
return NULL;
}
@@ -363,9 +374,10 @@ static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
if(!silent)
ERR("Context did not connect: %s\n", pa_strerror(err));
pa_context_unref(context);
- return NULL;
+ context = NULL;
}
+ alstr_reset(&binname);
return context;
}
@@ -443,7 +455,7 @@ static void clear_devlist(vector_DevMap *list)
#define DEINIT_STRS(i) (AL_STRING_DEINIT((i)->name),AL_STRING_DEINIT((i)->device_name))
VECTOR_FOR_EACH(DevMap, *list, DEINIT_STRS);
#undef DEINIT_STRS
- VECTOR_RESIZE(*list, 0);
+ VECTOR_RESIZE(*list, 0, 0);
}
@@ -460,7 +472,7 @@ typedef struct ALCpulsePlayback {
pa_stream *stream;
pa_context *context;
- volatile ALboolean killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCpulsePlayback;
@@ -483,13 +495,12 @@ static int ALCpulsePlayback_mixerProc(void *ptr);
static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device);
static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self);
static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name);
-static void ALCpulsePlayback_close(ALCpulsePlayback *self);
static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self);
static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self);
static void ALCpulsePlayback_stop(ALCpulsePlayback *self);
static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self);
+static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self);
static void ALCpulsePlayback_lock(ALCpulsePlayback *self);
static void ALCpulsePlayback_unlock(ALCpulsePlayback *self);
DECLARE_DEFAULT_ALLOCATORS(ALCpulsePlayback)
@@ -502,11 +513,20 @@ static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCpulsePlayback, ALCbackend, self);
+ self->loop = NULL;
AL_STRING_INIT(self->device_name);
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self)
{
+ if(self->loop)
+ {
+ pulse_close(self->loop, self->context, self->stream);
+ self->loop = NULL;
+ self->context = NULL;
+ self->stream = NULL;
+ }
AL_STRING_DEINIT(self->device_name);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -525,35 +545,35 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p
return;
}
-#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
+#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_INFO_NAME);
- if(iter != VECTOR_ITER_END(PlaybackDevices)) return;
+ if(iter != VECTOR_END(PlaybackDevices)) return;
#undef MATCH_INFO_NAME
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
- al_string_copy_cstr(&entry.device_name, info->name);
+ alstr_copy_cstr(&entry.device_name, info->name);
count = 0;
while(1)
{
- al_string_copy_cstr(&entry.name, info->description);
+ alstr_copy_cstr(&entry.name, info->description);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
- al_string_append_cstr(&entry.name, str);
+ alstr_append_cstr(&entry.name, str);
}
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
+ if(iter == VECTOR_END(PlaybackDevices)) break;
#undef MATCH_ENTRY
count++;
}
- TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
+ TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name));
VECTOR_PUSH_BACK(PlaybackDevices, entry);
}
@@ -618,6 +638,11 @@ static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata)
self->attr = *pa_stream_get_buffer_attr(stream);
TRACE("minreq=%d, tlength=%d, prebuf=%d\n", self->attr.minreq, self->attr.tlength, self->attr.prebuf);
+ /* FIXME: Update the device's UpdateSize (and/or NumUpdates) using the new
+ * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
+ * property, which probably shouldn't change between device resets. But
+ * leaving it alone means ALC_REFRESH will be off.
+ */
}
static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata)
@@ -626,7 +651,7 @@ static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pda
if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
{
ERR("Received context failure!\n");
- aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Playback state failure");
}
pa_threaded_mainloop_signal(self->loop, 0);
}
@@ -637,7 +662,7 @@ static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata)
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
{
ERR("Received stream failure!\n");
- aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Playback stream failure");
}
pa_threaded_mainloop_signal(self->loop, 0);
}
@@ -729,7 +754,7 @@ static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const
return;
}
- al_string_copy_cstr(&device->DeviceName, info->description);
+ alstr_copy_cstr(&device->DeviceName, info->description);
}
@@ -737,9 +762,9 @@ static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata)
{
ALCpulsePlayback *self = pdata;
- al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
+ alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
- TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
+ TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name));
}
@@ -751,6 +776,13 @@ static pa_stream *ALCpulsePlayback_connectStream(const char *device_name,
pa_stream_state_t state;
pa_stream *stream;
+ if(!device_name)
+ {
+ device_name = getenv("ALSOFT_PULSE_DEFAULT");
+ if(device_name && !device_name[0])
+ device_name = NULL;
+ }
+
stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter);
if(!stream)
{
@@ -789,7 +821,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
ALCpulsePlayback *self = ptr;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ALuint buffer_size;
- ALint update_size;
size_t frame_size;
ssize_t len;
@@ -798,18 +829,35 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
pa_threaded_mainloop_lock(self->loop);
frame_size = pa_frame_size(&self->spec);
- update_size = device->UpdateSize * frame_size;
-
- /* Sanitize buffer metrics, in case we actually have less than what we
- * asked for. */
- buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength);
- update_size = minu(update_size, buffer_size/2);
- do {
- len = pa_stream_writable_size(self->stream) - self->attr.tlength +
- buffer_size;
- if(len < update_size)
+
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
+ {
+ void *buf;
+ int ret;
+
+ len = pa_stream_writable_size(self->stream);
+ if(len < 0)
+ {
+ ERR("Failed to get writable size: %ld", (long)len);
+ aluHandleDisconnect(device, "Failed to get writable size: %ld", (long)len);
+ break;
+ }
+
+ /* Make sure we're going to write at least 2 'periods' (minreqs), in
+ * case the server increased it since starting playback. Also round up
+ * the number of writable periods if it's not an integer count.
+ */
+ buffer_size = maxu((self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2) *
+ self->attr.minreq;
+
+ /* NOTE: This assumes pa_stream_writable_size returns between 0 and
+ * tlength, else there will be more latency than intended.
+ */
+ len = mini(len - (ssize_t)self->attr.tlength, 0) + buffer_size;
+ if(len < (int32_t)self->attr.minreq)
{
- if(pa_stream_is_corked(self->stream) == 1)
+ if(pa_stream_is_corked(self->stream))
{
pa_operation *o;
o = pa_stream_cork(self->stream, 0, NULL, NULL);
@@ -818,26 +866,17 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
pa_threaded_mainloop_wait(self->loop);
continue;
}
- len -= len%update_size;
- while(len > 0)
- {
- size_t newlen = len;
- void *buf;
- pa_free_cb_t free_func = NULL;
+ len -= len%self->attr.minreq;
+ len -= len%frame_size;
- if(pa_stream_begin_write(self->stream, &buf, &newlen) < 0)
- {
- buf = pa_xmalloc(newlen);
- free_func = pa_xfree;
- }
+ buf = pa_xmalloc(len);
- aluMixData(device, buf, newlen/frame_size);
+ aluMixData(device, buf, len/frame_size);
- pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
- len -= newlen;
- }
- } while(!self->killNow && device->Connected);
+ ret = pa_stream_write(self->stream, buf, len, pa_xfree, 0, PA_SEEK_RELATIVE);
+ if(ret != PA_OK) ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
+ }
pa_threaded_mainloop_unlock(self->loop);
return 0;
@@ -858,12 +897,12 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
if(VECTOR_SIZE(PlaybackDevices) == 0)
ALCpulsePlayback_probeDevices();
-#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
+#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+ if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
- pulse_name = al_string_get_cstr(iter->device_name);
+ pulse_name = alstr_get_cstr(iter->device_name);
dev_name = iter->name;
}
@@ -894,11 +933,11 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
}
pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
- al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
- if(al_string_empty(dev_name))
+ alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
+ if(alstr_empty(dev_name))
{
pa_operation *o = pa_context_get_sink_info_by_name(
- self->context, al_string_get_cstr(self->device_name),
+ self->context, alstr_get_cstr(self->device_name),
ALCpulsePlayback_sinkNameCallback, self
);
wait_for_operation(o, self->loop);
@@ -906,7 +945,7 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
- al_string_copy(&device->DeviceName, dev_name);
+ alstr_copy(&device->DeviceName, dev_name);
}
pa_threaded_mainloop_unlock(self->loop);
@@ -914,16 +953,6 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
return ALC_NO_ERROR;
}
-static void ALCpulsePlayback_close(ALCpulsePlayback *self)
-{
- pulse_close(self->loop, self->context, self->stream);
- self->loop = NULL;
- self->context = NULL;
- self->stream = NULL;
-
- al_string_clear(&self->device_name);
-}
-
static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
@@ -931,7 +960,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
const char *mapname = NULL;
pa_channel_map chanmap;
pa_operation *o;
- ALuint len;
pa_threaded_mainloop_lock(self->loop);
@@ -946,11 +974,11 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
self->stream = NULL;
}
- o = pa_context_get_sink_info_by_name(self->context, al_string_get_cstr(self->device_name),
+ o = pa_context_get_sink_info_by_name(self->context, alstr_get_cstr(self->device_name),
ALCpulsePlayback_sinkInfoCallback, self);
wait_for_operation(o, self->loop);
- if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
+ if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
!(device->Flags&DEVICE_FREQUENCY_REQUEST))
flags |= PA_STREAM_FIX_RATE;
flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
@@ -984,7 +1012,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
break;
}
self->spec.rate = device->Frequency;
- self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
+ self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
if(pa_sample_spec_valid(&self->spec) == 0)
{
@@ -998,7 +1026,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
case DevFmtMono:
mapname = "mono";
break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -1034,9 +1062,9 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2);
self->attr.maxlength = -1;
- self->stream = ALCpulsePlayback_connectStream(al_string_get_cstr(self->device_name),
- self->loop, self->context, flags,
- &self->attr, &self->spec, &chanmap);
+ self->stream = ALCpulsePlayback_connectStream(alstr_get_cstr(self->device_name),
+ self->loop, self->context, flags, &self->attr, &self->spec, &chanmap
+ );
if(!self->stream)
{
pa_threaded_mainloop_unlock(self->loop);
@@ -1051,10 +1079,12 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
{
/* Server updated our playback rate, so modify the buffer attribs
* accordingly. */
- device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
- self->spec.rate + 0.5);
+ device->NumUpdates = (ALuint)clampd(
+ (ALdouble)device->NumUpdates/device->Frequency*self->spec.rate + 0.5, 2.0, 16.0
+ );
+
self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec);
- self->attr.tlength = self->attr.minreq * clampu(device->NumUpdates, 2, 16);
+ self->attr.tlength = self->attr.minreq * device->NumUpdates;
self->attr.maxlength = -1;
self->attr.prebuf = 0;
@@ -1068,10 +1098,30 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
ALCpulsePlayback_bufferAttrCallback(self->stream, self);
- len = self->attr.minreq / pa_frame_size(&self->spec);
- device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
- device->NumUpdates = clampu(device->NumUpdates, 2, 16);
- device->UpdateSize = len;
+ device->NumUpdates = (ALuint)clampu64(
+ (self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2, 16
+ );
+ device->UpdateSize = self->attr.minreq / pa_frame_size(&self->spec);
+
+ /* HACK: prebuf should be 0 as that's what we set it to. However on some
+ * systems it comes back as non-0, so we have to make sure the device will
+ * write enough audio to start playback. The lack of manual start control
+ * may have unintended consequences, but it's better than not starting at
+ * all.
+ */
+ if(self->attr.prebuf != 0)
+ {
+ ALuint len = self->attr.prebuf / pa_frame_size(&self->spec);
+ if(len <= device->UpdateSize*device->NumUpdates)
+ ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
+ len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
+ else
+ {
+ ERR("Large prebuf, %u samples (%u bytes), increasing device from %u samples",
+ len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
+ device->NumUpdates = (len+device->UpdateSize-1) / device->UpdateSize;
+ }
+ }
pa_threaded_mainloop_unlock(self->loop);
return ALC_TRUE;
@@ -1079,7 +1129,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self)
{
- self->killNow = AL_FALSE;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCpulsePlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
@@ -1090,10 +1140,9 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
pa_operation *o;
int res;
- if(!self->stream || self->killNow)
+ if(!self->stream || ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
- self->killNow = AL_TRUE;
/* Signal the main loop in case PulseAudio isn't sending us audio requests
* (e.g. if the device is suspended). We need to lock the mainloop in case
* the mixer is between checking the killNow flag but before waiting for
@@ -1113,12 +1162,18 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
}
-static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
+static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self)
{
- pa_usec_t latency = 0;
+ ClockLatency ret;
+ pa_usec_t latency;
int neg, err;
- if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
+ pa_threaded_mainloop_lock(self->loop);
+ ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
+ err = pa_stream_get_latency(self->stream, &latency, &neg);
+ pa_threaded_mainloop_unlock(self->loop);
+
+ if(UNLIKELY(err != 0))
{
/* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
* after starting the stream and no timing info has been received from
@@ -1126,11 +1181,14 @@ static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
* dummy value? Either way, it shouldn't be 0. */
if(err != -PA_ERR_NODATA)
ERR("Failed to get stream latency: 0x%x\n", err);
- return 0;
+ latency = 0;
+ neg = 0;
}
+ else if(UNLIKELY(neg))
+ latency = 0;
+ ret.Latency = (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
- if(neg) latency = 0;
- return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
+ return ret;
}
@@ -1180,13 +1238,12 @@ static pa_stream *ALCpulseCapture_connectStream(const char *device_name,
static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device);
static void ALCpulseCapture_Destruct(ALCpulseCapture *self);
static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name);
-static void ALCpulseCapture_close(ALCpulseCapture *self);
static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self);
static void ALCpulseCapture_stop(ALCpulseCapture *self);
static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self);
-static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self);
+static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self);
static void ALCpulseCapture_lock(ALCpulseCapture *self);
static void ALCpulseCapture_unlock(ALCpulseCapture *self);
DECLARE_DEFAULT_ALLOCATORS(ALCpulseCapture)
@@ -1199,11 +1256,19 @@ static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device)
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCpulseCapture, ALCbackend, self);
+ self->loop = NULL;
AL_STRING_INIT(self->device_name);
}
static void ALCpulseCapture_Destruct(ALCpulseCapture *self)
{
+ if(self->loop)
+ {
+ pulse_close(self->loop, self->context, self->stream);
+ self->loop = NULL;
+ self->context = NULL;
+ self->stream = NULL;
+ }
AL_STRING_DEINIT(self->device_name);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -1222,35 +1287,35 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa
return;
}
-#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
+#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_INFO_NAME);
- if(iter != VECTOR_ITER_END(CaptureDevices)) return;
+ if(iter != VECTOR_END(CaptureDevices)) return;
#undef MATCH_INFO_NAME
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
- al_string_copy_cstr(&entry.device_name, info->name);
+ alstr_copy_cstr(&entry.device_name, info->name);
count = 0;
while(1)
{
- al_string_copy_cstr(&entry.name, info->description);
+ alstr_copy_cstr(&entry.name, info->description);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
- al_string_append_cstr(&entry.name, str);
+ alstr_append_cstr(&entry.name, str);
}
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(CaptureDevices)) break;
+ if(iter == VECTOR_END(CaptureDevices)) break;
#undef MATCH_ENTRY
count++;
}
- TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
+ TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name));
VECTOR_PUSH_BACK(CaptureDevices, entry);
}
@@ -1315,7 +1380,7 @@ static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdat
if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
{
ERR("Received context failure!\n");
- aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Capture state failure");
}
pa_threaded_mainloop_signal(self->loop, 0);
}
@@ -1326,7 +1391,7 @@ static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata)
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
{
ERR("Received stream failure!\n");
- aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Capture stream failure");
}
pa_threaded_mainloop_signal(self->loop, 0);
}
@@ -1343,7 +1408,7 @@ static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), cons
return;
}
- al_string_copy_cstr(&device->DeviceName, info->description);
+ alstr_copy_cstr(&device->DeviceName, info->description);
}
@@ -1351,9 +1416,9 @@ static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata)
{
ALCpulseCapture *self = pdata;
- al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
+ alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
- TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
+ TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name));
}
@@ -1403,6 +1468,7 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
const char *pulse_name = NULL;
pa_stream_flags_t flags = 0;
+ const char *mapname = NULL;
pa_channel_map chanmap;
ALuint samples;
@@ -1413,13 +1479,13 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
if(VECTOR_SIZE(CaptureDevices) == 0)
ALCpulseCapture_probeDevices();
-#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
+#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(CaptureDevices))
+ if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
- pulse_name = al_string_get_cstr(iter->device_name);
- al_string_copy(&device->DeviceName, iter->name);
+ pulse_name = alstr_get_cstr(iter->device_name);
+ alstr_copy(&device->DeviceName, iter->name);
}
if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self))
@@ -1427,9 +1493,6 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
pa_threaded_mainloop_lock(self->loop);
- self->spec.rate = device->Frequency;
- self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
-
switch(device->FmtType)
{
case DevFmtUByte:
@@ -1452,6 +1515,44 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
goto fail;
}
+ switch(device->FmtChans)
+ {
+ case DevFmtMono:
+ mapname = "mono";
+ break;
+ case DevFmtStereo:
+ mapname = "front-left,front-right";
+ break;
+ case DevFmtQuad:
+ mapname = "front-left,front-right,rear-left,rear-right";
+ break;
+ case DevFmtX51:
+ mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
+ break;
+ case DevFmtX51Rear:
+ mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
+ break;
+ case DevFmtX61:
+ mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
+ break;
+ case DevFmtX71:
+ mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
+ break;
+ case DevFmtAmbi3D:
+ ERR("%s capture samples not supported\n", DevFmtChannelsString(device->FmtChans));
+ pa_threaded_mainloop_unlock(self->loop);
+ goto fail;
+ }
+ if(!pa_channel_map_parse(&chanmap, mapname))
+ {
+ ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans));
+ pa_threaded_mainloop_unlock(self->loop);
+ return ALC_FALSE;
+ }
+
+ self->spec.rate = device->Frequency;
+ self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+
if(pa_sample_spec_valid(&self->spec) == 0)
{
ERR("Invalid sample format\n");
@@ -1481,9 +1582,9 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
flags |= PA_STREAM_DONT_MOVE;
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
- self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context,
- flags, &self->attr, &self->spec,
- &chanmap);
+ self->stream = ALCpulseCapture_connectStream(pulse_name,
+ self->loop, self->context, flags, &self->attr, &self->spec, &chanmap
+ );
if(!self->stream)
{
pa_threaded_mainloop_unlock(self->loop);
@@ -1492,11 +1593,11 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self);
pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self);
- al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
- if(al_string_empty(device->DeviceName))
+ alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
+ if(alstr_empty(device->DeviceName))
{
pa_operation *o = pa_context_get_source_info_by_name(
- self->context, al_string_get_cstr(self->device_name),
+ self->context, alstr_get_cstr(self->device_name),
ALCpulseCapture_sourceNameCallback, self
);
wait_for_operation(o, self->loop);
@@ -1514,30 +1615,23 @@ fail:
return ALC_INVALID_VALUE;
}
-static void ALCpulseCapture_close(ALCpulseCapture *self)
-{
- pulse_close(self->loop, self->context, self->stream);
- self->loop = NULL;
- self->context = NULL;
- self->stream = NULL;
-
- al_string_clear(&self->device_name);
-}
-
static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self)
{
pa_operation *o;
+ pa_threaded_mainloop_lock(self->loop);
o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop);
wait_for_operation(o, self->loop);
-
+ pa_threaded_mainloop_unlock(self->loop);
return ALC_TRUE;
}
static void ALCpulseCapture_stop(ALCpulseCapture *self)
{
pa_operation *o;
+ pa_threaded_mainloop_lock(self->loop);
o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop);
wait_for_operation(o, self->loop);
+ pa_threaded_mainloop_unlock(self->loop);
}
static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples)
@@ -1548,6 +1642,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
/* Capture is done in fragment-sized chunks, so we loop until we get all
* that's available */
self->last_readable -= todo;
+ pa_threaded_mainloop_lock(self->loop);
while(todo > 0)
{
size_t rem = todo;
@@ -1559,14 +1654,15 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
state = pa_stream_get_state(self->stream);
if(!PA_STREAM_IS_GOOD(state))
{
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Bad capture state: %u", state);
break;
}
if(pa_stream_peek(self->stream, &self->cap_store, &self->cap_len) < 0)
{
ERR("pa_stream_peek() failed: %s\n",
pa_strerror(pa_context_errno(self->context)));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed retrieving capture samples: %s",
+ pa_strerror(pa_context_errno(self->context)));
break;
}
self->cap_remain = self->cap_len;
@@ -1587,6 +1683,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
self->cap_len = 0;
}
}
+ pa_threaded_mainloop_unlock(self->loop);
if(todo > 0)
memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
@@ -1598,16 +1695,19 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
size_t readable = self->cap_remain;
- if(device->Connected)
+ if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
- ssize_t got = pa_stream_readable_size(self->stream);
+ ssize_t got;
+ pa_threaded_mainloop_lock(self->loop);
+ got = pa_stream_readable_size(self->stream);
if(got < 0)
{
ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed getting readable size: %s", pa_strerror(got));
}
else if((size_t)got > self->cap_len)
readable += got - self->cap_len;
+ pa_threaded_mainloop_unlock(self->loop);
}
if(self->last_readable < readable)
@@ -1616,19 +1716,28 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
}
-static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self)
+static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self)
{
- pa_usec_t latency = 0;
- int neg;
+ ClockLatency ret;
+ pa_usec_t latency;
+ int neg, err;
+
+ pa_threaded_mainloop_lock(self->loop);
+ ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
+ err = pa_stream_get_latency(self->stream, &latency, &neg);
+ pa_threaded_mainloop_unlock(self->loop);
- if(pa_stream_get_latency(self->stream, &latency, &neg) != 0)
+ if(UNLIKELY(err != 0))
{
- ERR("Failed to get stream latency!\n");
- return 0;
+ ERR("Failed to get stream latency: 0x%x\n", err);
+ latency = 0;
+ neg = 0;
}
+ else if(UNLIKELY(neg))
+ latency = 0;
+ ret.Latency = (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
- if(neg) latency = 0;
- return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
+ return ret;
}
@@ -1651,9 +1760,8 @@ typedef struct ALCpulseBackendFactory {
static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory *self);
static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory *self);
static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory *self, ALCbackend_Type type);
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type);
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
@@ -1726,23 +1834,25 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
return ALC_FALSE;
}
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
+#define APPEND_OUTNAME(e) do { \
+ if(!alstr_empty((e)->name)) \
+ alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
+ VECTOR_END((e)->name)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
ALCpulsePlayback_probeDevices();
-#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(al_string_get_cstr((e)->name))
- VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST);
-#undef APPEND_ALL_DEVICES_LIST
+ VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
ALCpulseCapture_probeDevices();
-#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(al_string_get_cstr((e)->name))
- VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST);
-#undef APPEND_CAPTURE_DEVICE_LIST
+ VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
}
@@ -1790,7 +1900,7 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
return ALC_FALSE;
}
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type))
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
{
}
diff --git a/Alc/backends/qsa.c b/Alc/backends/qsa.c
index 291e49fc..81645096 100644
--- a/Alc/backends/qsa.c
+++ b/Alc/backends/qsa.c
@@ -33,6 +33,8 @@
#include "alu.h"
#include "threads.h"
+#include "backends/base.h"
+
typedef struct {
snd_pcm_t* pcmHandle;
@@ -44,7 +46,7 @@ typedef struct {
ALvoid* buffer;
ALsizei size;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} qsa_data;
@@ -117,8 +119,10 @@ static void deviceList(int type, vector_DevMap *devmap)
if(max_cards < 0)
return;
- VECTOR_RESERVE(*devmap, max_cards+1);
- VECTOR_RESIZE(*devmap, 0);
+#define FREE_NAME(iter) free((iter)->name)
+ VECTOR_FOR_EACH(DevMap, *devmap, FREE_NAME);
+#undef FREE_NAME
+ VECTOR_RESIZE(*devmap, 0, max_cards+1);
entry.name = strdup(qsaDevice);
entry.card = 0;
@@ -158,17 +162,39 @@ static void deviceList(int type, vector_DevMap *devmap)
}
-FORCE_ALIGN static int qsa_proc_playback(void* ptr)
+/* Wrappers to use an old-style backend with the new interface. */
+typedef struct PlaybackWrapper {
+ DERIVE_FROM_TYPE(ALCbackend);
+ qsa_data *ExtraData;
+} PlaybackWrapper;
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
+static void PlaybackWrapper_Destruct(PlaybackWrapper *self);
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
+static void PlaybackWrapper_stop(PlaybackWrapper *self);
+static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
+DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
+
+
+FORCE_ALIGN static int qsa_proc_playback(void *ptr)
{
- ALCdevice* device=(ALCdevice*)ptr;
- qsa_data* data=(qsa_data*)device->ExtraData;
- char* write_ptr;
- int avail;
+ PlaybackWrapper *self = ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ qsa_data *data = self->ExtraData;
snd_pcm_channel_status_t status;
struct sched_param param;
- fd_set wfds;
- int selectret;
struct timeval timeout;
+ char* write_ptr;
+ fd_set wfds;
+ ALint len;
+ int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
@@ -178,72 +204,69 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
param.sched_priority=param.sched_curpriority+1;
SchedSet(0, 0, SCHED_NOCHANGE, &param);
- ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ const ALint frame_size = FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
- while (!data->killNow)
+ V0(device->Backend,lock)();
+ while(!ATOMIC_LOAD(&data->killNow, almemory_order_acquire))
{
- ALint len=data->size;
- write_ptr=data->buffer;
-
- avail=len/frame_size;
- aluMixData(device, write_ptr, avail);
+ FD_ZERO(&wfds);
+ FD_SET(data->audio_fd, &wfds);
+ timeout.tv_sec=2;
+ timeout.tv_usec=0;
- while (len>0 && !data->killNow)
+ /* Select also works like time slice to OS */
+ V0(device->Backend,unlock)();
+ sret = select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
+ V0(device->Backend,lock)();
+ if(sret == -1)
{
- FD_ZERO(&wfds);
- FD_SET(data->audio_fd, &wfds);
- timeout.tv_sec=2;
- timeout.tv_usec=0;
-
- /* Select also works like time slice to OS */
- selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
- switch (selectret)
- {
- case -1:
- aluHandleDisconnect(device);
- return 1;
- case 0:
- break;
- default:
- if (FD_ISSET(data->audio_fd, &wfds))
- {
- break;
- }
- break;
- }
-
- int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
+ ERR("select error: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
+ break;
+ }
+ if(sret == 0)
+ {
+ ERR("select timeout\n");
+ continue;
+ }
- if (wrote<=0)
+ len = data->size;
+ write_ptr = data->buffer;
+ aluMixData(device, write_ptr, len/frame_size);
+ while(len>0 && !ATOMIC_LOAD(&data->killNow, almemory_order_acquire))
+ {
+ int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
+ if(wrote <= 0)
{
- if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
- {
+ if(errno==EAGAIN || errno==EWOULDBLOCK)
continue;
- }
- memset(&status, 0, sizeof (status));
- status.channel=SND_PCM_CHANNEL_PLAYBACK;
+ memset(&status, 0, sizeof(status));
+ status.channel = SND_PCM_CHANNEL_PLAYBACK;
snd_pcm_plugin_status(data->pcmHandle, &status);
/* we need to reinitialize the sound channel if we've underrun the buffer */
- if ((status.status==SND_PCM_STATUS_UNDERRUN) ||
- (status.status==SND_PCM_STATUS_READY))
+ if(status.status == SND_PCM_STATUS_UNDERRUN ||
+ status.status == SND_PCM_STATUS_READY)
{
- if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
+ if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
{
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Playback recovery failed");
break;
}
}
}
else
{
- write_ptr+=wrote;
- len-=wrote;
+ write_ptr += wrote;
+ len -= wrote;
}
}
}
+ V0(device->Backend,unlock)();
return 0;
}
@@ -252,8 +275,9 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
/* Playback */
/************/
-static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
+static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data;
int card, dev;
int status;
@@ -261,6 +285,7 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
data = (qsa_data*)calloc(1, sizeof(qsa_data));
if(data == NULL)
return ALC_OUT_OF_MEMORY;
+ ATOMIC_INIT(&data->killNow, AL_TRUE);
if(!deviceName)
deviceName = qsaDevice;
@@ -277,7 +302,7 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME);
#undef MATCH_DEVNAME
- if(iter == VECTOR_ITER_END(DeviceNameMap))
+ if(iter == VECTOR_END(DeviceNameMap))
{
free(data);
return ALC_INVALID_DEVICE;
@@ -300,15 +325,15 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
return ALC_INVALID_DEVICE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
- device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, deviceName);
+ self->ExtraData = data;
return ALC_NO_ERROR;
}
-static void qsa_close_playback(ALCdevice* device)
+static void qsa_close_playback(PlaybackWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
if (data->buffer!=NULL)
{
@@ -319,12 +344,13 @@ static void qsa_close_playback(ALCdevice* device)
snd_pcm_close(data->pcmHandle);
free(data);
- device->ExtraData=NULL;
+ self->ExtraData = NULL;
}
-static ALCboolean qsa_reset_playback(ALCdevice* device)
+static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ qsa_data *data = self->ExtraData;
int32_t format=-1;
switch(device->FmtType)
@@ -365,14 +391,14 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
data->cparams.start_mode=SND_PCM_START_FULL;
data->cparams.stop_mode=SND_PCM_STOP_STOP;
- data->cparams.buf.block.frag_size=device->UpdateSize*
- ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+ data->cparams.buf.block.frag_size=device->UpdateSize *
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
data->cparams.buf.block.frags_max=device->NumUpdates;
data->cparams.buf.block.frags_min=device->NumUpdates;
data->cparams.format.interleave=1;
data->cparams.format.rate=device->Frequency;
- data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+ data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
data->cparams.format.format=format;
if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
@@ -556,7 +582,7 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
SetDefaultChannelOrder(device);
device->UpdateSize=data->csetup.buf.block.frag_size/
- (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
device->NumUpdates=data->csetup.buf.block.frags;
data->size=data->csetup.buf.block.frag_size;
@@ -569,35 +595,93 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
return ALC_TRUE;
}
-static ALCboolean qsa_start_playback(ALCdevice* device)
+static ALCboolean qsa_start_playback(PlaybackWrapper *self)
{
- qsa_data *data = (qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
- data->killNow = 0;
- if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success)
+ ATOMIC_STORE(&data->killNow, AL_FALSE, almemory_order_release);
+ if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
-static void qsa_stop_playback(ALCdevice* device)
+static void qsa_stop_playback(PlaybackWrapper *self)
{
- qsa_data *data = (qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
int res;
- if(data->killNow)
+ if(ATOMIC_EXCHANGE(&data->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- data->killNow = 1;
althrd_join(data->thread, &res);
}
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
+
+ self->ExtraData = NULL;
+}
+
+static void PlaybackWrapper_Destruct(PlaybackWrapper *self)
+{
+ if(self->ExtraData)
+ qsa_close_playback(self);
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
+{
+ return qsa_open_playback(self, name);
+}
+
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
+{
+ return qsa_reset_playback(self);
+}
+
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
+{
+ return qsa_start_playback(self);
+}
+
+static void PlaybackWrapper_stop(PlaybackWrapper *self)
+{
+ qsa_stop_playback(self);
+}
+
+
+
/***********/
/* Capture */
/***********/
-static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
+typedef struct CaptureWrapper {
+ DERIVE_FROM_TYPE(ALCbackend);
+ qsa_data *ExtraData;
+} CaptureWrapper;
+
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
+static void CaptureWrapper_Destruct(CaptureWrapper *self);
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
+static void CaptureWrapper_stop(CaptureWrapper *self);
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
+DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
+
+
+static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data;
int card, dev;
int format=-1;
@@ -624,7 +708,7 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME);
#undef MATCH_DEVNAME
- if(iter == VECTOR_ITER_END(CaptureNameMap))
+ if(iter == VECTOR_END(CaptureNameMap))
{
free(data);
return ALC_INVALID_DEVICE;
@@ -647,8 +731,8 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
return ALC_INVALID_DEVICE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
- device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, deviceName);
+ self->ExtraData = data;
switch (device->FmtType)
{
@@ -688,20 +772,19 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
data->cparams.stop_mode=SND_PCM_STOP_STOP;
data->cparams.buf.block.frag_size=device->UpdateSize*
- ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
data->cparams.buf.block.frags_max=device->NumUpdates;
data->cparams.buf.block.frags_min=device->NumUpdates;
data->cparams.format.interleave=1;
data->cparams.format.rate=device->Frequency;
- data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+ data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
data->cparams.format.format=format;
if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
{
snd_pcm_close(data->pcmHandle);
free(data);
- device->ExtraData=NULL;
return ALC_INVALID_VALUE;
}
@@ -709,20 +792,20 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
return ALC_NO_ERROR;
}
-static void qsa_close_capture(ALCdevice* device)
+static void qsa_close_capture(CaptureWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
if (data->pcmHandle!=NULL)
snd_pcm_close(data->pcmHandle);
free(data);
- device->ExtraData=NULL;
+ self->ExtraData = NULL;
}
-static void qsa_start_capture(ALCdevice* device)
+static void qsa_start_capture(CaptureWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
int rstatus;
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
@@ -742,18 +825,18 @@ static void qsa_start_capture(ALCdevice* device)
snd_pcm_capture_go(data->pcmHandle);
}
-static void qsa_stop_capture(ALCdevice* device)
+static void qsa_stop_capture(CaptureWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
-
+ qsa_data *data = self->ExtraData;
snd_pcm_capture_flush(data->pcmHandle);
}
-static ALCuint qsa_available_samples(ALCdevice* device)
+static ALCuint qsa_available_samples(CaptureWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ qsa_data *data = self->ExtraData;
snd_pcm_channel_status_t status;
- ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
ALint free_size;
int rstatus;
@@ -766,7 +849,7 @@ static ALCuint qsa_available_samples(ALCdevice* device)
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
{
ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus));
return 0;
}
@@ -780,16 +863,17 @@ static ALCuint qsa_available_samples(ALCdevice* device)
return free_size/frame_size;
}
-static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ qsa_data *data = self->ExtraData;
char* read_ptr;
snd_pcm_channel_status_t status;
fd_set rfds;
int selectret;
struct timeval timeout;
int bytes_read;
- ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
ALint len=samples*frame_size;
int rstatus;
@@ -808,7 +892,7 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
switch (selectret)
{
case -1:
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to check capture samples");
return ALC_INVALID_DEVICE;
case 0:
break;
@@ -839,7 +923,8 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
{
ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed capture recovery: %s",
+ snd_strerror(rstatus));
return ALC_INVALID_DEVICE;
}
snd_pcm_capture_go(data->pcmHandle);
@@ -855,27 +940,68 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
return ALC_NO_ERROR;
}
-static const BackendFuncs qsa_funcs= {
- qsa_open_playback,
- qsa_close_playback,
- qsa_reset_playback,
- qsa_start_playback,
- qsa_stop_playback,
- qsa_open_capture,
- qsa_close_capture,
- qsa_start_capture,
- qsa_stop_capture,
- qsa_capture_samples,
- qsa_available_samples
-};
-ALCboolean alc_qsa_init(BackendFuncs* func_list)
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(CaptureWrapper, ALCbackend, self);
+
+ self->ExtraData = NULL;
+}
+
+static void CaptureWrapper_Destruct(CaptureWrapper *self)
+{
+ if(self->ExtraData)
+ qsa_close_capture(self);
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
+{
+ return qsa_open_capture(self, name);
+}
+
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
{
- *func_list = qsa_funcs;
+ qsa_start_capture(self);
return ALC_TRUE;
}
-void alc_qsa_deinit(void)
+static void CaptureWrapper_stop(CaptureWrapper *self)
+{
+ qsa_stop_capture(self);
+}
+
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
+{
+ return qsa_capture_samples(self, buffer, samples);
+}
+
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
+{
+ return qsa_available_samples(self);
+}
+
+
+typedef struct ALCqsaBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCqsaBackendFactory;
+#define ALCQSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCqsaBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self));
+static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self));
+static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type);
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames);
+static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
+
+static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self))
+{
+ return ALC_TRUE;
+}
+
+static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self))
{
#define FREE_NAME(iter) free((iter)->name)
VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
@@ -886,32 +1012,57 @@ void alc_qsa_deinit(void)
#undef FREE_NAME
}
-void alc_qsa_probe(enum DevProbe type)
+static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch (type)
{
+#define APPEND_OUTNAME(e) do { \
+ const char *n_ = (e)->name; \
+ if(n_ && n_[0]) \
+ alstr_append_range(outnames, n_, n_+strlen(n_)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
-#define FREE_NAME(iter) free((iter)->name)
- VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
-#undef FREE_NAME
- VECTOR_RESIZE(DeviceNameMap, 0);
-
deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
-#define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
- VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_DEVICE);
-#undef APPEND_DEVICE
+ VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
-#define FREE_NAME(iter) free((iter)->name)
- VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
-#undef FREE_NAME
- VECTOR_RESIZE(CaptureNameMap, 0);
-
deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
-#define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
- VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_DEVICE);
-#undef APPEND_DEVICE
+ VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
+ }
+}
+
+static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ PlaybackWrapper *backend;
+ NEW_OBJ(backend, PlaybackWrapper)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
}
+ if(type == ALCbackend_Capture)
+ {
+ CaptureWrapper *backend;
+ NEW_OBJ(backend, CaptureWrapper)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
+
+ALCbackendFactory *ALCqsaBackendFactory_getFactory(void)
+{
+ static ALCqsaBackendFactory factory = ALCQSABACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
}
diff --git a/Alc/backends/sdl2.c b/Alc/backends/sdl2.c
new file mode 100644
index 00000000..3495e6bf
--- /dev/null
+++ b/Alc/backends/sdl2.c
@@ -0,0 +1,288 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2018 by authors.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <SDL2/SDL.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+
+#ifdef _WIN32
+#define DEVNAME_PREFIX "OpenAL Soft on "
+#else
+#define DEVNAME_PREFIX ""
+#endif
+
+typedef struct ALCsdl2Backend {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ SDL_AudioDeviceID deviceID;
+ ALsizei frameSize;
+
+ ALuint Frequency;
+ enum DevFmtChannels FmtChans;
+ enum DevFmtType FmtType;
+ ALuint UpdateSize;
+} ALCsdl2Backend;
+
+static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device);
+static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self);
+static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name);
+static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self);
+static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self);
+static void ALCsdl2Backend_stop(ALCsdl2Backend *self);
+static DECLARE_FORWARD2(ALCsdl2Backend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ClockLatency, getClockLatency)
+static void ALCsdl2Backend_lock(ALCsdl2Backend *self);
+static void ALCsdl2Backend_unlock(ALCsdl2Backend *self);
+DECLARE_DEFAULT_ALLOCATORS(ALCsdl2Backend)
+
+DEFINE_ALCBACKEND_VTABLE(ALCsdl2Backend);
+
+static const ALCchar defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
+
+static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCsdl2Backend, ALCbackend, self);
+
+ self->deviceID = 0;
+ self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+ self->Frequency = device->Frequency;
+ self->FmtChans = device->FmtChans;
+ self->FmtType = device->FmtType;
+ self->UpdateSize = device->UpdateSize;
+}
+
+static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self)
+{
+ if(self->deviceID)
+ SDL_CloseAudioDevice(self->deviceID);
+ self->deviceID = 0;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static void ALCsdl2Backend_audioCallback(void *ptr, Uint8 *stream, int len)
+{
+ ALCsdl2Backend *self = (ALCsdl2Backend*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+ assert((len % self->frameSize) == 0);
+ aluMixData(device, stream, len / self->frameSize);
+}
+
+static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ SDL_AudioSpec want, have;
+
+ SDL_zero(want);
+ SDL_zero(have);
+
+ want.freq = device->Frequency;
+ switch(device->FmtType)
+ {
+ case DevFmtUByte: want.format = AUDIO_U8; break;
+ case DevFmtByte: want.format = AUDIO_S8; break;
+ case DevFmtUShort: want.format = AUDIO_U16SYS; break;
+ case DevFmtShort: want.format = AUDIO_S16SYS; break;
+ case DevFmtUInt: /* fall-through */
+ case DevFmtInt: want.format = AUDIO_S32SYS; break;
+ case DevFmtFloat: want.format = AUDIO_F32; break;
+ }
+ want.channels = (device->FmtChans == DevFmtMono) ? 1 : 2;
+ want.samples = device->UpdateSize;
+ want.callback = ALCsdl2Backend_audioCallback;
+ want.userdata = self;
+
+ /* Passing NULL to SDL_OpenAudioDevice opens a default, which isn't
+ * necessarily the first in the list.
+ */
+ if(!name || strcmp(name, defaultDeviceName) == 0)
+ self->deviceID = SDL_OpenAudioDevice(NULL, SDL_FALSE, &want, &have,
+ SDL_AUDIO_ALLOW_ANY_CHANGE);
+ else
+ {
+ const size_t prefix_len = strlen(DEVNAME_PREFIX);
+ if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
+ self->deviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
+ SDL_AUDIO_ALLOW_ANY_CHANGE);
+ else
+ self->deviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
+ SDL_AUDIO_ALLOW_ANY_CHANGE);
+ }
+ if(self->deviceID == 0)
+ return ALC_INVALID_VALUE;
+
+ device->Frequency = have.freq;
+ if(have.channels == 1)
+ device->FmtChans = DevFmtMono;
+ else if(have.channels == 2)
+ device->FmtChans = DevFmtStereo;
+ else
+ {
+ ERR("Got unhandled SDL channel count: %d\n", (int)have.channels);
+ return ALC_INVALID_VALUE;
+ }
+ switch(have.format)
+ {
+ case AUDIO_U8: device->FmtType = DevFmtUByte; break;
+ case AUDIO_S8: device->FmtType = DevFmtByte; break;
+ case AUDIO_U16SYS: device->FmtType = DevFmtUShort; break;
+ case AUDIO_S16SYS: device->FmtType = DevFmtShort; break;
+ case AUDIO_S32SYS: device->FmtType = DevFmtInt; break;
+ case AUDIO_F32SYS: device->FmtType = DevFmtFloat; break;
+ default:
+ ERR("Got unsupported SDL format: 0x%04x\n", have.format);
+ return ALC_INVALID_VALUE;
+ }
+ device->UpdateSize = have.samples;
+ device->NumUpdates = 2; /* SDL always (tries to) use two periods. */
+
+ self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+ self->Frequency = device->Frequency;
+ self->FmtChans = device->FmtChans;
+ self->FmtType = device->FmtType;
+ self->UpdateSize = device->UpdateSize;
+
+ alstr_copy_cstr(&device->DeviceName, name ? name : defaultDeviceName);
+
+ return ALC_NO_ERROR;
+}
+
+static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ device->Frequency = self->Frequency;
+ device->FmtChans = self->FmtChans;
+ device->FmtType = self->FmtType;
+ device->UpdateSize = self->UpdateSize;
+ device->NumUpdates = 2;
+ SetDefaultWFXChannelOrder(device);
+ return ALC_TRUE;
+}
+
+static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self)
+{
+ SDL_PauseAudioDevice(self->deviceID, 0);
+ return ALC_TRUE;
+}
+
+static void ALCsdl2Backend_stop(ALCsdl2Backend *self)
+{
+ SDL_PauseAudioDevice(self->deviceID, 1);
+}
+
+static void ALCsdl2Backend_lock(ALCsdl2Backend *self)
+{
+ SDL_LockAudioDevice(self->deviceID);
+}
+
+static void ALCsdl2Backend_unlock(ALCsdl2Backend *self)
+{
+ SDL_UnlockAudioDevice(self->deviceID);
+}
+
+
+typedef struct ALCsdl2BackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCsdl2BackendFactory;
+#define ALCsdl2BACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsdl2BackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
+
+static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory *self);
+static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory *self);
+static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory *self, ALCbackend_Type type);
+static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory);
+
+
+ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void)
+{
+ static ALCsdl2BackendFactory factory = ALCsdl2BACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory* UNUSED(self))
+{
+ if(SDL_InitSubSystem(SDL_INIT_AUDIO) == 0)
+ return AL_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory* UNUSED(self))
+{
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+}
+
+static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ return ALC_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
+{
+ int num_devices, i;
+ al_string name;
+
+ if(type != ALL_DEVICE_PROBE)
+ return;
+
+ AL_STRING_INIT(name);
+ num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
+
+ alstr_append_range(outnames, defaultDeviceName, defaultDeviceName+sizeof(defaultDeviceName));
+ for(i = 0;i < num_devices;++i)
+ {
+ alstr_copy_cstr(&name, DEVNAME_PREFIX);
+ alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE));
+ if(!alstr_empty(name))
+ alstr_append_range(outnames, VECTOR_BEGIN(name), VECTOR_END(name)+1);
+ }
+ alstr_reset(&name);
+}
+
+static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCsdl2Backend *backend;
+ NEW_OBJ(backend, ALCsdl2Backend)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
diff --git a/Alc/backends/sndio.c b/Alc/backends/sndio.c
index 52bff13a..dd174cba 100644
--- a/Alc/backends/sndio.c
+++ b/Alc/backends/sndio.c
@@ -27,6 +27,9 @@
#include "alMain.h"
#include "alu.h"
#include "threads.h"
+#include "ringbuffer.h"
+
+#include "backends/base.h"
#include <sndio.h>
@@ -34,49 +37,88 @@
static const ALCchar sndio_device[] = "SndIO Default";
-static ALCboolean sndio_load(void)
-{
- return ALC_TRUE;
-}
-
+typedef struct SndioPlayback {
+ DERIVE_FROM_TYPE(ALCbackend);
-typedef struct {
struct sio_hdl *sndHandle;
ALvoid *mix_data;
ALsizei data_size;
- volatile int killNow;
+ ATOMIC(int) killNow;
althrd_t thread;
-} sndio_data;
+} SndioPlayback;
+
+static int SndioPlayback_mixerProc(void *ptr);
+static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
+static void SndioPlayback_Destruct(SndioPlayback *self);
+static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
+static ALCboolean SndioPlayback_reset(SndioPlayback *self);
+static ALCboolean SndioPlayback_start(SndioPlayback *self);
+static void SndioPlayback_stop(SndioPlayback *self);
+static DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
+
+
+static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(SndioPlayback, ALCbackend, self);
+
+ self->sndHandle = NULL;
+ self->mix_data = NULL;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
-static int sndio_proc(void *ptr)
+static void SndioPlayback_Destruct(SndioPlayback *self)
{
- ALCdevice *device = ptr;
- sndio_data *data = device->ExtraData;
+ if(self->sndHandle)
+ sio_close(self->sndHandle);
+ self->sndHandle = NULL;
+
+ al_free(self->mix_data);
+ self->mix_data = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static int SndioPlayback_mixerProc(void *ptr)
+{
+ SndioPlayback *self = (SndioPlayback*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALsizei frameSize;
size_t wrote;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- while(!data->killNow && device->Connected)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
- ALsizei len = data->data_size;
- ALubyte *WritePtr = data->mix_data;
+ ALsizei len = self->data_size;
+ ALubyte *WritePtr = self->mix_data;
+ SndioPlayback_lock(self);
aluMixData(device, WritePtr, len/frameSize);
- while(len > 0 && !data->killNow)
+ SndioPlayback_unlock(self);
+ while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
- wrote = sio_write(data->sndHandle, WritePtr, len);
+ wrote = sio_write(self->sndHandle, WritePtr, len);
if(wrote == 0)
{
ERR("sio_write failed\n");
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to write playback samples");
ALCdevice_Unlock(device);
break;
}
@@ -90,45 +132,30 @@ static int sndio_proc(void *ptr)
}
-
-static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
+static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
{
- sndio_data *data;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
- if(!deviceName)
- deviceName = sndio_device;
- else if(strcmp(deviceName, sndio_device) != 0)
+ if(!name)
+ name = sndio_device;
+ else if(strcmp(name, sndio_device) != 0)
return ALC_INVALID_VALUE;
- data = calloc(1, sizeof(*data));
- data->killNow = 0;
-
- data->sndHandle = sio_open(NULL, SIO_PLAY, 0);
- if(data->sndHandle == NULL)
+ self->sndHandle = sio_open(NULL, SIO_PLAY, 0);
+ if(self->sndHandle == NULL)
{
- free(data);
ERR("Could not open device\n");
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
- device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void sndio_close_playback(ALCdevice *device)
+static ALCboolean SndioPlayback_reset(SndioPlayback *self)
{
- sndio_data *data = device->ExtraData;
-
- sio_close(data->sndHandle);
- free(data);
- device->ExtraData = NULL;
-}
-
-static ALCboolean sndio_reset_playback(ALCdevice *device)
-{
- sndio_data *data = device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
struct sio_par par;
sio_initpar(&par);
@@ -170,7 +197,7 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
- if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par))
+ if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
{
ERR("Failed to set device parameters\n");
return ALC_FALSE;
@@ -211,84 +238,363 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
-static ALCboolean sndio_start_playback(ALCdevice *device)
+static ALCboolean SndioPlayback_start(SndioPlayback *self)
{
- sndio_data *data = device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+
+ self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
+ al_free(self->mix_data);
+ self->mix_data = al_calloc(16, self->data_size);
- if(!sio_start(data->sndHandle))
+ if(!sio_start(self->sndHandle))
{
ERR("Error starting playback\n");
return ALC_FALSE;
}
- data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
- data->mix_data = calloc(1, data->data_size);
-
- data->killNow = 0;
- if(althrd_create(&data->thread, sndio_proc, device) != althrd_success)
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
+ if(althrd_create(&self->thread, SndioPlayback_mixerProc, self) != althrd_success)
{
- sio_stop(data->sndHandle);
- free(data->mix_data);
- data->mix_data = NULL;
+ sio_stop(self->sndHandle);
return ALC_FALSE;
}
return ALC_TRUE;
}
-static void sndio_stop_playback(ALCdevice *device)
+static void SndioPlayback_stop(SndioPlayback *self)
{
- sndio_data *data = device->ExtraData;
int res;
- if(data->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
+ althrd_join(self->thread, &res);
- data->killNow = 1;
- althrd_join(data->thread, &res);
-
- if(!sio_stop(data->sndHandle))
+ if(!sio_stop(self->sndHandle))
ERR("Error stopping device\n");
- free(data->mix_data);
- data->mix_data = NULL;
+ al_free(self->mix_data);
+ self->mix_data = NULL;
+}
+
+
+typedef struct SndioCapture {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ struct sio_hdl *sndHandle;
+
+ ll_ringbuffer_t *ring;
+
+ ATOMIC(int) killNow;
+ althrd_t thread;
+} SndioCapture;
+
+static int SndioCapture_recordProc(void *ptr);
+
+static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
+static void SndioCapture_Destruct(SndioCapture *self);
+static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
+static DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean SndioCapture_start(SndioCapture *self);
+static void SndioCapture_stop(SndioCapture *self);
+static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
+static ALCuint SndioCapture_availableSamples(SndioCapture *self);
+static DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
+
+DEFINE_ALCBACKEND_VTABLE(SndioCapture);
+
+
+static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(SndioCapture, ALCbackend, self);
+
+ self->sndHandle = NULL;
+ self->ring = NULL;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
+
+static void SndioCapture_Destruct(SndioCapture *self)
+{
+ if(self->sndHandle)
+ sio_close(self->sndHandle);
+ self->sndHandle = NULL;
+
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
-static const BackendFuncs sndio_funcs = {
- sndio_open_playback,
- sndio_close_playback,
- sndio_reset_playback,
- sndio_start_playback,
- sndio_stop_playback,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-ALCboolean alc_sndio_init(BackendFuncs *func_list)
+static int SndioCapture_recordProc(void* ptr)
{
- if(!sndio_load())
+ SndioCapture *self = (SndioCapture*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ALsizei frameSize;
+
+ SetRTPriority();
+ althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+
+ frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
+ {
+ ll_ringbuffer_data_t data[2];
+ size_t total, todo;
+
+ ll_ringbuffer_get_write_vector(self->ring, data);
+ todo = data[0].len + data[1].len;
+ if(todo == 0)
+ {
+ static char junk[4096];
+ sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
+ continue;
+ }
+
+ total = 0;
+ data[0].len *= frameSize;
+ data[1].len *= frameSize;
+ todo = minz(todo, device->UpdateSize) * frameSize;
+ while(total < todo)
+ {
+ size_t got;
+
+ if(!data[0].len)
+ data[0] = data[1];
+
+ got = sio_read(self->sndHandle, data[0].buf, minz(todo-total, data[0].len));
+ if(!got)
+ {
+ SndioCapture_lock(self);
+ aluHandleDisconnect(device, "Failed to read capture samples");
+ SndioCapture_unlock(self);
+ break;
+ }
+
+ data[0].buf += got;
+ data[0].len -= got;
+ total += got;
+ }
+ ll_ringbuffer_write_advance(self->ring, total / frameSize);
+ }
+
+ return 0;
+}
+
+
+static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ struct sio_par par;
+
+ if(!name)
+ name = sndio_device;
+ else if(strcmp(name, sndio_device) != 0)
+ return ALC_INVALID_VALUE;
+
+ self->sndHandle = sio_open(NULL, SIO_REC, 0);
+ if(self->sndHandle == NULL)
+ {
+ ERR("Could not open device\n");
+ return ALC_INVALID_VALUE;
+ }
+
+ sio_initpar(&par);
+
+ switch(device->FmtType)
+ {
+ case DevFmtByte:
+ par.bps = 1;
+ par.sig = 1;
+ break;
+ case DevFmtUByte:
+ par.bps = 1;
+ par.sig = 0;
+ break;
+ case DevFmtShort:
+ par.bps = 2;
+ par.sig = 1;
+ break;
+ case DevFmtUShort:
+ par.bps = 2;
+ par.sig = 0;
+ break;
+ case DevFmtInt:
+ par.bps = 4;
+ par.sig = 1;
+ break;
+ case DevFmtUInt:
+ par.bps = 4;
+ par.sig = 0;
+ break;
+ case DevFmtFloat:
+ ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
+ return ALC_INVALID_VALUE;
+ }
+ par.bits = par.bps * 8;
+ par.le = SIO_LE_NATIVE;
+ par.msb = SIO_LE_NATIVE ? 0 : 1;
+ par.rchan = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ par.rate = device->Frequency;
+
+ par.appbufsz = maxu(device->UpdateSize*device->NumUpdates, (device->Frequency+9)/10);
+ par.round = clampu(par.appbufsz/device->NumUpdates, (device->Frequency+99)/100,
+ (device->Frequency+19)/20);
+
+ device->UpdateSize = par.round;
+ device->NumUpdates = maxu(par.appbufsz/par.round, 1);
+
+ if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
+ {
+ ERR("Failed to set device parameters\n");
+ return ALC_INVALID_VALUE;
+ }
+
+ if(par.bits != par.bps*8)
+ {
+ ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
+ return ALC_INVALID_VALUE;
+ }
+
+ if(!((device->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
+ (device->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
+ (device->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
+ (device->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
+ (device->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
+ (device->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
+ ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)par.rchan ||
+ device->Frequency != par.rate)
+ {
+ ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
+ DevFmtTypeString(device->FmtType), DevFmtChannelsString(device->FmtChans),
+ device->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
+ return ALC_INVALID_VALUE;
+ }
+
+ self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, par.bps*par.rchan, 0);
+ if(!self->ring)
+ {
+ ERR("Failed to allocate %u-byte ringbuffer\n",
+ device->UpdateSize*device->NumUpdates*par.bps*par.rchan);
+ return ALC_OUT_OF_MEMORY;
+ }
+
+ SetDefaultChannelOrder(device);
+
+ alstr_copy_cstr(&device->DeviceName, name);
+
+ return ALC_NO_ERROR;
+}
+
+static ALCboolean SndioCapture_start(SndioCapture *self)
+{
+ if(!sio_start(self->sndHandle))
+ {
+ ERR("Error starting playback\n");
return ALC_FALSE;
- *func_list = sndio_funcs;
+ }
+
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
+ if(althrd_create(&self->thread, SndioCapture_recordProc, self) != althrd_success)
+ {
+ sio_stop(self->sndHandle);
+ return ALC_FALSE;
+ }
+
+ return ALC_TRUE;
+}
+
+static void SndioCapture_stop(SndioCapture *self)
+{
+ int res;
+
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
+ return;
+ althrd_join(self->thread, &res);
+
+ if(!sio_stop(self->sndHandle))
+ ERR("Error stopping device\n");
+}
+
+static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples)
+{
+ ll_ringbuffer_read(self->ring, buffer, samples);
+ return ALC_NO_ERROR;
+}
+
+static ALCuint SndioCapture_availableSamples(SndioCapture *self)
+{
+ return ll_ringbuffer_read_space(self->ring);
+}
+
+
+typedef struct SndioBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} SndioBackendFactory;
+#define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *SndioBackendFactory_getFactory(void);
+
+static ALCboolean SndioBackendFactory_init(SndioBackendFactory *self);
+static DECLARE_FORWARD(SndioBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory *self, ALCbackend_Type type);
+static void SndioBackendFactory_probe(SndioBackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(SndioBackendFactory);
+
+ALCbackendFactory *SndioBackendFactory_getFactory(void)
+{
+ static SndioBackendFactory factory = SNDIOBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+static ALCboolean SndioBackendFactory_init(SndioBackendFactory* UNUSED(self))
+{
+ /* No dynamic loading */
return ALC_TRUE;
}
-void alc_sndio_deinit(void)
+static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_sndio_probe(enum DevProbe type)
+static void SndioBackendFactory_probe(SndioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(sndio_device);
- break;
case CAPTURE_DEVICE_PROBE:
+ alstr_append_range(outnames, sndio_device, sndio_device+sizeof(sndio_device));
break;
}
}
+
+static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ SndioPlayback *backend;
+ NEW_OBJ(backend, SndioPlayback)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ SndioCapture *backend;
+ NEW_OBJ(backend, SndioCapture)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
diff --git a/Alc/backends/solaris.c b/Alc/backends/solaris.c
index 52ca9090..71282204 100644
--- a/Alc/backends/solaris.c
+++ b/Alc/backends/solaris.c
@@ -22,6 +22,7 @@
#include <sys/ioctl.h>
#include <sys/types.h>
+#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -33,6 +34,7 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
#include "threads.h"
#include "compat.h"
@@ -49,7 +51,7 @@ typedef struct ALCsolarisBackend {
ALubyte *mix_data;
int data_size;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCsolarisBackend;
@@ -58,13 +60,12 @@ static int ALCsolarisBackend_mixerProc(void *ptr);
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device);
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self);
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name);
-static void ALCsolarisBackend_close(ALCsolarisBackend *self);
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self);
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self);
static void ALCsolarisBackend_stop(ALCsolarisBackend *self);
static DECLARE_FORWARD2(ALCsolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
@@ -83,6 +84,8 @@ static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *devi
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
self->fd = -1;
+ self->mix_data = NULL;
+ ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
@@ -102,43 +105,67 @@ static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
static int ALCsolarisBackend_mixerProc(void *ptr)
{
ALCsolarisBackend *self = ptr;
- ALCdevice *Device = STATIC_CAST(ALCbackend,self)->mDevice;
- ALint frameSize;
- int wrote;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ struct timeval timeout;
+ ALubyte *write_ptr;
+ ALint frame_size;
+ ALint to_write;
+ ssize_t wrote;
+ fd_set wfds;
+ int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
+ frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- while(!self->killNow && Device->Connected)
+ ALCsolarisBackend_lock(self);
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
- ALint len = self->data_size;
- ALubyte *WritePtr = self->mix_data;
+ FD_ZERO(&wfds);
+ FD_SET(self->fd, &wfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ ALCsolarisBackend_unlock(self);
+ sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
+ ALCsolarisBackend_lock(self);
+ if(sret < 0)
+ {
+ if(errno == EINTR)
+ continue;
+ ERR("select failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed to wait for playback buffer: %s", strerror(errno));
+ break;
+ }
+ else if(sret == 0)
+ {
+ WARN("select timeout\n");
+ continue;
+ }
- aluMixData(Device, WritePtr, len/frameSize);
- while(len > 0 && !self->killNow)
+ write_ptr = self->mix_data;
+ to_write = self->data_size;
+ aluMixData(device, write_ptr, to_write/frame_size);
+ while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
{
- wrote = write(self->fd, WritePtr, len);
+ wrote = write(self->fd, write_ptr, to_write);
if(wrote < 0)
{
- if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
- {
- ERR("write failed: %s\n", strerror(errno));
- ALCsolarisBackend_lock(self);
- aluHandleDisconnect(Device);
- ALCsolarisBackend_unlock(self);
- break;
- }
-
- al_nssleep(1000000);
- continue;
+ if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ ERR("write failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed to write playback samples: %s",
+ strerror(errno));
+ break;
}
- len -= wrote;
- WritePtr += wrote;
+ to_write -= wrote;
+ write_ptr += wrote;
}
}
+ ALCsolarisBackend_unlock(self);
return 0;
}
@@ -161,23 +188,17 @@ static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *na
}
device = STATIC_CAST(ALCbackend,self)->mDevice;
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCsolarisBackend_close(ALCsolarisBackend *self)
-{
- close(self->fd);
- self->fd = -1;
-}
-
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
audio_info_t info;
- ALuint frameSize;
- int numChannels;
+ ALsizei frameSize;
+ ALsizei numChannels;
AUDIO_INITINFO(&info);
@@ -185,7 +206,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
if(device->FmtChans != DevFmtMono)
device->FmtChans = DevFmtStereo;
- numChannels = ChannelsFromDevFmt(device->FmtChans);
+ numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
info.play.channels = numChannels;
switch(device->FmtType)
@@ -219,9 +240,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
return ALC_FALSE;
}
- if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
+ if(ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)info.play.channels)
{
- ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
+ ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), info.play.channels);
return ALC_FALSE;
}
@@ -241,7 +262,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
SetDefaultChannelOrder(device);
free(self->mix_data);
- self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
self->mix_data = calloc(1, self->data_size);
return ALC_TRUE;
@@ -249,7 +272,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
{
- self->killNow = 0;
+ ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
@@ -259,10 +282,9 @@ static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
- self->killNow = 1;
althrd_join(self->thread, &res);
if(ioctl(self->fd, AUDIO_DRAIN) < 0)
@@ -280,7 +302,7 @@ ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
-static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
+static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
@@ -305,7 +327,7 @@ static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory
return ALC_FALSE;
}
-static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
@@ -315,7 +337,7 @@ static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self
struct stat buf;
if(stat(solaris_driver, &buf) == 0)
#endif
- AppendAllDevicesList(solaris_device);
+ alstr_append_range(outnames, solaris_device, solaris_device+sizeof(solaris_device));
}
break;
diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/wasapi.c
index e8563d33..b974321b 100644
--- a/Alc/backends/mmdevapi.c
+++ b/Alc/backends/wasapi.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <memory.h>
+#include <wtypes.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <cguid.h>
@@ -40,9 +41,11 @@
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
#include "alstring.h"
+#include "converter.h"
#include "backends/base.h"
@@ -52,6 +55,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
#define MONO SPEAKER_FRONT_CENTER
#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
@@ -62,11 +66,21 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x
#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
#define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
-#define DEVNAME_TAIL " on OpenAL Soft"
+#define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
+
+#define DEVNAME_HEAD "OpenAL Soft on "
+
+
+/* Scales the given value using 64-bit integer math, ceiling the result. */
+static inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
+{
+ return (val*new_scale + old_scale-1) / old_scale;
+}
typedef struct {
al_string name;
+ al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
WCHAR *devid;
} DevMap;
TYPEDEF_VECTOR(DevMap, vector_DevMap)
@@ -75,11 +89,12 @@ static void clear_devlist(vector_DevMap *list)
{
#define CLEAR_DEVMAP(i) do { \
AL_STRING_DEINIT((i)->name); \
+ AL_STRING_DEINIT((i)->endpoint_guid); \
free((i)->devid); \
(i)->devid = NULL; \
} while(0)
VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
- VECTOR_RESIZE(*list, 0);
+ VECTOR_RESIZE(*list, 0, 0);
#undef CLEAR_DEVMAP
}
@@ -104,6 +119,15 @@ typedef struct {
#define WM_USER_Enumerate (WM_USER+5)
#define WM_USER_Last (WM_USER+5)
+static const char MessageStr[WM_USER_Last+1-WM_USER][20] = {
+ "Open Device",
+ "Reset Device",
+ "Start Device",
+ "Stop Device",
+ "Close Device",
+ "Enumerate Devices",
+};
+
static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
{
req->result = res;
@@ -119,16 +143,21 @@ static HRESULT WaitForResponse(ThreadRequest *req)
}
-static void get_device_name(IMMDevice *device, al_string *name)
+static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid)
{
IPropertyStore *ps;
PROPVARIANT pvname;
+ PROPVARIANT pvguid;
HRESULT hr;
+ alstr_copy_cstr(name, DEVNAME_HEAD);
+
hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
if(FAILED(hr))
{
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
+ alstr_append_cstr(name, "Unknown Device Name");
+ if(guid!=NULL)alstr_copy_cstr(guid, "Unknown Device GUID");
return;
}
@@ -136,13 +165,39 @@ static void get_device_name(IMMDevice *device, al_string *name)
hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
if(FAILED(hr))
+ {
WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
+ alstr_append_cstr(name, "Unknown Device Name");
+ }
else if(pvname.vt == VT_LPWSTR)
- al_string_copy_wcstr(name, pvname.pwszVal);
+ alstr_append_wcstr(name, pvname.pwszVal);
else
+ {
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
-
+ alstr_append_cstr(name, "Unknown Device Name");
+ }
PropVariantClear(&pvname);
+
+ if(guid!=NULL){
+ PropVariantInit(&pvguid);
+
+ hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid);
+ if(FAILED(hr))
+ {
+ WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
+ alstr_copy_cstr(guid, "Unknown Device GUID");
+ }
+ else if(pvguid.vt == VT_LPWSTR)
+ alstr_copy_wcstr(guid, pvguid.pwszVal);
+ else
+ {
+ WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt);
+ alstr_copy_cstr(guid, "Unknown Device GUID");
+ }
+
+ PropVariantClear(&pvguid);
+ }
+
IPropertyStore_Release(ps);
}
@@ -176,7 +231,7 @@ static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfac
}
-static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
+static void add_device(IMMDevice *device, const WCHAR *devid, vector_DevMap *list)
{
int count = 0;
al_string tmpname;
@@ -184,40 +239,39 @@ static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
AL_STRING_INIT(tmpname);
AL_STRING_INIT(entry.name);
+ AL_STRING_INIT(entry.endpoint_guid);
entry.devid = strdupW(devid);
- get_device_name(device, &tmpname);
+ get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid);
while(1)
{
const DevMap *iter;
- al_string_copy(&entry.name, tmpname);
- if(count == 0)
- al_string_append_cstr(&entry.name, DEVNAME_TAIL);
- else
+ alstr_copy(&entry.name, tmpname);
+ if(count != 0)
{
char str[64];
- snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
- al_string_append_cstr(&entry.name, str);
+ snprintf(str, sizeof(str), " #%d", count+1);
+ alstr_append_cstr(&entry.name, str);
}
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(*list)) break;
+ if(iter == VECTOR_END(*list)) break;
#undef MATCH_ENTRY
count++;
}
- TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid);
+ TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.endpoint_guid), entry.devid);
VECTOR_PUSH_BACK(*list, entry);
AL_STRING_DEINIT(tmpname);
}
-static LPWSTR get_device_id(IMMDevice *device)
+static WCHAR *get_device_id(IMMDevice *device)
{
- LPWSTR devid;
+ WCHAR *devid;
HRESULT hr;
hr = IMMDevice_GetId(device, &devid);
@@ -234,7 +288,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
{
IMMDeviceCollection *coll;
IMMDevice *defdev = NULL;
- LPWSTR defdevid = NULL;
+ WCHAR *defdevid = NULL;
HRESULT hr;
UINT count;
UINT i;
@@ -251,11 +305,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
if(SUCCEEDED(hr) && count > 0)
{
clear_devlist(list);
- if(!VECTOR_RESERVE(*list, count))
- {
- IMMDeviceCollection_Release(coll);
- return E_OUTOFMEMORY;
- }
+ VECTOR_RESIZE(*list, 0, count);
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
eMultimedia, &defdev);
@@ -270,7 +320,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
for(i = 0;i < count;++i)
{
IMMDevice *device;
- LPWSTR devid;
+ WCHAR *devid;
hr = IMMDeviceCollection_Item(coll, i, &device);
if(FAILED(hr)) continue;
@@ -278,7 +328,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
devid = get_device_id(device);
if(devid)
{
- if(wcscmp(devid, defdevid) != 0)
+ if(!defdevid || wcscmp(devid, defdevid) != 0)
add_device(device, devid, list);
CoTaskMemFree(devid);
}
@@ -294,51 +344,51 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
/* Proxy interface used by the message handler. */
-struct ALCmmdevProxyVtable;
+struct ALCwasapiProxyVtable;
-typedef struct ALCmmdevProxy {
- const struct ALCmmdevProxyVtable *vtbl;
-} ALCmmdevProxy;
+typedef struct ALCwasapiProxy {
+ const struct ALCwasapiProxyVtable *vtbl;
+} ALCwasapiProxy;
-struct ALCmmdevProxyVtable {
- HRESULT (*const openProxy)(ALCmmdevProxy*);
- void (*const closeProxy)(ALCmmdevProxy*);
+struct ALCwasapiProxyVtable {
+ HRESULT (*const openProxy)(ALCwasapiProxy*);
+ void (*const closeProxy)(ALCwasapiProxy*);
- HRESULT (*const resetProxy)(ALCmmdevProxy*);
- HRESULT (*const startProxy)(ALCmmdevProxy*);
- void (*const stopProxy)(ALCmmdevProxy*);
+ HRESULT (*const resetProxy)(ALCwasapiProxy*);
+ HRESULT (*const startProxy)(ALCwasapiProxy*);
+ void (*const stopProxy)(ALCwasapiProxy*);
};
-#define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
-DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
-DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
-DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
-DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
-DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
+#define DEFINE_ALCWASAPIPROXY_VTABLE(T) \
+DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, openProxy) \
+DECLARE_THUNK(T, ALCwasapiProxy, void, closeProxy) \
+DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, resetProxy) \
+DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, startProxy) \
+DECLARE_THUNK(T, ALCwasapiProxy, void, stopProxy) \
\
-static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
- T##_ALCmmdevProxy_openProxy, \
- T##_ALCmmdevProxy_closeProxy, \
- T##_ALCmmdevProxy_resetProxy, \
- T##_ALCmmdevProxy_startProxy, \
- T##_ALCmmdevProxy_stopProxy, \
+static const struct ALCwasapiProxyVtable T##_ALCwasapiProxy_vtable = { \
+ T##_ALCwasapiProxy_openProxy, \
+ T##_ALCwasapiProxy_closeProxy, \
+ T##_ALCwasapiProxy_resetProxy, \
+ T##_ALCwasapiProxy_startProxy, \
+ T##_ALCwasapiProxy_stopProxy, \
}
-static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
-static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
+static void ALCwasapiProxy_Construct(ALCwasapiProxy* UNUSED(self)) { }
+static void ALCwasapiProxy_Destruct(ALCwasapiProxy* UNUSED(self)) { }
-static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
+static DWORD CALLBACK ALCwasapiProxy_messageHandler(void *ptr)
{
ThreadRequest *req = ptr;
IMMDeviceEnumerator *Enumerator;
ALuint deviceCount = 0;
- ALCmmdevProxy *proxy;
+ ALCwasapiProxy *proxy;
HRESULT hr, cohr;
MSG msg;
TRACE("Starting message thread\n");
- cohr = CoInitialize(NULL);
+ cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(cohr))
{
WARN("Failed to initialize COM: 0x%08lx\n", cohr);
@@ -372,16 +422,20 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
TRACE("Starting message loop\n");
while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
{
- TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
+ TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
+ (msg.message >= WM_USER && msg.message <= WM_USER_Last) ?
+ MessageStr[msg.message-WM_USER] : "Unknown",
+ msg.message, (void*)msg.lParam, (void*)msg.wParam
+ );
switch(msg.message)
{
case WM_USER_OpenDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
hr = cohr = S_OK;
if(++deviceCount == 1)
- hr = cohr = CoInitialize(NULL);
+ hr = cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(SUCCEEDED(hr))
hr = V0(proxy,openProxy)();
if(FAILED(hr))
@@ -395,7 +449,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
case WM_USER_ResetDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
hr = V0(proxy,resetProxy)();
ReturnMsgResponse(req, hr);
@@ -403,7 +457,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
case WM_USER_StartDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
hr = V0(proxy,startProxy)();
ReturnMsgResponse(req, hr);
@@ -411,7 +465,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
case WM_USER_StopDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
V0(proxy,stopProxy)();
ReturnMsgResponse(req, S_OK);
@@ -419,7 +473,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
case WM_USER_CloseDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
V0(proxy,closeProxy)();
if(--deviceCount == 0)
@@ -433,7 +487,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
hr = cohr = S_OK;
if(++deviceCount == 1)
- hr = cohr = CoInitialize(NULL);
+ hr = cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(SUCCEEDED(hr))
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
if(SUCCEEDED(hr))
@@ -466,9 +520,9 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
}
-typedef struct ALCmmdevPlayback {
+typedef struct ALCwasapiPlayback {
DERIVE_FROM_TYPE(ALCbackend);
- DERIVE_FROM_TYPE(ALCmmdevProxy);
+ DERIVE_FROM_TYPE(ALCwasapiProxy);
WCHAR *devid;
@@ -479,43 +533,42 @@ typedef struct ALCmmdevPlayback {
HANDLE MsgEvent;
- volatile UINT32 Padding;
+ ATOMIC(UINT32) Padding;
- volatile int killNow;
+ ATOMIC(int) killNow;
althrd_t thread;
-} ALCmmdevPlayback;
-
-static int ALCmmdevPlayback_mixerProc(void *arg);
-
-static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
-static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
-static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
-static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
-static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
-static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
-static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
-static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
-static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
-static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
-static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
-static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
-static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
-static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self);
-static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
-static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
-
-DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
-DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
-
-
-static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
+} ALCwasapiPlayback;
+
+static int ALCwasapiPlayback_mixerProc(void *arg);
+
+static void ALCwasapiPlayback_Construct(ALCwasapiPlayback *self, ALCdevice *device);
+static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback *self);
+static ALCenum ALCwasapiPlayback_open(ALCwasapiPlayback *self, const ALCchar *name);
+static HRESULT ALCwasapiPlayback_openProxy(ALCwasapiPlayback *self);
+static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback *self);
+static ALCboolean ALCwasapiPlayback_reset(ALCwasapiPlayback *self);
+static HRESULT ALCwasapiPlayback_resetProxy(ALCwasapiPlayback *self);
+static ALCboolean ALCwasapiPlayback_start(ALCwasapiPlayback *self);
+static HRESULT ALCwasapiPlayback_startProxy(ALCwasapiPlayback *self);
+static void ALCwasapiPlayback_stop(ALCwasapiPlayback *self);
+static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback *self);
+static DECLARE_FORWARD2(ALCwasapiPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback *self);
+static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwasapiPlayback)
+
+DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiPlayback);
+DEFINE_ALCBACKEND_VTABLE(ALCwasapiPlayback);
+
+
+static void ALCwasapiPlayback_Construct(ALCwasapiPlayback *self, ALCdevice *device)
{
- SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
- SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
+ SET_VTABLE2(ALCwasapiPlayback, ALCbackend, self);
+ SET_VTABLE2(ALCwasapiPlayback, ALCwasapiProxy, self);
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
- ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy, self));
self->devid = NULL;
@@ -526,13 +579,30 @@ static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device
self->MsgEvent = NULL;
- self->Padding = 0;
+ ATOMIC_INIT(&self->Padding, 0);
- self->killNow = 0;
+ ATOMIC_INIT(&self->killNow, 0);
}
-static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
+static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback *self)
{
+ if(self->MsgEvent)
+ {
+ ThreadRequest req = { self->MsgEvent, 0 };
+ if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
+ (void)WaitForResponse(&req);
+
+ CloseHandle(self->MsgEvent);
+ self->MsgEvent = NULL;
+ }
+
+ if(self->NotifyEvent)
+ CloseHandle(self->NotifyEvent);
+ self->NotifyEvent = NULL;
+
+ free(self->devid);
+ self->devid = NULL;
+
if(self->NotifyEvent != NULL)
CloseHandle(self->NotifyEvent);
self->NotifyEvent = NULL;
@@ -543,26 +613,26 @@ static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
free(self->devid);
self->devid = NULL;
- ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy, self));
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
-FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
+FORCE_ALIGN static int ALCwasapiPlayback_mixerProc(void *arg)
{
- ALCmmdevPlayback *self = arg;
+ ALCwasapiPlayback *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
UINT32 buffer_len, written;
ALuint update_size, len;
BYTE *buffer;
HRESULT hr;
- hr = CoInitialize(NULL);
+ hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(hr))
{
- ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
+ ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "COM init failed: 0x%08lx", hr);
V0(device->Backend,unlock)();
return 1;
}
@@ -572,18 +642,18 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
update_size = device->UpdateSize;
buffer_len = update_size * device->NumUpdates;
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed))
{
hr = IAudioClient_GetCurrentPadding(self->client, &written);
if(FAILED(hr))
{
ERR("Failed to get padding: 0x%08lx\n", hr);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to retrieve buffer padding: 0x%08lx", hr);
V0(device->Backend,unlock)();
break;
}
- self->Padding = written;
+ ATOMIC_STORE(&self->Padding, written, almemory_order_relaxed);
len = buffer_len - written;
if(len < update_size)
@@ -599,22 +669,22 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
if(SUCCEEDED(hr))
{
- V0(device->Backend,lock)();
+ ALCwasapiPlayback_lock(self);
aluMixData(device, buffer, len);
- self->Padding = written + len;
- V0(device->Backend,unlock)();
+ ATOMIC_STORE(&self->Padding, written + len, almemory_order_relaxed);
+ ALCwasapiPlayback_unlock(self);
hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
}
if(FAILED(hr))
{
ERR("Failed to buffer data: 0x%08lx\n", hr);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to send playback samples: 0x%08lx", hr);
V0(device->Backend,unlock)();
break;
}
}
- self->Padding = 0;
+ ATOMIC_STORE(&self->Padding, 0, almemory_order_release);
CoUninitialize();
return 0;
@@ -660,13 +730,12 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *
return ALC_TRUE;
}
-
-static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
+static ALCenum ALCwasapiPlayback_open(ALCwasapiPlayback *self, const ALCchar *deviceName)
{
HRESULT hr = S_OK;
- self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
{
ERR("Failed to create message events: %lu\n", GetLastError());
@@ -687,18 +756,32 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
}
hr = E_FAIL;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
+ alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+#undef MATCH_NAME
+ if(iter == VECTOR_END(PlaybackDevices))
+ {
+ int len;
+ if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
+ {
+ WCHAR *wname = calloc(sizeof(WCHAR), len);
+ MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
+#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
+ VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
+#undef MATCH_NAME
+ free(wname);
+ }
+ }
+ if(iter == VECTOR_END(PlaybackDevices))
WARN("Failed to find device name matching \"%s\"\n", deviceName);
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
self->devid = strdupW(iter->devid);
- al_string_copy(&device->DeviceName, iter->name);
+ alstr_copy(&device->DeviceName, iter->name);
hr = S_OK;
}
-#undef MATCH_NAME
}
}
@@ -707,7 +790,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
ThreadRequest req = { self->MsgEvent, 0 };
hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
else
ERR("Failed to post thread message: %lu\n", GetLastError());
@@ -732,7 +815,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
return ALC_NO_ERROR;
}
-static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
+static HRESULT ALCwasapiPlayback_openProxy(ALCwasapiPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
void *ptr;
@@ -754,11 +837,8 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
if(SUCCEEDED(hr))
{
self->client = ptr;
- if(al_string_empty(device->DeviceName))
- {
- get_device_name(self->mmdev, &device->DeviceName);
- al_string_append_cstr(&device->DeviceName, DEVNAME_TAIL);
- }
+ if(alstr_empty(device->DeviceName))
+ get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
}
if(FAILED(hr))
@@ -772,24 +852,7 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
}
-static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
-{
- ThreadRequest req = { self->MsgEvent, 0 };
-
- if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
- (void)WaitForResponse(&req);
-
- CloseHandle(self->MsgEvent);
- self->MsgEvent = NULL;
-
- CloseHandle(self->NotifyEvent);
- self->NotifyEvent = NULL;
-
- free(self->devid);
- self->devid = NULL;
-}
-
-static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
+static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback *self)
{
if(self->client)
IAudioClient_Release(self->client);
@@ -801,18 +864,18 @@ static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
}
-static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
+static ALCboolean ALCwasapiPlayback_reset(ALCwasapiPlayback *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
HRESULT hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
}
-static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
+static HRESULT ALCwasapiPlayback_resetProxy(ALCwasapiPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
EndpointFormFactor formfactor = UnknownFormFactor;
@@ -850,8 +913,8 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
CoTaskMemFree(wfx);
wfx = NULL;
- buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
- device->Frequency-1) / device->Frequency;
+ buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
+ device->Frequency);
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
device->Frequency = OutputType.Format.nSamplesPerSec;
@@ -881,7 +944,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
OutputType.Format.nChannels = 1;
OutputType.dwChannelMask = MONO;
break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -1022,7 +1085,9 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
}
get_device_formfactor(self->mmdev, &formfactor);
- device->IsHeadphones = (device->FmtChans == DevFmtStereo && formfactor == Headphones);
+ device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
+ (formfactor == Headphones || formfactor == Headset)
+ );
SetDefaultWFXChannelOrder(device);
@@ -1038,7 +1103,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
if(SUCCEEDED(hr))
{
- min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
+ min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC);
/* Find the nearest multiple of the period size to the update size */
if(min_len < device->UpdateSize)
min_len *= (device->UpdateSize + min_len/2)/min_len;
@@ -1070,18 +1135,18 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
}
-static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
+static ALCboolean ALCwasapiPlayback_start(ALCwasapiPlayback *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
HRESULT hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
}
-static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
+static HRESULT ALCwasapiPlayback_startProxy(ALCwasapiPlayback *self)
{
HRESULT hr;
void *ptr;
@@ -1096,8 +1161,8 @@ static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
if(SUCCEEDED(hr))
{
self->render = ptr;
- self->killNow = 0;
- if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
+ ATOMIC_STORE(&self->killNow, 0, almemory_order_release);
+ if(althrd_create(&self->thread, ALCwasapiPlayback_mixerProc, self) != althrd_success)
{
if(self->render)
IAudioRenderClient_Release(self->render);
@@ -1112,21 +1177,21 @@ static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
}
-static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
+static void ALCwasapiPlayback_stop(ALCwasapiPlayback *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
- if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
(void)WaitForResponse(&req);
}
-static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
+static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback *self)
{
int res;
if(!self->render)
return;
- self->killNow = 1;
+ ATOMIC_STORE_SEQ(&self->killNow, 1);
althrd_join(self->thread, &res);
IAudioRenderClient_Release(self->render);
@@ -1135,16 +1200,24 @@ static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
}
-static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self)
+static ClockLatency ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return (ALint64)self->Padding * 1000000000 / device->Frequency;
+ ClockLatency ret;
+
+ ALCwasapiPlayback_lock(self);
+ ret.ClockTime = GetDeviceClockTime(device);
+ ret.Latency = ATOMIC_LOAD(&self->Padding, almemory_order_relaxed) * DEVICE_CLOCK_RES /
+ device->Frequency;
+ ALCwasapiPlayback_unlock(self);
+
+ return ret;
}
-typedef struct ALCmmdevCapture {
+typedef struct ALCwasapiCapture {
DERIVE_FROM_TYPE(ALCbackend);
- DERIVE_FROM_TYPE(ALCmmdevProxy);
+ DERIVE_FROM_TYPE(ALCwasapiProxy);
WCHAR *devid;
@@ -1155,43 +1228,44 @@ typedef struct ALCmmdevCapture {
HANDLE MsgEvent;
+ ChannelConverter *ChannelConv;
+ SampleConverter *SampleConv;
ll_ringbuffer_t *Ring;
- volatile int killNow;
+ ATOMIC(int) killNow;
althrd_t thread;
-} ALCmmdevCapture;
-
-static int ALCmmdevCapture_recordProc(void *arg);
-
-static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
-static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
-static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
-static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
-static void ALCmmdevCapture_close(ALCmmdevCapture *self);
-static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
-static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
-static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
-static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
-static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
-static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
-static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
-static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
-static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
-static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALint64, getLatency)
-static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
-static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
-
-DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
-DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
-
-
-static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
+} ALCwasapiCapture;
+
+static int ALCwasapiCapture_recordProc(void *arg);
+
+static void ALCwasapiCapture_Construct(ALCwasapiCapture *self, ALCdevice *device);
+static void ALCwasapiCapture_Destruct(ALCwasapiCapture *self);
+static ALCenum ALCwasapiCapture_open(ALCwasapiCapture *self, const ALCchar *name);
+static HRESULT ALCwasapiCapture_openProxy(ALCwasapiCapture *self);
+static void ALCwasapiCapture_closeProxy(ALCwasapiCapture *self);
+static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, ALCboolean, reset)
+static HRESULT ALCwasapiCapture_resetProxy(ALCwasapiCapture *self);
+static ALCboolean ALCwasapiCapture_start(ALCwasapiCapture *self);
+static HRESULT ALCwasapiCapture_startProxy(ALCwasapiCapture *self);
+static void ALCwasapiCapture_stop(ALCwasapiCapture *self);
+static void ALCwasapiCapture_stopProxy(ALCwasapiCapture *self);
+static ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALuint ALCwasapiCapture_availableSamples(ALCwasapiCapture *self);
+static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwasapiCapture)
+
+DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiCapture);
+DEFINE_ALCBACKEND_VTABLE(ALCwasapiCapture);
+
+
+static void ALCwasapiCapture_Construct(ALCwasapiCapture *self, ALCdevice *device)
{
- SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
- SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
+ SET_VTABLE2(ALCwasapiCapture, ALCbackend, self);
+ SET_VTABLE2(ALCwasapiCapture, ALCwasapiProxy, self);
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
- ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy, self));
self->devid = NULL;
@@ -1202,50 +1276,64 @@ static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
self->MsgEvent = NULL;
+ self->ChannelConv = NULL;
+ self->SampleConv = NULL;
self->Ring = NULL;
- self->killNow = 0;
+ ATOMIC_INIT(&self->killNow, 0);
}
-static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
+static void ALCwasapiCapture_Destruct(ALCwasapiCapture *self)
{
- ll_ringbuffer_free(self->Ring);
- self->Ring = NULL;
+ if(self->MsgEvent)
+ {
+ ThreadRequest req = { self->MsgEvent, 0 };
+ if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
+ (void)WaitForResponse(&req);
+
+ CloseHandle(self->MsgEvent);
+ self->MsgEvent = NULL;
+ }
if(self->NotifyEvent != NULL)
CloseHandle(self->NotifyEvent);
self->NotifyEvent = NULL;
- if(self->MsgEvent != NULL)
- CloseHandle(self->MsgEvent);
- self->MsgEvent = NULL;
+
+ ll_ringbuffer_free(self->Ring);
+ self->Ring = NULL;
+
+ DestroySampleConverter(&self->SampleConv);
+ DestroyChannelConverter(&self->ChannelConv);
free(self->devid);
self->devid = NULL;
- ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy, self));
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
-FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
+FORCE_ALIGN int ALCwasapiCapture_recordProc(void *arg)
{
- ALCmmdevCapture *self = arg;
+ ALCwasapiCapture *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ALfloat *samples = NULL;
+ size_t samplesmax = 0;
HRESULT hr;
- hr = CoInitialize(NULL);
+ hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(hr))
{
- ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
+ ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "COM init failed: 0x%08lx", hr);
V0(device->Backend,unlock)();
return 1;
}
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed))
{
UINT32 avail;
DWORD res;
@@ -1253,39 +1341,81 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
if(FAILED(hr))
ERR("Failed to get next packet size: 0x%08lx\n", hr);
- else while(avail > 0 && SUCCEEDED(hr))
+ else if(avail > 0)
{
UINT32 numsamples;
DWORD flags;
- BYTE *data;
+ BYTE *rdata;
hr = IAudioCaptureClient_GetBuffer(self->capture,
- &data, &numsamples, &flags, NULL, NULL
+ &rdata, &numsamples, &flags, NULL, NULL
);
if(FAILED(hr))
- {
ERR("Failed to get capture buffer: 0x%08lx\n", hr);
- break;
- }
-
- ll_ringbuffer_write(self->Ring, (char*)data, numsamples);
-
- hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
- if(FAILED(hr))
+ else
{
- ERR("Failed to release capture buffer: 0x%08lx\n", hr);
- break;
+ ll_ringbuffer_data_t data[2];
+ size_t dstframes = 0;
+
+ if(self->ChannelConv)
+ {
+ if(samplesmax < numsamples)
+ {
+ size_t newmax = RoundUp(numsamples, 4096);
+ ALfloat *tmp = al_calloc(DEF_ALIGN, newmax*2*sizeof(ALfloat));
+ al_free(samples);
+ samples = tmp;
+ samplesmax = newmax;
+ }
+ ChannelConverterInput(self->ChannelConv, rdata, samples, numsamples);
+ rdata = (BYTE*)samples;
+ }
+
+ ll_ringbuffer_get_write_vector(self->Ring, data);
+
+ if(self->SampleConv)
+ {
+ const ALvoid *srcdata = rdata;
+ ALsizei srcframes = numsamples;
+
+ dstframes = SampleConverterInput(self->SampleConv,
+ &srcdata, &srcframes, data[0].buf, (ALsizei)minz(data[0].len, INT_MAX)
+ );
+ if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0)
+ {
+ /* If some source samples remain, all of the first dest
+ * block was filled, and there's space in the second
+ * dest block, do another run for the second block.
+ */
+ dstframes += SampleConverterInput(self->SampleConv,
+ &srcdata, &srcframes, data[1].buf, (ALsizei)minz(data[1].len, INT_MAX)
+ );
+ }
+ }
+ else
+ {
+ ALuint framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType,
+ device->AmbiOrder);
+ size_t len1 = minz(data[0].len, numsamples);
+ size_t len2 = minz(data[1].len, numsamples-len1);
+
+ memcpy(data[0].buf, rdata, len1*framesize);
+ if(len2 > 0)
+ memcpy(data[1].buf, rdata+len1*framesize, len2*framesize);
+ dstframes = len1 + len2;
+ }
+
+ ll_ringbuffer_write_advance(self->Ring, dstframes);
+
+ hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
+ if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
}
-
- hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
- if(FAILED(hr))
- ERR("Failed to get next packet size: 0x%08lx\n", hr);
}
if(FAILED(hr))
{
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to capture samples: 0x%08lx", hr);
V0(device->Backend,unlock)();
break;
}
@@ -1295,17 +1425,21 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
}
+ al_free(samples);
+ samples = NULL;
+ samplesmax = 0;
+
CoUninitialize();
return 0;
}
-static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
+static ALCenum ALCwasapiCapture_open(ALCwasapiCapture *self, const ALCchar *deviceName)
{
HRESULT hr = S_OK;
- self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
{
ERR("Failed to create message events: %lu\n", GetLastError());
@@ -1326,18 +1460,32 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
}
hr = E_FAIL;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
+ alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
- if(iter == VECTOR_ITER_END(CaptureDevices))
+#undef MATCH_NAME
+ if(iter == VECTOR_END(CaptureDevices))
+ {
+ int len;
+ if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
+ {
+ WCHAR *wname = calloc(sizeof(WCHAR), len);
+ MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
+#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
+ VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+#undef MATCH_NAME
+ free(wname);
+ }
+ }
+ if(iter == VECTOR_END(CaptureDevices))
WARN("Failed to find device name matching \"%s\"\n", deviceName);
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
self->devid = strdupW(iter->devid);
- al_string_copy(&device->DeviceName, iter->name);
+ alstr_copy(&device->DeviceName, iter->name);
hr = S_OK;
}
-#undef MATCH_NAME
}
}
@@ -1346,7 +1494,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
ThreadRequest req = { self->MsgEvent, 0 };
hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
else
ERR("Failed to post thread message: %lu\n", GetLastError());
@@ -1372,14 +1520,13 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
ThreadRequest req = { self->MsgEvent, 0 };
hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
else
ERR("Failed to post thread message: %lu\n", GetLastError());
if(FAILED(hr))
{
- ALCmmdevCapture_close(self);
if(hr == E_OUTOFMEMORY)
return ALC_OUT_OF_MEMORY;
return ALC_INVALID_VALUE;
@@ -1389,7 +1536,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
return ALC_NO_ERROR;
}
-static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
+static HRESULT ALCwasapiCapture_openProxy(ALCwasapiCapture *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
void *ptr;
@@ -1411,11 +1558,8 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
if(SUCCEEDED(hr))
{
self->client = ptr;
- if(al_string_empty(device->DeviceName))
- {
- get_device_name(self->mmdev, &device->DeviceName);
- al_string_append_cstr(&device->DeviceName, DEVNAME_TAIL);
- }
+ if(alstr_empty(device->DeviceName))
+ get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
}
if(FAILED(hr))
@@ -1429,27 +1573,7 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
}
-static void ALCmmdevCapture_close(ALCmmdevCapture *self)
-{
- ThreadRequest req = { self->MsgEvent, 0 };
-
- if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
- (void)WaitForResponse(&req);
-
- ll_ringbuffer_free(self->Ring);
- self->Ring = NULL;
-
- CloseHandle(self->MsgEvent);
- self->MsgEvent = NULL;
-
- CloseHandle(self->NotifyEvent);
- self->NotifyEvent = NULL;
-
- free(self->devid);
- self->devid = NULL;
-}
-
-static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
+static void ALCwasapiCapture_closeProxy(ALCwasapiCapture *self)
{
if(self->client)
IAudioClient_Release(self->client);
@@ -1461,11 +1585,12 @@ static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
}
-static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
+static HRESULT ALCwasapiCapture_resetProxy(ALCwasapiCapture *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
WAVEFORMATEXTENSIBLE OutputType;
WAVEFORMATEX *wfx = NULL;
+ enum DevFmtType srcType;
REFERENCE_TIME buf_time;
UINT32 buffer_len;
void *ptr = NULL;
@@ -1483,8 +1608,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
}
self->client = ptr;
- buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
- device->Frequency-1) / device->Frequency;
+ buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
+ device->Frequency);
+ // Make sure buffer is at least 100ms in size
+ buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
+ device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) /
+ device->NumUpdates;
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
switch(device->FmtChans)
@@ -1518,38 +1647,33 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
OutputType.dwChannelMask = X7DOT1;
break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
return E_FAIL;
}
switch(device->FmtType)
{
+ /* NOTE: Signedness doesn't matter, the converter will handle it. */
+ case DevFmtByte:
case DevFmtUByte:
OutputType.Format.wBitsPerSample = 8;
- OutputType.Samples.wValidBitsPerSample = 8;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtShort:
+ case DevFmtUShort:
OutputType.Format.wBitsPerSample = 16;
- OutputType.Samples.wValidBitsPerSample = 16;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtInt:
+ case DevFmtUInt:
OutputType.Format.wBitsPerSample = 32;
- OutputType.Samples.wValidBitsPerSample = 32;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtFloat:
OutputType.Format.wBitsPerSample = 32;
- OutputType.Samples.wValidBitsPerSample = 32;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
break;
-
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
- return E_FAIL;
}
+ OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
OutputType.Format.nSamplesPerSec = device->Frequency;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
@@ -1567,26 +1691,107 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
return hr;
}
- /* FIXME: We should do conversion/resampling if we didn't get a matching format. */
- if(wfx->nSamplesPerSec != OutputType.Format.nSamplesPerSec ||
- wfx->wBitsPerSample != OutputType.Format.wBitsPerSample ||
- wfx->nChannels != OutputType.Format.nChannels ||
- wfx->nBlockAlign != OutputType.Format.nBlockAlign)
+ DestroySampleConverter(&self->SampleConv);
+ DestroyChannelConverter(&self->ChannelConv);
+
+ if(wfx != NULL)
{
- ERR("Did not get matching format, wanted: %s %s %uhz, got: %d channel(s) %d-bit %luhz\n",
- DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), device->Frequency,
- wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec);
+ if(!(wfx->nChannels == OutputType.Format.nChannels ||
+ (wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
+ (wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
+ {
+ ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
+ wfx->nSamplesPerSec);
+ CoTaskMemFree(wfx);
+ return E_FAIL;
+ }
+
+ if(!MakeExtensible(&OutputType, wfx))
+ {
+ CoTaskMemFree(wfx);
+ return E_FAIL;
+ }
CoTaskMemFree(wfx);
- return E_FAIL;
+ wfx = NULL;
}
- if(!MakeExtensible(&OutputType, wfx))
+ if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
{
- CoTaskMemFree(wfx);
+ if(OutputType.Format.wBitsPerSample == 8)
+ srcType = DevFmtUByte;
+ else if(OutputType.Format.wBitsPerSample == 16)
+ srcType = DevFmtShort;
+ else if(OutputType.Format.wBitsPerSample == 32)
+ srcType = DevFmtInt;
+ else
+ {
+ ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
+ return E_FAIL;
+ }
+ }
+ else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+ {
+ if(OutputType.Format.wBitsPerSample == 32)
+ srcType = DevFmtFloat;
+ else
+ {
+ ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
+ return E_FAIL;
+ }
+ }
+ else
+ {
+ ERR("Unhandled format sub-type\n");
return E_FAIL;
}
- CoTaskMemFree(wfx);
- wfx = NULL;
+
+ if(device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
+ {
+ self->ChannelConv = CreateChannelConverter(srcType, DevFmtStereo,
+ device->FmtChans);
+ if(!self->ChannelConv)
+ {
+ ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
+ return E_FAIL;
+ }
+ TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
+ /* The channel converter always outputs float, so change the input type
+ * for the resampler/type-converter.
+ */
+ srcType = DevFmtFloat;
+ }
+ else if(device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
+ {
+ self->ChannelConv = CreateChannelConverter(srcType, DevFmtMono,
+ device->FmtChans);
+ if(!self->ChannelConv)
+ {
+ ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
+ return E_FAIL;
+ }
+ TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
+ srcType = DevFmtFloat;
+ }
+
+ if(device->Frequency != OutputType.Format.nSamplesPerSec || device->FmtType != srcType)
+ {
+ self->SampleConv = CreateSampleConverter(
+ srcType, device->FmtType, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder),
+ OutputType.Format.nSamplesPerSec, device->Frequency
+ );
+ if(!self->SampleConv)
+ {
+ ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
+ return E_FAIL;
+ }
+ TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
+ }
hr = IAudioClient_Initialize(self->client,
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
@@ -1605,9 +1810,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
return hr;
}
- buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
+ buffer_len = maxu(device->UpdateSize*device->NumUpdates, buffer_len);
ll_ringbuffer_free(self->Ring);
- self->Ring = ll_ringbuffer_create(buffer_len, OutputType.Format.nBlockAlign);
+ self->Ring = ll_ringbuffer_create(buffer_len,
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
+ false
+ );
if(!self->Ring)
{
ERR("Failed to allocate capture ring buffer\n");
@@ -1625,18 +1833,18 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
}
-static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
+static ALCboolean ALCwasapiCapture_start(ALCwasapiCapture *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
HRESULT hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
}
-static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
+static HRESULT ALCwasapiCapture_startProxy(ALCwasapiCapture *self)
{
HRESULT hr;
void *ptr;
@@ -1653,8 +1861,8 @@ static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
if(SUCCEEDED(hr))
{
self->capture = ptr;
- self->killNow = 0;
- if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
+ ATOMIC_STORE(&self->killNow, 0, almemory_order_release);
+ if(althrd_create(&self->thread, ALCwasapiCapture_recordProc, self) != althrd_success)
{
ERR("Failed to start thread\n");
IAudioCaptureClient_Release(self->capture);
@@ -1673,21 +1881,21 @@ static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
}
-static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
+static void ALCwasapiCapture_stop(ALCwasapiCapture *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
- if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
(void)WaitForResponse(&req);
}
-static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
+static void ALCwasapiCapture_stopProxy(ALCwasapiCapture *self)
{
int res;
if(!self->capture)
return;
- self->killNow = 1;
+ ATOMIC_STORE_SEQ(&self->killNow, 1);
althrd_join(self->thread, &res);
IAudioCaptureClient_Release(self->capture);
@@ -1697,72 +1905,62 @@ static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
}
-ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
+ALuint ALCwasapiCapture_availableSamples(ALCwasapiCapture *self)
{
return (ALuint)ll_ringbuffer_read_space(self->Ring);
}
-ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
+ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer, ALCuint samples)
{
- if(ALCmmdevCapture_availableSamples(self) < samples)
+ if(ALCwasapiCapture_availableSamples(self) < samples)
return ALC_INVALID_VALUE;
ll_ringbuffer_read(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
-static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
-static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
-
-typedef struct ALCmmdevBackendFactory {
+typedef struct ALCwasapiBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
-} ALCmmdevBackendFactory;
-#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
+} ALCwasapiBackendFactory;
+#define ALCWASAPIBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwasapiBackendFactory, ALCbackendFactory) } }
-static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
-static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
-static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
-static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
-static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory *self);
+static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory *self);
+static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory *self, ALCbackend_Type type);
+static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwasapiBackendFactory);
-static BOOL MMDevApiLoad(void)
+static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory* UNUSED(self))
{
static HRESULT InitResult;
+
+ VECTOR_INIT(PlaybackDevices);
+ VECTOR_INIT(CaptureDevices);
+
if(!ThreadHdl)
{
ThreadRequest req;
InitResult = E_FAIL;
- req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
if(req.FinishedEvt == NULL)
ERR("Failed to create event: %lu\n", GetLastError());
else
{
- ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
+ ThreadHdl = CreateThread(NULL, 0, ALCwasapiProxy_messageHandler, &req, 0, &ThreadID);
if(ThreadHdl != NULL)
InitResult = WaitForResponse(&req);
CloseHandle(req.FinishedEvt);
}
}
- return SUCCEEDED(InitResult);
-}
-static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
-{
- VECTOR_INIT(PlaybackDevices);
- VECTOR_INIT(CaptureDevices);
-
- if(!MMDevApiLoad())
- return ALC_FALSE;
- return ALC_TRUE;
+ return SUCCEEDED(InitResult) ? ALC_TRUE : ALC_FALSE;
}
-static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
+static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory* UNUSED(self))
{
clear_devlist(&PlaybackDevices);
VECTOR_DEINIT(PlaybackDevices);
@@ -1779,23 +1977,18 @@ static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
}
}
-static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
+static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory* UNUSED(self), ALCbackend_Type type)
{
- /* TODO: Disable capture with mmdevapi for now, since it doesn't do any
- * rechanneling or resampling; if the device is configured for 48000hz
- * stereo input, for example, and the app asks for 22050hz mono,
- * initialization will fail.
- */
- if(type == ALCbackend_Playback /*|| type == ALCbackend_Capture*/)
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
-static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
ThreadRequest req = { NULL, 0 };
- req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
if(req.FinishedEvt == NULL)
ERR("Failed to create event: %lu\n", GetLastError());
else
@@ -1805,32 +1998,38 @@ static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), e
hr = WaitForResponse(&req);
if(SUCCEEDED(hr)) switch(type)
{
+#define APPEND_OUTNAME(e) do { \
+ if(!alstr_empty((e)->name)) \
+ alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
+ VECTOR_END((e)->name)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
- VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+ VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
- VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+ VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
CloseHandle(req.FinishedEvt);
req.FinishedEvt = NULL;
}
}
-static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
- ALCmmdevPlayback *backend;
- NEW_OBJ(backend, ALCmmdevPlayback)(device);
+ ALCwasapiPlayback *backend;
+ NEW_OBJ(backend, ALCwasapiPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
- ALCmmdevCapture *backend;
- NEW_OBJ(backend, ALCmmdevCapture)(device);
+ ALCwasapiCapture *backend;
+ NEW_OBJ(backend, ALCwasapiCapture)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
@@ -1839,8 +2038,8 @@ static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory*
}
-ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
+ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void)
{
- static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
+ static ALCwasapiBackendFactory factory = ALCWASAPIBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
diff --git a/Alc/backends/wave.c b/Alc/backends/wave.c
index 6b47c611..390b2a5f 100644
--- a/Alc/backends/wave.c
+++ b/Alc/backends/wave.c
@@ -27,6 +27,7 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
#include "threads.h"
#include "compat.h"
@@ -56,16 +57,14 @@ static const ALubyte SUBTYPE_BFORMAT_FLOAT[] = {
static void fwrite16le(ALushort val, FILE *f)
{
- fputc(val&0xff, f);
- fputc((val>>8)&0xff, f);
+ ALubyte data[2] = { val&0xff, (val>>8)&0xff };
+ fwrite(data, 1, 2, f);
}
static void fwrite32le(ALuint val, FILE *f)
{
- fputc(val&0xff, f);
- fputc((val>>8)&0xff, f);
- fputc((val>>16)&0xff, f);
- fputc((val>>24)&0xff, f);
+ ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff };
+ fwrite(data, 1, 4, f);
}
@@ -78,22 +77,21 @@ typedef struct ALCwaveBackend {
ALvoid *mBuffer;
ALuint mSize;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCwaveBackend;
static int ALCwaveBackend_mixerProc(void *ptr);
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, Destruct)
+static void ALCwaveBackend_Destruct(ALCwaveBackend *self);
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name);
-static void ALCwaveBackend_close(ALCwaveBackend *self);
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self);
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self);
static void ALCwaveBackend_stop(ALCwaveBackend *self);
static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
@@ -112,9 +110,17 @@ static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device)
self->mBuffer = NULL;
self->mSize = 0;
- self->killNow = 1;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
+static void ALCwaveBackend_Destruct(ALCwaveBackend *self)
+{
+ if(self->mFile)
+ fclose(self->mFile);
+ self->mFile = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
static int ALCwaveBackend_mixerProc(void *ptr)
{
@@ -129,7 +135,7 @@ static int ALCwaveBackend_mixerProc(void *ptr)
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
done = 0;
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
@@ -137,7 +143,8 @@ static int ALCwaveBackend_mixerProc(void *ptr)
ERR("Failed to get starting time\n");
return 1;
}
- while(!self->killNow && device->Connected)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
@@ -159,42 +166,46 @@ static int ALCwaveBackend_mixerProc(void *ptr)
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
+ ALCwaveBackend_lock(self);
aluMixData(device, self->mBuffer, device->UpdateSize);
+ ALCwaveBackend_unlock(self);
done += device->UpdateSize;
if(!IS_LITTLE_ENDIAN)
{
ALuint bytesize = BytesFromDevFmt(device->FmtType);
- ALubyte *bytes = self->mBuffer;
ALuint i;
- if(bytesize == 1)
- {
- for(i = 0;i < self->mSize;i++)
- fputc(bytes[i], self->mFile);
- }
- else if(bytesize == 2)
+ if(bytesize == 2)
{
- for(i = 0;i < self->mSize;i++)
- fputc(bytes[i^1], self->mFile);
+ ALushort *samples = self->mBuffer;
+ ALuint len = self->mSize / 2;
+ for(i = 0;i < len;i++)
+ {
+ ALushort samp = samples[i];
+ samples[i] = (samp>>8) | (samp<<8);
+ }
}
else if(bytesize == 4)
{
- for(i = 0;i < self->mSize;i++)
- fputc(bytes[i^3], self->mFile);
+ ALuint *samples = self->mBuffer;
+ ALuint len = self->mSize / 4;
+ for(i = 0;i < len;i++)
+ {
+ ALuint samp = samples[i];
+ samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
+ ((samp<<8)&0x00ff0000) | (samp<<24);
+ }
}
}
- else
- {
- fs = fwrite(self->mBuffer, frameSize, device->UpdateSize,
- self->mFile);
- (void)fs;
- }
+
+ fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
+ (void)fs;
if(ferror(self->mFile))
{
ERR("Error writing to file\n");
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to write playback samples");
ALCdevice_Unlock(device);
break;
}
@@ -226,18 +237,11 @@ static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
}
device = STATIC_CAST(ALCbackend, self)->mDevice;
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCwaveBackend_close(ALCwaveBackend *self)
-{
- if(self->mFile)
- fclose(self->mFile);
- self->mFile = NULL;
-}
-
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -249,7 +253,10 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
clearerr(self->mFile);
if(GetConfigValueBool(NULL, "wave", "bformat", 0))
- device->FmtChans = DevFmtBFormat3D;
+ {
+ device->FmtChans = DevFmtAmbi3D;
+ device->AmbiOrder = 1;
+ }
switch(device->FmtType)
{
@@ -277,20 +284,23 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
+ /* .amb output requires FuMa */
+ device->AmbiLayout = AmbiLayout_FuMa;
+ device->AmbiScale = AmbiNorm_FuMa;
isbformat = 1;
chanmask = 0;
break;
}
bits = BytesFromDevFmt(device->FmtType) * 8;
- channels = ChannelsFromDevFmt(device->FmtChans);
+ channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
- fprintf(self->mFile, "RIFF");
+ fputs("RIFF", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
- fprintf(self->mFile, "WAVE");
+ fputs("WAVE", self->mFile);
- fprintf(self->mFile, "fmt ");
+ fputs("fmt ", self->mFile);
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
// 16-bit val, format type id (extensible: 0xFFFE)
@@ -312,11 +322,12 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
// 32-bit val, channel mask
fwrite32le(chanmask, self->mFile);
// 16 byte GUID, sub-type format
- val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
- (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile);
+ val = fwrite((device->FmtType == DevFmtFloat) ?
+ (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
+ (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
(void)val;
- fprintf(self->mFile, "data");
+ fputs("data", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
if(ferror(self->mFile))
@@ -335,7 +346,9 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->mSize = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->mSize = device->UpdateSize * FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
self->mBuffer = malloc(self->mSize);
if(!self->mBuffer)
{
@@ -343,7 +356,7 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
return ALC_FALSE;
}
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
{
free(self->mBuffer);
@@ -361,10 +374,8 @@ static void ALCwaveBackend_stop(ALCwaveBackend *self)
long size;
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
free(self->mBuffer);
@@ -392,7 +403,7 @@ ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
-static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
+static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
@@ -416,12 +427,12 @@ static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(waveDevice);
+ alstr_append_range(outnames, waveDevice, waveDevice+sizeof(waveDevice));
break;
case CAPTURE_DEVICE_PROBE:
break;
diff --git a/Alc/backends/winmm.c b/Alc/backends/winmm.c
index bf97ef2e..0d4a02b8 100644
--- a/Alc/backends/winmm.c
+++ b/Alc/backends/winmm.c
@@ -29,6 +29,7 @@
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "backends/base.h"
@@ -37,7 +38,7 @@
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
-#define DEVNAME_TAIL " on OpenAL Soft"
+#define DEVNAME_HEAD "OpenAL Soft on "
static vector_al_string PlaybackDevices;
@@ -45,8 +46,8 @@ static vector_al_string CaptureDevices;
static void clear_devlist(vector_al_string *list)
{
- VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
- VECTOR_RESIZE(*list, 0);
+ VECTOR_FOR_EACH(al_string, *list, alstr_reset);
+ VECTOR_RESIZE(*list, 0, 0);
}
@@ -58,7 +59,7 @@ static void ProbePlaybackDevices(void)
clear_devlist(&PlaybackDevices);
numdevs = waveOutGetNumDevs();
- VECTOR_RESERVE(PlaybackDevices, numdevs);
+ VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEOUTCAPSW WaveCaps;
@@ -71,24 +72,23 @@ static void ProbePlaybackDevices(void)
ALuint count = 0;
while(1)
{
- al_string_copy_wcstr(&dname, WaveCaps.szPname);
- if(count == 0)
- al_string_append_cstr(&dname, DEVNAME_TAIL);
- else
+ alstr_copy_cstr(&dname, DEVNAME_HEAD);
+ alstr_append_wcstr(&dname, WaveCaps.szPname);
+ if(count != 0)
{
char str[64];
- snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
- al_string_append_cstr(&dname, str);
+ snprintf(str, sizeof(str), " #%d", count+1);
+ alstr_append_cstr(&dname, str);
}
count++;
-#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
+ if(iter == VECTOR_END(PlaybackDevices)) break;
#undef MATCH_ENTRY
}
- TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+ TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(PlaybackDevices, dname);
}
@@ -102,7 +102,7 @@ static void ProbeCaptureDevices(void)
clear_devlist(&CaptureDevices);
numdevs = waveInGetNumDevs();
- VECTOR_RESERVE(CaptureDevices, numdevs);
+ VECTOR_RESIZE(CaptureDevices, 0, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEINCAPSW WaveCaps;
@@ -115,24 +115,23 @@ static void ProbeCaptureDevices(void)
ALuint count = 0;
while(1)
{
- al_string_copy_wcstr(&dname, WaveCaps.szPname);
- if(count == 0)
- al_string_append_cstr(&dname, DEVNAME_TAIL);
- else
+ alstr_copy_cstr(&dname, DEVNAME_HEAD);
+ alstr_append_wcstr(&dname, WaveCaps.szPname);
+ if(count != 0)
{
char str[64];
- snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
- al_string_append_cstr(&dname, str);
+ snprintf(str, sizeof(str), " #%d", count+1);
+ alstr_append_cstr(&dname, str);
}
count++;
-#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(CaptureDevices)) break;
+ if(iter == VECTOR_END(CaptureDevices)) break;
#undef MATCH_ENTRY
}
- TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+ TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(CaptureDevices, dname);
}
@@ -149,7 +148,7 @@ typedef struct ALCwinmmPlayback {
WAVEFORMATEX Format;
- volatile ALboolean killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCwinmmPlayback;
@@ -160,13 +159,12 @@ static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWO
static int ALCwinmmPlayback_mixerProc(void *arg);
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
-static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
@@ -182,7 +180,7 @@ static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device
InitRef(&self->WaveBuffersCommitted, 0);
self->OutHdl = NULL;
- self->killNow = AL_TRUE;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
@@ -226,7 +224,7 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
if(msg.message != WOM_DONE)
continue;
- if(self->killNow)
+ if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
if(ReadRef(&self->WaveBuffersCommitted) == 0)
break;
@@ -234,8 +232,10 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
}
WaveHdr = ((WAVEHDR*)msg.lParam);
+ ALCwinmmPlayback_lock(self);
aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
self->Format.nBlockAlign);
+ ALCwinmmPlayback_unlock(self);
// Send buffer back to play more data
waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
@@ -257,14 +257,14 @@ static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *devi
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
-#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && \
- (!deviceName || al_string_cmp_cstr(*(iter), deviceName) == 0))
+#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \
+ (!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0))
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+ if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
- DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
+ DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));
retry_open:
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
@@ -300,7 +300,7 @@ retry_open:
goto failure;
}
- al_string_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
+ alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
return ALC_NO_ERROR;
failure:
@@ -311,9 +311,6 @@ failure:
return ALC_INVALID_VALUE;
}
-static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
-{ }
-
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -374,7 +371,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
ALint BufferSize;
ALuint i;
- self->killNow = AL_FALSE;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
@@ -382,7 +379,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
// Create 4 Buffers
BufferSize = device->UpdateSize*device->NumUpdates / 4;
- BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
BufferData = calloc(4, BufferSize);
for(i = 0;i < 4;i++)
@@ -405,11 +402,8 @@ static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
void *buffer = NULL;
int i;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- // Set flag to stop processing headers
- self->killNow = AL_TRUE;
althrd_join(self->thread, &i);
// Release the wave buffers
@@ -432,11 +426,11 @@ typedef struct ALCwinmmCapture {
HWAVEIN InHdl;
- RingBuffer *Ring;
+ ll_ringbuffer_t *Ring;
WAVEFORMATEX Format;
- volatile ALboolean killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCwinmmCapture;
@@ -447,13 +441,12 @@ static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_
static int ALCwinmmCapture_captureProc(void *arg);
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
-static void ALCwinmmCapture_close(ALCwinmmCapture *self);
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
-static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
@@ -469,11 +462,38 @@ static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
InitRef(&self->WaveBuffersCommitted, 0);
self->InHdl = NULL;
- self->killNow = AL_TRUE;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
{
+ void *buffer = NULL;
+ int i;
+
+ /* Tell the processing thread to quit and wait for it to do so. */
+ if(!ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
+ {
+ PostThreadMessage(self->thread, WM_QUIT, 0, 0);
+
+ althrd_join(self->thread, &i);
+
+ /* Make sure capture is stopped and all pending buffers are flushed. */
+ waveInReset(self->InHdl);
+
+ // Release the wave buffers
+ for(i = 0;i < 4;i++)
+ {
+ waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+ if(i == 0) buffer = self->WaveBuffer[i].lpData;
+ self->WaveBuffer[i].lpData = NULL;
+ }
+ free(buffer);
+ }
+
+ ll_ringbuffer_free(self->Ring);
+ self->Ring = NULL;
+
+ // Close the Wave device
if(self->InHdl)
waveInClose(self->InHdl);
self->InHdl = 0;
@@ -512,12 +532,13 @@ static int ALCwinmmCapture_captureProc(void *arg)
continue;
/* Don't wait for other buffers to finish before quitting. We're
* closing so we don't need them. */
- if(self->killNow)
+ if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
break;
WaveHdr = ((WAVEHDR*)msg.lParam);
- WriteRingBuffer(self->Ring, (ALubyte*)WaveHdr->lpData,
- WaveHdr->dwBytesRecorded/self->Format.nBlockAlign);
+ ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
+ WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
+ );
// Send buffer back to capture more data
waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
@@ -543,13 +564,13 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
-#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && (!name || al_string_cmp_cstr(*iter, name) == 0))
+#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0))
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
- if(iter == VECTOR_ITER_END(CaptureDevices))
+ if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
- DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
+ DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
switch(device->FmtChans)
{
@@ -562,7 +583,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
return ALC_INVALID_ENUM;
}
@@ -583,7 +604,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
- self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+ self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
self->Format.nBlockAlign = self->Format.wBitsPerSample *
self->Format.nChannels / 8;
@@ -605,7 +626,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
CapturedDataSize = self->Format.nSamplesPerSec / 10;
- self->Ring = CreateRingBuffer(self->Format.nBlockAlign, CapturedDataSize);
+ self->Ring = ll_ringbuffer_create(CapturedDataSize, self->Format.nBlockAlign, false);
if(!self->Ring) goto failure;
InitRef(&self->WaveBuffersCommitted, 0);
@@ -631,11 +652,11 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
IncrementRef(&self->WaveBuffersCommitted);
}
- self->killNow = AL_FALSE;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
goto failure;
- al_string_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
+ alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
return ALC_NO_ERROR;
failure:
@@ -646,8 +667,7 @@ failure:
free(BufferData);
}
- if(self->Ring)
- DestroyRingBuffer(self->Ring);
+ ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->InHdl)
@@ -657,37 +677,6 @@ failure:
return ALC_INVALID_VALUE;
}
-static void ALCwinmmCapture_close(ALCwinmmCapture *self)
-{
- void *buffer = NULL;
- int i;
-
- /* Tell the processing thread to quit and wait for it to do so. */
- self->killNow = AL_TRUE;
- PostThreadMessage(self->thread, WM_QUIT, 0, 0);
-
- althrd_join(self->thread, &i);
-
- /* Make sure capture is stopped and all pending buffers are flushed. */
- waveInReset(self->InHdl);
-
- // Release the wave buffers
- for(i = 0;i < 4;i++)
- {
- waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
- if(i == 0) buffer = self->WaveBuffer[i].lpData;
- self->WaveBuffer[i].lpData = NULL;
- }
- free(buffer);
-
- DestroyRingBuffer(self->Ring);
- self->Ring = NULL;
-
- // Close the Wave device
- waveInClose(self->InHdl);
- self->InHdl = NULL;
-}
-
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
{
waveInStart(self->InHdl);
@@ -701,27 +690,16 @@ static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
{
- ReadRingBuffer(self->Ring, buffer, samples);
+ ll_ringbuffer_read(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
{
- return RingBufferSize(self->Ring);
+ return (ALCuint)ll_ringbuffer_read_space(self->Ring);
}
-static inline void AppendAllDevicesList2(const al_string *name)
-{
- if(!al_string_empty(*name))
- AppendAllDevicesList(al_string_get_cstr(*name));
-}
-static inline void AppendCaptureDeviceList2(const al_string *name)
-{
- if(!al_string_empty(*name))
- AppendCaptureDeviceList(al_string_get_cstr(*name));
-}
-
typedef struct ALCwinmmBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCwinmmBackendFactory;
@@ -730,7 +708,7 @@ typedef struct ALCwinmmBackendFactory {
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
-static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
@@ -760,19 +738,24 @@ static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UN
return ALC_FALSE;
}
-static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
+#define APPEND_OUTNAME(n) do { \
+ if(!alstr_empty(*(n))) \
+ alstr_append_range(outnames, VECTOR_BEGIN(*(n)), VECTOR_END(*(n))+1); \
+} while(0)
case ALL_DEVICE_PROBE:
ProbePlaybackDevices();
- VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
+ VECTOR_FOR_EACH(const al_string, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
ProbeCaptureDevices();
- VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
+ VECTOR_FOR_EACH(const al_string, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
}