diff options
Diffstat (limited to 'Alc/backends/alsa.c')
-rw-r--r-- | Alc/backends/alsa.c | 306 |
1 files changed, 195 insertions, 111 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 } } |