Provide more descriptive messages to disconnection events
[openal-soft.git] / Alc / backends / qsa.c
blob484cadaaa93ccf970600762264d06ba9cac3a668
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"
36 #include "backends/base.h"
39 typedef struct {
40 snd_pcm_t* pcmHandle;
41 int audio_fd;
43 snd_pcm_channel_setup_t csetup;
44 snd_pcm_channel_params_t cparams;
46 ALvoid* buffer;
47 ALsizei size;
49 volatile int killNow;
50 althrd_t thread;
51 } qsa_data;
53 typedef struct {
54 ALCchar* name;
55 int card;
56 int dev;
57 } DevMap;
58 TYPEDEF_VECTOR(DevMap, vector_DevMap)
60 static vector_DevMap DeviceNameMap;
61 static vector_DevMap CaptureNameMap;
63 static const ALCchar qsaDevice[] = "QSA Default";
65 static const struct {
66 int32_t format;
67 } formatlist[] = {
68 {SND_PCM_SFMT_FLOAT_LE},
69 {SND_PCM_SFMT_S32_LE},
70 {SND_PCM_SFMT_U32_LE},
71 {SND_PCM_SFMT_S16_LE},
72 {SND_PCM_SFMT_U16_LE},
73 {SND_PCM_SFMT_S8},
74 {SND_PCM_SFMT_U8},
75 {0},
78 static const struct {
79 int32_t rate;
80 } ratelist[] = {
81 {192000},
82 {176400},
83 {96000},
84 {88200},
85 {48000},
86 {44100},
87 {32000},
88 {24000},
89 {22050},
90 {16000},
91 {12000},
92 {11025},
93 {8000},
94 {0},
97 static const struct {
98 int32_t channels;
99 } channellist[] = {
100 {8},
101 {7},
102 {6},
103 {4},
104 {2},
105 {1},
106 {0},
109 static void deviceList(int type, vector_DevMap *devmap)
111 snd_ctl_t* handle;
112 snd_pcm_info_t pcminfo;
113 int max_cards, card, err, dev;
114 DevMap entry;
115 char name[1024];
116 struct snd_ctl_hw_info info;
118 max_cards = snd_cards();
119 if(max_cards < 0)
120 return;
122 VECTOR_RESIZE(*devmap, 0, max_cards+1);
124 entry.name = strdup(qsaDevice);
125 entry.card = 0;
126 entry.dev = 0;
127 VECTOR_PUSH_BACK(*devmap, entry);
129 for(card = 0;card < max_cards;card++)
131 if((err=snd_ctl_open(&handle, card)) < 0)
132 continue;
134 if((err=snd_ctl_hw_info(handle, &info)) < 0)
136 snd_ctl_close(handle);
137 continue;
140 for(dev = 0;dev < (int)info.pcmdevs;dev++)
142 if((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
143 continue;
145 if((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
146 (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
148 snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
149 entry.name = strdup(name);
150 entry.card = card;
151 entry.dev = dev;
153 VECTOR_PUSH_BACK(*devmap, entry);
154 TRACE("Got device \"%s\", card %d, dev %d\n", name, card, dev);
157 snd_ctl_close(handle);
162 /* Wrappers to use an old-style backend with the new interface. */
163 typedef struct PlaybackWrapper {
164 DERIVE_FROM_TYPE(ALCbackend);
165 qsa_data *ExtraData;
166 } PlaybackWrapper;
168 static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
169 static void PlaybackWrapper_Destruct(PlaybackWrapper *self);
170 static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
171 static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
172 static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
173 static void PlaybackWrapper_stop(PlaybackWrapper *self);
174 static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
175 static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
176 static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
177 static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
178 static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
179 DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
180 DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
183 FORCE_ALIGN static int qsa_proc_playback(void *ptr)
185 PlaybackWrapper *self = ptr;
186 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
187 qsa_data *data = self->ExtraData;
188 snd_pcm_channel_status_t status;
189 struct sched_param param;
190 struct timeval timeout;
191 char* write_ptr;
192 fd_set wfds;
193 ALint len;
194 int sret;
196 SetRTPriority();
197 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
199 /* Increase default 10 priority to 11 to avoid jerky sound */
200 SchedGet(0, 0, &param);
201 param.sched_priority=param.sched_curpriority+1;
202 SchedSet(0, 0, SCHED_NOCHANGE, &param);
204 const ALint frame_size = FrameSizeFromDevFmt(
205 device->FmtChans, device->FmtType, device->AmbiOrder
208 V0(device->Backend,lock)();
209 while(!data->killNow)
211 FD_ZERO(&wfds);
212 FD_SET(data->audio_fd, &wfds);
213 timeout.tv_sec=2;
214 timeout.tv_usec=0;
216 /* Select also works like time slice to OS */
217 V0(device->Backend,unlock)();
218 sret = select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
219 V0(device->Backend,lock)();
220 if(sret == -1)
222 ERR("select error: %s\n", strerror(errno));
223 aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
224 break;
226 if(sret == 0)
228 ERR("select timeout\n");
229 continue;
232 len = data->size;
233 write_ptr = data->buffer;
234 aluMixData(device, write_ptr, len/frame_size);
235 while(len>0 && !data->killNow)
237 int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
238 if(wrote <= 0)
240 if(errno==EAGAIN || errno==EWOULDBLOCK)
241 continue;
243 memset(&status, 0, sizeof(status));
244 status.channel = SND_PCM_CHANNEL_PLAYBACK;
246 snd_pcm_plugin_status(data->pcmHandle, &status);
248 /* we need to reinitialize the sound channel if we've underrun the buffer */
249 if(status.status == SND_PCM_STATUS_UNDERRUN ||
250 status.status == SND_PCM_STATUS_READY)
252 if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
254 aluHandleDisconnect(device, "Playback recovery failed");
255 break;
259 else
261 write_ptr += wrote;
262 len -= wrote;
266 V0(device->Backend,unlock)();
268 return 0;
271 /************/
272 /* Playback */
273 /************/
275 static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
277 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
278 qsa_data *data;
279 int card, dev;
280 int status;
282 data = (qsa_data*)calloc(1, sizeof(qsa_data));
283 if(data == NULL)
284 return ALC_OUT_OF_MEMORY;
286 if(!deviceName)
287 deviceName = qsaDevice;
289 if(strcmp(deviceName, qsaDevice) == 0)
290 status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
291 else
293 const DevMap *iter;
295 if(VECTOR_SIZE(DeviceNameMap) == 0)
296 deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
298 #define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
299 VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME);
300 #undef MATCH_DEVNAME
301 if(iter == VECTOR_END(DeviceNameMap))
303 free(data);
304 return ALC_INVALID_DEVICE;
307 status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK);
310 if(status < 0)
312 free(data);
313 return ALC_INVALID_DEVICE;
316 data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
317 if(data->audio_fd < 0)
319 snd_pcm_close(data->pcmHandle);
320 free(data);
321 return ALC_INVALID_DEVICE;
324 alstr_copy_cstr(&device->DeviceName, deviceName);
325 self->ExtraData = data;
327 return ALC_NO_ERROR;
330 static void qsa_close_playback(PlaybackWrapper *self)
332 qsa_data *data = self->ExtraData;
334 if (data->buffer!=NULL)
336 free(data->buffer);
337 data->buffer=NULL;
340 snd_pcm_close(data->pcmHandle);
341 free(data);
343 self->ExtraData = NULL;
346 static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
348 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
349 qsa_data *data = self->ExtraData;
350 int32_t format=-1;
352 switch(device->FmtType)
354 case DevFmtByte:
355 format=SND_PCM_SFMT_S8;
356 break;
357 case DevFmtUByte:
358 format=SND_PCM_SFMT_U8;
359 break;
360 case DevFmtShort:
361 format=SND_PCM_SFMT_S16_LE;
362 break;
363 case DevFmtUShort:
364 format=SND_PCM_SFMT_U16_LE;
365 break;
366 case DevFmtInt:
367 format=SND_PCM_SFMT_S32_LE;
368 break;
369 case DevFmtUInt:
370 format=SND_PCM_SFMT_U32_LE;
371 break;
372 case DevFmtFloat:
373 format=SND_PCM_SFMT_FLOAT_LE;
374 break;
377 /* we actually don't want to block on writes */
378 snd_pcm_nonblock_mode(data->pcmHandle, 1);
379 /* Disable mmap to control data transfer to the audio device */
380 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
381 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS);
383 // configure a sound channel
384 memset(&data->cparams, 0, sizeof(data->cparams));
385 data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK;
386 data->cparams.mode=SND_PCM_MODE_BLOCK;
387 data->cparams.start_mode=SND_PCM_START_FULL;
388 data->cparams.stop_mode=SND_PCM_STOP_STOP;
390 data->cparams.buf.block.frag_size=device->UpdateSize *
391 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
392 data->cparams.buf.block.frags_max=device->NumUpdates;
393 data->cparams.buf.block.frags_min=device->NumUpdates;
395 data->cparams.format.interleave=1;
396 data->cparams.format.rate=device->Frequency;
397 data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
398 data->cparams.format.format=format;
400 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
402 int original_rate=data->cparams.format.rate;
403 int original_voices=data->cparams.format.voices;
404 int original_format=data->cparams.format.format;
405 int it;
406 int jt;
408 for (it=0; it<1; it++)
410 /* Check for second pass */
411 if (it==1)
413 original_rate=ratelist[0].rate;
414 original_voices=channellist[0].channels;
415 original_format=formatlist[0].format;
418 do {
419 /* At first downgrade sample format */
420 jt=0;
421 do {
422 if (formatlist[jt].format==data->cparams.format.format)
424 data->cparams.format.format=formatlist[jt+1].format;
425 break;
427 if (formatlist[jt].format==0)
429 data->cparams.format.format=0;
430 break;
432 jt++;
433 } while(1);
435 if (data->cparams.format.format==0)
437 data->cparams.format.format=original_format;
439 /* At secod downgrade sample rate */
440 jt=0;
441 do {
442 if (ratelist[jt].rate==data->cparams.format.rate)
444 data->cparams.format.rate=ratelist[jt+1].rate;
445 break;
447 if (ratelist[jt].rate==0)
449 data->cparams.format.rate=0;
450 break;
452 jt++;
453 } while(1);
455 if (data->cparams.format.rate==0)
457 data->cparams.format.rate=original_rate;
458 data->cparams.format.format=original_format;
460 /* At third downgrade channels number */
461 jt=0;
462 do {
463 if(channellist[jt].channels==data->cparams.format.voices)
465 data->cparams.format.voices=channellist[jt+1].channels;
466 break;
468 if (channellist[jt].channels==0)
470 data->cparams.format.voices=0;
471 break;
473 jt++;
474 } while(1);
477 if (data->cparams.format.voices==0)
479 break;
483 data->cparams.buf.block.frag_size=device->UpdateSize*
484 data->cparams.format.voices*
485 snd_pcm_format_width(data->cparams.format.format)/8;
486 data->cparams.buf.block.frags_max=device->NumUpdates;
487 data->cparams.buf.block.frags_min=device->NumUpdates;
488 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
490 continue;
492 else
494 break;
496 } while(1);
498 if (data->cparams.format.voices!=0)
500 break;
504 if (data->cparams.format.voices==0)
506 return ALC_FALSE;
510 if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
512 return ALC_FALSE;
515 memset(&data->csetup, 0, sizeof(data->csetup));
516 data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK;
517 if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0)
519 return ALC_FALSE;
522 /* now fill back to the our AL device */
523 device->Frequency=data->cparams.format.rate;
525 switch (data->cparams.format.voices)
527 case 1:
528 device->FmtChans=DevFmtMono;
529 break;
530 case 2:
531 device->FmtChans=DevFmtStereo;
532 break;
533 case 4:
534 device->FmtChans=DevFmtQuad;
535 break;
536 case 6:
537 device->FmtChans=DevFmtX51;
538 break;
539 case 7:
540 device->FmtChans=DevFmtX61;
541 break;
542 case 8:
543 device->FmtChans=DevFmtX71;
544 break;
545 default:
546 device->FmtChans=DevFmtMono;
547 break;
550 switch (data->cparams.format.format)
552 case SND_PCM_SFMT_S8:
553 device->FmtType=DevFmtByte;
554 break;
555 case SND_PCM_SFMT_U8:
556 device->FmtType=DevFmtUByte;
557 break;
558 case SND_PCM_SFMT_S16_LE:
559 device->FmtType=DevFmtShort;
560 break;
561 case SND_PCM_SFMT_U16_LE:
562 device->FmtType=DevFmtUShort;
563 break;
564 case SND_PCM_SFMT_S32_LE:
565 device->FmtType=DevFmtInt;
566 break;
567 case SND_PCM_SFMT_U32_LE:
568 device->FmtType=DevFmtUInt;
569 break;
570 case SND_PCM_SFMT_FLOAT_LE:
571 device->FmtType=DevFmtFloat;
572 break;
573 default:
574 device->FmtType=DevFmtShort;
575 break;
578 SetDefaultChannelOrder(device);
580 device->UpdateSize=data->csetup.buf.block.frag_size/
581 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
582 device->NumUpdates=data->csetup.buf.block.frags;
584 data->size=data->csetup.buf.block.frag_size;
585 data->buffer=malloc(data->size);
586 if (!data->buffer)
588 return ALC_FALSE;
591 return ALC_TRUE;
594 static ALCboolean qsa_start_playback(PlaybackWrapper *self)
596 qsa_data *data = self->ExtraData;
598 data->killNow = 0;
599 if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success)
600 return ALC_FALSE;
602 return ALC_TRUE;
605 static void qsa_stop_playback(PlaybackWrapper *self)
607 qsa_data *data = self->ExtraData;
608 int res;
610 if(data->killNow)
611 return;
613 data->killNow = 1;
614 althrd_join(data->thread, &res);
618 static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
620 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
621 SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
623 self->ExtraData = NULL;
626 static void PlaybackWrapper_Destruct(PlaybackWrapper *self)
628 if(self->ExtraData)
629 qsa_close_playback(self);
631 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
634 static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
636 return qsa_open_playback(self, name);
639 static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
641 return qsa_reset_playback(self);
644 static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
646 return qsa_start_playback(self);
649 static void PlaybackWrapper_stop(PlaybackWrapper *self)
651 qsa_stop_playback(self);
656 /***********/
657 /* Capture */
658 /***********/
660 typedef struct CaptureWrapper {
661 DERIVE_FROM_TYPE(ALCbackend);
662 qsa_data *ExtraData;
663 } CaptureWrapper;
665 static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
666 static void CaptureWrapper_Destruct(CaptureWrapper *self);
667 static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
668 static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
669 static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
670 static void CaptureWrapper_stop(CaptureWrapper *self);
671 static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
672 static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
673 static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
674 static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
675 static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
676 DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
677 DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
680 static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
682 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
683 qsa_data *data;
684 int card, dev;
685 int format=-1;
686 int status;
688 data=(qsa_data*)calloc(1, sizeof(qsa_data));
689 if (data==NULL)
691 return ALC_OUT_OF_MEMORY;
694 if(!deviceName)
695 deviceName = qsaDevice;
697 if(strcmp(deviceName, qsaDevice) == 0)
698 status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
699 else
701 const DevMap *iter;
703 if(VECTOR_SIZE(CaptureNameMap) == 0)
704 deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
706 #define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
707 VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME);
708 #undef MATCH_DEVNAME
709 if(iter == VECTOR_END(CaptureNameMap))
711 free(data);
712 return ALC_INVALID_DEVICE;
715 status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE);
718 if(status < 0)
720 free(data);
721 return ALC_INVALID_DEVICE;
724 data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
725 if(data->audio_fd < 0)
727 snd_pcm_close(data->pcmHandle);
728 free(data);
729 return ALC_INVALID_DEVICE;
732 alstr_copy_cstr(&device->DeviceName, deviceName);
733 self->ExtraData = data;
735 switch (device->FmtType)
737 case DevFmtByte:
738 format=SND_PCM_SFMT_S8;
739 break;
740 case DevFmtUByte:
741 format=SND_PCM_SFMT_U8;
742 break;
743 case DevFmtShort:
744 format=SND_PCM_SFMT_S16_LE;
745 break;
746 case DevFmtUShort:
747 format=SND_PCM_SFMT_U16_LE;
748 break;
749 case DevFmtInt:
750 format=SND_PCM_SFMT_S32_LE;
751 break;
752 case DevFmtUInt:
753 format=SND_PCM_SFMT_U32_LE;
754 break;
755 case DevFmtFloat:
756 format=SND_PCM_SFMT_FLOAT_LE;
757 break;
760 /* we actually don't want to block on reads */
761 snd_pcm_nonblock_mode(data->pcmHandle, 1);
762 /* Disable mmap to control data transfer to the audio device */
763 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
765 /* configure a sound channel */
766 memset(&data->cparams, 0, sizeof(data->cparams));
767 data->cparams.mode=SND_PCM_MODE_BLOCK;
768 data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
769 data->cparams.start_mode=SND_PCM_START_GO;
770 data->cparams.stop_mode=SND_PCM_STOP_STOP;
772 data->cparams.buf.block.frag_size=device->UpdateSize*
773 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
774 data->cparams.buf.block.frags_max=device->NumUpdates;
775 data->cparams.buf.block.frags_min=device->NumUpdates;
777 data->cparams.format.interleave=1;
778 data->cparams.format.rate=device->Frequency;
779 data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
780 data->cparams.format.format=format;
782 if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
784 snd_pcm_close(data->pcmHandle);
785 free(data);
787 return ALC_INVALID_VALUE;
790 return ALC_NO_ERROR;
793 static void qsa_close_capture(CaptureWrapper *self)
795 qsa_data *data = self->ExtraData;
797 if (data->pcmHandle!=NULL)
798 snd_pcm_close(data->pcmHandle);
800 free(data);
801 self->ExtraData = NULL;
804 static void qsa_start_capture(CaptureWrapper *self)
806 qsa_data *data = self->ExtraData;
807 int rstatus;
809 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
811 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
812 return;
815 memset(&data->csetup, 0, sizeof(data->csetup));
816 data->csetup.channel=SND_PCM_CHANNEL_CAPTURE;
817 if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0)
819 ERR("capture setup failed: %s\n", snd_strerror(rstatus));
820 return;
823 snd_pcm_capture_go(data->pcmHandle);
826 static void qsa_stop_capture(CaptureWrapper *self)
828 qsa_data *data = self->ExtraData;
829 snd_pcm_capture_flush(data->pcmHandle);
832 static ALCuint qsa_available_samples(CaptureWrapper *self)
834 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
835 qsa_data *data = self->ExtraData;
836 snd_pcm_channel_status_t status;
837 ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
838 ALint free_size;
839 int rstatus;
841 memset(&status, 0, sizeof (status));
842 status.channel=SND_PCM_CHANNEL_CAPTURE;
843 snd_pcm_plugin_status(data->pcmHandle, &status);
844 if ((status.status==SND_PCM_STATUS_OVERRUN) ||
845 (status.status==SND_PCM_STATUS_READY))
847 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
849 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
850 aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus));
851 return 0;
854 snd_pcm_capture_go(data->pcmHandle);
855 return 0;
858 free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags;
859 free_size-=status.free;
861 return free_size/frame_size;
864 static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
866 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
867 qsa_data *data = self->ExtraData;
868 char* read_ptr;
869 snd_pcm_channel_status_t status;
870 fd_set rfds;
871 int selectret;
872 struct timeval timeout;
873 int bytes_read;
874 ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
875 ALint len=samples*frame_size;
876 int rstatus;
878 read_ptr=buffer;
880 while (len>0)
882 FD_ZERO(&rfds);
883 FD_SET(data->audio_fd, &rfds);
884 timeout.tv_sec=2;
885 timeout.tv_usec=0;
887 /* Select also works like time slice to OS */
888 bytes_read=0;
889 selectret=select(data->audio_fd+1, &rfds, NULL, NULL, &timeout);
890 switch (selectret)
892 case -1:
893 aluHandleDisconnect(device, "Failed to check capture samples");
894 return ALC_INVALID_DEVICE;
895 case 0:
896 break;
897 default:
898 if (FD_ISSET(data->audio_fd, &rfds))
900 bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len);
901 break;
903 break;
906 if (bytes_read<=0)
908 if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
910 continue;
913 memset(&status, 0, sizeof (status));
914 status.channel=SND_PCM_CHANNEL_CAPTURE;
915 snd_pcm_plugin_status(data->pcmHandle, &status);
917 /* we need to reinitialize the sound channel if we've overrun the buffer */
918 if ((status.status==SND_PCM_STATUS_OVERRUN) ||
919 (status.status==SND_PCM_STATUS_READY))
921 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
923 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
924 aluHandleDisconnect(device, "Failed capture recovery: %s",
925 snd_strerror(rstatus));
926 return ALC_INVALID_DEVICE;
928 snd_pcm_capture_go(data->pcmHandle);
931 else
933 read_ptr+=bytes_read;
934 len-=bytes_read;
938 return ALC_NO_ERROR;
942 static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
944 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
945 SET_VTABLE2(CaptureWrapper, ALCbackend, self);
947 self->ExtraData = NULL;
950 static void CaptureWrapper_Destruct(CaptureWrapper *self)
952 if(self->ExtraData)
953 qsa_close_capture(self);
955 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
958 static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
960 return qsa_open_capture(self, name);
963 static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
965 qsa_start_capture(self);
966 return ALC_TRUE;
969 static void CaptureWrapper_stop(CaptureWrapper *self)
971 qsa_stop_capture(self);
974 static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
976 return qsa_capture_samples(self, buffer, samples);
979 static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
981 return qsa_available_samples(self);
985 typedef struct ALCqsaBackendFactory {
986 DERIVE_FROM_TYPE(ALCbackendFactory);
987 } ALCqsaBackendFactory;
988 #define ALCQSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCqsaBackendFactory, ALCbackendFactory) } }
990 static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self));
991 static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self));
992 static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type);
993 static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type);
994 static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
995 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
997 static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self))
999 return ALC_TRUE;
1002 static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self))
1004 #define FREE_NAME(iter) free((iter)->name)
1005 VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
1006 VECTOR_DEINIT(DeviceNameMap);
1008 VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
1009 VECTOR_DEINIT(CaptureNameMap);
1010 #undef FREE_NAME
1013 static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type)
1015 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
1016 return ALC_TRUE;
1017 return ALC_FALSE;
1020 static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
1022 switch (type)
1024 case ALL_DEVICE_PROBE:
1025 #define FREE_NAME(iter) free((iter)->name)
1026 VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
1027 VECTOR_RESIZE(DeviceNameMap, 0, 0);
1028 #undef FREE_NAME
1030 deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
1031 #define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
1032 VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_DEVICE);
1033 #undef APPEND_DEVICE
1034 break;
1036 case CAPTURE_DEVICE_PROBE:
1037 #define FREE_NAME(iter) free((iter)->name)
1038 VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
1039 VECTOR_RESIZE(CaptureNameMap, 0, 0);
1040 #undef FREE_NAME
1042 deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
1043 #define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
1044 VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_DEVICE);
1045 #undef APPEND_DEVICE
1046 break;
1050 static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
1052 if(type == ALCbackend_Playback)
1054 PlaybackWrapper *backend;
1055 NEW_OBJ(backend, PlaybackWrapper)(device);
1056 if(!backend) return NULL;
1057 return STATIC_CAST(ALCbackend, backend);
1059 if(type == ALCbackend_Capture)
1061 CaptureWrapper *backend;
1062 NEW_OBJ(backend, CaptureWrapper)(device);
1063 if(!backend) return NULL;
1064 return STATIC_CAST(ALCbackend, backend);
1067 return NULL;
1070 ALCbackendFactory *ALCqsaBackendFactory_getFactory(void)
1072 static ALCqsaBackendFactory factory = ALCQSABACKENDFACTORY_INITIALIZER;
1073 return STATIC_CAST(ALCbackendFactory, &factory);