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
23 #include "backends/solaris.h"
25 #include <sys/ioctl.h>
26 #include <sys/types.h>
48 #include <sys/audioio.h>
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
;
64 ALCenum
open(const ALCchar
*name
) override
;
65 bool reset() override
;
66 bool start() override
;
71 al::vector
<ALubyte
> mBuffer
;
73 std::atomic
<bool> mKillNow
{true};
76 DEF_NEWDEL(SolarisBackend
)
79 SolarisBackend::~SolarisBackend()
86 int SolarisBackend::mixerProc()
89 althrd_setname(MIXER_THREAD_NAME
);
91 const ALuint frame_size
{mDevice
->frameSizeFromFmt()};
94 while(!mKillNow
.load(std::memory_order_acquire
) &&
95 mDevice
->Connected
.load(std::memory_order_acquire
))
99 pollitem
.events
= POLLOUT
;
102 int pret
{poll(&pollitem
, 1, 1000)};
106 if(errno
== EINTR
|| errno
== EAGAIN
)
108 ERR("poll failed: %s\n", strerror(errno
));
109 aluHandleDisconnect(mDevice
, "Failed to wait for playback buffer: %s",
115 WARN("poll timeout\n");
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
)};
127 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
129 ERR("write failed: %s\n", strerror(errno
));
130 aluHandleDisconnect(mDevice
, "Failed to write playback samples: %s",
145 ALCenum
SolarisBackend::open(const ALCchar
*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
);
155 ERR("Could not open %s: %s\n", solaris_driver
.c_str(), strerror(errno
));
156 return ALC_INVALID_VALUE
;
159 mDevice
->DeviceName
= name
;
163 bool SolarisBackend::reset()
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
)
178 info
.play
.precision
= 8;
179 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
182 info
.play
.precision
= 8;
183 info
.play
.encoding
= AUDIO_ENCODING_LINEAR8
;
189 mDevice
->FmtType
= DevFmtShort
;
192 info
.play
.precision
= 16;
193 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
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
));
206 if(mDevice
->channelsFromFmt() != info
.play
.channels
)
208 ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(mDevice
->FmtChans
),
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
);
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);
235 bool SolarisBackend::start()
238 mKillNow
.store(false, std::memory_order_release
);
239 mThread
= std::thread
{std::mem_fn(&SolarisBackend::mixerProc
), this};
242 catch(std::exception
& e
) {
243 ERR("Could not create playback thread: %s\n", e
.what());
250 void SolarisBackend::stop()
252 if(mKillNow
.exchange(true, std::memory_order_acq_rel
) || !mThread
.joinable())
256 if(ioctl(mFd
, AUDIO_DRAIN
) < 0)
257 ERR("Error draining device: %s\n", strerror(errno
));
262 BackendFactory
&SolarisBackendFactory::getFactory()
264 static SolarisBackendFactory factory
{};
268 bool SolarisBackendFactory::init()
270 if(auto devopt
= ConfigValueStr(nullptr, "solaris", "device"))
271 solaris_driver
= std::move(*devopt
);
275 bool SolarisBackendFactory::querySupport(BackendType type
)
276 { return type
== BackendType::Playback
; }
278 void SolarisBackendFactory::probe(DevProbe type
, std::string
*outnames
)
282 case DevProbe::Playback
:
286 if(stat(solaris_driver
.c_str(), &buf
) == 0)
288 outnames
->append(solaris_device
, sizeof(solaris_device
));
292 case DevProbe::Capture
:
297 BackendPtr
SolarisBackendFactory::createBackend(ALCdevice
*device
, BackendType type
)
299 if(type
== BackendType::Playback
)
300 return BackendPtr
{new SolarisBackend
{device
}};