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 #define CON_NAME "Controls"
32 #define HELP "'q'uit, '?', U/D-selection, L/R-change, 'd/D'efault, '>/<', '<n>'0%, 'u/U'pdate, e'x/X'change"
33 #define ERR_NODEFAULT "No default value"
40 #define WCON_H rows - 1
43 #define WHLP_Y rows - 1
47 #define ERR_OUT(format, arg...) ( endwin(), fprintf(stderr, format "\n", ## arg), refresh() )
52 control_t
*(*controls
)[];
61 void window_item_next(struct window
* w
) { if (w
->index
< w
->count
- 1) w
->index
++; }
62 void window_item_previous(struct window
* w
) { if (w
->index
> 0) w
->index
--; }
63 void suppress_jack_log(const char *msg
) { ; /* Just suppress Jack SPAM here ;-) */ }
66 int build_controls(struct window
*window
, state_t
*state
)
71 window
->state
= state
;
72 for (p
= 0, c
= 0; p
< state
->descriptor
->PortCount
; p
++)
73 if ( LADSPA_IS_PORT_INPUT(state
->descriptor
->PortDescriptors
[p
]) &&
74 LADSPA_IS_PORT_CONTROL(state
->descriptor
->PortDescriptors
[p
]) )
78 window
->controls
= (control_t
*(*)[])calloc(sizeof(control_t
*), c
);
79 if (!window
->controls
)
80 return (ERR_OUT ("memory allocation error"), 1);
82 for (p
= 0, c
= 0; p
< state
->descriptor
->PortCount
; p
++)
83 if ( LADSPA_IS_PORT_INPUT(state
->descriptor
->PortDescriptors
[p
]) &&
84 LADSPA_IS_PORT_CONTROL(state
->descriptor
->PortDescriptors
[p
]) )
86 (*window
->controls
)[c
] = (control_t
*)malloc(sizeof(control_t
));
87 if (!(*window
->controls
)[c
])
88 return (ERR_OUT ("memory allocation error"), 1);
89 if (control_init((*window
->controls
)[c
++], state
, p
))
96 void draw_border(struct window
* window_ptr
)
98 int col
= ( window_ptr
->width
- strlen(window_ptr
->name
) -
99 strlen(window_ptr
->state
->descriptor
->Label
) - 5 ) / 2;
100 if (col
< 0) col
= 0;
102 /* 0, 0 gives default characters for the vertical and horizontal lines */
103 box(window_ptr
->window_ptr
, 0, 0);
105 if (window_ptr
->selected
) {
106 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
107 mvwprintw( window_ptr
->window_ptr
, 0, col
, "=[%s:%s]=",
108 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
109 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(4));
112 mvwprintw( window_ptr
->window_ptr
, 0, col
, " [%s:%s] ",
113 window_ptr
->state
->descriptor
->Label
, window_ptr
->name
);
116 void draw_controls(struct window
* window_ptr
)
118 unsigned short row
, col
, color
, high
, rows
, cols
;
124 getmaxyx(window_ptr
->window_ptr
, rows
, cols
);
125 offset
= (long)window_ptr
->index
+ 3 - rows
; /* first displayed index */
126 if (offset
< 0) offset
= 0;
128 for (c
= offset
, row
= 1; c
< window_ptr
->count
; c
++, row
++) {
129 control
= (*window_ptr
->controls
)[c
];
130 high
= (row
== window_ptr
->index
- offset
+ 1) ?
131 (window_ptr
->selected
) ? 3 : 2 : 1;
132 color
= (row
== window_ptr
->index
- offset
+ 1) ?
133 (window_ptr
->selected
) ? 4 : 2 : 1;
135 wattron(window_ptr
->window_ptr
, COLOR_PAIR(color
));
136 snprintf(fmt
, sizeof(fmt
), "%%-%d.%ds", cols
/3 - 1, cols
/3 -1);
137 mvwprintw(window_ptr
->window_ptr
, row
, col
, fmt
, control
->name
);
138 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
139 wprintw(window_ptr
->window_ptr
, ": ");
140 wattron(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
141 snprintf(fmt
, sizeof(fmt
), "%%%dF", cols
/6 - 2);
142 wprintw(window_ptr
->window_ptr
, fmt
, *control
->val
);
143 wattroff(window_ptr
->window_ptr
, WA_BOLD
|COLOR_PAIR(color
));
144 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
145 wprintw( window_ptr
->window_ptr
, " %s ",
146 (control
->type
== JACKSPA_TOGGLE
) ?
147 "?" : (control
->type
== JACKSPA_INT
) ? "i" : "f" );
148 wattron(window_ptr
->window_ptr
, COLOR_PAIR(high
));
149 snprintf(fmt
, sizeof(fmt
), "%%-%dF", cols
/6 - 2);
150 wprintw(window_ptr
->window_ptr
, fmt
, control
->sel
);
151 wattron(window_ptr
->window_ptr
, COLOR_PAIR(1));
152 snprintf(fmt
, sizeof(fmt
), " [%%-%dF~%%%dF]", cols
/6 - 2, cols
/6 - 2);
153 wprintw(window_ptr
->window_ptr
, fmt
, control
->min
, control
->max
);
154 wattroff(window_ptr
->window_ptr
, COLOR_PAIR(1));
156 wclrtoeol(window_ptr
->window_ptr
);
158 draw_border(window_ptr
);
159 wrefresh(window_ptr
->window_ptr
);
163 create_window(struct window
* window_ptr
, int height
, int width
,
164 int starty
, int startx
, const char * name
)
166 window_ptr
->window_ptr
= newwin(height
, width
, starty
, startx
);
167 window_ptr
->selected
= FALSE
;
168 window_ptr
->width
= width
;
169 window_ptr
->height
= height
;
170 window_ptr
->name
= name
;
171 window_ptr
->index
= 0;
172 window_ptr
->controls
= NULL
;
173 window_ptr
->count
= 0;
174 scrollok(window_ptr
->window_ptr
, FALSE
);
178 resize_window(struct window
* window_ptr
, int height
, int width
, int starty
, int startx
)
180 /* delwin(window_ptr->window_ptr); */
181 /* window_ptr->window_ptr = newwin(height, width, starty, startx); */
182 wresize(window_ptr
->window_ptr
, height
, width
);
183 mvwin(window_ptr
->window_ptr
, starty
, startx
);
184 window_ptr
->width
= width
;
185 window_ptr
->height
= height
;
188 void cleanup_controls(struct window
* windows
)
192 struct window
* w
= windows
;
194 for (i
= 0; i
< NUM_WINDOWS
; i
++, w
++) {
195 for (c
= 0; c
< w
->count
; c
++)
196 free((*w
->controls
)[c
]);
202 select_window(struct window
* windows
, int cur
, int nex
)
206 else if (nex
>= NUM_WINDOWS
)
209 nex
= NUM_WINDOWS
- 1;
211 windows
[cur
].selected
= FALSE
;
212 windows
[nex
].selected
= TRUE
;
216 void draw_help(WINDOW
* w
, int c
, const char* msg
, float dsp_load
, bool rt
)
224 wattron(w
, COLOR_PAIR(c
));
225 mvwprintw(w
, 0, 1, msg
);
226 wattroff(w
, COLOR_PAIR(c
));
228 wattron(w
, COLOR_PAIR(7));
229 mvwprintw(w
, 0, cols
-12, "DSP:%4.2f%s", dsp_load
, rt
? "@RT" : "!RT" );
230 wattroff(w
, COLOR_PAIR(7));
235 #define cl ((*C)[W->index])
237 int main(int argc
, char *argv
[])
239 unsigned short rc
= 0, rows
, cols
, window_selection
= 0;
240 struct window windows
[NUM_WINDOWS
];
246 const char *err_message
= NULL
;
247 bool want_refresh
= FALSE
, help
= TRUE
;
251 /* Initialize ncurses */
253 curs_set(0); /* set cursor invisible */
255 getmaxyx(stdscr
, rows
, cols
);
257 if (has_colors() == FALSE
) {
258 rc
= -1, ERR_OUT("Your terminal does not support color");
262 init_pair(1, COLOR_CYAN
, COLOR_BLACK
);
263 init_pair(2, COLOR_BLACK
, COLOR_WHITE
);
264 init_pair(3, COLOR_BLACK
, COLOR_GREEN
);
265 init_pair(4, COLOR_WHITE
, COLOR_BLACK
);
266 init_pair(5, COLOR_BLACK
, COLOR_RED
);
267 init_pair(6, COLOR_YELLOW
, COLOR_BLACK
);
268 init_pair(7, COLOR_BLUE
, COLOR_BLACK
);
270 /* Create Help Window */
271 help_window
= newwin(WHLP_H
, WHLP_W
, WHLP_Y
, WHLP_X
);
272 keypad(help_window
, TRUE
);
273 wtimeout(help_window
, 3000);
275 /* Some Jack versions are very aggressive in breaking view */
276 jack_set_info_function(suppress_jack_log
);
277 jack_set_error_function(suppress_jack_log
);
279 /* Initialize jack */
282 if (!jackspa_init(&state
, argc
, argv
)) {
287 rt
= (bool)jack_is_realtime(state
.jack_client
);
290 create_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
, CON_NAME
);
291 windows
[window_selection
].selected
= TRUE
;
294 if (build_controls(windows
+0, &state
)) {
300 for (i
= 0; i
< NUM_WINDOWS
; i
++) draw_controls(windows
+i
);
301 W
= &windows
[window_selection
];
305 draw_help(help_window
, 5, err_message
, jack_cpu_load(state
.jack_client
), rt
);
309 draw_help(help_window
, 6, help
? HELP
: W
->state
->descriptor
->Name
,
310 jack_cpu_load(state
.jack_client
), rt
);
312 switch (k
= wgetch(help_window
)) {
314 window_selection
= select_window(windows
, window_selection
, window_selection
+1);
317 window_selection
= select_window(windows
, window_selection
, window_selection
-1);
324 getmaxyx(stdscr
, rows
, cols
);
325 wresize(help_window
, WHLP_H
, WHLP_W
);
326 mvwin(help_window
, WHLP_Y
, WHLP_X
);
327 resize_window(windows
+0, WCON_H
, WCON_W
, WCON_Y
, WCON_X
);
333 for (c
= 0; c
< W
->count
; c
++)
334 *(*C
)[c
]->val
= (*C
)[c
]->sel
;
337 control_exchange(cl
);
340 for (c
= 0; c
< W
->count
; c
++)
341 control_exchange((*W
->controls
)[c
]);
345 if (cl
->def
) cl
->sel
= *cl
->def
;
346 else err_message
= ERR_NODEFAULT
;
347 if (k
== KEY_BACKSPACE
) window_item_next(W
);
350 for (c
= 0; c
< W
->count
; c
++)
351 if ((*C
)[c
]->def
) (*C
)[c
]->sel
= *(*C
)[c
]->def
;
352 else err_message
= ERR_NODEFAULT
;
356 cl
->sel
-= cl
->inc
.coarse
;
357 if (cl
->sel
< cl
->min
) cl
->sel
= cl
->min
;
361 cl
->sel
+= cl
->inc
.coarse
;
362 if (cl
->sel
> cl
->max
) cl
->sel
= cl
->max
;
365 cl
->sel
-= cl
->inc
.fine
;
366 if (cl
->sel
< cl
->min
) cl
->sel
= cl
->min
;
369 cl
->sel
+= cl
->inc
.fine
;
370 if (cl
->sel
> cl
->max
) cl
->sel
= cl
->max
;
387 val
= ((LADSPA_Data
)(k
- 48)) / 10.0;
388 cl
->sel
= control_rounding(cl
, (1.0 - val
) * cl
->min
+ val
* cl
->max
);
396 window_item_previous(W
);
400 (windows
+window_selection
)->index
= 0;
404 (windows
+window_selection
)->index
= (windows
+window_selection
)->count
- 1;
409 if (!want_refresh
) goto loop
;
411 want_refresh
= FALSE
;
412 for (i
= 0; i
< NUM_WINDOWS
; i
++) {
413 if (windows
[i
].index
> windows
[i
].count
- 1) windows
[i
].index
= 0;
414 wclear(windows
[i
].window_ptr
);
419 cleanup_controls(windows
);
421 /* jackspa_exit(state); */
422 jack_deactivate(state
.jack_client
);
423 jack_client_close(state
.jack_client
);