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"
32 #define ERR_NODEFAULT "No default value"
39 #define WCON_H rows - 1
42 #define WHLP_Y rows - 1
46 #define ERR_OUT(format, arg...) ( endwin(), fprintf(stderr, format "\n", ## arg), refresh() )
54 typedef struct _control_t
{
56 /* value in the plugin */
58 /* values selected in the interface */
66 struct { LADSPA_Data fine
; LADSPA_Data coarse
; } inc
;
69 void c_exchange(control_t
*control
)
73 *control
->val
= control
->sel
;
80 control_t
*(*controls
)[];
89 void window_item_next(struct window
* w
) { if (w
->index
< w
->count
- 1) w
->index
++; }
90 void window_item_previous(struct window
* w
) { if (w
->index
> 0) w
->index
--; }
91 void suppress_jack_log(const char *msg
) { ; /* Just suppress Jack SPAM here ;-) */ }
94 int init_control(control_t
*control
, state_t
*state
, int port
)
96 LADSPA_PortRangeHint hint
= state
->descriptor
->PortRangeHints
[port
];
97 LADSPA_PortRangeHintDescriptor descriptor
= hint
.HintDescriptor
;
98 LADSPA_Data lower_bound
= hint
.LowerBound
;
99 LADSPA_Data upper_bound
= hint
.UpperBound
;
101 control
->name
= state
->descriptor
->PortNames
[port
];
102 control
->val
= &state
->control_port_values
[port
];
104 /* control->min, control->max */
105 if (LADSPA_IS_HINT_SAMPLE_RATE(descriptor
)) {
106 int sample_rate
= jack_get_sample_rate(state
->jack_client
);
107 lower_bound
*= sample_rate
;
108 upper_bound
*= sample_rate
;
110 if ( LADSPA_IS_HINT_BOUNDED_BELOW(descriptor
) &&
111 LADSPA_IS_HINT_BOUNDED_ABOVE(descriptor
) )
113 control
->min
= lower_bound
;
114 control
->max
= upper_bound
;
116 else if (LADSPA_IS_HINT_BOUNDED_BELOW(descriptor
)) {
117 control
->min
= lower_bound
;
120 else if (LADSPA_IS_HINT_BOUNDED_ABOVE(descriptor
)) {
122 control
->max
= upper_bound
;
130 if (LADSPA_IS_HINT_HAS_DEFAULT(descriptor
)) {
131 control
->def
= (LADSPA_Data
*)malloc(sizeof(LADSPA_Data
));
133 return (ERR_OUT ("memory allocation error"), 1);
134 switch (descriptor
& LADSPA_HINT_DEFAULT_MASK
) {
135 case LADSPA_HINT_DEFAULT_MINIMUM
:
136 *control
->def
= lower_bound
;
138 case LADSPA_HINT_DEFAULT_LOW
:
139 *control
->def
= lower_bound
* 0.75 + upper_bound
* 0.25;
141 case LADSPA_HINT_DEFAULT_MIDDLE
:
142 *control
->def
= lower_bound
* 0.5 + upper_bound
* 0.5;
144 case LADSPA_HINT_DEFAULT_HIGH
:
145 *control
->def
= lower_bound
* 0.25 + upper_bound
* 0.75;
147 case LADSPA_HINT_DEFAULT_MAXIMUM
:
148 *control
->def
= upper_bound
;
150 case LADSPA_HINT_DEFAULT_0
:
153 case LADSPA_HINT_DEFAULT_1
:
156 case LADSPA_HINT_DEFAULT_100
:
157 *control
->def
= 100.0;
159 case LADSPA_HINT_DEFAULT_440
:
160 *control
->def
= 440.0;
163 ERR_OUT ("default not found");
171 /* Check the default */
173 if (*control
->def
< control
->min
) {
174 ERR_OUT ("default smaller than the minimum");
175 *control
->def
= control
->min
;
177 if (*control
->def
> control
->max
) {
178 ERR_OUT ("default greater than the maximum");
179 *control
->def
= control
->max
;
183 /* control->inc & Overrides */
184 if (LADSPA_IS_HINT_TOGGLED(descriptor
)) {
187 control
->inc
.fine
= 1.0;
188 control
->inc
.coarse
= 1.0;
189 control
->type
= VAL_TOGGLE
;
190 if (control
->def
) *control
->def
= nearbyintf(*control
->def
);
192 else if (LADSPA_IS_HINT_INTEGER(descriptor
)) {
193 control
->min
= nearbyintf(control
->min
);
194 control
->max
= nearbyintf(control
->max
);
195 control
->inc
.fine
= 1.0;
196 control
->inc
.coarse
= 1.0;
198 control
->type
= VAL_INT
;
199 if (control
->def
) *control
->def
= nearbyintf(*control
->def
);
202 control
->inc
.fine
= (control
->max
- control
->min
) / 500;
203 control
->inc
.coarse
= (control
->max
- control
->min
) / 50;
205 control
->type
= VAL_FLOAT
;
208 /* control->sel, control->val */
209 if (control
->def
) control
->sel
= *control
->def
;
210 else control
->sel
= control
->min
;
211 control
->alt_sel
= control
->sel
;
212 *control
->val
= control
->sel
;
217 int build_controls(struct window
*window
, state_t
*state
)
222 window
->state
= state
;
223 for (p
= 0, c
= 0; p
< state
->descriptor
->PortCount
; p
++)
224 if ( LADSPA_IS_PORT_INPUT(state
->descriptor
->PortDescriptors
[p
]) &&
225 LADSPA_IS_PORT_CONTROL(state
->descriptor
->PortDescriptors
[p
]) )
229 window
->controls
= (control_t
*(*)[])calloc(sizeof(control_t
*), c
);
230 if (!window
->controls
)
231 return (ERR_OUT ("memory allocation error"), 1);
233 for (p
= 0, c
= 0; p
< state
->descriptor
->PortCount
; p
++)
234 if ( LADSPA_IS_PORT_INPUT(state
->descriptor
->PortDescriptors
[p
]) &&
235 LADSPA_IS_PORT_CONTROL(state
->descriptor
->PortDescriptors
[p
]) )
237 (*window
->controls
)[c
] = (control_t
*)malloc(sizeof(control_t
));
238 if (!(*window
->controls
)[c
])
239 return (ERR_OUT ("memory allocation error"), 1);
240 if (init_control((*window
->controls
)[c
++], state
, p
))
247 void draw_border(struct window
* window_ptr
)
249 int col
= ( window_ptr
->width
- strlen(window_ptr
->name
) -
250 strlen(window_ptr
->state
->descriptor
->Label
) - 5 ) / 2;
251 if (col
< 0) col
= 0;
253 /* 0, 0 gives default characters for the vertical and horizontal lines */
254 box(window_ptr
->window_ptr
, 0, 0);
256 if (window_ptr
->selected
) {
257 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
258 mvwprintw( window_ptr
->window_ptr
, 0, col
, "=[%s:%s]=",
259 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
260 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
263 mvwprintw( window_ptr
->window_ptr
, 0, col
, " [%s:%s] ",
264 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
267 void draw_controls(struct window
* window_ptr
)
269 unsigned short row
, col
, color
, high
, rows
, cols
;
275 getmaxyx(window_ptr
->window_ptr
, rows
, cols
);
276 offset
= (long)window_ptr
->index
+ 3 - rows
; /* first displayed index */
277 if (offset
< 0) offset
= 0;
279 for (c
= offset
, row
= 1; c
< window_ptr
->count
; c
++, row
++) {
280 control
= (*window_ptr
->controls
)[c
];
281 high
= (row
== window_ptr
->index
- offset
+ 1) ?
282 (window_ptr
->selected
) ? 3 : 2 : 1;
283 color
= (row
== window_ptr
->index
- offset
+ 1) ?
284 (window_ptr
->selected
) ? 4 : 2 : 1;
286 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
287 snprintf(fmt
, sizeof(fmt
), "%%-%d.%ds", cols
/3 - 1, cols
/3 -1);
288 mvwprintw(window_ptr
->window_ptr
, row
, col
, fmt
, control
->name
);
289 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
290 wprintw(window_ptr
->window_ptr
, ": ");
291 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
292 snprintf(fmt
, sizeof(fmt
), "%%%dF", cols
/6 - 2);
293 wprintw(window_ptr
->window_ptr
, fmt
, *control
->val
);
294 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
295 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
296 wprintw( window_ptr
->window_ptr
, " %s ",
297 (control
->type
== VAL_TOGGLE
) ?
298 "?" : (control
->type
== VAL_INT
) ? "i" : "f" );
299 wattron(window_ptr
->window_ptr
, COLOR_PAIR(high
));
300 snprintf(fmt
, sizeof(fmt
), "%%-%dF", cols
/6 - 2);
301 wprintw(window_ptr
->window_ptr
, fmt
, control
->sel
);
302 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
303 snprintf(fmt
, sizeof(fmt
), " [%%-%dF~%%%dF]", cols
/6 - 2, cols
/6 - 2);
304 wprintw(window_ptr
->window_ptr
, fmt
, control
->min
, control
->max
);
305 wattroff(window_ptr
->window_ptr
, COLOR_PAIR(1));
307 wclrtoeol(window_ptr
->window_ptr
);
309 draw_border(window_ptr
);
310 wrefresh(window_ptr
->window_ptr
);
314 create_window(struct window
* window_ptr
, int height
, int width
,
315 int starty
, int startx
, const char * name
)
317 window_ptr
->window_ptr
= newwin(height
, width
, starty
, startx
);
318 window_ptr
->selected
= FALSE
;
319 window_ptr
->width
= width
;
320 window_ptr
->height
= height
;
321 window_ptr
->name
= name
;
322 window_ptr
->index
= 0;
323 window_ptr
->controls
= NULL
;
324 window_ptr
->count
= 0;
325 scrollok(window_ptr
->window_ptr
, FALSE
);
329 resize_window(struct window
* window_ptr
, int height
, int width
, int starty
, int startx
)
331 /* delwin(window_ptr->window_ptr); */
332 /* window_ptr->window_ptr = newwin(height, width, starty, startx); */
333 wresize(window_ptr
->window_ptr
, height
, width
);
334 mvwin(window_ptr
->window_ptr
, starty
, startx
);
335 window_ptr
->width
= width
;
336 window_ptr
->height
= height
;
339 void cleanup_controls(struct window
* windows
)
343 struct window
* w
= windows
;
345 for (i
= 0; i
< NUM_WINDOWS
; i
++, w
++) {
346 for (c
= 0; c
< w
->count
; c
++)
347 free((*w
->controls
)[c
]);
353 select_window(struct window
* windows
, int cur
, int nex
)
357 else if (nex
>= NUM_WINDOWS
)
360 nex
= NUM_WINDOWS
- 1;
362 windows
[cur
].selected
= FALSE
;
363 windows
[nex
].selected
= TRUE
;
367 void draw_help(WINDOW
* w
, int c
, const char* msg
, float dsp_load
, bool rt
)
375 wattron(w
, COLOR_PAIR(c
));
376 mvwprintw(w
, 0, 1, msg
);
377 wattroff(w
, COLOR_PAIR(c
));
379 wattron(w
, COLOR_PAIR(7));
380 mvwprintw(w
, 0, cols
-12, "DSP:%4.2f%s", dsp_load
, rt
? "@RT" : "!RT" );
381 wattroff(w
, COLOR_PAIR(7));
386 #define cl ((*C)[W->index])
388 int main(int argc
, char *argv
[])
390 unsigned short rc
= 0, rows
, cols
, window_selection
= 0;
391 struct window windows
[NUM_WINDOWS
];
397 const char *err_message
= NULL
;
398 bool want_refresh
= FALSE
, help
= TRUE
;
402 /* Initialize ncurses */
404 curs_set(0); /* set cursor invisible */
406 getmaxyx(stdscr
, rows
, cols
);
408 if (has_colors() == FALSE
) {
409 rc
= -1, ERR_OUT("Your terminal does not support color");
413 init_pair(1, COLOR_CYAN
, COLOR_BLACK
);
414 init_pair(2, COLOR_BLACK
, COLOR_WHITE
);
415 init_pair(3, COLOR_BLACK
, COLOR_GREEN
);
416 init_pair(4, COLOR_WHITE
, COLOR_BLACK
);
417 init_pair(5, COLOR_BLACK
, COLOR_RED
);
418 init_pair(6, COLOR_YELLOW
, COLOR_BLACK
);
419 init_pair(7, COLOR_BLUE
, COLOR_BLACK
);
421 /* Create Help Window */
422 help_window
= newwin(WHLP_H
, WHLP_W
, WHLP_Y
, WHLP_X
);
423 keypad(help_window
, TRUE
);
424 wtimeout(help_window
, 3000);
426 /* Some Jack versions are very aggressive in breaking view */
427 jack_set_info_function(suppress_jack_log
);
428 jack_set_error_function(suppress_jack_log
);
430 /* Initialize jack */
433 if (!jackspa_init(&state
, argc
, argv
)) {
438 rt
= (bool)jack_is_realtime(state
.jack_client
);
441 create_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
, CON_NAME
);
442 windows
[window_selection
].selected
= TRUE
;
445 if (build_controls(windows
+0, &state
)) {
451 for (i
= 0; i
< NUM_WINDOWS
; i
++) draw_controls(windows
+i
);
452 W
= &windows
[window_selection
];
456 draw_help(help_window
, 5, err_message
, jack_cpu_load(state
.jack_client
), rt
);
460 draw_help(help_window
, 6, help
? HELP
: W
->state
->descriptor
->Name
,
461 jack_cpu_load(state
.jack_client
), rt
);
463 switch (k
= wgetch(help_window
)) {
465 window_selection
= select_window(windows
, window_selection
, window_selection
+1);
468 window_selection
= select_window(windows
, window_selection
, window_selection
-1);
475 getmaxyx(stdscr
, rows
, cols
);
476 wresize(help_window
, WHLP_H
, WHLP_W
);
477 mvwin(help_window
, WHLP_Y
, WHLP_X
);
478 resize_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
);
484 for (c
= 0; c
< W
->count
; c
++)
485 *(*C
)[c
]->val
= (*C
)[c
]->sel
;
491 for (c
= 0; c
< W
->count
; c
++)
492 c_exchange((*W
->controls
)[c
]);
496 if (cl
->def
) cl
->sel
= *cl
->def
;
497 else err_message
= ERR_NODEFAULT
;
498 if (k
== KEY_BACKSPACE
) window_item_next(W
);
501 for (c
= 0; c
< W
->count
; c
++)
502 if ((*C
)[c
]->def
) (*C
)[c
]->sel
= *(*C
)[c
]->def
;
503 else err_message
= ERR_NODEFAULT
;
507 cl
->sel
-= cl
->inc
.coarse
;
508 if (cl
->sel
< cl
->min
) cl
->sel
= cl
->min
;
512 cl
->sel
+= cl
->inc
.coarse
;
513 if (cl
->sel
> cl
->max
) cl
->sel
= cl
->max
;
516 cl
->sel
-= cl
->inc
.fine
;
517 if (cl
->sel
< cl
->min
) cl
->sel
= cl
->min
;
520 cl
->sel
+= cl
->inc
.fine
;
521 if (cl
->sel
> cl
->max
) cl
->sel
= cl
->max
;
538 val
= ((LADSPA_Data
)(k
- 48)) / 10.0;
539 cl
->sel
= (1.0 - val
) * cl
->min
+ val
* cl
->max
;
540 if (cl
->type
== VAL_INT
|| cl
->type
== VAL_TOGGLE
)
541 cl
->sel
= nearbyintf(cl
->sel
);
549 window_item_previous(W
);
553 (windows
+window_selection
)->index
= 0;
557 (windows
+window_selection
)->index
= (windows
+window_selection
)->count
- 1;
562 if (!want_refresh
) goto loop
;
564 want_refresh
= FALSE
;
565 for (i
= 0; i
< NUM_WINDOWS
; i
++) {
566 if (windows
[i
].index
> windows
[i
].count
- 1) windows
[i
].index
= 0;
567 wclear(windows
[i
].window_ptr
);
572 cleanup_controls(windows
);
574 /* jackspa_exit(state); */
575 jack_deactivate(state
.jack_client
);
576 jack_client_close(state
.jack_client
);