metro.c 7.29 KB
Newer Older
sletz's avatar
sletz committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
    Copyright (C) 2002 Anthony Van Groningen
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdlib.h>
#include <stdio.h>
sletz's avatar
Cleanup    
sletz committed
21
#include <errno.h>
sletz's avatar
sletz committed
22
#ifndef WIN32
sletz's avatar
Cleanup    
sletz committed
23
#include <unistd.h>
sletz's avatar
sletz committed
24
#endif
sletz's avatar
sletz committed
25
#include <math.h>
sletz's avatar
sletz committed
26
#include <signal.h>
sletz's avatar
sletz committed
27
28
#include <getopt.h>
#include <string.h>
29

sletz's avatar
sletz committed
30
#include <jack/jack.h>
31
#include <jack/transport.h>
sletz's avatar
sletz committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

typedef jack_default_audio_sample_t sample_t;

const double PI = 3.14;

jack_client_t *client;
jack_port_t *output_port;
unsigned long sr;
int freq = 880;
int bpm;
jack_nframes_t tone_length, wave_length;
sample_t *wave;
long offset = 0;
int transport_aware = 0;
jack_transport_state_t transport_state;

48
49
50
51
52
53
54
55
static void signal_handler(int sig)
{
	jack_client_close(client);
	fprintf(stderr, "signal received, exiting ...\n");
	exit(0);
}

static void
sletz's avatar
sletz committed
56
57
58
usage ()
{
	fprintf (stderr, "\n"
59
60
61
62
63
64
65
66
67
68
"usage: jack_metro \n"
"              [ --frequency OR -f frequency (in Hz) ]\n"
"              [ --amplitude OR -A maximum amplitude (between 0 and 1) ]\n"
"              [ --duration OR -D duration (in ms) ]\n"
"              [ --attack OR -a attack (in percent of duration) ]\n"
"              [ --decay OR -d decay (in percent of duration) ]\n"
"              [ --name OR -n jack name for metronome client ]\n"
"              [ --transport OR -t transport aware ]\n"
"              --bpm OR -b beats per minute\n"
);
sletz's avatar
sletz committed
69
70
}

71
static void
sletz's avatar
sletz committed
72
73
74
75
76
77
process_silence (jack_nframes_t nframes) 
{
	sample_t *buffer = (sample_t *) jack_port_get_buffer (output_port, nframes);
	memset (buffer, 0, sizeof (jack_default_audio_sample_t) * nframes);
}

78
79
static void
process_audio (jack_nframes_t nframes) 
sletz's avatar
sletz committed
80
81
82
83
84
85
86
87
88
89
90
91
92
{
	sample_t *buffer = (sample_t *) jack_port_get_buffer (output_port, nframes);
	jack_nframes_t frames_left = nframes;
		
	while (wave_length - offset < frames_left) {
		memcpy (buffer + (nframes - frames_left), wave + offset, sizeof (sample_t) * (wave_length - offset));
		frames_left -= wave_length - offset;
		offset = 0;
	}
	if (frames_left > 0) {
		memcpy (buffer + (nframes - frames_left), wave + offset, sizeof (sample_t) * frames_left);
		offset += frames_left;
	}
sletz's avatar
sletz committed
93
94
}

95
static int
sletz's avatar
sletz committed
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
process (jack_nframes_t nframes, void *arg)
{
	if (transport_aware) {
		jack_position_t pos;

		if (jack_transport_query (client, &pos)
		    != JackTransportRolling) {

			process_silence (nframes);
			return 0;
		}
		offset = pos.frame % wave_length;
	}
	process_audio (nframes);
	return 0;
}
112

sletz's avatar
sletz committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
int
main (int argc, char *argv[])
{
	sample_t scale;
	int i, attack_length, decay_length;
	double *amp;
	double max_amp = 0.5;
	int option_index;
	int opt;
	int got_bpm = 0;
	int attack_percent = 1, decay_percent = 10, dur_arg = 100;
	char *client_name = 0;
	char *bpm_string = "bpm";
	int verbose = 0;
127
	jack_status_t status;
sletz's avatar
sletz committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

	const char *options = "f:A:D:a:d:b:n:thv";
	struct option long_options[] =
	{
		{"frequency", 1, 0, 'f'},
		{"amplitude", 1, 0, 'A'},
		{"duration", 1, 0, 'D'},
		{"attack", 1, 0, 'a'},
		{"decay", 1, 0, 'd'},
		{"bpm", 1, 0, 'b'},
		{"name", 1, 0, 'n'},
		{"transport", 0, 0, 't'},
		{"help", 0, 0, 'h'},
		{"verbose", 0, 0, 'v'},
		{0, 0, 0, 0}
	};
144
	
sletz's avatar
sletz committed
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
	while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != EOF) {
		switch (opt) {
		case 'f':
			if ((freq = atoi (optarg)) <= 0) {
				fprintf (stderr, "invalid frequency\n");
				return -1;
			}
			break;
		case 'A':
			if (((max_amp = atof (optarg)) <= 0)|| (max_amp > 1)) {
				fprintf (stderr, "invalid amplitude\n");
				return -1;
			}
			break;
		case 'D':
			dur_arg = atoi (optarg);
			fprintf (stderr, "durarg = %u\n", dur_arg);
			break;
		case 'a':
			if (((attack_percent = atoi (optarg)) < 0) || (attack_percent > 100)) {
				fprintf (stderr, "invalid attack percent\n");
				return -1;
			}
			break;
		case 'd':
			if (((decay_percent = atoi (optarg)) < 0) || (decay_percent > 100)) {
				fprintf (stderr, "invalid decay percent\n");
				return -1;
			}
			break;
		case 'b':
			got_bpm = 1;
			if ((bpm = atoi (optarg)) < 0) {
				fprintf (stderr, "invalid bpm\n");
				return -1;
			}
			bpm_string = (char *) malloc ((strlen (optarg) + 4) * sizeof (char));
			strcpy (bpm_string, optarg);
			strcat (bpm_string, "_bpm");
			break;
		case 'n':
			client_name = (char *) malloc (strlen (optarg) * sizeof (char));
			strcpy (client_name, optarg);
			break;
		case 'v':
			verbose = 1;
			break;
		case 't':
			transport_aware = 1;
			break;
		default:
			fprintf (stderr, "unknown option %c\n", opt); 
		case 'h':
			usage ();
			return -1;
		}
	}
	if (!got_bpm) {
		fprintf (stderr, "bpm not specified\n");
		usage ();
		return -1;
	}

	/* Initial Jack setup, get sample rate */
	if (!client_name) {
		client_name = (char *) malloc (9 * sizeof (char));
		strcpy (client_name, "metro");
	}
213
	if ((client = jack_client_open (client_name, JackNoStartServer, &status)) == 0) {
sletz's avatar
sletz committed
214
215
216
		fprintf (stderr, "jack server not running?\n");
		return 1;
	}
217
	jack_set_process_callback (client, process, 0);
sletz's avatar
sletz committed
218
	output_port = jack_port_register (client, bpm_string, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
219
220

	sr = jack_get_sample_rate (client);
sletz's avatar
sletz committed
221
222
223
224
225
226
227
228
229

	/* setup wave table parameters */
	wave_length = 60 * sr / bpm;
	tone_length = sr * dur_arg / 1000;
	attack_length = tone_length * attack_percent / 100;
	decay_length = tone_length * decay_percent / 100;
	scale = 2 * PI * freq / sr;

	if (tone_length >= wave_length) {
230
		fprintf (stderr, "invalid duration (tone length = %u, wave length = %u\n", tone_length, wave_length);
sletz's avatar
sletz committed
231
232
233
234
235
236
237
238
239
240
241
242
243
244
		return -1;
	}
	if (attack_length + decay_length > (int)tone_length) {
		fprintf (stderr, "invalid attack/decay\n");
		return -1;
	}

	/* Build the wave table */
	wave = (sample_t *) malloc (wave_length * sizeof(sample_t));
	amp = (double *) malloc (tone_length * sizeof(double));

	for (i = 0; i < attack_length; i++) {
		amp[i] = max_amp * i / ((double) attack_length);
	}
245
	for (i = attack_length; i < (int)tone_length - decay_length; i++) {
sletz's avatar
sletz committed
246
247
248
249
250
		amp[i] = max_amp;
	}
	for (i = (int)tone_length - decay_length; i < (int)tone_length; i++) {
		amp[i] = - max_amp * (i - (double) tone_length) / ((double) decay_length);
	}
251
	for (i = 0; i < (int)tone_length; i++) {
sletz's avatar
sletz committed
252
253
		wave[i] = amp[i] * sin (scale * i);
	}
254
	for (i = tone_length; i < (int)wave_length; i++) {
sletz's avatar
sletz committed
255
256
		wave[i] = 0;
	}
257
258

	if (jack_activate (client)) {
259
		fprintf (stderr, "cannot activate client\n");
sletz's avatar
sletz committed
260
		goto error;
sletz's avatar
sletz committed
261
262
	}
    
sletz's avatar
Cleanup    
sletz committed
263
    /* install a signal handler to properly quits jack client */
sletz's avatar
sletz committed
264
#ifdef WIN32
sletz's avatar
Cleanup    
sletz committed
265
266
267
268
269
270
271
272
	signal(SIGINT, signal_handler);
    signal(SIGABRT, signal_handler);
	signal(SIGTERM, signal_handler);
#else
	signal(SIGQUIT, signal_handler);
	signal(SIGTERM, signal_handler);
	signal(SIGHUP, signal_handler);
	signal(SIGINT, signal_handler);
sletz's avatar
sletz committed
273
#endif
274
275

    /* run until interrupted */
sletz's avatar
Cleanup    
sletz committed
276
277
278
279
280
	while (1) {
	#ifdef WIN32
		Sleep(1000);
	#else
		sleep(1);
sletz's avatar
sletz committed
281
	#endif
sletz's avatar
sletz committed
282
	};
283
	
sletz's avatar
sletz committed
284
    jack_client_close(client);
sletz's avatar
sletz committed
285
286
287
288
    
error:
    free(amp);
    free(wave);
289
    exit (0);
sletz's avatar
sletz committed
290
}