]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/main.c
Further correction to digitalagc
[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
250     uni.mode.trx = top.swch.trx.next;
251     switch (uni.mode.trx) {
252       int i;
253     case TX:
254       tx.agc.gen->over = tx.tick + 3;
255       break;
256     case RX:
257       for(i = 0; i < uni.multirx.nrx; i++)
258         rx[i].agc.gen->over = rx[i].tick + 3;
259       break;
260     }
261
262     top.state = top.swch.run.last;
263     top.swch.bfct.want = top.swch.bfct.have = 0;
264
265     jack_ringbuffer_reset(top.jack.ring.o.l);
266     jack_ringbuffer_reset(top.jack.ring.o.r);
267     clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
268     clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
269
270     reset_meters();
271     reset_spectrum();
272     reset_counters();
273   }
274
275   process_samples(top.hold.buf.l, top.hold.buf.r,
276                   top.hold.aux.l, top.hold.aux.r,
277                   top.hold.size.frames);
278
279
280 //========================================================================
281
282 void
283 clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) {
284   int i;
285   char zero = 0;
286   for (i = 0; i < nbytes; i++)
287     jack_ringbuffer_write(rb, &zero, 1);
288 }
289
290 PRIVATE void 
291 audio_callback(jack_nframes_t nframes, void *arg) {
292   float *lp, *rp;
293   int nbytes = nframes * sizeof(float);
294
295   if (nframes == top.jack.size) {
296
297     // output: copy from ring to port
298     lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
299     rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
300
301     if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) {
302       jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
303       jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
304     } else { // rb pathology
305       memset((char *) lp, 0, nbytes);
306       memset((char *) rp, 0, nbytes);
307       jack_ringbuffer_reset(top.jack.ring.o.l);
308       jack_ringbuffer_reset(top.jack.ring.o.r);
309       clear_jack_ringbuffer(top.jack.ring.o.l, nbytes);
310       clear_jack_ringbuffer(top.jack.ring.o.r, nbytes);
311       top.jack.blow.rb.o++;
312     }
313     
314     // input: copy from port to ring
315     if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) {
316       lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
317       rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
318       jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
319       jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
320       lp = (float *) jack_port_get_buffer(top.jack.auxp.i.l, nframes);
321       rp = (float *) jack_port_get_buffer(top.jack.auxp.i.r, nframes);
322       jack_ringbuffer_write(top.jack.auxr.i.l, (char *) lp, nbytes);
323       jack_ringbuffer_write(top.jack.auxr.i.r, (char *) rp, nbytes);
324     } else { // rb pathology
325       jack_ringbuffer_reset(top.jack.ring.i.l);
326       jack_ringbuffer_reset(top.jack.ring.i.r);
327       clear_jack_ringbuffer(top.jack.ring.i.l, nbytes);
328       clear_jack_ringbuffer(top.jack.ring.i.r, nbytes);
329       jack_ringbuffer_reset(top.jack.auxr.i.l);
330       jack_ringbuffer_reset(top.jack.auxr.i.r);
331       clear_jack_ringbuffer(top.jack.auxr.i.l, nbytes);
332       clear_jack_ringbuffer(top.jack.auxr.i.r, nbytes);
333       top.jack.blow.rb.i++;
334     }
335
336   } else { // callback pathology
337     jack_ringbuffer_reset(top.jack.ring.i.l);
338     jack_ringbuffer_reset(top.jack.ring.i.r);
339     jack_ringbuffer_reset(top.jack.ring.o.l);
340     jack_ringbuffer_reset(top.jack.ring.o.r);
341     clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
342     clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
343     top.jack.blow.cb++;
344   }
345
346   // if enough accumulated in ring, fire dsp
347   if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
348     sem_post(&top.sync.buf.sem);
349
350   // check for blowups
351   if ((top.jack.blow.cb > 0) ||
352       (top.jack.blow.rb.i > 0) ||
353       (top.jack.blow.rb.o > 0))
354     sem_post(&top.sync.mon.sem);
355 }
356
357 //========================================================================
358
359 PRIVATE void 
360 process_samples_thread(void) {
361   while (top.running) {
362     sem_wait(&top.sync.buf.sem);
363     do {
364       gethold();
365       sem_wait(&top.sync.upd.sem);
366       switch (top.state) {
367       case RUN_MUTE: run_mute(); break;
368       case RUN_PASS: run_pass(); break;
369       case RUN_PLAY: run_play(); break;
370       case RUN_SWCH: run_swch(); break;
371       }
372       sem_post(&top.sync.upd.sem);
373     } while (canhold());
374   }
375   pthread_exit(0);
376 }
377
378 //========================================================================
379
380 PRIVATE void 
381 execute(void) {
382   // let updates run
383   sem_post(&top.sync.upd.sem);
384   
385   // rcfile
386   {
387     FILE *frc = find_rcfile(loc.path.rcfile);
388     if (frc) {
389       while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
390         do_update(top.parm.buff, top.verbose ? stderr : 0);
391       fclose(frc);
392     }
393   }
394
395   // start audio processing
396   if (jack_activate(top.jack.client))
397     perror("cannot activate jack client"), exit(1);
398   
399   // wait for threads to terminate
400   pthread_join(top.thrd.trx.id, 0);
401   pthread_join(top.thrd.upd.id, 0);
402   pthread_join(top.thrd.mon.id, 0);
403   if (uni.meter.flag)
404     pthread_join(top.thrd.mtr.id, 0);
405   if (uni.spec.flag)
406     pthread_join(top.thrd.pws.id, 0);
407   
408   // stop audio processing
409   jack_client_close(top.jack.client);
410 }
411
412 PRIVATE void 
413 closeup(void) {
414   jack_ringbuffer_free(top.jack.auxr.i.r);
415   jack_ringbuffer_free(top.jack.auxr.i.l);
416   jack_ringbuffer_free(top.jack.ring.o.r);
417   jack_ringbuffer_free(top.jack.ring.o.l);
418   jack_ringbuffer_free(top.jack.ring.i.r);
419   jack_ringbuffer_free(top.jack.ring.i.l);
420
421   safefree((char *) top.hold.buf.r);
422   safefree((char *) top.hold.buf.l);
423   safefree((char *) top.hold.aux.r);
424   safefree((char *) top.hold.aux.l);
425
426   fclose(top.parm.fp);
427
428   if (uni.meter.flag)
429     fclose(top.meas.mtr.fp);
430   if (uni.spec.flag)
431     fclose(top.meas.spec.fp);
432
433   destroy_workspace();
434
435   exit(0);
436 }
437
438 PRIVATE void 
439 usage(void) {
440   fprintf(stderr, "usage:\n");
441   fprintf(stderr, "jsdr [-flag [arg]] [file]\n");
442   fprintf(stderr, "flags:\n");
443   fprintf(stderr, "     -v              verbose commentary\n");
444   fprintf(stderr, "     -m              do metering\n");
445   fprintf(stderr, "     -s              do spectrum\n");
446   fprintf(stderr, "     -l file         execute update commands in file at startup\n");
447   fprintf(stderr, "'file' arg unused, but available\n");
448   exit(1);
449 }
450
451 PRIVATE void 
452 nonblock(int fd) {
453   long arg;
454   arg = fcntl(fd, F_GETFL);
455   arg |= O_NONBLOCK;
456 /*  if (fcntl(fd, F_GETFL, &arg) >= 0)
457     fcntl(fd, F_SETFL, arg | O_NONBLOCK); */ 
458   fcntl(fd, F_SETFL, arg);
459
460
461 //........................................................................
462
463 PRIVATE void 
464 setup_switching(void) {
465   top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
466   top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
467 }
468
469 PRIVATE void 
470 setup_local_audio(void) {
471   top.hold.size.frames = uni.buflen;
472   top.hold.size.bytes = top.hold.size.frames * sizeof(float);
473   top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
474                                        "main hold buffer left");
475   top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
476                                        "main hold buffer right");
477   top.hold.aux.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
478                                        "aux hold buffer left");
479   top.hold.aux.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
480                                        "aux hold buffer right");
481
482
483 PRIVATE void 
484 setup_updates(void) {
485   top.parm.path = loc.path.parm;
486   if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
487     perror(top.parm.path), exit(1);
488   if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
489     fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm);
490     exit(1);
491   }
492
493   // do this here 'cuz the update thread is controlling the action
494   if (uni.meter.flag) {
495     top.meas.mtr.path = loc.path.meter;
496     top.meas.mtr.fp = efopen(top.meas.mtr.path, "r+");
497   }
498   if (uni.spec.flag) {
499     top.meas.spec.path = loc.path.spec;
500     top.meas.spec.fp = efopen(top.meas.spec.path, "r+");
501   }
502 }
503
504 PRIVATE void
505 jack_xrun(void *arg) {
506   top.jack.blow.xr++;
507   sem_post(&top.sync.mon.sem);
508 }
509
510 PRIVATE void
511 jack_shutdown(void *arg) {}
512
513 PRIVATE void 
514 setup_system_audio(void) {
515   if (loc.name[0]) strcpy(top.jack.name, loc.name);
516   else sprintf(top.jack.name, "sdr-%d", top.pid);
517   if (!(top.jack.client = jack_client_new(top.jack.name)))
518     perror("can't make client -- jack not running?"), exit(1);
519
520   jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
521   jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0);
522   jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0);
523   top.jack.size = jack_get_buffer_size(top.jack.client);
524   memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
525
526   top.jack.port.i.l = jack_port_register(top.jack.client,
527                                          "il",
528                                          JACK_DEFAULT_AUDIO_TYPE,
529                                          JackPortIsInput,
530                                          0);
531   top.jack.port.i.r = jack_port_register(top.jack.client,
532                                          "ir",
533                                          JACK_DEFAULT_AUDIO_TYPE,
534                                          JackPortIsInput,
535                                          0);
536   top.jack.port.o.l = jack_port_register(top.jack.client,
537                                          "ol",
538                                          JACK_DEFAULT_AUDIO_TYPE,
539                                          JackPortIsOutput,
540                                          0);
541   top.jack.port.o.r = jack_port_register(top.jack.client,
542                                          "or",
543                                          JACK_DEFAULT_AUDIO_TYPE,
544                                          JackPortIsOutput,
545                                          0);
546   top.jack.auxp.i.l = jack_port_register(top.jack.client,
547                                          "al",
548                                          JACK_DEFAULT_AUDIO_TYPE,
549                                          JackPortIsInput,
550                                          0);
551   top.jack.auxp.i.r = jack_port_register(top.jack.client,
552                                          "ar",
553                                          JACK_DEFAULT_AUDIO_TYPE,
554                                          JackPortIsInput,
555                                          0);
556   top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
557   top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
558   top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
559   top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
560   top.jack.auxr.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
561   top.jack.auxr.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
562   clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float));
563   clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float));
564 }
565
566 PRIVATE void 
567 setup_threading(void) {
568   sem_init(&top.sync.upd.sem, 0, 0);
569   pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
570   sem_init(&top.sync.buf.sem, 0, 0);
571   pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
572   sem_init(&top.sync.mon.sem, 0, 0);
573   pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL);
574   if (uni.meter.flag) {
575     sem_init(&top.sync.mtr.sem, 0, 0);
576     pthread_create(&top.thrd.mtr.id, NULL, (void *) meter_thread, NULL);
577   }
578   if (uni.spec.flag) {
579     sem_init(&top.sync.pws.sem, 0, 0);
580     pthread_create(&top.thrd.pws.id, NULL, (void *) spectrum_thread, NULL);
581   }
582
583
584 //========================================================================
585 // hard defaults, then environment
586
587 PRIVATE void
588 setup_defaults(void) {
589   loc.name[0] = 0; // no default name for jack client
590   strcpy(loc.path.rcfile, RCBASE);
591   strcpy(loc.path.parm, PARMPATH);
592   strcpy(loc.path.meter, METERPATH);
593   strcpy(loc.path.spec, SPECPATH);
594   strcpy(loc.path.wisdom, WISDOMPATH);
595   loc.def.rate = DEFRATE;
596   loc.def.size = DEFSIZE;
597   loc.def.mode = DEFMODE;
598   loc.def.spec = DEFSPEC;
599   loc.def.comp = DEFCOMP;
600   loc.def.nrx = MAXRX;
601   loc.mult.ring = RINGMULT;
602
603   {
604     char *ep;
605     if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep);
606     if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep);
607     if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep);
608     if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep);
609     if ((ep = getenv("SDR_SPECPATH"))) strcpy(loc.path.spec, ep);
610     if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep);
611     if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep);
612     if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep);
613     if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep);
614     if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep);
615   }
616 }
617
618 //========================================================================
619 PRIVATE void 
620 setup(int argc, char **argv) {
621   int i;
622
623   top.uid = getuid();
624   top.pid = getpid();
625   top.start_tv = now_tv();
626   top.running = TRUE;
627   top.verbose = FALSE;
628   top.state = RUN_PLAY;
629
630   setup_defaults();
631   
632   for (i = 1; i < argc; i++)
633     if (argv[i][0] == '-')
634       switch (argv[i][1]) {
635       case 'v':
636         top.verbose = TRUE;
637         break;
638       case 'm':
639         uni.meter.flag = TRUE;
640         break;
641       case 's':
642         uni.spec.flag = TRUE;
643         break;
644       case 'l':
645         strcpy(loc.path.rcfile, argv[++i]);
646         break;
647       default:
648         usage();
649       }
650     else break;
651   if (i < argc) {
652     if (!freopen(argv[i], "r", stdin))
653       perror(argv[i]), exit(1);
654     i++;
655   }
656
657   setup_workspace();
658   setup_updates();
659
660   setup_local_audio();
661   setup_system_audio();
662
663   setup_threading();
664   setup_switching();
665 }
666
667 //========================================================================
668
669 int 
670 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }