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
23 #include "backends/qsa.h"
40 #include <sys/asoundlib.h>
41 #include <sys/neutrino.h>
47 snd_pcm_t
* pcmHandle
{nullptr};
50 snd_pcm_channel_setup_t csetup
{};
51 snd_pcm_channel_params_t cparams
{};
53 ALvoid
* buffer
{nullptr};
56 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
66 al::vector
<DevMap
> DeviceNameMap
;
67 al::vector
<DevMap
> CaptureNameMap
;
69 constexpr ALCchar qsaDevice
[] = "QSA Default";
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
},
115 void deviceList(int type
, al::vector
<DevMap
> *devmap
)
118 snd_pcm_info_t pcminfo
;
119 int max_cards
, card
, err
, dev
;
122 snd_ctl_hw_info info
;
124 max_cards
= snd_cards();
128 std::for_each(devmap
->begin(), devmap
->end(),
129 [](const DevMap
&entry
) -> void
130 { free(entry
.name
); }
134 entry
.name
= strdup(qsaDevice
);
137 devmap
->push_back(entry
);
139 for(card
= 0;card
< max_cards
;card
++)
141 if((err
=snd_ctl_open(&handle
, card
)) < 0)
144 if((err
=snd_ctl_hw_info(handle
, &info
)) < 0)
146 snd_ctl_close(handle
);
150 for(dev
= 0;dev
< (int)info
.pcmdevs
;dev
++)
152 if((err
=snd_ctl_pcm_info(handle
, dev
, &pcminfo
)) < 0)
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
);
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
;
200 althrd_setname(MIXER_THREAD_NAME
);
202 /* Increase default 10 priority to 11 to avoid jerky sound */
203 SchedGet(0, 0, ¶m
);
204 param
.sched_priority
=param
.sched_curpriority
+1;
205 SchedSet(0, 0, SCHED_NOCHANGE
, ¶m
);
207 const ALint frame_size
= device
->frameSizeFromFmt();
210 while(!data
->mKillNow
.load(std::memory_order_acquire
))
213 pollitem
.fd
= data
->audio_fd
;
214 pollitem
.events
= POLLOUT
;
216 /* Select also works like time slice to OS */
218 sret
= poll(&pollitem
, 1, 2000);
222 if(errno
== EINTR
|| errno
== EAGAIN
)
224 ERR("poll error: %s\n", strerror(errno
));
225 aluHandleDisconnect(device
, "Failed waiting for playback buffer: %s", strerror(errno
));
230 ERR("poll timeout\n");
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
);
242 if(errno
==EAGAIN
|| errno
==EWOULDBLOCK
)
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");
277 static ALCenum
qsa_open_playback(PlaybackWrapper
*self
, const ALCchar
* deviceName
)
279 ALCdevice
*device
= self
->mDevice
;
283 std::unique_ptr
<qsa_data
> data
{new qsa_data
{}};
284 data
->mKillNow
.store(AL_TRUE
, std::memory_order_relaxed
);
287 deviceName
= qsaDevice
;
289 if(strcmp(deviceName
, qsaDevice
) == 0)
290 status
= snd_pcm_open_preferred(&data
->pcmHandle
, &card
, &dev
, SND_PCM_OPEN_PLAYBACK
);
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
);
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
);
322 static void qsa_close_playback(PlaybackWrapper
*self
)
324 qsa_data
*data
= self
->mExtraData
.get();
326 if (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();
343 switch(device
->FmtType
)
346 format
=SND_PCM_SFMT_S8
;
349 format
=SND_PCM_SFMT_U8
;
352 format
=SND_PCM_SFMT_S16_LE
;
355 format
=SND_PCM_SFMT_U16_LE
;
358 format
=SND_PCM_SFMT_S32_LE
;
361 format
=SND_PCM_SFMT_U32_LE
;
364 format
=SND_PCM_SFMT_FLOAT_LE
;
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
;
398 for (it
=0; it
<1; it
++)
400 /* Check for second pass */
403 original_rate
=ratelist
[0].rate
;
404 original_voices
=channellist
[0].channels
;
405 original_format
=formatlist
[0].format
;
409 /* At first downgrade sample format */
412 if (formatlist
[jt
].format
==data
->cparams
.format
.format
)
414 data
->cparams
.format
.format
=formatlist
[jt
+1].format
;
417 if (formatlist
[jt
].format
==0)
419 data
->cparams
.format
.format
=0;
425 if (data
->cparams
.format
.format
==0)
427 data
->cparams
.format
.format
=original_format
;
429 /* At secod downgrade sample rate */
432 if (ratelist
[jt
].rate
==data
->cparams
.format
.rate
)
434 data
->cparams
.format
.rate
=ratelist
[jt
+1].rate
;
437 if (ratelist
[jt
].rate
==0)
439 data
->cparams
.format
.rate
=0;
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 */
453 if(channellist
[jt
].channels
==data
->cparams
.format
.voices
)
455 data
->cparams
.format
.voices
=channellist
[jt
+1].channels
;
458 if (channellist
[jt
].channels
==0)
460 data
->cparams
.format
.voices
=0;
467 if (data
->cparams
.format
.voices
==0)
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)
488 if (data
->cparams
.format
.voices
!=0)
494 if (data
->cparams
.format
.voices
==0)
500 if ((snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_PLAYBACK
))<0)
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)
512 /* now fill back to the our AL device */
513 device
->Frequency
=data
->cparams
.format
.rate
;
515 switch (data
->cparams
.format
.voices
)
518 device
->FmtChans
=DevFmtMono
;
521 device
->FmtChans
=DevFmtStereo
;
524 device
->FmtChans
=DevFmtQuad
;
527 device
->FmtChans
=DevFmtX51
;
530 device
->FmtChans
=DevFmtX61
;
533 device
->FmtChans
=DevFmtX71
;
536 device
->FmtChans
=DevFmtMono
;
540 switch (data
->cparams
.format
.format
)
542 case SND_PCM_SFMT_S8
:
543 device
->FmtType
=DevFmtByte
;
545 case SND_PCM_SFMT_U8
:
546 device
->FmtType
=DevFmtUByte
;
548 case SND_PCM_SFMT_S16_LE
:
549 device
->FmtType
=DevFmtShort
;
551 case SND_PCM_SFMT_U16_LE
:
552 device
->FmtType
=DevFmtUShort
;
554 case SND_PCM_SFMT_S32_LE
:
555 device
->FmtType
=DevFmtInt
;
557 case SND_PCM_SFMT_U32_LE
:
558 device
->FmtType
=DevFmtUInt
;
560 case SND_PCM_SFMT_FLOAT_LE
:
561 device
->FmtType
=DevFmtFloat
;
564 device
->FmtType
=DevFmtShort
;
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
);
583 static ALCboolean
qsa_start_playback(PlaybackWrapper
*self
)
585 qsa_data
*data
= self
->mExtraData
.get();
588 data
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
589 data
->mThread
= std::thread(qsa_proc_playback
, self
);
592 catch(std::exception
& e
) {
593 ERR("Could not create playback thread: %s\n", e
.what());
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())
606 data
->mThread
.join();
610 PlaybackWrapper::~PlaybackWrapper()
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); }
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
;
655 std::unique_ptr
<qsa_data
> data
{new qsa_data
{}};
658 deviceName
= qsaDevice
;
660 if(strcmp(deviceName
, qsaDevice
) == 0)
661 status
= snd_pcm_open_preferred(&data
->pcmHandle
, &card
, &dev
, SND_PCM_OPEN_CAPTURE
);
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
);
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
)
692 format
=SND_PCM_SFMT_S8
;
695 format
=SND_PCM_SFMT_U8
;
698 format
=SND_PCM_SFMT_S16_LE
;
701 format
=SND_PCM_SFMT_U16_LE
;
704 format
=SND_PCM_SFMT_S32_LE
;
707 format
=SND_PCM_SFMT_U32_LE
;
710 format
=SND_PCM_SFMT_FLOAT_LE
;
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
);
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();
762 if ((rstatus
=snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_CAPTURE
))<0)
764 ERR("capture prepare failed: %s\n", snd_strerror(rstatus
));
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
));
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();
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
));
807 snd_pcm_capture_go(data
->pcmHandle
);
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();
822 snd_pcm_channel_status_t status
;
825 ALint frame_size
=device
->frameSizeFromFmt();
826 ALint len
=samples
*frame_size
;
829 read_ptr
= static_cast<char*>(buffer
);
834 pollitem
.fd
= data
->audio_fd
;
835 pollitem
.events
= POLLOUT
;
837 /* Select also works like time slice to OS */
839 selectret
= poll(&pollitem
, 1, 2000);
843 aluHandleDisconnect(device
, "Failed to check capture samples");
844 return ALC_INVALID_DEVICE
;
848 bytes_read
=snd_pcm_plugin_read(data
->pcmHandle
, read_ptr
, len
);
854 if ((errno
==EAGAIN
) || (errno
==EWOULDBLOCK
))
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
);
879 read_ptr
+=bytes_read
;
888 CaptureWrapper::~CaptureWrapper()
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); }
912 bool QSABackendFactory::init()
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
;
924 outnames
->append(n
, strlen(n
)+1);
929 case DevProbe::Playback
:
930 deviceList(SND_PCM_CHANNEL_PLAYBACK
, &DeviceNameMap
);
931 std::for_each(DeviceNameMap
.cbegin(), DeviceNameMap
.cend(), add_device
);
933 case DevProbe::Capture
:
934 deviceList(SND_PCM_CHANNEL_CAPTURE
, &CaptureNameMap
);
935 std::for_each(CaptureNameMap
.cbegin(), CaptureNameMap
.cend(), add_device
);
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
}};
949 BackendFactory
&QSABackendFactory::getFactory()
951 static QSABackendFactory factory
{};