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>
8#include <ossia/detail/thread.hpp>
10#include <weak_libjack.h>
17#define USE_WEAK_JACK 1
18#define NO_JACK_METADATA 1
19#define OSSIA_AUDIO_JACK 1
25inline bool has_jackd_process()
27 auto plist = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
28 if(plist == INVALID_HANDLE_VALUE)
32 entry.dwSize =
sizeof(PROCESSENTRY32);
34 if(!Process32First(plist, &entry))
42 using namespace std::literals;
44 const auto* name = entry.szExeFile;
46 if(name == std::string(
"jackd.exe"))
49 if(name == std::wstring(L
"jackd.exe"))
52 }
while(Process32Next(plist, &entry));
61 jack_client(std::string name)
noexcept
63 client = jack_client_open(name.c_str(), JackNoStartServer,
nullptr);
69 jack_client_close(client);
71 operator jack_client_t*()
const noexcept {
return client; }
73 jack_client_t* client{};
76using transport_timebase_function = smallfun::function<void(
int, jack_position_t&), 16>;
77using transport_sync_function
78 = smallfun::function<int(jack_transport_state_t, jack_position_t*), 16>;
81 std::vector<std::string> inputs;
82 std::vector<std::string> outputs;
84 transport_mode transport{};
85 transport_sync_function sync_function;
86 transport_timebase_function timebase_function;
89class jack_engine final :
public audio_engine
93 std::shared_ptr<jack_client> clt,
int inputs,
int outputs,
94 std::optional<jack_settings> settings = {})
97 if(!m_client || !(*m_client))
99 std::cerr <<
"JACK server not running?" << std::endl;
100 throw std::runtime_error(
"Audio error: no JACK server");
103 jack_client_t* client = *m_client;
104 jack_set_process_callback(client, process,
this);
105 jack_set_sample_rate_callback(
106 client, [](jack_nframes_t nframes,
void* arg) ->
int {
return 0; },
this);
107 jack_on_shutdown(client, JackShutdownCallback{},
this);
108 for(
int i = 0; i < inputs; i++)
112 name = settings->inputs[i];
114 name =
"in_" + std::to_string(i + 1);
116 auto in = jack_port_register(
117 client, name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
120 jack_deactivate(client);
121 throw std::runtime_error(
"Audio error: cannot register JACK input");
123 input_ports.push_back(in);
125 for(
int i = 0; i < outputs; i++)
129 name = settings->outputs[i];
131 name =
"out_" + std::to_string(i + 1);
133 auto out = jack_port_register(
134 client, name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
137 jack_deactivate(client);
138 throw std::runtime_error(
"Audio error: cannot register JACK output");
140 output_ports.push_back(out);
143 int err = jack_activate(client);
144 this->effective_sample_rate = jack_get_sample_rate(client);
145 this->effective_buffer_size = jack_get_buffer_size(client);
146 this->effective_inputs = inputs;
147 this->effective_outputs = outputs;
150 jack_deactivate(client);
151 std::cerr <<
"JACK error: " << err << std::endl;
152 throw std::runtime_error(
"Audio error: JACK cannot activate");
155 if(settings ? settings->autoconnect : true)
158 auto ports = jack_get_ports(
159 client,
nullptr, JACK_DEFAULT_AUDIO_TYPE,
160 JackPortIsPhysical | JackPortIsOutput);
163 for(std::size_t i = 0; i < input_ports.size(); i++)
168 jack_connect(client, ports[i], jack_port_name(input_ports[i]));
175 auto ports = jack_get_ports(
176 client,
nullptr, JACK_DEFAULT_AUDIO_TYPE,
177 JackPortIsPhysical | JackPortIsInput);
180 for(std::size_t i = 0; i < output_ports.size(); i++)
185 jack_connect(client, jack_port_name(output_ports[i]), ports[i]);
193 if(settings && settings->transport != transport_mode::none)
195 transport = settings->transport;
196 if(settings->timebase_function.allocated())
198 if(transport == transport_mode::master)
200 this->timebase_function = std::move(settings->timebase_function);
201 jack_set_timebase_callback(
203 [](jack_transport_state_t state, jack_nframes_t nframes,
204 jack_position_t* pos,
int new_pos,
void* s) {
205 auto& self = (*(jack_engine*)s);
206 self.timebase_function(nframes, *pos);
212 if(settings->sync_function.allocated())
214 this->sync_function = std::move(settings->sync_function);
215 jack_set_sync_callback(
217 [](jack_transport_state_t st, jack_position_t* pos,
void* s) ->
int {
218 auto& self = (*(jack_engine*)s);
219 return self.sync_function(st, pos);
228 ~jack_engine()
override { stop(); }
230 bool running()
const override
239 audio_engine::stop();
243 jack_client_t* client = *m_client;
245 jack_deactivate(client);
246 for(
auto port : this->input_ports)
247 jack_port_unregister(client, port);
248 for(
auto port : this->output_ports)
249 jack_port_unregister(client, port);
256 clear_buffers(jack_engine& self, jack_nframes_t nframes, std::size_t outputs)
258 for(std::size_t i = 0; i < outputs; i++)
260 auto chan = (jack_default_audio_sample_t*)jack_port_get_buffer(
261 self.output_ports[i], nframes);
262 for(std::size_t j = 0; j < nframes; j++)
269 static int process(jack_nframes_t nframes,
void* arg)
272 static const thread_local auto _
274 ossia::set_thread_name(
"ossia audio 0");
275 ossia::set_thread_pinned(thread_type::Audio, 0);
279 auto& self = *
static_cast<jack_engine*
>(arg);
282 const auto inputs = self.input_ports.size();
283 const auto outputs = self.output_ports.size();
284 if(self.stop_processing)
287 return clear_buffers(self, nframes, outputs);
290 auto float_input = (
float**)alloca(
sizeof(
float*) * inputs);
291 auto float_output = (
float**)alloca(
sizeof(
float*) * outputs);
292 for(std::size_t i = 0; i < inputs; i++)
294 float_input[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(
295 self.input_ports[i], nframes);
297 for(std::size_t i = 0; i < outputs; i++)
299 float_output[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(
300 self.output_ports[i], nframes);
304 jack_position_t pos{};
305 std::optional<transport_status> st;
306 std::optional<uint64_t> transport_frames;
307 auto transport_state = jack_transport_query(self.m_client->client, &pos);
308 if(self.transport != transport_mode::none)
310 switch(transport_state)
312 case JackTransportStopped:
313 st = transport_status::stopped;
315 case JackTransportStarting:
318 st = transport_status::starting;
320 case JackTransportRolling:
321 case JackTransportLooping:
322 st = transport_status::playing;
325 transport_frames = jack_nframes_t(pos.frame);
331 ossia::audio_tick_state ts{
332 float_input, float_output, (int)inputs, (
int)outputs,
333 nframes, pos.usecs / 1e6, transport_frames, st};
340 std::shared_ptr<jack_client> m_client{};
341 std::vector<jack_port_t*> input_ports;
342 std::vector<jack_port_t*> output_ports;
345 transport_mode transport{};
346 transport_sync_function sync_function;
347 transport_timebase_function timebase_function;