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
35 #include "alc/alconfig.h"
36 #include "alnumeric.h"
39 #include "althrd_setname.h"
40 #include "core/device.h"
41 #include "core/helpers.h"
42 #include "core/logging.h"
44 #include "ringbuffer.h"
46 #include <jack/jack.h>
47 #include <jack/ringbuffer.h>
52 using namespace std::string_view_literals
;
55 #define JACK_FUNCS(MAGIC) \
56 MAGIC(jack_client_open); \
57 MAGIC(jack_client_close); \
58 MAGIC(jack_client_name_size); \
59 MAGIC(jack_get_client_name); \
60 MAGIC(jack_connect); \
61 MAGIC(jack_activate); \
62 MAGIC(jack_deactivate); \
63 MAGIC(jack_port_register); \
64 MAGIC(jack_port_unregister); \
65 MAGIC(jack_port_get_buffer); \
66 MAGIC(jack_port_name); \
67 MAGIC(jack_get_ports); \
69 MAGIC(jack_get_sample_rate); \
70 MAGIC(jack_set_error_function); \
71 MAGIC(jack_set_process_callback); \
72 MAGIC(jack_set_buffer_size_callback); \
73 MAGIC(jack_set_buffer_size); \
74 MAGIC(jack_get_buffer_size);
77 #define MAKE_FUNC(f) decltype(f) * p##f
79 decltype(jack_error_callback
) * pjack_error_callback
;
83 #define jack_client_open pjack_client_open
84 #define jack_client_close pjack_client_close
85 #define jack_client_name_size pjack_client_name_size
86 #define jack_get_client_name pjack_get_client_name
87 #define jack_connect pjack_connect
88 #define jack_activate pjack_activate
89 #define jack_deactivate pjack_deactivate
90 #define jack_port_register pjack_port_register
91 #define jack_port_unregister pjack_port_unregister
92 #define jack_port_get_buffer pjack_port_get_buffer
93 #define jack_port_name pjack_port_name
94 #define jack_get_ports pjack_get_ports
95 #define jack_free pjack_free
96 #define jack_get_sample_rate pjack_get_sample_rate
97 #define jack_set_error_function pjack_set_error_function
98 #define jack_set_process_callback pjack_set_process_callback
99 #define jack_set_buffer_size_callback pjack_set_buffer_size_callback
100 #define jack_set_buffer_size pjack_set_buffer_size
101 #define jack_get_buffer_size pjack_get_buffer_size
102 #define jack_error_callback (*pjack_error_callback)
107 jack_options_t ClientOptions
= JackNullOption
;
115 #define JACKLIB "libjack.dll"
117 #define JACKLIB "libjack.so.0"
119 jack_handle
= LoadLib(JACKLIB
);
122 WARN("Failed to load %s\n", JACKLIB
);
126 std::string missing_funcs
;
127 #define LOAD_FUNC(f) do { \
128 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
129 if(p##f == nullptr) missing_funcs += "\n" #f; \
131 JACK_FUNCS(LOAD_FUNC
);
133 /* Optional symbols. These don't exist in all versions of JACK. */
134 #define LOAD_SYM(f) p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f))
135 LOAD_SYM(jack_error_callback
);
138 if(!missing_funcs
.empty())
140 WARN("Missing expected functions:%s\n", missing_funcs
.c_str());
141 CloseLib(jack_handle
);
142 jack_handle
= nullptr;
153 void operator()(void *ptr
) { jack_free(ptr
); }
155 using JackPortsPtr
= std::unique_ptr
<const char*[],JackDeleter
>; /* NOLINT(*-avoid-c-arrays) */
159 std::string mPattern
;
161 DeviceEntry() = default;
162 DeviceEntry(const DeviceEntry
&) = default;
163 DeviceEntry(DeviceEntry
&&) = default;
164 template<typename T
, typename U
>
165 DeviceEntry(T
&& name
, U
&& pattern
)
166 : mName
{std::forward
<T
>(name
)}, mPattern
{std::forward
<U
>(pattern
)}
170 DeviceEntry
& operator=(const DeviceEntry
&) = default;
171 DeviceEntry
& operator=(DeviceEntry
&&) = default;
173 DeviceEntry::~DeviceEntry() = default;
175 std::vector
<DeviceEntry
> PlaybackList
;
178 void EnumerateDevices(jack_client_t
*client
, std::vector
<DeviceEntry
> &list
)
180 std::remove_reference_t
<decltype(list
)>{}.swap(list
);
182 if(JackPortsPtr ports
{jack_get_ports(client
, nullptr, JACK_DEFAULT_AUDIO_TYPE
, JackPortIsInput
)})
184 for(size_t i
{0};ports
[i
];++i
)
186 const std::string_view portname
{ports
[i
]};
187 const size_t seppos
{portname
.find(':')};
188 if(seppos
== 0 || seppos
>= portname
.size())
191 const auto portdev
= portname
.substr(0, seppos
);
192 auto check_name
= [portdev
](const DeviceEntry
&entry
) -> bool
193 { return entry
.mName
== portdev
; };
194 if(std::find_if(list
.cbegin(), list
.cend(), check_name
) != list
.cend())
197 const auto &entry
= list
.emplace_back(portdev
, std::string
{portdev
}+":");
198 TRACE("Got device: %s = %s\n", entry
.mName
.c_str(), entry
.mPattern
.c_str());
200 /* There are ports but couldn't get device names from them. Add a
203 if(ports
[0] && list
.empty())
205 WARN("No device names found in available ports, adding a generic name.\n");
206 list
.emplace_back("JACK"sv
, ""sv
);
210 if(auto listopt
= ConfigValueStr({}, "jack", "custom-devices"))
212 for(size_t strpos
{0};strpos
< listopt
->size();)
214 size_t nextpos
{listopt
->find(';', strpos
)};
215 size_t seppos
{listopt
->find('=', strpos
)};
216 if(seppos
>= nextpos
|| seppos
== strpos
)
218 const auto entry
= std::string_view
{*listopt
}.substr(strpos
, nextpos
-strpos
);
219 ERR("Invalid device entry: \"%.*s\"\n", al::sizei(entry
), entry
.data());
220 if(nextpos
!= std::string::npos
) ++nextpos
;
225 const auto name
= std::string_view
{*listopt
}.substr(strpos
, seppos
-strpos
);
226 const auto pattern
= std::string_view
{*listopt
}.substr(seppos
+1,
227 std::min(nextpos
, listopt
->size())-(seppos
+1));
229 /* Check if this custom pattern already exists in the list. */
230 auto check_pattern
= [pattern
](const DeviceEntry
&entry
) -> bool
231 { return entry
.mPattern
== pattern
; };
232 auto itemmatch
= std::find_if(list
.begin(), list
.end(), check_pattern
);
233 if(itemmatch
!= list
.end())
235 /* If so, replace the name with this custom one. */
236 itemmatch
->mName
= name
;
237 TRACE("Customized device name: %s = %s\n", itemmatch
->mName
.c_str(),
238 itemmatch
->mPattern
.c_str());
242 /* Otherwise, add a new device entry. */
243 const auto &entry
= list
.emplace_back(name
, pattern
);
244 TRACE("Got custom device: %s = %s\n", entry
.mName
.c_str(), entry
.mPattern
.c_str());
247 if(nextpos
!= std::string::npos
) ++nextpos
;
254 /* Rename entries that have matching names, by appending '#2', '#3',
257 for(auto curitem
= list
.begin()+1;curitem
!= list
.end();++curitem
)
259 auto check_match
= [curitem
](const DeviceEntry
&entry
) -> bool
260 { return entry
.mName
== curitem
->mName
; };
261 if(std::find_if(list
.begin(), curitem
, check_match
) != curitem
)
263 std::string name
{curitem
->mName
};
265 auto check_name
= [&name
](const DeviceEntry
&entry
) -> bool
266 { return entry
.mName
== name
; };
268 name
= curitem
->mName
;
270 name
+= std::to_string(++count
);
271 } while(std::find_if(list
.begin(), curitem
, check_name
) != curitem
);
272 curitem
->mName
= std::move(name
);
279 struct JackPlayback final
: public BackendBase
{
280 JackPlayback(DeviceBase
*device
) noexcept
: BackendBase
{device
} { }
281 ~JackPlayback() override
;
283 int processRt(jack_nframes_t numframes
) noexcept
;
284 static int processRtC(jack_nframes_t numframes
, void *arg
) noexcept
285 { return static_cast<JackPlayback
*>(arg
)->processRt(numframes
); }
287 int process(jack_nframes_t numframes
) noexcept
;
288 static int processC(jack_nframes_t numframes
, void *arg
) noexcept
289 { return static_cast<JackPlayback
*>(arg
)->process(numframes
); }
293 void open(std::string_view name
) override
;
294 bool reset() override
;
295 void start() override
;
296 void stop() override
;
297 ClockLatency
getClockLatency() override
;
299 std::string mPortPattern
;
301 jack_client_t
*mClient
{nullptr};
302 std::array
<jack_port_t
*,MaxOutputChannels
> mPort
{};
306 std::atomic
<bool> mPlaying
{false};
307 bool mRTMixing
{false};
311 std::atomic
<bool> mKillNow
{true};
315 JackPlayback::~JackPlayback()
320 auto unregister_port
= [this](jack_port_t
*port
) -> void
321 { if(port
) jack_port_unregister(mClient
, port
); };
322 std::for_each(mPort
.begin(), mPort
.end(), unregister_port
);
325 jack_client_close(mClient
);
330 int JackPlayback::processRt(jack_nframes_t numframes
) noexcept
332 auto outptrs
= std::array
<jack_default_audio_sample_t
*,MaxOutputChannels
>{};
333 auto numchans
= size_t{0};
334 for(auto port
: mPort
)
336 if(!port
|| numchans
== mDevice
->RealOut
.Buffer
.size())
338 outptrs
[numchans
++] = static_cast<float*>(jack_port_get_buffer(port
, numframes
));
341 const auto dst
= al::span
{outptrs
}.first(numchans
);
342 if(mPlaying
.load(std::memory_order_acquire
)) LIKELY
343 mDevice
->renderSamples(dst
, static_cast<uint
>(numframes
));
346 std::for_each(dst
.begin(), dst
.end(), [numframes
](float *outbuf
) -> void
347 { std::fill_n(outbuf
, numframes
, 0.0f
); });
354 int JackPlayback::process(jack_nframes_t numframes
) noexcept
356 std::array
<al::span
<float>,MaxOutputChannels
> out
;
358 for(auto port
: mPort
)
361 out
[numchans
++] = {static_cast<float*>(jack_port_get_buffer(port
, numframes
)), numframes
};
365 if(mPlaying
.load(std::memory_order_acquire
)) LIKELY
367 auto data
= mRing
->getReadVector();
368 const auto update_size
= size_t{mDevice
->UpdateSize
};
370 const auto outlen
= size_t{numframes
/ update_size
};
371 const auto len1
= size_t{std::min(data
.first
.len
/update_size
, outlen
)};
372 const auto len2
= size_t{std::min(data
.second
.len
/update_size
, outlen
-len1
)};
374 auto src
= al::span
{reinterpret_cast<float*>(data
.first
.buf
), update_size
*len1
*numchans
};
375 for(size_t i
{0};i
< len1
;++i
)
377 for(size_t c
{0};c
< numchans
;++c
)
379 const auto iter
= std::copy_n(src
.begin(), update_size
, out
[c
].begin());
380 out
[c
] = {iter
, out
[c
].end()};
381 src
= src
.subspan(update_size
);
383 total
+= update_size
;
386 src
= al::span
{reinterpret_cast<float*>(data
.second
.buf
), update_size
*len2
*numchans
};
387 for(size_t i
{0};i
< len2
;++i
)
389 for(size_t c
{0};c
< numchans
;++c
)
391 const auto iter
= std::copy_n(src
.begin(), update_size
, out
[c
].begin());
392 out
[c
] = {iter
, out
[c
].end()};
393 src
= src
.subspan(update_size
);
395 total
+= update_size
;
398 mRing
->readAdvance(total
);
402 if(numframes
> total
)
404 auto clear_buf
= [](const al::span
<float> outbuf
) -> void
405 { std::fill(outbuf
.begin(), outbuf
.end(), 0.0f
); };
406 std::for_each(out
.begin(), out
.begin()+numchans
, clear_buf
);
412 int JackPlayback::mixerProc()
415 althrd_setname(GetMixerThreadName());
417 const auto update_size
= uint
{mDevice
->UpdateSize
};
418 const auto num_channels
= size_t{mDevice
->channelsFromFmt()};
419 auto outptrs
= std::vector
<float*>(num_channels
);
421 while(!mKillNow
.load(std::memory_order_acquire
)
422 && mDevice
->Connected
.load(std::memory_order_acquire
))
424 if(mRing
->writeSpace() < update_size
)
430 auto data
= mRing
->getWriteVector();
431 const auto len1
= size_t{data
.first
.len
/ update_size
};
432 const auto len2
= size_t{data
.second
.len
/ update_size
};
434 std::lock_guard
<std::mutex
> dlock
{mMutex
};
435 auto buffer
= al::span
{reinterpret_cast<float*>(data
.first
.buf
),
436 data
.first
.len
*num_channels
};
437 auto bufiter
= buffer
.begin();
438 for(size_t i
{0};i
< len1
;++i
)
440 std::generate_n(outptrs
.begin(), outptrs
.size(), [&bufiter
,update_size
]
442 auto ret
= al::to_address(bufiter
);
443 bufiter
+= ptrdiff_t(update_size
);
446 mDevice
->renderSamples(outptrs
, update_size
);
450 buffer
= al::span
{reinterpret_cast<float*>(data
.second
.buf
),
451 data
.second
.len
*num_channels
};
452 bufiter
= buffer
.begin();
453 for(size_t i
{0};i
< len2
;++i
)
455 std::generate_n(outptrs
.begin(), outptrs
.size(), [&bufiter
,update_size
]
457 auto ret
= al::to_address(bufiter
);
458 bufiter
+= ptrdiff_t(update_size
);
461 mDevice
->renderSamples(outptrs
, update_size
);
464 mRing
->writeAdvance((len1
+len2
) * update_size
);
471 void JackPlayback::open(std::string_view name
)
475 const PathNamePair
&binname
= GetProcBinary();
476 const char *client_name
{binname
.fname
.empty() ? "alsoft" : binname
.fname
.c_str()};
478 jack_status_t status
{};
479 mClient
= jack_client_open(client_name
, ClientOptions
, &status
, nullptr);
480 if(mClient
== nullptr)
481 throw al::backend_exception
{al::backend_error::DeviceError
,
482 "Failed to open client connection: 0x%02x", status
};
483 if((status
&JackServerStarted
))
484 TRACE("JACK server started\n");
485 if((status
&JackNameNotUnique
))
487 client_name
= jack_get_client_name(mClient
);
488 TRACE("Client name not unique, got '%s' instead\n", client_name
);
492 if(PlaybackList
.empty())
493 EnumerateDevices(mClient
, PlaybackList
);
495 if(name
.empty() && !PlaybackList
.empty())
497 name
= PlaybackList
[0].mName
;
498 mPortPattern
= PlaybackList
[0].mPattern
;
502 auto check_name
= [name
](const DeviceEntry
&entry
) -> bool
503 { return entry
.mName
== name
; };
504 auto iter
= std::find_if(PlaybackList
.cbegin(), PlaybackList
.cend(), check_name
);
505 if(iter
== PlaybackList
.cend())
506 throw al::backend_exception
{al::backend_error::NoDevice
,
507 "Device name \"%.*s\" not found", al::sizei(name
), name
.data()};
508 mPortPattern
= iter
->mPattern
;
511 mDevice
->DeviceName
= name
;
514 bool JackPlayback::reset()
516 auto unregister_port
= [this](jack_port_t
*port
) -> void
517 { if(port
) jack_port_unregister(mClient
, port
); };
518 std::for_each(mPort
.begin(), mPort
.end(), unregister_port
);
521 mRTMixing
= GetConfigValueBool(mDevice
->DeviceName
, "jack", "rt-mix", true);
522 jack_set_process_callback(mClient
,
523 mRTMixing
? &JackPlayback::processRtC
: &JackPlayback::processC
, this);
525 /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
526 * ready for when requested.
528 mDevice
->Frequency
= jack_get_sample_rate(mClient
);
529 mDevice
->UpdateSize
= jack_get_buffer_size(mClient
);
532 /* Assume only two periods when directly mixing. Should try to query
533 * the total port latency when connected.
535 mDevice
->BufferSize
= mDevice
->UpdateSize
* 2;
539 const std::string_view devname
{mDevice
->DeviceName
};
540 uint bufsize
{ConfigValueUInt(devname
, "jack", "buffer-size").value_or(mDevice
->UpdateSize
)};
541 bufsize
= std::max(NextPowerOf2(bufsize
), mDevice
->UpdateSize
);
542 mDevice
->BufferSize
= bufsize
+ mDevice
->UpdateSize
;
545 /* Force 32-bit float output. */
546 mDevice
->FmtType
= DevFmtFloat
;
549 auto ports
= al::span
{mPort
}.first(mDevice
->channelsFromFmt());
550 auto bad_port
= ports
.begin();
551 while(bad_port
!= ports
.end())
553 std::string name
{"channel_" + std::to_string(++port_num
)};
554 *bad_port
= jack_port_register(mClient
, name
.c_str(), JACK_DEFAULT_AUDIO_TYPE
,
555 JackPortIsOutput
| JackPortIsTerminal
, 0);
556 if(!*bad_port
) break;
559 if(bad_port
!= ports
.end())
561 ERR("Failed to register enough JACK ports for %s output\n",
562 DevFmtChannelsString(mDevice
->FmtChans
));
563 if(bad_port
== ports
.begin()) return false;
565 if(bad_port
== ports
.begin()+1)
566 mDevice
->FmtChans
= DevFmtMono
;
569 const auto ports_end
= ports
.begin()+2;
570 while(bad_port
!= ports_end
)
572 jack_port_unregister(mClient
, *(--bad_port
));
575 mDevice
->FmtChans
= DevFmtStereo
;
579 setDefaultChannelOrder();
584 void JackPlayback::start()
586 if(jack_activate(mClient
))
587 throw al::backend_exception
{al::backend_error::DeviceError
, "Failed to activate client"};
589 const std::string_view devname
{mDevice
->DeviceName
};
590 if(ConfigValueBool(devname
, "jack", "connect-ports").value_or(true))
592 JackPortsPtr pnames
{jack_get_ports(mClient
, mPortPattern
.c_str(), JACK_DEFAULT_AUDIO_TYPE
,
596 jack_deactivate(mClient
);
597 throw al::backend_exception
{al::backend_error::DeviceError
, "No playback ports found"};
600 for(size_t i
{0};i
< std::size(mPort
) && mPort
[i
];++i
)
604 ERR("No physical playback port for \"%s\"\n", jack_port_name(mPort
[i
]));
607 if(jack_connect(mClient
, jack_port_name(mPort
[i
]), pnames
[i
]))
608 ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(mPort
[i
]),
613 /* Reconfigure buffer metrics in case the server changed it since the reset
614 * (it won't change again after jack_activate), then allocate the ring
615 * buffer with the appropriate size.
617 mDevice
->Frequency
= jack_get_sample_rate(mClient
);
618 mDevice
->UpdateSize
= jack_get_buffer_size(mClient
);
619 mDevice
->BufferSize
= mDevice
->UpdateSize
* 2;
623 mPlaying
.store(true, std::memory_order_release
);
626 uint bufsize
{ConfigValueUInt(devname
, "jack", "buffer-size").value_or(mDevice
->UpdateSize
)};
627 bufsize
= std::max(NextPowerOf2(bufsize
), mDevice
->UpdateSize
);
628 mDevice
->BufferSize
= bufsize
+ mDevice
->UpdateSize
;
630 mRing
= RingBuffer::Create(bufsize
, mDevice
->frameSizeFromFmt(), true);
633 mPlaying
.store(true, std::memory_order_release
);
634 mKillNow
.store(false, std::memory_order_release
);
635 mThread
= std::thread
{std::mem_fn(&JackPlayback::mixerProc
), this};
637 catch(std::exception
& e
) {
638 jack_deactivate(mClient
);
639 mPlaying
.store(false, std::memory_order_release
);
640 throw al::backend_exception
{al::backend_error::DeviceError
,
641 "Failed to start mixing thread: %s", e
.what()};
646 void JackPlayback::stop()
648 if(mPlaying
.load(std::memory_order_acquire
))
650 mKillNow
.store(true, std::memory_order_release
);
651 if(mThread
.joinable())
657 jack_deactivate(mClient
);
658 mPlaying
.store(false, std::memory_order_release
);
663 ClockLatency
JackPlayback::getClockLatency()
665 std::lock_guard
<std::mutex
> dlock
{mMutex
};
667 ret
.ClockTime
= mDevice
->getClockTime();
668 ret
.Latency
= std::chrono::seconds
{mRing
? mRing
->readSpace() : mDevice
->UpdateSize
};
669 ret
.Latency
/= mDevice
->Frequency
;
675 void jack_msg_handler(const char *message
)
677 WARN("%s\n", message
);
682 bool JackBackendFactory::init()
687 if(!GetConfigValueBool({}, "jack", "spawn-server", false))
688 ClientOptions
= static_cast<jack_options_t
>(ClientOptions
| JackNoStartServer
);
690 const PathNamePair
&binname
= GetProcBinary();
691 const char *client_name
{binname
.fname
.empty() ? "alsoft" : binname
.fname
.c_str()};
693 void (*old_error_cb
)(const char*){&jack_error_callback
? jack_error_callback
: nullptr};
694 jack_set_error_function(jack_msg_handler
);
695 jack_status_t status
{};
696 jack_client_t
*client
{jack_client_open(client_name
, ClientOptions
, &status
, nullptr)};
697 jack_set_error_function(old_error_cb
);
700 WARN("jack_client_open() failed, 0x%02x\n", status
);
701 if((status
&JackServerFailed
) && !(ClientOptions
&JackNoStartServer
))
702 ERR("Unable to connect to JACK server\n");
706 jack_client_close(client
);
710 bool JackBackendFactory::querySupport(BackendType type
)
711 { return (type
== BackendType::Playback
); }
713 auto JackBackendFactory::enumerate(BackendType type
) -> std::vector
<std::string
>
715 std::vector
<std::string
> outnames
;
716 auto append_name
= [&outnames
](const DeviceEntry
&entry
) -> void
717 { outnames
.emplace_back(entry
.mName
); };
719 const PathNamePair
&binname
= GetProcBinary();
720 const char *client_name
{binname
.fname
.empty() ? "alsoft" : binname
.fname
.c_str()};
721 jack_status_t status
{};
724 case BackendType::Playback
:
725 if(jack_client_t
*client
{jack_client_open(client_name
, ClientOptions
, &status
, nullptr)})
727 EnumerateDevices(client
, PlaybackList
);
728 jack_client_close(client
);
731 WARN("jack_client_open() failed, 0x%02x\n", status
);
732 outnames
.reserve(PlaybackList
.size());
733 std::for_each(PlaybackList
.cbegin(), PlaybackList
.cend(), append_name
);
735 case BackendType::Capture
:
741 BackendPtr
JackBackendFactory::createBackend(DeviceBase
*device
, BackendType type
)
743 if(type
== BackendType::Playback
)
744 return BackendPtr
{new JackPlayback
{device
}};
748 BackendFactory
&JackBackendFactory::getFactory()
750 static JackBackendFactory factory
{};