Convert the SndIO backend factory
[openal-soft.git] / Alc / backends / sndio.cpp
blob691bdcde23c9981e906a1f7e53cac181163cb7a6
1 /**
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
21 #include "config.h"
23 #include "backends/sndio.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
29 #include "alMain.h"
30 #include "alu.h"
31 #include "threads.h"
32 #include "ringbuffer.h"
34 #include <sndio.h>
37 static const ALCchar sndio_device[] = "SndIO Default";
40 struct SndioPlayback final : public ALCbackend {
41 struct sio_hdl *sndHandle;
43 ALvoid *mix_data;
44 ALsizei data_size;
46 ATOMIC(int) killNow;
47 althrd_t thread;
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)
81 if(self->sndHandle)
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;
97 ALsizei frameSize;
98 size_t wrote;
100 SetRTPriority();
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);
117 if(wrote == 0)
119 ERR("sio_write failed\n");
120 SndioPlayback_lock(self);
121 aluHandleDisconnect(device, "Failed to write playback samples");
122 SndioPlayback_unlock(self);
123 break;
126 len -= wrote;
127 WritePtr += wrote;
131 return 0;
135 static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
137 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
139 if(!name)
140 name = sndio_device;
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);
154 return ALC_NO_ERROR;
157 static ALCboolean SndioPlayback_reset(SndioPlayback *self)
159 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
160 struct sio_par par;
162 sio_initpar(&par);
164 par.rate = device->Frequency;
165 par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
167 switch(device->FmtType)
169 case DevFmtByte:
170 par.bits = 8;
171 par.sig = 1;
172 break;
173 case DevFmtUByte:
174 par.bits = 8;
175 par.sig = 0;
176 break;
177 case DevFmtFloat:
178 case DevFmtShort:
179 par.bits = 16;
180 par.sig = 1;
181 break;
182 case DevFmtUShort:
183 par.bits = 16;
184 par.sig = 0;
185 break;
186 case DevFmtInt:
187 par.bits = 32;
188 par.sig = 1;
189 break;
190 case DevFmtUInt:
191 par.bits = 32;
192 par.sig = 0;
193 break;
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");
204 return ALC_FALSE;
207 if(par.bits != par.bps*8)
209 ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
210 return ALC_FALSE;
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;
228 else
230 ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
231 return ALC_FALSE;
234 device->UpdateSize = par.round;
235 device->NumUpdates = (par.bufsz/par.round) + 1;
237 SetDefaultChannelOrder(device);
239 return ALC_TRUE;
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");
255 return ALC_FALSE;
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);
262 return ALC_FALSE;
265 return ALC_TRUE;
268 static void SndioPlayback_stop(SndioPlayback *self)
270 int res;
272 if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
273 return;
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;
289 ATOMIC(int) killNow;
290 althrd_t thread;
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)
324 if(self->sndHandle)
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;
340 ALsizei frameSize;
342 SetRTPriority();
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];
351 size_t total, todo;
353 ll_ringbuffer_get_write_vector(self->ring, data);
354 todo = data[0].len + data[1].len;
355 if(todo == 0)
357 static char junk[4096];
358 sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
359 continue;
362 total = 0;
363 data[0].len *= frameSize;
364 data[1].len *= frameSize;
365 todo = minz(todo, device->UpdateSize) * frameSize;
366 while(total < todo)
368 size_t got;
370 if(!data[0].len)
371 data[0] = data[1];
373 got = sio_read(self->sndHandle, data[0].buf, minz(todo-total, data[0].len));
374 if(!got)
376 SndioCapture_lock(self);
377 aluHandleDisconnect(device, "Failed to read capture samples");
378 SndioCapture_unlock(self);
379 break;
382 data[0].buf += got;
383 data[0].len -= got;
384 total += got;
386 ll_ringbuffer_write_advance(self->ring, total / frameSize);
389 return 0;
393 static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
395 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
396 struct sio_par par;
398 if(!name)
399 name = sndio_device;
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;
410 sio_initpar(&par);
412 switch(device->FmtType)
414 case DevFmtByte:
415 par.bps = 1;
416 par.sig = 1;
417 break;
418 case DevFmtUByte:
419 par.bps = 1;
420 par.sig = 0;
421 break;
422 case DevFmtShort:
423 par.bps = 2;
424 par.sig = 1;
425 break;
426 case DevFmtUShort:
427 par.bps = 2;
428 par.sig = 0;
429 break;
430 case DevFmtInt:
431 par.bps = 4;
432 par.sig = 1;
433 break;
434 case DevFmtUInt:
435 par.bps = 4;
436 par.sig = 0;
437 break;
438 case DevFmtFloat:
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);
483 if(!self->ring)
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);
495 return ALC_NO_ERROR;
498 static ALCboolean SndioCapture_start(SndioCapture *self)
500 if(!sio_start(self->sndHandle))
502 ERR("Error starting playback\n");
503 return ALC_FALSE;
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);
510 return ALC_FALSE;
513 return ALC_TRUE;
516 static void SndioCapture_stop(SndioCapture *self)
518 int res;
520 if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
521 return;
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);
531 return ALC_NO_ERROR;
534 static ALCuint SndioCapture_availableSamples(SndioCapture *self)
536 return ll_ringbuffer_read_space(self->ring);
540 BackendFactory &SndIOBackendFactory::getFactory()
542 static SndIOBackendFactory factory{};
543 return factory;
546 bool SndIOBackendFactory::init()
547 { return true; }
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)
554 switch(type)
556 case ALL_DEVICE_PROBE:
557 case CAPTURE_DEVICE_PROBE:
558 /* Includes null char. */
559 outnames->append(sndio_device, sizeof(sndio_device));
560 break;
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);
581 return nullptr;