2#include <ossia/detail/config.hpp>
4#if defined(OSSIA_ENABLE_JACK)
5#if __has_include(<jack/jack.h>) && !defined(__EMSCRIPTEN__)
7#include <ossia/audio/audio_engine.hpp>
9#include <ossia/detail/thread.hpp>
11#include <weak_libjack.h>
19#define USE_WEAK_JACK 1
20#define NO_JACK_METADATA 1
21#define OSSIA_AUDIO_JACK 1
27inline bool has_jackd_process()
29 auto plist = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
30 if(plist == INVALID_HANDLE_VALUE)
34 entry.dwSize =
sizeof(PROCESSENTRY32);
36 if(!Process32First(plist, &entry))
44 using namespace std::literals;
46 const auto* name = entry.szExeFile;
48 if(name == std::string(
"jackd.exe"))
51 if(name == std::wstring(L
"jackd.exe"))
54 }
while(Process32Next(plist, &entry));
63 jack_client(std::string name)
noexcept
65 client = jack_client_open(name.c_str(), JackNoStartServer,
nullptr);
71 jack_client_close(client);
73 operator jack_client_t*()
const noexcept {
return client; }
75 jack_client_t* client{};
78using transport_timebase_function = smallfun::function<void(
int, jack_position_t&), 16>;
79using transport_sync_function
80 = smallfun::function<int(jack_transport_state_t, jack_position_t*), 16>;
83 std::vector<std::string> inputs;
84 std::vector<std::string> outputs;
86 transport_mode transport{};
87 transport_sync_function sync_function;
88 transport_timebase_function timebase_function;
91class jack_engine final :
public audio_engine
95 std::shared_ptr<jack_client> clt,
int inputs,
int outputs,
96 std::optional<jack_settings> settings = {})
99 if(!m_client || !(*m_client))
101 std::cerr <<
"JACK server not running?" << std::endl;
102 throw std::runtime_error(
"Audio error: no JACK server");
105 jack_client_t* client = *m_client;
106 jack_set_process_callback(client, process,
this);
107 jack_set_sample_rate_callback(
108 client, [](jack_nframes_t nframes,
void* arg) ->
int {
return 0; },
this);
109 jack_on_shutdown(client, JackShutdownCallback{},
this);
110 jack_set_latency_callback(client, latency_callback,
this);
111 for(
int i = 0; i < inputs; i++)
115 name = settings->inputs[i];
117 name =
"in_" + std::to_string(i + 1);
119 auto in = jack_port_register(
120 client, name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
123 jack_deactivate(client);
124 throw std::runtime_error(
"Audio error: cannot register JACK input");
126 input_ports.push_back(in);
128 for(
int i = 0; i < outputs; i++)
132 name = settings->outputs[i];
134 name =
"out_" + std::to_string(i + 1);
136 auto out = jack_port_register(
137 client, name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
140 jack_deactivate(client);
141 throw std::runtime_error(
"Audio error: cannot register JACK output");
143 output_ports.push_back(out);
146 int err = jack_activate(client);
147 this->effective_sample_rate = jack_get_sample_rate(client);
148 this->effective_buffer_size = jack_get_buffer_size(client);
149 this->effective_inputs = inputs;
150 this->effective_outputs = outputs;
153 jack_deactivate(client);
154 std::cerr <<
"JACK error: " << err << std::endl;
155 throw std::runtime_error(
"Audio error: JACK cannot activate");
158 if(settings ? settings->autoconnect :
true)
161 auto ports = jack_get_ports(
162 client,
nullptr, JACK_DEFAULT_AUDIO_TYPE,
163 JackPortIsPhysical | JackPortIsOutput);
166 for(std::size_t i = 0; i < input_ports.size(); i++)
171 jack_connect(client, ports[i], jack_port_name(input_ports[i]));
178 auto ports = jack_get_ports(
179 client,
nullptr, JACK_DEFAULT_AUDIO_TYPE,
180 JackPortIsPhysical | JackPortIsInput);
183 for(std::size_t i = 0; i < output_ports.size(); i++)
188 jack_connect(client, jack_port_name(output_ports[i]), ports[i]);
201 "JACK: capture latency {} frames, playback latency {} frames",
202 this->effective_input_latency.load(), this->effective_output_latency.load());
204 if(settings && settings->transport != transport_mode::none)
206 transport = settings->transport;
207 if(settings->timebase_function.allocated())
209 if(transport == transport_mode::master)
211 this->timebase_function = std::move(settings->timebase_function);
212 jack_set_timebase_callback(
214 [](jack_transport_state_t state, jack_nframes_t nframes,
215 jack_position_t* pos,
int new_pos,
void* s) {
216 auto& self = (*(jack_engine*)s);
217 self.timebase_function(nframes, *pos);
223 if(settings->sync_function.allocated())
225 this->sync_function = std::move(settings->sync_function);
226 jack_set_sync_callback(
228 [](jack_transport_state_t st, jack_position_t* pos,
void* s) ->
int {
229 auto& self = (*(jack_engine*)s);
230 return self.sync_function(st, pos);
239 ~jack_engine()
override { stop(); }
241 bool running()
const override
250 audio_engine::stop();
254 jack_client_t* client = *m_client;
256 jack_deactivate(client);
257 for(
auto port : this->input_ports)
258 jack_port_unregister(client, port);
259 for(
auto port : this->output_ports)
260 jack_port_unregister(client, port);
266 void update_latencies() noexcept
268 if(!input_ports.empty())
270 jack_latency_range_t range{};
271 jack_port_get_latency_range(input_ports[0], JackCaptureLatency, &range);
272 this->effective_input_latency.store(
int(range.max), std::memory_order_relaxed);
274 if(!output_ports.empty())
276 jack_latency_range_t range{};
277 jack_port_get_latency_range(output_ports[0], JackPlaybackLatency, &range);
278 this->effective_output_latency.store(
int(range.max), std::memory_order_relaxed);
286 static void latency_callback(jack_latency_callback_mode_t mode,
void* arg)
288 auto& self = *
static_cast<jack_engine*
>(arg);
290 = mode == JackCaptureLatency ? self.input_ports : self.output_ports;
291 const auto& to = mode == JackCaptureLatency ? self.output_ports : self.input_ports;
295 jack_latency_range_t range{};
296 jack_port_get_latency_range(from[0], mode, &range);
297 for(std::size_t i = 1; i < from.size(); i++)
299 jack_latency_range_t r{};
300 jack_port_get_latency_range(from[i], mode, &r);
301 range.min = std::min(range.min, r.min);
302 range.max = std::max(range.max, r.max);
305 jack_port_set_latency_range(port, mode, &range);
308 self.update_latencies();
312 clear_buffers(jack_engine& self, jack_nframes_t nframes, std::size_t outputs)
314 for(std::size_t i = 0; i < outputs; i++)
316 auto chan = (jack_default_audio_sample_t*)jack_port_get_buffer(
317 self.output_ports[i], nframes);
318 for(std::size_t j = 0; j < nframes; j++)
325 static int process(jack_nframes_t nframes,
void* arg)
328 static const thread_local auto _
330 ossia::set_thread_name(
"ossia audio 0");
331 ossia::set_thread_pinned(thread_type::Audio, 0);
335 auto& self = *
static_cast<jack_engine*
>(arg);
338 const auto inputs = self.input_ports.size();
339 const auto outputs = self.output_ports.size();
340 if(self.stop_processing)
343 return clear_buffers(self, nframes, outputs);
346 auto float_input = (
float**)alloca(
sizeof(
float*) * inputs);
347 auto float_output = (
float**)alloca(
sizeof(
float*) * outputs);
348 for(std::size_t i = 0; i < inputs; i++)
350 float_input[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(
351 self.input_ports[i], nframes);
353 for(std::size_t i = 0; i < outputs; i++)
355 float_output[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(
356 self.output_ports[i], nframes);
360 jack_position_t pos{};
361 std::optional<transport_status> st;
362 std::optional<uint64_t> transport_frames;
363 auto transport_state = jack_transport_query(self.m_client->client, &pos);
364 if(self.transport != transport_mode::none)
366 switch(transport_state)
368 case JackTransportStopped:
369 st = transport_status::stopped;
371 case JackTransportStarting:
374 st = transport_status::starting;
376 case JackTransportRolling:
377 case JackTransportLooping:
378 st = transport_status::playing;
381 transport_frames = jack_nframes_t(pos.frame);
387 ossia::audio_tick_state ts{
388 float_input, float_output, (int)inputs, (
int)outputs,
389 nframes, pos.usecs / 1e6, transport_frames, st};
396 std::shared_ptr<jack_client> m_client{};
397 std::vector<jack_port_t*> input_ports;
398 std::vector<jack_port_t*> output_ports;
401 transport_mode transport{};
402 transport_sync_function sync_function;
403 transport_timebase_function timebase_function;
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition context.cpp:120