Report explicit full speed setting
[xiph/unicode.git] / rtrecord / tty.c
blob02a6e415ae13709122f4446d979aaeade9111290
1 /*
3 * rtrecord
4 *
5 * Copyright (C) 2006 Red Hat Inc.
7 * rtrecord is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
12 * rtrecord is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with rtrecord; see the file COPYING. If not, write to the
19 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 /* Encapsulate the curses/terminfo calls for presenting a little panel
25 display at the bottom of a terminal window. */
27 #define _GNU_SOURCE
28 #define _LARGEFILE_SOURCE
29 #define _LARGEFILE64_SOURCE
30 #define _FILE_OFFSET_BITS 64
32 #ifdef HAVE_CONFIG_H
33 # include <config.h>
34 #endif
36 #ifndef _REENTRANT
37 # define _REENTRANT
38 #endif
40 #include <ncurses.h>
41 #include <curses.h>
42 #include <term.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <signal.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <math.h>
50 #include "rtrecord.h"
52 TTY orig;
53 TTY term;
55 static int panel_lines=0;
56 static char *start_graphics=0;
57 static char *end_graphics=0;
58 static char *cup_str=0;
59 static char *cud_str=0;
60 static char *cursor_col=0;
61 static char *fore_string=0;
62 static char *back_string=0;
63 static char *unset=0;
64 static char *clear_str=0;
65 static char *clear_to=0;
67 static int initted=0;
68 static int cursor_line_offset=0;
70 static float disk_percent=100;
71 static float dma_percent=100;
72 static float *peak_dB=0;
73 static float *peak_hold_dB=0;
74 static float *rms_dB=0;
75 static int *peak_clip=0;
77 static int dma_once_overrun=0;
78 static int disk_once_overrun=0;
79 static int hold = 0;
81 static char *bargraphs[]={
82 " -120| -100| -80| -60| -40| -30| -20| -10| -6| -3| +0",
83 " -100| -80| -60| -40| -30| -20| -10| -6| -3| +0",
84 " -100| -80| -60| -40| -20| -10| -6| -3| +0",
85 " -80| -60| -40| -20| -10| -6| -3| +0",
86 " -80| -60| -40| -20| -10| -6| +0",
87 " -60| -40| -20| -10| -6| +0",
88 " -60| -40| -20| -6| +0",
89 " -60| -20| -6| +0",
90 " -60| -20| +0",
91 " -20| +0",
92 " +0",
95 static int bar_locations[]={0,6,13,20,27,34,41,48,55,62,69,76};
96 static float barposts[][12]={
97 {-150,-120,-100,-80,-60,-40,-30,-20,-10,-6,-3,0},
98 {-140,-100,-80,-60,-40,-30,-20,-10,-6,-3,0},
99 {-140,-100,-80,-60,-40,-20,-10,-6,-3,0},
100 {-120,-80,-60,-40,-20,-10,-6,-3,0},
101 {-120,-80,-60,-40,-20,-10,-6,0},
102 {-120,-60,-40,-20,-10,-6,0},
103 {-120,-60,-40,-20,-6,0},
104 {-120,-60,-20,-6,0},
105 {-120,-60,-20,-6,0},
106 {-120,-60,-20,0},
107 {-100,-20,0},
108 {-100,0},
111 static int barvals[]={11,10,9,8,7,6,5,4,3,2,1,0};
113 // relative-line cursor addressing; no idea where the cursor actually
114 // is absolutely */
115 static void cursor_to(int x, int y){
116 int yoff = y - cursor_line_offset;
118 while(yoff<0){
119 putp(cup_str);
120 cursor_line_offset--;
121 yoff++;
123 while(yoff>0){
124 putp(cud_str);
125 cursor_line_offset++;
126 yoff--;
129 putp(tparm(cursor_col,x));
130 fflush(stdout);
134 extern void _nc_init_acs(void);
135 static void enable_graphics(void){
136 char *g = tigetstr("enacs");
137 if(g && g != (char *)-1)
138 putp(g);
140 start_graphics=tigetstr("smacs");
141 end_graphics=tigetstr("rmacs");
142 _nc_init_acs();
145 static void hide_cursor(){
146 char *g = tigetstr("civis");
147 if(g) putp(g);
150 static void show_cursor(){
151 char *g = tigetstr("cvvis");
152 if(g) putp(g);
155 static void insert_lines(int n){
156 int i;
157 for(i=0;i<n-1;i++)
158 printf("\r\n");
159 cursor_line_offset=n-1;
162 static void setup_term_buf(void){
163 /* If curses is being provided by ncurses, we don't actually have
164 several of the 'canned' things like cbreak/noecho. Do it by
165 hand */
166 if (cur_term != 0) {
167 term = cur_term->Nttyb;
168 #ifdef TERMIOS
169 term.c_lflag &= ~ICANON;
170 term.c_iflag &= ~ICRNL;
171 term.c_lflag &= ~(ECHO | ECHONL);
172 term.c_iflag &= ~(ICRNL | INLCR | IGNCR);
173 term.c_oflag &= ~(ONLCR);
174 term.c_lflag |= ISIG;
175 term.c_cc[VMIN] = 1;
176 term.c_cc[VTIME] = 0;
177 #else
178 term.sg_flags |= CBREAK;
179 term.sg_flags &= ~(ECHO | CRMOD);
180 #endif
181 SET_TTY(STDOUT_FILENO,&term);
182 cur_term->Nttyb = term;
186 static void resetup_term(void){
187 SET_TTY(STDOUT_FILENO,&term);
188 cur_term->Nttyb = term;
191 void terminal_init_panel(){
193 if(!initted){
194 /* canned curses/termcap setup. We don't want full ncurses as we
195 don't want to take over the whole terminal screen */
197 GET_TTY(STDOUT_FILENO,&orig);
198 setupterm(0,STDOUT_FILENO,0);
199 setup_term_buf();
200 enable_graphics();
201 panel_lines = channels+4;
203 cup_str = tigetstr("cuu1");
204 cud_str = tigetstr("cud1");
205 cursor_col = tigetstr("hpa");
207 initted=1;
209 if(!cup_str || cup_str==(char *)-1 ||
210 !cud_str || cud_str==(char *)-1 ||
211 !cursor_col || cursor_col==(char *)-1
213 SET_TTY(STDOUT_FILENO,&orig);
214 initted=0;
215 fprintf(stderr,"Terminal provides insufficient cursor manipulation; rtrecord can run\n"
216 "in quiet mode (-q) only.\n");
217 //exit(1);
220 hide_cursor();
221 insert_lines(panel_lines);
222 fflush(stdout);
226 void terminal_remove_panel(){
227 if(initted){
228 char *delline = tigetstr("dl");
230 if(delline && delline!=(char *)-1){
231 cursor_to(0,0);
232 putp(tparm(delline,panel_lines));
234 show_cursor();
235 SET_TTY(STDOUT_FILENO,&orig);
236 initted=0;
237 fflush(stdout);
241 static void clear_line(void){
242 if(!clear_str){
243 clear_str = tigetstr("el");
244 if(!clear_str)clear_str=(char *)-1;
247 if(clear_str!=(char *)-1)
248 putp(clear_str);
251 static void clear_to_cursor(void){
252 if(!clear_to){
253 clear_to = tigetstr("el1");
254 if(!clear_to)clear_to=(char *)-1;
257 if(clear_to!=(char *)-1)
258 putp(clear_to);
261 static void graphics_mode(void){
262 if(start_graphics && start_graphics!=(char *)-1)
263 putp(start_graphics);
266 static void text_mode(void){
267 if(end_graphics && end_graphics!=(char *)-1)
268 putp(end_graphics);
271 static int barpos(int barchoice, float dB){
272 int i;
273 if(dB < barposts[barchoice][0]) return 0;
274 for(i=0;i<barvals[barchoice];i++)
275 if(dB < barposts[barchoice][i+1]){
276 float val0 = barposts[barchoice][i];
277 float val1 = barposts[barchoice][i+1];
278 return ((dB - val0) / (val1 - val0)) *
279 (bar_locations[i+1] - bar_locations[i]) +
280 bar_locations[i] + 1;
283 return barposts[barchoice][i];
286 static void unset_attributes(){
287 if(!unset){
288 unset = tigetstr("sgr0");
289 if(!unset)unset=(char *)-1;
291 if(unset!=(char *)-1)
292 putp(unset);
295 static void fill(char *buf,char c,int cols){
296 int i;
297 for(i=0;i<cols;i++)
298 buf[i]=c;
299 buf[i]=0;
302 static void print_into(char *buf,int pos, char *s){
303 int len = strlen(buf);
304 int len2 = strlen(s);
305 int i;
306 for(i=0; i+pos<len && i<len2; i++)
307 buf[i+pos]=s[i];
310 static void foreground(int c){
311 if(!fore_string){
312 fore_string = tigetstr("setaf");
313 if(!fore_string)fore_string=(char *)-1;
315 if(fore_string!=(char *)-1)
316 putp(tparm(fore_string,c));
319 static void background(int c){
320 if(!back_string){
321 back_string = tigetstr("setab");
322 if(!back_string)back_string=(char *)-1;
324 if(back_string!=(char *)-1)
325 putp(tparm(back_string,c));
328 static void color(int f,int b){
329 if(f==-1 || b==-1)
330 unset_attributes();
331 if(f!=-1)
332 foreground(f);
333 if(b!=-1)
334 background(b);
337 static void print_hline(char *s,int textcolor){
338 int pos=0,last=0;
340 while(s[pos]){
341 /* draw line */
342 while(s[pos] && s[pos]=='_')pos++;
343 if(pos>last){
344 if(start_graphics){
345 graphics_mode();
346 for(;last<pos;last++)
347 printf("%c",(int)ACS_HLINE);
348 text_mode();
349 }else{
350 fwrite(s+last,1,pos-last,stdout);
354 /* draw text */
355 while(s[pos] && s[pos]!='_')pos++;
356 if(pos>last){
357 if(textcolor>=0)
358 foreground(textcolor);
359 fwrite(s+last,1,pos-last,stdout);
360 if(textcolor>=0)
361 unset_attributes();
362 last = pos;
367 void terminal_paint_ui(){
368 int cols = columns;
370 char topline[cols+1];
371 char botline[cols+1];
372 char dmabuf[cols+1];
373 char diskbuf[cols+1];
374 char hwbuf[cols+1];
375 char linebuf[cols+1];
377 int barchoice=-1;
378 int barcols = cols - 25, barpad=0;
379 int barlength=0;
380 int i=0;
382 topline[0]='\0';
383 botline[0]='\0';
384 dmabuf[0]='\0';
385 diskbuf[0]='\0';
386 hwbuf[0]='\0';
387 linebuf[0]='\0';
389 /* determine which bargraph scale to use */
390 while(barvals[i]){
391 if((signed)strlen(bargraphs[i])<=barcols){
392 barchoice = i;
393 barlength = strlen(bargraphs[i]);
394 break;
396 i++;
399 /* didn't find one short enough */
400 if(barchoice==-1) barlength=0;
402 /* determine actual spacing of bars */
403 barcols = 25 + barlength;
404 barpad = (columns-barcols)/2;
406 /* determine per-channel feedback if disk thread has set some since
407 last update */
408 /* alloc structures if they haven't been alloced */
409 if(!peak_dB){
410 peak_dB=calloc(channels,sizeof(*peak_dB));
411 for(i=0;i<channels;i++)
412 peak_dB[i]=-150.;
414 if(!peak_hold_dB){
415 peak_hold_dB=calloc(channels,sizeof(*peak_hold_dB));
416 for(i=0;i<channels;i++)
417 peak_hold_dB[i]=-150.;
419 if(!rms_dB){
420 rms_dB=calloc(channels,sizeof(*rms_dB));
421 for(i=0;i<channels;i++)
422 rms_dB[i]=-150.;
424 if(!peak_clip)
425 peak_clip=calloc(channels,sizeof(*peak_clip));
427 if(disk_mark.bank!=disk_mark.read){
428 /* we have data to refresh display */
430 /* compute feedback */
431 if(!hold){
432 for(i=0;i<channels;i++){
433 float peak = todB(pcm_data[!disk_mark.bank][i].peak *.00000000046566128730);
434 if(peak>peak_dB[i])
435 peak_dB[i] = peak;
436 else{
437 peak_dB[i] *= (1.-smooth);
438 peak_dB[i] += smooth* peak;
441 rms_dB[i] *= (1.-smooth);
443 rms_dB[i] += smooth* todB(sqrt(pcm_data[!disk_mark.bank][i].weight_num/
444 pcm_data[!disk_mark.bank][i].weight_denom));
445 if(pcm_data[!disk_mark.bank][i].pcm_clip)peak_clip[i]=1;
446 if(peak_dB[i]>peak_hold_dB[i])peak_hold_dB[i] = peak_dB[i];
450 disk_mark.read=disk_mark.bank;
453 /* determine buffer feedback if dma thread has set some since
454 last update */
455 if(dma_mark.bank!=dma_mark.read){
456 if(dma_data[!dma_mark.bank].dmabuffer_overrun)
457 dma_once_overrun=1;
458 if(dma_data[!dma_mark.bank].diskbuffer_overrun)
459 disk_once_overrun=1;
461 dma_percent = (dma_data[!dma_mark.bank].dmabuffer_min*100./dmabuffer_frames);
462 disk_percent = (dma_data[!dma_mark.bank].diskbuffer_min*100./diskbuffer_frames);
464 dma_mark.read=dma_mark.bank;
467 /* construct the top line */
468 snprintf(dmabuf,cols+1," DMA buffer: %3d%% ",(int)rint(dma_percent));
469 snprintf(diskbuf,cols+1," Disk buffer: %3d%% ",(int)rint(disk_percent));
470 snprintf(hwbuf,cols+1," %s ",device);
471 fill(topline,'_',cols);
472 print_into(topline,3,dmabuf);
473 print_into(topline,5+strlen(dmabuf),diskbuf);
474 print_into(topline,columns - strlen(hwbuf)-3,hwbuf);
476 /* print the top line */
477 cursor_to(0,0);
478 print_hline(topline,-1);
480 /* blank next line (useful if coming back after SIGSTOP) */
481 cursor_to(0,1);
482 clear_line();
484 /* bargraphs */
486 for(i=0;i<channels;i++){
487 cursor_to(barpad,2+i);
488 clear_to_cursor();
489 snprintf(linebuf,cols+1,"%2d: [",i);
490 putp(linebuf);
492 if(barchoice>=0){
493 /* determine bar lengths */
494 int rms_pos = barpos(barchoice,rms_dB[i]);
495 int peak_pos = barpos(barchoice,peak_dB[i]);
497 if(peak_pos>barlength)peak_pos=barlength;
498 if(rms_pos>peak_pos)rms_pos=peak_pos;
500 /* color the bargraph */
501 if(rms_pos){
502 color(COLOR_BLACK,COLOR_WHITE);
503 fwrite(bargraphs[barchoice],1,rms_pos,stdout);
505 if(peak_pos-rms_pos>0){
506 color(COLOR_BLACK,COLOR_CYAN);
507 fwrite(bargraphs[barchoice]+rms_pos,1,peak_pos-rms_pos,stdout);
509 if(barlength-peak_pos>0){
510 color(COLOR_CYAN,COLOR_BLACK);
511 fwrite(bargraphs[barchoice]+peak_pos,1,barlength-peak_pos,stdout);
514 /* the RMS indicator is always normal colors */
515 unset_attributes();
518 if(weight){
519 snprintf(linebuf,cols+1,"] %+6.1fdBA,",(double)rms_dB[i]);
520 }else{
521 snprintf(linebuf,cols+1,"] %+6.1fdB, ",(double)rms_dB[i]);
523 putp(linebuf);
525 /* the peak indicator may read a number or CLIP */
526 if(peak_clip[i]){
527 color(COLOR_RED,-1);
528 putp("**CLIP**");
529 color(-1,-1);
530 }else{
531 snprintf(linebuf,cols+1,"%+6.1fdB",(double)peak_hold_dB[i]);
532 putp(linebuf);
534 if(barpad + barcols < columns) clear_line();
537 /* blank next line (useful if coming back after SIGSTOP) */
538 cursor_to(0,channels+2);
539 clear_line();
541 /* construct the bottom line */
542 fill(botline,'_',cols);
544 int x=3;
545 if(paused){
546 print_into(botline,x," PAUSED ");
547 x+=10;
549 if(hold){
550 print_into(botline,x," HOLD ");
551 x+=8;
553 if(dma_once_overrun){
554 print_into(botline,x," DMA OVERRUN ");
555 x+=16;
557 if(disk_once_overrun){
558 print_into(botline,x," DISK OVERRUN ");
559 x+=17;
563 /* print the bottom line */
564 cursor_to(0,channels+3);
565 print_hline(botline,COLOR_RED);
566 fflush(stdout);
569 void terminal_main_loop(int quiet){
570 int i;
571 int exiting=0;
573 while(!exiting){
574 char buf;
575 if(!quiet){
576 resetup_term();
577 terminal_paint_ui();
579 read(STDIN_FILENO,&buf,1);
581 if(!quiet){
582 switch(buf){
583 case 'p':
584 paused = !paused;
585 break;
586 case 'h':
587 hold = !hold;
588 break;
589 case ' ':
590 dma_once_overrun=0;
591 disk_once_overrun=0;
592 for(i=0;i<channels;i++){
593 peak_clip[i]=0;
594 peak_hold_dB[i]=peak_dB[i];
596 break;
597 case 'q':case 'Q':
598 exiting=1;
599 break;
605 #if 0
606 ---- DMA buffer: 100% -- Disk buffer: 100% ----------------------------------------------- :1,0 -----
608 1: [ -120| -100| -80| -60| -40| -30| -20| -10| -6| -3| 0] -70.0dB, -100.0dB
609 2: [ 0] -70.0dB, -100.0dB
610 3: [ 0] -70.0dB, -100.0dB
611 4: [ 0] -70.0dB, -100.0dB
612 5: [ 0] -70.0dB, -100.0dB
613 6: [ 0] -70.0dB, -100.0dB
615 -----------------------------------------------------------------------------------------------------
616 #endif