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>
45 #include <sys/audioio.h>
48 struct ALCsolarisBackend final
: public ALCbackend
{
51 ALubyte
*mix_data
{nullptr};
54 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
58 static int ALCsolarisBackend_mixerProc(ALCsolarisBackend
*self
);
60 static void ALCsolarisBackend_Construct(ALCsolarisBackend
*self
, ALCdevice
*device
);
61 static void ALCsolarisBackend_Destruct(ALCsolarisBackend
*self
);
62 static ALCenum
ALCsolarisBackend_open(ALCsolarisBackend
*self
, const ALCchar
*name
);
63 static ALCboolean
ALCsolarisBackend_reset(ALCsolarisBackend
*self
);
64 static ALCboolean
ALCsolarisBackend_start(ALCsolarisBackend
*self
);
65 static void ALCsolarisBackend_stop(ALCsolarisBackend
*self
);
66 static DECLARE_FORWARD2(ALCsolarisBackend
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
67 static DECLARE_FORWARD(ALCsolarisBackend
, ALCbackend
, ALCuint
, availableSamples
)
68 static DECLARE_FORWARD(ALCsolarisBackend
, ALCbackend
, ClockLatency
, getClockLatency
)
69 static DECLARE_FORWARD(ALCsolarisBackend
, ALCbackend
, void, lock
)
70 static DECLARE_FORWARD(ALCsolarisBackend
, ALCbackend
, void, unlock
)
71 DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend
)
73 DEFINE_ALCBACKEND_VTABLE(ALCsolarisBackend
);
76 static const ALCchar solaris_device
[] = "Solaris Default";
78 static const char *solaris_driver
= "/dev/audio";
81 static void ALCsolarisBackend_Construct(ALCsolarisBackend
*self
, ALCdevice
*device
)
83 new (self
) ALCsolarisBackend
{};
84 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
85 SET_VTABLE2(ALCsolarisBackend
, ALCbackend
, self
);
88 static void ALCsolarisBackend_Destruct(ALCsolarisBackend
*self
)
95 self
->mix_data
= nullptr;
98 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
99 self
->~ALCsolarisBackend();
103 static int ALCsolarisBackend_mixerProc(ALCsolarisBackend
*self
)
105 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
106 struct timeval timeout
;
115 althrd_setname(MIXER_THREAD_NAME
);
117 frame_size
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
);
119 ALCsolarisBackend_lock(self
);
120 while(!self
->mKillNow
.load(std::memory_order_acquire
) &&
121 device
->Connected
.load(std::memory_order_acquire
))
124 FD_SET(self
->fd
, &wfds
);
128 ALCsolarisBackend_unlock(self
);
129 sret
= select(self
->fd
+1, nullptr, &wfds
, nullptr, &timeout
);
130 ALCsolarisBackend_lock(self
);
135 ERR("select failed: %s\n", strerror(errno
));
136 aluHandleDisconnect(device
, "Failed to wait for playback buffer: %s", strerror(errno
));
141 WARN("select timeout\n");
145 write_ptr
= self
->mix_data
;
146 to_write
= self
->data_size
;
147 aluMixData(device
, write_ptr
, to_write
/frame_size
);
148 while(to_write
> 0 && !self
->mKillNow
.load())
150 wrote
= write(self
->fd
, write_ptr
, to_write
);
153 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
155 ERR("write failed: %s\n", strerror(errno
));
156 aluHandleDisconnect(device
, "Failed to write playback samples: %s",
165 ALCsolarisBackend_unlock(self
);
171 static ALCenum
ALCsolarisBackend_open(ALCsolarisBackend
*self
, const ALCchar
*name
)
176 name
= solaris_device
;
177 else if(strcmp(name
, solaris_device
) != 0)
178 return ALC_INVALID_VALUE
;
180 self
->fd
= open(solaris_driver
, O_WRONLY
);
183 ERR("Could not open %s: %s\n", solaris_driver
, strerror(errno
));
184 return ALC_INVALID_VALUE
;
187 device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
188 device
->DeviceName
= name
;
193 static ALCboolean
ALCsolarisBackend_reset(ALCsolarisBackend
*self
)
195 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
200 AUDIO_INITINFO(&info
);
202 info
.play
.sample_rate
= device
->Frequency
;
204 if(device
->FmtChans
!= DevFmtMono
)
205 device
->FmtChans
= DevFmtStereo
;
206 numChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
207 info
.play
.channels
= numChannels
;
209 switch(device
->FmtType
)
212 info
.play
.precision
= 8;
213 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
216 info
.play
.precision
= 8;
217 info
.play
.encoding
= AUDIO_ENCODING_LINEAR8
;
223 device
->FmtType
= DevFmtShort
;
226 info
.play
.precision
= 16;
227 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
231 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
232 info
.play
.buffer_size
= device
->UpdateSize
*device
->NumUpdates
* frameSize
;
234 if(ioctl(self
->fd
, AUDIO_SETINFO
, &info
) < 0)
236 ERR("ioctl failed: %s\n", strerror(errno
));
240 if(ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
) != (ALsizei
)info
.play
.channels
)
242 ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device
->FmtChans
), info
.play
.channels
);
246 if(!((info
.play
.precision
== 8 && info
.play
.encoding
== AUDIO_ENCODING_LINEAR8
&& device
->FmtType
== DevFmtUByte
) ||
247 (info
.play
.precision
== 8 && info
.play
.encoding
== AUDIO_ENCODING_LINEAR
&& device
->FmtType
== DevFmtByte
) ||
248 (info
.play
.precision
== 16 && info
.play
.encoding
== AUDIO_ENCODING_LINEAR
&& device
->FmtType
== DevFmtShort
) ||
249 (info
.play
.precision
== 32 && info
.play
.encoding
== AUDIO_ENCODING_LINEAR
&& device
->FmtType
== DevFmtInt
)))
251 ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device
->FmtType
),
252 info
.play
.precision
, info
.play
.encoding
);
256 device
->Frequency
= info
.play
.sample_rate
;
257 device
->UpdateSize
= (info
.play
.buffer_size
/device
->NumUpdates
) + 1;
259 SetDefaultChannelOrder(device
);
261 free(self
->mix_data
);
262 self
->data_size
= device
->UpdateSize
* FrameSizeFromDevFmt(
263 device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
265 self
->mix_data
= static_cast<ALubyte
*>(calloc(1, self
->data_size
));
270 static ALCboolean
ALCsolarisBackend_start(ALCsolarisBackend
*self
)
273 self
->mKillNow
.store(AL_FALSE
);
274 self
->mThread
= std::thread(ALCsolarisBackend_mixerProc
, self
);
277 catch(std::exception
& e
) {
278 ERR("Could not create playback thread: %s\n", e
.what());
285 static void ALCsolarisBackend_stop(ALCsolarisBackend
*self
)
287 if(self
->mKillNow
.exchange(AL_TRUE
) || !self
->mThread
.joinable())
290 self
->mThread
.join();
292 if(ioctl(self
->fd
, AUDIO_DRAIN
) < 0)
293 ERR("Error draining device: %s\n", strerror(errno
));
297 BackendFactory
&SolarisBackendFactory::getFactory()
299 static SolarisBackendFactory factory
{};
303 bool SolarisBackendFactory::init()
305 ConfigValueStr(nullptr, "solaris", "device", &solaris_driver
);
309 bool SolarisBackendFactory::querySupport(ALCbackend_Type type
)
310 { return (type
== ALCbackend_Playback
); }
312 void SolarisBackendFactory::probe(enum DevProbe type
, std::string
*outnames
)
316 case ALL_DEVICE_PROBE
:
320 if(stat(solaris_driver
, &buf
) == 0)
322 outnames
->append(solaris_device
, sizeof(solaris_device
));
326 case CAPTURE_DEVICE_PROBE
:
331 ALCbackend
*SolarisBackendFactory::createBackend(ALCdevice
*device
, ALCbackend_Type type
)
333 if(type
== ALCbackend_Playback
)
335 ALCsolarisBackend
*backend
;
336 NEW_OBJ(backend
, ALCsolarisBackend
)(device
);
337 if(!backend
) return nullptr;
338 return STATIC_CAST(ALCbackend
, backend
);