Add a PortAudio backend
[openal-soft.git] / Alc / sdl.c
blob236aaf9de46796c87ba201c44eb6e63696393b2d
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "alMain.h"
24 #include "AL/al.h"
25 #include "AL/alc.h"
27 #include <string.h>
28 #include <stdlib.h>
29 #ifdef HAVE_DLFCN_H
30 #include <dlfcn.h>
31 #endif
33 #ifdef HAVE_SDL_SDL_H
34 #include <SDL/SDL.h>
35 #elif defined(HAVE_SDL_H)
36 #include <SDL.h>
37 #endif
39 static void *sdl_handle;
40 #define MAKE_FUNC(x) static typeof(x) * p##x
41 MAKE_FUNC(SDL_PauseAudio);
42 MAKE_FUNC(SDL_CloseAudio);
43 MAKE_FUNC(SDL_OpenAudio);
44 MAKE_FUNC(SDL_InitSubSystem);
45 MAKE_FUNC(SDL_GetError);
46 MAKE_FUNC(SDL_LockAudio);
47 MAKE_FUNC(SDL_UnlockAudio);
48 #undef MAKE_FUNC
50 static char *sdl_device;
51 /* SDL audio can only be initialized once per process */
52 static int initialized;
54 typedef struct {
55 SDL_AudioSpec audioSpec;
56 volatile int killNow;
57 ALvoid *thread;
59 ALubyte *mix_data;
60 int data_size;
61 int data_read;
62 int data_write;
63 } sdl_data;
66 static void SDLCALL fillAudio(void *userdata, Uint8 *stream, int len)
68 sdl_data *data = (sdl_data*)userdata;
69 int rem = data->data_size-data->data_read;
70 if(len >= rem)
72 memcpy(stream, data->mix_data + data->data_read, rem);
73 stream += rem;
74 len -= rem;
75 data->data_read = 0;
77 if(len > 0)
79 memcpy(stream, data->mix_data + data->data_read, len);
80 data->data_read += len;
84 static ALuint SDLProc(ALvoid *ptr)
86 ALCdevice *pDevice = (ALCdevice*)ptr;
87 sdl_data *data = (sdl_data*)pDevice->ExtraData;
88 int len, rem;
90 pSDL_PauseAudio(0);
91 while(!data->killNow)
93 pSDL_LockAudio();
95 len = (data->data_read-data->data_write+data->data_size)%data->data_size;
96 if(len == 0)
98 pSDL_UnlockAudio();
100 Sleep(1);
101 continue;
104 rem = data->data_size - data->data_write;
106 SuspendContext(NULL);
107 if(len > rem)
109 aluMixData(pDevice->Context, data->mix_data+data->data_write, rem, pDevice->Format);
110 aluMixData(pDevice->Context, data->mix_data, len-rem, pDevice->Format);
112 else
113 aluMixData(pDevice->Context, data->mix_data+data->data_write, len, pDevice->Format);
114 ProcessContext(NULL);
116 data->data_write = data->data_read;
118 pSDL_UnlockAudio();
120 pSDL_PauseAudio(1);
122 return 0;
125 static ALCboolean sdl_open_playback(ALCdevice *device, const ALCchar *deviceName)
127 SDL_AudioSpec sdlSpec;
128 ALuint frameSize;
129 sdl_data *data;
131 if(initialized || !sdl_device)
132 return ALC_FALSE;
134 if(deviceName)
136 if(strcmp(deviceName, sdl_device))
137 return ALC_FALSE;
138 device->szDeviceName = sdl_device;
140 else
141 device->szDeviceName = sdl_device;
143 data = (sdl_data*)calloc(1, sizeof(sdl_data));
144 data->killNow = 0;
146 frameSize = aluBytesFromFormat(device->Format) *
147 aluChannelsFromFormat(device->Format);
149 sdlSpec.freq = device->Frequency;
150 sdlSpec.channels = aluChannelsFromFormat(device->Format);
151 switch(aluBytesFromFormat(device->Format))
153 case 1:
154 sdlSpec.format = AUDIO_U8;
155 break;
156 case 2:
157 sdlSpec.format = AUDIO_S16SYS;
158 break;
159 default:
160 AL_PRINT("Unknown format?! %x\n", device->Format);
161 free(data);
162 return ALC_FALSE;
164 sdlSpec.samples = 1;
165 while(sdlSpec.samples < device->UpdateSize)
166 sdlSpec.samples <<= 1;
167 sdlSpec.samples >>= 1;
169 sdlSpec.callback = fillAudio;
170 sdlSpec.userdata = data;
172 if(pSDL_OpenAudio(&sdlSpec, &data->audioSpec) < 0)
174 AL_PRINT("Audio init failed: %s\n", pSDL_GetError());
175 free(data);
176 return ALC_FALSE;
179 if(!((data->audioSpec.format == AUDIO_U8 && aluBytesFromFormat(device->Format) == 1) ||
180 (data->audioSpec.format == AUDIO_S16SYS && aluBytesFromFormat(device->Format) == 2)))
182 AL_PRINT("Could not set %d-bit, got format %#x instead\n", aluBytesFromFormat(device->Format), data->audioSpec.format);
183 pSDL_CloseAudio();
184 free(data);
185 return ALC_FALSE;
187 if(aluChannelsFromFormat(device->Format) != data->audioSpec.channels)
189 AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), data->audioSpec.channels);
190 pSDL_CloseAudio();
191 free(data);
192 return ALC_FALSE;
195 device->Frequency = data->audioSpec.freq;
196 device->UpdateSize = data->audioSpec.size / frameSize;
198 data->data_size = device->UpdateSize * frameSize * 2;
199 data->mix_data = malloc(data->data_size);
200 if(data->mix_data == NULL)
202 AL_PRINT("Could not allocate %d bytes\n", data->data_size);
203 pSDL_CloseAudio();
204 free(data);
205 return ALC_FALSE;
207 memset(data->mix_data, data->audioSpec.silence, data->data_size);
209 device->ExtraData = data;
210 data->thread = StartThread(SDLProc, device);
211 if(data->thread == NULL)
213 pSDL_CloseAudio();
214 device->ExtraData = NULL;
215 free(data->mix_data);
216 free(data);
217 return ALC_FALSE;
220 initialized = 1;
222 return ALC_TRUE;
225 static void sdl_close_playback(ALCdevice *device)
227 sdl_data *data = (sdl_data*)device->ExtraData;
228 data->killNow = 1;
229 StopThread(data->thread);
231 pSDL_CloseAudio();
232 initialized = 0;
234 free(data->mix_data);
235 free(data);
236 device->ExtraData = NULL;
240 static ALCboolean sdl_open_capture(ALCdevice *device, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
242 return ALC_FALSE;
243 (void)device;
244 (void)deviceName;
245 (void)frequency;
246 (void)format;
247 (void)SampleSize;
251 BackendFuncs sdl_funcs = {
252 sdl_open_playback,
253 sdl_close_playback,
254 sdl_open_capture,
255 NULL,
256 NULL,
257 NULL,
258 NULL,
259 NULL
262 void alc_sdl_init(BackendFuncs *func_list)
264 const char *str;
266 *func_list = sdl_funcs;
268 #ifdef HAVE_DLFCN_H
269 #if defined(__APPLE__) && defined(__MACH__)
270 # define SDLLIB "SDL.framework/SDL"
271 #else
272 # define SDLLIB "libSDL.so"
273 #endif
274 sdl_handle = dlopen(SDLLIB, RTLD_NOW);
275 if(!sdl_handle)
276 return;
277 dlerror();
279 #define LOAD_FUNC(f) do { \
280 p##f = (typeof(f)*)dlsym(sdl_handle, #f); \
281 if((str=dlerror()) != NULL) \
283 dlclose(sdl_handle); \
284 sdl_handle = NULL; \
285 AL_PRINT("Could not load %s from "SDLLIB": %s\n", #f, str); \
286 return; \
288 } while(0)
289 #else
290 str = NULL;
291 sdl_handle = 0xDEADBEEF;
292 #define LOAD_FUNC(f) p##f = f
293 #endif
295 LOAD_FUNC(SDL_PauseAudio);
296 LOAD_FUNC(SDL_CloseAudio);
297 LOAD_FUNC(SDL_OpenAudio);
298 LOAD_FUNC(SDL_InitSubSystem);
299 LOAD_FUNC(SDL_GetError);
300 LOAD_FUNC(SDL_LockAudio);
301 LOAD_FUNC(SDL_UnlockAudio);
303 #undef LOAD_FUNC
305 if(pSDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
306 return;
308 sdl_device = AppendDeviceList("SDL Software");
309 AppendAllDeviceList(sdl_device);