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