Add "fast" variants for the bsinc resamplers
[openal-soft.git] / alc / backends / solaris.cpp
blob128924bb1c6da9efc257d9596d246e07d7ef1b36
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/solaris.h"
25 #include <sys/ioctl.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <memory.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <poll.h>
36 #include <math.h>
38 #include <thread>
39 #include <functional>
41 #include "alcmain.h"
42 #include "alu.h"
43 #include "alconfig.h"
44 #include "threads.h"
45 #include "vector.h"
46 #include "compat.h"
48 #include <sys/audioio.h>
51 namespace {
53 constexpr ALCchar solaris_device[] = "Solaris Default";
55 std::string solaris_driver{"/dev/audio"};
58 struct SolarisBackend final : public BackendBase {
59 SolarisBackend(ALCdevice *device) noexcept : BackendBase{device} { }
60 ~SolarisBackend() override;
62 int mixerProc();
64 ALCenum open(const ALCchar *name) override;
65 bool reset() override;
66 bool start() override;
67 void stop() override;
69 int mFd{-1};
71 al::vector<ALubyte> mBuffer;
73 std::atomic<bool> mKillNow{true};
74 std::thread mThread;
76 DEF_NEWDEL(SolarisBackend)
79 SolarisBackend::~SolarisBackend()
81 if(mFd != -1)
82 close(mFd);
83 mFd = -1;
86 int SolarisBackend::mixerProc()
88 SetRTPriority();
89 althrd_setname(MIXER_THREAD_NAME);
91 const ALuint frame_size{mDevice->frameSizeFromFmt()};
93 lock();
94 while(!mKillNow.load(std::memory_order_acquire) &&
95 mDevice->Connected.load(std::memory_order_acquire))
97 pollfd pollitem{};
98 pollitem.fd = mFd;
99 pollitem.events = POLLOUT;
101 unlock();
102 int pret{poll(&pollitem, 1, 1000)};
103 lock();
104 if(pret < 0)
106 if(errno == EINTR || errno == EAGAIN)
107 continue;
108 ERR("poll failed: %s\n", strerror(errno));
109 aluHandleDisconnect(mDevice, "Failed to wait for playback buffer: %s",
110 strerror(errno));
111 break;
113 else if(pret == 0)
115 WARN("poll timeout\n");
116 continue;
119 ALubyte *write_ptr{mBuffer.data()};
120 size_t to_write{mBuffer.size()};
121 aluMixData(mDevice, write_ptr, to_write/frame_size);
122 while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
124 ssize_t wrote{write(mFd, write_ptr, to_write)};
125 if(wrote < 0)
127 if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
128 continue;
129 ERR("write failed: %s\n", strerror(errno));
130 aluHandleDisconnect(mDevice, "Failed to write playback samples: %s",
131 strerror(errno));
132 break;
135 to_write -= wrote;
136 write_ptr += wrote;
139 unlock();
141 return 0;
145 ALCenum SolarisBackend::open(const ALCchar *name)
147 if(!name)
148 name = solaris_device;
149 else if(strcmp(name, solaris_device) != 0)
150 return ALC_INVALID_VALUE;
152 mFd = ::open(solaris_driver.c_str(), O_WRONLY);
153 if(mFd == -1)
155 ERR("Could not open %s: %s\n", solaris_driver.c_str(), strerror(errno));
156 return ALC_INVALID_VALUE;
159 mDevice->DeviceName = name;
160 return ALC_NO_ERROR;
163 bool SolarisBackend::reset()
165 audio_info_t info;
166 AUDIO_INITINFO(&info);
168 info.play.sample_rate = mDevice->Frequency;
170 if(mDevice->FmtChans != DevFmtMono)
171 mDevice->FmtChans = DevFmtStereo;
172 ALuint numChannels{mDevice->channelsFromFmt()};
173 info.play.channels = numChannels;
175 switch(mDevice->FmtType)
177 case DevFmtByte:
178 info.play.precision = 8;
179 info.play.encoding = AUDIO_ENCODING_LINEAR;
180 break;
181 case DevFmtUByte:
182 info.play.precision = 8;
183 info.play.encoding = AUDIO_ENCODING_LINEAR8;
184 break;
185 case DevFmtUShort:
186 case DevFmtInt:
187 case DevFmtUInt:
188 case DevFmtFloat:
189 mDevice->FmtType = DevFmtShort;
190 /* fall-through */
191 case DevFmtShort:
192 info.play.precision = 16;
193 info.play.encoding = AUDIO_ENCODING_LINEAR;
194 break;
197 ALuint frameSize{numChannels * mDevice->bytesFromFmt()};
198 info.play.buffer_size = mDevice->BufferSize * frameSize;
200 if(ioctl(mFd, AUDIO_SETINFO, &info) < 0)
202 ERR("ioctl failed: %s\n", strerror(errno));
203 return false;
206 if(mDevice->channelsFromFmt() != info.play.channels)
208 ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
209 info.play.channels);
210 return false;
213 if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && mDevice->FmtType == DevFmtUByte) ||
214 (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtByte) ||
215 (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtShort) ||
216 (info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtInt)))
218 ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(mDevice->FmtType),
219 info.play.precision, info.play.encoding);
220 return false;
223 mDevice->Frequency = info.play.sample_rate;
224 mDevice->BufferSize = info.play.buffer_size / frameSize;
225 mDevice->UpdateSize = mDevice->BufferSize / 2;
227 SetDefaultChannelOrder(mDevice);
229 mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
230 std::fill(mBuffer.begin(), mBuffer.end(), 0);
232 return true;
235 bool SolarisBackend::start()
237 try {
238 mKillNow.store(false, std::memory_order_release);
239 mThread = std::thread{std::mem_fn(&SolarisBackend::mixerProc), this};
240 return true;
242 catch(std::exception& e) {
243 ERR("Could not create playback thread: %s\n", e.what());
245 catch(...) {
247 return false;
250 void SolarisBackend::stop()
252 if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
253 return;
254 mThread.join();
256 if(ioctl(mFd, AUDIO_DRAIN) < 0)
257 ERR("Error draining device: %s\n", strerror(errno));
260 } // namespace
262 BackendFactory &SolarisBackendFactory::getFactory()
264 static SolarisBackendFactory factory{};
265 return factory;
268 bool SolarisBackendFactory::init()
270 if(auto devopt = ConfigValueStr(nullptr, "solaris", "device"))
271 solaris_driver = std::move(*devopt);
272 return true;
275 bool SolarisBackendFactory::querySupport(BackendType type)
276 { return type == BackendType::Playback; }
278 void SolarisBackendFactory::probe(DevProbe type, std::string *outnames)
280 switch(type)
282 case DevProbe::Playback:
284 #ifdef HAVE_STAT
285 struct stat buf;
286 if(stat(solaris_driver.c_str(), &buf) == 0)
287 #endif
288 outnames->append(solaris_device, sizeof(solaris_device));
290 break;
292 case DevProbe::Capture:
293 break;
297 BackendPtr SolarisBackendFactory::createBackend(ALCdevice *device, BackendType type)
299 if(type == BackendType::Playback)
300 return BackendPtr{new SolarisBackend{device}};
301 return nullptr;