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
{nullptr};
43 ALvoid
*mix_data
{nullptr};
46 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
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
);
75 static void SndioPlayback_Destruct(SndioPlayback
*self
)
78 sio_close(self
->sndHandle
);
79 self
->sndHandle
= nullptr;
81 al_free(self
->mix_data
);
82 self
->mix_data
= nullptr;
84 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
85 self
->~SndioPlayback();
89 static int SndioPlayback_mixerProc(void *ptr
)
91 SndioPlayback
*self
= static_cast<SndioPlayback
*>(ptr
);
92 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
97 althrd_setname(MIXER_THREAD_NAME
);
99 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
);
101 while(!self
->mKillNow
.load(std::memory_order_acquire
) &&
102 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
104 ALsizei len
= self
->data_size
;
105 ALubyte
*WritePtr
= static_cast<ALubyte
*>(self
->mix_data
);
107 SndioPlayback_lock(self
);
108 aluMixData(device
, WritePtr
, len
/frameSize
);
109 SndioPlayback_unlock(self
);
110 while(len
> 0 && !self
->mKillNow
.load(std::memory_order_acquire
))
112 wrote
= sio_write(self
->sndHandle
, WritePtr
, len
);
115 ERR("sio_write failed\n");
116 SndioPlayback_lock(self
);
117 aluHandleDisconnect(device
, "Failed to write playback samples");
118 SndioPlayback_unlock(self
);
131 static ALCenum
SndioPlayback_open(SndioPlayback
*self
, const ALCchar
*name
)
133 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
137 else if(strcmp(name
, sndio_device
) != 0)
138 return ALC_INVALID_VALUE
;
140 self
->sndHandle
= sio_open(nullptr, SIO_PLAY
, 0);
141 if(self
->sndHandle
== nullptr)
143 ERR("Could not open device\n");
144 return ALC_INVALID_VALUE
;
147 device
->DeviceName
= name
;
151 static ALCboolean
SndioPlayback_reset(SndioPlayback
*self
)
153 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
158 par
.rate
= device
->Frequency
;
159 par
.pchan
= ((device
->FmtChans
!= DevFmtMono
) ? 2 : 1);
161 switch(device
->FmtType
)
189 par
.le
= SIO_LE_NATIVE
;
191 par
.round
= device
->UpdateSize
;
192 par
.appbufsz
= device
->UpdateSize
* (device
->NumUpdates
-1);
193 if(!par
.appbufsz
) par
.appbufsz
= device
->UpdateSize
;
195 if(!sio_setpar(self
->sndHandle
, &par
) || !sio_getpar(self
->sndHandle
, &par
))
197 ERR("Failed to set device parameters\n");
201 if(par
.bits
!= par
.bps
*8)
203 ERR("Padded samples not supported (%u of %u bits)\n", par
.bits
, par
.bps
*8);
207 device
->Frequency
= par
.rate
;
208 device
->FmtChans
= ((par
.pchan
==1) ? DevFmtMono
: DevFmtStereo
);
210 if(par
.bits
== 8 && par
.sig
== 1)
211 device
->FmtType
= DevFmtByte
;
212 else if(par
.bits
== 8 && par
.sig
== 0)
213 device
->FmtType
= DevFmtUByte
;
214 else if(par
.bits
== 16 && par
.sig
== 1)
215 device
->FmtType
= DevFmtShort
;
216 else if(par
.bits
== 16 && par
.sig
== 0)
217 device
->FmtType
= DevFmtUShort
;
218 else if(par
.bits
== 32 && par
.sig
== 1)
219 device
->FmtType
= DevFmtInt
;
220 else if(par
.bits
== 32 && par
.sig
== 0)
221 device
->FmtType
= DevFmtUInt
;
224 ERR("Unhandled sample format: %s %u-bit\n", (par
.sig
?"signed":"unsigned"), par
.bits
);
228 device
->UpdateSize
= par
.round
;
229 device
->NumUpdates
= (par
.bufsz
/par
.round
) + 1;
231 SetDefaultChannelOrder(device
);
236 static ALCboolean
SndioPlayback_start(SndioPlayback
*self
)
238 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
240 self
->data_size
= device
->UpdateSize
* FrameSizeFromDevFmt(
241 device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
243 al_free(self
->mix_data
);
244 self
->mix_data
= al_calloc(16, self
->data_size
);
246 if(!sio_start(self
->sndHandle
))
248 ERR("Error starting playback\n");
252 self
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
253 if(althrd_create(&self
->thread
, SndioPlayback_mixerProc
, self
) != althrd_success
)
255 sio_stop(self
->sndHandle
);
262 static void SndioPlayback_stop(SndioPlayback
*self
)
266 if(self
->mKillNow
.exchange(AL_TRUE
, std::memory_order_acq_rel
))
268 althrd_join(self
->thread
, &res
);
270 if(!sio_stop(self
->sndHandle
))
271 ERR("Error stopping device\n");
273 al_free(self
->mix_data
);
274 self
->mix_data
= nullptr;
278 struct SndioCapture final
: public ALCbackend
{
279 struct sio_hdl
*sndHandle
{nullptr};
281 ll_ringbuffer_t
*ring
{nullptr};
283 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
287 static int SndioCapture_recordProc(void *ptr
);
289 static void SndioCapture_Construct(SndioCapture
*self
, ALCdevice
*device
);
290 static void SndioCapture_Destruct(SndioCapture
*self
);
291 static ALCenum
SndioCapture_open(SndioCapture
*self
, const ALCchar
*name
);
292 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, ALCboolean
, reset
)
293 static ALCboolean
SndioCapture_start(SndioCapture
*self
);
294 static void SndioCapture_stop(SndioCapture
*self
);
295 static ALCenum
SndioCapture_captureSamples(SndioCapture
*self
, void *buffer
, ALCuint samples
);
296 static ALCuint
SndioCapture_availableSamples(SndioCapture
*self
);
297 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
298 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, void, lock
)
299 static DECLARE_FORWARD(SndioCapture
, ALCbackend
, void, unlock
)
300 DECLARE_DEFAULT_ALLOCATORS(SndioCapture
)
302 DEFINE_ALCBACKEND_VTABLE(SndioCapture
);
305 static void SndioCapture_Construct(SndioCapture
*self
, ALCdevice
*device
)
307 new (self
) SndioCapture
{};
308 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
309 SET_VTABLE2(SndioCapture
, ALCbackend
, self
);
312 static void SndioCapture_Destruct(SndioCapture
*self
)
315 sio_close(self
->sndHandle
);
316 self
->sndHandle
= nullptr;
318 ll_ringbuffer_free(self
->ring
);
319 self
->ring
= nullptr;
321 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
322 self
->~SndioCapture();
326 static int SndioCapture_recordProc(void* ptr
)
328 SndioCapture
*self
= static_cast<SndioCapture
*>(ptr
);
329 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
333 althrd_setname(RECORD_THREAD_NAME
);
335 frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
);
337 while(!self
->mKillNow
.load(std::memory_order_acquire
) &&
338 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
342 auto data
= ll_ringbuffer_get_write_vector(self
->ring
);
343 todo
= data
.first
.len
+ data
.second
.len
;
346 static char junk
[4096];
347 sio_read(self
->sndHandle
, junk
, minz(sizeof(junk
)/frameSize
, device
->UpdateSize
)*frameSize
);
352 data
.first
.len
*= frameSize
;
353 data
.second
.len
*= frameSize
;
354 todo
= minz(todo
, device
->UpdateSize
) * frameSize
;
360 data
.first
= data
.second
;
362 got
= sio_read(self
->sndHandle
, data
.first
.buf
, minz(todo
-total
, data
.first
.len
));
365 SndioCapture_lock(self
);
366 aluHandleDisconnect(device
, "Failed to read capture samples");
367 SndioCapture_unlock(self
);
371 data
.first
.buf
+= got
;
372 data
.first
.len
-= got
;
375 ll_ringbuffer_write_advance(self
->ring
, total
/ frameSize
);
382 static ALCenum
SndioCapture_open(SndioCapture
*self
, const ALCchar
*name
)
384 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
389 else if(strcmp(name
, sndio_device
) != 0)
390 return ALC_INVALID_VALUE
;
392 self
->sndHandle
= sio_open(nullptr, SIO_REC
, 0);
393 if(self
->sndHandle
== nullptr)
395 ERR("Could not open device\n");
396 return ALC_INVALID_VALUE
;
401 switch(device
->FmtType
)
428 ERR("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
429 return ALC_INVALID_VALUE
;
431 par
.bits
= par
.bps
* 8;
432 par
.le
= SIO_LE_NATIVE
;
433 par
.msb
= SIO_LE_NATIVE
? 0 : 1;
434 par
.rchan
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
435 par
.rate
= device
->Frequency
;
437 par
.appbufsz
= maxu(device
->UpdateSize
*device
->NumUpdates
, (device
->Frequency
+9)/10);
438 par
.round
= clampu(par
.appbufsz
/device
->NumUpdates
, (device
->Frequency
+99)/100,
439 (device
->Frequency
+19)/20);
441 device
->UpdateSize
= par
.round
;
442 device
->NumUpdates
= maxu(par
.appbufsz
/par
.round
, 1);
444 if(!sio_setpar(self
->sndHandle
, &par
) || !sio_getpar(self
->sndHandle
, &par
))
446 ERR("Failed to set device parameters\n");
447 return ALC_INVALID_VALUE
;
450 if(par
.bits
!= par
.bps
*8)
452 ERR("Padded samples not supported (%u of %u bits)\n", par
.bits
, par
.bps
*8);
453 return ALC_INVALID_VALUE
;
456 if(!((device
->FmtType
== DevFmtByte
&& par
.bits
== 8 && par
.sig
!= 0) ||
457 (device
->FmtType
== DevFmtUByte
&& par
.bits
== 8 && par
.sig
== 0) ||
458 (device
->FmtType
== DevFmtShort
&& par
.bits
== 16 && par
.sig
!= 0) ||
459 (device
->FmtType
== DevFmtUShort
&& par
.bits
== 16 && par
.sig
== 0) ||
460 (device
->FmtType
== DevFmtInt
&& par
.bits
== 32 && par
.sig
!= 0) ||
461 (device
->FmtType
== DevFmtUInt
&& par
.bits
== 32 && par
.sig
== 0)) ||
462 ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
) != (ALsizei
)par
.rchan
||
463 device
->Frequency
!= par
.rate
)
465 ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
466 DevFmtTypeString(device
->FmtType
), DevFmtChannelsString(device
->FmtChans
),
467 device
->Frequency
, par
.sig
?'s':'u', par
.bits
, par
.rchan
, par
.rate
);
468 return ALC_INVALID_VALUE
;
471 self
->ring
= ll_ringbuffer_create(device
->UpdateSize
*device
->NumUpdates
, par
.bps
*par
.rchan
, 0);
474 ERR("Failed to allocate %u-byte ringbuffer\n",
475 device
->UpdateSize
*device
->NumUpdates
*par
.bps
*par
.rchan
);
476 return ALC_OUT_OF_MEMORY
;
479 SetDefaultChannelOrder(device
);
481 device
->DeviceName
= name
;
485 static ALCboolean
SndioCapture_start(SndioCapture
*self
)
487 if(!sio_start(self
->sndHandle
))
489 ERR("Error starting playback\n");
493 self
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
494 if(althrd_create(&self
->thread
, SndioCapture_recordProc
, self
) != althrd_success
)
496 sio_stop(self
->sndHandle
);
503 static void SndioCapture_stop(SndioCapture
*self
)
507 if(self
->mKillNow
.exchange(AL_TRUE
, std::memory_order_acq_rel
))
509 althrd_join(self
->thread
, &res
);
511 if(!sio_stop(self
->sndHandle
))
512 ERR("Error stopping device\n");
515 static ALCenum
SndioCapture_captureSamples(SndioCapture
*self
, void *buffer
, ALCuint samples
)
517 ll_ringbuffer_read(self
->ring
, static_cast<char*>(buffer
), samples
);
521 static ALCuint
SndioCapture_availableSamples(SndioCapture
*self
)
523 return ll_ringbuffer_read_space(self
->ring
);
527 BackendFactory
&SndIOBackendFactory::getFactory()
529 static SndIOBackendFactory factory
{};
533 bool SndIOBackendFactory::init()
536 bool SndIOBackendFactory::querySupport(ALCbackend_Type type
)
537 { return (type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
); }
539 void SndIOBackendFactory::probe(enum DevProbe type
, std::string
*outnames
)
543 case ALL_DEVICE_PROBE
:
544 case CAPTURE_DEVICE_PROBE
:
545 /* Includes null char. */
546 outnames
->append(sndio_device
, sizeof(sndio_device
));
551 ALCbackend
*SndIOBackendFactory::createBackend(ALCdevice
*device
, ALCbackend_Type type
)
553 if(type
== ALCbackend_Playback
)
555 SndioPlayback
*backend
;
556 NEW_OBJ(backend
, SndioPlayback
)(device
);
557 if(!backend
) return nullptr;
558 return STATIC_CAST(ALCbackend
, backend
);
560 if(type
== ALCbackend_Capture
)
562 SndioCapture
*backend
;
563 NEW_OBJ(backend
, SndioCapture
)(device
);
564 if(!backend
) return nullptr;
565 return STATIC_CAST(ALCbackend
, backend
);