Mercurial > sdl-ios-xcode
comparison src/audio/qsa/SDL_qsa_audio.c @ 3099:82e60908fab1
Date: Mon, 23 Mar 2009 09:17:24 +0200
From: "Mike Gorchak"
Subject: New QNX patches
Please apply patch qnx4.diff, which is attached. What has been done:
1)Added back OpenGL ES renderer for QNX target. Added few corrections to
OpenGL ES renderer to let it work under QNX. OpenGL ES renderer do not
support textures under QNX, so I think some additional work must be done.
2) Added GL_OES_query_matrix extension to SDL_opengles.h header file, which
required by OpenGL ES 1.1 specification.
3) Added attribute clearing at the entrance of function
SDL_GL_GetAttribure(). Added error checking into the function
SDL_GL_GetAttribure(), because some attributes can't be obtained in OpenGL
ES 1.0.
4) Porting testdyngles to OpenGL ES 1.0 (1.1 has glColor4ub() and
glColor4f() functions, but 1.0 has glColor4f() only).
5) Added error checking after obtaining attributes using
SDL_GL_GetAttribute() function to the testgl2 and testgles.
6) Small correction to testmultiaudio with printing errors.
7) Added software and accelerated OpenGL ES 1.0 support into the QNX GF
driver.
Please remove ./src/audio/nto directory - it will not be used anymore.
Please create ./src/audio/qsa directory and add content of the archive
qsa.tar.gz into this directory. I rewrote some sound code, added support for
multiple audio cards, enumeration, etc. Added initial support for capture.
As far as I can understand SDL 1.3 is not supporting audio capture right now
? Sam, Am I right ? Or audio capture must be supported through the
PlayDevice routine ?
And last, please put file SDL_gf_opengles.c to the ./src/video/qnxgf
directory. It is OpenGL ES 1.1 emulation layer for some functions, which are
not supported by OpenGL ES 1.0.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Tue, 24 Mar 2009 10:33:12 +0000 |
parents | |
children | 60301ed80050 |
comparison
equal
deleted
inserted
replaced
3098:5f372cef955d | 3099:82e60908fab1 |
---|---|
1 /* | |
2 SDL - Simple DirectMedia Layer | |
3 Copyright (C) 1997-2009 Sam Lantinga | |
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 | |
20 slouken@libsdl.org | |
21 | |
22 QNX Sound Architecture (QSA) SDL driver | |
23 Copyright (C) 2009 Mike Gorchak | |
24 (mike@malva.ua, lestat@i.com.ua) | |
25 */ | |
26 | |
27 #include "SDL_config.h" | |
28 | |
29 #include <errno.h> | |
30 #include <unistd.h> | |
31 #include <fcntl.h> | |
32 #include <signal.h> | |
33 #include <sys/types.h> | |
34 #include <sys/time.h> | |
35 #include <sched.h> | |
36 #include <sys/select.h> | |
37 #include <sys/neutrino.h> | |
38 #include <sys/asoundlib.h> | |
39 | |
40 #include "SDL_timer.h" | |
41 #include "SDL_audio.h" | |
42 #include "../SDL_audiomem.h" | |
43 #include "../SDL_audio_c.h" | |
44 #include "SDL_qsa_audio.h" | |
45 | |
46 /* The tag name used by QSA audio framework */ | |
47 #define DRIVER_NAME "qsa" | |
48 | |
49 /* default channel communication parameters */ | |
50 #define DEFAULT_CPARAMS_RATE 44100 | |
51 #define DEFAULT_CPARAMS_VOICES 1 | |
52 | |
53 #define DEFAULT_CPARAMS_FRAG_SIZE 4096 | |
54 #define DEFAULT_CPARAMS_FRAGS_MIN 1 | |
55 #define DEFAULT_CPARAMS_FRAGS_MAX 1 | |
56 | |
57 #define QSA_NO_WORKAROUNDS 0x00000000 | |
58 #define QSA_MMAP_WORKAROUND 0x00000001 | |
59 | |
60 struct BuggyCards | |
61 { | |
62 char* cardname; | |
63 unsigned long bugtype; | |
64 }; | |
65 | |
66 #define QSA_WA_CARDS 3 | |
67 #define QSA_MAX_CARD_NAME_LENGTH 33 | |
68 | |
69 struct BuggyCards buggycards[QSA_WA_CARDS]= | |
70 { | |
71 {"Sound Blaster Live!", QSA_MMAP_WORKAROUND}, | |
72 {"Vortex 8820", QSA_MMAP_WORKAROUND}, | |
73 {"Vortex 8830", QSA_MMAP_WORKAROUND}, | |
74 }; | |
75 | |
76 /* List of found devices */ | |
77 #define QSA_MAX_DEVICES 32 | |
78 #define QSA_MAX_NAME_LENGTH 81+16 /* Hardcoded in QSA, can't be changed */ | |
79 | |
80 typedef struct _QSA_Device | |
81 { | |
82 char name[QSA_MAX_NAME_LENGTH]; /* Long audio device name for SDL */ | |
83 int cardno; | |
84 int deviceno; | |
85 } QSA_Device; | |
86 | |
87 QSA_Device qsa_playback_device[QSA_MAX_DEVICES]; | |
88 uint32_t qsa_playback_devices; | |
89 | |
90 QSA_Device qsa_capture_device[QSA_MAX_DEVICES]; | |
91 uint32_t qsa_capture_devices; | |
92 | |
93 static inline void QSA_SetError(const char* fn, int status) | |
94 { | |
95 SDL_SetError("QSA: %s failed: %s", fn, snd_strerror(status)); | |
96 } | |
97 | |
98 /* card names check to apply the workarounds */ | |
99 static int QSA_CheckBuggyCards(_THIS, unsigned long checkfor) | |
100 { | |
101 char scardname[QSA_MAX_CARD_NAME_LENGTH]; | |
102 int it; | |
103 | |
104 if (snd_card_get_name(this->hidden->cardno, scardname, QSA_MAX_CARD_NAME_LENGTH-1)<0) | |
105 { | |
106 return 0; | |
107 } | |
108 | |
109 for (it=0; it<QSA_WA_CARDS; it++) | |
110 { | |
111 if (SDL_strcmp(buggycards[it].cardname, scardname)==0) | |
112 { | |
113 if (buggycards[it].bugtype==checkfor) | |
114 { | |
115 return 1; | |
116 } | |
117 } | |
118 } | |
119 | |
120 return 0; | |
121 } | |
122 | |
123 static void QSA_ThreadInit(_THIS) | |
124 { | |
125 struct sched_param param; | |
126 int status; | |
127 | |
128 /* Increase default 10 priority to 25 to avoid jerky sound */ | |
129 status=SchedGet(0, 0, ¶m); | |
130 param.sched_priority=param.sched_curpriority + 15; | |
131 status=SchedSet(0, 0, SCHED_NOCHANGE, ¶m); | |
132 } | |
133 | |
134 /* PCM channel parameters initialize function */ | |
135 static void QSA_InitAudioParams(snd_pcm_channel_params_t* cpars) | |
136 { | |
137 SDL_memset(cpars, 0, sizeof(snd_pcm_channel_params_t)); | |
138 | |
139 cpars->channel=SND_PCM_CHANNEL_PLAYBACK; | |
140 cpars->mode=SND_PCM_MODE_BLOCK; | |
141 cpars->start_mode=SND_PCM_START_DATA; | |
142 cpars->stop_mode=SND_PCM_STOP_STOP; | |
143 cpars->format.format=SND_PCM_SFMT_S16_LE; | |
144 cpars->format.interleave=1; | |
145 cpars->format.rate=DEFAULT_CPARAMS_RATE; | |
146 cpars->format.voices=DEFAULT_CPARAMS_VOICES; | |
147 cpars->buf.block.frag_size=DEFAULT_CPARAMS_FRAG_SIZE; | |
148 cpars->buf.block.frags_min=DEFAULT_CPARAMS_FRAGS_MIN; | |
149 cpars->buf.block.frags_max=DEFAULT_CPARAMS_FRAGS_MAX; | |
150 } | |
151 | |
152 /* This function waits until it is possible to write a full sound buffer */ | |
153 static void QSA_WaitDevice(_THIS) | |
154 { | |
155 fd_set wfds; | |
156 fd_set rfds; | |
157 int selectret; | |
158 struct timeval timeout; | |
159 | |
160 if (!this->hidden->iscapture) | |
161 { | |
162 FD_ZERO(&wfds); | |
163 FD_SET(this->hidden->audio_fd, &wfds); | |
164 } | |
165 else | |
166 { | |
167 FD_ZERO(&rfds); | |
168 FD_SET(this->hidden->audio_fd, &rfds); | |
169 } | |
170 | |
171 do { | |
172 /* Setup timeout for playing one fragment equal to 2 seconds */ | |
173 /* If timeout occured than something wrong with hardware or driver */ | |
174 /* For example, Vortex 8820 audio driver stucks on second DAC because */ | |
175 /* it doesn't exist ! */ | |
176 timeout.tv_sec=2; | |
177 timeout.tv_usec=0; | |
178 this->hidden->timeout_on_wait=0; | |
179 | |
180 if (!this->hidden->iscapture) | |
181 { | |
182 selectret=select(this->hidden->audio_fd+1, NULL, &wfds, NULL, &timeout); | |
183 } | |
184 else | |
185 { | |
186 selectret=select(this->hidden->audio_fd+1, &rfds, NULL, NULL, &timeout); | |
187 } | |
188 | |
189 switch(selectret) | |
190 { | |
191 case -1: | |
192 { | |
193 SDL_SetError("QSA: select() failed: %s\n", strerror(errno)); | |
194 return; | |
195 } | |
196 break; | |
197 case 0: | |
198 { | |
199 SDL_SetError("QSA: timeout on buffer waiting occured\n"); | |
200 this->hidden->timeout_on_wait=1; | |
201 return; | |
202 } | |
203 break; | |
204 default: | |
205 { | |
206 if (!this->hidden->iscapture) | |
207 { | |
208 if (FD_ISSET(this->hidden->audio_fd, &wfds)) | |
209 { | |
210 return; | |
211 } | |
212 } | |
213 else | |
214 { | |
215 if (FD_ISSET(this->hidden->audio_fd, &rfds)) | |
216 { | |
217 return; | |
218 } | |
219 } | |
220 } | |
221 break; | |
222 } | |
223 } while (1); | |
224 } | |
225 | |
226 static void QSA_PlayDevice(_THIS) | |
227 { | |
228 snd_pcm_channel_status_t cstatus; | |
229 int written; | |
230 int status; | |
231 int towrite; | |
232 void* pcmbuffer; | |
233 | |
234 if ((!this->enabled) || (!this->hidden)) | |
235 { | |
236 return; | |
237 } | |
238 | |
239 towrite = this->spec.size; | |
240 pcmbuffer = this->hidden->pcm_buf; | |
241 | |
242 /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ | |
243 do { | |
244 written=snd_pcm_plugin_write(this->hidden->audio_handle, pcmbuffer, towrite); | |
245 if (written!=towrite) | |
246 { | |
247 /* Check if samples playback got stuck somewhere in hardware or in */ | |
248 /* the audio device driver */ | |
249 if ((errno==EAGAIN) && (written==0)) | |
250 { | |
251 if (this->hidden->timeout_on_wait!=0) | |
252 { | |
253 SDL_SetError("QSA: buffer playback timeout\n"); | |
254 return; | |
255 } | |
256 } | |
257 | |
258 /* Check for errors or conditions */ | |
259 if ((errno==EAGAIN)||(errno==EWOULDBLOCK)) | |
260 { | |
261 /* Let a little CPU time go by and try to write again */ | |
262 SDL_Delay(1); | |
263 | |
264 /* if we wrote some data */ | |
265 towrite-=written; | |
266 pcmbuffer+=written*this->spec.channels; | |
267 continue; | |
268 } | |
269 else | |
270 { | |
271 if ((errno==EINVAL)||(errno==EIO)) | |
272 { | |
273 SDL_memset(&cstatus, 0, sizeof(cstatus)); | |
274 if (!this->hidden->iscapture) | |
275 { | |
276 cstatus.channel=SND_PCM_CHANNEL_PLAYBACK; | |
277 } | |
278 else | |
279 { | |
280 cstatus.channel=SND_PCM_CHANNEL_CAPTURE; | |
281 } | |
282 | |
283 status=snd_pcm_plugin_status(this->hidden->audio_handle, &cstatus); | |
284 if (status<0) | |
285 { | |
286 QSA_SetError("snd_pcm_plugin_status", status); | |
287 return; | |
288 } | |
289 | |
290 if ((cstatus.status==SND_PCM_STATUS_UNDERRUN) || | |
291 (cstatus.status==SND_PCM_STATUS_READY)) | |
292 { | |
293 if (!this->hidden->iscapture) | |
294 { | |
295 status=snd_pcm_plugin_prepare(this->hidden->audio_handle, | |
296 SND_PCM_CHANNEL_PLAYBACK); | |
297 } | |
298 else | |
299 { | |
300 status=snd_pcm_plugin_prepare(this->hidden->audio_handle, | |
301 SND_PCM_CHANNEL_CAPTURE); | |
302 } | |
303 if (status<0) | |
304 { | |
305 QSA_SetError("snd_pcm_plugin_prepare", status); | |
306 return; | |
307 } | |
308 } | |
309 continue; | |
310 } | |
311 else | |
312 { | |
313 return; | |
314 } | |
315 } | |
316 } | |
317 else | |
318 { | |
319 /* we wrote all remaining data */ | |
320 towrite -= written; | |
321 pcmbuffer += written * this->spec.channels; | |
322 } | |
323 } while ((towrite>0) && (this->enabled)); | |
324 | |
325 /* If we couldn't write, assume fatal error for now */ | |
326 if (towrite!=0) | |
327 { | |
328 this->enabled=0; | |
329 } | |
330 } | |
331 | |
332 static Uint8* QSA_GetDeviceBuf(_THIS) | |
333 { | |
334 return this->hidden->pcm_buf; | |
335 } | |
336 | |
337 static void QSA_CloseDevice(_THIS) | |
338 { | |
339 if (this->hidden!=NULL) | |
340 { | |
341 if (this->hidden->audio_handle!=NULL) | |
342 { | |
343 if (!this->hidden->iscapture) | |
344 { | |
345 /* Finish playing available samples */ | |
346 snd_pcm_plugin_flush(this->hidden->audio_handle, SND_PCM_CHANNEL_PLAYBACK); | |
347 } | |
348 else | |
349 { | |
350 /* Cancel unread samples during capture */ | |
351 snd_pcm_plugin_flush(this->hidden->audio_handle, SND_PCM_CHANNEL_CAPTURE); | |
352 } | |
353 snd_pcm_close(this->hidden->audio_handle); | |
354 this->hidden->audio_handle=NULL; | |
355 } | |
356 | |
357 if (this->hidden->pcm_buf!=NULL) | |
358 { | |
359 SDL_FreeAudioMem(this->hidden->pcm_buf); | |
360 this->hidden->pcm_buf=NULL; | |
361 } | |
362 | |
363 SDL_free(this->hidden); | |
364 this->hidden=NULL; | |
365 } | |
366 } | |
367 | |
368 static int QSA_OpenDevice(_THIS, const char* devname, int iscapture) | |
369 { | |
370 int status=0; | |
371 int format=0; | |
372 SDL_AudioFormat test_format=0; | |
373 int found=0; | |
374 snd_pcm_channel_setup_t csetup; | |
375 snd_pcm_channel_params_t cparams; | |
376 | |
377 /* Initialize all variables that we clean on shutdown */ | |
378 this->hidden=(struct SDL_PrivateAudioData*)SDL_calloc(1, (sizeof(struct SDL_PrivateAudioData))); | |
379 if (this->hidden==NULL) | |
380 { | |
381 SDL_OutOfMemory(); | |
382 return 0; | |
383 } | |
384 SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData)); | |
385 | |
386 /* Initialize channel transfer parameters to default */ | |
387 QSA_InitAudioParams(&cparams); | |
388 | |
389 /* Initialize channel direction: capture or playback */ | |
390 this->hidden->iscapture=iscapture; | |
391 | |
392 /* Find deviceid and cardid by device name for playback */ | |
393 if ((!this->hidden->iscapture) && (devname!=NULL)) | |
394 { | |
395 uint32_t device; | |
396 int32_t status; | |
397 | |
398 /* Search in the playback devices */ | |
399 device=0; | |
400 do { | |
401 status=SDL_strcmp(qsa_playback_device[device].name, devname); | |
402 if (status==0) | |
403 { | |
404 /* Found requested device */ | |
405 this->hidden->deviceno=qsa_playback_device[device].deviceno; | |
406 this->hidden->cardno=qsa_playback_device[device].cardno; | |
407 break; | |
408 } | |
409 device++; | |
410 if (device>=qsa_playback_devices) | |
411 { | |
412 QSA_CloseDevice(this); | |
413 SDL_SetError("No such playback device"); | |
414 return 0; | |
415 } | |
416 } while(1); | |
417 } | |
418 | |
419 /* Find deviceid and cardid by device name for capture */ | |
420 if ((this->hidden->iscapture) && (devname!=NULL)) | |
421 { | |
422 /* Search in the capture devices */ | |
423 uint32_t device; | |
424 int32_t status; | |
425 | |
426 /* Searching in the playback devices */ | |
427 device=0; | |
428 do { | |
429 status=SDL_strcmp(qsa_capture_device[device].name, devname); | |
430 if (status==0) | |
431 { | |
432 /* Found requested device */ | |
433 this->hidden->deviceno=qsa_capture_device[device].deviceno; | |
434 this->hidden->cardno=qsa_capture_device[device].cardno; | |
435 break; | |
436 } | |
437 device++; | |
438 if (device>=qsa_capture_devices) | |
439 { | |
440 QSA_CloseDevice(this); | |
441 SDL_SetError("No such capture device"); | |
442 return 0; | |
443 } | |
444 } while(1); | |
445 } | |
446 | |
447 /* Check if SDL requested default audio device */ | |
448 if (devname==NULL) | |
449 { | |
450 /* Open system default audio device */ | |
451 if (!this->hidden->iscapture) | |
452 { | |
453 status=snd_pcm_open_preferred(&this->hidden->audio_handle, | |
454 &this->hidden->cardno, | |
455 &this->hidden->deviceno, SND_PCM_OPEN_PLAYBACK); | |
456 } | |
457 else | |
458 { | |
459 status=snd_pcm_open_preferred(&this->hidden->audio_handle, | |
460 &this->hidden->cardno, | |
461 &this->hidden->deviceno, SND_PCM_OPEN_CAPTURE); | |
462 } | |
463 } | |
464 else | |
465 { | |
466 /* Open requested audio device */ | |
467 if (!this->hidden->iscapture) | |
468 { | |
469 status=snd_pcm_open(&this->hidden->audio_handle, this->hidden->cardno, | |
470 this->hidden->deviceno, SND_PCM_OPEN_PLAYBACK); | |
471 } | |
472 else | |
473 { | |
474 status=snd_pcm_open(&this->hidden->audio_handle, this->hidden->cardno, | |
475 this->hidden->deviceno, SND_PCM_OPEN_CAPTURE); | |
476 } | |
477 } | |
478 | |
479 /* Check if requested device is opened */ | |
480 if (status<0) | |
481 { | |
482 QSA_CloseDevice(this); | |
483 QSA_SetError("snd_pcm_open", status); | |
484 return 0; | |
485 } | |
486 | |
487 if (!QSA_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) | |
488 { | |
489 /* Disable QSA MMAP plugin for buggy audio drivers */ | |
490 status=snd_pcm_plugin_set_disable(this->hidden->audio_handle, PLUGIN_DISABLE_MMAP); | |
491 if (status<0) | |
492 { | |
493 QSA_CloseDevice(this); | |
494 QSA_SetError("snd_pcm_plugin_set_disable", status); | |
495 return 0; | |
496 } | |
497 } | |
498 | |
499 /* Try for a closest match on audio format */ | |
500 format = 0; | |
501 /* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */ | |
502 found = 0; | |
503 | |
504 for (test_format = SDL_FirstAudioFormat(this->spec.format); !found;) | |
505 { | |
506 /* if match found set format to equivalent QSA format */ | |
507 switch (test_format) | |
508 { | |
509 case AUDIO_U8: | |
510 { | |
511 format=SND_PCM_SFMT_U8; | |
512 found=1; | |
513 } | |
514 break; | |
515 case AUDIO_S8: | |
516 { | |
517 format=SND_PCM_SFMT_S8; | |
518 found=1; | |
519 } | |
520 break; | |
521 case AUDIO_S16LSB: | |
522 { | |
523 format=SND_PCM_SFMT_S16_LE; | |
524 found=1; | |
525 } | |
526 break; | |
527 case AUDIO_S16MSB: | |
528 { | |
529 format=SND_PCM_SFMT_S16_BE; | |
530 found=1; | |
531 } | |
532 break; | |
533 case AUDIO_U16LSB: | |
534 { | |
535 format=SND_PCM_SFMT_U16_LE; | |
536 found=1; | |
537 } | |
538 break; | |
539 case AUDIO_U16MSB: | |
540 { | |
541 format=SND_PCM_SFMT_U16_BE; | |
542 found=1; | |
543 } | |
544 break; | |
545 case AUDIO_S32LSB: | |
546 { | |
547 format=SND_PCM_SFMT_S32_LE; | |
548 found=1; | |
549 } | |
550 break; | |
551 case AUDIO_S32MSB: | |
552 { | |
553 format=SND_PCM_SFMT_S32_BE; | |
554 found=1; | |
555 } | |
556 break; | |
557 case AUDIO_F32LSB: | |
558 { | |
559 format=SND_PCM_SFMT_FLOAT_LE; | |
560 found=1; | |
561 } | |
562 break; | |
563 case AUDIO_F32MSB: | |
564 { | |
565 format=SND_PCM_SFMT_FLOAT_BE; | |
566 found=1; | |
567 } | |
568 break; | |
569 default: | |
570 { | |
571 break; | |
572 } | |
573 } | |
574 | |
575 if (!found) | |
576 { | |
577 test_format = SDL_NextAudioFormat(); | |
578 } | |
579 } | |
580 | |
581 /* assumes test_format not 0 on success */ | |
582 if (test_format==0) | |
583 { | |
584 QSA_CloseDevice(this); | |
585 SDL_SetError("QSA: Couldn't find any hardware audio formats"); | |
586 return 0; | |
587 } | |
588 | |
589 this->spec.format=test_format; | |
590 | |
591 /* Set the audio format */ | |
592 cparams.format.format=format; | |
593 | |
594 /* Set mono/stereo/4ch/6ch/8ch audio */ | |
595 cparams.format.voices=this->spec.channels; | |
596 | |
597 /* Set rate */ | |
598 cparams.format.rate=this->spec.freq; | |
599 | |
600 /* Setup the transfer parameters according to cparams */ | |
601 status=snd_pcm_plugin_params(this->hidden->audio_handle, &cparams); | |
602 if (status<0) | |
603 { | |
604 QSA_CloseDevice(this); | |
605 QSA_SetError("snd_pcm_channel_params", status); | |
606 return 0; | |
607 } | |
608 | |
609 /* Make sure channel is setup right one last time */ | |
610 SDL_memset(&csetup, '\0', sizeof(csetup)); | |
611 if (!this->hidden->iscapture) | |
612 { | |
613 csetup.channel=SND_PCM_CHANNEL_PLAYBACK; | |
614 } | |
615 else | |
616 { | |
617 csetup.channel=SND_PCM_CHANNEL_CAPTURE; | |
618 } | |
619 | |
620 /* Setup an audio channel */ | |
621 if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) | |
622 { | |
623 QSA_CloseDevice(this); | |
624 SDL_SetError("QSA: Unable to setup channel\n"); | |
625 return 0; | |
626 } | |
627 | |
628 /* Calculate the final parameters for this audio specification */ | |
629 SDL_CalculateAudioSpec(&this->spec); | |
630 | |
631 this->hidden->pcm_len = this->spec.size; | |
632 | |
633 if (this->hidden->pcm_len==0) | |
634 { | |
635 this->hidden->pcm_len=csetup.buf.block.frag_size * this->spec.channels * | |
636 (snd_pcm_format_width(format) / 8); | |
637 } | |
638 | |
639 /* | |
640 * Allocate memory to the audio buffer and initialize with silence | |
641 * (Note that buffer size must be a multiple of fragment size, so find | |
642 * closest multiple) | |
643 */ | |
644 this->hidden->pcm_buf=(Uint8*)SDL_AllocAudioMem(this->hidden->pcm_len); | |
645 if (this->hidden->pcm_buf==NULL) | |
646 { | |
647 QSA_CloseDevice(this); | |
648 SDL_OutOfMemory(); | |
649 return 0; | |
650 } | |
651 SDL_memset(this->hidden->pcm_buf, this->spec.silence, this->hidden->pcm_len); | |
652 | |
653 /* get the file descriptor */ | |
654 if (!this->hidden->iscapture) | |
655 { | |
656 this->hidden->audio_fd=snd_pcm_file_descriptor(this->hidden->audio_handle, | |
657 SND_PCM_CHANNEL_PLAYBACK); | |
658 } | |
659 else | |
660 { | |
661 this->hidden->audio_fd=snd_pcm_file_descriptor(this->hidden->audio_handle, | |
662 SND_PCM_CHANNEL_CAPTURE); | |
663 } | |
664 | |
665 if (this->hidden->audio_fd<0) | |
666 { | |
667 QSA_CloseDevice(this); | |
668 QSA_SetError("snd_pcm_file_descriptor", status); | |
669 return 0; | |
670 } | |
671 | |
672 /* Prepare an audio channel */ | |
673 if (!this->hidden->iscapture) | |
674 { | |
675 /* Prepare audio playback */ | |
676 status=snd_pcm_plugin_prepare(this->hidden->audio_handle, SND_PCM_CHANNEL_PLAYBACK); | |
677 } | |
678 else | |
679 { | |
680 /* Prepare audio capture */ | |
681 status=snd_pcm_plugin_prepare(this->hidden->audio_handle, SND_PCM_CHANNEL_CAPTURE); | |
682 } | |
683 | |
684 if (status<0) | |
685 { | |
686 QSA_CloseDevice(this); | |
687 QSA_SetError("snd_pcm_plugin_prepare", status); | |
688 return 0; | |
689 } | |
690 | |
691 /* We're really ready to rock and roll. :-) */ | |
692 return 1; | |
693 } | |
694 | |
695 int QSA_DetectDevices(int iscapture) | |
696 { | |
697 uint32_t it; | |
698 uint32_t cards; | |
699 uint32_t devices; | |
700 int32_t status; | |
701 | |
702 /* Detect amount of available devices */ | |
703 /* this value can be changed in the runtime */ | |
704 cards=snd_cards(); | |
705 | |
706 /* If io-audio manager is not running we will get 0 as number */ | |
707 /* of available audio devices */ | |
708 if (cards==0) | |
709 { | |
710 /* We have no any available audio devices */ | |
711 return 0; | |
712 } | |
713 | |
714 /* Find requested devices by type */ | |
715 if (!iscapture) | |
716 { | |
717 /* Playback devices enumeration requested */ | |
718 for(it=0; it<cards; it++) | |
719 { | |
720 devices=0; | |
721 do { | |
722 status=snd_card_get_longname(it, qsa_playback_device[qsa_playback_devices].name, QSA_MAX_NAME_LENGTH); | |
723 if (status==EOK) | |
724 { | |
725 snd_pcm_t* handle; | |
726 | |
727 /* Add device number to device name */ | |
728 sprintf(qsa_playback_device[qsa_playback_devices].name + SDL_strlen(qsa_playback_device[qsa_playback_devices].name), " d%d", devices); | |
729 | |
730 /* Store associated card number id */ | |
731 qsa_playback_device[qsa_playback_devices].cardno=it; | |
732 | |
733 /* Check if this device id could play anything */ | |
734 status=snd_pcm_open(&handle, it, devices, SND_PCM_OPEN_PLAYBACK); | |
735 if (status==EOK) | |
736 { | |
737 qsa_playback_device[qsa_playback_devices].deviceno=devices; | |
738 status=snd_pcm_close(handle); | |
739 if (status==EOK) | |
740 { | |
741 qsa_playback_devices++; | |
742 } | |
743 } | |
744 else | |
745 { | |
746 /* Check if we got end of devices list */ | |
747 if (status==-ENOENT) | |
748 { | |
749 break; | |
750 } | |
751 } | |
752 } | |
753 else | |
754 { | |
755 break; | |
756 } | |
757 | |
758 /* Check if we reached maximum devices count */ | |
759 if (qsa_playback_devices>=QSA_MAX_DEVICES) | |
760 { | |
761 break; | |
762 } | |
763 devices++; | |
764 } while(1); | |
765 | |
766 /* Check if we reached maximum devices count */ | |
767 if (qsa_playback_devices>=QSA_MAX_DEVICES) | |
768 { | |
769 break; | |
770 } | |
771 } | |
772 } | |
773 else | |
774 { | |
775 /* Capture devices enumeration requested */ | |
776 for(it=0; it<cards; it++) | |
777 { | |
778 devices=0; | |
779 do { | |
780 status=snd_card_get_longname(it, qsa_capture_device[qsa_capture_devices].name, QSA_MAX_NAME_LENGTH); | |
781 if (status==EOK) | |
782 { | |
783 snd_pcm_t* handle; | |
784 | |
785 /* Add device number to device name */ | |
786 sprintf(qsa_capture_device[qsa_capture_devices].name + SDL_strlen(qsa_capture_device[qsa_capture_devices].name), " d%d", devices); | |
787 | |
788 /* Store associated card number id */ | |
789 qsa_capture_device[qsa_capture_devices].cardno=it; | |
790 | |
791 /* Check if this device id could play anything */ | |
792 status=snd_pcm_open(&handle, it, devices, SND_PCM_OPEN_CAPTURE); | |
793 if (status==EOK) | |
794 { | |
795 qsa_capture_device[qsa_capture_devices].deviceno=devices; | |
796 status=snd_pcm_close(handle); | |
797 if (status==EOK) | |
798 { | |
799 qsa_capture_devices++; | |
800 } | |
801 } | |
802 else | |
803 { | |
804 /* Check if we got end of devices list */ | |
805 if (status==-ENOENT) | |
806 { | |
807 break; | |
808 } | |
809 } | |
810 | |
811 /* Check if we reached maximum devices count */ | |
812 if (qsa_capture_devices>=QSA_MAX_DEVICES) | |
813 { | |
814 break; | |
815 } | |
816 } | |
817 else | |
818 { | |
819 break; | |
820 } | |
821 devices++; | |
822 } while(1); | |
823 | |
824 /* Check if we reached maximum devices count */ | |
825 if (qsa_capture_devices>=QSA_MAX_DEVICES) | |
826 { | |
827 break; | |
828 } | |
829 } | |
830 } | |
831 | |
832 /* Return amount of available playback or capture devices */ | |
833 if (!iscapture) | |
834 { | |
835 return qsa_playback_devices; | |
836 } | |
837 else | |
838 { | |
839 return qsa_capture_devices; | |
840 } | |
841 } | |
842 | |
843 const char* QSA_GetDeviceName(int index, int iscapture) | |
844 { | |
845 if (!iscapture) | |
846 { | |
847 if (index>=qsa_playback_devices) | |
848 { | |
849 return "No such playback device"; | |
850 } | |
851 | |
852 return qsa_playback_device[index].name; | |
853 } | |
854 else | |
855 { | |
856 if (index>=qsa_capture_devices) | |
857 { | |
858 return "No such capture device"; | |
859 } | |
860 | |
861 return qsa_capture_device[index].name; | |
862 } | |
863 } | |
864 | |
865 void QSA_WaitDone(_THIS) | |
866 { | |
867 if (!this->hidden->iscapture) | |
868 { | |
869 if (this->hidden->audio_handle!=NULL) | |
870 { | |
871 /* Wait till last fragment is played and stop channel */ | |
872 snd_pcm_plugin_flush(this->hidden->audio_handle, SND_PCM_CHANNEL_PLAYBACK); | |
873 } | |
874 } | |
875 else | |
876 { | |
877 if (this->hidden->audio_handle!=NULL) | |
878 { | |
879 /* Discard all unread data and stop channel */ | |
880 snd_pcm_plugin_flush(this->hidden->audio_handle, SND_PCM_CHANNEL_CAPTURE); | |
881 } | |
882 } | |
883 } | |
884 | |
885 void QSA_Deinitialize(void) | |
886 { | |
887 /* Clear devices array on shutdown */ | |
888 SDL_memset(qsa_playback_device, 0x00, sizeof(QSA_Device)*QSA_MAX_DEVICES); | |
889 SDL_memset(qsa_capture_device, 0x00, sizeof(QSA_Device)*QSA_MAX_DEVICES); | |
890 qsa_playback_devices=0; | |
891 qsa_capture_devices=0; | |
892 } | |
893 | |
894 static int QSA_Init(SDL_AudioDriverImpl* impl) | |
895 { | |
896 snd_pcm_t* handle=NULL; | |
897 int32_t status=0; | |
898 | |
899 /* Clear devices array */ | |
900 SDL_memset(qsa_playback_device, 0x00, sizeof(QSA_Device)*QSA_MAX_DEVICES); | |
901 SDL_memset(qsa_capture_device, 0x00, sizeof(QSA_Device)*QSA_MAX_DEVICES); | |
902 qsa_playback_devices=0; | |
903 qsa_capture_devices=0; | |
904 | |
905 /* Set function pointers */ | |
906 /* DeviceLock and DeviceUnlock functions are used default, */ | |
907 /* provided by SDL, which uses pthread_mutex for lock/unlock */ | |
908 impl->DetectDevices=QSA_DetectDevices; | |
909 impl->GetDeviceName=QSA_GetDeviceName; | |
910 impl->OpenDevice=QSA_OpenDevice; | |
911 impl->ThreadInit=QSA_ThreadInit; | |
912 impl->WaitDevice=QSA_WaitDevice; | |
913 impl->PlayDevice=QSA_PlayDevice; | |
914 impl->GetDeviceBuf=QSA_GetDeviceBuf; | |
915 impl->CloseDevice=QSA_CloseDevice; | |
916 impl->WaitDone=QSA_WaitDone; | |
917 impl->Deinitialize=QSA_Deinitialize; | |
918 impl->LockDevice=NULL; | |
919 impl->UnlockDevice=NULL; | |
920 | |
921 impl->OnlyHasDefaultOutputDevice=0; | |
922 impl->ProvidesOwnCallbackThread=0; | |
923 impl->SkipMixerLock=0; | |
924 impl->HasCaptureSupport=1; | |
925 impl->OnlyHasDefaultOutputDevice=0; | |
926 impl->OnlyHasDefaultInputDevice=0; | |
927 | |
928 /* Check if io-audio manager is running or not */ | |
929 status=snd_cards(); | |
930 if (status==0) | |
931 { | |
932 /* if no, return immediately */ | |
933 return 1; | |
934 } | |
935 | |
936 /* At this point we are definitely has an audio device */ | |
937 return 2; | |
938 } | |
939 | |
940 AudioBootStrap QSAAUDIO_bootstrap= | |
941 { | |
942 DRIVER_NAME, "QNX QSA Audio", QSA_Init, 0 | |
943 }; | |
944 | |
945 /* vi: set ts=4 sw=4 expandtab: */ |