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/>.
28 #define PROGRAM_NAME "njackspa"
29 #define CON_NAME "Controls"
31 #define HELP "'q'uit, '?', U/D-selection, L/R-change, 'd/D'efault, '>/<', '<n>'0%, 'u/U'pdate, e'x/X'change"
38 #define WCON_H rows - 1
41 #define WHLP_Y rows - 1
45 #define ERR_OUT(format, arg...) ( endwin(), fprintf(stderr, format "\n", ## arg), refresh() )
53 typedef struct _control_t
{
55 /* value in the plugin */
57 /* values selected in the interface */
65 struct { LADSPA_Data fine
; LADSPA_Data coarse
; } inc
;
68 void c_exchange(control_t
*control
)
72 *control
->val
= control
->sel
;
79 control_t
*(*controls
)[];
88 void window_item_next(struct window
* w
) { if (w
->index
< w
->count
- 1) w
->index
++; }
89 void window_item_previous(struct window
* w
) { if (w
->index
> 0) w
->index
--; }
90 void suppress_jack_log(const char *msg
) { ; /* Just suppress Jack SPAM here ;-) */ }
93 int init_control(control_t
*control
, state_t
*state
, int port
)
95 LADSPA_PortRangeHint hint
= state
->descriptor
->PortRangeHints
[port
];
96 LADSPA_PortRangeHintDescriptor descriptor
= hint
.HintDescriptor
;
97 LADSPA_Data lower_bound
= hint
.LowerBound
;
98 LADSPA_Data upper_bound
= hint
.UpperBound
;
100 control
->name
= state
->descriptor
->PortNames
[port
];
101 control
->val
= &state
->control_port_values
[port
];
103 /* control->min, control->max */
104 if (LADSPA_IS_HINT_SAMPLE_RATE(descriptor
)) {
105 int sample_rate
= jack_get_sample_rate(state
->jack_client
);
106 lower_bound
*= sample_rate
;
107 upper_bound
*= sample_rate
;
109 if ( LADSPA_IS_HINT_BOUNDED_BELOW(descriptor
) &&
110 LADSPA_IS_HINT_BOUNDED_ABOVE(descriptor
) )
112 control
->min
= lower_bound
;
113 control
->max
= upper_bound
;
115 else if (LADSPA_IS_HINT_BOUNDED_BELOW(descriptor
)) {
116 control
->min
= lower_bound
;
119 else if (LADSPA_IS_HINT_BOUNDED_ABOVE(descriptor
)) {
121 control
->max
= upper_bound
;
128 /* control->inc & Overrides */
129 if (LADSPA_IS_HINT_TOGGLED(descriptor
)) {
132 control
->inc
.fine
= 1.0;
133 control
->inc
.coarse
= 1.0;
134 control
->type
= VAL_TOGGLE
;
136 else if (LADSPA_IS_HINT_INTEGER(descriptor
)) {
137 control
->inc
.fine
= 1.0;
138 control
->inc
.coarse
= 1.0;
140 control
->type
= VAL_INT
;
143 control
->inc
.fine
= (control
->max
- control
->min
) / 500;
144 control
->inc
.coarse
= (control
->max
- control
->min
) / 50;
146 control
->type
= VAL_FLOAT
;
150 if (LADSPA_IS_HINT_HAS_DEFAULT(descriptor
)) {
151 control
->def
= (LADSPA_Data
*)malloc(sizeof(LADSPA_Data
));
153 return (ERR_OUT ("memory allocation error"), 1);
154 switch (descriptor
& LADSPA_HINT_DEFAULT_MASK
) {
155 case LADSPA_HINT_DEFAULT_MINIMUM
:
156 *control
->def
= lower_bound
;
158 case LADSPA_HINT_DEFAULT_LOW
:
159 *control
->def
= lower_bound
* 0.75 + upper_bound
* 0.25;
161 case LADSPA_HINT_DEFAULT_MIDDLE
:
162 *control
->def
= lower_bound
* 0.5 + upper_bound
* 0.5;
164 case LADSPA_HINT_DEFAULT_HIGH
:
165 *control
->def
= lower_bound
* 0.25 + upper_bound
* 0.75;
167 case LADSPA_HINT_DEFAULT_MAXIMUM
:
168 *control
->def
= upper_bound
;
170 case LADSPA_HINT_DEFAULT_0
:
173 case LADSPA_HINT_DEFAULT_1
:
176 case LADSPA_HINT_DEFAULT_100
:
177 *control
->def
= 100.0;
179 case LADSPA_HINT_DEFAULT_440
:
180 *control
->def
= 440.0;
190 /* control->sel, control->val */
191 if (control
->def
) control
->sel
= *control
->def
;
192 else control
->sel
= control
->min
;
193 control
->alt_sel
= control
->sel
;
194 *control
->val
= control
->sel
;
199 int build_controls(struct window
*window
, state_t
*state
)
204 window
->state
= state
;
205 for (p
= 0, c
= 0; p
< state
->descriptor
->PortCount
; p
++)
206 if ( LADSPA_IS_PORT_INPUT(state
->descriptor
->PortDescriptors
[p
]) &&
207 LADSPA_IS_PORT_CONTROL(state
->descriptor
->PortDescriptors
[p
]) )
211 window
->controls
= (control_t
*(*)[])calloc(sizeof(control_t
*), c
);
212 if (!window
->controls
)
213 return (ERR_OUT ("memory allocation error"), 1);
215 for (p
= 0, c
= 0; p
< state
->descriptor
->PortCount
; p
++)
216 if ( LADSPA_IS_PORT_INPUT(state
->descriptor
->PortDescriptors
[p
]) &&
217 LADSPA_IS_PORT_CONTROL(state
->descriptor
->PortDescriptors
[p
]) )
219 (*window
->controls
)[c
] = (control_t
*)malloc(sizeof(control_t
));
220 if (!(*window
->controls
)[c
])
221 return (ERR_OUT ("memory allocation error"), 1);
222 if (init_control((*window
->controls
)[c
++], state
, p
))
229 void draw_border(struct window
* window_ptr
)
231 int col
= ( window_ptr
->width
- strlen(window_ptr
->name
) -
232 strlen(window_ptr
->state
->descriptor
->Label
) - 5 ) / 2;
233 if (col
< 0) col
= 0;
235 /* 0, 0 gives default characters for the vertical and horizontal lines */
236 box(window_ptr
->window_ptr
, 0, 0);
238 if (window_ptr
->selected
) {
239 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
240 mvwprintw( window_ptr
->window_ptr
, 0, col
, "=[%s:%s]=",
241 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
242 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
245 mvwprintw( window_ptr
->window_ptr
, 0, col
, " [%s:%s] ",
246 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
249 void draw_controls(struct window
* window_ptr
)
251 unsigned short row
, col
, color
, high
, rows
, cols
;
257 getmaxyx(window_ptr
->window_ptr
, rows
, cols
);
258 offset
= (long)window_ptr
->index
+ 3 - rows
; /* first displayed index */
259 if (offset
< 0) offset
= 0;
261 for (c
= offset
, row
= 1; c
< window_ptr
->count
; c
++, row
++) {
262 control
= (*window_ptr
->controls
)[c
];
263 high
= (row
== window_ptr
->index
- offset
+ 1) ?
264 (window_ptr
->selected
) ? 3 : 2 : 1;
265 color
= (row
== window_ptr
->index
- offset
+ 1) ?
266 (window_ptr
->selected
) ? 4 : 2 : 1;
268 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
269 snprintf(fmt
, sizeof(fmt
), "%%-%d.%ds", cols
/3 - 1, cols
/3 -1);
270 mvwprintw(window_ptr
->window_ptr
, row
, col
, fmt
, control
->name
);
271 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
272 wprintw(window_ptr
->window_ptr
, ": ");
273 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
274 snprintf(fmt
, sizeof(fmt
), "%%%dF", cols
/6 - 2);
275 wprintw(window_ptr
->window_ptr
, fmt
, *control
->val
);
276 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
277 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
278 wprintw( window_ptr
->window_ptr
, " %s ",
279 (control
->type
== VAL_TOGGLE
) ?
280 "?" : (control
->type
== VAL_INT
) ? "i" : "f" );
281 wattron(window_ptr
->window_ptr
, COLOR_PAIR(high
));
282 snprintf(fmt
, sizeof(fmt
), "%%-%dF", cols
/6 - 2);
283 wprintw(window_ptr
->window_ptr
, fmt
, control
->sel
);
284 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
285 snprintf(fmt
, sizeof(fmt
), " [%%-%dF~%%%dF]", cols
/6 - 2, cols
/6 - 2);
286 wprintw(window_ptr
->window_ptr
, fmt
, control
->min
, control
->max
);
287 wattroff(window_ptr
->window_ptr
, COLOR_PAIR(1));
289 wclrtoeol(window_ptr
->window_ptr
);
291 draw_border(window_ptr
);
292 wrefresh(window_ptr
->window_ptr
);
296 create_window(struct window
* window_ptr
, int height
, int width
,
297 int starty
, int startx
, const char * name
)
299 window_ptr
->window_ptr
= newwin(height
, width
, starty
, startx
);
300 window_ptr
->selected
= FALSE
;
301 window_ptr
->width
= width
;
302 window_ptr
->height
= height
;
303 window_ptr
->name
= name
;
304 window_ptr
->index
= 0;
305 window_ptr
->controls
= NULL
;
306 window_ptr
->count
= 0;
307 scrollok(window_ptr
->window_ptr
, FALSE
);
311 resize_window(struct window
* window_ptr
, int height
, int width
, int starty
, int startx
)
313 /* delwin(window_ptr->window_ptr); */
314 /* window_ptr->window_ptr = newwin(height, width, starty, startx); */
315 wresize(window_ptr
->window_ptr
, height
, width
);
316 mvwin(window_ptr
->window_ptr
, starty
, startx
);
317 window_ptr
->width
= width
;
318 window_ptr
->height
= height
;
321 void cleanup_controls(struct window
* windows
)
325 struct window
* w
= windows
;
327 for (i
= 0; i
< NUM_WINDOWS
; i
++, w
++) {
328 for (c
= 0; c
< w
->count
; c
++)
329 free((*w
->controls
)[c
]);
335 select_window(struct window
* windows
, int cur
, int nex
)
339 else if (nex
>= NUM_WINDOWS
)
342 nex
= NUM_WINDOWS
- 1;
344 windows
[cur
].selected
= FALSE
;
345 windows
[nex
].selected
= TRUE
;
349 void draw_help(WINDOW
* w
, int c
, const char* msg
, float dsp_load
, bool rt
)
357 wattron(w
, COLOR_PAIR(c
));
358 mvwprintw(w
, 0, 1, msg
);
359 wattroff(w
, COLOR_PAIR(c
));
361 wattron(w
, COLOR_PAIR(7));
362 mvwprintw(w
, 0, cols
-12, "DSP:%4.2f%s", dsp_load
, rt
? "@RT" : "!RT" );
363 wattroff(w
, COLOR_PAIR(7));
368 #define cl ((*C)[W->index])
370 int main(int argc
, char *argv
[])
372 unsigned short rc
= 0, rows
, cols
, window_selection
= 0;
373 struct window windows
[NUM_WINDOWS
];
379 const char *err_message
= NULL
;
380 bool want_refresh
= FALSE
, help
= TRUE
;
384 /* Initialize ncurses */
386 curs_set(0); /* set cursor invisible */
388 getmaxyx(stdscr
, rows
, cols
);
390 if (has_colors() == FALSE
) {
391 rc
= -1, ERR_OUT("Your terminal does not support color");
395 init_pair(1, COLOR_CYAN
, COLOR_BLACK
);
396 init_pair(2, COLOR_BLACK
, COLOR_WHITE
);
397 init_pair(3, COLOR_BLACK
, COLOR_GREEN
);
398 init_pair(4, COLOR_WHITE
, COLOR_BLACK
);
399 init_pair(5, COLOR_BLACK
, COLOR_RED
);
400 init_pair(6, COLOR_YELLOW
, COLOR_BLACK
);
401 init_pair(7, COLOR_BLUE
, COLOR_BLACK
);
403 /* Create Help Window */
404 help_window
= newwin(WHLP_H
, WHLP_W
, WHLP_Y
, WHLP_X
);
405 keypad(help_window
, TRUE
);
406 wtimeout(help_window
, 3000);
408 /* Some Jack versions are very aggressive in breaking view */
409 jack_set_info_function(suppress_jack_log
);
410 jack_set_error_function(suppress_jack_log
);
412 /* Initialize jack */
415 if (!jackspa_init(&state
, argc
, argv
)) {
420 rt
= (bool)jack_is_realtime(state
.jack_client
);
423 create_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
, CON_NAME
);
424 windows
[window_selection
].selected
= TRUE
;
427 if (build_controls(windows
+0, &state
)) {
433 for (i
= 0; i
< NUM_WINDOWS
; i
++) draw_controls(windows
+i
);
434 W
= &windows
[window_selection
];
438 draw_help(help_window
, 5, err_message
, jack_cpu_load(state
.jack_client
), rt
);
442 draw_help(help_window
, 6, help
? HELP
: W
->state
->descriptor
->Name
,
443 jack_cpu_load(state
.jack_client
), rt
);
445 switch (k
= wgetch(help_window
)) {
447 window_selection
= select_window(windows
, window_selection
, window_selection
+1);
450 window_selection
= select_window(windows
, window_selection
, window_selection
-1);
457 getmaxyx(stdscr
, rows
, cols
);
458 wresize(help_window
, WHLP_H
, WHLP_W
);
459 mvwin(help_window
, WHLP_Y
, WHLP_X
);
460 resize_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
);
466 for (c
= 0; c
< W
->count
; c
++)
467 *(*C
)[c
]->val
= (*C
)[c
]->sel
;
473 for (c
= 0; c
< W
->count
; c
++)
474 c_exchange((*W
->controls
)[c
]);
478 if (cl
->def
) cl
->sel
= *cl
->def
;
479 if (k
== KEY_BACKSPACE
) window_item_next(W
);
482 for (c
= 0; c
< W
->count
; c
++)
483 if ((*C
)[c
]->def
) (*C
)[c
]->sel
= *(*C
)[c
]->def
;
487 cl
->sel
-= cl
->inc
.coarse
;
488 if (cl
->sel
< cl
->min
) cl
->sel
= cl
->min
;
492 cl
->sel
+= cl
->inc
.coarse
;
493 if (cl
->sel
> cl
->max
) cl
->sel
= cl
->max
;
496 cl
->sel
-= cl
->inc
.fine
;
497 if (cl
->sel
< cl
->min
) cl
->sel
= cl
->min
;
500 cl
->sel
+= cl
->inc
.fine
;
501 if (cl
->sel
> cl
->max
) cl
->sel
= cl
->max
;
518 val
= ((LADSPA_Data
)(k
- 48)) / 10.0;
519 cl
->sel
= (1.0 - val
) * cl
->min
+ val
* cl
->max
;
520 if (cl
->type
== VAL_INT
|| cl
->type
== VAL_TOGGLE
)
521 cl
->sel
= nearbyintf(cl
->sel
);
529 window_item_previous(W
);
533 (windows
+window_selection
)->index
= 0;
537 (windows
+window_selection
)->index
= (windows
+window_selection
)->count
- 1;
542 if (!want_refresh
) goto loop
;
544 want_refresh
= FALSE
;
545 for (i
= 0; i
< NUM_WINDOWS
; i
++) {
546 if (windows
[i
].index
> windows
[i
].count
- 1) windows
[i
].index
= 0;
547 wclear(windows
[i
].window_ptr
);
552 cleanup_controls(windows
);
554 /* jackspa_exit(state); */
555 jack_deactivate(state
.jack_client
);
556 jack_client_close(state
.jack_client
);