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/>.
29 #define PROGRAM_NAME "njackspa"
30 #include "interface.c"
33 #define ERR_OUT(format, arg...) ( endwin(), fprintf(stderr, format "\n", ## arg), refresh() )
35 #define CON_NAME "Controls"
36 #define HELP "'q'uit, '?', 'TAB', U/D-selection, L/R-change, 'd/D'efault, '>/<', '<n>'0%, 'u/U'pdate, e'x/X'change, 'i'nput, 'N'arrow"
37 #define ERR_NODEFAULT "No default value"
38 #define ERR_INVALIDVALUE "Invalid value entered"
45 #define WCON_H rows - 1
48 #define WHLP_Y rows - 1
56 unsigned long count
; /* number of controls */
62 enum { Oneline
, Twolines
} layout
; /* state variable */
63 enum { Active
, Alternative
} sel
; /* state variable */
66 void window_item_next(struct window
* w
) { if (w
->index
< w
->count
- 1) w
->index
++; }
67 void window_item_previous(struct window
* w
) { if (w
->index
> 0) w
->index
--; }
68 void suppress_jack_log(const char *msg
) { ; /* Just suppress Jack SPAM here ;-) */ }
71 int init_window(struct window
*window
, state_t
*state
)
75 rc
= control_buildall(&window
->count
, &window
->controls
, state
);
76 window
->state
= state
;
82 void draw_border(struct window
* window_ptr
)
84 int col
= ( window_ptr
->width
- strlen(window_ptr
->name
) -
85 strlen(window_ptr
->state
->descriptor
->Label
) - 5 ) / 2;
88 /* 0, 0 gives default characters for the vertical and horizontal lines */
89 box(window_ptr
->window_ptr
, 0, 0);
91 if (window_ptr
->selected
) {
92 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
93 mvwprintw( window_ptr
->window_ptr
, 0, col
, "=[%s:%s]=",
94 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
95 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
98 mvwprintw( window_ptr
->window_ptr
, 0, col
, " [%s:%s] ",
99 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
102 void draw_controls(struct window
* window_ptr
)
104 int row
, col
, color
, high
, rows
, cols
;
105 /* start position of a control, colors, screen size */
106 unsigned long c
; /* loop variable for controls */
107 long offset
; /* first displayed control index */
109 char fmt
[40]; /* buffer for printf format strings */
110 int nw
, vw
, bw
; /* computed field widths */
111 int height
; /* lines for one control */
113 getmaxyx(window_ptr
->window_ptr
, rows
, cols
);
114 if (window_ptr
->layout
== Oneline
) {
115 nw
= cols
-2 - (cols
/6-2)*4 - 9;
123 vw
= cols
-2 - 2*(cols
/3-2) - 6;
126 offset
= (long)window_ptr
->index
+1 + (2-rows
)/height
;
127 if ((rows
-2)/height
< 1) offset
--;
128 if (offset
< 0) offset
= 0;
131 for (c
= offset
, row
= 1; c
< window_ptr
->count
&& row
< rows
-1; c
++, row
++) {
132 control
= (window_ptr
->controls
)[c
];
133 high
= (row
-1 == (window_ptr
->index
- offset
) * height
) ?
134 (window_ptr
->selected
) ? 3 : 2 : 1;
135 /* currently selected widget (higlighted or normal) */
136 color
= (row
-1 == (window_ptr
->index
- offset
) * height
) ?
137 (window_ptr
->selected
) ? 4 : 2 : 1;
138 /* emphasized widget (white or normal) */
141 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
142 snprintf(fmt
, sizeof(fmt
), "%%-%d.%ds", nw
, nw
);
143 mvwprintw(window_ptr
->window_ptr
, row
, col
, fmt
, control
->name
);
144 /* Separator ':' [2] */
145 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
146 wprintw(window_ptr
->window_ptr
, ": ");
147 /* Active value (bold) [vw] */
148 snprintf(fmt
, sizeof(fmt
), "%%%dF", vw
);
149 if (window_ptr
->sel
== Active
) {
150 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(high
));
151 wprintw(window_ptr
->window_ptr
, fmt
, *control
->val
);
152 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(high
));
155 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
156 wprintw(window_ptr
->window_ptr
, fmt
, *control
->val
);
157 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
159 if (window_ptr
->layout
== Oneline
) {
161 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
162 wprintw( window_ptr
->window_ptr
, " %s ",
163 (control
->type
== JACKSPA_TOGGLE
) ?
164 "?" : (control
->type
== JACKSPA_INT
) ? "i" : "f" );
165 /* Selection (emphasized) [vw] */
166 snprintf(fmt
, sizeof(fmt
), "%%-%dF", vw
);
167 if (window_ptr
->sel
== Active
)
168 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
170 wattron(window_ptr
->window_ptr
, COLOR_PAIR(high
));
171 wprintw(window_ptr
->window_ptr
, fmt
, control
->sel
);
172 /* Range [2*bw + 4] */
173 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
174 snprintf(fmt
, sizeof(fmt
), " [%%-%dF~%%%dF]", bw
, bw
);
175 wprintw(window_ptr
->window_ptr
, fmt
, control
->min
, control
->max
);
176 wattroff(window_ptr
->window_ptr
, COLOR_PAIR(1));
179 wclrtoeol(window_ptr
->window_ptr
);
182 /* Range & Type [2*bw + 6] */
183 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
184 snprintf( fmt
, sizeof(fmt
), " %%-%dF~%s~%%%dF ", bw
,
185 (control
->type
== JACKSPA_TOGGLE
) ?
186 "?" : (control
->type
== JACKSPA_INT
) ? "i" : "f",
188 mvwprintw(window_ptr
->window_ptr
, row
, col
, fmt
, control
->min
, control
->max
);
189 wattroff(window_ptr
->window_ptr
, COLOR_PAIR(1));
190 /* Selection (emphasized) [vw] */
191 snprintf(fmt
, sizeof(fmt
), "%%%dF", vw
);
192 if (window_ptr
->sel
== Active
)
193 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
195 wattron(window_ptr
->window_ptr
, COLOR_PAIR(high
));
196 wprintw(window_ptr
->window_ptr
, fmt
, control
->sel
);
197 wattroff(window_ptr
->window_ptr
, COLOR_PAIR(1));
199 wclrtoeol(window_ptr
->window_ptr
);
201 if (row
< rows
-1 && window_ptr
->layout
!= Oneline
) {
202 /* clear the unused last line */
203 wmove(window_ptr
->window_ptr
, row
, col
);
204 wclrtoeol(window_ptr
->window_ptr
);
207 draw_border(window_ptr
);
208 wrefresh(window_ptr
->window_ptr
);
212 create_window(struct window
* window_ptr
, int height
, int width
,
213 int starty
, int startx
, const char * name
)
215 window_ptr
->window_ptr
= newwin(height
, width
, starty
, startx
);
216 window_ptr
->selected
= FALSE
;
217 window_ptr
->width
= width
;
218 window_ptr
->height
= height
;
219 window_ptr
->name
= name
;
220 window_ptr
->index
= 0;
221 window_ptr
->controls
= NULL
;
222 window_ptr
->count
= 0;
223 window_ptr
->layout
= Oneline
;
224 window_ptr
->sel
= Alternative
;
225 scrollok(window_ptr
->window_ptr
, FALSE
);
229 resize_window(struct window
* window_ptr
, int height
, int width
, int starty
, int startx
)
231 /* delwin(window_ptr->window_ptr); */
232 /* window_ptr->window_ptr = newwin(height, width, starty, startx); */
233 wresize(window_ptr
->window_ptr
, height
, width
);
234 mvwin(window_ptr
->window_ptr
, starty
, startx
);
235 window_ptr
->width
= width
;
236 window_ptr
->height
= height
;
239 void cleanup_windows(struct window
* windows
)
242 struct window
* w
= windows
;
244 for (i
= 0; i
< NUM_WINDOWS
; i
++, w
++)
245 control_cleanupall(w
->count
, &w
->controls
);
249 select_window(struct window
* windows
, int cur
, int nex
)
253 else if (nex
>= NUM_WINDOWS
)
256 nex
= NUM_WINDOWS
- 1;
258 windows
[cur
].selected
= FALSE
;
259 windows
[nex
].selected
= TRUE
;
263 void draw_help(WINDOW
* w
, int c
, const char* msg
, float dsp_load
, bool rt
)
271 wattron(w
, COLOR_PAIR(c
));
272 mvwprintw(w
, 0, 1, msg
);
273 wattroff(w
, COLOR_PAIR(c
));
275 wattron(w
, COLOR_PAIR(7));
276 mvwprintw(w
, 0, cols
-12, "DSP:%4.2f%s", dsp_load
, rt
? "@RT" : "!RT" );
277 wattroff(w
, COLOR_PAIR(7));
282 #define cl ((*C)[W->index])
285 int main(int argc
, char *argv
[])
287 unsigned short rc
= 0; /* return code */
288 int rows
, cols
; /* screen size */
289 struct window windows
[NUM_WINDOWS
];
290 unsigned short window_selection
= 0; /* state variable */
291 struct window
*W
; /* selected window */
292 controls_t
*C
; /* controls of the selected window */
293 LADSPA_Data
*mod
; /* value of the selected window to be acted upon */
294 unsigned long c
; /* loop variable for controls */
296 const char *err_message
= NULL
; /* state variable */
297 bool want_refresh
= FALSE
, help
= TRUE
; /* state variables */
298 bool rt
; /* jack RT capability */
299 int k
, i
; /* current curses character, loop variable */
300 LADSPA_Data val
; /* buffering variable */
301 char input
[40] = { 0 }; /* text input */
303 /* Command line options */
304 GOptionContext
*context
= interface_context();
305 GError
*error
= NULL
;
306 if (!g_option_context_parse(context
, &argc
, &argv
, &error
))
307 return (ERR_OUT("option parsing failed: %s\n", error
->message
), -1);
309 /* Initialize ncurses */
311 curs_set(0); /* set cursor invisible */
314 getmaxyx(stdscr
, rows
, cols
);
316 if (has_colors() == FALSE
) {
317 rc
= -1, ERR_OUT("Your terminal does not support color");
321 init_pair(1, COLOR_CYAN
, COLOR_BLACK
);
322 init_pair(2, COLOR_BLACK
, COLOR_WHITE
);
323 init_pair(3, COLOR_BLACK
, COLOR_GREEN
);
324 init_pair(4, COLOR_WHITE
, COLOR_BLACK
);
325 init_pair(5, COLOR_BLACK
, COLOR_RED
);
326 init_pair(6, COLOR_YELLOW
, COLOR_BLACK
);
327 init_pair(7, COLOR_BLUE
, COLOR_BLACK
);
329 /* Create Help Window */
330 help_window
= newwin(WHLP_H
, WHLP_W
, WHLP_Y
, WHLP_X
);
331 keypad(help_window
, TRUE
);
332 wtimeout(help_window
, 3000);
334 /* Some Jack versions are very aggressive in breaking view */
335 jack_set_info_function(suppress_jack_log
);
336 jack_set_error_function(suppress_jack_log
);
338 /* Initialize jack */
341 if (!jackspa_init(&state
, argc
, argv
)) {
346 rt
= (bool)jack_is_realtime(state
.jack_client
);
349 create_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
, CON_NAME
);
350 windows
[window_selection
].selected
= TRUE
;
354 if (init_window(windows
+0, &state
)) {
361 for (i
= 0; i
< NUM_WINDOWS
; i
++) draw_controls(windows
+i
);
362 W
= &windows
[window_selection
];
364 mod
= W
->sel
== Active
? cl
->val
: &cl
->sel
;
367 draw_help(help_window
, 5, err_message
, jack_cpu_load(state
.jack_client
), rt
);
371 draw_help(help_window
, 6, help
? HELP
: W
->state
->descriptor
->Name
,
372 jack_cpu_load(state
.jack_client
), rt
);
374 switch (k
= wgetch(help_window
)) {
380 getmaxyx(stdscr
, rows
, cols
);
381 wresize(help_window
, WHLP_H
, WHLP_W
);
382 mvwin(help_window
, WHLP_Y
, WHLP_X
);
383 resize_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
);
386 if (W
->sel
== Active
)
392 if (W
->sel
== Active
)
393 for (c
= 0; c
< W
->count
; c
++)
394 (*C
)[c
]->sel
= *(*C
)[c
]->val
;
396 for (c
= 0; c
< W
->count
; c
++)
397 *(*C
)[c
]->val
= (*C
)[c
]->sel
;
400 control_exchange(cl
);
403 for (c
= 0; c
< W
->count
; c
++)
404 control_exchange((W
->controls
)[c
]);
408 if (cl
->def
) *mod
= *cl
->def
;
409 else err_message
= ERR_NODEFAULT
;
410 if (k
== KEY_BACKSPACE
) window_item_next(W
);
413 if (W
->sel
== Active
) {
414 for (c
= 0; c
< W
->count
; c
++)
415 if ((*C
)[c
]->def
) *(*C
)[c
]->val
= *(*C
)[c
]->def
;
416 else err_message
= ERR_NODEFAULT
;
419 for (c
= 0; c
< W
->count
; c
++)
420 if ((*C
)[c
]->def
) (*C
)[c
]->sel
= *(*C
)[c
]->def
;
421 else err_message
= ERR_NODEFAULT
;
426 *mod
-= cl
->inc
.coarse
;
427 if (*mod
< cl
->min
) *mod
= cl
->min
;
431 *mod
+= cl
->inc
.coarse
;
432 if (*mod
> cl
->max
) *mod
= cl
->max
;
435 *mod
-= cl
->inc
.fine
;
436 if (*mod
< cl
->min
) *mod
= cl
->min
;
439 *mod
+= cl
->inc
.fine
;
440 if (*mod
> cl
->max
) *mod
= cl
->max
;
457 val
= ((LADSPA_Data
)(k
- 48)) / 10.0;
458 *mod
= control_rounding(cl
, (1.0 - val
) * cl
->min
+ val
* cl
->max
);
463 wtextentry(help_window
, input
, sizeof(input
));
464 if (control_set_value(mod
, input
, cl
) < 0)
465 err_message
= ERR_INVALIDVALUE
;
474 window_item_previous(W
);
478 (windows
+window_selection
)->index
= 0;
482 (windows
+window_selection
)->index
= (windows
+window_selection
)->count
- 1;
485 W
->sel
= W
->sel
== Active
? Alternative
: Active
;
488 W
->sel
= Alternative
;
496 W
->layout
= W
->layout
== Oneline
? Twolines
: Oneline
;
501 if (!want_refresh
) goto loop
;
503 want_refresh
= FALSE
;
504 for (i
= 0; i
< NUM_WINDOWS
; i
++) {
505 if (windows
[i
].index
> windows
[i
].count
- 1) windows
[i
].index
= 0;
506 wclear(windows
[i
].window_ptr
);
511 cleanup_windows(windows
);
513 jackspa_fini(&state
);