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"
32 #define ERR_OUT(format, arg...) ( endwin(), fprintf(stderr, format "\n", ## arg), refresh() )
34 #define CON_NAME "Controls"
35 #define HELP "'q'uit, '?', 'TAB', U/D-selection, L/R-change, 'd/D'efault, '>/<', '<n>'0%, 'u/U'pdate, e'x/X'change"
36 #define ERR_NODEFAULT "No default value"
43 #define WCON_H rows - 1
46 #define WHLP_Y rows - 1
54 unsigned long count
; /* number of controls */
60 enum { Active
, Alternative
} sel
; /* state variable */
63 void window_item_next(struct window
* w
) { if (w
->index
< w
->count
- 1) w
->index
++; }
64 void window_item_previous(struct window
* w
) { if (w
->index
> 0) w
->index
--; }
65 void suppress_jack_log(const char *msg
) { ; /* Just suppress Jack SPAM here ;-) */ }
68 int init_window(struct window
*window
, state_t
*state
)
72 rc
= control_buildall(&window
->count
, &window
->controls
, state
);
73 window
->state
= state
;
79 void draw_border(struct window
* window_ptr
)
81 int col
= ( window_ptr
->width
- strlen(window_ptr
->name
) -
82 strlen(window_ptr
->state
->descriptor
->Label
) - 5 ) / 2;
85 /* 0, 0 gives default characters for the vertical and horizontal lines */
86 box(window_ptr
->window_ptr
, 0, 0);
88 if (window_ptr
->selected
) {
89 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
90 mvwprintw( window_ptr
->window_ptr
, 0, col
, "=[%s:%s]=",
91 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
92 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
95 mvwprintw( window_ptr
->window_ptr
, 0, col
, " [%s:%s] ",
96 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
99 void draw_controls(struct window
* window_ptr
)
101 int row
, col
, color
, high
, rows
, cols
;
102 /* start position of a control, colors, screen size */
103 unsigned long c
; /* loop variable for controls */
104 long offset
; /* first displayed control index */
106 char fmt
[40]; /* buffer for printf format strings */
107 int w
; /* computed field width */
109 getmaxyx(window_ptr
->window_ptr
, rows
, cols
);
110 offset
= (long)window_ptr
->index
+ 3-rows
;
111 if (offset
< 0) offset
= 0;
113 for (c
= offset
, row
= 1; c
< window_ptr
->count
; c
++, row
++) {
114 control
= (window_ptr
->controls
)[c
];
115 high
= (row
== window_ptr
->index
- offset
+ 1) ?
116 (window_ptr
->selected
) ? 3 : 2 : 1;
117 /* currently selected widget (higlighted or normal) */
118 color
= (row
== window_ptr
->index
- offset
+ 1) ?
119 (window_ptr
->selected
) ? 4 : 2 : 1;
120 /* emphasized widget (white or normal) */
122 /* Name [ramaining space] */
123 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
124 w
= cols
-2 - (cols
/6-2)*4 - 9;
125 snprintf(fmt
, sizeof(fmt
), "%%-%d.%ds", w
, w
);
126 mvwprintw(window_ptr
->window_ptr
, row
, col
, fmt
, control
->name
);
127 /* Separator ':' [2] */
128 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
129 wprintw(window_ptr
->window_ptr
, ": ");
130 /* Active value (bold) [c/6-2] */
131 snprintf(fmt
, sizeof(fmt
), "%%%dF", cols
/6-2);
132 if (window_ptr
->sel
== Active
) {
133 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(high
));
134 wprintw(window_ptr
->window_ptr
, fmt
, *control
->val
);
135 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(high
));
138 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
139 wprintw(window_ptr
->window_ptr
, fmt
, *control
->val
);
140 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
143 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
144 wprintw( window_ptr
->window_ptr
, " %s ",
145 (control
->type
== JACKSPA_TOGGLE
) ?
146 "?" : (control
->type
== JACKSPA_INT
) ? "i" : "f" );
147 /* Selection (emphasized) [c/6-2] */
148 snprintf(fmt
, sizeof(fmt
), "%%-%dF", cols
/6-2);
149 if (window_ptr
->sel
== Active
)
150 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
152 wattron(window_ptr
->window_ptr
, COLOR_PAIR(high
));
153 wprintw(window_ptr
->window_ptr
, fmt
, control
->sel
);
154 /* Range [2* (c/6-2) + 4] */
155 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
156 snprintf(fmt
, sizeof(fmt
), " [%%-%dF~%%%dF]", cols
/6-2, cols
/6-2);
157 wprintw(window_ptr
->window_ptr
, fmt
, control
->min
, control
->max
);
158 wattroff(window_ptr
->window_ptr
, COLOR_PAIR(1));
160 wclrtoeol(window_ptr
->window_ptr
);
162 draw_border(window_ptr
);
163 wrefresh(window_ptr
->window_ptr
);
167 create_window(struct window
* window_ptr
, int height
, int width
,
168 int starty
, int startx
, const char * name
)
170 window_ptr
->window_ptr
= newwin(height
, width
, starty
, startx
);
171 window_ptr
->selected
= FALSE
;
172 window_ptr
->width
= width
;
173 window_ptr
->height
= height
;
174 window_ptr
->name
= name
;
175 window_ptr
->index
= 0;
176 window_ptr
->controls
= NULL
;
177 window_ptr
->count
= 0;
178 window_ptr
->sel
= Alternative
;
179 scrollok(window_ptr
->window_ptr
, FALSE
);
183 resize_window(struct window
* window_ptr
, int height
, int width
, int starty
, int startx
)
185 /* delwin(window_ptr->window_ptr); */
186 /* window_ptr->window_ptr = newwin(height, width, starty, startx); */
187 wresize(window_ptr
->window_ptr
, height
, width
);
188 mvwin(window_ptr
->window_ptr
, starty
, startx
);
189 window_ptr
->width
= width
;
190 window_ptr
->height
= height
;
193 void cleanup_windows(struct window
* windows
)
196 struct window
* w
= windows
;
198 for (i
= 0; i
< NUM_WINDOWS
; i
++, w
++)
199 control_cleanupall(w
->count
, &w
->controls
);
203 select_window(struct window
* windows
, int cur
, int nex
)
207 else if (nex
>= NUM_WINDOWS
)
210 nex
= NUM_WINDOWS
- 1;
212 windows
[cur
].selected
= FALSE
;
213 windows
[nex
].selected
= TRUE
;
217 void draw_help(WINDOW
* w
, int c
, const char* msg
, float dsp_load
, bool rt
)
225 wattron(w
, COLOR_PAIR(c
));
226 mvwprintw(w
, 0, 1, msg
);
227 wattroff(w
, COLOR_PAIR(c
));
229 wattron(w
, COLOR_PAIR(7));
230 mvwprintw(w
, 0, cols
-12, "DSP:%4.2f%s", dsp_load
, rt
? "@RT" : "!RT" );
231 wattroff(w
, COLOR_PAIR(7));
236 #define cl ((*C)[W->index])
239 int main(int argc
, char *argv
[])
241 unsigned short rc
= 0; /* return code */
242 int rows
, cols
; /* screen size */
243 struct window windows
[NUM_WINDOWS
];
244 unsigned short window_selection
= 0; /* state variable */
245 struct window
*W
; /* selected window */
246 controls_t
*C
; /* controls of the selected window */
247 LADSPA_Data
*mod
; /* value of the selected window to be acted upon */
248 unsigned long c
; /* loop variable for controls */
249 LADSPA_Data val
; /* buffering variable */
251 const char *err_message
= NULL
; /* state variable */
252 bool want_refresh
= FALSE
, help
= TRUE
; /* state variables */
253 bool rt
; /* jack RT capability */
254 int k
, i
; /* current curses character, loop variable */
256 /* Command line options */
257 GOptionContext
*context
= interface_context();
258 GError
*error
= NULL
;
259 if (!g_option_context_parse(context
, &argc
, &argv
, &error
))
260 return (ERR_OUT("option parsing failed: %s\n", error
->message
), -1);
262 /* Initialize ncurses */
264 curs_set(0); /* set cursor invisible */
266 getmaxyx(stdscr
, rows
, cols
);
268 if (has_colors() == FALSE
) {
269 rc
= -1, ERR_OUT("Your terminal does not support color");
273 init_pair(1, COLOR_CYAN
, COLOR_BLACK
);
274 init_pair(2, COLOR_BLACK
, COLOR_WHITE
);
275 init_pair(3, COLOR_BLACK
, COLOR_GREEN
);
276 init_pair(4, COLOR_WHITE
, COLOR_BLACK
);
277 init_pair(5, COLOR_BLACK
, COLOR_RED
);
278 init_pair(6, COLOR_YELLOW
, COLOR_BLACK
);
279 init_pair(7, COLOR_BLUE
, COLOR_BLACK
);
281 /* Create Help Window */
282 help_window
= newwin(WHLP_H
, WHLP_W
, WHLP_Y
, WHLP_X
);
283 keypad(help_window
, TRUE
);
284 wtimeout(help_window
, 3000);
286 /* Some Jack versions are very aggressive in breaking view */
287 jack_set_info_function(suppress_jack_log
);
288 jack_set_error_function(suppress_jack_log
);
290 /* Initialize jack */
293 if (!jackspa_init(&state
, argc
, argv
)) {
298 rt
= (bool)jack_is_realtime(state
.jack_client
);
301 create_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
, CON_NAME
);
302 windows
[window_selection
].selected
= TRUE
;
306 if (init_window(windows
+0, &state
)) {
313 for (i
= 0; i
< NUM_WINDOWS
; i
++) draw_controls(windows
+i
);
314 W
= &windows
[window_selection
];
316 mod
= W
->sel
== Active
? cl
->val
: &cl
->sel
;
319 draw_help(help_window
, 5, err_message
, jack_cpu_load(state
.jack_client
), rt
);
323 draw_help(help_window
, 6, help
? HELP
: W
->state
->descriptor
->Name
,
324 jack_cpu_load(state
.jack_client
), rt
);
326 switch (k
= wgetch(help_window
)) {
332 getmaxyx(stdscr
, rows
, cols
);
333 wresize(help_window
, WHLP_H
, WHLP_W
);
334 mvwin(help_window
, WHLP_Y
, WHLP_X
);
335 resize_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
);
338 if (W
->sel
== Active
)
344 if (W
->sel
== Active
)
345 for (c
= 0; c
< W
->count
; c
++)
346 (*C
)[c
]->sel
= *(*C
)[c
]->val
;
348 for (c
= 0; c
< W
->count
; c
++)
349 *(*C
)[c
]->val
= (*C
)[c
]->sel
;
352 control_exchange(cl
);
355 for (c
= 0; c
< W
->count
; c
++)
356 control_exchange((W
->controls
)[c
]);
360 if (cl
->def
) *mod
= *cl
->def
;
361 else err_message
= ERR_NODEFAULT
;
362 if (k
== KEY_BACKSPACE
) window_item_next(W
);
365 if (W
->sel
== Active
) {
366 for (c
= 0; c
< W
->count
; c
++)
367 if ((*C
)[c
]->def
) *(*C
)[c
]->val
= *(*C
)[c
]->def
;
368 else err_message
= ERR_NODEFAULT
;
371 for (c
= 0; c
< W
->count
; c
++)
372 if ((*C
)[c
]->def
) (*C
)[c
]->sel
= *(*C
)[c
]->def
;
373 else err_message
= ERR_NODEFAULT
;
378 *mod
-= cl
->inc
.coarse
;
379 if (*mod
< cl
->min
) *mod
= cl
->min
;
383 *mod
+= cl
->inc
.coarse
;
384 if (*mod
> cl
->max
) *mod
= cl
->max
;
387 *mod
-= cl
->inc
.fine
;
388 if (*mod
< cl
->min
) *mod
= cl
->min
;
391 *mod
+= cl
->inc
.fine
;
392 if (*mod
> cl
->max
) *mod
= cl
->max
;
409 val
= ((LADSPA_Data
)(k
- 48)) / 10.0;
410 *mod
= control_rounding(cl
, (1.0 - val
) * cl
->min
+ val
* cl
->max
);
418 window_item_previous(W
);
422 (windows
+window_selection
)->index
= 0;
426 (windows
+window_selection
)->index
= (windows
+window_selection
)->count
- 1;
430 W
->sel
= W
->sel
== Active
? Alternative
: Active
;
433 W
->sel
= Alternative
;
441 if (!want_refresh
) goto loop
;
443 want_refresh
= FALSE
;
444 for (i
= 0; i
< NUM_WINDOWS
; i
++) {
445 if (windows
[i
].index
> windows
[i
].count
- 1) windows
[i
].index
= 0;
446 wclear(windows
[i
].window_ptr
);
451 cleanup_windows(windows
);
453 /* jackspa_exit(state); */
454 jack_deactivate(state
.jack_client
);
455 jack_client_close(state
.jack_client
);