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
23 #include "backends/oss.h"
25 #include <sys/ioctl.h>
26 #include <sys/types.h>
47 #include "ringbuffer.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
59 #ifndef SOUND_MIXER_WRITE
60 #define SOUND_MIXER_WRITE MIXER_WRITE
63 #if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
64 #define ALC_OSS_COMPAT
66 #ifndef SNDCTL_AUDIOINFO
67 #define ALC_OSS_COMPAT
71 * FreeBSD strongly discourages the use of specific devices,
72 * such as those returned in oss_audioinfo.devnode
75 #define ALC_OSS_DEVNODE_TRUC
80 constexpr char DefaultName
[] = "OSS Default";
81 const char *DefaultPlayback
{"/dev/dsp"};
82 const char *DefaultCapture
{"/dev/dsp"};
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_
)}
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
; }
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
);
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
++)
124 if(strncmp(path
+ i
, handle
+ hlen
+ i
- plen
, plen
- i
) == 0)
125 hlen
= hlen
+ i
- plen
;
130 if(handle
[0] == '\0')
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())
149 std::string newname
{basename
};
150 while(checkName(PlaybackDevices
, 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
)};
168 TRACE("Could not open /dev/mixer: %s\n", strerror(errno
));
172 struct oss_sysinfo si
;
173 if(ioctl(fd
, SNDCTL_SYSINFO
, &si
) == -1)
175 TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno
));
179 for(int i
{0};i
< si
.numaudios
;i
++)
181 struct oss_audioinfo ai
;
183 if(ioctl(fd
, SNDCTL_AUDIOINFO
, &ai
) == -1)
185 ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i
, strerror(errno
));
188 if(!(ai
.caps
&type_flag
) || ai
.devnode
[0] == '\0')
193 if(ai
.handle
[0] != '\0')
195 len
= strnlen(ai
.handle
, sizeof(ai
.handle
));
200 len
= strnlen(ai
.name
, sizeof(ai
.name
));
204 ALCossListAppend(devlist
, handle
, len
, ai
.devnode
,
205 strnlen(ai
.devnode
, sizeof(ai
.devnode
)));
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
});
222 DevMap entry
{std::move(*iter
)};
223 devlist
->erase(iter
);
224 devlist
->insert(devlist
->begin(), std::move(entry
));
226 devlist
->shrink_to_fit();
243 struct ALCplaybackOSS final
: public ALCbackend
{
246 al::vector
<ALubyte
> mMixData
;
248 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
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
)
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
;
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
))
308 FD_SET(self
->fd
, &wfds
);
312 ALCplaybackOSS_unlock(self
);
313 sret
= select(self
->fd
+1, nullptr, &wfds
, nullptr, &timeout
);
314 ALCplaybackOSS_lock(self
);
319 ERR("select failed: %s\n", strerror(errno
));
320 aluHandleDisconnect(device
, "Failed waiting for playback buffer: %s", strerror(errno
));
325 WARN("select timeout\n");
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
);
337 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
339 ERR("write failed: %s\n", strerror(errno
));
340 aluHandleDisconnect(device
, "Failed writing playback samples: %s",
349 ALCplaybackOSS_unlock(self
);
355 ALCenum
ALCplaybackOSS_open(ALCplaybackOSS
*self
, const ALCchar
*name
)
357 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
359 const char *devname
{DefaultPlayback
};
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
);
379 ERR("Could not open %s: %s\n", devname
, strerror(errno
));
380 return ALC_INVALID_VALUE
;
383 device
->DeviceName
= name
;
387 ALCboolean
ALCplaybackOSS_reset(ALCplaybackOSS
*self
)
389 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
390 int numFragmentsLogSize
;
391 int log2FragmentSize
;
392 unsigned int periods
;
400 switch(device
->FmtType
)
412 device
->FmtType
= DevFmtShort
;
415 ossFormat
= AFMT_S16_NE
;
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) { \
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
));
441 ERR("%s failed: %s\n", err
, strerror(errno
));
446 if((int)ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
) != numChannels
)
448 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device
->FmtChans
), numChannels
);
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
);
460 device
->Frequency
= ossSpeed
;
461 device
->UpdateSize
= info
.fragsize
/ frameSize
;
462 device
->NumUpdates
= info
.fragments
;
464 SetDefaultChannelOrder(device
);
469 ALCboolean
ALCplaybackOSS_start(ALCplaybackOSS
*self
)
471 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
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
);
482 catch(std::exception
& e
) {
483 ERR("Could not create playback thread: %s\n", e
.what());
490 void ALCplaybackOSS_stop(ALCplaybackOSS
*self
)
492 if(self
->mKillNow
.exchange(AL_TRUE
) || !self
->mThread
.joinable())
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
{
506 ll_ringbuffer_t
*mRing
{nullptr};
508 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
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
)
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
;
559 althrd_setname(RECORD_THREAD_NAME
);
561 frame_size
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
);
563 while(!self
->mKillNow
.load())
566 FD_SET(self
->fd
, &rfds
);
570 sret
= select(self
->fd
+1, &rfds
, nullptr, nullptr, &timeout
);
575 ERR("select failed: %s\n", strerror(errno
));
576 aluHandleDisconnect(device
, "Failed to check capture samples: %s", strerror(errno
));
581 WARN("select timeout\n");
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
);
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
);
597 ll_ringbuffer_write_advance(self
->mRing
, amt
/frame_size
);
605 ALCenum
ALCcaptureOSS_open(ALCcaptureOSS
*self
, const ALCchar
*name
)
607 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
609 const char *devname
{DefaultCapture
};
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
);
629 ERR("Could not open %s: %s\n", devname
, strerror(errno
));
630 return ALC_INVALID_VALUE
;
634 switch(device
->FmtType
)
643 ossFormat
= AFMT_S16_NE
;
649 ERR("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
650 return ALC_INVALID_VALUE
;
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
};
666 #define CHECKERR(func) if((func) < 0) { \
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
));
678 ERR("%s failed: %s\n", err
, strerror(errno
));
681 return ALC_INVALID_VALUE
;
685 if((int)ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
) != numChannels
)
687 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device
->FmtChans
), numChannels
);
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
);
700 return ALC_INVALID_VALUE
;
703 self
->mRing
= ll_ringbuffer_create(device
->UpdateSize
*device
->NumUpdates
, frameSize
, false);
706 ERR("Ring buffer create failed\n");
709 return ALC_OUT_OF_MEMORY
;
712 device
->DeviceName
= name
;
716 ALCboolean
ALCcaptureOSS_start(ALCcaptureOSS
*self
)
719 self
->mKillNow
.store(AL_FALSE
);
720 self
->mThread
= std::thread(ALCcaptureOSS_recordProc
, self
);
723 catch(std::exception
& e
) {
724 ERR("Could not create record thread: %s\n", e
.what());
731 void ALCcaptureOSS_stop(ALCcaptureOSS
*self
)
733 if(self
->mKillNow
.exchange(AL_TRUE
) || !self
->mThread
.joinable())
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
);
748 ALCuint
ALCcaptureOSS_availableSamples(ALCcaptureOSS
*self
)
750 return ll_ringbuffer_read_space(self
->mRing
);
756 BackendFactory
&OSSBackendFactory::getFactory()
758 static OSSBackendFactory factory
{};
762 bool OSSBackendFactory::init()
764 ConfigValueStr(nullptr, "oss", "device", &DefaultPlayback
);
765 ConfigValueStr(nullptr, "oss", "capture", &DefaultCapture
);
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
785 if(stat(entry
.device_name
.c_str(), &buf
) == 0)
788 /* Includes null char. */
789 outnames
->append(entry
.name
.c_str(), entry
.name
.length()+1);
795 case ALL_DEVICE_PROBE
:
796 PlaybackDevices
.clear();
797 ALCossListPopulate(&PlaybackDevices
, DSP_CAP_OUTPUT
);
798 std::for_each(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(), add_device
);
801 case CAPTURE_DEVICE_PROBE
:
802 CaptureDevices
.clear();
803 ALCossListPopulate(&CaptureDevices
, DSP_CAP_INPUT
);
804 std::for_each(CaptureDevices
.cbegin(), CaptureDevices
.cend(), add_device
);
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
);