Hass Enhancer: Cleanup, added enhanced metering, added seamless bypass
[calf.git] / src / jack_client.cpp
blob3dd01b64341c6165dd032fd70f5803ca6ffb519a
1 /* Calf DSP Library Utility Application - calfjackhost
2 * A class wrapping a JACK client
4 * Copyright (C) 2007-2010 Krzysztof Foltman
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
22 #include <stdint.h>
23 #include <jack/jack.h>
24 #include <jack/midiport.h>
25 #include <calf/giface.h>
26 #include <calf/jackhost.h>
27 #include <set>
29 using namespace std;
30 using namespace calf_utils;
31 using namespace calf_plugins;
33 jack_client::jack_client()
35 input_nr = output_nr = midi_nr = 1;
36 input_name = "input_%d";
37 output_name = "output_%d";
38 midi_name = "midi_%d";
39 sample_rate = 0;
40 client = NULL;
41 automation_port = NULL;
44 void jack_client::add(jack_host *plugin)
46 calf_utils::ptlock lock(mutex);
47 plugins.push_back(plugin);
50 void jack_client::del(jack_host *plugin)
52 calf_utils::ptlock lock(mutex);
53 for (unsigned int i = 0; i < plugins.size(); i++)
55 if (plugins[i] == plugin)
57 plugins.erase(plugins.begin()+i);
58 return;
61 assert(0);
64 void jack_client::open(const char *client_name, const char *jack_session_id)
66 jack_status_t status;
67 if (jack_session_id && !jack_session_id)
68 client = jack_client_open(client_name, JackSessionID, &status, jack_session_id);
69 else
70 client = jack_client_open(client_name, JackNullOption, &status);
71 if (!client)
72 throw calf_utils::text_exception("Could not initialize JACK subsystem");
73 sample_rate = jack_get_sample_rate(client);
74 jack_set_process_callback(client, do_jack_process, this);
75 jack_set_buffer_size_callback(client, do_jack_bufsize, this);
76 name = get_name();
79 std::string jack_client::get_name()
81 return std::string(jack_get_client_name(client));
84 void jack_client::activate()
86 jack_activate(client);
89 void jack_client::deactivate()
91 jack_deactivate(client);
94 void jack_client::connect(const std::string &p1, const std::string &p2)
96 if (jack_connect(client, p1.c_str(), p2.c_str()) != 0)
97 throw calf_utils::text_exception("Could not connect JACK ports "+p1+" and "+p2);
100 void jack_client::close()
102 jack_client_close(client);
105 const char **jack_client::get_ports(const char *name_re, const char *type_re, unsigned long flags)
107 return jack_get_ports(client, name_re, type_re, flags);
110 namespace {
112 class jack_automation: public automation_iface
114 int event_pos;
115 int event_count;
116 jack_host *plugin;
117 void *midi_data;
118 jack_midi_event_t event;
120 void process_event()
122 if (event.size == 3 && ((event.buffer[0] & 0xF0) == 0xB0))
124 int designator = ((event.buffer[0] & 0xF) << 8) | event.buffer[1];
125 plugin->handle_automation_cc(designator, event.buffer[2]);
128 public:
129 jack_automation(jack_port_t *automation_port, int nframes, jack_host *_plugin)
131 event_pos = 0;
132 plugin = _plugin;
133 midi_data = jack_port_get_buffer(automation_port, nframes);
134 event_count = jack_midi_get_event_count(midi_data NFRAMES_MAYBE(nframes));
137 uint32_t apply_and_adjust(uint32_t start, uint32_t time)
139 while(event_pos < event_count) {
140 jack_midi_event_get(&event, midi_data, event_pos NFRAMES_MAYBE(nframes));
141 if (event.time > start && event.time < time)
142 return event.time;
143 event_pos++;
144 process_event();
146 return time;
152 int jack_client::do_jack_process(jack_nframes_t nframes, void *p)
154 jack_client *self = (jack_client *)p;
155 pttrylock lock(self->mutex);
156 if (lock.is_locked())
158 for(unsigned int i = 0; i < self->plugins.size(); i++)
160 jack_automation au(self->automation_port, nframes, self->plugins[i]);
161 self->plugins[i]->process(nframes, au);
164 return 0;
167 int jack_client::do_jack_bufsize(jack_nframes_t numsamples, void *p)
169 jack_client *self = (jack_client *)p;
170 ptlock lock(self->mutex);
171 for(unsigned int i = 0; i < self->plugins.size(); i++)
172 self->plugins[i]->cache_ports();
173 return 0;
176 void jack_client::delete_plugins()
178 ptlock lock(mutex);
179 for (unsigned int i = 0; i < plugins.size(); i++) {
180 delete plugins[i];
182 plugins.clear();
185 void jack_client::create_automation_input()
187 automation_port = jack_port_register(client, "Automation MIDI In", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
188 if (!automation_port)
189 throw text_exception("Could not create JACK MIDI automation port");
192 void jack_client::destroy_automation_input()
194 if (automation_port)
195 jack_port_unregister(client, automation_port);
198 void jack_client::calculate_plugin_order(std::vector<int> &indices)
200 map<string, int> port_to_plugin;
201 multimap<int, int> run_before;
202 for (unsigned int i = 0; i < plugins.size(); i++)
204 vector<jack_host::port *> ports;
205 plugins[i]->get_all_input_ports(ports);
206 for (unsigned int j = 0; j < ports.size(); j++)
207 port_to_plugin[ports[j]->nice_name] = i;
210 for (unsigned int i = 0; i < plugins.size(); i++)
212 vector<jack_host::port *> ports;
213 plugins[i]->get_all_output_ports(ports);
214 for (unsigned int j = 0; j < ports.size(); j++)
216 const char **conns = jack_port_get_connections(ports[j]->handle);
217 if (!conns)
218 continue;
219 for (const char **k = conns; *k; k++)
221 int cnlen = name.length();
222 if (0 != strncmp(*k, name.c_str(), cnlen))
223 continue;
224 if ((*k)[cnlen] != ':')
225 continue;
226 map<string, int>::const_iterator p = port_to_plugin.find((*k) + cnlen + 1);
227 if (p != port_to_plugin.end())
229 run_before.insert(make_pair<int, int>(p->second, i));
232 jack_free(conns);
236 struct deptracker
238 vector<int> &indices;
239 const multimap<int, int> &run_before;
240 set<int> already_added;
241 int count;
243 deptracker(vector<int> &i, const multimap<int, int> &rb, int c)
244 : indices(i)
245 , run_before(rb)
246 , count(c)
249 void add_with_dependent(int item)
251 if (already_added.count(item))
252 return;
253 already_added.insert(item);
254 for(multimap<int, int>::const_iterator i = run_before.find(item); i != run_before.end() && i->first == item; i++)
255 add_with_dependent(i->second);
256 indices.push_back(item);
258 void run()
260 for (int i = 0; i < count; i++)
261 add_with_dependent(i);
264 indices.clear();
265 deptracker(indices, run_before, plugins.size()).run();
268 void jack_client::apply_plugin_order(const std::vector<int> &indices)
270 std::vector<jack_host *> plugins_new;
271 assert(indices.size() == plugins.size());
272 for (unsigned int i = 0; i < indices.size(); i++)
273 plugins_new.push_back(plugins[indices[i]]);
274 ptlock lock(mutex);
275 plugins.swap(plugins_new);
277 string s;
278 for (unsigned int i = 0; i < plugins.size(); i++)
280 if (i)
281 s += " -> ";
282 s += plugins[i]->instance_name;
284 printf("Order: %s\n", s.c_str());