1 /* Calf DSP Library Utility Application - calfjackhost
2  * A class wrapping a JACK client
3  *
4  * Copyright (C) 2007-2010 Krzysztof Foltman
5  *
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.
10  *
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.
15  *
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.
20  */
21 
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>
28 
29 using namespace std;
30 using namespace calf_utils;
31 using namespace calf_plugins;
32 
jack_client()33 jack_client::jack_client()
34 {
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;
42 }
43 
add(jack_host * plugin)44 void jack_client::add(jack_host *plugin)
45 {
46     calf_utils::ptlock lock(mutex);
47     plugins.push_back(plugin);
48 }
49 
del(jack_host * plugin)50 void jack_client::del(jack_host *plugin)
51 {
52     calf_utils::ptlock lock(mutex);
53     for (unsigned int i = 0; i < plugins.size(); i++)
54     {
55         if (plugins[i] == plugin)
56         {
57             plugins.erase(plugins.begin()+i);
58             return;
59         }
60     }
61     assert(0);
62 }
63 
open(const char * client_name,const char * jack_session_id)64 void jack_client::open(const char *client_name, const char *jack_session_id)
65 {
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();
77 }
78 
get_name()79 std::string jack_client::get_name()
80 {
81     return std::string(jack_get_client_name(client));
82 }
83 
activate()84 void jack_client::activate()
85 {
86     jack_activate(client);
87 }
88 
deactivate()89 void jack_client::deactivate()
90 {
91     jack_deactivate(client);
92 }
93 
connect(const std::string & p1,const std::string & p2)94 void jack_client::connect(const std::string &p1, const std::string &p2)
95 {
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);
98 }
99 
close()100 void jack_client::close()
101 {
102     jack_client_close(client);
103 }
104 
get_ports(const char * name_re,const char * type_re,unsigned long flags)105 const char **jack_client::get_ports(const char *name_re, const char *type_re, unsigned long flags)
106 {
107     return jack_get_ports(client, name_re, type_re, flags);
108 }
109 
110 namespace {
111 
112 class jack_automation: public automation_iface
113 {
114     int event_pos;
115     int event_count;
116     jack_host *plugin;
117     void *midi_data;
118     jack_midi_event_t event;
119 
process_event()120     void process_event()
121     {
122         if (event.size == 3 && ((event.buffer[0] & 0xF0) == 0xB0))
123         {
124             int designator = ((event.buffer[0] & 0xF) << 8) | event.buffer[1];
125             plugin->handle_automation_cc(designator, event.buffer[2]);
126         }
127     }
128 public:
jack_automation(jack_port_t * automation_port,int nframes,jack_host * _plugin)129     jack_automation(jack_port_t *automation_port, int nframes, jack_host *_plugin)
130     {
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));
135     }
136 
apply_and_adjust(uint32_t start,uint32_t time)137     uint32_t apply_and_adjust(uint32_t start, uint32_t time)
138     {
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();
145         }
146         return time;
147     }
148 };
149 
150 }
151 
do_jack_process(jack_nframes_t nframes,void * p)152 int jack_client::do_jack_process(jack_nframes_t nframes, void *p)
153 {
154     jack_client *self = (jack_client *)p;
155     pttrylock lock(self->mutex);
156     if (lock.is_locked())
157     {
158         for(unsigned int i = 0; i < self->plugins.size(); i++)
159         {
160             jack_automation au(self->automation_port, nframes, self->plugins[i]);
161             self->plugins[i]->process(nframes, au);
162         }
163     }
164     return 0;
165 }
166 
do_jack_bufsize(jack_nframes_t numsamples,void * p)167 int jack_client::do_jack_bufsize(jack_nframes_t numsamples, void *p)
168 {
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;
174 }
175 
delete_plugins()176 void jack_client::delete_plugins()
177 {
178     ptlock lock(mutex);
179     for (unsigned int i = 0; i < plugins.size(); i++) {
180         delete plugins[i];
181     }
182     plugins.clear();
183 }
184 
create_automation_input()185 void jack_client::create_automation_input()
186 {
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");
190 }
191 
destroy_automation_input()192 void jack_client::destroy_automation_input()
193 {
194     if (automation_port)
195         jack_port_unregister(client, automation_port);
196 }
197 
calculate_plugin_order(std::vector<int> & indices)198 void jack_client::calculate_plugin_order(std::vector<int> &indices)
199 {
200     map<string, int> port_to_plugin;
201     multimap<int, int> run_before;
202     for (unsigned int i = 0; i < plugins.size(); i++)
203     {
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;
208     }
209 
210     for (unsigned int i = 0; i < plugins.size(); i++)
211     {
212         vector<jack_host::port *> ports;
213         plugins[i]->get_all_output_ports(ports);
214         for (unsigned int j = 0; j < ports.size(); j++)
215         {
216             const char **conns = jack_port_get_connections(ports[j]->handle);
217             if (!conns)
218                 continue;
219             for (const char **k = conns; *k; k++)
220             {
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())
228                 {
229                     run_before.insert(make_pair<const int&, unsigned int&>(p->second, i));
230                 }
231             }
232             jack_free(conns);
233         }
234     }
235 
236     struct deptracker
237     {
238         vector<int> &indices;
239         const multimap<int, int> &run_before;
240         set<int> already_added;
241         int count;
242 
243         deptracker(vector<int> &i, const multimap<int, int> &rb, int c)
244         : indices(i)
245         , run_before(rb)
246         , count(c)
247         {
248         }
249         void add_with_dependent(int item)
250         {
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);
257         }
258         void run()
259         {
260             for (int i = 0; i < count; i++)
261                 add_with_dependent(i);
262         }
263     };
264     indices.clear();
265     deptracker(indices, run_before, plugins.size()).run();
266 }
267 
apply_plugin_order(const std::vector<int> & indices)268 void jack_client::apply_plugin_order(const std::vector<int> &indices)
269 {
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);
276 
277     string s;
278     for (unsigned int i = 0; i < plugins.size(); i++)
279     {
280         if (i)
281             s += " -> ";
282         s += plugins[i]->instance_name;
283     }
284     printf("Order: %s\n", s.c_str());
285 }
286