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
30 #include "ringbuffer.h"
32 #include "backends/base.h"
37 static const ALCchar sndio_device
[] = "SndIO Default";
40 typedef struct SndioPlayback
{
41 DERIVE_FROM_TYPE(ALCbackend
);
43 struct sio_hdl
*sndHandle
;
52 static int SndioPlayback_mixerProc(void *ptr
);
54 static void SndioPlayback_Construct(SndioPlayback
*self
, ALCdevice
*device
);
55 static void SndioPlayback_Destruct(SndioPlayback
*self
);
56 static ALCenum
SndioPlayback_open(SndioPlayback
*self
, const ALCchar
*name
);
57 static ALCboolean
SndioPlayback_reset(SndioPlayback
*self
);
58 static ALCboolean
SndioPlayback_start(SndioPlayback
*self
);
59 static void SndioPlayback_stop(SndioPlayback
*self
);
60 static DECLARE_FORWARD2(SndioPlayback
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
61 static DECLARE_FORWARD(SndioPlayback
, ALCbackend
, ALCuint
, availableSamples
)
62 static DECLARE_FORWARD(SndioPlayback
, ALCbackend
, ClockLatency
, getClockLatency
)
63 static DECLARE_FORWARD(SndioPlayback
, ALCbackend
, void, lock
)
64 static DECLARE_FORWARD(SndioPlayback
, ALCbackend
, void, unlock
)
65 DECLARE_DEFAULT_ALLOCATORS(SndioPlayback
)
67 DEFINE_ALCBACKEND_VTABLE(SndioPlayback
);
70 static void SndioPlayback_Construct(SndioPlayback
*self
, ALCdevice
*device
)
72 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
73 SET_VTABLE2(SndioPlayback
, ALCbackend
, self
);
75 self
->sndHandle
= NULL
;
76 self
->mix_data
= NULL
;
77 ATOMIC_INIT(&self
->killNow
, AL_TRUE
);
80 static void SndioPlayback_Destruct(SndioPlayback
*self
)
83 sio_close(self
->sndHandle
);
84 self
->sndHandle
= NULL
;
86 al_free(self
->mix_data
);
87 self
->mix_data
= NULL
;
89 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
93 static int SndioPlayback_mixerProc(void *ptr
)
95 SndioPlayback
*self
= (SndioPlayback
*)ptr
;
96 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
101 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
103 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
105 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
) &&
106 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
108 ALsizei len
= self
->data_size
;
109 ALubyte
*WritePtr
= self
->mix_data
;
111 SndioPlayback_lock(self
);
112 aluMixData(device
, WritePtr
, len
/frameSize
);
113 SndioPlayback_unlock(self
);
114 while(len
> 0 && !ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
))
116 wrote
= sio_write(self
->sndHandle
, WritePtr
, len
);
119 ERR("sio_write failed\n");
120 ALCdevice_Lock(device
);
121 aluHandleDisconnect(device
, "Failed to write playback samples");
122 ALCdevice_Unlock(device
);
135 static ALCenum
SndioPlayback_open(SndioPlayback
*self
, const ALCchar
*name
)
137 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
141 else if(strcmp(name
, sndio_device
) != 0)
142 return ALC_INVALID_VALUE
;
144 self
->sndHandle
= sio_open(NULL
, SIO_PLAY
, 0);
145 if(self
->sndHandle
== NULL
)
147 ERR("Could not open device\n");
148 return ALC_INVALID_VALUE
;
151 alstr_copy_cstr(&device
->DeviceName
, name
);
156 static ALCboolean
SndioPlayback_reset(SndioPlayback
*self
)
158 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
163 par
.rate
= device
->Frequency
;
164 par
.pchan
= ((device
->FmtChans
!= DevFmtMono
) ? 2 : 1);
166 switch(device
->FmtType
)
194 par
.le
= SIO_LE_NATIVE
;
196 par
.round
= device
->UpdateSize
;
197 par
.appbufsz
= device
->UpdateSize
* (device
->NumUpdates
-1);
198 if(!par
.appbufsz
) par
.appbufsz
= device
->UpdateSize
;
200 if(!sio_setpar(self
->sndHandle
, &par
) || !sio_getpar(self
->sndHandle
, &par
))
202 ERR("Failed to set device parameters\n");
206 if(par
.bits
!= par
.bps
*8)
208 ERR("Padded samples not supported (%u of %u bits)\n", par
.bits
, par
.bps
*8);
212 device
->Frequency
= par
.rate
;
213 device
->FmtChans
= ((par
.pchan
==1) ? DevFmtMono
: DevFmtStereo
);
215 if(par
.bits
== 8 && par
.sig
== 1)
216 device
->FmtType
= DevFmtByte
;
217 else if(par
.bits
== 8 && par
.sig
== 0)
218 device
->FmtType
= DevFmtUByte
;
219 else if(par
.bits
== 16 && par
.sig
== 1)
220 device
->FmtType
= DevFmtShort
;
221 else if(par
.bits
== 16 && par
.sig
== 0)
222 device
->FmtType
= DevFmtUShort
;
223 else if(par
.bits
== 32 && par
.sig
== 1)
224 device
->FmtType
= DevFmtInt
;
225 else if(par
.bits
== 32 && par
.sig
== 0)
226 device
->FmtType
= DevFmtUInt
;
229 ERR("Unhandled sample format: %s %u-bit\n", (par
.sig
?"signed":"unsigned"), par
.bits
);
233 device
->UpdateSize
= par
.round
;
234 device
->NumUpdates
= (par
.bufsz
/par
.round
) + 1;
236 SetDefaultChannelOrder(device
);
241 static ALCboolean
SndioPlayback_start(SndioPlayback
*self
)
243 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
245 self
->data_size
= device
->UpdateSize
* FrameSizeFromDevFmt(
246 device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
248 al_free(self
->mix_data
);
249 self
->mix_data
= al_calloc(16, self
->data_size
);
251 if(!sio_start(self
->sndHandle
))
253 ERR("Error starting playback\n");
257 ATOMIC_STORE(&self
->killNow
, AL_FALSE
, almemory_order_release
);
258 if(althrd_create(&self
->thread
, SndioPlayback_mixerProc
, self
) != althrd_success
)
260 sio_stop(self
->sndHandle
);
267 static void SndioPlayback_stop(SndioPlayback
*self
)
271 if(ATOMIC_EXCHANGE(&self
->killNow
, AL_TRUE
, almemory_order_acq_rel
))
273 althrd_join(self
->thread
, &res
);
275 if(!sio_stop(self
->sndHandle
))
276 ERR("Error stopping device\n");
278 al_free(self
->mix_data
);
279 self
->mix_data
= NULL
;
283 typedef struct SndioCapture
{
284 DERIVE_FROM_TYPE(ALCbackend
);
286 struct sio_hdl
*sndHandle
;
288 ll_ringbuffer_t
*ring
;
294 static int SndioCapture_recordProc(void *ptr
);
296 static void SndioCapture_Construct(SndioCapture
*self
, ALCdevice
*device
);
297 static void SndioCapture_Destruct(SndioCapture
*self
);
298 static ALCenum
SndioCapture_open(SndioCapture
*self
, const ALCchar
*name
);
299 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, ALCboolean
, reset
)
300 static ALCboolean
SndioCapture_start(SndioCapture
*self
);
301 static void SndioCapture_stop(SndioCapture
*self
);
302 static ALCenum
SndioCapture_captureSamples(SndioCapture
*self
, void *buffer
, ALCuint samples
);
303 static ALCuint
SndioCapture_availableSamples(SndioCapture
*self
);
304 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
305 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, void, lock
)
306 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, void, unlock
)
307 DECLARE_DEFAULT_ALLOCATORS(SndioCapture
)
309 DEFINE_ALCBACKEND_VTABLE(SndioCapture
);
312 static void SndioCapture_Construct(SndioCapture
*self
, ALCdevice
*device
)
314 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
315 SET_VTABLE2(SndioCapture
, ALCbackend
, self
);
317 self
->sndHandle
= NULL
;
319 ATOMIC_INIT(&self
->killNow
, AL_TRUE
);
322 static void SndioCapture_Destruct(SndioCapture
*self
)
325 sio_close(self
->sndHandle
);
326 self
->sndHandle
= NULL
;
328 ll_ringbuffer_free(self
->ring
);
331 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
335 static int SndioCapture_recordProc(void* ptr
)
337 SndioCapture
*self
= (SndioCapture
*)ptr
;
338 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
342 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
344 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
346 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
) &&
347 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
349 ll_ringbuffer_data_t data
[2];
352 ll_ringbuffer_get_write_vector(self
->ring
, data
);
353 todo
= data
[0].len
+ data
[1].len
;
356 static char junk
[4096];
357 sio_read(self
->sndHandle
, junk
, minz(sizeof(junk
)/frameSize
, device
->UpdateSize
)*frameSize
);
362 data
[0].len
*= frameSize
;
363 data
[1].len
*= frameSize
;
364 todo
= minz(todo
, device
->UpdateSize
) * frameSize
;
372 got
= sio_read(self
->sndHandle
, data
[0].buf
, minz(todo
-total
, data
[0].len
));
375 SndioCapture_lock(self
);
376 aluHandleDisconnect(device
, "Failed to read capture samples");
377 SndioCapture_unlock(self
);
385 ll_ringbuffer_write_advance(self
->ring
, total
/ frameSize
);
392 static ALCenum
SndioCapture_open(SndioCapture
*self
, const ALCchar
*name
)
394 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
399 else if(strcmp(name
, sndio_device
) != 0)
400 return ALC_INVALID_VALUE
;
402 self
->sndHandle
= sio_open(NULL
, SIO_REC
, 0);
403 if(self
->sndHandle
== NULL
)
405 ERR("Could not open device\n");
406 return ALC_INVALID_VALUE
;
411 switch(device
->FmtType
)
438 ERR("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
439 return ALC_INVALID_VALUE
;
441 par
.bits
= par
.bps
* 8;
442 par
.le
= SIO_LE_NATIVE
;
443 par
.msb
= SIO_LE_NATIVE
? 0 : 1;
444 par
.rchan
= ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
);
445 par
.rate
= device
->Frequency
;
447 par
.appbufsz
= maxu(device
->UpdateSize
*device
->NumUpdates
, (device
->Frequency
+9)/10);
448 par
.round
= clampu(par
.appbufsz
/device
->NumUpdates
, (device
->Frequency
+99)/100,
449 (device
->Frequency
+19)/20);
451 device
->UpdateSize
= par
.round
;
452 device
->NumUpdates
= maxu(par
.appbufsz
/par
.round
, 1);
454 if(!sio_setpar(self
->sndHandle
, &par
) || !sio_getpar(self
->sndHandle
, &par
))
456 ERR("Failed to set device parameters\n");
457 return ALC_INVALID_VALUE
;
460 if(par
.bits
!= par
.bps
*8)
462 ERR("Padded samples not supported (%u of %u bits)\n", par
.bits
, par
.bps
*8);
463 return ALC_INVALID_VALUE
;
466 if(!((device
->FmtType
== DevFmtByte
&& par
.bits
== 8 && par
.sig
!= 0) ||
467 (device
->FmtType
== DevFmtUByte
&& par
.bits
== 8 && par
.sig
== 0) ||
468 (device
->FmtType
== DevFmtShort
&& par
.bits
== 16 && par
.sig
!= 0) ||
469 (device
->FmtType
== DevFmtUShort
&& par
.bits
== 16 && par
.sig
== 0) ||
470 (device
->FmtType
== DevFmtInt
&& par
.bits
== 32 && par
.sig
!= 0) ||
471 (device
->FmtType
== DevFmtUInt
&& par
.bits
== 32 && par
.sig
== 0)) ||
472 ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
) != (ALsizei
)par
.rchan
||
473 device
->Frequency
!= par
.rate
)
475 ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
476 DevFmtTypeString(device
->FmtType
), DevFmtChannelsString(device
->FmtChans
),
477 device
->Frequency
, par
.sig
?'s':'u', par
.bits
, par
.rchan
, par
.rate
);
478 return ALC_INVALID_VALUE
;
481 self
->ring
= ll_ringbuffer_create(device
->UpdateSize
*device
->NumUpdates
, par
.bps
*par
.rchan
, 0);
484 ERR("Failed to allocate %u-byte ringbuffer\n",
485 device
->UpdateSize
*device
->NumUpdates
*par
.bps
*par
.rchan
);
486 return ALC_OUT_OF_MEMORY
;
489 SetDefaultChannelOrder(device
);
491 alstr_copy_cstr(&device
->DeviceName
, name
);
496 static ALCboolean
SndioCapture_start(SndioCapture
*self
)
498 if(!sio_start(self
->sndHandle
))
500 ERR("Error starting playback\n");
504 ATOMIC_STORE(&self
->killNow
, AL_FALSE
, almemory_order_release
);
505 if(althrd_create(&self
->thread
, SndioCapture_recordProc
, self
) != althrd_success
)
507 sio_stop(self
->sndHandle
);
514 static void SndioCapture_stop(SndioCapture
*self
)
518 if(ATOMIC_EXCHANGE(&self
->killNow
, AL_TRUE
, almemory_order_acq_rel
))
520 althrd_join(self
->thread
, &res
);
522 if(!sio_stop(self
->sndHandle
))
523 ERR("Error stopping device\n");
526 static ALCenum
SndioCapture_captureSamples(SndioCapture
*self
, void *buffer
, ALCuint samples
)
528 ll_ringbuffer_read(self
->ring
, buffer
, samples
);
532 static ALCuint
SndioCapture_availableSamples(SndioCapture
*self
)
534 return ll_ringbuffer_read_space(self
->ring
);
538 typedef struct SndioBackendFactory
{
539 DERIVE_FROM_TYPE(ALCbackendFactory
);
540 } SndioBackendFactory
;
541 #define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
543 ALCbackendFactory
*SndioBackendFactory_getFactory(void);
545 static ALCboolean
SndioBackendFactory_init(SndioBackendFactory
*self
);
546 static DECLARE_FORWARD(SndioBackendFactory
, ALCbackendFactory
, void, deinit
)
547 static ALCboolean
SndioBackendFactory_querySupport(SndioBackendFactory
*self
, ALCbackend_Type type
);
548 static void SndioBackendFactory_probe(SndioBackendFactory
*self
, enum DevProbe type
);
549 static ALCbackend
* SndioBackendFactory_createBackend(SndioBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
550 DEFINE_ALCBACKENDFACTORY_VTABLE(SndioBackendFactory
);
552 ALCbackendFactory
*SndioBackendFactory_getFactory(void)
554 static SndioBackendFactory factory
= SNDIOBACKENDFACTORY_INITIALIZER
;
555 return STATIC_CAST(ALCbackendFactory
, &factory
);
558 static ALCboolean
SndioBackendFactory_init(SndioBackendFactory
* UNUSED(self
))
560 /* No dynamic loading */
564 static ALCboolean
SndioBackendFactory_querySupport(SndioBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
566 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
571 static void SndioBackendFactory_probe(SndioBackendFactory
* UNUSED(self
), enum DevProbe type
)
575 case ALL_DEVICE_PROBE
:
576 AppendAllDevicesList(sndio_device
);
578 case CAPTURE_DEVICE_PROBE
:
579 AppendCaptureDeviceList(sndio_device
);
584 static ALCbackend
* SndioBackendFactory_createBackend(SndioBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
586 if(type
== ALCbackend_Playback
)
588 SndioPlayback
*backend
;
589 NEW_OBJ(backend
, SndioPlayback
)(device
);
590 if(!backend
) return NULL
;
591 return STATIC_CAST(ALCbackend
, backend
);
593 if(type
== ALCbackend_Capture
)
595 SndioCapture
*backend
;
596 NEW_OBJ(backend
, SndioCapture
)(device
);
597 if(!backend
) return NULL
;
598 return STATIC_CAST(ALCbackend
, backend
);