Rename some struct members
[openal-soft.git] / Alc / backends / sndio.cpp
blobd7340f4ad44df9c7399d0d32fd9ffa124d0fe4b4
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{nullptr};
43 ALvoid *mix_data{nullptr};
44 ALsizei data_size{0};
46 std::atomic<ALenum> mKillNow{AL_TRUE};
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);
75 static void SndioPlayback_Destruct(SndioPlayback *self)
77 if(self->sndHandle)
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;
93 ALsizei frameSize;
94 size_t wrote;
96 SetRTPriority();
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);
113 if(wrote == 0)
115 ERR("sio_write failed\n");
116 SndioPlayback_lock(self);
117 aluHandleDisconnect(device, "Failed to write playback samples");
118 SndioPlayback_unlock(self);
119 break;
122 len -= wrote;
123 WritePtr += wrote;
127 return 0;
131 static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
133 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
135 if(!name)
136 name = sndio_device;
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;
148 return ALC_NO_ERROR;
151 static ALCboolean SndioPlayback_reset(SndioPlayback *self)
153 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
154 struct sio_par par;
156 sio_initpar(&par);
158 par.rate = device->Frequency;
159 par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
161 switch(device->FmtType)
163 case DevFmtByte:
164 par.bits = 8;
165 par.sig = 1;
166 break;
167 case DevFmtUByte:
168 par.bits = 8;
169 par.sig = 0;
170 break;
171 case DevFmtFloat:
172 case DevFmtShort:
173 par.bits = 16;
174 par.sig = 1;
175 break;
176 case DevFmtUShort:
177 par.bits = 16;
178 par.sig = 0;
179 break;
180 case DevFmtInt:
181 par.bits = 32;
182 par.sig = 1;
183 break;
184 case DevFmtUInt:
185 par.bits = 32;
186 par.sig = 0;
187 break;
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");
198 return ALC_FALSE;
201 if(par.bits != par.bps*8)
203 ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
204 return ALC_FALSE;
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;
222 else
224 ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
225 return ALC_FALSE;
228 device->UpdateSize = par.round;
229 device->NumUpdates = (par.bufsz/par.round) + 1;
231 SetDefaultChannelOrder(device);
233 return ALC_TRUE;
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");
249 return ALC_FALSE;
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);
256 return ALC_FALSE;
259 return ALC_TRUE;
262 static void SndioPlayback_stop(SndioPlayback *self)
264 int res;
266 if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel))
267 return;
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};
284 althrd_t thread;
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)
314 if(self->sndHandle)
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;
330 ALsizei frameSize;
332 SetRTPriority();
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))
340 size_t total, todo;
342 auto data = ll_ringbuffer_get_write_vector(self->ring);
343 todo = data.first.len + data.second.len;
344 if(todo == 0)
346 static char junk[4096];
347 sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
348 continue;
351 total = 0;
352 data.first.len *= frameSize;
353 data.second.len *= frameSize;
354 todo = minz(todo, device->UpdateSize) * frameSize;
355 while(total < todo)
357 size_t got;
359 if(!data.first.len)
360 data.first = data.second;
362 got = sio_read(self->sndHandle, data.first.buf, minz(todo-total, data.first.len));
363 if(!got)
365 SndioCapture_lock(self);
366 aluHandleDisconnect(device, "Failed to read capture samples");
367 SndioCapture_unlock(self);
368 break;
371 data.first.buf += got;
372 data.first.len -= got;
373 total += got;
375 ll_ringbuffer_write_advance(self->ring, total / frameSize);
378 return 0;
382 static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
384 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
385 struct sio_par par;
387 if(!name)
388 name = sndio_device;
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;
399 sio_initpar(&par);
401 switch(device->FmtType)
403 case DevFmtByte:
404 par.bps = 1;
405 par.sig = 1;
406 break;
407 case DevFmtUByte:
408 par.bps = 1;
409 par.sig = 0;
410 break;
411 case DevFmtShort:
412 par.bps = 2;
413 par.sig = 1;
414 break;
415 case DevFmtUShort:
416 par.bps = 2;
417 par.sig = 0;
418 break;
419 case DevFmtInt:
420 par.bps = 4;
421 par.sig = 1;
422 break;
423 case DevFmtUInt:
424 par.bps = 4;
425 par.sig = 0;
426 break;
427 case DevFmtFloat:
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);
472 if(!self->ring)
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;
482 return ALC_NO_ERROR;
485 static ALCboolean SndioCapture_start(SndioCapture *self)
487 if(!sio_start(self->sndHandle))
489 ERR("Error starting playback\n");
490 return ALC_FALSE;
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);
497 return ALC_FALSE;
500 return ALC_TRUE;
503 static void SndioCapture_stop(SndioCapture *self)
505 int res;
507 if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel))
508 return;
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);
518 return ALC_NO_ERROR;
521 static ALCuint SndioCapture_availableSamples(SndioCapture *self)
523 return ll_ringbuffer_read_space(self->ring);
527 BackendFactory &SndIOBackendFactory::getFactory()
529 static SndIOBackendFactory factory{};
530 return factory;
533 bool SndIOBackendFactory::init()
534 { return true; }
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)
541 switch(type)
543 case ALL_DEVICE_PROBE:
544 case CAPTURE_DEVICE_PROBE:
545 /* Includes null char. */
546 outnames->append(sndio_device, sizeof(sndio_device));
547 break;
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);
568 return nullptr;