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
234 jack_client_t* client = *m_client;
235 jack_deactivate(client);
236 for(
auto port : this->input_ports)
237 jack_port_unregister(client, port);
238 for(
auto port : this->output_ports)
239 jack_port_unregister(client, port);
243 bool running()
const override
252 clear_buffers(jack_engine& self, jack_nframes_t nframes, std::size_t outputs)
254 for(std::size_t i = 0; i < outputs; i++)
256 auto chan = (jack_default_audio_sample_t*)jack_port_get_buffer(
257 self.output_ports[i], nframes);
258 for(std::size_t j = 0; j < nframes; j++)
265 static int process(jack_nframes_t nframes,
void* arg)
268 static const thread_local auto _
270 ossia::set_thread_name(
"ossia audio 0");
271 ossia::set_thread_pinned(thread_type::Audio, 0);
275 auto& self = *
static_cast<jack_engine*
>(arg);
278 const auto inputs = self.input_ports.size();
279 const auto outputs = self.output_ports.size();
280 if(self.stop_processing)
283 return clear_buffers(self, nframes, outputs);
286 auto float_input = (
float**)alloca(
sizeof(
float*) * inputs);
287 auto float_output = (
float**)alloca(
sizeof(
float*) * outputs);
288 for(std::size_t i = 0; i < inputs; i++)
290 float_input[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(
291 self.input_ports[i], nframes);
293 for(std::size_t i = 0; i < outputs; i++)
295 float_output[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(
296 self.output_ports[i], nframes);
300 jack_position_t pos{};
301 std::optional<transport_status> st;
302 std::optional<uint64_t> transport_frames;
303 auto transport_state = jack_transport_query(self.m_client->client, &pos);
304 if(self.transport != transport_mode::none)
306 switch(transport_state)
308 case JackTransportStopped:
309 st = transport_status::stopped;
311 case JackTransportStarting:
314 st = transport_status::starting;
316 case JackTransportRolling:
317 case JackTransportLooping:
318 st = transport_status::playing;
321 transport_frames = jack_nframes_t(pos.frame);
327 ossia::audio_tick_state ts{
328 float_input, float_output, (int)inputs, (
int)outputs,
329 nframes, pos.usecs / 1e6, transport_frames, st};
336 std::shared_ptr<jack_client> m_client{};
337 std::vector<jack_port_t*> input_ports;
338 std::vector<jack_port_t*> output_ports;
341 transport_mode transport{};
342 transport_sync_function sync_function;
343 transport_timebase_function timebase_function;