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, &param);
130 param.sched_priority=param.sched_curpriority + 15;
131 status=SchedSet(0, 0, SCHED_NOCHANGE, &param);
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: */