Remove the GetLatency method from the old BackendFuncs
[openal-soft.git] / Alc / backends / qsa.c
blobaca2d73c86a0987e0214e4ac33ae7567c161376d
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2011-2013 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <sched.h>
26 #include <errno.h>
27 #include <memory.h>
28 #include <sys/select.h>
29 #include <sys/asoundlib.h>
30 #include <sys/neutrino.h>
32 #include "alMain.h"
33 #include "alu.h"
34 #include "threads.h"
37 typedef struct {
38 snd_pcm_t* pcmHandle;
39 int audio_fd;
41 snd_pcm_channel_setup_t csetup;
42 snd_pcm_channel_params_t cparams;
44 ALvoid* buffer;
45 ALsizei size;
47 volatile int killNow;
48 althrd_t thread;
49 } qsa_data;
51 typedef struct {
52 ALCchar* name;
53 int card;
54 int dev;
55 } DevMap;
57 static const ALCchar qsaDevice[] = "QSA Default";
58 static DevMap* allDevNameMap;
59 static ALuint numDevNames;
60 static DevMap* allCaptureDevNameMap;
61 static ALuint numCaptureDevNames;
63 static const struct {
64 int32_t format;
65 } formatlist[] = {
66 {SND_PCM_SFMT_FLOAT_LE},
67 {SND_PCM_SFMT_S32_LE},
68 {SND_PCM_SFMT_U32_LE},
69 {SND_PCM_SFMT_S16_LE},
70 {SND_PCM_SFMT_U16_LE},
71 {SND_PCM_SFMT_S8},
72 {SND_PCM_SFMT_U8},
73 {0},
76 static const struct {
77 int32_t rate;
78 } ratelist[] = {
79 {192000},
80 {176400},
81 {96000},
82 {88200},
83 {48000},
84 {44100},
85 {32000},
86 {24000},
87 {22050},
88 {16000},
89 {12000},
90 {11025},
91 {8000},
92 {0},
95 static const struct {
96 int32_t channels;
97 } channellist[] = {
98 {8},
99 {7},
100 {6},
101 {4},
102 {2},
103 {1},
104 {0},
107 static DevMap *deviceList(int type, ALuint *count)
109 snd_ctl_t* handle;
110 snd_pcm_info_t pcminfo;
111 int max_cards, card, err, dev, num_devices, idx;
112 DevMap* dev_list;
113 char name[1024];
114 struct snd_ctl_hw_info info;
115 void* temp;
117 idx=0;
118 num_devices=0;
119 max_cards=snd_cards();
121 if (max_cards<=0)
123 return 0;
126 dev_list=malloc(sizeof(DevMap)*1);
127 dev_list[0].name=strdup(qsaDevice);
128 num_devices=1;
130 for (card=0; card<max_cards; card++)
132 if ((err=snd_ctl_open(&handle, card))<0)
134 continue;
136 if ((err=snd_ctl_hw_info(handle, &info))<0)
138 snd_ctl_close(handle);
139 continue;
142 for (dev=0; dev<(int)info.pcmdevs; dev++)
144 if ((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
146 continue;
149 if ((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
150 (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
152 temp=realloc(dev_list, sizeof(DevMap)*(num_devices+1));
153 if (temp)
155 dev_list=temp;
156 snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
157 dev_list[num_devices].name=strdup(name);
158 dev_list[num_devices].card=card;
159 dev_list[num_devices].dev=dev;
160 num_devices++;
164 snd_ctl_close (handle);
167 *count=num_devices;
169 return dev_list;
173 FORCE_ALIGN static int qsa_proc_playback(void* ptr)
175 ALCdevice* device=(ALCdevice*)ptr;
176 qsa_data* data=(qsa_data*)device->ExtraData;
177 char* write_ptr;
178 int avail;
179 snd_pcm_channel_status_t status;
180 struct sched_param param;
181 fd_set wfds;
182 int selectret;
183 struct timeval timeout;
185 SetRTPriority();
186 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
188 /* Increase default 10 priority to 11 to avoid jerky sound */
189 SchedGet(0, 0, &param);
190 param.sched_priority=param.sched_curpriority+1;
191 SchedSet(0, 0, SCHED_NOCHANGE, &param);
193 ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
195 while (!data->killNow)
197 ALint len=data->size;
198 write_ptr=data->buffer;
200 avail=len/frame_size;
201 aluMixData(device, write_ptr, avail);
203 while (len>0 && !data->killNow)
205 FD_ZERO(&wfds);
206 FD_SET(data->audio_fd, &wfds);
207 timeout.tv_sec=2;
208 timeout.tv_usec=0;
210 /* Select also works like time slice to OS */
211 selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
212 switch (selectret)
214 case -1:
215 aluHandleDisconnect(device);
216 return 1;
217 case 0:
218 break;
219 default:
220 if (FD_ISSET(data->audio_fd, &wfds))
222 break;
224 break;
227 int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
229 if (wrote<=0)
231 if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
233 continue;
236 memset(&status, 0, sizeof (status));
237 status.channel=SND_PCM_CHANNEL_PLAYBACK;
239 snd_pcm_plugin_status(data->pcmHandle, &status);
241 /* we need to reinitialize the sound channel if we've underrun the buffer */
242 if ((status.status==SND_PCM_STATUS_UNDERRUN) ||
243 (status.status==SND_PCM_STATUS_READY))
245 if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
247 aluHandleDisconnect(device);
248 break;
252 else
254 write_ptr+=wrote;
255 len-=wrote;
260 return 0;
263 /************/
264 /* Playback */
265 /************/
267 static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
269 qsa_data* data;
270 char driver[64];
271 int status;
272 int card, dev;
274 strncpy(driver, GetConfigValue("qsa", "device", qsaDevice), sizeof(driver)-1);
275 driver[sizeof(driver)-1]=0;
277 data=(qsa_data*)calloc(1, sizeof(qsa_data));
278 if (data==NULL)
280 return ALC_OUT_OF_MEMORY;
283 if (!deviceName)
285 deviceName=driver;
288 if (strcmp(deviceName, qsaDevice)==0)
290 if (!deviceName)
292 deviceName=qsaDevice;
295 status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
297 else
299 size_t idx;
301 if (!allDevNameMap)
303 allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames);
306 for (idx=0; idx<numDevNames; idx++)
308 if (allDevNameMap[idx].name && strcmp(deviceName, allDevNameMap[idx].name)==0)
310 if (idx>0)
312 break;
316 if (idx==numDevNames)
318 free(data);
319 return ALC_INVALID_DEVICE;
322 status=snd_pcm_open(&data->pcmHandle, allDevNameMap[idx].card, allDevNameMap[idx].dev, SND_PCM_OPEN_PLAYBACK);
325 if (status<0)
327 free(data);
328 return ALC_INVALID_DEVICE;
331 data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
332 if (data->audio_fd<0)
334 free(data);
335 return ALC_INVALID_DEVICE;
338 al_string_copy_cstr(&device->DeviceName, deviceName);
339 device->ExtraData = data;
341 return ALC_NO_ERROR;
344 static void qsa_close_playback(ALCdevice* device)
346 qsa_data* data=(qsa_data*)device->ExtraData;
348 if (data->buffer!=NULL)
350 free(data->buffer);
351 data->buffer=NULL;
354 snd_pcm_close(data->pcmHandle);
355 free(data);
357 device->ExtraData=NULL;
360 static ALCboolean qsa_reset_playback(ALCdevice* device)
362 qsa_data* data=(qsa_data*)device->ExtraData;
363 int32_t format=-1;
365 switch(device->FmtType)
367 case DevFmtByte:
368 format=SND_PCM_SFMT_S8;
369 break;
370 case DevFmtUByte:
371 format=SND_PCM_SFMT_U8;
372 break;
373 case DevFmtShort:
374 format=SND_PCM_SFMT_S16_LE;
375 break;
376 case DevFmtUShort:
377 format=SND_PCM_SFMT_U16_LE;
378 break;
379 case DevFmtInt:
380 format=SND_PCM_SFMT_S32_LE;
381 break;
382 case DevFmtUInt:
383 format=SND_PCM_SFMT_U32_LE;
384 break;
385 case DevFmtFloat:
386 format=SND_PCM_SFMT_FLOAT_LE;
387 break;
390 /* we actually don't want to block on writes */
391 snd_pcm_nonblock_mode(data->pcmHandle, 1);
392 /* Disable mmap to control data transfer to the audio device */
393 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
394 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS);
396 // configure a sound channel
397 memset(&data->cparams, 0, sizeof(data->cparams));
398 data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK;
399 data->cparams.mode=SND_PCM_MODE_BLOCK;
400 data->cparams.start_mode=SND_PCM_START_FULL;
401 data->cparams.stop_mode=SND_PCM_STOP_STOP;
403 data->cparams.buf.block.frag_size=device->UpdateSize*
404 ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
405 data->cparams.buf.block.frags_max=device->NumUpdates;
406 data->cparams.buf.block.frags_min=device->NumUpdates;
408 data->cparams.format.interleave=1;
409 data->cparams.format.rate=device->Frequency;
410 data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
411 data->cparams.format.format=format;
413 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
415 int original_rate=data->cparams.format.rate;
416 int original_voices=data->cparams.format.voices;
417 int original_format=data->cparams.format.format;
418 int it;
419 int jt;
421 for (it=0; it<1; it++)
423 /* Check for second pass */
424 if (it==1)
426 original_rate=ratelist[0].rate;
427 original_voices=channellist[0].channels;
428 original_format=formatlist[0].format;
431 do {
432 /* At first downgrade sample format */
433 jt=0;
434 do {
435 if (formatlist[jt].format==data->cparams.format.format)
437 data->cparams.format.format=formatlist[jt+1].format;
438 break;
440 if (formatlist[jt].format==0)
442 data->cparams.format.format=0;
443 break;
445 jt++;
446 } while(1);
448 if (data->cparams.format.format==0)
450 data->cparams.format.format=original_format;
452 /* At secod downgrade sample rate */
453 jt=0;
454 do {
455 if (ratelist[jt].rate==data->cparams.format.rate)
457 data->cparams.format.rate=ratelist[jt+1].rate;
458 break;
460 if (ratelist[jt].rate==0)
462 data->cparams.format.rate=0;
463 break;
465 jt++;
466 } while(1);
468 if (data->cparams.format.rate==0)
470 data->cparams.format.rate=original_rate;
471 data->cparams.format.format=original_format;
473 /* At third downgrade channels number */
474 jt=0;
475 do {
476 if(channellist[jt].channels==data->cparams.format.voices)
478 data->cparams.format.voices=channellist[jt+1].channels;
479 break;
481 if (channellist[jt].channels==0)
483 data->cparams.format.voices=0;
484 break;
486 jt++;
487 } while(1);
490 if (data->cparams.format.voices==0)
492 break;
496 data->cparams.buf.block.frag_size=device->UpdateSize*
497 data->cparams.format.voices*
498 snd_pcm_format_width(data->cparams.format.format)/8;
499 data->cparams.buf.block.frags_max=device->NumUpdates;
500 data->cparams.buf.block.frags_min=device->NumUpdates;
501 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
503 continue;
505 else
507 break;
509 } while(1);
511 if (data->cparams.format.voices!=0)
513 break;
517 if (data->cparams.format.voices==0)
519 return ALC_FALSE;
523 if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
525 return ALC_FALSE;
528 memset(&data->csetup, 0, sizeof(data->csetup));
529 data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK;
530 if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0)
532 return ALC_FALSE;
535 /* now fill back to the our AL device */
536 device->Frequency=data->cparams.format.rate;
538 switch (data->cparams.format.voices)
540 case 1:
541 device->FmtChans=DevFmtMono;
542 break;
543 case 2:
544 device->FmtChans=DevFmtStereo;
545 break;
546 case 4:
547 device->FmtChans=DevFmtQuad;
548 break;
549 case 6:
550 device->FmtChans=DevFmtX51;
551 break;
552 case 7:
553 device->FmtChans=DevFmtX61;
554 break;
555 case 8:
556 device->FmtChans=DevFmtX71;
557 break;
558 default:
559 device->FmtChans=DevFmtMono;
560 break;
563 switch (data->cparams.format.format)
565 case SND_PCM_SFMT_S8:
566 device->FmtType=DevFmtByte;
567 break;
568 case SND_PCM_SFMT_U8:
569 device->FmtType=DevFmtUByte;
570 break;
571 case SND_PCM_SFMT_S16_LE:
572 device->FmtType=DevFmtShort;
573 break;
574 case SND_PCM_SFMT_U16_LE:
575 device->FmtType=DevFmtUShort;
576 break;
577 case SND_PCM_SFMT_S32_LE:
578 device->FmtType=DevFmtInt;
579 break;
580 case SND_PCM_SFMT_U32_LE:
581 device->FmtType=DevFmtUInt;
582 break;
583 case SND_PCM_SFMT_FLOAT_LE:
584 device->FmtType=DevFmtFloat;
585 break;
586 default:
587 device->FmtType=DevFmtShort;
588 break;
591 SetDefaultChannelOrder(device);
593 device->UpdateSize=data->csetup.buf.block.frag_size/
594 (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
595 device->NumUpdates=data->csetup.buf.block.frags;
597 data->size=data->csetup.buf.block.frag_size;
598 data->buffer=malloc(data->size);
599 if (!data->buffer)
601 return ALC_FALSE;
604 return ALC_TRUE;
607 static ALCboolean qsa_start_playback(ALCdevice* device)
609 qsa_data *data = (qsa_data*)device->ExtraData;
611 data->killNow = 0;
612 if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success)
613 return ALC_FALSE;
615 return ALC_TRUE;
618 static void qsa_stop_playback(ALCdevice* device)
620 qsa_data *data = (qsa_data*)device->ExtraData;
621 int res;
623 if(data->killNow)
624 return;
626 data->killNow = 1;
627 althrd_join(data->thread, &res);
630 /***********/
631 /* Capture */
632 /***********/
634 static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
636 qsa_data* data;
637 int format=-1;
638 char driver[64];
639 int card, dev;
640 int status;
642 strncpy(driver, GetConfigValue("qsa", "capture", qsaDevice), sizeof(driver)-1);
643 driver[sizeof(driver)-1]=0;
645 data=(qsa_data*)calloc(1, sizeof(qsa_data));
646 if (data==NULL)
648 return ALC_OUT_OF_MEMORY;
651 if (!deviceName)
653 deviceName=driver;
656 if (strcmp(deviceName, qsaDevice)==0)
658 if (!deviceName)
660 deviceName=qsaDevice;
663 status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
665 else
667 size_t idx;
669 if (!allCaptureDevNameMap)
671 allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numDevNames);
674 for (idx=0; idx<numDevNames; idx++)
676 if (allCaptureDevNameMap[idx].name && strcmp(deviceName, allCaptureDevNameMap[idx].name)==0)
678 if (idx>0)
680 break;
684 if (idx==numDevNames)
686 free(data);
687 return ALC_INVALID_DEVICE;
690 status=snd_pcm_open(&data->pcmHandle, allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev, SND_PCM_OPEN_CAPTURE);
693 if (status<0)
695 free(data);
696 return ALC_INVALID_DEVICE;
699 data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
700 if (data->audio_fd<0)
702 free(data);
703 return ALC_INVALID_DEVICE;
706 al_string_copy_cstr(&device->DeviceName, deviceName);
707 device->ExtraData = data;
709 switch (device->FmtType)
711 case DevFmtByte:
712 format=SND_PCM_SFMT_S8;
713 break;
714 case DevFmtUByte:
715 format=SND_PCM_SFMT_U8;
716 break;
717 case DevFmtShort:
718 format=SND_PCM_SFMT_S16_LE;
719 break;
720 case DevFmtUShort:
721 format=SND_PCM_SFMT_U16_LE;
722 break;
723 case DevFmtInt:
724 format=SND_PCM_SFMT_S32_LE;
725 break;
726 case DevFmtUInt:
727 format=SND_PCM_SFMT_U32_LE;
728 break;
729 case DevFmtFloat:
730 format=SND_PCM_SFMT_FLOAT_LE;
731 break;
734 /* we actually don't want to block on reads */
735 snd_pcm_nonblock_mode(data->pcmHandle, 1);
736 /* Disable mmap to control data transfer to the audio device */
737 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
739 /* configure a sound channel */
740 memset(&data->cparams, 0, sizeof(data->cparams));
741 data->cparams.mode=SND_PCM_MODE_BLOCK;
742 data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
743 data->cparams.start_mode=SND_PCM_START_GO;
744 data->cparams.stop_mode=SND_PCM_STOP_STOP;
746 data->cparams.buf.block.frag_size=device->UpdateSize*
747 ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
748 data->cparams.buf.block.frags_max=device->NumUpdates;
749 data->cparams.buf.block.frags_min=device->NumUpdates;
751 data->cparams.format.interleave=1;
752 data->cparams.format.rate=device->Frequency;
753 data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
754 data->cparams.format.format=format;
756 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
758 int original_rate=data->cparams.format.rate;
759 int original_voices=data->cparams.format.voices;
760 int original_format=data->cparams.format.format;
761 int it;
762 int jt;
764 for (it=0; it<1; it++)
766 /* Check for second pass */
767 if (it==1)
769 original_rate=ratelist[0].rate;
770 original_voices=channellist[0].channels;
771 original_format=formatlist[0].format;
774 do {
775 /* At first downgrade sample format */
776 jt=0;
777 do {
778 if (formatlist[jt].format==data->cparams.format.format)
780 data->cparams.format.format=formatlist[jt+1].format;
781 break;
783 if (formatlist[jt].format==0)
785 data->cparams.format.format=0;
786 break;
788 jt++;
789 } while(1);
791 if (data->cparams.format.format==0)
793 data->cparams.format.format=original_format;
795 /* At secod downgrade sample rate */
796 jt=0;
797 do {
798 if (ratelist[jt].rate==data->cparams.format.rate)
800 data->cparams.format.rate=ratelist[jt+1].rate;
801 break;
803 if (ratelist[jt].rate==0)
805 data->cparams.format.rate=0;
806 break;
808 jt++;
809 } while(1);
811 if (data->cparams.format.rate==0)
813 data->cparams.format.rate=original_rate;
814 data->cparams.format.format=original_format;
816 /* At third downgrade channels number */
817 jt=0;
818 do {
819 if(channellist[jt].channels==data->cparams.format.voices)
821 data->cparams.format.voices=channellist[jt+1].channels;
822 break;
824 if (channellist[jt].channels==0)
826 data->cparams.format.voices=0;
827 break;
829 jt++;
830 } while(1);
833 if (data->cparams.format.voices==0)
835 break;
839 data->cparams.buf.block.frag_size=device->UpdateSize*
840 data->cparams.format.voices*
841 snd_pcm_format_width(data->cparams.format.format)/8;
842 data->cparams.buf.block.frags_max=device->NumUpdates;
843 data->cparams.buf.block.frags_min=device->NumUpdates;
844 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
846 continue;
848 else
850 break;
852 } while(1);
854 if (data->cparams.format.voices!=0)
856 break;
860 if (data->cparams.format.voices==0)
862 return ALC_INVALID_VALUE;
866 /* now fill back to the our AL device */
867 device->Frequency=data->cparams.format.rate;
869 switch (data->cparams.format.voices)
871 case 1:
872 device->FmtChans=DevFmtMono;
873 break;
874 case 2:
875 device->FmtChans=DevFmtStereo;
876 break;
877 case 4:
878 device->FmtChans=DevFmtQuad;
879 break;
880 case 6:
881 device->FmtChans=DevFmtX51;
882 break;
883 case 7:
884 device->FmtChans=DevFmtX61;
885 break;
886 case 8:
887 device->FmtChans=DevFmtX71;
888 break;
889 default:
890 device->FmtChans=DevFmtMono;
891 break;
894 switch (data->cparams.format.format)
896 case SND_PCM_SFMT_S8:
897 device->FmtType=DevFmtByte;
898 break;
899 case SND_PCM_SFMT_U8:
900 device->FmtType=DevFmtUByte;
901 break;
902 case SND_PCM_SFMT_S16_LE:
903 device->FmtType=DevFmtShort;
904 break;
905 case SND_PCM_SFMT_U16_LE:
906 device->FmtType=DevFmtUShort;
907 break;
908 case SND_PCM_SFMT_S32_LE:
909 device->FmtType=DevFmtInt;
910 break;
911 case SND_PCM_SFMT_U32_LE:
912 device->FmtType=DevFmtUInt;
913 break;
914 case SND_PCM_SFMT_FLOAT_LE:
915 device->FmtType=DevFmtFloat;
916 break;
917 default:
918 device->FmtType=DevFmtShort;
919 break;
922 return ALC_NO_ERROR;
925 static void qsa_close_capture(ALCdevice* device)
927 qsa_data* data=(qsa_data*)device->ExtraData;
929 if (data->pcmHandle!=NULL)
931 snd_pcm_close(data->pcmHandle);
933 free(data);
934 device->ExtraData=NULL;
937 static void qsa_start_capture(ALCdevice* device)
939 qsa_data* data=(qsa_data*)device->ExtraData;
940 int rstatus;
942 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
944 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
945 return;
948 memset(&data->csetup, 0, sizeof(data->csetup));
949 data->csetup.channel=SND_PCM_CHANNEL_CAPTURE;
950 if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0)
952 ERR("capture setup failed: %s\n", snd_strerror(rstatus));
953 return;
956 snd_pcm_capture_go(data->pcmHandle);
958 device->UpdateSize=data->csetup.buf.block.frag_size/
959 (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
960 device->NumUpdates=data->csetup.buf.block.frags;
963 static void qsa_stop_capture(ALCdevice* device)
965 qsa_data* data=(qsa_data*)device->ExtraData;
967 snd_pcm_capture_flush(data->pcmHandle);
970 static ALCuint qsa_available_samples(ALCdevice* device)
972 qsa_data* data=(qsa_data*)device->ExtraData;
973 snd_pcm_channel_status_t status;
974 ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
975 ALint free_size;
976 int rstatus;
978 memset(&status, 0, sizeof (status));
979 status.channel=SND_PCM_CHANNEL_CAPTURE;
980 snd_pcm_plugin_status(data->pcmHandle, &status);
981 if ((status.status==SND_PCM_STATUS_OVERRUN) ||
982 (status.status==SND_PCM_STATUS_READY))
984 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
986 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
987 aluHandleDisconnect(device);
988 return 0;
991 snd_pcm_capture_go(data->pcmHandle);
992 return 0;
995 free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags;
996 free_size-=status.free;
998 return free_size/frame_size;
1001 static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
1003 qsa_data* data=(qsa_data*)device->ExtraData;
1004 char* read_ptr;
1005 snd_pcm_channel_status_t status;
1006 fd_set rfds;
1007 int selectret;
1008 struct timeval timeout;
1009 int bytes_read;
1010 ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
1011 ALint len=samples*frame_size;
1012 int rstatus;
1014 read_ptr=buffer;
1016 while (len>0)
1018 FD_ZERO(&rfds);
1019 FD_SET(data->audio_fd, &rfds);
1020 timeout.tv_sec=2;
1021 timeout.tv_usec=0;
1023 /* Select also works like time slice to OS */
1024 bytes_read=0;
1025 selectret=select(data->audio_fd+1, &rfds, NULL, NULL, &timeout);
1026 switch (selectret)
1028 case -1:
1029 aluHandleDisconnect(device);
1030 return ALC_INVALID_DEVICE;
1031 case 0:
1032 break;
1033 default:
1034 if (FD_ISSET(data->audio_fd, &rfds))
1036 bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len);
1037 break;
1039 break;
1042 if (bytes_read<=0)
1044 if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
1046 continue;
1049 memset(&status, 0, sizeof (status));
1050 status.channel=SND_PCM_CHANNEL_CAPTURE;
1051 snd_pcm_plugin_status(data->pcmHandle, &status);
1053 /* we need to reinitialize the sound channel if we've overrun the buffer */
1054 if ((status.status==SND_PCM_STATUS_OVERRUN) ||
1055 (status.status==SND_PCM_STATUS_READY))
1057 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
1059 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
1060 aluHandleDisconnect(device);
1061 return ALC_INVALID_DEVICE;
1063 snd_pcm_capture_go(data->pcmHandle);
1066 else
1068 read_ptr+=bytes_read;
1069 len-=bytes_read;
1073 return ALC_NO_ERROR;
1076 static const BackendFuncs qsa_funcs= {
1077 qsa_open_playback,
1078 qsa_close_playback,
1079 qsa_reset_playback,
1080 qsa_start_playback,
1081 qsa_stop_playback,
1082 qsa_open_capture,
1083 qsa_close_capture,
1084 qsa_start_capture,
1085 qsa_stop_capture,
1086 qsa_capture_samples,
1087 qsa_available_samples
1090 ALCboolean alc_qsa_init(BackendFuncs* func_list)
1092 *func_list=qsa_funcs;
1094 return ALC_TRUE;
1097 void alc_qsa_deinit(void)
1099 ALuint i;
1101 for (i=0; i<numDevNames; ++i)
1103 free(allDevNameMap[i].name);
1105 free(allDevNameMap);
1106 allDevNameMap=NULL;
1107 numDevNames=0;
1109 for (i=0; i<numCaptureDevNames; ++i)
1111 free(allCaptureDevNameMap[i].name);
1113 free(allCaptureDevNameMap);
1114 allCaptureDevNameMap=NULL;
1115 numCaptureDevNames=0;
1118 void alc_qsa_probe(enum DevProbe type)
1120 ALuint i;
1122 switch (type)
1124 case ALL_DEVICE_PROBE:
1125 for (i=0; i<numDevNames; ++i)
1127 free(allDevNameMap[i].name);
1129 free(allDevNameMap);
1131 allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames);
1132 for (i=0; i<numDevNames; ++i)
1134 AppendAllDevicesList(allDevNameMap[i].name);
1136 break;
1137 case CAPTURE_DEVICE_PROBE:
1138 for (i=0; i<numCaptureDevNames; ++i)
1140 free(allCaptureDevNameMap[i].name);
1142 free(allCaptureDevNameMap);
1144 allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numCaptureDevNames);
1145 for (i=0; i<numCaptureDevNames; ++i)
1147 AppendCaptureDeviceList(allCaptureDevNameMap[i].name);
1149 break;