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 <sys/ioctl.h>
24 #include <sys/types.h>
41 #include "backends/base.h"
43 #include <sys/soundcard.h>
46 * The OSS documentation talks about SOUND_MIXER_READ, but the header
47 * only contains MIXER_READ. Play safe. Same for WRITE.
49 #ifndef SOUND_MIXER_READ
50 #define SOUND_MIXER_READ MIXER_READ
52 #ifndef SOUND_MIXER_WRITE
53 #define SOUND_MIXER_WRITE MIXER_WRITE
56 #if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
57 #define ALC_OSS_COMPAT
59 #ifndef SNDCTL_AUDIOINFO
60 #define ALC_OSS_COMPAT
64 * FreeBSD strongly discourages the use of specific devices,
65 * such as those returned in oss_audioinfo.devnode
68 #define ALC_OSS_DEVNODE_TRUC
72 const ALCchar
*handle
;
74 struct oss_device
*next
;
77 static struct oss_device oss_playback
= {
83 static struct oss_device oss_capture
= {
91 #define DSP_CAP_OUTPUT 0x00020000
92 #define DSP_CAP_INPUT 0x00010000
93 static void ALCossListPopulate(struct oss_device
*UNUSED(devlist
), int UNUSED(type_flag
))
100 static size_t strnlen(const char *str
, size_t maxlen
)
102 const char *end
= memchr(str
, 0, maxlen
);
103 if(!end
) return maxlen
;
108 static void ALCossListAppend(struct oss_device
*list
, const char *handle
, size_t hlen
, const char *path
, size_t plen
)
110 struct oss_device
*next
;
111 struct oss_device
*last
;
114 /* skip the first item "OSS Default" */
117 #ifdef ALC_OSS_DEVNODE_TRUC
118 for(i
= 0;i
< plen
;i
++)
122 if(strncmp(path
+ i
, handle
+ hlen
+ i
- plen
, plen
- i
) == 0)
123 hlen
= hlen
+ i
- plen
;
130 if(handle
[0] == '\0')
138 if(strncmp(next
->path
, path
, plen
) == 0)
144 next
= (struct oss_device
*)malloc(sizeof(struct oss_device
) + hlen
+ plen
+ 2);
145 next
->handle
= (char*)(next
+ 1);
146 next
->path
= next
->handle
+ hlen
+ 1;
150 strncpy((char*)next
->handle
, handle
, hlen
);
151 ((char*)next
->handle
)[hlen
] = '\0';
152 strncpy((char*)next
->path
, path
, plen
);
153 ((char*)next
->path
)[plen
] = '\0';
155 TRACE("Got device \"%s\", \"%s\"\n", next
->handle
, next
->path
);
158 static void ALCossListPopulate(struct oss_device
*devlist
, int type_flag
)
160 struct oss_sysinfo si
;
161 struct oss_audioinfo ai
;
164 if((fd
=open("/dev/mixer", O_RDONLY
)) < 0)
166 ERR("Could not open /dev/mixer\n");
169 if(ioctl(fd
, SNDCTL_SYSINFO
, &si
) == -1)
171 ERR("SNDCTL_SYSINFO failed: %s\n", strerror(errno
));
174 for(i
= 0;i
< si
.numaudios
;i
++)
180 if(ioctl(fd
, SNDCTL_AUDIOINFO
, &ai
) == -1)
182 ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i
, strerror(errno
));
185 if(ai
.devnode
[0] == '\0')
188 if(ai
.handle
[0] != '\0')
190 len
= strnlen(ai
.handle
, sizeof(ai
.handle
));
195 len
= strnlen(ai
.name
, sizeof(ai
.name
));
198 if((ai
.caps
&type_flag
))
199 ALCossListAppend(devlist
, handle
, len
, ai
.devnode
,
200 strnlen(ai
.devnode
, sizeof(ai
.devnode
)));
209 static void ALCossListFree(struct oss_device
*list
)
211 struct oss_device
*cur
;
215 /* skip the first item "OSS Default" */
221 struct oss_device
*next
= cur
->next
;
227 static int log2i(ALCuint x
)
238 typedef struct ALCplaybackOSS
{
239 DERIVE_FROM_TYPE(ALCbackend
);
246 ATOMIC(ALenum
) killNow
;
250 static int ALCplaybackOSS_mixerProc(void *ptr
);
252 static void ALCplaybackOSS_Construct(ALCplaybackOSS
*self
, ALCdevice
*device
);
253 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, void, Destruct
)
254 static ALCenum
ALCplaybackOSS_open(ALCplaybackOSS
*self
, const ALCchar
*name
);
255 static void ALCplaybackOSS_close(ALCplaybackOSS
*self
);
256 static ALCboolean
ALCplaybackOSS_reset(ALCplaybackOSS
*self
);
257 static ALCboolean
ALCplaybackOSS_start(ALCplaybackOSS
*self
);
258 static void ALCplaybackOSS_stop(ALCplaybackOSS
*self
);
259 static DECLARE_FORWARD2(ALCplaybackOSS
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
260 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, ALCuint
, availableSamples
)
261 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, ClockLatency
, getClockLatency
)
262 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, void, lock
)
263 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, void, unlock
)
264 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS
)
265 DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS
);
268 static int ALCplaybackOSS_mixerProc(void *ptr
)
270 ALCplaybackOSS
*self
= (ALCplaybackOSS
*)ptr
;
271 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
272 struct timeval timeout
;
281 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
283 frame_size
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
285 ALCplaybackOSS_lock(self
);
286 while(!ATOMIC_LOAD_SEQ(&self
->killNow
) && device
->Connected
)
289 FD_SET(self
->fd
, &wfds
);
293 ALCplaybackOSS_unlock(self
);
294 sret
= select(self
->fd
+1, NULL
, &wfds
, NULL
, &timeout
);
295 ALCplaybackOSS_lock(self
);
300 ERR("select failed: %s\n", strerror(errno
));
301 aluHandleDisconnect(device
);
306 WARN("select timeout\n");
310 write_ptr
= self
->mix_data
;
311 to_write
= self
->data_size
;
312 aluMixData(device
, write_ptr
, to_write
/frame_size
);
313 while(to_write
> 0 && !ATOMIC_LOAD_SEQ(&self
->killNow
))
315 wrote
= write(self
->fd
, write_ptr
, to_write
);
318 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
320 ERR("write failed: %s\n", strerror(errno
));
321 aluHandleDisconnect(device
);
329 ALCplaybackOSS_unlock(self
);
335 static void ALCplaybackOSS_Construct(ALCplaybackOSS
*self
, ALCdevice
*device
)
337 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
338 SET_VTABLE2(ALCplaybackOSS
, ALCbackend
, self
);
340 ATOMIC_INIT(&self
->killNow
, AL_FALSE
);
343 static ALCenum
ALCplaybackOSS_open(ALCplaybackOSS
*self
, const ALCchar
*name
)
345 struct oss_device
*dev
= &oss_playback
;
346 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
348 if(!name
|| strcmp(name
, dev
->handle
) == 0)
354 ALCossListPopulate(&oss_playback
, DSP_CAP_OUTPUT
);
359 if (strcmp(dev
->handle
, name
) == 0)
365 WARN("Could not find \"%s\" in device list\n", name
);
366 return ALC_INVALID_VALUE
;
370 self
->fd
= open(dev
->path
, O_WRONLY
);
373 ERR("Could not open %s: %s\n", dev
->path
, strerror(errno
));
374 return ALC_INVALID_VALUE
;
377 alstr_copy_cstr(&device
->DeviceName
, name
);
382 static void ALCplaybackOSS_close(ALCplaybackOSS
*self
)
388 static ALCboolean
ALCplaybackOSS_reset(ALCplaybackOSS
*self
)
390 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
391 int numFragmentsLogSize
;
392 int log2FragmentSize
;
393 unsigned int periods
;
401 switch(device
->FmtType
)
413 device
->FmtType
= DevFmtShort
;
416 ossFormat
= AFMT_S16_NE
;
420 periods
= device
->NumUpdates
;
421 numChannels
= ChannelsFromDevFmt(device
->FmtChans
);
422 ossSpeed
= device
->Frequency
;
423 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
424 /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
425 log2FragmentSize
= maxi(log2i(device
->UpdateSize
*frameSize
), 4);
426 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
428 #define CHECKERR(func) if((func) < 0) { \
432 /* Don't fail if SETFRAGMENT fails. We can handle just about anything
433 * that's reported back via GETOSPACE */
434 ioctl(self
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
);
435 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
));
436 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
));
437 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
));
438 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_GETOSPACE
, &info
));
442 ERR("%s failed: %s\n", err
, strerror(errno
));
447 if((int)ChannelsFromDevFmt(device
->FmtChans
) != numChannels
)
449 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device
->FmtChans
), numChannels
);
453 if(!((ossFormat
== AFMT_S8
&& device
->FmtType
== DevFmtByte
) ||
454 (ossFormat
== AFMT_U8
&& device
->FmtType
== DevFmtUByte
) ||
455 (ossFormat
== AFMT_S16_NE
&& device
->FmtType
== DevFmtShort
)))
457 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device
->FmtType
), ossFormat
);
461 device
->Frequency
= ossSpeed
;
462 device
->UpdateSize
= info
.fragsize
/ frameSize
;
463 device
->NumUpdates
= info
.fragments
;
465 SetDefaultChannelOrder(device
);
470 static ALCboolean
ALCplaybackOSS_start(ALCplaybackOSS
*self
)
472 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
474 self
->data_size
= device
->UpdateSize
* FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
475 self
->mix_data
= calloc(1, self
->data_size
);
477 ATOMIC_STORE_SEQ(&self
->killNow
, AL_FALSE
);
478 if(althrd_create(&self
->thread
, ALCplaybackOSS_mixerProc
, self
) != althrd_success
)
480 free(self
->mix_data
);
481 self
->mix_data
= NULL
;
488 static void ALCplaybackOSS_stop(ALCplaybackOSS
*self
)
492 if(ATOMIC_EXCHANGE_SEQ(ALenum
, &self
->killNow
, AL_TRUE
))
494 althrd_join(self
->thread
, &res
);
496 if(ioctl(self
->fd
, SNDCTL_DSP_RESET
) != 0)
497 ERR("Error resetting device: %s\n", strerror(errno
));
499 free(self
->mix_data
);
500 self
->mix_data
= NULL
;
504 typedef struct ALCcaptureOSS
{
505 DERIVE_FROM_TYPE(ALCbackend
);
509 ll_ringbuffer_t
*ring
;
511 ATOMIC(ALenum
) killNow
;
515 static int ALCcaptureOSS_recordProc(void *ptr
);
517 static void ALCcaptureOSS_Construct(ALCcaptureOSS
*self
, ALCdevice
*device
);
518 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, void, Destruct
)
519 static ALCenum
ALCcaptureOSS_open(ALCcaptureOSS
*self
, const ALCchar
*name
);
520 static void ALCcaptureOSS_close(ALCcaptureOSS
*self
);
521 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, ALCboolean
, reset
)
522 static ALCboolean
ALCcaptureOSS_start(ALCcaptureOSS
*self
);
523 static void ALCcaptureOSS_stop(ALCcaptureOSS
*self
);
524 static ALCenum
ALCcaptureOSS_captureSamples(ALCcaptureOSS
*self
, ALCvoid
*buffer
, ALCuint samples
);
525 static ALCuint
ALCcaptureOSS_availableSamples(ALCcaptureOSS
*self
);
526 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, ClockLatency
, getClockLatency
)
527 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, void, lock
)
528 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, void, unlock
)
529 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS
)
530 DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS
);
533 static int ALCcaptureOSS_recordProc(void *ptr
)
535 ALCcaptureOSS
*self
= (ALCcaptureOSS
*)ptr
;
536 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
537 struct timeval timeout
;
544 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
546 frame_size
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
548 while(!ATOMIC_LOAD_SEQ(&self
->killNow
))
550 ll_ringbuffer_data_t vec
[2];
553 FD_SET(self
->fd
, &rfds
);
557 sret
= select(self
->fd
+1, &rfds
, NULL
, NULL
, &timeout
);
562 ERR("select failed: %s\n", strerror(errno
));
563 aluHandleDisconnect(device
);
568 WARN("select timeout\n");
572 ll_ringbuffer_get_write_vector(self
->ring
, vec
);
575 amt
= read(self
->fd
, vec
[0].buf
, vec
[0].len
*frame_size
);
578 ERR("read failed: %s\n", strerror(errno
));
579 ALCcaptureOSS_lock(self
);
580 aluHandleDisconnect(device
);
581 ALCcaptureOSS_unlock(self
);
584 ll_ringbuffer_write_advance(self
->ring
, amt
/frame_size
);
592 static void ALCcaptureOSS_Construct(ALCcaptureOSS
*self
, ALCdevice
*device
)
594 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
595 SET_VTABLE2(ALCcaptureOSS
, ALCbackend
, self
);
597 ATOMIC_INIT(&self
->killNow
, AL_FALSE
);
600 static ALCenum
ALCcaptureOSS_open(ALCcaptureOSS
*self
, const ALCchar
*name
)
602 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
603 struct oss_device
*dev
= &oss_capture
;
604 int numFragmentsLogSize
;
605 int log2FragmentSize
;
606 unsigned int periods
;
614 if(!name
|| strcmp(name
, dev
->handle
) == 0)
620 ALCossListPopulate(&oss_capture
, DSP_CAP_INPUT
);
625 if (strcmp(dev
->handle
, name
) == 0)
631 WARN("Could not find \"%s\" in device list\n", name
);
632 return ALC_INVALID_VALUE
;
636 self
->fd
= open(dev
->path
, O_RDONLY
);
639 ERR("Could not open %s: %s\n", dev
->path
, strerror(errno
));
640 return ALC_INVALID_VALUE
;
643 switch(device
->FmtType
)
652 ossFormat
= AFMT_S16_NE
;
658 ERR("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
659 return ALC_INVALID_VALUE
;
663 numChannels
= ChannelsFromDevFmt(device
->FmtChans
);
664 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
665 ossSpeed
= device
->Frequency
;
666 log2FragmentSize
= log2i(device
->UpdateSize
* device
->NumUpdates
*
667 frameSize
/ periods
);
669 /* according to the OSS spec, 16 bytes are the minimum */
670 if (log2FragmentSize
< 4)
671 log2FragmentSize
= 4;
672 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
674 #define CHECKERR(func) if((func) < 0) { \
678 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
));
679 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
));
680 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
));
681 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
));
682 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_GETISPACE
, &info
));
686 ERR("%s failed: %s\n", err
, strerror(errno
));
689 return ALC_INVALID_VALUE
;
693 if((int)ChannelsFromDevFmt(device
->FmtChans
) != numChannels
)
695 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device
->FmtChans
), numChannels
);
698 return ALC_INVALID_VALUE
;
701 if(!((ossFormat
== AFMT_S8
&& device
->FmtType
== DevFmtByte
) ||
702 (ossFormat
== AFMT_U8
&& device
->FmtType
== DevFmtUByte
) ||
703 (ossFormat
== AFMT_S16_NE
&& device
->FmtType
== DevFmtShort
)))
705 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device
->FmtType
), ossFormat
);
708 return ALC_INVALID_VALUE
;
711 self
->ring
= ll_ringbuffer_create(device
->UpdateSize
*device
->NumUpdates
+ 1, frameSize
);
714 ERR("Ring buffer create failed\n");
717 return ALC_OUT_OF_MEMORY
;
720 alstr_copy_cstr(&device
->DeviceName
, name
);
725 static void ALCcaptureOSS_close(ALCcaptureOSS
*self
)
730 ll_ringbuffer_free(self
->ring
);
734 static ALCboolean
ALCcaptureOSS_start(ALCcaptureOSS
*self
)
736 ATOMIC_STORE_SEQ(&self
->killNow
, AL_FALSE
);
737 if(althrd_create(&self
->thread
, ALCcaptureOSS_recordProc
, self
) != althrd_success
)
742 static void ALCcaptureOSS_stop(ALCcaptureOSS
*self
)
746 if(ATOMIC_EXCHANGE_SEQ(ALenum
, &self
->killNow
, AL_TRUE
))
749 althrd_join(self
->thread
, &res
);
751 if(ioctl(self
->fd
, SNDCTL_DSP_RESET
) != 0)
752 ERR("Error resetting device: %s\n", strerror(errno
));
755 static ALCenum
ALCcaptureOSS_captureSamples(ALCcaptureOSS
*self
, ALCvoid
*buffer
, ALCuint samples
)
757 ll_ringbuffer_read(self
->ring
, buffer
, samples
);
761 static ALCuint
ALCcaptureOSS_availableSamples(ALCcaptureOSS
*self
)
763 return ll_ringbuffer_read_space(self
->ring
);
767 typedef struct ALCossBackendFactory
{
768 DERIVE_FROM_TYPE(ALCbackendFactory
);
769 } ALCossBackendFactory
;
770 #define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } }
772 ALCbackendFactory
*ALCossBackendFactory_getFactory(void);
774 static ALCboolean
ALCossBackendFactory_init(ALCossBackendFactory
*self
);
775 static void ALCossBackendFactory_deinit(ALCossBackendFactory
*self
);
776 static ALCboolean
ALCossBackendFactory_querySupport(ALCossBackendFactory
*self
, ALCbackend_Type type
);
777 static void ALCossBackendFactory_probe(ALCossBackendFactory
*self
, enum DevProbe type
);
778 static ALCbackend
* ALCossBackendFactory_createBackend(ALCossBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
779 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory
);
782 ALCbackendFactory
*ALCossBackendFactory_getFactory(void)
784 static ALCossBackendFactory factory
= ALCOSSBACKENDFACTORY_INITIALIZER
;
785 return STATIC_CAST(ALCbackendFactory
, &factory
);
789 ALCboolean
ALCossBackendFactory_init(ALCossBackendFactory
* UNUSED(self
))
791 ConfigValueStr(NULL
, "oss", "device", &oss_playback
.path
);
792 ConfigValueStr(NULL
, "oss", "capture", &oss_capture
.path
);
797 void ALCossBackendFactory_deinit(ALCossBackendFactory
* UNUSED(self
))
799 ALCossListFree(&oss_playback
);
800 ALCossListFree(&oss_capture
);
804 ALCboolean
ALCossBackendFactory_querySupport(ALCossBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
806 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
811 void ALCossBackendFactory_probe(ALCossBackendFactory
* UNUSED(self
), enum DevProbe type
)
813 struct oss_device
*cur
;
816 case ALL_DEVICE_PROBE
:
817 ALCossListFree(&oss_playback
);
818 ALCossListPopulate(&oss_playback
, DSP_CAP_OUTPUT
);
822 AppendAllDevicesList(cur
->handle
);
827 case CAPTURE_DEVICE_PROBE
:
828 ALCossListFree(&oss_capture
);
829 ALCossListPopulate(&oss_capture
, DSP_CAP_INPUT
);
833 AppendCaptureDeviceList(cur
->handle
);
840 ALCbackend
* ALCossBackendFactory_createBackend(ALCossBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
842 if(type
== ALCbackend_Playback
)
844 ALCplaybackOSS
*backend
;
845 NEW_OBJ(backend
, ALCplaybackOSS
)(device
);
846 if(!backend
) return NULL
;
847 return STATIC_CAST(ALCbackend
, backend
);
849 if(type
== ALCbackend_Capture
)
851 ALCcaptureOSS
*backend
;
852 NEW_OBJ(backend
, ALCcaptureOSS
)(device
);
853 if(!backend
) return NULL
;
854 return STATIC_CAST(ALCbackend
, backend
);