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
31 #include "backends/base.h"
38 typedef struct ALCsndioBackend
{
39 DERIVE_FROM_TYPE(ALCbackend
);
41 struct sio_hdl
*sndHandle
;
50 static int ALCsndioBackend_mixerProc(void *ptr
);
52 static void ALCsndioBackend_Construct(ALCsndioBackend
*self
, ALCdevice
*device
);
53 static void ALCsndioBackend_Destruct(ALCsndioBackend
*self
);
54 static ALCenum
ALCsndioBackend_open(ALCsndioBackend
*self
, const ALCchar
*name
);
55 static void ALCsndioBackend_close(ALCsndioBackend
*self
);
56 static ALCboolean
ALCsndioBackend_reset(ALCsndioBackend
*self
);
57 static ALCboolean
ALCsndioBackend_start(ALCsndioBackend
*self
);
58 static void ALCsndioBackend_stop(ALCsndioBackend
*self
);
59 static DECLARE_FORWARD2(ALCsndioBackend
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
60 static DECLARE_FORWARD(ALCsndioBackend
, ALCbackend
, ALCuint
, availableSamples
)
61 static DECLARE_FORWARD(ALCsndioBackend
, ALCbackend
, ClockLatency
, getClockLatency
)
62 static DECLARE_FORWARD(ALCsndioBackend
, ALCbackend
, void, lock
)
63 static DECLARE_FORWARD(ALCsndioBackend
, ALCbackend
, void, unlock
)
64 DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend
)
66 DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend
);
69 static const ALCchar sndio_device
[] = "SndIO Default";
72 static void ALCsndioBackend_Construct(ALCsndioBackend
*self
, ALCdevice
*device
)
74 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
75 SET_VTABLE2(ALCsndioBackend
, ALCbackend
, self
);
78 static void ALCsndioBackend_Destruct(ALCsndioBackend
*self
)
81 sio_close(self
->sndHandle
);
82 self
->sndHandle
= NULL
;
84 al_free(self
->mix_data
);
85 self
->mix_data
= NULL
;
87 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
91 static int ALCsndioBackend_mixerProc(void *ptr
)
93 ALCsndioBackend
*self
= (ALCsndioBackend
*)ptr
;
94 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
99 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
101 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
103 while(!self
->killNow
&& device
->Connected
)
105 ALsizei len
= self
->data_size
;
106 ALubyte
*WritePtr
= self
->mix_data
;
108 ALCsndioBackend_lock(self
);
109 aluMixData(device
, WritePtr
, len
/frameSize
);
110 ALCsndioBackend_unlock(self
);
111 while(len
> 0 && !self
->killNow
)
113 wrote
= sio_write(self
->sndHandle
, WritePtr
, len
);
116 ERR("sio_write failed\n");
117 ALCdevice_Lock(device
);
118 aluHandleDisconnect(device
);
119 ALCdevice_Unlock(device
);
132 static ALCenum
ALCsndioBackend_open(ALCsndioBackend
*self
, const ALCchar
*name
)
134 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
138 else if(strcmp(name
, sndio_device
) != 0)
139 return ALC_INVALID_VALUE
;
141 self
->sndHandle
= sio_open(NULL
, SIO_PLAY
, 0);
142 if(self
->sndHandle
== NULL
)
144 ERR("Could not open device\n");
145 return ALC_INVALID_VALUE
;
148 alstr_copy_cstr(&device
->DeviceName
, name
);
153 static void ALCsndioBackend_close(ALCsndioBackend
*self
)
155 sio_close(self
->sndHandle
);
156 self
->sndHandle
= NULL
;
159 static ALCboolean
ALCsndioBackend_reset(ALCsndioBackend
*self
)
161 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
166 par
.rate
= device
->Frequency
;
167 par
.pchan
= ((device
->FmtChans
!= DevFmtMono
) ? 2 : 1);
169 switch(device
->FmtType
)
197 par
.le
= SIO_LE_NATIVE
;
199 par
.round
= device
->UpdateSize
;
200 par
.appbufsz
= device
->UpdateSize
* (device
->NumUpdates
-1);
201 if(!par
.appbufsz
) par
.appbufsz
= device
->UpdateSize
;
203 if(!sio_setpar(self
->sndHandle
, &par
) || !sio_getpar(self
->sndHandle
, &par
))
205 ERR("Failed to set device parameters\n");
209 if(par
.bits
!= par
.bps
*8)
211 ERR("Padded samples not supported (%u of %u bits)\n", par
.bits
, par
.bps
*8);
215 device
->Frequency
= par
.rate
;
216 device
->FmtChans
= ((par
.pchan
==1) ? DevFmtMono
: DevFmtStereo
);
218 if(par
.bits
== 8 && par
.sig
== 1)
219 device
->FmtType
= DevFmtByte
;
220 else if(par
.bits
== 8 && par
.sig
== 0)
221 device
->FmtType
= DevFmtUByte
;
222 else if(par
.bits
== 16 && par
.sig
== 1)
223 device
->FmtType
= DevFmtShort
;
224 else if(par
.bits
== 16 && par
.sig
== 0)
225 device
->FmtType
= DevFmtUShort
;
226 else if(par
.bits
== 32 && par
.sig
== 1)
227 device
->FmtType
= DevFmtInt
;
228 else if(par
.bits
== 32 && par
.sig
== 0)
229 device
->FmtType
= DevFmtUInt
;
232 ERR("Unhandled sample format: %s %u-bit\n", (par
.sig
?"signed":"unsigned"), par
.bits
);
236 device
->UpdateSize
= par
.round
;
237 device
->NumUpdates
= (par
.bufsz
/par
.round
) + 1;
239 SetDefaultChannelOrder(device
);
244 static ALCboolean
ALCsndioBackend_start(ALCsndioBackend
*self
)
246 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
248 self
->data_size
= device
->UpdateSize
* FrameSizeFromDevFmt(
249 device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
251 al_free(self
->mix_data
);
252 self
->mix_data
= al_calloc(16, self
->data_size
);
254 if(!sio_start(self
->sndHandle
))
256 ERR("Error starting playback\n");
261 if(althrd_create(&self
->thread
, ALCsndioBackend_mixerProc
, self
) != althrd_success
)
263 sio_stop(self
->sndHandle
);
270 static void ALCsndioBackend_stop(ALCsndioBackend
*self
)
278 althrd_join(self
->thread
, &res
);
280 if(!sio_stop(self
->sndHandle
))
281 ERR("Error stopping device\n");
283 al_free(self
->mix_data
);
284 self
->mix_data
= NULL
;
288 typedef struct ALCsndioBackendFactory
{
289 DERIVE_FROM_TYPE(ALCbackendFactory
);
290 } ALCsndioBackendFactory
;
291 #define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
293 ALCbackendFactory
*ALCsndioBackendFactory_getFactory(void);
295 static ALCboolean
ALCsndioBackendFactory_init(ALCsndioBackendFactory
*self
);
296 static DECLARE_FORWARD(ALCsndioBackendFactory
, ALCbackendFactory
, void, deinit
)
297 static ALCboolean
ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory
*self
, ALCbackend_Type type
);
298 static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory
*self
, enum DevProbe type
);
299 static ALCbackend
* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
300 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory
);
303 ALCbackendFactory
*ALCsndioBackendFactory_getFactory(void)
305 static ALCsndioBackendFactory factory
= ALCSNDIOBACKENDFACTORY_INITIALIZER
;
306 return STATIC_CAST(ALCbackendFactory
, &factory
);
310 static ALCboolean
ALCsndioBackendFactory_init(ALCsndioBackendFactory
* UNUSED(self
))
312 /* No dynamic loading */
316 static ALCboolean
ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
318 if(type
== ALCbackend_Playback
)
323 static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory
* UNUSED(self
), enum DevProbe type
)
327 case ALL_DEVICE_PROBE
:
328 AppendAllDevicesList(sndio_device
);
330 case CAPTURE_DEVICE_PROBE
:
335 static ALCbackend
* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
337 if(type
== ALCbackend_Playback
)
339 ALCsndioBackend
*backend
;
340 NEW_OBJ(backend
, ALCsndioBackend
)(device
);
341 if(!backend
) return NULL
;
342 return STATIC_CAST(ALCbackend
, backend
);