Mercurial > sdl-ios-xcode
annotate src/audio/alsa/SDL_alsa_audio.c @ 315:3333b6e68289
Date: Sat, 23 Mar 2002 13:53:37 +0200
From: "Mike Gorchak" <mike@malva.ua>
Subject: Big QNX patch again.
Added 8bit palette emulation code for window mode with bpp>=15.
Added store/restore original palette for 8bit modes.
Added more information about photon API call fails.
Rewroten change palette code, slow but works.
Fixed bug with set caption before window was inited.
Fixed bugs with some initial state of variables.
Fixed bug with storing old video mode settings.
Fixed bug with switching to fullscreen mode and back.
Fixed few double SEGFAULTS during parachute mode.
Removed compilation warning with no PgWaitHWIdle prototype.
Removed pack of dead unusable code.
Cleanups SDL_PrivateVideoData structure, some headers.
Some code formatting.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sat, 23 Mar 2002 20:19:44 +0000 |
parents | f6ffac90895c |
children | 30935e76acb5 |
rev | line source |
---|---|
0 | 1 /* |
2 SDL - Simple DirectMedia Layer | |
297
f6ffac90895c
Updated copyright information for 2002
Sam Lantinga <slouken@libsdl.org>
parents:
252
diff
changeset
|
3 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga |
0 | 4 |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 of the License, or (at your option) any later version. | |
9 | |
10 This library is distributed in the hope that it will be useful, | |
11 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
252
e8157fcb3114
Updated the source with the correct e-mail address
Sam Lantinga <slouken@libsdl.org>
parents:
0
diff
changeset
|
20 slouken@libsdl.org |
0 | 21 */ |
22 | |
23 | |
24 | |
25 /* Allow access to a raw mixing buffer */ | |
26 | |
27 #include <stdlib.h> | |
28 #include <stdio.h> | |
29 #include <string.h> | |
30 #include <errno.h> | |
31 #include <unistd.h> | |
32 #include <fcntl.h> | |
33 #include <signal.h> | |
34 #include <sys/types.h> | |
35 #include <sys/time.h> | |
36 | |
37 #include "SDL_audio.h" | |
38 #include "SDL_error.h" | |
39 #include "SDL_audiomem.h" | |
40 #include "SDL_audio_c.h" | |
41 #include "SDL_timer.h" | |
42 #include "SDL_alsa_audio.h" | |
43 | |
44 /* The tag name used by ALSA audio */ | |
45 #define DRIVER_NAME "alsa" | |
46 | |
47 /* default card and device numbers as listed in dev/snd */ | |
48 static int card_no = 0; | |
49 static int device_no = 0; | |
50 | |
51 /* default channel communication parameters */ | |
52 #define DEFAULT_CPARAMS_RATE 22050 | |
53 #define DEFAULT_CPARAMS_VOICES 1 | |
54 #define DEFAULT_CPARAMS_FRAG_SIZE 512 | |
55 #define DEFAULT_CPARAMS_FRAGS_MIN 1 | |
56 #define DEFAULT_CPARAMS_FRAGS_MAX -1 | |
57 | |
58 /* Open the audio device for playback, and don't block if busy */ | |
59 #define OPEN_FLAGS (SND_PCM_OPEN_PLAYBACK|SND_PCM_OPEN_NONBLOCK) | |
60 | |
61 /* Audio driver functions */ | |
62 static int PCM_OpenAudio(_THIS, SDL_AudioSpec *spec); | |
63 static void PCM_WaitAudio(_THIS); | |
64 static void PCM_PlayAudio(_THIS); | |
65 static Uint8 *PCM_GetAudioBuf(_THIS); | |
66 static void PCM_CloseAudio(_THIS); | |
67 | |
68 /* PCM transfer channel parameters initialize function */ | |
69 static void init_pcm_cparams(snd_pcm_channel_params_t* cparams) | |
70 { | |
71 memset(cparams,0,sizeof(snd_pcm_channel_params_t)); | |
72 | |
73 cparams->channel = SND_PCM_CHANNEL_PLAYBACK; | |
74 cparams->mode = SND_PCM_MODE_BLOCK; | |
75 cparams->start_mode = SND_PCM_START_DATA; //_FULL | |
76 cparams->stop_mode = SND_PCM_STOP_STOP; | |
77 cparams->format.format = SND_PCM_SFMT_S16_LE; | |
78 cparams->format.interleave = 1; | |
79 cparams->format.rate = DEFAULT_CPARAMS_RATE; | |
80 cparams->format.voices = DEFAULT_CPARAMS_VOICES; | |
81 cparams->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE; | |
82 cparams->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN; | |
83 cparams->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; | |
84 } | |
85 | |
86 /* Audio driver bootstrap functions */ | |
87 | |
88 static int Audio_Available(void) | |
89 /* | |
90 See if we can open a nonblocking channel. | |
91 Return value '1' means we can. | |
92 Return value '0' means we cannot. | |
93 */ | |
94 { | |
95 int available; | |
96 int rval; | |
97 snd_pcm_t *handle; | |
98 snd_pcm_channel_params_t cparams; | |
99 #ifdef DEBUG_AUDIO | |
100 snd_pcm_channel_status_t cstatus; | |
101 #endif | |
102 | |
103 available = 0; | |
104 handle = NULL; | |
105 | |
106 init_pcm_cparams(&cparams); | |
107 | |
108 rval = snd_pcm_open(&handle, card_no, device_no, OPEN_FLAGS); | |
109 if (rval >= 0) | |
110 { | |
111 rval = snd_pcm_plugin_params(handle, &cparams); | |
112 | |
113 #ifdef DEBUG_AUDIO | |
114 snd_pcm_plugin_status(handle, &cstatus); | |
115 printf("status after snd_pcm_plugin_params call = %d\n",cstatus.status); | |
116 #endif | |
117 if (rval >= 0) | |
118 { | |
119 available = 1; | |
120 } | |
121 else | |
122 { | |
123 SDL_SetError("snd_pcm_channel_params failed: %s\n", snd_strerror (rval)); | |
124 } | |
125 | |
126 if ((rval = snd_pcm_close(handle)) < 0) | |
127 { | |
128 SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval)); | |
129 available = 0; | |
130 } | |
131 } | |
132 else | |
133 { | |
134 SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval)); | |
135 } | |
136 | |
137 return(available); | |
138 } | |
139 | |
140 static void Audio_DeleteDevice(SDL_AudioDevice *device) | |
141 { | |
142 free(device->hidden); | |
143 free(device); | |
144 } | |
145 | |
146 static SDL_AudioDevice *Audio_CreateDevice(int devindex) | |
147 { | |
148 SDL_AudioDevice *this; | |
149 | |
150 /* Initialize all variables that we clean on shutdown */ | |
151 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); | |
152 if ( this ) { | |
153 memset(this, 0, (sizeof *this)); | |
154 this->hidden = (struct SDL_PrivateAudioData *) | |
155 malloc((sizeof *this->hidden)); | |
156 } | |
157 if ( (this == NULL) || (this->hidden == NULL) ) { | |
158 SDL_OutOfMemory(); | |
159 if ( this ) { | |
160 free(this); | |
161 } | |
162 return(0); | |
163 } | |
164 memset(this->hidden, 0, (sizeof *this->hidden)); | |
165 audio_handle = NULL; | |
166 | |
167 /* Set the function pointers */ | |
168 this->OpenAudio = PCM_OpenAudio; | |
169 this->WaitAudio = PCM_WaitAudio; | |
170 this->PlayAudio = PCM_PlayAudio; | |
171 this->GetAudioBuf = PCM_GetAudioBuf; | |
172 this->CloseAudio = PCM_CloseAudio; | |
173 | |
174 this->free = Audio_DeleteDevice; | |
175 | |
176 return this; | |
177 } | |
178 | |
179 AudioBootStrap ALSA_bootstrap = { | |
180 DRIVER_NAME, "ALSA PCM audio", | |
181 Audio_Available, Audio_CreateDevice | |
182 }; | |
183 | |
184 /* This function waits until it is possible to write a full sound buffer */ | |
185 static void PCM_WaitAudio(_THIS) | |
186 { | |
187 | |
188 /* Check to see if the thread-parent process is still alive */ | |
189 { static int cnt = 0; | |
190 /* Note that this only works with thread implementations | |
191 that use a different process id for each thread. | |
192 */ | |
193 if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ | |
194 if ( kill(parent, 0) < 0 ) { | |
195 this->enabled = 0; | |
196 } | |
197 } | |
198 } | |
199 | |
200 /* See if we need to use timed audio synchronization */ | |
201 if ( frame_ticks ) | |
202 { | |
203 /* Use timer for general audio synchronization */ | |
204 Sint32 ticks; | |
205 | |
206 ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; | |
207 if ( ticks > 0 ) | |
208 { | |
209 SDL_Delay(ticks); | |
210 } | |
211 } | |
212 else | |
213 { | |
214 /* Use select() for audio synchronization */ | |
215 fd_set fdset; | |
216 struct timeval timeout; | |
217 FD_ZERO(&fdset); | |
218 FD_SET(audio_fd, &fdset); | |
219 timeout.tv_sec = 10; | |
220 timeout.tv_usec = 0; | |
221 #ifdef DEBUG_AUDIO | |
222 fprintf(stderr, "Waiting for audio to get ready\n"); | |
223 #endif | |
224 if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) | |
225 { | |
226 const char *message = | |
227 "Audio timeout - buggy audio driver? (disabled)"; | |
228 /* In general we should never print to the screen, | |
229 but in this case we have no other way of letting | |
230 the user know what happened. | |
231 */ | |
232 fprintf(stderr, "SDL: %s\n", message); | |
233 this->enabled = 0; | |
234 /* Don't try to close - may hang */ | |
235 audio_fd = -1; | |
236 #ifdef DEBUG_AUDIO | |
237 fprintf(stderr, "Done disabling audio\n"); | |
238 #endif | |
239 } | |
240 #ifdef DEBUG_AUDIO | |
241 fprintf(stderr, "Ready!\n"); | |
242 #endif | |
243 } | |
244 } | |
245 | |
246 static snd_pcm_channel_status_t cstatus; | |
247 | |
248 static void PCM_PlayAudio(_THIS) | |
249 { | |
250 int written, rval; | |
251 | |
252 /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ | |
253 do { | |
254 written = snd_pcm_plugin_write(audio_handle, pcm_buf, pcm_len); | |
255 #ifdef DEBUG_AUDIO | |
256 fprintf(stderr, "written = %d pcm_len = %d\n",written,pcm_len); | |
257 #endif | |
258 if (written != pcm_len) | |
259 { | |
260 if (errno == EAGAIN) | |
261 { | |
262 SDL_Delay(1); /* Let a little CPU time go by and try to write again */ | |
263 #ifdef DEBUG_AUDIO | |
264 fprintf(stderr, "errno == EAGAIN\n"); | |
265 #endif | |
266 } | |
267 else | |
268 { | |
269 if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 ) | |
270 { | |
271 SDL_SetError("snd_pcm_plugin_status failed: %s\n", snd_strerror(rval)); | |
272 return; | |
273 } | |
274 if ( (cstatus.status == SND_PCM_STATUS_UNDERRUN) | |
275 ||(cstatus.status == SND_PCM_STATUS_READY) ) | |
276 { | |
277 #ifdef DEBUG_AUDIO | |
278 fprintf(stderr, "buffer underrun\n"); | |
279 #endif | |
280 if ( (rval = snd_pcm_plugin_prepare (audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0 ) | |
281 { | |
282 SDL_SetError("snd_pcm_plugin_prepare failed: %s\n",snd_strerror(rval) ); | |
283 return; | |
284 } | |
285 /* if we reach here, try to write again */ | |
286 } | |
287 } | |
288 } | |
289 } while ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ); | |
290 | |
291 /* Set the next write frame */ | |
292 if ( frame_ticks ) { | |
293 next_frame += frame_ticks; | |
294 } | |
295 | |
296 /* If we couldn't write, assume fatal error for now */ | |
297 if ( written < 0 ) { | |
298 this->enabled = 0; | |
299 } | |
300 return; | |
301 } | |
302 | |
303 static Uint8 *PCM_GetAudioBuf(_THIS) | |
304 { | |
305 return(pcm_buf); | |
306 } | |
307 | |
308 static void PCM_CloseAudio(_THIS) | |
309 { | |
310 int rval; | |
311 | |
312 if ( pcm_buf != NULL ) { | |
313 free(pcm_buf); | |
314 pcm_buf = NULL; | |
315 } | |
316 if ( audio_handle != NULL ) { | |
317 if ((rval = snd_pcm_plugin_flush(audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0) | |
318 { | |
319 SDL_SetError("snd_pcm_plugin_flush failed: %s\n",snd_strerror(rval)); | |
320 return; | |
321 } | |
322 if ((rval = snd_pcm_close(audio_handle)) < 0) | |
323 { | |
324 SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval)); | |
325 return; | |
326 } | |
327 audio_handle = NULL; | |
328 } | |
329 } | |
330 | |
331 static int PCM_OpenAudio(_THIS, SDL_AudioSpec *spec) | |
332 { | |
333 int rval; | |
334 snd_pcm_channel_params_t cparams; | |
335 snd_pcm_channel_setup_t csetup; | |
336 int format; | |
337 Uint16 test_format; | |
338 int twidth; | |
339 | |
340 /* initialize channel transfer parameters to default */ | |
341 init_pcm_cparams(&cparams); | |
342 | |
343 /* Reset the timer synchronization flag */ | |
344 frame_ticks = 0.0; | |
345 | |
346 /* Open the audio device */ | |
347 | |
348 rval = snd_pcm_open(&audio_handle, card_no, device_no, OPEN_FLAGS); | |
349 if ( rval < 0 ) { | |
350 SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval)); | |
351 return(-1); | |
352 } | |
353 | |
354 #ifdef PLUGIN_DISABLE_MMAP /* This is gone in newer versions of ALSA? */ | |
355 /* disable count status parameter */ | |
356 if ((rval = snd_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP))<0) | |
357 { | |
358 SDL_SetError("snd_plugin_set_disable failed: %s\n", snd_strerror(rval)); | |
359 return(-1); | |
360 } | |
361 #endif | |
362 | |
363 pcm_buf = NULL; | |
364 | |
365 /* Try for a closest match on audio format */ | |
366 format = 0; | |
367 for ( test_format = SDL_FirstAudioFormat(spec->format); | |
368 ! format && test_format; ) | |
369 { | |
370 #ifdef DEBUG_AUDIO | |
371 fprintf(stderr, "Trying format 0x%4.4x spec->samples %d\n", test_format,spec->samples); | |
372 #endif | |
373 /* if match found set format to equivalent ALSA format */ | |
374 switch ( test_format ) { | |
375 case AUDIO_U8: | |
376 format = SND_PCM_SFMT_U8; | |
377 cparams.buf.block.frag_size = spec->samples * spec->channels; | |
378 break; | |
379 case AUDIO_S8: | |
380 format = SND_PCM_SFMT_S8; | |
381 cparams.buf.block.frag_size = spec->samples * spec->channels; | |
382 break; | |
383 case AUDIO_S16LSB: | |
384 format = SND_PCM_SFMT_S16_LE; | |
385 cparams.buf.block.frag_size = spec->samples*2 * spec->channels; | |
386 break; | |
387 case AUDIO_S16MSB: | |
388 format = SND_PCM_SFMT_S16_BE; | |
389 cparams.buf.block.frag_size = spec->samples*2 * spec->channels; | |
390 break; | |
391 case AUDIO_U16LSB: | |
392 format = SND_PCM_SFMT_U16_LE; | |
393 cparams.buf.block.frag_size = spec->samples*2 * spec->channels; | |
394 break; | |
395 case AUDIO_U16MSB: | |
396 format = SND_PCM_SFMT_U16_BE; | |
397 cparams.buf.block.frag_size = spec->samples*2 * spec->channels; | |
398 break; | |
399 default: | |
400 break; | |
401 } | |
402 if ( ! format ) { | |
403 test_format = SDL_NextAudioFormat(); | |
404 } | |
405 } | |
406 if ( format == 0 ) { | |
407 SDL_SetError("Couldn't find any hardware audio formats"); | |
408 return(-1); | |
409 } | |
410 spec->format = test_format; | |
411 | |
412 /* Set the audio format */ | |
413 cparams.format.format = format; | |
414 | |
415 /* Set mono or stereo audio (currently only two channels supported) */ | |
416 cparams.format.voices = spec->channels; | |
417 | |
418 #ifdef DEBUG_AUDIO | |
419 printf("intializing channels %d\n", cparams.format.voices); | |
420 #endif | |
421 | |
422 /* Set rate */ | |
423 cparams.format.rate = spec->freq ; | |
424 | |
425 /* Setup the transfer parameters according to cparams */ | |
426 rval = snd_pcm_plugin_params(audio_handle, &cparams); | |
427 if (rval < 0) { | |
428 SDL_SetError("snd_pcm_channel_params failed: %s\n", snd_strerror (rval)); | |
429 return(-1); | |
430 } | |
431 | |
432 /* Make sure channel is setup right one last time */ | |
433 memset( &csetup, 0, sizeof( csetup ) ); | |
434 csetup.channel = SND_PCM_CHANNEL_PLAYBACK; | |
435 if ( snd_pcm_plugin_setup( audio_handle, &csetup ) < 0 ) | |
436 { | |
437 SDL_SetError("Unable to setup playback channel\n" ); | |
438 return(-1); | |
439 } | |
440 | |
441 #ifdef DEBUG_AUDIO | |
442 else | |
443 { | |
444 fprintf(stderr,"requested format: %d\n",cparams.format.format); | |
445 fprintf(stderr,"requested frag size: %d\n",cparams.buf.block.frag_size); | |
446 fprintf(stderr,"requested max frags: %d\n\n",cparams.buf.block.frags_max); | |
447 | |
448 fprintf(stderr,"real format: %d\n", csetup.format.format ); | |
449 fprintf(stderr,"real frag size : %d\n", csetup.buf.block.frag_size ); | |
450 fprintf(stderr,"real max frags : %d\n", csetup.buf.block.frags_max ); | |
451 } | |
452 #endif // DEBUG_AUDIO | |
453 | |
454 /* Allocate memory to the audio buffer and initialize with silence | |
455 (Note that buffer size must be a multiple of fragment size, so find closest multiple) | |
456 */ | |
457 | |
458 twidth = snd_pcm_format_width(format); | |
459 if (twidth < 0) { | |
460 printf("snd_pcm_format_width failed\n"); | |
461 twidth = 0; | |
462 } | |
463 #ifdef DEBUG_AUDIO | |
464 printf("format is %d bits wide\n",twidth); | |
465 #endif | |
466 | |
467 pcm_len = csetup.buf.block.frag_size * (twidth/8) * csetup.format.voices ; | |
468 | |
469 #ifdef DEBUG_AUDIO | |
470 printf("pcm_len set to %d\n", pcm_len); | |
471 #endif | |
472 | |
473 if (pcm_len == 0) | |
474 { | |
475 pcm_len = csetup.buf.block.frag_size; | |
476 } | |
477 | |
478 pcm_buf = (Uint8*)malloc(pcm_len); | |
479 if (pcm_buf == NULL) { | |
480 SDL_SetError("pcm_buf malloc failed\n"); | |
481 return(-1); | |
482 } | |
483 memset(pcm_buf,spec->silence,pcm_len); | |
484 | |
485 #ifdef DEBUG_AUDIO | |
486 fprintf(stderr,"pcm_buf malloced and silenced.\n"); | |
487 #endif | |
488 | |
489 /* get the file descriptor */ | |
490 if( (audio_fd = snd_pcm_file_descriptor(audio_handle, device_no)) < 0) | |
491 { | |
492 fprintf(stderr, "snd_pcm_file_descriptor failed with error code: %d\n", audio_fd); | |
493 } | |
494 | |
495 /* Trigger audio playback */ | |
496 rval = snd_pcm_plugin_prepare( audio_handle, SND_PCM_CHANNEL_PLAYBACK); | |
497 if (rval < 0) { | |
498 SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rval)); | |
499 return(-1); | |
500 } | |
501 rval = snd_pcm_playback_go(audio_handle); | |
502 if (rval < 0) { | |
503 SDL_SetError("snd_pcm_playback_go failed: %s\n", snd_strerror (rval)); | |
504 return(-1); | |
505 } | |
506 | |
507 /* Check to see if we need to use select() workaround */ | |
508 { char *workaround; | |
509 workaround = getenv("SDL_DSP_NOSELECT"); | |
510 if ( workaround ) { | |
511 frame_ticks = (float)(spec->samples*1000)/spec->freq; | |
512 next_frame = SDL_GetTicks()+frame_ticks; | |
513 } | |
514 } | |
515 | |
516 /* Get the parent process id (we're the parent of the audio thread) */ | |
517 parent = getpid(); | |
518 | |
519 /* We're ready to rock and roll. :-) */ | |
520 return(0); | |
521 } |