Remove the last remaining uses of althrd_t
[openal-soft.git] / Alc / backends / qsa.cpp
blob3d09a7448b290c07131efd03e9526d6431e872d2
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>
31 #include <thread>
32 #include <memory>
33 #include <algorithm>
35 #include "alMain.h"
36 #include "alu.h"
37 #include "threads.h"
39 #include <sys/select.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 struct 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 ALCbackend {
174 std::unique_ptr<qsa_data> ExtraData;
177 static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
178 static void PlaybackWrapper_Destruct(PlaybackWrapper *self);
179 static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
180 static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
181 static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
182 static void PlaybackWrapper_stop(PlaybackWrapper *self);
183 static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
184 static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
185 static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
186 static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
187 static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
188 DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
189 DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
192 FORCE_ALIGN static int qsa_proc_playback(void *ptr)
194 PlaybackWrapper *self = static_cast<PlaybackWrapper*>(ptr);
195 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
196 qsa_data *data = self->ExtraData;
197 snd_pcm_channel_status_t status;
198 struct sched_param param;
199 struct timeval timeout;
200 char* write_ptr;
201 fd_set wfds;
202 ALint len;
203 int sret;
205 SetRTPriority();
206 althrd_setname(MIXER_THREAD_NAME);
208 /* Increase default 10 priority to 11 to avoid jerky sound */
209 SchedGet(0, 0, &param);
210 param.sched_priority=param.sched_curpriority+1;
211 SchedSet(0, 0, SCHED_NOCHANGE, &param);
213 const ALint frame_size = FrameSizeFromDevFmt(
214 device->FmtChans, device->FmtType, device->mAmbiOrder
217 PlaybackWrapper_lock(self);
218 while(!data->mKillNow.load(std::memory_order_acquire))
220 FD_ZERO(&wfds);
221 FD_SET(data->audio_fd, &wfds);
222 timeout.tv_sec=2;
223 timeout.tv_usec=0;
225 /* Select also works like time slice to OS */
226 PlaybackWrapper_unlock(self);
227 sret = select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
228 PlaybackWrapper_lock(self);
229 if(sret == -1)
231 ERR("select error: %s\n", strerror(errno));
232 aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
233 break;
235 if(sret == 0)
237 ERR("select timeout\n");
238 continue;
241 len = data->size;
242 write_ptr = static_cast<char*>(data->buffer);
243 aluMixData(device, write_ptr, len/frame_size);
244 while(len>0 && !data->mKillNow.load(std::memory_order_acquire))
246 int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
247 if(wrote <= 0)
249 if(errno==EAGAIN || errno==EWOULDBLOCK)
250 continue;
252 memset(&status, 0, sizeof(status));
253 status.channel = SND_PCM_CHANNEL_PLAYBACK;
255 snd_pcm_plugin_status(data->pcmHandle, &status);
257 /* we need to reinitialize the sound channel if we've underrun the buffer */
258 if(status.status == SND_PCM_STATUS_UNDERRUN ||
259 status.status == SND_PCM_STATUS_READY)
261 if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
263 aluHandleDisconnect(device, "Playback recovery failed");
264 break;
268 else
270 write_ptr += wrote;
271 len -= wrote;
275 PlaybackWrapper_unlock(self);
277 return 0;
280 /************/
281 /* Playback */
282 /************/
284 static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
286 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
287 int card, dev;
288 int status;
290 std::unique_ptr<qsa_data> data{new qsa_data{}};
291 data->mKillNow.store(AL_TRUE, std::memory_order_relaxed);
293 if(!deviceName)
294 deviceName = qsaDevice;
296 if(strcmp(deviceName, qsaDevice) == 0)
297 status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
298 else
300 if(DeviceNameMap.empty())
301 deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
303 auto iter = std::find_if(DeviceNameMap.begin(), DeviceNameMap.end(),
304 [deviceName](const DevMap &entry) -> bool
305 { return entry.name && strcmp(deviceName, entry.name) == 0; }
307 if(iter == DeviceNameMap.cend())
308 return ALC_INVALID_DEVICE;
310 status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK);
313 if(status < 0)
314 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 return ALC_INVALID_DEVICE;
323 device->DeviceName = deviceName;
324 self->ExtraData = std::move(data);
326 return ALC_NO_ERROR;
329 static void qsa_close_playback(PlaybackWrapper *self)
331 qsa_data *data = self->ExtraData.get();
333 if (data->buffer!=NULL)
335 free(data->buffer);
336 data->buffer=NULL;
339 snd_pcm_close(data->pcmHandle);
341 self->ExtraData = nullptr;
344 static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
346 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
347 qsa_data *data = self->ExtraData.get();
348 int32_t format=-1;
350 switch(device->FmtType)
352 case DevFmtByte:
353 format=SND_PCM_SFMT_S8;
354 break;
355 case DevFmtUByte:
356 format=SND_PCM_SFMT_U8;
357 break;
358 case DevFmtShort:
359 format=SND_PCM_SFMT_S16_LE;
360 break;
361 case DevFmtUShort:
362 format=SND_PCM_SFMT_U16_LE;
363 break;
364 case DevFmtInt:
365 format=SND_PCM_SFMT_S32_LE;
366 break;
367 case DevFmtUInt:
368 format=SND_PCM_SFMT_U32_LE;
369 break;
370 case DevFmtFloat:
371 format=SND_PCM_SFMT_FLOAT_LE;
372 break;
375 /* we actually don't want to block on writes */
376 snd_pcm_nonblock_mode(data->pcmHandle, 1);
377 /* Disable mmap to control data transfer to the audio device */
378 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
379 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS);
381 // configure a sound channel
382 memset(&data->cparams, 0, sizeof(data->cparams));
383 data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK;
384 data->cparams.mode=SND_PCM_MODE_BLOCK;
385 data->cparams.start_mode=SND_PCM_START_FULL;
386 data->cparams.stop_mode=SND_PCM_STOP_STOP;
388 data->cparams.buf.block.frag_size=device->UpdateSize *
389 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->mAmbiOrder);
390 data->cparams.buf.block.frags_max=device->NumUpdates;
391 data->cparams.buf.block.frags_min=device->NumUpdates;
393 data->cparams.format.interleave=1;
394 data->cparams.format.rate=device->Frequency;
395 data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
396 data->cparams.format.format=format;
398 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
400 int original_rate=data->cparams.format.rate;
401 int original_voices=data->cparams.format.voices;
402 int original_format=data->cparams.format.format;
403 int it;
404 int jt;
406 for (it=0; it<1; it++)
408 /* Check for second pass */
409 if (it==1)
411 original_rate=ratelist[0].rate;
412 original_voices=channellist[0].channels;
413 original_format=formatlist[0].format;
416 do {
417 /* At first downgrade sample format */
418 jt=0;
419 do {
420 if (formatlist[jt].format==data->cparams.format.format)
422 data->cparams.format.format=formatlist[jt+1].format;
423 break;
425 if (formatlist[jt].format==0)
427 data->cparams.format.format=0;
428 break;
430 jt++;
431 } while(1);
433 if (data->cparams.format.format==0)
435 data->cparams.format.format=original_format;
437 /* At secod downgrade sample rate */
438 jt=0;
439 do {
440 if (ratelist[jt].rate==data->cparams.format.rate)
442 data->cparams.format.rate=ratelist[jt+1].rate;
443 break;
445 if (ratelist[jt].rate==0)
447 data->cparams.format.rate=0;
448 break;
450 jt++;
451 } while(1);
453 if (data->cparams.format.rate==0)
455 data->cparams.format.rate=original_rate;
456 data->cparams.format.format=original_format;
458 /* At third downgrade channels number */
459 jt=0;
460 do {
461 if(channellist[jt].channels==data->cparams.format.voices)
463 data->cparams.format.voices=channellist[jt+1].channels;
464 break;
466 if (channellist[jt].channels==0)
468 data->cparams.format.voices=0;
469 break;
471 jt++;
472 } while(1);
475 if (data->cparams.format.voices==0)
477 break;
481 data->cparams.buf.block.frag_size=device->UpdateSize*
482 data->cparams.format.voices*
483 snd_pcm_format_width(data->cparams.format.format)/8;
484 data->cparams.buf.block.frags_max=device->NumUpdates;
485 data->cparams.buf.block.frags_min=device->NumUpdates;
486 if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
488 continue;
490 else
492 break;
494 } while(1);
496 if (data->cparams.format.voices!=0)
498 break;
502 if (data->cparams.format.voices==0)
504 return ALC_FALSE;
508 if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
510 return ALC_FALSE;
513 memset(&data->csetup, 0, sizeof(data->csetup));
514 data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK;
515 if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0)
517 return ALC_FALSE;
520 /* now fill back to the our AL device */
521 device->Frequency=data->cparams.format.rate;
523 switch (data->cparams.format.voices)
525 case 1:
526 device->FmtChans=DevFmtMono;
527 break;
528 case 2:
529 device->FmtChans=DevFmtStereo;
530 break;
531 case 4:
532 device->FmtChans=DevFmtQuad;
533 break;
534 case 6:
535 device->FmtChans=DevFmtX51;
536 break;
537 case 7:
538 device->FmtChans=DevFmtX61;
539 break;
540 case 8:
541 device->FmtChans=DevFmtX71;
542 break;
543 default:
544 device->FmtChans=DevFmtMono;
545 break;
548 switch (data->cparams.format.format)
550 case SND_PCM_SFMT_S8:
551 device->FmtType=DevFmtByte;
552 break;
553 case SND_PCM_SFMT_U8:
554 device->FmtType=DevFmtUByte;
555 break;
556 case SND_PCM_SFMT_S16_LE:
557 device->FmtType=DevFmtShort;
558 break;
559 case SND_PCM_SFMT_U16_LE:
560 device->FmtType=DevFmtUShort;
561 break;
562 case SND_PCM_SFMT_S32_LE:
563 device->FmtType=DevFmtInt;
564 break;
565 case SND_PCM_SFMT_U32_LE:
566 device->FmtType=DevFmtUInt;
567 break;
568 case SND_PCM_SFMT_FLOAT_LE:
569 device->FmtType=DevFmtFloat;
570 break;
571 default:
572 device->FmtType=DevFmtShort;
573 break;
576 SetDefaultChannelOrder(device);
578 device->UpdateSize=data->csetup.buf.block.frag_size/
579 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->mAmbiOrder);
580 device->NumUpdates=data->csetup.buf.block.frags;
582 data->size=data->csetup.buf.block.frag_size;
583 data->buffer=malloc(data->size);
584 if (!data->buffer)
586 return ALC_FALSE;
589 return ALC_TRUE;
592 static ALCboolean qsa_start_playback(PlaybackWrapper *self)
594 qsa_data *data = self->ExtraData.get();
596 try {
597 data->mKillNow.store(AL_FALSE, std::memory_order_release);
598 data->mThread = std::thread(qsa_proc_playback, self);
599 return ALC_TRUE;
601 catch(std::exception& e) {
602 ERR("Could not create playback thread: %s\n", e.what());
604 catch(...) {
606 return ALC_FALSE;
609 static void qsa_stop_playback(PlaybackWrapper *self)
611 qsa_data *data = self->ExtraData.get();
613 if(data->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !data->mThread.joinable())
614 return;
615 data->mThread.join();
619 static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
621 new (self) PlaybackWrapper{};
622 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
623 SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
625 self->ExtraData = NULL;
628 static void PlaybackWrapper_Destruct(PlaybackWrapper *self)
630 if(self->ExtraData)
631 qsa_close_playback(self);
633 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
634 self->~PlaybackWrapper();
637 static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
639 return qsa_open_playback(self, name);
642 static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
644 return qsa_reset_playback(self);
647 static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
649 return qsa_start_playback(self);
652 static void PlaybackWrapper_stop(PlaybackWrapper *self)
654 qsa_stop_playback(self);
659 /***********/
660 /* Capture */
661 /***********/
663 struct CaptureWrapper final : public ALCbackend {
664 std::unique_ptr<qsa_data> ExtraData;
667 static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
668 static void CaptureWrapper_Destruct(CaptureWrapper *self);
669 static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
670 static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
671 static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
672 static void CaptureWrapper_stop(CaptureWrapper *self);
673 static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
674 static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
675 static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
676 static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
677 static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
678 DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
679 DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
682 static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
684 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
685 int card, dev;
686 int format=-1;
687 int status;
689 std::unique_ptr<qsa_data> data{new qsa_data{}};
691 if(!deviceName)
692 deviceName = qsaDevice;
694 if(strcmp(deviceName, qsaDevice) == 0)
695 status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
696 else
698 if(CaptureNameMap.empty())
699 deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
701 auto iter = std::find_if(CaptureNameMap.cbegin(), CaptureNameMap.cend(),
702 [deviceName](const DevMap &entry) -> bool
703 { return entry.name && strcmp(deviceName, entry.name) == 0; }
705 if(iter == CaptureNameMap.cend())
706 return ALC_INVALID_DEVICE;
708 status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE);
711 if(status < 0)
712 return ALC_INVALID_DEVICE;
714 data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
715 if(data->audio_fd < 0)
717 snd_pcm_close(data->pcmHandle);
718 return ALC_INVALID_DEVICE;
721 device->DeviceName = deviceName;
723 switch (device->FmtType)
725 case DevFmtByte:
726 format=SND_PCM_SFMT_S8;
727 break;
728 case DevFmtUByte:
729 format=SND_PCM_SFMT_U8;
730 break;
731 case DevFmtShort:
732 format=SND_PCM_SFMT_S16_LE;
733 break;
734 case DevFmtUShort:
735 format=SND_PCM_SFMT_U16_LE;
736 break;
737 case DevFmtInt:
738 format=SND_PCM_SFMT_S32_LE;
739 break;
740 case DevFmtUInt:
741 format=SND_PCM_SFMT_U32_LE;
742 break;
743 case DevFmtFloat:
744 format=SND_PCM_SFMT_FLOAT_LE;
745 break;
748 /* we actually don't want to block on reads */
749 snd_pcm_nonblock_mode(data->pcmHandle, 1);
750 /* Disable mmap to control data transfer to the audio device */
751 snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
753 /* configure a sound channel */
754 memset(&data->cparams, 0, sizeof(data->cparams));
755 data->cparams.mode=SND_PCM_MODE_BLOCK;
756 data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
757 data->cparams.start_mode=SND_PCM_START_GO;
758 data->cparams.stop_mode=SND_PCM_STOP_STOP;
760 data->cparams.buf.block.frag_size=device->UpdateSize*
761 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->mAmbiOrder);
762 data->cparams.buf.block.frags_max=device->NumUpdates;
763 data->cparams.buf.block.frags_min=device->NumUpdates;
765 data->cparams.format.interleave=1;
766 data->cparams.format.rate=device->Frequency;
767 data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
768 data->cparams.format.format=format;
770 if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
772 snd_pcm_close(data->pcmHandle);
773 return ALC_INVALID_VALUE;
776 self->ExtraData = std::move(data);
778 return ALC_NO_ERROR;
781 static void qsa_close_capture(CaptureWrapper *self)
783 qsa_data *data = self->ExtraData.get();
785 if (data->pcmHandle!=nullptr)
786 snd_pcm_close(data->pcmHandle);
787 data->pcmHandle = nullptr
789 self->ExtraData = nullptr;
792 static void qsa_start_capture(CaptureWrapper *self)
794 qsa_data *data = self->ExtraData.get();
795 int rstatus;
797 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
799 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
800 return;
803 memset(&data->csetup, 0, sizeof(data->csetup));
804 data->csetup.channel=SND_PCM_CHANNEL_CAPTURE;
805 if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0)
807 ERR("capture setup failed: %s\n", snd_strerror(rstatus));
808 return;
811 snd_pcm_capture_go(data->pcmHandle);
814 static void qsa_stop_capture(CaptureWrapper *self)
816 qsa_data *data = self->ExtraData.get();
817 snd_pcm_capture_flush(data->pcmHandle);
820 static ALCuint qsa_available_samples(CaptureWrapper *self)
822 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
823 qsa_data *data = self->ExtraData.get();
824 snd_pcm_channel_status_t status;
825 ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->mAmbiOrder);
826 ALint free_size;
827 int rstatus;
829 memset(&status, 0, sizeof (status));
830 status.channel=SND_PCM_CHANNEL_CAPTURE;
831 snd_pcm_plugin_status(data->pcmHandle, &status);
832 if ((status.status==SND_PCM_STATUS_OVERRUN) ||
833 (status.status==SND_PCM_STATUS_READY))
835 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
837 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
838 aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus));
839 return 0;
842 snd_pcm_capture_go(data->pcmHandle);
843 return 0;
846 free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags;
847 free_size-=status.free;
849 return free_size/frame_size;
852 static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
854 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
855 qsa_data *data = self->ExtraData.get();
856 char* read_ptr;
857 snd_pcm_channel_status_t status;
858 fd_set rfds;
859 int selectret;
860 struct timeval timeout;
861 int bytes_read;
862 ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->mAmbiOrder);
863 ALint len=samples*frame_size;
864 int rstatus;
866 read_ptr = static_cast<char*>(buffer);
868 while (len>0)
870 FD_ZERO(&rfds);
871 FD_SET(data->audio_fd, &rfds);
872 timeout.tv_sec=2;
873 timeout.tv_usec=0;
875 /* Select also works like time slice to OS */
876 bytes_read=0;
877 selectret=select(data->audio_fd+1, &rfds, NULL, NULL, &timeout);
878 switch (selectret)
880 case -1:
881 aluHandleDisconnect(device, "Failed to check capture samples");
882 return ALC_INVALID_DEVICE;
883 case 0:
884 break;
885 default:
886 if (FD_ISSET(data->audio_fd, &rfds))
888 bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len);
889 break;
891 break;
894 if (bytes_read<=0)
896 if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
898 continue;
901 memset(&status, 0, sizeof (status));
902 status.channel=SND_PCM_CHANNEL_CAPTURE;
903 snd_pcm_plugin_status(data->pcmHandle, &status);
905 /* we need to reinitialize the sound channel if we've overrun the buffer */
906 if ((status.status==SND_PCM_STATUS_OVERRUN) ||
907 (status.status==SND_PCM_STATUS_READY))
909 if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
911 ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
912 aluHandleDisconnect(device, "Failed capture recovery: %s",
913 snd_strerror(rstatus));
914 return ALC_INVALID_DEVICE;
916 snd_pcm_capture_go(data->pcmHandle);
919 else
921 read_ptr+=bytes_read;
922 len-=bytes_read;
926 return ALC_NO_ERROR;
930 static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
932 new (self) CaptureWrapper{};
933 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
934 SET_VTABLE2(CaptureWrapper, ALCbackend, self);
937 static void CaptureWrapper_Destruct(CaptureWrapper *self)
939 if(self->ExtraData)
940 qsa_close_capture(self);
942 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
943 self->~CaptureWrapper();
946 static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
948 return qsa_open_capture(self, name);
951 static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
953 qsa_start_capture(self);
954 return ALC_TRUE;
957 static void CaptureWrapper_stop(CaptureWrapper *self)
959 qsa_stop_capture(self);
962 static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
964 return qsa_capture_samples(self, buffer, samples);
967 static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
969 return qsa_available_samples(self);
972 } // namespace
975 bool QSABackendFactory::init()
976 { return true; }
978 void QSABackendFactory::deinit()
980 std::for_each(DeviceNameMap.begin(), DeviceNameMap.end(),
981 [](DevMap &entry) -> void { free(entry.name); }
983 DeviceNameMap.clear();
985 std::for_each(CaptureNameMap.begin(), CaptureNameMap.end(),
986 [](DevMap &entry) -> void { free(entry.name); }
988 CaptureNameMap.clear();
991 bool QSABackendFactory::querySupport(ALCbackend_Type type)
992 { return (type == ALCbackend_Playback || type == ALCbackend_Capture); }
994 void QSABackendFactory::probe(enum DevProbe type, std::string *outnames)
996 auto add_device = [outnames](const DevMap &entry) -> void
998 const char *n = entry.name;
999 if(n && n[0])
1000 outnames->append(n, strlen(n)+1);
1003 switch (type)
1005 case ALL_DEVICE_PROBE:
1006 deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
1007 std::for_each(DeviceNameMap.cbegin(), DeviceNameMap.cend(), add_device);
1008 break;
1009 case CAPTURE_DEVICE_PROBE:
1010 deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
1011 std::for_each(CaptureNameMap.cbegin(), CaptureNameMap.cend(), add_device);
1012 break;
1016 ALCbackend *QSABackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
1018 if(type == ALCbackend_Playback)
1020 PlaybackWrapper *backend;
1021 NEW_OBJ(backend, PlaybackWrapper)(device);
1022 if(!backend) return NULL;
1023 return STATIC_CAST(ALCbackend, backend);
1025 if(type == ALCbackend_Capture)
1027 CaptureWrapper *backend;
1028 NEW_OBJ(backend, CaptureWrapper)(device);
1029 if(!backend) return NULL;
1030 return STATIC_CAST(ALCbackend, backend);
1033 return NULL;
1036 BackendFactory &QSABackendFactory::getFactory()
1038 static QSABackendFactory factory{};
1039 return factory;