From 9989f33fc24042578bf4bf2c50a06a6366d07c1d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 20 Aug 2011 00:41:47 -0700 Subject: Move backend sources to a separate sub-directory --- Alc/alsa.c | 1138 ------------------------------------ Alc/backends/alsa.c | 1138 ++++++++++++++++++++++++++++++++++++ Alc/backends/coreaudio.c | 719 +++++++++++++++++++++++ Alc/backends/dsound.c | 627 ++++++++++++++++++++ Alc/backends/loopback.c | 77 +++ Alc/backends/mmdevapi.c | 775 ++++++++++++++++++++++++ Alc/backends/null.c | 164 ++++++ Alc/backends/opensl.c | 425 ++++++++++++++ Alc/backends/oss.c | 536 +++++++++++++++++ Alc/backends/portaudio.c | 449 ++++++++++++++ Alc/backends/pulseaudio.c | 1430 +++++++++++++++++++++++++++++++++++++++++++++ Alc/backends/sndio.c | 381 ++++++++++++ Alc/backends/solaris.c | 282 +++++++++ Alc/backends/wave.c | 355 +++++++++++ Alc/backends/winmm.c | 780 +++++++++++++++++++++++++ Alc/coreaudio.c | 719 ----------------------- Alc/dsound.c | 627 -------------------- Alc/loopback.c | 77 --- Alc/mmdevapi.c | 775 ------------------------ Alc/null.c | 164 ------ Alc/opensl.c | 425 -------------- Alc/oss.c | 536 ----------------- Alc/portaudio.c | 449 -------------- Alc/pulseaudio.c | 1430 --------------------------------------------- Alc/sndio.c | 381 ------------ Alc/solaris.c | 282 --------- Alc/wave.c | 355 ----------- Alc/winmm.c | 780 ------------------------- CMakeLists.txt | 29 +- 29 files changed, 8153 insertions(+), 8152 deletions(-) delete mode 100644 Alc/alsa.c create mode 100644 Alc/backends/alsa.c create mode 100644 Alc/backends/coreaudio.c create mode 100644 Alc/backends/dsound.c create mode 100644 Alc/backends/loopback.c create mode 100644 Alc/backends/mmdevapi.c create mode 100644 Alc/backends/null.c create mode 100644 Alc/backends/opensl.c create mode 100644 Alc/backends/oss.c create mode 100644 Alc/backends/portaudio.c create mode 100644 Alc/backends/pulseaudio.c create mode 100644 Alc/backends/sndio.c create mode 100644 Alc/backends/solaris.c create mode 100644 Alc/backends/wave.c create mode 100644 Alc/backends/winmm.c delete mode 100644 Alc/coreaudio.c delete mode 100644 Alc/dsound.c delete mode 100644 Alc/loopback.c delete mode 100644 Alc/mmdevapi.c delete mode 100644 Alc/null.c delete mode 100644 Alc/opensl.c delete mode 100644 Alc/oss.c delete mode 100644 Alc/portaudio.c delete mode 100644 Alc/pulseaudio.c delete mode 100644 Alc/sndio.c delete mode 100644 Alc/solaris.c delete mode 100644 Alc/wave.c delete mode 100644 Alc/winmm.c diff --git a/Alc/alsa.c b/Alc/alsa.c deleted file mode 100644 index ffed94a8..00000000 --- a/Alc/alsa.c +++ /dev/null @@ -1,1138 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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 -#include -#include - -#include "alMain.h" - -#include - - -static const ALCchar alsaDevice[] = "ALSA Default"; - - -static void *alsa_handle; -#ifdef HAVE_DYNLOAD -#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_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_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); -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); -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_wait); -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_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); -#undef MAKE_FUNC - -#define snd_strerror psnd_strerror -#define snd_pcm_open psnd_pcm_open -#define snd_pcm_close psnd_pcm_close -#define snd_pcm_nonblock psnd_pcm_nonblock -#define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes -#define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames -#define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc -#define snd_pcm_hw_params_free psnd_pcm_hw_params_free -#define snd_pcm_hw_params_any psnd_pcm_hw_params_any -#define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access -#define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format -#define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels -#define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near -#define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near -#define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate -#define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample -#define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near -#define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near -#define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near -#define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near -#define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min -#define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size -#define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size -#define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access -#define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods -#define snd_pcm_hw_params psnd_pcm_hw_params -#define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc -#define snd_pcm_sw_params_current psnd_pcm_sw_params_current -#define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min -#define snd_pcm_sw_params psnd_pcm_sw_params -#define snd_pcm_sw_params_free psnd_pcm_sw_params_free -#define snd_pcm_prepare psnd_pcm_prepare -#define snd_pcm_start psnd_pcm_start -#define snd_pcm_resume psnd_pcm_resume -#define snd_pcm_wait psnd_pcm_wait -#define snd_pcm_state psnd_pcm_state -#define snd_pcm_avail_update psnd_pcm_avail_update -#define snd_pcm_areas_silence psnd_pcm_areas_silence -#define snd_pcm_mmap_begin psnd_pcm_mmap_begin -#define snd_pcm_mmap_commit psnd_pcm_mmap_commit -#define snd_pcm_readi psnd_pcm_readi -#define snd_pcm_writei psnd_pcm_writei -#define snd_pcm_drain psnd_pcm_drain -#define snd_pcm_recover psnd_pcm_recover -#define snd_pcm_info_malloc psnd_pcm_info_malloc -#define snd_pcm_info_free psnd_pcm_info_free -#define snd_pcm_info_set_device psnd_pcm_info_set_device -#define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice -#define snd_pcm_info_set_stream psnd_pcm_info_set_stream -#define snd_pcm_info_get_name psnd_pcm_info_get_name -#define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device -#define snd_ctl_pcm_info psnd_ctl_pcm_info -#define snd_ctl_open psnd_ctl_open -#define snd_ctl_close psnd_ctl_close -#define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc -#define snd_ctl_card_info_free psnd_ctl_card_info_free -#define snd_ctl_card_info psnd_ctl_card_info -#define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name -#define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id -#define snd_card_next psnd_card_next -#endif - - -static ALCboolean alsa_load(void) -{ - if(!alsa_handle) - { -#ifdef HAVE_DYNLOAD - alsa_handle = LoadLib("libasound.so.2"); - if(!alsa_handle) - return 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; \ - } \ -} 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_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_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); - 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); - 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_wait); - 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_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); -#undef LOAD_FUNC -#else - alsa_handle = (void*)0xDEADBEEF; -#endif - } - return ALC_TRUE; -} - - -typedef struct { - snd_pcm_t *pcmHandle; - - ALvoid *buffer; - ALsizei size; - - ALboolean doCapture; - RingBuffer *ring; - - volatile int killNow; - ALvoid *thread; -} alsa_data; - -typedef struct { - ALCchar *name; - char *card; - int dev; -} DevMap; - -static DevMap *allDevNameMap; -static ALuint numDevNames; -static DevMap *allCaptureDevNameMap; -static ALuint numCaptureDevNames; - -static const char *device_prefix; -static const char *capture_prefix; - - -static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count) -{ - snd_ctl_t *handle; - int card, err, dev, idx; - snd_ctl_card_info_t *info; - snd_pcm_info_t *pcminfo; - DevMap *DevList; - char name[1024]; - - snd_ctl_card_info_malloc(&info); - snd_pcm_info_malloc(&pcminfo); - - card = -1; - if((err=snd_card_next(&card)) < 0) - ERR("Failed to find a card: %s\n", snd_strerror(err)); - - DevList = malloc(sizeof(DevMap) * 1); - DevList[0].name = strdup("ALSA Default"); - DevList[0].card = NULL; - DevList[0].dev = 0; - idx = 1; - while(card >= 0) - { - sprintf(name, "hw:%d", card); - if((err = snd_ctl_open(&handle, name, 0)) < 0) - { - ERR("control open (%i): %s\n", card, snd_strerror(err)); - goto next_card; - } - if((err = snd_ctl_card_info(handle, info)) < 0) - { - ERR("control hardware info (%i): %s\n", card, snd_strerror(err)); - snd_ctl_close(handle); - goto next_card; - } - - dev = -1; - while(1) - { - const char *cname, *dname, *cid; - void *temp; - - if(snd_ctl_pcm_next_device(handle, &dev) < 0) - ERR("snd_ctl_pcm_next_device failed\n"); - if(dev < 0) - break; - - snd_pcm_info_set_device(pcminfo, dev); - snd_pcm_info_set_subdevice(pcminfo, 0); - snd_pcm_info_set_stream(pcminfo, stream); - if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { - if(err != -ENOENT) - ERR("control digital audio info (%i): %s\n", card, snd_strerror(err)); - continue; - } - - temp = realloc(DevList, sizeof(DevMap) * (idx+1)); - if(temp) - { - DevList = temp; - cname = snd_ctl_card_info_get_name(info); - dname = snd_pcm_info_get_name(pcminfo); - cid = snd_ctl_card_info_get_id(info); - snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)", - cname, dname, cid, dev); - DevList[idx].name = strdup(name); - DevList[idx].card = strdup(cid); - DevList[idx].dev = dev; - idx++; - } - } - snd_ctl_close(handle); - next_card: - if(snd_card_next(&card) < 0) { - ERR("snd_card_next failed\n"); - break; - } - } - - snd_pcm_info_free(pcminfo); - snd_ctl_card_info_free(info); - - *count = idx; - return DevList; -} - - -static int xrun_recovery(snd_pcm_t *handle, int err) -{ - err = snd_pcm_recover(handle, err, 1); - if(err < 0) - ERR("recover failed: %s\n", snd_strerror(err)); - return err; -} - -static int verify_state(snd_pcm_t *handle) -{ - snd_pcm_state_t state = snd_pcm_state(handle); - if(state == SND_PCM_STATE_DISCONNECTED) - return -ENODEV; - if(state == SND_PCM_STATE_XRUN) - { - int err = xrun_recovery(handle, -EPIPE); - if(err < 0) return err; - } - else if(state == SND_PCM_STATE_SUSPENDED) - { - int err = xrun_recovery(handle, -ESTRPIPE); - if(err < 0) return err; - } - - return state; -} - - -static ALuint ALSAProc(ALvoid *ptr) -{ - ALCdevice *pDevice = (ALCdevice*)ptr; - alsa_data *data = (alsa_data*)pDevice->ExtraData; - const snd_pcm_channel_area_t *areas = NULL; - snd_pcm_sframes_t avail, commitres; - snd_pcm_uframes_t offset, frames; - char *WritePtr; - int err; - - SetRTPriority(); - - while(!data->killNow) - { - int state = verify_state(data->pcmHandle); - if(state < 0) - { - ERR("Invalid state detected: %s\n", snd_strerror(state)); - aluHandleDisconnect(pDevice); - break; - } - - avail = snd_pcm_avail_update(data->pcmHandle); - if(avail < 0) - { - ERR("available update failed: %s\n", snd_strerror(avail)); - continue; - } - - // make sure there's frames to process - if((snd_pcm_uframes_t)avail < pDevice->UpdateSize) - { - if(state != SND_PCM_STATE_RUNNING) - { - err = snd_pcm_start(data->pcmHandle); - if(err < 0) - { - ERR("start failed: %s\n", snd_strerror(err)); - continue; - } - } - if(snd_pcm_wait(data->pcmHandle, 1000) == 0) - ERR("Wait timeout... buffer size too low?\n"); - continue; - } - avail -= avail%pDevice->UpdateSize; - - // it is possible that contiguous areas are smaller, thus we use a loop - while(avail > 0) - { - frames = avail; - - err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); - if(err < 0) - { - ERR("mmap begin error: %s\n", snd_strerror(err)); - break; - } - - WritePtr = (char*)areas->addr + (offset * areas->step / 8); - aluMixData(pDevice, WritePtr, frames); - - commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames); - if(commitres < 0 || (commitres-frames) != 0) - { - ERR("mmap commit error: %s\n", - snd_strerror(commitres >= 0 ? -EPIPE : commitres)); - break; - } - - avail -= frames; - } - } - - return 0; -} - -static ALuint ALSANoMMapProc(ALvoid *ptr) -{ - ALCdevice *pDevice = (ALCdevice*)ptr; - alsa_data *data = (alsa_data*)pDevice->ExtraData; - snd_pcm_sframes_t avail; - char *WritePtr; - - SetRTPriority(); - - while(!data->killNow) - { - int state = verify_state(data->pcmHandle); - if(state < 0) - { - ERR("Invalid state detected: %s\n", snd_strerror(state)); - aluHandleDisconnect(pDevice); - break; - } - - WritePtr = data->buffer; - avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1); - aluMixData(pDevice, WritePtr, avail); - - while(avail > 0) - { - int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail); - switch (ret) - { - case -EAGAIN: - continue; - case -ESTRPIPE: - case -EPIPE: - case -EINTR: - ret = snd_pcm_recover(data->pcmHandle, ret, 1); - if(ret < 0) - avail = 0; - break; - default: - if (ret >= 0) - { - WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret); - avail -= ret; - } - break; - } - if (ret < 0) - { - ret = snd_pcm_prepare(data->pcmHandle); - if(ret < 0) - break; - } - } - } - - return 0; -} - -static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - alsa_data *data; - char driver[128]; - int i; - - strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1); - driver[sizeof(driver)-1] = 0; - - if(!deviceName) - deviceName = alsaDevice; - else if(strcmp(deviceName, alsaDevice) != 0) - { - size_t idx; - - if(!allDevNameMap) - allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); - - for(idx = 0;idx < numDevNames;idx++) - { - if(allDevNameMap[idx].name && - strcmp(deviceName, allDevNameMap[idx].name) == 0) - { - if(idx > 0) - snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", device_prefix, - allDevNameMap[idx].card, allDevNameMap[idx].dev); - break; - } - } - if(idx == numDevNames) - return ALC_FALSE; - } - - data = (alsa_data*)calloc(1, sizeof(alsa_data)); - - i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - if(i >= 0) - { - i = snd_pcm_nonblock(data->pcmHandle, 0); - if(i < 0) - snd_pcm_close(data->pcmHandle); - } - if(i < 0) - { - free(data); - ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(i)); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - device->ExtraData = data; - return ALC_TRUE; -} - -static void alsa_close_playback(ALCdevice *device) -{ - alsa_data *data = (alsa_data*)device->ExtraData; - - snd_pcm_close(data->pcmHandle); - free(data); - device->ExtraData = NULL; -} - -static ALCboolean alsa_reset_playback(ALCdevice *device) -{ - alsa_data *data = (alsa_data*)device->ExtraData; - snd_pcm_uframes_t periodSizeInFrames; - unsigned int periodLen, bufferLen; - snd_pcm_sw_params_t *sp = NULL; - snd_pcm_hw_params_t *p = NULL; - snd_pcm_access_t access; - snd_pcm_format_t format; - unsigned int periods; - unsigned int rate; - int allowmmap; - char *err; - int i; - - - format = -1; - switch(device->FmtType) - { - case DevFmtByte: - format = SND_PCM_FORMAT_S8; - break; - case DevFmtUByte: - format = SND_PCM_FORMAT_U8; - break; - case DevFmtShort: - format = SND_PCM_FORMAT_S16; - break; - case DevFmtUShort: - format = SND_PCM_FORMAT_U16; - break; - case DevFmtFloat: - format = SND_PCM_FORMAT_FLOAT; - break; - } - - allowmmap = GetConfigValueBool("alsa", "mmap", 1); - periods = device->NumUpdates; - periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency; - bufferLen = periodLen * periods; - rate = device->Frequency; - - err = NULL; - snd_pcm_hw_params_malloc(&p); - - if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0) - err = "any"; - /* set interleaved access */ - if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0)) - { - if(periods > 2) - { - periods--; - bufferLen = periodLen * periods; - } - if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) - err = "set access"; - } - /* set format (implicitly sets sample bits) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0) - { - device->FmtType = DevFmtFloat; - if(format == SND_PCM_FORMAT_FLOAT || - (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0) - { - device->FmtType = DevFmtShort; - if(format == SND_PCM_FORMAT_S16 || - (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0) - { - device->FmtType = DevFmtUByte; - if(format == SND_PCM_FORMAT_U8 || - (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0) - err = "set format"; - } - } - } - /* set channels (implicitly sets frame bits) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0) - { - if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0) - { - if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0) - err = "set channels"; - else - { - if((device->Flags&DEVICE_CHANNELS_REQUEST)) - ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans)); - device->FmtChans = DevFmtMono; - } - } - else - { - if((device->Flags&DEVICE_CHANNELS_REQUEST)) - ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans)); - device->FmtChans = DevFmtStereo; - } - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - } - if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0) - { - ERR("Failed to disable ALSA resampler\n"); - i = 0; - } - /* set rate (implicitly constrains period/buffer parameters) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0) - err = "set rate near"; - /* set buffer time (implicitly constrains period/buffer parameters) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0) - err = "set buffer time near"; - /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0) - err = "set period time near"; - /* install and prepare hardware configuration */ - if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0) - err = "set params"; - if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0) - err = "get access"; - if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0) - err = "get period size"; - if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0) - err = "get periods"; - if(i < 0) - { - ERR("%s failed: %s\n", err, snd_strerror(i)); - snd_pcm_hw_params_free(p); - return ALC_FALSE; - } - - snd_pcm_hw_params_free(p); - - err = NULL; - snd_pcm_sw_params_malloc(&sp); - - if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0) - err = "sw current"; - if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0) - err = "sw set avail min"; - if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0) - err = "sw set params"; - if(i != 0) - { - ERR("%s failed: %s\n", err, snd_strerror(i)); - snd_pcm_sw_params_free(sp); - return ALC_FALSE; - } - - snd_pcm_sw_params_free(sp); - - if(device->Frequency != rate) - { - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, rate); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - device->Frequency = rate; - } - - SetDefaultChannelOrder(device); - - data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames); - if(access == SND_PCM_ACCESS_RW_INTERLEAVED) - { - /* Increase periods by one, since the temp buffer counts as an extra - * period */ - periods++; - data->buffer = malloc(data->size); - if(!data->buffer) - { - ERR("buffer malloc failed\n"); - return ALC_FALSE; - } - device->UpdateSize = periodSizeInFrames; - device->NumUpdates = periods; - data->thread = StartThread(ALSANoMMapProc, device); - } - else - { - i = snd_pcm_prepare(data->pcmHandle); - if(i < 0) - { - ERR("prepare error: %s\n", snd_strerror(i)); - return ALC_FALSE; - } - device->UpdateSize = periodSizeInFrames; - device->NumUpdates = periods; - data->thread = StartThread(ALSAProc, device); - } - if(data->thread == NULL) - { - ERR("Could not create playback thread\n"); - free(data->buffer); - data->buffer = NULL; - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void alsa_stop_playback(ALCdevice *device) -{ - alsa_data *data = (alsa_data*)device->ExtraData; - - if(data->thread) - { - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - } - data->killNow = 0; - free(data->buffer); - data->buffer = NULL; -} - - -static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName) -{ - snd_pcm_hw_params_t *p; - snd_pcm_uframes_t bufferSizeInFrames; - snd_pcm_format_t format; - ALuint frameSize; - alsa_data *data; - char driver[128]; - char *err; - int i; - - strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1); - driver[sizeof(driver)-1] = 0; - - if(!allCaptureDevNameMap) - allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); - - if(!deviceName) - deviceName = allCaptureDevNameMap[0].name; - else - { - size_t idx; - - for(idx = 0;idx < numCaptureDevNames;idx++) - { - if(allCaptureDevNameMap[idx].name && - strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0) - { - if(idx > 0) - snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", capture_prefix, - allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev); - break; - } - } - if(idx == numCaptureDevNames) - return ALC_FALSE; - } - - data = (alsa_data*)calloc(1, sizeof(alsa_data)); - - i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); - if(i < 0) - { - ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(i)); - free(data); - return ALC_FALSE; - } - - format = -1; - switch(pDevice->FmtType) - { - case DevFmtByte: - format = SND_PCM_FORMAT_S8; - break; - case DevFmtUByte: - format = SND_PCM_FORMAT_U8; - break; - case DevFmtShort: - format = SND_PCM_FORMAT_S16; - break; - case DevFmtUShort: - format = SND_PCM_FORMAT_U16; - break; - case DevFmtFloat: - format = SND_PCM_FORMAT_FLOAT; - break; - } - - err = NULL; - bufferSizeInFrames = pDevice->UpdateSize * pDevice->NumUpdates; - snd_pcm_hw_params_malloc(&p); - - if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0) - err = "any"; - /* set interleaved access */ - if(i >= 0 && (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) - err = "set access"; - /* set format (implicitly sets sample bits) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0) - err = "set format"; - /* set channels (implicitly sets frame bits) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(pDevice->FmtChans))) < 0) - err = "set channels"; - /* set rate (implicitly constrains period/buffer parameters) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0) - err = "set rate near"; - /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ - if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0) - err = "set buffer size near"; - /* install and prepare hardware configuration */ - if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0) - err = "set params"; - if(i < 0) - { - ERR("%s failed: %s\n", err, snd_strerror(i)); - snd_pcm_hw_params_free(p); - goto error; - } - - if((i=snd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0) - { - ERR("get size failed: %s\n", snd_strerror(i)); - snd_pcm_hw_params_free(p); - goto error; - } - - snd_pcm_hw_params_free(p); - - frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); - - data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates); - if(!data->ring) - { - ERR("ring buffer create failed\n"); - goto error; - } - - data->size = snd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames); - data->buffer = malloc(data->size); - if(!data->buffer) - { - ERR("buffer malloc failed\n"); - goto error; - } - - pDevice->szDeviceName = strdup(deviceName); - - pDevice->ExtraData = data; - return ALC_TRUE; - -error: - free(data->buffer); - DestroyRingBuffer(data->ring); - snd_pcm_close(data->pcmHandle); - free(data); - - pDevice->ExtraData = NULL; - return ALC_FALSE; -} - -static void alsa_close_capture(ALCdevice *pDevice) -{ - alsa_data *data = (alsa_data*)pDevice->ExtraData; - - snd_pcm_close(data->pcmHandle); - DestroyRingBuffer(data->ring); - - free(data->buffer); - free(data); - pDevice->ExtraData = NULL; -} - -static void alsa_start_capture(ALCdevice *Device) -{ - alsa_data *data = (alsa_data*)Device->ExtraData; - int err; - - err = snd_pcm_start(data->pcmHandle); - if(err < 0) - { - ERR("start failed: %s\n", snd_strerror(err)); - aluHandleDisconnect(Device); - } - else - data->doCapture = AL_TRUE; -} - -static void alsa_stop_capture(ALCdevice *Device) -{ - alsa_data *data = (alsa_data*)Device->ExtraData; - snd_pcm_drain(data->pcmHandle); - data->doCapture = AL_FALSE; -} - -static ALCuint alsa_available_samples(ALCdevice *Device) -{ - alsa_data *data = (alsa_data*)Device->ExtraData; - snd_pcm_sframes_t avail; - - avail = (Device->Connected ? snd_pcm_avail_update(data->pcmHandle) : 0); - if(avail < 0) - { - ERR("avail update failed: %s\n", snd_strerror(avail)); - - if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0) - { - if(data->doCapture) - avail = snd_pcm_start(data->pcmHandle); - if(avail >= 0) - avail = snd_pcm_avail_update(data->pcmHandle); - } - if(avail < 0) - { - ERR("restore error: %s\n", snd_strerror(avail)); - aluHandleDisconnect(Device); - } - } - while(avail > 0) - { - snd_pcm_sframes_t amt; - - amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); - if(avail < amt) amt = avail; - - amt = snd_pcm_readi(data->pcmHandle, data->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(data->doCapture) - amt = snd_pcm_start(data->pcmHandle); - if(amt >= 0) - amt = snd_pcm_avail_update(data->pcmHandle); - } - if(amt < 0) - { - ERR("restore error: %s\n", snd_strerror(amt)); - aluHandleDisconnect(Device); - break; - } - avail = amt; - continue; - } - - WriteRingBuffer(data->ring, data->buffer, amt); - avail -= amt; - } - - return RingBufferSize(data->ring); -} - -static void alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples) -{ - alsa_data *data = (alsa_data*)Device->ExtraData; - - if(Samples <= alsa_available_samples(Device)) - ReadRingBuffer(data->ring, Buffer, Samples); - else - alcSetError(Device, ALC_INVALID_VALUE); -} - - -static const BackendFuncs alsa_funcs = { - alsa_open_playback, - alsa_close_playback, - alsa_reset_playback, - alsa_stop_playback, - alsa_open_capture, - alsa_close_capture, - alsa_start_capture, - alsa_stop_capture, - alsa_capture_samples, - alsa_available_samples -}; - -ALCboolean alc_alsa_init(BackendFuncs *func_list) -{ - if(!alsa_load()) - return ALC_FALSE; - device_prefix = GetConfigValue("alsa", "device-prefix", "plughw:"); - capture_prefix = GetConfigValue("alsa", "capture-prefix", "plughw:"); - *func_list = alsa_funcs; - return ALC_TRUE; -} - -void alc_alsa_deinit(void) -{ - ALuint i; - - for(i = 0;i < numDevNames;++i) - { - free(allDevNameMap[i].name); - free(allDevNameMap[i].card); - } - free(allDevNameMap); - allDevNameMap = NULL; - numDevNames = 0; - - for(i = 0;i < numCaptureDevNames;++i) - { - free(allCaptureDevNameMap[i].name); - free(allCaptureDevNameMap[i].card); - } - free(allCaptureDevNameMap); - allCaptureDevNameMap = NULL; - numCaptureDevNames = 0; - -#ifdef HAVE_DYNLOAD - if(alsa_handle) - CloseLib(alsa_handle); - alsa_handle = NULL; -#endif -} - -void alc_alsa_probe(enum DevProbe type) -{ - ALuint i; - - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(alsaDevice); - break; - - case ALL_DEVICE_PROBE: - for(i = 0;i < numDevNames;++i) - { - free(allDevNameMap[i].name); - free(allDevNameMap[i].card); - } - - free(allDevNameMap); - allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); - - for(i = 0;i < numDevNames;++i) - AppendAllDeviceList(allDevNameMap[i].name); - break; - - case CAPTURE_DEVICE_PROBE: - for(i = 0;i < numCaptureDevNames;++i) - { - free(allCaptureDevNameMap[i].name); - free(allCaptureDevNameMap[i].card); - } - - free(allCaptureDevNameMap); - allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); - - for(i = 0;i < numCaptureDevNames;++i) - AppendCaptureDeviceList(allCaptureDevNameMap[i].name); - break; - } -} diff --git a/Alc/backends/alsa.c b/Alc/backends/alsa.c new file mode 100644 index 00000000..ffed94a8 --- /dev/null +++ b/Alc/backends/alsa.c @@ -0,0 +1,1138 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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 +#include +#include + +#include "alMain.h" + +#include + + +static const ALCchar alsaDevice[] = "ALSA Default"; + + +static void *alsa_handle; +#ifdef HAVE_DYNLOAD +#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_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_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); +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); +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_wait); +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_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); +#undef MAKE_FUNC + +#define snd_strerror psnd_strerror +#define snd_pcm_open psnd_pcm_open +#define snd_pcm_close psnd_pcm_close +#define snd_pcm_nonblock psnd_pcm_nonblock +#define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes +#define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames +#define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc +#define snd_pcm_hw_params_free psnd_pcm_hw_params_free +#define snd_pcm_hw_params_any psnd_pcm_hw_params_any +#define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access +#define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format +#define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels +#define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near +#define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near +#define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate +#define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample +#define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near +#define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near +#define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near +#define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near +#define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min +#define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size +#define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size +#define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access +#define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods +#define snd_pcm_hw_params psnd_pcm_hw_params +#define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc +#define snd_pcm_sw_params_current psnd_pcm_sw_params_current +#define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min +#define snd_pcm_sw_params psnd_pcm_sw_params +#define snd_pcm_sw_params_free psnd_pcm_sw_params_free +#define snd_pcm_prepare psnd_pcm_prepare +#define snd_pcm_start psnd_pcm_start +#define snd_pcm_resume psnd_pcm_resume +#define snd_pcm_wait psnd_pcm_wait +#define snd_pcm_state psnd_pcm_state +#define snd_pcm_avail_update psnd_pcm_avail_update +#define snd_pcm_areas_silence psnd_pcm_areas_silence +#define snd_pcm_mmap_begin psnd_pcm_mmap_begin +#define snd_pcm_mmap_commit psnd_pcm_mmap_commit +#define snd_pcm_readi psnd_pcm_readi +#define snd_pcm_writei psnd_pcm_writei +#define snd_pcm_drain psnd_pcm_drain +#define snd_pcm_recover psnd_pcm_recover +#define snd_pcm_info_malloc psnd_pcm_info_malloc +#define snd_pcm_info_free psnd_pcm_info_free +#define snd_pcm_info_set_device psnd_pcm_info_set_device +#define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice +#define snd_pcm_info_set_stream psnd_pcm_info_set_stream +#define snd_pcm_info_get_name psnd_pcm_info_get_name +#define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device +#define snd_ctl_pcm_info psnd_ctl_pcm_info +#define snd_ctl_open psnd_ctl_open +#define snd_ctl_close psnd_ctl_close +#define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc +#define snd_ctl_card_info_free psnd_ctl_card_info_free +#define snd_ctl_card_info psnd_ctl_card_info +#define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name +#define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id +#define snd_card_next psnd_card_next +#endif + + +static ALCboolean alsa_load(void) +{ + if(!alsa_handle) + { +#ifdef HAVE_DYNLOAD + alsa_handle = LoadLib("libasound.so.2"); + if(!alsa_handle) + return 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; \ + } \ +} 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_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_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); + 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); + 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_wait); + 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_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); +#undef LOAD_FUNC +#else + alsa_handle = (void*)0xDEADBEEF; +#endif + } + return ALC_TRUE; +} + + +typedef struct { + snd_pcm_t *pcmHandle; + + ALvoid *buffer; + ALsizei size; + + ALboolean doCapture; + RingBuffer *ring; + + volatile int killNow; + ALvoid *thread; +} alsa_data; + +typedef struct { + ALCchar *name; + char *card; + int dev; +} DevMap; + +static DevMap *allDevNameMap; +static ALuint numDevNames; +static DevMap *allCaptureDevNameMap; +static ALuint numCaptureDevNames; + +static const char *device_prefix; +static const char *capture_prefix; + + +static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count) +{ + snd_ctl_t *handle; + int card, err, dev, idx; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + DevMap *DevList; + char name[1024]; + + snd_ctl_card_info_malloc(&info); + snd_pcm_info_malloc(&pcminfo); + + card = -1; + if((err=snd_card_next(&card)) < 0) + ERR("Failed to find a card: %s\n", snd_strerror(err)); + + DevList = malloc(sizeof(DevMap) * 1); + DevList[0].name = strdup("ALSA Default"); + DevList[0].card = NULL; + DevList[0].dev = 0; + idx = 1; + while(card >= 0) + { + sprintf(name, "hw:%d", card); + if((err = snd_ctl_open(&handle, name, 0)) < 0) + { + ERR("control open (%i): %s\n", card, snd_strerror(err)); + goto next_card; + } + if((err = snd_ctl_card_info(handle, info)) < 0) + { + ERR("control hardware info (%i): %s\n", card, snd_strerror(err)); + snd_ctl_close(handle); + goto next_card; + } + + dev = -1; + while(1) + { + const char *cname, *dname, *cid; + void *temp; + + if(snd_ctl_pcm_next_device(handle, &dev) < 0) + ERR("snd_ctl_pcm_next_device failed\n"); + if(dev < 0) + break; + + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { + if(err != -ENOENT) + ERR("control digital audio info (%i): %s\n", card, snd_strerror(err)); + continue; + } + + temp = realloc(DevList, sizeof(DevMap) * (idx+1)); + if(temp) + { + DevList = temp; + cname = snd_ctl_card_info_get_name(info); + dname = snd_pcm_info_get_name(pcminfo); + cid = snd_ctl_card_info_get_id(info); + snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)", + cname, dname, cid, dev); + DevList[idx].name = strdup(name); + DevList[idx].card = strdup(cid); + DevList[idx].dev = dev; + idx++; + } + } + snd_ctl_close(handle); + next_card: + if(snd_card_next(&card) < 0) { + ERR("snd_card_next failed\n"); + break; + } + } + + snd_pcm_info_free(pcminfo); + snd_ctl_card_info_free(info); + + *count = idx; + return DevList; +} + + +static int xrun_recovery(snd_pcm_t *handle, int err) +{ + err = snd_pcm_recover(handle, err, 1); + if(err < 0) + ERR("recover failed: %s\n", snd_strerror(err)); + return err; +} + +static int verify_state(snd_pcm_t *handle) +{ + snd_pcm_state_t state = snd_pcm_state(handle); + if(state == SND_PCM_STATE_DISCONNECTED) + return -ENODEV; + if(state == SND_PCM_STATE_XRUN) + { + int err = xrun_recovery(handle, -EPIPE); + if(err < 0) return err; + } + else if(state == SND_PCM_STATE_SUSPENDED) + { + int err = xrun_recovery(handle, -ESTRPIPE); + if(err < 0) return err; + } + + return state; +} + + +static ALuint ALSAProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + alsa_data *data = (alsa_data*)pDevice->ExtraData; + const snd_pcm_channel_area_t *areas = NULL; + snd_pcm_sframes_t avail, commitres; + snd_pcm_uframes_t offset, frames; + char *WritePtr; + int err; + + SetRTPriority(); + + while(!data->killNow) + { + int state = verify_state(data->pcmHandle); + if(state < 0) + { + ERR("Invalid state detected: %s\n", snd_strerror(state)); + aluHandleDisconnect(pDevice); + break; + } + + avail = snd_pcm_avail_update(data->pcmHandle); + if(avail < 0) + { + ERR("available update failed: %s\n", snd_strerror(avail)); + continue; + } + + // make sure there's frames to process + if((snd_pcm_uframes_t)avail < pDevice->UpdateSize) + { + if(state != SND_PCM_STATE_RUNNING) + { + err = snd_pcm_start(data->pcmHandle); + if(err < 0) + { + ERR("start failed: %s\n", snd_strerror(err)); + continue; + } + } + if(snd_pcm_wait(data->pcmHandle, 1000) == 0) + ERR("Wait timeout... buffer size too low?\n"); + continue; + } + avail -= avail%pDevice->UpdateSize; + + // it is possible that contiguous areas are smaller, thus we use a loop + while(avail > 0) + { + frames = avail; + + err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); + if(err < 0) + { + ERR("mmap begin error: %s\n", snd_strerror(err)); + break; + } + + WritePtr = (char*)areas->addr + (offset * areas->step / 8); + aluMixData(pDevice, WritePtr, frames); + + commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames); + if(commitres < 0 || (commitres-frames) != 0) + { + ERR("mmap commit error: %s\n", + snd_strerror(commitres >= 0 ? -EPIPE : commitres)); + break; + } + + avail -= frames; + } + } + + return 0; +} + +static ALuint ALSANoMMapProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + alsa_data *data = (alsa_data*)pDevice->ExtraData; + snd_pcm_sframes_t avail; + char *WritePtr; + + SetRTPriority(); + + while(!data->killNow) + { + int state = verify_state(data->pcmHandle); + if(state < 0) + { + ERR("Invalid state detected: %s\n", snd_strerror(state)); + aluHandleDisconnect(pDevice); + break; + } + + WritePtr = data->buffer; + avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1); + aluMixData(pDevice, WritePtr, avail); + + while(avail > 0) + { + int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail); + switch (ret) + { + case -EAGAIN: + continue; + case -ESTRPIPE: + case -EPIPE: + case -EINTR: + ret = snd_pcm_recover(data->pcmHandle, ret, 1); + if(ret < 0) + avail = 0; + break; + default: + if (ret >= 0) + { + WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret); + avail -= ret; + } + break; + } + if (ret < 0) + { + ret = snd_pcm_prepare(data->pcmHandle); + if(ret < 0) + break; + } + } + } + + return 0; +} + +static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + alsa_data *data; + char driver[128]; + int i; + + strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + + if(!deviceName) + deviceName = alsaDevice; + else if(strcmp(deviceName, alsaDevice) != 0) + { + size_t idx; + + if(!allDevNameMap) + allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); + + for(idx = 0;idx < numDevNames;idx++) + { + if(allDevNameMap[idx].name && + strcmp(deviceName, allDevNameMap[idx].name) == 0) + { + if(idx > 0) + snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", device_prefix, + allDevNameMap[idx].card, allDevNameMap[idx].dev); + break; + } + } + if(idx == numDevNames) + return ALC_FALSE; + } + + data = (alsa_data*)calloc(1, sizeof(alsa_data)); + + i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if(i >= 0) + { + i = snd_pcm_nonblock(data->pcmHandle, 0); + if(i < 0) + snd_pcm_close(data->pcmHandle); + } + if(i < 0) + { + free(data); + ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(i)); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void alsa_close_playback(ALCdevice *device) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + + snd_pcm_close(data->pcmHandle); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean alsa_reset_playback(ALCdevice *device) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + snd_pcm_uframes_t periodSizeInFrames; + unsigned int periodLen, bufferLen; + snd_pcm_sw_params_t *sp = NULL; + snd_pcm_hw_params_t *p = NULL; + snd_pcm_access_t access; + snd_pcm_format_t format; + unsigned int periods; + unsigned int rate; + int allowmmap; + char *err; + int i; + + + format = -1; + switch(device->FmtType) + { + case DevFmtByte: + format = SND_PCM_FORMAT_S8; + break; + case DevFmtUByte: + format = SND_PCM_FORMAT_U8; + break; + case DevFmtShort: + format = SND_PCM_FORMAT_S16; + break; + case DevFmtUShort: + format = SND_PCM_FORMAT_U16; + break; + case DevFmtFloat: + format = SND_PCM_FORMAT_FLOAT; + break; + } + + allowmmap = GetConfigValueBool("alsa", "mmap", 1); + periods = device->NumUpdates; + periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency; + bufferLen = periodLen * periods; + rate = device->Frequency; + + err = NULL; + snd_pcm_hw_params_malloc(&p); + + if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0) + err = "any"; + /* set interleaved access */ + if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0)) + { + if(periods > 2) + { + periods--; + bufferLen = periodLen * periods; + } + if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + err = "set access"; + } + /* set format (implicitly sets sample bits) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0) + { + device->FmtType = DevFmtFloat; + if(format == SND_PCM_FORMAT_FLOAT || + (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0) + { + device->FmtType = DevFmtShort; + if(format == SND_PCM_FORMAT_S16 || + (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0) + { + device->FmtType = DevFmtUByte; + if(format == SND_PCM_FORMAT_U8 || + (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0) + err = "set format"; + } + } + } + /* set channels (implicitly sets frame bits) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0) + { + if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0) + { + if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0) + err = "set channels"; + else + { + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans)); + device->FmtChans = DevFmtMono; + } + } + else + { + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans)); + device->FmtChans = DevFmtStereo; + } + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0) + { + ERR("Failed to disable ALSA resampler\n"); + i = 0; + } + /* set rate (implicitly constrains period/buffer parameters) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0) + err = "set rate near"; + /* set buffer time (implicitly constrains period/buffer parameters) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0) + err = "set buffer time near"; + /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0) + err = "set period time near"; + /* install and prepare hardware configuration */ + if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0) + err = "set params"; + if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0) + err = "get access"; + if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0) + err = "get period size"; + if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0) + err = "get periods"; + if(i < 0) + { + ERR("%s failed: %s\n", err, snd_strerror(i)); + snd_pcm_hw_params_free(p); + return ALC_FALSE; + } + + snd_pcm_hw_params_free(p); + + err = NULL; + snd_pcm_sw_params_malloc(&sp); + + if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0) + err = "sw current"; + if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0) + err = "sw set avail min"; + if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0) + err = "sw set params"; + if(i != 0) + { + ERR("%s failed: %s\n", err, snd_strerror(i)); + snd_pcm_sw_params_free(sp); + return ALC_FALSE; + } + + snd_pcm_sw_params_free(sp); + + if(device->Frequency != rate) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, rate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = rate; + } + + SetDefaultChannelOrder(device); + + data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames); + if(access == SND_PCM_ACCESS_RW_INTERLEAVED) + { + /* Increase periods by one, since the temp buffer counts as an extra + * period */ + periods++; + data->buffer = malloc(data->size); + if(!data->buffer) + { + ERR("buffer malloc failed\n"); + return ALC_FALSE; + } + device->UpdateSize = periodSizeInFrames; + device->NumUpdates = periods; + data->thread = StartThread(ALSANoMMapProc, device); + } + else + { + i = snd_pcm_prepare(data->pcmHandle); + if(i < 0) + { + ERR("prepare error: %s\n", snd_strerror(i)); + return ALC_FALSE; + } + device->UpdateSize = periodSizeInFrames; + device->NumUpdates = periods; + data->thread = StartThread(ALSAProc, device); + } + if(data->thread == NULL) + { + ERR("Could not create playback thread\n"); + free(data->buffer); + data->buffer = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void alsa_stop_playback(ALCdevice *device) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + + if(data->thread) + { + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + } + data->killNow = 0; + free(data->buffer); + data->buffer = NULL; +} + + +static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName) +{ + snd_pcm_hw_params_t *p; + snd_pcm_uframes_t bufferSizeInFrames; + snd_pcm_format_t format; + ALuint frameSize; + alsa_data *data; + char driver[128]; + char *err; + int i; + + strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + + if(!allCaptureDevNameMap) + allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); + + if(!deviceName) + deviceName = allCaptureDevNameMap[0].name; + else + { + size_t idx; + + for(idx = 0;idx < numCaptureDevNames;idx++) + { + if(allCaptureDevNameMap[idx].name && + strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0) + { + if(idx > 0) + snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", capture_prefix, + allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev); + break; + } + } + if(idx == numCaptureDevNames) + return ALC_FALSE; + } + + data = (alsa_data*)calloc(1, sizeof(alsa_data)); + + i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + if(i < 0) + { + ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(i)); + free(data); + return ALC_FALSE; + } + + format = -1; + switch(pDevice->FmtType) + { + case DevFmtByte: + format = SND_PCM_FORMAT_S8; + break; + case DevFmtUByte: + format = SND_PCM_FORMAT_U8; + break; + case DevFmtShort: + format = SND_PCM_FORMAT_S16; + break; + case DevFmtUShort: + format = SND_PCM_FORMAT_U16; + break; + case DevFmtFloat: + format = SND_PCM_FORMAT_FLOAT; + break; + } + + err = NULL; + bufferSizeInFrames = pDevice->UpdateSize * pDevice->NumUpdates; + snd_pcm_hw_params_malloc(&p); + + if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0) + err = "any"; + /* set interleaved access */ + if(i >= 0 && (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + err = "set access"; + /* set format (implicitly sets sample bits) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0) + err = "set format"; + /* set channels (implicitly sets frame bits) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(pDevice->FmtChans))) < 0) + err = "set channels"; + /* set rate (implicitly constrains period/buffer parameters) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0) + err = "set rate near"; + /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0) + err = "set buffer size near"; + /* install and prepare hardware configuration */ + if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0) + err = "set params"; + if(i < 0) + { + ERR("%s failed: %s\n", err, snd_strerror(i)); + snd_pcm_hw_params_free(p); + goto error; + } + + if((i=snd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0) + { + ERR("get size failed: %s\n", snd_strerror(i)); + snd_pcm_hw_params_free(p); + goto error; + } + + snd_pcm_hw_params_free(p); + + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates); + if(!data->ring) + { + ERR("ring buffer create failed\n"); + goto error; + } + + data->size = snd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames); + data->buffer = malloc(data->size); + if(!data->buffer) + { + ERR("buffer malloc failed\n"); + goto error; + } + + pDevice->szDeviceName = strdup(deviceName); + + pDevice->ExtraData = data; + return ALC_TRUE; + +error: + free(data->buffer); + DestroyRingBuffer(data->ring); + snd_pcm_close(data->pcmHandle); + free(data); + + pDevice->ExtraData = NULL; + return ALC_FALSE; +} + +static void alsa_close_capture(ALCdevice *pDevice) +{ + alsa_data *data = (alsa_data*)pDevice->ExtraData; + + snd_pcm_close(data->pcmHandle); + DestroyRingBuffer(data->ring); + + free(data->buffer); + free(data); + pDevice->ExtraData = NULL; +} + +static void alsa_start_capture(ALCdevice *Device) +{ + alsa_data *data = (alsa_data*)Device->ExtraData; + int err; + + err = snd_pcm_start(data->pcmHandle); + if(err < 0) + { + ERR("start failed: %s\n", snd_strerror(err)); + aluHandleDisconnect(Device); + } + else + data->doCapture = AL_TRUE; +} + +static void alsa_stop_capture(ALCdevice *Device) +{ + alsa_data *data = (alsa_data*)Device->ExtraData; + snd_pcm_drain(data->pcmHandle); + data->doCapture = AL_FALSE; +} + +static ALCuint alsa_available_samples(ALCdevice *Device) +{ + alsa_data *data = (alsa_data*)Device->ExtraData; + snd_pcm_sframes_t avail; + + avail = (Device->Connected ? snd_pcm_avail_update(data->pcmHandle) : 0); + if(avail < 0) + { + ERR("avail update failed: %s\n", snd_strerror(avail)); + + if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0) + { + if(data->doCapture) + avail = snd_pcm_start(data->pcmHandle); + if(avail >= 0) + avail = snd_pcm_avail_update(data->pcmHandle); + } + if(avail < 0) + { + ERR("restore error: %s\n", snd_strerror(avail)); + aluHandleDisconnect(Device); + } + } + while(avail > 0) + { + snd_pcm_sframes_t amt; + + amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); + if(avail < amt) amt = avail; + + amt = snd_pcm_readi(data->pcmHandle, data->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(data->doCapture) + amt = snd_pcm_start(data->pcmHandle); + if(amt >= 0) + amt = snd_pcm_avail_update(data->pcmHandle); + } + if(amt < 0) + { + ERR("restore error: %s\n", snd_strerror(amt)); + aluHandleDisconnect(Device); + break; + } + avail = amt; + continue; + } + + WriteRingBuffer(data->ring, data->buffer, amt); + avail -= amt; + } + + return RingBufferSize(data->ring); +} + +static void alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples) +{ + alsa_data *data = (alsa_data*)Device->ExtraData; + + if(Samples <= alsa_available_samples(Device)) + ReadRingBuffer(data->ring, Buffer, Samples); + else + alcSetError(Device, ALC_INVALID_VALUE); +} + + +static const BackendFuncs alsa_funcs = { + alsa_open_playback, + alsa_close_playback, + alsa_reset_playback, + alsa_stop_playback, + alsa_open_capture, + alsa_close_capture, + alsa_start_capture, + alsa_stop_capture, + alsa_capture_samples, + alsa_available_samples +}; + +ALCboolean alc_alsa_init(BackendFuncs *func_list) +{ + if(!alsa_load()) + return ALC_FALSE; + device_prefix = GetConfigValue("alsa", "device-prefix", "plughw:"); + capture_prefix = GetConfigValue("alsa", "capture-prefix", "plughw:"); + *func_list = alsa_funcs; + return ALC_TRUE; +} + +void alc_alsa_deinit(void) +{ + ALuint i; + + for(i = 0;i < numDevNames;++i) + { + free(allDevNameMap[i].name); + free(allDevNameMap[i].card); + } + free(allDevNameMap); + allDevNameMap = NULL; + numDevNames = 0; + + for(i = 0;i < numCaptureDevNames;++i) + { + free(allCaptureDevNameMap[i].name); + free(allCaptureDevNameMap[i].card); + } + free(allCaptureDevNameMap); + allCaptureDevNameMap = NULL; + numCaptureDevNames = 0; + +#ifdef HAVE_DYNLOAD + if(alsa_handle) + CloseLib(alsa_handle); + alsa_handle = NULL; +#endif +} + +void alc_alsa_probe(enum DevProbe type) +{ + ALuint i; + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(alsaDevice); + break; + + case ALL_DEVICE_PROBE: + for(i = 0;i < numDevNames;++i) + { + free(allDevNameMap[i].name); + free(allDevNameMap[i].card); + } + + free(allDevNameMap); + allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); + + for(i = 0;i < numDevNames;++i) + AppendAllDeviceList(allDevNameMap[i].name); + break; + + case CAPTURE_DEVICE_PROBE: + for(i = 0;i < numCaptureDevNames;++i) + { + free(allCaptureDevNameMap[i].name); + free(allCaptureDevNameMap[i].card); + } + + free(allCaptureDevNameMap); + allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); + + for(i = 0;i < numCaptureDevNames;++i) + AppendCaptureDeviceList(allCaptureDevNameMap[i].name); + break; + } +} diff --git a/Alc/backends/coreaudio.c b/Alc/backends/coreaudio.c new file mode 100644 index 00000000..c84be846 --- /dev/null +++ b/Alc/backends/coreaudio.c @@ -0,0 +1,719 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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 +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include +#include +#include +#include + + +typedef struct { + AudioUnit audioUnit; + + ALuint frameSize; + ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate + AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD + + AudioConverterRef audioConverter; // Sample rate converter if needed + AudioBufferList *bufferList; // Buffer for data coming from the input device + ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling + + RingBuffer *ring; +} ca_data; + +static const ALCchar ca_device[] = "CoreAudio Default"; + + +static void destroy_buffer_list(AudioBufferList* list) +{ + if(list) + { + UInt32 i; + for(i = 0;i < list->mNumberBuffers;i++) + free(list->mBuffers[i].mData); + free(list); + } +} + +static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize) +{ + AudioBufferList *list; + + list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer)); + if(list) + { + list->mNumberBuffers = 1; + + list->mBuffers[0].mNumberChannels = channelCount; + list->mBuffers[0].mDataByteSize = byteSize; + list->mBuffers[0].mData = malloc(byteSize); + if(list->mBuffers[0].mData == NULL) + { + free(list); + list = NULL; + } + } + return list; +} + +static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + ALCdevice *device = (ALCdevice*)inRefCon; + ca_data *data = (ca_data*)device->ExtraData; + + aluMixData(device, ioData->mBuffers[0].mData, + ioData->mBuffers[0].mDataByteSize / data->frameSize); + + return noErr; +} + +static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, + AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData) +{ + ALCdevice *device = (ALCdevice*)inUserData; + ca_data *data = (ca_data*)device->ExtraData; + + // Read from the ring buffer and store temporarily in a large buffer + ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets)); + + // Set the input data + ioData->mNumberBuffers = 1; + ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; + ioData->mBuffers[0].mData = data->resampleBuffer; + ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame; + + return noErr; +} + +static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, + UInt32 inNumberFrames, AudioBufferList *ioData) +{ + ALCdevice *device = (ALCdevice*)inRefCon; + ca_data *data = (ca_data*)device->ExtraData; + AudioUnitRenderActionFlags flags = 0; + OSStatus err; + + // fill the bufferList with data from the input device + err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList); + if(err != noErr) + { + ERR("AudioUnitRender error: %d\n", err); + return err; + } + + WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames); + + return noErr; +} + +static ALCboolean ca_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + ComponentDescription desc; + Component comp; + ca_data *data; + OSStatus err; + + if(!deviceName) + deviceName = ca_device; + else if(strcmp(deviceName, ca_device) != 0) + return ALC_FALSE; + + /* open the default output unit */ + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + comp = FindNextComponent(NULL, &desc); + if(comp == NULL) + { + ERR("FindNextComponent failed\n"); + return ALC_FALSE; + } + + data = calloc(1, sizeof(*data)); + device->ExtraData = data; + + err = OpenAComponent(comp, &data->audioUnit); + if(err != noErr) + { + ERR("OpenAComponent failed\n"); + free(data); + device->ExtraData = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ca_close_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + + CloseComponent(data->audioUnit); + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean ca_reset_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + AudioStreamBasicDescription streamFormat; + AURenderCallbackStruct input; + OSStatus err; + UInt32 size; + + /* init and start the default audio unit... */ + err = AudioUnitInitialize(data->audioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + return ALC_FALSE; + } + + err = AudioOutputUnitStart(data->audioUnit); + if(err != noErr) + { + ERR("AudioOutputUnitStart failed\n"); + return ALC_FALSE; + } + + /* retrieve default output unit's properties (output side) */ + size = sizeof(AudioStreamBasicDescription); + err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size); + if(err != noErr || size != sizeof(AudioStreamBasicDescription)) + { + ERR("AudioUnitGetProperty failed\n"); + return ALC_FALSE; + } + +#if 0 + TRACE("Output streamFormat of default output unit -\n"); + TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket); + TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame); + TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel); + TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket); + TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame); + TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate); +#endif + + /* set default output unit's input side to match output side */ + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + if(device->Frequency != streamFormat.mSampleRate) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("CoreAudio does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, streamFormat.mSampleRate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + + device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * + streamFormat.mSampleRate / + device->Frequency); + device->Frequency = streamFormat.mSampleRate; + } + + /* FIXME: How to tell what channels are what in the output device, and how + * to specify what we're giving? eg, 6.0 vs 5.1 */ + switch(streamFormat.mChannelsPerFrame) + { + case 1: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtMono) + { + ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtMono; + break; + case 2: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtStereo) + { + ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtStereo; + break; + case 4: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtQuad) + { + ERR("Failed to set %s, got Quad instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtQuad; + break; + case 6: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtX51) + { + ERR("Failed to set %s, got 5.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtX51; + break; + case 7: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtX61) + { + ERR("Failed to set %s, got 6.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtX61; + break; + case 8: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtX71) + { + ERR("Failed to set %s, got 7.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtX71; + break; + default: + ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + device->FmtChans = DevFmtStereo; + streamFormat.mChannelsPerFrame = 2; + break; + } + SetDefaultWFXChannelOrder(device); + + /* use channel count and sample rate from the default output unit's current + * parameters, but reset everything else */ + streamFormat.mFramesPerPacket = 1; + switch(device->FmtType) + { + case DevFmtUByte: + device->FmtType = DevFmtByte; + /* fall-through */ + case DevFmtByte: + streamFormat.mBitsPerChannel = 8; + streamFormat.mBytesPerPacket = streamFormat.mChannelsPerFrame; + streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame; + break; + case DevFmtUShort: + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + streamFormat.mBitsPerChannel = 16; + streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame; + streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame; + break; + } + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | + kAudioFormatFlagsNativeEndian | + kLinearPCMFormatFlagIsPacked; + + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + /* setup callback */ + data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + input.inputProc = ca_callback; + input.inputProcRefCon = device; + + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ca_stop_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err; + + AudioOutputUnitStop(data->audioUnit); + err = AudioUnitUninitialize(data->audioUnit); + if(err != noErr) + ERR("-- AudioUnitUninitialize failed.\n"); +} + +static ALCboolean ca_open_capture(ALCdevice *device, const ALCchar *deviceName) +{ + AudioStreamBasicDescription requestedFormat; // The application requested format + AudioStreamBasicDescription hardwareFormat; // The hardware format + AudioStreamBasicDescription outputFormat; // The AudioUnit output format + AURenderCallbackStruct input; + ComponentDescription desc; + AudioDeviceID inputDevice; + UInt32 outputFrameCount; + UInt32 propertySize; + UInt32 enableIO; + Component comp; + ca_data *data; + OSStatus err; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_HALOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + // Search for component with given description + comp = FindNextComponent(NULL, &desc); + if(comp == NULL) + { + ERR("FindNextComponent failed\n"); + return ALC_FALSE; + } + + data = calloc(1, sizeof(*data)); + device->ExtraData = data; + + // Open the component + err = OpenAComponent(comp, &data->audioUnit); + if(err != noErr) + { + ERR("OpenAComponent failed\n"); + goto error; + } + + // Turn off AudioUnit output + enableIO = 0; + err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // Turn on AudioUnit input + enableIO = 1; + err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // Get the default input device + propertySize = sizeof(AudioDeviceID); + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice); + if(err != noErr) + { + ERR("AudioHardwareGetProperty failed\n"); + goto error; + } + + if(inputDevice == kAudioDeviceUnknown) + { + ERR("No input device found\n"); + goto error; + } + + // Track the input device + err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // set capture callback + input.inputProc = ca_capture_callback; + input.inputProcRefCon = device; + + err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // Initialize the device + err = AudioUnitInitialize(data->audioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + goto error; + } + + // Get the hardware format + propertySize = sizeof(AudioStreamBasicDescription); + err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize); + if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription)) + { + ERR("AudioUnitGetProperty failed\n"); + goto error; + } + + // Set up the requested format description + switch(device->FmtType) + { + case DevFmtUByte: + requestedFormat.mBitsPerChannel = 8; + requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; + break; + case DevFmtShort: + requestedFormat.mBitsPerChannel = 16; + requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; + break; + case DevFmtFloat: + requestedFormat.mBitsPerChannel = 32; + requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; + break; + case DevFmtByte: + case DevFmtUShort: + ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType)); + goto error; + } + + switch(device->FmtChans) + { + case DevFmtMono: + requestedFormat.mChannelsPerFrame = 1; + break; + case DevFmtStereo: + requestedFormat.mChannelsPerFrame = 2; + break; + + case DevFmtQuad: + case DevFmtX51: + case DevFmtX51Side: + case DevFmtX61: + case DevFmtX71: + ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans)); + goto error; + } + + requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8; + requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame; + requestedFormat.mSampleRate = device->Frequency; + requestedFormat.mFormatID = kAudioFormatLinearPCM; + requestedFormat.mReserved = 0; + requestedFormat.mFramesPerPacket = 1; + + // save requested format description for later use + data->format = requestedFormat; + data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + // Use intermediate format for sample rate conversion (outputFormat) + // Set sample rate to the same as hardware for resampling later + outputFormat = requestedFormat; + outputFormat.mSampleRate = hardwareFormat.mSampleRate; + + // Determine sample rate ratio for resampling + data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency; + + // The output format should be the requested format, but using the hardware sample rate + // This is because the AudioUnit will automatically scale other properties, except for sample rate + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // Set the AudioUnit output format frame count + outputFrameCount = device->UpdateSize * data->sampleRateRatio; + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed: %d\n", err); + goto error; + } + + // Set up sample converter + err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter); + if(err != noErr) + { + ERR("AudioConverterNew failed: %d\n", err); + goto error; + } + + // Create a buffer for use in the resample callback + data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio); + + // Allocate buffer for the AudioUnit output + data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio); + if(data->bufferList == NULL) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + goto error; + } + + data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates); + if(data->ring == NULL) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + goto error; + } + + return ALC_TRUE; + +error: + DestroyRingBuffer(data->ring); + free(data->resampleBuffer); + destroy_buffer_list(data->bufferList); + + if(data->audioConverter) + AudioConverterDispose(data->audioConverter); + if(data->audioUnit) + CloseComponent(data->audioUnit); + + free(data); + device->ExtraData = NULL; + + return ALC_FALSE; +} + +static void ca_close_capture(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + + DestroyRingBuffer(data->ring); + free(data->resampleBuffer); + destroy_buffer_list(data->bufferList); + + AudioConverterDispose(data->audioConverter); + CloseComponent(data->audioUnit); + + free(data); + device->ExtraData = NULL; +} + +static void ca_start_capture(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err = AudioOutputUnitStart(data->audioUnit); + if(err != noErr) + ERR("AudioOutputUnitStart failed\n"); +} + +static void ca_stop_capture(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err = AudioOutputUnitStop(data->audioUnit); + if(err != noErr) + ERR("AudioOutputUnitStop failed\n"); +} + +static ALCuint ca_available_samples(ALCdevice *device) +{ + ca_data *data = device->ExtraData; + return RingBufferSize(data->ring) / data->sampleRateRatio; +} + +static void ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +{ + ca_data *data = (ca_data*)device->ExtraData; + + if(samples <= ca_available_samples(device)) + { + AudioBufferList *list; + UInt32 frameCount; + OSStatus err; + + // If no samples are requested, just return + if(samples == 0) + return; + + // Allocate a temporary AudioBufferList to use as the return resamples data + list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer)); + + // Point the resampling buffer to the capture buffer + list->mNumberBuffers = 1; + list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; + list->mBuffers[0].mDataByteSize = samples * data->frameSize; + list->mBuffers[0].mData = buffer; + + // Resample into another AudioBufferList + frameCount = samples; + err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback, device, + &frameCount, list, NULL); + if(err != noErr) + { + ERR("AudioConverterFillComplexBuffer error: %d\n", err); + alcSetError(device, ALC_INVALID_VALUE); + } + } + else + alcSetError(device, ALC_INVALID_VALUE); +} + +static const BackendFuncs ca_funcs = { + ca_open_playback, + ca_close_playback, + ca_reset_playback, + ca_stop_playback, + ca_open_capture, + ca_close_capture, + ca_start_capture, + ca_stop_capture, + ca_capture_samples, + ca_available_samples +}; + +ALCboolean alc_ca_init(BackendFuncs *func_list) +{ + *func_list = ca_funcs; + return ALC_TRUE; +} + +void alc_ca_deinit(void) +{ +} + +void alc_ca_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(ca_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(ca_device); + break; + case CAPTURE_DEVICE_PROBE: + AppendCaptureDeviceList(ca_device); + break; + } +} diff --git a/Alc/backends/dsound.c b/Alc/backends/dsound.c new file mode 100644 index 00000000..08a1d13a --- /dev/null +++ b/Alc/backends/dsound.c @@ -0,0 +1,627 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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" + +#define _WIN32_WINNT 0x0500 +#include +#include +#include + +#include +#include +#include +#ifndef _WAVEFORMATEXTENSIBLE_ +#include +#include +#endif + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#ifndef DSSPEAKER_5POINT1 +#define DSSPEAKER_5POINT1 6 +#endif +#ifndef DSSPEAKER_7POINT1 +#define DSSPEAKER_7POINT1 7 +#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); + + +static HMODULE ds_handle; +static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter); +static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext); + +#define DirectSoundCreate pDirectSoundCreate +#define DirectSoundEnumerateA pDirectSoundEnumerateA + + +typedef struct { + // DirectSound Playback Device + IDirectSound *lpDS; + IDirectSoundBuffer *DSpbuffer; + IDirectSoundBuffer *DSsbuffer; + IDirectSoundNotify *DSnotify; + HANDLE hNotifyEvent; + + volatile int killNow; + ALvoid *thread; +} DSoundData; + + +typedef struct { + ALCchar *name; + GUID guid; +} DevMap; + +static const ALCchar dsDevice[] = "DirectSound Default"; +static DevMap *DeviceList; +static ALuint NumDevices; + +#define MAX_UPDATES 128 + +static ALCboolean DSoundLoad(void) +{ + ALCboolean ok = ALC_TRUE; + if(!ds_handle) + { + ds_handle = LoadLibraryA("dsound.dll"); + if(ds_handle == NULL) + { + ERR("Failed to load dsound.dll\n"); + return ALC_FALSE; + } + +#define LOAD_FUNC(x) do { \ + if((p##x = (void*)GetProcAddress(ds_handle, #x)) == NULL) { \ + ERR("Could not load %s from dsound.dll\n", #x); \ + ok = ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(DirectSoundCreate); + LOAD_FUNC(DirectSoundEnumerateA); +#undef LOAD_FUNC + + if(!ok) + { + FreeLibrary(ds_handle); + ds_handle = NULL; + } + } + return ok; +} + + +static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data) +{ + char str[1024]; + void *temp; + int count; + ALuint i; + + (void)data; + (void)drvname; + + if(NumDevices == 0) + { + temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1)); + if(temp) + { + DeviceList = temp; + DeviceList[NumDevices].name = strdup(dsDevice); + DeviceList[NumDevices].guid = GUID_NULL; + NumDevices++; + } + } + + if(!guid) + return TRUE; + + count = 0; + do { + if(count == 0) + snprintf(str, sizeof(str), "%s", desc); + else + snprintf(str, sizeof(str), "%s #%d", desc, count+1); + count++; + + for(i = 0;i < NumDevices;i++) + { + if(strcmp(str, DeviceList[i].name) == 0) + break; + } + } while(i != NumDevices); + + temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1)); + if(temp) + { + DeviceList = temp; + DeviceList[NumDevices].name = strdup(str); + DeviceList[NumDevices].guid = *guid; + NumDevices++; + } + + return TRUE; +} + + +static ALuint DSoundProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + DSoundData *pData = (DSoundData*)pDevice->ExtraData; + DSBCAPS DSBCaps; + DWORD LastCursor = 0; + DWORD PlayCursor; + VOID *WritePtr1, *WritePtr2; + DWORD WriteCnt1, WriteCnt2; + BOOL Playing = FALSE; + DWORD FrameSize; + DWORD FragSize; + DWORD avail; + HRESULT err; + + SetRTPriority(); + + memset(&DSBCaps, 0, sizeof(DSBCaps)); + DSBCaps.dwSize = sizeof(DSBCaps); + err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps); + if(FAILED(err)) + { + ERR("Failed to get buffer caps: 0x%lx\n", err); + aluHandleDisconnect(pDevice); + return 1; + } + + FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + FragSize = pDevice->UpdateSize * FrameSize; + + IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL); + while(!pData->killNow) + { + // Get current play cursor + IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL); + avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes; + + if(avail < FragSize) + { + if(!Playing) + { + err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING); + if(FAILED(err)) + { + ERR("Failed to play buffer: 0x%lx\n", err); + aluHandleDisconnect(pDevice); + return 1; + } + Playing = TRUE; + } + + avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE); + if(avail != WAIT_OBJECT_0) + ERR("WaitForSingleObjectEx error: 0x%lx\n", avail); + continue; + } + avail -= avail%FragSize; + + // Lock output buffer + WriteCnt1 = 0; + WriteCnt2 = 0; + err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); + + // If the buffer is lost, restore it and lock + if(err == DSERR_BUFFERLOST) + { + WARN("Buffer lost, restoring...\n"); + err = IDirectSoundBuffer_Restore(pData->DSsbuffer); + if(SUCCEEDED(err)) + { + Playing = FALSE; + LastCursor = 0; + err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); + } + } + + // Successfully locked the output buffer + if(SUCCEEDED(err)) + { + // If we have an active context, mix data directly into output buffer otherwise fill with silence + aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize); + aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize); + + // Unlock output buffer only when successfully locked + IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); + } + else + { + ERR("Buffer lock error: %#lx\n", err); + aluHandleDisconnect(pDevice); + return 1; + } + + // Update old write cursor location + LastCursor += WriteCnt1+WriteCnt2; + LastCursor %= DSBCaps.dwBufferBytes; + } + + return 0; +} + +static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +{ + DSoundData *pData = NULL; + LPGUID guid = NULL; + HRESULT hr; + + if(!deviceName) + deviceName = dsDevice; + else if(strcmp(deviceName, dsDevice) != 0) + { + ALuint i; + + if(!DeviceList) + { + hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL); + if(FAILED(hr)) + ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr); + } + + for(i = 0;i < NumDevices;i++) + { + if(strcmp(deviceName, DeviceList[i].name) == 0) + { + if(i > 0) + guid = &DeviceList[i].guid; + break; + } + } + if(i == NumDevices) + return ALC_FALSE; + } + + //Initialise requested device + pData = calloc(1, sizeof(DSoundData)); + if(!pData) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + hr = DS_OK; + pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(pData->hNotifyEvent == NULL) + hr = E_FAIL; + + //DirectSound Init code + if(SUCCEEDED(hr)) + hr = DirectSoundCreate(guid, &pData->lpDS, NULL); + if(SUCCEEDED(hr)) + hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY); + if(FAILED(hr)) + { + if(pData->lpDS) + IDirectSound_Release(pData->lpDS); + if(pData->hNotifyEvent) + CloseHandle(pData->hNotifyEvent); + free(pData); + ERR("Device init failed: 0x%08lx\n", hr); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = pData; + return ALC_TRUE; +} + +static void DSoundClosePlayback(ALCdevice *device) +{ + DSoundData *pData = device->ExtraData; + + IDirectSound_Release(pData->lpDS); + CloseHandle(pData->hNotifyEvent); + free(pData); + device->ExtraData = NULL; +} + +static ALCboolean DSoundResetPlayback(ALCdevice *device) +{ + DSoundData *pData = (DSoundData*)device->ExtraData; + DSBUFFERDESC DSBDescription; + WAVEFORMATEXTENSIBLE OutputType; + DWORD speakers; + HRESULT hr; + + memset(&OutputType, 0, sizeof(OutputType)); + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + break; + case DevFmtUByte: + case DevFmtShort: + case DevFmtFloat: + break; + } + + hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers); + if(SUCCEEDED(hr)) + { + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) + { + speakers = DSSPEAKER_CONFIG(speakers); + if(speakers == DSSPEAKER_MONO) + device->FmtChans = DevFmtMono; + else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE) + device->FmtChans = DevFmtStereo; + else if(speakers == DSSPEAKER_QUAD) + device->FmtChans = DevFmtQuad; + else if(speakers == DSSPEAKER_5POINT1) + device->FmtChans = DevFmtX51; + else if(speakers == DSSPEAKER_7POINT1) + device->FmtChans = DevFmtX71; + else + ERR("Unknown system speaker config: 0x%lx\n", speakers); + } + + switch(device->FmtChans) + { + case DevFmtMono: + OutputType.dwChannelMask = SPEAKER_FRONT_CENTER; + break; + case DevFmtStereo: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT; + break; + case DevFmtQuad: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT; + break; + case DevFmtX51: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT; + break; + case DevFmtX51Side: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT; + break; + case DevFmtX61: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_CENTER | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT; + break; + case DevFmtX71: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT; + break; + } + + OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; + OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans); + OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; + OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8; + OutputType.Format.nSamplesPerSec = device->Frequency; + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign; + OutputType.Format.cbSize = 0; + } + + if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat) + { + OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + if(device->FmtType == DevFmtFloat) + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + else + { + if(SUCCEEDED(hr)) + { + memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); + DSBDescription.dwSize=sizeof(DSBUFFERDESC); + DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER; + hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL); + } + if(SUCCEEDED(hr)) + hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format); + } + + if(SUCCEEDED(hr)) + { + if(device->NumUpdates > MAX_UPDATES) + { + device->UpdateSize = (device->UpdateSize*device->NumUpdates + + MAX_UPDATES-1) / MAX_UPDATES; + device->NumUpdates = MAX_UPDATES; + } + + memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); + DSBDescription.dwSize=sizeof(DSBUFFERDESC); + DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS; + DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * + OutputType.Format.nBlockAlign; + DSBDescription.lpwfxFormat=&OutputType.Format; + hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL); + } + + if(SUCCEEDED(hr)) + { + hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify); + if(SUCCEEDED(hr)) + { + DSBPOSITIONNOTIFY notifies[MAX_UPDATES]; + ALuint i; + + for(i = 0;i < device->NumUpdates;++i) + { + notifies[i].dwOffset = i * device->UpdateSize * + OutputType.Format.nBlockAlign; + notifies[i].hEventNotify = pData->hNotifyEvent; + } + if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK) + hr = E_FAIL; + } + } + + if(SUCCEEDED(hr)) + { + ResetEvent(pData->hNotifyEvent); + SetDefaultWFXChannelOrder(device); + pData->thread = StartThread(DSoundProc, device); + if(pData->thread == NULL) + hr = E_FAIL; + } + + if(FAILED(hr)) + { + if(pData->DSnotify != NULL) + IDirectSoundNotify_Release(pData->DSnotify); + pData->DSnotify = NULL; + if(pData->DSsbuffer != NULL) + IDirectSoundBuffer_Release(pData->DSsbuffer); + pData->DSsbuffer = NULL; + if(pData->DSpbuffer != NULL) + IDirectSoundBuffer_Release(pData->DSpbuffer); + pData->DSpbuffer = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void DSoundStopPlayback(ALCdevice *device) +{ + DSoundData *pData = device->ExtraData; + + if(!pData->thread) + return; + + pData->killNow = 1; + StopThread(pData->thread); + pData->thread = NULL; + + pData->killNow = 0; + + IDirectSoundNotify_Release(pData->DSnotify); + pData->DSnotify = NULL; + IDirectSoundBuffer_Release(pData->DSsbuffer); + pData->DSsbuffer = NULL; + if(pData->DSpbuffer != NULL) + IDirectSoundBuffer_Release(pData->DSpbuffer); + pData->DSpbuffer = NULL; +} + + +static const BackendFuncs DSoundFuncs = { + DSoundOpenPlayback, + DSoundClosePlayback, + DSoundResetPlayback, + DSoundStopPlayback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +ALCboolean alcDSoundInit(BackendFuncs *FuncList) +{ + if(!DSoundLoad()) + return ALC_FALSE; + *FuncList = DSoundFuncs; + return ALC_TRUE; +} + +void alcDSoundDeinit(void) +{ + ALuint i; + + for(i = 0;i < NumDevices;++i) + free(DeviceList[i].name); + free(DeviceList); + DeviceList = NULL; + NumDevices = 0; + + if(ds_handle) + FreeLibrary(ds_handle); + ds_handle = NULL; +} + +void alcDSoundProbe(enum DevProbe type) +{ + HRESULT hr; + ALuint i; + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(dsDevice); + break; + + case ALL_DEVICE_PROBE: + for(i = 0;i < NumDevices;++i) + free(DeviceList[i].name); + free(DeviceList); + DeviceList = NULL; + NumDevices = 0; + + hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL); + if(FAILED(hr)) + ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr); + else + { + for(i = 0;i < NumDevices;i++) + AppendAllDeviceList(DeviceList[i].name); + } + break; + + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/Alc/backends/loopback.c b/Alc/backends/loopback.c new file mode 100644 index 00000000..86e53555 --- /dev/null +++ b/Alc/backends/loopback.c @@ -0,0 +1,77 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2011 by Chris Robinson + * 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 +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +static ALCboolean loopback_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + device->szDeviceName = strdup(deviceName); + return ALC_TRUE; +} + +static void loopback_close_playback(ALCdevice *device) +{ + (void)device; +} + +static ALCboolean loopback_reset_playback(ALCdevice *device) +{ + SetDefaultWFXChannelOrder(device); + return ALC_TRUE; +} + +static void loopback_stop_playback(ALCdevice *device) +{ + (void)device; +} + +static const BackendFuncs loopback_funcs = { + loopback_open_playback, + loopback_close_playback, + loopback_reset_playback, + loopback_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_loopback_init(BackendFuncs *func_list) +{ + *func_list = loopback_funcs; + return ALC_TRUE; +} + +void alc_loopback_deinit(void) +{ +} + +void alc_loopback_probe(enum DevProbe type) +{ + (void)type; +} diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c new file mode 100644 index 00000000..702569c6 --- /dev/null +++ b/Alc/backends/mmdevapi.c @@ -0,0 +1,775 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2011 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" + +#define COBJMACROS +#define _WIN32_WINNT 0x0500 +#include +#include +#include + +#include +#include +#include +#include +#ifndef _WAVEFORMATEXTENSIBLE_ +#include +#include +#endif + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); + +#define MONO SPEAKER_FRONT_CENTER +#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) +#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) +#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) +#define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) +#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) +#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) + + +typedef struct { + IMMDevice *mmdev; + IAudioClient *client; + HANDLE hNotifyEvent; + + HANDLE MsgEvent; + + volatile int killNow; + ALvoid *thread; +} MMDevApiData; + + +static const ALCchar mmDevice[] = "WASAPI Default"; + + +static HANDLE ThreadHdl; +static DWORD ThreadID; + +typedef struct { + HANDLE FinishedEvt; + HRESULT result; +} ThreadRequest; + +#define WM_USER_OpenDevice (WM_USER+0) +#define WM_USER_ResetDevice (WM_USER+1) +#define WM_USER_StopDevice (WM_USER+2) +#define WM_USER_CloseDevice (WM_USER+3) + +static HRESULT WaitForResponse(ThreadRequest *req) +{ + if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0) + return req->result; + ERR("Message response error: %lu\n", GetLastError()); + return E_FAIL; +} + + +static ALuint MMDevApiProc(ALvoid *ptr) +{ + ALCdevice *device = ptr; + MMDevApiData *data = device->ExtraData; + union { + IAudioRenderClient *iface; + void *ptr; + } render; + UINT32 written, len; + BYTE *buffer; + HRESULT hr; + + hr = CoInitialize(NULL); + if(FAILED(hr)) + { + ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); + aluHandleDisconnect(device); + return 0; + } + + hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr); + if(FAILED(hr)) + { + ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr); + aluHandleDisconnect(device); + return 0; + } + + SetRTPriority(); + + while(!data->killNow) + { + hr = IAudioClient_GetCurrentPadding(data->client, &written); + if(FAILED(hr)) + { + ERR("Failed to get padding: 0x%08lx\n", hr); + aluHandleDisconnect(device); + break; + } + + len = device->UpdateSize*device->NumUpdates - written; + if(len < device->UpdateSize) + { + DWORD res; + res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE); + if(res != WAIT_OBJECT_0) + ERR("WaitForSingleObjectEx error: 0x%lx\n", res); + continue; + } + len -= len%device->UpdateSize; + + hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer); + if(SUCCEEDED(hr)) + { + aluMixData(device, buffer, len); + hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0); + } + if(FAILED(hr)) + { + ERR("Failed to buffer data: 0x%08lx\n", hr); + aluHandleDisconnect(device); + break; + } + } + + IAudioRenderClient_Release(render.iface); + + CoUninitialize(); + return 0; +} + + +static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) +{ + memset(out, 0, sizeof(*out)); + if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + *out = *(WAVEFORMATEXTENSIBLE*)in; + else if(in->wFormatTag == WAVE_FORMAT_PCM) + { + out->Format = *in; + out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + out->Format.cbSize = sizeof(*out) - sizeof(*in); + if(out->Format.nChannels == 1) + out->dwChannelMask = MONO; + else if(out->Format.nChannels == 2) + out->dwChannelMask = STEREO; + else + ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels); + out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) + { + out->Format = *in; + out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + out->Format.cbSize = sizeof(*out) - sizeof(*in); + if(out->Format.nChannels == 1) + out->dwChannelMask = MONO; + else if(out->Format.nChannels == 2) + out->dwChannelMask = STEREO; + else + ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels); + out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } + else + { + ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag); + return ALC_FALSE; + } + return ALC_TRUE; +} + +static HRESULT DoReset(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + WAVEFORMATEXTENSIBLE OutputType; + WAVEFORMATEX *wfx = NULL; + REFERENCE_TIME min_per; + UINT32 buffer_len, min_len; + HRESULT hr; + + hr = IAudioClient_GetMixFormat(data->client, &wfx); + if(FAILED(hr)) + { + ERR("Failed to get mix format: 0x%08lx\n", hr); + return hr; + } + + if(!MakeExtensible(&OutputType, wfx)) + { + CoTaskMemFree(wfx); + return E_FAIL; + } + CoTaskMemFree(wfx); + wfx = NULL; + + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) + device->Frequency = OutputType.Format.nSamplesPerSec; + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) + { + if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) + device->FmtChans = DevFmtMono; + else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) + device->FmtChans = DevFmtStereo; + else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) + device->FmtChans = DevFmtQuad; + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) + device->FmtChans = DevFmtX51; + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) + device->FmtChans = DevFmtX51Side; + else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) + device->FmtChans = DevFmtX61; + else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1) + device->FmtChans = DevFmtX71; + else + ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask); + } + + switch(device->FmtChans) + { + case DevFmtMono: + OutputType.Format.nChannels = 1; + OutputType.dwChannelMask = MONO; + break; + case DevFmtStereo: + OutputType.Format.nChannels = 2; + OutputType.dwChannelMask = STEREO; + break; + case DevFmtQuad: + OutputType.Format.nChannels = 4; + OutputType.dwChannelMask = QUAD; + break; + case DevFmtX51: + OutputType.Format.nChannels = 6; + OutputType.dwChannelMask = X5DOT1; + break; + case DevFmtX51Side: + OutputType.Format.nChannels = 6; + OutputType.dwChannelMask = X5DOT1SIDE; + break; + case DevFmtX61: + OutputType.Format.nChannels = 7; + OutputType.dwChannelMask = X6DOT1; + break; + case DevFmtX71: + OutputType.Format.nChannels = 8; + OutputType.dwChannelMask = X7DOT1; + break; + } + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + /* fall-through */ + case DevFmtUByte: + OutputType.Format.wBitsPerSample = 8; + OutputType.Samples.wValidBitsPerSample = 8; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + OutputType.Format.wBitsPerSample = 16; + OutputType.Samples.wValidBitsPerSample = 16; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtFloat: + OutputType.Format.wBitsPerSample = 32; + OutputType.Samples.wValidBitsPerSample = 32; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + break; + } + OutputType.Format.nSamplesPerSec = device->Frequency; + + OutputType.Format.nBlockAlign = OutputType.Format.nChannels * + OutputType.Format.wBitsPerSample / 8; + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * + OutputType.Format.nBlockAlign; + + hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); + if(FAILED(hr)) + { + ERR("Failed to check format support: 0x%08lx\n", hr); + hr = IAudioClient_GetMixFormat(data->client, &wfx); + } + if(FAILED(hr)) + { + ERR("Failed to find a supported format: 0x%08lx\n", hr); + return hr; + } + + if(wfx != NULL) + { + if(!MakeExtensible(&OutputType, wfx)) + { + CoTaskMemFree(wfx); + return E_FAIL; + } + CoTaskMemFree(wfx); + wfx = NULL; + + if(device->Frequency != OutputType.Format.nSamplesPerSec) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set %dhz, got %ldhz instead\n", device->Frequency, OutputType.Format.nSamplesPerSec); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = OutputType.Format.nSamplesPerSec; + } + + if(!((device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) || + (device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) || + (device->FmtChans == DevFmtQuad && OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) || + (device->FmtChans == DevFmtX51 && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) || + (device->FmtChans == DevFmtX51Side && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) || + (device->FmtChans == DevFmtX61 && OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) || + (device->FmtChans == DevFmtX71 && OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1))) + { + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device->FmtChans), OutputType.Format.nChannels, OutputType.dwChannelMask); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + + if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) + device->FmtChans = DevFmtMono; + else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) + device->FmtChans = DevFmtStereo; + else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) + device->FmtChans = DevFmtQuad; + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) + device->FmtChans = DevFmtX51; + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) + device->FmtChans = DevFmtX51Side; + else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) + device->FmtChans = DevFmtX61; + else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1) + device->FmtChans = DevFmtX71; + else + { + ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask); + device->FmtChans = DevFmtStereo; + OutputType.Format.nChannels = 2; + OutputType.dwChannelMask = STEREO; + } + } + + if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) + { + if(OutputType.Samples.wValidBitsPerSample == 0) + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample || + !((device->FmtType == DevFmtUByte && OutputType.Format.wBitsPerSample == 8) || + (device->FmtType == DevFmtShort && OutputType.Format.wBitsPerSample == 16))) + { + ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample); + if(OutputType.Format.wBitsPerSample == 8) + device->FmtType = DevFmtUByte; + else if(OutputType.Format.wBitsPerSample == 16) + device->FmtType = DevFmtShort; + else + { + device->FmtType = DevFmtShort; + OutputType.Format.wBitsPerSample = 16; + } + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + } + } + else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + if(OutputType.Samples.wValidBitsPerSample == 0) + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample || + !((device->FmtType == DevFmtFloat && OutputType.Format.wBitsPerSample == 32))) + { + ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample); + if(OutputType.Format.wBitsPerSample != 32) + { + device->FmtType = DevFmtFloat; + OutputType.Format.wBitsPerSample = 32; + } + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + } + } + else + { + ERR("Unhandled format sub-type\n"); + device->FmtType = DevFmtShort; + OutputType.Format.wBitsPerSample = 16; + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + } + + SetDefaultWFXChannelOrder(device); + + hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL); + if(SUCCEEDED(hr)) + { + min_len = (min_per*device->Frequency + 10000000-1) / 10000000; + if(min_len < device->UpdateSize) + min_len *= (device->UpdateSize + min_len/2)/min_len; + + device->NumUpdates = (device->NumUpdates*device->UpdateSize + min_len/2) / + min_len; + device->NumUpdates = maxu(device->NumUpdates, 2); + device->UpdateSize = min_len; + + hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + ((REFERENCE_TIME)device->UpdateSize* + device->NumUpdates*10000000 + + device->Frequency-1) / device->Frequency, + 0, &OutputType.Format, NULL); + } + if(FAILED(hr)) + { + ERR("Failed to initialize audio client: 0x%08lx\n", hr); + return hr; + } + + hr = IAudioClient_GetBufferSize(data->client, &buffer_len); + if(FAILED(hr)) + { + ERR("Failed to get audio buffer info: 0x%08lx\n", hr); + return hr; + } + + device->NumUpdates = buffer_len / device->UpdateSize; + if(device->NumUpdates <= 1) + { + device->NumUpdates = 1; + ERR("Audio client returned buffer_len < period*2; expect break up\n"); + } + + ResetEvent(data->hNotifyEvent); + hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent); + if(SUCCEEDED(hr)) + hr = IAudioClient_Start(data->client); + if(FAILED(hr)) + { + ERR("Failed to start audio client: 0x%08lx\n", hr); + return hr; + } + + data->thread = StartThread(MMDevApiProc, device); + if(!data->thread) + { + IAudioClient_Stop(data->client); + ERR("Failed to start thread\n"); + return E_FAIL; + } + + return hr; +} + + +static DWORD CALLBACK MessageProc(void *ptr) +{ + ThreadRequest *req = ptr; + IMMDeviceEnumerator *Enumerator; + MMDevApiData *data; + ALCdevice *device; + HRESULT hr; + MSG msg; + + TRACE("Starting message thread\n"); + + hr = CoInitialize(NULL); + if(FAILED(hr)) + { + WARN("Failed to initialize COM: 0x%08lx\n", hr); + req->result = hr; + SetEvent(req->FinishedEvt); + return 0; + } + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(FAILED(hr)) + { + WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); + CoUninitialize(); + req->result = hr; + SetEvent(req->FinishedEvt); + return 0; + } + Enumerator = ptr; + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + + req->result = S_OK; + SetEvent(req->FinishedEvt); + + TRACE("Starting message loop\n"); + while(GetMessage(&msg, NULL, 0, 0)) + { + TRACE("Got message %u\n", msg.message); + switch(msg.message) + { + case WM_USER_OpenDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(SUCCEEDED(hr)) + { + Enumerator = ptr; + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + } + if(SUCCEEDED(hr)) + hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); + if(SUCCEEDED(hr)) + data->client = ptr; + + if(FAILED(hr)) + { + if(data->mmdev) + IMMDevice_Release(data->mmdev); + data->mmdev = NULL; + } + + req->result = hr; + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_ResetDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + + req->result = DoReset(device); + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_StopDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + if(data->thread) + { + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + + IAudioClient_Stop(data->client); + } + + req->result = S_OK; + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_CloseDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + IAudioClient_Release(data->client); + data->client = NULL; + + IMMDevice_Release(data->mmdev); + data->mmdev = NULL; + + req->result = S_OK; + SetEvent(req->FinishedEvt); + continue; + + default: + ERR("Unexpected message: %u\n", msg.message); + continue; + } + } + TRACE("Message loop finished\n"); + + CoUninitialize(); + return 0; +} + + +static BOOL MMDevApiLoad(void) +{ + static HRESULT InitResult; + if(!ThreadHdl) + { + ThreadRequest req; + InitResult = E_FAIL; + + req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); + if(req.FinishedEvt == NULL) + ERR("Failed to create event: %lu\n", GetLastError()); + else + { + ThreadHdl = CreateThread(NULL, 0, MessageProc, &req, 0, &ThreadID); + if(ThreadHdl != NULL) + InitResult = WaitForResponse(&req); + CloseHandle(req.FinishedEvt); + } + } + return SUCCEEDED(InitResult); +} + + +static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +{ + MMDevApiData *data = NULL; + HRESULT hr; + + if(!deviceName) + deviceName = mmDevice; + else if(strcmp(deviceName, mmDevice) != 0) + return ALC_FALSE; + + //Initialise requested device + data = calloc(1, sizeof(MMDevApiData)); + if(!data) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + device->ExtraData = data; + + hr = S_OK; + data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(data->hNotifyEvent == NULL || data->MsgEvent == NULL) + hr = E_FAIL; + + if(SUCCEEDED(hr)) + { + ThreadRequest req = { data->MsgEvent, 0 }; + + hr = E_FAIL; + if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device)) + hr = WaitForResponse(&req); + } + + if(FAILED(hr)) + { + if(data->hNotifyEvent != NULL) + CloseHandle(data->hNotifyEvent); + data->hNotifyEvent = NULL; + if(data->MsgEvent != NULL) + CloseHandle(data->MsgEvent); + data->MsgEvent = NULL; + + free(data); + device->ExtraData = NULL; + + ERR("Device init failed: 0x%08lx\n", hr); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + return ALC_TRUE; +} + +static void MMDevApiClosePlayback(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; + + if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device)) + (void)WaitForResponse(&req); + + CloseHandle(data->MsgEvent); + data->MsgEvent = NULL; + + CloseHandle(data->hNotifyEvent); + data->hNotifyEvent = NULL; + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean MMDevApiResetPlayback(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; + HRESULT hr = E_FAIL; + + if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device)) + hr = WaitForResponse(&req); + + return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; +} + +static void MMDevApiStopPlayback(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; + + if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device)) + (void)WaitForResponse(&req); +} + + +static const BackendFuncs MMDevApiFuncs = { + MMDevApiOpenPlayback, + MMDevApiClosePlayback, + MMDevApiResetPlayback, + MMDevApiStopPlayback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +ALCboolean alcMMDevApiInit(BackendFuncs *FuncList) +{ + if(!MMDevApiLoad()) + return ALC_FALSE; + *FuncList = MMDevApiFuncs; + return ALC_TRUE; +} + +void alcMMDevApiDeinit(void) +{ + if(ThreadHdl) + { + TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID); + PostThreadMessage(ThreadID, WM_QUIT, 0, 0); + CloseHandle(ThreadHdl); + ThreadHdl = NULL; + } +} + +void alcMMDevApiProbe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(mmDevice); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(mmDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/Alc/backends/null.c b/Alc/backends/null.c new file mode 100644 index 00000000..dd1ac216 --- /dev/null +++ b/Alc/backends/null.c @@ -0,0 +1,164 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2010 by Chris Robinson + * 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 +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + volatile int killNow; + ALvoid *thread; +} null_data; + + +static const ALCchar nullDevice[] = "No Output"; + +static ALuint NullProc(ALvoid *ptr) +{ + ALCdevice *Device = (ALCdevice*)ptr; + null_data *data = (null_data*)Device->ExtraData; + ALuint now, start; + ALuint64 avail, done; + const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 / + Device->Frequency / 2; + + done = 0; + start = timeGetTime(); + while(!data->killNow && Device->Connected) + { + now = timeGetTime(); + + avail = (ALuint64)(now-start) * Device->Frequency / 1000; + if(avail < done) + { + /* Timer wrapped. Add the remainder of the cycle to the available + * count and reset the number of samples done */ + avail += (ALuint64)0xFFFFFFFFu*Device->Frequency/1000 - done; + done = 0; + } + if(avail-done < Device->UpdateSize) + { + Sleep(restTime); + continue; + } + + while(avail-done >= Device->UpdateSize) + { + aluMixData(Device, NULL, Device->UpdateSize); + done += Device->UpdateSize; + } + } + + return 0; +} + +static ALCboolean null_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + null_data *data; + + if(!deviceName) + deviceName = nullDevice; + else if(strcmp(deviceName, nullDevice) != 0) + return ALC_FALSE; + + data = (null_data*)calloc(1, sizeof(*data)); + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void null_close_playback(ALCdevice *device) +{ + null_data *data = (null_data*)device->ExtraData; + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean null_reset_playback(ALCdevice *device) +{ + null_data *data = (null_data*)device->ExtraData; + + SetDefaultWFXChannelOrder(device); + + data->thread = StartThread(NullProc, device); + if(data->thread == NULL) + return ALC_FALSE; + + return ALC_TRUE; +} + +static void null_stop_playback(ALCdevice *device) +{ + null_data *data = (null_data*)device->ExtraData; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; +} + + +static const BackendFuncs null_funcs = { + null_open_playback, + null_close_playback, + null_reset_playback, + null_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_null_init(BackendFuncs *func_list) +{ + *func_list = null_funcs; + return ALC_TRUE; +} + +void alc_null_deinit(void) +{ +} + +void alc_null_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(nullDevice); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(nullDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/Alc/backends/opensl.c b/Alc/backends/opensl.c new file mode 100644 index 00000000..88d05505 --- /dev/null +++ b/Alc/backends/opensl.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This is an OpenAL backend for Android using the native audio APIs based on + * OpenSL ES 1.0.1. It is based on source code for the native-audio sample app + * bundled with NDK. + */ + +#include "config.h" + +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +#include +#if 1 +#include +#else +extern SLAPIENTRY const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + +struct SLAndroidSimpleBufferQueueItf_; +typedef const struct SLAndroidSimpleBufferQueueItf_ * const * SLAndroidSimpleBufferQueueItf; + +typedef void (*slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller, void *pContext); + +typedef struct SLAndroidSimpleBufferQueueState_ { + SLuint32 count; + SLuint32 index; +} SLAndroidSimpleBufferQueueState; + + +struct SLAndroidSimpleBufferQueueItf_ { + SLresult (*Enqueue) ( + SLAndroidSimpleBufferQueueItf self, + const void *pBuffer, + SLuint32 size + ); + SLresult (*Clear) ( + SLAndroidSimpleBufferQueueItf self + ); + SLresult (*GetState) ( + SLAndroidSimpleBufferQueueItf self, + SLAndroidSimpleBufferQueueState *pState + ); + SLresult (*RegisterCallback) ( + SLAndroidSimpleBufferQueueItf self, + slAndroidSimpleBufferQueueCallback callback, + void* pContext + ); +}; + +#define SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE ((SLuint32) 0x800007BD) + +typedef struct SLDataLocator_AndroidSimpleBufferQueue { + SLuint32 locatorType; + SLuint32 numBuffers; +} SLDataLocator_AndroidSimpleBufferQueue; + +#endif + +/* Helper macros */ +#define SLObjectItf_Realize(a,b) ((*(a))->Realize((a),(b))) +#define SLObjectItf_GetInterface(a,b,c) ((*(a))->GetInterface((a),(b),(c))) +#define SLObjectItf_Destroy(a) ((*(a))->Destroy((a))) + +#define SLEngineItf_CreateOutputMix(a,b,c,d,e) ((*(a))->CreateOutputMix((a),(b),(c),(d),(e))) +#define SLEngineItf_CreateAudioPlayer(a,b,c,d,e,f,g) ((*(a))->CreateAudioPlayer((a),(b),(c),(d),(e),(f),(g))) + +#define SLPlayItf_SetPlayState(a,b) ((*(a))->SetPlayState((a),(b))) + + +typedef struct { + /* engine interfaces */ + SLObjectItf engineObject; + SLEngineItf engine; + + /* output mix interfaces */ + SLObjectItf outputMix; + + /* buffer queue player interfaces */ + SLObjectItf bufferQueueObject; + + void *buffer; + ALuint bufferSize; + + ALuint frameSize; +} osl_data; + + +static const ALCchar opensl_device[] = "OpenSL"; + + +static SLuint32 GetChannelMask(enum DevFmtChannels chans) +{ + switch(chans) + { + case DevFmtMono: return SL_SPEAKER_FRONT_CENTER; + case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; + case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT; + case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| + SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT; + case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| + SL_SPEAKER_BACK_CENTER| + SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; + case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| + SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT| + SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; + case DevFmtX51Side: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| + SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; + } + return 0; +} + +static const char *res_str(SLresult result) +{ + switch(result) + { + case SL_RESULT_SUCCESS: return "Success"; + case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated"; + case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid"; + case SL_RESULT_MEMORY_FAILURE: return "Memory failure"; + case SL_RESULT_RESOURCE_ERROR: return "Resource error"; + case SL_RESULT_RESOURCE_LOST: return "Resource lost"; + case SL_RESULT_IO_ERROR: return "I/O error"; + case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient"; + case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted"; + case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported"; + case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found"; + case SL_RESULT_PERMISSION_DENIED: return "Permission denied"; + case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported"; + case SL_RESULT_INTERNAL_ERROR: return "Internal error"; + case SL_RESULT_UNKNOWN_ERROR: return "Unknown error"; + case SL_RESULT_OPERATION_ABORTED: return "Operation aborted"; + case SL_RESULT_CONTROL_LOST: return "Control lost"; + case SL_RESULT_READONLY: return "ReadOnly"; + case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported"; + case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible"; + } + return "Unknown error code"; +} + +#define PRINTERR(x, s) do { \ + if((x) != SL_RESULT_SUCCESS) \ + ERR("%s: %s\n", (s), res_str((x))); \ +} while(0) + +/* this callback handler is called every time a buffer finishes playing */ +static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + ALCdevice *Device = context; + osl_data *data = Device->ExtraData; + SLresult result; + + aluMixData(Device, data->buffer, data->bufferSize/data->frameSize); + + result = (*bq)->Enqueue(bq, data->buffer, data->bufferSize); + PRINTERR(result, "bq->Enqueue"); +} + + +static ALCboolean opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName) +{ + osl_data *data = NULL; + SLresult result; + + if(!deviceName) + deviceName = opensl_device; + else if(strcmp(deviceName, opensl_device) != 0) + return ALC_FALSE; + + data = calloc(1, sizeof(*data)); + if(!data) + { + alcSetError(Device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + // create engine + result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL); + PRINTERR(result, "slCreateEngine"); + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_Realize(data->engineObject, SL_BOOLEAN_FALSE); + PRINTERR(result, "engine->Realize"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_GetInterface(data->engineObject, SL_IID_ENGINE, &data->engine); + PRINTERR(result, "engine->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLEngineItf_CreateOutputMix(data->engine, &data->outputMix, 0, NULL, NULL); + PRINTERR(result, "engine->CreateOutputMix"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_Realize(data->outputMix, SL_BOOLEAN_FALSE); + PRINTERR(result, "outputMix->Realize"); + } + + if(SL_RESULT_SUCCESS != result) + { + if(data->outputMix != NULL) + SLObjectItf_Destroy(data->outputMix); + data->outputMix = NULL; + + if(data->engineObject != NULL) + SLObjectItf_Destroy(data->engineObject); + data->engineObject = NULL; + data->engine = NULL; + + free(data); + return ALC_FALSE; + } + + Device->szDeviceName = strdup(deviceName); + Device->ExtraData = data; + + return ALC_TRUE; +} + + +static void opensl_close_playback(ALCdevice *Device) +{ + osl_data *data = Device->ExtraData; + + SLObjectItf_Destroy(data->outputMix); + data->outputMix = NULL; + + SLObjectItf_Destroy(data->engineObject); + data->engineObject = NULL; + data->engine = NULL; + + free(data); + Device->ExtraData = NULL; +} + +static ALCboolean opensl_reset_playback(ALCdevice *Device) +{ + osl_data *data = Device->ExtraData; + SLDataLocator_AndroidSimpleBufferQueue loc_bufq; + SLAndroidSimpleBufferQueueItf bufferQueue; + SLDataLocator_OutputMix loc_outmix; + SLDataFormat_PCM format_pcm; + SLDataSource audioSrc; + SLDataSink audioSnk; + SLPlayItf player; + SLInterfaceID id; + SLboolean req; + SLresult result; + ALuint i; + + + Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency; + Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2; + Device->NumUpdates = 2; + + Device->Frequency = 44100; + Device->FmtChans = DevFmtStereo; + Device->FmtType = DevFmtShort; + + SetDefaultWFXChannelOrder(Device); + + + id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + req = SL_BOOLEAN_TRUE; + + loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + loc_bufq.numBuffers = Device->NumUpdates; + + format_pcm.formatType = SL_DATAFORMAT_PCM; + format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans); + format_pcm.samplesPerSec = Device->Frequency * 1000; + format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8; + format_pcm.containerSize = format_pcm.bitsPerSample; + format_pcm.channelMask = GetChannelMask(Device->FmtChans); + format_pcm.endianness = SL_BYTEORDER_NATIVE; + + audioSrc.pLocator = &loc_bufq; + audioSrc.pFormat = &format_pcm; + + loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; + loc_outmix.outputMix = data->outputMix; + audioSnk.pLocator = &loc_outmix; + audioSnk.pFormat = NULL; + + + result = SLEngineItf_CreateAudioPlayer(data->engine, &data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req); + PRINTERR(result, "engine->CreateAudioPlayer"); + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_Realize(data->bufferQueueObject, SL_BOOLEAN_FALSE); + PRINTERR(result, "bufferQueue->Realize"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_BUFFERQUEUE, &bufferQueue); + PRINTERR(result, "bufferQueue->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = (*bufferQueue)->RegisterCallback(bufferQueue, opensl_callback, Device); + PRINTERR(result, "bufferQueue->RegisterCallback"); + } + if(SL_RESULT_SUCCESS == result) + { + data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); + data->bufferSize = Device->UpdateSize * data->frameSize; + data->buffer = calloc(1, data->bufferSize); + if(!data->buffer) + { + result = SL_RESULT_MEMORY_FAILURE; + PRINTERR(result, "calloc"); + } + } + /* enqueue the first buffer to kick off the callbacks */ + for(i = 0;i < Device->NumUpdates;i++) + { + if(SL_RESULT_SUCCESS == result) + { + result = (*bufferQueue)->Enqueue(bufferQueue, data->buffer, data->bufferSize); + PRINTERR(result, "bufferQueue->Enqueue"); + } + } + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_PLAY, &player); + PRINTERR(result, "bufferQueue->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLPlayItf_SetPlayState(player, SL_PLAYSTATE_PLAYING); + PRINTERR(result, "player->SetPlayState"); + } + + if(SL_RESULT_SUCCESS != result) + { + if(data->bufferQueueObject != NULL) + SLObjectItf_Destroy(data->bufferQueueObject); + data->bufferQueueObject = NULL; + + free(data->buffer); + data->buffer = NULL; + data->bufferSize = 0; + + return ALC_FALSE; + } + + return ALC_TRUE; +} + + +static void opensl_stop_playback(ALCdevice *Device) +{ + osl_data *data = Device->ExtraData; + + if(data->bufferQueueObject != NULL) + SLObjectItf_Destroy(data->bufferQueueObject); + data->bufferQueueObject = NULL; + + free(data->buffer); + data->buffer = NULL; + data->bufferSize = 0; +} + + +static const BackendFuncs opensl_funcs = { + opensl_open_playback, + opensl_close_playback, + opensl_reset_playback, + opensl_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +ALCboolean alc_opensl_init(BackendFuncs *func_list) +{ + *func_list = opensl_funcs; + return ALC_TRUE; +} + +void alc_opensl_deinit(void) +{ +} + +void alc_opensl_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(opensl_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(opensl_device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/Alc/backends/oss.c b/Alc/backends/oss.c new file mode 100644 index 00000000..724b23c2 --- /dev/null +++ b/Alc/backends/oss.c @@ -0,0 +1,536 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include + +/* + * The OSS documentation talks about SOUND_MIXER_READ, but the header + * only contains MIXER_READ. Play safe. Same for WRITE. + */ +#ifndef SOUND_MIXER_READ +#define SOUND_MIXER_READ MIXER_READ +#endif +#ifndef SOUND_MIXER_WRITE +#define SOUND_MIXER_WRITE MIXER_WRITE +#endif + +static const ALCchar oss_device[] = "OSS Default"; + +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; + while (x > 1) + { + x >>= 1; + y++; + } + return y; +} + + +static ALuint OSSProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + oss_data *data = (oss_data*)pDevice->ExtraData; + ALint frameSize; + ssize_t wrote; + + SetRTPriority(); + + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + while(!data->killNow && pDevice->Connected) + { + ALint len = data->data_size; + ALubyte *WritePtr = data->mix_data; + + aluMixData(pDevice, WritePtr, len/frameSize); + while(len > 0 && !data->killNow) + { + wrote = write(data->fd, WritePtr, len); + if(wrote < 0) + { + if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + { + ERR("write failed: %s\n", strerror(errno)); + aluHandleDisconnect(pDevice); + break; + } + + Sleep(1); + continue; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + +static ALuint OSSCaptureProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + oss_data *data = (oss_data*)pDevice->ExtraData; + int frameSize; + int amt; + + SetRTPriority(); + + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + while(!data->killNow) + { + amt = read(data->fd, data->mix_data, data->data_size); + if(amt < 0) + { + ERR("read failed: %s\n", strerror(errno)); + aluHandleDisconnect(pDevice); + break; + } + if(amt == 0) + { + Sleep(1); + continue; + } + if(data->doCapture) + WriteRingBuffer(data->ring, data->mix_data, amt/frameSize); + } + + return 0; +} + +static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + char driver[64]; + oss_data *data; + + strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + if(!deviceName) + deviceName = oss_device; + else if(strcmp(deviceName, oss_device) != 0) + return ALC_FALSE; + + data = (oss_data*)calloc(1, sizeof(oss_data)); + data->killNow = 0; + + data->fd = open(driver, O_WRONLY); + if(data->fd == -1) + { + free(data); + ERR("Could not open %s: %s\n", driver, strerror(errno)); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void oss_close_playback(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + + close(data->fd); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean oss_reset_playback(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + int numFragmentsLogSize; + int log2FragmentSize; + unsigned int periods; + audio_buf_info info; + ALuint frameSize; + int numChannels; + int ossFormat; + int ossSpeed; + char *err; + + switch(device->FmtType) + { + case DevFmtByte: + ossFormat = AFMT_S8; + break; + case DevFmtUByte: + ossFormat = AFMT_U8; + break; + case DevFmtUShort: + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + ossFormat = AFMT_S16_NE; + break; + } + + periods = device->NumUpdates; + numChannels = ChannelsFromDevFmt(device->FmtChans); + frameSize = numChannels * BytesFromDevFmt(device->FmtType); + + ossSpeed = device->Frequency; + log2FragmentSize = log2i(device->UpdateSize * frameSize); + + /* according to the OSS spec, 16 bytes are the minimum */ + if (log2FragmentSize < 4) + log2FragmentSize = 4; + /* Subtract one period since the temp mixing buffer counts as one. Still + * need at least two on the card, though. */ + if(periods > 2) periods--; + numFragmentsLogSize = (periods << 16) | log2FragmentSize; + +#define CHECKERR(func) if((func) < 0) { \ + err = #func; \ + goto err; \ +} + /* 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)); + if(0) + { + err: + ERR("%s failed: %s\n", err, strerror(errno)); + return ALC_FALSE; + } +#undef CHECKERR + + if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) + { + ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); + return ALC_FALSE; + } + + if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) || + (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) || + (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort))) + { + ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat); + return ALC_FALSE; + } + + if(device->Frequency != (ALuint)ossSpeed) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, ossSpeed); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = ossSpeed; + } + device->UpdateSize = info.fragsize / frameSize; + device->NumUpdates = info.fragments + 1; + + data->data_size = device->UpdateSize * frameSize; + data->mix_data = calloc(1, data->data_size); + + SetDefaultChannelOrder(device); + + data->thread = StartThread(OSSProc, device); + if(data->thread == NULL) + { + free(data->mix_data); + data->mix_data = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void oss_stop_playback(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0) + ERR("Error resetting device: %s\n", strerror(errno)); + + free(data->mix_data); + data->mix_data = NULL; +} + + +static ALCboolean oss_open_capture(ALCdevice *device, const ALCchar *deviceName) +{ + int numFragmentsLogSize; + int log2FragmentSize; + unsigned int periods; + audio_buf_info info; + ALuint frameSize; + int numChannels; + char driver[64]; + oss_data *data; + int ossFormat; + int ossSpeed; + char *err; + + strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + if(!deviceName) + deviceName = oss_device; + else if(strcmp(deviceName, oss_device) != 0) + return ALC_FALSE; + + data = (oss_data*)calloc(1, sizeof(oss_data)); + data->killNow = 0; + + data->fd = open(driver, O_RDONLY); + if(data->fd == -1) + { + free(data); + ERR("Could not open %s: %s\n", driver, strerror(errno)); + return ALC_FALSE; + } + + switch(device->FmtType) + { + case DevFmtByte: + ossFormat = AFMT_S8; + break; + case DevFmtUByte: + ossFormat = AFMT_U8; + break; + case DevFmtShort: + ossFormat = AFMT_S16_NE; + break; + case DevFmtUShort: + case DevFmtFloat: + free(data); + ERR("%s capture samples not supported on OSS\n", DevFmtTypeString(device->FmtType)); + return ALC_FALSE; + } + + periods = 4; + numChannels = ChannelsFromDevFmt(device->FmtChans); + frameSize = numChannels * BytesFromDevFmt(device->FmtType); + ossSpeed = device->Frequency; + log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates * + frameSize / periods); + + /* according to the OSS spec, 16 bytes are the minimum */ + if (log2FragmentSize < 4) + log2FragmentSize = 4; + numFragmentsLogSize = (periods << 16) | log2FragmentSize; + +#define CHECKERR(func) if((func) < 0) { \ + 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)); + if(0) + { + err: + ERR("%s failed: %s\n", err, strerror(errno)); + close(data->fd); + free(data); + return ALC_FALSE; + } +#undef CHECKERR + + 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); + return ALC_FALSE; + } + + if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) || + (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) || + (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); + return ALC_FALSE; + } + + data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates); + if(!data->ring) + { + ERR("Ring buffer create failed\n"); + close(data->fd); + free(data); + return ALC_FALSE; + } + + data->data_size = info.fragsize; + data->mix_data = calloc(1, data->data_size); + + device->ExtraData = data; + data->thread = StartThread(OSSCaptureProc, device); + if(data->thread == NULL) + { + device->ExtraData = NULL; + free(data->mix_data); + free(data); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + return ALC_TRUE; +} + +static void oss_close_capture(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + data->killNow = 1; + StopThread(data->thread); + + close(data->fd); + + DestroyRingBuffer(data->ring); + + free(data->mix_data); + free(data); + device->ExtraData = NULL; +} + +static void oss_start_capture(ALCdevice *pDevice) +{ + oss_data *data = (oss_data*)pDevice->ExtraData; + data->doCapture = 1; +} + +static void oss_stop_capture(ALCdevice *pDevice) +{ + oss_data *data = (oss_data*)pDevice->ExtraData; + data->doCapture = 0; +} + +static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + oss_data *data = (oss_data*)pDevice->ExtraData; + if(lSamples <= (ALCuint)RingBufferSize(data->ring)) + ReadRingBuffer(data->ring, pBuffer, lSamples); + else + alcSetError(pDevice, ALC_INVALID_VALUE); +} + +static ALCuint oss_available_samples(ALCdevice *pDevice) +{ + oss_data *data = (oss_data*)pDevice->ExtraData; + return RingBufferSize(data->ring); +} + + +static const BackendFuncs oss_funcs = { + oss_open_playback, + oss_close_playback, + oss_reset_playback, + oss_stop_playback, + oss_open_capture, + oss_close_capture, + oss_start_capture, + oss_stop_capture, + oss_capture_samples, + oss_available_samples +}; + +ALCboolean alc_oss_init(BackendFuncs *func_list) +{ + *func_list = oss_funcs; + return ALC_TRUE; +} + +void alc_oss_deinit(void) +{ +} + +void alc_oss_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0) +#endif + AppendDeviceList(oss_device); + } + break; + + case ALL_DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0) +#endif + AppendAllDeviceList(oss_device); + } + break; + + case CAPTURE_DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf) == 0) +#endif + AppendCaptureDeviceList(oss_device); + } + break; + } +} diff --git a/Alc/backends/portaudio.c b/Alc/backends/portaudio.c new file mode 100644 index 00000000..4f3dfd5f --- /dev/null +++ b/Alc/backends/portaudio.c @@ -0,0 +1,449 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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 +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include + + +static const ALCchar pa_device[] = "PortAudio Default"; + + +static void *pa_handle; +#ifdef HAVE_DYNLOAD +#define MAKE_FUNC(x) static typeof(x) * p##x +MAKE_FUNC(Pa_Initialize); +MAKE_FUNC(Pa_Terminate); +MAKE_FUNC(Pa_GetErrorText); +MAKE_FUNC(Pa_StartStream); +MAKE_FUNC(Pa_StopStream); +MAKE_FUNC(Pa_OpenStream); +MAKE_FUNC(Pa_CloseStream); +MAKE_FUNC(Pa_GetDefaultOutputDevice); +MAKE_FUNC(Pa_GetStreamInfo); +#undef MAKE_FUNC + +#define Pa_Initialize pPa_Initialize +#define Pa_Terminate pPa_Terminate +#define Pa_GetErrorText pPa_GetErrorText +#define Pa_StartStream pPa_StartStream +#define Pa_StopStream pPa_StopStream +#define Pa_OpenStream pPa_OpenStream +#define Pa_CloseStream pPa_CloseStream +#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice +#define Pa_GetStreamInfo pPa_GetStreamInfo +#endif + +static ALCboolean pa_load(void) +{ + if(!pa_handle) + { + PaError err; + +#ifdef HAVE_DYNLOAD +#ifdef _WIN32 +# define PALIB "portaudio.dll" +#elif defined(__APPLE__) && defined(__MACH__) +# define PALIB "libportaudio.2.dylib" +#elif defined(__OpenBSD__) +# define PALIB "libportaudio.so" +#else +# define PALIB "libportaudio.so.2" +#endif + + pa_handle = LoadLib(PALIB); + if(!pa_handle) + return ALC_FALSE; + +#define LOAD_FUNC(f) do { \ + p##f = GetSymbol(pa_handle, #f); \ + if(p##f == NULL) \ + { \ + CloseLib(pa_handle); \ + pa_handle = NULL; \ + return ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(Pa_Initialize); + LOAD_FUNC(Pa_Terminate); + LOAD_FUNC(Pa_GetErrorText); + LOAD_FUNC(Pa_StartStream); + LOAD_FUNC(Pa_StopStream); + LOAD_FUNC(Pa_OpenStream); + LOAD_FUNC(Pa_CloseStream); + LOAD_FUNC(Pa_GetDefaultOutputDevice); + LOAD_FUNC(Pa_GetStreamInfo); +#undef LOAD_FUNC +#else + pa_handle = (void*)0xDEADBEEF; +#endif + + if((err=Pa_Initialize()) != paNoError) + { + ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); + CloseLib(pa_handle); + pa_handle = NULL; + return ALC_FALSE; + } + } + return ALC_TRUE; +} + + +typedef struct { + PaStream *stream; + ALuint update_size; + + RingBuffer *ring; +} pa_data; + + +static int pa_callback(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags 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) +{ + ALCdevice *device = (ALCdevice*)userData; + pa_data *data = (pa_data*)device->ExtraData; + + (void)outputBuffer; + (void)timeInfo; + (void)statusFlags; + + WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer); + return 0; +} + + +static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + PaStreamParameters outParams; + pa_data *data; + PaError err; + + if(!deviceName) + deviceName = pa_device; + else if(strcmp(deviceName, pa_device) != 0) + return ALC_FALSE; + + data = (pa_data*)calloc(1, sizeof(pa_data)); + data->update_size = device->UpdateSize; + + device->ExtraData = data; + + outParams.device = GetConfigValueInt("port", "device", -1); + if(outParams.device < 0) + outParams.device = Pa_GetDefaultOutputDevice(); + outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) / + (float)device->Frequency; + outParams.hostApiSpecificStreamInfo = NULL; + + switch(device->FmtType) + { + case DevFmtByte: + outParams.sampleFormat = paInt8; + break; + case DevFmtUByte: + outParams.sampleFormat = paUInt8; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + outParams.sampleFormat = paInt16; + break; + case DevFmtFloat: + outParams.sampleFormat = paFloat32; + break; + } + outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2); + + SetDefaultChannelOrder(device); + + err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency, + device->UpdateSize, paNoFlag, pa_callback, device); + if(err != paNoError) + { + ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); + device->ExtraData = NULL; + free(data); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + + if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans)) + { + if(outParams.channelCount != 1 && outParams.channelCount != 2) + { + ERR("Unhandled channel count: %u\n", outParams.channelCount); + Pa_CloseStream(data->stream); + device->ExtraData = NULL; + free(data); + return ALC_FALSE; + } + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo); + } + + return ALC_TRUE; +} + +static void pa_close_playback(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + err = Pa_CloseStream(data->stream); + if(err != paNoError) + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean pa_reset_playback(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + const PaStreamInfo *streamInfo; + PaError err; + + streamInfo = Pa_GetStreamInfo(data->stream); + if(device->Frequency != streamInfo->sampleRate) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("PortAudio does not support changing sample rates (wanted %dhz, got %.1fhz)\n", device->Frequency, streamInfo->sampleRate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = streamInfo->sampleRate; + } + device->UpdateSize = data->update_size; + + err = Pa_StartStream(data->stream); + if(err != paNoError) + { + ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err)); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void pa_stop_playback(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + err = Pa_StopStream(data->stream); + if(err != paNoError) + ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); +} + + +static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName) +{ + PaStreamParameters inParams; + ALuint frame_size; + pa_data *data; + PaError err; + + if(!deviceName) + deviceName = pa_device; + else if(strcmp(deviceName, pa_device) != 0) + return ALC_FALSE; + + data = (pa_data*)calloc(1, sizeof(pa_data)); + if(data == NULL) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates); + if(data->ring == NULL) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + goto error; + } + + inParams.device = GetConfigValueInt("port", "capture", -1); + if(inParams.device < 0) + inParams.device = Pa_GetDefaultOutputDevice(); + inParams.suggestedLatency = 0.0f; + inParams.hostApiSpecificStreamInfo = NULL; + + switch(device->FmtType) + { + case DevFmtByte: + inParams.sampleFormat = paInt8; + break; + case DevFmtUByte: + inParams.sampleFormat = paUInt8; + break; + case DevFmtShort: + inParams.sampleFormat = paInt16; + break; + case DevFmtFloat: + inParams.sampleFormat = paFloat32; + break; + case DevFmtUShort: + ERR("Unsigned short samples not supported\n"); + goto error; + } + inParams.channelCount = ChannelsFromDevFmt(device->FmtChans); + + err = Pa_OpenStream(&data->stream, &inParams, NULL, device->Frequency, + paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device); + if(err != paNoError) + { + ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); + goto error; + } + + device->szDeviceName = strdup(deviceName); + + device->ExtraData = data; + return ALC_TRUE; + +error: + DestroyRingBuffer(data->ring); + free(data); + return ALC_FALSE; +} + +static void pa_close_capture(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + err = Pa_CloseStream(data->stream); + if(err != paNoError) + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); + + free(data); + device->ExtraData = NULL; +} + +static void pa_start_capture(ALCdevice *device) +{ + pa_data *data = device->ExtraData; + PaError err; + + err = Pa_StartStream(data->stream); + if(err != paNoError) + ERR("Error starting stream: %s\n", Pa_GetErrorText(err)); +} + +static void pa_stop_capture(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + err = Pa_StopStream(data->stream); + if(err != paNoError) + ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); +} + +static void pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +{ + pa_data *data = device->ExtraData; + if(samples <= (ALCuint)RingBufferSize(data->ring)) + ReadRingBuffer(data->ring, buffer, samples); + else + alcSetError(device, ALC_INVALID_VALUE); +} + +static ALCuint pa_available_samples(ALCdevice *device) +{ + pa_data *data = device->ExtraData; + return RingBufferSize(data->ring); +} + + +static const BackendFuncs pa_funcs = { + pa_open_playback, + pa_close_playback, + pa_reset_playback, + pa_stop_playback, + pa_open_capture, + pa_close_capture, + pa_start_capture, + pa_stop_capture, + pa_capture_samples, + pa_available_samples +}; + +ALCboolean alc_pa_init(BackendFuncs *func_list) +{ + if(!pa_load()) + return ALC_FALSE; + *func_list = pa_funcs; + return ALC_TRUE; +} + +void alc_pa_deinit(void) +{ + if(pa_handle) + { + Pa_Terminate(); +#ifdef HAVE_DYNLOAD + CloseLib(pa_handle); +#endif + pa_handle = NULL; + } +} + +void alc_pa_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(pa_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(pa_device); + break; + case CAPTURE_DEVICE_PROBE: + AppendCaptureDeviceList(pa_device); + break; + } +} diff --git a/Alc/backends/pulseaudio.c b/Alc/backends/pulseaudio.c new file mode 100644 index 00000000..39df3282 --- /dev/null +++ b/Alc/backends/pulseaudio.c @@ -0,0 +1,1430 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2009 by Konstantinos Natsakis + * Copyright (C) 2010 by Chris Robinson + * 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 "alMain.h" + +#include + +#if PA_API_VERSION == 11 +#define PA_STREAM_ADJUST_LATENCY 0x2000U +#define PA_STREAM_EARLY_REQUESTS 0x4000U +static __inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) +{ + return (x == PA_STREAM_CREATING || x == PA_STREAM_READY); +} +static __inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) +{ + return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING || + x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY); +} +#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD +#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD +#elif PA_API_VERSION != 12 +#error Invalid PulseAudio API version +#endif + +#ifndef PA_CHECK_VERSION +#define PA_CHECK_VERSION(major,minor,micro) \ + ((PA_MAJOR > (major)) || \ + (PA_MAJOR == (major) && PA_MINOR > (minor)) || \ + (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro))) +#endif + +static void *pa_handle; +#ifdef HAVE_DYNLOAD +#define MAKE_FUNC(x) static typeof(x) * p##x +MAKE_FUNC(pa_context_unref); +MAKE_FUNC(pa_sample_spec_valid); +MAKE_FUNC(pa_stream_drop); +MAKE_FUNC(pa_strerror); +MAKE_FUNC(pa_context_get_state); +MAKE_FUNC(pa_stream_get_state); +MAKE_FUNC(pa_threaded_mainloop_signal); +MAKE_FUNC(pa_stream_peek); +MAKE_FUNC(pa_threaded_mainloop_wait); +MAKE_FUNC(pa_threaded_mainloop_unlock); +MAKE_FUNC(pa_threaded_mainloop_in_thread); +MAKE_FUNC(pa_context_new); +MAKE_FUNC(pa_threaded_mainloop_stop); +MAKE_FUNC(pa_context_disconnect); +MAKE_FUNC(pa_threaded_mainloop_start); +MAKE_FUNC(pa_threaded_mainloop_get_api); +MAKE_FUNC(pa_context_set_state_callback); +MAKE_FUNC(pa_stream_write); +MAKE_FUNC(pa_xfree); +MAKE_FUNC(pa_stream_connect_record); +MAKE_FUNC(pa_stream_connect_playback); +MAKE_FUNC(pa_stream_readable_size); +MAKE_FUNC(pa_stream_writable_size); +MAKE_FUNC(pa_stream_cork); +MAKE_FUNC(pa_stream_is_suspended); +MAKE_FUNC(pa_stream_get_device_name); +MAKE_FUNC(pa_path_get_filename); +MAKE_FUNC(pa_get_binary_name); +MAKE_FUNC(pa_threaded_mainloop_free); +MAKE_FUNC(pa_context_errno); +MAKE_FUNC(pa_xmalloc); +MAKE_FUNC(pa_stream_unref); +MAKE_FUNC(pa_threaded_mainloop_accept); +MAKE_FUNC(pa_stream_set_write_callback); +MAKE_FUNC(pa_threaded_mainloop_new); +MAKE_FUNC(pa_context_connect); +MAKE_FUNC(pa_stream_set_buffer_attr); +MAKE_FUNC(pa_stream_get_buffer_attr); +MAKE_FUNC(pa_stream_get_sample_spec); +MAKE_FUNC(pa_stream_get_time); +MAKE_FUNC(pa_stream_set_read_callback); +MAKE_FUNC(pa_stream_set_state_callback); +MAKE_FUNC(pa_stream_set_moved_callback); +MAKE_FUNC(pa_stream_set_underflow_callback); +MAKE_FUNC(pa_stream_new); +MAKE_FUNC(pa_stream_disconnect); +MAKE_FUNC(pa_threaded_mainloop_lock); +MAKE_FUNC(pa_channel_map_init_auto); +MAKE_FUNC(pa_channel_map_parse); +MAKE_FUNC(pa_channel_map_snprint); +MAKE_FUNC(pa_channel_map_equal); +MAKE_FUNC(pa_context_get_server_info); +MAKE_FUNC(pa_context_get_sink_info_by_name); +MAKE_FUNC(pa_context_get_sink_info_list); +MAKE_FUNC(pa_context_get_source_info_list); +MAKE_FUNC(pa_operation_get_state); +MAKE_FUNC(pa_operation_unref); +#if PA_CHECK_VERSION(0,9,15) +MAKE_FUNC(pa_channel_map_superset); +MAKE_FUNC(pa_stream_set_buffer_attr_callback); +#endif +#if PA_CHECK_VERSION(0,9,16) +MAKE_FUNC(pa_stream_begin_write); +#endif +#undef MAKE_FUNC + +#define pa_context_unref ppa_context_unref +#define pa_sample_spec_valid ppa_sample_spec_valid +#define pa_stream_drop ppa_stream_drop +#define pa_strerror ppa_strerror +#define pa_context_get_state ppa_context_get_state +#define pa_stream_get_state ppa_stream_get_state +#define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal +#define pa_stream_peek ppa_stream_peek +#define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait +#define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock +#define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread +#define pa_context_new ppa_context_new +#define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop +#define pa_context_disconnect ppa_context_disconnect +#define pa_threaded_mainloop_start ppa_threaded_mainloop_start +#define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api +#define pa_context_set_state_callback ppa_context_set_state_callback +#define pa_stream_write ppa_stream_write +#define pa_xfree ppa_xfree +#define pa_stream_connect_record ppa_stream_connect_record +#define pa_stream_connect_playback ppa_stream_connect_playback +#define pa_stream_readable_size ppa_stream_readable_size +#define pa_stream_writable_size ppa_stream_writable_size +#define pa_stream_cork ppa_stream_cork +#define pa_stream_is_suspended ppa_stream_is_suspended +#define pa_stream_get_device_name ppa_stream_get_device_name +#define pa_path_get_filename ppa_path_get_filename +#define pa_get_binary_name ppa_get_binary_name +#define pa_threaded_mainloop_free ppa_threaded_mainloop_free +#define pa_context_errno ppa_context_errno +#define pa_xmalloc ppa_xmalloc +#define pa_stream_unref ppa_stream_unref +#define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept +#define pa_stream_set_write_callback ppa_stream_set_write_callback +#define pa_threaded_mainloop_new ppa_threaded_mainloop_new +#define pa_context_connect ppa_context_connect +#define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr +#define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr +#define pa_stream_get_sample_spec ppa_stream_get_sample_spec +#define pa_stream_get_time ppa_stream_get_time +#define pa_stream_set_read_callback ppa_stream_set_read_callback +#define pa_stream_set_state_callback ppa_stream_set_state_callback +#define pa_stream_set_moved_callback ppa_stream_set_moved_callback +#define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback +#define pa_stream_new ppa_stream_new +#define pa_stream_disconnect ppa_stream_disconnect +#define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock +#define pa_channel_map_init_auto ppa_channel_map_init_auto +#define pa_channel_map_parse ppa_channel_map_parse +#define pa_channel_map_snprint ppa_channel_map_snprint +#define pa_channel_map_equal ppa_channel_map_equal +#define pa_context_get_server_info ppa_context_get_server_info +#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name +#define pa_context_get_sink_info_list ppa_context_get_sink_info_list +#define pa_context_get_source_info_list ppa_context_get_source_info_list +#define pa_operation_get_state ppa_operation_get_state +#define pa_operation_unref ppa_operation_unref +#if PA_CHECK_VERSION(0,9,15) +#define pa_channel_map_superset ppa_channel_map_superset +#define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback +#endif +#if PA_CHECK_VERSION(0,9,16) +#define pa_stream_begin_write ppa_stream_begin_write +#endif + +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +typedef struct { + char *device_name; + + ALCuint samples; + ALCuint frame_size; + + RingBuffer *ring; + + 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 const ALCchar pulse_device[] = "PulseAudio Default"; +static DevMap *allDevNameMap; +static ALuint numDevNames; +static DevMap *allCaptureDevNameMap; +static ALuint numCaptureDevNames; +static pa_context_flags_t pulse_ctx_flags; + + +static void context_state_callback(pa_context *context, void *pdata) +{ + pa_threaded_mainloop *loop = pdata; + pa_context_state_t state; + + state = pa_context_get_state(context); + if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state)) + pa_threaded_mainloop_signal(loop, 0); +} + +static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) +{ + 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); + + context = pa_context_new(pa_threaded_mainloop_get_api(loop), name); + if(!context) + { + ERR("pa_context_new() failed\n"); + return NULL; + } + + 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_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 ALCboolean pulse_load(void) //{{{ +{ + ALCboolean ret = ALC_FALSE; + if(!pa_handle) + { + pa_threaded_mainloop *loop; + +#ifdef HAVE_DYNLOAD + +#ifdef _WIN32 +#define PALIB "libpulse-0.dll" +#elif defined(__APPLE__) && defined(__MACH__) +#define PALIB "libpulse.0.dylib" +#else +#define PALIB "libpulse.so.0" +#endif + pa_handle = LoadLib(PALIB); + if(!pa_handle) + return ALC_FALSE; + +#define LOAD_FUNC(x) do { \ + p##x = GetSymbol(pa_handle, #x); \ + if(!(p##x)) { \ + CloseLib(pa_handle); \ + pa_handle = NULL; \ + return ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(pa_context_unref); + LOAD_FUNC(pa_sample_spec_valid); + LOAD_FUNC(pa_stream_drop); + LOAD_FUNC(pa_strerror); + LOAD_FUNC(pa_context_get_state); + LOAD_FUNC(pa_stream_get_state); + LOAD_FUNC(pa_threaded_mainloop_signal); + LOAD_FUNC(pa_stream_peek); + LOAD_FUNC(pa_threaded_mainloop_wait); + LOAD_FUNC(pa_threaded_mainloop_unlock); + LOAD_FUNC(pa_threaded_mainloop_in_thread); + LOAD_FUNC(pa_context_new); + LOAD_FUNC(pa_threaded_mainloop_stop); + LOAD_FUNC(pa_context_disconnect); + LOAD_FUNC(pa_threaded_mainloop_start); + LOAD_FUNC(pa_threaded_mainloop_get_api); + LOAD_FUNC(pa_context_set_state_callback); + LOAD_FUNC(pa_stream_write); + LOAD_FUNC(pa_xfree); + LOAD_FUNC(pa_stream_connect_record); + LOAD_FUNC(pa_stream_connect_playback); + LOAD_FUNC(pa_stream_readable_size); + LOAD_FUNC(pa_stream_writable_size); + LOAD_FUNC(pa_stream_cork); + LOAD_FUNC(pa_stream_is_suspended); + LOAD_FUNC(pa_stream_get_device_name); + LOAD_FUNC(pa_path_get_filename); + LOAD_FUNC(pa_get_binary_name); + LOAD_FUNC(pa_threaded_mainloop_free); + LOAD_FUNC(pa_context_errno); + LOAD_FUNC(pa_xmalloc); + LOAD_FUNC(pa_stream_unref); + LOAD_FUNC(pa_threaded_mainloop_accept); + LOAD_FUNC(pa_stream_set_write_callback); + LOAD_FUNC(pa_threaded_mainloop_new); + LOAD_FUNC(pa_context_connect); + LOAD_FUNC(pa_stream_set_buffer_attr); + LOAD_FUNC(pa_stream_get_buffer_attr); + LOAD_FUNC(pa_stream_get_sample_spec); + LOAD_FUNC(pa_stream_get_time); + LOAD_FUNC(pa_stream_set_read_callback); + LOAD_FUNC(pa_stream_set_state_callback); + LOAD_FUNC(pa_stream_set_moved_callback); + LOAD_FUNC(pa_stream_set_underflow_callback); + LOAD_FUNC(pa_stream_new); + LOAD_FUNC(pa_stream_disconnect); + LOAD_FUNC(pa_threaded_mainloop_lock); + LOAD_FUNC(pa_channel_map_init_auto); + LOAD_FUNC(pa_channel_map_parse); + LOAD_FUNC(pa_channel_map_snprint); + LOAD_FUNC(pa_channel_map_equal); + LOAD_FUNC(pa_context_get_server_info); + LOAD_FUNC(pa_context_get_sink_info_by_name); + LOAD_FUNC(pa_context_get_sink_info_list); + LOAD_FUNC(pa_context_get_source_info_list); + LOAD_FUNC(pa_operation_get_state); + LOAD_FUNC(pa_operation_unref); +#undef LOAD_FUNC +#define LOAD_OPTIONAL_FUNC(x) do { \ + p##x = GetSymbol(pa_handle, #x); \ +} while(0) +#if PA_CHECK_VERSION(0,9,15) + LOAD_OPTIONAL_FUNC(pa_channel_map_superset); + LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback); +#endif +#if PA_CHECK_VERSION(0,9,16) + LOAD_OPTIONAL_FUNC(pa_stream_begin_write); +#endif +#undef LOAD_OPTIONAL_FUNC + +#else /* HAVE_DYNLOAD */ + pa_handle = (void*)0xDEADBEEF; +#endif + + 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_TRUE); + if(context) + { + ret = ALC_TRUE; + + 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); + + if(!ret) + { +#ifdef HAVE_DYNLOAD + CloseLib(pa_handle); +#endif + pa_handle = NULL; + } + } + return ret; +} //}}} + +// PulseAudio Event Callbacks //{{{ +static void stream_state_callback(pa_stream *stream, void *pdata) //{{{ +{ + pa_threaded_mainloop *loop = pdata; + pa_stream_state_t state; + + state = pa_stream_get_state(stream); + if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state)) + pa_threaded_mainloop_signal(loop, 0); +}//}}} + +static void stream_signal_callback(pa_stream *stream, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + (void)stream; + + pa_threaded_mainloop_signal(data->loop, 0); +}//}}} + +static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + + LockDevice(Device); + + data->attr = *(pa_stream_get_buffer_attr(stream)); + Device->UpdateSize = data->attr.minreq / data->frame_size; + Device->NumUpdates = (data->attr.tlength/data->frame_size) / Device->UpdateSize; + if(Device->NumUpdates <= 1) + { + Device->NumUpdates = 1; + ERR("PulseAudio returned minreq > tlength/2; expect break up\n"); + } + + UnlockDevice(Device); +}//}}} + +static void stream_device_callback(pa_stream *stream, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + + free(data->device_name); + data->device_name = strdup(pa_stream_get_device_name(stream)); +}//}}} + +static void context_state_callback2(pa_context *context, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + + if(pa_context_get_state(context) == PA_CONTEXT_FAILED) + { + ERR("Received context failure!\n"); + aluHandleDisconnect(Device); + } + pa_threaded_mainloop_signal(data->loop, 0); +}//}}} + +static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + + if(pa_stream_get_state(stream) == PA_STREAM_FAILED) + { + ERR("Received stream failure!\n"); + aluHandleDisconnect(Device); + } + 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_threaded_mainloop_signal(data->loop, 0); +}//}}} + +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) + { + pa_threaded_mainloop_signal(data->loop, 0); + return; + } + + for(i = 0;chanmaps[i].str;i++) + { + pa_channel_map map; + if(!pa_channel_map_parse(&map, chanmaps[i].str)) + continue; + + 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_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 void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{ +{ + pa_threaded_mainloop *loop = pdata; + char str[1024]; + void *temp; + int count; + ALuint i; + + (void)context; + + if(eol) + { + pa_threaded_mainloop_signal(loop, 0); + return; + } + + count = 0; + do { + if(count == 0) + snprintf(str, sizeof(str), "%s", info->description); + else + snprintf(str, sizeof(str), "%s #%d", info->description, count+1); + count++; + + for(i = 0;i < numDevNames;i++) + { + if(strcmp(str, allDevNameMap[i].name) == 0) + break; + } + } while(i != numDevNames); + + temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap)); + if(temp) + { + allDevNameMap = temp; + allDevNameMap[numDevNames].name = strdup(str); + allDevNameMap[numDevNames].device_name = strdup(info->name); + numDevNames++; + } +}//}}} + +static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) //{{{ +{ + pa_threaded_mainloop *loop = pdata; + char str[1024]; + void *temp; + int count; + ALuint i; + + (void)context; + + if(eol) + { + pa_threaded_mainloop_signal(loop, 0); + return; + } + + count = 0; + do { + if(count == 0) + snprintf(str, sizeof(str), "%s", info->description); + else + snprintf(str, sizeof(str), "%s #%d", info->description, count+1); + count++; + + for(i = 0;i < numCaptureDevNames;i++) + { + if(strcmp(str, allCaptureDevNameMap[i].name) == 0) + break; + } + } while(i != numCaptureDevNames); + + temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap)); + if(temp) + { + allCaptureDevNameMap = temp; + allCaptureDevNameMap[numCaptureDevNames].name = strdup(str); + allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name); + numCaptureDevNames++; + } +}//}}} +//}}} + +// PulseAudio I/O Callbacks //{{{ +static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + (void)stream; + (void)len; + + pa_threaded_mainloop_signal(data->loop, 0); +} //}}} +//}}} + +static ALuint PulseProc(ALvoid *param) +{ + ALCdevice *Device = param; + pulse_data *data = Device->ExtraData; + ssize_t len; + + SetRTPriority(); + + pa_threaded_mainloop_lock(data->loop); + do { + len = (Device->Connected ? pa_stream_writable_size(data->stream) : 0); + len -= len%(Device->UpdateSize*data->frame_size); + if(len == 0) + { + pa_threaded_mainloop_wait(data->loop); + continue; + } + + while(len > 0) + { + size_t newlen = len; + void *buf; + pa_free_cb_t free_func = NULL; + +#if PA_CHECK_VERSION(0,9,16) + if(!pa_stream_begin_write || + pa_stream_begin_write(data->stream, &buf, &newlen) < 0) +#endif + { + buf = pa_xmalloc(newlen); + free_func = pa_xfree; + } + pa_threaded_mainloop_unlock(data->loop); + + aluMixData(Device, buf, newlen/data->frame_size); + + pa_threaded_mainloop_lock(data->loop); + pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); + len -= newlen; + } + } while(Device->Connected && !data->killNow); + pa_threaded_mainloop_unlock(data->loop); + + return 0; +} + +static pa_stream *connect_playback_stream(ALCdevice *device, + pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, + pa_channel_map *chanmap) +{ + pulse_data *data = device->ExtraData; + pa_stream_state_t state; + pa_stream *stream; + + stream = pa_stream_new(data->context, "Playback Stream", spec, chanmap); + if(!stream) + { + ERR("pa_stream_new() failed: %s\n", + pa_strerror(pa_context_errno(data->context))); + return NULL; + } + + pa_stream_set_state_callback(stream, stream_state_callback, data->loop); + + if(pa_stream_connect_playback(stream, data->device_name, attr, flags, NULL, NULL) < 0) + { + ERR("Stream did not connect: %s\n", + pa_strerror(pa_context_errno(data->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(data->context))); + pa_stream_unref(stream); + return NULL; + } + + pa_threaded_mainloop_wait(data->loop); + } + pa_stream_set_state_callback(stream, NULL, NULL); + + return stream; +} + +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) + { + allDevNameMap[0].name = strdup(pulse_device); + allDevNameMap[0].device_name = NULL; + numDevNames = 1; + + o = pa_context_get_sink_info_list(context, sink_device_callback, loop); + } + else + { + allCaptureDevNameMap[0].name = strdup(pulse_device); + allCaptureDevNameMap[0].device_name = NULL; + numCaptureDevNames = 1; + + o = pa_context_get_source_info_list(context, source_device_callback, loop); + } + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(loop); + pa_operation_unref(o); + + 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 ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{ +{ + 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); + + device->szDeviceName = strdup(device_name); + + 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_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); + + DestroyRingBuffer(data->ring); + free(data->device_name); + + device->ExtraData = NULL; + pa_xfree(data); +} //}}} +//}}} + +// OpenAL {{{ +static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{ +{ + char *pulse_name = NULL; + pa_sample_spec spec; + pulse_data *data; + + if(!allDevNameMap) + probe_devices(AL_FALSE); + + if(!device_name && numDevNames > 0) + device_name = allDevNameMap[0].name; + else + { + ALuint i; + + for(i = 0;i < numDevNames;i++) + { + if(strcmp(device_name, allDevNameMap[i].name) == 0) + { + pulse_name = allDevNameMap[i].device_name; + break; + } + } + if(i == numDevNames) + return ALC_FALSE; + } + + if(pulse_open(device, device_name) == ALC_FALSE) + return ALC_FALSE; + + data = device->ExtraData; + + pa_threaded_mainloop_lock(data->loop); + + spec.format = PA_SAMPLE_S16NE; + spec.rate = 44100; + spec.channels = 2; + + data->device_name = pulse_name; + pa_stream *stream = connect_playback_stream(device, 0, NULL, &spec, NULL); + if(!stream) + { + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + if(pa_stream_is_suspended(stream)) + { + ERR("Device is suspended\n"); + pa_stream_disconnect(stream); + pa_stream_unref(stream); + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + data->device_name = strdup(pa_stream_get_device_name(stream)); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + + pa_threaded_mainloop_unlock(data->loop); + + return ALC_TRUE; + +fail: + pulse_close(device); + return ALC_FALSE; +} //}}} + +static void pulse_close_playback(ALCdevice *device) //{{{ +{ + pulse_close(device); +} //}}} + +static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + pa_stream_flags_t flags = 0; + pa_channel_map chanmap; + + pa_threaded_mainloop_lock(data->loop); + + 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); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(data->loop); + pa_operation_unref(o); + } + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) + flags |= PA_STREAM_FIX_RATE; + + data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + data->attr.prebuf = -1; + data->attr.fragsize = -1; + data->attr.minreq = device->UpdateSize * data->frame_size; + data->attr.tlength = data->attr.minreq * device->NumUpdates; + if(data->attr.tlength < data->attr.minreq*2) + data->attr.tlength = data->attr.minreq*2; + data->attr.maxlength = data->attr.tlength; + flags |= PA_STREAM_EARLY_REQUESTS; + flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + /* fall-through */ + case DevFmtUByte: + data->spec.format = PA_SAMPLE_U8; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + data->spec.format = PA_SAMPLE_S16NE; + break; + case DevFmtFloat: + data->spec.format = PA_SAMPLE_FLOAT32NE; + break; + } + data->spec.rate = device->Frequency; + data->spec.channels = ChannelsFromDevFmt(device->FmtChans); + + if(pa_sample_spec_valid(&data->spec) == 0) + { + ERR("Invalid sample format\n"); + pa_threaded_mainloop_unlock(data->loop); + return ALC_FALSE; + } + + if(!pa_channel_map_init_auto(&chanmap, data->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); + return ALC_FALSE; + } + SetDefaultWFXChannelOrder(device); + + data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap); + if(!data->stream) + { + pa_threaded_mainloop_unlock(data->loop); + return ALC_FALSE; + } + + pa_stream_set_state_callback(data->stream, stream_state_callback2, device); + + data->spec = *(pa_stream_get_sample_spec(data->stream)); + if(device->Frequency != data->spec.rate) + { + pa_operation *o; + + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + + /* Server updated our playback rate, so modify the buffer attribs + * accordingly. */ + data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) * + data->spec.rate / device->Frequency * data->frame_size; + data->attr.tlength = data->attr.minreq * device->NumUpdates; + data->attr.maxlength = data->attr.tlength; + + o = pa_stream_set_buffer_attr(data->stream, &data->attr, + stream_success_callback, device); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(data->loop); + pa_operation_unref(o); + + device->Frequency = data->spec.rate; + } + + stream_buffer_attr_callback(data->stream, 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); +#endif + pa_stream_set_moved_callback(data->stream, stream_device_callback, device); + pa_stream_set_write_callback(data->stream, stream_write_callback, device); + pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device); + + data->thread = StartThread(PulseProc, device); + if(!data->thread) + { +#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_set_moved_callback(data->stream, NULL, NULL); + pa_stream_set_write_callback(data->stream, NULL, NULL); + pa_stream_set_underflow_callback(data->stream, NULL, NULL); + pa_stream_disconnect(data->stream); + pa_stream_unref(data->stream); + data->stream = NULL; + + pa_threaded_mainloop_unlock(data->loop); + return ALC_FALSE; + } + + pa_threaded_mainloop_unlock(data->loop); + return ALC_TRUE; +} //}}} + +static void pulse_stop_playback(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + + if(!data->stream) + return; + + data->killNow = AL_TRUE; + if(data->thread) + { + pa_threaded_mainloop_signal(data->loop, 0); + StopThread(data->thread); + data->thread = NULL; + } + data->killNow = AL_FALSE; + + pa_threaded_mainloop_lock(data->loop); + +#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_set_moved_callback(data->stream, NULL, NULL); + pa_stream_set_write_callback(data->stream, NULL, NULL); + pa_stream_set_underflow_callback(data->stream, NULL, NULL); + pa_stream_disconnect(data->stream); + pa_stream_unref(data->stream); + data->stream = NULL; + + pa_threaded_mainloop_unlock(data->loop); +} //}}} + + +static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{ +{ + char *pulse_name = NULL; + pulse_data *data; + pa_stream_flags_t flags = 0; + pa_stream_state_t state; + pa_channel_map chanmap; + + if(!allCaptureDevNameMap) + probe_devices(AL_TRUE); + + if(!device_name && numCaptureDevNames > 0) + device_name = allCaptureDevNameMap[0].name; + else + { + ALuint i; + + for(i = 0;i < numCaptureDevNames;i++) + { + if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) + { + pulse_name = allCaptureDevNameMap[i].device_name; + break; + } + } + if(i == numCaptureDevNames) + return ALC_FALSE; + } + + if(pulse_open(device, device_name) == ALC_FALSE) + return ALC_FALSE; + + data = device->ExtraData; + pa_threaded_mainloop_lock(data->loop); + + data->samples = device->UpdateSize * device->NumUpdates; + data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + if(data->samples < 100 * device->Frequency / 1000) + data->samples = 100 * device->Frequency / 1000; + + if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples))) + { + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + data->attr.minreq = -1; + data->attr.prebuf = -1; + data->attr.maxlength = data->samples * data->frame_size; + data->attr.tlength = -1; + data->attr.fragsize = minu(data->samples, 50*device->Frequency/1000) * + data->frame_size; + + data->spec.rate = device->Frequency; + data->spec.channels = ChannelsFromDevFmt(device->FmtChans); + + switch(device->FmtType) + { + case DevFmtUByte: + data->spec.format = PA_SAMPLE_U8; + break; + case DevFmtShort: + data->spec.format = PA_SAMPLE_S16NE; + break; + case DevFmtFloat: + data->spec.format = PA_SAMPLE_FLOAT32NE; + break; + case DevFmtByte: + case DevFmtUShort: + ERR("Capture format type %#x capture not supported on PulseAudio\n", device->FmtType); + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + if(pa_sample_spec_valid(&data->spec) == 0) + { + ERR("Invalid sample format\n"); + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + if(!pa_channel_map_init_auto(&chanmap, data->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); + goto fail; + } + + data->stream = pa_stream_new(data->context, "Capture Stream", &data->spec, &chanmap); + if(!data->stream) + { + ERR("pa_stream_new() failed: %s\n", + pa_strerror(pa_context_errno(data->context))); + + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + pa_stream_set_state_callback(data->stream, stream_state_callback, data->loop); + + flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; + if(pa_stream_connect_record(data->stream, pulse_name, &data->attr, flags) < 0) + { + ERR("Stream did not connect: %s\n", + pa_strerror(pa_context_errno(data->context))); + + pa_stream_unref(data->stream); + data->stream = NULL; + + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + while((state=pa_stream_get_state(data->stream)) != PA_STREAM_READY) + { + if(!PA_STREAM_IS_GOOD(state)) + { + ERR("Stream did not get ready: %s\n", + pa_strerror(pa_context_errno(data->context))); + + pa_stream_unref(data->stream); + data->stream = NULL; + + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + pa_threaded_mainloop_wait(data->loop); + } + pa_stream_set_state_callback(data->stream, stream_state_callback2, device); + + pa_threaded_mainloop_unlock(data->loop); + return ALC_TRUE; + +fail: + pulse_close(device); + return ALC_FALSE; +} //}}} + +static void pulse_close_capture(ALCdevice *device) //{{{ +{ + pulse_close(device); +} //}}} + +static void pulse_start_capture(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + pa_operation *o; + + pa_threaded_mainloop_lock(data->loop); + o = pa_stream_cork(data->stream, 0, stream_success_callback, device); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(data->loop); + pa_operation_unref(o); + pa_threaded_mainloop_unlock(data->loop); +} //}}} + +static void pulse_stop_capture(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + pa_operation *o; + + pa_threaded_mainloop_lock(data->loop); + o = pa_stream_cork(data->stream, 1, stream_success_callback, device); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(data->loop); + pa_operation_unref(o); + pa_threaded_mainloop_unlock(data->loop); +} //}}} + +static ALCuint pulse_available_samples(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + size_t samples; + + pa_threaded_mainloop_lock(data->loop); + /* Capture is done in fragment-sized chunks, so we loop until we get all + * that's available */ + samples = (device->Connected ? pa_stream_readable_size(data->stream) : 0); + while(samples > 0) + { + const void *buf; + size_t length; + + if(pa_stream_peek(data->stream, &buf, &length) < 0) + { + ERR("pa_stream_peek() failed: %s\n", + pa_strerror(pa_context_errno(data->context))); + break; + } + + WriteRingBuffer(data->ring, buf, length/data->frame_size); + samples -= length; + + pa_stream_drop(data->stream); + } + pa_threaded_mainloop_unlock(data->loop); + + return RingBufferSize(data->ring); +} //}}} + +static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{ +{ + pulse_data *data = device->ExtraData; + + if(pulse_available_samples(device) >= samples) + ReadRingBuffer(data->ring, buffer, samples); + else + alcSetError(device, ALC_INVALID_VALUE); +} //}}} + + +static const BackendFuncs pulse_funcs = { //{{{ + pulse_open_playback, + pulse_close_playback, + pulse_reset_playback, + pulse_stop_playback, + pulse_open_capture, + pulse_close_capture, + pulse_start_capture, + pulse_stop_capture, + pulse_capture_samples, + pulse_available_samples +}; //}}} + +ALCboolean alc_pulse_init(BackendFuncs *func_list) //{{{ +{ + if(!pulse_load()) + return ALC_FALSE; + + *func_list = pulse_funcs; + + pulse_ctx_flags = 0; + if(!GetConfigValueBool("pulse", "spawn-server", 0)) + pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; + + return ALC_TRUE; +} //}}} + +void alc_pulse_deinit(void) //{{{ +{ + ALuint i; + + for(i = 0;i < numDevNames;++i) + { + free(allDevNameMap[i].name); + free(allDevNameMap[i].device_name); + } + free(allDevNameMap); + allDevNameMap = NULL; + numDevNames = 0; + + for(i = 0;i < numCaptureDevNames;++i) + { + free(allCaptureDevNameMap[i].name); + free(allCaptureDevNameMap[i].device_name); + } + free(allCaptureDevNameMap); + allCaptureDevNameMap = NULL; + numCaptureDevNames = 0; + +#ifdef HAVE_DYNLOAD + if(pa_handle) + CloseLib(pa_handle); + pa_handle = NULL; +#endif +} //}}} + +void alc_pulse_probe(enum DevProbe type) //{{{ +{ + pa_threaded_mainloop *loop; + ALuint i; + + switch(type) + { + case DEVICE_PROBE: + 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) + { + AppendDeviceList(pulse_device); + + 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); + break; + + case ALL_DEVICE_PROBE: + for(i = 0;i < numDevNames;++i) + { + free(allDevNameMap[i].name); + free(allDevNameMap[i].device_name); + } + free(allDevNameMap); + allDevNameMap = NULL; + numDevNames = 0; + + probe_devices(AL_FALSE); + + for(i = 0;i < numDevNames;i++) + AppendAllDeviceList(allDevNameMap[i].name); + break; + + case CAPTURE_DEVICE_PROBE: + for(i = 0;i < numCaptureDevNames;++i) + { + free(allCaptureDevNameMap[i].name); + free(allCaptureDevNameMap[i].device_name); + } + free(allCaptureDevNameMap); + allCaptureDevNameMap = NULL; + numCaptureDevNames = 0; + + probe_devices(AL_TRUE); + + for(i = 0;i < numCaptureDevNames;i++) + AppendCaptureDeviceList(allCaptureDevNameMap[i].name); + break; + } +} //}}} +//}}} diff --git a/Alc/backends/sndio.c b/Alc/backends/sndio.c new file mode 100644 index 00000000..5f9ad0cd --- /dev/null +++ b/Alc/backends/sndio.c @@ -0,0 +1,381 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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 +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include + + +static const ALCchar sndio_device[] = "SndIO Default"; + + +static void *sndio_handle; +#ifdef HAVE_DYNLOAD +#define MAKE_FUNC(x) static typeof(x) * p##x +MAKE_FUNC(sio_initpar); +MAKE_FUNC(sio_open); +MAKE_FUNC(sio_close); +MAKE_FUNC(sio_setpar); +MAKE_FUNC(sio_getpar); +MAKE_FUNC(sio_getcap); +MAKE_FUNC(sio_onmove); +MAKE_FUNC(sio_write); +MAKE_FUNC(sio_read); +MAKE_FUNC(sio_start); +MAKE_FUNC(sio_stop); +MAKE_FUNC(sio_nfds); +MAKE_FUNC(sio_pollfd); +MAKE_FUNC(sio_revents); +MAKE_FUNC(sio_eof); +MAKE_FUNC(sio_setvol); +MAKE_FUNC(sio_onvol); + +#define sio_initpar psio_initpar +#define sio_open psio_open +#define sio_close psio_close +#define sio_setpar psio_setpar +#define sio_getpar psio_getpar +#define sio_getcap psio_getcap +#define sio_onmove psio_onmove +#define sio_write psio_write +#define sio_read psio_read +#define sio_start psio_start +#define sio_stop psio_stop +#define sio_nfds psio_nfds +#define sio_pollfd psio_pollfd +#define sio_revents psio_revents +#define sio_eof psio_eof +#define sio_setvol psio_setvol +#define sio_onvol psio_onvol +#endif + + +static ALCboolean sndio_load(void) +{ + if(!sndio_handle) + { +#ifdef HAVE_DYNLOAD + sndio_handle = LoadLib("libsndio.so"); + if(!sndio_handle) + return ALC_FALSE; + +#define LOAD_FUNC(f) do { \ + p##f = GetSymbol(sndio_handle, #f); \ + if(p##f == NULL) { \ + CloseLib(sndio_handle); \ + sndio_handle = NULL; \ + return ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(sio_initpar); + LOAD_FUNC(sio_open); + LOAD_FUNC(sio_close); + LOAD_FUNC(sio_setpar); + LOAD_FUNC(sio_getpar); + LOAD_FUNC(sio_getcap); + LOAD_FUNC(sio_onmove); + LOAD_FUNC(sio_write); + LOAD_FUNC(sio_read); + LOAD_FUNC(sio_start); + LOAD_FUNC(sio_stop); + LOAD_FUNC(sio_nfds); + LOAD_FUNC(sio_pollfd); + LOAD_FUNC(sio_revents); + LOAD_FUNC(sio_eof); + LOAD_FUNC(sio_setvol); + LOAD_FUNC(sio_onvol); +#undef LOAD_FUNC +#else + sndio_handle = (void*)0xDEADBEEF; +#endif + } + return ALC_TRUE; +} + + +typedef struct { + struct sio_hdl *sndHandle; + + ALvoid *mix_data; + ALsizei data_size; + + volatile int killNow; + ALvoid *thread; +} sndio_data; + + +static ALuint sndio_proc(ALvoid *ptr) +{ + ALCdevice *device = ptr; + sndio_data *data = device->ExtraData; + ALsizei frameSize; + size_t wrote; + + SetRTPriority(); + + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + while(!data->killNow && device->Connected) + { + ALsizei len = data->data_size; + ALubyte *WritePtr = data->mix_data; + + aluMixData(device, WritePtr, len/frameSize); + while(len > 0 && !data->killNow) + { + wrote = sio_write(data->sndHandle, WritePtr, len); + if(wrote == 0) + { + ERR("sio_write failed\n"); + aluHandleDisconnect(device); + break; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + + + +static ALCboolean sndio_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + sndio_data *data; + + if(!deviceName) + deviceName = sndio_device; + else if(strcmp(deviceName, sndio_device) != 0) + return ALC_FALSE; + + data = calloc(1, sizeof(*data)); + data->killNow = 0; + + data->sndHandle = sio_open(NULL, SIO_PLAY, 0); + if(data->sndHandle == NULL) + { + free(data); + ERR("Could not open device\n"); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + + return ALC_TRUE; +} + +static void sndio_close_playback(ALCdevice *device) +{ + sndio_data *data = device->ExtraData; + + sio_close(data->sndHandle); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean sndio_reset_playback(ALCdevice *device) +{ + sndio_data *data = device->ExtraData; + struct sio_par par; + + sio_initpar(&par); + + par.rate = device->Frequency; + + par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1); + + switch(device->FmtType) + { + case DevFmtByte: + par.bits = 8; + par.sig = 1; + break; + case DevFmtUByte: + par.bits = 8; + par.sig = 0; + break; + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + par.bits = 16; + par.sig = 1; + break; + case DevFmtUShort: + par.bits = 16; + par.sig = 0; + break; + } + par.le = SIO_LE_NATIVE; + + par.round = device->UpdateSize; + par.appbufsz = device->UpdateSize * (device->NumUpdates-1); + if(!par.appbufsz) par.appbufsz = device->UpdateSize; + + + if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par)) + { + ERR("Failed to set device parameters\n"); + return ALC_FALSE; + } + + if(par.rate != device->Frequency) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set frequency %uhz, got %uhz instead\n", device->Frequency, par.rate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = par.rate; + } + + if(par.pchan != ChannelsFromDevFmt(device->FmtChans)) + { + if(par.pchan != 1 && par.pchan != 2) + { + ERR("Unhandled channel count: %u\n", par.pchan); + return ALC_FALSE; + } + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), par.pchan); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo); + } + + if(par.bits != par.bps*8) + { + ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8); + return ALC_FALSE; + } + + if(par.bits == 8 && par.sig == 1) + device->FmtType = DevFmtByte; + else if(par.bits == 8 && par.sig == 0) + device->FmtType = DevFmtUByte; + else if(par.bits == 16 && par.sig == 1) + device->FmtType = DevFmtShort; + else if(par.bits == 16 && par.sig == 0) + device->FmtType = DevFmtUShort; + else + { + ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits); + return ALC_FALSE; + } + + + device->UpdateSize = par.round; + device->NumUpdates = (par.bufsz/par.round) + 1; + + SetDefaultChannelOrder(device); + + + if(!sio_start(data->sndHandle)) + { + ERR("Error starting playback\n"); + return ALC_FALSE; + } + + data->data_size = device->UpdateSize * par.bps * par.pchan; + data->mix_data = calloc(1, data->data_size); + + data->thread = StartThread(sndio_proc, device); + if(data->thread == NULL) + { + sio_stop(data->sndHandle); + free(data->mix_data); + data->mix_data = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void sndio_stop_playback(ALCdevice *device) +{ + sndio_data *data = device->ExtraData; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + if(!sio_stop(data->sndHandle)) + ERR("Error stopping device\n"); + + free(data->mix_data); + data->mix_data = NULL; +} + + +static const BackendFuncs sndio_funcs = { + sndio_open_playback, + sndio_close_playback, + sndio_reset_playback, + sndio_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_sndio_init(BackendFuncs *func_list) +{ + if(!sndio_load()) + return ALC_FALSE; + *func_list = sndio_funcs; + return ALC_TRUE; +} + +void alc_sndio_deinit(void) +{ +#ifdef HAVE_DYNLOAD + if(sndio_handle) + CloseLib(sndio_handle); + sndio_handle = NULL; +#endif +} + +void alc_sndio_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(sndio_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(sndio_device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/Alc/backends/solaris.c b/Alc/backends/solaris.c new file mode 100644 index 00000000..b2b8196d --- /dev/null +++ b/Alc/backends/solaris.c @@ -0,0 +1,282 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include + + +static const ALCchar solaris_device[] = "Solaris Default"; + +typedef struct { + int fd; + volatile int killNow; + ALvoid *thread; + + ALubyte *mix_data; + int data_size; +} solaris_data; + + +static ALuint SolarisProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + solaris_data *data = (solaris_data*)pDevice->ExtraData; + ALint frameSize; + int wrote; + + SetRTPriority(); + + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + while(!data->killNow && pDevice->Connected) + { + ALint len = data->data_size; + ALubyte *WritePtr = data->mix_data; + + aluMixData(pDevice, WritePtr, len/frameSize); + while(len > 0 && !data->killNow) + { + wrote = write(data->fd, WritePtr, len); + if(wrote < 0) + { + if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + { + ERR("write failed: %s\n", strerror(errno)); + aluHandleDisconnect(pDevice); + break; + } + + Sleep(1); + continue; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + + +static ALCboolean solaris_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + char driver[64]; + solaris_data *data; + + strncpy(driver, GetConfigValue("solaris", "device", "/dev/audio"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + + if(!deviceName) + deviceName = solaris_device; + else if(strcmp(deviceName, solaris_device) != 0) + return ALC_FALSE; + + data = (solaris_data*)calloc(1, sizeof(solaris_data)); + data->killNow = 0; + + data->fd = open(driver, O_WRONLY); + if(data->fd == -1) + { + free(data); + ERR("Could not open %s: %s\n", driver, strerror(errno)); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void solaris_close_playback(ALCdevice *device) +{ + solaris_data *data = (solaris_data*)device->ExtraData; + + close(data->fd); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean solaris_reset_playback(ALCdevice *device) +{ + solaris_data *data = (solaris_data*)device->ExtraData; + audio_info_t info; + ALuint frameSize; + int numChannels; + + AUDIO_INITINFO(&info); + + info.play.sample_rate = device->Frequency; + + if(device->FmtChans != DevFmtMono) + device->FmtChans = DevFmtStereo; + numChannels = ChannelsFromDevFmt(device->FmtChans); + info.play.channels = numChannels; + + switch(device->FmtType) + { + case DevFmtByte: + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_LINEAR; + break; + case DevFmtUByte: + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_LINEAR8; + break; + case DevFmtUShort: + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + info.play.precision = 16; + info.play.encoding = AUDIO_ENCODING_LINEAR; + break; + } + + frameSize = numChannels * BytesFromDevFmt(device->FmtType); + info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize; + + if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0) + { + ERR("ioctl failed: %s\n", strerror(errno)); + return ALC_FALSE; + } + + if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels) + { + ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels); + return ALC_FALSE; + } + + if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && + device->FmtType == DevFmtByte) || + (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && + device->FmtType == DevFmtUByte) || + (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && + device->FmtType == DevFmtShort))) + { + ERR("Could not set %#x sample type, got %d (%#x)\n", + device->FmtType, info.play.precision, info.play.encoding); + return ALC_FALSE; + } + + if(device->Frequency != info.play.sample_rate) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set requested frequency %dhz, got %dhz instead\n", device->Frequency, info.play.sample_rate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = info.play.sample_rate; + } + device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1; + + data->data_size = device->UpdateSize * frameSize; + data->mix_data = calloc(1, data->data_size); + + SetDefaultChannelOrder(device); + + data->thread = StartThread(SolarisProc, device); + if(data->thread == NULL) + { + free(data->mix_data); + data->mix_data = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void solaris_stop_playback(ALCdevice *device) +{ + solaris_data *data = (solaris_data*)device->ExtraData; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + if(ioctl(data->fd, AUDIO_DRAIN) < 0) + ERR("Error draining device: %s\n", strerror(errno)); + + free(data->mix_data); + data->mix_data = NULL; +} + + +static const BackendFuncs solaris_funcs = { + solaris_open_playback, + solaris_close_playback, + solaris_reset_playback, + solaris_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_solaris_init(BackendFuncs *func_list) +{ + *func_list = solaris_funcs; + return ALC_TRUE; +} + +void alc_solaris_deinit(void) +{ +} + +void alc_solaris_probe(enum DevProbe type) +{ +#ifdef HAVE_STAT + struct stat buf; + if(stat(GetConfigValue("solaris", "device", "/dev/audio"), &buf) != 0) + return; +#endif + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(solaris_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(solaris_device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/Alc/backends/wave.c b/Alc/backends/wave.c new file mode 100644 index 00000000..465087ce --- /dev/null +++ b/Alc/backends/wave.c @@ -0,0 +1,355 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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 +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + FILE *f; + long DataStart; + + ALvoid *buffer; + ALuint size; + + volatile int killNow; + ALvoid *thread; +} wave_data; + + +static const ALCchar waveDevice[] = "Wave File Writer"; + +static const ALubyte SUBTYPE_PCM[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, + 0x00, 0x38, 0x9b, 0x71 +}; +static const ALubyte SUBTYPE_FLOAT[] = { + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, + 0x00, 0x38, 0x9b, 0x71 +}; + +static const ALuint channel_masks[] = { + 0, /* invalid */ + 0x4, /* Mono */ + 0x1 | 0x2, /* Stereo */ + 0, /* 3 channel */ + 0x1 | 0x2 | 0x10 | 0x20, /* Quad */ + 0, /* 5 channel */ + 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */ + 0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */ + 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */ +}; + + +static void fwrite16le(ALushort val, FILE *f) +{ + fputc(val&0xff, f); + fputc((val>>8)&0xff, f); +} + +static void fwrite32le(ALuint val, FILE *f) +{ + fputc(val&0xff, f); + fputc((val>>8)&0xff, f); + fputc((val>>16)&0xff, f); + fputc((val>>24)&0xff, f); +} + + +static ALuint WaveProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + wave_data *data = (wave_data*)pDevice->ExtraData; + ALuint frameSize; + ALuint now, start; + ALuint64 avail, done; + size_t fs; + union { + short s; + char b[sizeof(short)]; + } uSB; + const ALuint restTime = (ALuint64)pDevice->UpdateSize * 1000 / + pDevice->Frequency / 2; + + uSB.s = 1; + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + done = 0; + start = timeGetTime(); + while(!data->killNow && pDevice->Connected) + { + now = timeGetTime(); + + avail = (ALuint64)(now-start) * pDevice->Frequency / 1000; + if(avail < done) + { + /* Timer wrapped. Add the remainder of the cycle to the available + * count and reset the number of samples done */ + avail += (ALuint64)0xFFFFFFFFu*pDevice->Frequency/1000 - done; + done = 0; + } + if(avail-done < pDevice->UpdateSize) + { + Sleep(restTime); + continue; + } + + while(avail-done >= pDevice->UpdateSize) + { + aluMixData(pDevice, data->buffer, pDevice->UpdateSize); + done += pDevice->UpdateSize; + + if(uSB.b[0] != 1) + { + ALuint bytesize = BytesFromDevFmt(pDevice->FmtType); + ALubyte *bytes = data->buffer; + ALuint i; + + if(bytesize == 1) + { + for(i = 0;i < data->size;i++) + fputc(bytes[i], data->f); + } + else if(bytesize == 2) + { + for(i = 0;i < data->size;i++) + fputc(bytes[i^1], data->f); + } + else if(bytesize == 4) + { + for(i = 0;i < data->size;i++) + fputc(bytes[i^3], data->f); + } + } + else + fs = fwrite(data->buffer, frameSize, pDevice->UpdateSize, + data->f); + if(ferror(data->f)) + { + ERR("Error writing to file\n"); + aluHandleDisconnect(pDevice); + break; + } + } + } + + return 0; +} + +static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + wave_data *data; + const char *fname; + + fname = GetConfigValue("wave", "file", ""); + if(!fname[0]) + return ALC_FALSE; + + if(!deviceName) + deviceName = waveDevice; + else if(strcmp(deviceName, waveDevice) != 0) + return ALC_FALSE; + + data = (wave_data*)calloc(1, sizeof(wave_data)); + + data->f = fopen(fname, "wb"); + if(!data->f) + { + free(data); + ERR("Could not open file '%s': %s\n", fname, strerror(errno)); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void wave_close_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + + fclose(data->f); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean wave_reset_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + ALuint channels=0, bits=0; + size_t val; + + fseek(data->f, 0, SEEK_SET); + clearerr(data->f); + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + break; + case DevFmtUByte: + case DevFmtShort: + case DevFmtFloat: + break; + } + bits = BytesFromDevFmt(device->FmtType) * 8; + channels = ChannelsFromDevFmt(device->FmtChans); + + fprintf(data->f, "RIFF"); + fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close + + fprintf(data->f, "WAVE"); + + fprintf(data->f, "fmt "); + fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE + + // 16-bit val, format type id (extensible: 0xFFFE) + fwrite16le(0xFFFE, data->f); + // 16-bit val, channel count + fwrite16le(channels, data->f); + // 32-bit val, frequency + fwrite32le(device->Frequency, data->f); + // 32-bit val, bytes per second + fwrite32le(device->Frequency * channels * bits / 8, data->f); + // 16-bit val, frame size + fwrite16le(channels * bits / 8, data->f); + // 16-bit val, bits per sample + fwrite16le(bits, data->f); + // 16-bit val, extra byte count + fwrite16le(22, data->f); + // 16-bit val, valid bits per sample + fwrite16le(bits, data->f); + // 32-bit val, channel mask + fwrite32le(channel_masks[channels], data->f); + // 16 byte GUID, sub-type format + val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f); + + fprintf(data->f, "data"); + fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close + + if(ferror(data->f)) + { + ERR("Error writing header: %s\n", strerror(errno)); + return ALC_FALSE; + } + + data->DataStart = ftell(data->f); + + data->size = device->UpdateSize * channels * bits / 8; + data->buffer = malloc(data->size); + if(!data->buffer) + { + ERR("Buffer malloc failed\n"); + return ALC_FALSE; + } + + SetDefaultWFXChannelOrder(device); + + data->thread = StartThread(WaveProc, device); + if(data->thread == NULL) + { + free(data->buffer); + data->buffer = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void wave_stop_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + ALuint dataLen; + long size; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + + free(data->buffer); + data->buffer = NULL; + + size = ftell(data->f); + if(size > 0) + { + dataLen = size - data->DataStart; + if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0) + fwrite32le(dataLen, data->f); // 'data' header len + if(fseek(data->f, 4, SEEK_SET) == 0) + fwrite32le(size-8, data->f); // 'WAVE' header len + } +} + + +static const BackendFuncs wave_funcs = { + wave_open_playback, + wave_close_playback, + wave_reset_playback, + wave_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_wave_init(BackendFuncs *func_list) +{ + *func_list = wave_funcs; + return ALC_TRUE; +} + +void alc_wave_deinit(void) +{ +} + +void alc_wave_probe(enum DevProbe type) +{ + if(!ConfigValueExists("wave", "file")) + return; + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(waveDevice); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(waveDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/Alc/backends/winmm.c b/Alc/backends/winmm.c new file mode 100644 index 00000000..445dd51e --- /dev/null +++ b/Alc/backends/winmm.c @@ -0,0 +1,780 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 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" + +#define _WIN32_WINNT 0x0500 +#include +#include +#include + +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + // MMSYSTEM Device + volatile ALboolean bWaveShutdown; + HANDLE hWaveThreadEvent; + HANDLE hWaveThread; + DWORD ulWaveThreadID; + LONG lWaveBuffersCommitted; + WAVEHDR WaveBuffer[4]; + + union { + HWAVEIN In; + HWAVEOUT Out; + } hWaveHandle; + + ALuint Frequency; + + RingBuffer *pRing; +} WinMMData; + + +static const ALCchar woDefault[] = "WaveOut Default"; + +static ALCchar **PlaybackDeviceList; +static ALuint NumPlaybackDevices; +static ALCchar **CaptureDeviceList; +static ALuint NumCaptureDevices; + + +static void ProbePlaybackDevices(void) +{ + ALuint i; + + for(i = 0;i < NumPlaybackDevices;i++) + free(PlaybackDeviceList[i]); + + NumPlaybackDevices = waveOutGetNumDevs(); + PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices); + for(i = 0;i < NumPlaybackDevices;i++) + { + WAVEOUTCAPS WaveCaps; + + PlaybackDeviceList[i] = NULL; + if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) + { + char name[1024]; + ALuint count, j; + + count = 0; + do { + if(count == 0) + snprintf(name, sizeof(name), "%s", WaveCaps.szPname); + else + snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1); + count++; + + for(j = 0;j < i;j++) + { + if(strcmp(name, PlaybackDeviceList[j]) == 0) + break; + } + } while(j != i); + + PlaybackDeviceList[i] = strdup(name); + } + } +} + +static void ProbeCaptureDevices(void) +{ + ALuint i; + + for(i = 0;i < NumCaptureDevices;i++) + free(CaptureDeviceList[i]); + + NumCaptureDevices = waveInGetNumDevs(); + CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices); + for(i = 0;i < NumCaptureDevices;i++) + { + WAVEINCAPS WaveInCaps; + + CaptureDeviceList[i] = NULL; + if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) + { + char name[1024]; + ALuint count, j; + + count = 0; + do { + if(count == 0) + snprintf(name, sizeof(name), "%s", WaveInCaps.szPname); + else + snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1); + count++; + + for(j = 0;j < i;j++) + { + if(strcmp(name, CaptureDeviceList[j]) == 0) + break; + } + } while(j != i); + + CaptureDeviceList[i] = strdup(name); + } + } +} + + +/* + WaveOutProc + + Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and + returns to the application (for more data) +*/ +static void CALLBACK WaveOutProc(HWAVEOUT hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) +{ + ALCdevice *pDevice = (ALCdevice*)dwInstance; + WinMMData *pData = pDevice->ExtraData; + + (void)hDevice; + (void)dwParam2; + + if(uMsg != WOM_DONE) + return; + + // Decrement number of buffers in use + InterlockedDecrement(&pData->lWaveBuffersCommitted); + + if(pData->bWaveShutdown == AL_FALSE) + { + // Notify Wave Processor Thread that a Wave Header has returned + PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1); + } + else + { + if(pData->lWaveBuffersCommitted == 0) + { + // Post 'Quit' Message to WaveOut Processor Thread + PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0); + } + } +} + +/* + PlaybackThreadProc + + Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its + audio data. +*/ +static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter) +{ + ALCdevice *pDevice = (ALCdevice*)lpParameter; + WinMMData *pData = pDevice->ExtraData; + LPWAVEHDR pWaveHdr; + ALuint FrameSize; + MSG msg; + + FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + SetRTPriority(); + + while(GetMessage(&msg, NULL, 0, 0)) + { + if(msg.message != WOM_DONE || pData->bWaveShutdown) + continue; + + pWaveHdr = ((LPWAVEHDR)msg.lParam); + + aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize); + + // Send buffer back to play more data + waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + // Signal Wave Thread completed event + if(pData->hWaveThreadEvent) + SetEvent(pData->hWaveThreadEvent); + + ExitThread(0); + + return 0; +} + +/* + WaveInProc + + Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and + returns to the application (with more data) +*/ +static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) +{ + ALCdevice *pDevice = (ALCdevice*)dwInstance; + WinMMData *pData = pDevice->ExtraData; + + (void)hDevice; + (void)dwParam2; + + if(uMsg != WIM_DATA) + return; + + // Decrement number of buffers in use + InterlockedDecrement(&pData->lWaveBuffersCommitted); + + if(pData->bWaveShutdown == AL_FALSE) + { + // Notify Wave Processor Thread that a Wave Header has returned + PostThreadMessage(pData->ulWaveThreadID,uMsg,0,dwParam1); + } + else + { + if(pData->lWaveBuffersCommitted == 0) + { + // Post 'Quit' Message to WaveIn Processor Thread + PostThreadMessage(pData->ulWaveThreadID,WM_QUIT,0,0); + } + } +} + +/* + CaptureThreadProc + + Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new + audio data. +*/ +static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter) +{ + ALCdevice *pDevice = (ALCdevice*)lpParameter; + WinMMData *pData = pDevice->ExtraData; + LPWAVEHDR pWaveHdr; + ALuint FrameSize; + MSG msg; + + FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + while(GetMessage(&msg, NULL, 0, 0)) + { + if(msg.message != WIM_DATA || pData->bWaveShutdown) + continue; + + pWaveHdr = ((LPWAVEHDR)msg.lParam); + + WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData, + pWaveHdr->dwBytesRecorded/FrameSize); + + // Send buffer back to capture more data + waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + // Signal Wave Thread completed event + if(pData->hWaveThreadEvent) + SetEvent(pData->hWaveThreadEvent); + + ExitThread(0); + + return 0; +} + + +static ALCboolean WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName) +{ + WAVEFORMATEX wfexFormat; + WinMMData *pData = NULL; + UINT lDeviceID = 0; + MMRESULT res; + ALuint i = 0; + + // Find the Device ID matching the deviceName if valid + if(!deviceName || strcmp(deviceName, woDefault) == 0) + lDeviceID = WAVE_MAPPER; + else + { + if(!PlaybackDeviceList) + ProbePlaybackDevices(); + + for(i = 0;i < NumPlaybackDevices;i++) + { + if(PlaybackDeviceList[i] && + strcmp(deviceName, PlaybackDeviceList[i]) == 0) + { + lDeviceID = i; + break; + } + } + if(i == NumPlaybackDevices) + return ALC_FALSE; + } + + pData = calloc(1, sizeof(*pData)); + if(!pData) + { + alcSetError(pDevice, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + pDevice->ExtraData = pData; + + if(pDevice->FmtChans != DevFmtMono) + { + if((pDevice->Flags&DEVICE_CHANNELS_REQUEST) && + pDevice->FmtChans != DevFmtStereo) + { + ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice->FmtChans)); + pDevice->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + pDevice->FmtChans = DevFmtStereo; + } + switch(pDevice->FmtType) + { + case DevFmtByte: + pDevice->FmtType = DevFmtUByte; + break; + case DevFmtUShort: + case DevFmtFloat: + pDevice->FmtType = DevFmtShort; + break; + case DevFmtUByte: + case DevFmtShort: + break; + } + + memset(&wfexFormat, 0, sizeof(WAVEFORMATEX)); + wfexFormat.wFormatTag = WAVE_FORMAT_PCM; + wfexFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans); + wfexFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8; + wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample * + wfexFormat.nChannels / 8; + wfexFormat.nSamplesPerSec = pDevice->Frequency; + wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec * + wfexFormat.nBlockAlign; + wfexFormat.cbSize = 0; + + if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) + { + ERR("waveOutOpen failed: %u\n", res); + goto failure; + } + + pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(pData->hWaveThreadEvent == NULL) + { + ERR("CreateEvent failed: %lu\n", GetLastError()); + goto failure; + } + + pData->Frequency = pDevice->Frequency; + + pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault : + PlaybackDeviceList[lDeviceID]); + return ALC_TRUE; + +failure: + if(pData->hWaveThreadEvent) + CloseHandle(pData->hWaveThreadEvent); + + if(pData->hWaveHandle.Out) + waveOutClose(pData->hWaveHandle.Out); + + free(pData); + pDevice->ExtraData = NULL; + return ALC_FALSE; +} + +static void WinMMClosePlayback(ALCdevice *device) +{ + WinMMData *pData = (WinMMData*)device->ExtraData; + + // Close the Wave device + CloseHandle(pData->hWaveThreadEvent); + pData->hWaveThreadEvent = 0; + + waveOutClose(pData->hWaveHandle.Out); + pData->hWaveHandle.Out = 0; + + free(pData); + device->ExtraData = NULL; +} + +static ALCboolean WinMMResetPlayback(ALCdevice *device) +{ + WinMMData *pData = (WinMMData*)device->ExtraData; + ALbyte *BufferData; + ALint lBufferSize; + ALuint i; + + pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID); + if(pData->hWaveThread == NULL) + return ALC_FALSE; + + device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * + pData->Frequency / device->Frequency); + if(device->Frequency != pData->Frequency) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("WinMM does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, pData->Frequency); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = pData->Frequency; + } + + SetDefaultWFXChannelOrder(device); + + pData->lWaveBuffersCommitted = 0; + + // Create 4 Buffers + lBufferSize = device->UpdateSize*device->NumUpdates / 4; + lBufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + BufferData = calloc(4, lBufferSize); + for(i = 0;i < 4;i++) + { + memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); + pData->WaveBuffer[i].dwBufferLength = lBufferSize; + pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : + (pData->WaveBuffer[i-1].lpData + + pData->WaveBuffer[i-1].dwBufferLength)); + waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + return ALC_TRUE; +} + +static void WinMMStopPlayback(ALCdevice *device) +{ + WinMMData *pData = (WinMMData*)device->ExtraData; + void *buffer = NULL; + int i; + + if(pData->hWaveThread == NULL) + return; + + // Set flag to stop processing headers + pData->bWaveShutdown = AL_TRUE; + + // Wait for signal that Wave Thread has been destroyed + WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); + + CloseHandle(pData->hWaveThread); + pData->hWaveThread = 0; + + pData->bWaveShutdown = AL_FALSE; + + // Release the wave buffers + for(i = 0;i < 4;i++) + { + waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) buffer = pData->WaveBuffer[i].lpData; + pData->WaveBuffer[i].lpData = NULL; + } + free(buffer); +} + + +static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName) +{ + WAVEFORMATEX wfexCaptureFormat; + DWORD ulCapturedDataSize; + WinMMData *pData = NULL; + UINT lDeviceID = 0; + ALbyte *BufferData; + ALint lBufferSize; + MMRESULT res; + ALuint i; + + if(!CaptureDeviceList) + ProbeCaptureDevices(); + + // Find the Device ID matching the deviceName if valid + if(deviceName) + { + for(i = 0;i < NumCaptureDevices;i++) + { + if(CaptureDeviceList[i] && + strcmp(deviceName, CaptureDeviceList[i]) == 0) + { + lDeviceID = i; + break; + } + } + } + else + { + for(i = 0;i < NumCaptureDevices;i++) + { + if(CaptureDeviceList[i]) + { + lDeviceID = i; + break; + } + } + } + if(i == NumCaptureDevices) + return ALC_FALSE; + + pData = calloc(1, sizeof(*pData)); + if(!pData) + { + alcSetError(pDevice, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + pDevice->ExtraData = pData; + + if((pDevice->FmtChans != DevFmtMono && pDevice->FmtChans != DevFmtStereo) || + (pDevice->FmtType != DevFmtUByte && pDevice->FmtType != DevFmtShort)) + { + alcSetError(pDevice, ALC_INVALID_ENUM); + goto failure; + } + + memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX)); + wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM; + wfexCaptureFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans); + wfexCaptureFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8; + wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample * + wfexCaptureFormat.nChannels / 8; + wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency; + wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec * + wfexCaptureFormat.nBlockAlign; + wfexCaptureFormat.cbSize = 0; + + if((res=waveInOpen(&pData->hWaveHandle.In, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) + { + ERR("waveInOpen failed: %u\n", res); + goto failure; + } + + pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(pData->hWaveThreadEvent == NULL) + { + ERR("CreateEvent failed: %lu\n", GetLastError()); + goto failure; + } + + pData->Frequency = pDevice->Frequency; + + // Allocate circular memory buffer for the captured audio + ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates; + + // Make sure circular buffer is at least 100ms in size + if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10)) + ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10; + + pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize); + if(!pData->pRing) + goto failure; + + pData->lWaveBuffersCommitted = 0; + + // Create 4 Buffers of 50ms each + lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20; + lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign); + + BufferData = calloc(4, lBufferSize); + if(!BufferData) + goto failure; + + for(i = 0;i < 4;i++) + { + memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); + pData->WaveBuffer[i].dwBufferLength = lBufferSize; + pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : + (pData->WaveBuffer[i-1].lpData + + pData->WaveBuffer[i-1].dwBufferLength)); + pData->WaveBuffer[i].dwFlags = 0; + pData->WaveBuffer[i].dwLoops = 0; + waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID); + if (pData->hWaveThread == NULL) + goto failure; + + pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]); + return ALC_TRUE; + +failure: + if(pData->hWaveThread) + CloseHandle(pData->hWaveThread); + + for(i = 0;i < 4;i++) + { + if(pData->WaveBuffer[i].lpData) + { + waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) + free(pData->WaveBuffer[i].lpData); + } + } + + if(pData->pRing) + DestroyRingBuffer(pData->pRing); + + if(pData->hWaveThreadEvent) + CloseHandle(pData->hWaveThreadEvent); + + if(pData->hWaveHandle.In) + waveInClose(pData->hWaveHandle.In); + + free(pData); + pDevice->ExtraData = NULL; + return ALC_FALSE; +} + +static void WinMMCloseCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + void *buffer = NULL; + int i; + + // Call waveOutReset to shutdown wave device + pData->bWaveShutdown = AL_TRUE; + waveInReset(pData->hWaveHandle.In); + + // Wait for signal that Wave Thread has been destroyed + WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); + + CloseHandle(pData->hWaveThread); + pData->hWaveThread = 0; + + // Release the wave buffers + for(i = 0;i < 4;i++) + { + waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) buffer = pData->WaveBuffer[i].lpData; + pData->WaveBuffer[i].lpData = NULL; + } + free(buffer); + + DestroyRingBuffer(pData->pRing); + pData->pRing = NULL; + + // Close the Wave device + CloseHandle(pData->hWaveThreadEvent); + pData->hWaveThreadEvent = 0; + + waveInClose(pData->hWaveHandle.In); + pData->hWaveHandle.In = 0; + + free(pData); + pDevice->ExtraData = NULL; +} + +static void WinMMStartCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + waveInStart(pData->hWaveHandle.In); +} + +static void WinMMStopCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + waveInStop(pData->hWaveHandle.In); +} + +static ALCuint WinMMAvailableSamples(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + return RingBufferSize(pData->pRing); +} + +static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + + if(WinMMAvailableSamples(pDevice) >= lSamples) + ReadRingBuffer(pData->pRing, pBuffer, lSamples); + else + alcSetError(pDevice, ALC_INVALID_VALUE); +} + + +static const BackendFuncs WinMMFuncs = { + WinMMOpenPlayback, + WinMMClosePlayback, + WinMMResetPlayback, + WinMMStopPlayback, + WinMMOpenCapture, + WinMMCloseCapture, + WinMMStartCapture, + WinMMStopCapture, + WinMMCaptureSamples, + WinMMAvailableSamples +}; + +ALCboolean alcWinMMInit(BackendFuncs *FuncList) +{ + *FuncList = WinMMFuncs; + return ALC_TRUE; +} + +void alcWinMMDeinit() +{ + ALuint lLoop; + + for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++) + free(PlaybackDeviceList[lLoop]); + free(PlaybackDeviceList); + PlaybackDeviceList = NULL; + + NumPlaybackDevices = 0; + + + for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++) + free(CaptureDeviceList[lLoop]); + free(CaptureDeviceList); + CaptureDeviceList = NULL; + + NumCaptureDevices = 0; +} + +void alcWinMMProbe(enum DevProbe type) +{ + ALuint i; + + switch(type) + { + case DEVICE_PROBE: + ProbePlaybackDevices(); + if(NumPlaybackDevices > 0) + AppendDeviceList(woDefault); + break; + + case ALL_DEVICE_PROBE: + ProbePlaybackDevices(); + if(NumPlaybackDevices > 0) + AppendAllDeviceList(woDefault); + for(i = 0;i < NumPlaybackDevices;i++) + { + if(PlaybackDeviceList[i]) + AppendAllDeviceList(PlaybackDeviceList[i]); + } + break; + + case CAPTURE_DEVICE_PROBE: + ProbeCaptureDevices(); + for(i = 0;i < NumCaptureDevices;i++) + { + if(CaptureDeviceList[i]) + AppendCaptureDeviceList(CaptureDeviceList[i]); + } + break; + } +} diff --git a/Alc/coreaudio.c b/Alc/coreaudio.c deleted file mode 100644 index c84be846..00000000 --- a/Alc/coreaudio.c +++ /dev/null @@ -1,719 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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 -#include -#include - -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - -#include -#include -#include -#include - - -typedef struct { - AudioUnit audioUnit; - - ALuint frameSize; - ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate - AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD - - AudioConverterRef audioConverter; // Sample rate converter if needed - AudioBufferList *bufferList; // Buffer for data coming from the input device - ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling - - RingBuffer *ring; -} ca_data; - -static const ALCchar ca_device[] = "CoreAudio Default"; - - -static void destroy_buffer_list(AudioBufferList* list) -{ - if(list) - { - UInt32 i; - for(i = 0;i < list->mNumberBuffers;i++) - free(list->mBuffers[i].mData); - free(list); - } -} - -static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize) -{ - AudioBufferList *list; - - list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer)); - if(list) - { - list->mNumberBuffers = 1; - - list->mBuffers[0].mNumberChannels = channelCount; - list->mBuffers[0].mDataByteSize = byteSize; - list->mBuffers[0].mData = malloc(byteSize); - if(list->mBuffers[0].mData == NULL) - { - free(list); - list = NULL; - } - } - return list; -} - -static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - ALCdevice *device = (ALCdevice*)inRefCon; - ca_data *data = (ca_data*)device->ExtraData; - - aluMixData(device, ioData->mBuffers[0].mData, - ioData->mBuffers[0].mDataByteSize / data->frameSize); - - return noErr; -} - -static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, - AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData) -{ - ALCdevice *device = (ALCdevice*)inUserData; - ca_data *data = (ca_data*)device->ExtraData; - - // Read from the ring buffer and store temporarily in a large buffer - ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets)); - - // Set the input data - ioData->mNumberBuffers = 1; - ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; - ioData->mBuffers[0].mData = data->resampleBuffer; - ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame; - - return noErr; -} - -static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, - UInt32 inNumberFrames, AudioBufferList *ioData) -{ - ALCdevice *device = (ALCdevice*)inRefCon; - ca_data *data = (ca_data*)device->ExtraData; - AudioUnitRenderActionFlags flags = 0; - OSStatus err; - - // fill the bufferList with data from the input device - err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList); - if(err != noErr) - { - ERR("AudioUnitRender error: %d\n", err); - return err; - } - - WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames); - - return noErr; -} - -static ALCboolean ca_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - ComponentDescription desc; - Component comp; - ca_data *data; - OSStatus err; - - if(!deviceName) - deviceName = ca_device; - else if(strcmp(deviceName, ca_device) != 0) - return ALC_FALSE; - - /* open the default output unit */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_DefaultOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - comp = FindNextComponent(NULL, &desc); - if(comp == NULL) - { - ERR("FindNextComponent failed\n"); - return ALC_FALSE; - } - - data = calloc(1, sizeof(*data)); - device->ExtraData = data; - - err = OpenAComponent(comp, &data->audioUnit); - if(err != noErr) - { - ERR("OpenAComponent failed\n"); - free(data); - device->ExtraData = NULL; - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void ca_close_playback(ALCdevice *device) -{ - ca_data *data = (ca_data*)device->ExtraData; - - CloseComponent(data->audioUnit); - - free(data); - device->ExtraData = NULL; -} - -static ALCboolean ca_reset_playback(ALCdevice *device) -{ - ca_data *data = (ca_data*)device->ExtraData; - AudioStreamBasicDescription streamFormat; - AURenderCallbackStruct input; - OSStatus err; - UInt32 size; - - /* init and start the default audio unit... */ - err = AudioUnitInitialize(data->audioUnit); - if(err != noErr) - { - ERR("AudioUnitInitialize failed\n"); - return ALC_FALSE; - } - - err = AudioOutputUnitStart(data->audioUnit); - if(err != noErr) - { - ERR("AudioOutputUnitStart failed\n"); - return ALC_FALSE; - } - - /* retrieve default output unit's properties (output side) */ - size = sizeof(AudioStreamBasicDescription); - err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size); - if(err != noErr || size != sizeof(AudioStreamBasicDescription)) - { - ERR("AudioUnitGetProperty failed\n"); - return ALC_FALSE; - } - -#if 0 - TRACE("Output streamFormat of default output unit -\n"); - TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket); - TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame); - TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel); - TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket); - TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame); - TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate); -#endif - - /* set default output unit's input side to match output side */ - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed\n"); - return ALC_FALSE; - } - - if(device->Frequency != streamFormat.mSampleRate) - { - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("CoreAudio does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, streamFormat.mSampleRate); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - - device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * - streamFormat.mSampleRate / - device->Frequency); - device->Frequency = streamFormat.mSampleRate; - } - - /* FIXME: How to tell what channels are what in the output device, and how - * to specify what we're giving? eg, 6.0 vs 5.1 */ - switch(streamFormat.mChannelsPerFrame) - { - case 1: - if((device->Flags&DEVICE_CHANNELS_REQUEST) && - device->FmtChans != DevFmtMono) - { - ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans)); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - } - device->FmtChans = DevFmtMono; - break; - case 2: - if((device->Flags&DEVICE_CHANNELS_REQUEST) && - device->FmtChans != DevFmtStereo) - { - ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans)); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - } - device->FmtChans = DevFmtStereo; - break; - case 4: - if((device->Flags&DEVICE_CHANNELS_REQUEST) && - device->FmtChans != DevFmtQuad) - { - ERR("Failed to set %s, got Quad instead\n", DevFmtChannelsString(device->FmtChans)); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - } - device->FmtChans = DevFmtQuad; - break; - case 6: - if((device->Flags&DEVICE_CHANNELS_REQUEST) && - device->FmtChans != DevFmtX51) - { - ERR("Failed to set %s, got 5.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - } - device->FmtChans = DevFmtX51; - break; - case 7: - if((device->Flags&DEVICE_CHANNELS_REQUEST) && - device->FmtChans != DevFmtX61) - { - ERR("Failed to set %s, got 6.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - } - device->FmtChans = DevFmtX61; - break; - case 8: - if((device->Flags&DEVICE_CHANNELS_REQUEST) && - device->FmtChans != DevFmtX71) - { - ERR("Failed to set %s, got 7.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - } - device->FmtChans = DevFmtX71; - break; - default: - ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - device->FmtChans = DevFmtStereo; - streamFormat.mChannelsPerFrame = 2; - break; - } - SetDefaultWFXChannelOrder(device); - - /* use channel count and sample rate from the default output unit's current - * parameters, but reset everything else */ - streamFormat.mFramesPerPacket = 1; - switch(device->FmtType) - { - case DevFmtUByte: - device->FmtType = DevFmtByte; - /* fall-through */ - case DevFmtByte: - streamFormat.mBitsPerChannel = 8; - streamFormat.mBytesPerPacket = streamFormat.mChannelsPerFrame; - streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame; - break; - case DevFmtUShort: - case DevFmtFloat: - device->FmtType = DevFmtShort; - /* fall-through */ - case DevFmtShort: - streamFormat.mBitsPerChannel = 16; - streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame; - streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame; - break; - } - streamFormat.mFormatID = kAudioFormatLinearPCM; - streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | - kAudioFormatFlagsNativeEndian | - kLinearPCMFormatFlagIsPacked; - - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription)); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed\n"); - return ALC_FALSE; - } - - /* setup callback */ - data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - input.inputProc = ca_callback; - input.inputProcRefCon = device; - - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct)); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed\n"); - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void ca_stop_playback(ALCdevice *device) -{ - ca_data *data = (ca_data*)device->ExtraData; - OSStatus err; - - AudioOutputUnitStop(data->audioUnit); - err = AudioUnitUninitialize(data->audioUnit); - if(err != noErr) - ERR("-- AudioUnitUninitialize failed.\n"); -} - -static ALCboolean ca_open_capture(ALCdevice *device, const ALCchar *deviceName) -{ - AudioStreamBasicDescription requestedFormat; // The application requested format - AudioStreamBasicDescription hardwareFormat; // The hardware format - AudioStreamBasicDescription outputFormat; // The AudioUnit output format - AURenderCallbackStruct input; - ComponentDescription desc; - AudioDeviceID inputDevice; - UInt32 outputFrameCount; - UInt32 propertySize; - UInt32 enableIO; - Component comp; - ca_data *data; - OSStatus err; - - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_HALOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - // Search for component with given description - comp = FindNextComponent(NULL, &desc); - if(comp == NULL) - { - ERR("FindNextComponent failed\n"); - return ALC_FALSE; - } - - data = calloc(1, sizeof(*data)); - device->ExtraData = data; - - // Open the component - err = OpenAComponent(comp, &data->audioUnit); - if(err != noErr) - { - ERR("OpenAComponent failed\n"); - goto error; - } - - // Turn off AudioUnit output - enableIO = 0; - err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint)); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed\n"); - goto error; - } - - // Turn on AudioUnit input - enableIO = 1; - err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint)); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed\n"); - goto error; - } - - // Get the default input device - propertySize = sizeof(AudioDeviceID); - err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice); - if(err != noErr) - { - ERR("AudioHardwareGetProperty failed\n"); - goto error; - } - - if(inputDevice == kAudioDeviceUnknown) - { - ERR("No input device found\n"); - goto error; - } - - // Track the input device - err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID)); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed\n"); - goto error; - } - - // set capture callback - input.inputProc = ca_capture_callback; - input.inputProcRefCon = device; - - err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct)); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed\n"); - goto error; - } - - // Initialize the device - err = AudioUnitInitialize(data->audioUnit); - if(err != noErr) - { - ERR("AudioUnitInitialize failed\n"); - goto error; - } - - // Get the hardware format - propertySize = sizeof(AudioStreamBasicDescription); - err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize); - if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription)) - { - ERR("AudioUnitGetProperty failed\n"); - goto error; - } - - // Set up the requested format description - switch(device->FmtType) - { - case DevFmtUByte: - requestedFormat.mBitsPerChannel = 8; - requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; - break; - case DevFmtShort: - requestedFormat.mBitsPerChannel = 16; - requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; - break; - case DevFmtFloat: - requestedFormat.mBitsPerChannel = 32; - requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; - break; - case DevFmtByte: - case DevFmtUShort: - ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType)); - goto error; - } - - switch(device->FmtChans) - { - case DevFmtMono: - requestedFormat.mChannelsPerFrame = 1; - break; - case DevFmtStereo: - requestedFormat.mChannelsPerFrame = 2; - break; - - case DevFmtQuad: - case DevFmtX51: - case DevFmtX51Side: - case DevFmtX61: - case DevFmtX71: - ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans)); - goto error; - } - - requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8; - requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame; - requestedFormat.mSampleRate = device->Frequency; - requestedFormat.mFormatID = kAudioFormatLinearPCM; - requestedFormat.mReserved = 0; - requestedFormat.mFramesPerPacket = 1; - - // save requested format description for later use - data->format = requestedFormat; - data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - - // Use intermediate format for sample rate conversion (outputFormat) - // Set sample rate to the same as hardware for resampling later - outputFormat = requestedFormat; - outputFormat.mSampleRate = hardwareFormat.mSampleRate; - - // Determine sample rate ratio for resampling - data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency; - - // The output format should be the requested format, but using the hardware sample rate - // This is because the AudioUnit will automatically scale other properties, except for sample rate - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat)); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed\n"); - goto error; - } - - // Set the AudioUnit output format frame count - outputFrameCount = device->UpdateSize * data->sampleRateRatio; - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount)); - if(err != noErr) - { - ERR("AudioUnitSetProperty failed: %d\n", err); - goto error; - } - - // Set up sample converter - err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter); - if(err != noErr) - { - ERR("AudioConverterNew failed: %d\n", err); - goto error; - } - - // Create a buffer for use in the resample callback - data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio); - - // Allocate buffer for the AudioUnit output - data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio); - if(data->bufferList == NULL) - { - alcSetError(device, ALC_OUT_OF_MEMORY); - goto error; - } - - data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates); - if(data->ring == NULL) - { - alcSetError(device, ALC_OUT_OF_MEMORY); - goto error; - } - - return ALC_TRUE; - -error: - DestroyRingBuffer(data->ring); - free(data->resampleBuffer); - destroy_buffer_list(data->bufferList); - - if(data->audioConverter) - AudioConverterDispose(data->audioConverter); - if(data->audioUnit) - CloseComponent(data->audioUnit); - - free(data); - device->ExtraData = NULL; - - return ALC_FALSE; -} - -static void ca_close_capture(ALCdevice *device) -{ - ca_data *data = (ca_data*)device->ExtraData; - - DestroyRingBuffer(data->ring); - free(data->resampleBuffer); - destroy_buffer_list(data->bufferList); - - AudioConverterDispose(data->audioConverter); - CloseComponent(data->audioUnit); - - free(data); - device->ExtraData = NULL; -} - -static void ca_start_capture(ALCdevice *device) -{ - ca_data *data = (ca_data*)device->ExtraData; - OSStatus err = AudioOutputUnitStart(data->audioUnit); - if(err != noErr) - ERR("AudioOutputUnitStart failed\n"); -} - -static void ca_stop_capture(ALCdevice *device) -{ - ca_data *data = (ca_data*)device->ExtraData; - OSStatus err = AudioOutputUnitStop(data->audioUnit); - if(err != noErr) - ERR("AudioOutputUnitStop failed\n"); -} - -static ALCuint ca_available_samples(ALCdevice *device) -{ - ca_data *data = device->ExtraData; - return RingBufferSize(data->ring) / data->sampleRateRatio; -} - -static void ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) -{ - ca_data *data = (ca_data*)device->ExtraData; - - if(samples <= ca_available_samples(device)) - { - AudioBufferList *list; - UInt32 frameCount; - OSStatus err; - - // If no samples are requested, just return - if(samples == 0) - return; - - // Allocate a temporary AudioBufferList to use as the return resamples data - list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer)); - - // Point the resampling buffer to the capture buffer - list->mNumberBuffers = 1; - list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; - list->mBuffers[0].mDataByteSize = samples * data->frameSize; - list->mBuffers[0].mData = buffer; - - // Resample into another AudioBufferList - frameCount = samples; - err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback, device, - &frameCount, list, NULL); - if(err != noErr) - { - ERR("AudioConverterFillComplexBuffer error: %d\n", err); - alcSetError(device, ALC_INVALID_VALUE); - } - } - else - alcSetError(device, ALC_INVALID_VALUE); -} - -static const BackendFuncs ca_funcs = { - ca_open_playback, - ca_close_playback, - ca_reset_playback, - ca_stop_playback, - ca_open_capture, - ca_close_capture, - ca_start_capture, - ca_stop_capture, - ca_capture_samples, - ca_available_samples -}; - -ALCboolean alc_ca_init(BackendFuncs *func_list) -{ - *func_list = ca_funcs; - return ALC_TRUE; -} - -void alc_ca_deinit(void) -{ -} - -void alc_ca_probe(enum DevProbe type) -{ - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(ca_device); - break; - case ALL_DEVICE_PROBE: - AppendAllDeviceList(ca_device); - break; - case CAPTURE_DEVICE_PROBE: - AppendCaptureDeviceList(ca_device); - break; - } -} diff --git a/Alc/dsound.c b/Alc/dsound.c deleted file mode 100644 index 08a1d13a..00000000 --- a/Alc/dsound.c +++ /dev/null @@ -1,627 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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" - -#define _WIN32_WINNT 0x0500 -#include -#include -#include - -#include -#include -#include -#ifndef _WAVEFORMATEXTENSIBLE_ -#include -#include -#endif - -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - -#ifndef DSSPEAKER_5POINT1 -#define DSSPEAKER_5POINT1 6 -#endif -#ifndef DSSPEAKER_7POINT1 -#define DSSPEAKER_7POINT1 7 -#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); - - -static HMODULE ds_handle; -static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter); -static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext); - -#define DirectSoundCreate pDirectSoundCreate -#define DirectSoundEnumerateA pDirectSoundEnumerateA - - -typedef struct { - // DirectSound Playback Device - IDirectSound *lpDS; - IDirectSoundBuffer *DSpbuffer; - IDirectSoundBuffer *DSsbuffer; - IDirectSoundNotify *DSnotify; - HANDLE hNotifyEvent; - - volatile int killNow; - ALvoid *thread; -} DSoundData; - - -typedef struct { - ALCchar *name; - GUID guid; -} DevMap; - -static const ALCchar dsDevice[] = "DirectSound Default"; -static DevMap *DeviceList; -static ALuint NumDevices; - -#define MAX_UPDATES 128 - -static ALCboolean DSoundLoad(void) -{ - ALCboolean ok = ALC_TRUE; - if(!ds_handle) - { - ds_handle = LoadLibraryA("dsound.dll"); - if(ds_handle == NULL) - { - ERR("Failed to load dsound.dll\n"); - return ALC_FALSE; - } - -#define LOAD_FUNC(x) do { \ - if((p##x = (void*)GetProcAddress(ds_handle, #x)) == NULL) { \ - ERR("Could not load %s from dsound.dll\n", #x); \ - ok = ALC_FALSE; \ - } \ -} while(0) - LOAD_FUNC(DirectSoundCreate); - LOAD_FUNC(DirectSoundEnumerateA); -#undef LOAD_FUNC - - if(!ok) - { - FreeLibrary(ds_handle); - ds_handle = NULL; - } - } - return ok; -} - - -static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data) -{ - char str[1024]; - void *temp; - int count; - ALuint i; - - (void)data; - (void)drvname; - - if(NumDevices == 0) - { - temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1)); - if(temp) - { - DeviceList = temp; - DeviceList[NumDevices].name = strdup(dsDevice); - DeviceList[NumDevices].guid = GUID_NULL; - NumDevices++; - } - } - - if(!guid) - return TRUE; - - count = 0; - do { - if(count == 0) - snprintf(str, sizeof(str), "%s", desc); - else - snprintf(str, sizeof(str), "%s #%d", desc, count+1); - count++; - - for(i = 0;i < NumDevices;i++) - { - if(strcmp(str, DeviceList[i].name) == 0) - break; - } - } while(i != NumDevices); - - temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1)); - if(temp) - { - DeviceList = temp; - DeviceList[NumDevices].name = strdup(str); - DeviceList[NumDevices].guid = *guid; - NumDevices++; - } - - return TRUE; -} - - -static ALuint DSoundProc(ALvoid *ptr) -{ - ALCdevice *pDevice = (ALCdevice*)ptr; - DSoundData *pData = (DSoundData*)pDevice->ExtraData; - DSBCAPS DSBCaps; - DWORD LastCursor = 0; - DWORD PlayCursor; - VOID *WritePtr1, *WritePtr2; - DWORD WriteCnt1, WriteCnt2; - BOOL Playing = FALSE; - DWORD FrameSize; - DWORD FragSize; - DWORD avail; - HRESULT err; - - SetRTPriority(); - - memset(&DSBCaps, 0, sizeof(DSBCaps)); - DSBCaps.dwSize = sizeof(DSBCaps); - err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps); - if(FAILED(err)) - { - ERR("Failed to get buffer caps: 0x%lx\n", err); - aluHandleDisconnect(pDevice); - return 1; - } - - FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); - FragSize = pDevice->UpdateSize * FrameSize; - - IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL); - while(!pData->killNow) - { - // Get current play cursor - IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL); - avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes; - - if(avail < FragSize) - { - if(!Playing) - { - err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING); - if(FAILED(err)) - { - ERR("Failed to play buffer: 0x%lx\n", err); - aluHandleDisconnect(pDevice); - return 1; - } - Playing = TRUE; - } - - avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE); - if(avail != WAIT_OBJECT_0) - ERR("WaitForSingleObjectEx error: 0x%lx\n", avail); - continue; - } - avail -= avail%FragSize; - - // Lock output buffer - WriteCnt1 = 0; - WriteCnt2 = 0; - err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); - - // If the buffer is lost, restore it and lock - if(err == DSERR_BUFFERLOST) - { - WARN("Buffer lost, restoring...\n"); - err = IDirectSoundBuffer_Restore(pData->DSsbuffer); - if(SUCCEEDED(err)) - { - Playing = FALSE; - LastCursor = 0; - err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); - } - } - - // Successfully locked the output buffer - if(SUCCEEDED(err)) - { - // If we have an active context, mix data directly into output buffer otherwise fill with silence - aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize); - aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize); - - // Unlock output buffer only when successfully locked - IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); - } - else - { - ERR("Buffer lock error: %#lx\n", err); - aluHandleDisconnect(pDevice); - return 1; - } - - // Update old write cursor location - LastCursor += WriteCnt1+WriteCnt2; - LastCursor %= DSBCaps.dwBufferBytes; - } - - return 0; -} - -static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName) -{ - DSoundData *pData = NULL; - LPGUID guid = NULL; - HRESULT hr; - - if(!deviceName) - deviceName = dsDevice; - else if(strcmp(deviceName, dsDevice) != 0) - { - ALuint i; - - if(!DeviceList) - { - hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL); - if(FAILED(hr)) - ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr); - } - - for(i = 0;i < NumDevices;i++) - { - if(strcmp(deviceName, DeviceList[i].name) == 0) - { - if(i > 0) - guid = &DeviceList[i].guid; - break; - } - } - if(i == NumDevices) - return ALC_FALSE; - } - - //Initialise requested device - pData = calloc(1, sizeof(DSoundData)); - if(!pData) - { - alcSetError(device, ALC_OUT_OF_MEMORY); - return ALC_FALSE; - } - - hr = DS_OK; - pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(pData->hNotifyEvent == NULL) - hr = E_FAIL; - - //DirectSound Init code - if(SUCCEEDED(hr)) - hr = DirectSoundCreate(guid, &pData->lpDS, NULL); - if(SUCCEEDED(hr)) - hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY); - if(FAILED(hr)) - { - if(pData->lpDS) - IDirectSound_Release(pData->lpDS); - if(pData->hNotifyEvent) - CloseHandle(pData->hNotifyEvent); - free(pData); - ERR("Device init failed: 0x%08lx\n", hr); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - device->ExtraData = pData; - return ALC_TRUE; -} - -static void DSoundClosePlayback(ALCdevice *device) -{ - DSoundData *pData = device->ExtraData; - - IDirectSound_Release(pData->lpDS); - CloseHandle(pData->hNotifyEvent); - free(pData); - device->ExtraData = NULL; -} - -static ALCboolean DSoundResetPlayback(ALCdevice *device) -{ - DSoundData *pData = (DSoundData*)device->ExtraData; - DSBUFFERDESC DSBDescription; - WAVEFORMATEXTENSIBLE OutputType; - DWORD speakers; - HRESULT hr; - - memset(&OutputType, 0, sizeof(OutputType)); - - switch(device->FmtType) - { - case DevFmtByte: - device->FmtType = DevFmtUByte; - break; - case DevFmtUShort: - device->FmtType = DevFmtShort; - break; - case DevFmtUByte: - case DevFmtShort: - case DevFmtFloat: - break; - } - - hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers); - if(SUCCEEDED(hr)) - { - if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) - { - speakers = DSSPEAKER_CONFIG(speakers); - if(speakers == DSSPEAKER_MONO) - device->FmtChans = DevFmtMono; - else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE) - device->FmtChans = DevFmtStereo; - else if(speakers == DSSPEAKER_QUAD) - device->FmtChans = DevFmtQuad; - else if(speakers == DSSPEAKER_5POINT1) - device->FmtChans = DevFmtX51; - else if(speakers == DSSPEAKER_7POINT1) - device->FmtChans = DevFmtX71; - else - ERR("Unknown system speaker config: 0x%lx\n", speakers); - } - - switch(device->FmtChans) - { - case DevFmtMono: - OutputType.dwChannelMask = SPEAKER_FRONT_CENTER; - break; - case DevFmtStereo: - OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT; - break; - case DevFmtQuad: - OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_BACK_LEFT | - SPEAKER_BACK_RIGHT; - break; - case DevFmtX51: - OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_FRONT_CENTER | - SPEAKER_LOW_FREQUENCY | - SPEAKER_BACK_LEFT | - SPEAKER_BACK_RIGHT; - break; - case DevFmtX51Side: - OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_FRONT_CENTER | - SPEAKER_LOW_FREQUENCY | - SPEAKER_SIDE_LEFT | - SPEAKER_SIDE_RIGHT; - break; - case DevFmtX61: - OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_FRONT_CENTER | - SPEAKER_LOW_FREQUENCY | - SPEAKER_BACK_CENTER | - SPEAKER_SIDE_LEFT | - SPEAKER_SIDE_RIGHT; - break; - case DevFmtX71: - OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_FRONT_CENTER | - SPEAKER_LOW_FREQUENCY | - SPEAKER_BACK_LEFT | - SPEAKER_BACK_RIGHT | - SPEAKER_SIDE_LEFT | - SPEAKER_SIDE_RIGHT; - break; - } - - OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; - OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans); - OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; - OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8; - OutputType.Format.nSamplesPerSec = device->Frequency; - OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign; - OutputType.Format.cbSize = 0; - } - - if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat) - { - OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); - if(device->FmtType == DevFmtFloat) - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - else - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - else - { - if(SUCCEEDED(hr)) - { - memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); - DSBDescription.dwSize=sizeof(DSBUFFERDESC); - DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER; - hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL); - } - if(SUCCEEDED(hr)) - hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format); - } - - if(SUCCEEDED(hr)) - { - if(device->NumUpdates > MAX_UPDATES) - { - device->UpdateSize = (device->UpdateSize*device->NumUpdates + - MAX_UPDATES-1) / MAX_UPDATES; - device->NumUpdates = MAX_UPDATES; - } - - memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); - DSBDescription.dwSize=sizeof(DSBUFFERDESC); - DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS; - DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * - OutputType.Format.nBlockAlign; - DSBDescription.lpwfxFormat=&OutputType.Format; - hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL); - } - - if(SUCCEEDED(hr)) - { - hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify); - if(SUCCEEDED(hr)) - { - DSBPOSITIONNOTIFY notifies[MAX_UPDATES]; - ALuint i; - - for(i = 0;i < device->NumUpdates;++i) - { - notifies[i].dwOffset = i * device->UpdateSize * - OutputType.Format.nBlockAlign; - notifies[i].hEventNotify = pData->hNotifyEvent; - } - if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK) - hr = E_FAIL; - } - } - - if(SUCCEEDED(hr)) - { - ResetEvent(pData->hNotifyEvent); - SetDefaultWFXChannelOrder(device); - pData->thread = StartThread(DSoundProc, device); - if(pData->thread == NULL) - hr = E_FAIL; - } - - if(FAILED(hr)) - { - if(pData->DSnotify != NULL) - IDirectSoundNotify_Release(pData->DSnotify); - pData->DSnotify = NULL; - if(pData->DSsbuffer != NULL) - IDirectSoundBuffer_Release(pData->DSsbuffer); - pData->DSsbuffer = NULL; - if(pData->DSpbuffer != NULL) - IDirectSoundBuffer_Release(pData->DSpbuffer); - pData->DSpbuffer = NULL; - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void DSoundStopPlayback(ALCdevice *device) -{ - DSoundData *pData = device->ExtraData; - - if(!pData->thread) - return; - - pData->killNow = 1; - StopThread(pData->thread); - pData->thread = NULL; - - pData->killNow = 0; - - IDirectSoundNotify_Release(pData->DSnotify); - pData->DSnotify = NULL; - IDirectSoundBuffer_Release(pData->DSsbuffer); - pData->DSsbuffer = NULL; - if(pData->DSpbuffer != NULL) - IDirectSoundBuffer_Release(pData->DSpbuffer); - pData->DSpbuffer = NULL; -} - - -static const BackendFuncs DSoundFuncs = { - DSoundOpenPlayback, - DSoundClosePlayback, - DSoundResetPlayback, - DSoundStopPlayback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - - -ALCboolean alcDSoundInit(BackendFuncs *FuncList) -{ - if(!DSoundLoad()) - return ALC_FALSE; - *FuncList = DSoundFuncs; - return ALC_TRUE; -} - -void alcDSoundDeinit(void) -{ - ALuint i; - - for(i = 0;i < NumDevices;++i) - free(DeviceList[i].name); - free(DeviceList); - DeviceList = NULL; - NumDevices = 0; - - if(ds_handle) - FreeLibrary(ds_handle); - ds_handle = NULL; -} - -void alcDSoundProbe(enum DevProbe type) -{ - HRESULT hr; - ALuint i; - - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(dsDevice); - break; - - case ALL_DEVICE_PROBE: - for(i = 0;i < NumDevices;++i) - free(DeviceList[i].name); - free(DeviceList); - DeviceList = NULL; - NumDevices = 0; - - hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL); - if(FAILED(hr)) - ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr); - else - { - for(i = 0;i < NumDevices;i++) - AppendAllDeviceList(DeviceList[i].name); - } - break; - - case CAPTURE_DEVICE_PROBE: - break; - } -} diff --git a/Alc/loopback.c b/Alc/loopback.c deleted file mode 100644 index 86e53555..00000000 --- a/Alc/loopback.c +++ /dev/null @@ -1,77 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2011 by Chris Robinson - * 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 -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - - -static ALCboolean loopback_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - device->szDeviceName = strdup(deviceName); - return ALC_TRUE; -} - -static void loopback_close_playback(ALCdevice *device) -{ - (void)device; -} - -static ALCboolean loopback_reset_playback(ALCdevice *device) -{ - SetDefaultWFXChannelOrder(device); - return ALC_TRUE; -} - -static void loopback_stop_playback(ALCdevice *device) -{ - (void)device; -} - -static const BackendFuncs loopback_funcs = { - loopback_open_playback, - loopback_close_playback, - loopback_reset_playback, - loopback_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -ALCboolean alc_loopback_init(BackendFuncs *func_list) -{ - *func_list = loopback_funcs; - return ALC_TRUE; -} - -void alc_loopback_deinit(void) -{ -} - -void alc_loopback_probe(enum DevProbe type) -{ - (void)type; -} diff --git a/Alc/mmdevapi.c b/Alc/mmdevapi.c deleted file mode 100644 index 702569c6..00000000 --- a/Alc/mmdevapi.c +++ /dev/null @@ -1,775 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2011 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" - -#define COBJMACROS -#define _WIN32_WINNT 0x0500 -#include -#include -#include - -#include -#include -#include -#include -#ifndef _WAVEFORMATEXTENSIBLE_ -#include -#include -#endif - -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - - -DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); -DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); - -#define MONO SPEAKER_FRONT_CENTER -#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) -#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) -#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) -#define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) -#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) -#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) - - -typedef struct { - IMMDevice *mmdev; - IAudioClient *client; - HANDLE hNotifyEvent; - - HANDLE MsgEvent; - - volatile int killNow; - ALvoid *thread; -} MMDevApiData; - - -static const ALCchar mmDevice[] = "WASAPI Default"; - - -static HANDLE ThreadHdl; -static DWORD ThreadID; - -typedef struct { - HANDLE FinishedEvt; - HRESULT result; -} ThreadRequest; - -#define WM_USER_OpenDevice (WM_USER+0) -#define WM_USER_ResetDevice (WM_USER+1) -#define WM_USER_StopDevice (WM_USER+2) -#define WM_USER_CloseDevice (WM_USER+3) - -static HRESULT WaitForResponse(ThreadRequest *req) -{ - if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0) - return req->result; - ERR("Message response error: %lu\n", GetLastError()); - return E_FAIL; -} - - -static ALuint MMDevApiProc(ALvoid *ptr) -{ - ALCdevice *device = ptr; - MMDevApiData *data = device->ExtraData; - union { - IAudioRenderClient *iface; - void *ptr; - } render; - UINT32 written, len; - BYTE *buffer; - HRESULT hr; - - hr = CoInitialize(NULL); - if(FAILED(hr)) - { - ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); - aluHandleDisconnect(device); - return 0; - } - - hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr); - if(FAILED(hr)) - { - ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr); - aluHandleDisconnect(device); - return 0; - } - - SetRTPriority(); - - while(!data->killNow) - { - hr = IAudioClient_GetCurrentPadding(data->client, &written); - if(FAILED(hr)) - { - ERR("Failed to get padding: 0x%08lx\n", hr); - aluHandleDisconnect(device); - break; - } - - len = device->UpdateSize*device->NumUpdates - written; - if(len < device->UpdateSize) - { - DWORD res; - res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE); - if(res != WAIT_OBJECT_0) - ERR("WaitForSingleObjectEx error: 0x%lx\n", res); - continue; - } - len -= len%device->UpdateSize; - - hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer); - if(SUCCEEDED(hr)) - { - aluMixData(device, buffer, len); - hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0); - } - if(FAILED(hr)) - { - ERR("Failed to buffer data: 0x%08lx\n", hr); - aluHandleDisconnect(device); - break; - } - } - - IAudioRenderClient_Release(render.iface); - - CoUninitialize(); - return 0; -} - - -static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) -{ - memset(out, 0, sizeof(*out)); - if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) - *out = *(WAVEFORMATEXTENSIBLE*)in; - else if(in->wFormatTag == WAVE_FORMAT_PCM) - { - out->Format = *in; - out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - out->Format.cbSize = sizeof(*out) - sizeof(*in); - if(out->Format.nChannels == 1) - out->dwChannelMask = MONO; - else if(out->Format.nChannels == 2) - out->dwChannelMask = STEREO; - else - ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels); - out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) - { - out->Format = *in; - out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - out->Format.cbSize = sizeof(*out) - sizeof(*in); - if(out->Format.nChannels == 1) - out->dwChannelMask = MONO; - else if(out->Format.nChannels == 2) - out->dwChannelMask = STEREO; - else - ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels); - out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } - else - { - ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag); - return ALC_FALSE; - } - return ALC_TRUE; -} - -static HRESULT DoReset(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - WAVEFORMATEXTENSIBLE OutputType; - WAVEFORMATEX *wfx = NULL; - REFERENCE_TIME min_per; - UINT32 buffer_len, min_len; - HRESULT hr; - - hr = IAudioClient_GetMixFormat(data->client, &wfx); - if(FAILED(hr)) - { - ERR("Failed to get mix format: 0x%08lx\n", hr); - return hr; - } - - if(!MakeExtensible(&OutputType, wfx)) - { - CoTaskMemFree(wfx); - return E_FAIL; - } - CoTaskMemFree(wfx); - wfx = NULL; - - if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) - device->Frequency = OutputType.Format.nSamplesPerSec; - if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) - { - if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) - device->FmtChans = DevFmtMono; - else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) - device->FmtChans = DevFmtStereo; - else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) - device->FmtChans = DevFmtQuad; - else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) - device->FmtChans = DevFmtX51; - else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) - device->FmtChans = DevFmtX51Side; - else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) - device->FmtChans = DevFmtX61; - else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1) - device->FmtChans = DevFmtX71; - else - ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask); - } - - switch(device->FmtChans) - { - case DevFmtMono: - OutputType.Format.nChannels = 1; - OutputType.dwChannelMask = MONO; - break; - case DevFmtStereo: - OutputType.Format.nChannels = 2; - OutputType.dwChannelMask = STEREO; - break; - case DevFmtQuad: - OutputType.Format.nChannels = 4; - OutputType.dwChannelMask = QUAD; - break; - case DevFmtX51: - OutputType.Format.nChannels = 6; - OutputType.dwChannelMask = X5DOT1; - break; - case DevFmtX51Side: - OutputType.Format.nChannels = 6; - OutputType.dwChannelMask = X5DOT1SIDE; - break; - case DevFmtX61: - OutputType.Format.nChannels = 7; - OutputType.dwChannelMask = X6DOT1; - break; - case DevFmtX71: - OutputType.Format.nChannels = 8; - OutputType.dwChannelMask = X7DOT1; - break; - } - switch(device->FmtType) - { - case DevFmtByte: - device->FmtType = DevFmtUByte; - /* fall-through */ - case DevFmtUByte: - OutputType.Format.wBitsPerSample = 8; - OutputType.Samples.wValidBitsPerSample = 8; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - break; - case DevFmtUShort: - device->FmtType = DevFmtShort; - /* fall-through */ - case DevFmtShort: - OutputType.Format.wBitsPerSample = 16; - OutputType.Samples.wValidBitsPerSample = 16; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - break; - case DevFmtFloat: - OutputType.Format.wBitsPerSample = 32; - OutputType.Samples.wValidBitsPerSample = 32; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - break; - } - OutputType.Format.nSamplesPerSec = device->Frequency; - - OutputType.Format.nBlockAlign = OutputType.Format.nChannels * - OutputType.Format.wBitsPerSample / 8; - OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * - OutputType.Format.nBlockAlign; - - hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); - if(FAILED(hr)) - { - ERR("Failed to check format support: 0x%08lx\n", hr); - hr = IAudioClient_GetMixFormat(data->client, &wfx); - } - if(FAILED(hr)) - { - ERR("Failed to find a supported format: 0x%08lx\n", hr); - return hr; - } - - if(wfx != NULL) - { - if(!MakeExtensible(&OutputType, wfx)) - { - CoTaskMemFree(wfx); - return E_FAIL; - } - CoTaskMemFree(wfx); - wfx = NULL; - - if(device->Frequency != OutputType.Format.nSamplesPerSec) - { - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("Failed to set %dhz, got %ldhz instead\n", device->Frequency, OutputType.Format.nSamplesPerSec); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - device->Frequency = OutputType.Format.nSamplesPerSec; - } - - if(!((device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) || - (device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) || - (device->FmtChans == DevFmtQuad && OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) || - (device->FmtChans == DevFmtX51 && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) || - (device->FmtChans == DevFmtX51Side && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) || - (device->FmtChans == DevFmtX61 && OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) || - (device->FmtChans == DevFmtX71 && OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1))) - { - if((device->Flags&DEVICE_CHANNELS_REQUEST)) - ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device->FmtChans), OutputType.Format.nChannels, OutputType.dwChannelMask); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - - if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) - device->FmtChans = DevFmtMono; - else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) - device->FmtChans = DevFmtStereo; - else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) - device->FmtChans = DevFmtQuad; - else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) - device->FmtChans = DevFmtX51; - else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) - device->FmtChans = DevFmtX51Side; - else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) - device->FmtChans = DevFmtX61; - else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1) - device->FmtChans = DevFmtX71; - else - { - ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask); - device->FmtChans = DevFmtStereo; - OutputType.Format.nChannels = 2; - OutputType.dwChannelMask = STEREO; - } - } - - if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) - { - if(OutputType.Samples.wValidBitsPerSample == 0) - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample || - !((device->FmtType == DevFmtUByte && OutputType.Format.wBitsPerSample == 8) || - (device->FmtType == DevFmtShort && OutputType.Format.wBitsPerSample == 16))) - { - ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample); - if(OutputType.Format.wBitsPerSample == 8) - device->FmtType = DevFmtUByte; - else if(OutputType.Format.wBitsPerSample == 16) - device->FmtType = DevFmtShort; - else - { - device->FmtType = DevFmtShort; - OutputType.Format.wBitsPerSample = 16; - } - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - } - } - else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) - { - if(OutputType.Samples.wValidBitsPerSample == 0) - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample || - !((device->FmtType == DevFmtFloat && OutputType.Format.wBitsPerSample == 32))) - { - ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample); - if(OutputType.Format.wBitsPerSample != 32) - { - device->FmtType = DevFmtFloat; - OutputType.Format.wBitsPerSample = 32; - } - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - } - } - else - { - ERR("Unhandled format sub-type\n"); - device->FmtType = DevFmtShort; - OutputType.Format.wBitsPerSample = 16; - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - } - - SetDefaultWFXChannelOrder(device); - - hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL); - if(SUCCEEDED(hr)) - { - min_len = (min_per*device->Frequency + 10000000-1) / 10000000; - if(min_len < device->UpdateSize) - min_len *= (device->UpdateSize + min_len/2)/min_len; - - device->NumUpdates = (device->NumUpdates*device->UpdateSize + min_len/2) / - min_len; - device->NumUpdates = maxu(device->NumUpdates, 2); - device->UpdateSize = min_len; - - hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - ((REFERENCE_TIME)device->UpdateSize* - device->NumUpdates*10000000 + - device->Frequency-1) / device->Frequency, - 0, &OutputType.Format, NULL); - } - if(FAILED(hr)) - { - ERR("Failed to initialize audio client: 0x%08lx\n", hr); - return hr; - } - - hr = IAudioClient_GetBufferSize(data->client, &buffer_len); - if(FAILED(hr)) - { - ERR("Failed to get audio buffer info: 0x%08lx\n", hr); - return hr; - } - - device->NumUpdates = buffer_len / device->UpdateSize; - if(device->NumUpdates <= 1) - { - device->NumUpdates = 1; - ERR("Audio client returned buffer_len < period*2; expect break up\n"); - } - - ResetEvent(data->hNotifyEvent); - hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent); - if(SUCCEEDED(hr)) - hr = IAudioClient_Start(data->client); - if(FAILED(hr)) - { - ERR("Failed to start audio client: 0x%08lx\n", hr); - return hr; - } - - data->thread = StartThread(MMDevApiProc, device); - if(!data->thread) - { - IAudioClient_Stop(data->client); - ERR("Failed to start thread\n"); - return E_FAIL; - } - - return hr; -} - - -static DWORD CALLBACK MessageProc(void *ptr) -{ - ThreadRequest *req = ptr; - IMMDeviceEnumerator *Enumerator; - MMDevApiData *data; - ALCdevice *device; - HRESULT hr; - MSG msg; - - TRACE("Starting message thread\n"); - - hr = CoInitialize(NULL); - if(FAILED(hr)) - { - WARN("Failed to initialize COM: 0x%08lx\n", hr); - req->result = hr; - SetEvent(req->FinishedEvt); - return 0; - } - - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); - if(FAILED(hr)) - { - WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); - CoUninitialize(); - req->result = hr; - SetEvent(req->FinishedEvt); - return 0; - } - Enumerator = ptr; - IMMDeviceEnumerator_Release(Enumerator); - Enumerator = NULL; - - req->result = S_OK; - SetEvent(req->FinishedEvt); - - TRACE("Starting message loop\n"); - while(GetMessage(&msg, NULL, 0, 0)) - { - TRACE("Got message %u\n", msg.message); - switch(msg.message) - { - case WM_USER_OpenDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - data = device->ExtraData; - - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); - if(SUCCEEDED(hr)) - { - Enumerator = ptr; - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); - IMMDeviceEnumerator_Release(Enumerator); - Enumerator = NULL; - } - if(SUCCEEDED(hr)) - hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); - if(SUCCEEDED(hr)) - data->client = ptr; - - if(FAILED(hr)) - { - if(data->mmdev) - IMMDevice_Release(data->mmdev); - data->mmdev = NULL; - } - - req->result = hr; - SetEvent(req->FinishedEvt); - continue; - - case WM_USER_ResetDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - - req->result = DoReset(device); - SetEvent(req->FinishedEvt); - continue; - - case WM_USER_StopDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - data = device->ExtraData; - - if(data->thread) - { - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - - data->killNow = 0; - - IAudioClient_Stop(data->client); - } - - req->result = S_OK; - SetEvent(req->FinishedEvt); - continue; - - case WM_USER_CloseDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - data = device->ExtraData; - - IAudioClient_Release(data->client); - data->client = NULL; - - IMMDevice_Release(data->mmdev); - data->mmdev = NULL; - - req->result = S_OK; - SetEvent(req->FinishedEvt); - continue; - - default: - ERR("Unexpected message: %u\n", msg.message); - continue; - } - } - TRACE("Message loop finished\n"); - - CoUninitialize(); - return 0; -} - - -static BOOL MMDevApiLoad(void) -{ - static HRESULT InitResult; - if(!ThreadHdl) - { - ThreadRequest req; - InitResult = E_FAIL; - - req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); - if(req.FinishedEvt == NULL) - ERR("Failed to create event: %lu\n", GetLastError()); - else - { - ThreadHdl = CreateThread(NULL, 0, MessageProc, &req, 0, &ThreadID); - if(ThreadHdl != NULL) - InitResult = WaitForResponse(&req); - CloseHandle(req.FinishedEvt); - } - } - return SUCCEEDED(InitResult); -} - - -static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName) -{ - MMDevApiData *data = NULL; - HRESULT hr; - - if(!deviceName) - deviceName = mmDevice; - else if(strcmp(deviceName, mmDevice) != 0) - return ALC_FALSE; - - //Initialise requested device - data = calloc(1, sizeof(MMDevApiData)); - if(!data) - { - alcSetError(device, ALC_OUT_OF_MEMORY); - return ALC_FALSE; - } - device->ExtraData = data; - - hr = S_OK; - data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(data->hNotifyEvent == NULL || data->MsgEvent == NULL) - hr = E_FAIL; - - if(SUCCEEDED(hr)) - { - ThreadRequest req = { data->MsgEvent, 0 }; - - hr = E_FAIL; - if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device)) - hr = WaitForResponse(&req); - } - - if(FAILED(hr)) - { - if(data->hNotifyEvent != NULL) - CloseHandle(data->hNotifyEvent); - data->hNotifyEvent = NULL; - if(data->MsgEvent != NULL) - CloseHandle(data->MsgEvent); - data->MsgEvent = NULL; - - free(data); - device->ExtraData = NULL; - - ERR("Device init failed: 0x%08lx\n", hr); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - return ALC_TRUE; -} - -static void MMDevApiClosePlayback(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - ThreadRequest req = { data->MsgEvent, 0 }; - - if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device)) - (void)WaitForResponse(&req); - - CloseHandle(data->MsgEvent); - data->MsgEvent = NULL; - - CloseHandle(data->hNotifyEvent); - data->hNotifyEvent = NULL; - - free(data); - device->ExtraData = NULL; -} - -static ALCboolean MMDevApiResetPlayback(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - ThreadRequest req = { data->MsgEvent, 0 }; - HRESULT hr = E_FAIL; - - if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device)) - hr = WaitForResponse(&req); - - return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; -} - -static void MMDevApiStopPlayback(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - ThreadRequest req = { data->MsgEvent, 0 }; - - if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device)) - (void)WaitForResponse(&req); -} - - -static const BackendFuncs MMDevApiFuncs = { - MMDevApiOpenPlayback, - MMDevApiClosePlayback, - MMDevApiResetPlayback, - MMDevApiStopPlayback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - - -ALCboolean alcMMDevApiInit(BackendFuncs *FuncList) -{ - if(!MMDevApiLoad()) - return ALC_FALSE; - *FuncList = MMDevApiFuncs; - return ALC_TRUE; -} - -void alcMMDevApiDeinit(void) -{ - if(ThreadHdl) - { - TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID); - PostThreadMessage(ThreadID, WM_QUIT, 0, 0); - CloseHandle(ThreadHdl); - ThreadHdl = NULL; - } -} - -void alcMMDevApiProbe(enum DevProbe type) -{ - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(mmDevice); - break; - case ALL_DEVICE_PROBE: - AppendAllDeviceList(mmDevice); - break; - case CAPTURE_DEVICE_PROBE: - break; - } -} diff --git a/Alc/null.c b/Alc/null.c deleted file mode 100644 index dd1ac216..00000000 --- a/Alc/null.c +++ /dev/null @@ -1,164 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2010 by Chris Robinson - * 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 -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - - -typedef struct { - volatile int killNow; - ALvoid *thread; -} null_data; - - -static const ALCchar nullDevice[] = "No Output"; - -static ALuint NullProc(ALvoid *ptr) -{ - ALCdevice *Device = (ALCdevice*)ptr; - null_data *data = (null_data*)Device->ExtraData; - ALuint now, start; - ALuint64 avail, done; - const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 / - Device->Frequency / 2; - - done = 0; - start = timeGetTime(); - while(!data->killNow && Device->Connected) - { - now = timeGetTime(); - - avail = (ALuint64)(now-start) * Device->Frequency / 1000; - if(avail < done) - { - /* Timer wrapped. Add the remainder of the cycle to the available - * count and reset the number of samples done */ - avail += (ALuint64)0xFFFFFFFFu*Device->Frequency/1000 - done; - done = 0; - } - if(avail-done < Device->UpdateSize) - { - Sleep(restTime); - continue; - } - - while(avail-done >= Device->UpdateSize) - { - aluMixData(Device, NULL, Device->UpdateSize); - done += Device->UpdateSize; - } - } - - return 0; -} - -static ALCboolean null_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - null_data *data; - - if(!deviceName) - deviceName = nullDevice; - else if(strcmp(deviceName, nullDevice) != 0) - return ALC_FALSE; - - data = (null_data*)calloc(1, sizeof(*data)); - - device->szDeviceName = strdup(deviceName); - device->ExtraData = data; - return ALC_TRUE; -} - -static void null_close_playback(ALCdevice *device) -{ - null_data *data = (null_data*)device->ExtraData; - - free(data); - device->ExtraData = NULL; -} - -static ALCboolean null_reset_playback(ALCdevice *device) -{ - null_data *data = (null_data*)device->ExtraData; - - SetDefaultWFXChannelOrder(device); - - data->thread = StartThread(NullProc, device); - if(data->thread == NULL) - return ALC_FALSE; - - return ALC_TRUE; -} - -static void null_stop_playback(ALCdevice *device) -{ - null_data *data = (null_data*)device->ExtraData; - - if(!data->thread) - return; - - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - - data->killNow = 0; -} - - -static const BackendFuncs null_funcs = { - null_open_playback, - null_close_playback, - null_reset_playback, - null_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -ALCboolean alc_null_init(BackendFuncs *func_list) -{ - *func_list = null_funcs; - return ALC_TRUE; -} - -void alc_null_deinit(void) -{ -} - -void alc_null_probe(enum DevProbe type) -{ - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(nullDevice); - break; - case ALL_DEVICE_PROBE: - AppendAllDeviceList(nullDevice); - break; - case CAPTURE_DEVICE_PROBE: - break; - } -} diff --git a/Alc/opensl.c b/Alc/opensl.c deleted file mode 100644 index 88d05505..00000000 --- a/Alc/opensl.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* This is an OpenAL backend for Android using the native audio APIs based on - * OpenSL ES 1.0.1. It is based on source code for the native-audio sample app - * bundled with NDK. - */ - -#include "config.h" - -#include -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - - -#include -#if 1 -#include -#else -extern SLAPIENTRY const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - -struct SLAndroidSimpleBufferQueueItf_; -typedef const struct SLAndroidSimpleBufferQueueItf_ * const * SLAndroidSimpleBufferQueueItf; - -typedef void (*slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller, void *pContext); - -typedef struct SLAndroidSimpleBufferQueueState_ { - SLuint32 count; - SLuint32 index; -} SLAndroidSimpleBufferQueueState; - - -struct SLAndroidSimpleBufferQueueItf_ { - SLresult (*Enqueue) ( - SLAndroidSimpleBufferQueueItf self, - const void *pBuffer, - SLuint32 size - ); - SLresult (*Clear) ( - SLAndroidSimpleBufferQueueItf self - ); - SLresult (*GetState) ( - SLAndroidSimpleBufferQueueItf self, - SLAndroidSimpleBufferQueueState *pState - ); - SLresult (*RegisterCallback) ( - SLAndroidSimpleBufferQueueItf self, - slAndroidSimpleBufferQueueCallback callback, - void* pContext - ); -}; - -#define SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE ((SLuint32) 0x800007BD) - -typedef struct SLDataLocator_AndroidSimpleBufferQueue { - SLuint32 locatorType; - SLuint32 numBuffers; -} SLDataLocator_AndroidSimpleBufferQueue; - -#endif - -/* Helper macros */ -#define SLObjectItf_Realize(a,b) ((*(a))->Realize((a),(b))) -#define SLObjectItf_GetInterface(a,b,c) ((*(a))->GetInterface((a),(b),(c))) -#define SLObjectItf_Destroy(a) ((*(a))->Destroy((a))) - -#define SLEngineItf_CreateOutputMix(a,b,c,d,e) ((*(a))->CreateOutputMix((a),(b),(c),(d),(e))) -#define SLEngineItf_CreateAudioPlayer(a,b,c,d,e,f,g) ((*(a))->CreateAudioPlayer((a),(b),(c),(d),(e),(f),(g))) - -#define SLPlayItf_SetPlayState(a,b) ((*(a))->SetPlayState((a),(b))) - - -typedef struct { - /* engine interfaces */ - SLObjectItf engineObject; - SLEngineItf engine; - - /* output mix interfaces */ - SLObjectItf outputMix; - - /* buffer queue player interfaces */ - SLObjectItf bufferQueueObject; - - void *buffer; - ALuint bufferSize; - - ALuint frameSize; -} osl_data; - - -static const ALCchar opensl_device[] = "OpenSL"; - - -static SLuint32 GetChannelMask(enum DevFmtChannels chans) -{ - switch(chans) - { - case DevFmtMono: return SL_SPEAKER_FRONT_CENTER; - case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; - case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| - SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT; - case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| - SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| - SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT; - case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| - SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| - SL_SPEAKER_BACK_CENTER| - SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; - case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| - SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| - SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT| - SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; - case DevFmtX51Side: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| - SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| - SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; - } - return 0; -} - -static const char *res_str(SLresult result) -{ - switch(result) - { - case SL_RESULT_SUCCESS: return "Success"; - case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated"; - case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid"; - case SL_RESULT_MEMORY_FAILURE: return "Memory failure"; - case SL_RESULT_RESOURCE_ERROR: return "Resource error"; - case SL_RESULT_RESOURCE_LOST: return "Resource lost"; - case SL_RESULT_IO_ERROR: return "I/O error"; - case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient"; - case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted"; - case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported"; - case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found"; - case SL_RESULT_PERMISSION_DENIED: return "Permission denied"; - case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported"; - case SL_RESULT_INTERNAL_ERROR: return "Internal error"; - case SL_RESULT_UNKNOWN_ERROR: return "Unknown error"; - case SL_RESULT_OPERATION_ABORTED: return "Operation aborted"; - case SL_RESULT_CONTROL_LOST: return "Control lost"; - case SL_RESULT_READONLY: return "ReadOnly"; - case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported"; - case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible"; - } - return "Unknown error code"; -} - -#define PRINTERR(x, s) do { \ - if((x) != SL_RESULT_SUCCESS) \ - ERR("%s: %s\n", (s), res_str((x))); \ -} while(0) - -/* this callback handler is called every time a buffer finishes playing */ -static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) -{ - ALCdevice *Device = context; - osl_data *data = Device->ExtraData; - SLresult result; - - aluMixData(Device, data->buffer, data->bufferSize/data->frameSize); - - result = (*bq)->Enqueue(bq, data->buffer, data->bufferSize); - PRINTERR(result, "bq->Enqueue"); -} - - -static ALCboolean opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName) -{ - osl_data *data = NULL; - SLresult result; - - if(!deviceName) - deviceName = opensl_device; - else if(strcmp(deviceName, opensl_device) != 0) - return ALC_FALSE; - - data = calloc(1, sizeof(*data)); - if(!data) - { - alcSetError(Device, ALC_OUT_OF_MEMORY); - return ALC_FALSE; - } - - // create engine - result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL); - PRINTERR(result, "slCreateEngine"); - if(SL_RESULT_SUCCESS == result) - { - result = SLObjectItf_Realize(data->engineObject, SL_BOOLEAN_FALSE); - PRINTERR(result, "engine->Realize"); - } - if(SL_RESULT_SUCCESS == result) - { - result = SLObjectItf_GetInterface(data->engineObject, SL_IID_ENGINE, &data->engine); - PRINTERR(result, "engine->GetInterface"); - } - if(SL_RESULT_SUCCESS == result) - { - result = SLEngineItf_CreateOutputMix(data->engine, &data->outputMix, 0, NULL, NULL); - PRINTERR(result, "engine->CreateOutputMix"); - } - if(SL_RESULT_SUCCESS == result) - { - result = SLObjectItf_Realize(data->outputMix, SL_BOOLEAN_FALSE); - PRINTERR(result, "outputMix->Realize"); - } - - if(SL_RESULT_SUCCESS != result) - { - if(data->outputMix != NULL) - SLObjectItf_Destroy(data->outputMix); - data->outputMix = NULL; - - if(data->engineObject != NULL) - SLObjectItf_Destroy(data->engineObject); - data->engineObject = NULL; - data->engine = NULL; - - free(data); - return ALC_FALSE; - } - - Device->szDeviceName = strdup(deviceName); - Device->ExtraData = data; - - return ALC_TRUE; -} - - -static void opensl_close_playback(ALCdevice *Device) -{ - osl_data *data = Device->ExtraData; - - SLObjectItf_Destroy(data->outputMix); - data->outputMix = NULL; - - SLObjectItf_Destroy(data->engineObject); - data->engineObject = NULL; - data->engine = NULL; - - free(data); - Device->ExtraData = NULL; -} - -static ALCboolean opensl_reset_playback(ALCdevice *Device) -{ - osl_data *data = Device->ExtraData; - SLDataLocator_AndroidSimpleBufferQueue loc_bufq; - SLAndroidSimpleBufferQueueItf bufferQueue; - SLDataLocator_OutputMix loc_outmix; - SLDataFormat_PCM format_pcm; - SLDataSource audioSrc; - SLDataSink audioSnk; - SLPlayItf player; - SLInterfaceID id; - SLboolean req; - SLresult result; - ALuint i; - - - Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency; - Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2; - Device->NumUpdates = 2; - - Device->Frequency = 44100; - Device->FmtChans = DevFmtStereo; - Device->FmtType = DevFmtShort; - - SetDefaultWFXChannelOrder(Device); - - - id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - req = SL_BOOLEAN_TRUE; - - loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - loc_bufq.numBuffers = Device->NumUpdates; - - format_pcm.formatType = SL_DATAFORMAT_PCM; - format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans); - format_pcm.samplesPerSec = Device->Frequency * 1000; - format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8; - format_pcm.containerSize = format_pcm.bitsPerSample; - format_pcm.channelMask = GetChannelMask(Device->FmtChans); - format_pcm.endianness = SL_BYTEORDER_NATIVE; - - audioSrc.pLocator = &loc_bufq; - audioSrc.pFormat = &format_pcm; - - loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; - loc_outmix.outputMix = data->outputMix; - audioSnk.pLocator = &loc_outmix; - audioSnk.pFormat = NULL; - - - result = SLEngineItf_CreateAudioPlayer(data->engine, &data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req); - PRINTERR(result, "engine->CreateAudioPlayer"); - if(SL_RESULT_SUCCESS == result) - { - result = SLObjectItf_Realize(data->bufferQueueObject, SL_BOOLEAN_FALSE); - PRINTERR(result, "bufferQueue->Realize"); - } - if(SL_RESULT_SUCCESS == result) - { - result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_BUFFERQUEUE, &bufferQueue); - PRINTERR(result, "bufferQueue->GetInterface"); - } - if(SL_RESULT_SUCCESS == result) - { - result = (*bufferQueue)->RegisterCallback(bufferQueue, opensl_callback, Device); - PRINTERR(result, "bufferQueue->RegisterCallback"); - } - if(SL_RESULT_SUCCESS == result) - { - data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); - data->bufferSize = Device->UpdateSize * data->frameSize; - data->buffer = calloc(1, data->bufferSize); - if(!data->buffer) - { - result = SL_RESULT_MEMORY_FAILURE; - PRINTERR(result, "calloc"); - } - } - /* enqueue the first buffer to kick off the callbacks */ - for(i = 0;i < Device->NumUpdates;i++) - { - if(SL_RESULT_SUCCESS == result) - { - result = (*bufferQueue)->Enqueue(bufferQueue, data->buffer, data->bufferSize); - PRINTERR(result, "bufferQueue->Enqueue"); - } - } - if(SL_RESULT_SUCCESS == result) - { - result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_PLAY, &player); - PRINTERR(result, "bufferQueue->GetInterface"); - } - if(SL_RESULT_SUCCESS == result) - { - result = SLPlayItf_SetPlayState(player, SL_PLAYSTATE_PLAYING); - PRINTERR(result, "player->SetPlayState"); - } - - if(SL_RESULT_SUCCESS != result) - { - if(data->bufferQueueObject != NULL) - SLObjectItf_Destroy(data->bufferQueueObject); - data->bufferQueueObject = NULL; - - free(data->buffer); - data->buffer = NULL; - data->bufferSize = 0; - - return ALC_FALSE; - } - - return ALC_TRUE; -} - - -static void opensl_stop_playback(ALCdevice *Device) -{ - osl_data *data = Device->ExtraData; - - if(data->bufferQueueObject != NULL) - SLObjectItf_Destroy(data->bufferQueueObject); - data->bufferQueueObject = NULL; - - free(data->buffer); - data->buffer = NULL; - data->bufferSize = 0; -} - - -static const BackendFuncs opensl_funcs = { - opensl_open_playback, - opensl_close_playback, - opensl_reset_playback, - opensl_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - - -ALCboolean alc_opensl_init(BackendFuncs *func_list) -{ - *func_list = opensl_funcs; - return ALC_TRUE; -} - -void alc_opensl_deinit(void) -{ -} - -void alc_opensl_probe(enum DevProbe type) -{ - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(opensl_device); - break; - case ALL_DEVICE_PROBE: - AppendAllDeviceList(opensl_device); - break; - case CAPTURE_DEVICE_PROBE: - break; - } -} diff --git a/Alc/oss.c b/Alc/oss.c deleted file mode 100644 index 724b23c2..00000000 --- a/Alc/oss.c +++ /dev/null @@ -1,536 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - -#include - -/* - * The OSS documentation talks about SOUND_MIXER_READ, but the header - * only contains MIXER_READ. Play safe. Same for WRITE. - */ -#ifndef SOUND_MIXER_READ -#define SOUND_MIXER_READ MIXER_READ -#endif -#ifndef SOUND_MIXER_WRITE -#define SOUND_MIXER_WRITE MIXER_WRITE -#endif - -static const ALCchar oss_device[] = "OSS Default"; - -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; - while (x > 1) - { - x >>= 1; - y++; - } - return y; -} - - -static ALuint OSSProc(ALvoid *ptr) -{ - ALCdevice *pDevice = (ALCdevice*)ptr; - oss_data *data = (oss_data*)pDevice->ExtraData; - ALint frameSize; - ssize_t wrote; - - SetRTPriority(); - - frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); - - while(!data->killNow && pDevice->Connected) - { - ALint len = data->data_size; - ALubyte *WritePtr = data->mix_data; - - aluMixData(pDevice, WritePtr, len/frameSize); - while(len > 0 && !data->killNow) - { - wrote = write(data->fd, WritePtr, len); - if(wrote < 0) - { - if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) - { - ERR("write failed: %s\n", strerror(errno)); - aluHandleDisconnect(pDevice); - break; - } - - Sleep(1); - continue; - } - - len -= wrote; - WritePtr += wrote; - } - } - - return 0; -} - -static ALuint OSSCaptureProc(ALvoid *ptr) -{ - ALCdevice *pDevice = (ALCdevice*)ptr; - oss_data *data = (oss_data*)pDevice->ExtraData; - int frameSize; - int amt; - - SetRTPriority(); - - frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); - - while(!data->killNow) - { - amt = read(data->fd, data->mix_data, data->data_size); - if(amt < 0) - { - ERR("read failed: %s\n", strerror(errno)); - aluHandleDisconnect(pDevice); - break; - } - if(amt == 0) - { - Sleep(1); - continue; - } - if(data->doCapture) - WriteRingBuffer(data->ring, data->mix_data, amt/frameSize); - } - - return 0; -} - -static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - char driver[64]; - oss_data *data; - - strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1); - driver[sizeof(driver)-1] = 0; - if(!deviceName) - deviceName = oss_device; - else if(strcmp(deviceName, oss_device) != 0) - return ALC_FALSE; - - data = (oss_data*)calloc(1, sizeof(oss_data)); - data->killNow = 0; - - data->fd = open(driver, O_WRONLY); - if(data->fd == -1) - { - free(data); - ERR("Could not open %s: %s\n", driver, strerror(errno)); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - device->ExtraData = data; - return ALC_TRUE; -} - -static void oss_close_playback(ALCdevice *device) -{ - oss_data *data = (oss_data*)device->ExtraData; - - close(data->fd); - free(data); - device->ExtraData = NULL; -} - -static ALCboolean oss_reset_playback(ALCdevice *device) -{ - oss_data *data = (oss_data*)device->ExtraData; - int numFragmentsLogSize; - int log2FragmentSize; - unsigned int periods; - audio_buf_info info; - ALuint frameSize; - int numChannels; - int ossFormat; - int ossSpeed; - char *err; - - switch(device->FmtType) - { - case DevFmtByte: - ossFormat = AFMT_S8; - break; - case DevFmtUByte: - ossFormat = AFMT_U8; - break; - case DevFmtUShort: - case DevFmtFloat: - device->FmtType = DevFmtShort; - /* fall-through */ - case DevFmtShort: - ossFormat = AFMT_S16_NE; - break; - } - - periods = device->NumUpdates; - numChannels = ChannelsFromDevFmt(device->FmtChans); - frameSize = numChannels * BytesFromDevFmt(device->FmtType); - - ossSpeed = device->Frequency; - log2FragmentSize = log2i(device->UpdateSize * frameSize); - - /* according to the OSS spec, 16 bytes are the minimum */ - if (log2FragmentSize < 4) - log2FragmentSize = 4; - /* Subtract one period since the temp mixing buffer counts as one. Still - * need at least two on the card, though. */ - if(periods > 2) periods--; - numFragmentsLogSize = (periods << 16) | log2FragmentSize; - -#define CHECKERR(func) if((func) < 0) { \ - err = #func; \ - goto err; \ -} - /* 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)); - if(0) - { - err: - ERR("%s failed: %s\n", err, strerror(errno)); - return ALC_FALSE; - } -#undef CHECKERR - - if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) - { - ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); - return ALC_FALSE; - } - - if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) || - (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) || - (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort))) - { - ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat); - return ALC_FALSE; - } - - if(device->Frequency != (ALuint)ossSpeed) - { - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, ossSpeed); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - device->Frequency = ossSpeed; - } - device->UpdateSize = info.fragsize / frameSize; - device->NumUpdates = info.fragments + 1; - - data->data_size = device->UpdateSize * frameSize; - data->mix_data = calloc(1, data->data_size); - - SetDefaultChannelOrder(device); - - data->thread = StartThread(OSSProc, device); - if(data->thread == NULL) - { - free(data->mix_data); - data->mix_data = NULL; - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void oss_stop_playback(ALCdevice *device) -{ - oss_data *data = (oss_data*)device->ExtraData; - - if(!data->thread) - return; - - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - - data->killNow = 0; - if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0) - ERR("Error resetting device: %s\n", strerror(errno)); - - free(data->mix_data); - data->mix_data = NULL; -} - - -static ALCboolean oss_open_capture(ALCdevice *device, const ALCchar *deviceName) -{ - int numFragmentsLogSize; - int log2FragmentSize; - unsigned int periods; - audio_buf_info info; - ALuint frameSize; - int numChannels; - char driver[64]; - oss_data *data; - int ossFormat; - int ossSpeed; - char *err; - - strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1); - driver[sizeof(driver)-1] = 0; - if(!deviceName) - deviceName = oss_device; - else if(strcmp(deviceName, oss_device) != 0) - return ALC_FALSE; - - data = (oss_data*)calloc(1, sizeof(oss_data)); - data->killNow = 0; - - data->fd = open(driver, O_RDONLY); - if(data->fd == -1) - { - free(data); - ERR("Could not open %s: %s\n", driver, strerror(errno)); - return ALC_FALSE; - } - - switch(device->FmtType) - { - case DevFmtByte: - ossFormat = AFMT_S8; - break; - case DevFmtUByte: - ossFormat = AFMT_U8; - break; - case DevFmtShort: - ossFormat = AFMT_S16_NE; - break; - case DevFmtUShort: - case DevFmtFloat: - free(data); - ERR("%s capture samples not supported on OSS\n", DevFmtTypeString(device->FmtType)); - return ALC_FALSE; - } - - periods = 4; - numChannels = ChannelsFromDevFmt(device->FmtChans); - frameSize = numChannels * BytesFromDevFmt(device->FmtType); - ossSpeed = device->Frequency; - log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates * - frameSize / periods); - - /* according to the OSS spec, 16 bytes are the minimum */ - if (log2FragmentSize < 4) - log2FragmentSize = 4; - numFragmentsLogSize = (periods << 16) | log2FragmentSize; - -#define CHECKERR(func) if((func) < 0) { \ - 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)); - if(0) - { - err: - ERR("%s failed: %s\n", err, strerror(errno)); - close(data->fd); - free(data); - return ALC_FALSE; - } -#undef CHECKERR - - 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); - return ALC_FALSE; - } - - if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) || - (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) || - (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); - return ALC_FALSE; - } - - data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates); - if(!data->ring) - { - ERR("Ring buffer create failed\n"); - close(data->fd); - free(data); - return ALC_FALSE; - } - - data->data_size = info.fragsize; - data->mix_data = calloc(1, data->data_size); - - device->ExtraData = data; - data->thread = StartThread(OSSCaptureProc, device); - if(data->thread == NULL) - { - device->ExtraData = NULL; - free(data->mix_data); - free(data); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - return ALC_TRUE; -} - -static void oss_close_capture(ALCdevice *device) -{ - oss_data *data = (oss_data*)device->ExtraData; - data->killNow = 1; - StopThread(data->thread); - - close(data->fd); - - DestroyRingBuffer(data->ring); - - free(data->mix_data); - free(data); - device->ExtraData = NULL; -} - -static void oss_start_capture(ALCdevice *pDevice) -{ - oss_data *data = (oss_data*)pDevice->ExtraData; - data->doCapture = 1; -} - -static void oss_stop_capture(ALCdevice *pDevice) -{ - oss_data *data = (oss_data*)pDevice->ExtraData; - data->doCapture = 0; -} - -static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) -{ - oss_data *data = (oss_data*)pDevice->ExtraData; - if(lSamples <= (ALCuint)RingBufferSize(data->ring)) - ReadRingBuffer(data->ring, pBuffer, lSamples); - else - alcSetError(pDevice, ALC_INVALID_VALUE); -} - -static ALCuint oss_available_samples(ALCdevice *pDevice) -{ - oss_data *data = (oss_data*)pDevice->ExtraData; - return RingBufferSize(data->ring); -} - - -static const BackendFuncs oss_funcs = { - oss_open_playback, - oss_close_playback, - oss_reset_playback, - oss_stop_playback, - oss_open_capture, - oss_close_capture, - oss_start_capture, - oss_stop_capture, - oss_capture_samples, - oss_available_samples -}; - -ALCboolean alc_oss_init(BackendFuncs *func_list) -{ - *func_list = oss_funcs; - return ALC_TRUE; -} - -void alc_oss_deinit(void) -{ -} - -void alc_oss_probe(enum DevProbe type) -{ - switch(type) - { - case DEVICE_PROBE: - { -#ifdef HAVE_STAT - struct stat buf; - if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0) -#endif - AppendDeviceList(oss_device); - } - break; - - case ALL_DEVICE_PROBE: - { -#ifdef HAVE_STAT - struct stat buf; - if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0) -#endif - AppendAllDeviceList(oss_device); - } - break; - - case CAPTURE_DEVICE_PROBE: - { -#ifdef HAVE_STAT - struct stat buf; - if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf) == 0) -#endif - AppendCaptureDeviceList(oss_device); - } - break; - } -} diff --git a/Alc/portaudio.c b/Alc/portaudio.c deleted file mode 100644 index 4f3dfd5f..00000000 --- a/Alc/portaudio.c +++ /dev/null @@ -1,449 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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 -#include -#include -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - -#include - - -static const ALCchar pa_device[] = "PortAudio Default"; - - -static void *pa_handle; -#ifdef HAVE_DYNLOAD -#define MAKE_FUNC(x) static typeof(x) * p##x -MAKE_FUNC(Pa_Initialize); -MAKE_FUNC(Pa_Terminate); -MAKE_FUNC(Pa_GetErrorText); -MAKE_FUNC(Pa_StartStream); -MAKE_FUNC(Pa_StopStream); -MAKE_FUNC(Pa_OpenStream); -MAKE_FUNC(Pa_CloseStream); -MAKE_FUNC(Pa_GetDefaultOutputDevice); -MAKE_FUNC(Pa_GetStreamInfo); -#undef MAKE_FUNC - -#define Pa_Initialize pPa_Initialize -#define Pa_Terminate pPa_Terminate -#define Pa_GetErrorText pPa_GetErrorText -#define Pa_StartStream pPa_StartStream -#define Pa_StopStream pPa_StopStream -#define Pa_OpenStream pPa_OpenStream -#define Pa_CloseStream pPa_CloseStream -#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice -#define Pa_GetStreamInfo pPa_GetStreamInfo -#endif - -static ALCboolean pa_load(void) -{ - if(!pa_handle) - { - PaError err; - -#ifdef HAVE_DYNLOAD -#ifdef _WIN32 -# define PALIB "portaudio.dll" -#elif defined(__APPLE__) && defined(__MACH__) -# define PALIB "libportaudio.2.dylib" -#elif defined(__OpenBSD__) -# define PALIB "libportaudio.so" -#else -# define PALIB "libportaudio.so.2" -#endif - - pa_handle = LoadLib(PALIB); - if(!pa_handle) - return ALC_FALSE; - -#define LOAD_FUNC(f) do { \ - p##f = GetSymbol(pa_handle, #f); \ - if(p##f == NULL) \ - { \ - CloseLib(pa_handle); \ - pa_handle = NULL; \ - return ALC_FALSE; \ - } \ -} while(0) - LOAD_FUNC(Pa_Initialize); - LOAD_FUNC(Pa_Terminate); - LOAD_FUNC(Pa_GetErrorText); - LOAD_FUNC(Pa_StartStream); - LOAD_FUNC(Pa_StopStream); - LOAD_FUNC(Pa_OpenStream); - LOAD_FUNC(Pa_CloseStream); - LOAD_FUNC(Pa_GetDefaultOutputDevice); - LOAD_FUNC(Pa_GetStreamInfo); -#undef LOAD_FUNC -#else - pa_handle = (void*)0xDEADBEEF; -#endif - - if((err=Pa_Initialize()) != paNoError) - { - ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); - CloseLib(pa_handle); - pa_handle = NULL; - return ALC_FALSE; - } - } - return ALC_TRUE; -} - - -typedef struct { - PaStream *stream; - ALuint update_size; - - RingBuffer *ring; -} pa_data; - - -static int pa_callback(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags 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) -{ - ALCdevice *device = (ALCdevice*)userData; - pa_data *data = (pa_data*)device->ExtraData; - - (void)outputBuffer; - (void)timeInfo; - (void)statusFlags; - - WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer); - return 0; -} - - -static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - PaStreamParameters outParams; - pa_data *data; - PaError err; - - if(!deviceName) - deviceName = pa_device; - else if(strcmp(deviceName, pa_device) != 0) - return ALC_FALSE; - - data = (pa_data*)calloc(1, sizeof(pa_data)); - data->update_size = device->UpdateSize; - - device->ExtraData = data; - - outParams.device = GetConfigValueInt("port", "device", -1); - if(outParams.device < 0) - outParams.device = Pa_GetDefaultOutputDevice(); - outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) / - (float)device->Frequency; - outParams.hostApiSpecificStreamInfo = NULL; - - switch(device->FmtType) - { - case DevFmtByte: - outParams.sampleFormat = paInt8; - break; - case DevFmtUByte: - outParams.sampleFormat = paUInt8; - break; - case DevFmtUShort: - device->FmtType = DevFmtShort; - /* fall-through */ - case DevFmtShort: - outParams.sampleFormat = paInt16; - break; - case DevFmtFloat: - outParams.sampleFormat = paFloat32; - break; - } - outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2); - - SetDefaultChannelOrder(device); - - err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency, - device->UpdateSize, paNoFlag, pa_callback, device); - if(err != paNoError) - { - ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); - device->ExtraData = NULL; - free(data); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - - if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans)) - { - if(outParams.channelCount != 1 && outParams.channelCount != 2) - { - ERR("Unhandled channel count: %u\n", outParams.channelCount); - Pa_CloseStream(data->stream); - device->ExtraData = NULL; - free(data); - return ALC_FALSE; - } - if((device->Flags&DEVICE_CHANNELS_REQUEST)) - ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo); - } - - return ALC_TRUE; -} - -static void pa_close_playback(ALCdevice *device) -{ - pa_data *data = (pa_data*)device->ExtraData; - PaError err; - - err = Pa_CloseStream(data->stream); - if(err != paNoError) - ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); - - free(data); - device->ExtraData = NULL; -} - -static ALCboolean pa_reset_playback(ALCdevice *device) -{ - pa_data *data = (pa_data*)device->ExtraData; - const PaStreamInfo *streamInfo; - PaError err; - - streamInfo = Pa_GetStreamInfo(data->stream); - if(device->Frequency != streamInfo->sampleRate) - { - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("PortAudio does not support changing sample rates (wanted %dhz, got %.1fhz)\n", device->Frequency, streamInfo->sampleRate); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - device->Frequency = streamInfo->sampleRate; - } - device->UpdateSize = data->update_size; - - err = Pa_StartStream(data->stream); - if(err != paNoError) - { - ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err)); - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void pa_stop_playback(ALCdevice *device) -{ - pa_data *data = (pa_data*)device->ExtraData; - PaError err; - - err = Pa_StopStream(data->stream); - if(err != paNoError) - ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); -} - - -static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName) -{ - PaStreamParameters inParams; - ALuint frame_size; - pa_data *data; - PaError err; - - if(!deviceName) - deviceName = pa_device; - else if(strcmp(deviceName, pa_device) != 0) - return ALC_FALSE; - - data = (pa_data*)calloc(1, sizeof(pa_data)); - if(data == NULL) - { - alcSetError(device, ALC_OUT_OF_MEMORY); - return ALC_FALSE; - } - - frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates); - if(data->ring == NULL) - { - alcSetError(device, ALC_OUT_OF_MEMORY); - goto error; - } - - inParams.device = GetConfigValueInt("port", "capture", -1); - if(inParams.device < 0) - inParams.device = Pa_GetDefaultOutputDevice(); - inParams.suggestedLatency = 0.0f; - inParams.hostApiSpecificStreamInfo = NULL; - - switch(device->FmtType) - { - case DevFmtByte: - inParams.sampleFormat = paInt8; - break; - case DevFmtUByte: - inParams.sampleFormat = paUInt8; - break; - case DevFmtShort: - inParams.sampleFormat = paInt16; - break; - case DevFmtFloat: - inParams.sampleFormat = paFloat32; - break; - case DevFmtUShort: - ERR("Unsigned short samples not supported\n"); - goto error; - } - inParams.channelCount = ChannelsFromDevFmt(device->FmtChans); - - err = Pa_OpenStream(&data->stream, &inParams, NULL, device->Frequency, - paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device); - if(err != paNoError) - { - ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); - goto error; - } - - device->szDeviceName = strdup(deviceName); - - device->ExtraData = data; - return ALC_TRUE; - -error: - DestroyRingBuffer(data->ring); - free(data); - return ALC_FALSE; -} - -static void pa_close_capture(ALCdevice *device) -{ - pa_data *data = (pa_data*)device->ExtraData; - PaError err; - - err = Pa_CloseStream(data->stream); - if(err != paNoError) - ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); - - free(data); - device->ExtraData = NULL; -} - -static void pa_start_capture(ALCdevice *device) -{ - pa_data *data = device->ExtraData; - PaError err; - - err = Pa_StartStream(data->stream); - if(err != paNoError) - ERR("Error starting stream: %s\n", Pa_GetErrorText(err)); -} - -static void pa_stop_capture(ALCdevice *device) -{ - pa_data *data = (pa_data*)device->ExtraData; - PaError err; - - err = Pa_StopStream(data->stream); - if(err != paNoError) - ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); -} - -static void pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) -{ - pa_data *data = device->ExtraData; - if(samples <= (ALCuint)RingBufferSize(data->ring)) - ReadRingBuffer(data->ring, buffer, samples); - else - alcSetError(device, ALC_INVALID_VALUE); -} - -static ALCuint pa_available_samples(ALCdevice *device) -{ - pa_data *data = device->ExtraData; - return RingBufferSize(data->ring); -} - - -static const BackendFuncs pa_funcs = { - pa_open_playback, - pa_close_playback, - pa_reset_playback, - pa_stop_playback, - pa_open_capture, - pa_close_capture, - pa_start_capture, - pa_stop_capture, - pa_capture_samples, - pa_available_samples -}; - -ALCboolean alc_pa_init(BackendFuncs *func_list) -{ - if(!pa_load()) - return ALC_FALSE; - *func_list = pa_funcs; - return ALC_TRUE; -} - -void alc_pa_deinit(void) -{ - if(pa_handle) - { - Pa_Terminate(); -#ifdef HAVE_DYNLOAD - CloseLib(pa_handle); -#endif - pa_handle = NULL; - } -} - -void alc_pa_probe(enum DevProbe type) -{ - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(pa_device); - break; - case ALL_DEVICE_PROBE: - AppendAllDeviceList(pa_device); - break; - case CAPTURE_DEVICE_PROBE: - AppendCaptureDeviceList(pa_device); - break; - } -} diff --git a/Alc/pulseaudio.c b/Alc/pulseaudio.c deleted file mode 100644 index 39df3282..00000000 --- a/Alc/pulseaudio.c +++ /dev/null @@ -1,1430 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2009 by Konstantinos Natsakis - * Copyright (C) 2010 by Chris Robinson - * 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 "alMain.h" - -#include - -#if PA_API_VERSION == 11 -#define PA_STREAM_ADJUST_LATENCY 0x2000U -#define PA_STREAM_EARLY_REQUESTS 0x4000U -static __inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) -{ - return (x == PA_STREAM_CREATING || x == PA_STREAM_READY); -} -static __inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) -{ - return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING || - x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY); -} -#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD -#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD -#elif PA_API_VERSION != 12 -#error Invalid PulseAudio API version -#endif - -#ifndef PA_CHECK_VERSION -#define PA_CHECK_VERSION(major,minor,micro) \ - ((PA_MAJOR > (major)) || \ - (PA_MAJOR == (major) && PA_MINOR > (minor)) || \ - (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro))) -#endif - -static void *pa_handle; -#ifdef HAVE_DYNLOAD -#define MAKE_FUNC(x) static typeof(x) * p##x -MAKE_FUNC(pa_context_unref); -MAKE_FUNC(pa_sample_spec_valid); -MAKE_FUNC(pa_stream_drop); -MAKE_FUNC(pa_strerror); -MAKE_FUNC(pa_context_get_state); -MAKE_FUNC(pa_stream_get_state); -MAKE_FUNC(pa_threaded_mainloop_signal); -MAKE_FUNC(pa_stream_peek); -MAKE_FUNC(pa_threaded_mainloop_wait); -MAKE_FUNC(pa_threaded_mainloop_unlock); -MAKE_FUNC(pa_threaded_mainloop_in_thread); -MAKE_FUNC(pa_context_new); -MAKE_FUNC(pa_threaded_mainloop_stop); -MAKE_FUNC(pa_context_disconnect); -MAKE_FUNC(pa_threaded_mainloop_start); -MAKE_FUNC(pa_threaded_mainloop_get_api); -MAKE_FUNC(pa_context_set_state_callback); -MAKE_FUNC(pa_stream_write); -MAKE_FUNC(pa_xfree); -MAKE_FUNC(pa_stream_connect_record); -MAKE_FUNC(pa_stream_connect_playback); -MAKE_FUNC(pa_stream_readable_size); -MAKE_FUNC(pa_stream_writable_size); -MAKE_FUNC(pa_stream_cork); -MAKE_FUNC(pa_stream_is_suspended); -MAKE_FUNC(pa_stream_get_device_name); -MAKE_FUNC(pa_path_get_filename); -MAKE_FUNC(pa_get_binary_name); -MAKE_FUNC(pa_threaded_mainloop_free); -MAKE_FUNC(pa_context_errno); -MAKE_FUNC(pa_xmalloc); -MAKE_FUNC(pa_stream_unref); -MAKE_FUNC(pa_threaded_mainloop_accept); -MAKE_FUNC(pa_stream_set_write_callback); -MAKE_FUNC(pa_threaded_mainloop_new); -MAKE_FUNC(pa_context_connect); -MAKE_FUNC(pa_stream_set_buffer_attr); -MAKE_FUNC(pa_stream_get_buffer_attr); -MAKE_FUNC(pa_stream_get_sample_spec); -MAKE_FUNC(pa_stream_get_time); -MAKE_FUNC(pa_stream_set_read_callback); -MAKE_FUNC(pa_stream_set_state_callback); -MAKE_FUNC(pa_stream_set_moved_callback); -MAKE_FUNC(pa_stream_set_underflow_callback); -MAKE_FUNC(pa_stream_new); -MAKE_FUNC(pa_stream_disconnect); -MAKE_FUNC(pa_threaded_mainloop_lock); -MAKE_FUNC(pa_channel_map_init_auto); -MAKE_FUNC(pa_channel_map_parse); -MAKE_FUNC(pa_channel_map_snprint); -MAKE_FUNC(pa_channel_map_equal); -MAKE_FUNC(pa_context_get_server_info); -MAKE_FUNC(pa_context_get_sink_info_by_name); -MAKE_FUNC(pa_context_get_sink_info_list); -MAKE_FUNC(pa_context_get_source_info_list); -MAKE_FUNC(pa_operation_get_state); -MAKE_FUNC(pa_operation_unref); -#if PA_CHECK_VERSION(0,9,15) -MAKE_FUNC(pa_channel_map_superset); -MAKE_FUNC(pa_stream_set_buffer_attr_callback); -#endif -#if PA_CHECK_VERSION(0,9,16) -MAKE_FUNC(pa_stream_begin_write); -#endif -#undef MAKE_FUNC - -#define pa_context_unref ppa_context_unref -#define pa_sample_spec_valid ppa_sample_spec_valid -#define pa_stream_drop ppa_stream_drop -#define pa_strerror ppa_strerror -#define pa_context_get_state ppa_context_get_state -#define pa_stream_get_state ppa_stream_get_state -#define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal -#define pa_stream_peek ppa_stream_peek -#define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait -#define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock -#define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread -#define pa_context_new ppa_context_new -#define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop -#define pa_context_disconnect ppa_context_disconnect -#define pa_threaded_mainloop_start ppa_threaded_mainloop_start -#define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api -#define pa_context_set_state_callback ppa_context_set_state_callback -#define pa_stream_write ppa_stream_write -#define pa_xfree ppa_xfree -#define pa_stream_connect_record ppa_stream_connect_record -#define pa_stream_connect_playback ppa_stream_connect_playback -#define pa_stream_readable_size ppa_stream_readable_size -#define pa_stream_writable_size ppa_stream_writable_size -#define pa_stream_cork ppa_stream_cork -#define pa_stream_is_suspended ppa_stream_is_suspended -#define pa_stream_get_device_name ppa_stream_get_device_name -#define pa_path_get_filename ppa_path_get_filename -#define pa_get_binary_name ppa_get_binary_name -#define pa_threaded_mainloop_free ppa_threaded_mainloop_free -#define pa_context_errno ppa_context_errno -#define pa_xmalloc ppa_xmalloc -#define pa_stream_unref ppa_stream_unref -#define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept -#define pa_stream_set_write_callback ppa_stream_set_write_callback -#define pa_threaded_mainloop_new ppa_threaded_mainloop_new -#define pa_context_connect ppa_context_connect -#define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr -#define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr -#define pa_stream_get_sample_spec ppa_stream_get_sample_spec -#define pa_stream_get_time ppa_stream_get_time -#define pa_stream_set_read_callback ppa_stream_set_read_callback -#define pa_stream_set_state_callback ppa_stream_set_state_callback -#define pa_stream_set_moved_callback ppa_stream_set_moved_callback -#define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback -#define pa_stream_new ppa_stream_new -#define pa_stream_disconnect ppa_stream_disconnect -#define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock -#define pa_channel_map_init_auto ppa_channel_map_init_auto -#define pa_channel_map_parse ppa_channel_map_parse -#define pa_channel_map_snprint ppa_channel_map_snprint -#define pa_channel_map_equal ppa_channel_map_equal -#define pa_context_get_server_info ppa_context_get_server_info -#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name -#define pa_context_get_sink_info_list ppa_context_get_sink_info_list -#define pa_context_get_source_info_list ppa_context_get_source_info_list -#define pa_operation_get_state ppa_operation_get_state -#define pa_operation_unref ppa_operation_unref -#if PA_CHECK_VERSION(0,9,15) -#define pa_channel_map_superset ppa_channel_map_superset -#define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback -#endif -#if PA_CHECK_VERSION(0,9,16) -#define pa_stream_begin_write ppa_stream_begin_write -#endif - -#endif - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -typedef struct { - char *device_name; - - ALCuint samples; - ALCuint frame_size; - - RingBuffer *ring; - - 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 const ALCchar pulse_device[] = "PulseAudio Default"; -static DevMap *allDevNameMap; -static ALuint numDevNames; -static DevMap *allCaptureDevNameMap; -static ALuint numCaptureDevNames; -static pa_context_flags_t pulse_ctx_flags; - - -static void context_state_callback(pa_context *context, void *pdata) -{ - pa_threaded_mainloop *loop = pdata; - pa_context_state_t state; - - state = pa_context_get_state(context); - if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state)) - pa_threaded_mainloop_signal(loop, 0); -} - -static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) -{ - 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); - - context = pa_context_new(pa_threaded_mainloop_get_api(loop), name); - if(!context) - { - ERR("pa_context_new() failed\n"); - return NULL; - } - - 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_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 ALCboolean pulse_load(void) //{{{ -{ - ALCboolean ret = ALC_FALSE; - if(!pa_handle) - { - pa_threaded_mainloop *loop; - -#ifdef HAVE_DYNLOAD - -#ifdef _WIN32 -#define PALIB "libpulse-0.dll" -#elif defined(__APPLE__) && defined(__MACH__) -#define PALIB "libpulse.0.dylib" -#else -#define PALIB "libpulse.so.0" -#endif - pa_handle = LoadLib(PALIB); - if(!pa_handle) - return ALC_FALSE; - -#define LOAD_FUNC(x) do { \ - p##x = GetSymbol(pa_handle, #x); \ - if(!(p##x)) { \ - CloseLib(pa_handle); \ - pa_handle = NULL; \ - return ALC_FALSE; \ - } \ -} while(0) - LOAD_FUNC(pa_context_unref); - LOAD_FUNC(pa_sample_spec_valid); - LOAD_FUNC(pa_stream_drop); - LOAD_FUNC(pa_strerror); - LOAD_FUNC(pa_context_get_state); - LOAD_FUNC(pa_stream_get_state); - LOAD_FUNC(pa_threaded_mainloop_signal); - LOAD_FUNC(pa_stream_peek); - LOAD_FUNC(pa_threaded_mainloop_wait); - LOAD_FUNC(pa_threaded_mainloop_unlock); - LOAD_FUNC(pa_threaded_mainloop_in_thread); - LOAD_FUNC(pa_context_new); - LOAD_FUNC(pa_threaded_mainloop_stop); - LOAD_FUNC(pa_context_disconnect); - LOAD_FUNC(pa_threaded_mainloop_start); - LOAD_FUNC(pa_threaded_mainloop_get_api); - LOAD_FUNC(pa_context_set_state_callback); - LOAD_FUNC(pa_stream_write); - LOAD_FUNC(pa_xfree); - LOAD_FUNC(pa_stream_connect_record); - LOAD_FUNC(pa_stream_connect_playback); - LOAD_FUNC(pa_stream_readable_size); - LOAD_FUNC(pa_stream_writable_size); - LOAD_FUNC(pa_stream_cork); - LOAD_FUNC(pa_stream_is_suspended); - LOAD_FUNC(pa_stream_get_device_name); - LOAD_FUNC(pa_path_get_filename); - LOAD_FUNC(pa_get_binary_name); - LOAD_FUNC(pa_threaded_mainloop_free); - LOAD_FUNC(pa_context_errno); - LOAD_FUNC(pa_xmalloc); - LOAD_FUNC(pa_stream_unref); - LOAD_FUNC(pa_threaded_mainloop_accept); - LOAD_FUNC(pa_stream_set_write_callback); - LOAD_FUNC(pa_threaded_mainloop_new); - LOAD_FUNC(pa_context_connect); - LOAD_FUNC(pa_stream_set_buffer_attr); - LOAD_FUNC(pa_stream_get_buffer_attr); - LOAD_FUNC(pa_stream_get_sample_spec); - LOAD_FUNC(pa_stream_get_time); - LOAD_FUNC(pa_stream_set_read_callback); - LOAD_FUNC(pa_stream_set_state_callback); - LOAD_FUNC(pa_stream_set_moved_callback); - LOAD_FUNC(pa_stream_set_underflow_callback); - LOAD_FUNC(pa_stream_new); - LOAD_FUNC(pa_stream_disconnect); - LOAD_FUNC(pa_threaded_mainloop_lock); - LOAD_FUNC(pa_channel_map_init_auto); - LOAD_FUNC(pa_channel_map_parse); - LOAD_FUNC(pa_channel_map_snprint); - LOAD_FUNC(pa_channel_map_equal); - LOAD_FUNC(pa_context_get_server_info); - LOAD_FUNC(pa_context_get_sink_info_by_name); - LOAD_FUNC(pa_context_get_sink_info_list); - LOAD_FUNC(pa_context_get_source_info_list); - LOAD_FUNC(pa_operation_get_state); - LOAD_FUNC(pa_operation_unref); -#undef LOAD_FUNC -#define LOAD_OPTIONAL_FUNC(x) do { \ - p##x = GetSymbol(pa_handle, #x); \ -} while(0) -#if PA_CHECK_VERSION(0,9,15) - LOAD_OPTIONAL_FUNC(pa_channel_map_superset); - LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback); -#endif -#if PA_CHECK_VERSION(0,9,16) - LOAD_OPTIONAL_FUNC(pa_stream_begin_write); -#endif -#undef LOAD_OPTIONAL_FUNC - -#else /* HAVE_DYNLOAD */ - pa_handle = (void*)0xDEADBEEF; -#endif - - 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_TRUE); - if(context) - { - ret = ALC_TRUE; - - 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); - - if(!ret) - { -#ifdef HAVE_DYNLOAD - CloseLib(pa_handle); -#endif - pa_handle = NULL; - } - } - return ret; -} //}}} - -// PulseAudio Event Callbacks //{{{ -static void stream_state_callback(pa_stream *stream, void *pdata) //{{{ -{ - pa_threaded_mainloop *loop = pdata; - pa_stream_state_t state; - - state = pa_stream_get_state(stream); - if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state)) - pa_threaded_mainloop_signal(loop, 0); -}//}}} - -static void stream_signal_callback(pa_stream *stream, void *pdata) //{{{ -{ - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; - (void)stream; - - pa_threaded_mainloop_signal(data->loop, 0); -}//}}} - -static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{ -{ - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; - - LockDevice(Device); - - data->attr = *(pa_stream_get_buffer_attr(stream)); - Device->UpdateSize = data->attr.minreq / data->frame_size; - Device->NumUpdates = (data->attr.tlength/data->frame_size) / Device->UpdateSize; - if(Device->NumUpdates <= 1) - { - Device->NumUpdates = 1; - ERR("PulseAudio returned minreq > tlength/2; expect break up\n"); - } - - UnlockDevice(Device); -}//}}} - -static void stream_device_callback(pa_stream *stream, void *pdata) //{{{ -{ - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; - - free(data->device_name); - data->device_name = strdup(pa_stream_get_device_name(stream)); -}//}}} - -static void context_state_callback2(pa_context *context, void *pdata) //{{{ -{ - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; - - if(pa_context_get_state(context) == PA_CONTEXT_FAILED) - { - ERR("Received context failure!\n"); - aluHandleDisconnect(Device); - } - pa_threaded_mainloop_signal(data->loop, 0); -}//}}} - -static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{ -{ - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; - - if(pa_stream_get_state(stream) == PA_STREAM_FAILED) - { - ERR("Received stream failure!\n"); - aluHandleDisconnect(Device); - } - 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_threaded_mainloop_signal(data->loop, 0); -}//}}} - -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) - { - pa_threaded_mainloop_signal(data->loop, 0); - return; - } - - for(i = 0;chanmaps[i].str;i++) - { - pa_channel_map map; - if(!pa_channel_map_parse(&map, chanmaps[i].str)) - continue; - - 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_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 void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{ -{ - pa_threaded_mainloop *loop = pdata; - char str[1024]; - void *temp; - int count; - ALuint i; - - (void)context; - - if(eol) - { - pa_threaded_mainloop_signal(loop, 0); - return; - } - - count = 0; - do { - if(count == 0) - snprintf(str, sizeof(str), "%s", info->description); - else - snprintf(str, sizeof(str), "%s #%d", info->description, count+1); - count++; - - for(i = 0;i < numDevNames;i++) - { - if(strcmp(str, allDevNameMap[i].name) == 0) - break; - } - } while(i != numDevNames); - - temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap)); - if(temp) - { - allDevNameMap = temp; - allDevNameMap[numDevNames].name = strdup(str); - allDevNameMap[numDevNames].device_name = strdup(info->name); - numDevNames++; - } -}//}}} - -static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) //{{{ -{ - pa_threaded_mainloop *loop = pdata; - char str[1024]; - void *temp; - int count; - ALuint i; - - (void)context; - - if(eol) - { - pa_threaded_mainloop_signal(loop, 0); - return; - } - - count = 0; - do { - if(count == 0) - snprintf(str, sizeof(str), "%s", info->description); - else - snprintf(str, sizeof(str), "%s #%d", info->description, count+1); - count++; - - for(i = 0;i < numCaptureDevNames;i++) - { - if(strcmp(str, allCaptureDevNameMap[i].name) == 0) - break; - } - } while(i != numCaptureDevNames); - - temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap)); - if(temp) - { - allCaptureDevNameMap = temp; - allCaptureDevNameMap[numCaptureDevNames].name = strdup(str); - allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name); - numCaptureDevNames++; - } -}//}}} -//}}} - -// PulseAudio I/O Callbacks //{{{ -static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{ -{ - ALCdevice *Device = pdata; - pulse_data *data = Device->ExtraData; - (void)stream; - (void)len; - - pa_threaded_mainloop_signal(data->loop, 0); -} //}}} -//}}} - -static ALuint PulseProc(ALvoid *param) -{ - ALCdevice *Device = param; - pulse_data *data = Device->ExtraData; - ssize_t len; - - SetRTPriority(); - - pa_threaded_mainloop_lock(data->loop); - do { - len = (Device->Connected ? pa_stream_writable_size(data->stream) : 0); - len -= len%(Device->UpdateSize*data->frame_size); - if(len == 0) - { - pa_threaded_mainloop_wait(data->loop); - continue; - } - - while(len > 0) - { - size_t newlen = len; - void *buf; - pa_free_cb_t free_func = NULL; - -#if PA_CHECK_VERSION(0,9,16) - if(!pa_stream_begin_write || - pa_stream_begin_write(data->stream, &buf, &newlen) < 0) -#endif - { - buf = pa_xmalloc(newlen); - free_func = pa_xfree; - } - pa_threaded_mainloop_unlock(data->loop); - - aluMixData(Device, buf, newlen/data->frame_size); - - pa_threaded_mainloop_lock(data->loop); - pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); - len -= newlen; - } - } while(Device->Connected && !data->killNow); - pa_threaded_mainloop_unlock(data->loop); - - return 0; -} - -static pa_stream *connect_playback_stream(ALCdevice *device, - pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, - pa_channel_map *chanmap) -{ - pulse_data *data = device->ExtraData; - pa_stream_state_t state; - pa_stream *stream; - - stream = pa_stream_new(data->context, "Playback Stream", spec, chanmap); - if(!stream) - { - ERR("pa_stream_new() failed: %s\n", - pa_strerror(pa_context_errno(data->context))); - return NULL; - } - - pa_stream_set_state_callback(stream, stream_state_callback, data->loop); - - if(pa_stream_connect_playback(stream, data->device_name, attr, flags, NULL, NULL) < 0) - { - ERR("Stream did not connect: %s\n", - pa_strerror(pa_context_errno(data->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(data->context))); - pa_stream_unref(stream); - return NULL; - } - - pa_threaded_mainloop_wait(data->loop); - } - pa_stream_set_state_callback(stream, NULL, NULL); - - return stream; -} - -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) - { - allDevNameMap[0].name = strdup(pulse_device); - allDevNameMap[0].device_name = NULL; - numDevNames = 1; - - o = pa_context_get_sink_info_list(context, sink_device_callback, loop); - } - else - { - allCaptureDevNameMap[0].name = strdup(pulse_device); - allCaptureDevNameMap[0].device_name = NULL; - numCaptureDevNames = 1; - - o = pa_context_get_source_info_list(context, source_device_callback, loop); - } - while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(loop); - pa_operation_unref(o); - - 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 ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{ -{ - 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); - - device->szDeviceName = strdup(device_name); - - 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_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); - - DestroyRingBuffer(data->ring); - free(data->device_name); - - device->ExtraData = NULL; - pa_xfree(data); -} //}}} -//}}} - -// OpenAL {{{ -static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{ -{ - char *pulse_name = NULL; - pa_sample_spec spec; - pulse_data *data; - - if(!allDevNameMap) - probe_devices(AL_FALSE); - - if(!device_name && numDevNames > 0) - device_name = allDevNameMap[0].name; - else - { - ALuint i; - - for(i = 0;i < numDevNames;i++) - { - if(strcmp(device_name, allDevNameMap[i].name) == 0) - { - pulse_name = allDevNameMap[i].device_name; - break; - } - } - if(i == numDevNames) - return ALC_FALSE; - } - - if(pulse_open(device, device_name) == ALC_FALSE) - return ALC_FALSE; - - data = device->ExtraData; - - pa_threaded_mainloop_lock(data->loop); - - spec.format = PA_SAMPLE_S16NE; - spec.rate = 44100; - spec.channels = 2; - - data->device_name = pulse_name; - pa_stream *stream = connect_playback_stream(device, 0, NULL, &spec, NULL); - if(!stream) - { - pa_threaded_mainloop_unlock(data->loop); - goto fail; - } - - if(pa_stream_is_suspended(stream)) - { - ERR("Device is suspended\n"); - pa_stream_disconnect(stream); - pa_stream_unref(stream); - pa_threaded_mainloop_unlock(data->loop); - goto fail; - } - data->device_name = strdup(pa_stream_get_device_name(stream)); - - pa_stream_disconnect(stream); - pa_stream_unref(stream); - - pa_threaded_mainloop_unlock(data->loop); - - return ALC_TRUE; - -fail: - pulse_close(device); - return ALC_FALSE; -} //}}} - -static void pulse_close_playback(ALCdevice *device) //{{{ -{ - pulse_close(device); -} //}}} - -static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{ -{ - pulse_data *data = device->ExtraData; - pa_stream_flags_t flags = 0; - pa_channel_map chanmap; - - pa_threaded_mainloop_lock(data->loop); - - 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); - while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(data->loop); - pa_operation_unref(o); - } - if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) - flags |= PA_STREAM_FIX_RATE; - - data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - data->attr.prebuf = -1; - data->attr.fragsize = -1; - data->attr.minreq = device->UpdateSize * data->frame_size; - data->attr.tlength = data->attr.minreq * device->NumUpdates; - if(data->attr.tlength < data->attr.minreq*2) - data->attr.tlength = data->attr.minreq*2; - data->attr.maxlength = data->attr.tlength; - flags |= PA_STREAM_EARLY_REQUESTS; - flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; - - switch(device->FmtType) - { - case DevFmtByte: - device->FmtType = DevFmtUByte; - /* fall-through */ - case DevFmtUByte: - data->spec.format = PA_SAMPLE_U8; - break; - case DevFmtUShort: - device->FmtType = DevFmtShort; - /* fall-through */ - case DevFmtShort: - data->spec.format = PA_SAMPLE_S16NE; - break; - case DevFmtFloat: - data->spec.format = PA_SAMPLE_FLOAT32NE; - break; - } - data->spec.rate = device->Frequency; - data->spec.channels = ChannelsFromDevFmt(device->FmtChans); - - if(pa_sample_spec_valid(&data->spec) == 0) - { - ERR("Invalid sample format\n"); - pa_threaded_mainloop_unlock(data->loop); - return ALC_FALSE; - } - - if(!pa_channel_map_init_auto(&chanmap, data->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); - return ALC_FALSE; - } - SetDefaultWFXChannelOrder(device); - - data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap); - if(!data->stream) - { - pa_threaded_mainloop_unlock(data->loop); - return ALC_FALSE; - } - - pa_stream_set_state_callback(data->stream, stream_state_callback2, device); - - data->spec = *(pa_stream_get_sample_spec(data->stream)); - if(device->Frequency != data->spec.rate) - { - pa_operation *o; - - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - - /* Server updated our playback rate, so modify the buffer attribs - * accordingly. */ - data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) * - data->spec.rate / device->Frequency * data->frame_size; - data->attr.tlength = data->attr.minreq * device->NumUpdates; - data->attr.maxlength = data->attr.tlength; - - o = pa_stream_set_buffer_attr(data->stream, &data->attr, - stream_success_callback, device); - while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(data->loop); - pa_operation_unref(o); - - device->Frequency = data->spec.rate; - } - - stream_buffer_attr_callback(data->stream, 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); -#endif - pa_stream_set_moved_callback(data->stream, stream_device_callback, device); - pa_stream_set_write_callback(data->stream, stream_write_callback, device); - pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device); - - data->thread = StartThread(PulseProc, device); - if(!data->thread) - { -#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_set_moved_callback(data->stream, NULL, NULL); - pa_stream_set_write_callback(data->stream, NULL, NULL); - pa_stream_set_underflow_callback(data->stream, NULL, NULL); - pa_stream_disconnect(data->stream); - pa_stream_unref(data->stream); - data->stream = NULL; - - pa_threaded_mainloop_unlock(data->loop); - return ALC_FALSE; - } - - pa_threaded_mainloop_unlock(data->loop); - return ALC_TRUE; -} //}}} - -static void pulse_stop_playback(ALCdevice *device) //{{{ -{ - pulse_data *data = device->ExtraData; - - if(!data->stream) - return; - - data->killNow = AL_TRUE; - if(data->thread) - { - pa_threaded_mainloop_signal(data->loop, 0); - StopThread(data->thread); - data->thread = NULL; - } - data->killNow = AL_FALSE; - - pa_threaded_mainloop_lock(data->loop); - -#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_set_moved_callback(data->stream, NULL, NULL); - pa_stream_set_write_callback(data->stream, NULL, NULL); - pa_stream_set_underflow_callback(data->stream, NULL, NULL); - pa_stream_disconnect(data->stream); - pa_stream_unref(data->stream); - data->stream = NULL; - - pa_threaded_mainloop_unlock(data->loop); -} //}}} - - -static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{ -{ - char *pulse_name = NULL; - pulse_data *data; - pa_stream_flags_t flags = 0; - pa_stream_state_t state; - pa_channel_map chanmap; - - if(!allCaptureDevNameMap) - probe_devices(AL_TRUE); - - if(!device_name && numCaptureDevNames > 0) - device_name = allCaptureDevNameMap[0].name; - else - { - ALuint i; - - for(i = 0;i < numCaptureDevNames;i++) - { - if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) - { - pulse_name = allCaptureDevNameMap[i].device_name; - break; - } - } - if(i == numCaptureDevNames) - return ALC_FALSE; - } - - if(pulse_open(device, device_name) == ALC_FALSE) - return ALC_FALSE; - - data = device->ExtraData; - pa_threaded_mainloop_lock(data->loop); - - data->samples = device->UpdateSize * device->NumUpdates; - data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - if(data->samples < 100 * device->Frequency / 1000) - data->samples = 100 * device->Frequency / 1000; - - if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples))) - { - pa_threaded_mainloop_unlock(data->loop); - goto fail; - } - - data->attr.minreq = -1; - data->attr.prebuf = -1; - data->attr.maxlength = data->samples * data->frame_size; - data->attr.tlength = -1; - data->attr.fragsize = minu(data->samples, 50*device->Frequency/1000) * - data->frame_size; - - data->spec.rate = device->Frequency; - data->spec.channels = ChannelsFromDevFmt(device->FmtChans); - - switch(device->FmtType) - { - case DevFmtUByte: - data->spec.format = PA_SAMPLE_U8; - break; - case DevFmtShort: - data->spec.format = PA_SAMPLE_S16NE; - break; - case DevFmtFloat: - data->spec.format = PA_SAMPLE_FLOAT32NE; - break; - case DevFmtByte: - case DevFmtUShort: - ERR("Capture format type %#x capture not supported on PulseAudio\n", device->FmtType); - pa_threaded_mainloop_unlock(data->loop); - goto fail; - } - - if(pa_sample_spec_valid(&data->spec) == 0) - { - ERR("Invalid sample format\n"); - pa_threaded_mainloop_unlock(data->loop); - goto fail; - } - - if(!pa_channel_map_init_auto(&chanmap, data->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); - goto fail; - } - - data->stream = pa_stream_new(data->context, "Capture Stream", &data->spec, &chanmap); - if(!data->stream) - { - ERR("pa_stream_new() failed: %s\n", - pa_strerror(pa_context_errno(data->context))); - - pa_threaded_mainloop_unlock(data->loop); - goto fail; - } - - pa_stream_set_state_callback(data->stream, stream_state_callback, data->loop); - - flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; - if(pa_stream_connect_record(data->stream, pulse_name, &data->attr, flags) < 0) - { - ERR("Stream did not connect: %s\n", - pa_strerror(pa_context_errno(data->context))); - - pa_stream_unref(data->stream); - data->stream = NULL; - - pa_threaded_mainloop_unlock(data->loop); - goto fail; - } - - while((state=pa_stream_get_state(data->stream)) != PA_STREAM_READY) - { - if(!PA_STREAM_IS_GOOD(state)) - { - ERR("Stream did not get ready: %s\n", - pa_strerror(pa_context_errno(data->context))); - - pa_stream_unref(data->stream); - data->stream = NULL; - - pa_threaded_mainloop_unlock(data->loop); - goto fail; - } - - pa_threaded_mainloop_wait(data->loop); - } - pa_stream_set_state_callback(data->stream, stream_state_callback2, device); - - pa_threaded_mainloop_unlock(data->loop); - return ALC_TRUE; - -fail: - pulse_close(device); - return ALC_FALSE; -} //}}} - -static void pulse_close_capture(ALCdevice *device) //{{{ -{ - pulse_close(device); -} //}}} - -static void pulse_start_capture(ALCdevice *device) //{{{ -{ - pulse_data *data = device->ExtraData; - pa_operation *o; - - pa_threaded_mainloop_lock(data->loop); - o = pa_stream_cork(data->stream, 0, stream_success_callback, device); - while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(data->loop); - pa_operation_unref(o); - pa_threaded_mainloop_unlock(data->loop); -} //}}} - -static void pulse_stop_capture(ALCdevice *device) //{{{ -{ - pulse_data *data = device->ExtraData; - pa_operation *o; - - pa_threaded_mainloop_lock(data->loop); - o = pa_stream_cork(data->stream, 1, stream_success_callback, device); - while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(data->loop); - pa_operation_unref(o); - pa_threaded_mainloop_unlock(data->loop); -} //}}} - -static ALCuint pulse_available_samples(ALCdevice *device) //{{{ -{ - pulse_data *data = device->ExtraData; - size_t samples; - - pa_threaded_mainloop_lock(data->loop); - /* Capture is done in fragment-sized chunks, so we loop until we get all - * that's available */ - samples = (device->Connected ? pa_stream_readable_size(data->stream) : 0); - while(samples > 0) - { - const void *buf; - size_t length; - - if(pa_stream_peek(data->stream, &buf, &length) < 0) - { - ERR("pa_stream_peek() failed: %s\n", - pa_strerror(pa_context_errno(data->context))); - break; - } - - WriteRingBuffer(data->ring, buf, length/data->frame_size); - samples -= length; - - pa_stream_drop(data->stream); - } - pa_threaded_mainloop_unlock(data->loop); - - return RingBufferSize(data->ring); -} //}}} - -static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{ -{ - pulse_data *data = device->ExtraData; - - if(pulse_available_samples(device) >= samples) - ReadRingBuffer(data->ring, buffer, samples); - else - alcSetError(device, ALC_INVALID_VALUE); -} //}}} - - -static const BackendFuncs pulse_funcs = { //{{{ - pulse_open_playback, - pulse_close_playback, - pulse_reset_playback, - pulse_stop_playback, - pulse_open_capture, - pulse_close_capture, - pulse_start_capture, - pulse_stop_capture, - pulse_capture_samples, - pulse_available_samples -}; //}}} - -ALCboolean alc_pulse_init(BackendFuncs *func_list) //{{{ -{ - if(!pulse_load()) - return ALC_FALSE; - - *func_list = pulse_funcs; - - pulse_ctx_flags = 0; - if(!GetConfigValueBool("pulse", "spawn-server", 0)) - pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; - - return ALC_TRUE; -} //}}} - -void alc_pulse_deinit(void) //{{{ -{ - ALuint i; - - for(i = 0;i < numDevNames;++i) - { - free(allDevNameMap[i].name); - free(allDevNameMap[i].device_name); - } - free(allDevNameMap); - allDevNameMap = NULL; - numDevNames = 0; - - for(i = 0;i < numCaptureDevNames;++i) - { - free(allCaptureDevNameMap[i].name); - free(allCaptureDevNameMap[i].device_name); - } - free(allCaptureDevNameMap); - allCaptureDevNameMap = NULL; - numCaptureDevNames = 0; - -#ifdef HAVE_DYNLOAD - if(pa_handle) - CloseLib(pa_handle); - pa_handle = NULL; -#endif -} //}}} - -void alc_pulse_probe(enum DevProbe type) //{{{ -{ - pa_threaded_mainloop *loop; - ALuint i; - - switch(type) - { - case DEVICE_PROBE: - 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) - { - AppendDeviceList(pulse_device); - - 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); - break; - - case ALL_DEVICE_PROBE: - for(i = 0;i < numDevNames;++i) - { - free(allDevNameMap[i].name); - free(allDevNameMap[i].device_name); - } - free(allDevNameMap); - allDevNameMap = NULL; - numDevNames = 0; - - probe_devices(AL_FALSE); - - for(i = 0;i < numDevNames;i++) - AppendAllDeviceList(allDevNameMap[i].name); - break; - - case CAPTURE_DEVICE_PROBE: - for(i = 0;i < numCaptureDevNames;++i) - { - free(allCaptureDevNameMap[i].name); - free(allCaptureDevNameMap[i].device_name); - } - free(allCaptureDevNameMap); - allCaptureDevNameMap = NULL; - numCaptureDevNames = 0; - - probe_devices(AL_TRUE); - - for(i = 0;i < numCaptureDevNames;i++) - AppendCaptureDeviceList(allCaptureDevNameMap[i].name); - break; - } -} //}}} -//}}} diff --git a/Alc/sndio.c b/Alc/sndio.c deleted file mode 100644 index 5f9ad0cd..00000000 --- a/Alc/sndio.c +++ /dev/null @@ -1,381 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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 -#include -#include -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - -#include - - -static const ALCchar sndio_device[] = "SndIO Default"; - - -static void *sndio_handle; -#ifdef HAVE_DYNLOAD -#define MAKE_FUNC(x) static typeof(x) * p##x -MAKE_FUNC(sio_initpar); -MAKE_FUNC(sio_open); -MAKE_FUNC(sio_close); -MAKE_FUNC(sio_setpar); -MAKE_FUNC(sio_getpar); -MAKE_FUNC(sio_getcap); -MAKE_FUNC(sio_onmove); -MAKE_FUNC(sio_write); -MAKE_FUNC(sio_read); -MAKE_FUNC(sio_start); -MAKE_FUNC(sio_stop); -MAKE_FUNC(sio_nfds); -MAKE_FUNC(sio_pollfd); -MAKE_FUNC(sio_revents); -MAKE_FUNC(sio_eof); -MAKE_FUNC(sio_setvol); -MAKE_FUNC(sio_onvol); - -#define sio_initpar psio_initpar -#define sio_open psio_open -#define sio_close psio_close -#define sio_setpar psio_setpar -#define sio_getpar psio_getpar -#define sio_getcap psio_getcap -#define sio_onmove psio_onmove -#define sio_write psio_write -#define sio_read psio_read -#define sio_start psio_start -#define sio_stop psio_stop -#define sio_nfds psio_nfds -#define sio_pollfd psio_pollfd -#define sio_revents psio_revents -#define sio_eof psio_eof -#define sio_setvol psio_setvol -#define sio_onvol psio_onvol -#endif - - -static ALCboolean sndio_load(void) -{ - if(!sndio_handle) - { -#ifdef HAVE_DYNLOAD - sndio_handle = LoadLib("libsndio.so"); - if(!sndio_handle) - return ALC_FALSE; - -#define LOAD_FUNC(f) do { \ - p##f = GetSymbol(sndio_handle, #f); \ - if(p##f == NULL) { \ - CloseLib(sndio_handle); \ - sndio_handle = NULL; \ - return ALC_FALSE; \ - } \ -} while(0) - LOAD_FUNC(sio_initpar); - LOAD_FUNC(sio_open); - LOAD_FUNC(sio_close); - LOAD_FUNC(sio_setpar); - LOAD_FUNC(sio_getpar); - LOAD_FUNC(sio_getcap); - LOAD_FUNC(sio_onmove); - LOAD_FUNC(sio_write); - LOAD_FUNC(sio_read); - LOAD_FUNC(sio_start); - LOAD_FUNC(sio_stop); - LOAD_FUNC(sio_nfds); - LOAD_FUNC(sio_pollfd); - LOAD_FUNC(sio_revents); - LOAD_FUNC(sio_eof); - LOAD_FUNC(sio_setvol); - LOAD_FUNC(sio_onvol); -#undef LOAD_FUNC -#else - sndio_handle = (void*)0xDEADBEEF; -#endif - } - return ALC_TRUE; -} - - -typedef struct { - struct sio_hdl *sndHandle; - - ALvoid *mix_data; - ALsizei data_size; - - volatile int killNow; - ALvoid *thread; -} sndio_data; - - -static ALuint sndio_proc(ALvoid *ptr) -{ - ALCdevice *device = ptr; - sndio_data *data = device->ExtraData; - ALsizei frameSize; - size_t wrote; - - SetRTPriority(); - - frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - - while(!data->killNow && device->Connected) - { - ALsizei len = data->data_size; - ALubyte *WritePtr = data->mix_data; - - aluMixData(device, WritePtr, len/frameSize); - while(len > 0 && !data->killNow) - { - wrote = sio_write(data->sndHandle, WritePtr, len); - if(wrote == 0) - { - ERR("sio_write failed\n"); - aluHandleDisconnect(device); - break; - } - - len -= wrote; - WritePtr += wrote; - } - } - - return 0; -} - - - -static ALCboolean sndio_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - sndio_data *data; - - if(!deviceName) - deviceName = sndio_device; - else if(strcmp(deviceName, sndio_device) != 0) - return ALC_FALSE; - - data = calloc(1, sizeof(*data)); - data->killNow = 0; - - data->sndHandle = sio_open(NULL, SIO_PLAY, 0); - if(data->sndHandle == NULL) - { - free(data); - ERR("Could not open device\n"); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - device->ExtraData = data; - - return ALC_TRUE; -} - -static void sndio_close_playback(ALCdevice *device) -{ - sndio_data *data = device->ExtraData; - - sio_close(data->sndHandle); - free(data); - device->ExtraData = NULL; -} - -static ALCboolean sndio_reset_playback(ALCdevice *device) -{ - sndio_data *data = device->ExtraData; - struct sio_par par; - - sio_initpar(&par); - - par.rate = device->Frequency; - - par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1); - - switch(device->FmtType) - { - case DevFmtByte: - par.bits = 8; - par.sig = 1; - break; - case DevFmtUByte: - par.bits = 8; - par.sig = 0; - break; - case DevFmtFloat: - device->FmtType = DevFmtShort; - /* fall-through */ - case DevFmtShort: - par.bits = 16; - par.sig = 1; - break; - case DevFmtUShort: - par.bits = 16; - par.sig = 0; - break; - } - par.le = SIO_LE_NATIVE; - - par.round = device->UpdateSize; - par.appbufsz = device->UpdateSize * (device->NumUpdates-1); - if(!par.appbufsz) par.appbufsz = device->UpdateSize; - - - if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par)) - { - ERR("Failed to set device parameters\n"); - return ALC_FALSE; - } - - if(par.rate != device->Frequency) - { - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("Failed to set frequency %uhz, got %uhz instead\n", device->Frequency, par.rate); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - device->Frequency = par.rate; - } - - if(par.pchan != ChannelsFromDevFmt(device->FmtChans)) - { - if(par.pchan != 1 && par.pchan != 2) - { - ERR("Unhandled channel count: %u\n", par.pchan); - return ALC_FALSE; - } - if((device->Flags&DEVICE_CHANNELS_REQUEST)) - ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), par.pchan); - device->Flags &= ~DEVICE_CHANNELS_REQUEST; - device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo); - } - - if(par.bits != par.bps*8) - { - ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8); - return ALC_FALSE; - } - - if(par.bits == 8 && par.sig == 1) - device->FmtType = DevFmtByte; - else if(par.bits == 8 && par.sig == 0) - device->FmtType = DevFmtUByte; - else if(par.bits == 16 && par.sig == 1) - device->FmtType = DevFmtShort; - else if(par.bits == 16 && par.sig == 0) - device->FmtType = DevFmtUShort; - else - { - ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits); - return ALC_FALSE; - } - - - device->UpdateSize = par.round; - device->NumUpdates = (par.bufsz/par.round) + 1; - - SetDefaultChannelOrder(device); - - - if(!sio_start(data->sndHandle)) - { - ERR("Error starting playback\n"); - return ALC_FALSE; - } - - data->data_size = device->UpdateSize * par.bps * par.pchan; - data->mix_data = calloc(1, data->data_size); - - data->thread = StartThread(sndio_proc, device); - if(data->thread == NULL) - { - sio_stop(data->sndHandle); - free(data->mix_data); - data->mix_data = NULL; - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void sndio_stop_playback(ALCdevice *device) -{ - sndio_data *data = device->ExtraData; - - if(!data->thread) - return; - - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - - data->killNow = 0; - if(!sio_stop(data->sndHandle)) - ERR("Error stopping device\n"); - - free(data->mix_data); - data->mix_data = NULL; -} - - -static const BackendFuncs sndio_funcs = { - sndio_open_playback, - sndio_close_playback, - sndio_reset_playback, - sndio_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -ALCboolean alc_sndio_init(BackendFuncs *func_list) -{ - if(!sndio_load()) - return ALC_FALSE; - *func_list = sndio_funcs; - return ALC_TRUE; -} - -void alc_sndio_deinit(void) -{ -#ifdef HAVE_DYNLOAD - if(sndio_handle) - CloseLib(sndio_handle); - sndio_handle = NULL; -#endif -} - -void alc_sndio_probe(enum DevProbe type) -{ - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(sndio_device); - break; - case ALL_DEVICE_PROBE: - AppendAllDeviceList(sndio_device); - break; - case CAPTURE_DEVICE_PROBE: - break; - } -} diff --git a/Alc/solaris.c b/Alc/solaris.c deleted file mode 100644 index b2b8196d..00000000 --- a/Alc/solaris.c +++ /dev/null @@ -1,282 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - -#include - - -static const ALCchar solaris_device[] = "Solaris Default"; - -typedef struct { - int fd; - volatile int killNow; - ALvoid *thread; - - ALubyte *mix_data; - int data_size; -} solaris_data; - - -static ALuint SolarisProc(ALvoid *ptr) -{ - ALCdevice *pDevice = (ALCdevice*)ptr; - solaris_data *data = (solaris_data*)pDevice->ExtraData; - ALint frameSize; - int wrote; - - SetRTPriority(); - - frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); - - while(!data->killNow && pDevice->Connected) - { - ALint len = data->data_size; - ALubyte *WritePtr = data->mix_data; - - aluMixData(pDevice, WritePtr, len/frameSize); - while(len > 0 && !data->killNow) - { - wrote = write(data->fd, WritePtr, len); - if(wrote < 0) - { - if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) - { - ERR("write failed: %s\n", strerror(errno)); - aluHandleDisconnect(pDevice); - break; - } - - Sleep(1); - continue; - } - - len -= wrote; - WritePtr += wrote; - } - } - - return 0; -} - - -static ALCboolean solaris_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - char driver[64]; - solaris_data *data; - - strncpy(driver, GetConfigValue("solaris", "device", "/dev/audio"), sizeof(driver)-1); - driver[sizeof(driver)-1] = 0; - - if(!deviceName) - deviceName = solaris_device; - else if(strcmp(deviceName, solaris_device) != 0) - return ALC_FALSE; - - data = (solaris_data*)calloc(1, sizeof(solaris_data)); - data->killNow = 0; - - data->fd = open(driver, O_WRONLY); - if(data->fd == -1) - { - free(data); - ERR("Could not open %s: %s\n", driver, strerror(errno)); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - device->ExtraData = data; - return ALC_TRUE; -} - -static void solaris_close_playback(ALCdevice *device) -{ - solaris_data *data = (solaris_data*)device->ExtraData; - - close(data->fd); - free(data); - device->ExtraData = NULL; -} - -static ALCboolean solaris_reset_playback(ALCdevice *device) -{ - solaris_data *data = (solaris_data*)device->ExtraData; - audio_info_t info; - ALuint frameSize; - int numChannels; - - AUDIO_INITINFO(&info); - - info.play.sample_rate = device->Frequency; - - if(device->FmtChans != DevFmtMono) - device->FmtChans = DevFmtStereo; - numChannels = ChannelsFromDevFmt(device->FmtChans); - info.play.channels = numChannels; - - switch(device->FmtType) - { - case DevFmtByte: - info.play.precision = 8; - info.play.encoding = AUDIO_ENCODING_LINEAR; - break; - case DevFmtUByte: - info.play.precision = 8; - info.play.encoding = AUDIO_ENCODING_LINEAR8; - break; - case DevFmtUShort: - case DevFmtFloat: - device->FmtType = DevFmtShort; - /* fall-through */ - case DevFmtShort: - info.play.precision = 16; - info.play.encoding = AUDIO_ENCODING_LINEAR; - break; - } - - frameSize = numChannels * BytesFromDevFmt(device->FmtType); - info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize; - - if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0) - { - ERR("ioctl failed: %s\n", strerror(errno)); - return ALC_FALSE; - } - - if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels) - { - ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels); - return ALC_FALSE; - } - - if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && - device->FmtType == DevFmtByte) || - (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && - device->FmtType == DevFmtUByte) || - (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && - device->FmtType == DevFmtShort))) - { - ERR("Could not set %#x sample type, got %d (%#x)\n", - device->FmtType, info.play.precision, info.play.encoding); - return ALC_FALSE; - } - - if(device->Frequency != info.play.sample_rate) - { - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("Failed to set requested frequency %dhz, got %dhz instead\n", device->Frequency, info.play.sample_rate); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - device->Frequency = info.play.sample_rate; - } - device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1; - - data->data_size = device->UpdateSize * frameSize; - data->mix_data = calloc(1, data->data_size); - - SetDefaultChannelOrder(device); - - data->thread = StartThread(SolarisProc, device); - if(data->thread == NULL) - { - free(data->mix_data); - data->mix_data = NULL; - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void solaris_stop_playback(ALCdevice *device) -{ - solaris_data *data = (solaris_data*)device->ExtraData; - - if(!data->thread) - return; - - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - - data->killNow = 0; - if(ioctl(data->fd, AUDIO_DRAIN) < 0) - ERR("Error draining device: %s\n", strerror(errno)); - - free(data->mix_data); - data->mix_data = NULL; -} - - -static const BackendFuncs solaris_funcs = { - solaris_open_playback, - solaris_close_playback, - solaris_reset_playback, - solaris_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -ALCboolean alc_solaris_init(BackendFuncs *func_list) -{ - *func_list = solaris_funcs; - return ALC_TRUE; -} - -void alc_solaris_deinit(void) -{ -} - -void alc_solaris_probe(enum DevProbe type) -{ -#ifdef HAVE_STAT - struct stat buf; - if(stat(GetConfigValue("solaris", "device", "/dev/audio"), &buf) != 0) - return; -#endif - - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(solaris_device); - break; - case ALL_DEVICE_PROBE: - AppendAllDeviceList(solaris_device); - break; - case CAPTURE_DEVICE_PROBE: - break; - } -} diff --git a/Alc/wave.c b/Alc/wave.c deleted file mode 100644 index 465087ce..00000000 --- a/Alc/wave.c +++ /dev/null @@ -1,355 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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 -#include -#include -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - - -typedef struct { - FILE *f; - long DataStart; - - ALvoid *buffer; - ALuint size; - - volatile int killNow; - ALvoid *thread; -} wave_data; - - -static const ALCchar waveDevice[] = "Wave File Writer"; - -static const ALubyte SUBTYPE_PCM[] = { - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, - 0x00, 0x38, 0x9b, 0x71 -}; -static const ALubyte SUBTYPE_FLOAT[] = { - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, - 0x00, 0x38, 0x9b, 0x71 -}; - -static const ALuint channel_masks[] = { - 0, /* invalid */ - 0x4, /* Mono */ - 0x1 | 0x2, /* Stereo */ - 0, /* 3 channel */ - 0x1 | 0x2 | 0x10 | 0x20, /* Quad */ - 0, /* 5 channel */ - 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */ - 0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */ - 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */ -}; - - -static void fwrite16le(ALushort val, FILE *f) -{ - fputc(val&0xff, f); - fputc((val>>8)&0xff, f); -} - -static void fwrite32le(ALuint val, FILE *f) -{ - fputc(val&0xff, f); - fputc((val>>8)&0xff, f); - fputc((val>>16)&0xff, f); - fputc((val>>24)&0xff, f); -} - - -static ALuint WaveProc(ALvoid *ptr) -{ - ALCdevice *pDevice = (ALCdevice*)ptr; - wave_data *data = (wave_data*)pDevice->ExtraData; - ALuint frameSize; - ALuint now, start; - ALuint64 avail, done; - size_t fs; - union { - short s; - char b[sizeof(short)]; - } uSB; - const ALuint restTime = (ALuint64)pDevice->UpdateSize * 1000 / - pDevice->Frequency / 2; - - uSB.s = 1; - frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); - - done = 0; - start = timeGetTime(); - while(!data->killNow && pDevice->Connected) - { - now = timeGetTime(); - - avail = (ALuint64)(now-start) * pDevice->Frequency / 1000; - if(avail < done) - { - /* Timer wrapped. Add the remainder of the cycle to the available - * count and reset the number of samples done */ - avail += (ALuint64)0xFFFFFFFFu*pDevice->Frequency/1000 - done; - done = 0; - } - if(avail-done < pDevice->UpdateSize) - { - Sleep(restTime); - continue; - } - - while(avail-done >= pDevice->UpdateSize) - { - aluMixData(pDevice, data->buffer, pDevice->UpdateSize); - done += pDevice->UpdateSize; - - if(uSB.b[0] != 1) - { - ALuint bytesize = BytesFromDevFmt(pDevice->FmtType); - ALubyte *bytes = data->buffer; - ALuint i; - - if(bytesize == 1) - { - for(i = 0;i < data->size;i++) - fputc(bytes[i], data->f); - } - else if(bytesize == 2) - { - for(i = 0;i < data->size;i++) - fputc(bytes[i^1], data->f); - } - else if(bytesize == 4) - { - for(i = 0;i < data->size;i++) - fputc(bytes[i^3], data->f); - } - } - else - fs = fwrite(data->buffer, frameSize, pDevice->UpdateSize, - data->f); - if(ferror(data->f)) - { - ERR("Error writing to file\n"); - aluHandleDisconnect(pDevice); - break; - } - } - } - - return 0; -} - -static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName) -{ - wave_data *data; - const char *fname; - - fname = GetConfigValue("wave", "file", ""); - if(!fname[0]) - return ALC_FALSE; - - if(!deviceName) - deviceName = waveDevice; - else if(strcmp(deviceName, waveDevice) != 0) - return ALC_FALSE; - - data = (wave_data*)calloc(1, sizeof(wave_data)); - - data->f = fopen(fname, "wb"); - if(!data->f) - { - free(data); - ERR("Could not open file '%s': %s\n", fname, strerror(errno)); - return ALC_FALSE; - } - - device->szDeviceName = strdup(deviceName); - device->ExtraData = data; - return ALC_TRUE; -} - -static void wave_close_playback(ALCdevice *device) -{ - wave_data *data = (wave_data*)device->ExtraData; - - fclose(data->f); - free(data); - device->ExtraData = NULL; -} - -static ALCboolean wave_reset_playback(ALCdevice *device) -{ - wave_data *data = (wave_data*)device->ExtraData; - ALuint channels=0, bits=0; - size_t val; - - fseek(data->f, 0, SEEK_SET); - clearerr(data->f); - - switch(device->FmtType) - { - case DevFmtByte: - device->FmtType = DevFmtUByte; - break; - case DevFmtUShort: - device->FmtType = DevFmtShort; - break; - case DevFmtUByte: - case DevFmtShort: - case DevFmtFloat: - break; - } - bits = BytesFromDevFmt(device->FmtType) * 8; - channels = ChannelsFromDevFmt(device->FmtChans); - - fprintf(data->f, "RIFF"); - fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close - - fprintf(data->f, "WAVE"); - - fprintf(data->f, "fmt "); - fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE - - // 16-bit val, format type id (extensible: 0xFFFE) - fwrite16le(0xFFFE, data->f); - // 16-bit val, channel count - fwrite16le(channels, data->f); - // 32-bit val, frequency - fwrite32le(device->Frequency, data->f); - // 32-bit val, bytes per second - fwrite32le(device->Frequency * channels * bits / 8, data->f); - // 16-bit val, frame size - fwrite16le(channels * bits / 8, data->f); - // 16-bit val, bits per sample - fwrite16le(bits, data->f); - // 16-bit val, extra byte count - fwrite16le(22, data->f); - // 16-bit val, valid bits per sample - fwrite16le(bits, data->f); - // 32-bit val, channel mask - fwrite32le(channel_masks[channels], data->f); - // 16 byte GUID, sub-type format - val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f); - - fprintf(data->f, "data"); - fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close - - if(ferror(data->f)) - { - ERR("Error writing header: %s\n", strerror(errno)); - return ALC_FALSE; - } - - data->DataStart = ftell(data->f); - - data->size = device->UpdateSize * channels * bits / 8; - data->buffer = malloc(data->size); - if(!data->buffer) - { - ERR("Buffer malloc failed\n"); - return ALC_FALSE; - } - - SetDefaultWFXChannelOrder(device); - - data->thread = StartThread(WaveProc, device); - if(data->thread == NULL) - { - free(data->buffer); - data->buffer = NULL; - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static void wave_stop_playback(ALCdevice *device) -{ - wave_data *data = (wave_data*)device->ExtraData; - ALuint dataLen; - long size; - - if(!data->thread) - return; - - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - - data->killNow = 0; - - free(data->buffer); - data->buffer = NULL; - - size = ftell(data->f); - if(size > 0) - { - dataLen = size - data->DataStart; - if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0) - fwrite32le(dataLen, data->f); // 'data' header len - if(fseek(data->f, 4, SEEK_SET) == 0) - fwrite32le(size-8, data->f); // 'WAVE' header len - } -} - - -static const BackendFuncs wave_funcs = { - wave_open_playback, - wave_close_playback, - wave_reset_playback, - wave_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -ALCboolean alc_wave_init(BackendFuncs *func_list) -{ - *func_list = wave_funcs; - return ALC_TRUE; -} - -void alc_wave_deinit(void) -{ -} - -void alc_wave_probe(enum DevProbe type) -{ - if(!ConfigValueExists("wave", "file")) - return; - - switch(type) - { - case DEVICE_PROBE: - AppendDeviceList(waveDevice); - break; - case ALL_DEVICE_PROBE: - AppendAllDeviceList(waveDevice); - break; - case CAPTURE_DEVICE_PROBE: - break; - } -} diff --git a/Alc/winmm.c b/Alc/winmm.c deleted file mode 100644 index 445dd51e..00000000 --- a/Alc/winmm.c +++ /dev/null @@ -1,780 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 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" - -#define _WIN32_WINNT 0x0500 -#include -#include -#include - -#include -#include - -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" - - -typedef struct { - // MMSYSTEM Device - volatile ALboolean bWaveShutdown; - HANDLE hWaveThreadEvent; - HANDLE hWaveThread; - DWORD ulWaveThreadID; - LONG lWaveBuffersCommitted; - WAVEHDR WaveBuffer[4]; - - union { - HWAVEIN In; - HWAVEOUT Out; - } hWaveHandle; - - ALuint Frequency; - - RingBuffer *pRing; -} WinMMData; - - -static const ALCchar woDefault[] = "WaveOut Default"; - -static ALCchar **PlaybackDeviceList; -static ALuint NumPlaybackDevices; -static ALCchar **CaptureDeviceList; -static ALuint NumCaptureDevices; - - -static void ProbePlaybackDevices(void) -{ - ALuint i; - - for(i = 0;i < NumPlaybackDevices;i++) - free(PlaybackDeviceList[i]); - - NumPlaybackDevices = waveOutGetNumDevs(); - PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices); - for(i = 0;i < NumPlaybackDevices;i++) - { - WAVEOUTCAPS WaveCaps; - - PlaybackDeviceList[i] = NULL; - if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) - { - char name[1024]; - ALuint count, j; - - count = 0; - do { - if(count == 0) - snprintf(name, sizeof(name), "%s", WaveCaps.szPname); - else - snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1); - count++; - - for(j = 0;j < i;j++) - { - if(strcmp(name, PlaybackDeviceList[j]) == 0) - break; - } - } while(j != i); - - PlaybackDeviceList[i] = strdup(name); - } - } -} - -static void ProbeCaptureDevices(void) -{ - ALuint i; - - for(i = 0;i < NumCaptureDevices;i++) - free(CaptureDeviceList[i]); - - NumCaptureDevices = waveInGetNumDevs(); - CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices); - for(i = 0;i < NumCaptureDevices;i++) - { - WAVEINCAPS WaveInCaps; - - CaptureDeviceList[i] = NULL; - if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) - { - char name[1024]; - ALuint count, j; - - count = 0; - do { - if(count == 0) - snprintf(name, sizeof(name), "%s", WaveInCaps.szPname); - else - snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1); - count++; - - for(j = 0;j < i;j++) - { - if(strcmp(name, CaptureDeviceList[j]) == 0) - break; - } - } while(j != i); - - CaptureDeviceList[i] = strdup(name); - } - } -} - - -/* - WaveOutProc - - Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and - returns to the application (for more data) -*/ -static void CALLBACK WaveOutProc(HWAVEOUT hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) -{ - ALCdevice *pDevice = (ALCdevice*)dwInstance; - WinMMData *pData = pDevice->ExtraData; - - (void)hDevice; - (void)dwParam2; - - if(uMsg != WOM_DONE) - return; - - // Decrement number of buffers in use - InterlockedDecrement(&pData->lWaveBuffersCommitted); - - if(pData->bWaveShutdown == AL_FALSE) - { - // Notify Wave Processor Thread that a Wave Header has returned - PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1); - } - else - { - if(pData->lWaveBuffersCommitted == 0) - { - // Post 'Quit' Message to WaveOut Processor Thread - PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0); - } - } -} - -/* - PlaybackThreadProc - - Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its - audio data. -*/ -static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter) -{ - ALCdevice *pDevice = (ALCdevice*)lpParameter; - WinMMData *pData = pDevice->ExtraData; - LPWAVEHDR pWaveHdr; - ALuint FrameSize; - MSG msg; - - FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); - - SetRTPriority(); - - while(GetMessage(&msg, NULL, 0, 0)) - { - if(msg.message != WOM_DONE || pData->bWaveShutdown) - continue; - - pWaveHdr = ((LPWAVEHDR)msg.lParam); - - aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize); - - // Send buffer back to play more data - waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR)); - InterlockedIncrement(&pData->lWaveBuffersCommitted); - } - - // Signal Wave Thread completed event - if(pData->hWaveThreadEvent) - SetEvent(pData->hWaveThreadEvent); - - ExitThread(0); - - return 0; -} - -/* - WaveInProc - - Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and - returns to the application (with more data) -*/ -static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) -{ - ALCdevice *pDevice = (ALCdevice*)dwInstance; - WinMMData *pData = pDevice->ExtraData; - - (void)hDevice; - (void)dwParam2; - - if(uMsg != WIM_DATA) - return; - - // Decrement number of buffers in use - InterlockedDecrement(&pData->lWaveBuffersCommitted); - - if(pData->bWaveShutdown == AL_FALSE) - { - // Notify Wave Processor Thread that a Wave Header has returned - PostThreadMessage(pData->ulWaveThreadID,uMsg,0,dwParam1); - } - else - { - if(pData->lWaveBuffersCommitted == 0) - { - // Post 'Quit' Message to WaveIn Processor Thread - PostThreadMessage(pData->ulWaveThreadID,WM_QUIT,0,0); - } - } -} - -/* - CaptureThreadProc - - Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new - audio data. -*/ -static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter) -{ - ALCdevice *pDevice = (ALCdevice*)lpParameter; - WinMMData *pData = pDevice->ExtraData; - LPWAVEHDR pWaveHdr; - ALuint FrameSize; - MSG msg; - - FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); - - while(GetMessage(&msg, NULL, 0, 0)) - { - if(msg.message != WIM_DATA || pData->bWaveShutdown) - continue; - - pWaveHdr = ((LPWAVEHDR)msg.lParam); - - WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData, - pWaveHdr->dwBytesRecorded/FrameSize); - - // Send buffer back to capture more data - waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR)); - InterlockedIncrement(&pData->lWaveBuffersCommitted); - } - - // Signal Wave Thread completed event - if(pData->hWaveThreadEvent) - SetEvent(pData->hWaveThreadEvent); - - ExitThread(0); - - return 0; -} - - -static ALCboolean WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName) -{ - WAVEFORMATEX wfexFormat; - WinMMData *pData = NULL; - UINT lDeviceID = 0; - MMRESULT res; - ALuint i = 0; - - // Find the Device ID matching the deviceName if valid - if(!deviceName || strcmp(deviceName, woDefault) == 0) - lDeviceID = WAVE_MAPPER; - else - { - if(!PlaybackDeviceList) - ProbePlaybackDevices(); - - for(i = 0;i < NumPlaybackDevices;i++) - { - if(PlaybackDeviceList[i] && - strcmp(deviceName, PlaybackDeviceList[i]) == 0) - { - lDeviceID = i; - break; - } - } - if(i == NumPlaybackDevices) - return ALC_FALSE; - } - - pData = calloc(1, sizeof(*pData)); - if(!pData) - { - alcSetError(pDevice, ALC_OUT_OF_MEMORY); - return ALC_FALSE; - } - pDevice->ExtraData = pData; - - if(pDevice->FmtChans != DevFmtMono) - { - if((pDevice->Flags&DEVICE_CHANNELS_REQUEST) && - pDevice->FmtChans != DevFmtStereo) - { - ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice->FmtChans)); - pDevice->Flags &= ~DEVICE_CHANNELS_REQUEST; - } - pDevice->FmtChans = DevFmtStereo; - } - switch(pDevice->FmtType) - { - case DevFmtByte: - pDevice->FmtType = DevFmtUByte; - break; - case DevFmtUShort: - case DevFmtFloat: - pDevice->FmtType = DevFmtShort; - break; - case DevFmtUByte: - case DevFmtShort: - break; - } - - memset(&wfexFormat, 0, sizeof(WAVEFORMATEX)); - wfexFormat.wFormatTag = WAVE_FORMAT_PCM; - wfexFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans); - wfexFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8; - wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample * - wfexFormat.nChannels / 8; - wfexFormat.nSamplesPerSec = pDevice->Frequency; - wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec * - wfexFormat.nBlockAlign; - wfexFormat.cbSize = 0; - - if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) - { - ERR("waveOutOpen failed: %u\n", res); - goto failure; - } - - pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(pData->hWaveThreadEvent == NULL) - { - ERR("CreateEvent failed: %lu\n", GetLastError()); - goto failure; - } - - pData->Frequency = pDevice->Frequency; - - pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault : - PlaybackDeviceList[lDeviceID]); - return ALC_TRUE; - -failure: - if(pData->hWaveThreadEvent) - CloseHandle(pData->hWaveThreadEvent); - - if(pData->hWaveHandle.Out) - waveOutClose(pData->hWaveHandle.Out); - - free(pData); - pDevice->ExtraData = NULL; - return ALC_FALSE; -} - -static void WinMMClosePlayback(ALCdevice *device) -{ - WinMMData *pData = (WinMMData*)device->ExtraData; - - // Close the Wave device - CloseHandle(pData->hWaveThreadEvent); - pData->hWaveThreadEvent = 0; - - waveOutClose(pData->hWaveHandle.Out); - pData->hWaveHandle.Out = 0; - - free(pData); - device->ExtraData = NULL; -} - -static ALCboolean WinMMResetPlayback(ALCdevice *device) -{ - WinMMData *pData = (WinMMData*)device->ExtraData; - ALbyte *BufferData; - ALint lBufferSize; - ALuint i; - - pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID); - if(pData->hWaveThread == NULL) - return ALC_FALSE; - - device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * - pData->Frequency / device->Frequency); - if(device->Frequency != pData->Frequency) - { - if((device->Flags&DEVICE_FREQUENCY_REQUEST)) - ERR("WinMM does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, pData->Frequency); - device->Flags &= ~DEVICE_FREQUENCY_REQUEST; - device->Frequency = pData->Frequency; - } - - SetDefaultWFXChannelOrder(device); - - pData->lWaveBuffersCommitted = 0; - - // Create 4 Buffers - lBufferSize = device->UpdateSize*device->NumUpdates / 4; - lBufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - - BufferData = calloc(4, lBufferSize); - for(i = 0;i < 4;i++) - { - memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); - pData->WaveBuffer[i].dwBufferLength = lBufferSize; - pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : - (pData->WaveBuffer[i-1].lpData + - pData->WaveBuffer[i-1].dwBufferLength)); - waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); - waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); - InterlockedIncrement(&pData->lWaveBuffersCommitted); - } - - return ALC_TRUE; -} - -static void WinMMStopPlayback(ALCdevice *device) -{ - WinMMData *pData = (WinMMData*)device->ExtraData; - void *buffer = NULL; - int i; - - if(pData->hWaveThread == NULL) - return; - - // Set flag to stop processing headers - pData->bWaveShutdown = AL_TRUE; - - // Wait for signal that Wave Thread has been destroyed - WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); - - CloseHandle(pData->hWaveThread); - pData->hWaveThread = 0; - - pData->bWaveShutdown = AL_FALSE; - - // Release the wave buffers - for(i = 0;i < 4;i++) - { - waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); - if(i == 0) buffer = pData->WaveBuffer[i].lpData; - pData->WaveBuffer[i].lpData = NULL; - } - free(buffer); -} - - -static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName) -{ - WAVEFORMATEX wfexCaptureFormat; - DWORD ulCapturedDataSize; - WinMMData *pData = NULL; - UINT lDeviceID = 0; - ALbyte *BufferData; - ALint lBufferSize; - MMRESULT res; - ALuint i; - - if(!CaptureDeviceList) - ProbeCaptureDevices(); - - // Find the Device ID matching the deviceName if valid - if(deviceName) - { - for(i = 0;i < NumCaptureDevices;i++) - { - if(CaptureDeviceList[i] && - strcmp(deviceName, CaptureDeviceList[i]) == 0) - { - lDeviceID = i; - break; - } - } - } - else - { - for(i = 0;i < NumCaptureDevices;i++) - { - if(CaptureDeviceList[i]) - { - lDeviceID = i; - break; - } - } - } - if(i == NumCaptureDevices) - return ALC_FALSE; - - pData = calloc(1, sizeof(*pData)); - if(!pData) - { - alcSetError(pDevice, ALC_OUT_OF_MEMORY); - return ALC_FALSE; - } - pDevice->ExtraData = pData; - - if((pDevice->FmtChans != DevFmtMono && pDevice->FmtChans != DevFmtStereo) || - (pDevice->FmtType != DevFmtUByte && pDevice->FmtType != DevFmtShort)) - { - alcSetError(pDevice, ALC_INVALID_ENUM); - goto failure; - } - - memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX)); - wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM; - wfexCaptureFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans); - wfexCaptureFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8; - wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample * - wfexCaptureFormat.nChannels / 8; - wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency; - wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec * - wfexCaptureFormat.nBlockAlign; - wfexCaptureFormat.cbSize = 0; - - if((res=waveInOpen(&pData->hWaveHandle.In, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) - { - ERR("waveInOpen failed: %u\n", res); - goto failure; - } - - pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(pData->hWaveThreadEvent == NULL) - { - ERR("CreateEvent failed: %lu\n", GetLastError()); - goto failure; - } - - pData->Frequency = pDevice->Frequency; - - // Allocate circular memory buffer for the captured audio - ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates; - - // Make sure circular buffer is at least 100ms in size - if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10)) - ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10; - - pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize); - if(!pData->pRing) - goto failure; - - pData->lWaveBuffersCommitted = 0; - - // Create 4 Buffers of 50ms each - lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20; - lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign); - - BufferData = calloc(4, lBufferSize); - if(!BufferData) - goto failure; - - for(i = 0;i < 4;i++) - { - memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); - pData->WaveBuffer[i].dwBufferLength = lBufferSize; - pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : - (pData->WaveBuffer[i-1].lpData + - pData->WaveBuffer[i-1].dwBufferLength)); - pData->WaveBuffer[i].dwFlags = 0; - pData->WaveBuffer[i].dwLoops = 0; - waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); - waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); - InterlockedIncrement(&pData->lWaveBuffersCommitted); - } - - pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID); - if (pData->hWaveThread == NULL) - goto failure; - - pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]); - return ALC_TRUE; - -failure: - if(pData->hWaveThread) - CloseHandle(pData->hWaveThread); - - for(i = 0;i < 4;i++) - { - if(pData->WaveBuffer[i].lpData) - { - waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); - if(i == 0) - free(pData->WaveBuffer[i].lpData); - } - } - - if(pData->pRing) - DestroyRingBuffer(pData->pRing); - - if(pData->hWaveThreadEvent) - CloseHandle(pData->hWaveThreadEvent); - - if(pData->hWaveHandle.In) - waveInClose(pData->hWaveHandle.In); - - free(pData); - pDevice->ExtraData = NULL; - return ALC_FALSE; -} - -static void WinMMCloseCapture(ALCdevice *pDevice) -{ - WinMMData *pData = (WinMMData*)pDevice->ExtraData; - void *buffer = NULL; - int i; - - // Call waveOutReset to shutdown wave device - pData->bWaveShutdown = AL_TRUE; - waveInReset(pData->hWaveHandle.In); - - // Wait for signal that Wave Thread has been destroyed - WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); - - CloseHandle(pData->hWaveThread); - pData->hWaveThread = 0; - - // Release the wave buffers - for(i = 0;i < 4;i++) - { - waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); - if(i == 0) buffer = pData->WaveBuffer[i].lpData; - pData->WaveBuffer[i].lpData = NULL; - } - free(buffer); - - DestroyRingBuffer(pData->pRing); - pData->pRing = NULL; - - // Close the Wave device - CloseHandle(pData->hWaveThreadEvent); - pData->hWaveThreadEvent = 0; - - waveInClose(pData->hWaveHandle.In); - pData->hWaveHandle.In = 0; - - free(pData); - pDevice->ExtraData = NULL; -} - -static void WinMMStartCapture(ALCdevice *pDevice) -{ - WinMMData *pData = (WinMMData*)pDevice->ExtraData; - waveInStart(pData->hWaveHandle.In); -} - -static void WinMMStopCapture(ALCdevice *pDevice) -{ - WinMMData *pData = (WinMMData*)pDevice->ExtraData; - waveInStop(pData->hWaveHandle.In); -} - -static ALCuint WinMMAvailableSamples(ALCdevice *pDevice) -{ - WinMMData *pData = (WinMMData*)pDevice->ExtraData; - return RingBufferSize(pData->pRing); -} - -static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) -{ - WinMMData *pData = (WinMMData*)pDevice->ExtraData; - - if(WinMMAvailableSamples(pDevice) >= lSamples) - ReadRingBuffer(pData->pRing, pBuffer, lSamples); - else - alcSetError(pDevice, ALC_INVALID_VALUE); -} - - -static const BackendFuncs WinMMFuncs = { - WinMMOpenPlayback, - WinMMClosePlayback, - WinMMResetPlayback, - WinMMStopPlayback, - WinMMOpenCapture, - WinMMCloseCapture, - WinMMStartCapture, - WinMMStopCapture, - WinMMCaptureSamples, - WinMMAvailableSamples -}; - -ALCboolean alcWinMMInit(BackendFuncs *FuncList) -{ - *FuncList = WinMMFuncs; - return ALC_TRUE; -} - -void alcWinMMDeinit() -{ - ALuint lLoop; - - for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++) - free(PlaybackDeviceList[lLoop]); - free(PlaybackDeviceList); - PlaybackDeviceList = NULL; - - NumPlaybackDevices = 0; - - - for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++) - free(CaptureDeviceList[lLoop]); - free(CaptureDeviceList); - CaptureDeviceList = NULL; - - NumCaptureDevices = 0; -} - -void alcWinMMProbe(enum DevProbe type) -{ - ALuint i; - - switch(type) - { - case DEVICE_PROBE: - ProbePlaybackDevices(); - if(NumPlaybackDevices > 0) - AppendDeviceList(woDefault); - break; - - case ALL_DEVICE_PROBE: - ProbePlaybackDevices(); - if(NumPlaybackDevices > 0) - AppendAllDeviceList(woDefault); - for(i = 0;i < NumPlaybackDevices;i++) - { - if(PlaybackDeviceList[i]) - AppendAllDeviceList(PlaybackDeviceList[i]); - } - break; - - case CAPTURE_DEVICE_PROBE: - ProbeCaptureDevices(); - for(i = 0;i < NumCaptureDevices;i++) - { - if(CaptureDeviceList[i]) - AppendCaptureDeviceList(CaptureDeviceList[i]); - } - break; - } -} diff --git a/CMakeLists.txt b/CMakeLists.txt index 9901f511..6d343a62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -387,8 +387,9 @@ SET(ALC_OBJS Alc/ALc.c Alc/hrtf.c Alc/mixer.c Alc/panning.c - Alc/loopback.c - Alc/null.c + # Default backends, always available + Alc/backends/loopback.c + Alc/backends/null.c ) SET(BACKENDS "") @@ -411,7 +412,7 @@ IF(ALSA) CHECK_SHARED_FUNCTION_EXISTS(snd_pcm_open "alsa/asoundlib.h" asound "" HAVE_LIBASOUND) IF(HAVE_LIBASOUND OR HAVE_DLFCN_H OR WIN32) SET(HAVE_ALSA 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/alsa.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/alsa.c) IF(HAVE_DLFCN_H OR WIN32) SET(BACKENDS "${BACKENDS} ALSA,") ELSE() @@ -430,7 +431,7 @@ IF(OSS) CHECK_INCLUDE_FILE(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) IF(HAVE_SYS_SOUNDCARD_H) SET(HAVE_OSS 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/oss.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/oss.c) SET(BACKENDS "${BACKENDS} OSS,") ENDIF() ENDIF() @@ -443,7 +444,7 @@ IF(SOLARIS) CHECK_INCLUDE_FILE(sys/audioio.h HAVE_SYS_AUDIOIO_H) IF(HAVE_SYS_AUDIOIO_H) SET(HAVE_SOLARIS 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/solaris.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/solaris.c) SET(BACKENDS "${BACKENDS} Solaris,") ENDIF() ENDIF() @@ -458,7 +459,7 @@ IF(SNDIO) CHECK_SHARED_FUNCTION_EXISTS(sio_open "sndio.h" sndio "" HAVE_LIBSNDIO) IF(HAVE_LIBSNDIO OR HAVE_DLFCN_H OR WIN32) SET(HAVE_SNDIO 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/sndio.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/sndio.c) IF(HAVE_DLFCN_H OR WIN32) SET(BACKENDS "${BACKENDS} SndIO,") ELSE() @@ -478,7 +479,7 @@ IF(HAVE_WINDOWS_H) CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H) IF(HAVE_MMDEVICEAPI_H) SET(HAVE_MMDEVAPI 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/mmdevapi.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/mmdevapi.c) SET(BACKENDS "${BACKENDS} MMDevApi,") ENDIF() @@ -495,7 +496,7 @@ IF(DSOUND) CHECK_SHARED_FUNCTION_EXISTS(DirectSoundCreate "dsound.h" dsound "" HAVE_LIBDSOUND) IF(HAVE_LIBDSOUND OR HAVE_DLFCN_H OR WIN32) SET(HAVE_DSOUND 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/dsound.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/dsound.c) IF(HAVE_DLFCN_H OR WIN32) SET(BACKENDS "${BACKENDS} DirectSound,") @@ -515,7 +516,7 @@ IF(HAVE_WINDOWS_H) CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H -D_WIN32_WINNT=0x0500) IF(HAVE_MMSYSTEM_H AND HAVE_LIBWINMM) SET(HAVE_WINMM 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/winmm.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/winmm.c) SET(BACKENDS "${BACKENDS} WinMM,") ENDIF() ENDIF() @@ -531,7 +532,7 @@ IF(PORTAUDIO) CHECK_SHARED_FUNCTION_EXISTS(Pa_Initialize "portaudio.h" portaudio "" HAVE_LIBPORTAUDIO) IF(HAVE_LIBPORTAUDIO OR HAVE_DLFCN_H OR WIN32) SET(HAVE_PORTAUDIO 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/portaudio.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/portaudio.c) IF(HAVE_DLFCN_H OR WIN32) SET(BACKENDS "${BACKENDS} PortAudio,") ELSE() @@ -552,7 +553,7 @@ IF(PULSEAUDIO) CHECK_SHARED_FUNCTION_EXISTS(pa_context_new "pulse/pulseaudio.h" pulse "" HAVE_LIBPULSE) IF(HAVE_LIBPULSE OR HAVE_DLFCN_H OR WIN32) SET(HAVE_PULSEAUDIO 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/pulseaudio.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/pulseaudio.c) IF(HAVE_DLFCN_H OR WIN32) SET(BACKENDS "${BACKENDS} PulseAudio,") ELSE() @@ -571,7 +572,7 @@ IF(COREAUDIO) CHECK_INCLUDE_FILE(/System/Library/Frameworks/CoreAudio.framework/Headers/CoreAudio.h HAVE_COREAUDIO_FRAMEWORK) IF(HAVE_COREAUDIO_FRAMEWORK) SET(HAVE_COREAUDIO 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/coreaudio.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/coreaudio.c) SET(BACKENDS "${BACKENDS} CoreAudio,") SET(EXTRA_LIBS /System/Library/Frameworks/CoreAudio.framework ${EXTRA_LIBS}) SET(EXTRA_LIBS /System/Library/Frameworks/AudioUnit.framework ${EXTRA_LIBS}) @@ -589,7 +590,7 @@ IF(OPENSL) CHECK_SHARED_FUNCTION_EXISTS(slCreateEngine "SLES/OpenSLES.h" OpenSLES "" HAVE_LIBOPENSLES) IF(HAVE_LIBOPENSLES) SET(HAVE_OPENSL 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/opensl.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/opensl.c) SET(BACKENDS "${BACKENDS} OpenSL,") SET(EXTRA_LIBS OpenSLES ${EXTRA_LIBS}) ENDIF() @@ -602,7 +603,7 @@ ENDIF() # Optionally enable the Wave Writer backend IF(WAVE) SET(HAVE_WAVE 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/wave.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/wave.c) SET(BACKENDS "${BACKENDS} WaveFile,") ENDIF() -- cgit v1.2.3