slouken@0
|
1 |
/*
|
slouken@5535
|
2 |
Simple DirectMedia Layer
|
slouken@9998
|
3 |
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
|
slouken@0
|
4 |
|
slouken@5535
|
5 |
This software is provided 'as-is', without any express or implied
|
slouken@5535
|
6 |
warranty. In no event will the authors be held liable for any damages
|
slouken@5535
|
7 |
arising from the use of this software.
|
slouken@0
|
8 |
|
slouken@5535
|
9 |
Permission is granted to anyone to use this software for any purpose,
|
slouken@5535
|
10 |
including commercial applications, and to alter it and redistribute it
|
slouken@5535
|
11 |
freely, subject to the following restrictions:
|
slouken@0
|
12 |
|
slouken@5535
|
13 |
1. The origin of this software must not be misrepresented; you must not
|
slouken@5535
|
14 |
claim that you wrote the original software. If you use this software
|
slouken@5535
|
15 |
in a product, an acknowledgment in the product documentation would be
|
slouken@5535
|
16 |
appreciated but is not required.
|
slouken@5535
|
17 |
2. Altered source versions must be plainly marked as such, and must not be
|
slouken@5535
|
18 |
misrepresented as being the original software.
|
slouken@5535
|
19 |
3. This notice may not be removed or altered from any source distribution.
|
slouken@0
|
20 |
*/
|
icculus@8093
|
21 |
#include "../SDL_internal.h"
|
slouken@0
|
22 |
|
slouken@0
|
23 |
/* Allow access to a raw mixing buffer */
|
slouken@0
|
24 |
|
slouken@0
|
25 |
#include "SDL.h"
|
slouken@2984
|
26 |
#include "SDL_audio.h"
|
slouken@0
|
27 |
#include "SDL_audio_c.h"
|
slouken@0
|
28 |
#include "SDL_sysaudio.h"
|
icculus@10146
|
29 |
#include "../thread/SDL_systhread.h"
|
slouken@0
|
30 |
|
aschiffler@4865
|
31 |
#define _THIS SDL_AudioDevice *_this
|
icculus@2049
|
32 |
|
icculus@2049
|
33 |
static SDL_AudioDriver current_audio;
|
icculus@2049
|
34 |
static SDL_AudioDevice *open_devices[16];
|
icculus@2049
|
35 |
|
icculus@2049
|
36 |
/*
|
icculus@2049
|
37 |
* Not all of these will be compiled and linked in, but it's convenient
|
icculus@2049
|
38 |
* to have a complete list here and saves yet-another block of #ifdefs...
|
icculus@2049
|
39 |
* Please see bootstrap[], below, for the actual #ifdef mess.
|
icculus@2049
|
40 |
*/
|
icculus@2049
|
41 |
extern AudioBootStrap BSD_AUDIO_bootstrap;
|
icculus@2049
|
42 |
extern AudioBootStrap DSP_bootstrap;
|
icculus@2049
|
43 |
extern AudioBootStrap ALSA_bootstrap;
|
icculus@2271
|
44 |
extern AudioBootStrap PULSEAUDIO_bootstrap;
|
slouken@3099
|
45 |
extern AudioBootStrap QSAAUDIO_bootstrap;
|
icculus@2049
|
46 |
extern AudioBootStrap SUNAUDIO_bootstrap;
|
icculus@2049
|
47 |
extern AudioBootStrap ARTS_bootstrap;
|
icculus@2049
|
48 |
extern AudioBootStrap ESD_bootstrap;
|
gabomdq@8833
|
49 |
extern AudioBootStrap NACLAUD_bootstrap;
|
icculus@2049
|
50 |
extern AudioBootStrap NAS_bootstrap;
|
icculus@5592
|
51 |
extern AudioBootStrap XAUDIO2_bootstrap;
|
icculus@2049
|
52 |
extern AudioBootStrap DSOUND_bootstrap;
|
icculus@5588
|
53 |
extern AudioBootStrap WINMM_bootstrap;
|
icculus@2049
|
54 |
extern AudioBootStrap PAUDIO_bootstrap;
|
icculus@7981
|
55 |
extern AudioBootStrap HAIKUAUDIO_bootstrap;
|
icculus@2049
|
56 |
extern AudioBootStrap COREAUDIO_bootstrap;
|
icculus@2049
|
57 |
extern AudioBootStrap SNDMGR_bootstrap;
|
icculus@2049
|
58 |
extern AudioBootStrap DISKAUD_bootstrap;
|
icculus@2049
|
59 |
extern AudioBootStrap DUMMYAUD_bootstrap;
|
icculus@2049
|
60 |
extern AudioBootStrap DCAUD_bootstrap;
|
icculus@2049
|
61 |
extern AudioBootStrap DART_bootstrap;
|
slouken@2735
|
62 |
extern AudioBootStrap NDSAUD_bootstrap;
|
slouken@2947
|
63 |
extern AudioBootStrap FUSIONSOUND_bootstrap;
|
paul@4718
|
64 |
extern AudioBootStrap ANDROIDAUD_bootstrap;
|
kimonline@7009
|
65 |
extern AudioBootStrap PSPAUD_bootstrap;
|
icculus@7367
|
66 |
extern AudioBootStrap SNDIO_bootstrap;
|
icculus@9278
|
67 |
extern AudioBootStrap EmscriptenAudio_bootstrap;
|
icculus@1190
|
68 |
|
gabomdq@8833
|
69 |
|
slouken@0
|
70 |
/* Available audio drivers */
|
slouken@3162
|
71 |
static const AudioBootStrap *const bootstrap[] = {
|
icculus@2939
|
72 |
#if SDL_AUDIO_DRIVER_PULSEAUDIO
|
icculus@2939
|
73 |
&PULSEAUDIO_bootstrap,
|
slouken@0
|
74 |
#endif
|
slouken@1361
|
75 |
#if SDL_AUDIO_DRIVER_ALSA
|
slouken@1895
|
76 |
&ALSA_bootstrap,
|
slouken@0
|
77 |
#endif
|
icculus@7367
|
78 |
#if SDL_AUDIO_DRIVER_SNDIO
|
icculus@7367
|
79 |
&SNDIO_bootstrap,
|
icculus@7367
|
80 |
#endif
|
slouken@4548
|
81 |
#if SDL_AUDIO_DRIVER_BSD
|
slouken@4548
|
82 |
&BSD_AUDIO_bootstrap,
|
slouken@4548
|
83 |
#endif
|
icculus@2939
|
84 |
#if SDL_AUDIO_DRIVER_OSS
|
icculus@2939
|
85 |
&DSP_bootstrap,
|
icculus@2271
|
86 |
#endif
|
slouken@3099
|
87 |
#if SDL_AUDIO_DRIVER_QSA
|
slouken@3099
|
88 |
&QSAAUDIO_bootstrap,
|
slouken@663
|
89 |
#endif
|
slouken@1361
|
90 |
#if SDL_AUDIO_DRIVER_SUNAUDIO
|
slouken@1895
|
91 |
&SUNAUDIO_bootstrap,
|
slouken@148
|
92 |
#endif
|
slouken@1361
|
93 |
#if SDL_AUDIO_DRIVER_ARTS
|
slouken@1895
|
94 |
&ARTS_bootstrap,
|
slouken@0
|
95 |
#endif
|
slouken@1361
|
96 |
#if SDL_AUDIO_DRIVER_ESD
|
slouken@1895
|
97 |
&ESD_bootstrap,
|
slouken@0
|
98 |
#endif
|
gabomdq@8833
|
99 |
#if SDL_AUDIO_DRIVER_NACL
|
icculus@10233
|
100 |
&NACLAUD_bootstrap,
|
gabomdq@8833
|
101 |
#endif
|
slouken@1361
|
102 |
#if SDL_AUDIO_DRIVER_NAS
|
slouken@1895
|
103 |
&NAS_bootstrap,
|
slouken@0
|
104 |
#endif
|
icculus@5592
|
105 |
#if SDL_AUDIO_DRIVER_XAUDIO2
|
icculus@5592
|
106 |
&XAUDIO2_bootstrap,
|
icculus@5592
|
107 |
#endif
|
slouken@1361
|
108 |
#if SDL_AUDIO_DRIVER_DSOUND
|
slouken@1895
|
109 |
&DSOUND_bootstrap,
|
slouken@0
|
110 |
#endif
|
icculus@5588
|
111 |
#if SDL_AUDIO_DRIVER_WINMM
|
icculus@5588
|
112 |
&WINMM_bootstrap,
|
slouken@0
|
113 |
#endif
|
icculus@2049
|
114 |
#if SDL_AUDIO_DRIVER_PAUDIO
|
icculus@2049
|
115 |
&PAUDIO_bootstrap,
|
slouken@1361
|
116 |
#endif
|
icculus@7981
|
117 |
#if SDL_AUDIO_DRIVER_HAIKU
|
icculus@7981
|
118 |
&HAIKUAUDIO_bootstrap,
|
slouken@0
|
119 |
#endif
|
slouken@1361
|
120 |
#if SDL_AUDIO_DRIVER_COREAUDIO
|
slouken@1895
|
121 |
&COREAUDIO_bootstrap,
|
slouken@935
|
122 |
#endif
|
slouken@1361
|
123 |
#if SDL_AUDIO_DRIVER_DISK
|
slouken@1895
|
124 |
&DISKAUD_bootstrap,
|
slouken@68
|
125 |
#endif
|
icculus@1532
|
126 |
#if SDL_AUDIO_DRIVER_DUMMY
|
slouken@1895
|
127 |
&DUMMYAUD_bootstrap,
|
icculus@1532
|
128 |
#endif
|
slouken@2947
|
129 |
#if SDL_AUDIO_DRIVER_FUSIONSOUND
|
slouken@2947
|
130 |
&FUSIONSOUND_bootstrap,
|
slouken@2947
|
131 |
#endif
|
paul@4718
|
132 |
#if SDL_AUDIO_DRIVER_ANDROID
|
paul@4718
|
133 |
&ANDROIDAUD_bootstrap,
|
paul@4718
|
134 |
#endif
|
kimonline@7009
|
135 |
#if SDL_AUDIO_DRIVER_PSP
|
kimonline@7009
|
136 |
&PSPAUD_bootstrap,
|
kimonline@7009
|
137 |
#endif
|
icculus@9278
|
138 |
#if SDL_AUDIO_DRIVER_EMSCRIPTEN
|
icculus@9278
|
139 |
&EmscriptenAudio_bootstrap,
|
icculus@9278
|
140 |
#endif
|
slouken@1895
|
141 |
NULL
|
slouken@0
|
142 |
};
|
icculus@2049
|
143 |
|
slouken@2060
|
144 |
static SDL_AudioDevice *
|
slouken@2060
|
145 |
get_audio_device(SDL_AudioDeviceID id)
|
icculus@2049
|
146 |
{
|
icculus@2049
|
147 |
id--;
|
slouken@2060
|
148 |
if ((id >= SDL_arraysize(open_devices)) || (open_devices[id] == NULL)) {
|
icculus@2049
|
149 |
SDL_SetError("Invalid audio device ID");
|
icculus@2049
|
150 |
return NULL;
|
icculus@2049
|
151 |
}
|
icculus@2049
|
152 |
|
icculus@2049
|
153 |
return open_devices[id];
|
icculus@2049
|
154 |
}
|
icculus@2049
|
155 |
|
icculus@2049
|
156 |
|
icculus@2049
|
157 |
/* stubs for audio drivers that don't need a specific entry point... */
|
icculus@5593
|
158 |
static void
|
icculus@9394
|
159 |
SDL_AudioDetectDevices_Default(void)
|
icculus@9394
|
160 |
{
|
icculus@9394
|
161 |
/* you have to write your own implementation if these assertions fail. */
|
icculus@9394
|
162 |
SDL_assert(current_audio.impl.OnlyHasDefaultOutputDevice);
|
icculus@10258
|
163 |
SDL_assert(current_audio.impl.OnlyHasDefaultCaptureDevice || !current_audio.impl.HasCaptureSupport);
|
icculus@9394
|
164 |
|
icculus@9394
|
165 |
SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) ((size_t) 0x1));
|
icculus@9394
|
166 |
if (current_audio.impl.HasCaptureSupport) {
|
icculus@9394
|
167 |
SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) ((size_t) 0x2));
|
icculus@9394
|
168 |
}
|
slouken@2060
|
169 |
}
|
slouken@2735
|
170 |
|
slouken@2060
|
171 |
static void
|
slouken@2060
|
172 |
SDL_AudioThreadInit_Default(_THIS)
|
slouken@2060
|
173 |
{ /* no-op. */
|
slouken@2060
|
174 |
}
|
slouken@2735
|
175 |
|
slouken@2060
|
176 |
static void
|
slouken@2060
|
177 |
SDL_AudioWaitDevice_Default(_THIS)
|
slouken@2060
|
178 |
{ /* no-op. */
|
slouken@2060
|
179 |
}
|
slouken@2735
|
180 |
|
slouken@2060
|
181 |
static void
|
slouken@2060
|
182 |
SDL_AudioPlayDevice_Default(_THIS)
|
slouken@2060
|
183 |
{ /* no-op. */
|
slouken@2060
|
184 |
}
|
slouken@2735
|
185 |
|
icculus@9031
|
186 |
static int
|
icculus@9031
|
187 |
SDL_AudioGetPendingBytes_Default(_THIS)
|
icculus@9031
|
188 |
{
|
icculus@9031
|
189 |
return 0;
|
icculus@9031
|
190 |
}
|
icculus@9031
|
191 |
|
slouken@2060
|
192 |
static Uint8 *
|
slouken@2060
|
193 |
SDL_AudioGetDeviceBuf_Default(_THIS)
|
slouken@2060
|
194 |
{
|
slouken@2060
|
195 |
return NULL;
|
slouken@2060
|
196 |
}
|
slouken@2735
|
197 |
|
slouken@2060
|
198 |
static void
|
slouken@2060
|
199 |
SDL_AudioWaitDone_Default(_THIS)
|
slouken@2060
|
200 |
{ /* no-op. */
|
slouken@2060
|
201 |
}
|
slouken@2735
|
202 |
|
icculus@10239
|
203 |
static int
|
icculus@10239
|
204 |
SDL_AudioCaptureFromDevice_Default(_THIS, void *buffer, int buflen)
|
icculus@10239
|
205 |
{
|
icculus@10239
|
206 |
return -1; /* just fail immediately. */
|
icculus@10239
|
207 |
}
|
icculus@10239
|
208 |
|
icculus@10239
|
209 |
static void
|
icculus@10239
|
210 |
SDL_AudioFlushCapture_Default(_THIS)
|
icculus@10239
|
211 |
{ /* no-op. */
|
icculus@10239
|
212 |
}
|
icculus@10239
|
213 |
|
slouken@2060
|
214 |
static void
|
slouken@2060
|
215 |
SDL_AudioCloseDevice_Default(_THIS)
|
slouken@2060
|
216 |
{ /* no-op. */
|
slouken@2060
|
217 |
}
|
slouken@2735
|
218 |
|
slouken@2060
|
219 |
static void
|
slouken@2060
|
220 |
SDL_AudioDeinitialize_Default(void)
|
slouken@2060
|
221 |
{ /* no-op. */
|
slouken@2060
|
222 |
}
|
icculus@2049
|
223 |
|
icculus@9394
|
224 |
static void
|
icculus@9394
|
225 |
SDL_AudioFreeDeviceHandle_Default(void *handle)
|
icculus@9394
|
226 |
{ /* no-op. */
|
icculus@9394
|
227 |
}
|
icculus@9394
|
228 |
|
icculus@9394
|
229 |
|
icculus@2049
|
230 |
static int
|
icculus@9394
|
231 |
SDL_AudioOpenDevice_Default(_THIS, void *handle, const char *devname, int iscapture)
|
icculus@2049
|
232 |
{
|
icculus@9394
|
233 |
return SDL_Unsupported();
|
icculus@2049
|
234 |
}
|
icculus@2049
|
235 |
|
icculus@9010
|
236 |
static SDL_INLINE SDL_bool
|
icculus@9010
|
237 |
is_in_audio_device_thread(SDL_AudioDevice * device)
|
icculus@9010
|
238 |
{
|
icculus@9010
|
239 |
/* The device thread locks the same mutex, but not through the public API.
|
icculus@9010
|
240 |
This check is in case the application, in the audio callback,
|
icculus@9010
|
241 |
tries to lock the thread that we've already locked from the
|
icculus@9010
|
242 |
device thread...just in case we only have non-recursive mutexes. */
|
icculus@9010
|
243 |
if (device->thread && (SDL_ThreadID() == device->threadid)) {
|
icculus@9010
|
244 |
return SDL_TRUE;
|
icculus@9010
|
245 |
}
|
icculus@9010
|
246 |
|
icculus@9010
|
247 |
return SDL_FALSE;
|
icculus@9010
|
248 |
}
|
icculus@9010
|
249 |
|
icculus@2049
|
250 |
static void
|
icculus@2049
|
251 |
SDL_AudioLockDevice_Default(SDL_AudioDevice * device)
|
icculus@2049
|
252 |
{
|
icculus@9010
|
253 |
if (!is_in_audio_device_thread(device)) {
|
icculus@9010
|
254 |
SDL_LockMutex(device->mixer_lock);
|
icculus@2049
|
255 |
}
|
icculus@2049
|
256 |
}
|
icculus@2049
|
257 |
|
icculus@2049
|
258 |
static void
|
icculus@2049
|
259 |
SDL_AudioUnlockDevice_Default(SDL_AudioDevice * device)
|
icculus@2049
|
260 |
{
|
icculus@9010
|
261 |
if (!is_in_audio_device_thread(device)) {
|
icculus@9010
|
262 |
SDL_UnlockMutex(device->mixer_lock);
|
icculus@2049
|
263 |
}
|
icculus@2049
|
264 |
}
|
icculus@2049
|
265 |
|
slouken@0
|
266 |
|
slouken@2060
|
267 |
static void
|
slouken@2060
|
268 |
finalize_audio_entry_points(void)
|
icculus@2049
|
269 |
{
|
icculus@2049
|
270 |
/*
|
icculus@2049
|
271 |
* Fill in stub functions for unused driver entry points. This lets us
|
icculus@2049
|
272 |
* blindly call them without having to check for validity first.
|
icculus@2049
|
273 |
*/
|
icculus@2049
|
274 |
|
slouken@2060
|
275 |
#define FILL_STUB(x) \
|
icculus@2049
|
276 |
if (current_audio.impl.x == NULL) { \
|
icculus@2049
|
277 |
current_audio.impl.x = SDL_Audio##x##_Default; \
|
icculus@2049
|
278 |
}
|
icculus@2049
|
279 |
FILL_STUB(DetectDevices);
|
icculus@2049
|
280 |
FILL_STUB(OpenDevice);
|
icculus@2049
|
281 |
FILL_STUB(ThreadInit);
|
icculus@2049
|
282 |
FILL_STUB(WaitDevice);
|
icculus@2049
|
283 |
FILL_STUB(PlayDevice);
|
icculus@9031
|
284 |
FILL_STUB(GetPendingBytes);
|
icculus@2049
|
285 |
FILL_STUB(GetDeviceBuf);
|
icculus@2049
|
286 |
FILL_STUB(WaitDone);
|
icculus@10239
|
287 |
FILL_STUB(CaptureFromDevice);
|
icculus@10239
|
288 |
FILL_STUB(FlushCapture);
|
icculus@2049
|
289 |
FILL_STUB(CloseDevice);
|
icculus@2049
|
290 |
FILL_STUB(LockDevice);
|
icculus@2049
|
291 |
FILL_STUB(UnlockDevice);
|
icculus@9394
|
292 |
FILL_STUB(FreeDeviceHandle);
|
icculus@2049
|
293 |
FILL_STUB(Deinitialize);
|
slouken@2060
|
294 |
#undef FILL_STUB
|
icculus@2049
|
295 |
}
|
icculus@2049
|
296 |
|
slouken@2716
|
297 |
|
icculus@9393
|
298 |
/* device hotplug support... */
|
icculus@9393
|
299 |
|
icculus@9393
|
300 |
static int
|
icculus@9394
|
301 |
add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices, int *devCount)
|
icculus@9393
|
302 |
{
|
icculus@9393
|
303 |
int retval = -1;
|
icculus@9394
|
304 |
const size_t size = sizeof (SDL_AudioDeviceItem) + SDL_strlen(name) + 1;
|
icculus@9394
|
305 |
SDL_AudioDeviceItem *item = (SDL_AudioDeviceItem *) SDL_malloc(size);
|
icculus@9394
|
306 |
if (item == NULL) {
|
icculus@9394
|
307 |
return -1;
|
icculus@9394
|
308 |
}
|
icculus@9393
|
309 |
|
icculus@9410
|
310 |
SDL_assert(handle != NULL); /* we reserve NULL, audio backends can't use it. */
|
icculus@9394
|
311 |
|
icculus@9394
|
312 |
item->handle = handle;
|
icculus@9394
|
313 |
SDL_strlcpy(item->name, name, size - sizeof (SDL_AudioDeviceItem));
|
icculus@9394
|
314 |
|
icculus@9394
|
315 |
SDL_LockMutex(current_audio.detectionLock);
|
icculus@9394
|
316 |
item->next = *devices;
|
icculus@9394
|
317 |
*devices = item;
|
icculus@9394
|
318 |
retval = (*devCount)++;
|
icculus@9394
|
319 |
SDL_UnlockMutex(current_audio.detectionLock);
|
icculus@9393
|
320 |
|
icculus@9393
|
321 |
return retval;
|
icculus@9393
|
322 |
}
|
icculus@9393
|
323 |
|
icculus@9394
|
324 |
static SDL_INLINE int
|
icculus@9394
|
325 |
add_capture_device(const char *name, void *handle)
|
icculus@9393
|
326 |
{
|
icculus@10231
|
327 |
SDL_assert(current_audio.impl.HasCaptureSupport);
|
icculus@9394
|
328 |
return add_audio_device(name, handle, ¤t_audio.inputDevices, ¤t_audio.inputDeviceCount);
|
icculus@9393
|
329 |
}
|
icculus@9393
|
330 |
|
icculus@9394
|
331 |
static SDL_INLINE int
|
icculus@9394
|
332 |
add_output_device(const char *name, void *handle)
|
icculus@9393
|
333 |
{
|
icculus@9394
|
334 |
return add_audio_device(name, handle, ¤t_audio.outputDevices, ¤t_audio.outputDeviceCount);
|
icculus@9393
|
335 |
}
|
icculus@9393
|
336 |
|
icculus@9393
|
337 |
static void
|
icculus@9394
|
338 |
free_device_list(SDL_AudioDeviceItem **devices, int *devCount)
|
icculus@9393
|
339 |
{
|
icculus@9394
|
340 |
SDL_AudioDeviceItem *item, *next;
|
icculus@9394
|
341 |
for (item = *devices; item != NULL; item = next) {
|
icculus@9394
|
342 |
next = item->next;
|
icculus@9394
|
343 |
if (item->handle != NULL) {
|
icculus@9394
|
344 |
current_audio.impl.FreeDeviceHandle(item->handle);
|
icculus@9393
|
345 |
}
|
icculus@9394
|
346 |
SDL_free(item);
|
icculus@9393
|
347 |
}
|
icculus@9393
|
348 |
*devices = NULL;
|
icculus@9393
|
349 |
*devCount = 0;
|
icculus@9393
|
350 |
}
|
icculus@9393
|
351 |
|
icculus@9393
|
352 |
|
icculus@9393
|
353 |
/* The audio backends call this when a new device is plugged in. */
|
icculus@9393
|
354 |
void
|
icculus@9394
|
355 |
SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
|
icculus@9393
|
356 |
{
|
icculus@9394
|
357 |
const int device_index = iscapture ? add_capture_device(name, handle) : add_output_device(name, handle);
|
icculus@9393
|
358 |
if (device_index != -1) {
|
icculus@9393
|
359 |
/* Post the event, if desired */
|
icculus@9393
|
360 |
if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
|
icculus@9393
|
361 |
SDL_Event event;
|
icculus@9401
|
362 |
SDL_zero(event);
|
icculus@9393
|
363 |
event.adevice.type = SDL_AUDIODEVICEADDED;
|
icculus@9393
|
364 |
event.adevice.which = device_index;
|
icculus@9393
|
365 |
event.adevice.iscapture = iscapture;
|
icculus@9393
|
366 |
SDL_PushEvent(&event);
|
icculus@9393
|
367 |
}
|
icculus@9393
|
368 |
}
|
icculus@9393
|
369 |
}
|
icculus@9393
|
370 |
|
icculus@9394
|
371 |
/* The audio backends call this when a currently-opened device is lost. */
|
icculus@9394
|
372 |
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
|
icculus@9393
|
373 |
{
|
icculus@9394
|
374 |
SDL_assert(get_audio_device(device->id) == device);
|
icculus@9396
|
375 |
|
icculus@10238
|
376 |
if (!SDL_AtomicGet(&device->enabled)) {
|
icculus@9396
|
377 |
return;
|
icculus@9396
|
378 |
}
|
icculus@9394
|
379 |
|
icculus@9394
|
380 |
/* Ends the audio callback and mark the device as STOPPED, but the
|
icculus@9394
|
381 |
app still needs to close the device to free resources. */
|
icculus@9394
|
382 |
current_audio.impl.LockDevice(device);
|
icculus@10238
|
383 |
SDL_AtomicSet(&device->enabled, 0);
|
icculus@9394
|
384 |
current_audio.impl.UnlockDevice(device);
|
icculus@9393
|
385 |
|
icculus@9394
|
386 |
/* Post the event, if desired */
|
icculus@9394
|
387 |
if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
|
icculus@9394
|
388 |
SDL_Event event;
|
icculus@9401
|
389 |
SDL_zero(event);
|
icculus@9394
|
390 |
event.adevice.type = SDL_AUDIODEVICEREMOVED;
|
icculus@9394
|
391 |
event.adevice.which = device->id;
|
icculus@9394
|
392 |
event.adevice.iscapture = device->iscapture ? 1 : 0;
|
icculus@9394
|
393 |
SDL_PushEvent(&event);
|
icculus@9394
|
394 |
}
|
icculus@9394
|
395 |
}
|
icculus@9393
|
396 |
|
icculus@9394
|
397 |
static void
|
icculus@9394
|
398 |
mark_device_removed(void *handle, SDL_AudioDeviceItem *devices, SDL_bool *removedFlag)
|
icculus@9394
|
399 |
{
|
icculus@9394
|
400 |
SDL_AudioDeviceItem *item;
|
icculus@9394
|
401 |
SDL_assert(handle != NULL);
|
icculus@9394
|
402 |
for (item = devices; item != NULL; item = item->next) {
|
icculus@9394
|
403 |
if (item->handle == handle) {
|
icculus@9394
|
404 |
item->handle = NULL;
|
icculus@9394
|
405 |
*removedFlag = SDL_TRUE;
|
icculus@9394
|
406 |
return;
|
icculus@9393
|
407 |
}
|
icculus@9393
|
408 |
}
|
icculus@9394
|
409 |
}
|
icculus@9393
|
410 |
|
icculus@9394
|
411 |
/* The audio backends call this when a device is removed from the system. */
|
icculus@9394
|
412 |
void
|
icculus@9399
|
413 |
SDL_RemoveAudioDevice(const int iscapture, void *handle)
|
icculus@9394
|
414 |
{
|
icculus@9394
|
415 |
SDL_LockMutex(current_audio.detectionLock);
|
icculus@9399
|
416 |
if (iscapture) {
|
icculus@9399
|
417 |
mark_device_removed(handle, current_audio.inputDevices, ¤t_audio.captureDevicesRemoved);
|
icculus@9399
|
418 |
} else {
|
icculus@9399
|
419 |
mark_device_removed(handle, current_audio.outputDevices, ¤t_audio.outputDevicesRemoved);
|
icculus@9399
|
420 |
}
|
icculus@9394
|
421 |
SDL_UnlockMutex(current_audio.detectionLock);
|
icculus@9394
|
422 |
current_audio.impl.FreeDeviceHandle(handle);
|
icculus@9393
|
423 |
}
|
icculus@9393
|
424 |
|
icculus@9393
|
425 |
|
icculus@9012
|
426 |
|
icculus@9012
|
427 |
/* buffer queueing support... */
|
icculus@9012
|
428 |
|
icculus@9012
|
429 |
/* this expects that you managed thread safety elsewhere. */
|
icculus@9012
|
430 |
static void
|
icculus@10262
|
431 |
free_audio_queue(SDL_AudioBufferQueue *packet)
|
icculus@9012
|
432 |
{
|
icculus@10262
|
433 |
while (packet) {
|
icculus@10262
|
434 |
SDL_AudioBufferQueue *next = packet->next;
|
icculus@10262
|
435 |
SDL_free(packet);
|
icculus@10262
|
436 |
packet = next;
|
icculus@9012
|
437 |
}
|
icculus@9012
|
438 |
}
|
icculus@9012
|
439 |
|
icculus@10262
|
440 |
/* NOTE: This assumes you'll hold the mixer lock before calling! */
|
icculus@10262
|
441 |
static int
|
icculus@10262
|
442 |
queue_audio_to_device(SDL_AudioDevice *device, const Uint8 *data, Uint32 len)
|
icculus@9012
|
443 |
{
|
icculus@9012
|
444 |
SDL_AudioBufferQueue *orighead;
|
icculus@9012
|
445 |
SDL_AudioBufferQueue *origtail;
|
icculus@9012
|
446 |
Uint32 origlen;
|
icculus@9012
|
447 |
Uint32 datalen;
|
icculus@9012
|
448 |
|
icculus@9012
|
449 |
orighead = device->buffer_queue_head;
|
icculus@9012
|
450 |
origtail = device->buffer_queue_tail;
|
icculus@9012
|
451 |
origlen = origtail ? origtail->datalen : 0;
|
icculus@9012
|
452 |
|
icculus@9012
|
453 |
while (len > 0) {
|
icculus@9012
|
454 |
SDL_AudioBufferQueue *packet = device->buffer_queue_tail;
|
icculus@9012
|
455 |
SDL_assert(!packet || (packet->datalen <= SDL_AUDIOBUFFERQUEUE_PACKETLEN));
|
icculus@9012
|
456 |
if (!packet || (packet->datalen >= SDL_AUDIOBUFFERQUEUE_PACKETLEN)) {
|
icculus@9012
|
457 |
/* tail packet missing or completely full; we need a new packet. */
|
icculus@9012
|
458 |
packet = device->buffer_queue_pool;
|
icculus@9012
|
459 |
if (packet != NULL) {
|
icculus@9012
|
460 |
/* we have one available in the pool. */
|
icculus@9012
|
461 |
device->buffer_queue_pool = packet->next;
|
icculus@9012
|
462 |
} else {
|
icculus@9012
|
463 |
/* Have to allocate a new one! */
|
icculus@9012
|
464 |
packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
|
icculus@9012
|
465 |
if (packet == NULL) {
|
icculus@9012
|
466 |
/* uhoh, reset so we've queued nothing new, free what we can. */
|
icculus@9012
|
467 |
if (!origtail) {
|
icculus@9012
|
468 |
packet = device->buffer_queue_head; /* whole queue. */
|
icculus@9012
|
469 |
} else {
|
icculus@9012
|
470 |
packet = origtail->next; /* what we added to existing queue. */
|
icculus@9012
|
471 |
origtail->next = NULL;
|
icculus@9012
|
472 |
origtail->datalen = origlen;
|
icculus@9012
|
473 |
}
|
icculus@9012
|
474 |
device->buffer_queue_head = orighead;
|
icculus@9012
|
475 |
device->buffer_queue_tail = origtail;
|
icculus@9012
|
476 |
device->buffer_queue_pool = NULL;
|
icculus@9012
|
477 |
|
icculus@9012
|
478 |
free_audio_queue(packet); /* give back what we can. */
|
icculus@9012
|
479 |
|
icculus@9012
|
480 |
return SDL_OutOfMemory();
|
icculus@9012
|
481 |
}
|
icculus@9012
|
482 |
}
|
icculus@9012
|
483 |
packet->datalen = 0;
|
icculus@9012
|
484 |
packet->startpos = 0;
|
icculus@9012
|
485 |
packet->next = NULL;
|
icculus@9012
|
486 |
|
icculus@9012
|
487 |
SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
|
icculus@9012
|
488 |
if (device->buffer_queue_tail == NULL) {
|
icculus@9012
|
489 |
device->buffer_queue_head = packet;
|
icculus@9012
|
490 |
} else {
|
icculus@9012
|
491 |
device->buffer_queue_tail->next = packet;
|
icculus@9012
|
492 |
}
|
icculus@9012
|
493 |
device->buffer_queue_tail = packet;
|
icculus@9012
|
494 |
}
|
icculus@9012
|
495 |
|
icculus@9012
|
496 |
datalen = SDL_min(len, SDL_AUDIOBUFFERQUEUE_PACKETLEN - packet->datalen);
|
icculus@9012
|
497 |
SDL_memcpy(packet->data + packet->datalen, data, datalen);
|
icculus@9012
|
498 |
data += datalen;
|
icculus@9012
|
499 |
len -= datalen;
|
icculus@9012
|
500 |
packet->datalen += datalen;
|
icculus@9012
|
501 |
device->queued_bytes += datalen;
|
icculus@9012
|
502 |
}
|
icculus@9012
|
503 |
|
icculus@10262
|
504 |
return 0;
|
icculus@10262
|
505 |
}
|
icculus@10262
|
506 |
|
icculus@10262
|
507 |
/* NOTE: This assumes you'll hold the mixer lock before calling! */
|
icculus@10262
|
508 |
static Uint32
|
icculus@10262
|
509 |
dequeue_audio_from_device(SDL_AudioDevice *device, Uint8 *stream, Uint32 len)
|
icculus@10262
|
510 |
{
|
icculus@10262
|
511 |
SDL_AudioBufferQueue *packet;
|
icculus@10262
|
512 |
Uint8 *ptr = stream;
|
icculus@10262
|
513 |
|
icculus@10262
|
514 |
while ((len > 0) && ((packet = device->buffer_queue_head) != NULL)) {
|
icculus@10262
|
515 |
const Uint32 avail = packet->datalen - packet->startpos;
|
icculus@10262
|
516 |
const Uint32 cpy = SDL_min(len, avail);
|
icculus@10262
|
517 |
SDL_assert(device->queued_bytes >= avail);
|
icculus@10262
|
518 |
|
icculus@10262
|
519 |
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
icculus@10262
|
520 |
packet->startpos += cpy;
|
icculus@10262
|
521 |
ptr += cpy;
|
icculus@10262
|
522 |
device->queued_bytes -= cpy;
|
icculus@10262
|
523 |
len -= cpy;
|
icculus@10262
|
524 |
|
icculus@10262
|
525 |
if (packet->startpos == packet->datalen) { /* packet is done, put it in the pool. */
|
icculus@10262
|
526 |
device->buffer_queue_head = packet->next;
|
icculus@10262
|
527 |
SDL_assert((packet->next != NULL) || (packet == device->buffer_queue_tail));
|
icculus@10262
|
528 |
packet->next = device->buffer_queue_pool;
|
icculus@10262
|
529 |
device->buffer_queue_pool = packet;
|
icculus@10262
|
530 |
}
|
icculus@10262
|
531 |
}
|
icculus@10262
|
532 |
|
icculus@10262
|
533 |
SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
|
icculus@10262
|
534 |
|
icculus@10262
|
535 |
if (device->buffer_queue_head == NULL) {
|
icculus@10262
|
536 |
device->buffer_queue_tail = NULL; /* in case we drained the queue entirely. */
|
icculus@10262
|
537 |
}
|
icculus@10262
|
538 |
|
icculus@10262
|
539 |
return (Uint32) (ptr - stream);
|
icculus@10262
|
540 |
}
|
icculus@10262
|
541 |
|
icculus@10262
|
542 |
static void SDLCALL
|
icculus@10262
|
543 |
SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int len)
|
icculus@10262
|
544 |
{
|
icculus@10262
|
545 |
/* this function always holds the mixer lock before being called. */
|
icculus@10262
|
546 |
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
|
icculus@10262
|
547 |
Uint32 written;
|
icculus@10262
|
548 |
|
icculus@10262
|
549 |
SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */
|
icculus@10262
|
550 |
SDL_assert(!device->iscapture); /* this shouldn't ever happen, right?! */
|
icculus@10262
|
551 |
SDL_assert(len >= 0); /* this shouldn't ever happen, right?! */
|
icculus@10262
|
552 |
|
icculus@10262
|
553 |
written = dequeue_audio_from_device(device, stream, (Uint32) len);
|
icculus@10262
|
554 |
stream += written;
|
icculus@10262
|
555 |
len -= (int) written;
|
icculus@9012
|
556 |
|
icculus@10262
|
557 |
if (len > 0) { /* fill any remaining space in the stream with silence. */
|
icculus@10262
|
558 |
SDL_assert(device->buffer_queue_head == NULL);
|
icculus@10262
|
559 |
SDL_memset(stream, device->spec.silence, len);
|
icculus@10262
|
560 |
}
|
icculus@10262
|
561 |
}
|
icculus@10262
|
562 |
|
icculus@10262
|
563 |
static void SDLCALL
|
icculus@10262
|
564 |
SDL_BufferQueueFillCallback(void *userdata, Uint8 *stream, int len)
|
icculus@10262
|
565 |
{
|
icculus@10262
|
566 |
/* this function always holds the mixer lock before being called. */
|
icculus@10262
|
567 |
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
|
icculus@10262
|
568 |
|
icculus@10262
|
569 |
SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */
|
icculus@10262
|
570 |
SDL_assert(device->iscapture); /* this shouldn't ever happen, right?! */
|
icculus@10262
|
571 |
SDL_assert(len >= 0); /* this shouldn't ever happen, right?! */
|
icculus@10262
|
572 |
|
icculus@10262
|
573 |
/* note that if this needs to allocate more space and run out of memory,
|
icculus@10262
|
574 |
we have no choice but to quietly drop the data and hope it works out
|
icculus@10262
|
575 |
later, but you probably have bigger problems in this case anyhow. */
|
icculus@10262
|
576 |
queue_audio_to_device(device, stream, (Uint32) len);
|
icculus@10262
|
577 |
}
|
icculus@10262
|
578 |
|
icculus@10262
|
579 |
int
|
icculus@10262
|
580 |
SDL_QueueAudio(SDL_AudioDeviceID devid, const void *data, Uint32 len)
|
icculus@10262
|
581 |
{
|
icculus@10262
|
582 |
SDL_AudioDevice *device = get_audio_device(devid);
|
icculus@10262
|
583 |
int rc = 0;
|
icculus@10262
|
584 |
|
icculus@10262
|
585 |
if (!device) {
|
icculus@10262
|
586 |
return -1; /* get_audio_device() will have set the error state */
|
icculus@10262
|
587 |
} else if (device->iscapture) {
|
icculus@10262
|
588 |
return SDL_SetError("This is a capture device, queueing not allowed");
|
icculus@10262
|
589 |
} else if (device->spec.callback != SDL_BufferQueueDrainCallback) {
|
icculus@10262
|
590 |
return SDL_SetError("Audio device has a callback, queueing not allowed");
|
icculus@10262
|
591 |
}
|
icculus@10262
|
592 |
|
icculus@10262
|
593 |
if (len > 0) {
|
icculus@10262
|
594 |
current_audio.impl.LockDevice(device);
|
icculus@10262
|
595 |
rc = queue_audio_to_device(device, data, len);
|
icculus@10262
|
596 |
current_audio.impl.UnlockDevice(device);
|
icculus@10262
|
597 |
}
|
icculus@10262
|
598 |
|
icculus@10262
|
599 |
return rc;
|
icculus@10262
|
600 |
}
|
icculus@10262
|
601 |
|
icculus@10262
|
602 |
Uint32
|
icculus@10262
|
603 |
SDL_DequeueAudio(SDL_AudioDeviceID devid, void *data, Uint32 len)
|
icculus@10262
|
604 |
{
|
icculus@10262
|
605 |
SDL_AudioDevice *device = get_audio_device(devid);
|
icculus@10262
|
606 |
Uint32 rc;
|
icculus@10262
|
607 |
|
icculus@10262
|
608 |
if ( (len == 0) || /* nothing to do? */
|
icculus@10262
|
609 |
(!device) || /* called with bogus device id */
|
icculus@10262
|
610 |
(!device->iscapture) || /* playback devices can't dequeue */
|
icculus@10262
|
611 |
(device->spec.callback != SDL_BufferQueueFillCallback) ) { /* not set for queueing */
|
icculus@10262
|
612 |
return 0; /* just report zero bytes dequeued. */
|
icculus@10262
|
613 |
}
|
icculus@10262
|
614 |
|
icculus@10262
|
615 |
current_audio.impl.LockDevice(device);
|
icculus@10262
|
616 |
rc = dequeue_audio_from_device(device, data, len);
|
icculus@10262
|
617 |
current_audio.impl.UnlockDevice(device);
|
icculus@10262
|
618 |
return rc;
|
icculus@9012
|
619 |
}
|
icculus@9012
|
620 |
|
icculus@9012
|
621 |
Uint32
|
icculus@9012
|
622 |
SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid)
|
icculus@9012
|
623 |
{
|
icculus@9012
|
624 |
Uint32 retval = 0;
|
icculus@9012
|
625 |
SDL_AudioDevice *device = get_audio_device(devid);
|
icculus@9032
|
626 |
|
icculus@10262
|
627 |
if (!device) {
|
icculus@10262
|
628 |
return 0;
|
icculus@10262
|
629 |
}
|
icculus@10262
|
630 |
|
icculus@9032
|
631 |
/* Nothing to do unless we're set up for queueing. */
|
icculus@10262
|
632 |
if (device->spec.callback == SDL_BufferQueueDrainCallback) {
|
icculus@9012
|
633 |
current_audio.impl.LockDevice(device);
|
icculus@9031
|
634 |
retval = device->queued_bytes + current_audio.impl.GetPendingBytes(device);
|
icculus@9012
|
635 |
current_audio.impl.UnlockDevice(device);
|
icculus@10262
|
636 |
} else if (device->spec.callback == SDL_BufferQueueFillCallback) {
|
icculus@10262
|
637 |
current_audio.impl.LockDevice(device);
|
icculus@10262
|
638 |
retval = device->queued_bytes;
|
icculus@10262
|
639 |
current_audio.impl.UnlockDevice(device);
|
icculus@9012
|
640 |
}
|
icculus@9012
|
641 |
|
icculus@9012
|
642 |
return retval;
|
icculus@9012
|
643 |
}
|
icculus@9012
|
644 |
|
icculus@9012
|
645 |
void
|
icculus@9012
|
646 |
SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
|
icculus@9012
|
647 |
{
|
icculus@9012
|
648 |
SDL_AudioDevice *device = get_audio_device(devid);
|
icculus@10260
|
649 |
SDL_AudioBufferQueue *packet;
|
icculus@10260
|
650 |
|
icculus@9012
|
651 |
if (!device) {
|
icculus@9012
|
652 |
return; /* nothing to do. */
|
icculus@9012
|
653 |
}
|
icculus@9012
|
654 |
|
icculus@9012
|
655 |
/* Blank out the device and release the mutex. Free it afterwards. */
|
icculus@9012
|
656 |
current_audio.impl.LockDevice(device);
|
icculus@10260
|
657 |
|
icculus@10260
|
658 |
/* merge the available pool and the current queue into one list. */
|
icculus@10260
|
659 |
packet = device->buffer_queue_head;
|
icculus@10260
|
660 |
if (packet) {
|
icculus@10260
|
661 |
device->buffer_queue_tail->next = device->buffer_queue_pool;
|
icculus@10260
|
662 |
} else {
|
icculus@10260
|
663 |
packet = device->buffer_queue_pool;
|
icculus@10260
|
664 |
}
|
icculus@10260
|
665 |
|
icculus@10260
|
666 |
/* Remove the queued packets from the device. */
|
icculus@9012
|
667 |
device->buffer_queue_tail = NULL;
|
icculus@9012
|
668 |
device->buffer_queue_head = NULL;
|
icculus@9012
|
669 |
device->queued_bytes = 0;
|
icculus@10260
|
670 |
device->buffer_queue_pool = packet;
|
icculus@10260
|
671 |
|
icculus@10260
|
672 |
/* Keep up to two packets in the pool to reduce future malloc pressure. */
|
icculus@10260
|
673 |
if (packet) {
|
icculus@10260
|
674 |
if (!packet->next) {
|
icculus@10260
|
675 |
packet = NULL; /* one packet (the only one) for the pool. */
|
icculus@10260
|
676 |
} else {
|
icculus@10260
|
677 |
SDL_AudioBufferQueue *next = packet->next->next;
|
icculus@10260
|
678 |
packet->next->next = NULL; /* two packets for the pool. */
|
icculus@10260
|
679 |
packet = next; /* rest will be freed. */
|
icculus@10260
|
680 |
}
|
icculus@10260
|
681 |
}
|
icculus@10260
|
682 |
|
icculus@9012
|
683 |
current_audio.impl.UnlockDevice(device);
|
icculus@9012
|
684 |
|
icculus@10260
|
685 |
/* free any extra packets we didn't keep in the pool. */
|
icculus@10260
|
686 |
free_audio_queue(packet);
|
icculus@9012
|
687 |
}
|
icculus@9012
|
688 |
|
icculus@9012
|
689 |
|
slouken@0
|
690 |
/* The general mixing thread function */
|
icculus@10239
|
691 |
static int SDLCALL
|
icculus@2049
|
692 |
SDL_RunAudio(void *devicep)
|
slouken@0
|
693 |
{
|
icculus@2049
|
694 |
SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
|
icculus@9398
|
695 |
const int silence = (int) device->spec.silence;
|
icculus@9398
|
696 |
const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
|
icculus@9398
|
697 |
const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
|
slouken@1895
|
698 |
Uint8 *stream;
|
icculus@9398
|
699 |
void *udata = device->spec.userdata;
|
icculus@10239
|
700 |
void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback;
|
icculus@10239
|
701 |
|
icculus@10239
|
702 |
SDL_assert(!device->iscapture);
|
slouken@2716
|
703 |
|
slouken@5509
|
704 |
/* The audio mixing is always a high priority thread */
|
slouken@5509
|
705 |
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
slouken@5509
|
706 |
|
slouken@1895
|
707 |
/* Perform any thread setup */
|
icculus@2049
|
708 |
device->threadid = SDL_ThreadID();
|
icculus@2049
|
709 |
current_audio.impl.ThreadInit(device);
|
slouken@0
|
710 |
|
icculus@9398
|
711 |
/* Loop, filling the audio buffers */
|
icculus@10233
|
712 |
while (!SDL_AtomicGet(&device->shutdown)) {
|
icculus@9398
|
713 |
/* Fill the current buffer with sound */
|
icculus@9398
|
714 |
if (device->convert.needed) {
|
icculus@9398
|
715 |
stream = device->convert.buf;
|
icculus@10238
|
716 |
} else if (SDL_AtomicGet(&device->enabled)) {
|
icculus@9398
|
717 |
stream = current_audio.impl.GetDeviceBuf(device);
|
icculus@9398
|
718 |
} else {
|
icculus@9398
|
719 |
/* if the device isn't enabled, we still write to the
|
icculus@9398
|
720 |
fake_stream, so the app's callback will fire with
|
icculus@9398
|
721 |
a regular frequency, in case they depend on that
|
icculus@9398
|
722 |
for timing or progress. They can use hotplug
|
icculus@9398
|
723 |
now to know if the device failed. */
|
icculus@9398
|
724 |
stream = NULL;
|
slouken@2716
|
725 |
}
|
slouken@3336
|
726 |
|
icculus@9398
|
727 |
if (stream == NULL) {
|
icculus@9398
|
728 |
stream = device->fake_stream;
|
icculus@9398
|
729 |
}
|
slouken@2716
|
730 |
|
icculus@9398
|
731 |
/* !!! FIXME: this should be LockDevice. */
|
icculus@9398
|
732 |
SDL_LockMutex(device->mixer_lock);
|
icculus@10238
|
733 |
if (SDL_AtomicGet(&device->paused)) {
|
icculus@9398
|
734 |
SDL_memset(stream, silence, stream_len);
|
icculus@9398
|
735 |
} else {
|
icculus@10239
|
736 |
(*callback) (udata, stream, stream_len);
|
icculus@9398
|
737 |
}
|
icculus@9398
|
738 |
SDL_UnlockMutex(device->mixer_lock);
|
slouken@2716
|
739 |
|
icculus@9398
|
740 |
/* Convert the audio if necessary */
|
icculus@10238
|
741 |
if (device->convert.needed && SDL_AtomicGet(&device->enabled)) {
|
icculus@9398
|
742 |
SDL_ConvertAudio(&device->convert);
|
icculus@9398
|
743 |
stream = current_audio.impl.GetDeviceBuf(device);
|
icculus@9397
|
744 |
if (stream == NULL) {
|
icculus@9397
|
745 |
stream = device->fake_stream;
|
icculus@9398
|
746 |
} else {
|
icculus@9398
|
747 |
SDL_memcpy(stream, device->convert.buf,
|
icculus@9398
|
748 |
device->convert.len_cvt);
|
slouken@1895
|
749 |
}
|
icculus@9398
|
750 |
}
|
slouken@1562
|
751 |
|
icculus@9398
|
752 |
/* Ready current buffer for play and change current buffer */
|
icculus@9398
|
753 |
if (stream == device->fake_stream) {
|
icculus@9398
|
754 |
SDL_Delay(delay);
|
icculus@9398
|
755 |
} else {
|
icculus@9398
|
756 |
current_audio.impl.PlayDevice(device);
|
icculus@9398
|
757 |
current_audio.impl.WaitDevice(device);
|
slouken@1895
|
758 |
}
|
slouken@1895
|
759 |
}
|
slouken@1562
|
760 |
|
icculus@9398
|
761 |
/* Wait for the audio to drain. */
|
icculus@10239
|
762 |
/* !!! FIXME: can we rename this WaitDrain? */
|
icculus@2049
|
763 |
current_audio.impl.WaitDone(device);
|
slouken@21
|
764 |
|
icculus@9382
|
765 |
return 0;
|
slouken@0
|
766 |
}
|
slouken@0
|
767 |
|
icculus@10239
|
768 |
/* The general capture thread function */
|
icculus@10239
|
769 |
static int SDLCALL
|
icculus@10239
|
770 |
SDL_CaptureAudio(void *devicep)
|
icculus@10239
|
771 |
{
|
icculus@10239
|
772 |
SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
|
icculus@10239
|
773 |
const int silence = (int) device->spec.silence;
|
icculus@10239
|
774 |
const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
|
icculus@10239
|
775 |
const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
|
icculus@10239
|
776 |
Uint8 *stream;
|
icculus@10239
|
777 |
void *udata = device->spec.userdata;
|
icculus@10239
|
778 |
void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback;
|
icculus@10239
|
779 |
|
icculus@10239
|
780 |
SDL_assert(device->iscapture);
|
icculus@10239
|
781 |
|
icculus@10239
|
782 |
/* The audio mixing is always a high priority thread */
|
icculus@10239
|
783 |
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
icculus@10239
|
784 |
|
icculus@10239
|
785 |
/* Perform any thread setup */
|
icculus@10239
|
786 |
device->threadid = SDL_ThreadID();
|
icculus@10239
|
787 |
current_audio.impl.ThreadInit(device);
|
icculus@10239
|
788 |
|
icculus@10239
|
789 |
/* Loop, filling the audio buffers */
|
icculus@10239
|
790 |
while (!SDL_AtomicGet(&device->shutdown)) {
|
icculus@10239
|
791 |
int still_need;
|
icculus@10239
|
792 |
Uint8 *ptr;
|
icculus@10239
|
793 |
|
icculus@10239
|
794 |
if (!SDL_AtomicGet(&device->enabled) || SDL_AtomicGet(&device->paused)) {
|
icculus@10239
|
795 |
SDL_Delay(delay); /* just so we don't cook the CPU. */
|
icculus@10239
|
796 |
current_audio.impl.FlushCapture(device); /* dump anything pending. */
|
icculus@10239
|
797 |
continue;
|
icculus@10239
|
798 |
}
|
icculus@10239
|
799 |
|
icculus@10239
|
800 |
/* Fill the current buffer with sound */
|
icculus@10239
|
801 |
still_need = stream_len;
|
icculus@10239
|
802 |
if (device->convert.needed) {
|
icculus@10239
|
803 |
ptr = stream = device->convert.buf;
|
icculus@10239
|
804 |
} else {
|
icculus@10239
|
805 |
/* just use the "fake" stream to hold data read from the device. */
|
icculus@10239
|
806 |
ptr = stream = device->fake_stream;
|
icculus@10239
|
807 |
}
|
icculus@10239
|
808 |
|
icculus@10239
|
809 |
/* We still read from the device when "paused" to keep the state sane,
|
icculus@10239
|
810 |
and block when there isn't data so this thread isn't eating CPU.
|
icculus@10239
|
811 |
But we don't process it further or call the app's callback. */
|
icculus@10239
|
812 |
|
icculus@10239
|
813 |
while (still_need > 0) {
|
icculus@10239
|
814 |
const int rc = current_audio.impl.CaptureFromDevice(device, ptr, still_need);
|
icculus@10239
|
815 |
SDL_assert(rc <= still_need); /* device should not overflow buffer. :) */
|
icculus@10239
|
816 |
if (rc > 0) {
|
icculus@10239
|
817 |
still_need -= rc;
|
icculus@10239
|
818 |
ptr += rc;
|
icculus@10239
|
819 |
} else { /* uhoh, device failed for some reason! */
|
icculus@10239
|
820 |
SDL_OpenedAudioDeviceDisconnected(device);
|
icculus@10239
|
821 |
break;
|
icculus@10239
|
822 |
}
|
icculus@10239
|
823 |
}
|
icculus@10239
|
824 |
|
icculus@10239
|
825 |
if (still_need > 0) {
|
icculus@10239
|
826 |
/* Keep any data we already read, silence the rest. */
|
icculus@10239
|
827 |
SDL_memset(ptr, silence, still_need);
|
icculus@10239
|
828 |
}
|
icculus@10239
|
829 |
|
icculus@10239
|
830 |
if (device->convert.needed) {
|
icculus@10239
|
831 |
SDL_ConvertAudio(&device->convert);
|
icculus@10239
|
832 |
}
|
icculus@10239
|
833 |
|
icculus@10239
|
834 |
/* !!! FIXME: this should be LockDevice. */
|
icculus@10239
|
835 |
SDL_LockMutex(device->mixer_lock);
|
icculus@10241
|
836 |
if (SDL_AtomicGet(&device->paused)) {
|
icculus@10241
|
837 |
current_audio.impl.FlushCapture(device); /* one snuck in! */
|
icculus@10241
|
838 |
} else {
|
icculus@10239
|
839 |
(*callback)(udata, stream, stream_len);
|
icculus@10239
|
840 |
}
|
icculus@10239
|
841 |
SDL_UnlockMutex(device->mixer_lock);
|
icculus@10239
|
842 |
}
|
icculus@10239
|
843 |
|
icculus@10239
|
844 |
current_audio.impl.FlushCapture(device);
|
icculus@10239
|
845 |
|
icculus@10239
|
846 |
return 0;
|
icculus@10239
|
847 |
}
|
icculus@10239
|
848 |
|
slouken@322
|
849 |
|
icculus@1982
|
850 |
static SDL_AudioFormat
|
slouken@1895
|
851 |
SDL_ParseAudioFormat(const char *string)
|
slouken@1794
|
852 |
{
|
icculus@2076
|
853 |
#define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) return AUDIO_##x
|
icculus@2049
|
854 |
CHECK_FMT_STRING(U8);
|
icculus@2049
|
855 |
CHECK_FMT_STRING(S8);
|
icculus@2049
|
856 |
CHECK_FMT_STRING(U16LSB);
|
icculus@2049
|
857 |
CHECK_FMT_STRING(S16LSB);
|
icculus@2049
|
858 |
CHECK_FMT_STRING(U16MSB);
|
icculus@2049
|
859 |
CHECK_FMT_STRING(S16MSB);
|
icculus@2049
|
860 |
CHECK_FMT_STRING(U16SYS);
|
icculus@2049
|
861 |
CHECK_FMT_STRING(S16SYS);
|
icculus@2049
|
862 |
CHECK_FMT_STRING(U16);
|
icculus@2049
|
863 |
CHECK_FMT_STRING(S16);
|
icculus@2049
|
864 |
CHECK_FMT_STRING(S32LSB);
|
icculus@2049
|
865 |
CHECK_FMT_STRING(S32MSB);
|
icculus@2049
|
866 |
CHECK_FMT_STRING(S32SYS);
|
icculus@2049
|
867 |
CHECK_FMT_STRING(S32);
|
icculus@2049
|
868 |
CHECK_FMT_STRING(F32LSB);
|
icculus@2049
|
869 |
CHECK_FMT_STRING(F32MSB);
|
icculus@2049
|
870 |
CHECK_FMT_STRING(F32SYS);
|
icculus@2049
|
871 |
CHECK_FMT_STRING(F32);
|
slouken@2060
|
872 |
#undef CHECK_FMT_STRING
|
icculus@2049
|
873 |
return 0;
|
slouken@1895
|
874 |
}
|
slouken@1895
|
875 |
|
slouken@1895
|
876 |
int
|
slouken@1895
|
877 |
SDL_GetNumAudioDrivers(void)
|
slouken@1895
|
878 |
{
|
icculus@9382
|
879 |
return SDL_arraysize(bootstrap) - 1;
|
slouken@1895
|
880 |
}
|
slouken@1895
|
881 |
|
slouken@1895
|
882 |
const char *
|
slouken@1895
|
883 |
SDL_GetAudioDriver(int index)
|
slouken@1895
|
884 |
{
|
slouken@1895
|
885 |
if (index >= 0 && index < SDL_GetNumAudioDrivers()) {
|
icculus@9382
|
886 |
return bootstrap[index]->name;
|
slouken@1895
|
887 |
}
|
icculus@9382
|
888 |
return NULL;
|
slouken@1794
|
889 |
}
|
slouken@1794
|
890 |
|
slouken@1895
|
891 |
int
|
slouken@1895
|
892 |
SDL_AudioInit(const char *driver_name)
|
slouken@0
|
893 |
{
|
icculus@2049
|
894 |
int i = 0;
|
icculus@2049
|
895 |
int initialized = 0;
|
icculus@2049
|
896 |
int tried_to_init = 0;
|
slouken@0
|
897 |
|
icculus@2049
|
898 |
if (SDL_WasInit(SDL_INIT_AUDIO)) {
|
slouken@2060
|
899 |
SDL_AudioQuit(); /* shutdown driver if already running. */
|
slouken@1895
|
900 |
}
|
slouken@0
|
901 |
|
icculus@9392
|
902 |
SDL_zero(current_audio);
|
icculus@9392
|
903 |
SDL_zero(open_devices);
|
icculus@2049
|
904 |
|
slouken@1895
|
905 |
/* Select the proper audio driver */
|
slouken@1909
|
906 |
if (driver_name == NULL) {
|
slouken@1909
|
907 |
driver_name = SDL_getenv("SDL_AUDIODRIVER");
|
slouken@1909
|
908 |
}
|
icculus@2049
|
909 |
|
icculus@2049
|
910 |
for (i = 0; (!initialized) && (bootstrap[i]); ++i) {
|
icculus@2049
|
911 |
/* make sure we should even try this driver before doing so... */
|
icculus@2049
|
912 |
const AudioBootStrap *backend = bootstrap[i];
|
slouken@6900
|
913 |
if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) ||
|
slouken@6900
|
914 |
(!driver_name && backend->demand_only)) {
|
icculus@2049
|
915 |
continue;
|
icculus@2049
|
916 |
}
|
slouken@0
|
917 |
|
icculus@2049
|
918 |
tried_to_init = 1;
|
icculus@9392
|
919 |
SDL_zero(current_audio);
|
icculus@2049
|
920 |
current_audio.name = backend->name;
|
icculus@2049
|
921 |
current_audio.desc = backend->desc;
|
icculus@3699
|
922 |
initialized = backend->init(¤t_audio.impl);
|
slouken@1895
|
923 |
}
|
icculus@2049
|
924 |
|
icculus@2049
|
925 |
if (!initialized) {
|
icculus@2049
|
926 |
/* specific drivers will set the error message if they fail... */
|
icculus@2049
|
927 |
if (!tried_to_init) {
|
slouken@1895
|
928 |
if (driver_name) {
|
icculus@3699
|
929 |
SDL_SetError("Audio target '%s' not available", driver_name);
|
slouken@1895
|
930 |
} else {
|
slouken@1895
|
931 |
SDL_SetError("No available audio device");
|
slouken@1895
|
932 |
}
|
slouken@1895
|
933 |
}
|
icculus@2049
|
934 |
|
icculus@9392
|
935 |
SDL_zero(current_audio);
|
icculus@9382
|
936 |
return -1; /* No driver was available, so fail. */
|
slouken@1895
|
937 |
}
|
icculus@2049
|
938 |
|
icculus@9394
|
939 |
current_audio.detectionLock = SDL_CreateMutex();
|
icculus@9393
|
940 |
|
icculus@2049
|
941 |
finalize_audio_entry_points();
|
icculus@2049
|
942 |
|
icculus@9393
|
943 |
/* Make sure we have a list of devices available at startup. */
|
icculus@9394
|
944 |
current_audio.impl.DetectDevices();
|
icculus@9393
|
945 |
|
icculus@9382
|
946 |
return 0;
|
slouken@0
|
947 |
}
|
slouken@0
|
948 |
|
slouken@1895
|
949 |
/*
|
slouken@1895
|
950 |
* Get the current audio driver name
|
slouken@1895
|
951 |
*/
|
slouken@1895
|
952 |
const char *
|
slouken@1895
|
953 |
SDL_GetCurrentAudioDriver()
|
slouken@0
|
954 |
{
|
icculus@2049
|
955 |
return current_audio.name;
|
slouken@0
|
956 |
}
|
slouken@0
|
957 |
|
icculus@9394
|
958 |
/* Clean out devices that we've removed but had to keep around for stability. */
|
icculus@9394
|
959 |
static void
|
icculus@9394
|
960 |
clean_out_device_list(SDL_AudioDeviceItem **devices, int *devCount, SDL_bool *removedFlag)
|
icculus@9394
|
961 |
{
|
icculus@9394
|
962 |
SDL_AudioDeviceItem *item = *devices;
|
icculus@9394
|
963 |
SDL_AudioDeviceItem *prev = NULL;
|
icculus@9394
|
964 |
int total = 0;
|
icculus@9394
|
965 |
|
icculus@9394
|
966 |
while (item) {
|
icculus@9394
|
967 |
SDL_AudioDeviceItem *next = item->next;
|
icculus@9394
|
968 |
if (item->handle != NULL) {
|
icculus@9394
|
969 |
total++;
|
icculus@9394
|
970 |
prev = item;
|
icculus@9394
|
971 |
} else {
|
icculus@9394
|
972 |
if (prev) {
|
icculus@9394
|
973 |
prev->next = next;
|
icculus@9394
|
974 |
} else {
|
icculus@9394
|
975 |
*devices = next;
|
icculus@9394
|
976 |
}
|
icculus@9394
|
977 |
SDL_free(item);
|
icculus@9394
|
978 |
}
|
icculus@9394
|
979 |
item = next;
|
icculus@9394
|
980 |
}
|
icculus@9394
|
981 |
|
icculus@9394
|
982 |
*devCount = total;
|
icculus@9394
|
983 |
*removedFlag = SDL_FALSE;
|
icculus@9394
|
984 |
}
|
icculus@9394
|
985 |
|
icculus@9394
|
986 |
|
slouken@1895
|
987 |
int
|
icculus@2049
|
988 |
SDL_GetNumAudioDevices(int iscapture)
|
slouken@0
|
989 |
{
|
icculus@5593
|
990 |
int retval = 0;
|
icculus@5593
|
991 |
|
icculus@2049
|
992 |
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
icculus@2049
|
993 |
return -1;
|
icculus@2049
|
994 |
}
|
icculus@5593
|
995 |
|
icculus@9394
|
996 |
SDL_LockMutex(current_audio.detectionLock);
|
icculus@9394
|
997 |
if (iscapture && current_audio.captureDevicesRemoved) {
|
icculus@9394
|
998 |
clean_out_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount, ¤t_audio.captureDevicesRemoved);
|
icculus@2049
|
999 |
}
|
icculus@2049
|
1000 |
|
icculus@9394
|
1001 |
if (!iscapture && current_audio.outputDevicesRemoved) {
|
icculus@9394
|
1002 |
clean_out_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount, ¤t_audio.outputDevicesRemoved);
|
icculus@9394
|
1003 |
current_audio.outputDevicesRemoved = SDL_FALSE;
|
icculus@5593
|
1004 |
}
|
icculus@5593
|
1005 |
|
icculus@9393
|
1006 |
retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
|
icculus@9394
|
1007 |
SDL_UnlockMutex(current_audio.detectionLock);
|
icculus@9393
|
1008 |
|
icculus@5593
|
1009 |
return retval;
|
icculus@2049
|
1010 |
}
|
icculus@2049
|
1011 |
|
slouken@0
|
1012 |
|
icculus@2049
|
1013 |
const char *
|
icculus@2049
|
1014 |
SDL_GetAudioDeviceName(int index, int iscapture)
|
icculus@2049
|
1015 |
{
|
icculus@9393
|
1016 |
const char *retval = NULL;
|
icculus@9393
|
1017 |
|
icculus@2049
|
1018 |
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
icculus@2049
|
1019 |
SDL_SetError("Audio subsystem is not initialized");
|
icculus@2049
|
1020 |
return NULL;
|
icculus@2049
|
1021 |
}
|
icculus@2049
|
1022 |
|
icculus@2049
|
1023 |
if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
|
icculus@2049
|
1024 |
SDL_SetError("No capture support");
|
icculus@2049
|
1025 |
return NULL;
|
slouken@1895
|
1026 |
}
|
icculus@2049
|
1027 |
|
icculus@9394
|
1028 |
if (index >= 0) {
|
icculus@9394
|
1029 |
SDL_AudioDeviceItem *item;
|
icculus@9394
|
1030 |
int i;
|
slouken@0
|
1031 |
|
icculus@9394
|
1032 |
SDL_LockMutex(current_audio.detectionLock);
|
icculus@9394
|
1033 |
item = iscapture ? current_audio.inputDevices : current_audio.outputDevices;
|
icculus@9394
|
1034 |
i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
|
icculus@9394
|
1035 |
if (index < i) {
|
icculus@9394
|
1036 |
for (i--; i > index; i--, item = item->next) {
|
icculus@9394
|
1037 |
SDL_assert(item != NULL);
|
icculus@9394
|
1038 |
}
|
icculus@9394
|
1039 |
SDL_assert(item != NULL);
|
icculus@9394
|
1040 |
retval = item->name;
|
slouken@7904
|
1041 |
}
|
icculus@9394
|
1042 |
SDL_UnlockMutex(current_audio.detectionLock);
|
slouken@1895
|
1043 |
}
|
slouken@262
|
1044 |
|
icculus@9394
|
1045 |
if (retval == NULL) {
|
icculus@9394
|
1046 |
SDL_SetError("No such device");
|
icculus@5593
|
1047 |
}
|
icculus@5593
|
1048 |
|
icculus@9394
|
1049 |
return retval;
|
icculus@2049
|
1050 |
}
|
icculus@2049
|
1051 |
|
icculus@2049
|
1052 |
|
icculus@2049
|
1053 |
static void
|
slouken@2060
|
1054 |
close_audio_device(SDL_AudioDevice * device)
|
icculus@2049
|
1055 |
{
|
icculus@10233
|
1056 |
SDL_AtomicSet(&device->shutdown, 1);
|
icculus@10238
|
1057 |
SDL_AtomicSet(&device->enabled, 0);
|
icculus@2049
|
1058 |
if (device->thread != NULL) {
|
icculus@2049
|
1059 |
SDL_WaitThread(device->thread, NULL);
|
icculus@2049
|
1060 |
}
|
icculus@2049
|
1061 |
if (device->mixer_lock != NULL) {
|
icculus@2049
|
1062 |
SDL_DestroyMutex(device->mixer_lock);
|
icculus@2049
|
1063 |
}
|
icculus@10256
|
1064 |
SDL_free(device->fake_stream);
|
icculus@2049
|
1065 |
if (device->convert.needed) {
|
icculus@10256
|
1066 |
SDL_free(device->convert.buf);
|
icculus@2049
|
1067 |
}
|
icculus@10255
|
1068 |
if (device->hidden != NULL) {
|
icculus@2049
|
1069 |
current_audio.impl.CloseDevice(device);
|
icculus@2049
|
1070 |
}
|
icculus@9012
|
1071 |
|
icculus@9012
|
1072 |
free_audio_queue(device->buffer_queue_head);
|
icculus@9012
|
1073 |
free_audio_queue(device->buffer_queue_pool);
|
icculus@9012
|
1074 |
|
icculus@10256
|
1075 |
SDL_free(device);
|
icculus@2049
|
1076 |
}
|
icculus@2049
|
1077 |
|
icculus@2049
|
1078 |
|
icculus@2049
|
1079 |
/*
|
icculus@2049
|
1080 |
* Sanity check desired AudioSpec for SDL_OpenAudio() in (orig).
|
icculus@2049
|
1081 |
* Fills in a sanitized copy in (prepared).
|
icculus@2049
|
1082 |
* Returns non-zero if okay, zero on fatal parameters in (orig).
|
icculus@2049
|
1083 |
*/
|
icculus@2049
|
1084 |
static int
|
slouken@2060
|
1085 |
prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared)
|
icculus@2049
|
1086 |
{
|
slouken@2060
|
1087 |
SDL_memcpy(prepared, orig, sizeof(SDL_AudioSpec));
|
icculus@2049
|
1088 |
|
icculus@2049
|
1089 |
if (orig->freq == 0) {
|
icculus@2049
|
1090 |
const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY");
|
slouken@2060
|
1091 |
if ((!env) || ((prepared->freq = SDL_atoi(env)) == 0)) {
|
slouken@2060
|
1092 |
prepared->freq = 22050; /* a reasonable default */
|
slouken@1895
|
1093 |
}
|
slouken@1895
|
1094 |
}
|
icculus@2049
|
1095 |
|
icculus@2049
|
1096 |
if (orig->format == 0) {
|
icculus@2049
|
1097 |
const char *env = SDL_getenv("SDL_AUDIO_FORMAT");
|
icculus@2049
|
1098 |
if ((!env) || ((prepared->format = SDL_ParseAudioFormat(env)) == 0)) {
|
slouken@2060
|
1099 |
prepared->format = AUDIO_S16; /* a reasonable default */
|
slouken@1895
|
1100 |
}
|
slouken@1895
|
1101 |
}
|
icculus@2049
|
1102 |
|
icculus@2049
|
1103 |
switch (orig->channels) {
|
slouken@2060
|
1104 |
case 0:{
|
slouken@2060
|
1105 |
const char *env = SDL_getenv("SDL_AUDIO_CHANNELS");
|
slouken@2141
|
1106 |
if ((!env) || ((prepared->channels = (Uint8) SDL_atoi(env)) == 0)) {
|
slouken@2060
|
1107 |
prepared->channels = 2; /* a reasonable default */
|
slouken@2060
|
1108 |
}
|
slouken@2060
|
1109 |
break;
|
icculus@2049
|
1110 |
}
|
slouken@1895
|
1111 |
case 1: /* Mono */
|
slouken@1895
|
1112 |
case 2: /* Stereo */
|
slouken@1895
|
1113 |
case 4: /* surround */
|
slouken@1895
|
1114 |
case 6: /* surround with center and lfe */
|
slouken@1895
|
1115 |
break;
|
slouken@1895
|
1116 |
default:
|
icculus@2049
|
1117 |
SDL_SetError("Unsupported number of audio channels.");
|
icculus@2049
|
1118 |
return 0;
|
slouken@1895
|
1119 |
}
|
icculus@2049
|
1120 |
|
icculus@2049
|
1121 |
if (orig->samples == 0) {
|
icculus@2049
|
1122 |
const char *env = SDL_getenv("SDL_AUDIO_SAMPLES");
|
slouken@2060
|
1123 |
if ((!env) || ((prepared->samples = (Uint16) SDL_atoi(env)) == 0)) {
|
icculus@2049
|
1124 |
/* Pick a default of ~46 ms at desired frequency */
|
icculus@2049
|
1125 |
/* !!! FIXME: remove this when the non-Po2 resampling is in. */
|
icculus@2049
|
1126 |
const int samples = (prepared->freq / 1000) * 46;
|
icculus@2049
|
1127 |
int power2 = 1;
|
icculus@2049
|
1128 |
while (power2 < samples) {
|
icculus@2049
|
1129 |
power2 *= 2;
|
icculus@2049
|
1130 |
}
|
icculus@2049
|
1131 |
prepared->samples = power2;
|
slouken@1895
|
1132 |
}
|
slouken@1895
|
1133 |
}
|
slouken@0
|
1134 |
|
slouken@1895
|
1135 |
/* Calculate the silence and size of the audio specification */
|
icculus@2049
|
1136 |
SDL_CalculateAudioSpec(prepared);
|
slouken@21
|
1137 |
|
icculus@2049
|
1138 |
return 1;
|
icculus@2049
|
1139 |
}
|
slouken@1408
|
1140 |
|
icculus@2049
|
1141 |
static SDL_AudioDeviceID
|
icculus@2049
|
1142 |
open_audio_device(const char *devname, int iscapture,
|
slouken@2866
|
1143 |
const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
|
slouken@2866
|
1144 |
int allowed_changes, int min_id)
|
icculus@2049
|
1145 |
{
|
icculus@10270
|
1146 |
const SDL_bool is_internal_thread = (desired->callback != NULL);
|
icculus@2049
|
1147 |
SDL_AudioDeviceID id = 0;
|
slouken@2866
|
1148 |
SDL_AudioSpec _obtained;
|
icculus@2049
|
1149 |
SDL_AudioDevice *device;
|
slouken@2866
|
1150 |
SDL_bool build_cvt;
|
icculus@9394
|
1151 |
void *handle = NULL;
|
icculus@2049
|
1152 |
int i = 0;
|
slouken@21
|
1153 |
|
icculus@2049
|
1154 |
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
icculus@2049
|
1155 |
SDL_SetError("Audio subsystem is not initialized");
|
icculus@2049
|
1156 |
return 0;
|
icculus@2049
|
1157 |
}
|
icculus@2049
|
1158 |
|
icculus@2049
|
1159 |
if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
|
icculus@2049
|
1160 |
SDL_SetError("No capture support");
|
icculus@2049
|
1161 |
return 0;
|
icculus@2049
|
1162 |
}
|
icculus@2049
|
1163 |
|
icculus@9393
|
1164 |
/* Find an available device ID... */
|
icculus@9393
|
1165 |
for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
|
icculus@9393
|
1166 |
if (open_devices[id] == NULL) {
|
icculus@9393
|
1167 |
break;
|
icculus@9393
|
1168 |
}
|
icculus@9393
|
1169 |
}
|
icculus@9393
|
1170 |
|
icculus@9393
|
1171 |
if (id == SDL_arraysize(open_devices)) {
|
icculus@9393
|
1172 |
SDL_SetError("Too many open audio devices");
|
icculus@9393
|
1173 |
return 0;
|
icculus@9393
|
1174 |
}
|
icculus@9393
|
1175 |
|
slouken@2866
|
1176 |
if (!obtained) {
|
slouken@2866
|
1177 |
obtained = &_obtained;
|
slouken@2866
|
1178 |
}
|
slouken@2866
|
1179 |
if (!prepare_audiospec(desired, obtained)) {
|
icculus@2049
|
1180 |
return 0;
|
icculus@2049
|
1181 |
}
|
icculus@2049
|
1182 |
|
icculus@2049
|
1183 |
/* If app doesn't care about a specific device, let the user override. */
|
icculus@2049
|
1184 |
if (devname == NULL) {
|
icculus@2049
|
1185 |
devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME");
|
slouken@1895
|
1186 |
}
|
slouken@21
|
1187 |
|
icculus@2049
|
1188 |
/*
|
icculus@2049
|
1189 |
* Catch device names at the high level for the simple case...
|
icculus@2049
|
1190 |
* This lets us have a basic "device enumeration" for systems that
|
icculus@2049
|
1191 |
* don't have multiple devices, but makes sure the device name is
|
icculus@2049
|
1192 |
* always NULL when it hits the low level.
|
icculus@2049
|
1193 |
*
|
icculus@2049
|
1194 |
* Also make sure that the simple case prevents multiple simultaneous
|
icculus@2049
|
1195 |
* opens of the default system device.
|
icculus@2049
|
1196 |
*/
|
icculus@2049
|
1197 |
|
icculus@10258
|
1198 |
if ((iscapture) && (current_audio.impl.OnlyHasDefaultCaptureDevice)) {
|
icculus@2049
|
1199 |
if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
|
icculus@2049
|
1200 |
SDL_SetError("No such device");
|
icculus@2049
|
1201 |
return 0;
|
icculus@2049
|
1202 |
}
|
icculus@2049
|
1203 |
devname = NULL;
|
icculus@2049
|
1204 |
|
icculus@2049
|
1205 |
for (i = 0; i < SDL_arraysize(open_devices); i++) {
|
icculus@2049
|
1206 |
if ((open_devices[i]) && (open_devices[i]->iscapture)) {
|
icculus@2049
|
1207 |
SDL_SetError("Audio device already open");
|
icculus@2049
|
1208 |
return 0;
|
icculus@2049
|
1209 |
}
|
icculus@2049
|
1210 |
}
|
icculus@9394
|
1211 |
} else if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
|
icculus@2049
|
1212 |
if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
|
icculus@2049
|
1213 |
SDL_SetError("No such device");
|
icculus@2049
|
1214 |
return 0;
|
icculus@2049
|
1215 |
}
|
icculus@2049
|
1216 |
devname = NULL;
|
icculus@2049
|
1217 |
|
icculus@2049
|
1218 |
for (i = 0; i < SDL_arraysize(open_devices); i++) {
|
icculus@2049
|
1219 |
if ((open_devices[i]) && (!open_devices[i]->iscapture)) {
|
icculus@2049
|
1220 |
SDL_SetError("Audio device already open");
|
icculus@2049
|
1221 |
return 0;
|
icculus@2049
|
1222 |
}
|
icculus@2049
|
1223 |
}
|
icculus@9394
|
1224 |
} else if (devname != NULL) {
|
icculus@9394
|
1225 |
/* if the app specifies an exact string, we can pass the backend
|
icculus@9394
|
1226 |
an actual device handle thingey, which saves them the effort of
|
icculus@9394
|
1227 |
figuring out what device this was (such as, reenumerating
|
icculus@9394
|
1228 |
everything again to find the matching human-readable name).
|
icculus@9394
|
1229 |
It might still need to open a device based on the string for,
|
icculus@9394
|
1230 |
say, a network audio server, but this optimizes some cases. */
|
icculus@9394
|
1231 |
SDL_AudioDeviceItem *item;
|
icculus@9394
|
1232 |
SDL_LockMutex(current_audio.detectionLock);
|
icculus@9394
|
1233 |
for (item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; item; item = item->next) {
|
icculus@9394
|
1234 |
if ((item->handle != NULL) && (SDL_strcmp(item->name, devname) == 0)) {
|
icculus@9394
|
1235 |
handle = item->handle;
|
icculus@9394
|
1236 |
break;
|
icculus@9394
|
1237 |
}
|
icculus@9394
|
1238 |
}
|
icculus@9394
|
1239 |
SDL_UnlockMutex(current_audio.detectionLock);
|
icculus@9394
|
1240 |
}
|
icculus@9394
|
1241 |
|
icculus@9394
|
1242 |
if (!current_audio.impl.AllowsArbitraryDeviceNames) {
|
icculus@9394
|
1243 |
/* has to be in our device list, or the default device. */
|
icculus@9394
|
1244 |
if ((handle == NULL) && (devname != NULL)) {
|
icculus@9394
|
1245 |
SDL_SetError("No such device.");
|
icculus@9394
|
1246 |
return 0;
|
icculus@9394
|
1247 |
}
|
icculus@2049
|
1248 |
}
|
icculus@2049
|
1249 |
|
icculus@10256
|
1250 |
device = (SDL_AudioDevice *) SDL_calloc(1, sizeof (SDL_AudioDevice));
|
icculus@2049
|
1251 |
if (device == NULL) {
|
icculus@2049
|
1252 |
SDL_OutOfMemory();
|
icculus@2049
|
1253 |
return 0;
|
icculus@2049
|
1254 |
}
|
icculus@9393
|
1255 |
device->id = id + 1;
|
slouken@2866
|
1256 |
device->spec = *obtained;
|
icculus@10235
|
1257 |
device->iscapture = iscapture ? SDL_TRUE : SDL_FALSE;
|
icculus@2049
|
1258 |
|
icculus@10238
|
1259 |
SDL_AtomicSet(&device->shutdown, 0); /* just in case. */
|
icculus@10238
|
1260 |
SDL_AtomicSet(&device->paused, 1);
|
icculus@10238
|
1261 |
SDL_AtomicSet(&device->enabled, 1);
|
icculus@10238
|
1262 |
|
icculus@9391
|
1263 |
/* Create a mutex for locking the sound buffers */
|
icculus@2049
|
1264 |
if (!current_audio.impl.SkipMixerLock) {
|
icculus@2049
|
1265 |
device->mixer_lock = SDL_CreateMutex();
|
icculus@2049
|
1266 |
if (device->mixer_lock == NULL) {
|
icculus@2049
|
1267 |
close_audio_device(device);
|
icculus@2049
|
1268 |
SDL_SetError("Couldn't create mixer lock");
|
icculus@2049
|
1269 |
return 0;
|
icculus@2049
|
1270 |
}
|
icculus@2049
|
1271 |
}
|
icculus@2049
|
1272 |
|
icculus@9394
|
1273 |
if (current_audio.impl.OpenDevice(device, handle, devname, iscapture) < 0) {
|
icculus@2049
|
1274 |
close_audio_device(device);
|
icculus@2049
|
1275 |
return 0;
|
icculus@2049
|
1276 |
}
|
icculus@10255
|
1277 |
|
icculus@10255
|
1278 |
/* if your target really doesn't need it, set it to 0x1 or something. */
|
icculus@10255
|
1279 |
/* otherwise, close_audio_device() won't call impl.CloseDevice(). */
|
icculus@10255
|
1280 |
SDL_assert(device->hidden != NULL);
|
slouken@0
|
1281 |
|
slouken@1895
|
1282 |
/* See if we need to do any conversion */
|
slouken@2866
|
1283 |
build_cvt = SDL_FALSE;
|
slouken@2866
|
1284 |
if (obtained->freq != device->spec.freq) {
|
slouken@2866
|
1285 |
if (allowed_changes & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) {
|
slouken@2866
|
1286 |
obtained->freq = device->spec.freq;
|
slouken@2866
|
1287 |
} else {
|
slouken@2866
|
1288 |
build_cvt = SDL_TRUE;
|
slouken@2866
|
1289 |
}
|
slouken@2866
|
1290 |
}
|
slouken@2866
|
1291 |
if (obtained->format != device->spec.format) {
|
slouken@2866
|
1292 |
if (allowed_changes & SDL_AUDIO_ALLOW_FORMAT_CHANGE) {
|
slouken@2866
|
1293 |
obtained->format = device->spec.format;
|
slouken@2866
|
1294 |
} else {
|
slouken@2866
|
1295 |
build_cvt = SDL_TRUE;
|
slouken@2866
|
1296 |
}
|
slouken@2866
|
1297 |
}
|
slouken@2866
|
1298 |
if (obtained->channels != device->spec.channels) {
|
slouken@2866
|
1299 |
if (allowed_changes & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) {
|
slouken@2866
|
1300 |
obtained->channels = device->spec.channels;
|
slouken@2866
|
1301 |
} else {
|
slouken@2866
|
1302 |
build_cvt = SDL_TRUE;
|
slouken@2866
|
1303 |
}
|
slouken@2866
|
1304 |
}
|
slouken@7518
|
1305 |
|
slouken@7518
|
1306 |
/* If the audio driver changes the buffer size, accept it.
|
slouken@7518
|
1307 |
This needs to be done after the format is modified above,
|
slouken@7518
|
1308 |
otherwise it might not have the correct buffer size.
|
slouken@7518
|
1309 |
*/
|
slouken@7518
|
1310 |
if (device->spec.samples != obtained->samples) {
|
slouken@7518
|
1311 |
obtained->samples = device->spec.samples;
|
slouken@7518
|
1312 |
SDL_CalculateAudioSpec(obtained);
|
slouken@7518
|
1313 |
}
|
slouken@7518
|
1314 |
|
slouken@2866
|
1315 |
if (build_cvt) {
|
slouken@1895
|
1316 |
/* Build an audio conversion block */
|
icculus@2049
|
1317 |
if (SDL_BuildAudioCVT(&device->convert,
|
slouken@2866
|
1318 |
obtained->format, obtained->channels,
|
slouken@2866
|
1319 |
obtained->freq,
|
icculus@2049
|
1320 |
device->spec.format, device->spec.channels,
|
icculus@2049
|
1321 |
device->spec.freq) < 0) {
|
icculus@2049
|
1322 |
close_audio_device(device);
|
icculus@2049
|
1323 |
return 0;
|
slouken@1895
|
1324 |
}
|
icculus@2049
|
1325 |
if (device->convert.needed) {
|
slouken@7518
|
1326 |
device->convert.len = (int) (((double) device->spec.size) /
|
slouken@2060
|
1327 |
device->convert.len_ratio);
|
icculus@2053
|
1328 |
|
icculus@2049
|
1329 |
device->convert.buf =
|
icculus@10256
|
1330 |
(Uint8 *) SDL_malloc(device->convert.len *
|
icculus@2049
|
1331 |
device->convert.len_mult);
|
icculus@2049
|
1332 |
if (device->convert.buf == NULL) {
|
icculus@2049
|
1333 |
close_audio_device(device);
|
slouken@1895
|
1334 |
SDL_OutOfMemory();
|
icculus@2049
|
1335 |
return 0;
|
slouken@1895
|
1336 |
}
|
slouken@1895
|
1337 |
}
|
slouken@1895
|
1338 |
}
|
icculus@2049
|
1339 |
|
icculus@9012
|
1340 |
if (device->spec.callback == NULL) { /* use buffer queueing? */
|
icculus@9012
|
1341 |
/* pool a few packets to start. Enough for two callbacks. */
|
icculus@9012
|
1342 |
const int packetlen = SDL_AUDIOBUFFERQUEUE_PACKETLEN;
|
icculus@9012
|
1343 |
const int wantbytes = ((device->convert.needed) ? device->convert.len : device->spec.size) * 2;
|
icculus@9012
|
1344 |
const int wantpackets = (wantbytes / packetlen) + ((wantbytes % packetlen) ? packetlen : 0);
|
icculus@9012
|
1345 |
for (i = 0; i < wantpackets; i++) {
|
icculus@9012
|
1346 |
SDL_AudioBufferQueue *packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
|
icculus@9012
|
1347 |
if (packet) { /* don't care if this fails, we'll deal later. */
|
icculus@9012
|
1348 |
packet->datalen = 0;
|
icculus@9012
|
1349 |
packet->startpos = 0;
|
icculus@9012
|
1350 |
packet->next = device->buffer_queue_pool;
|
icculus@9012
|
1351 |
device->buffer_queue_pool = packet;
|
icculus@9012
|
1352 |
}
|
icculus@9012
|
1353 |
}
|
icculus@9012
|
1354 |
|
icculus@10262
|
1355 |
device->spec.callback = iscapture ? SDL_BufferQueueFillCallback : SDL_BufferQueueDrainCallback;
|
icculus@9012
|
1356 |
device->spec.userdata = device;
|
icculus@9012
|
1357 |
}
|
icculus@9012
|
1358 |
|
icculus@9393
|
1359 |
/* add it to our list of open devices. */
|
icculus@9393
|
1360 |
open_devices[id] = device;
|
icculus@2049
|
1361 |
|
slouken@1895
|
1362 |
/* Start the audio thread if necessary */
|
icculus@2049
|
1363 |
if (!current_audio.impl.ProvidesOwnCallbackThread) {
|
slouken@1895
|
1364 |
/* Start the audio thread */
|
icculus@10146
|
1365 |
|
icculus@10271
|
1366 |
/* Allocate a fake audio buffer; only used by our internal threads. */
|
icculus@10271
|
1367 |
Uint32 stream_len = (device->convert.needed) ? device->convert.len_cvt : 0;
|
icculus@10271
|
1368 |
if (device->spec.size > stream_len) {
|
icculus@10271
|
1369 |
stream_len = device->spec.size;
|
icculus@10271
|
1370 |
}
|
icculus@10271
|
1371 |
SDL_assert(stream_len > 0);
|
icculus@10271
|
1372 |
|
icculus@10271
|
1373 |
device->fake_stream = (Uint8 *) SDL_malloc(stream_len);
|
icculus@10271
|
1374 |
if (device->fake_stream == NULL) {
|
icculus@10271
|
1375 |
close_audio_device(device);
|
icculus@10271
|
1376 |
SDL_OutOfMemory();
|
icculus@10271
|
1377 |
return 0;
|
icculus@10271
|
1378 |
}
|
icculus@10271
|
1379 |
|
icculus@10146
|
1380 |
/* !!! FIXME: we don't force the audio thread stack size here because it calls into user code, but maybe we should? */
|
icculus@10146
|
1381 |
/* buffer queueing callback only needs a few bytes, so make the stack tiny. */
|
icculus@5969
|
1382 |
char name[64];
|
icculus@10239
|
1383 |
const size_t stacksize = is_internal_thread ? 64 * 1024 : 0;
|
icculus@9393
|
1384 |
SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
|
icculus@10239
|
1385 |
device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, name, stacksize, device);
|
icculus@10146
|
1386 |
|
icculus@2049
|
1387 |
if (device->thread == NULL) {
|
icculus@9393
|
1388 |
SDL_CloseAudioDevice(device->id);
|
slouken@1895
|
1389 |
SDL_SetError("Couldn't create audio thread");
|
icculus@2049
|
1390 |
return 0;
|
icculus@2049
|
1391 |
}
|
icculus@2049
|
1392 |
}
|
icculus@2049
|
1393 |
|
icculus@9393
|
1394 |
return device->id;
|
icculus@2049
|
1395 |
}
|
icculus@2049
|
1396 |
|
icculus@2049
|
1397 |
|
icculus@2049
|
1398 |
int
|
slouken@2866
|
1399 |
SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
|
icculus@2049
|
1400 |
{
|
icculus@2049
|
1401 |
SDL_AudioDeviceID id = 0;
|
icculus@2049
|
1402 |
|
icculus@2049
|
1403 |
/* Start up the audio driver, if necessary. This is legacy behaviour! */
|
icculus@2049
|
1404 |
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
icculus@2049
|
1405 |
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
icculus@9382
|
1406 |
return -1;
|
slouken@1895
|
1407 |
}
|
icculus@2049
|
1408 |
}
|
slouken@0
|
1409 |
|
icculus@2049
|
1410 |
/* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
|
icculus@2049
|
1411 |
if (open_devices[0] != NULL) {
|
icculus@2049
|
1412 |
SDL_SetError("Audio device is already opened");
|
icculus@9382
|
1413 |
return -1;
|
slouken@1895
|
1414 |
}
|
icculus@2049
|
1415 |
|
slouken@2866
|
1416 |
if (obtained) {
|
slouken@2866
|
1417 |
id = open_audio_device(NULL, 0, desired, obtained,
|
slouken@2866
|
1418 |
SDL_AUDIO_ALLOW_ANY_CHANGE, 1);
|
slouken@2866
|
1419 |
} else {
|
slouken@8919
|
1420 |
id = open_audio_device(NULL, 0, desired, NULL, 0, 1);
|
slouken@2866
|
1421 |
}
|
slouken@21
|
1422 |
|
icculus@5964
|
1423 |
SDL_assert((id == 0) || (id == 1));
|
icculus@9382
|
1424 |
return (id == 0) ? -1 : 0;
|
icculus@2049
|
1425 |
}
|
slouken@21
|
1426 |
|
icculus@2049
|
1427 |
SDL_AudioDeviceID
|
icculus@2049
|
1428 |
SDL_OpenAudioDevice(const char *device, int iscapture,
|
slouken@2866
|
1429 |
const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
|
slouken@2866
|
1430 |
int allowed_changes)
|
icculus@2049
|
1431 |
{
|
slouken@2866
|
1432 |
return open_audio_device(device, iscapture, desired, obtained,
|
slouken@2866
|
1433 |
allowed_changes, 2);
|
slouken@1895
|
1434 |
}
|
slouken@1895
|
1435 |
|
slouken@3537
|
1436 |
SDL_AudioStatus
|
icculus@2049
|
1437 |
SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid)
|
slouken@1895
|
1438 |
{
|
icculus@2049
|
1439 |
SDL_AudioDevice *device = get_audio_device(devid);
|
slouken@3537
|
1440 |
SDL_AudioStatus status = SDL_AUDIO_STOPPED;
|
icculus@10238
|
1441 |
if (device && SDL_AtomicGet(&device->enabled)) {
|
icculus@10238
|
1442 |
if (SDL_AtomicGet(&device->paused)) {
|
slouken@1895
|
1443 |
status = SDL_AUDIO_PAUSED;
|
slouken@1895
|
1444 |
} else {
|
slouken@1895
|
1445 |
status = SDL_AUDIO_PLAYING;
|
slouken@1895
|
1446 |
}
|
slouken@1895
|
1447 |
}
|
icculus@9382
|
1448 |
return status;
|
slouken@0
|
1449 |
}
|
slouken@0
|
1450 |
|
icculus@2049
|
1451 |
|
slouken@3537
|
1452 |
SDL_AudioStatus
|
icculus@2049
|
1453 |
SDL_GetAudioStatus(void)
|
icculus@2049
|
1454 |
{
|
icculus@2049
|
1455 |
return SDL_GetAudioDeviceStatus(1);
|
icculus@2049
|
1456 |
}
|
icculus@2049
|
1457 |
|
icculus@2049
|
1458 |
void
|
icculus@2049
|
1459 |
SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on)
|
icculus@2049
|
1460 |
{
|
icculus@2049
|
1461 |
SDL_AudioDevice *device = get_audio_device(devid);
|
gabomdq@9148
|
1462 |
if (device) {
|
gabomdq@9148
|
1463 |
current_audio.impl.LockDevice(device);
|
icculus@10238
|
1464 |
SDL_AtomicSet(&device->paused, pause_on ? 1 : 0);
|
gabomdq@9148
|
1465 |
current_audio.impl.UnlockDevice(device);
|
icculus@2049
|
1466 |
}
|
icculus@2049
|
1467 |
}
|
icculus@2049
|
1468 |
|
slouken@1895
|
1469 |
void
|
slouken@1895
|
1470 |
SDL_PauseAudio(int pause_on)
|
slouken@0
|
1471 |
{
|
gabomdq@9148
|
1472 |
SDL_PauseAudioDevice(1, pause_on);
|
icculus@2049
|
1473 |
}
|
icculus@2049
|
1474 |
|
slouken@0
|
1475 |
|
icculus@2049
|
1476 |
void
|
icculus@2049
|
1477 |
SDL_LockAudioDevice(SDL_AudioDeviceID devid)
|
icculus@2049
|
1478 |
{
|
icculus@2049
|
1479 |
/* Obtain a lock on the mixing buffers */
|
icculus@2049
|
1480 |
SDL_AudioDevice *device = get_audio_device(devid);
|
icculus@2049
|
1481 |
if (device) {
|
icculus@2049
|
1482 |
current_audio.impl.LockDevice(device);
|
slouken@1895
|
1483 |
}
|
slouken@0
|
1484 |
}
|
slouken@0
|
1485 |
|
slouken@1895
|
1486 |
void
|
slouken@1895
|
1487 |
SDL_LockAudio(void)
|
slouken@0
|
1488 |
{
|
icculus@2049
|
1489 |
SDL_LockAudioDevice(1);
|
icculus@2049
|
1490 |
}
|
slouken@0
|
1491 |
|
icculus@2049
|
1492 |
void
|
icculus@2049
|
1493 |
SDL_UnlockAudioDevice(SDL_AudioDeviceID devid)
|
icculus@2049
|
1494 |
{
|
slouken@1895
|
1495 |
/* Obtain a lock on the mixing buffers */
|
icculus@2049
|
1496 |
SDL_AudioDevice *device = get_audio_device(devid);
|
icculus@2049
|
1497 |
if (device) {
|
icculus@2049
|
1498 |
current_audio.impl.UnlockDevice(device);
|
slouken@1895
|
1499 |
}
|
slouken@0
|
1500 |
}
|
slouken@0
|
1501 |
|
slouken@1895
|
1502 |
void
|
slouken@1895
|
1503 |
SDL_UnlockAudio(void)
|
slouken@0
|
1504 |
{
|
icculus@2049
|
1505 |
SDL_UnlockAudioDevice(1);
|
icculus@2049
|
1506 |
}
|
slouken@0
|
1507 |
|
icculus@2049
|
1508 |
void
|
icculus@2049
|
1509 |
SDL_CloseAudioDevice(SDL_AudioDeviceID devid)
|
icculus@2049
|
1510 |
{
|
icculus@2049
|
1511 |
SDL_AudioDevice *device = get_audio_device(devid);
|
icculus@2049
|
1512 |
if (device) {
|
icculus@2049
|
1513 |
close_audio_device(device);
|
slouken@2060
|
1514 |
open_devices[devid - 1] = NULL;
|
slouken@1895
|
1515 |
}
|
slouken@0
|
1516 |
}
|
slouken@0
|
1517 |
|
slouken@1895
|
1518 |
void
|
slouken@1895
|
1519 |
SDL_CloseAudio(void)
|
slouken@0
|
1520 |
{
|
icculus@2049
|
1521 |
SDL_CloseAudioDevice(1);
|
slouken@0
|
1522 |
}
|
slouken@0
|
1523 |
|
slouken@1895
|
1524 |
void
|
slouken@1895
|
1525 |
SDL_AudioQuit(void)
|
slouken@0
|
1526 |
{
|
icculus@7348
|
1527 |
SDL_AudioDeviceID i;
|
icculus@7348
|
1528 |
|
icculus@7345
|
1529 |
if (!current_audio.name) { /* not initialized?! */
|
icculus@7345
|
1530 |
return;
|
icculus@7345
|
1531 |
}
|
icculus@7345
|
1532 |
|
icculus@2049
|
1533 |
for (i = 0; i < SDL_arraysize(open_devices); i++) {
|
icculus@7342
|
1534 |
if (open_devices[i] != NULL) {
|
icculus@7446
|
1535 |
SDL_CloseAudioDevice(i+1);
|
icculus@7342
|
1536 |
}
|
icculus@2049
|
1537 |
}
|
slouken@0
|
1538 |
|
icculus@9394
|
1539 |
free_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount);
|
icculus@9394
|
1540 |
free_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount);
|
icculus@9394
|
1541 |
|
icculus@2049
|
1542 |
/* Free the driver data */
|
icculus@2049
|
1543 |
current_audio.impl.Deinitialize();
|
icculus@9393
|
1544 |
|
icculus@9394
|
1545 |
SDL_DestroyMutex(current_audio.detectionLock);
|
icculus@9393
|
1546 |
|
icculus@9393
|
1547 |
SDL_zero(current_audio);
|
icculus@9393
|
1548 |
SDL_zero(open_devices);
|
slouken@0
|
1549 |
}
|
slouken@0
|
1550 |
|
icculus@1982
|
1551 |
#define NUM_FORMATS 10
|
slouken@0
|
1552 |
static int format_idx;
|
slouken@0
|
1553 |
static int format_idx_sub;
|
icculus@1982
|
1554 |
static SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS] = {
|
slouken@1895
|
1555 |
{AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
|
icculus@1982
|
1556 |
AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
|
slouken@1895
|
1557 |
{AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
|
icculus@1982
|
1558 |
AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
|
icculus@1982
|
1559 |
{AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S32LSB,
|
icculus@1982
|
1560 |
AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
|
icculus@1982
|
1561 |
{AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S32MSB,
|
icculus@1982
|
1562 |
AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
|
icculus@1982
|
1563 |
{AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_S32LSB,
|
icculus@1982
|
1564 |
AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
|
icculus@1982
|
1565 |
{AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_S32MSB,
|
icculus@1982
|
1566 |
AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
|
icculus@1993
|
1567 |
{AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S16LSB,
|
icculus@1993
|
1568 |
AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
|
icculus@1993
|
1569 |
{AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S16MSB,
|
icculus@1993
|
1570 |
AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
|
icculus@1993
|
1571 |
{AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_S16LSB,
|
icculus@1993
|
1572 |
AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
|
icculus@1993
|
1573 |
{AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_S16MSB,
|
icculus@1993
|
1574 |
AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
|
slouken@0
|
1575 |
};
|
slouken@0
|
1576 |
|
icculus@1982
|
1577 |
SDL_AudioFormat
|
icculus@1982
|
1578 |
SDL_FirstAudioFormat(SDL_AudioFormat format)
|
slouken@0
|
1579 |
{
|
slouken@1895
|
1580 |
for (format_idx = 0; format_idx < NUM_FORMATS; ++format_idx) {
|
slouken@1895
|
1581 |
if (format_list[format_idx][0] == format) {
|
slouken@1895
|
1582 |
break;
|
slouken@1895
|
1583 |
}
|
slouken@1895
|
1584 |
}
|
slouken@1895
|
1585 |
format_idx_sub = 0;
|
icculus@9382
|
1586 |
return SDL_NextAudioFormat();
|
slouken@0
|
1587 |
}
|
slouken@0
|
1588 |
|
icculus@1982
|
1589 |
SDL_AudioFormat
|
slouken@1895
|
1590 |
SDL_NextAudioFormat(void)
|
slouken@0
|
1591 |
{
|
slouken@1895
|
1592 |
if ((format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS)) {
|
icculus@9382
|
1593 |
return 0;
|
slouken@1895
|
1594 |
}
|
icculus@9382
|
1595 |
return format_list[format_idx][format_idx_sub++];
|
slouken@0
|
1596 |
}
|
slouken@0
|
1597 |
|
slouken@1895
|
1598 |
void
|
slouken@1895
|
1599 |
SDL_CalculateAudioSpec(SDL_AudioSpec * spec)
|
slouken@0
|
1600 |
{
|
slouken@1895
|
1601 |
switch (spec->format) {
|
slouken@1895
|
1602 |
case AUDIO_U8:
|
slouken@1895
|
1603 |
spec->silence = 0x80;
|
slouken@1895
|
1604 |
break;
|
slouken@1895
|
1605 |
default:
|
slouken@1895
|
1606 |
spec->silence = 0x00;
|
slouken@1895
|
1607 |
break;
|
slouken@1895
|
1608 |
}
|
icculus@2049
|
1609 |
spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8;
|
slouken@1895
|
1610 |
spec->size *= spec->channels;
|
slouken@1895
|
1611 |
spec->size *= spec->samples;
|
slouken@0
|
1612 |
}
|
slouken@1895
|
1613 |
|
icculus@2049
|
1614 |
|
icculus@2049
|
1615 |
/*
|
icculus@2049
|
1616 |
* Moved here from SDL_mixer.c, since it relies on internals of an opened
|
icculus@2049
|
1617 |
* audio device (and is deprecated, by the way!).
|
icculus@2049
|
1618 |
*/
|
icculus@2049
|
1619 |
void
|
icculus@2049
|
1620 |
SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
|
icculus@2049
|
1621 |
{
|
icculus@2049
|
1622 |
/* Mix the user-level audio format */
|
icculus@2049
|
1623 |
SDL_AudioDevice *device = get_audio_device(1);
|
icculus@2049
|
1624 |
if (device != NULL) {
|
icculus@2049
|
1625 |
SDL_AudioFormat format;
|
icculus@2049
|
1626 |
if (device->convert.needed) {
|
icculus@2049
|
1627 |
format = device->convert.src_format;
|
icculus@2049
|
1628 |
} else {
|
icculus@2049
|
1629 |
format = device->spec.format;
|
icculus@2049
|
1630 |
}
|
icculus@2049
|
1631 |
SDL_MixAudioFormat(dst, src, format, len, volume);
|
icculus@2049
|
1632 |
}
|
icculus@2049
|
1633 |
}
|
icculus@2049
|
1634 |
|
slouken@1895
|
1635 |
/* vi: set ts=4 sw=4 expandtab: */
|