diff options
Diffstat (limited to 'Alc/backends')
-rw-r--r-- | Alc/backends/alsa.c | 893 | ||||
-rw-r--r-- | Alc/backends/base.c | 230 | ||||
-rw-r--r-- | Alc/backends/base.h | 133 | ||||
-rw-r--r-- | Alc/backends/coreaudio.c | 2 | ||||
-rw-r--r-- | Alc/backends/dsound.c | 37 | ||||
-rw-r--r-- | Alc/backends/loopback.c | 116 | ||||
-rw-r--r-- | Alc/backends/mmdevapi.c | 15 | ||||
-rw-r--r-- | Alc/backends/null.c | 184 | ||||
-rw-r--r-- | Alc/backends/opensl.c | 40 | ||||
-rw-r--r-- | Alc/backends/oss.c | 457 | ||||
-rw-r--r-- | Alc/backends/portaudio.c | 28 | ||||
-rw-r--r-- | Alc/backends/pulseaudio.c | 1516 | ||||
-rw-r--r-- | Alc/backends/qsa.c | 1169 | ||||
-rw-r--r-- | Alc/backends/sndio.c | 9 | ||||
-rw-r--r-- | Alc/backends/solaris.c | 13 | ||||
-rw-r--r-- | Alc/backends/wave.c | 16 | ||||
-rw-r--r-- | Alc/backends/winmm.c | 17 |
17 files changed, 3460 insertions, 1415 deletions
diff --git a/Alc/backends/alsa.c b/Alc/backends/alsa.c index 5ff6307f..874b31c3 100644 --- a/Alc/backends/alsa.c +++ b/Alc/backends/alsa.c @@ -26,6 +26,10 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.h" #include <alsa/asoundlib.h> @@ -34,80 +38,83 @@ static const ALCchar alsaDevice[] = "ALSA Default"; #ifdef HAVE_DYNLOAD +#define ALSA_FUNCS(MAGIC) \ + MAGIC(snd_strerror); \ + MAGIC(snd_pcm_open); \ + MAGIC(snd_pcm_close); \ + MAGIC(snd_pcm_nonblock); \ + MAGIC(snd_pcm_frames_to_bytes); \ + MAGIC(snd_pcm_bytes_to_frames); \ + MAGIC(snd_pcm_hw_params_malloc); \ + MAGIC(snd_pcm_hw_params_free); \ + MAGIC(snd_pcm_hw_params_any); \ + MAGIC(snd_pcm_hw_params_current); \ + MAGIC(snd_pcm_hw_params_set_access); \ + MAGIC(snd_pcm_hw_params_set_format); \ + MAGIC(snd_pcm_hw_params_set_channels); \ + MAGIC(snd_pcm_hw_params_set_periods_near); \ + MAGIC(snd_pcm_hw_params_set_rate_near); \ + MAGIC(snd_pcm_hw_params_set_rate); \ + MAGIC(snd_pcm_hw_params_set_rate_resample); \ + MAGIC(snd_pcm_hw_params_set_buffer_time_near); \ + MAGIC(snd_pcm_hw_params_set_period_time_near); \ + MAGIC(snd_pcm_hw_params_set_buffer_size_near); \ + MAGIC(snd_pcm_hw_params_set_period_size_near); \ + MAGIC(snd_pcm_hw_params_set_buffer_size_min); \ + MAGIC(snd_pcm_hw_params_get_buffer_time_min); \ + MAGIC(snd_pcm_hw_params_get_buffer_time_max); \ + MAGIC(snd_pcm_hw_params_get_period_time_min); \ + MAGIC(snd_pcm_hw_params_get_period_time_max); \ + MAGIC(snd_pcm_hw_params_get_buffer_size); \ + MAGIC(snd_pcm_hw_params_get_period_size); \ + MAGIC(snd_pcm_hw_params_get_access); \ + MAGIC(snd_pcm_hw_params_get_periods); \ + MAGIC(snd_pcm_hw_params_test_format); \ + MAGIC(snd_pcm_hw_params_test_channels); \ + MAGIC(snd_pcm_hw_params); \ + MAGIC(snd_pcm_sw_params_malloc); \ + MAGIC(snd_pcm_sw_params_current); \ + MAGIC(snd_pcm_sw_params_set_avail_min); \ + MAGIC(snd_pcm_sw_params_set_stop_threshold); \ + MAGIC(snd_pcm_sw_params); \ + MAGIC(snd_pcm_sw_params_free); \ + MAGIC(snd_pcm_prepare); \ + MAGIC(snd_pcm_start); \ + MAGIC(snd_pcm_resume); \ + MAGIC(snd_pcm_reset); \ + MAGIC(snd_pcm_wait); \ + MAGIC(snd_pcm_delay); \ + MAGIC(snd_pcm_state); \ + MAGIC(snd_pcm_avail_update); \ + MAGIC(snd_pcm_areas_silence); \ + MAGIC(snd_pcm_mmap_begin); \ + MAGIC(snd_pcm_mmap_commit); \ + MAGIC(snd_pcm_readi); \ + MAGIC(snd_pcm_writei); \ + MAGIC(snd_pcm_drain); \ + MAGIC(snd_pcm_drop); \ + MAGIC(snd_pcm_recover); \ + MAGIC(snd_pcm_info_malloc); \ + MAGIC(snd_pcm_info_free); \ + MAGIC(snd_pcm_info_set_device); \ + MAGIC(snd_pcm_info_set_subdevice); \ + MAGIC(snd_pcm_info_set_stream); \ + MAGIC(snd_pcm_info_get_name); \ + MAGIC(snd_ctl_pcm_next_device); \ + MAGIC(snd_ctl_pcm_info); \ + MAGIC(snd_ctl_open); \ + MAGIC(snd_ctl_close); \ + MAGIC(snd_ctl_card_info_malloc); \ + MAGIC(snd_ctl_card_info_free); \ + MAGIC(snd_ctl_card_info); \ + MAGIC(snd_ctl_card_info_get_name); \ + MAGIC(snd_ctl_card_info_get_id); \ + MAGIC(snd_card_next); \ + MAGIC(snd_config_update_free_global) + static void *alsa_handle; -#define MAKE_FUNC(f) static typeof(f) * p##f -MAKE_FUNC(snd_strerror); -MAKE_FUNC(snd_pcm_open); -MAKE_FUNC(snd_pcm_close); -MAKE_FUNC(snd_pcm_nonblock); -MAKE_FUNC(snd_pcm_frames_to_bytes); -MAKE_FUNC(snd_pcm_bytes_to_frames); -MAKE_FUNC(snd_pcm_hw_params_malloc); -MAKE_FUNC(snd_pcm_hw_params_free); -MAKE_FUNC(snd_pcm_hw_params_any); -MAKE_FUNC(snd_pcm_hw_params_current); -MAKE_FUNC(snd_pcm_hw_params_set_access); -MAKE_FUNC(snd_pcm_hw_params_set_format); -MAKE_FUNC(snd_pcm_hw_params_set_channels); -MAKE_FUNC(snd_pcm_hw_params_set_periods_near); -MAKE_FUNC(snd_pcm_hw_params_set_rate_near); -MAKE_FUNC(snd_pcm_hw_params_set_rate); -MAKE_FUNC(snd_pcm_hw_params_set_rate_resample); -MAKE_FUNC(snd_pcm_hw_params_set_buffer_time_near); -MAKE_FUNC(snd_pcm_hw_params_set_period_time_near); -MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near); -MAKE_FUNC(snd_pcm_hw_params_set_period_size_near); -MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min); -MAKE_FUNC(snd_pcm_hw_params_get_buffer_time_min); -MAKE_FUNC(snd_pcm_hw_params_get_buffer_time_max); -MAKE_FUNC(snd_pcm_hw_params_get_period_time_min); -MAKE_FUNC(snd_pcm_hw_params_get_period_time_max); -MAKE_FUNC(snd_pcm_hw_params_get_buffer_size); -MAKE_FUNC(snd_pcm_hw_params_get_period_size); -MAKE_FUNC(snd_pcm_hw_params_get_access); -MAKE_FUNC(snd_pcm_hw_params_get_periods); -MAKE_FUNC(snd_pcm_hw_params_test_format); -MAKE_FUNC(snd_pcm_hw_params_test_channels); -MAKE_FUNC(snd_pcm_hw_params); -MAKE_FUNC(snd_pcm_sw_params_malloc); -MAKE_FUNC(snd_pcm_sw_params_current); -MAKE_FUNC(snd_pcm_sw_params_set_avail_min); -MAKE_FUNC(snd_pcm_sw_params_set_stop_threshold); -MAKE_FUNC(snd_pcm_sw_params); -MAKE_FUNC(snd_pcm_sw_params_free); -MAKE_FUNC(snd_pcm_prepare); -MAKE_FUNC(snd_pcm_start); -MAKE_FUNC(snd_pcm_resume); -MAKE_FUNC(snd_pcm_reset); -MAKE_FUNC(snd_pcm_wait); -MAKE_FUNC(snd_pcm_delay); -MAKE_FUNC(snd_pcm_state); -MAKE_FUNC(snd_pcm_avail_update); -MAKE_FUNC(snd_pcm_areas_silence); -MAKE_FUNC(snd_pcm_mmap_begin); -MAKE_FUNC(snd_pcm_mmap_commit); -MAKE_FUNC(snd_pcm_readi); -MAKE_FUNC(snd_pcm_writei); -MAKE_FUNC(snd_pcm_drain); -MAKE_FUNC(snd_pcm_drop); -MAKE_FUNC(snd_pcm_recover); -MAKE_FUNC(snd_pcm_info_malloc); -MAKE_FUNC(snd_pcm_info_free); -MAKE_FUNC(snd_pcm_info_set_device); -MAKE_FUNC(snd_pcm_info_set_subdevice); -MAKE_FUNC(snd_pcm_info_set_stream); -MAKE_FUNC(snd_pcm_info_get_name); -MAKE_FUNC(snd_ctl_pcm_next_device); -MAKE_FUNC(snd_ctl_pcm_info); -MAKE_FUNC(snd_ctl_open); -MAKE_FUNC(snd_ctl_close); -MAKE_FUNC(snd_ctl_card_info_malloc); -MAKE_FUNC(snd_ctl_card_info_free); -MAKE_FUNC(snd_ctl_card_info); -MAKE_FUNC(snd_ctl_card_info_get_name); -MAKE_FUNC(snd_ctl_card_info_get_id); -MAKE_FUNC(snd_card_next); -MAKE_FUNC(snd_config_update_free_global); +#define MAKE_FUNC(f) static __typeof(f) * p##f +ALSA_FUNCS(MAKE_FUNC); #undef MAKE_FUNC #define snd_strerror psnd_strerror @@ -187,6 +194,8 @@ MAKE_FUNC(snd_config_update_free_global); static ALCboolean alsa_load(void) { + ALCboolean error = ALC_FALSE; + #ifdef HAVE_DYNLOAD if(!alsa_handle) { @@ -194,107 +203,28 @@ static ALCboolean alsa_load(void) if(!alsa_handle) return ALC_FALSE; + error = ALC_FALSE; #define LOAD_FUNC(f) do { \ p##f = GetSymbol(alsa_handle, #f); \ if(p##f == NULL) { \ - CloseLib(alsa_handle); \ - alsa_handle = NULL; \ - return ALC_FALSE; \ + error = ALC_TRUE; \ } \ } while(0) - LOAD_FUNC(snd_strerror); - LOAD_FUNC(snd_pcm_open); - LOAD_FUNC(snd_pcm_close); - LOAD_FUNC(snd_pcm_nonblock); - LOAD_FUNC(snd_pcm_frames_to_bytes); - LOAD_FUNC(snd_pcm_bytes_to_frames); - LOAD_FUNC(snd_pcm_hw_params_malloc); - LOAD_FUNC(snd_pcm_hw_params_free); - LOAD_FUNC(snd_pcm_hw_params_any); - LOAD_FUNC(snd_pcm_hw_params_current); - LOAD_FUNC(snd_pcm_hw_params_set_access); - LOAD_FUNC(snd_pcm_hw_params_set_format); - LOAD_FUNC(snd_pcm_hw_params_set_channels); - LOAD_FUNC(snd_pcm_hw_params_set_periods_near); - LOAD_FUNC(snd_pcm_hw_params_set_rate_near); - LOAD_FUNC(snd_pcm_hw_params_set_rate); - LOAD_FUNC(snd_pcm_hw_params_set_rate_resample); - LOAD_FUNC(snd_pcm_hw_params_set_buffer_time_near); - LOAD_FUNC(snd_pcm_hw_params_set_period_time_near); - LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near); - LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min); - LOAD_FUNC(snd_pcm_hw_params_set_period_size_near); - LOAD_FUNC(snd_pcm_hw_params_get_buffer_time_min); - LOAD_FUNC(snd_pcm_hw_params_get_buffer_time_max); - LOAD_FUNC(snd_pcm_hw_params_get_period_time_min); - LOAD_FUNC(snd_pcm_hw_params_get_period_time_max); - LOAD_FUNC(snd_pcm_hw_params_get_buffer_size); - LOAD_FUNC(snd_pcm_hw_params_get_period_size); - LOAD_FUNC(snd_pcm_hw_params_get_access); - LOAD_FUNC(snd_pcm_hw_params_get_periods); - LOAD_FUNC(snd_pcm_hw_params_test_format); - LOAD_FUNC(snd_pcm_hw_params_test_channels); - LOAD_FUNC(snd_pcm_hw_params); - LOAD_FUNC(snd_pcm_sw_params_malloc); - LOAD_FUNC(snd_pcm_sw_params_current); - LOAD_FUNC(snd_pcm_sw_params_set_avail_min); - LOAD_FUNC(snd_pcm_sw_params_set_stop_threshold); - LOAD_FUNC(snd_pcm_sw_params); - LOAD_FUNC(snd_pcm_sw_params_free); - LOAD_FUNC(snd_pcm_prepare); - LOAD_FUNC(snd_pcm_start); - LOAD_FUNC(snd_pcm_resume); - LOAD_FUNC(snd_pcm_reset); - LOAD_FUNC(snd_pcm_wait); - LOAD_FUNC(snd_pcm_delay); - LOAD_FUNC(snd_pcm_state); - LOAD_FUNC(snd_pcm_avail_update); - LOAD_FUNC(snd_pcm_areas_silence); - LOAD_FUNC(snd_pcm_mmap_begin); - LOAD_FUNC(snd_pcm_mmap_commit); - LOAD_FUNC(snd_pcm_readi); - LOAD_FUNC(snd_pcm_writei); - LOAD_FUNC(snd_pcm_drain); - LOAD_FUNC(snd_pcm_drop); - LOAD_FUNC(snd_pcm_recover); - LOAD_FUNC(snd_pcm_info_malloc); - LOAD_FUNC(snd_pcm_info_free); - LOAD_FUNC(snd_pcm_info_set_device); - LOAD_FUNC(snd_pcm_info_set_subdevice); - LOAD_FUNC(snd_pcm_info_set_stream); - LOAD_FUNC(snd_pcm_info_get_name); - LOAD_FUNC(snd_ctl_pcm_next_device); - LOAD_FUNC(snd_ctl_pcm_info); - LOAD_FUNC(snd_ctl_open); - LOAD_FUNC(snd_ctl_close); - LOAD_FUNC(snd_ctl_card_info_malloc); - LOAD_FUNC(snd_ctl_card_info_free); - LOAD_FUNC(snd_ctl_card_info); - LOAD_FUNC(snd_ctl_card_info_get_name); - LOAD_FUNC(snd_ctl_card_info_get_id); - LOAD_FUNC(snd_card_next); - LOAD_FUNC(snd_config_update_free_global); + ALSA_FUNCS(LOAD_FUNC); #undef LOAD_FUNC + + if(error) + { + CloseLib(alsa_handle); + alsa_handle = NULL; + return ALC_FALSE; + } } #endif - return ALC_TRUE; -} - - -typedef struct { - snd_pcm_t *pcmHandle; - - ALvoid *buffer; - ALsizei size; - - ALboolean doCapture; - RingBuffer *ring; - snd_pcm_sframes_t last_avail; + return !error; +} - volatile int killNow; - ALvoid *thread; -} alsa_data; typedef struct { ALCchar *name; @@ -451,10 +381,46 @@ static int verify_state(snd_pcm_t *handle) } -static ALuint ALSAProc(ALvoid *ptr) +typedef struct ALCplaybackAlsa { + DERIVE_FROM_TYPE(ALCbackend); + + snd_pcm_t *pcmHandle; + + ALvoid *buffer; + ALsizei size; + + volatile int killNow; + althread_t thread; +} ALCplaybackAlsa; +DECLARE_ALCBACKEND_VTABLE(ALCplaybackAlsa); + +static ALuint ALCplaybackAlsa_mixerProc(ALvoid *ptr); +static ALuint ALCplaybackAlsa_mixerNoMMapProc(ALvoid *ptr); + +static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device); +static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct) +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 DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock) + + +static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self); +} + + +static ALuint ALCplaybackAlsa_mixerProc(ALvoid *ptr) { - ALCdevice *Device = (ALCdevice*)ptr; - alsa_data *data = (alsa_data*)Device->ExtraData; + ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; const snd_pcm_channel_area_t *areas = NULL; snd_pcm_uframes_t update_size, num_updates; snd_pcm_sframes_t avail, commitres; @@ -463,22 +429,23 @@ static ALuint ALSAProc(ALvoid *ptr) int err; SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); - update_size = Device->UpdateSize; - num_updates = Device->NumUpdates; - while(!data->killNow) + update_size = device->UpdateSize; + num_updates = device->NumUpdates; + while(!self->killNow) { - int state = verify_state(data->pcmHandle); + int state = verify_state(self->pcmHandle); if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); - ALCdevice_Lock(Device); - aluHandleDisconnect(Device); - ALCdevice_Unlock(Device); + ALCplaybackAlsa_lock(self); + aluHandleDisconnect(device); + ALCplaybackAlsa_unlock(self); break; } - avail = snd_pcm_avail_update(data->pcmHandle); + avail = snd_pcm_avail_update(self->pcmHandle); if(avail < 0) { ERR("available update failed: %s\n", snd_strerror(avail)); @@ -488,7 +455,7 @@ static ALuint ALSAProc(ALvoid *ptr) if((snd_pcm_uframes_t)avail > update_size*(num_updates+1)) { WARN("available samples exceeds the buffer size\n"); - snd_pcm_reset(data->pcmHandle); + snd_pcm_reset(self->pcmHandle); continue; } @@ -497,26 +464,26 @@ static ALuint ALSAProc(ALvoid *ptr) { if(state != SND_PCM_STATE_RUNNING) { - err = snd_pcm_start(data->pcmHandle); + err = snd_pcm_start(self->pcmHandle); if(err < 0) { ERR("start failed: %s\n", snd_strerror(err)); continue; } } - if(snd_pcm_wait(data->pcmHandle, 1000) == 0) + if(snd_pcm_wait(self->pcmHandle, 1000) == 0) ERR("Wait timeout... buffer size too low?\n"); continue; } avail -= avail%update_size; // it is possible that contiguous areas are smaller, thus we use a loop - ALCdevice_Lock(Device); + ALCplaybackAlsa_lock(self); while(avail > 0) { frames = avail; - err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); + err = snd_pcm_mmap_begin(self->pcmHandle, &areas, &offset, &frames); if(err < 0) { ERR("mmap begin error: %s\n", snd_strerror(err)); @@ -524,9 +491,9 @@ static ALuint ALSAProc(ALvoid *ptr) } WritePtr = (char*)areas->addr + (offset * areas->step / 8); - aluMixData(Device, WritePtr, frames); + aluMixData(device, WritePtr, frames); - commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames); + commitres = snd_pcm_mmap_commit(self->pcmHandle, offset, frames); if(commitres < 0 || (commitres-frames) != 0) { ERR("mmap commit error: %s\n", @@ -536,38 +503,39 @@ static ALuint ALSAProc(ALvoid *ptr) avail -= frames; } - ALCdevice_Unlock(Device); + ALCplaybackAlsa_unlock(self); } return 0; } -static ALuint ALSANoMMapProc(ALvoid *ptr) +static ALuint ALCplaybackAlsa_mixerNoMMapProc(ALvoid *ptr) { - ALCdevice *Device = (ALCdevice*)ptr; - alsa_data *data = (alsa_data*)Device->ExtraData; + ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; snd_pcm_uframes_t update_size, num_updates; snd_pcm_sframes_t avail; char *WritePtr; int err; SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); - update_size = Device->UpdateSize; - num_updates = Device->NumUpdates; - while(!data->killNow) + update_size = device->UpdateSize; + num_updates = device->NumUpdates; + while(!self->killNow) { - int state = verify_state(data->pcmHandle); + int state = verify_state(self->pcmHandle); if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); - ALCdevice_Lock(Device); - aluHandleDisconnect(Device); - ALCdevice_Unlock(Device); + ALCplaybackAlsa_lock(self); + aluHandleDisconnect(device); + ALCplaybackAlsa_unlock(self); break; } - avail = snd_pcm_avail_update(data->pcmHandle); + avail = snd_pcm_avail_update(self->pcmHandle); if(avail < 0) { ERR("available update failed: %s\n", snd_strerror(avail)); @@ -577,7 +545,7 @@ static ALuint ALSANoMMapProc(ALvoid *ptr) if((snd_pcm_uframes_t)avail > update_size*num_updates) { WARN("available samples exceeds the buffer size\n"); - snd_pcm_reset(data->pcmHandle); + snd_pcm_reset(self->pcmHandle); continue; } @@ -585,26 +553,26 @@ static ALuint ALSANoMMapProc(ALvoid *ptr) { if(state != SND_PCM_STATE_RUNNING) { - err = snd_pcm_start(data->pcmHandle); + err = snd_pcm_start(self->pcmHandle); if(err < 0) { ERR("start failed: %s\n", snd_strerror(err)); continue; } } - if(snd_pcm_wait(data->pcmHandle, 1000) == 0) + if(snd_pcm_wait(self->pcmHandle, 1000) == 0) ERR("Wait timeout... buffer size too low?\n"); continue; } - ALCdevice_Lock(Device); - WritePtr = data->buffer; - avail = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); - aluMixData(Device, WritePtr, avail); + ALCplaybackAlsa_lock(self); + WritePtr = self->buffer; + avail = snd_pcm_bytes_to_frames(self->pcmHandle, self->size); + aluMixData(device, WritePtr, avail); while(avail > 0) { - int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail); + int ret = snd_pcm_writei(self->pcmHandle, WritePtr, avail); switch (ret) { case -EAGAIN: @@ -612,38 +580,39 @@ static ALuint ALSANoMMapProc(ALvoid *ptr) case -ESTRPIPE: case -EPIPE: case -EINTR: - ret = snd_pcm_recover(data->pcmHandle, ret, 1); + ret = snd_pcm_recover(self->pcmHandle, ret, 1); if(ret < 0) avail = 0; break; default: if (ret >= 0) { - WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret); + WritePtr += snd_pcm_frames_to_bytes(self->pcmHandle, ret); avail -= ret; } break; } if (ret < 0) { - ret = snd_pcm_prepare(data->pcmHandle); + ret = snd_pcm_prepare(self->pcmHandle); if(ret < 0) break; } } - ALCdevice_Unlock(Device); + ALCplaybackAlsa_unlock(self); } return 0; } -static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) + +static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name) { + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; const char *driver = NULL; - alsa_data *data; int err; - if(deviceName) + if(name) { size_t idx; @@ -652,7 +621,7 @@ static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) for(idx = 0;idx < numDevNames;idx++) { - if(strcmp(deviceName, allDevNameMap[idx].name) == 0) + if(strcmp(name, allDevNameMap[idx].name) == 0) { driver = allDevNameMap[idx].device; break; @@ -663,16 +632,13 @@ static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) } else { - deviceName = alsaDevice; + name = alsaDevice; driver = GetConfigValue("alsa", "device", "default"); } - data = (alsa_data*)calloc(1, sizeof(alsa_data)); - - err = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if(err < 0) { - free(data); ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err)); return ALC_OUT_OF_MEMORY; } @@ -680,23 +646,19 @@ static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); - device->DeviceName = strdup(deviceName); - device->ExtraData = data; + device->DeviceName = strdup(name); + return ALC_NO_ERROR; } -static void alsa_close_playback(ALCdevice *device) +static void ALCplaybackAlsa_close(ALCplaybackAlsa *self) { - alsa_data *data = (alsa_data*)device->ExtraData; - - snd_pcm_close(data->pcmHandle); - free(data); - device->ExtraData = NULL; + snd_pcm_close(self->pcmHandle); } -static ALCboolean alsa_reset_playback(ALCdevice *device) +static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self) { - alsa_data *data = (alsa_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; snd_pcm_uframes_t periodSizeInFrames; unsigned int periodLen, bufferLen; snd_pcm_sw_params_t *sp = NULL; @@ -743,15 +705,15 @@ static ALCboolean alsa_reset_playback(ALCdevice *device) snd_pcm_hw_params_malloc(&hp); #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error - CHECK(snd_pcm_hw_params_any(data->pcmHandle, hp)); + CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp)); /* set interleaved access */ - if(!allowmmap || snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) + if(!allowmmap || snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) { /* No mmap */ - CHECK(snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED)); + CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED)); } /* test and set format (implicitly sets sample bits) */ - if(snd_pcm_hw_params_test_format(data->pcmHandle, hp, format) < 0) + if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) < 0) { static const struct { snd_pcm_format_t format; @@ -770,16 +732,16 @@ static ALCboolean alsa_reset_playback(ALCdevice *device) for(k = 0;k < COUNTOF(formatlist);k++) { format = formatlist[k].format; - if(snd_pcm_hw_params_test_format(data->pcmHandle, hp, format) >= 0) + if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) >= 0) { device->FmtType = formatlist[k].fmttype; break; } } } - CHECK(snd_pcm_hw_params_set_format(data->pcmHandle, hp, format)); + 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(data->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0) + if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0) { static const enum DevFmtChannels channellist[] = { DevFmtStereo, @@ -792,26 +754,26 @@ static ALCboolean alsa_reset_playback(ALCdevice *device) for(k = 0;k < COUNTOF(channellist);k++) { - if(snd_pcm_hw_params_test_channels(data->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0) + if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0) { device->FmtChans = channellist[k]; break; } } } - CHECK(snd_pcm_hw_params_set_channels(data->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans))); + CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans))); /* set rate (implicitly constrains period/buffer parameters) */ - if(snd_pcm_hw_params_set_rate_resample(data->pcmHandle, hp, 0) < 0) + if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0) ERR("Failed to disable ALSA resampler\n"); - CHECK(snd_pcm_hw_params_set_rate_near(data->pcmHandle, hp, &rate, NULL)); + CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL)); /* set buffer time (implicitly constrains period/buffer parameters) */ - if(snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, hp, &bufferLen, NULL) < 0) + if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0) ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err)); /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */ - if(snd_pcm_hw_params_set_period_time_near(data->pcmHandle, hp, &periodLen, NULL) < 0) + if((err=snd_pcm_hw_params_set_period_time_near(self->pcmHandle, hp, &periodLen, NULL)) < 0) ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err)); /* install and prepare hardware configuration */ - CHECK(snd_pcm_hw_params(data->pcmHandle, hp)); + CHECK(snd_pcm_hw_params(self->pcmHandle, hp)); /* retrieve configuration info */ CHECK(snd_pcm_hw_params_get_access(hp, &access)); CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL)); @@ -821,10 +783,10 @@ static ALCboolean alsa_reset_playback(ALCdevice *device) hp = NULL; snd_pcm_sw_params_malloc(&sp); - CHECK(snd_pcm_sw_params_current(data->pcmHandle, sp)); - CHECK(snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)); - CHECK(snd_pcm_sw_params_set_stop_threshold(data->pcmHandle, sp, periodSizeInFrames*periods)); - CHECK(snd_pcm_sw_params(data->pcmHandle, sp)); + CHECK(snd_pcm_sw_params_current(self->pcmHandle, sp)); + CHECK(snd_pcm_sw_params_set_avail_min(self->pcmHandle, sp, periodSizeInFrames)); + CHECK(snd_pcm_sw_params_set_stop_threshold(self->pcmHandle, sp, periodSizeInFrames*periods)); + CHECK(snd_pcm_sw_params(self->pcmHandle, sp)); #undef CHECK snd_pcm_sw_params_free(sp); sp = NULL; @@ -844,9 +806,10 @@ error: return ALC_FALSE; } -static ALCboolean alsa_start_playback(ALCdevice *device) +static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self) { - alsa_data *data = (alsa_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + ALuint (*thread_func)(ALvoid*) = NULL; snd_pcm_hw_params_t *hp = NULL; snd_pcm_access_t access; const char *funcerr; @@ -854,39 +817,39 @@ static ALCboolean alsa_start_playback(ALCdevice *device) snd_pcm_hw_params_malloc(&hp); #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error - CHECK(snd_pcm_hw_params_current(data->pcmHandle, hp)); + CHECK(snd_pcm_hw_params_current(self->pcmHandle, hp)); /* retrieve configuration info */ CHECK(snd_pcm_hw_params_get_access(hp, &access)); #undef CHECK snd_pcm_hw_params_free(hp); hp = NULL; - data->size = snd_pcm_frames_to_bytes(data->pcmHandle, device->UpdateSize); + self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize); if(access == SND_PCM_ACCESS_RW_INTERLEAVED) { - data->buffer = malloc(data->size); - if(!data->buffer) + self->buffer = malloc(self->size); + if(!self->buffer) { ERR("buffer malloc failed\n"); return ALC_FALSE; } - data->thread = StartThread(ALSANoMMapProc, device); + thread_func = ALCplaybackAlsa_mixerNoMMapProc; } else { - err = snd_pcm_prepare(data->pcmHandle); + err = snd_pcm_prepare(self->pcmHandle); if(err < 0) { ERR("snd_pcm_prepare(data->pcmHandle) failed: %s\n", snd_strerror(err)); return ALC_FALSE; } - data->thread = StartThread(ALSAProc, device); + thread_func = ALCplaybackAlsa_mixerProc; } - if(data->thread == NULL) + if(!StartThread(&self->thread, thread_func, self)) { ERR("Could not create playback thread\n"); - free(data->buffer); - data->buffer = NULL; + free(self->buffer); + self->buffer = NULL; return ALC_FALSE; } @@ -898,24 +861,80 @@ error: return ALC_FALSE; } -static void alsa_stop_playback(ALCdevice *device) +static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self) { - alsa_data *data = (alsa_data*)device->ExtraData; + if(self->thread) + { + self->killNow = 1; + StopThread(self->thread); + self->thread = NULL; + } + self->killNow = 0; + free(self->buffer); + self->buffer = NULL; +} - if(data->thread) +static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + snd_pcm_sframes_t delay = 0; + int err; + + if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0) { - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; + ERR("Failed to get pcm delay: %s\n", snd_strerror(err)); + return 0; } - data->killNow = 0; - free(data->buffer); - data->buffer = NULL; + return maxi64((ALint64)delay*1000000000/device->Frequency, 0); +} + + +static void ALCplaybackAlsa_Delete(ALCplaybackAlsa *self) +{ + free(self); +} + +DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa); + + +typedef struct ALCcaptureAlsa { + DERIVE_FROM_TYPE(ALCbackend); + + snd_pcm_t *pcmHandle; + + ALvoid *buffer; + ALsizei size; + + ALboolean doCapture; + RingBuffer *ring; + + snd_pcm_sframes_t last_avail; +} ALCcaptureAlsa; +DECLARE_ALCBACKEND_VTABLE(ALCcaptureAlsa); + +static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device); +static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct) +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 DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock) + + +static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self); } -static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName) +static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) { + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; const char *driver = NULL; snd_pcm_hw_params_t *hp; snd_pcm_uframes_t bufferSizeInFrames; @@ -923,10 +942,9 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName) ALboolean needring = AL_FALSE; snd_pcm_format_t format; const char *funcerr; - alsa_data *data; int err; - if(deviceName) + if(name) { size_t idx; @@ -935,7 +953,7 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName) for(idx = 0;idx < numCaptureDevNames;idx++) { - if(strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0) + if(strcmp(name, allCaptureDevNameMap[idx].name) == 0) { driver = allCaptureDevNameMap[idx].device; break; @@ -946,17 +964,14 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName) } else { - deviceName = alsaDevice; + name = alsaDevice; driver = GetConfigValue("alsa", "capture", "default"); } - data = (alsa_data*)calloc(1, sizeof(alsa_data)); - - err = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if(err < 0) { ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err)); - free(data); return ALC_INVALID_VALUE; } @@ -964,7 +979,7 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName) snd_config_update_free_global(); format = -1; - switch(Device->FmtType) + switch(device->FmtType) { case DevFmtByte: format = SND_PCM_FORMAT_S8; @@ -990,32 +1005,32 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName) } funcerr = NULL; - bufferSizeInFrames = maxu(Device->UpdateSize*Device->NumUpdates, - 100*Device->Frequency/1000); - periodSizeInFrames = minu(bufferSizeInFrames, 25*Device->Frequency/1000); + bufferSizeInFrames = maxu(device->UpdateSize*device->NumUpdates, + 100*device->Frequency/1000); + periodSizeInFrames = minu(bufferSizeInFrames, 25*device->Frequency/1000); snd_pcm_hw_params_malloc(&hp); #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error - CHECK(snd_pcm_hw_params_any(data->pcmHandle, hp)); + CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp)); /* set interleaved access */ - CHECK(snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED)); + CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED)); /* set format (implicitly sets sample bits) */ - CHECK(snd_pcm_hw_params_set_format(data->pcmHandle, hp, format)); + CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format)); /* set channels (implicitly sets frame bits) */ - CHECK(snd_pcm_hw_params_set_channels(data->pcmHandle, hp, ChannelsFromDevFmt(Device->FmtChans))); + CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans))); /* set rate (implicitly constrains period/buffer parameters) */ - CHECK(snd_pcm_hw_params_set_rate(data->pcmHandle, hp, Device->Frequency, 0)); + 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) */ - if(snd_pcm_hw_params_set_buffer_size_min(data->pcmHandle, hp, &bufferSizeInFrames) < 0) + if(snd_pcm_hw_params_set_buffer_size_min(self->pcmHandle, hp, &bufferSizeInFrames) < 0) { TRACE("Buffer too large, using intermediate ring buffer\n"); needring = AL_TRUE; - CHECK(snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, hp, &bufferSizeInFrames)); + CHECK(snd_pcm_hw_params_set_buffer_size_near(self->pcmHandle, hp, &bufferSizeInFrames)); } /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ - CHECK(snd_pcm_hw_params_set_period_size_near(data->pcmHandle, hp, &periodSizeInFrames, NULL)); + CHECK(snd_pcm_hw_params_set_period_size_near(self->pcmHandle, hp, &periodSizeInFrames, NULL)); /* install and prepare hardware configuration */ - CHECK(snd_pcm_hw_params(data->pcmHandle, hp)); + CHECK(snd_pcm_hw_params(self->pcmHandle, hp)); /* retrieve configuration info */ CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL)); #undef CHECK @@ -1024,26 +1039,25 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName) if(needring) { - data->ring = CreateRingBuffer(FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType), - Device->UpdateSize*Device->NumUpdates); - if(!data->ring) + self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType), + device->UpdateSize*device->NumUpdates); + if(!self->ring) { ERR("ring buffer create failed\n"); goto error2; } - data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames); - data->buffer = malloc(data->size); - if(!data->buffer) + 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; } } - Device->DeviceName = strdup(deviceName); + device->DeviceName = strdup(name); - Device->ExtraData = data; return ALC_NO_ERROR; error: @@ -1051,227 +1065,221 @@ error: if(hp) snd_pcm_hw_params_free(hp); error2: - free(data->buffer); - DestroyRingBuffer(data->ring); - snd_pcm_close(data->pcmHandle); - free(data); + free(self->buffer); + self->buffer = NULL; + DestroyRingBuffer(self->ring); + self->ring = NULL; + snd_pcm_close(self->pcmHandle); - Device->ExtraData = NULL; return ALC_INVALID_VALUE; } -static void alsa_close_capture(ALCdevice *Device) +static void ALCcaptureAlsa_close(ALCcaptureAlsa *self) { - alsa_data *data = (alsa_data*)Device->ExtraData; + snd_pcm_close(self->pcmHandle); + DestroyRingBuffer(self->ring); - snd_pcm_close(data->pcmHandle); - DestroyRingBuffer(data->ring); + free(self->buffer); + self->buffer = NULL; +} - free(data->buffer); - free(data); - Device->ExtraData = NULL; +static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self) +{ + int err = snd_pcm_start(self->pcmHandle); + if(err < 0) + { + ERR("start failed: %s\n", snd_strerror(err)); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); + return ALC_FALSE; + } + + self->doCapture = AL_TRUE; + return ALC_TRUE; } -static void alsa_start_capture(ALCdevice *Device) +static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self) { - alsa_data *data = (alsa_data*)Device->ExtraData; + ALCuint avail; int err; - err = snd_pcm_start(data->pcmHandle); - if(err < 0) + /* OpenAL requires access to unread audio after stopping, but ALSA's + * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's + * available now so it'll be available later after the drop. */ + avail = ALCcaptureAlsa_availableSamples(self); + if(!self->ring && avail > 0) { - ERR("start failed: %s\n", snd_strerror(err)); - aluHandleDisconnect(Device); + /* The ring buffer implicitly captures when checking availability. + * Direct access needs to explicitly capture it into temp storage. */ + ALsizei size; + void *ptr; + + size = snd_pcm_frames_to_bytes(self->pcmHandle, avail); + ptr = realloc(self->buffer, size); + if(ptr) + { + self->buffer = ptr; + ALCcaptureAlsa_captureSamples(self, self->buffer, avail); + self->size = size; + } } - else - data->doCapture = AL_TRUE; + err = snd_pcm_drop(self->pcmHandle); + if(err < 0) + ERR("drop failed: %s\n", snd_strerror(err)); + self->doCapture = AL_FALSE; } -static ALCenum alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples) +static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples) { - alsa_data *data = (alsa_data*)Device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - if(data->ring) + if(self->ring) { - ReadRingBuffer(data->ring, Buffer, Samples); + ReadRingBuffer(self->ring, buffer, samples); return ALC_NO_ERROR; } - data->last_avail -= Samples; - while(Device->Connected && Samples > 0) + self->last_avail -= samples; + while(device->Connected && samples > 0) { snd_pcm_sframes_t amt = 0; - if(data->size > 0) + if(self->size > 0) { /* First get any data stored from the last stop */ - amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); - if((snd_pcm_uframes_t)amt > Samples) amt = Samples; + amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size); + if((snd_pcm_uframes_t)amt > samples) amt = samples; - amt = snd_pcm_frames_to_bytes(data->pcmHandle, amt); - memmove(Buffer, data->buffer, amt); + amt = snd_pcm_frames_to_bytes(self->pcmHandle, amt); + memmove(buffer, self->buffer, amt); - if(data->size > amt) + if(self->size > amt) { - memmove(data->buffer, data->buffer+amt, data->size - amt); - data->size -= amt; + memmove(self->buffer, self->buffer+amt, self->size - amt); + self->size -= amt; } else { - free(data->buffer); - data->buffer = NULL; - data->size = 0; + free(self->buffer); + self->buffer = NULL; + self->size = 0; } - amt = snd_pcm_bytes_to_frames(data->pcmHandle, amt); + amt = snd_pcm_bytes_to_frames(self->pcmHandle, amt); } - else if(data->doCapture) - amt = snd_pcm_readi(data->pcmHandle, Buffer, Samples); + else if(self->doCapture) + amt = snd_pcm_readi(self->pcmHandle, buffer, samples); if(amt < 0) { ERR("read error: %s\n", snd_strerror(amt)); if(amt == -EAGAIN) continue; - if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0) + if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0) { - amt = snd_pcm_start(data->pcmHandle); + amt = snd_pcm_start(self->pcmHandle); if(amt >= 0) - amt = snd_pcm_avail_update(data->pcmHandle); + amt = snd_pcm_avail_update(self->pcmHandle); } if(amt < 0) { ERR("restore error: %s\n", snd_strerror(amt)); - aluHandleDisconnect(Device); + aluHandleDisconnect(device); break; } /* If the amount available is less than what's asked, we lost it * during recovery. So just give silence instead. */ - if((snd_pcm_uframes_t)amt < Samples) + if((snd_pcm_uframes_t)amt < samples) break; continue; } - Buffer = (ALbyte*)Buffer + amt; - Samples -= amt; + buffer = (ALbyte*)buffer + amt; + samples -= amt; } - if(Samples > 0) - memset(Buffer, ((Device->FmtType == DevFmtUByte) ? 0x80 : 0), - snd_pcm_frames_to_bytes(data->pcmHandle, Samples)); + if(samples > 0) + memset(buffer, ((device->FmtType == DevFmtUByte) ? 0x80 : 0), + snd_pcm_frames_to_bytes(self->pcmHandle, samples)); return ALC_NO_ERROR; } -static ALCuint alsa_available_samples(ALCdevice *Device) +static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self) { - alsa_data *data = (alsa_data*)Device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; snd_pcm_sframes_t avail = 0; - if(Device->Connected && data->doCapture) - avail = snd_pcm_avail_update(data->pcmHandle); + if(device->Connected && self->doCapture) + avail = snd_pcm_avail_update(self->pcmHandle); if(avail < 0) { ERR("avail update failed: %s\n", snd_strerror(avail)); - if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0) + if((avail=snd_pcm_recover(self->pcmHandle, avail, 1)) >= 0) { - if(data->doCapture) - avail = snd_pcm_start(data->pcmHandle); + if(self->doCapture) + avail = snd_pcm_start(self->pcmHandle); if(avail >= 0) - avail = snd_pcm_avail_update(data->pcmHandle); + avail = snd_pcm_avail_update(self->pcmHandle); } if(avail < 0) { ERR("restore error: %s\n", snd_strerror(avail)); - aluHandleDisconnect(Device); + aluHandleDisconnect(device); } } - if(!data->ring) + if(!self->ring) { if(avail < 0) avail = 0; - avail += snd_pcm_bytes_to_frames(data->pcmHandle, data->size); - if(avail > data->last_avail) data->last_avail = avail; - return data->last_avail; + avail += snd_pcm_bytes_to_frames(self->pcmHandle, self->size); + if(avail > self->last_avail) self->last_avail = avail; + return self->last_avail; } while(avail > 0) { snd_pcm_sframes_t amt; - amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); + amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size); if(avail < amt) amt = avail; - amt = snd_pcm_readi(data->pcmHandle, data->buffer, amt); + amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt); if(amt < 0) { ERR("read error: %s\n", snd_strerror(amt)); if(amt == -EAGAIN) continue; - if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0) + if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0) { - if(data->doCapture) - amt = snd_pcm_start(data->pcmHandle); + if(self->doCapture) + amt = snd_pcm_start(self->pcmHandle); if(amt >= 0) - amt = snd_pcm_avail_update(data->pcmHandle); + amt = snd_pcm_avail_update(self->pcmHandle); } if(amt < 0) { ERR("restore error: %s\n", snd_strerror(amt)); - aluHandleDisconnect(Device); + aluHandleDisconnect(device); break; } avail = amt; continue; } - WriteRingBuffer(data->ring, data->buffer, amt); + WriteRingBuffer(self->ring, self->buffer, amt); avail -= amt; } - return RingBufferSize(data->ring); -} - -static void alsa_stop_capture(ALCdevice *Device) -{ - alsa_data *data = (alsa_data*)Device->ExtraData; - ALCuint avail; - int err; - - /* OpenAL requires access to unread audio after stopping, but ALSA's - * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's - * available now so it'll be available later after the drop. */ - avail = alsa_available_samples(Device); - if(!data->ring && avail > 0) - { - /* The ring buffer implicitly captures when checking availability. - * Direct access needs to explicitly capture it into temp storage. */ - ALsizei size; - void *ptr; - - size = snd_pcm_frames_to_bytes(data->pcmHandle, avail); - ptr = realloc(data->buffer, size); - if(ptr) - { - data->buffer = ptr; - alsa_capture_samples(Device, data->buffer, avail); - data->size = size; - } - } - err = snd_pcm_drop(data->pcmHandle); - if(err < 0) - ERR("drop failed: %s\n", snd_strerror(err)); - data->doCapture = AL_FALSE; + return RingBufferSize(self->ring); } - -static ALint64 alsa_get_latency(ALCdevice *device) +static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self) { - alsa_data *data = (alsa_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; snd_pcm_sframes_t delay = 0; int err; - if((err=snd_pcm_delay(data->pcmHandle, &delay)) < 0) + if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0) { ERR("Failed to get pcm delay: %s\n", snd_strerror(err)); return 0; @@ -1279,33 +1287,28 @@ static ALint64 alsa_get_latency(ALCdevice *device) return maxi64((ALint64)delay*1000000000/device->Frequency, 0); } +static void ALCcaptureAlsa_Delete(ALCcaptureAlsa *self) +{ + free(self); +} + +DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa); + + -static const BackendFuncs alsa_funcs = { - alsa_open_playback, - alsa_close_playback, - alsa_reset_playback, - alsa_start_playback, - alsa_stop_playback, - alsa_open_capture, - alsa_close_capture, - alsa_start_capture, - alsa_stop_capture, - alsa_capture_samples, - alsa_available_samples, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, - alsa_get_latency -}; - -ALCboolean alc_alsa_init(BackendFuncs *func_list) +typedef struct ALCalsaBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCalsaBackendFactory; +#define ALCALSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCalsaBackendFactory, ALCbackendFactory) } } + +static ALCboolean ALCalsaBackendFactory_init(ALCalsaBackendFactory* UNUSED(self)) { if(!alsa_load()) return ALC_FALSE; - *func_list = alsa_funcs; return ALC_TRUE; } -void alc_alsa_deinit(void) +static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory* UNUSED(self)) { ALuint i; @@ -1334,7 +1337,14 @@ void alc_alsa_deinit(void) #endif } -void alc_alsa_probe(enum DevProbe type) +static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type) { ALuint i; @@ -1369,3 +1379,40 @@ void alc_alsa_probe(enum DevProbe type) break; } } + +static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCplaybackAlsa *backend; + + backend = calloc(1, sizeof(*backend)); + if(!backend) return NULL; + + ALCplaybackAlsa_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + ALCcaptureAlsa *backend; + + backend = calloc(1, sizeof(*backend)); + if(!backend) return NULL; + + ALCcaptureAlsa_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCalsaBackendFactory); + + +ALCbackendFactory *ALCalsaBackendFactory_getFactory(void) +{ + static ALCalsaBackendFactory factory = ALCALSABACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} diff --git a/Alc/backends/base.c b/Alc/backends/base.c new file mode 100644 index 00000000..fe797562 --- /dev/null +++ b/Alc/backends/base.c @@ -0,0 +1,230 @@ + +#include "config.h" + +#include <stdlib.h> + +#include "alMain.h" + +#include "backends/base.h" + + +/* Base ALCbackend method implementations. */ +void ALCbackend_Construct(ALCbackend *self, ALCdevice *device) +{ + self->mDevice = device; + InitializeCriticalSection(&self->mMutex); +} + +void ALCbackend_Destruct(ALCbackend *self) +{ + DeleteCriticalSection(&self->mMutex); +} + +ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self)) +{ + return ALC_FALSE; +} + +ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples)) +{ + return ALC_INVALID_DEVICE; +} + +ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self)) +{ + return 0; +} + +ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self)) +{ + return 0; +} + +void ALCbackend_lock(ALCbackend *self) +{ + EnterCriticalSection(&self->mMutex); +} + +void ALCbackend_unlock(ALCbackend *self) +{ + LeaveCriticalSection(&self->mMutex); +} + + +/* Base ALCbackendFactory method implementations. */ +void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self)) +{ +} + + +/* Wrappers to use an old-style backend with the new interface. */ +typedef struct PlaybackWrapper { + DERIVE_FROM_TYPE(ALCbackend); +} PlaybackWrapper; + +static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device); +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 ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self); +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock) +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock) +static void PlaybackWrapper_Delete(PlaybackWrapper *self); +DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper); + +static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(PlaybackWrapper, ALCbackend, self); +} + +static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return device->Funcs->OpenPlayback(device, name); +} + +static void PlaybackWrapper_close(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + device->Funcs->ClosePlayback(device); +} + +static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return device->Funcs->ResetPlayback(device); +} + +static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return device->Funcs->StartPlayback(device); +} + +static void PlaybackWrapper_stop(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + device->Funcs->StopPlayback(device); +} + +static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return device->Funcs->GetLatency(device); +} + +static void PlaybackWrapper_Delete(PlaybackWrapper *self) +{ + free(self); +} + + +typedef struct CaptureWrapper { + DERIVE_FROM_TYPE(ALCbackend); +} CaptureWrapper; + +static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device); +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 ALint64 CaptureWrapper_getLatency(CaptureWrapper *self); +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock) +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock) +static void CaptureWrapper_Delete(CaptureWrapper *self); +DEFINE_ALCBACKEND_VTABLE(CaptureWrapper); + + +static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(CaptureWrapper, ALCbackend, self); +} + +static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return device->Funcs->OpenCapture(device, name); +} + +static void CaptureWrapper_close(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + device->Funcs->CloseCapture(device); +} + +static ALCboolean CaptureWrapper_start(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + device->Funcs->StartCapture(device); + return ALC_TRUE; +} + +static void CaptureWrapper_stop(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + device->Funcs->StopCapture(device); +} + +static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return device->Funcs->CaptureSamples(device, buffer, samples); +} + +static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return device->Funcs->AvailableSamples(device); +} + +static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return device->Funcs->GetLatency(device); +} + +static void CaptureWrapper_Delete(CaptureWrapper *self) +{ + free(self); +} + + +ALCbackend *create_backend_wrapper(ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + PlaybackWrapper *backend; + + backend = malloc(sizeof(*backend)); + if(!backend) return NULL; + + PlaybackWrapper_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + if(type == ALCbackend_Capture) + { + CaptureWrapper *backend; + + backend = malloc(sizeof(*backend)); + if(!backend) return NULL; + + CaptureWrapper_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/Alc/backends/base.h b/Alc/backends/base.h new file mode 100644 index 00000000..1bbc1fb0 --- /dev/null +++ b/Alc/backends/base.h @@ -0,0 +1,133 @@ +#ifndef AL_BACKENDS_BASE_H +#define AL_BACKENDS_BASE_H + +#include "alMain.h" +#include "compat.h" + + +struct ALCbackendVtable; + +typedef struct ALCbackend { + const struct ALCbackendVtable *vtbl; + + ALCdevice *mDevice; + + CRITICAL_SECTION mMutex; +} ALCbackend; + +void ALCbackend_Construct(ALCbackend *self, ALCdevice *device); +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); +void ALCbackend_lock(ALCbackend *self); +void ALCbackend_unlock(ALCbackend *self); + +struct ALCbackendVtable { + void (*const Destruct)(ALCbackend*); + + ALCenum (*const open)(ALCbackend*, const ALCchar*); + void (*const close)(ALCbackend*); + + ALCboolean (*const reset)(ALCbackend*); + ALCboolean (*const start)(ALCbackend*); + void (*const stop)(ALCbackend*); + + ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint); + ALCuint (*const availableSamples)(ALCbackend*); + + ALint64 (*const getLatency)(ALCbackend*); + + void (*const lock)(ALCbackend*); + void (*const unlock)(ALCbackend*); + + void (*const Delete)(ALCbackend*); +}; + +#define DECLARE_ALCBACKEND_VTABLE(T) \ +static const struct ALCbackendVtable T##_ALCbackend_vtable + +#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, void, lock) \ +DECLARE_THUNK(T, ALCbackend, void, unlock) \ +DECLARE_THUNK(T, ALCbackend, void, Delete) \ + \ +DECLARE_ALCBACKEND_VTABLE(T) = { \ + 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_lock, \ + T##_ALCbackend_unlock, \ + \ + T##_ALCbackend_Delete, \ +} + + +typedef enum ALCbackend_Type { + ALCbackend_Playback, + ALCbackend_Capture, + ALCbackend_Loopback +} ALCbackend_Type; + + +struct ALCbackendFactoryVtable; + +typedef struct ALCbackendFactory { + const struct ALCbackendFactoryVtable *vtbl; +} ALCbackendFactory; + +void ALCbackendFactory_deinit(ALCbackendFactory *self); + +struct ALCbackendFactoryVtable { + ALCboolean (*const init)(ALCbackendFactory *self); + void (*const deinit)(ALCbackendFactory *self); + + ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type); + + void (*const probe)(ALCbackendFactory *self, enum DevProbe type); + + ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type); +}; + +#define DEFINE_ALCBACKENDFACTORY_VTABLE(T) \ +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, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \ + \ +static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \ + T##_ALCbackendFactory_init, \ + T##_ALCbackendFactory_deinit, \ + T##_ALCbackendFactory_querySupport, \ + T##_ALCbackendFactory_probe, \ + T##_ALCbackendFactory_createBackend, \ +} + + +ALCbackendFactory *ALCpulseBackendFactory_getFactory(void); +ALCbackendFactory *ALCalsaBackendFactory_getFactory(void); +ALCbackendFactory *ALCossBackendFactory_getFactory(void); +ALCbackendFactory *ALCnullBackendFactory_getFactory(void); +ALCbackendFactory *ALCloopbackFactory_getFactory(void); + +ALCbackend *create_backend_wrapper(ALCdevice *device, ALCbackend_Type type); + +#endif /* AL_BACKENDS_BASE_H */ diff --git a/Alc/backends/coreaudio.c b/Alc/backends/coreaudio.c index 150f37ac..5c9b69c8 100644 --- a/Alc/backends/coreaudio.c +++ b/Alc/backends/coreaudio.c @@ -677,8 +677,6 @@ static const BackendFuncs ca_funcs = { ca_stop_capture, ca_capture_samples, ca_available_samples, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, ALCdevice_GetLatencyDefault }; diff --git a/Alc/backends/dsound.c b/Alc/backends/dsound.c index c6f666e1..6b108fba 100644 --- a/Alc/backends/dsound.c +++ b/Alc/backends/dsound.c @@ -34,13 +34,22 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" +#include "compat.h" #ifndef DSSPEAKER_5POINT1 -#define DSSPEAKER_5POINT1 6 +# define DSSPEAKER_5POINT1 0x00000006 #endif #ifndef DSSPEAKER_7POINT1 -#define DSSPEAKER_7POINT1 7 +# define DSSPEAKER_7POINT1 0x00000007 #endif +#ifndef DSSPEAKER_7POINT1_SURROUND +# define DSSPEAKER_7POINT1_SURROUND 0x00000008 +#endif +#ifndef DSSPEAKER_5POINT1_SURROUND +# define DSSPEAKER_5POINT1_SURROUND 0x00000009 +#endif + 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); @@ -67,7 +76,7 @@ typedef struct { HANDLE NotifyEvent; volatile int killNow; - ALvoid *thread; + althread_t thread; } DSoundPlaybackData; typedef struct { @@ -121,7 +130,7 @@ static ALCboolean DSoundLoad(void) } -static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data) +static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR UNUSED(drvname), LPVOID UNUSED(data)) { LPOLESTR guidstr = NULL; char str[1024]; @@ -130,9 +139,6 @@ static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR int count; ALuint i; - (void)data; - (void)drvname; - if(!guid) return TRUE; @@ -171,7 +177,7 @@ static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR } -static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data) +static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR UNUSED(drvname), LPVOID UNUSED(data)) { LPOLESTR guidstr = NULL; char str[1024]; @@ -180,9 +186,6 @@ static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR d int count; ALuint i; - (void)data; - (void)drvname; - if(!guid) return TRUE; @@ -221,7 +224,7 @@ static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR d } -static ALuint DSoundPlaybackProc(ALvoid *ptr) +FORCE_ALIGN static ALuint DSoundPlaybackProc(ALvoid *ptr) { ALCdevice *Device = (ALCdevice*)ptr; DSoundPlaybackData *data = (DSoundPlaybackData*)Device->ExtraData; @@ -237,6 +240,7 @@ static ALuint DSoundPlaybackProc(ALvoid *ptr) HRESULT err; SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); memset(&DSBCaps, 0, sizeof(DSBCaps)); DSBCaps.dwSize = sizeof(DSBCaps); @@ -466,9 +470,9 @@ static ALCboolean DSoundResetPlayback(ALCdevice *device) device->FmtChans = DevFmtStereo; else if(speakers == DSSPEAKER_QUAD) device->FmtChans = DevFmtQuad; - else if(speakers == DSSPEAKER_5POINT1) + else if(speakers == DSSPEAKER_5POINT1 || speakers == DSSPEAKER_5POINT1_SURROUND) device->FmtChans = DevFmtX51; - else if(speakers == DSSPEAKER_7POINT1) + else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND) device->FmtChans = DevFmtX71; else ERR("Unknown system speaker config: 0x%lx\n", speakers); @@ -630,8 +634,7 @@ static ALCboolean DSoundStartPlayback(ALCdevice *device) { DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData; - data->thread = StartThread(DSoundPlaybackProc, device); - if(data->thread == NULL) + if(!StartThread(&data->thread, DSoundPlaybackProc, device)) return ALC_FALSE; return ALC_TRUE; @@ -952,8 +955,6 @@ static const BackendFuncs DSoundFuncs = { DSoundStopCapture, DSoundCaptureSamples, DSoundAvailableSamples, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, ALCdevice_GetLatencyDefault }; diff --git a/Alc/backends/loopback.c b/Alc/backends/loopback.c index e1bdd3c1..cd5b1a1e 100644 --- a/Alc/backends/loopback.c +++ b/Alc/backends/loopback.c @@ -25,64 +25,116 @@ #include "alMain.h" #include "alu.h" +#include "backends/base.h" -static ALCenum loopback_open_playback(ALCdevice *device, const ALCchar *deviceName) + +typedef struct ALCloopback { + DERIVE_FROM_TYPE(ALCbackend); +} 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, void, lock) +static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock) +static void ALCloopback_Delete(ALCloopback *self); +DEFINE_ALCBACKEND_VTABLE(ALCloopback); + + +static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device) { - device->DeviceName = strdup(deviceName); + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCloopback, ALCbackend, self); +} + + +static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + + device->DeviceName = strdup(name); return ALC_NO_ERROR; } -static void loopback_close_playback(ALCdevice *device) +static void ALCloopback_close(ALCloopback* UNUSED(self)) { - (void)device; } -static ALCboolean loopback_reset_playback(ALCdevice *device) +static ALCboolean ALCloopback_reset(ALCloopback *self) { - SetDefaultWFXChannelOrder(device); + SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice); return ALC_TRUE; } -static ALCboolean loopback_start_playback(ALCdevice *device) +static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self)) { return ALC_TRUE; - (void)device; } -static void loopback_stop_playback(ALCdevice *device) +static void ALCloopback_stop(ALCloopback* UNUSED(self)) +{ +} + + +static void ALCloopback_Delete(ALCloopback *self) { - (void)device; + free(self); } -static const BackendFuncs loopback_funcs = { - loopback_open_playback, - loopback_close_playback, - loopback_reset_playback, - loopback_start_playback, - loopback_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, - ALCdevice_GetLatencyDefault -}; - -ALCboolean alc_loopback_init(BackendFuncs *func_list) +typedef struct ALCloopbackFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCloopbackFactory; +#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } } + +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 ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory); + + +ALCbackendFactory *ALCloopbackFactory_getFactory(void) +{ + static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + +static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self)) { - *func_list = loopback_funcs; return ALC_TRUE; } -void alc_loopback_deinit(void) +static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type) { + if(type == ALCbackend_Loopback) + return ALC_TRUE; + return ALC_FALSE; } -void alc_loopback_probe(enum DevProbe type) +static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type)) { - (void)type; +} + +static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + ALCloopback *backend; + + assert(type == ALCbackend_Loopback); + + backend = calloc(1, sizeof(*backend)); + if(!backend) return NULL; + + ALCloopback_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); } diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c index 2555c7f3..fa7c54f9 100644 --- a/Alc/backends/mmdevapi.c +++ b/Alc/backends/mmdevapi.c @@ -40,6 +40,8 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" +#include "compat.h" DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); @@ -69,7 +71,7 @@ typedef struct { volatile UINT32 Padding; volatile int killNow; - ALvoid *thread; + althread_t thread; } MMDevApiData; @@ -218,7 +220,7 @@ static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALu } -static ALuint MMDevApiProc(ALvoid *ptr) +FORCE_ALIGN static ALuint MMDevApiProc(ALvoid *ptr) { ALCdevice *device = ptr; MMDevApiData *data = device->ExtraData; @@ -238,6 +240,7 @@ static ALuint MMDevApiProc(ALvoid *ptr) } SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); update_size = device->UpdateSize; buffer_len = update_size * device->NumUpdates; @@ -676,8 +679,7 @@ static DWORD CALLBACK MMDevApiMsgProc(void *ptr) if(SUCCEEDED(hr)) { data->render = ptr; - data->thread = StartThread(MMDevApiProc, device); - if(!data->thread) + if(!StartThread(&data->thread, MMDevApiProc, device)) { if(data->render) IAudioRenderClient_Release(data->render); @@ -878,6 +880,9 @@ static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName CloseHandle(data->MsgEvent); data->MsgEvent = NULL; + free(data->devid); + data->devid = NULL; + free(data); device->ExtraData = NULL; @@ -963,8 +968,6 @@ static const BackendFuncs MMDevApiFuncs = { NULL, NULL, NULL, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, MMDevApiGetLatency }; diff --git a/Alc/backends/null.c b/Alc/backends/null.c index 93b38063..a7056369 100644 --- a/Alc/backends/null.c +++ b/Alc/backends/null.c @@ -27,139 +27,175 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" +#include "compat.h" +#include "backends/base.h" -typedef struct { - volatile int killNow; - ALvoid *thread; -} null_data; +typedef struct ALCnullBackend { + DERIVE_FROM_TYPE(ALCbackend); + + volatile int killNow; + althread_t thread; +} ALCnullBackend; +DECLARE_ALCBACKEND_VTABLE(ALCnullBackend); + +static ALuint ALCnullBackend_mixerProc(ALvoid *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, void, lock) +static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock) static const ALCchar nullDevice[] = "No Output"; -static ALuint NullProc(ALvoid *ptr) + +static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device) { - ALCdevice *Device = (ALCdevice*)ptr; - null_data *data = (null_data*)Device->ExtraData; + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCnullBackend, ALCbackend, self); +} + + +static ALuint ALCnullBackend_mixerProc(ALvoid *ptr) +{ + ALCnullBackend *self = (ALCnullBackend*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; ALuint now, start; ALuint64 avail, done; - const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 / - Device->Frequency / 2; + + SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); done = 0; start = timeGetTime(); - while(!data->killNow && Device->Connected) + while(!self->killNow && device->Connected) { now = timeGetTime(); - avail = (ALuint64)(now-start) * Device->Frequency / 1000; + avail = (ALuint64)(now-start) * device->Frequency / 1000; if(avail < done) { /* Timer wrapped (50 days???). Add the remainder of the cycle to * the available count and reset the number of samples done */ - avail += ((ALuint64)1<<32)*Device->Frequency/1000 - done; + avail += (U64(1)<<32)*device->Frequency/1000 - done; done = 0; } - if(avail-done < Device->UpdateSize) + if(avail-done < device->UpdateSize) { + ALuint restTime = (ALuint)((device->UpdateSize - (avail-done)) * 1000 / + device->Frequency); Sleep(restTime); continue; } - while(avail-done >= Device->UpdateSize) - { - aluMixData(Device, NULL, Device->UpdateSize); - done += Device->UpdateSize; - } + do { + aluMixData(device, NULL, device->UpdateSize); + done += device->UpdateSize; + } while(avail-done >= device->UpdateSize); } return 0; } -static ALCenum null_open_playback(ALCdevice *device, const ALCchar *deviceName) + +static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name) { - null_data *data; + ALCdevice *device; - if(!deviceName) - deviceName = nullDevice; - else if(strcmp(deviceName, nullDevice) != 0) + if(!name) + name = nullDevice; + else if(strcmp(name, nullDevice) != 0) return ALC_INVALID_VALUE; - data = (null_data*)calloc(1, sizeof(*data)); + device = STATIC_CAST(ALCbackend, self)->mDevice; + device->DeviceName = strdup(name); - device->DeviceName = strdup(deviceName); - device->ExtraData = data; return ALC_NO_ERROR; } -static void null_close_playback(ALCdevice *device) +static void ALCnullBackend_close(ALCnullBackend* UNUSED(self)) { - null_data *data = (null_data*)device->ExtraData; +} - free(data); - device->ExtraData = NULL; +static ALCboolean ALCnullBackend_reset(ALCnullBackend *self) +{ + SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice); + return ALC_TRUE; } -static ALCboolean null_reset_playback(ALCdevice *device) +static ALCboolean ALCnullBackend_start(ALCnullBackend *self) { - SetDefaultWFXChannelOrder(device); + if(!StartThread(&self->thread, ALCnullBackend_mixerProc, self)) + return ALC_FALSE; return ALC_TRUE; } -static ALCboolean null_start_playback(ALCdevice *device) +static void ALCnullBackend_stop(ALCnullBackend *self) { - null_data *data = (null_data*)device->ExtraData; + if(!self->thread) + return; - data->thread = StartThread(NullProc, device); - if(data->thread == NULL) - return ALC_FALSE; + self->killNow = 1; + StopThread(self->thread); + self->thread = NULL; - return ALC_TRUE; + self->killNow = 0; } -static void null_stop_playback(ALCdevice *device) + +static void ALCnullBackend_Delete(ALCnullBackend *self) { - null_data *data = (null_data*)device->ExtraData; + free(self); +} - if(!data->thread) - return; +DEFINE_ALCBACKEND_VTABLE(ALCnullBackend); + + +typedef struct ALCnullBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCnullBackendFactory; +#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } } + +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 ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory); - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - data->killNow = 0; +ALCbackendFactory *ALCnullBackendFactory_getFactory(void) +{ + static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); } -static const BackendFuncs null_funcs = { - null_open_playback, - null_close_playback, - null_reset_playback, - null_start_playback, - null_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, - ALCdevice_GetLatencyDefault -}; - -ALCboolean alc_null_init(BackendFuncs *func_list) +static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self)) { - *func_list = null_funcs; return ALC_TRUE; } -void alc_null_deinit(void) +static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type) { + if(type == ALCbackend_Playback) + return ALC_TRUE; + return ALC_FALSE; } -void alc_null_probe(enum DevProbe type) +static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type) { switch(type) { @@ -170,3 +206,17 @@ void alc_null_probe(enum DevProbe type) break; } } + +static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + ALCnullBackend *backend; + + assert(type == ALCbackend_Playback); + + backend = calloc(1, sizeof(*backend)); + if(!backend) return NULL; + + ALCnullBackend_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); +} diff --git a/Alc/backends/opensl.c b/Alc/backends/opensl.c index 1a104029..76cbaf1a 100644 --- a/Alc/backends/opensl.c +++ b/Alc/backends/opensl.c @@ -83,6 +83,10 @@ typedef struct SLDataLocator_AndroidSimpleBufferQueue { #define SLPlayItf_SetPlayState(a,b) ((*(a))->SetPlayState((a),(b))) +/* Should start using these generic callers instead of the name-specific ones above. */ +#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS +#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS + typedef struct { /* engine interfaces */ @@ -97,6 +101,7 @@ typedef struct { void *buffer; ALuint bufferSize; + ALuint curBuffer; ALuint frameSize; } osl_data; @@ -175,12 +180,16 @@ static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) { ALCdevice *Device = context; osl_data *data = Device->ExtraData; + ALvoid *buf; SLresult result; - aluMixData(Device, data->buffer, data->bufferSize/data->frameSize); + buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize; + aluMixData(Device, buf, data->bufferSize/data->frameSize); - result = (*bq)->Enqueue(bq, data->buffer, data->bufferSize); + result = (*bq)->Enqueue(bq, buf, data->bufferSize); PRINTERR(result, "bq->Enqueue"); + + data->curBuffer = (data->curBuffer+1) % Device->NumUpdates; } @@ -354,7 +363,7 @@ static ALCboolean opensl_start_playback(ALCdevice *Device) { data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); data->bufferSize = Device->UpdateSize * data->frameSize; - data->buffer = calloc(1, data->bufferSize); + data->buffer = calloc(Device->NumUpdates, data->bufferSize); if(!data->buffer) { result = SL_RESULT_MEMORY_FAILURE; @@ -366,10 +375,12 @@ static ALCboolean opensl_start_playback(ALCdevice *Device) { if(SL_RESULT_SUCCESS == result) { - result = (*bufferQueue)->Enqueue(bufferQueue, data->buffer, data->bufferSize); + ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize; + result = (*bufferQueue)->Enqueue(bufferQueue, buf, data->bufferSize); PRINTERR(result, "bufferQueue->Enqueue"); } } + data->curBuffer = 0; if(SL_RESULT_SUCCESS == result) { result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_PLAY, &player); @@ -401,6 +412,25 @@ static ALCboolean opensl_start_playback(ALCdevice *Device) static void opensl_stop_playback(ALCdevice *Device) { osl_data *data = Device->ExtraData; + SLPlayItf player; + SLAndroidSimpleBufferQueueItf bufferQueue; + SLresult result; + + result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player); + PRINTERR(result, "bufferQueue->GetInterface"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED); + PRINTERR(result, "player->SetPlayState"); + } + + result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue); + PRINTERR(result, "bufferQueue->GetInterface"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL0(bufferQueue,Clear)(); + PRINTERR(result, "bufferQueue->Clear"); + } free(data->buffer); data->buffer = NULL; @@ -420,8 +450,6 @@ static const BackendFuncs opensl_funcs = { NULL, NULL, NULL, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, ALCdevice_GetLatencyDefault }; diff --git a/Alc/backends/oss.c b/Alc/backends/oss.c index 0ed49517..c79793c2 100644 --- a/Alc/backends/oss.c +++ b/Alc/backends/oss.c @@ -33,6 +33,10 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.h" #include <sys/soundcard.h> @@ -47,24 +51,12 @@ #define SOUND_MIXER_WRITE MIXER_WRITE #endif + static const ALCchar oss_device[] = "OSS Default"; static const char *oss_driver = "/dev/dsp"; static const char *oss_capture = "/dev/dsp"; -typedef struct { - int fd; - volatile int killNow; - ALvoid *thread; - - ALubyte *mix_data; - int data_size; - - RingBuffer *ring; - int doCapture; -} oss_data; - - static int log2i(ALCuint x) { int y = 0; @@ -77,34 +69,65 @@ static int log2i(ALCuint x) } -static ALuint OSSProc(ALvoid *ptr) +typedef struct ALCplaybackOSS { + DERIVE_FROM_TYPE(ALCbackend); + + int fd; + + ALubyte *mix_data; + int data_size; + + volatile int killNow; + althread_t thread; +} ALCplaybackOSS; + +static ALuint ALCplaybackOSS_mixerProc(ALvoid *ptr); + +static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device); +static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct) +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, void, lock) +static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock) +static void ALCplaybackOSS_Delete(ALCplaybackOSS *self); +DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS); + + +static ALuint ALCplaybackOSS_mixerProc(ALvoid *ptr) { - ALCdevice *Device = (ALCdevice*)ptr; - oss_data *data = (oss_data*)Device->ExtraData; + ALCplaybackOSS *self = (ALCplaybackOSS*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; ALint frameSize; ssize_t wrote; SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); - frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - while(!data->killNow && Device->Connected) + while(!self->killNow && device->Connected) { - ALint len = data->data_size; - ALubyte *WritePtr = data->mix_data; + ALint len = self->data_size; + ALubyte *WritePtr = self->mix_data; - aluMixData(Device, WritePtr, len/frameSize); - while(len > 0 && !data->killNow) + aluMixData(device, WritePtr, len/frameSize); + while(len > 0 && !self->killNow) { - wrote = write(data->fd, WritePtr, len); + wrote = write(self->fd, WritePtr, len); if(wrote < 0) { if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { ERR("write failed: %s\n", strerror(errno)); - ALCdevice_Lock(Device); - aluHandleDisconnect(Device); - ALCdevice_Unlock(Device); + ALCplaybackOSS_lock(self); + aluHandleDisconnect(device); + ALCplaybackOSS_unlock(self); break; } @@ -120,77 +143,45 @@ static ALuint OSSProc(ALvoid *ptr) return 0; } -static ALuint OSSCaptureProc(ALvoid *ptr) -{ - ALCdevice *Device = (ALCdevice*)ptr; - oss_data *data = (oss_data*)Device->ExtraData; - int frameSize; - int amt; - - SetRTPriority(); - - frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); - - while(!data->killNow) - { - amt = read(data->fd, data->mix_data, data->data_size); - if(amt < 0) - { - ERR("read failed: %s\n", strerror(errno)); - ALCdevice_Lock(Device); - aluHandleDisconnect(Device); - ALCdevice_Unlock(Device); - break; - } - if(amt == 0) - { - Sleep(1); - continue; - } - if(data->doCapture) - WriteRingBuffer(data->ring, data->mix_data, amt/frameSize); - } - return 0; +static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCplaybackOSS, ALCbackend, self); } -static ALCenum oss_open_playback(ALCdevice *device, const ALCchar *deviceName) +static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name) { - oss_data *data; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - if(!deviceName) - deviceName = oss_device; - else if(strcmp(deviceName, oss_device) != 0) + if(!name) + name = oss_device; + else if(strcmp(name, oss_device) != 0) return ALC_INVALID_VALUE; - data = (oss_data*)calloc(1, sizeof(oss_data)); - data->killNow = 0; + self->killNow = 0; - data->fd = open(oss_driver, O_WRONLY); - if(data->fd == -1) + self->fd = open(oss_driver, O_WRONLY); + if(self->fd == -1) { - free(data); ERR("Could not open %s: %s\n", oss_driver, strerror(errno)); return ALC_INVALID_VALUE; } - device->DeviceName = strdup(deviceName); - device->ExtraData = data; + device->DeviceName = strdup(name); + return ALC_NO_ERROR; } -static void oss_close_playback(ALCdevice *device) +static void ALCplaybackOSS_close(ALCplaybackOSS *self) { - oss_data *data = (oss_data*)device->ExtraData; - - close(data->fd); - free(data); - device->ExtraData = NULL; + close(self->fd); + self->fd = -1; } -static ALCboolean oss_reset_playback(ALCdevice *device) +static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self) { - oss_data *data = (oss_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; int numFragmentsLogSize; int log2FragmentSize; unsigned int periods; @@ -241,11 +232,11 @@ static ALCboolean oss_reset_playback(ALCdevice *device) } /* Don't fail if SETFRAGMENT fails. We can handle just about anything * that's reported back via GETOSPACE */ - ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize); - CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat)); - CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels)); - CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed)); - CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info)); + ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info)); if(0) { err: @@ -277,69 +268,142 @@ static ALCboolean oss_reset_playback(ALCdevice *device) return ALC_TRUE; } -static ALCboolean oss_start_playback(ALCdevice *device) +static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self) { - oss_data *data = (oss_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - data->mix_data = calloc(1, data->data_size); + self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + self->mix_data = calloc(1, self->data_size); - data->thread = StartThread(OSSProc, device); - if(data->thread == NULL) + if(!StartThread(&self->thread, ALCplaybackOSS_mixerProc, self)) { - free(data->mix_data); - data->mix_data = NULL; + free(self->mix_data); + self->mix_data = NULL; return ALC_FALSE; } return ALC_TRUE; } -static void oss_stop_playback(ALCdevice *device) +static void ALCplaybackOSS_stop(ALCplaybackOSS *self) { - oss_data *data = (oss_data*)device->ExtraData; - - if(!data->thread) + if(!self->thread) return; - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; + self->killNow = 1; + StopThread(self->thread); + self->thread = NULL; - data->killNow = 0; - if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0) + self->killNow = 0; + if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0) ERR("Error resetting device: %s\n", strerror(errno)); - free(data->mix_data); - data->mix_data = NULL; + free(self->mix_data); + self->mix_data = NULL; +} + +static void ALCplaybackOSS_Delete(ALCplaybackOSS *self) +{ + free(self); } -static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName) +typedef struct ALCcaptureOSS { + DERIVE_FROM_TYPE(ALCbackend); + + int fd; + + ALubyte *read_data; + int data_size; + + RingBuffer *ring; + int doCapture; + + volatile int killNow; + althread_t thread; +} ALCcaptureOSS; + +static ALuint ALCcaptureOSS_recordProc(ALvoid *ptr); + +static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device); +static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct) +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, void, lock) +static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock) +static void ALCcaptureOSS_Delete(ALCcaptureOSS *self); +DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS); + + +static ALuint ALCcaptureOSS_recordProc(ALvoid *ptr) { + ALCcaptureOSS *self = (ALCcaptureOSS*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + int frameSize; + int amt; + + SetRTPriority(); + SetThreadName("alsoft-record"); + + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + while(!self->killNow) + { + amt = read(self->fd, self->read_data, self->data_size); + if(amt < 0) + { + ERR("read failed: %s\n", strerror(errno)); + ALCcaptureOSS_lock(self); + aluHandleDisconnect(device); + ALCcaptureOSS_unlock(self); + break; + } + if(amt == 0) + { + Sleep(1); + continue; + } + if(self->doCapture) + WriteRingBuffer(self->ring, self->read_data, amt/frameSize); + } + + return 0; +} + + +static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCcaptureOSS, ALCbackend, self); +} + +static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; int numFragmentsLogSize; int log2FragmentSize; unsigned int periods; audio_buf_info info; ALuint frameSize; int numChannels; - oss_data *data; int ossFormat; int ossSpeed; char *err; - if(!deviceName) - deviceName = oss_device; - else if(strcmp(deviceName, oss_device) != 0) + if(!name) + name = oss_device; + else if(strcmp(name, oss_device) != 0) return ALC_INVALID_VALUE; - data = (oss_data*)calloc(1, sizeof(oss_data)); - data->killNow = 0; - - data->fd = open(oss_capture, O_RDONLY); - if(data->fd == -1) + self->fd = open(oss_capture, O_RDONLY); + if(self->fd == -1) { - free(data); ERR("Could not open %s: %s\n", oss_capture, strerror(errno)); return ALC_INVALID_VALUE; } @@ -359,7 +423,6 @@ static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName) case DevFmtInt: case DevFmtUInt: case DevFmtFloat: - free(data); ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType)); return ALC_INVALID_VALUE; } @@ -380,17 +443,17 @@ static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName) err = #func; \ goto err; \ } - CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize)); - CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat)); - CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels)); - CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed)); - CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info)); if(0) { err: ERR("%s failed: %s\n", err, strerror(errno)); - close(data->fd); - free(data); + close(self->fd); + self->fd = -1; return ALC_INVALID_VALUE; } #undef CHECKERR @@ -398,8 +461,8 @@ static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName) if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) { ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); - close(data->fd); - free(data); + close(self->fd); + self->fd = -1; return ALC_INVALID_VALUE; } @@ -408,109 +471,119 @@ static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName) (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort))) { ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat); - close(data->fd); - free(data); + close(self->fd); + self->fd = -1; return ALC_INVALID_VALUE; } - data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates); - if(!data->ring) + self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates); + if(!self->ring) { ERR("Ring buffer create failed\n"); - close(data->fd); - free(data); + close(self->fd); + self->fd = -1; return ALC_OUT_OF_MEMORY; } - data->data_size = info.fragsize; - data->mix_data = calloc(1, data->data_size); + self->data_size = info.fragsize; + self->read_data = calloc(1, self->data_size); - device->ExtraData = data; - data->thread = StartThread(OSSCaptureProc, device); - if(data->thread == NULL) + if(!StartThread(&self->thread, ALCcaptureOSS_recordProc, self)) { device->ExtraData = NULL; - free(data->mix_data); - free(data); + close(self->fd); + self->fd = -1; return ALC_OUT_OF_MEMORY; } - device->DeviceName = strdup(deviceName); + device->DeviceName = strdup(name); + return ALC_NO_ERROR; } -static void oss_close_capture(ALCdevice *device) +static void ALCcaptureOSS_close(ALCcaptureOSS *self) { - oss_data *data = (oss_data*)device->ExtraData; - data->killNow = 1; - StopThread(data->thread); + self->killNow = 1; + StopThread(self->thread); + self->killNow = 0; - close(data->fd); + close(self->fd); + self->fd = -1; - DestroyRingBuffer(data->ring); + DestroyRingBuffer(self->ring); + self->ring = NULL; - free(data->mix_data); - free(data); - device->ExtraData = NULL; + free(self->read_data); + self->read_data = NULL; } -static void oss_start_capture(ALCdevice *Device) +static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self) { - oss_data *data = (oss_data*)Device->ExtraData; - data->doCapture = 1; + self->doCapture = 1; + return ALC_TRUE; } -static void oss_stop_capture(ALCdevice *Device) +static void ALCcaptureOSS_stop(ALCcaptureOSS *self) { - oss_data *data = (oss_data*)Device->ExtraData; - data->doCapture = 0; + self->doCapture = 0; } -static ALCenum oss_capture_samples(ALCdevice *Device, ALCvoid *pBuffer, ALCuint lSamples) +static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples) { - oss_data *data = (oss_data*)Device->ExtraData; - ReadRingBuffer(data->ring, pBuffer, lSamples); + ReadRingBuffer(self->ring, buffer, samples); return ALC_NO_ERROR; } -static ALCuint oss_available_samples(ALCdevice *Device) +static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self) +{ + return RingBufferSize(self->ring); +} + +void ALCcaptureOSS_Delete(ALCcaptureOSS *self) +{ + free(self); +} + + + +typedef struct ALCossBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCossBackendFactory; +#define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } } + +ALCbackendFactory *ALCossBackendFactory_getFactory(void); + +static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self); +static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit) +static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type); +static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory); + + +ALCbackendFactory *ALCossBackendFactory_getFactory(void) { - oss_data *data = (oss_data*)Device->ExtraData; - return RingBufferSize(data->ring); + static ALCossBackendFactory factory = ALCOSSBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); } -static const BackendFuncs oss_funcs = { - oss_open_playback, - oss_close_playback, - oss_reset_playback, - oss_start_playback, - oss_stop_playback, - oss_open_capture, - oss_close_capture, - oss_start_capture, - oss_stop_capture, - oss_capture_samples, - oss_available_samples, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, - ALCdevice_GetLatencyDefault -}; - -ALCboolean alc_oss_init(BackendFuncs *func_list) +ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self)) { ConfigValueStr("oss", "device", &oss_driver); ConfigValueStr("oss", "capture", &oss_capture); - *func_list = oss_funcs; return ALC_TRUE; } -void alc_oss_deinit(void) +ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type) { + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; } -void alc_oss_probe(enum DevProbe type) +void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type) { switch(type) { @@ -535,3 +608,31 @@ void alc_oss_probe(enum DevProbe type) break; } } + +ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCplaybackOSS *backend; + + backend = calloc(1, sizeof(*backend)); + if(!backend) return NULL; + + ALCplaybackOSS_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + ALCcaptureOSS *backend; + + backend = calloc(1, sizeof(*backend)); + if(!backend) return NULL; + + ALCcaptureOSS_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/Alc/backends/portaudio.c b/Alc/backends/portaudio.c index 2f576639..162788fc 100644 --- a/Alc/backends/portaudio.c +++ b/Alc/backends/portaudio.c @@ -26,6 +26,7 @@ #include "alMain.h" #include "alu.h" +#include "compat.h" #include <portaudio.h> @@ -35,7 +36,7 @@ static const ALCchar pa_device[] = "PortAudio Default"; #ifdef HAVE_DYNLOAD static void *pa_handle; -#define MAKE_FUNC(x) static typeof(x) * p##x +#define MAKE_FUNC(x) static __typeof(x) * p##x MAKE_FUNC(Pa_Initialize); MAKE_FUNC(Pa_Terminate); MAKE_FUNC(Pa_GetErrorText); @@ -127,31 +128,23 @@ typedef struct { } pa_data; -static int pa_callback(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData) +static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo), + const PaStreamCallbackFlags UNUSED(statusFlags), void *userData) { ALCdevice *device = (ALCdevice*)userData; - (void)inputBuffer; - (void)timeInfo; - (void)statusFlags; - aluMixData(device, outputBuffer, framesPerBuffer); return 0; } -static int pa_capture_cb(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData) +static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer), + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo), + const PaStreamCallbackFlags UNUSED(statusFlags), void *userData) { ALCdevice *device = (ALCdevice*)userData; pa_data *data = (pa_data*)device->ExtraData; - (void)outputBuffer; - (void)timeInfo; - (void)statusFlags; - WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer); return 0; } @@ -381,6 +374,9 @@ static void pa_close_capture(ALCdevice *device) if(err != paNoError) ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); + DestroyRingBuffer(data->ring); + data->ring = NULL; + free(data); device->ExtraData = NULL; } @@ -431,8 +427,6 @@ static const BackendFuncs pa_funcs = { pa_stop_capture, pa_capture_samples, pa_available_samples, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, ALCdevice_GetLatencyDefault }; diff --git a/Alc/backends/pulseaudio.c b/Alc/backends/pulseaudio.c index af44e3de..e2ae52ae 100644 --- a/Alc/backends/pulseaudio.c +++ b/Alc/backends/pulseaudio.c @@ -25,6 +25,10 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.h" #include <pulse/pulseaudio.h> @@ -39,7 +43,7 @@ #ifdef HAVE_DYNLOAD static void *pa_handle; -#define MAKE_FUNC(x) static typeof(x) * p##x +#define MAKE_FUNC(x) static __typeof(x) * p##x MAKE_FUNC(pa_context_unref); MAKE_FUNC(pa_sample_spec_valid); MAKE_FUNC(pa_frame_size); @@ -187,45 +191,6 @@ MAKE_FUNC(pa_stream_begin_write); #endif -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -typedef struct { - char *device_name; - - const void *cap_store; - size_t cap_len; - size_t cap_remain; - - ALCuint last_readable; - - pa_buffer_attr attr; - pa_sample_spec spec; - - pa_threaded_mainloop *loop; - - ALvoid *thread; - volatile ALboolean killNow; - - pa_stream *stream; - pa_context *context; -} pulse_data; - -typedef struct { - char *name; - char *device_name; -} DevMap; - - -static DevMap *allDevNameMap; -static ALuint numDevNames; -static DevMap *allCaptureDevNameMap; -static ALuint numCaptureDevNames; -static pa_context_flags_t pulse_ctx_flags; -static pa_proplist *prop_filter; - - static ALCboolean pulse_load(void) { ALCboolean ret = ALC_TRUE; @@ -336,6 +301,15 @@ static ALCboolean pulse_load(void) return ret; } + +/* Global flags and properties */ +static pa_context_flags_t pulse_ctx_flags; +static pa_proplist *prop_filter; + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + /* PulseAudio Event Callbacks */ static void context_state_callback(pa_context *context, void *pdata) { @@ -357,112 +331,202 @@ static void stream_state_callback(pa_stream *stream, void *pdata) pa_threaded_mainloop_signal(loop, 0); } -static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) +static void stream_success_callback(pa_stream *UNUSED(stream), int UNUSED(success), void *pdata) { - ALCdevice *device = pdata; - pulse_data *data = device->ExtraData; - - data->attr = *pa_stream_get_buffer_attr(stream); - TRACE("minreq=%d, tlength=%d, prebuf=%d\n", data->attr.minreq, data->attr.tlength, data->attr.prebuf); + pa_threaded_mainloop *loop = pdata; + pa_threaded_mainloop_signal(loop, 0); } -static void context_state_callback2(pa_context *context, void *pdata) +static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop) { - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; - - if(pa_context_get_state(context) == PA_CONTEXT_FAILED) + if(op) { - ERR("Received context failure!\n"); - aluHandleDisconnect(Device); + while(pa_operation_get_state(op) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(loop); + pa_operation_unref(op); } - pa_threaded_mainloop_signal(data->loop, 0); } -static void stream_state_callback2(pa_stream *stream, void *pdata) + +static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) { - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; + const char *name = "OpenAL Soft"; + char path_name[PATH_MAX]; + pa_context_state_t state; + pa_context *context; + int err; - if(pa_stream_get_state(stream) == PA_STREAM_FAILED) + if(pa_get_binary_name(path_name, sizeof(path_name))) + name = pa_path_get_filename(path_name); + + context = pa_context_new(pa_threaded_mainloop_get_api(loop), name); + if(!context) { - ERR("Received stream failure!\n"); - aluHandleDisconnect(Device); + ERR("pa_context_new() failed\n"); + return NULL; } - pa_threaded_mainloop_signal(data->loop, 0); -} -static void stream_success_callback(pa_stream *stream, int success, void *pdata) -{ - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; - (void)stream; - (void)success; + pa_context_set_state_callback(context, context_state_callback, loop); + + if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0) + { + while((state=pa_context_get_state(context)) != PA_CONTEXT_READY) + { + if(!PA_CONTEXT_IS_GOOD(state)) + { + err = pa_context_errno(context); + if(err > 0) err = -err; + break; + } - pa_threaded_mainloop_signal(data->loop, 0); + pa_threaded_mainloop_wait(loop); + } + } + pa_context_set_state_callback(context, NULL, NULL); + + if(err < 0) + { + if(!silent) + ERR("Context did not connect: %s\n", pa_strerror(err)); + pa_context_unref(context); + return NULL; + } + + return context; } -static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) -{ - ALCdevice *device = pdata; - pulse_data *data = device->ExtraData; - char chanmap_str[256] = ""; - const struct { - const char *str; - enum DevFmtChannels chans; - } chanmaps[] = { - { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right", - DevFmtX71 }, - { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right", - DevFmtX61 }, - { "front-left,front-right,front-center,lfe,rear-left,rear-right", - DevFmtX51 }, - { "front-left,front-right,front-center,lfe,side-left,side-right", - DevFmtX51Side }, - { "front-left,front-right,rear-left,rear-right", DevFmtQuad }, - { "front-left,front-right", DevFmtStereo }, - { "mono", DevFmtMono }, - { NULL, 0 } - }; - int i; - (void)context; - if(eol) +static ALCboolean pulse_open(pa_threaded_mainloop **loop, pa_context **context, + void(*state_cb)(pa_context*,void*), void *ptr) +{ + if(!(*loop = pa_threaded_mainloop_new())) { - pa_threaded_mainloop_signal(data->loop, 0); - return; + ERR("pa_threaded_mainloop_new() failed!\n"); + return ALC_FALSE; + } + if(pa_threaded_mainloop_start(*loop) < 0) + { + ERR("pa_threaded_mainloop_start() failed\n"); + goto error; } - for(i = 0;chanmaps[i].str;i++) + pa_threaded_mainloop_lock(*loop); + + *context = connect_context(*loop, AL_FALSE); + if(!*context) { - pa_channel_map map; - if(!pa_channel_map_parse(&map, chanmaps[i].str)) - continue; + pa_threaded_mainloop_unlock(*loop); + pa_threaded_mainloop_stop(*loop); + goto error; + } + pa_context_set_state_callback(*context, state_cb, ptr); - if(pa_channel_map_equal(&info->channel_map, &map) + pa_threaded_mainloop_unlock(*loop); + return ALC_TRUE; + +error: + pa_threaded_mainloop_free(*loop); + *loop = NULL; + + return ALC_FALSE; +} + +static void pulse_close(pa_threaded_mainloop *loop, pa_context *context, + pa_stream *stream) +{ + pa_threaded_mainloop_lock(loop); + + if(stream) + { + pa_stream_set_moved_callback(stream, NULL, NULL); #if PA_CHECK_VERSION(0,9,15) - || (pa_channel_map_superset && - pa_channel_map_superset(&info->channel_map, &map)) + if(pa_stream_set_buffer_attr_callback) + pa_stream_set_buffer_attr_callback(stream, NULL, NULL); #endif - ) - { - device->FmtChans = chanmaps[i].chans; - return; - } + pa_stream_disconnect(stream); + pa_stream_unref(stream); } - pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map); - ERR("Failed to find format for channel map:\n %s\n", chanmap_str); + pa_context_disconnect(context); + pa_context_unref(context); + + pa_threaded_mainloop_unlock(loop); + + pa_threaded_mainloop_stop(loop); + pa_threaded_mainloop_free(loop); +} + + +typedef struct { + char *name; + char *device_name; +} DevMap; + +static DevMap *allDevNameMap; +static ALuint numDevNames; +static DevMap *allCaptureDevNameMap; +static ALuint numCaptureDevNames; + + +typedef struct ALCpulsePlayback { + DERIVE_FROM_TYPE(ALCbackend); + + char *device_name; + + pa_buffer_attr attr; + pa_sample_spec spec; + + pa_threaded_mainloop *loop; + + pa_stream *stream; + pa_context *context; + + volatile ALboolean killNow; + althread_t thread; +} ALCpulsePlayback; +DECLARE_ALCBACKEND_VTABLE(ALCpulsePlayback); + +static void ALCpulsePlayback_deviceCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); +static void ALCpulsePlayback_probeDevices(void); + +static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata); +static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata); +static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata); +static void ALCpulsePlayback_sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); +static void ALCpulsePlayback_sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); +static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata); +static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, pa_threaded_mainloop *loop, + pa_context *context, pa_stream_flags_t flags, + pa_buffer_attr *attr, pa_sample_spec *spec, + pa_channel_map *chanmap); +static ALuint ALCpulsePlayback_mixerProc(ALvoid *ptr); + +static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device); +static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, void, Destruct) +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 void ALCpulsePlayback_lock(ALCpulsePlayback *self); +static void ALCpulsePlayback_unlock(ALCpulsePlayback *self); + + +static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCpulsePlayback, ALCbackend, self); } -static void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) + +static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) { pa_threaded_mainloop *loop = pdata; void *temp; ALuint i; - (void)context; - if(eol) { pa_threaded_mainloop_signal(loop, 0); @@ -487,172 +551,169 @@ static void sink_device_callback(pa_context *context, const pa_sink_info *info, } } -static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) +static void ALCpulsePlayback_probeDevices(void) { - pa_threaded_mainloop *loop = pdata; - void *temp; - ALuint i; - - (void)context; + pa_threaded_mainloop *loop; - if(eol) + allDevNameMap = malloc(sizeof(DevMap) * 1); + if((loop=pa_threaded_mainloop_new()) && + pa_threaded_mainloop_start(loop) >= 0) { - pa_threaded_mainloop_signal(loop, 0); - return; - } + pa_context *context; - for(i = 0;i < numCaptureDevNames;i++) - { - if(strcmp(info->name, allCaptureDevNameMap[i].device_name) == 0) - return; - } + pa_threaded_mainloop_lock(loop); + context = connect_context(loop, AL_FALSE); + if(context) + { + pa_operation *o; + pa_stream_flags_t flags; + pa_sample_spec spec; + pa_stream *stream; - TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name); + flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | + PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE; - temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap)); - if(temp) - { - allCaptureDevNameMap = temp; - allCaptureDevNameMap[numCaptureDevNames].name = strdup(info->description); - allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name); - numCaptureDevNames++; + spec.format = PA_SAMPLE_S16NE; + spec.rate = 44100; + spec.channels = 2; + + stream = ALCpulsePlayback_connectStream(NULL, loop, context, flags, + NULL, &spec, NULL); + if(stream) + { + o = pa_context_get_sink_info_by_name(context, pa_stream_get_device_name(stream), + ALCpulsePlayback_deviceCallback, loop); + wait_for_operation(o, loop); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = NULL; + } + + o = pa_context_get_sink_info_list(context, ALCpulsePlayback_deviceCallback, loop); + wait_for_operation(o, loop); + + pa_context_disconnect(context); + pa_context_unref(context); + } + pa_threaded_mainloop_unlock(loop); + pa_threaded_mainloop_stop(loop); } + if(loop) + pa_threaded_mainloop_free(loop); } -static void sink_name_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) -{ - ALCdevice *device = pdata; - pulse_data *data = device->ExtraData; - (void)context; - if(eol) - { - pa_threaded_mainloop_signal(data->loop, 0); - return; - } +static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata) +{ + ALCpulsePlayback *self = pdata; - free(device->DeviceName); - device->DeviceName = strdup(info->description); + 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); } -static void source_name_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) +static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata) { - ALCdevice *device = pdata; - pulse_data *data = device->ExtraData; - (void)context; - - if(eol) + ALCpulsePlayback *self = pdata; + if(pa_context_get_state(context) == PA_CONTEXT_FAILED) { - pa_threaded_mainloop_signal(data->loop, 0); - return; + ERR("Received context failure!\n"); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); } - - free(device->DeviceName); - device->DeviceName = strdup(info->description); + pa_threaded_mainloop_signal(self->loop, 0); } - -static void stream_moved_callback(pa_stream *stream, void *pdata) +static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata) { - ALCdevice *device = pdata; - pulse_data *data = device->ExtraData; - (void)stream; - - free(data->device_name); - data->device_name = strdup(pa_stream_get_device_name(data->stream)); - - TRACE("Stream moved to %s\n", data->device_name); + ALCpulsePlayback *self = pdata; + if(pa_stream_get_state(stream) == PA_STREAM_FAILED) + { + ERR("Received stream failure!\n"); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + } + pa_threaded_mainloop_signal(self->loop, 0); } - -static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) +static void ALCpulsePlayback_sinkInfoCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) { - const char *name = "OpenAL Soft"; - char path_name[PATH_MAX]; - 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); + ALCpulsePlayback *self = pdata; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + char chanmap_str[256] = ""; + const struct { + const char *str; + enum DevFmtChannels chans; + } chanmaps[] = { + { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right", + DevFmtX71 }, + { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right", + DevFmtX61 }, + { "front-left,front-right,front-center,lfe,rear-left,rear-right", + DevFmtX51 }, + { "front-left,front-right,front-center,lfe,side-left,side-right", + DevFmtX51Side }, + { "front-left,front-right,rear-left,rear-right", DevFmtQuad }, + { "front-left,front-right", DevFmtStereo }, + { "mono", DevFmtMono }, + { NULL, 0 } + }; + int i; - context = pa_context_new(pa_threaded_mainloop_get_api(loop), name); - if(!context) + if(eol) { - ERR("pa_context_new() failed\n"); - return NULL; + pa_threaded_mainloop_signal(self->loop, 0); + return; } - pa_context_set_state_callback(context, context_state_callback, loop); - - if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0) + for(i = 0;chanmaps[i].str;i++) { - while((state=pa_context_get_state(context)) != PA_CONTEXT_READY) - { - if(!PA_CONTEXT_IS_GOOD(state)) - { - err = pa_context_errno(context); - if(err > 0) err = -err; - break; - } + pa_channel_map map; + if(!pa_channel_map_parse(&map, chanmaps[i].str)) + continue; - pa_threaded_mainloop_wait(loop); + if(pa_channel_map_equal(&info->channel_map, &map) +#if PA_CHECK_VERSION(0,9,15) + || (pa_channel_map_superset && + pa_channel_map_superset(&info->channel_map, &map)) +#endif + ) + { + device->FmtChans = chanmaps[i].chans; + return; } } - pa_context_set_state_callback(context, NULL, NULL); - if(err < 0) - { - if(!silent) - ERR("Context did not connect: %s\n", pa_strerror(err)); - pa_context_unref(context); - return NULL; - } - - return context; + pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map); + ERR("Failed to find format for channel map:\n %s\n", chanmap_str); } -static pa_stream *connect_playback_stream(const char *device_name, - pa_threaded_mainloop *loop, pa_context *context, - pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, - pa_channel_map *chanmap) +static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) { - pa_stream_state_t state; - pa_stream *stream; + ALCpulsePlayback *self = pdata; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; - stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter); - if(!stream) + if(eol) { - ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context))); - return NULL; + pa_threaded_mainloop_signal(self->loop, 0); + return; } - pa_stream_set_state_callback(stream, stream_state_callback, loop); + free(device->DeviceName); + device->DeviceName = strdup(info->description); +} - if(pa_stream_connect_playback(stream, device_name, attr, flags, NULL, NULL) < 0) - { - ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context))); - pa_stream_unref(stream); - return NULL; - } - while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) - { - if(!PA_STREAM_IS_GOOD(state)) - { - ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context))); - pa_stream_unref(stream); - return NULL; - } +static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata) +{ + ALCpulsePlayback *self = pdata; - pa_threaded_mainloop_wait(loop); - } - pa_stream_set_state_callback(stream, NULL, NULL); + free(self->device_name); + self->device_name = strdup(pa_stream_get_device_name(stream)); - return stream; + TRACE("Stream moved to %s\n", self->device_name); } -static pa_stream *connect_record_stream(const char *device_name, + +static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, pa_threaded_mainloop *loop, pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap) @@ -660,7 +721,7 @@ static pa_stream *connect_record_stream(const char *device_name, pa_stream_state_t state; pa_stream *stream; - stream = pa_stream_new_with_proplist(context, "Capture Stream", spec, chanmap, prop_filter); + stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter); if(!stream) { ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context))); @@ -669,7 +730,7 @@ static pa_stream *connect_record_stream(const char *device_name, pa_stream_set_state_callback(stream, stream_state_callback, loop); - if(pa_stream_connect_record(stream, device_name, attr, flags) < 0) + if(pa_stream_connect_playback(stream, device_name, attr, flags, NULL, NULL) < 0) { ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context))); pa_stream_unref(stream); @@ -693,137 +754,40 @@ static pa_stream *connect_record_stream(const char *device_name, } -static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop) -{ - if(op) - { - while(pa_operation_get_state(op) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(loop); - pa_operation_unref(op); - } -} - - -static void probe_devices(ALboolean capture) -{ - pa_threaded_mainloop *loop; - - if(capture == AL_FALSE) - allDevNameMap = malloc(sizeof(DevMap) * 1); - else - allCaptureDevNameMap = malloc(sizeof(DevMap) * 1); - - if((loop=pa_threaded_mainloop_new()) && - pa_threaded_mainloop_start(loop) >= 0) - { - pa_context *context; - - pa_threaded_mainloop_lock(loop); - context = connect_context(loop, AL_FALSE); - if(context) - { - pa_operation *o; - - if(capture == AL_FALSE) - { - pa_stream_flags_t flags; - pa_sample_spec spec; - pa_stream *stream; - - flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | - PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE; - - spec.format = PA_SAMPLE_S16NE; - spec.rate = 44100; - spec.channels = 2; - - stream = connect_playback_stream(NULL, loop, context, flags, - NULL, &spec, NULL); - if(stream) - { - o = pa_context_get_sink_info_by_name(context, pa_stream_get_device_name(stream), sink_device_callback, loop); - wait_for_operation(o, loop); - - pa_stream_disconnect(stream); - pa_stream_unref(stream); - stream = NULL; - } - - o = pa_context_get_sink_info_list(context, sink_device_callback, loop); - } - else - { - pa_stream_flags_t flags; - pa_sample_spec spec; - pa_stream *stream; - - flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | - PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE; - - spec.format = PA_SAMPLE_S16NE; - spec.rate = 44100; - spec.channels = 1; - - stream = connect_record_stream(NULL, loop, context, flags, - NULL, &spec, NULL); - if(stream) - { - o = pa_context_get_source_info_by_name(context, pa_stream_get_device_name(stream), source_device_callback, loop); - wait_for_operation(o, loop); - - pa_stream_disconnect(stream); - pa_stream_unref(stream); - stream = NULL; - } - - o = pa_context_get_source_info_list(context, source_device_callback, loop); - } - wait_for_operation(o, loop); - - pa_context_disconnect(context); - pa_context_unref(context); - } - pa_threaded_mainloop_unlock(loop); - pa_threaded_mainloop_stop(loop); - } - if(loop) - pa_threaded_mainloop_free(loop); -} - - -static ALuint PulseProc(ALvoid *param) +static ALuint ALCpulsePlayback_mixerProc(ALvoid *ptr) { - ALCdevice *Device = param; - pulse_data *data = Device->ExtraData; + ALCpulsePlayback *self = ptr; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; ALuint buffer_size; ALint update_size; size_t frame_size; ssize_t len; SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); - pa_threaded_mainloop_lock(data->loop); - frame_size = pa_frame_size(&data->spec); - update_size = Device->UpdateSize * frame_size; + 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, data->attr.tlength); + buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength); update_size = minu(update_size, buffer_size/2); do { - len = pa_stream_writable_size(data->stream) - data->attr.tlength + + len = pa_stream_writable_size(self->stream) - self->attr.tlength + buffer_size; if(len < update_size) { - if(pa_stream_is_corked(data->stream) == 1) + if(pa_stream_is_corked(self->stream) == 1) { pa_operation *o; - o = pa_stream_cork(data->stream, 0, NULL, NULL); + o = pa_stream_cork(self->stream, 0, NULL, NULL); if(o) pa_operation_unref(o); } - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); Sleep(1); - pa_threaded_mainloop_lock(data->loop); + pa_threaded_mainloop_lock(self->loop); continue; } len -= len%update_size; @@ -836,117 +800,42 @@ static ALuint PulseProc(ALvoid *param) #if PA_CHECK_VERSION(0,9,16) if(!pa_stream_begin_write || - pa_stream_begin_write(data->stream, &buf, &newlen) < 0) + pa_stream_begin_write(self->stream, &buf, &newlen) < 0) #endif { buf = pa_xmalloc(newlen); free_func = pa_xfree; } - aluMixData(Device, buf, newlen/frame_size); + aluMixData(device, buf, newlen/frame_size); - pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); + pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); len -= newlen; } - } while(!data->killNow && Device->Connected); - pa_threaded_mainloop_unlock(data->loop); + } while(!self->killNow && device->Connected); + pa_threaded_mainloop_unlock(self->loop); return 0; } -static ALCboolean pulse_open(ALCdevice *device) -{ - pulse_data *data = pa_xmalloc(sizeof(pulse_data)); - memset(data, 0, sizeof(*data)); - - if(!(data->loop = pa_threaded_mainloop_new())) - { - ERR("pa_threaded_mainloop_new() failed!\n"); - goto out; - } - if(pa_threaded_mainloop_start(data->loop) < 0) - { - ERR("pa_threaded_mainloop_start() failed\n"); - goto out; - } - - pa_threaded_mainloop_lock(data->loop); - device->ExtraData = data; - - data->context = connect_context(data->loop, AL_FALSE); - if(!data->context) - { - pa_threaded_mainloop_unlock(data->loop); - goto out; - } - pa_context_set_state_callback(data->context, context_state_callback2, device); - - pa_threaded_mainloop_unlock(data->loop); - return ALC_TRUE; - -out: - if(data->loop) - { - pa_threaded_mainloop_stop(data->loop); - pa_threaded_mainloop_free(data->loop); - } - - device->ExtraData = NULL; - pa_xfree(data); - return ALC_FALSE; -} - -static void pulse_close(ALCdevice *device) -{ - pulse_data *data = device->ExtraData; - - pa_threaded_mainloop_lock(data->loop); - - if(data->stream) - { - pa_stream_set_moved_callback(data->stream, NULL, NULL); -#if PA_CHECK_VERSION(0,9,15) - if(pa_stream_set_buffer_attr_callback) - pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); -#endif - pa_stream_disconnect(data->stream); - pa_stream_unref(data->stream); - } - - pa_context_disconnect(data->context); - pa_context_unref(data->context); - - pa_threaded_mainloop_unlock(data->loop); - - pa_threaded_mainloop_stop(data->loop); - pa_threaded_mainloop_free(data->loop); - - free(data->device_name); - - device->ExtraData = NULL; - pa_xfree(data); -} - -/* OpenAL */ -static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name) +static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name) { const char *pulse_name = NULL; pa_stream_flags_t flags; pa_sample_spec spec; - pulse_data *data; pa_operation *o; - if(device_name) + if(name) { ALuint i; if(!allDevNameMap) - probe_devices(AL_FALSE); + ALCpulsePlayback_probeDevices(); for(i = 0;i < numDevNames;i++) { - if(strcmp(device_name, allDevNameMap[i].name) == 0) + if(strcmp(name, allDevNameMap[i].name) == 0) { pulse_name = allDevNameMap[i].device_name; break; @@ -956,11 +845,10 @@ static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name return ALC_INVALID_VALUE; } - if(pulse_open(device) == ALC_FALSE) + if(!pulse_open(&self->loop, &self->context, ALCpulsePlayback_contextStateCallback, self)) return ALC_INVALID_VALUE; - data = device->ExtraData; - pa_threaded_mainloop_lock(data->loop); + pa_threaded_mainloop_lock(self->loop); flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS; @@ -971,58 +859,67 @@ static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name spec.rate = 44100; spec.channels = 2; - data->stream = connect_playback_stream(pulse_name, data->loop, data->context, - flags, NULL, &spec, NULL); - if(!data->stream) + self->stream = ALCpulsePlayback_connectStream(pulse_name, self->loop, self->context, + flags, NULL, &spec, NULL); + if(!self->stream) { - pa_threaded_mainloop_unlock(data->loop); - pulse_close(device); + pa_threaded_mainloop_unlock(self->loop); + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; return ALC_INVALID_VALUE; } + pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self); - data->device_name = strdup(pa_stream_get_device_name(data->stream)); - o = pa_context_get_sink_info_by_name(data->context, data->device_name, - sink_name_callback, device); - wait_for_operation(o, data->loop); - - pa_stream_set_moved_callback(data->stream, stream_moved_callback, device); + self->device_name = strdup(pa_stream_get_device_name(self->stream)); + o = pa_context_get_sink_info_by_name(self->context, self->device_name, + ALCpulsePlayback_sinkNameCallback, self); + wait_for_operation(o, self->loop); - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); return ALC_NO_ERROR; } -static void pulse_close_playback(ALCdevice *device) +static void ALCpulsePlayback_close(ALCpulsePlayback *self) { - pulse_close(device); + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + self->stream = NULL; + + free(self->device_name); + self->device_name = NULL; } -static ALCboolean pulse_reset_playback(ALCdevice *device) +static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) { - pulse_data *data = device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; pa_stream_flags_t flags = 0; pa_channel_map chanmap; + const char *mapname; ALuint len; - pa_threaded_mainloop_lock(data->loop); + pa_threaded_mainloop_lock(self->loop); - if(data->stream) + if(self->stream) { - pa_stream_set_moved_callback(data->stream, NULL, NULL); + pa_stream_set_moved_callback(self->stream, NULL, NULL); #if PA_CHECK_VERSION(0,9,15) if(pa_stream_set_buffer_attr_callback) - pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); + pa_stream_set_buffer_attr_callback(self->stream, NULL, NULL); #endif - pa_stream_disconnect(data->stream); - pa_stream_unref(data->stream); - data->stream = NULL; + pa_stream_disconnect(self->stream); + pa_stream_unref(self->stream); + self->stream = NULL; } if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) { pa_operation *o; - o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device); - wait_for_operation(o, data->loop); + o = pa_context_get_sink_info_by_name(self->context, self->device_name, + ALCpulsePlayback_sinkInfoCallback, self); + wait_for_operation(o, self->loop); } if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) flags |= PA_STREAM_FIX_RATE; @@ -1039,152 +936,434 @@ static ALCboolean pulse_reset_playback(ALCdevice *device) device->FmtType = DevFmtUByte; /* fall-through */ case DevFmtUByte: - data->spec.format = PA_SAMPLE_U8; + self->spec.format = PA_SAMPLE_U8; break; case DevFmtUShort: device->FmtType = DevFmtShort; /* fall-through */ case DevFmtShort: - data->spec.format = PA_SAMPLE_S16NE; + self->spec.format = PA_SAMPLE_S16NE; break; case DevFmtUInt: device->FmtType = DevFmtInt; /* fall-through */ case DevFmtInt: - data->spec.format = PA_SAMPLE_S32NE; + self->spec.format = PA_SAMPLE_S32NE; break; case DevFmtFloat: - data->spec.format = PA_SAMPLE_FLOAT32NE; + self->spec.format = PA_SAMPLE_FLOAT32NE; break; } - data->spec.rate = device->Frequency; - data->spec.channels = ChannelsFromDevFmt(device->FmtChans); + self->spec.rate = device->Frequency; + self->spec.channels = ChannelsFromDevFmt(device->FmtChans); - if(pa_sample_spec_valid(&data->spec) == 0) + if(pa_sample_spec_valid(&self->spec) == 0) { ERR("Invalid sample format\n"); - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); return ALC_FALSE; } - if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) + mapname = "(invalid)"; + switch(device->FmtChans) { - ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); - pa_threaded_mainloop_unlock(data->loop); + 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,rear-left,rear-right"; + break; + case DevFmtX51Side: + mapname = "front-left,front-right,front-center,lfe,side-left,side-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; + } + 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; } SetDefaultWFXChannelOrder(device); - data->attr.fragsize = -1; - data->attr.prebuf = 0; - data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec); - data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2); - data->attr.maxlength = -1; + self->attr.fragsize = -1; + self->attr.prebuf = 0; + self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec); + self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2); + self->attr.maxlength = -1; - data->stream = connect_playback_stream(data->device_name, data->loop, - data->context, flags, &data->attr, - &data->spec, &chanmap); - if(!data->stream) + self->stream = ALCpulsePlayback_connectStream(self->device_name, self->loop, + self->context, flags, &self->attr, + &self->spec, &chanmap); + if(!self->stream) { - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); return ALC_FALSE; } - pa_stream_set_state_callback(data->stream, stream_state_callback2, device); + pa_stream_set_state_callback(self->stream, ALCpulsePlayback_streamStateCallback, self); + pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self); - data->spec = *(pa_stream_get_sample_spec(data->stream)); - if(device->Frequency != data->spec.rate) + self->spec = *(pa_stream_get_sample_spec(self->stream)); + if(device->Frequency != self->spec.rate) { pa_operation *o; /* Server updated our playback rate, so modify the buffer attribs * accordingly. */ device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency * - data->spec.rate + 0.5); - data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec); - data->attr.tlength = data->attr.minreq * clampu(device->NumUpdates, 2, 16); - data->attr.maxlength = -1; - data->attr.prebuf = 0; + self->spec.rate + 0.5); + self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec); + self->attr.tlength = self->attr.minreq * clampu(device->NumUpdates, 2, 16); + self->attr.maxlength = -1; + self->attr.prebuf = 0; - o = pa_stream_set_buffer_attr(data->stream, &data->attr, - stream_success_callback, device); - wait_for_operation(o, data->loop); + o = pa_stream_set_buffer_attr(self->stream, &self->attr, + stream_success_callback, self->loop); + wait_for_operation(o, self->loop); - device->Frequency = data->spec.rate; + device->Frequency = self->spec.rate; } - pa_stream_set_moved_callback(data->stream, stream_moved_callback, device); #if PA_CHECK_VERSION(0,9,15) if(pa_stream_set_buffer_attr_callback) - pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device); + pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self); #endif - stream_buffer_attr_callback(data->stream, device); + ALCpulsePlayback_bufferAttrCallback(self->stream, self); - len = data->attr.minreq / pa_frame_size(&data->spec); + len = self->attr.minreq / pa_frame_size(&self->spec); if((CPUCapFlags&CPU_CAP_SSE)) len = (len+3)&~3; device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5); device->NumUpdates = clampu(device->NumUpdates, 2, 16); device->UpdateSize = len; - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); return ALC_TRUE; } -static ALCboolean pulse_start_playback(ALCdevice *device) +static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self) { - pulse_data *data = device->ExtraData; - - data->thread = StartThread(PulseProc, device); - if(!data->thread) + if(!StartThread(&self->thread, ALCpulsePlayback_mixerProc, self)) return ALC_FALSE; - return ALC_TRUE; } -static void pulse_stop_playback(ALCdevice *device) +static void ALCpulsePlayback_stop(ALCpulsePlayback *self) { - pulse_data *data = device->ExtraData; pa_operation *o; - if(!data->stream) + if(!self->stream) return; - data->killNow = AL_TRUE; - if(data->thread) + self->killNow = AL_TRUE; + if(self->thread) + { + StopThread(self->thread); + self->thread = NULL; + } + self->killNow = AL_FALSE; + + 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 void ALCpulsePlayback_lock(ALCpulsePlayback *self) +{ + pa_threaded_mainloop_lock(self->loop); +} + +static void ALCpulsePlayback_unlock(ALCpulsePlayback *self) +{ + pa_threaded_mainloop_unlock(self->loop); +} + + +static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self) +{ + pa_usec_t latency = 0; + int neg; + + if(pa_stream_get_latency(self->stream, &latency, &neg) != 0) { - StopThread(data->thread); - data->thread = NULL; + ERR("Failed to get stream latency!\n"); + return 0; } - data->killNow = AL_FALSE; - pa_threaded_mainloop_lock(data->loop); + if(neg) latency = 0; + return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000; +} - o = pa_stream_cork(data->stream, 1, stream_success_callback, device); - wait_for_operation(o, data->loop); - pa_threaded_mainloop_unlock(data->loop); +static void ALCpulsePlayback_Delete(ALCpulsePlayback *self) +{ + free(self); } +DEFINE_ALCBACKEND_VTABLE(ALCpulsePlayback); + + +typedef struct ALCpulseCapture { + DERIVE_FROM_TYPE(ALCbackend); + + char *device_name; + + const void *cap_store; + size_t cap_len; + size_t cap_remain; + + ALCuint last_readable; + + pa_buffer_attr attr; + pa_sample_spec spec; -static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name) + pa_threaded_mainloop *loop; + + pa_stream *stream; + pa_context *context; +} ALCpulseCapture; +DECLARE_ALCBACKEND_VTABLE(ALCpulseCapture); + +static void ALCpulseCapture_deviceCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata); +static void ALCpulseCapture_probeDevices(void); + +static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdata); +static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata); +static void ALCpulseCapture_sourceNameCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata); +static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata); +static pa_stream *ALCpulseCapture_connectStream(const char *device_name, + pa_threaded_mainloop *loop, pa_context *context, + pa_stream_flags_t flags, pa_buffer_attr *attr, + pa_sample_spec *spec, pa_channel_map *chanmap); + +static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device); +static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, void, Destruct) +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 void ALCpulseCapture_lock(ALCpulseCapture *self); +static void ALCpulseCapture_unlock(ALCpulseCapture *self); + + +static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device) { + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCpulseCapture, ALCbackend, self); +} + + +static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata) +{ + pa_threaded_mainloop *loop = pdata; + void *temp; + ALuint i; + + if(eol) + { + pa_threaded_mainloop_signal(loop, 0); + return; + } + + for(i = 0;i < numCaptureDevNames;i++) + { + if(strcmp(info->name, allCaptureDevNameMap[i].device_name) == 0) + return; + } + + TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name); + + temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap)); + if(temp) + { + allCaptureDevNameMap = temp; + allCaptureDevNameMap[numCaptureDevNames].name = strdup(info->description); + allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name); + numCaptureDevNames++; + } +} + +static void ALCpulseCapture_probeDevices(void) +{ + pa_threaded_mainloop *loop; + + allCaptureDevNameMap = malloc(sizeof(DevMap) * 1); + if((loop=pa_threaded_mainloop_new()) && + pa_threaded_mainloop_start(loop) >= 0) + { + pa_context *context; + + pa_threaded_mainloop_lock(loop); + context = connect_context(loop, AL_FALSE); + if(context) + { + pa_operation *o; + pa_stream_flags_t flags; + pa_sample_spec spec; + pa_stream *stream; + + flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | + PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE; + + spec.format = PA_SAMPLE_S16NE; + spec.rate = 44100; + spec.channels = 1; + + stream = ALCpulseCapture_connectStream(NULL, loop, context, flags, + NULL, &spec, NULL); + if(stream) + { + o = pa_context_get_source_info_by_name(context, pa_stream_get_device_name(stream), + ALCpulseCapture_deviceCallback, loop); + wait_for_operation(o, loop); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = NULL; + } + + o = pa_context_get_source_info_list(context, ALCpulseCapture_deviceCallback, loop); + wait_for_operation(o, loop); + + pa_context_disconnect(context); + pa_context_unref(context); + } + pa_threaded_mainloop_unlock(loop); + pa_threaded_mainloop_stop(loop); + } + if(loop) + pa_threaded_mainloop_free(loop); +} + + +static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdata) +{ + ALCpulseCapture *self = pdata; + if(pa_context_get_state(context) == PA_CONTEXT_FAILED) + { + ERR("Received context failure!\n"); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + } + pa_threaded_mainloop_signal(self->loop, 0); +} + +static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata) +{ + ALCpulseCapture *self = pdata; + if(pa_stream_get_state(stream) == PA_STREAM_FAILED) + { + ERR("Received stream failure!\n"); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + } + pa_threaded_mainloop_signal(self->loop, 0); +} + + +static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata) +{ + ALCpulseCapture *self = pdata; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + + if(eol) + { + pa_threaded_mainloop_signal(self->loop, 0); + return; + } + + free(device->DeviceName); + device->DeviceName = strdup(info->description); +} + + +static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata) +{ + ALCpulseCapture *self = pdata; + + free(self->device_name); + self->device_name = strdup(pa_stream_get_device_name(stream)); + + TRACE("Stream moved to %s\n", self->device_name); +} + + +static pa_stream *ALCpulseCapture_connectStream(const char *device_name, + pa_threaded_mainloop *loop, pa_context *context, + pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, + pa_channel_map *chanmap) +{ + pa_stream_state_t state; + pa_stream *stream; + + stream = pa_stream_new_with_proplist(context, "Capture Stream", spec, chanmap, prop_filter); + if(!stream) + { + ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context))); + return NULL; + } + + pa_stream_set_state_callback(stream, stream_state_callback, loop); + + if(pa_stream_connect_record(stream, device_name, attr, flags) < 0) + { + ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context))); + pa_stream_unref(stream); + return NULL; + } + + while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) + { + if(!PA_STREAM_IS_GOOD(state)) + { + ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context))); + pa_stream_unref(stream); + return NULL; + } + + pa_threaded_mainloop_wait(loop); + } + pa_stream_set_state_callback(stream, NULL, NULL); + + return stream; +} + + +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; pa_channel_map chanmap; - pulse_data *data; pa_operation *o; ALuint samples; - if(device_name) + if(name) { ALuint i; if(!allCaptureDevNameMap) - probe_devices(AL_TRUE); + ALCpulseCapture_probeDevices(); for(i = 0;i < numCaptureDevNames;i++) { - if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) + if(strcmp(name, allCaptureDevNameMap[i].name) == 0) { pulse_name = allCaptureDevNameMap[i].device_name; break; @@ -1194,158 +1373,164 @@ static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name) return ALC_INVALID_VALUE; } - if(pulse_open(device) == ALC_FALSE) + if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self)) return ALC_INVALID_VALUE; - data = device->ExtraData; - pa_threaded_mainloop_lock(data->loop); + pa_threaded_mainloop_lock(self->loop); - data->spec.rate = device->Frequency; - data->spec.channels = ChannelsFromDevFmt(device->FmtChans); + self->spec.rate = device->Frequency; + self->spec.channels = ChannelsFromDevFmt(device->FmtChans); switch(device->FmtType) { case DevFmtUByte: - data->spec.format = PA_SAMPLE_U8; + self->spec.format = PA_SAMPLE_U8; break; case DevFmtShort: - data->spec.format = PA_SAMPLE_S16NE; + self->spec.format = PA_SAMPLE_S16NE; break; case DevFmtInt: - data->spec.format = PA_SAMPLE_S32NE; + self->spec.format = PA_SAMPLE_S32NE; break; case DevFmtFloat: - data->spec.format = PA_SAMPLE_FLOAT32NE; + self->spec.format = PA_SAMPLE_FLOAT32NE; break; case DevFmtByte: case DevFmtUShort: case DevFmtUInt: ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType)); - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); goto fail; } - if(pa_sample_spec_valid(&data->spec) == 0) + if(pa_sample_spec_valid(&self->spec) == 0) { ERR("Invalid sample format\n"); - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); goto fail; } - if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) + if(!pa_channel_map_init_auto(&chanmap, self->spec.channels, PA_CHANNEL_MAP_WAVEEX)) { - ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); - pa_threaded_mainloop_unlock(data->loop); + ERR("Couldn't build map for channel count (%d)!\n", self->spec.channels); + pa_threaded_mainloop_unlock(self->loop); goto fail; } samples = device->UpdateSize * device->NumUpdates; samples = maxu(samples, 100 * device->Frequency / 1000); - data->attr.minreq = -1; - data->attr.prebuf = -1; - data->attr.maxlength = samples * pa_frame_size(&data->spec); - data->attr.tlength = -1; - data->attr.fragsize = minu(samples, 50*device->Frequency/1000) * - pa_frame_size(&data->spec); + self->attr.minreq = -1; + self->attr.prebuf = -1; + self->attr.maxlength = samples * pa_frame_size(&self->spec); + self->attr.tlength = -1; + self->attr.fragsize = minu(samples, 50*device->Frequency/1000) * + pa_frame_size(&self->spec); flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; if(!GetConfigValueBool("pulse", "allow-moves", 0)) flags |= PA_STREAM_DONT_MOVE; - data->stream = connect_record_stream(pulse_name, data->loop, data->context, - flags, &data->attr, &data->spec, - &chanmap); - if(!data->stream) + self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context, + flags, &self->attr, &self->spec, + &chanmap); + if(!self->stream) { - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); goto fail; } - pa_stream_set_state_callback(data->stream, stream_state_callback2, device); + pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self); + pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self); - data->device_name = strdup(pa_stream_get_device_name(data->stream)); - o = pa_context_get_source_info_by_name(data->context, data->device_name, - source_name_callback, device); - wait_for_operation(o, data->loop); + self->device_name = strdup(pa_stream_get_device_name(self->stream)); + o = pa_context_get_source_info_by_name(self->context, self->device_name, + ALCpulseCapture_sourceNameCallback, self); + wait_for_operation(o, self->loop); - pa_stream_set_moved_callback(data->stream, stream_moved_callback, device); - - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); return ALC_NO_ERROR; fail: - pulse_close(device); + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + self->stream = NULL; + return ALC_INVALID_VALUE; } -static void pulse_close_capture(ALCdevice *device) +static void ALCpulseCapture_close(ALCpulseCapture *self) { - pulse_close(device); + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + self->stream = NULL; + + free(self->device_name); + self->device_name = NULL; } -static void pulse_start_capture(ALCdevice *device) +static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self) { - pulse_data *data = device->ExtraData; pa_operation *o; + o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop); + wait_for_operation(o, self->loop); - o = pa_stream_cork(data->stream, 0, stream_success_callback, device); - wait_for_operation(o, data->loop); + return ALC_TRUE; } -static void pulse_stop_capture(ALCdevice *device) +static void ALCpulseCapture_stop(ALCpulseCapture *self) { - pulse_data *data = device->ExtraData; pa_operation *o; - - o = pa_stream_cork(data->stream, 1, stream_success_callback, device); - wait_for_operation(o, data->loop); + o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop); + wait_for_operation(o, self->loop); } -static ALCenum pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples) { - pulse_data *data = device->ExtraData; - ALCuint todo = samples * pa_frame_size(&data->spec); + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + ALCuint todo = samples * pa_frame_size(&self->spec); /* Capture is done in fragment-sized chunks, so we loop until we get all * that's available */ - data->last_readable -= todo; + self->last_readable -= todo; while(todo > 0) { size_t rem = todo; - if(data->cap_len == 0) + if(self->cap_len == 0) { pa_stream_state_t state; - state = pa_stream_get_state(data->stream); + state = pa_stream_get_state(self->stream); if(!PA_STREAM_IS_GOOD(state)) { aluHandleDisconnect(device); break; } - if(pa_stream_peek(data->stream, &data->cap_store, &data->cap_len) < 0) + 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(data->context))); + pa_strerror(pa_context_errno(self->context))); aluHandleDisconnect(device); break; } - data->cap_remain = data->cap_len; + self->cap_remain = self->cap_len; } - if(rem > data->cap_remain) - rem = data->cap_remain; + if(rem > self->cap_remain) + rem = self->cap_remain; - memcpy(buffer, data->cap_store, rem); + memcpy(buffer, self->cap_store, rem); buffer = (ALbyte*)buffer + rem; todo -= rem; - data->cap_store = (ALbyte*)data->cap_store + rem; - data->cap_remain -= rem; - if(data->cap_remain == 0) + self->cap_store = (ALbyte*)self->cap_store + rem; + self->cap_remain -= rem; + if(self->cap_remain == 0) { - pa_stream_drop(data->stream); - data->cap_len = 0; + pa_stream_drop(self->stream); + self->cap_len = 0; } } if(todo > 0) @@ -1354,77 +1539,70 @@ static ALCenum pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint return ALC_NO_ERROR; } -static ALCuint pulse_available_samples(ALCdevice *device) +static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self) { - pulse_data *data = device->ExtraData; - size_t readable = data->cap_remain; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + size_t readable = self->cap_remain; if(device->Connected) { - ssize_t got = pa_stream_readable_size(data->stream); + ssize_t got = pa_stream_readable_size(self->stream); if(got < 0) { ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got)); aluHandleDisconnect(device); } - else if((size_t)got > data->cap_len) - readable += got - data->cap_len; + else if((size_t)got > self->cap_len) + readable += got - self->cap_len; } - if(data->last_readable < readable) - data->last_readable = readable; - return data->last_readable / pa_frame_size(&data->spec); + if(self->last_readable < readable) + self->last_readable = readable; + return self->last_readable / pa_frame_size(&self->spec); } -static void pulse_lock(ALCdevice *device) +static void ALCpulseCapture_lock(ALCpulseCapture *self) { - pulse_data *data = device->ExtraData; - pa_threaded_mainloop_lock(data->loop); + pa_threaded_mainloop_lock(self->loop); } -static void pulse_unlock(ALCdevice *device) +static void ALCpulseCapture_unlock(ALCpulseCapture *self) { - pulse_data *data = device->ExtraData; - pa_threaded_mainloop_unlock(data->loop); + pa_threaded_mainloop_unlock(self->loop); } -static ALint64 pulse_get_latency(ALCdevice *device) +static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self) { - pulse_data *data = device->ExtraData; pa_usec_t latency = 0; int neg; - if(pa_stream_get_latency(data->stream, &latency, &neg) == 0) + if(pa_stream_get_latency(self->stream, &latency, &neg) != 0) { - if(neg) - latency = 0; - return (ALint64)minu64(latency, MAKEU64(0x7fffffff,0xffffffff)/1000) * 1000; + ERR("Failed to get stream latency!\n"); + return 0; } - ERR("Failed to get stream latency!\n"); - return 0; + + if(neg) latency = 0; + return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000; } -static const BackendFuncs pulse_funcs = { - pulse_open_playback, - pulse_close_playback, - pulse_reset_playback, - pulse_start_playback, - pulse_stop_playback, - pulse_open_capture, - pulse_close_capture, - pulse_start_capture, - pulse_stop_capture, - pulse_capture_samples, - pulse_available_samples, - pulse_lock, - pulse_unlock, - pulse_get_latency -}; - -ALCboolean alc_pulse_init(BackendFuncs *func_list) +static void ALCpulseCapture_Delete(ALCpulseCapture *self) +{ + free(self); +} + +DEFINE_ALCBACKEND_VTABLE(ALCpulseCapture); + + +typedef struct ALCpulseBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCpulseBackendFactory; +#define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } } + +static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self)) { ALCboolean ret = ALC_FALSE; @@ -1445,7 +1623,6 @@ ALCboolean alc_pulse_init(BackendFuncs *func_list) context = connect_context(loop, AL_TRUE); if(context) { - *func_list = pulse_funcs; ret = ALC_TRUE; /* Some libraries (Phonon, Qt) set some pulseaudio properties @@ -1469,7 +1646,7 @@ ALCboolean alc_pulse_init(BackendFuncs *func_list) return ret; } -void alc_pulse_deinit(void) +static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self)) { ALuint i; @@ -1498,7 +1675,14 @@ void alc_pulse_deinit(void) /* PulseAudio doesn't like being CloseLib'd sometimes */ } -void alc_pulse_probe(enum DevProbe type) +static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type) { ALuint i; @@ -1514,7 +1698,7 @@ void alc_pulse_probe(enum DevProbe type) allDevNameMap = NULL; numDevNames = 0; - probe_devices(AL_FALSE); + ALCpulsePlayback_probeDevices(); for(i = 0;i < numDevNames;i++) AppendAllDevicesList(allDevNameMap[i].name); @@ -1530,7 +1714,7 @@ void alc_pulse_probe(enum DevProbe type) allCaptureDevNameMap = NULL; numCaptureDevNames = 0; - probe_devices(AL_TRUE); + ALCpulseCapture_probeDevices(); for(i = 0;i < numCaptureDevNames;i++) AppendCaptureDeviceList(allCaptureDevNameMap[i].name); @@ -1538,17 +1722,75 @@ void alc_pulse_probe(enum DevProbe type) } } -#else +static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCpulsePlayback *backend; + + backend = calloc(1, sizeof(*backend)); + if(!backend) return NULL; + + ALCpulsePlayback_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + ALCpulseCapture *backend; + + backend = calloc(1, sizeof(*backend)); + if(!backend) return NULL; + + ALCpulseCapture_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory); + + +#else /* PA_API_VERSION == 12 */ #warning "Unsupported API version, backend will be unavailable!" -ALCboolean alc_pulse_init(BackendFuncs *func_list) -{ return ALC_FALSE; (void)func_list; } +typedef struct ALCpulseBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCpulseBackendFactory; +#define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } } -void alc_pulse_deinit(void) -{ } +static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self)) +{ + return ALC_FALSE; +} -void alc_pulse_probe(enum DevProbe type) -{ (void)type; } +static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self)) +{ +} -#endif +static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UNUSED(self), ALCbackend_Type UNUSED(type)) +{ + return ALC_FALSE; +} + +static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type)) +{ +} + +static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* UNUSED(self), ALCdevice* UNUSED(device), ALCbackend_Type UNUSED(type)) +{ + return NULL; +} + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory); + +#endif /* PA_API_VERSION == 12 */ + +ALCbackendFactory *ALCpulseBackendFactory_getFactory(void) +{ + static ALCpulseBackendFactory factory = ALCPULSEBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} diff --git a/Alc/backends/qsa.c b/Alc/backends/qsa.c new file mode 100644 index 00000000..c9762f85 --- /dev/null +++ b/Alc/backends/qsa.c @@ -0,0 +1,1169 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2011-2013 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> +#include <errno.h> +#include <memory.h> +#include <sys/select.h> +#include <sys/asoundlib.h> +#include <sys/neutrino.h> + +#include "alMain.h" +#include "alu.h" +#include "threads.h" + + +typedef struct +{ + snd_pcm_t* pcmHandle; + int audio_fd; + + snd_pcm_channel_setup_t csetup; + snd_pcm_channel_params_t cparams; + + ALvoid* buffer; + ALsizei size; + + volatile int killNow; + althread_t thread; +} qsa_data; + +typedef struct +{ + ALCchar* name; + int card; + int dev; +} DevMap; + +static const ALCchar qsaDevice[]="QSA Default"; +static DevMap* allDevNameMap; +static ALuint numDevNames; +static DevMap* allCaptureDevNameMap; +static ALuint numCaptureDevNames; + +static const struct +{ + int32_t format; +} formatlist[]= +{ + {SND_PCM_SFMT_FLOAT_LE}, + {SND_PCM_SFMT_S32_LE}, + {SND_PCM_SFMT_U32_LE}, + {SND_PCM_SFMT_S16_LE}, + {SND_PCM_SFMT_U16_LE}, + {SND_PCM_SFMT_S8}, + {SND_PCM_SFMT_U8}, + {0}, +}; + +static const struct +{ + int32_t rate; +} ratelist[]= +{ + {192000}, + {176400}, + {96000}, + {88200}, + {48000}, + {44100}, + {32000}, + {24000}, + {22050}, + {16000}, + {12000}, + {11025}, + {8000}, + {0}, +}; + +static const struct +{ + int32_t channels; +} channellist[]= +{ + {8}, + {7}, + {6}, + {4}, + {2}, + {1}, + {0}, +}; + +static DevMap* deviceList(int type, ALuint* count) +{ + snd_ctl_t* handle; + snd_pcm_info_t pcminfo; + int max_cards, card, err, dev, num_devices, idx; + DevMap* dev_list; + char name[1024]; + struct snd_ctl_hw_info info; + void* temp; + + idx=0; + num_devices=0; + max_cards=snd_cards(); + + if (max_cards<=0) + { + return 0; + } + + dev_list=malloc(sizeof(DevMap)*1); + dev_list[0].name=strdup(qsaDevice); + num_devices=1; + + for (card=0; card<max_cards; card++) + { + if ((err=snd_ctl_open(&handle, card))<0) + { + continue; + } + if ((err=snd_ctl_hw_info(handle, &info))<0) + { + snd_ctl_close(handle); + continue; + } + + for (dev=0; dev<(int)info.pcmdevs; dev++) + { + if ((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0) + { + continue; + } + + if ((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) || + (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE))) + { + temp=realloc(dev_list, sizeof(DevMap)*(num_devices+1)); + if (temp) + { + dev_list=temp; + snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev); + dev_list[num_devices].name=strdup(name); + dev_list[num_devices].card=card; + dev_list[num_devices].dev=dev; + num_devices++; + } + } + } + snd_ctl_close (handle); + } + + *count=num_devices; + + return dev_list; +} + + +FORCE_ALIGN static ALuint qsa_proc_playback(ALvoid* ptr) +{ + ALCdevice* device=(ALCdevice*)ptr; + qsa_data* data=(qsa_data*)device->ExtraData; + char* write_ptr; + int avail; + snd_pcm_channel_status_t status; + struct sched_param param; + fd_set wfds; + int selectret; + struct timeval timeout; + + SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); + + /* Increase default 10 priority to 11 to avoid jerky sound */ + SchedGet(0, 0, ¶m); + param.sched_priority=param.sched_curpriority+1; + SchedSet(0, 0, SCHED_NOCHANGE, ¶m); + + ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + while (!data->killNow) + { + ALint len=data->size; + write_ptr=data->buffer; + + avail=len/frame_size; + aluMixData(device, write_ptr, avail); + + while (len>0 && !data->killNow) + { + 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); + + if (wrote<=0) + { + if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) + { + continue; + } + + 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 ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0) + { + aluHandleDisconnect(device); + break; + } + } + } + else + { + write_ptr+=wrote; + len-=wrote; + } + } + } + + return 0; +} + +/************/ +/* Playback */ +/************/ + +static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName) +{ + qsa_data* data; + char driver[64]; + int status; + int card, dev; + + strncpy(driver, GetConfigValue("qsa", "device", qsaDevice), sizeof(driver)-1); + driver[sizeof(driver)-1]=0; + + data=(qsa_data*)calloc(1, sizeof(qsa_data)); + if (data==NULL) + { + return ALC_OUT_OF_MEMORY; + } + + if (!deviceName) + { + deviceName=driver; + } + + if (strcmp(deviceName, qsaDevice)==0) + { + if (!deviceName) + { + deviceName=qsaDevice; + } + + status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK); + } + else + { + size_t idx; + + if (!allDevNameMap) + { + allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames); + } + + for (idx=0; idx<numDevNames; idx++) + { + if (allDevNameMap[idx].name && strcmp(deviceName, allDevNameMap[idx].name)==0) + { + if (idx>0) + { + break; + } + } + } + if (idx==numDevNames) + { + free(data); + return ALC_INVALID_DEVICE; + } + + status=snd_pcm_open(&data->pcmHandle, allDevNameMap[idx].card, allDevNameMap[idx].dev, SND_PCM_OPEN_PLAYBACK); + } + + if (status<0) + { + free(data); + return ALC_INVALID_DEVICE; + } + + data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK); + if (data->audio_fd<0) + { + free(data); + return ALC_INVALID_DEVICE; + } + + device->DeviceName=strdup(deviceName); + device->ExtraData=data; + + return ALC_NO_ERROR; +} + +static void qsa_close_playback(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + + if (data->buffer!=NULL) + { + free(data->buffer); + data->buffer=NULL; + } + + snd_pcm_close(data->pcmHandle); + free(data); + + device->ExtraData=NULL; +} + +static ALCboolean qsa_reset_playback(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + int32_t format=-1; + + switch(device->FmtType) + { + case DevFmtByte: + format=SND_PCM_SFMT_S8; + break; + case DevFmtUByte: + format=SND_PCM_SFMT_U8; + break; + case DevFmtShort: + format=SND_PCM_SFMT_S16_LE; + break; + case DevFmtUShort: + format=SND_PCM_SFMT_U16_LE; + break; + case DevFmtInt: + format=SND_PCM_SFMT_S32_LE; + break; + case DevFmtUInt: + format=SND_PCM_SFMT_U32_LE; + break; + case DevFmtFloat: + format=SND_PCM_SFMT_FLOAT_LE; + break; + } + + /* we actually don't want to block on writes */ + snd_pcm_nonblock_mode(data->pcmHandle, 1); + /* Disable mmap to control data transfer to the audio device */ + snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP); + snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS); + + // configure a sound channel + memset(&data->cparams, 0, sizeof(data->cparams)); + data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK; + data->cparams.mode=SND_PCM_MODE_BLOCK; + 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.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.format=format; + + if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) + { + int original_rate=data->cparams.format.rate; + int original_voices=data->cparams.format.voices; + int original_format=data->cparams.format.format; + int it; + int jt; + + for (it=0; it<1; it++) + { + /* Check for second pass */ + if (it==1) + { + original_rate=ratelist[0].rate; + original_voices=channellist[0].channels; + original_format=formatlist[0].format; + } + + do { + /* At first downgrade sample format */ + jt=0; + do { + if (formatlist[jt].format==data->cparams.format.format) + { + data->cparams.format.format=formatlist[jt+1].format; + break; + } + if (formatlist[jt].format==0) + { + data->cparams.format.format=0; + break; + } + jt++; + } while(1); + + if (data->cparams.format.format==0) + { + data->cparams.format.format=original_format; + + /* At secod downgrade sample rate */ + jt=0; + do { + if (ratelist[jt].rate==data->cparams.format.rate) + { + data->cparams.format.rate=ratelist[jt+1].rate; + break; + } + if (ratelist[jt].rate==0) + { + data->cparams.format.rate=0; + break; + } + jt++; + } while(1); + + if (data->cparams.format.rate==0) + { + data->cparams.format.rate=original_rate; + data->cparams.format.format=original_format; + + /* At third downgrade channels number */ + jt=0; + do { + if(channellist[jt].channels==data->cparams.format.voices) + { + data->cparams.format.voices=channellist[jt+1].channels; + break; + } + if (channellist[jt].channels==0) + { + data->cparams.format.voices=0; + break; + } + jt++; + } while(1); + } + + if (data->cparams.format.voices==0) + { + break; + } + } + + data->cparams.buf.block.frag_size=device->UpdateSize* + data->cparams.format.voices* + snd_pcm_format_width(data->cparams.format.format)/8; + data->cparams.buf.block.frags_max=device->NumUpdates; + data->cparams.buf.block.frags_min=device->NumUpdates; + if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) + { + continue; + } + else + { + break; + } + } while(1); + + if (data->cparams.format.voices!=0) + { + break; + } + } + + if (data->cparams.format.voices==0) + { + return ALC_FALSE; + } + } + + if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0) + { + return ALC_FALSE; + } + + memset(&data->csetup, 0, sizeof(data->csetup)); + data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK; + if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0) + { + return ALC_FALSE; + } + + /* now fill back to the our AL device */ + device->Frequency=data->cparams.format.rate; + + switch (data->cparams.format.voices) + { + case 1: + device->FmtChans=DevFmtMono; + break; + case 2: + device->FmtChans=DevFmtStereo; + break; + case 4: + device->FmtChans=DevFmtQuad; + break; + case 6: + device->FmtChans=DevFmtX51; + break; + case 7: + device->FmtChans=DevFmtX61; + break; + case 8: + device->FmtChans=DevFmtX71; + break; + default: + device->FmtChans=DevFmtMono; + break; + } + + switch (data->cparams.format.format) + { + case SND_PCM_SFMT_S8: + device->FmtType=DevFmtByte; + break; + case SND_PCM_SFMT_U8: + device->FmtType=DevFmtUByte; + break; + case SND_PCM_SFMT_S16_LE: + device->FmtType=DevFmtShort; + break; + case SND_PCM_SFMT_U16_LE: + device->FmtType=DevFmtUShort; + break; + case SND_PCM_SFMT_S32_LE: + device->FmtType=DevFmtInt; + break; + case SND_PCM_SFMT_U32_LE: + device->FmtType=DevFmtUInt; + break; + case SND_PCM_SFMT_FLOAT_LE: + device->FmtType=DevFmtFloat; + break; + default: + device->FmtType=DevFmtShort; + break; + } + + SetDefaultChannelOrder(device); + + device->UpdateSize=data->csetup.buf.block.frag_size/ + (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType)); + device->NumUpdates=data->csetup.buf.block.frags; + + data->size=data->csetup.buf.block.frag_size; + data->buffer=malloc(data->size); + if (!data->buffer) + { + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static ALCboolean qsa_start_playback(ALCdevice* device) +{ + qsa_data *data = (qsa_data*)device->ExtraData; + + if(!StartThread(&data->thread, qsa_proc_playback, device)) + return ALC_FALSE; + + return ALC_TRUE; +} + +static void qsa_stop_playback(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + + if (data->thread) + { + data->killNow=1; + StopThread(data->thread); + data->thread=NULL; + } + data->killNow=0; +} + +/***********/ +/* Capture */ +/***********/ + +static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName) +{ + qsa_data* data; + int format=-1; + char driver[64]; + int card, dev; + int status; + + strncpy(driver, GetConfigValue("qsa", "capture", qsaDevice), sizeof(driver)-1); + driver[sizeof(driver)-1]=0; + + data=(qsa_data*)calloc(1, sizeof(qsa_data)); + if (data==NULL) + { + return ALC_OUT_OF_MEMORY; + } + + if (!deviceName) + { + deviceName=driver; + } + + if (strcmp(deviceName, qsaDevice)==0) + { + if (!deviceName) + { + deviceName=qsaDevice; + } + + status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE); + } + else + { + size_t idx; + + if (!allCaptureDevNameMap) + { + allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numDevNames); + } + + for (idx=0; idx<numDevNames; idx++) + { + if (allCaptureDevNameMap[idx].name && strcmp(deviceName, allCaptureDevNameMap[idx].name)==0) + { + if (idx>0) + { + break; + } + } + } + if (idx==numDevNames) + { + free(data); + return ALC_INVALID_DEVICE; + } + + status=snd_pcm_open(&data->pcmHandle, allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev, SND_PCM_OPEN_CAPTURE); + } + + if (status<0) + { + free(data); + return ALC_INVALID_DEVICE; + } + + data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE); + if (data->audio_fd<0) + { + free(data); + return ALC_INVALID_DEVICE; + } + + device->DeviceName=strdup(deviceName); + device->ExtraData=data; + + switch (device->FmtType) + { + case DevFmtByte: + format=SND_PCM_SFMT_S8; + break; + case DevFmtUByte: + format=SND_PCM_SFMT_U8; + break; + case DevFmtShort: + format=SND_PCM_SFMT_S16_LE; + break; + case DevFmtUShort: + format=SND_PCM_SFMT_U16_LE; + break; + case DevFmtInt: + format=SND_PCM_SFMT_S32_LE; + break; + case DevFmtUInt: + format=SND_PCM_SFMT_U32_LE; + break; + case DevFmtFloat: + format=SND_PCM_SFMT_FLOAT_LE; + break; + } + + /* we actually don't want to block on reads */ + snd_pcm_nonblock_mode(data->pcmHandle, 1); + /* Disable mmap to control data transfer to the audio device */ + snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP); + + /* configure a sound channel */ + memset(&data->cparams, 0, sizeof(data->cparams)); + data->cparams.mode=SND_PCM_MODE_BLOCK; + data->cparams.channel=SND_PCM_CHANNEL_CAPTURE; + data->cparams.start_mode=SND_PCM_START_GO; + 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.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.format=format; + + if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) + { + int original_rate=data->cparams.format.rate; + int original_voices=data->cparams.format.voices; + int original_format=data->cparams.format.format; + int it; + int jt; + + for (it=0; it<1; it++) + { + /* Check for second pass */ + if (it==1) + { + original_rate=ratelist[0].rate; + original_voices=channellist[0].channels; + original_format=formatlist[0].format; + } + + do { + /* At first downgrade sample format */ + jt=0; + do { + if (formatlist[jt].format==data->cparams.format.format) + { + data->cparams.format.format=formatlist[jt+1].format; + break; + } + if (formatlist[jt].format==0) + { + data->cparams.format.format=0; + break; + } + jt++; + } while(1); + + if (data->cparams.format.format==0) + { + data->cparams.format.format=original_format; + + /* At secod downgrade sample rate */ + jt=0; + do { + if (ratelist[jt].rate==data->cparams.format.rate) + { + data->cparams.format.rate=ratelist[jt+1].rate; + break; + } + if (ratelist[jt].rate==0) + { + data->cparams.format.rate=0; + break; + } + jt++; + } while(1); + + if (data->cparams.format.rate==0) + { + data->cparams.format.rate=original_rate; + data->cparams.format.format=original_format; + + /* At third downgrade channels number */ + jt=0; + do { + if(channellist[jt].channels==data->cparams.format.voices) + { + data->cparams.format.voices=channellist[jt+1].channels; + break; + } + if (channellist[jt].channels==0) + { + data->cparams.format.voices=0; + break; + } + jt++; + } while(1); + } + + if (data->cparams.format.voices==0) + { + break; + } + } + + data->cparams.buf.block.frag_size=device->UpdateSize* + data->cparams.format.voices* + snd_pcm_format_width(data->cparams.format.format)/8; + data->cparams.buf.block.frags_max=device->NumUpdates; + data->cparams.buf.block.frags_min=device->NumUpdates; + if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) + { + continue; + } + else + { + break; + } + } while(1); + + if (data->cparams.format.voices!=0) + { + break; + } + } + + if (data->cparams.format.voices==0) + { + return ALC_INVALID_VALUE; + } + } + + /* now fill back to the our AL device */ + device->Frequency=data->cparams.format.rate; + + switch (data->cparams.format.voices) + { + case 1: + device->FmtChans=DevFmtMono; + break; + case 2: + device->FmtChans=DevFmtStereo; + break; + case 4: + device->FmtChans=DevFmtQuad; + break; + case 6: + device->FmtChans=DevFmtX51; + break; + case 7: + device->FmtChans=DevFmtX61; + break; + case 8: + device->FmtChans=DevFmtX71; + break; + default: + device->FmtChans=DevFmtMono; + break; + } + + switch (data->cparams.format.format) + { + case SND_PCM_SFMT_S8: + device->FmtType=DevFmtByte; + break; + case SND_PCM_SFMT_U8: + device->FmtType=DevFmtUByte; + break; + case SND_PCM_SFMT_S16_LE: + device->FmtType=DevFmtShort; + break; + case SND_PCM_SFMT_U16_LE: + device->FmtType=DevFmtUShort; + break; + case SND_PCM_SFMT_S32_LE: + device->FmtType=DevFmtInt; + break; + case SND_PCM_SFMT_U32_LE: + device->FmtType=DevFmtUInt; + break; + case SND_PCM_SFMT_FLOAT_LE: + device->FmtType=DevFmtFloat; + break; + default: + device->FmtType=DevFmtShort; + break; + } + + return ALC_NO_ERROR; +} + +static void qsa_close_capture(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + + if (data->pcmHandle!=NULL) + { + snd_pcm_close(data->pcmHandle); + } + free(data); + device->ExtraData=NULL; +} + +static void qsa_start_capture(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + int rstatus; + + if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) + { + ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); + return; + } + + memset(&data->csetup, 0, sizeof(data->csetup)); + data->csetup.channel=SND_PCM_CHANNEL_CAPTURE; + if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0) + { + ERR("capture setup failed: %s\n", snd_strerror(rstatus)); + return; + } + + snd_pcm_capture_go(data->pcmHandle); + + device->UpdateSize=data->csetup.buf.block.frag_size/ + (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType)); + device->NumUpdates=data->csetup.buf.block.frags; +} + +static void qsa_stop_capture(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + + snd_pcm_capture_flush(data->pcmHandle); +} + +static ALCuint qsa_available_samples(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + snd_pcm_channel_status_t status; + ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + ALint free_size; + int rstatus; + + memset(&status, 0, sizeof (status)); + status.channel=SND_PCM_CHANNEL_CAPTURE; + snd_pcm_plugin_status(data->pcmHandle, &status); + if ((status.status==SND_PCM_STATUS_OVERRUN) || + (status.status==SND_PCM_STATUS_READY)) + { + if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) + { + ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); + aluHandleDisconnect(device); + return 0; + } + + snd_pcm_capture_go(data->pcmHandle); + return 0; + } + + free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags; + free_size-=status.free; + + return free_size/frame_size; +} + +static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +{ + qsa_data* data=(qsa_data*)device->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 len=samples*frame_size; + int rstatus; + + read_ptr=buffer; + + while (len>0) + { + FD_ZERO(&rfds); + FD_SET(data->audio_fd, &rfds); + timeout.tv_sec=2; + timeout.tv_usec=0; + + /* Select also works like time slice to OS */ + bytes_read=0; + selectret=select(data->audio_fd+1, &rfds, NULL, NULL, &timeout); + switch (selectret) + { + case -1: + aluHandleDisconnect(device); + return ALC_INVALID_DEVICE; + case 0: + break; + default: + if (FD_ISSET(data->audio_fd, &rfds)) + { + bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len); + break; + } + break; + } + + if (bytes_read<=0) + { + if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) + { + continue; + } + + memset(&status, 0, sizeof (status)); + status.channel=SND_PCM_CHANNEL_CAPTURE; + snd_pcm_plugin_status(data->pcmHandle, &status); + + /* we need to reinitialize the sound channel if we've overrun the buffer */ + if ((status.status==SND_PCM_STATUS_OVERRUN) || + (status.status==SND_PCM_STATUS_READY)) + { + if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) + { + ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); + aluHandleDisconnect(device); + return ALC_INVALID_DEVICE; + } + snd_pcm_capture_go(data->pcmHandle); + } + } + else + { + read_ptr+=bytes_read; + len-=bytes_read; + } + } + + return ALC_NO_ERROR; +} + +static ALint64 qsa_get_latency(ALCdevice* device) +{ + ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + return (ALint64)(device->UpdateSize*device->NumUpdates/frame_size)* + 1000000000/device->Frequency; +} + +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, + qsa_get_latency, +}; + +ALCboolean alc_qsa_init(BackendFuncs* func_list) +{ + *func_list=qsa_funcs; + + return ALC_TRUE; +} + +void alc_qsa_deinit(void) +{ + ALuint i; + + for (i=0; i<numDevNames; ++i) + { + free(allDevNameMap[i].name); + } + free(allDevNameMap); + allDevNameMap=NULL; + numDevNames=0; + + for (i=0; i<numCaptureDevNames; ++i) + { + free(allCaptureDevNameMap[i].name); + } + free(allCaptureDevNameMap); + allCaptureDevNameMap=NULL; + numCaptureDevNames=0; +} + +void alc_qsa_probe(enum DevProbe type) +{ + ALuint i; + + switch (type) + { + case ALL_DEVICE_PROBE: + for (i=0; i<numDevNames; ++i) + { + free(allDevNameMap[i].name); + } + free(allDevNameMap); + + allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames); + for (i=0; i<numDevNames; ++i) + { + AppendAllDevicesList(allDevNameMap[i].name); + } + break; + case CAPTURE_DEVICE_PROBE: + for (i=0; i<numCaptureDevNames; ++i) + { + free(allCaptureDevNameMap[i].name); + } + free(allCaptureDevNameMap); + + allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numCaptureDevNames); + for (i=0; i<numCaptureDevNames; ++i) + { + AppendCaptureDeviceList(allCaptureDevNameMap[i].name); + } + break; + } +} diff --git a/Alc/backends/sndio.c b/Alc/backends/sndio.c index 2be88746..80aebfd1 100644 --- a/Alc/backends/sndio.c +++ b/Alc/backends/sndio.c @@ -26,6 +26,7 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" #include <sndio.h> @@ -46,7 +47,7 @@ typedef struct { ALsizei data_size; volatile int killNow; - ALvoid *thread; + althread_t thread; } sndio_data; @@ -58,6 +59,7 @@ static ALuint sndio_proc(ALvoid *ptr) size_t wrote; SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); @@ -222,8 +224,7 @@ static ALCboolean sndio_start_playback(ALCdevice *device) data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); data->mix_data = calloc(1, data->data_size); - data->thread = StartThread(sndio_proc, device); - if(data->thread == NULL) + if(!StartThread(&data->thread, sndio_proc, device)) { sio_stop(data->sndHandle); free(data->mix_data); @@ -266,8 +267,6 @@ static const BackendFuncs sndio_funcs = { NULL, NULL, NULL, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, ALCdevice_GetLatencyDefault }; diff --git a/Alc/backends/solaris.c b/Alc/backends/solaris.c index 1c781387..700131c8 100644 --- a/Alc/backends/solaris.c +++ b/Alc/backends/solaris.c @@ -33,6 +33,8 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" +#include "compat.h" #include <sys/audioio.h> @@ -43,11 +45,12 @@ static const char *solaris_driver = "/dev/audio"; typedef struct { int fd; - volatile int killNow; - ALvoid *thread; ALubyte *mix_data; int data_size; + + volatile int killNow; + althread_t thread; } solaris_data; @@ -59,6 +62,7 @@ static ALuint SolarisProc(ALvoid *ptr) int wrote; SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); @@ -207,8 +211,7 @@ static ALCboolean solaris_start_playback(ALCdevice *device) data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); data->mix_data = calloc(1, data->data_size); - data->thread = StartThread(SolarisProc, device); - if(data->thread == NULL) + if(!StartThread(&data->thread, SolarisProc, device)) { free(data->mix_data); data->mix_data = NULL; @@ -250,8 +253,6 @@ static const BackendFuncs solaris_funcs = { NULL, NULL, NULL, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, ALCdevice_GetLatencyDefault }; diff --git a/Alc/backends/wave.c b/Alc/backends/wave.c index be528c9a..2fafc4b9 100644 --- a/Alc/backends/wave.c +++ b/Alc/backends/wave.c @@ -23,12 +23,15 @@ #include <stdlib.h> #include <stdio.h> #include <memory.h> +#include <errno.h> #ifdef HAVE_WINDOWS_H #include <windows.h> #endif #include "alMain.h" #include "alu.h" +#include "threads.h" +#include "compat.h" typedef struct { @@ -39,7 +42,7 @@ typedef struct { ALuint size; volatile int killNow; - ALvoid *thread; + althread_t thread; } wave_data; @@ -93,6 +96,8 @@ static ALuint WaveProc(ALvoid *ptr) const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 / Device->Frequency / 2; + SetThreadName(MIXER_THREAD_NAME); + frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); done = 0; @@ -146,7 +151,7 @@ static ALuint WaveProc(ALvoid *ptr) { fs = fwrite(data->buffer, frameSize, Device->UpdateSize, data->f); - fs = fs; + (void)fs; } if(ferror(data->f)) { @@ -257,7 +262,7 @@ static ALCboolean wave_reset_playback(ALCdevice *device) fwrite32le(channel_masks[channels], data->f); // 16 byte GUID, sub-type format val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f); - val = val; + (void)val; fprintf(data->f, "data"); fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close @@ -286,8 +291,7 @@ static ALCboolean wave_start_playback(ALCdevice *device) return ALC_FALSE; } - data->thread = StartThread(WaveProc, device); - if(data->thread == NULL) + if(!StartThread(&data->thread, WaveProc, device)) { free(data->buffer); data->buffer = NULL; @@ -339,8 +343,6 @@ static const BackendFuncs wave_funcs = { NULL, NULL, NULL, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, ALCdevice_GetLatencyDefault }; diff --git a/Alc/backends/winmm.c b/Alc/backends/winmm.c index 4786d9b4..7082a874 100644 --- a/Alc/backends/winmm.c +++ b/Alc/backends/winmm.c @@ -29,6 +29,7 @@ #include "alMain.h" #include "alu.h" +#include "threads.h" #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 @@ -146,14 +147,11 @@ static void ProbeCaptureDevices(void) Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and returns to the application (for more data) */ -static void CALLBACK WaveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2) +static void CALLBACK WaveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2)) { ALCdevice *Device = (ALCdevice*)instance; WinMMData *data = Device->ExtraData; - (void)device; - (void)param2; - if(msg != WOM_DONE) return; @@ -167,7 +165,7 @@ static void CALLBACK WaveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its audio data. */ -static DWORD WINAPI PlaybackThreadProc(LPVOID param) +FORCE_ALIGN static DWORD WINAPI PlaybackThreadProc(LPVOID param) { ALCdevice *Device = (ALCdevice*)param; WinMMData *data = Device->ExtraData; @@ -178,6 +176,7 @@ static DWORD WINAPI PlaybackThreadProc(LPVOID param) FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); SetRTPriority(); + SetThreadName(MIXER_THREAD_NAME); while(GetMessage(&msg, NULL, 0, 0)) { @@ -213,14 +212,11 @@ static DWORD WINAPI PlaybackThreadProc(LPVOID param) Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and returns to the application (with more data) */ -static void CALLBACK WaveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2) +static void CALLBACK WaveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2)) { ALCdevice *Device = (ALCdevice*)instance; WinMMData *data = Device->ExtraData; - (void)device; - (void)param2; - if(msg != WIM_DATA) return; @@ -243,6 +239,7 @@ static DWORD WINAPI CaptureThreadProc(LPVOID param) MSG msg; FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); + SetThreadName("alsoft-record"); while(GetMessage(&msg, NULL, 0, 0)) { @@ -716,8 +713,6 @@ static const BackendFuncs WinMMFuncs = { WinMMStopCapture, WinMMCaptureSamples, WinMMAvailableSamples, - ALCdevice_LockDefault, - ALCdevice_UnlockDefault, ALCdevice_GetLatencyDefault }; |