supernova: jack backend - improve xrun handling
[supercollider.git] / server / supernova / audio_backend / jack_backend.hpp
blobaa6d82f24f720d36dc4bf440dfe6e61fbda08a8b
1 // native jack backend
2 // Copyright (C) 2009, 2010 Tim Blechmann
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program 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
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; see the file COPYING. If not, write to
16 // the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 // Boston, MA 02111-1307, USA.
19 #ifndef AUDIO_BACKEND_JACK_BACKEND_HPP
20 #define AUDIO_BACKEND_JACK_BACKEND_HPP
22 #include <iostream>
23 #include <string>
24 #include <vector>
26 #include <boost/lexical_cast.hpp>
28 #include <jack/jack.h>
29 #include <jack/thread.h>
31 #include "nova-tt/name_thread.hpp"
32 #include "nova-tt/thread_affinity.hpp"
33 #include "utilities/branch_hints.hpp"
35 #include "audio_backend_common.hpp"
36 #include "cpu_time_info.hpp"
38 namespace nova
41 /** jack backend
43 * the jack callback thread is pinned to the first cpu of the system
45 * \todo later it may be interesting to directly map the io busses to the jack port regions
46 * \todo rethink the use of output port lock
48 template <typename engine_functor,
49 typename sample_type = float,
50 bool blocking = false
52 class jack_backend:
53 public detail::audio_delivery_helper<sample_type, jack_default_audio_sample_t, blocking, false>,
54 public detail::audio_settings_basic,
55 protected engine_functor
57 typedef detail::audio_delivery_helper<sample_type, jack_default_audio_sample_t, blocking, false> super;
59 public:
60 jack_backend(void):
61 client(NULL), time_is_synced(false)
64 ~jack_backend(void)
66 if (audio_is_active())
67 deactivate_audio();
69 close_client();
72 uint32_t get_audio_blocksize(void) const
74 return blocksize_;
77 public:
78 void open_client(std::string const & server_name, std::string const & name, uint32_t input_port_count,
79 uint32_t output_port_count, uint32_t blocksize)
81 blocksize_ = blocksize;
83 /* open client */
84 client = server_name.empty() ? jack_client_open(name.c_str(), JackNoStartServer, &status)
85 : jack_client_open(name.c_str(), jack_options_t(JackNoStartServer | JackServerName),
86 &status, server_name.c_str());
87 boost::atomic_thread_fence(boost::memory_order_release); // ensure visibility on other threads
89 if (status & JackServerFailed)
90 throw std::runtime_error("Unable to connect to JACK server");
92 if (status & JackNameNotUnique) {
93 const char * client_name = jack_get_client_name(client);
94 std::cout << "unique client name: " << client_name << std::endl;
97 /* initialize callbacks */
98 jack_set_thread_init_callback (client, jack_thread_init_callback, this);
99 jack_set_process_callback (client, jack_process_callback, this);
100 jack_set_xrun_callback(client, jack_xrun_callback, this);
101 jack_on_info_shutdown(client, (JackInfoShutdownCallback)jack_on_info_shutdown_callback, NULL);
103 /* register ports */
104 input_ports.clear();
105 for (uint32_t i = 0; i != input_port_count; ++i) {
106 std::string portname ("input_");
107 portname += boost::lexical_cast<std::string>(i+1);
108 jack_port_t * port =
109 jack_port_register(client, portname.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
110 input_ports.push_back(port);
112 input_channels = input_port_count;
113 super::input_samples.resize(input_port_count);
115 output_ports.clear();
116 for (uint32_t i = 0; i != output_port_count; ++i) {
117 std::string portname ("output_");
118 portname += boost::lexical_cast<std::string>(i+1);
119 jack_port_t * port =
120 jack_port_register(client, portname.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
121 output_ports.push_back(port);
123 output_channels = output_port_count;
124 super::output_samples.resize(output_port_count);
126 samplerate_ = jack_get_sample_rate(client);
127 jack_frames = jack_get_buffer_size(client);
129 if (jack_frames % blocksize_)
130 throw std::runtime_error("Jack buffer size is not a multiple of blocksize");
133 void close_client(void)
135 if (client) {
136 jack_client_close(client);
137 client = NULL;
141 bool audio_is_opened(void)
143 return client != NULL;
146 bool audio_is_active(void)
148 return is_active;
151 void activate_audio(void)
153 is_active = true;
154 jack_activate(client);
157 void deactivate_audio(void)
159 jack_deactivate(client);
160 is_active = false;
163 void get_cpuload(float & peak, float & average) const
165 cpu_time_accumulator.get(peak, average);
168 int connect_input(int channel, const char * portname)
170 if (channel >= input_ports.size())
171 return -1;
172 return jack_connect(client, portname, jack_port_name(input_ports[channel]));
175 int connect_output(int channel, const char * portname)
177 if (channel >= output_ports.size())
178 return -1;
179 return jack_connect(client, jack_port_name(output_ports[channel]), portname);
182 int connect_all_inputs(const char * client_name)
184 const char **ports = jack_get_ports (client, client_name, NULL, JackPortIsOutput);
186 if (!ports)
187 return -1;
189 std::size_t i = 0;
190 while (ports[i]) {
191 if (i == input_ports.size())
192 break;
194 int err = jack_connect(client, ports[i], jack_port_name(input_ports[i]));
195 if (err)
196 return err;
197 ++i;
200 free(ports);
201 return 0;
204 int connect_all_outputs(const char * client_name)
206 const char **ports = jack_get_ports (client, client_name, NULL, JackPortIsInput);
208 if (!ports)
209 return -1;
211 std::size_t i = 0;
212 while (ports[i]) {
213 if (i == output_ports.size())
214 break;
216 int err = jack_connect(client, jack_port_name(output_ports[i]), ports[i]);
217 if (err)
218 return err;
219 ++i;
222 free(ports);
223 return 0;
226 int max_realtime_priority(void) const
228 return jack_client_max_real_time_priority(client);
231 private:
232 static void jack_thread_init_callback(void * arg)
234 boost::atomic_thread_fence(boost::memory_order_acquire);
235 jack_backend * self = static_cast<jack_backend*>(arg);
236 if (jack_client_thread_id(self->client) == pthread_self())
237 engine_functor::init_thread();
238 else
239 name_thread("Jack Helper");
242 static int jack_process_callback(jack_nframes_t frames, void * arg)
244 return static_cast<jack_backend*>(arg)->perform(frames);
247 static int jack_on_info_shutdown_callback(jack_status_t code, const char *reason, void *arg)
249 std::cerr << "Jack server was shut down: " << reason << std::endl;
250 std::cerr << "Exiting ..." << std::endl;
251 exit(0); // TODO: later we may want to call a function
254 static int jack_xrun_callback(void * arg)
256 return static_cast<jack_backend*>(arg)->handle_xrun();
259 int handle_xrun(void)
261 time_is_synced = false;
262 engine_functor::log_("Jack: xrun detected - resyncing clock\n");
263 return 0;
266 int perform(jack_nframes_t frames)
268 if (unlikely(!time_is_synced)) {
269 engine_functor::sync_clock();
270 time_is_synced = true;
273 /* get port regions */
274 jack_default_audio_sample_t * inputs[input_channels];
275 jack_default_audio_sample_t * outputs[output_channels];
276 for (uint16_t i = 0; i != input_channels; ++i)
277 inputs[i] = (jack_default_audio_sample_t*) jack_port_get_buffer(input_ports[i], frames);
279 for (uint16_t i = 0; i != output_channels; ++i)
280 outputs[i] = (jack_default_audio_sample_t*) jack_port_get_buffer(output_ports[i], frames);
282 jack_nframes_t processed = 0;
283 while (processed != frames) {
284 fetch_inputs(inputs, blocksize_);
286 engine_functor::run_tick();
288 deliver_outputs(outputs, blocksize_);
290 processed += blocksize_;
293 cpu_time_accumulator.update(jack_cpu_load(client));
295 return 0;
298 void fetch_inputs(jack_default_audio_sample_t ** inputs, size_t frames)
300 if (is_multiple_of_vectorsize(frames)) {
301 for (uint16_t i = 0; i != input_channels; ++i) {
302 if (is_aligned(inputs[i]))
303 copyvec_simd(super::input_samples[i].get(), inputs[i], frames);
304 else
305 copyvec(super::input_samples[i].get(), inputs[i], frames);
306 inputs[i] += blocksize_;
308 } else {
309 for (uint16_t i = 0; i != input_channels; ++i) {
310 copyvec(super::input_samples[i].get(), inputs[i], frames);
311 inputs[i] += blocksize_;
316 void deliver_outputs(jack_default_audio_sample_t ** outputs, size_t frames)
318 if (is_multiple_of_vectorsize(frames)) {
319 for (uint16_t i = 0; i != output_channels; ++i) {
320 if (is_aligned(outputs[i]))
321 copyvec_simd(outputs[i], super::output_samples[i].get(), frames);
322 else
323 copyvec(outputs[i], super::output_samples[i].get(), frames);
324 outputs[i] += blocksize_;
326 } else {
327 for (uint16_t i = 0; i != output_channels; ++i) {
328 copyvec(outputs[i], super::output_samples[i].get(), frames);
329 outputs[i] += blocksize_;
334 static bool is_aligned(void * arg)
336 size_t mask = sizeof(vec<float>::size) * sizeof(float) * 8 - 1;
337 return !((size_t)arg & mask);
340 static bool is_multiple_of_vectorsize(size_t count)
342 return !(count & (vec<float>::objects_per_cacheline - 1));
345 static int jack_buffersize_callback(jack_nframes_t frames, void * arg)
347 return static_cast<jack_backend*>(arg)->buffer_size_callback(frames);
350 int buffer_size_callback(jack_nframes_t frames)
352 jack_frames = frames;
353 if (jack_frames % blocksize_)
354 /* we need a multiple of the blocksize */
355 return 1;
356 return 0;
359 jack_client_t * client;
360 jack_status_t status;
362 bool is_active;
363 bool time_is_synced;
364 uint32_t blocksize_;
366 std::vector<jack_port_t*> input_ports, output_ports;
367 jack_nframes_t jack_frames;
368 cpu_time_info cpu_time_accumulator;
371 } /* namespace nova */
374 #endif /* AUDIO_BACKEND_JACK_BACKEND_HPP */