Implement capture support for SoundIO
[openal-soft.git] / Alc / backends / sndio.c
blob062e67321b33d43db481b7ad4102033a0474a25e
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 <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include "alMain.h"
28 #include "alu.h"
29 #include "threads.h"
30 #include "ringbuffer.h"
32 #include "backends/base.h"
34 #include <sndio.h>
37 static const ALCchar sndio_device[] = "SndIO Default";
40 typedef struct SndioPlayback {
41 DERIVE_FROM_TYPE(ALCbackend);
43 struct sio_hdl *sndHandle;
45 ALvoid *mix_data;
46 ALsizei data_size;
48 ATOMIC(int) killNow;
49 althrd_t thread;
50 } SndioPlayback;
52 static int SndioPlayback_mixerProc(void *ptr);
54 static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
55 static void SndioPlayback_Destruct(SndioPlayback *self);
56 static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
57 static ALCboolean SndioPlayback_reset(SndioPlayback *self);
58 static ALCboolean SndioPlayback_start(SndioPlayback *self);
59 static void SndioPlayback_stop(SndioPlayback *self);
60 static DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
61 static DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
62 static DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
63 static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
64 static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
65 DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
67 DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
70 static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
72 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
73 SET_VTABLE2(SndioPlayback, ALCbackend, self);
75 self->sndHandle = NULL;
76 self->mix_data = NULL;
77 ATOMIC_INIT(&self->killNow, AL_TRUE);
80 static void SndioPlayback_Destruct(SndioPlayback *self)
82 if(self->sndHandle)
83 sio_close(self->sndHandle);
84 self->sndHandle = NULL;
86 al_free(self->mix_data);
87 self->mix_data = NULL;
89 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
93 static int SndioPlayback_mixerProc(void *ptr)
95 SndioPlayback *self = (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 = 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 ALCdevice_Lock(device);
121 aluHandleDisconnect(device, "Failed to write playback samples");
122 ALCdevice_Unlock(device);
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(NULL, SIO_PLAY, 0);
145 if(self->sndHandle == NULL)
147 ERR("Could not open device\n");
148 return ALC_INVALID_VALUE;
151 alstr_copy_cstr(&device->DeviceName, name);
153 return ALC_NO_ERROR;
156 static ALCboolean SndioPlayback_reset(SndioPlayback *self)
158 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
159 struct sio_par par;
161 sio_initpar(&par);
163 par.rate = device->Frequency;
164 par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
166 switch(device->FmtType)
168 case DevFmtByte:
169 par.bits = 8;
170 par.sig = 1;
171 break;
172 case DevFmtUByte:
173 par.bits = 8;
174 par.sig = 0;
175 break;
176 case DevFmtFloat:
177 case DevFmtShort:
178 par.bits = 16;
179 par.sig = 1;
180 break;
181 case DevFmtUShort:
182 par.bits = 16;
183 par.sig = 0;
184 break;
185 case DevFmtInt:
186 par.bits = 32;
187 par.sig = 1;
188 break;
189 case DevFmtUInt:
190 par.bits = 32;
191 par.sig = 0;
192 break;
194 par.le = SIO_LE_NATIVE;
196 par.round = device->UpdateSize;
197 par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
198 if(!par.appbufsz) par.appbufsz = device->UpdateSize;
200 if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
202 ERR("Failed to set device parameters\n");
203 return ALC_FALSE;
206 if(par.bits != par.bps*8)
208 ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
209 return ALC_FALSE;
212 device->Frequency = par.rate;
213 device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
215 if(par.bits == 8 && par.sig == 1)
216 device->FmtType = DevFmtByte;
217 else if(par.bits == 8 && par.sig == 0)
218 device->FmtType = DevFmtUByte;
219 else if(par.bits == 16 && par.sig == 1)
220 device->FmtType = DevFmtShort;
221 else if(par.bits == 16 && par.sig == 0)
222 device->FmtType = DevFmtUShort;
223 else if(par.bits == 32 && par.sig == 1)
224 device->FmtType = DevFmtInt;
225 else if(par.bits == 32 && par.sig == 0)
226 device->FmtType = DevFmtUInt;
227 else
229 ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
230 return ALC_FALSE;
233 device->UpdateSize = par.round;
234 device->NumUpdates = (par.bufsz/par.round) + 1;
236 SetDefaultChannelOrder(device);
238 return ALC_TRUE;
241 static ALCboolean SndioPlayback_start(SndioPlayback *self)
243 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
245 self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
246 device->FmtChans, device->FmtType, device->AmbiOrder
248 al_free(self->mix_data);
249 self->mix_data = al_calloc(16, self->data_size);
251 if(!sio_start(self->sndHandle))
253 ERR("Error starting playback\n");
254 return ALC_FALSE;
257 ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
258 if(althrd_create(&self->thread, SndioPlayback_mixerProc, self) != althrd_success)
260 sio_stop(self->sndHandle);
261 return ALC_FALSE;
264 return ALC_TRUE;
267 static void SndioPlayback_stop(SndioPlayback *self)
269 int res;
271 if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
272 return;
273 althrd_join(self->thread, &res);
275 if(!sio_stop(self->sndHandle))
276 ERR("Error stopping device\n");
278 al_free(self->mix_data);
279 self->mix_data = NULL;
283 typedef struct SndioCapture {
284 DERIVE_FROM_TYPE(ALCbackend);
286 struct sio_hdl *sndHandle;
288 ll_ringbuffer_t *ring;
290 ATOMIC(int) killNow;
291 althrd_t thread;
292 } SndioCapture;
294 static int SndioCapture_recordProc(void *ptr);
296 static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
297 static void SndioCapture_Destruct(SndioCapture *self);
298 static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
299 static DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
300 static ALCboolean SndioCapture_start(SndioCapture *self);
301 static void SndioCapture_stop(SndioCapture *self);
302 static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
303 static ALCuint SndioCapture_availableSamples(SndioCapture *self);
304 static DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
305 static DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
306 static DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
307 DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
309 DEFINE_ALCBACKEND_VTABLE(SndioCapture);
312 static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
314 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
315 SET_VTABLE2(SndioCapture, ALCbackend, self);
317 self->sndHandle = NULL;
318 self->ring = NULL;
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 = NULL;
328 ll_ringbuffer_free(self->ring);
329 self->ring = NULL;
331 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
335 static int SndioCapture_recordProc(void* ptr)
337 SndioCapture *self = (SndioCapture*)ptr;
338 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
339 ALsizei frameSize;
341 SetRTPriority();
342 althrd_setname(althrd_current(), RECORD_THREAD_NAME);
344 frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
346 while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
347 ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
349 ll_ringbuffer_data_t data[2];
350 size_t total, todo;
352 ll_ringbuffer_get_write_vector(self->ring, data);
353 todo = data[0].len + data[1].len;
354 if(todo == 0)
356 static char junk[4096];
357 sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
358 continue;
361 total = 0;
362 data[0].len *= frameSize;
363 data[1].len *= frameSize;
364 todo = minz(todo, device->UpdateSize) * frameSize;
365 while(total < todo)
367 size_t got;
369 if(!data[0].len)
370 data[0] = data[1];
372 got = sio_read(self->sndHandle, data[0].buf, minz(todo-total, data[0].len));
373 if(!got)
375 SndioCapture_lock(self);
376 aluHandleDisconnect(device, "Failed to read capture samples");
377 SndioCapture_unlock(self);
378 break;
381 data[0].buf += got;
382 data[0].len -= got;
383 total += got;
385 ll_ringbuffer_write_advance(self->ring, total / frameSize);
388 return 0;
392 static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
394 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
395 struct sio_par par;
397 if(!name)
398 name = sndio_device;
399 else if(strcmp(name, sndio_device) != 0)
400 return ALC_INVALID_VALUE;
402 self->sndHandle = sio_open(NULL, SIO_REC, 0);
403 if(self->sndHandle == NULL)
405 ERR("Could not open device\n");
406 return ALC_INVALID_VALUE;
409 sio_initpar(&par);
411 switch(device->FmtType)
413 case DevFmtByte:
414 par.bps = 1;
415 par.sig = 1;
416 break;
417 case DevFmtUByte:
418 par.bps = 1;
419 par.sig = 0;
420 break;
421 case DevFmtShort:
422 par.bps = 2;
423 par.sig = 1;
424 break;
425 case DevFmtUShort:
426 par.bps = 2;
427 par.sig = 0;
428 break;
429 case DevFmtInt:
430 par.bps = 4;
431 par.sig = 1;
432 break;
433 case DevFmtUInt:
434 par.bps = 4;
435 par.sig = 0;
436 break;
437 case DevFmtFloat:
438 ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
439 return ALC_INVALID_VALUE;
441 par.bits = par.bps * 8;
442 par.le = SIO_LE_NATIVE;
443 par.msb = SIO_LE_NATIVE ? 0 : 1;
444 par.rchan = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
445 par.rate = device->Frequency;
447 par.appbufsz = maxu(device->UpdateSize*device->NumUpdates, (device->Frequency+9)/10);
448 par.round = clampu(par.appbufsz/device->NumUpdates, (device->Frequency+99)/100,
449 (device->Frequency+19)/20);
451 device->UpdateSize = par.round;
452 device->NumUpdates = maxu(par.appbufsz/par.round, 1);
454 if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
456 ERR("Failed to set device parameters\n");
457 return ALC_INVALID_VALUE;
460 if(par.bits != par.bps*8)
462 ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
463 return ALC_INVALID_VALUE;
466 if(!((device->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
467 (device->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
468 (device->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
469 (device->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
470 (device->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
471 (device->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
472 ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)par.rchan ||
473 device->Frequency != par.rate)
475 ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
476 DevFmtTypeString(device->FmtType), DevFmtChannelsString(device->FmtChans),
477 device->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
478 return ALC_INVALID_VALUE;
481 self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, par.bps*par.rchan, 0);
482 if(!self->ring)
484 ERR("Failed to allocate %u-byte ringbuffer\n",
485 device->UpdateSize*device->NumUpdates*par.bps*par.rchan);
486 return ALC_OUT_OF_MEMORY;
489 SetDefaultChannelOrder(device);
491 alstr_copy_cstr(&device->DeviceName, name);
493 return ALC_NO_ERROR;
496 static ALCboolean SndioCapture_start(SndioCapture *self)
498 if(!sio_start(self->sndHandle))
500 ERR("Error starting playback\n");
501 return ALC_FALSE;
504 ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
505 if(althrd_create(&self->thread, SndioCapture_recordProc, self) != althrd_success)
507 sio_stop(self->sndHandle);
508 return ALC_FALSE;
511 return ALC_TRUE;
514 static void SndioCapture_stop(SndioCapture *self)
516 int res;
518 if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
519 return;
520 althrd_join(self->thread, &res);
522 if(!sio_stop(self->sndHandle))
523 ERR("Error stopping device\n");
526 static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples)
528 ll_ringbuffer_read(self->ring, buffer, samples);
529 return ALC_NO_ERROR;
532 static ALCuint SndioCapture_availableSamples(SndioCapture *self)
534 return ll_ringbuffer_read_space(self->ring);
538 typedef struct SndioBackendFactory {
539 DERIVE_FROM_TYPE(ALCbackendFactory);
540 } SndioBackendFactory;
541 #define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
543 ALCbackendFactory *SndioBackendFactory_getFactory(void);
545 static ALCboolean SndioBackendFactory_init(SndioBackendFactory *self);
546 static DECLARE_FORWARD(SndioBackendFactory, ALCbackendFactory, void, deinit)
547 static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory *self, ALCbackend_Type type);
548 static void SndioBackendFactory_probe(SndioBackendFactory *self, enum DevProbe type);
549 static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
550 DEFINE_ALCBACKENDFACTORY_VTABLE(SndioBackendFactory);
552 ALCbackendFactory *SndioBackendFactory_getFactory(void)
554 static SndioBackendFactory factory = SNDIOBACKENDFACTORY_INITIALIZER;
555 return STATIC_CAST(ALCbackendFactory, &factory);
558 static ALCboolean SndioBackendFactory_init(SndioBackendFactory* UNUSED(self))
560 /* No dynamic loading */
561 return ALC_TRUE;
564 static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
566 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
567 return ALC_TRUE;
568 return ALC_FALSE;
571 static void SndioBackendFactory_probe(SndioBackendFactory* UNUSED(self), enum DevProbe type)
573 switch(type)
575 case ALL_DEVICE_PROBE:
576 AppendAllDevicesList(sndio_device);
577 break;
578 case CAPTURE_DEVICE_PROBE:
579 AppendCaptureDeviceList(sndio_device);
580 break;
584 static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
586 if(type == ALCbackend_Playback)
588 SndioPlayback *backend;
589 NEW_OBJ(backend, SndioPlayback)(device);
590 if(!backend) return NULL;
591 return STATIC_CAST(ALCbackend, backend);
593 if(type == ALCbackend_Capture)
595 SndioCapture *backend;
596 NEW_OBJ(backend, SndioCapture)(device);
597 if(!backend) return NULL;
598 return STATIC_CAST(ALCbackend, backend);
601 return NULL;