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)
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. */
28 #define _LARGEFILE_SOURCE
29 #define _LARGEFILE64_SOURCE
30 #define _FILE_OFFSET_BITS 64
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;
64 static char *clear_str
=0;
65 static char *clear_to
=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;
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",
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},
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
115 static void cursor_to(int x
, int y
){
116 int yoff
= y
- cursor_line_offset
;
120 cursor_line_offset
--;
125 cursor_line_offset
++;
129 putp(tparm(cursor_col
,x
));
134 extern void _nc_init_acs(void);
135 static void enable_graphics(void){
136 char *g
= tigetstr("enacs");
137 if(g
&& g
!= (char *)-1)
140 start_graphics
=tigetstr("smacs");
141 end_graphics
=tigetstr("rmacs");
145 static void hide_cursor(){
146 char *g
= tigetstr("civis");
150 static void show_cursor(){
151 char *g
= tigetstr("cvvis");
155 static void insert_lines(int 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
167 term
= cur_term
->Nttyb
;
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
;
176 term
.c_cc
[VTIME
] = 0;
178 term
.sg_flags
|= CBREAK
;
179 term
.sg_flags
&= ~(ECHO
| CRMOD
);
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(){
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);
201 panel_lines
= channels
+4;
203 cup_str
= tigetstr("cuu1");
204 cud_str
= tigetstr("cud1");
205 cursor_col
= tigetstr("hpa");
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
);
215 fprintf(stderr
,"Terminal provides insufficient cursor manipulation; rtrecord can run\n"
216 "in quiet mode (-q) only.\n");
221 insert_lines(panel_lines
);
226 void terminal_remove_panel(){
228 char *delline
= tigetstr("dl");
230 if(delline
&& delline
!=(char *)-1){
232 putp(tparm(delline
,panel_lines
));
235 SET_TTY(STDOUT_FILENO
,&orig
);
241 static void clear_line(void){
243 clear_str
= tigetstr("el");
244 if(!clear_str
)clear_str
=(char *)-1;
247 if(clear_str
!=(char *)-1)
251 static void clear_to_cursor(void){
253 clear_to
= tigetstr("el1");
254 if(!clear_to
)clear_to
=(char *)-1;
257 if(clear_to
!=(char *)-1)
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)
271 static int barpos(int barchoice
, float dB
){
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(){
288 unset
= tigetstr("sgr0");
289 if(!unset
)unset
=(char *)-1;
291 if(unset
!=(char *)-1)
295 static void fill(char *buf
,char c
,int cols
){
302 static void print_into(char *buf
,int pos
, char *s
){
303 int len
= strlen(buf
);
304 int len2
= strlen(s
);
306 for(i
=0; i
+pos
<len
&& i
<len2
; i
++)
310 static void foreground(int c
){
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
){
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
){
337 static void print_hline(char *s
,int textcolor
){
342 while(s
[pos
] && s
[pos
]=='_')pos
++;
346 for(;last
<pos
;last
++)
347 printf("%c",(int)ACS_HLINE
);
350 fwrite(s
+last
,1,pos
-last
,stdout
);
355 while(s
[pos
] && s
[pos
]!='_')pos
++;
358 foreground(textcolor
);
359 fwrite(s
+last
,1,pos
-last
,stdout
);
367 void terminal_paint_ui(){
370 char topline
[cols
+1];
371 char botline
[cols
+1];
373 char diskbuf
[cols
+1];
375 char linebuf
[cols
+1];
378 int barcols
= cols
- 25, barpad
=0;
389 /* determine which bargraph scale to use */
391 if((signed)strlen(bargraphs
[i
])<=barcols
){
393 barlength
= strlen(bargraphs
[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
408 /* alloc structures if they haven't been alloced */
410 peak_dB
=calloc(channels
,sizeof(*peak_dB
));
411 for(i
=0;i
<channels
;i
++)
415 peak_hold_dB
=calloc(channels
,sizeof(*peak_hold_dB
));
416 for(i
=0;i
<channels
;i
++)
417 peak_hold_dB
[i
]=-150.;
420 rms_dB
=calloc(channels
,sizeof(*rms_dB
));
421 for(i
=0;i
<channels
;i
++)
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 */
432 for(i
=0;i
<channels
;i
++){
433 float peak
= todB(pcm_data
[!disk_mark
.bank
][i
].peak
*.00000000046566128730);
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
455 if(dma_mark
.bank
!=dma_mark
.read
){
456 if(dma_data
[!dma_mark
.bank
].dmabuffer_overrun
)
458 if(dma_data
[!dma_mark
.bank
].diskbuffer_overrun
)
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 */
478 print_hline(topline
,-1);
480 /* blank next line (useful if coming back after SIGSTOP) */
486 for(i
=0;i
<channels
;i
++){
487 cursor_to(barpad
,2+i
);
489 snprintf(linebuf
,cols
+1,"%2d: [",i
);
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 */
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 */
519 snprintf(linebuf
,cols
+1,"] %+6.1fdBA,",(double)rms_dB
[i
]);
521 snprintf(linebuf
,cols
+1,"] %+6.1fdB, ",(double)rms_dB
[i
]);
525 /* the peak indicator may read a number or CLIP */
531 snprintf(linebuf
,cols
+1,"%+6.1fdB",(double)peak_hold_dB
[i
]);
534 if(barpad
+ barcols
< columns
) clear_line();
537 /* blank next line (useful if coming back after SIGSTOP) */
538 cursor_to(0,channels
+2);
541 /* construct the bottom line */
542 fill(botline
,'_',cols
);
546 print_into(botline
,x
," PAUSED ");
550 print_into(botline
,x
," HOLD ");
553 if(dma_once_overrun
){
554 print_into(botline
,x
," DMA OVERRUN ");
557 if(disk_once_overrun
){
558 print_into(botline
,x
," DISK OVERRUN ");
563 /* print the bottom line */
564 cursor_to(0,channels
+3);
565 print_hline(botline
,COLOR_RED
);
569 void terminal_main_loop(int quiet
){
579 read(STDIN_FILENO
,&buf
,1);
592 for(i
=0;i
<channels
;i
++){
594 peak_hold_dB
[i
]=peak_dB
[i
];
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 -----------------------------------------------------------------------------------------------------