Going to rename ChangeLog to NEWS
[jackmeter.git] / jack_meter.c
blob714c08b82ac778c8e9c5c5e9022c511e0253fc3a
1 /*
3 jackmeter.c
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.
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <math.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <unistd.h>
30 #include <jack/jack.h>
31 #include <getopt.h>
32 #include "config.h"
35 float bias = 1.0f;
36 float peak = 0.0f;
38 int dpeak = 0;
39 int dtime = 0;
40 int decay_len;
42 jack_port_t *input_port = NULL;
43 jack_client_t *client = NULL;
47 /* Read and reset the recent peak sample */
48 static float read_peak()
50 float tmp = peak;
51 peak = 0.0f;
53 return tmp;
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;
62 unsigned int i;
65 /* just incase the port isn't registered yet */
66 if (input_port == NULL) {
67 return 0;
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]);
75 if (s > peak) {
76 peak = s;
81 return 0;
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 */
92 if (db < -70.0f) {
93 def = 0.0f;
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;
106 } else {
107 def = 100.0f;
110 return (int)( (def / 100.0f) * ((float) size) );
114 /* Close down JACK when exiting */
115 static void cleanup()
117 const char **all_ports;
118 unsigned int i;
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)
140 jack_port_t *port;
142 // Get the port we are connecting to
143 port = jack_port_by_name(client, port_name);
144 if (port == NULL) {
145 fprintf(stderr, "Can't find port '%s'\n", port_name);
146 exit(1);
149 // Connect the port to our input port
150 printf("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));
153 exit(1);
158 /* Sleep for a fraction of a second */
159 static int fsleep( float secs )
162 //#ifdef HAVE_USLEEP
163 return usleep( secs * 1000000 );
164 //#endif
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] [<port>]\n\n", progname);
173 fprintf(stderr, "where freqency is how often to update the meter per second [8]\n");
174 fprintf(stderr, " ref-level is the reference signal level for 0dB on the meter\n");
175 fprintf(stderr, " width is how wide to make the meter [79]\n");
176 fprintf(stderr, " port is the JACK port to monitor\n");
177 exit(1);
181 void display_scale( int width )
183 int i=0;
184 const int marks[11] = { 0, -5, -10, -15, -20, -25, -30, -35, -40, -50, -60 };
185 char *scale = malloc( width+1 );
186 char *line = malloc( width+1 );
189 // Initialise the scale
190 for(i=0; i<width; i++) { scale[i] = ' '; line[i]='_'; }
191 scale[width] = 0;
192 line[width] = 0;
195 // 'draw' on each of the db marks
196 for(i=0; i < 11; i++) {
197 char mark[5];
198 int pos = iec_scale( marks[i], width )-1;
199 int spos, slen;
201 // Create string of the db value
202 snprintf(mark, 4, "%d", marks[i]);
204 // Position the label string
205 slen = strlen(mark);
206 spos = pos-(slen/2);
207 if (spos<0) spos=0;
208 if (spos+strlen(mark)>width) spos=width-slen;
209 memcpy( scale+spos, mark, slen );
211 // Position little marker
212 line[pos] = '|';
215 // Print it to screen
216 printf("%s\n", scale);
217 printf("%s\n", line);
218 free(scale);
219 free(line);
223 void display_meter( float peak, int width )
225 float db = 20.0f * log10f(peak * bias);
226 int size = iec_scale( db, width );
227 int i;
229 if (size > dpeak) {
230 dpeak = size;
231 dtime = 0;
232 } else if (dtime++ > decay_len) {
233 dpeak = size;
236 printf("\r");
238 for(i=0; i<size-1; i++) { printf("#"); }
240 if (dpeak==size) {
241 printf("I");
242 } else {
243 printf("#");
244 for(i=0; i<dpeak-size-1; i++) { printf(" "); }
245 printf("I");
248 for(i=0; i<width-dpeak; i++) { printf(" "); }
252 int main(int argc, char *argv[])
254 int console_width = 79;
255 char client_name[255];
256 int running = 1;
257 float ref_lev;
258 int rate = 8;
259 int opt;
261 // Make STDOUT unbuffered
262 setbuf(stdout, NULL);
264 while ((opt = getopt(argc, argv, "w:f:r:h")) != -1) {
265 switch (opt) {
266 case 'r':
267 ref_lev = atof(optarg);
268 printf("Reference level: %.1fdB\n", ref_lev);
269 bias = powf(10.0f, ref_lev * -0.05f);
270 break;
271 case 'f':
272 rate = atoi(optarg);
273 printf("Updates per second: %d\n", rate);
274 break;
275 case 'w':
276 console_width = atoi(optarg);
277 printf("Console Width: %d\n", console_width);
278 break;
279 case 'h':
280 /* Force help to be shown */
281 usage( argv[0] );
282 break;
283 default:
284 usage( argv[0] );
285 break;
291 // Register with Jack
292 snprintf(client_name, 255, "meter-%d", getpid());
293 if ((client = jack_client_new(client_name)) == 0) {
294 fprintf(stderr, "JACK server not running?\n");
295 exit(1);
297 printf("Registering as %s.\n", client_name);
299 // Create our input port
300 if (!(input_port = jack_port_register(client, "meter", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0))) {
301 fprintf(stderr, "Cannot register input port 'meter'.\n");
302 exit(1);
305 // Register the cleanup function to be called when program exits
306 atexit( cleanup );
308 // Register the peak signal callback
309 jack_set_process_callback(client, process_peak, 0);
312 if (jack_activate(client)) {
313 fprintf(stderr, "Cannot activate client.\n");
314 exit(1);
318 // Connect our port to specified port
319 if (argc > optind) {
320 connect_port( client, argv[ optind ] );
321 } else {
322 printf("Meter is not connected to a port.\n");
325 // Calculate the decay length (should be 1600ms)
326 decay_len = (int)(1.6f / (1.0f/rate));
329 // Display the scale
330 display_scale( console_width );
332 while (running) {
333 display_meter( read_peak(), console_width );
334 fsleep( 1.0f/rate );
337 return 0;