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 "backends/sndio.h"
32 #include "ringbuffer.h"
37 static const ALCchar sndio_device
[] = "SndIO Default";
40 struct SndioPlayback final
: public ALCbackend
{
41 struct sio_hdl
*sndHandle
;
50 static int SndioPlayback_mixerProc(void *ptr
);
52 static void SndioPlayback_Construct(SndioPlayback
*self
, ALCdevice
*device
);
53 static void SndioPlayback_Destruct(SndioPlayback
*self
);
54 static ALCenum
SndioPlayback_open(SndioPlayback
*self
, const ALCchar
*name
);
55 static ALCboolean
SndioPlayback_reset(SndioPlayback
*self
);
56 static ALCboolean
SndioPlayback_start(SndioPlayback
*self
);
57 static void SndioPlayback_stop(SndioPlayback
*self
);
58 static DECLARE_FORWARD2(SndioPlayback
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
59 static DECLARE_FORWARD(SndioPlayback
, ALCbackend
, ALCuint
, availableSamples
)
60 static DECLARE_FORWARD(SndioPlayback
, ALCbackend
, ClockLatency
, getClockLatency
)
61 static DECLARE_FORWARD(SndioPlayback
, ALCbackend
, void, lock
)
62 static DECLARE_FORWARD(SndioPlayback
, ALCbackend
, void, unlock
)
63 DECLARE_DEFAULT_ALLOCATORS(SndioPlayback
)
65 DEFINE_ALCBACKEND_VTABLE(SndioPlayback
);
68 static void SndioPlayback_Construct(SndioPlayback
*self
, ALCdevice
*device
)
70 new (self
) SndioPlayback
{};
71 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
72 SET_VTABLE2(SndioPlayback
, ALCbackend
, self
);
74 self
->sndHandle
= nullptr;
75 self
->mix_data
= nullptr;
76 ATOMIC_INIT(&self
->killNow
, AL_TRUE
);
79 static void SndioPlayback_Destruct(SndioPlayback
*self
)
82 sio_close(self
->sndHandle
);
83 self
->sndHandle
= nullptr;
85 al_free(self
->mix_data
);
86 self
->mix_data
= nullptr;
88 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
89 self
->~SndioPlayback();
93 static int SndioPlayback_mixerProc(void *ptr
)
95 SndioPlayback
*self
= static_cast<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
= static_cast<ALubyte
*>(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 SndioPlayback_lock(self
);
121 aluHandleDisconnect(device
, "Failed to write playback samples");
122 SndioPlayback_unlock(self
);
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(nullptr, SIO_PLAY
, 0);
145 if(self
->sndHandle
== nullptr)
147 ERR("Could not open device\n");
148 return ALC_INVALID_VALUE
;
151 al_free(device
->DeviceName
);
152 device
->DeviceName
= alstrdup(name
);
157 static ALCboolean
SndioPlayback_reset(SndioPlayback
*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
SndioPlayback_start(SndioPlayback
*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
, SndioPlayback_mixerProc
, self
) != althrd_success
)
261 sio_stop(self
->sndHandle
);
268 static void SndioPlayback_stop(SndioPlayback
*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
= nullptr;
284 struct SndioCapture final
: public ALCbackend
{
285 struct sio_hdl
*sndHandle
;
287 ll_ringbuffer_t
*ring
;
293 static int SndioCapture_recordProc(void *ptr
);
295 static void SndioCapture_Construct(SndioCapture
*self
, ALCdevice
*device
);
296 static void SndioCapture_Destruct(SndioCapture
*self
);
297 static ALCenum
SndioCapture_open(SndioCapture
*self
, const ALCchar
*name
);
298 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, ALCboolean
, reset
)
299 static ALCboolean
SndioCapture_start(SndioCapture
*self
);
300 static void SndioCapture_stop(SndioCapture
*self
);
301 static ALCenum
SndioCapture_captureSamples(SndioCapture
*self
, void *buffer
, ALCuint samples
);
302 static ALCuint
SndioCapture_availableSamples(SndioCapture
*self
);
303 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
304 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, void, lock
)
305 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, void, unlock
)
306 DECLARE_DEFAULT_ALLOCATORS(SndioCapture
)
308 DEFINE_ALCBACKEND_VTABLE(SndioCapture
);
311 static void SndioCapture_Construct(SndioCapture
*self
, ALCdevice
*device
)
313 new (self
) SndioCapture
{};
314 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
315 SET_VTABLE2(SndioCapture
, ALCbackend
, self
);
317 self
->sndHandle
= nullptr;
318 self
->ring
= nullptr;
319 ATOMIC_INIT(&self
->killNow
, AL_TRUE
);
322 static void SndioCapture_Destruct(SndioCapture
*self
)
325 sio_close(self
->sndHandle
);
326 self
->sndHandle
= nullptr;
328 ll_ringbuffer_free(self
->ring
);
329 self
->ring
= nullptr;
331 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
332 self
->~SndioCapture();
336 static int SndioCapture_recordProc(void* ptr
)
338 SndioCapture
*self
= static_cast<SndioCapture
*>(ptr
);
339 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
343 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
345 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
347 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
) &&
348 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
350 ll_ringbuffer_data_t data
[2];
353 ll_ringbuffer_get_write_vector(self
->ring
, data
);
354 todo
= data
[0].len
+ data
[1].len
;
357 static char junk
[4096];
358 sio_read(self
->sndHandle
, junk
, minz(sizeof(junk
)/frameSize
, device
->UpdateSize
)*frameSize
);
363 data
[0].len
*= frameSize
;
364 data
[1].len
*= frameSize
;
365 todo
= minz(todo
, device
->UpdateSize
) * frameSize
;
373 got
= sio_read(self
->sndHandle
, data
[0].buf
, minz(todo
-total
, data
[0].len
));
376 SndioCapture_lock(self
);
377 aluHandleDisconnect(device
, "Failed to read capture samples");
378 SndioCapture_unlock(self
);
386 ll_ringbuffer_write_advance(self
->ring
, total
/ frameSize
);
393 static ALCenum
SndioCapture_open(SndioCapture
*self
, const ALCchar
*name
)
395 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
400 else if(strcmp(name
, sndio_device
) != 0)
401 return ALC_INVALID_VALUE
;
403 self
->sndHandle
= sio_open(nullptr, SIO_REC
, 0);
404 if(self
->sndHandle
== nullptr)
406 ERR("Could not open device\n");
407 return ALC_INVALID_VALUE
;
412 switch(device
->FmtType
)
439 ERR("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
440 return ALC_INVALID_VALUE
;
442 par
.bits
= par
.bps
* 8;
443 par
.le
= SIO_LE_NATIVE
;
444 par
.msb
= SIO_LE_NATIVE
? 0 : 1;
445 par
.rchan
= ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
);
446 par
.rate
= device
->Frequency
;
448 par
.appbufsz
= maxu(device
->UpdateSize
*device
->NumUpdates
, (device
->Frequency
+9)/10);
449 par
.round
= clampu(par
.appbufsz
/device
->NumUpdates
, (device
->Frequency
+99)/100,
450 (device
->Frequency
+19)/20);
452 device
->UpdateSize
= par
.round
;
453 device
->NumUpdates
= maxu(par
.appbufsz
/par
.round
, 1);
455 if(!sio_setpar(self
->sndHandle
, &par
) || !sio_getpar(self
->sndHandle
, &par
))
457 ERR("Failed to set device parameters\n");
458 return ALC_INVALID_VALUE
;
461 if(par
.bits
!= par
.bps
*8)
463 ERR("Padded samples not supported (%u of %u bits)\n", par
.bits
, par
.bps
*8);
464 return ALC_INVALID_VALUE
;
467 if(!((device
->FmtType
== DevFmtByte
&& par
.bits
== 8 && par
.sig
!= 0) ||
468 (device
->FmtType
== DevFmtUByte
&& par
.bits
== 8 && par
.sig
== 0) ||
469 (device
->FmtType
== DevFmtShort
&& par
.bits
== 16 && par
.sig
!= 0) ||
470 (device
->FmtType
== DevFmtUShort
&& par
.bits
== 16 && par
.sig
== 0) ||
471 (device
->FmtType
== DevFmtInt
&& par
.bits
== 32 && par
.sig
!= 0) ||
472 (device
->FmtType
== DevFmtUInt
&& par
.bits
== 32 && par
.sig
== 0)) ||
473 ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
) != (ALsizei
)par
.rchan
||
474 device
->Frequency
!= par
.rate
)
476 ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
477 DevFmtTypeString(device
->FmtType
), DevFmtChannelsString(device
->FmtChans
),
478 device
->Frequency
, par
.sig
?'s':'u', par
.bits
, par
.rchan
, par
.rate
);
479 return ALC_INVALID_VALUE
;
482 self
->ring
= ll_ringbuffer_create(device
->UpdateSize
*device
->NumUpdates
, par
.bps
*par
.rchan
, 0);
485 ERR("Failed to allocate %u-byte ringbuffer\n",
486 device
->UpdateSize
*device
->NumUpdates
*par
.bps
*par
.rchan
);
487 return ALC_OUT_OF_MEMORY
;
490 SetDefaultChannelOrder(device
);
492 al_free(device
->DeviceName
);
493 device
->DeviceName
= alstrdup(name
);
498 static ALCboolean
SndioCapture_start(SndioCapture
*self
)
500 if(!sio_start(self
->sndHandle
))
502 ERR("Error starting playback\n");
506 ATOMIC_STORE(&self
->killNow
, AL_FALSE
, almemory_order_release
);
507 if(althrd_create(&self
->thread
, SndioCapture_recordProc
, self
) != althrd_success
)
509 sio_stop(self
->sndHandle
);
516 static void SndioCapture_stop(SndioCapture
*self
)
520 if(ATOMIC_EXCHANGE(&self
->killNow
, AL_TRUE
, almemory_order_acq_rel
))
522 althrd_join(self
->thread
, &res
);
524 if(!sio_stop(self
->sndHandle
))
525 ERR("Error stopping device\n");
528 static ALCenum
SndioCapture_captureSamples(SndioCapture
*self
, void *buffer
, ALCuint samples
)
530 ll_ringbuffer_read(self
->ring
, static_cast<char*>(buffer
), samples
);
534 static ALCuint
SndioCapture_availableSamples(SndioCapture
*self
)
536 return ll_ringbuffer_read_space(self
->ring
);
540 BackendFactory
&SndIOBackendFactory::getFactory()
542 static SndIOBackendFactory factory
{};
546 bool SndIOBackendFactory::init()
549 bool SndIOBackendFactory::querySupport(ALCbackend_Type type
)
550 { return (type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
); }
552 void SndIOBackendFactory::probe(enum DevProbe type
, std::string
*outnames
)
556 case ALL_DEVICE_PROBE
:
557 case CAPTURE_DEVICE_PROBE
:
558 /* Includes null char. */
559 outnames
->append(sndio_device
, sizeof(sndio_device
));
564 ALCbackend
*SndIOBackendFactory::createBackend(ALCdevice
*device
, ALCbackend_Type type
)
566 if(type
== ALCbackend_Playback
)
568 SndioPlayback
*backend
;
569 NEW_OBJ(backend
, SndioPlayback
)(device
);
570 if(!backend
) return nullptr;
571 return STATIC_CAST(ALCbackend
, backend
);
573 if(type
== ALCbackend_Capture
)
575 SndioCapture
*backend
;
576 NEW_OBJ(backend
, SndioCapture
)(device
);
577 if(!backend
) return nullptr;
578 return STATIC_CAST(ALCbackend
, backend
);