Add "fast" variants for the bsinc resamplers
[openal-soft.git] / alc / backends / sndio.cpp
blobd2654853367341e004bf6ec2ce0dcda71be2bb7f
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 <thread>
30 #include <functional>
32 #include "alcmain.h"
33 #include "alu.h"
34 #include "threads.h"
35 #include "vector.h"
36 #include "ringbuffer.h"
38 #include <sndio.h>
41 namespace {
43 static const ALCchar sndio_device[] = "SndIO Default";
46 struct SndioPlayback final : public BackendBase {
47 SndioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
48 ~SndioPlayback() override;
50 int mixerProc();
52 ALCenum open(const ALCchar *name) override;
53 bool reset() override;
54 bool start() override;
55 void stop() override;
57 sio_hdl *mSndHandle{nullptr};
59 al::vector<ALubyte> mBuffer;
61 std::atomic<bool> mKillNow{true};
62 std::thread mThread;
64 DEF_NEWDEL(SndioPlayback)
67 SndioPlayback::~SndioPlayback()
69 if(mSndHandle)
70 sio_close(mSndHandle);
71 mSndHandle = nullptr;
74 int SndioPlayback::mixerProc()
76 SetRTPriority();
77 althrd_setname(MIXER_THREAD_NAME);
79 const ALuint frameSize{mDevice->frameSizeFromFmt()};
81 while(!mKillNow.load(std::memory_order_acquire) &&
82 mDevice->Connected.load(std::memory_order_acquire))
84 ALubyte *WritePtr{mBuffer.data()};
85 size_t len{mBuffer.size()};
87 lock();
88 aluMixData(mDevice, WritePtr, static_cast<ALuint>(len/frameSize));
89 unlock();
90 while(len > 0 && !mKillNow.load(std::memory_order_acquire))
92 size_t wrote{sio_write(mSndHandle, WritePtr, len)};
93 if(wrote == 0)
95 ERR("sio_write failed\n");
96 aluHandleDisconnect(mDevice, "Failed to write playback samples");
97 break;
100 len -= wrote;
101 WritePtr += wrote;
105 return 0;
109 ALCenum SndioPlayback::open(const ALCchar *name)
111 if(!name)
112 name = sndio_device;
113 else if(strcmp(name, sndio_device) != 0)
114 return ALC_INVALID_VALUE;
116 mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
117 if(mSndHandle == nullptr)
119 ERR("Could not open device\n");
120 return ALC_INVALID_VALUE;
123 mDevice->DeviceName = name;
124 return ALC_NO_ERROR;
127 bool SndioPlayback::reset()
129 sio_par par;
130 sio_initpar(&par);
132 par.rate = mDevice->Frequency;
133 par.pchan = ((mDevice->FmtChans != DevFmtMono) ? 2 : 1);
135 switch(mDevice->FmtType)
137 case DevFmtByte:
138 par.bits = 8;
139 par.sig = 1;
140 break;
141 case DevFmtUByte:
142 par.bits = 8;
143 par.sig = 0;
144 break;
145 case DevFmtFloat:
146 case DevFmtShort:
147 par.bits = 16;
148 par.sig = 1;
149 break;
150 case DevFmtUShort:
151 par.bits = 16;
152 par.sig = 0;
153 break;
154 case DevFmtInt:
155 par.bits = 32;
156 par.sig = 1;
157 break;
158 case DevFmtUInt:
159 par.bits = 32;
160 par.sig = 0;
161 break;
163 par.le = SIO_LE_NATIVE;
165 par.round = mDevice->UpdateSize;
166 par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
167 if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
169 if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
171 ERR("Failed to set device parameters\n");
172 return false;
175 if(par.bits != par.bps*8)
177 ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
178 return true;
181 mDevice->Frequency = par.rate;
182 mDevice->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
184 if(par.bits == 8 && par.sig == 1)
185 mDevice->FmtType = DevFmtByte;
186 else if(par.bits == 8 && par.sig == 0)
187 mDevice->FmtType = DevFmtUByte;
188 else if(par.bits == 16 && par.sig == 1)
189 mDevice->FmtType = DevFmtShort;
190 else if(par.bits == 16 && par.sig == 0)
191 mDevice->FmtType = DevFmtUShort;
192 else if(par.bits == 32 && par.sig == 1)
193 mDevice->FmtType = DevFmtInt;
194 else if(par.bits == 32 && par.sig == 0)
195 mDevice->FmtType = DevFmtUInt;
196 else
198 ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
199 return false;
202 SetDefaultChannelOrder(mDevice);
204 mDevice->UpdateSize = par.round;
205 mDevice->BufferSize = par.bufsz + par.round;
207 mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
208 std::fill(mBuffer.begin(), mBuffer.end(), 0);
210 return true;
213 bool SndioPlayback::start()
215 if(!sio_start(mSndHandle))
217 ERR("Error starting playback\n");
218 return false;
221 try {
222 mKillNow.store(false, std::memory_order_release);
223 mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), this};
224 return true;
226 catch(std::exception& e) {
227 ERR("Could not create playback thread: %s\n", e.what());
229 catch(...) {
231 sio_stop(mSndHandle);
232 return false;
235 void SndioPlayback::stop()
237 if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
238 return;
239 mThread.join();
241 if(!sio_stop(mSndHandle))
242 ERR("Error stopping device\n");
246 struct SndioCapture final : public BackendBase {
247 SndioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
248 ~SndioCapture() override;
250 int recordProc();
252 ALCenum open(const ALCchar *name) override;
253 bool start() override;
254 void stop() override;
255 ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
256 ALCuint availableSamples() override;
258 sio_hdl *mSndHandle{nullptr};
260 RingBufferPtr mRing;
262 std::atomic<bool> mKillNow{true};
263 std::thread mThread;
265 DEF_NEWDEL(SndioCapture)
268 SndioCapture::~SndioCapture()
270 if(mSndHandle)
271 sio_close(mSndHandle);
272 mSndHandle = nullptr;
275 int SndioCapture::recordProc()
277 SetRTPriority();
278 althrd_setname(RECORD_THREAD_NAME);
280 const ALuint frameSize{mDevice->frameSizeFromFmt()};
282 while(!mKillNow.load(std::memory_order_acquire) &&
283 mDevice->Connected.load(std::memory_order_acquire))
285 auto data = mRing->getWriteVector();
286 size_t todo{data.first.len + data.second.len};
287 if(todo == 0)
289 static char junk[4096];
290 sio_read(mSndHandle, junk,
291 minz(sizeof(junk)/frameSize, mDevice->UpdateSize)*frameSize);
292 continue;
295 size_t total{0u};
296 data.first.len *= frameSize;
297 data.second.len *= frameSize;
298 todo = minz(todo, mDevice->UpdateSize) * frameSize;
299 while(total < todo)
301 if(!data.first.len)
302 data.first = data.second;
304 size_t got{sio_read(mSndHandle, data.first.buf, minz(todo-total, data.first.len))};
305 if(!got)
307 aluHandleDisconnect(mDevice, "Failed to read capture samples");
308 break;
311 data.first.buf += got;
312 data.first.len -= got;
313 total += got;
315 mRing->writeAdvance(total / frameSize);
318 return 0;
322 ALCenum SndioCapture::open(const ALCchar *name)
324 if(!name)
325 name = sndio_device;
326 else if(strcmp(name, sndio_device) != 0)
327 return ALC_INVALID_VALUE;
329 mSndHandle = sio_open(nullptr, SIO_REC, 0);
330 if(mSndHandle == nullptr)
332 ERR("Could not open device\n");
333 return ALC_INVALID_VALUE;
336 sio_par par;
337 sio_initpar(&par);
339 switch(mDevice->FmtType)
341 case DevFmtByte:
342 par.bps = 1;
343 par.sig = 1;
344 break;
345 case DevFmtUByte:
346 par.bps = 1;
347 par.sig = 0;
348 break;
349 case DevFmtShort:
350 par.bps = 2;
351 par.sig = 1;
352 break;
353 case DevFmtUShort:
354 par.bps = 2;
355 par.sig = 0;
356 break;
357 case DevFmtInt:
358 par.bps = 4;
359 par.sig = 1;
360 break;
361 case DevFmtUInt:
362 par.bps = 4;
363 par.sig = 0;
364 break;
365 case DevFmtFloat:
366 ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
367 return ALC_INVALID_VALUE;
369 par.bits = par.bps * 8;
370 par.le = SIO_LE_NATIVE;
371 par.msb = SIO_LE_NATIVE ? 0 : 1;
372 par.rchan = mDevice->channelsFromFmt();
373 par.rate = mDevice->Frequency;
375 par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10);
376 par.round = minu(par.appbufsz, mDevice->Frequency/40);
378 mDevice->UpdateSize = par.round;
379 mDevice->BufferSize = par.appbufsz;
381 if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
383 ERR("Failed to set device parameters\n");
384 return ALC_INVALID_VALUE;
387 if(par.bits != par.bps*8)
389 ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
390 return ALC_INVALID_VALUE;
393 if(!((mDevice->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
394 (mDevice->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
395 (mDevice->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
396 (mDevice->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
397 (mDevice->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
398 (mDevice->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
399 mDevice->channelsFromFmt() != par.rchan || mDevice->Frequency != par.rate)
401 ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
402 DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
403 mDevice->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
404 return ALC_INVALID_VALUE;
407 mRing = CreateRingBuffer(mDevice->BufferSize, par.bps*par.rchan, false);
408 if(!mRing)
410 ERR("Failed to allocate %u-byte ringbuffer\n", mDevice->BufferSize*par.bps*par.rchan);
411 return ALC_OUT_OF_MEMORY;
414 SetDefaultChannelOrder(mDevice);
416 mDevice->DeviceName = name;
417 return ALC_NO_ERROR;
420 bool SndioCapture::start()
422 if(!sio_start(mSndHandle))
424 ERR("Error starting playback\n");
425 return false;
428 try {
429 mKillNow.store(false, std::memory_order_release);
430 mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), this};
431 return true;
433 catch(std::exception& e) {
434 ERR("Could not create record thread: %s\n", e.what());
436 catch(...) {
438 sio_stop(mSndHandle);
439 return false;
442 void SndioCapture::stop()
444 if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
445 return;
446 mThread.join();
448 if(!sio_stop(mSndHandle))
449 ERR("Error stopping device\n");
452 ALCenum SndioCapture::captureSamples(al::byte *buffer, ALCuint samples)
454 mRing->read(buffer, samples);
455 return ALC_NO_ERROR;
458 ALCuint SndioCapture::availableSamples()
459 { return static_cast<ALCuint>(mRing->readSpace()); }
461 } // namespace
463 BackendFactory &SndIOBackendFactory::getFactory()
465 static SndIOBackendFactory factory{};
466 return factory;
469 bool SndIOBackendFactory::init()
470 { return true; }
472 bool SndIOBackendFactory::querySupport(BackendType type)
473 { return (type == BackendType::Playback || type == BackendType::Capture); }
475 void SndIOBackendFactory::probe(DevProbe type, std::string *outnames)
477 switch(type)
479 case DevProbe::Playback:
480 case DevProbe::Capture:
481 /* Includes null char. */
482 outnames->append(sndio_device, sizeof(sndio_device));
483 break;
487 BackendPtr SndIOBackendFactory::createBackend(ALCdevice *device, BackendType type)
489 if(type == BackendType::Playback)
490 return BackendPtr{new SndioPlayback{device}};
491 if(type == BackendType::Capture)
492 return BackendPtr{new SndioCapture{device}};
493 return nullptr;