4 Simple console based Digital Peak Meter for JACK
5 Copyright (C) 2005 Nicholas J. Humfrey
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program 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 this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <sys/types.h>
30 #include <jack/jack.h>
41 char *server_name
= NULL
;
42 jack_port_t
*input_port
= NULL
;
43 jack_client_t
*client
= NULL
;
44 jack_options_t options
= JackNoStartServer
;
47 /* Read and reset the recent peak sample */
48 static float read_peak()
57 /* Callback called by JACK when audio is available.
58 Stores value of peak sample */
59 static int process_peak(jack_nframes_t nframes
, void *arg
)
61 jack_default_audio_sample_t
*in
;
65 /* just incase the port isn't registered yet */
66 if (input_port
== NULL
) {
71 /* get the audio samples, and find the peak sample */
72 in
= (jack_default_audio_sample_t
*) jack_port_get_buffer(input_port
, nframes
);
73 for (i
= 0; i
< nframes
; i
++) {
74 const float s
= fabs(in
[i
]);
86 db: the signal stength in db
87 width: the size of the meter
89 static int iec_scale(float db
, int size
) {
90 float def
= 0.0f
; /* Meter deflection %age */
94 } else if (db
< -60.0f
) {
95 def
= (db
+ 70.0f
) * 0.25f
;
96 } else if (db
< -50.0f
) {
97 def
= (db
+ 60.0f
) * 0.5f
+ 2.5f
;
98 } else if (db
< -40.0f
) {
99 def
= (db
+ 50.0f
) * 0.75f
+ 7.5;
100 } else if (db
< -30.0f
) {
101 def
= (db
+ 40.0f
) * 1.5f
+ 15.0f
;
102 } else if (db
< -20.0f
) {
103 def
= (db
+ 30.0f
) * 2.0f
+ 30.0f
;
104 } else if (db
< 0.0f
) {
105 def
= (db
+ 20.0f
) * 2.5f
+ 50.0f
;
110 return (int)( (def
/ 100.0f
) * ((float) size
) );
114 /* Close down JACK when exiting */
115 static void cleanup()
117 const char **all_ports
;
120 fprintf(stderr
,"cleanup()\n");
122 if (input_port
!= NULL
) {
124 all_ports
= jack_port_get_all_connections(client
, input_port
);
126 for (i
=0; all_ports
&& all_ports
[i
]; i
++) {
127 jack_disconnect(client
, all_ports
[i
], jack_port_name(input_port
));
131 /* Leave the jack graph */
132 jack_client_close(client
);
137 /* Connect the chosen port to ours */
138 static void connect_port(jack_client_t
*client
, char *port_name
)
142 // Get the port we are connecting to
143 port
= jack_port_by_name(client
, port_name
);
145 fprintf(stderr
, "Can't find port '%s'\n", port_name
);
149 // Connect the port to our input port
150 fprintf(stderr
,"Connecting '%s' to '%s'...\n", jack_port_name(port
), jack_port_name(input_port
));
151 if (jack_connect(client
, jack_port_name(port
), jack_port_name(input_port
))) {
152 fprintf(stderr
, "Cannot connect port '%s' to '%s'\n", jack_port_name(port
), jack_port_name(input_port
));
158 /* Sleep for a fraction of a second */
159 static int fsleep( float secs
)
163 return usleep( secs
* 1000000 );
168 /* Display how to use this program */
169 static int usage( const char * progname
)
171 fprintf(stderr
, "jackmeter version %s\n\n", VERSION
);
172 fprintf(stderr
, "Usage %s [-f freqency] [-r ref-level] [-w width] [-s servername] [-n] [<port>, ...]\n\n", progname
);
173 fprintf(stderr
, "where -f is how often to update the meter per second [8]\n");
174 fprintf(stderr
, " -r is the reference signal level for 0dB on the meter\n");
175 fprintf(stderr
, " -w is how wide to make the meter [79]\n");
176 fprintf(stderr
, " -s is the [optional] name given the jack server when it was started\n");
177 fprintf(stderr
, " -n changes mode to output meter level as number in decibels\n");
178 fprintf(stderr
, " <port> the port(s) to monitor (multiple ports are mixed)\n");
183 void display_scale( int width
)
186 const int marks
[11] = { 0, -5, -10, -15, -20, -25, -30, -35, -40, -50, -60 };
187 char *scale
= malloc( width
+1 );
188 char *line
= malloc( width
+1 );
191 // Initialise the scale
192 for(i
=0; i
<width
; i
++) { scale
[i
] = ' '; line
[i
]='_'; }
197 // 'draw' on each of the db marks
198 for(i
=0; i
< 11; i
++) {
200 int pos
= iec_scale( marks
[i
], width
)-1;
203 // Create string of the db value
204 snprintf(mark
, 4, "%d", marks
[i
]);
206 // Position the label string
210 if (spos
+strlen(mark
)>width
) spos
=width
-slen
;
211 memcpy( scale
+spos
, mark
, slen
);
213 // Position little marker
217 // Print it to screen
218 printf("%s\n", scale
);
219 printf("%s\n", line
);
225 void display_meter( int db
, int width
)
227 int size
= iec_scale( db
, width
);
233 } else if (dtime
++ > decay_len
) {
239 for(i
=0; i
<size
-1; i
++) { printf("#"); }
245 for(i
=0; i
<dpeak
-size
-1; i
++) { printf(" "); }
249 for(i
=0; i
<width
-dpeak
; i
++) { printf(" "); }
253 int main(int argc
, char *argv
[])
255 int console_width
= 79;
256 jack_status_t status
;
259 int decibels_mode
= 0;
263 // Make STDOUT unbuffered
264 setbuf(stdout
, NULL
);
266 while ((opt
= getopt(argc
, argv
, "s:w:f:r:nhv")) != -1) {
269 server_name
= (char *) malloc (sizeof (char) * strlen(optarg
));
270 strcpy (server_name
, optarg
);
271 options
|= JackServerName
;
274 ref_lev
= atof(optarg
);
275 fprintf(stderr
,"Reference level: %.1fdB\n", ref_lev
);
276 bias
= powf(10.0f
, ref_lev
* -0.05f
);
280 fprintf(stderr
,"Updates per second: %d\n", rate
);
283 console_width
= atoi(optarg
);
284 fprintf(stderr
,"Console Width: %d\n", console_width
);
292 /* Show usage/version information */
300 // Register with Jack
301 if ((client
= jack_client_open("meter", options
, &status
, server_name
)) == 0) {
302 fprintf(stderr
, "Failed to start jack client: %d\n", status
);
305 fprintf(stderr
,"Registering as '%s'.\n", jack_get_client_name( client
) );
307 // Create our input port
308 if (!(input_port
= jack_port_register(client
, "in", JACK_DEFAULT_AUDIO_TYPE
, JackPortIsInput
, 0))) {
309 fprintf(stderr
, "Cannot register input port 'meter'.\n");
313 // Register the cleanup function to be called when program exits
316 // Register the peak signal callback
317 jack_set_process_callback(client
, process_peak
, 0);
320 if (jack_activate(client
)) {
321 fprintf(stderr
, "Cannot activate client.\n");
326 // Connect our port to specified port(s)
328 while (argc
> optind
) {
329 connect_port( client
, argv
[ optind
] );
333 fprintf(stderr
,"Meter is not connected to a port.\n");
336 // Calculate the decay length (should be 1600ms)
337 decay_len
= (int)(1.6f
/ (1.0f
/rate
));
341 if (decibels_mode
==0) {
342 display_scale( console_width
);
346 float db
= 20.0f
* log10f(read_peak() * bias
);
348 if (decibels_mode
==1) {
349 printf("%1.1f\n", db
);
351 display_meter( db
, console_width
);