Rename some struct members
[openal-soft.git] / Alc / backends / oss.cpp
blob58a4dbad623674febd52583c438ddc4c1f38949c
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 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/oss.h"
25 #include <sys/ioctl.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <memory.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <math.h>
38 #include <atomic>
39 #include <thread>
40 #include <vector>
41 #include <string>
42 #include <algorithm>
44 #include "alMain.h"
45 #include "alu.h"
46 #include "alconfig.h"
47 #include "ringbuffer.h"
48 #include "compat.h"
50 #include <sys/soundcard.h>
53 * The OSS documentation talks about SOUND_MIXER_READ, but the header
54 * only contains MIXER_READ. Play safe. Same for WRITE.
56 #ifndef SOUND_MIXER_READ
57 #define SOUND_MIXER_READ MIXER_READ
58 #endif
59 #ifndef SOUND_MIXER_WRITE
60 #define SOUND_MIXER_WRITE MIXER_WRITE
61 #endif
63 #if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
64 #define ALC_OSS_COMPAT
65 #endif
66 #ifndef SNDCTL_AUDIOINFO
67 #define ALC_OSS_COMPAT
68 #endif
71 * FreeBSD strongly discourages the use of specific devices,
72 * such as those returned in oss_audioinfo.devnode
74 #ifdef __FreeBSD__
75 #define ALC_OSS_DEVNODE_TRUC
76 #endif
78 namespace {
80 constexpr char DefaultName[] = "OSS Default";
81 const char *DefaultPlayback{"/dev/dsp"};
82 const char *DefaultCapture{"/dev/dsp"};
84 struct DevMap {
85 std::string name;
86 std::string device_name;
88 template<typename StrT0, typename StrT1>
89 DevMap(StrT0&& name_, StrT1&& devname_)
90 : name{std::forward<StrT0>(name_)}, device_name{std::forward<StrT1>(devname_)}
91 { }
94 bool checkName(const al::vector<DevMap> &list, const std::string &name)
96 return std::find_if(list.cbegin(), list.cend(),
97 [&name](const DevMap &entry) -> bool
98 { return entry.name == name; }
99 ) != list.cend();
102 al::vector<DevMap> PlaybackDevices;
103 al::vector<DevMap> CaptureDevices;
106 #ifdef ALC_OSS_COMPAT
108 #define DSP_CAP_OUTPUT 0x00020000
109 #define DSP_CAP_INPUT 0x00010000
110 void ALCossListPopulate(al::vector<DevMap> *devlist, int type)
112 devlist->emplace_back(DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback);
115 #else
117 void ALCossListAppend(al::vector<DevMap> *list, const char *handle, size_t hlen, const char *path, size_t plen)
119 #ifdef ALC_OSS_DEVNODE_TRUC
120 for(size_t i{0};i < plen;i++)
122 if(path[i] == '.')
124 if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
125 hlen = hlen + i - plen;
126 plen = i;
129 #endif
130 if(handle[0] == '\0')
132 handle = path;
133 hlen = plen;
136 std::string basename{handle, hlen};
137 basename.erase(std::find(basename.begin(), basename.end(), '\0'), basename.end());
138 std::string devname{path, plen};
139 devname.erase(std::find(devname.begin(), devname.end(), '\0'), devname.end());
141 auto iter = std::find_if(list->cbegin(), list->cend(),
142 [&devname](const DevMap &entry) -> bool
143 { return entry.device_name == devname; }
145 if(iter != list->cend())
146 return;
148 int count{1};
149 std::string newname{basename};
150 while(checkName(PlaybackDevices, newname))
152 newname = basename;
153 newname += " #";
154 newname += std::to_string(++count);
157 list->emplace_back(std::move(newname), std::move(devname));
158 const DevMap &entry = list->back();
160 TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
163 void ALCossListPopulate(al::vector<DevMap> *devlist, int type_flag)
165 int fd{open("/dev/mixer", O_RDONLY)};
166 if(fd < 0)
168 TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
169 goto done;
172 struct oss_sysinfo si;
173 if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
175 TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
176 goto done;
179 for(int i{0};i < si.numaudios;i++)
181 struct oss_audioinfo ai;
182 ai.dev = i;
183 if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
185 ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
186 continue;
188 if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
189 continue;
191 const char *handle;
192 size_t len;
193 if(ai.handle[0] != '\0')
195 len = strnlen(ai.handle, sizeof(ai.handle));
196 handle = ai.handle;
198 else
200 len = strnlen(ai.name, sizeof(ai.name));
201 handle = ai.name;
204 ALCossListAppend(devlist, handle, len, ai.devnode,
205 strnlen(ai.devnode, sizeof(ai.devnode)));
208 done:
209 if(fd >= 0)
210 close(fd);
211 fd = -1;
213 const char *defdev{(type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback};
214 auto iter = std::find_if(devlist->cbegin(), devlist->cend(),
215 [defdev](const DevMap &entry) -> bool
216 { return entry.device_name == defdev; }
218 if(iter == devlist->cend())
219 devlist->insert(devlist->begin(), DevMap{DefaultName, defdev});
220 else
222 DevMap entry{std::move(*iter)};
223 devlist->erase(iter);
224 devlist->insert(devlist->begin(), std::move(entry));
226 devlist->shrink_to_fit();
229 #endif
231 int log2i(ALCuint x)
233 int y = 0;
234 while (x > 1)
236 x >>= 1;
237 y++;
239 return y;
243 struct ALCplaybackOSS final : public ALCbackend {
244 int fd{-1};
246 al::vector<ALubyte> mMixData;
248 std::atomic<ALenum> mKillNow{AL_TRUE};
249 std::thread mThread;
252 int ALCplaybackOSS_mixerProc(ALCplaybackOSS *self);
254 void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
255 void ALCplaybackOSS_Destruct(ALCplaybackOSS *self);
256 ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
257 ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
258 ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
259 void ALCplaybackOSS_stop(ALCplaybackOSS *self);
260 DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
261 DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
262 DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
263 DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
264 DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
265 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
266 DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
269 void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
271 new (self) ALCplaybackOSS{};
272 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
273 SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
276 void ALCplaybackOSS_Destruct(ALCplaybackOSS *self)
278 if(self->fd != -1)
279 close(self->fd);
280 self->fd = -1;
282 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
283 self->~ALCplaybackOSS();
287 int ALCplaybackOSS_mixerProc(ALCplaybackOSS *self)
289 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
290 struct timeval timeout;
291 ALubyte *write_ptr;
292 ALint frame_size;
293 ALint to_write;
294 ssize_t wrote;
295 fd_set wfds;
296 int sret;
298 SetRTPriority();
299 althrd_setname(MIXER_THREAD_NAME);
301 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->mAmbiOrder);
303 ALCplaybackOSS_lock(self);
304 while(!self->mKillNow.load(std::memory_order_acquire) &&
305 ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
307 FD_ZERO(&wfds);
308 FD_SET(self->fd, &wfds);
309 timeout.tv_sec = 1;
310 timeout.tv_usec = 0;
312 ALCplaybackOSS_unlock(self);
313 sret = select(self->fd+1, nullptr, &wfds, nullptr, &timeout);
314 ALCplaybackOSS_lock(self);
315 if(sret < 0)
317 if(errno == EINTR)
318 continue;
319 ERR("select failed: %s\n", strerror(errno));
320 aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
321 break;
323 else if(sret == 0)
325 WARN("select timeout\n");
326 continue;
329 write_ptr = self->mMixData.data();
330 to_write = self->mMixData.size();
331 aluMixData(device, write_ptr, to_write/frame_size);
332 while(to_write > 0 && !self->mKillNow.load())
334 wrote = write(self->fd, write_ptr, to_write);
335 if(wrote < 0)
337 if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
338 continue;
339 ERR("write failed: %s\n", strerror(errno));
340 aluHandleDisconnect(device, "Failed writing playback samples: %s",
341 strerror(errno));
342 break;
345 to_write -= wrote;
346 write_ptr += wrote;
349 ALCplaybackOSS_unlock(self);
351 return 0;
355 ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
357 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
359 const char *devname{DefaultPlayback};
360 if(!name)
361 name = DefaultName;
362 else
364 if(PlaybackDevices.empty())
365 ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
367 auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
368 [&name](const DevMap &entry) -> bool
369 { return entry.name == name; }
371 if(iter == PlaybackDevices.cend())
372 return ALC_INVALID_VALUE;
373 devname = iter->device_name.c_str();
376 self->fd = open(devname, O_WRONLY);
377 if(self->fd == -1)
379 ERR("Could not open %s: %s\n", devname, strerror(errno));
380 return ALC_INVALID_VALUE;
383 device->DeviceName = name;
384 return ALC_NO_ERROR;
387 ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
389 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
390 int numFragmentsLogSize;
391 int log2FragmentSize;
392 unsigned int periods;
393 audio_buf_info info;
394 ALuint frameSize;
395 int numChannels;
396 int ossFormat;
397 int ossSpeed;
398 const char *err;
400 switch(device->FmtType)
402 case DevFmtByte:
403 ossFormat = AFMT_S8;
404 break;
405 case DevFmtUByte:
406 ossFormat = AFMT_U8;
407 break;
408 case DevFmtUShort:
409 case DevFmtInt:
410 case DevFmtUInt:
411 case DevFmtFloat:
412 device->FmtType = DevFmtShort;
413 /* fall-through */
414 case DevFmtShort:
415 ossFormat = AFMT_S16_NE;
416 break;
419 periods = device->NumUpdates;
420 numChannels = ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
421 ossSpeed = device->Frequency;
422 frameSize = numChannels * BytesFromDevFmt(device->FmtType);
423 /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
424 log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4);
425 numFragmentsLogSize = (periods << 16) | log2FragmentSize;
427 #define CHECKERR(func) if((func) < 0) { \
428 err = #func; \
429 goto err; \
431 /* Don't fail if SETFRAGMENT fails. We can handle just about anything
432 * that's reported back via GETOSPACE */
433 ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
434 CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
435 CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
436 CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
437 CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info));
438 if(0)
440 err:
441 ERR("%s failed: %s\n", err, strerror(errno));
442 return ALC_FALSE;
444 #undef CHECKERR
446 if((int)ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder) != numChannels)
448 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
449 return ALC_FALSE;
452 if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
453 (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
454 (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
456 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
457 return ALC_FALSE;
460 device->Frequency = ossSpeed;
461 device->UpdateSize = info.fragsize / frameSize;
462 device->NumUpdates = info.fragments;
464 SetDefaultChannelOrder(device);
466 return ALC_TRUE;
469 ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
471 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
473 try {
474 self->mMixData.resize(device->UpdateSize * FrameSizeFromDevFmt(
475 device->FmtChans, device->FmtType, device->mAmbiOrder
478 self->mKillNow.store(AL_FALSE);
479 self->mThread = std::thread(ALCplaybackOSS_mixerProc, self);
480 return ALC_TRUE;
482 catch(std::exception& e) {
483 ERR("Could not create playback thread: %s\n", e.what());
485 catch(...) {
487 return ALC_FALSE;
490 void ALCplaybackOSS_stop(ALCplaybackOSS *self)
492 if(self->mKillNow.exchange(AL_TRUE) || !self->mThread.joinable())
493 return;
494 self->mThread.join();
496 if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
497 ERR("Error resetting device: %s\n", strerror(errno));
499 self->mMixData.clear();
503 struct ALCcaptureOSS final : public ALCbackend {
504 int fd{-1};
506 ll_ringbuffer_t *mRing{nullptr};
508 std::atomic<ALenum> mKillNow{AL_TRUE};
509 std::thread mThread;
512 int ALCcaptureOSS_recordProc(ALCcaptureOSS *self);
514 void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
515 void ALCcaptureOSS_Destruct(ALCcaptureOSS *self);
516 ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
517 DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
518 ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
519 void ALCcaptureOSS_stop(ALCcaptureOSS *self);
520 ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
521 ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
522 DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
523 DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
524 DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
525 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
526 DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
529 void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
531 new (self) ALCcaptureOSS{};
532 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
533 SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
536 void ALCcaptureOSS_Destruct(ALCcaptureOSS *self)
538 if(self->fd != -1)
539 close(self->fd);
540 self->fd = -1;
542 ll_ringbuffer_free(self->mRing);
543 self->mRing = nullptr;
544 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
545 self->~ALCcaptureOSS();
549 int ALCcaptureOSS_recordProc(ALCcaptureOSS *self)
551 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
552 struct timeval timeout;
553 int frame_size;
554 fd_set rfds;
555 ssize_t amt;
556 int sret;
558 SetRTPriority();
559 althrd_setname(RECORD_THREAD_NAME);
561 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->mAmbiOrder);
563 while(!self->mKillNow.load())
565 FD_ZERO(&rfds);
566 FD_SET(self->fd, &rfds);
567 timeout.tv_sec = 1;
568 timeout.tv_usec = 0;
570 sret = select(self->fd+1, &rfds, nullptr, nullptr, &timeout);
571 if(sret < 0)
573 if(errno == EINTR)
574 continue;
575 ERR("select failed: %s\n", strerror(errno));
576 aluHandleDisconnect(device, "Failed to check capture samples: %s", strerror(errno));
577 break;
579 else if(sret == 0)
581 WARN("select timeout\n");
582 continue;
585 auto vec = ll_ringbuffer_get_write_vector(self->mRing);
586 if(vec.first.len > 0)
588 amt = read(self->fd, vec.first.buf, vec.first.len*frame_size);
589 if(amt < 0)
591 ERR("read failed: %s\n", strerror(errno));
592 ALCcaptureOSS_lock(self);
593 aluHandleDisconnect(device, "Failed reading capture samples: %s", strerror(errno));
594 ALCcaptureOSS_unlock(self);
595 break;
597 ll_ringbuffer_write_advance(self->mRing, amt/frame_size);
601 return 0;
605 ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
607 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
609 const char *devname{DefaultCapture};
610 if(!name)
611 name = DefaultName;
612 else
614 if(CaptureDevices.empty())
615 ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
617 auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
618 [&name](const DevMap &entry) -> bool
619 { return entry.name == name; }
621 if(iter == CaptureDevices.cend())
622 return ALC_INVALID_VALUE;
623 devname = iter->device_name.c_str();
626 self->fd = open(devname, O_RDONLY);
627 if(self->fd == -1)
629 ERR("Could not open %s: %s\n", devname, strerror(errno));
630 return ALC_INVALID_VALUE;
633 int ossFormat{};
634 switch(device->FmtType)
636 case DevFmtByte:
637 ossFormat = AFMT_S8;
638 break;
639 case DevFmtUByte:
640 ossFormat = AFMT_U8;
641 break;
642 case DevFmtShort:
643 ossFormat = AFMT_S16_NE;
644 break;
645 case DevFmtUShort:
646 case DevFmtInt:
647 case DevFmtUInt:
648 case DevFmtFloat:
649 ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
650 return ALC_INVALID_VALUE;
653 int periods{4};
654 int numChannels{ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder)};
655 int frameSize{numChannels * BytesFromDevFmt(device->FmtType)};
656 int ossSpeed{static_cast<int>(device->Frequency)};
657 int log2FragmentSize{log2i(device->UpdateSize * device->NumUpdates *
658 frameSize / periods)};
660 /* according to the OSS spec, 16 bytes are the minimum */
661 log2FragmentSize = std::max(log2FragmentSize, 4);
662 int numFragmentsLogSize{(periods << 16) | log2FragmentSize};
664 audio_buf_info info;
665 const char *err;
666 #define CHECKERR(func) if((func) < 0) { \
667 err = #func; \
668 goto err; \
670 CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
671 CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
672 CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
673 CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
674 CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info));
675 if(0)
677 err:
678 ERR("%s failed: %s\n", err, strerror(errno));
679 close(self->fd);
680 self->fd = -1;
681 return ALC_INVALID_VALUE;
683 #undef CHECKERR
685 if((int)ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder) != numChannels)
687 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
688 close(self->fd);
689 self->fd = -1;
690 return ALC_INVALID_VALUE;
693 if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
694 (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
695 (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
697 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
698 close(self->fd);
699 self->fd = -1;
700 return ALC_INVALID_VALUE;
703 self->mRing = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, frameSize, false);
704 if(!self->mRing)
706 ERR("Ring buffer create failed\n");
707 close(self->fd);
708 self->fd = -1;
709 return ALC_OUT_OF_MEMORY;
712 device->DeviceName = name;
713 return ALC_NO_ERROR;
716 ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
718 try {
719 self->mKillNow.store(AL_FALSE);
720 self->mThread = std::thread(ALCcaptureOSS_recordProc, self);
721 return ALC_TRUE;
723 catch(std::exception& e) {
724 ERR("Could not create record thread: %s\n", e.what());
726 catch(...) {
728 return ALC_FALSE;
731 void ALCcaptureOSS_stop(ALCcaptureOSS *self)
733 if(self->mKillNow.exchange(AL_TRUE) || !self->mThread.joinable())
734 return;
736 self->mThread.join();
738 if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
739 ERR("Error resetting device: %s\n", strerror(errno));
742 ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
744 ll_ringbuffer_read(self->mRing, static_cast<char*>(buffer), samples);
745 return ALC_NO_ERROR;
748 ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
750 return ll_ringbuffer_read_space(self->mRing);
753 } // namespace
756 BackendFactory &OSSBackendFactory::getFactory()
758 static OSSBackendFactory factory{};
759 return factory;
762 bool OSSBackendFactory::init()
764 ConfigValueStr(nullptr, "oss", "device", &DefaultPlayback);
765 ConfigValueStr(nullptr, "oss", "capture", &DefaultCapture);
767 return true;
770 void OSSBackendFactory::deinit()
772 PlaybackDevices.clear();
773 CaptureDevices.clear();
776 bool OSSBackendFactory::querySupport(ALCbackend_Type type)
777 { return (type == ALCbackend_Playback || type == ALCbackend_Capture); }
779 void OSSBackendFactory::probe(enum DevProbe type, std::string *outnames)
781 auto add_device = [outnames](const DevMap &entry) -> void
783 #ifdef HAVE_STAT
784 struct stat buf;
785 if(stat(entry.device_name.c_str(), &buf) == 0)
786 #endif
788 /* Includes null char. */
789 outnames->append(entry.name.c_str(), entry.name.length()+1);
793 switch(type)
795 case ALL_DEVICE_PROBE:
796 PlaybackDevices.clear();
797 ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
798 std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
799 break;
801 case CAPTURE_DEVICE_PROBE:
802 CaptureDevices.clear();
803 ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
804 std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
805 break;
809 ALCbackend *OSSBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
811 if(type == ALCbackend_Playback)
813 ALCplaybackOSS *backend;
814 NEW_OBJ(backend, ALCplaybackOSS)(device);
815 if(!backend) return nullptr;
816 return STATIC_CAST(ALCbackend, backend);
818 if(type == ALCbackend_Capture)
820 ALCcaptureOSS *backend;
821 NEW_OBJ(backend, ALCcaptureOSS)(device);
822 if(!backend) return nullptr;
823 return STATIC_CAST(ALCbackend, backend);
826 return nullptr;