]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/main.c
Major changes. Added metering and power spectrum, other fixes. Rearranged headers...
[dttsp.git] / jDttSP / main.c
1 /* main.c
2
3 This file is part of a program that implements a Software-Defined Radio.
4
5 Copyright (C) 2004-5 by Frank Brickle, AB2KT and Bob McGwier, N4HY
6
7 This program 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 of the License, or
10 (at your option) any later version.
11
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.
16
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
20
21 The authors can be reached by email at
22
23 ab2kt@arrl.net
24 or
25 rwmcgwier@comcast.net
26
27 or by paper mail at
28
29 The DTTS Microwave Society
30 6 Kathleen Place
31 Bridgewater, NJ 08807
32 */  
33   
34 #include <common.h>
35   
36 /////////////////////////////////////////////////////////////////////////
37 // elementary defaults
38
39 struct _loc loc;  
40
41 /////////////////////////////////////////////////////////////////////////
42 // most of what little we know here about the inner loop,
43 // functionally speaking
44
45 extern void reset_meters(void);
46 extern void reset_spectrum(void);
47 extern void reset_counters(void);
48 extern void process_samples(float *, float *, float *, float *, int);
49 extern void setup_workspace(void);
50 extern void destroy_workspace(void);
51
52 //========================================================================
53
54 PRIVATE void
55 spectrum_thread(void) {
56
57   while (top.running) {
58     sem_wait(&top.sync.pws.sem);
59
60     compute_spectrum(&uni.spec);
61
62     if (fwrite((char *) &uni.spec.label, sizeof(int), 1, top.meas.spec.fp)
63         != 1) {
64       fprintf(stderr, "error writing spectrum label\n");
65       exit(1);
66     }
67
68     if (fwrite((char *) uni.spec.output, sizeof(float), uni.spec.size, top.meas.spec.fp)
69         != uni.spec.size) {
70       fprintf(stderr, "error writing spectrum\n");
71       exit(1);
72     }
73
74     fflush(top.meas.spec.fp);
75   }
76
77   pthread_exit(0);
78 }
79
80 PRIVATE void
81 meter_thread(void) {
82
83   while (top.running) {
84     sem_wait(&top.sync.mtr.sem);
85
86     if (fwrite((char *) &uni.meter.label, sizeof(int), 1, top.meas.mtr.fp)
87         != 1) {
88       fprintf(stderr, "error writing meter label\n");
89       exit(1);
90     }
91
92     if (fwrite((char *) uni.meter.snap.rx, sizeof(REAL), MAXRX * RXMETERPTS, top.meas.mtr.fp)
93         != MAXRX * RXMETERPTS) {
94       fprintf(stderr, "error writing rx meter\n");
95       exit(1);
96     }
97
98     if (fwrite((char *) uni.meter.snap.tx, sizeof(REAL), TXMETERPTS, top.meas.mtr.fp)
99         != TXMETERPTS) {
100       fprintf(stderr, "error writing tx meter\n");
101       exit(1);
102     }
103
104     fflush(top.meas.mtr.fp);
105   }
106
107   pthread_exit(0);
108 }
109
110 //========================================================================
111
112 PRIVATE void
113 monitor_thread(void) {
114   while (top.running) {
115     sem_wait(&top.sync.mon.sem);
116     fprintf(stderr,
117             "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n",
118             uni.tick,
119             top.jack.blow.cb,
120             top.jack.blow.rb.i,
121             top.jack.blow.rb.o,
122             top.jack.blow.xr);
123     memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
124   }
125   pthread_exit(0);
126 }
127
128 //========================================================================
129
130 PRIVATE void 
131 process_updates_thread(void) {
132   while (top.running) {
133     pthread_testcancel();
134     while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp))
135       do_update(top.parm.buff, top.verbose ? stderr : 0);
136   }
137   pthread_exit(0);
138 }
139
140 //========================================================================
141
142 PRIVATE void 
143 gethold(void) {
144   if (jack_ringbuffer_write_space(top.jack.ring.o.l)
145       < top.hold.size.bytes) {
146     // pathology
147     jack_ringbuffer_reset(top.jack.ring.o.l);
148     jack_ringbuffer_reset(top.jack.ring.o.r);
149     top.jack.blow.rb.o++;
150   }
151   jack_ringbuffer_write(top.jack.ring.o.l,
152                         (char *) top.hold.buf.l,
153                         top.hold.size.bytes);
154   jack_ringbuffer_write(top.jack.ring.o.r,
155                         (char *) top.hold.buf.r,
156                         top.hold.size.bytes);
157   if (jack_ringbuffer_read_space(top.jack.ring.i.l)
158       < top.hold.size.bytes) {
159     // pathology
160     jack_ringbuffer_reset(top.jack.ring.i.l);
161     jack_ringbuffer_reset(top.jack.ring.i.r);
162     memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
163     memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
164     jack_ringbuffer_reset(top.jack.auxr.i.l);
165     jack_ringbuffer_reset(top.jack.auxr.i.r);
166     memset((char *) top.hold.aux.l, 0, top.hold.size.bytes);
167     memset((char *) top.hold.aux.r, 0, top.hold.size.bytes);
168     top.jack.blow.rb.i++;
169   } else {
170     jack_ringbuffer_read(top.jack.ring.i.l,
171                          (char *) top.hold.buf.l,
172                          top.hold.size.bytes);
173     jack_ringbuffer_read(top.jack.ring.i.r,
174                          (char *) top.hold.buf.r,
175                          top.hold.size.bytes);
176     jack_ringbuffer_read(top.jack.auxr.i.l,
177                          (char *) top.hold.aux.l,
178                          top.hold.size.bytes);
179     jack_ringbuffer_read(top.jack.auxr.i.r,
180                          (char *) top.hold.aux.r,
181                          top.hold.size.bytes);
182   }
183 }
184
185 PRIVATE BOOLEAN
186 canhold(void) {
187   return
188     jack_ringbuffer_read_space(top.jack.ring.i.l)
189     >= top.hold.size.bytes;
190 }
191
192 //------------------------------------------------------------------------
193
194 PRIVATE void 
195 run_mute(void) {
196   memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
197   memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
198   memset((char *) top.hold.aux.l, 0, top.hold.size.bytes);
199   memset((char *) top.hold.aux.r, 0, top.hold.size.bytes);
200   uni.tick++;
201 }
202
203 PRIVATE void 
204 run_pass(void) { uni.tick++; }
205
206 PRIVATE void 
207 run_play(void) {
208   process_samples(top.hold.buf.l, top.hold.buf.r,
209                   top.hold.aux.l, top.hold.aux.r,
210                   top.hold.size.frames);
211
212
213 // NB do not set RUN_SWCH directly via setRunState;
214 // use setSWCH instead
215
216 PRIVATE void 
217 run_swch(void) {
218   if (top.swch.bfct.have == 0) {
219     // first time
220     // apply ramp down
221     int i, m = top.swch.fade, n = top.swch.tail;
222     for (i = 0; i < m; i++) {
223       float w = (float) 1.0 - (float) i / m;
224       top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
225     }
226     memset((char *) (top.hold.buf.l + m), 0, n);
227     memset((char *) (top.hold.buf.r + m), 0, n);
228     top.swch.bfct.have++;
229   } else if (top.swch.bfct.have < top.swch.bfct.want) {
230     // in medias res
231     memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
232     memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
233     top.swch.bfct.have++;
234   } else {
235     // last time
236     // apply ramp up
237     int i, m = top.swch.fade, n = top.swch.tail;
238     for (i = 0; i < m; i++) {
239       float w = (float) i / m;
240       top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
241     }
242     uni.mode.trx = top.swch.trx.next;
243
244     top.state = top.swch.run.last;
245     top.swch.bfct.want = top.swch.bfct.have = 0;
246
247     jack_ringbuffer_reset(top.jack.ring.o.l);
248     jack_ringbuffer_reset(top.jack.ring.o.r);
249
250     reset_meters();
251     reset_spectrum();
252     reset_counters();
253   }
254
255   process_samples(top.hold.buf.l, top.hold.buf.r,
256                   top.hold.aux.l, top.hold.aux.r,
257                   top.hold.size.frames);
258
259
260 //========================================================================
261
262 void
263 clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) {
264   int i;
265   char zero = 0;
266   for (i = 0; i < nbytes; i++)
267     jack_ringbuffer_write(rb, &zero, 1);
268 }
269
270 PRIVATE void 
271 audio_callback(jack_nframes_t nframes, void *arg) {
272   float *lp, *rp;
273   int nbytes = nframes * sizeof(float);
274
275   if (nframes == top.jack.size) {
276
277     // output: copy from ring to port
278     lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
279     rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
280
281     if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) {
282       jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
283       jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
284     } else { // rb pathology
285       memset((char *) lp, 0, nbytes);
286       memset((char *) rp, 0, nbytes);
287       jack_ringbuffer_reset(top.jack.ring.o.l);
288       jack_ringbuffer_reset(top.jack.ring.o.r);
289       clear_jack_ringbuffer(top.jack.ring.o.l, nbytes);
290       clear_jack_ringbuffer(top.jack.ring.o.r, nbytes);
291       top.jack.blow.rb.o++;
292     }
293     
294     // input: copy from port to ring
295     if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) {
296       lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
297       rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
298       jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
299       jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
300       lp = (float *) jack_port_get_buffer(top.jack.auxp.i.l, nframes);
301       rp = (float *) jack_port_get_buffer(top.jack.auxp.i.r, nframes);
302       jack_ringbuffer_write(top.jack.auxr.i.l, (char *) lp, nbytes);
303       jack_ringbuffer_write(top.jack.auxr.i.r, (char *) rp, nbytes);
304     } else { // rb pathology
305       jack_ringbuffer_reset(top.jack.ring.i.l);
306       jack_ringbuffer_reset(top.jack.ring.i.r);
307       clear_jack_ringbuffer(top.jack.ring.i.l, nbytes);
308       clear_jack_ringbuffer(top.jack.ring.i.r, nbytes);
309       jack_ringbuffer_reset(top.jack.auxr.i.l);
310       jack_ringbuffer_reset(top.jack.auxr.i.r);
311       clear_jack_ringbuffer(top.jack.auxr.i.l, nbytes);
312       clear_jack_ringbuffer(top.jack.auxr.i.r, nbytes);
313       top.jack.blow.rb.i++;
314     }
315
316   } else { // callback pathology
317     jack_ringbuffer_reset(top.jack.ring.i.l);
318     jack_ringbuffer_reset(top.jack.ring.i.r);
319     jack_ringbuffer_reset(top.jack.ring.o.l);
320     jack_ringbuffer_reset(top.jack.ring.o.r);
321     clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
322     clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
323     top.jack.blow.cb++;
324   }
325
326   // if enough accumulated in ring, fire dsp
327   if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
328     sem_post(&top.sync.buf.sem);
329
330   // check for blowups
331   if ((top.jack.blow.cb > 0) ||
332       (top.jack.blow.rb.i > 0) ||
333       (top.jack.blow.rb.o > 0))
334     sem_post(&top.sync.mon.sem);
335 }
336
337 //========================================================================
338
339 PRIVATE void 
340 process_samples_thread(void) {
341   while (top.running) {
342     sem_wait(&top.sync.buf.sem);
343     do {
344       gethold();
345       sem_wait(&top.sync.upd.sem);
346       switch (top.state) {
347       case RUN_MUTE: run_mute(); break;
348       case RUN_PASS: run_pass(); break;
349       case RUN_PLAY: run_play(); break;
350       case RUN_SWCH: run_swch(); break;
351       }
352       sem_post(&top.sync.upd.sem);
353     } while (canhold());
354   }
355   pthread_exit(0);
356 }
357
358 //========================================================================
359
360 PRIVATE void 
361 execute(void) {
362   // let updates run
363   sem_post(&top.sync.upd.sem);
364   
365   // rcfile
366   {
367     FILE *frc = find_rcfile(loc.path.rcfile);
368     if (frc) {
369       while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
370         do_update(top.parm.buff, top.verbose ? stderr : 0);
371       fclose(frc);
372     }
373   }
374
375   // start audio processing
376   if (jack_activate(top.jack.client))
377     perror("cannot activate jack client"), exit(1);
378   
379   // wait for threads to terminate
380   pthread_join(top.thrd.trx.id, 0);
381   pthread_join(top.thrd.upd.id, 0);
382   pthread_join(top.thrd.mon.id, 0);
383   if (uni.meter.flag)
384     pthread_join(top.thrd.mtr.id, 0);
385   if (uni.spec.flag)
386     pthread_join(top.thrd.pws.id, 0);
387   
388   // stop audio processing
389   jack_client_close(top.jack.client);
390 }
391
392 PRIVATE void 
393 closeup(void) {
394   jack_ringbuffer_free(top.jack.auxr.i.r);
395   jack_ringbuffer_free(top.jack.auxr.i.l);
396   jack_ringbuffer_free(top.jack.ring.o.r);
397   jack_ringbuffer_free(top.jack.ring.o.l);
398   jack_ringbuffer_free(top.jack.ring.i.r);
399   jack_ringbuffer_free(top.jack.ring.i.l);
400
401   safefree((char *) top.hold.buf.r);
402   safefree((char *) top.hold.buf.l);
403   safefree((char *) top.hold.aux.r);
404   safefree((char *) top.hold.aux.l);
405
406   fclose(top.parm.fp);
407
408   if (uni.meter.flag)
409     fclose(top.meas.mtr.fp);
410   if (uni.spec.flag)
411     fclose(top.meas.spec.fp);
412
413   destroy_workspace();
414
415   exit(0);
416 }
417
418 PRIVATE void 
419 usage(void) {
420   fprintf(stderr, "usage:\n");
421   fprintf(stderr, "jsdr [-flag [arg]] [file]\n");
422   fprintf(stderr, "flags:\n");
423   fprintf(stderr, "     -v              verbose commentary\n");
424   fprintf(stderr, "     -m              do metering\n");
425   fprintf(stderr, "     -s              do spectrum\n");
426   fprintf(stderr, "     -l file         execute update commands in file at startup\n");
427   fprintf(stderr, "'file' arg unused, but available\n");
428   exit(1);
429 }
430
431 PRIVATE void 
432 nonblock(int fd) {
433   long arg;
434   arg = fcntl(fd, F_GETFL);
435   arg |= O_NONBLOCK;
436 /*  if (fcntl(fd, F_GETFL, &arg) >= 0)
437     fcntl(fd, F_SETFL, arg | O_NONBLOCK); */ 
438   fcntl(fd, F_SETFL, arg);
439
440
441 //........................................................................
442
443 PRIVATE void 
444 setup_switching(void) {
445   top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
446   top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
447 }
448
449 PRIVATE void 
450 setup_local_audio(void) {
451   top.hold.size.frames = uni.buflen;
452   top.hold.size.bytes = top.hold.size.frames * sizeof(float);
453   top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
454                                        "main hold buffer left");
455   top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
456                                        "main hold buffer right");
457   top.hold.aux.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
458                                        "aux hold buffer left");
459   top.hold.aux.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
460                                        "aux hold buffer right");
461
462
463 PRIVATE void 
464 setup_updates(void) {
465   top.parm.path = loc.path.parm;
466   if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
467     perror(top.parm.path), exit(1);
468   if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
469     fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm);
470     exit(1);
471   }
472
473   // do this here 'cuz the update thread is controlling the action
474   if (uni.meter.flag) {
475     top.meas.mtr.path = loc.path.meter;
476     top.meas.mtr.fp = efopen(top.meas.mtr.path, "r+");
477   }
478   if (uni.spec.flag) {
479     top.meas.spec.path = loc.path.spec;
480     top.meas.spec.fp = efopen(top.meas.spec.path, "r+");
481   }
482 }
483
484 PRIVATE void
485 jack_xrun(void *arg) {
486   top.jack.blow.xr++;
487   sem_post(&top.sync.mon.sem);
488 }
489
490 PRIVATE void
491 jack_shutdown(void *arg) {}
492
493 PRIVATE void 
494 setup_system_audio(void) {
495   if (loc.name[0]) strcpy(top.jack.name, loc.name);
496   else sprintf(top.jack.name, "sdr-%d", top.pid);
497   if (!(top.jack.client = jack_client_new(top.jack.name)))
498     perror("can't make client -- jack not running?"), exit(1);
499
500   jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
501   jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0);
502   jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0);
503   top.jack.size = jack_get_buffer_size(top.jack.client);
504   memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
505
506   top.jack.port.i.l = jack_port_register(top.jack.client,
507                                          "il",
508                                          JACK_DEFAULT_AUDIO_TYPE,
509                                          JackPortIsInput,
510                                          0);
511   top.jack.port.i.r = jack_port_register(top.jack.client,
512                                          "ir",
513                                          JACK_DEFAULT_AUDIO_TYPE,
514                                          JackPortIsInput,
515                                          0);
516   top.jack.port.o.l = jack_port_register(top.jack.client,
517                                          "ol",
518                                          JACK_DEFAULT_AUDIO_TYPE,
519                                          JackPortIsOutput,
520                                          0);
521   top.jack.port.o.r = jack_port_register(top.jack.client,
522                                          "or",
523                                          JACK_DEFAULT_AUDIO_TYPE,
524                                          JackPortIsOutput,
525                                          0);
526   top.jack.auxp.i.l = jack_port_register(top.jack.client,
527                                          "al",
528                                          JACK_DEFAULT_AUDIO_TYPE,
529                                          JackPortIsInput,
530                                          0);
531   top.jack.auxp.i.r = jack_port_register(top.jack.client,
532                                          "ar",
533                                          JACK_DEFAULT_AUDIO_TYPE,
534                                          JackPortIsInput,
535                                          0);
536   top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
537   top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
538   top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
539   top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
540   top.jack.auxr.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
541   top.jack.auxr.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
542   clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float));
543   clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float));
544 }
545
546 PRIVATE void 
547 setup_threading(void) {
548   sem_init(&top.sync.upd.sem, 0, 0);
549   pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
550   sem_init(&top.sync.buf.sem, 0, 0);
551   pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
552   sem_init(&top.sync.mon.sem, 0, 0);
553   pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL);
554   if (uni.meter.flag) {
555     sem_init(&top.sync.mtr.sem, 0, 0);
556     pthread_create(&top.thrd.mtr.id, NULL, (void *) meter_thread, NULL);
557   }
558   if (uni.spec.flag) {
559     sem_init(&top.sync.pws.sem, 0, 0);
560     pthread_create(&top.thrd.pws.id, NULL, (void *) spectrum_thread, NULL);
561   }
562
563
564 //========================================================================
565 // hard defaults, then environment
566
567 PRIVATE void
568 setup_defaults(void) {
569   loc.name[0] = 0; // no default name for jack client
570   strcpy(loc.path.rcfile, RCBASE);
571   strcpy(loc.path.parm, PARMPATH);
572   strcpy(loc.path.meter, METERPATH);
573   strcpy(loc.path.spec, SPECPATH);
574   strcpy(loc.path.wisdom, WISDOMPATH);
575   loc.def.rate = DEFRATE;
576   loc.def.size = DEFSIZE;
577   loc.def.mode = DEFMODE;
578   loc.def.spec = DEFSPEC;
579   loc.def.nrx = MAXRX;
580   loc.mult.ring = RINGMULT;
581
582   {
583     char *ep;
584     if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep);
585     if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep);
586     if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep);
587     if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep);
588     if ((ep = getenv("SDR_SPECPATH"))) strcpy(loc.path.spec, ep);
589     if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep);
590     if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep);
591     if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep);
592     if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep);
593     if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep);
594   }
595 }
596
597 //========================================================================
598 PRIVATE void 
599 setup(int argc, char **argv) {
600   int i;
601
602   top.uid = getuid();
603   top.pid = getpid();
604   top.start_tv = now_tv();
605   top.running = TRUE;
606   top.verbose = FALSE;
607   top.state = RUN_PLAY;
608
609   setup_defaults();
610   
611   for (i = 1; i < argc; i++)
612     if (argv[i][0] == '-')
613       switch (argv[i][1]) {
614       case 'v':
615         top.verbose = TRUE;
616         break;
617       case 'm':
618         uni.meter.flag = TRUE;
619         break;
620       case 's':
621         uni.spec.flag = TRUE;
622         break;
623       case 'l':
624         strcpy(loc.path.rcfile, argv[++i]);
625         break;
626       default:
627         usage();
628       }
629     else break;
630   if (i < argc) {
631     if (!freopen(argv[i], "r", stdin))
632       perror(argv[i]), exit(1);
633     i++;
634   }
635
636   setup_workspace();
637   setup_updates();
638
639   setup_local_audio();
640   setup_system_audio();
641
642   setup_threading();
643   setup_switching();
644 }
645
646 //========================================================================
647
648 int 
649 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }