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>
49 #include <sys/audioio.h>
54 constexpr ALCchar solaris_device
[] = "Solaris Default";
56 std::string solaris_driver
{"/dev/audio"};
59 struct SolarisBackend final
: public BackendBase
{
60 SolarisBackend(ALCdevice
*device
) noexcept
: BackendBase
{device
} { }
61 ~SolarisBackend() override
;
65 void open(const ALCchar
*name
) override
;
66 bool reset() override
;
67 bool start() override
;
72 al::vector
<ALubyte
> mBuffer
;
74 std::atomic
<bool> mKillNow
{true};
77 DEF_NEWDEL(SolarisBackend
)
80 SolarisBackend::~SolarisBackend()
87 int SolarisBackend::mixerProc()
90 althrd_setname(MIXER_THREAD_NAME
);
92 const ALuint frame_size
{mDevice
->frameSizeFromFmt()};
94 std::unique_lock
<SolarisBackend
> dlock
{*this};
95 while(!mKillNow
.load(std::memory_order_acquire
) &&
96 mDevice
->Connected
.load(std::memory_order_acquire
))
100 pollitem
.events
= POLLOUT
;
103 int pret
{poll(&pollitem
, 1, 1000)};
107 if(errno
== EINTR
|| errno
== EAGAIN
)
109 ERR("poll failed: %s\n", strerror(errno
));
110 aluHandleDisconnect(mDevice
, "Failed to wait for playback buffer: %s",
116 WARN("poll timeout\n");
120 ALubyte
*write_ptr
{mBuffer
.data()};
121 size_t to_write
{mBuffer
.size()};
122 aluMixData(mDevice
, write_ptr
, to_write
/frame_size
);
123 while(to_write
> 0 && !mKillNow
.load(std::memory_order_acquire
))
125 ssize_t wrote
{write(mFd
, write_ptr
, to_write
)};
128 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
130 ERR("write failed: %s\n", strerror(errno
));
131 aluHandleDisconnect(mDevice
, "Failed to write playback samples: %s",
145 void SolarisBackend::open(const ALCchar
*name
)
148 name
= solaris_device
;
149 else if(strcmp(name
, solaris_device
) != 0)
150 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device name \"%s\" not found", name
};
152 mFd
= ::open(solaris_driver
.c_str(), O_WRONLY
);
154 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not open %s: %s",
155 solaris_driver
.c_str(), strerror(errno
)};
157 mDevice
->DeviceName
= name
;
160 bool SolarisBackend::reset()
163 AUDIO_INITINFO(&info
);
165 info
.play
.sample_rate
= mDevice
->Frequency
;
167 if(mDevice
->FmtChans
!= DevFmtMono
)
168 mDevice
->FmtChans
= DevFmtStereo
;
169 ALuint numChannels
{mDevice
->channelsFromFmt()};
170 info
.play
.channels
= numChannels
;
172 switch(mDevice
->FmtType
)
175 info
.play
.precision
= 8;
176 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
179 info
.play
.precision
= 8;
180 info
.play
.encoding
= AUDIO_ENCODING_LINEAR8
;
186 mDevice
->FmtType
= DevFmtShort
;
189 info
.play
.precision
= 16;
190 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
194 ALuint frameSize
{numChannels
* mDevice
->bytesFromFmt()};
195 info
.play
.buffer_size
= mDevice
->BufferSize
* frameSize
;
197 if(ioctl(mFd
, AUDIO_SETINFO
, &info
) < 0)
199 ERR("ioctl failed: %s\n", strerror(errno
));
203 if(mDevice
->channelsFromFmt() != info
.play
.channels
)
205 ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(mDevice
->FmtChans
),
210 if(!((info
.play
.precision
== 8 && info
.play
.encoding
== AUDIO_ENCODING_LINEAR8
&& mDevice
->FmtType
== DevFmtUByte
) ||
211 (info
.play
.precision
== 8 && info
.play
.encoding
== AUDIO_ENCODING_LINEAR
&& mDevice
->FmtType
== DevFmtByte
) ||
212 (info
.play
.precision
== 16 && info
.play
.encoding
== AUDIO_ENCODING_LINEAR
&& mDevice
->FmtType
== DevFmtShort
) ||
213 (info
.play
.precision
== 32 && info
.play
.encoding
== AUDIO_ENCODING_LINEAR
&& mDevice
->FmtType
== DevFmtInt
)))
215 ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(mDevice
->FmtType
),
216 info
.play
.precision
, info
.play
.encoding
);
220 mDevice
->Frequency
= info
.play
.sample_rate
;
221 mDevice
->BufferSize
= info
.play
.buffer_size
/ frameSize
;
222 mDevice
->UpdateSize
= mDevice
->BufferSize
/ 2;
224 SetDefaultChannelOrder(mDevice
);
226 mBuffer
.resize(mDevice
->UpdateSize
* mDevice
->frameSizeFromFmt());
227 std::fill(mBuffer
.begin(), mBuffer
.end(), 0);
232 bool SolarisBackend::start()
235 mKillNow
.store(false, std::memory_order_release
);
236 mThread
= std::thread
{std::mem_fn(&SolarisBackend::mixerProc
), this};
239 catch(std::exception
& e
) {
240 ERR("Could not create playback thread: %s\n", e
.what());
247 void SolarisBackend::stop()
249 if(mKillNow
.exchange(true, std::memory_order_acq_rel
) || !mThread
.joinable())
253 if(ioctl(mFd
, AUDIO_DRAIN
) < 0)
254 ERR("Error draining device: %s\n", strerror(errno
));
259 BackendFactory
&SolarisBackendFactory::getFactory()
261 static SolarisBackendFactory factory
{};
265 bool SolarisBackendFactory::init()
267 if(auto devopt
= ConfigValueStr(nullptr, "solaris", "device"))
268 solaris_driver
= std::move(*devopt
);
272 bool SolarisBackendFactory::querySupport(BackendType type
)
273 { return type
== BackendType::Playback
; }
275 void SolarisBackendFactory::probe(DevProbe type
, std::string
*outnames
)
279 case DevProbe::Playback
:
283 if(stat(solaris_driver
.c_str(), &buf
) == 0)
285 outnames
->append(solaris_device
, sizeof(solaris_device
));
289 case DevProbe::Capture
:
294 BackendPtr
SolarisBackendFactory::createBackend(ALCdevice
*device
, BackendType type
)
296 if(type
== BackendType::Playback
)
297 return BackendPtr
{new SolarisBackend
{device
}};