rev |
line source |
paulo@0
|
1 /*
|
paulo@0
|
2 Copyright (C) 2002 Anthony Van Groningen
|
paulo@0
|
3
|
paulo@0
|
4 This program is free software; you can redistribute it and/or modify
|
paulo@0
|
5 it under the terms of the GNU General Public License as published by
|
paulo@0
|
6 the Free Software Foundation; either version 2 of the License, or
|
paulo@0
|
7 (at your option) any later version.
|
paulo@0
|
8
|
paulo@0
|
9 This program is distributed in the hope that it will be useful,
|
paulo@0
|
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
paulo@0
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
paulo@0
|
12 GNU General Public License for more details.
|
paulo@0
|
13
|
paulo@0
|
14 You should have received a copy of the GNU General Public License
|
paulo@0
|
15 along with this program; if not, write to the Free Software
|
paulo@0
|
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
paulo@0
|
17 */
|
paulo@0
|
18
|
paulo@0
|
19 // 20090516 PBA: started modifying metro.c for lsonify
|
paulo@0
|
20
|
paulo@0
|
21 #include <stdlib.h>
|
paulo@0
|
22 #include <stdio.h>
|
paulo@0
|
23 #include <errno.h>
|
paulo@0
|
24 #include <unistd.h>
|
paulo@0
|
25 #include <math.h>
|
paulo@0
|
26 #include <getopt.h>
|
paulo@0
|
27 #include <string.h>
|
paulo@0
|
28
|
paulo@0
|
29 #include <jack/jack.h>
|
paulo@0
|
30 #include <jack/transport.h>
|
paulo@0
|
31
|
paulo@0
|
32 typedef jack_default_audio_sample_t sample_t;
|
paulo@0
|
33
|
paulo@0
|
34 const double PI = 3.14;
|
paulo@0
|
35
|
paulo@0
|
36 jack_client_t *client;
|
paulo@0
|
37 jack_port_t *output_port;
|
paulo@0
|
38 unsigned long sr;
|
paulo@0
|
39 int freq = 880;
|
paulo@0
|
40 int bpm;
|
paulo@0
|
41 jack_nframes_t tone_length, wave_length;
|
paulo@0
|
42 sample_t *wave;
|
paulo@0
|
43 long offset = 0;
|
paulo@0
|
44 int transport_aware = 0;
|
paulo@0
|
45 jack_transport_state_t transport_state;
|
paulo@0
|
46
|
paulo@0
|
47 void
|
paulo@0
|
48 usage ()
|
paulo@0
|
49
|
paulo@0
|
50 {
|
paulo@0
|
51 fprintf (stderr, "\n"
|
paulo@1
|
52 "usage: lsonify \n"
|
paulo@0
|
53 " [ --frequency OR -f frequency (in Hz) ]\n"
|
paulo@0
|
54 " [ --amplitude OR -A maximum amplitude (between 0 and 1) ]\n"
|
paulo@0
|
55 " [ --duration OR -D duration (in ms) ]\n"
|
paulo@0
|
56 " [ --attack OR -a attack (in percent of duration) ]\n"
|
paulo@0
|
57 " [ --decay OR -d decay (in percent of duration) ]\n"
|
paulo@0
|
58 " [ --name OR -n jack name for metronome client ]\n"
|
paulo@0
|
59 " [ --transport OR -t transport aware ]\n"
|
paulo@1
|
60 " [ --nophysical OR -p to prevent auto-connect to all physical ports]\n"
|
paulo@0
|
61 " --bpm OR -b beats per minute\n"
|
paulo@0
|
62 );
|
paulo@0
|
63 }
|
paulo@0
|
64
|
paulo@0
|
65 void
|
paulo@0
|
66 process_silence (jack_nframes_t nframes)
|
paulo@0
|
67 {
|
paulo@0
|
68 sample_t *buffer = (sample_t *) jack_port_get_buffer (output_port, nframes);
|
paulo@0
|
69 memset (buffer, 0, sizeof (jack_default_audio_sample_t) * nframes);
|
paulo@0
|
70 }
|
paulo@0
|
71
|
paulo@0
|
72 void
|
paulo@0
|
73 process_audio (jack_nframes_t nframes)
|
paulo@0
|
74 {
|
paulo@0
|
75
|
paulo@0
|
76 sample_t *buffer = (sample_t *) jack_port_get_buffer (output_port, nframes);
|
paulo@0
|
77 jack_nframes_t frames_left = nframes;
|
paulo@0
|
78
|
paulo@0
|
79 while (wave_length - offset < frames_left) {
|
paulo@0
|
80 memcpy (buffer + (nframes - frames_left), wave + offset, sizeof (sample_t) * (wave_length - offset));
|
paulo@0
|
81 frames_left -= wave_length - offset;
|
paulo@0
|
82 offset = 0;
|
paulo@0
|
83 }
|
paulo@0
|
84 if (frames_left > 0) {
|
paulo@0
|
85 memcpy (buffer + (nframes - frames_left), wave + offset, sizeof (sample_t) * frames_left);
|
paulo@0
|
86 offset += frames_left;
|
paulo@0
|
87 }
|
paulo@0
|
88 }
|
paulo@0
|
89
|
paulo@0
|
90 int
|
paulo@0
|
91 process (jack_nframes_t nframes, void *arg)
|
paulo@0
|
92 {
|
paulo@0
|
93 if (transport_aware) {
|
paulo@0
|
94 jack_position_t pos;
|
paulo@0
|
95
|
paulo@0
|
96 if (jack_transport_query (client, &pos)
|
paulo@0
|
97 != JackTransportRolling) {
|
paulo@0
|
98
|
paulo@0
|
99 process_silence (nframes);
|
paulo@0
|
100 return 0;
|
paulo@0
|
101 }
|
paulo@0
|
102 offset = pos.frame % wave_length;
|
paulo@0
|
103 }
|
paulo@0
|
104 process_audio (nframes);
|
paulo@0
|
105 return 0;
|
paulo@0
|
106 }
|
paulo@0
|
107
|
paulo@0
|
108 int
|
paulo@3
|
109 sample_rate_change (jack_nframes_t nframes, void *arg) {
|
paulo@3
|
110 if (nframes != sr) {
|
paulo@3
|
111 printf("Sample rate has changed! Exiting...\n");
|
paulo@3
|
112 exit(-1);
|
paulo@3
|
113 }
|
paulo@3
|
114 return 0;
|
paulo@0
|
115 }
|
paulo@0
|
116
|
paulo@0
|
117 int
|
paulo@0
|
118 main (int argc, char *argv[])
|
paulo@0
|
119 {
|
paulo@0
|
120
|
paulo@0
|
121 sample_t scale;
|
paulo@0
|
122 int i, attack_length, decay_length;
|
paulo@0
|
123 double *amp;
|
paulo@0
|
124 double max_amp = 0.5;
|
paulo@0
|
125 int opt;
|
paulo@0
|
126 int got_bpm = 0;
|
paulo@0
|
127 int attack_percent = 1, decay_percent = 10, dur_arg = 100;
|
paulo@0
|
128 char *client_name = 0;
|
paulo@1
|
129 char *port_string = "out";
|
paulo@0
|
130 int verbose = 0;
|
paulo@1
|
131 int connect_physical_ports = 1;
|
paulo@0
|
132 jack_status_t status;
|
paulo@0
|
133
|
paulo@1
|
134 const char *options = "f:A:D:a:d:b:n:tphv";
|
paulo@0
|
135 struct option long_options[] =
|
paulo@0
|
136 {
|
paulo@0
|
137 {"frequency", 1, 0, 'f'},
|
paulo@0
|
138 {"amplitude", 1, 0, 'A'},
|
paulo@0
|
139 {"duration", 1, 0, 'D'},
|
paulo@0
|
140 {"attack", 1, 0, 'a'},
|
paulo@0
|
141 {"decay", 1, 0, 'd'},
|
paulo@0
|
142 {"bpm", 1, 0, 'b'},
|
paulo@0
|
143 {"name", 1, 0, 'n'},
|
paulo@0
|
144 {"transport", 0, 0, 't'},
|
paulo@1
|
145 {"nophysical", 0, 0, 'p'},
|
paulo@0
|
146 {"help", 0, 0, 'h'},
|
paulo@0
|
147 {"verbose", 0, 0, 'v'},
|
paulo@0
|
148 {0, 0, 0, 0}
|
paulo@0
|
149 };
|
paulo@0
|
150
|
paulo@1
|
151 while ((opt = getopt_long (argc, argv, options, long_options, NULL)) != EOF) {
|
paulo@0
|
152 switch (opt) {
|
paulo@0
|
153 case 'f':
|
paulo@0
|
154 if ((freq = atoi (optarg)) <= 0) {
|
paulo@0
|
155 fprintf (stderr, "invalid frequency\n");
|
paulo@0
|
156 return -1;
|
paulo@0
|
157 }
|
paulo@0
|
158 break;
|
paulo@0
|
159 case 'A':
|
paulo@0
|
160 if (((max_amp = atof (optarg)) <= 0)|| (max_amp > 1)) {
|
paulo@0
|
161 fprintf (stderr, "invalid amplitude\n");
|
paulo@0
|
162 return -1;
|
paulo@0
|
163 }
|
paulo@0
|
164 break;
|
paulo@0
|
165 case 'D':
|
paulo@0
|
166 dur_arg = atoi (optarg);
|
paulo@0
|
167 fprintf (stderr, "durarg = %u\n", dur_arg);
|
paulo@0
|
168 break;
|
paulo@0
|
169 case 'a':
|
paulo@0
|
170 if (((attack_percent = atoi (optarg)) < 0) || (attack_percent > 100)) {
|
paulo@0
|
171 fprintf (stderr, "invalid attack percent\n");
|
paulo@0
|
172 return -1;
|
paulo@0
|
173 }
|
paulo@0
|
174 break;
|
paulo@0
|
175 case 'd':
|
paulo@0
|
176 if (((decay_percent = atoi (optarg)) < 0) || (decay_percent > 100)) {
|
paulo@0
|
177 fprintf (stderr, "invalid decay percent\n");
|
paulo@0
|
178 return -1;
|
paulo@0
|
179 }
|
paulo@0
|
180 break;
|
paulo@0
|
181 case 'b':
|
paulo@0
|
182 got_bpm = 1;
|
paulo@0
|
183 if ((bpm = atoi (optarg)) < 0) {
|
paulo@0
|
184 fprintf (stderr, "invalid bpm\n");
|
paulo@0
|
185 return -1;
|
paulo@0
|
186 }
|
paulo@0
|
187 break;
|
paulo@0
|
188 case 'n':
|
paulo@0
|
189 client_name = (char *) malloc (strlen (optarg) * sizeof (char));
|
paulo@0
|
190 strcpy (client_name, optarg);
|
paulo@0
|
191 break;
|
paulo@0
|
192 case 'v':
|
paulo@0
|
193 verbose = 1;
|
paulo@0
|
194 break;
|
paulo@0
|
195 case 't':
|
paulo@0
|
196 transport_aware = 1;
|
paulo@0
|
197 break;
|
paulo@1
|
198 case 'p':
|
paulo@1
|
199 connect_physical_ports = 0;
|
paulo@1
|
200 break;
|
paulo@0
|
201 default:
|
paulo@0
|
202 fprintf (stderr, "unknown option %c\n", opt);
|
paulo@0
|
203 case 'h':
|
paulo@0
|
204 usage ();
|
paulo@0
|
205 return -1;
|
paulo@0
|
206 }
|
paulo@0
|
207 }
|
paulo@0
|
208 if (!got_bpm) {
|
paulo@0
|
209 fprintf (stderr, "bpm not specified\n");
|
paulo@0
|
210 usage ();
|
paulo@0
|
211 return -1;
|
paulo@0
|
212 }
|
paulo@0
|
213
|
paulo@0
|
214 /* Initial Jack setup, get sample rate */
|
paulo@0
|
215 if (!client_name) {
|
paulo@1
|
216 pid_t pid = getpid();
|
paulo@1
|
217 client_name = (char *) malloc (32 * sizeof (char));
|
paulo@1
|
218 snprintf (client_name, 32, "metro_%d", pid);
|
paulo@0
|
219 }
|
paulo@0
|
220 if ((client = jack_client_open (client_name, JackNoStartServer, &status)) == 0) {
|
paulo@0
|
221 fprintf (stderr, "jack server not running?\n");
|
paulo@0
|
222 return 1;
|
paulo@0
|
223 }
|
paulo@0
|
224 jack_set_process_callback (client, process, 0);
|
paulo@1
|
225 output_port = jack_port_register (client, port_string, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
paulo@0
|
226
|
paulo@0
|
227 sr = jack_get_sample_rate (client);
|
paulo@3
|
228 jack_set_sample_rate_callback (client, sample_rate_change, 0);
|
paulo@0
|
229
|
paulo@0
|
230 /* setup wave table parameters */
|
paulo@0
|
231 wave_length = 60 * sr / bpm;
|
paulo@0
|
232 tone_length = sr * dur_arg / 1000;
|
paulo@0
|
233 attack_length = tone_length * attack_percent / 100;
|
paulo@0
|
234 decay_length = tone_length * decay_percent / 100;
|
paulo@0
|
235 scale = 2 * PI * freq / sr;
|
paulo@0
|
236
|
paulo@0
|
237 if (tone_length >= wave_length) {
|
paulo@0
|
238 fprintf (stderr, "invalid duration (tone length = %" PRIu32
|
paulo@0
|
239 ", wave length = %" PRIu32 "\n", tone_length,
|
paulo@0
|
240 wave_length);
|
paulo@0
|
241 return -1;
|
paulo@0
|
242 }
|
paulo@0
|
243 if (attack_length + decay_length > (int)tone_length) {
|
paulo@0
|
244 fprintf (stderr, "invalid attack/decay\n");
|
paulo@0
|
245 return -1;
|
paulo@0
|
246 }
|
paulo@0
|
247
|
paulo@0
|
248 /* Build the wave table */
|
paulo@0
|
249 wave = (sample_t *) malloc (wave_length * sizeof(sample_t));
|
paulo@0
|
250 amp = (double *) malloc (tone_length * sizeof(double));
|
paulo@0
|
251
|
paulo@0
|
252 for (i = 0; i < attack_length; i++) {
|
paulo@0
|
253 amp[i] = max_amp * i / ((double) attack_length);
|
paulo@0
|
254 }
|
paulo@0
|
255 for (i = attack_length; i < (int)tone_length - decay_length; i++) {
|
paulo@0
|
256 amp[i] = max_amp;
|
paulo@0
|
257 }
|
paulo@0
|
258 for (i = (int)tone_length - decay_length; i < (int)tone_length; i++) {
|
paulo@0
|
259 amp[i] = - max_amp * (i - (double) tone_length) / ((double) decay_length);
|
paulo@0
|
260 }
|
paulo@0
|
261 for (i = 0; i < (int)tone_length; i++) {
|
paulo@0
|
262 wave[i] = amp[i] * sin (scale * i);
|
paulo@0
|
263 }
|
paulo@0
|
264 for (i = tone_length; i < (int)wave_length; i++) {
|
paulo@0
|
265 wave[i] = 0;
|
paulo@0
|
266 }
|
paulo@0
|
267
|
paulo@0
|
268 if (jack_activate (client)) {
|
paulo@0
|
269 fprintf (stderr, "cannot activate client");
|
paulo@0
|
270 return 1;
|
paulo@0
|
271 }
|
paulo@0
|
272
|
paulo@1
|
273 /* connect to physical ports */
|
paulo@1
|
274 if (connect_physical_ports) {
|
paulo@1
|
275 const char **ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
|
paulo@1
|
276 const char **c;
|
paulo@1
|
277 for (c = ports; c && *c; c++)
|
paulo@1
|
278 jack_connect (client, jack_port_name(output_port), *c);
|
paulo@1
|
279 }
|
paulo@1
|
280
|
paulo@0
|
281 while (1) {
|
paulo@0
|
282 sleep(1);
|
paulo@0
|
283 };
|
paulo@0
|
284
|
paulo@0
|
285 }
|