Avoid raw lock/unlock calls
[openal-soft.git] / alc / backends / jack.cpp
blob7c3f182272eb78929853685e9f970435ec17466c
1 /**
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
21 #include "config.h"
23 #include "backends/jack.h"
25 #include <cstdlib>
26 #include <cstdio>
27 #include <memory.h>
29 #include <thread>
30 #include <functional>
32 #include "alcmain.h"
33 #include "alu.h"
34 #include "alconfig.h"
35 #include "alexcpt.h"
36 #include "dynload.h"
37 #include "ringbuffer.h"
38 #include "threads.h"
40 #include <jack/jack.h>
41 #include <jack/ringbuffer.h>
44 namespace {
46 constexpr ALCchar jackDevice[] = "JACK Default";
49 #ifdef HAVE_DYNLOAD
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); \
63 MAGIC(jack_free); \
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);
71 void *jack_handle;
72 #define MAKE_FUNC(f) decltype(f) * p##f
73 JACK_FUNCS(MAKE_FUNC);
74 decltype(jack_error_callback) * pjack_error_callback;
75 #undef MAKE_FUNC
77 #ifndef IN_IDE_PARSER
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)
98 #endif
99 #endif
102 jack_options_t ClientOptions = JackNullOption;
104 ALCboolean jack_load()
106 ALCboolean error = ALC_FALSE;
108 #ifdef HAVE_DYNLOAD
109 if(!jack_handle)
111 std::string missing_funcs;
113 #ifdef _WIN32
114 #define JACKLIB "libjack.dll"
115 #else
116 #define JACKLIB "libjack.so.0"
117 #endif
118 jack_handle = LoadLib(JACKLIB);
119 if(!jack_handle)
121 WARN("Failed to load %s\n", JACKLIB);
122 return ALC_FALSE;
125 error = ALC_FALSE;
126 #define LOAD_FUNC(f) do { \
127 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
128 if(p##f == nullptr) { \
129 error = ALC_TRUE; \
130 missing_funcs += "\n" #f; \
132 } while(0)
133 JACK_FUNCS(LOAD_FUNC);
134 #undef 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);
138 #undef LOAD_SYM
140 if(error)
142 WARN("Missing expected functions:%s\n", missing_funcs.c_str());
143 CloseLib(jack_handle);
144 jack_handle = nullptr;
147 #endif
149 return !error;
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);
165 int mixerProc();
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]{};
176 RingBufferPtr mRing;
177 al::semaphore mSem;
179 std::atomic<bool> mKillNow{true};
180 std::thread mThread;
182 DEF_NEWDEL(JackPlayback)
185 JackPlayback::~JackPlayback()
187 if(!mClient)
188 return;
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);
196 mClient = nullptr;
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);
213 mRing = nullptr;
214 mRing = CreateRingBuffer(bufsize, mDevice->frameSizeFromFmt(), true);
215 if(!mRing)
217 ERR("Failed to reallocate ringbuffer\n");
218 aluHandleDisconnect(mDevice, "Failed to reallocate %u-sample buffer", bufsize);
220 return 0;
224 int JackPlayback::process(jack_nframes_t numframes)
226 jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
227 ALsizei numchans{0};
228 for(auto port : mPort)
230 if(!port) break;
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
243 ALfloat ret{*in};
244 in += numchans;
245 return ret;
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));
255 if(todo > 0)
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
264 ALfloat ret{*in};
265 in += numchans;
266 return ret;
269 data.second.buf += sizeof(ALfloat);
270 return outbuf + todo;
273 total += todo;
276 mRing->readAdvance(total);
277 mSem.post();
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;
291 return 0;
294 int JackPlayback::mixerProc()
296 SetRTPriority();
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)
305 dlock.unlock();
306 mSem.wait();
307 dlock.lock();
308 continue;
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);
319 if(len2 > 0)
320 aluMixData(mDevice, data.second.buf, len2);
321 mRing->writeAdvance(todo);
324 return 0;
328 void JackPlayback::open(const ALCchar *name)
330 if(!name)
331 name = jackDevice;
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",
342 status};
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;
398 else
400 ports_end = mPort+2;
401 while(bad_port != ports_end)
403 jack_port_unregister(mClient, *(--bad_port));
404 *bad_port = nullptr;
406 mDevice->FmtChans = DevFmtStereo;
410 mRing = nullptr;
411 mRing = CreateRingBuffer(bufsize, mDevice->frameSizeFromFmt(), true);
412 if(!mRing)
414 ERR("Failed to allocate ringbuffer\n");
415 return false;
418 SetDefaultChannelOrder(mDevice);
420 return true;
423 bool JackPlayback::start()
425 if(jack_activate(mClient))
427 ERR("Failed to activate client\n");
428 return false;
431 const char **ports{jack_get_ports(mClient, nullptr, nullptr,
432 JackPortIsPhysical|JackPortIsInput)};
433 if(ports == nullptr)
435 ERR("No physical playback ports found\n");
436 jack_deactivate(mClient);
437 return false;
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;
443 if(!pname)
445 ERR("No physical playback port for \"%s\"\n", jack_port_name(port));
446 return false;
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),
450 pname);
451 return true;
454 jack_free(ports);
456 try {
457 mKillNow.store(false, std::memory_order_release);
458 mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this};
459 return true;
461 catch(std::exception& e) {
462 ERR("Could not create playback thread: %s\n", e.what());
464 catch(...) {
466 jack_deactivate(mClient);
467 return false;
470 void JackPlayback::stop()
472 if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
473 return;
475 mSem.post();
476 mThread.join();
478 jack_deactivate(mClient);
482 ClockLatency JackPlayback::getClockLatency()
484 ClockLatency ret;
486 std::lock_guard<JackPlayback> _{*this};
487 ret.ClockTime = GetDeviceClockTime(mDevice);
488 ret.Latency = std::chrono::seconds{mRing->readSpace()};
489 ret.Latency /= mDevice->Frequency;
491 return ret;
495 void jack_msg_handler(const char *message)
497 WARN("%s\n", message);
500 } // namespace
502 bool JackBackendFactory::init()
504 if(!jack_load())
505 return false;
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);
515 if(!client)
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");
520 return false;
523 jack_client_close(client);
524 return true;
527 bool JackBackendFactory::querySupport(BackendType type)
528 { return (type == BackendType::Playback); }
530 void JackBackendFactory::probe(DevProbe type, std::string *outnames)
532 switch(type)
534 case DevProbe::Playback:
535 /* Includes null char. */
536 outnames->append(jackDevice, sizeof(jackDevice));
537 break;
539 case DevProbe::Capture:
540 break;
544 BackendPtr JackBackendFactory::createBackend(ALCdevice *device, BackendType type)
546 if(type == BackendType::Playback)
547 return BackendPtr{new JackPlayback{device}};
548 return nullptr;
551 BackendFactory &JackBackendFactory::getFactory()
553 static JackBackendFactory factory{};
554 return factory;