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/jack.h"
37 #include "ringbuffer.h"
40 #include <jack/jack.h>
41 #include <jack/ringbuffer.h>
46 constexpr ALCchar jackDevice
[] = "JACK Default";
50 #define JACK_FUNCS(MAGIC) \
51 MAGIC(jack_client_open); \
52 MAGIC(jack_client_close); \
53 MAGIC(jack_client_name_size); \
54 MAGIC(jack_get_client_name); \
55 MAGIC(jack_connect); \
56 MAGIC(jack_activate); \
57 MAGIC(jack_deactivate); \
58 MAGIC(jack_port_register); \
59 MAGIC(jack_port_unregister); \
60 MAGIC(jack_port_get_buffer); \
61 MAGIC(jack_port_name); \
62 MAGIC(jack_get_ports); \
64 MAGIC(jack_get_sample_rate); \
65 MAGIC(jack_set_error_function); \
66 MAGIC(jack_set_process_callback); \
67 MAGIC(jack_set_buffer_size_callback); \
68 MAGIC(jack_set_buffer_size); \
69 MAGIC(jack_get_buffer_size);
72 #define MAKE_FUNC(f) decltype(f) * p##f
73 JACK_FUNCS(MAKE_FUNC
);
74 decltype(jack_error_callback
) * pjack_error_callback
;
78 #define jack_client_open pjack_client_open
79 #define jack_client_close pjack_client_close
80 #define jack_client_name_size pjack_client_name_size
81 #define jack_get_client_name pjack_get_client_name
82 #define jack_connect pjack_connect
83 #define jack_activate pjack_activate
84 #define jack_deactivate pjack_deactivate
85 #define jack_port_register pjack_port_register
86 #define jack_port_unregister pjack_port_unregister
87 #define jack_port_get_buffer pjack_port_get_buffer
88 #define jack_port_name pjack_port_name
89 #define jack_get_ports pjack_get_ports
90 #define jack_free pjack_free
91 #define jack_get_sample_rate pjack_get_sample_rate
92 #define jack_set_error_function pjack_set_error_function
93 #define jack_set_process_callback pjack_set_process_callback
94 #define jack_set_buffer_size_callback pjack_set_buffer_size_callback
95 #define jack_set_buffer_size pjack_set_buffer_size
96 #define jack_get_buffer_size pjack_get_buffer_size
97 #define jack_error_callback (*pjack_error_callback)
102 jack_options_t ClientOptions
= JackNullOption
;
104 ALCboolean
jack_load()
106 ALCboolean error
= ALC_FALSE
;
111 std::string missing_funcs
;
114 #define JACKLIB "libjack.dll"
116 #define JACKLIB "libjack.so.0"
118 jack_handle
= LoadLib(JACKLIB
);
121 WARN("Failed to load %s\n", JACKLIB
);
126 #define LOAD_FUNC(f) do { \
127 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
128 if(p##f == nullptr) { \
130 missing_funcs += "\n" #f; \
133 JACK_FUNCS(LOAD_FUNC
);
135 /* Optional symbols. These don't exist in all versions of JACK. */
136 #define LOAD_SYM(f) p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f))
137 LOAD_SYM(jack_error_callback
);
142 WARN("Missing expected functions:%s\n", missing_funcs
.c_str());
143 CloseLib(jack_handle
);
144 jack_handle
= nullptr;
153 struct JackPlayback final
: public BackendBase
{
154 JackPlayback(ALCdevice
*device
) noexcept
: BackendBase
{device
} { }
155 ~JackPlayback() override
;
157 static int bufferSizeNotifyC(jack_nframes_t numframes
, void *arg
)
158 { return static_cast<JackPlayback
*>(arg
)->bufferSizeNotify(numframes
); }
159 int bufferSizeNotify(jack_nframes_t numframes
);
161 static int processC(jack_nframes_t numframes
, void *arg
)
162 { return static_cast<JackPlayback
*>(arg
)->process(numframes
); }
163 int process(jack_nframes_t numframes
);
167 void open(const ALCchar
*name
) override
;
168 bool reset() override
;
169 bool start() override
;
170 void stop() override
;
171 ClockLatency
getClockLatency() override
;
173 jack_client_t
*mClient
{nullptr};
174 jack_port_t
*mPort
[MAX_OUTPUT_CHANNELS
]{};
179 std::atomic
<bool> mKillNow
{true};
182 DEF_NEWDEL(JackPlayback
)
185 JackPlayback::~JackPlayback()
190 std::for_each(std::begin(mPort
), std::end(mPort
),
191 [this](jack_port_t
*port
) -> void
192 { if(port
) jack_port_unregister(mClient
, port
); }
194 std::fill(std::begin(mPort
), std::end(mPort
), nullptr);
195 jack_client_close(mClient
);
200 int JackPlayback::bufferSizeNotify(jack_nframes_t numframes
)
202 std::lock_guard
<std::mutex
> _
{mDevice
->StateLock
};
203 mDevice
->UpdateSize
= numframes
;
204 mDevice
->BufferSize
= numframes
*2;
206 const char *devname
{mDevice
->DeviceName
.c_str()};
207 ALuint bufsize
{ConfigValueUInt(devname
, "jack", "buffer-size").value_or(mDevice
->UpdateSize
)};
208 bufsize
= maxu(NextPowerOf2(bufsize
), mDevice
->UpdateSize
);
209 mDevice
->BufferSize
= bufsize
+ mDevice
->UpdateSize
;
211 TRACE("%u / %u buffer\n", mDevice
->UpdateSize
, mDevice
->BufferSize
);
214 mRing
= CreateRingBuffer(bufsize
, mDevice
->frameSizeFromFmt(), true);
217 ERR("Failed to reallocate ringbuffer\n");
218 aluHandleDisconnect(mDevice
, "Failed to reallocate %u-sample buffer", bufsize
);
224 int JackPlayback::process(jack_nframes_t numframes
)
226 jack_default_audio_sample_t
*out
[MAX_OUTPUT_CHANNELS
];
228 for(auto port
: mPort
)
231 out
[numchans
++] = static_cast<float*>(jack_port_get_buffer(port
, numframes
));
234 auto data
= mRing
->getReadVector();
235 jack_nframes_t todo
{minu(numframes
, static_cast<ALuint
>(data
.first
.len
))};
236 std::transform(out
, out
+numchans
, out
,
237 [&data
,numchans
,todo
](ALfloat
*outbuf
) -> ALfloat
*
239 const ALfloat
*RESTRICT in
= reinterpret_cast<ALfloat
*>(data
.first
.buf
);
240 std::generate_n(outbuf
, todo
,
241 [&in
,numchans
]() noexcept
-> ALfloat
248 data
.first
.buf
+= sizeof(ALfloat
);
249 return outbuf
+ todo
;
252 jack_nframes_t total
{todo
};
254 todo
= minu(numframes
-total
, static_cast<ALuint
>(data
.second
.len
));
257 std::transform(out
, out
+numchans
, out
,
258 [&data
,numchans
,todo
](ALfloat
*outbuf
) -> ALfloat
*
260 const ALfloat
*RESTRICT in
= reinterpret_cast<ALfloat
*>(data
.second
.buf
);
261 std::generate_n(outbuf
, todo
,
262 [&in
,numchans
]() noexcept
-> ALfloat
269 data
.second
.buf
+= sizeof(ALfloat
);
270 return outbuf
+ todo
;
276 mRing
->readAdvance(total
);
279 if(numframes
> total
)
281 todo
= numframes
-total
;
282 std::transform(out
, out
+numchans
, out
,
283 [todo
](ALfloat
*outbuf
) -> ALfloat
*
285 std::fill_n(outbuf
, todo
, 0.0f
);
286 return outbuf
+ todo
;
294 int JackPlayback::mixerProc()
297 althrd_setname(MIXER_THREAD_NAME
);
299 std::unique_lock
<JackPlayback
> dlock
{*this};
300 while(!mKillNow
.load(std::memory_order_acquire
) &&
301 mDevice
->Connected
.load(std::memory_order_acquire
))
303 if(mRing
->writeSpace() < mDevice
->UpdateSize
)
311 auto data
= mRing
->getWriteVector();
312 auto todo
= static_cast<ALuint
>(data
.first
.len
+ data
.second
.len
);
313 todo
-= todo
%mDevice
->UpdateSize
;
315 ALuint len1
{minu(static_cast<ALuint
>(data
.first
.len
), todo
)};
316 ALuint len2
{minu(static_cast<ALuint
>(data
.second
.len
), todo
-len1
)};
318 aluMixData(mDevice
, data
.first
.buf
, len1
);
320 aluMixData(mDevice
, data
.second
.buf
, len2
);
321 mRing
->writeAdvance(todo
);
328 void JackPlayback::open(const ALCchar
*name
)
332 else if(strcmp(name
, jackDevice
) != 0)
333 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device name \"%s\" not found", name
};
335 const char *client_name
{"alsoft"};
336 jack_status_t status
;
337 mClient
= jack_client_open(client_name
, ClientOptions
, &status
, nullptr);
338 if(mClient
== nullptr)
340 ERR("jack_client_open() failed, status = 0x%02x\n", status
);
341 throw al::backend_exception
{ALC_INVALID_VALUE
, "Failed to connect to JACK server: 0x%02x",
344 if((status
&JackServerStarted
))
345 TRACE("JACK server started\n");
346 if((status
&JackNameNotUnique
))
348 client_name
= jack_get_client_name(mClient
);
349 TRACE("Client name not unique, got `%s' instead\n", client_name
);
352 jack_set_process_callback(mClient
, &JackPlayback::processC
, this);
353 jack_set_buffer_size_callback(mClient
, &JackPlayback::bufferSizeNotifyC
, this);
355 mDevice
->DeviceName
= name
;
358 bool JackPlayback::reset()
360 std::for_each(std::begin(mPort
), std::end(mPort
),
361 [this](jack_port_t
*port
) -> void
362 { if(port
) jack_port_unregister(mClient
, port
); }
364 std::fill(std::begin(mPort
), std::end(mPort
), nullptr);
366 /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
367 * ready for when requested.
369 mDevice
->Frequency
= jack_get_sample_rate(mClient
);
370 mDevice
->UpdateSize
= jack_get_buffer_size(mClient
);
371 mDevice
->BufferSize
= mDevice
->UpdateSize
* 2;
373 const char *devname
{mDevice
->DeviceName
.c_str()};
374 ALuint bufsize
{ConfigValueUInt(devname
, "jack", "buffer-size").value_or(mDevice
->UpdateSize
)};
375 bufsize
= maxu(NextPowerOf2(bufsize
), mDevice
->UpdateSize
);
376 mDevice
->BufferSize
= bufsize
+ mDevice
->UpdateSize
;
378 /* Force 32-bit float output. */
379 mDevice
->FmtType
= DevFmtFloat
;
381 auto ports_end
= std::begin(mPort
) + mDevice
->channelsFromFmt();
382 auto bad_port
= std::find_if_not(std::begin(mPort
), ports_end
,
383 [this](jack_port_t
*&port
) -> bool
385 std::string name
{"channel_" + std::to_string(&port
- mPort
+ 1)};
386 port
= jack_port_register(mClient
, name
.c_str(), JACK_DEFAULT_AUDIO_TYPE
,
387 JackPortIsOutput
, 0);
388 return port
!= nullptr;
391 if(bad_port
!= ports_end
)
393 ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(mDevice
->FmtChans
));
394 if(bad_port
== std::begin(mPort
)) return false;
396 if(bad_port
== std::begin(mPort
)+1)
397 mDevice
->FmtChans
= DevFmtMono
;
401 while(bad_port
!= ports_end
)
403 jack_port_unregister(mClient
, *(--bad_port
));
406 mDevice
->FmtChans
= DevFmtStereo
;
411 mRing
= CreateRingBuffer(bufsize
, mDevice
->frameSizeFromFmt(), true);
414 ERR("Failed to allocate ringbuffer\n");
418 SetDefaultChannelOrder(mDevice
);
423 bool JackPlayback::start()
425 if(jack_activate(mClient
))
427 ERR("Failed to activate client\n");
431 const char **ports
{jack_get_ports(mClient
, nullptr, nullptr,
432 JackPortIsPhysical
|JackPortIsInput
)};
435 ERR("No physical playback ports found\n");
436 jack_deactivate(mClient
);
439 std::mismatch(std::begin(mPort
), std::end(mPort
), ports
,
440 [this](const jack_port_t
*port
, const char *pname
) -> bool
442 if(!port
) return false;
445 ERR("No physical playback port for \"%s\"\n", jack_port_name(port
));
448 if(jack_connect(mClient
, jack_port_name(port
), pname
))
449 ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(port
),
457 mKillNow
.store(false, std::memory_order_release
);
458 mThread
= std::thread
{std::mem_fn(&JackPlayback::mixerProc
), this};
461 catch(std::exception
& e
) {
462 ERR("Could not create playback thread: %s\n", e
.what());
466 jack_deactivate(mClient
);
470 void JackPlayback::stop()
472 if(mKillNow
.exchange(true, std::memory_order_acq_rel
) || !mThread
.joinable())
478 jack_deactivate(mClient
);
482 ClockLatency
JackPlayback::getClockLatency()
486 std::lock_guard
<JackPlayback
> _
{*this};
487 ret
.ClockTime
= GetDeviceClockTime(mDevice
);
488 ret
.Latency
= std::chrono::seconds
{mRing
->readSpace()};
489 ret
.Latency
/= mDevice
->Frequency
;
495 void jack_msg_handler(const char *message
)
497 WARN("%s\n", message
);
502 bool JackBackendFactory::init()
507 if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0))
508 ClientOptions
= static_cast<jack_options_t
>(ClientOptions
| JackNoStartServer
);
510 void (*old_error_cb
)(const char*){&jack_error_callback
? jack_error_callback
: nullptr};
511 jack_set_error_function(jack_msg_handler
);
512 jack_status_t status
;
513 jack_client_t
*client
{jack_client_open("alsoft", ClientOptions
, &status
, nullptr)};
514 jack_set_error_function(old_error_cb
);
517 WARN("jack_client_open() failed, 0x%02x\n", status
);
518 if((status
&JackServerFailed
) && !(ClientOptions
&JackNoStartServer
))
519 ERR("Unable to connect to JACK server\n");
523 jack_client_close(client
);
527 bool JackBackendFactory::querySupport(BackendType type
)
528 { return (type
== BackendType::Playback
); }
530 void JackBackendFactory::probe(DevProbe type
, std::string
*outnames
)
534 case DevProbe::Playback
:
535 /* Includes null char. */
536 outnames
->append(jackDevice
, sizeof(jackDevice
));
539 case DevProbe::Capture
:
544 BackendPtr
JackBackendFactory::createBackend(ALCdevice
*device
, BackendType type
)
546 if(type
== BackendType::Playback
)
547 return BackendPtr
{new JackPlayback
{device
}};
551 BackendFactory
&JackBackendFactory::getFactory()
553 static JackBackendFactory factory
{};