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 ALCboolean
ALCsndioBackend_reset(ALCsndioBackend
*self
);
56 static ALCboolean
ALCsndioBackend_start(ALCsndioBackend
*self
);
57 static void ALCsndioBackend_stop(ALCsndioBackend
*self
);
58 static DECLARE_FORWARD2(ALCsndioBackend
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
59 static DECLARE_FORWARD(ALCsndioBackend
, ALCbackend
, ALCuint
, availableSamples
)
60 static DECLARE_FORWARD(ALCsndioBackend
, ALCbackend
, ClockLatency
, getClockLatency
)
61 static DECLARE_FORWARD(ALCsndioBackend
, ALCbackend
, void, lock
)
62 static DECLARE_FORWARD(ALCsndioBackend
, ALCbackend
, void, unlock
)
63 DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend
)
65 DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend
);
68 static const ALCchar sndio_device
[] = "SndIO Default";
71 static void ALCsndioBackend_Construct(ALCsndioBackend
*self
, ALCdevice
*device
)
73 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
74 SET_VTABLE2(ALCsndioBackend
, ALCbackend
, self
);
76 self
->sndHandle
= NULL
;
77 self
->mix_data
= NULL
;
78 ATOMIC_INIT(&self
->killNow
, AL_TRUE
);
81 static void ALCsndioBackend_Destruct(ALCsndioBackend
*self
)
84 sio_close(self
->sndHandle
);
85 self
->sndHandle
= NULL
;
87 al_free(self
->mix_data
);
88 self
->mix_data
= NULL
;
90 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
94 static int ALCsndioBackend_mixerProc(void *ptr
)
96 ALCsndioBackend
*self
= (ALCsndioBackend
*)ptr
;
97 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
102 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
104 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
106 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
) &&
107 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
109 ALsizei len
= self
->data_size
;
110 ALubyte
*WritePtr
= self
->mix_data
;
112 ALCsndioBackend_lock(self
);
113 aluMixData(device
, WritePtr
, len
/frameSize
);
114 ALCsndioBackend_unlock(self
);
115 while(len
> 0 && !ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
))
117 wrote
= sio_write(self
->sndHandle
, WritePtr
, len
);
120 ERR("sio_write failed\n");
121 ALCdevice_Lock(device
);
122 aluHandleDisconnect(device
, "Failed to write playback samples");
123 ALCdevice_Unlock(device
);
136 static ALCenum
ALCsndioBackend_open(ALCsndioBackend
*self
, const ALCchar
*name
)
138 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
142 else if(strcmp(name
, sndio_device
) != 0)
143 return ALC_INVALID_VALUE
;
145 self
->sndHandle
= sio_open(NULL
, SIO_PLAY
, 0);
146 if(self
->sndHandle
== NULL
)
148 ERR("Could not open device\n");
149 return ALC_INVALID_VALUE
;
152 alstr_copy_cstr(&device
->DeviceName
, name
);
157 static ALCboolean
ALCsndioBackend_reset(ALCsndioBackend
*self
)
159 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
164 par
.rate
= device
->Frequency
;
165 par
.pchan
= ((device
->FmtChans
!= DevFmtMono
) ? 2 : 1);
167 switch(device
->FmtType
)
195 par
.le
= SIO_LE_NATIVE
;
197 par
.round
= device
->UpdateSize
;
198 par
.appbufsz
= device
->UpdateSize
* (device
->NumUpdates
-1);
199 if(!par
.appbufsz
) par
.appbufsz
= device
->UpdateSize
;
201 if(!sio_setpar(self
->sndHandle
, &par
) || !sio_getpar(self
->sndHandle
, &par
))
203 ERR("Failed to set device parameters\n");
207 if(par
.bits
!= par
.bps
*8)
209 ERR("Padded samples not supported (%u of %u bits)\n", par
.bits
, par
.bps
*8);
213 device
->Frequency
= par
.rate
;
214 device
->FmtChans
= ((par
.pchan
==1) ? DevFmtMono
: DevFmtStereo
);
216 if(par
.bits
== 8 && par
.sig
== 1)
217 device
->FmtType
= DevFmtByte
;
218 else if(par
.bits
== 8 && par
.sig
== 0)
219 device
->FmtType
= DevFmtUByte
;
220 else if(par
.bits
== 16 && par
.sig
== 1)
221 device
->FmtType
= DevFmtShort
;
222 else if(par
.bits
== 16 && par
.sig
== 0)
223 device
->FmtType
= DevFmtUShort
;
224 else if(par
.bits
== 32 && par
.sig
== 1)
225 device
->FmtType
= DevFmtInt
;
226 else if(par
.bits
== 32 && par
.sig
== 0)
227 device
->FmtType
= DevFmtUInt
;
230 ERR("Unhandled sample format: %s %u-bit\n", (par
.sig
?"signed":"unsigned"), par
.bits
);
234 device
->UpdateSize
= par
.round
;
235 device
->NumUpdates
= (par
.bufsz
/par
.round
) + 1;
237 SetDefaultChannelOrder(device
);
242 static ALCboolean
ALCsndioBackend_start(ALCsndioBackend
*self
)
244 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
246 self
->data_size
= device
->UpdateSize
* FrameSizeFromDevFmt(
247 device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
249 al_free(self
->mix_data
);
250 self
->mix_data
= al_calloc(16, self
->data_size
);
252 if(!sio_start(self
->sndHandle
))
254 ERR("Error starting playback\n");
258 ATOMIC_STORE(&self
->killNow
, AL_FALSE
, almemory_order_release
);
259 if(althrd_create(&self
->thread
, ALCsndioBackend_mixerProc
, self
) != althrd_success
)
261 sio_stop(self
->sndHandle
);
268 static void ALCsndioBackend_stop(ALCsndioBackend
*self
)
272 if(ATOMIC_EXCHANGE(&self
->killNow
, AL_TRUE
, almemory_order_acq_rel
))
274 althrd_join(self
->thread
, &res
);
276 if(!sio_stop(self
->sndHandle
))
277 ERR("Error stopping device\n");
279 al_free(self
->mix_data
);
280 self
->mix_data
= NULL
;
284 typedef struct ALCsndioBackendFactory
{
285 DERIVE_FROM_TYPE(ALCbackendFactory
);
286 } ALCsndioBackendFactory
;
287 #define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
289 ALCbackendFactory
*ALCsndioBackendFactory_getFactory(void);
291 static ALCboolean
ALCsndioBackendFactory_init(ALCsndioBackendFactory
*self
);
292 static DECLARE_FORWARD(ALCsndioBackendFactory
, ALCbackendFactory
, void, deinit
)
293 static ALCboolean
ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory
*self
, ALCbackend_Type type
);
294 static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory
*self
, enum DevProbe type
);
295 static ALCbackend
* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
296 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory
);
299 ALCbackendFactory
*ALCsndioBackendFactory_getFactory(void)
301 static ALCsndioBackendFactory factory
= ALCSNDIOBACKENDFACTORY_INITIALIZER
;
302 return STATIC_CAST(ALCbackendFactory
, &factory
);
306 static ALCboolean
ALCsndioBackendFactory_init(ALCsndioBackendFactory
* UNUSED(self
))
308 /* No dynamic loading */
312 static ALCboolean
ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
314 if(type
== ALCbackend_Playback
)
319 static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory
* UNUSED(self
), enum DevProbe type
)
323 case ALL_DEVICE_PROBE
:
324 AppendAllDevicesList(sndio_device
);
326 case CAPTURE_DEVICE_PROBE
:
331 static ALCbackend
* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
333 if(type
== ALCbackend_Playback
)
335 ALCsndioBackend
*backend
;
336 NEW_OBJ(backend
, ALCsndioBackend
)(device
);
337 if(!backend
) return NULL
;
338 return STATIC_CAST(ALCbackend
, backend
);