📄 sdl_alsa_audio.c
字号:
/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2004 Sam Lantinga 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 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Sam Lantinga slouken@libsdl.org*/#include "SDL_config.h"/* Allow access to a raw mixing buffer */#include <sys/types.h>#include <signal.h> /* For kill() */#include "SDL_timer.h"#include "SDL_audio.h"#include "../SDL_audiomem.h"#include "../SDL_audio_c.h"#include "SDL_alsa_audio.h"#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC#include <dlfcn.h>#include "SDL_name.h"#include "SDL_loadso.h"#else#define SDL_NAME(X) X#endif/* The tag name used by ALSA audio */#define DRIVER_NAME "alsa"/* The default ALSA audio driver */#define DEFAULT_DEVICE "default"/* Audio driver functions */static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec);static void ALSA_WaitAudio(_THIS);static void ALSA_PlayAudio(_THIS);static Uint8 *ALSA_GetAudioBuf(_THIS);static void ALSA_CloseAudio(_THIS);#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMICstatic const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;static void *alsa_handle = NULL;static int alsa_loaded = 0;static int (*SDL_snd_pcm_open)(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);static int (*SDL_NAME(snd_pcm_resume))(snd_pcm_t *pcm);static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);static const char *(*SDL_NAME(snd_strerror))(int errnum);static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);static int (*SDL_NAME(snd_pcm_hw_params_set_access))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access);static int (*SDL_NAME(snd_pcm_hw_params_set_format))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val);static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params);static unsigned int (*SDL_NAME(snd_pcm_hw_params_set_rate_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int *dir);static snd_pcm_uframes_t (*SDL_NAME(snd_pcm_hw_params_set_period_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int *dir);static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_hw_params_get_period_size))(const snd_pcm_hw_params_t *params);static unsigned int (*SDL_NAME(snd_pcm_hw_params_set_periods_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int *dir);static int (*SDL_NAME(snd_pcm_hw_params_get_periods))(snd_pcm_hw_params_t *params);static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);/**/static int (*SDL_NAME(snd_pcm_sw_params_current))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams);static int (*SDL_NAME(snd_pcm_sw_params_set_start_threshold))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);static int (*SDL_NAME(snd_pcm_sw_params_set_avail_min))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);static int (*SDL_NAME(snd_pcm_sw_params))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock);#define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)#define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof)/* cast funcs to char* first, to please GCC's strict aliasing rules. */static struct { const char *name; void **func;} alsa_functions[] = { { "snd_pcm_open", (void**)(char*)&SDL_NAME(snd_pcm_open) }, { "snd_pcm_close", (void**)(char*)&SDL_NAME(snd_pcm_close) }, { "snd_pcm_writei", (void**)(char*)&SDL_NAME(snd_pcm_writei) }, { "snd_pcm_resume", (void**)(char*)&SDL_NAME(snd_pcm_resume) }, { "snd_pcm_prepare", (void**)(char*)&SDL_NAME(snd_pcm_prepare) }, { "snd_pcm_drain", (void**)(char*)&SDL_NAME(snd_pcm_drain) }, { "snd_strerror", (void**)(char*)&SDL_NAME(snd_strerror) }, { "snd_pcm_hw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof) }, { "snd_pcm_sw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof) }, { "snd_pcm_hw_params_any", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_any) }, { "snd_pcm_hw_params_set_access", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access) }, { "snd_pcm_hw_params_set_format", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format) }, { "snd_pcm_hw_params_set_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_channels) }, { "snd_pcm_hw_params_get_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_channels) }, { "snd_pcm_hw_params_set_rate_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_rate_near) }, { "snd_pcm_hw_params_set_period_size_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_period_size_near) }, { "snd_pcm_hw_params_get_period_size", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_period_size) }, { "snd_pcm_hw_params_set_periods_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_periods_near) }, { "snd_pcm_hw_params_get_periods", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_periods) }, { "snd_pcm_hw_params", (void**)(char*)&SDL_NAME(snd_pcm_hw_params) }, { "snd_pcm_sw_params_current", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_current) }, { "snd_pcm_sw_params_set_start_threshold", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_start_threshold) }, { "snd_pcm_sw_params_set_avail_min", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_avail_min) }, { "snd_pcm_sw_params", (void**)(char*)&SDL_NAME(snd_pcm_sw_params) }, { "snd_pcm_nonblock", (void**)(char*)&SDL_NAME(snd_pcm_nonblock) },};static void UnloadALSALibrary(void) { if (alsa_loaded) {/* SDL_UnloadObject(alsa_handle);*/ dlclose(alsa_handle); alsa_handle = NULL; alsa_loaded = 0; }}static int LoadALSALibrary(void) { int i, retval = -1;/* alsa_handle = SDL_LoadObject(alsa_library);*/ alsa_handle = dlopen(alsa_library,RTLD_NOW); if (alsa_handle) { alsa_loaded = 1; retval = 0; for (i = 0; i < SDL_arraysize(alsa_functions); i++) {/* *alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);*/#if HAVE_DLVSYM *alsa_functions[i].func = dlvsym(alsa_handle,alsa_functions[i].name,"ALSA_0.9"); if (!*alsa_functions[i].func)#endif *alsa_functions[i].func = dlsym(alsa_handle,alsa_functions[i].name); if (!*alsa_functions[i].func) { retval = -1; UnloadALSALibrary(); break; } } } return retval;}#elsestatic void UnloadALSALibrary(void) { return;}static int LoadALSALibrary(void) { return 0;}#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */static const char *get_audio_device(int channels){ const char *device; device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */ if ( device == NULL ) { if (channels == 6) device = "surround51"; else if (channels == 4) device = "surround40"; else device = DEFAULT_DEVICE; } return device;}/* Audio driver bootstrap functions */static int Audio_Available(void){ int available; int status; snd_pcm_t *handle; available = 0; if (LoadALSALibrary() < 0) { return available; } status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(2), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if ( status >= 0 ) { available = 1; SDL_NAME(snd_pcm_close)(handle); } UnloadALSALibrary(); return(available);}static void Audio_DeleteDevice(SDL_AudioDevice *device){ SDL_free(device->hidden); SDL_free(device); UnloadALSALibrary();}static SDL_AudioDevice *Audio_CreateDevice(int devindex){ SDL_AudioDevice *this; /* Initialize all variables that we clean on shutdown */ LoadALSALibrary(); this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); if ( this ) { SDL_memset(this, 0, (sizeof *this)); this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc((sizeof *this->hidden)); } if ( (this == NULL) || (this->hidden == NULL) ) { SDL_OutOfMemory(); if ( this ) { SDL_free(this); } return(0); } SDL_memset(this->hidden, 0, (sizeof *this->hidden)); /* Set the function pointers */ this->OpenAudio = ALSA_OpenAudio; this->WaitAudio = ALSA_WaitAudio; this->PlayAudio = ALSA_PlayAudio; this->GetAudioBuf = ALSA_GetAudioBuf; this->CloseAudio = ALSA_CloseAudio; this->free = Audio_DeleteDevice; return this;}AudioBootStrap ALSA_bootstrap = { DRIVER_NAME, "ALSA 0.9 PCM audio", Audio_Available, Audio_CreateDevice};/* This function waits until it is possible to write a full sound buffer */static void ALSA_WaitAudio(_THIS){ /* Check to see if the thread-parent process is still alive */ { static int cnt = 0; /* Note that this only works with thread implementations that use a different process id for each thread. */ if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ if ( kill(parent, 0) < 0 ) { this->enabled = 0; } } }}/* * http://bugzilla.libsdl.org/show_bug.cgi?id=110 * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -