Add "fast" variants for the bsinc resamplers
[openal-soft.git] / alc / backends / qsa.cpp
blob5fee298933a2231d08b6934f4a7696f810ccdb70
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 "backends/qsa.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <sched.h>
28 #include <errno.h>
29 #include <memory.h>
30 #include <poll.h>
32 #include <thread>
33 #include <memory>
34 #include <algorithm>
36 #include "alcmain.h"
37 #include "alu.h"
38 #include "threads.h"
40 #include <sys/asoundlib.h>
41 #include <sys/neutrino.h>
44 namespace {
46 struct qsa_data {
47 snd_pcm_t* pcmHandle{nullptr};
48 int audio_fd{-1};
50 snd_pcm_channel_setup_t csetup{};
51 snd_pcm_channel_params_t cparams{};
53 ALvoid* buffer{nullptr};
54 ALsizei size{0};
56 std::atomic<ALenum> mKillNow{AL_TRUE};
57 std::thread mThread;
60 struct DevMap {
61 ALCchar* name;
62 int card;
63 int dev;
66 al::vector<DevMap> DeviceNameMap;
67 al::vector<DevMap> CaptureNameMap;
69 constexpr ALCchar qsaDevice[] = "QSA Default";
71 constexpr struct {
72 int32_t format;
73 } formatlist[] = {
74 {SND_PCM_SFMT_FLOAT_LE},
75 {SND_PCM_SFMT_S32_LE},
76 {SND_PCM_SFMT_U32_LE},
77 {SND_PCM_SFMT_S16_LE},
78 {SND_PCM_SFMT_U16_LE},
79 {SND_PCM_SFMT_S8},
80 {SND_PCM_SFMT_U8},
81 {0},
84 constexpr struct {
85 int32_t rate;
86 } ratelist[] = {
87 {192000},
88 {176400},
89 {96000},
90 {88200},
91 {48000},
92 {44100},
93 {32000},
94 {24000},
95 {22050},
96 {16000},
97 {12000},
98 {11025},
99 {8000},
100 {0},
103 constexpr struct {
104 int32_t channels;
105 } channellist[] = {
106 {8},
107 {7},
108 {6},
109 {4},
110 {2},
111 {1},
112 {0},
115 void deviceList(int type, al::vector<DevMap> *devmap)
117 snd_ctl_t* handle;
118 snd_pcm_info_t pcminfo;
119 int max_cards, card, err, dev;
120 DevMap entry;
121 char name[1024];
122 snd_ctl_hw_info info;
124 max_cards = snd_cards();
125 if(max_cards < 0)
126 return;
128 std::for_each(devmap->begin(), devmap->end(),
129 [](const DevMap &entry) -> void
130 { free(entry.name); }
132 devmap->clear();
134 entry.name = strdup(qsaDevice);
135 entry.card = 0;
136 entry.dev = 0;
137 devmap->push_back(entry);
139 for(card = 0;card < max_cards;card++)
141 if((err=snd_ctl_open(&handle, card)) < 0)
142 continue;
144 if((err=snd_ctl_hw_info(handle, &info)) < 0)
146 snd_ctl_close(handle);
147 continue;
150 for(dev = 0;dev < (int)info.pcmdevs;dev++)
152 if((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
153 continue;
155 if((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
156 (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
158 snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
159 entry.name = strdup(name);
160 entry.card = card;
161 entry.dev = dev;
163 devmap->push_back(entry);
164 TRACE("Got device \"%s\", card %d, dev %d\n", name, card, dev);
167 snd_ctl_close(handle);
172 /* Wrappers to use an old-style backend with the new interface. */
173 struct PlaybackWrapper final : public BackendBase {
174 PlaybackWrapper(ALCdevice *device) noexcept : BackendBase{device} { }
175 ~PlaybackWrapper() override;
177 ALCenum open(const ALCchar *name) override;
178 bool reset() override;
179 bool start() override;
180 void stop() override;
182 std::unique_ptr<qsa_data> mExtraData;
184 DEF_NEWDEL(PlaybackWrapper)
188 FORCE_ALIGN static int qsa_proc_playback(void *ptr)
190 PlaybackWrapper *self = static_cast<PlaybackWrapper*>(ptr);
191 ALCdevice *device = self->mDevice;
192 qsa_data *data = self->mExtraData.get();
193 snd_pcm_channel_status_t status;
194 sched_param param;
195 char* write_ptr;
196 ALint len;
197 int sret;
199 SetRTPriority();
200 althrd_setname(MIXER_THREAD_NAME);
202 /* Increase default 10 priority to 11 to avoid jerky sound */
203 SchedGet(0, 0, &param);
204 param.sched_priority=param.sched_curpriority+1;
205 SchedSet(0, 0, SCHED_NOCHANGE, &param);
207 const ALint frame_size = device->frameSizeFromFmt();
209 self->lock();
210 while(!data->mKillNow.load(std::memory_order_acquire))
212 pollfd pollitem{};
213 pollitem.fd = data->audio_fd;
214 pollitem.events = POLLOUT;
216 /* Select also works like time slice to OS */
217 self->unlock();
218 sret = poll(&pollitem, 1, 2000);
219 self->lock();
220 if(sret == -1)
222 if(errno == EINTR || errno == EAGAIN)
223 continue;
224 ERR("poll error: %s\n", strerror(errno));
225 aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
226 break;
228 if(sret == 0)
230 ERR("poll timeout\n");
231 continue;
234 len = data->size;
235 write_ptr = static_cast<char*>(data->buffer);
236 aluMixData(device, write_ptr, len/frame_size);
237 while(len>0 && !data->mKillNow.load(std::memory_order_acquire))
239 int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
240 if(wrote <= 0)
242 if(errno==EAGAIN || errno==EWOULDBLOCK)
243 continue;
245 memset(&status, 0, sizeof(status));
246 status.channel = SND_PCM_CHANNEL_PLAYBACK;
248 snd_pcm_plugin_status(data->pcmHandle, &status);
250 /* we need to reinitialize the sound channel if we've underrun the buffer */
251 if(status.status == SND_PCM_STATUS_UNDERRUN ||
252 status.status == SND_PCM_STATUS_READY)
254 if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
256 aluHandleDisconnect(device, "Playback recovery failed");
257 break;
261 else
263 write_ptr += wrote;
264 len -= wrote;
268 self->unlock();
270 return 0;
273 /************/
274 /* Playback */
275 /************/
277 static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
279 ALCdevice *device = self->mDevice;
280 int card, dev;
281 int status;
283 std::unique_ptr<qsa_data> data{new qsa_data{}};
284 data->mKillNow.store(AL_TRUE, std::memory_order_relaxed);
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 if(DeviceNameMap.empty())
294 deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
296 auto iter = std::find_if(DeviceNameMap.begin(), DeviceNameMap.end(),
297 [deviceName](const DevMap &entry) -> bool
298 { return entry.name && strcmp(deviceName, entry.name) == 0; }
300 if(iter == DeviceNameMap.cend())
301 return ALC_INVALID_DEVICE;
303 status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK);
306 if(status < 0)
307 return ALC_INVALID_DEVICE;
309 data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
310 if(data->audio_fd < 0)
312 snd_pcm_close(data->pcmHandle);
313 return ALC_INVALID_DEVICE;
316 device->DeviceName = deviceName;
317 self->mExtraData = std::move(data);
319 return ALC_NO_ERROR;
322 static void qsa_close_playback(PlaybackWrapper *self)
324 qsa_data *data = self->mExtraData.get();
326 if (data->buffer!=NULL)
328 free(data->buffer);
329 data->buffer=NULL;
332 snd_pcm_close(data->pcmHandle);
334 self->mExtraData = nullptr;
337 static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
339 ALCdevice *device = self->mDevice;
340 qsa_data *data = self->mExtraData.get();
341 int32_t format=-1;
343 switch(device->FmtType)
345 case DevFmtByte:
346 format=SND_PCM_SFMT_S8;
347 break;
348 case DevFmtUByte:
349 format=SND_PCM_SFMT_U8;
350 break;
351 case DevFmtShort:
352 format=SND_PCM_SFMT_S16_LE;
353 break;
354 case DevFmtUShort:
355 format=SND_PCM_SFMT_U16_LE;
356 break;
357 case DevFmtInt:
358 format=SND_PCM_SFMT_S32_LE;
359 break;
360 case DevFmtUInt:
361 format=SND_PCM_SFMT_U32_LE;
362 break;
363 case DevFmtFloat:
364 format=SND_PCM_SFMT_FLOAT_LE;
365 break;
368 /* we actually don't want to block on writes */
369 snd_pcm_nonblock_mode(data->pcmHandle, 1);
370 /* Disable mmap to control data transfer to the audio device */
371 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
372 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS);
374 // configure a sound channel
375 memset(&data->cparams, 0, sizeof(data->cparams));
376 data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK;
377 data->cparams.mode=SND_PCM_MODE_BLOCK;
378 data->cparams.start_mode=SND_PCM_START_FULL;
379 data->cparams.stop_mode=SND_PCM_STOP_STOP;
381 data->cparams.buf.block.frag_size=device->UpdateSize * device->frameSizeFromFmt();
382 data->cparams.buf.block.frags_max=device->BufferSize / device->UpdateSize;
383 data->cparams.buf.block.frags_min=data->cparams.buf.block.frags_max;
385 data->cparams.format.interleave=1;
386 data->cparams.format.rate=device->Frequency;
387 data->cparams.format.voices=device->channelsFromFmt();
388 data->cparams.format.format=format;
390 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
392 int original_rate=data->cparams.format.rate;
393 int original_voices=data->cparams.format.voices;
394 int original_format=data->cparams.format.format;
395 int it;
396 int jt;
398 for (it=0; it<1; it++)
400 /* Check for second pass */
401 if (it==1)
403 original_rate=ratelist[0].rate;
404 original_voices=channellist[0].channels;
405 original_format=formatlist[0].format;
408 do {
409 /* At first downgrade sample format */
410 jt=0;
411 do {
412 if (formatlist[jt].format==data->cparams.format.format)
414 data->cparams.format.format=formatlist[jt+1].format;
415 break;
417 if (formatlist[jt].format==0)
419 data->cparams.format.format=0;
420 break;
422 jt++;
423 } while(1);
425 if (data->cparams.format.format==0)
427 data->cparams.format.format=original_format;
429 /* At secod downgrade sample rate */
430 jt=0;
431 do {
432 if (ratelist[jt].rate==data->cparams.format.rate)
434 data->cparams.format.rate=ratelist[jt+1].rate;
435 break;
437 if (ratelist[jt].rate==0)
439 data->cparams.format.rate=0;
440 break;
442 jt++;
443 } while(1);
445 if (data->cparams.format.rate==0)
447 data->cparams.format.rate=original_rate;
448 data->cparams.format.format=original_format;
450 /* At third downgrade channels number */
451 jt=0;
452 do {
453 if(channellist[jt].channels==data->cparams.format.voices)
455 data->cparams.format.voices=channellist[jt+1].channels;
456 break;
458 if (channellist[jt].channels==0)
460 data->cparams.format.voices=0;
461 break;
463 jt++;
464 } while(1);
467 if (data->cparams.format.voices==0)
469 break;
473 data->cparams.buf.block.frag_size=device->UpdateSize*
474 data->cparams.format.voices*
475 snd_pcm_format_width(data->cparams.format.format)/8;
476 data->cparams.buf.block.frags_max=device->NumUpdates;
477 data->cparams.buf.block.frags_min=device->NumUpdates;
478 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
480 continue;
482 else
484 break;
486 } while(1);
488 if (data->cparams.format.voices!=0)
490 break;
494 if (data->cparams.format.voices==0)
496 return ALC_FALSE;
500 if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
502 return ALC_FALSE;
505 memset(&data->csetup, 0, sizeof(data->csetup));
506 data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK;
507 if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0)
509 return ALC_FALSE;
512 /* now fill back to the our AL device */
513 device->Frequency=data->cparams.format.rate;
515 switch (data->cparams.format.voices)
517 case 1:
518 device->FmtChans=DevFmtMono;
519 break;
520 case 2:
521 device->FmtChans=DevFmtStereo;
522 break;
523 case 4:
524 device->FmtChans=DevFmtQuad;
525 break;
526 case 6:
527 device->FmtChans=DevFmtX51;
528 break;
529 case 7:
530 device->FmtChans=DevFmtX61;
531 break;
532 case 8:
533 device->FmtChans=DevFmtX71;
534 break;
535 default:
536 device->FmtChans=DevFmtMono;
537 break;
540 switch (data->cparams.format.format)
542 case SND_PCM_SFMT_S8:
543 device->FmtType=DevFmtByte;
544 break;
545 case SND_PCM_SFMT_U8:
546 device->FmtType=DevFmtUByte;
547 break;
548 case SND_PCM_SFMT_S16_LE:
549 device->FmtType=DevFmtShort;
550 break;
551 case SND_PCM_SFMT_U16_LE:
552 device->FmtType=DevFmtUShort;
553 break;
554 case SND_PCM_SFMT_S32_LE:
555 device->FmtType=DevFmtInt;
556 break;
557 case SND_PCM_SFMT_U32_LE:
558 device->FmtType=DevFmtUInt;
559 break;
560 case SND_PCM_SFMT_FLOAT_LE:
561 device->FmtType=DevFmtFloat;
562 break;
563 default:
564 device->FmtType=DevFmtShort;
565 break;
568 SetDefaultChannelOrder(device);
570 device->UpdateSize=data->csetup.buf.block.frag_size / device->frameSizeFromFmt();
571 device->NumUpdates=data->csetup.buf.block.frags;
573 data->size=data->csetup.buf.block.frag_size;
574 data->buffer=malloc(data->size);
575 if (!data->buffer)
577 return ALC_FALSE;
580 return ALC_TRUE;
583 static ALCboolean qsa_start_playback(PlaybackWrapper *self)
585 qsa_data *data = self->mExtraData.get();
587 try {
588 data->mKillNow.store(AL_FALSE, std::memory_order_release);
589 data->mThread = std::thread(qsa_proc_playback, self);
590 return ALC_TRUE;
592 catch(std::exception& e) {
593 ERR("Could not create playback thread: %s\n", e.what());
595 catch(...) {
597 return ALC_FALSE;
600 static void qsa_stop_playback(PlaybackWrapper *self)
602 qsa_data *data = self->mExtraData.get();
604 if(data->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !data->mThread.joinable())
605 return;
606 data->mThread.join();
610 PlaybackWrapper::~PlaybackWrapper()
612 if(mExtraData)
613 qsa_close_playback(this);
616 ALCenum PlaybackWrapper::open(const ALCchar *name)
617 { return qsa_open_playback(this, name); }
619 bool PlaybackWrapper::reset()
620 { return qsa_reset_playback(this); }
622 bool PlaybackWrapper::start()
623 { return qsa_start_playback(this); }
625 void PlaybackWrapper::stop()
626 { qsa_stop_playback(this); }
629 /***********/
630 /* Capture */
631 /***********/
633 struct CaptureWrapper final : public BackendBase {
634 CaptureWrapper(ALCdevice *device) noexcept : BackendBase{device} { }
635 ~CaptureWrapper() override;
637 ALCenum open(const ALCchar *name) override;
638 bool start() override;
639 void stop() override;
640 ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
641 ALCuint availableSamples() override;
643 std::unique_ptr<qsa_data> mExtraData;
645 DEF_NEWDEL(CaptureWrapper)
648 static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
650 ALCdevice *device = self->mDevice;
651 int card, dev;
652 int format=-1;
653 int status;
655 std::unique_ptr<qsa_data> data{new qsa_data{}};
657 if(!deviceName)
658 deviceName = qsaDevice;
660 if(strcmp(deviceName, qsaDevice) == 0)
661 status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
662 else
664 if(CaptureNameMap.empty())
665 deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
667 auto iter = std::find_if(CaptureNameMap.cbegin(), CaptureNameMap.cend(),
668 [deviceName](const DevMap &entry) -> bool
669 { return entry.name && strcmp(deviceName, entry.name) == 0; }
671 if(iter == CaptureNameMap.cend())
672 return ALC_INVALID_DEVICE;
674 status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE);
677 if(status < 0)
678 return ALC_INVALID_DEVICE;
680 data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
681 if(data->audio_fd < 0)
683 snd_pcm_close(data->pcmHandle);
684 return ALC_INVALID_DEVICE;
687 device->DeviceName = deviceName;
689 switch (device->FmtType)
691 case DevFmtByte:
692 format=SND_PCM_SFMT_S8;
693 break;
694 case DevFmtUByte:
695 format=SND_PCM_SFMT_U8;
696 break;
697 case DevFmtShort:
698 format=SND_PCM_SFMT_S16_LE;
699 break;
700 case DevFmtUShort:
701 format=SND_PCM_SFMT_U16_LE;
702 break;
703 case DevFmtInt:
704 format=SND_PCM_SFMT_S32_LE;
705 break;
706 case DevFmtUInt:
707 format=SND_PCM_SFMT_U32_LE;
708 break;
709 case DevFmtFloat:
710 format=SND_PCM_SFMT_FLOAT_LE;
711 break;
714 /* we actually don't want to block on reads */
715 snd_pcm_nonblock_mode(data->pcmHandle, 1);
716 /* Disable mmap to control data transfer to the audio device */
717 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
719 /* configure a sound channel */
720 memset(&data->cparams, 0, sizeof(data->cparams));
721 data->cparams.mode=SND_PCM_MODE_BLOCK;
722 data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
723 data->cparams.start_mode=SND_PCM_START_GO;
724 data->cparams.stop_mode=SND_PCM_STOP_STOP;
726 data->cparams.buf.block.frag_size=device->UpdateSize * device->frameSizeFromFmt();
727 data->cparams.buf.block.frags_max=device->NumUpdates;
728 data->cparams.buf.block.frags_min=device->NumUpdates;
730 data->cparams.format.interleave=1;
731 data->cparams.format.rate=device->Frequency;
732 data->cparams.format.voices=device->channelsFromFmt();
733 data->cparams.format.format=format;
735 if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
737 snd_pcm_close(data->pcmHandle);
738 return ALC_INVALID_VALUE;
741 self->mExtraData = std::move(data);
743 return ALC_NO_ERROR;
746 static void qsa_close_capture(CaptureWrapper *self)
748 qsa_data *data = self->mExtraData.get();
750 if (data->pcmHandle!=nullptr)
751 snd_pcm_close(data->pcmHandle);
752 data->pcmHandle = nullptr;
754 self->mExtraData = nullptr;
757 static void qsa_start_capture(CaptureWrapper *self)
759 qsa_data *data = self->mExtraData.get();
760 int rstatus;
762 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
764 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
765 return;
768 memset(&data->csetup, 0, sizeof(data->csetup));
769 data->csetup.channel=SND_PCM_CHANNEL_CAPTURE;
770 if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0)
772 ERR("capture setup failed: %s\n", snd_strerror(rstatus));
773 return;
776 snd_pcm_capture_go(data->pcmHandle);
779 static void qsa_stop_capture(CaptureWrapper *self)
781 qsa_data *data = self->mExtraData.get();
782 snd_pcm_capture_flush(data->pcmHandle);
785 static ALCuint qsa_available_samples(CaptureWrapper *self)
787 ALCdevice *device = self->mDevice;
788 qsa_data *data = self->mExtraData.get();
789 snd_pcm_channel_status_t status;
790 ALint frame_size = device->frameSizeFromFmt();
791 ALint free_size;
792 int rstatus;
794 memset(&status, 0, sizeof (status));
795 status.channel=SND_PCM_CHANNEL_CAPTURE;
796 snd_pcm_plugin_status(data->pcmHandle, &status);
797 if ((status.status==SND_PCM_STATUS_OVERRUN) ||
798 (status.status==SND_PCM_STATUS_READY))
800 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
802 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
803 aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus));
804 return 0;
807 snd_pcm_capture_go(data->pcmHandle);
808 return 0;
811 free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags;
812 free_size-=status.free;
814 return free_size/frame_size;
817 static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
819 ALCdevice *device = self->mDevice;
820 qsa_data *data = self->mExtraData.get();
821 char* read_ptr;
822 snd_pcm_channel_status_t status;
823 int selectret;
824 int bytes_read;
825 ALint frame_size=device->frameSizeFromFmt();
826 ALint len=samples*frame_size;
827 int rstatus;
829 read_ptr = static_cast<char*>(buffer);
831 while (len>0)
833 pollfd pollitem{};
834 pollitem.fd = data->audio_fd;
835 pollitem.events = POLLOUT;
837 /* Select also works like time slice to OS */
838 bytes_read=0;
839 selectret = poll(&pollitem, 1, 2000);
840 switch (selectret)
842 case -1:
843 aluHandleDisconnect(device, "Failed to check capture samples");
844 return ALC_INVALID_DEVICE;
845 case 0:
846 break;
847 default:
848 bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len);
849 break;
852 if (bytes_read<=0)
854 if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
856 continue;
859 memset(&status, 0, sizeof (status));
860 status.channel=SND_PCM_CHANNEL_CAPTURE;
861 snd_pcm_plugin_status(data->pcmHandle, &status);
863 /* we need to reinitialize the sound channel if we've overrun the buffer */
864 if ((status.status==SND_PCM_STATUS_OVERRUN) ||
865 (status.status==SND_PCM_STATUS_READY))
867 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
869 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
870 aluHandleDisconnect(device, "Failed capture recovery: %s",
871 snd_strerror(rstatus));
872 return ALC_INVALID_DEVICE;
874 snd_pcm_capture_go(data->pcmHandle);
877 else
879 read_ptr+=bytes_read;
880 len-=bytes_read;
884 return ALC_NO_ERROR;
888 CaptureWrapper::~CaptureWrapper()
890 if(mExtraData)
891 qsa_close_capture(this);
894 ALCenum CaptureWrapper::open(const ALCchar *name)
895 { return qsa_open_capture(this, name); }
897 bool CaptureWrapper::start()
898 { qsa_start_capture(this); return true; }
900 void CaptureWrapper::stop()
901 { qsa_stop_capture(this); }
903 ALCenum CaptureWrapper::captureSamples(al::byte *buffer, ALCuint samples)
904 { return qsa_capture_samples(this, buffer, samples); }
906 ALCuint CaptureWrapper::availableSamples()
907 { return qsa_available_samples(this); }
909 } // namespace
912 bool QSABackendFactory::init()
913 { return true; }
915 bool QSABackendFactory::querySupport(BackendType type)
916 { return (type == BackendType::Playback || type == BackendType::Capture); }
918 void QSABackendFactory::probe(DevProbe type, std::string *outnames)
920 auto add_device = [outnames](const DevMap &entry) -> void
922 const char *n = entry.name;
923 if(n && n[0])
924 outnames->append(n, strlen(n)+1);
927 switch (type)
929 case DevProbe::Playback:
930 deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
931 std::for_each(DeviceNameMap.cbegin(), DeviceNameMap.cend(), add_device);
932 break;
933 case DevProbe::Capture:
934 deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
935 std::for_each(CaptureNameMap.cbegin(), CaptureNameMap.cend(), add_device);
936 break;
940 BackendPtr QSABackendFactory::createBackend(ALCdevice *device, BackendType type)
942 if(type == BackendType::Playback)
943 return BackendPtr{new PlaybackWrapper{device}};
944 if(type == BackendType::Capture)
945 return BackendPtr{new CaptureWrapper{device}};
946 return nullptr;
949 BackendFactory &QSABackendFactory::getFactory()
951 static QSABackendFactory factory{};
952 return factory;