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>
39 #include "backends/base.h"
41 #include <sys/soundcard.h>
44 * The OSS documentation talks about SOUND_MIXER_READ, but the header
45 * only contains MIXER_READ. Play safe. Same for WRITE.
47 #ifndef SOUND_MIXER_READ
48 #define SOUND_MIXER_READ MIXER_READ
50 #ifndef SOUND_MIXER_WRITE
51 #define SOUND_MIXER_WRITE MIXER_WRITE
55 static const ALCchar oss_device
[] = "OSS Default";
57 static const char *oss_driver
= "/dev/dsp";
58 static const char *oss_capture
= "/dev/dsp";
60 static int log2i(ALCuint x
)
72 typedef struct ALCplaybackOSS
{
73 DERIVE_FROM_TYPE(ALCbackend
);
84 static int ALCplaybackOSS_mixerProc(void *ptr
);
86 static void ALCplaybackOSS_Construct(ALCplaybackOSS
*self
, ALCdevice
*device
);
87 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, void, Destruct
)
88 static ALCenum
ALCplaybackOSS_open(ALCplaybackOSS
*self
, const ALCchar
*name
);
89 static void ALCplaybackOSS_close(ALCplaybackOSS
*self
);
90 static ALCboolean
ALCplaybackOSS_reset(ALCplaybackOSS
*self
);
91 static ALCboolean
ALCplaybackOSS_start(ALCplaybackOSS
*self
);
92 static void ALCplaybackOSS_stop(ALCplaybackOSS
*self
);
93 static DECLARE_FORWARD2(ALCplaybackOSS
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
94 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, ALCuint
, availableSamples
)
95 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, ALint64
, getLatency
)
96 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, void, lock
)
97 static DECLARE_FORWARD(ALCplaybackOSS
, ALCbackend
, void, unlock
)
98 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS
)
99 DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS
);
102 static int ALCplaybackOSS_mixerProc(void *ptr
)
104 ALCplaybackOSS
*self
= (ALCplaybackOSS
*)ptr
;
105 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
110 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
112 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
114 while(!self
->killNow
&& device
->Connected
)
116 ALint len
= self
->data_size
;
117 ALubyte
*WritePtr
= self
->mix_data
;
119 aluMixData(device
, WritePtr
, len
/frameSize
);
120 while(len
> 0 && !self
->killNow
)
122 wrote
= write(self
->fd
, WritePtr
, len
);
125 if(errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&& errno
!= EINTR
)
127 ERR("write failed: %s\n", strerror(errno
));
128 ALCplaybackOSS_lock(self
);
129 aluHandleDisconnect(device
);
130 ALCplaybackOSS_unlock(self
);
147 static void ALCplaybackOSS_Construct(ALCplaybackOSS
*self
, ALCdevice
*device
)
149 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
150 SET_VTABLE2(ALCplaybackOSS
, ALCbackend
, self
);
153 static ALCenum
ALCplaybackOSS_open(ALCplaybackOSS
*self
, const ALCchar
*name
)
155 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
159 else if(strcmp(name
, oss_device
) != 0)
160 return ALC_INVALID_VALUE
;
164 self
->fd
= open(oss_driver
, O_WRONLY
);
167 ERR("Could not open %s: %s\n", oss_driver
, strerror(errno
));
168 return ALC_INVALID_VALUE
;
171 al_string_copy_cstr(&device
->DeviceName
, name
);
176 static void ALCplaybackOSS_close(ALCplaybackOSS
*self
)
182 static ALCboolean
ALCplaybackOSS_reset(ALCplaybackOSS
*self
)
184 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
185 int numFragmentsLogSize
;
186 int log2FragmentSize
;
187 unsigned int periods
;
195 switch(device
->FmtType
)
207 device
->FmtType
= DevFmtShort
;
210 ossFormat
= AFMT_S16_NE
;
214 periods
= device
->NumUpdates
;
215 numChannels
= ChannelsFromDevFmt(device
->FmtChans
);
216 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
218 ossSpeed
= device
->Frequency
;
219 log2FragmentSize
= log2i(device
->UpdateSize
* frameSize
);
221 /* according to the OSS spec, 16 bytes are the minimum */
222 if (log2FragmentSize
< 4)
223 log2FragmentSize
= 4;
224 /* Subtract one period since the temp mixing buffer counts as one. Still
225 * need at least two on the card, though. */
226 if(periods
> 2) periods
--;
227 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
229 #define CHECKERR(func) if((func) < 0) { \
233 /* Don't fail if SETFRAGMENT fails. We can handle just about anything
234 * that's reported back via GETOSPACE */
235 ioctl(self
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
);
236 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
));
237 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
));
238 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
));
239 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_GETOSPACE
, &info
));
243 ERR("%s failed: %s\n", err
, strerror(errno
));
248 if((int)ChannelsFromDevFmt(device
->FmtChans
) != numChannels
)
250 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device
->FmtChans
), numChannels
);
254 if(!((ossFormat
== AFMT_S8
&& device
->FmtType
== DevFmtByte
) ||
255 (ossFormat
== AFMT_U8
&& device
->FmtType
== DevFmtUByte
) ||
256 (ossFormat
== AFMT_S16_NE
&& device
->FmtType
== DevFmtShort
)))
258 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device
->FmtType
), ossFormat
);
262 device
->Frequency
= ossSpeed
;
263 device
->UpdateSize
= info
.fragsize
/ frameSize
;
264 device
->NumUpdates
= info
.fragments
+ 1;
266 SetDefaultChannelOrder(device
);
271 static ALCboolean
ALCplaybackOSS_start(ALCplaybackOSS
*self
)
273 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
275 self
->data_size
= device
->UpdateSize
* FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
276 self
->mix_data
= calloc(1, self
->data_size
);
279 if(althrd_create(&self
->thread
, ALCplaybackOSS_mixerProc
, self
) != althrd_success
)
281 free(self
->mix_data
);
282 self
->mix_data
= NULL
;
289 static void ALCplaybackOSS_stop(ALCplaybackOSS
*self
)
297 althrd_join(self
->thread
, &res
);
299 if(ioctl(self
->fd
, SNDCTL_DSP_RESET
) != 0)
300 ERR("Error resetting device: %s\n", strerror(errno
));
302 free(self
->mix_data
);
303 self
->mix_data
= NULL
;
307 typedef struct ALCcaptureOSS
{
308 DERIVE_FROM_TYPE(ALCbackend
);
318 volatile int killNow
;
322 static int ALCcaptureOSS_recordProc(void *ptr
);
324 static void ALCcaptureOSS_Construct(ALCcaptureOSS
*self
, ALCdevice
*device
);
325 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, void, Destruct
)
326 static ALCenum
ALCcaptureOSS_open(ALCcaptureOSS
*self
, const ALCchar
*name
);
327 static void ALCcaptureOSS_close(ALCcaptureOSS
*self
);
328 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, ALCboolean
, reset
)
329 static ALCboolean
ALCcaptureOSS_start(ALCcaptureOSS
*self
);
330 static void ALCcaptureOSS_stop(ALCcaptureOSS
*self
);
331 static ALCenum
ALCcaptureOSS_captureSamples(ALCcaptureOSS
*self
, ALCvoid
*buffer
, ALCuint samples
);
332 static ALCuint
ALCcaptureOSS_availableSamples(ALCcaptureOSS
*self
);
333 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, ALint64
, getLatency
)
334 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, void, lock
)
335 static DECLARE_FORWARD(ALCcaptureOSS
, ALCbackend
, void, unlock
)
336 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS
)
337 DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS
);
340 static int ALCcaptureOSS_recordProc(void *ptr
)
342 ALCcaptureOSS
*self
= (ALCcaptureOSS
*)ptr
;
343 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
348 althrd_setname(althrd_current(), "alsoft-record");
350 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
352 while(!self
->killNow
)
354 amt
= read(self
->fd
, self
->read_data
, self
->data_size
);
357 ERR("read failed: %s\n", strerror(errno
));
358 ALCcaptureOSS_lock(self
);
359 aluHandleDisconnect(device
);
360 ALCcaptureOSS_unlock(self
);
369 WriteRingBuffer(self
->ring
, self
->read_data
, amt
/frameSize
);
376 static void ALCcaptureOSS_Construct(ALCcaptureOSS
*self
, ALCdevice
*device
)
378 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
379 SET_VTABLE2(ALCcaptureOSS
, ALCbackend
, self
);
382 static ALCenum
ALCcaptureOSS_open(ALCcaptureOSS
*self
, const ALCchar
*name
)
384 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
385 int numFragmentsLogSize
;
386 int log2FragmentSize
;
387 unsigned int periods
;
397 else if(strcmp(name
, oss_device
) != 0)
398 return ALC_INVALID_VALUE
;
400 self
->fd
= open(oss_capture
, O_RDONLY
);
403 ERR("Could not open %s: %s\n", oss_capture
, strerror(errno
));
404 return ALC_INVALID_VALUE
;
407 switch(device
->FmtType
)
416 ossFormat
= AFMT_S16_NE
;
422 ERR("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
423 return ALC_INVALID_VALUE
;
427 numChannels
= ChannelsFromDevFmt(device
->FmtChans
);
428 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
429 ossSpeed
= device
->Frequency
;
430 log2FragmentSize
= log2i(device
->UpdateSize
* device
->NumUpdates
*
431 frameSize
/ periods
);
433 /* according to the OSS spec, 16 bytes are the minimum */
434 if (log2FragmentSize
< 4)
435 log2FragmentSize
= 4;
436 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
438 #define CHECKERR(func) if((func) < 0) { \
442 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
));
443 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
));
444 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
));
445 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
));
446 CHECKERR(ioctl(self
->fd
, SNDCTL_DSP_GETISPACE
, &info
));
450 ERR("%s failed: %s\n", err
, strerror(errno
));
453 return ALC_INVALID_VALUE
;
457 if((int)ChannelsFromDevFmt(device
->FmtChans
) != numChannels
)
459 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device
->FmtChans
), numChannels
);
462 return ALC_INVALID_VALUE
;
465 if(!((ossFormat
== AFMT_S8
&& device
->FmtType
== DevFmtByte
) ||
466 (ossFormat
== AFMT_U8
&& device
->FmtType
== DevFmtUByte
) ||
467 (ossFormat
== AFMT_S16_NE
&& device
->FmtType
== DevFmtShort
)))
469 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device
->FmtType
), ossFormat
);
472 return ALC_INVALID_VALUE
;
475 self
->ring
= CreateRingBuffer(frameSize
, device
->UpdateSize
* device
->NumUpdates
);
478 ERR("Ring buffer create failed\n");
481 return ALC_OUT_OF_MEMORY
;
484 self
->data_size
= info
.fragsize
;
485 self
->read_data
= calloc(1, self
->data_size
);
488 if(althrd_create(&self
->thread
, ALCcaptureOSS_recordProc
, self
) != althrd_success
)
490 device
->ExtraData
= NULL
;
493 return ALC_OUT_OF_MEMORY
;
496 al_string_copy_cstr(&device
->DeviceName
, name
);
501 static void ALCcaptureOSS_close(ALCcaptureOSS
*self
)
506 althrd_join(self
->thread
, &res
);
511 DestroyRingBuffer(self
->ring
);
514 free(self
->read_data
);
515 self
->read_data
= NULL
;
518 static ALCboolean
ALCcaptureOSS_start(ALCcaptureOSS
*self
)
524 static void ALCcaptureOSS_stop(ALCcaptureOSS
*self
)
529 static ALCenum
ALCcaptureOSS_captureSamples(ALCcaptureOSS
*self
, ALCvoid
*buffer
, ALCuint samples
)
531 ReadRingBuffer(self
->ring
, buffer
, samples
);
535 static ALCuint
ALCcaptureOSS_availableSamples(ALCcaptureOSS
*self
)
537 return RingBufferSize(self
->ring
);
541 typedef struct ALCossBackendFactory
{
542 DERIVE_FROM_TYPE(ALCbackendFactory
);
543 } ALCossBackendFactory
;
544 #define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } }
546 ALCbackendFactory
*ALCossBackendFactory_getFactory(void);
548 static ALCboolean
ALCossBackendFactory_init(ALCossBackendFactory
*self
);
549 static DECLARE_FORWARD(ALCossBackendFactory
, ALCbackendFactory
, void, deinit
)
550 static ALCboolean
ALCossBackendFactory_querySupport(ALCossBackendFactory
*self
, ALCbackend_Type type
);
551 static void ALCossBackendFactory_probe(ALCossBackendFactory
*self
, enum DevProbe type
);
552 static ALCbackend
* ALCossBackendFactory_createBackend(ALCossBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
553 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory
);
556 ALCbackendFactory
*ALCossBackendFactory_getFactory(void)
558 static ALCossBackendFactory factory
= ALCOSSBACKENDFACTORY_INITIALIZER
;
559 return STATIC_CAST(ALCbackendFactory
, &factory
);
563 ALCboolean
ALCossBackendFactory_init(ALCossBackendFactory
* UNUSED(self
))
565 ConfigValueStr("oss", "device", &oss_driver
);
566 ConfigValueStr("oss", "capture", &oss_capture
);
571 ALCboolean
ALCossBackendFactory_querySupport(ALCossBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
573 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
578 void ALCossBackendFactory_probe(ALCossBackendFactory
* UNUSED(self
), enum DevProbe type
)
582 case ALL_DEVICE_PROBE
:
586 if(stat(oss_driver
, &buf
) == 0)
588 AppendAllDevicesList(oss_device
);
592 case CAPTURE_DEVICE_PROBE
:
596 if(stat(oss_capture
, &buf
) == 0)
598 AppendCaptureDeviceList(oss_device
);
604 ALCbackend
* ALCossBackendFactory_createBackend(ALCossBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
606 if(type
== ALCbackend_Playback
)
608 ALCplaybackOSS
*backend
;
610 backend
= ALCplaybackOSS_New(sizeof(*backend
));
611 if(!backend
) return NULL
;
612 memset(backend
, 0, sizeof(*backend
));
614 ALCplaybackOSS_Construct(backend
, device
);
616 return STATIC_CAST(ALCbackend
, backend
);
618 if(type
== ALCbackend_Capture
)
620 ALCcaptureOSS
*backend
;
622 backend
= ALCcaptureOSS_New(sizeof(*backend
));
623 if(!backend
) return NULL
;
624 memset(backend
, 0, sizeof(*backend
));
626 ALCcaptureOSS_Construct(backend
, device
);
628 return STATIC_CAST(ALCbackend
, backend
);