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"
39 #include <sys/select.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 struct 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 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
;
206 althrd_setname(MIXER_THREAD_NAME
);
208 /* Increase default 10 priority to 11 to avoid jerky sound */
209 SchedGet(0, 0, ¶m
);
210 param
.sched_priority
=param
.sched_curpriority
+1;
211 SchedSet(0, 0, SCHED_NOCHANGE
, ¶m
);
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
))
221 FD_SET(data
->audio_fd
, &wfds
);
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
);
231 ERR("select error: %s\n", strerror(errno
));
232 aluHandleDisconnect(device
, "Failed waiting for playback buffer: %s", strerror(errno
));
237 ERR("select timeout\n");
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
);
249 if(errno
==EAGAIN
|| errno
==EWOULDBLOCK
)
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");
275 PlaybackWrapper_unlock(self
);
284 static ALCenum
qsa_open_playback(PlaybackWrapper
*self
, const ALCchar
* deviceName
)
286 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
290 std::unique_ptr
<qsa_data
> data
{new qsa_data
{}};
291 data
->mKillNow
.store(AL_TRUE
, std::memory_order_relaxed
);
294 deviceName
= qsaDevice
;
296 if(strcmp(deviceName
, qsaDevice
) == 0)
297 status
= snd_pcm_open_preferred(&data
->pcmHandle
, &card
, &dev
, SND_PCM_OPEN_PLAYBACK
);
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
);
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
);
329 static void qsa_close_playback(PlaybackWrapper
*self
)
331 qsa_data
*data
= self
->ExtraData
.get();
333 if (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();
350 switch(device
->FmtType
)
353 format
=SND_PCM_SFMT_S8
;
356 format
=SND_PCM_SFMT_U8
;
359 format
=SND_PCM_SFMT_S16_LE
;
362 format
=SND_PCM_SFMT_U16_LE
;
365 format
=SND_PCM_SFMT_S32_LE
;
368 format
=SND_PCM_SFMT_U32_LE
;
371 format
=SND_PCM_SFMT_FLOAT_LE
;
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
;
406 for (it
=0; it
<1; it
++)
408 /* Check for second pass */
411 original_rate
=ratelist
[0].rate
;
412 original_voices
=channellist
[0].channels
;
413 original_format
=formatlist
[0].format
;
417 /* At first downgrade sample format */
420 if (formatlist
[jt
].format
==data
->cparams
.format
.format
)
422 data
->cparams
.format
.format
=formatlist
[jt
+1].format
;
425 if (formatlist
[jt
].format
==0)
427 data
->cparams
.format
.format
=0;
433 if (data
->cparams
.format
.format
==0)
435 data
->cparams
.format
.format
=original_format
;
437 /* At secod downgrade sample rate */
440 if (ratelist
[jt
].rate
==data
->cparams
.format
.rate
)
442 data
->cparams
.format
.rate
=ratelist
[jt
+1].rate
;
445 if (ratelist
[jt
].rate
==0)
447 data
->cparams
.format
.rate
=0;
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 */
461 if(channellist
[jt
].channels
==data
->cparams
.format
.voices
)
463 data
->cparams
.format
.voices
=channellist
[jt
+1].channels
;
466 if (channellist
[jt
].channels
==0)
468 data
->cparams
.format
.voices
=0;
475 if (data
->cparams
.format
.voices
==0)
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)
496 if (data
->cparams
.format
.voices
!=0)
502 if (data
->cparams
.format
.voices
==0)
508 if ((snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_PLAYBACK
))<0)
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)
520 /* now fill back to the our AL device */
521 device
->Frequency
=data
->cparams
.format
.rate
;
523 switch (data
->cparams
.format
.voices
)
526 device
->FmtChans
=DevFmtMono
;
529 device
->FmtChans
=DevFmtStereo
;
532 device
->FmtChans
=DevFmtQuad
;
535 device
->FmtChans
=DevFmtX51
;
538 device
->FmtChans
=DevFmtX61
;
541 device
->FmtChans
=DevFmtX71
;
544 device
->FmtChans
=DevFmtMono
;
548 switch (data
->cparams
.format
.format
)
550 case SND_PCM_SFMT_S8
:
551 device
->FmtType
=DevFmtByte
;
553 case SND_PCM_SFMT_U8
:
554 device
->FmtType
=DevFmtUByte
;
556 case SND_PCM_SFMT_S16_LE
:
557 device
->FmtType
=DevFmtShort
;
559 case SND_PCM_SFMT_U16_LE
:
560 device
->FmtType
=DevFmtUShort
;
562 case SND_PCM_SFMT_S32_LE
:
563 device
->FmtType
=DevFmtInt
;
565 case SND_PCM_SFMT_U32_LE
:
566 device
->FmtType
=DevFmtUInt
;
568 case SND_PCM_SFMT_FLOAT_LE
:
569 device
->FmtType
=DevFmtFloat
;
572 device
->FmtType
=DevFmtShort
;
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
);
592 static ALCboolean
qsa_start_playback(PlaybackWrapper
*self
)
594 qsa_data
*data
= self
->ExtraData
.get();
597 data
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
598 data
->mThread
= std::thread(qsa_proc_playback
, self
);
601 catch(std::exception
& e
) {
602 ERR("Could not create playback thread: %s\n", e
.what());
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())
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
)
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
);
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
;
689 std::unique_ptr
<qsa_data
> data
{new qsa_data
{}};
692 deviceName
= qsaDevice
;
694 if(strcmp(deviceName
, qsaDevice
) == 0)
695 status
= snd_pcm_open_preferred(&data
->pcmHandle
, &card
, &dev
, SND_PCM_OPEN_CAPTURE
);
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
);
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
)
726 format
=SND_PCM_SFMT_S8
;
729 format
=SND_PCM_SFMT_U8
;
732 format
=SND_PCM_SFMT_S16_LE
;
735 format
=SND_PCM_SFMT_U16_LE
;
738 format
=SND_PCM_SFMT_S32_LE
;
741 format
=SND_PCM_SFMT_U32_LE
;
744 format
=SND_PCM_SFMT_FLOAT_LE
;
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
);
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();
797 if ((rstatus
=snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_CAPTURE
))<0)
799 ERR("capture prepare failed: %s\n", snd_strerror(rstatus
));
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
));
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
);
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
));
842 snd_pcm_capture_go(data
->pcmHandle
);
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();
857 snd_pcm_channel_status_t status
;
860 struct timeval timeout
;
862 ALint frame_size
=FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
);
863 ALint len
=samples
*frame_size
;
866 read_ptr
= static_cast<char*>(buffer
);
871 FD_SET(data
->audio_fd
, &rfds
);
875 /* Select also works like time slice to OS */
877 selectret
=select(data
->audio_fd
+1, &rfds
, NULL
, NULL
, &timeout
);
881 aluHandleDisconnect(device
, "Failed to check capture samples");
882 return ALC_INVALID_DEVICE
;
886 if (FD_ISSET(data
->audio_fd
, &rfds
))
888 bytes_read
=snd_pcm_plugin_read(data
->pcmHandle
, read_ptr
, len
);
896 if ((errno
==EAGAIN
) || (errno
==EWOULDBLOCK
))
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
);
921 read_ptr
+=bytes_read
;
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
)
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
);
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
);
975 bool QSABackendFactory::init()
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
;
1000 outnames
->append(n
, strlen(n
)+1);
1005 case ALL_DEVICE_PROBE
:
1006 deviceList(SND_PCM_CHANNEL_PLAYBACK
, &DeviceNameMap
);
1007 std::for_each(DeviceNameMap
.cbegin(), DeviceNameMap
.cend(), add_device
);
1009 case CAPTURE_DEVICE_PROBE
:
1010 deviceList(SND_PCM_CHANNEL_CAPTURE
, &CaptureNameMap
);
1011 std::for_each(CaptureNameMap
.cbegin(), CaptureNameMap
.cend(), add_device
);
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
);
1036 BackendFactory
&QSABackendFactory::getFactory()
1038 static QSABackendFactory factory
{};