1 /*****************************************************************
2 * gmerlin - a general purpose multimedia framework and applications
3 *
4 * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5 * gmerlin-general@lists.sourceforge.net
6 * http://gmerlin.sourceforge.net
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * *****************************************************************/
21 #include <string.h>
22
23 #include "jack_common.h"
24
25 #define LOG_DOMAIN "jack"
26
27
28 static const bg_parameter_info_t parameters[] =
29 {
30 {
31 .name = "connect_ports",
32 .long_name = TRS("Connect ports"),
33 .type = BG_PARAMETER_CHECKBUTTON,
34 .val_default = { .val_i = 1 },
35 .help_string = TRS("Autoconnect ports"),
36 },
37 { /* End */ },
38 };
39
40 const bg_parameter_info_t *
bg_jack_get_parameters(void * p)41 bg_jack_get_parameters(void * p)
42 {
43 return parameters;
44 }
45
46 /* Set parameter */
47
48 void
bg_jack_set_parameter(void * p,const char * name,const bg_parameter_value_t * val)49 bg_jack_set_parameter(void * p, const char * name,
50 const bg_parameter_value_t * val)
51 {
52 jack_t * priv = p;
53 if(!name)
54 return;
55
56 if(!strcmp(name, "connect_ports"))
57 priv->connect_ports = val->val_i;
58 }
59
bg_jack_create()60 void * bg_jack_create()
61 {
62 jack_t * ret = calloc(1, sizeof(*ret));
63
64 pthread_mutex_init(&ret->running_mutex, NULL);
65 pthread_mutex_init(&ret->active_mutex, NULL);
66
67 return ret;
68 }
69
bg_jack_start(void * data)70 int bg_jack_start(void * data)
71 {
72 jack_t * priv = data;
73
74 pthread_mutex_lock(&priv->active_mutex);
75 priv->active = 1;
76 pthread_mutex_unlock(&priv->active_mutex);
77
78 return 1;
79 }
80
bg_jack_stop(void * data)81 void bg_jack_stop(void * data)
82 {
83 jack_t * priv = data;
84 pthread_mutex_lock(&priv->active_mutex);
85 priv->active = 0;
86 pthread_mutex_unlock(&priv->active_mutex);
87 }
88
create_channel_map(jack_t * priv,int output)89 static void create_channel_map(jack_t * priv, int output)
90 {
91 int i;
92
93 int flags = JackPortIsPhysical;
94
95 if(output)
96 flags |= JackPortIsInput;
97 else
98 flags |= JackPortIsOutput;
99
100 priv->ext_ports =
101 jack_get_ports(priv->client, NULL, NULL, flags);
102
103 /* Count ports */
104 priv->num_ports = 0;
105 while(priv->ext_ports[priv->num_ports])
106 priv->num_ports++;
107
108 priv->ports = calloc(priv->num_ports, sizeof(*priv->ports));
109
110 /* Set channel map */
111
112 if(priv->num_ports == 1)
113 {
114 priv->ports[0].channel_id = GAVL_CHID_FRONT_CENTER;
115 }
116 if(priv->num_ports >= 2)
117 {
118 priv->ports[0].channel_id = GAVL_CHID_FRONT_LEFT;
119 priv->ports[1].channel_id = GAVL_CHID_FRONT_RIGHT;
120 }
121 if(priv->num_ports >= 4)
122 {
123 priv->ports[2].channel_id = GAVL_CHID_REAR_LEFT;
124 priv->ports[3].channel_id = GAVL_CHID_REAR_RIGHT;
125 }
126 if(priv->num_ports >= 5)
127 {
128 priv->ports[4].channel_id = GAVL_CHID_FRONT_CENTER;
129 }
130 if(priv->num_ports >= 6)
131 {
132 priv->ports[5].channel_id = GAVL_CHID_LFE;
133 }
134 if(priv->num_ports >= 8)
135 {
136 priv->ports[6].channel_id = GAVL_CHID_SIDE_LEFT;
137 priv->ports[7].channel_id = GAVL_CHID_SIDE_RIGHT;
138 }
139
140 flags = JackPortIsTerminal;
141
142 if(output)
143 flags |= JackPortIsOutput;
144 else
145 flags |= JackPortIsInput;
146
147 /* Set ports and buffers */
148 for(i = 0; i < priv->num_ports; i++)
149 {
150 priv->ports[i].ext_name = priv->ext_ports[i];
151
152 priv->ports[i].int_port =
153 jack_port_register(priv->client,
154 gavl_channel_id_to_string(priv->ports[i].channel_id),
155 JACK_DEFAULT_AUDIO_TYPE,
156 flags, 0);
157 priv->ports[i].buffer =
158 jack_ringbuffer_create(sizeof(float)*priv->samples_per_frame*4);
159 }
160
161 }
162
connect_ports(jack_t * priv,int output)163 static void connect_ports(jack_t * priv, int output)
164 {
165 int i;
166 const char * src;
167 const char * dst;
168
169 for(i = 0; i < priv->num_ports; i++)
170 {
171 if(output)
172 {
173 src = jack_port_name(priv->ports[i].int_port);
174 dst = priv->ports[i].ext_name;
175 }
176 else
177 {
178 src = priv->ports[i].ext_name;
179 dst = jack_port_name(priv->ports[i].int_port);
180 }
181
182 if(jack_connect(priv->client, src, dst))
183 bg_log(BG_LOG_WARNING, LOG_DOMAIN,
184 "Connecting %s with %s failed", src, dst);
185 }
186 }
187
jack_shutdown(void * arg)188 static void jack_shutdown (void *arg)
189 {
190 jack_t * priv = arg;
191 fprintf(stderr, "Jack shutdown\n");
192
193 pthread_mutex_lock(&priv->running_mutex);
194 priv->running = 0;
195 pthread_mutex_unlock(&priv->running_mutex);
196
197 // exit (1);
198 }
199
bg_jack_open_client(jack_t * priv,int output,int (* process)(jack_nframes_t,void *))200 int bg_jack_open_client(jack_t * priv, int output,
201 int (*process)(jack_nframes_t, void *))
202 {
203 priv->client = jack_client_open((output ?
204 "gmerlin-output" : "gmerlin-input"),
205 0, NULL);
206
207 if(!priv->client)
208 return 0;
209 /* Set callbacks */
210 jack_set_process_callback(priv->client, process, priv);
211 jack_on_shutdown(priv->client, jack_shutdown, priv);
212
213 priv->samples_per_frame = jack_get_buffer_size(priv->client);
214 priv->samplerate = jack_get_sample_rate(priv->client);
215
216 create_channel_map(priv, output);
217
218 if(jack_activate(priv->client))
219 return 0;
220
221 pthread_mutex_lock(&priv->running_mutex);
222 priv->running = 1;
223 pthread_mutex_unlock(&priv->running_mutex);
224
225 if(priv->connect_ports)
226 {
227 connect_ports(priv, output);
228 }
229
230 return 1;
231 }
232
destroy_channel_map(jack_t * priv)233 static void destroy_channel_map(jack_t * priv)
234 {
235 int i;
236 for(i = 0; i < priv->num_ports; i++)
237 {
238 jack_port_unregister(priv->client,
239 priv->ports[i].int_port);
240 jack_ringbuffer_free(priv->ports[i].buffer);
241 }
242
243 if(priv->ports)
244 free(priv->ports);
245 if(priv->ext_ports)
246 free(priv->ext_ports);
247
248 }
249
250
bg_jack_close_client(jack_t * priv)251 int bg_jack_close_client(jack_t * priv)
252 {
253 int running;
254 gavl_time_t delay_time;
255 delay_time = GAVL_TIME_SCALE / 100; /* 10 ms */
256
257 jack_deactivate(priv->client);
258
259 while(1)
260 {
261 pthread_mutex_lock(&priv->running_mutex);
262 running = priv->running;
263 pthread_mutex_unlock(&priv->running_mutex);
264
265 if(running)
266 gavl_time_delay(&delay_time);
267 else
268 break;
269 }
270 destroy_channel_map(priv);
271 return 1;
272 }
273
bg_jack_destroy(void * p)274 void bg_jack_destroy(void * p)
275 {
276 jack_t * priv = p;
277
278 /* Stop thread and close client connection */
279 if(priv->client)
280 bg_jack_close_client(priv);
281
282 pthread_mutex_destroy(&priv->running_mutex);
283 pthread_mutex_destroy(&priv->active_mutex);
284
285 free(priv);
286 }
287