1 /* njackspa.c - simple ncurses LADSPA host for the Jack Audio Connection Kit
2 * based on njconnect.c by Xj <xj@wp.pl>
3 * Copyright © 2012,2013 Xj <xj@wp.pl>
4 * Copyright © 2013 Géraud Meyer <graud@gmx.com>
6 * This file is part if ng-jackspa.
8 * ng-jackspa is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License version 2 as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along
18 * with ng-jackspa. If not, see <http://www.gnu.org/licenses/>.
27 #define PROGRAM_NAME "njackspa"
28 #define CON_NAME_A "Values"
29 #define CON_NAME_M "Switches"
31 #define ERR_UPDATE "Connection failed"
32 #define ERR_DISCONNECT "Disconnection failed"
33 #define HELP "'q'uit, U/D-selection, L/R-change, 'd/D'efault, '<n>'0%, 'u/U'pdate, e'x/X'change"
37 #define WOUT_W cols / 2
38 #define WOUT_H rows / 2
40 #define WIN_X cols / 2
42 #define WIN_W cols - cols / 2
43 #define WIN_H rows / 2
46 #define WCON_Y rows / 2
48 #define WCON_H rows - rows / 2 - 1
51 #define WHLP_Y rows - 1
55 #define MSG_OUT(format, arg...) printf(format "\n", ## arg)
56 #define ERR_OUT(format, arg...) fprintf(stderr, format "\n", ## arg)
69 typedef struct _control_t
{
71 /* value in the plugin */
73 /* values selected in the interface */
81 struct { LADSPA_Data fine
; LADSPA_Data coarse
; } inc
;
88 control_t
*(*controls
)[];
99 void window_item_next(struct window
* w
) { if (w
->index
< w
->count
- 1) w
->index
++; }
100 void window_item_previous(struct window
* w
) { if (w
->index
> 0) w
->index
--; }
101 void suppress_jack_log(const char *msg
) { ; /* Just suppress Jack SPAM here ;-) */ }
103 int init_control(control_t
*control
, state_t
*state
, int port
)
105 LADSPA_PortRangeHint hint
= state
->descriptor
->PortRangeHints
[port
];
106 LADSPA_PortRangeHintDescriptor descriptor
= hint
.HintDescriptor
;
107 LADSPA_Data lower_bound
= hint
.LowerBound
;
108 LADSPA_Data upper_bound
= hint
.UpperBound
;
110 control
->name
= state
->descriptor
->PortNames
[port
];
111 control
->val
= &state
->control_port_values
[port
];
113 if (LADSPA_IS_HINT_SAMPLE_RATE(descriptor
)) {
114 int sample_rate
= jack_get_sample_rate(state
->jack_client
);
115 lower_bound
*= sample_rate
;
116 upper_bound
*= sample_rate
;
118 if ( LADSPA_IS_HINT_BOUNDED_BELOW(descriptor
) &&
119 LADSPA_IS_HINT_BOUNDED_ABOVE(descriptor
) )
121 control
->min
= lower_bound
;
122 control
->max
= upper_bound
;
124 else if (LADSPA_IS_HINT_BOUNDED_BELOW(descriptor
)) {
125 control
->min
= lower_bound
;
128 else if (LADSPA_IS_HINT_BOUNDED_ABOVE(descriptor
)) {
130 control
->max
= upper_bound
;
137 if (LADSPA_IS_HINT_TOGGLED(descriptor
)) {
140 control
->inc
.fine
= 1.0;
141 control
->inc
.coarse
= 1.0;
142 control
->type
= VAL_TOGGLE
;
144 else if (LADSPA_IS_HINT_INTEGER(descriptor
)) {
145 control
->inc
.fine
= 1.0;
146 control
->inc
.coarse
= 1.0;
148 control
->type
= VAL_INT
;
151 control
->inc
.fine
= 0.05;
152 control
->inc
.coarse
= 0.1;
154 control
->type
= VAL_FLOAT
;
157 control
->sel
= control
->min
;
158 if (LADSPA_IS_HINT_HAS_DEFAULT(descriptor
)) {
159 control
->def
= (LADSPA_Data
*)malloc(sizeof(LADSPA_Data
));
161 ERR_OUT ("memory allocation error");
164 switch (descriptor
& LADSPA_HINT_DEFAULT_MASK
) {
165 case LADSPA_HINT_DEFAULT_MINIMUM
:
166 *control
->def
= lower_bound
;
168 case LADSPA_HINT_DEFAULT_LOW
:
169 *control
->def
= lower_bound
* 0.75 + upper_bound
* 0.25;
171 case LADSPA_HINT_DEFAULT_MIDDLE
:
172 *control
->def
= lower_bound
* 0.5 + upper_bound
* 0.5;
174 case LADSPA_HINT_DEFAULT_HIGH
:
175 *control
->def
= lower_bound
* 0.25 + upper_bound
* 0.75;
177 case LADSPA_HINT_DEFAULT_MAXIMUM
:
178 *control
->def
= upper_bound
;
180 case LADSPA_HINT_DEFAULT_0
:
183 case LADSPA_HINT_DEFAULT_1
:
186 case LADSPA_HINT_DEFAULT_100
:
187 *control
->def
= 100.0;
189 case LADSPA_HINT_DEFAULT_440
:
190 *control
->def
= 440.0;
196 control
->sel
= *control
->def
;
201 control
->alt_sel
= control
->sel
;
202 *control
->val
= control
->sel
;
207 int build_controls(struct window
*window
, state_t
*state
)
212 window
->state
= state
;
213 for (p
= 0, c
= 0; p
< state
->descriptor
->PortCount
; p
++)
214 if ( LADSPA_IS_PORT_INPUT(state
->descriptor
->PortDescriptors
[p
]) &&
215 LADSPA_IS_PORT_CONTROL(state
->descriptor
->PortDescriptors
[p
]) )
219 window
->controls
= (control_t
*(*)[])calloc(sizeof(control_t
*), c
);
220 if (!window
->controls
) {
221 ERR_OUT ("memory allocation error");
225 for (p
= 0, c
= 0; p
< state
->descriptor
->PortCount
; p
++)
226 if ( LADSPA_IS_PORT_INPUT(state
->descriptor
->PortDescriptors
[p
]) &&
227 LADSPA_IS_PORT_CONTROL(state
->descriptor
->PortDescriptors
[p
]) )
229 (*window
->controls
)[c
] = (control_t
*)malloc(sizeof(control_t
));
230 if (!(*window
->controls
)[c
]) {
231 ERR_OUT ("memory allocation error");
234 if (init_control((*window
->controls
)[c
++], state
, p
))
241 void draw_border(struct window
* window_ptr
)
243 int col
= (window_ptr
->width
- strlen(window_ptr
->name
) - 4)/2;
244 if (col
< 0) col
= 0;
246 /* 0, 0 gives default characters for the vertical and horizontal lines */
247 box(window_ptr
->window_ptr
, 0, 0);
249 if (window_ptr
->selected
) {
250 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
251 mvwprintw( window_ptr
->window_ptr
, 0, col
, "=[%s]=", window_ptr
->name
);
252 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
254 mvwprintw( window_ptr
->window_ptr
, 0, col
, " [%s] ", window_ptr
->name
);
258 void draw_controls(struct window
* window_ptr
)
260 unsigned short row
, col
, color
, rows
, cols
;
267 getmaxyx(window_ptr
->window_ptr
, rows
, cols
);
268 offset
= (long)window_ptr
->index
+ 3 - rows
; // first displayed index
269 if (offset
< 0) offset
= 0;
270 for (c
= offset
; c
< window_ptr
->count
; c
++) {
271 color
= (row
== window_ptr
->index
- offset
+ 1) ? (window_ptr
->selected
) ? 3 : 2 : 1;
272 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
274 switch (window_ptr
->type
) {
277 case WIN_CONNECTIONS
:
278 control
= (*window_ptr
->controls
)[c
];
279 snprintf(fmt
, sizeof(fmt
), "%%-%d.%ds: ", cols
/3 - 2, cols
/3 - 2);
280 mvwprintw(window_ptr
->window_ptr
, row
, col
, fmt
, control
->name
);
281 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
282 snprintf(fmt
, sizeof(fmt
), "%%%dF", cols
/6 - 2);
283 wprintw(window_ptr
->window_ptr
, fmt
, *control
->val
);
284 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
285 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
286 snprintf(fmt
, sizeof(fmt
), " x %%-%dF [%%-%dF %%%dF]", cols
/6 - 2, cols
/6 - 2, cols
/6 - 2);
287 wprintw(window_ptr
->window_ptr
, fmt
,
288 control
->sel
, control
->min
, control
->max
);
291 ERR_OUT("Unknown WinType");
293 wattroff(window_ptr
->window_ptr
, COLOR_PAIR(color
));
294 wclrtoeol(window_ptr
->window_ptr
);
297 draw_border(window_ptr
);
298 wrefresh(window_ptr
->window_ptr
);
302 create_window(struct window
* window_ptr
, int height
, int width
,
303 int starty
, int startx
, const char * name
, enum WinType type
)
305 window_ptr
->window_ptr
= newwin(height
, width
, starty
, startx
);
306 window_ptr
->selected
= FALSE
;
307 window_ptr
->width
= width
;
308 window_ptr
->height
= height
;
309 window_ptr
->name
= name
;
310 window_ptr
->index
= 0;
311 window_ptr
->type
= type
;
312 window_ptr
->controls
= NULL
;
313 window_ptr
->count
= 0;
314 // scrollok(window_ptr->window_ptr, TRUE);
318 resize_window(struct window
* window_ptr
, int height
, int width
, int starty
, int startx
)
320 // delwin(window_ptr->window_ptr);
321 // window_ptr->window_ptr = newwin(height, width, starty, startx);
322 wresize(window_ptr
->window_ptr
, height
, width
);
323 mvwin(window_ptr
->window_ptr
, starty
, startx
);
324 window_ptr
->width
= width
;
325 window_ptr
->height
= height
;
328 void cleanup_controls(struct window
* windows
)
332 struct window
* w
= windows
;
334 for(i
= 0; i
< 3; i
++, w
++)
335 if (w
->type
== WIN_CONNECTIONS
) {
336 for (c
= 0; c
< w
->count
; c
++)
337 free((*w
->controls
)[c
]);
343 select_window(struct window
* windows
, int cur
, int nex
)
352 windows
[cur
].selected
= FALSE
;
353 windows
[nex
].selected
= TRUE
;
357 void draw_help(WINDOW
* w
, int c
, const char* msg
, float dsp_load
, bool rt
)
365 wattron(w
, COLOR_PAIR(c
));
366 mvwprintw(w
, 0, 1, msg
);
367 wattroff(w
, COLOR_PAIR(c
));
369 wattron(w
, COLOR_PAIR(7));
370 mvwprintw(w
, 0, cols
-12, "DSP:%4.2f%s", dsp_load
, rt
? "@RT" : "!RT" );
371 wattroff(w
, COLOR_PAIR(7));
377 int main(int argc
, char *argv
[])
379 unsigned short i
, rc
= 0, rows
, cols
, window_selection
= 0;
380 struct window windows
[3];
382 const char *err_message
= NULL
;
383 const char *PortsType
= JACK_DEFAULT_MIDI_TYPE
;
384 bool want_refresh
= FALSE
;
387 /* Initialize ncurses */
389 curs_set(0); /* set cursor invisible */
391 getmaxyx(stdscr
, rows
, cols
);
393 if (has_colors() == FALSE
) {
394 ERR_OUT("Your terminal does not support color");
399 init_pair(1, COLOR_CYAN
, COLOR_BLACK
);
400 init_pair(2, COLOR_BLACK
, COLOR_WHITE
);
401 init_pair(3, COLOR_BLACK
, COLOR_GREEN
);
402 init_pair(4, COLOR_WHITE
, COLOR_BLACK
);
403 init_pair(5, COLOR_BLACK
, COLOR_RED
);
404 init_pair(6, COLOR_YELLOW
, COLOR_BLACK
);
405 init_pair(7, COLOR_BLUE
, COLOR_BLACK
);
407 /* Create Help Window */
408 help_window
= newwin(WHLP_H
, WHLP_W
, WHLP_Y
, WHLP_X
);
409 keypad(help_window
, TRUE
);
410 wtimeout(help_window
, 3000);
412 /* Some Jack versions are very aggressive in breaking view */
413 jack_set_info_function(suppress_jack_log
);
414 jack_set_error_function(suppress_jack_log
);
416 /* Initialize jack */
419 if (!jackspa_init(&state
, argc
, argv
)) {
424 rt
= (bool)jack_is_realtime(state
.jack_client
);
427 create_window(windows
, WOUT_H
, WOUT_W
, WOUT_Y
, WOUT_Y
, "Output Ports", WIN_PORTS
);
428 create_window(windows
+1, WIN_H
, WIN_W
, WIN_Y
, WIN_X
, "Input Ports", WIN_PORTS
);
429 create_window(windows
+2, WCON_H
, WCON_W
, WCON_Y
, WCON_X
, CON_NAME_M
, WIN_CONNECTIONS
);
430 windows
[window_selection
].selected
= TRUE
;
433 if (build_controls(windows
+2, &state
)) {
439 for (i
= 0; i
< 3; i
++) draw_controls(windows
+i
);
442 draw_help(help_window
, 5, err_message
, jack_cpu_load(state
.jack_client
), rt
);
446 draw_help(help_window
, 6, HELP
, jack_cpu_load(state
.jack_client
), rt
);
448 switch (wgetch(help_window
)) {
450 window_selection
= select_window(windows
, window_selection
, window_selection
+1);
453 window_selection
= select_window(windows
, window_selection
, window_selection
-1);
456 windows
[2].name
= CON_NAME_A
;
457 PortsType
= JACK_DEFAULT_AUDIO_TYPE
;
460 windows
[2].name
= CON_NAME_M
;
461 PortsType
= JACK_DEFAULT_MIDI_TYPE
;
467 getmaxyx(stdscr
, rows
, cols
);
468 wresize(help_window
, WHLP_H
, WHLP_W
);
469 mvwin(help_window
, WHLP_Y
, WHLP_X
);
470 resize_window(windows
, WOUT_H
, WOUT_W
, WOUT_Y
, WOUT_X
);
471 resize_window(windows
+1, WIN_H
, WIN_W
, WIN_Y
, WIN_X
);
472 resize_window(windows
+2, WCON_H
, WCON_W
, WCON_Y
, WCON_X
);
475 err_message
= ERR_UPDATE
;
478 err_message
= ERR_UPDATE
;
481 window_item_next(windows
+window_selection
);
484 window_item_previous(windows
+window_selection
);
487 window_selection
= select_window(windows
, window_selection
, 0);
490 window_selection
= select_window(windows
, window_selection
, 1);
493 if (! want_refresh
) goto loop
;
495 want_refresh
= FALSE
;
497 for(i
= 0; i
< 3; i
++) {
498 if (windows
[i
].index
> windows
[i
].count
- 1) windows
[i
].index
= 0;
499 wclear(windows
[i
].window_ptr
);
504 cleanup_controls(windows
);
506 //jackspa_exit(state);
507 jack_deactivate(state
.jack_client
);
508 jack_client_close(state
.jack_client
);