Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
411 kaklik 1
//
2
// sidd.c:  A VLF signal monitor.
3
//
4
// authors: Paul Nicholson (paul@abelian.demon.co.uk), Jakub kakona (kaklik@mlab.cz)
5
//
6
 
7
#include <stdlib.h>
8
#include <unistd.h>
9
#include <math.h>
10
#include <stdio.h>
11
#include <sys/stat.h>
12
#include <sys/ioctl.h>
13
#include <sys/time.h>
14
#include <sys/param.h>
15
#include <fcntl.h>
16
#include <errno.h>
17
#include <stdarg.h>
18
#include <ctype.h>
19
#include <string.h>
20
#include <signal.h>
21
#include <time.h>
22
#include <sched.h>
23
#include <linux/soundcard.h>
24
 
25
#include <fftw3.h>
26
 
27
///////////////////////////////////////////////////////////////////////////////
28
//  Tuneable Settings                                                        //
29
///////////////////////////////////////////////////////////////////////////////
30
 
31
#define VERSION "0.94"
32
 
33
//
34
//  Number of soundcard bytes to read at a time.
35
#define NREAD 2048
36
 
37
//
38
//  Max number of bands which can be read from the config file.
39
#define MAXBANDS 20
40
 
41
//
42
//  Name of the configuration file.
43
#define CONFIG_FILE "sidd.conf"
44
 
45
///////////////////////////////////////////////////////////////////////////////
46
//  Globals and fixed definitions                                            // 
47
///////////////////////////////////////////////////////////////////////////////
48
//
49
//  Default values here are over-ridden by the config file.
50
 
51
int mode = 1;                                          //  1 = mono, 2 = stereo
52
int bits = 16;                                    // Sample width, 8 or 16 bits
53
int BINS = 2048;                                    // Number of frequency bins
54
#define FFTWID (2 * BINS)                    // Number of samples in FFT period
55
 
56
int background = 1;                        // Set zero if running in foreground
57
int fdi;                                                   // Input file handle
58
int fdm;                                                   // Mixer file handle
59
int VFLAG = 0;                                    //  Set non-zero by -v option
60
int MFLAG = 0;                                    //  Set non-zero by -m option
61
 
62
int spec_max = 100;       // Issue a spectrum for every spec_max output records
63
int spec_cnt = 0;
64
int sample_rate = 100000;                                 // Samples per second
65
 
66
int chans = 1;
67
int alert_on = 0;
68
 
69
int priority = 0;                       // Set to 1 if high scheduling priority
70
struct sigaction sa;
71
char mailaddr[100];
72
 
73
double los_thresh = 0;                    // Threshold for loss of signal, 0..1
74
int los_timeout = 0;        // Number of seconds before loss of signal declared
75
 
76
double DF;                                   // Frequency resolution of the FFT
77
int bailout_flag = 0;                           // To prevent bailout() looping
78
int grab_cnt = 0;                       // Count of samples into the FFT buffer
79
 
80
// Mixer gain settings requested by config file.
81
int req_lgain = -1;              // Line gain
82
int req_mgain = -1;              // Microphone gain
83
int req_igain = -1;              // Input gain 
84
int req_rgain = -1;              // Record level
85
 
86
 
87
// Actual mixer values, read by open_mixer()
88
int mixer_recmask;      // Recording device mask
89
int mixer_stereo;       // Stereo device mask
90
int mixer_line;         // Line input gain setting
91
int mixer_microphone;	// Microphone input gain
92
int mixer_igain;        // Overall input gain setting
93
int mixer_reclev;       // Recording level setting
94
int mixer_recsrc;       // Mask indicating which inputs are set to record
95
 
96
//
97
// Various filenames, contents set by config file.
98
//
99
char logfile[100] = "";
100
char device[100] = "/dev/dsp";
101
char mixer[100] = "/dev/mixer";
102
char spectrum_file[100] = "/tmp/sidspec"; 
103
char datadir[100] = ".";
104
 
105
//
106
// Table of frequency bands to monitor
107
//
108
 
109
struct BAND
110
{
111
   char ident[50];
112
 
113
   int start;
114
   int end;
115
}
116
 bands[MAXBANDS];    // Table of bands to be monitored
117
 
118
int nbands = 0;
119
 
120
//
121
//  Independent state variables and buffers for left and right channels
122
//
123
struct CHAN
124
{
125
   char *name;
126
   double *signal_avg;
127
   double *powspec;
128
   double *fft_inbuf;
129
   fftw_complex *fft_data;
130
   fftw_plan ffp;
131
   double peak;
132
   double sum_sq;
133
   int los_state;
134
   time_t los_time;
135
   FILE *fo;
136
   char fname[100];
137
}
138
 left = { "left" }, right = { "right" };
139
 
140
///////////////////////////////////////////////////////////////////////////////
141
//  Various Utility Functions                                                //
142
///////////////////////////////////////////////////////////////////////////////
143
 
144
//
145
//  Issue a message to the log file, if the verbosity level is high enough...
146
//
147
 
148
void report( int level, char *format, ...)
149
{
150
   va_list ap;
151
   void bailout( char *format, ...);
152
   char temp[ 200];
153
 
154
   if( VFLAG < level) return;
155
 
156
   va_start( ap, format);
157
   vsprintf( temp, format, ap);
158
   va_end( ap);
159
 
160
   if( !logfile[0] || !background)
161
      if( background != 2) fprintf( stderr, "%s\n", temp);
162
 
163
   if( logfile[0])
164
   {
165
      time_t now = time( NULL);
166
      struct tm *tm = gmtime( &now);
167
      FILE *flog = NULL;
168
 
169
      if( (flog = fopen( logfile, "a+")) == NULL)
170
         bailout( "cannot open logfile [%s]: %s", logfile, strerror( errno));
171
 
172
      fprintf( flog, "%04d/%02d/%02d %02d:%02d:%02d %s\n", 
173
                tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
174
                tm->tm_hour, tm->tm_min, tm->tm_sec, temp);
175
      fclose( flog);
176
   }
177
}
178
 
179
void alert( char *format, ...)
180
{
181
   FILE *f;
182
   va_list( ap);
183
   char cmd[100], temp[100];
184
 
185
   va_start( ap, format);
186
   vsprintf( temp, format, ap);
187
   va_end( ap);
188
 
189
   report( -1, "%s", temp);
190
 
191
   if( !alert_on || !mailaddr[0]) return;
192
 
193
   sprintf( cmd, "mail -s 'sidd alert' '%s'", mailaddr);
194
   if( (f=popen( cmd, "w")) == NULL)
195
   {
196
      report( 0, "cannot exec [%s]: %s", cmd, strerror( errno));
197
      return;
198
   }
199
 
200
   fprintf( f, "sidd: %s\n", temp);
201
   fclose( f);
202
}
203
 
204
//
205
//  We try to exit the program through here, if possible.
206
//  
207
 
208
void bailout( char *format, ...)
209
{
210
   va_list ap;
211
   char temp[ 200];
212
 
213
   if( bailout_flag) exit( 1);
214
   bailout_flag = 1;
215
   va_start( ap, format);
216
   vsprintf( temp, format, ap);
217
   va_end( ap);
218
 
219
   alert( "terminating: %s", temp);
220
   exit( 1);
221
}
222
 
223
//
224
//  Exit with a message if we get any signals.
225
//  
226
 
227
void handle_sigs( int signum)
228
{
229
   bailout( "got signal %d", signum);
230
}
231
 
232
///////////////////////////////////////////////////////////////////////////////
233
//  Soundcard Setup                                                          //
234
///////////////////////////////////////////////////////////////////////////////
235
 
236
//
237
//  Prepare the input stream, setting up the soundcard if the input
238
//  is a character device.
239
//
240
 
241
void setup_input_stream( void)
242
{
243
   struct stat st;
244
 
245
   report( 1, "taking data from [%s]", device);
246
 
247
   if( (fdi = open( device, O_RDONLY)) < 0)
248
      bailout( "cannot open [%s]: %s", strerror( errno));
249
 
250
   if( fstat( fdi, &st) < 0)
251
      bailout( "cannot stat input stream: %s", strerror( errno));
252
 
253
   if( S_ISCHR( st.st_mode)) 
254
   {
255
      int blksize;
256
      int fragreq = 0x7fff000a;
257
      unsigned int format;
258
      unsigned int req_format = AFMT_S16_LE;
259
      if( bits == 8) req_format = AFMT_U8;
260
 
261
      if (ioctl( fdi, SNDCTL_DSP_SETFRAGMENT, &fragreq))
262
         report( 01, "cannot set fragment size");
263
 
264
      if( ioctl( fdi, SNDCTL_DSP_RESET, NULL) < 0)
265
         bailout( "cannot reset input device");
266
 
267
      chans = mode;
268
      if( ioctl( fdi, SNDCTL_DSP_CHANNELS, &chans) < 0)
269
         bailout( "cannot set channels on input device");
270
 
271
      if( ioctl( fdi, SNDCTL_DSP_GETFMTS, &format) < 0)
272
         bailout( "cannot get formats from input device");
273
 
274
      report( 2, "formats available: %08X", format);
275
      if( (format & req_format) == 0)
276
      {
277
         report( 0, "available dsp modes are %08X", format);
278
         bailout( "unable to set %d bit dsp mode", bits);
279
      }
280
 
281
      format = req_format;
282
      if( ioctl( fdi, SNDCTL_DSP_SETFMT, &format) < 0)
283
         bailout( "cannot set dsp format on input device");
284
 
285
      if( ioctl( fdi, SNDCTL_DSP_GETBLKSIZE, &blksize) < 0)
286
         bailout( "cannot get block size from input device");
287
 
288
      report( 2, "dsp block size: %d", blksize);
289
      if( ioctl( fdi, SNDCTL_DSP_CHANNELS, &chans) < 0)
290
         bailout( "cannot get channels from input device");
291
 
292
      report( 1, "requesting rate %d", sample_rate);
293
      if( ioctl( fdi, SNDCTL_DSP_SPEED, &sample_rate) < 0)
294
         bailout( "cannot set sample rate of input device");
295
 
296
      report( 1, "actual rate set: %d samples/sec", sample_rate);
297
      report( 1, "soundcard channels: %d  bits: %d", chans, bits);
298
   }
299
}
300
 
301
///////////////////////////////////////////////////////////////////////////////
302
//  Output Functions                                                         //
303
///////////////////////////////////////////////////////////////////////////////
304
 
305
void maybe_output_spectrum( void)
306
{
307
   FILE *f;
308
   int i;
309
 
310
   if( ++spec_cnt < spec_max) return;  // Wait for spec_max records
311
   spec_cnt = 0;
312
 
313
   if( !spectrum_file[0]) return;     // Spectrum file not wanted.
314
 
315
   if( (f=fopen( spectrum_file, "w+")) == NULL)
316
      bailout( "cannot open spectrum file %s, %s", strerror( errno));
317
 
318
   if( mode == 1){
319
      fprintf( f, "Frequency PowerL \n");
320
      for( i=0; i<BINS; i++) fprintf( f, "%.5e %.5e\n", 
321
             (i+0.5) * DF, left.signal_avg[i]/spec_max);}
322
   else{
323
      fprintf( f, "Frequncy PowerL PowerR \n");
324
      for( i=0; i<BINS; i++) fprintf( f, "%.5e %.5e %.5e\n", 
325
             (i+0.5) * DF, left.signal_avg[i]/spec_max,
326
                          right.signal_avg[i]/spec_max);}
327
   fclose( f);
328
 
329
   for( i=0; i<BINS; i++) left.signal_avg[i] = 0;
330
   if( mode == 2) for( i=0; i<BINS; i++) right.signal_avg[i] = 0;
331
}
332
 
333
void output_record( struct CHAN *c, char *prefix, double fsecs)
334
{
335
   int i, j;
336
   char test[100];
337
 
338
   if( mode == 1)
339
      sprintf( test, "%s.dat", prefix);
340
   else
341
      sprintf( test, "%s.%s.dat", prefix, c->name);
342
 
343
   if( !c->fo || strcmp( test, c->fname))
344
   {
345
      if( c->fo) fclose( c->fo);
346
      strcpy( c->fname, test);
347
      report( 0, "using output file [%s]", c->fname);
348
      if( (c->fo=fopen( c->fname, "a+")) == NULL)
349
         bailout( "cannot open [%s], %s", c->fname, strerror( errno));
350
   }
351
 
352
   fprintf( c->fo, "%.3f %.3f %.3f", fsecs, c->peak, sqrt( c->sum_sq/FFTWID));
353
 
354
   for( i=0; i<nbands; i++)
355
   {
356
      double e = 0;
357
      int n1 = bands[i].start/DF;
358
      int n2 = bands[i].end/DF;
359
      for( j=n1; j<= n2; j++) e += c->powspec[j];
360
      e /= n2 - n1 + 1;
361
      fprintf( c->fo, " %.2e", e);
362
   }
363
   fprintf( c->fo, "\n");
364
   fflush( c->fo);
365
 
366
   c->peak = c->sum_sq = 0;
367
}
368
 
369
void output_records( void)
370
{
371
   struct timeval tv;
372
   struct tm *tm;
373
   double fsecs;
374
   time_t ud;
375
   char prefix[100];
376
 
377
   gettimeofday( &tv, NULL);
378
   fsecs = tv.tv_sec + 1e-6 * tv.tv_usec;
379
   ud = tv.tv_sec - tv.tv_sec % 86400;
380
   tm = gmtime( &ud);
381
   sprintf( prefix, "%s/%02d%02d%02d", datadir,
382
                  tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday);
383
 
384
   output_record( &left, prefix, fsecs);
385
   if( mode == 2) output_record( &right, prefix, fsecs);
386
}
387
 
388
void check_los( struct CHAN *c)
389
{
390
   if( !c->los_state)
391
   {
392
      if( !c->los_time && c->peak < los_thresh) time( &c->los_time);
393
      if( c->los_time && c->peak > los_thresh) c->los_time = 0;
394
      if( c->los_time && c->los_time + los_timeout < time( NULL))
395
      {
396
         c->los_state = 1;
397
         c->los_time = 0;
398
         if( mode == 1) alert( "loss of signal");
399
         else alert( "loss of signal on %s", c->name);
400
      }
401
   }
402
   else
403
   {
404
      if( !c->los_time && c->peak > los_thresh) time( &c->los_time);
405
      if( c->los_time && c->peak < los_thresh) c->los_time = 0;
406
      if( c->los_time && c->los_time + los_timeout < time( NULL))
407
      {
408
         c->los_state = 0;
409
         c->los_time = 0;
410
         if( mode == 1) alert( "signal restored");
411
         else alert( "signal restored on %s", c->name);
412
      }
413
   } 
414
}
415
 
416
///////////////////////////////////////////////////////////////////////////////
417
//  Signal Processing                                                        //
418
///////////////////////////////////////////////////////////////////////////////
419
 
420
void process_fft( struct CHAN *c)
421
{
422
   int i;
423
 
424
   //
425
   //  Do the FFT.  First time through, initialise the fft plan.
426
   //
427
 
428
   if( !c->ffp)
429
      c->ffp = fftw_plan_dft_r2c_1d( FFTWID, c->fft_inbuf, c->fft_data,
430
                           FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
431
 
432
   fftw_execute( c->ffp);
433
 
434
   //
435
   //  Obtain squared amplitude of each bin.
436
   //
437
 
438
   c->powspec[ 0] = 0.0;  // Zero the DC component
439
   for( i=1; i<BINS; i++)
440
   {
441
      double t1 = c->fft_data[ i][0];  
442
      double t2 = c->fft_data[ i][1]; 
443
      c->powspec[ i] = t1*t1 + t2*t2;
444
   }
445
 
446
   //
447
   //  Accumulate average signal levels in each bin.  signal_avg is used
448
   //  only for the spectrum file output.
449
   //
450
 
451
   for( i=0; i<BINS; i++) c->signal_avg[ i] += c->powspec[i];
452
   check_los( c);
453
}
454
 
455
void insert_sample( struct CHAN *c, double f)
456
{
457
   c->sum_sq += f * f;
458
   if( f > c->peak) c->peak = f;
459
   if( f < -c->peak) c->peak = -f;
460
 
461
   c->fft_inbuf[ grab_cnt] = f * sin( grab_cnt/(double) FFTWID * M_PI);
462
}
463
 
464
void maybe_do_fft( void)
465
{
466
   if( ++grab_cnt < FFTWID) return;
467
   grab_cnt = 0;
468
 
469
   process_fft( &left);
470
   if( mode == 2) process_fft( &right);
471
 
472
   output_records();
473
   maybe_output_spectrum(); 
474
}
475
 
476
//
477
// Main signal processing loop.  Never returns.
478
//
479
 
480
void process_signal( void)
481
{
482
   unsigned char buff[ NREAD];
483
 
484
   while( 1) 
485
   {
486
      int i, q;
487
 
488
      if( (q=read( fdi, buff, NREAD)) <= 0) 
489
      {
490
         if( !q || errno == ENOENT || errno == 0) 
491
         {  
492
            sched_yield();
493
            usleep( 50000); 
494
            continue;
495
         }
496
 
497
         report( 0, "input file: read error, count=%d errno=%d", q, errno);
498
         exit( 1);
499
      }
500
 
501
      //  Unpack the input buffer into signed 16 bit words.
502
      //  then scale to -1..+1 for further processing.
503
      //  We use 'chans' to decide if the soundcard is giving stereo or
504
      //  mono samples, rather than 'mode', because some cards will refuse
505
      //  to do mono.  
506
      if( bits == 16)
507
      {
508
         if( chans == 1)
509
         {
510
            for( i=0; i<q; i += 2)
511
            {
512
               int fh = *(short *)(buff + i);
513
 
514
               insert_sample( &left, fh/32768.0);
515
               maybe_do_fft();
516
            }
517
         }
518
         else  // chans must be 2
519
         {
520
            if( mode == 1)
521
               for( i=0; i<q; i += 4)
522
               {
523
                  int fh = *(short *)(buff + i);
524
                  insert_sample( &left, fh/32768.0);
525
                  maybe_do_fft();
526
               }
527
            else  // mode == 2
528
               for( i=0; i<q; i += 4)
529
               {
530
                  int fh = *(short *)(buff + i);
531
                  insert_sample( &left, fh/32768.0);
532
 
533
                  fh = *(short *)(buff + i + 2);
534
                  insert_sample( &right, fh/32768.0);
535
                  maybe_do_fft();
536
               }
537
         }
538
      }
539
      else   // bits must be 8
540
      {
541
         if( chans == 1)
542
         {
543
            for( i=0; i<q; i++)
544
            {
545
               int fh = ((short)buff[i] - 128)*256;
546
               insert_sample( &left, fh/32768.0);
547
               maybe_do_fft();
548
            }
549
         }
550
         else  // chans must be 2
551
         {
552
            if( mode == 1)
553
               for( i=0; i<q; i += 2)
554
               {
555
                  int fh = ((short)buff[i] - 128)*256;
556
                  insert_sample( &left, fh/32768.0);
557
                  maybe_do_fft();
558
               }
559
            else  // mode == 2
560
               for( i=0; i<q; i += 2)
561
               {
562
                  int fh = ((short)buff[i] - 128)*256;
563
                  insert_sample( &left, fh/32768.0);
564
 
565
                  fh = ((short)buff[i+1] - 128)*256;
566
                  insert_sample( &right, fh/32768.0);
567
                  maybe_do_fft();
568
               }
569
         }
570
      }
571
   }
572
}
573
 
574
///////////////////////////////////////////////////////////////////////////////
575
//  Configuration File Stuff                                                 //
576
///////////////////////////////////////////////////////////////////////////////
577
 
578
void config_band( char *ident, char *start, char *end)
579
{
580
   struct BAND *b = bands + nbands++;
581
 
582
   if( nbands == MAXBANDS) bailout( "too many bands specified in config file");
583
 
584
   strcpy( b->ident, ident);
585
   b->start = atoi( start);
586
   b->end = atoi( end);
587
 
588
   report( 1, "band %s %d %d", b->ident, b->start, b->end);
589
}
590
 
591
void load_config( void)
592
{
593
   int lino = 0, nf;
594
   FILE *f;
595
   char buff[100], *p, *fields[20];
596
 
597
   if( (f=fopen( CONFIG_FILE, "r")) == NULL)
598
      bailout( "no config file found");
599
 
600
   while( fgets( buff, 99, f))
601
   {
602
      lino++;
603
 
604
      if( (p=strchr( buff, '\r')) != NULL) *p = 0;
605
      if( (p=strchr( buff, '\n')) != NULL) *p = 0;
606
      if( (p=strchr( buff, ';')) != NULL) *p = 0;
607
 
608
      p = buff;  nf = 0;
609
      while( 1)
610
      {
611
         while( *p && isspace( *p)) p++;
612
         if( !*p) break;
613
         fields[nf++] = p;
614
         while( *p && !isspace( *p)) p++;
615
         if( *p) *p++ = 0;
616
      }
617
      if( !nf) continue;
618
 
619
      if( nf == 4 && !strcasecmp( fields[0], "band")) 
620
         config_band( fields[1], fields[2], fields[3]);
621
      else
622
      if( nf == 2 && !strcasecmp( fields[0], "logfile"))
623
      {
624
         strcpy( logfile, fields[1]);
625
         report( 1, "logfile %s", logfile);
626
      }
627
      else
628
      if( nf == 3 && !strcasecmp( fields[0], "los"))
629
      {
630
         los_thresh = atof( fields[1]);
631
         los_timeout = atoi( fields[2]);
632
         report( 1, "los threshold %.3f, timeout %d seconds", 
633
                    los_thresh, los_timeout);
634
      }
635
      else
636
      if( nf == 2 && !strcasecmp( fields[0], "device"))
637
         strcpy( device, fields[1]);
638
      else
639
      if( nf == 2 && !strcasecmp( fields[0], "mixer"))
640
         strcpy( mixer, fields[1]);
641
      else
642
      if( nf == 2 && !strcasecmp( fields[0], "mode"))
643
      {
644
         if( !strcasecmp( fields[1], "mono")) mode = 1;
645
         else
646
         if( !strcasecmp( fields[1], "stereo")) mode = 2;
647
         else
648
            bailout( "error in config file, line %d", lino);
649
      }
650
      else
651
      if( nf == 2 && !strcasecmp( fields[0], "bits"))
652
      {
653
         bits = atoi( fields[1]);
654
         if( bits != 8 && bits != 16)
655
            bailout( "can only do 8 or 16 bits, config file line %d", lino);
656
      }
657
      else
658
      if( nf == 3 && !strcasecmp( fields[0], "spectrum"))
659
      {
660
         strcpy( spectrum_file, fields[1]);
661
         spec_max = atoi( fields[2]);
662
      }
663
      else
664
      if( nf == 2 && !strcasecmp( fields[0], "sched")
665
                  && !strcasecmp( fields[1], "high"))
666
      {
667
         priority = 1;
668
      }
669
      else
670
      if( nf == 4 && !strcasecmp( fields[0], "gain"))
671
      {
672
         int left = atoi( fields[2]);
673
         int right = atoi( fields[3]);
674
         int gain = (right << 8) | left;
675
 
676
         if( !strcasecmp( fields[1], "line"))
677
         {
678
           req_lgain = gain;
679
           mixer_recsrc = SOUND_MASK_LINE;
680
         }
681
         else
682
         if( !strcasecmp( fields[1], "mic"))
683
         { 
684
           req_mgain = gain;
685
           mixer_recsrc = SOUND_MASK_MIC;
686
         }
687
         else
688
         if( !strcasecmp( fields[1], "overall")) req_igain = gain;
689
         else
690
         if( !strcasecmp( fields[1], "record")) req_rgain = gain;
691
         else
692
            bailout( "unknown gain control [%s]", fields[1]);
693
      }
694
      else
695
      if( nf == 2 && !strcasecmp( fields[0], "rate"))
696
         sample_rate = atoi( fields[1]);
697
      else
698
      if( nf == 2 && !strcasecmp( fields[0], "bins"))
699
         BINS = atoi( fields[1]);
700
      else
701
      if( nf == 2 && !strcasecmp( fields[0], "datadir"))
702
      {
703
         struct stat st;
704
         strcpy( datadir, fields[1]);
705
         if( stat( datadir, &st) < 0 || !S_ISDIR( st.st_mode))
706
            bailout( "no data directory, %s", datadir);
707
      }
708
      else
709
         bailout( "error in config file, line %d", lino);
710
   }
711
 
712
   fclose( f);
713
}
714
 
715
///////////////////////////////////////////////////////////////////////////////
716
//  Mixer Stuff                                                              //
717
///////////////////////////////////////////////////////////////////////////////
718
 
719
void open_mixer( void)
720
{
721
   if( (fdm = open( mixer, O_RDWR)) < 0)
722
      bailout( "cannot open [%s]: %s", mixer, strerror( errno));
723
 
724
   // Determine the available mixer recording gain controls.
725
   // We must at least have a line input.
726
 
727
   if( ioctl( fdm, SOUND_MIXER_READ_RECMASK, &mixer_recmask) < 0)
728
      bailout( "cannot read mixer devmask");
729
 
730
   if( (mixer_recmask & SOUND_MASK_LINE) == 0)
731
      bailout( "mixer has no line device");
732
 
733
   if( ioctl( fdm, SOUND_MIXER_READ_STEREODEVS, &mixer_stereo) < 0)
734
      bailout( "cannot read mixer stereodevs");
735
 
736
   if( ioctl( fdm, SOUND_MIXER_READ_RECSRC, &mixer_recsrc) < 0)
737
      bailout( "cannot read mixer recsrc");
738
 
739
   // Read the line input gain.  
740
   if( ioctl( fdm, SOUND_MIXER_READ_LINE, &mixer_line) < 0)
741
      bailout( "cannot read mixer line");
742
 
743
   // Read overall input gain?  Optional.
744
   if( (mixer_recmask & SOUND_MASK_IGAIN) &&
745
        ioctl( fdm, SOUND_MIXER_READ_IGAIN, &mixer_igain) < 0)
746
           bailout( "cannot read mixer igain");
747
 
748
   // Read overall recording level?  Optional.
749
   if( (mixer_recmask & SOUND_MASK_RECLEV) &&
750
        ioctl( fdm, SOUND_MIXER_READ_RECLEV, &mixer_reclev) < 0)
751
           bailout( "cannot read mixer reclev");
752
}
753
 
754
void report_mixer_settings( void)
755
{
756
   report( 1, "mixer: line input is %s", 
757
     mixer_stereo & SOUND_MASK_LINE ? "stereo" : "mono");
758
 
759
   report( 1, "mixer: line input is %s", 
760
     mixer_recsrc & SOUND_MASK_LINE ? "on" : "off");
761
 
762
   report( 1, "mixer: line input gain: left=%d right=%d", 
763
             mixer_line & 0xff, (mixer_line >> 8) & 0xff);
764
 
765
   // Overall input gain?  Optional.
766
   if( mixer_recmask & SOUND_MASK_IGAIN)
767
   {
768
      report( 1, "mixer: igain: left=%d right=%d", 
769
                 mixer_igain & 0xff, (mixer_igain >> 8) & 0xff);
770
   }
771
   else report( 1, "mixer: igain: n/a");
772
 
773
   // Overall recording level?  Optional.
774
   if( mixer_recmask & SOUND_MASK_RECLEV)
775
   {
776
      report( 1, "mixer: reclev: left=%d right=%d", 
777
                mixer_reclev & 0xff, (mixer_reclev >> 8) & 0xff);
778
   }
779
   else report( 1, "mixer: reclev: n/a");
780
 
781
}
782
 
783
void setup_mixer( void)
784
{
785
   if( req_lgain >= 0)
786
   {
787
      report( 1, "requesting line input gains left=%d right=%d",
788
             req_lgain & 0xff, (req_lgain >> 8) & 0xff);
789
 
790
      if( ioctl( fdm, SOUND_MIXER_WRITE_LINE, &req_lgain) < 0 ||
791
          ioctl( fdm, SOUND_MIXER_READ_LINE, &mixer_line) < 0)
792
         bailout( "error setting mixer line gain");
793
 
794
      report( 1, "line input gains set to: left=%d right=%d", 
795
             mixer_line & 0xff, (mixer_line >> 8) & 0xff);
796
   }
797
 
798
   if( req_mgain >= 0)
799
   {
800
      report( 1, "requesting microphone input gains left=%d right=%d",
801
             req_mgain & 0xff, (req_mgain >> 8) & 0xff);
802
 
803
      if( ioctl( fdm, SOUND_MIXER_WRITE_MIC, &req_mgain) < 0 ||
804
          ioctl( fdm, SOUND_MIXER_READ_MIC, &mixer_microphone) < 0)
805
         bailout( "error setting mixer microphone gain");
806
 
807
      report( 1, "Microphone input gains set to: left=%d right=%d", 
808
             mixer_microphone & 0xff, (mixer_microphone >> 8) & 0xff);
809
   }
810
 
811
   if( req_igain >= 0 &&
812
       (mixer_recmask & SOUND_MASK_IGAIN))
813
   {
814
      report( 1, "requesting overall input gains left=%d right=%d",
815
             req_igain & 0xff, (req_igain >> 8) & 0xff);
816
 
817
      if( ioctl( fdm, SOUND_MIXER_WRITE_IGAIN, &req_igain) < 0 ||
818
          ioctl( fdm, SOUND_MIXER_READ_IGAIN, &mixer_igain) < 0)
819
         bailout( "error setting mixer overall input gain");
820
 
821
      report( 1, "overall input gains set to: left=%d right=%d", 
822
                 mixer_igain & 0xff, (mixer_igain >> 8) & 0xff);
823
   }
824
 
825
   if( req_rgain >= 0 &&
826
       (mixer_recmask & SOUND_MASK_RECLEV))
827
   {
828
      report( 1, "requesting overall record levels left=%d right=%d",
829
             req_rgain & 0xff, (req_rgain >> 8) & 0xff);
830
 
831
      if( ioctl( fdm, SOUND_MIXER_WRITE_RECLEV, &req_rgain) < 0 ||
832
          ioctl( fdm, SOUND_MIXER_READ_RECLEV, &mixer_reclev) < 0)
833
         bailout( "error setting mixer record level");
834
 
835
      report( 1, "mixer record levels set to: left=%d right=%d", 
836
                 mixer_reclev & 0xff, (mixer_reclev >> 8) & 0xff);
837
   }
838
 
839
//mixer_recsrc= SOUND_MASK_LINE;
840
mixer_recsrc= SOUND_MASK_MIC;
841
 
842
   switch (mixer_recsrc)
843
   {
844
     case SOUND_MASK_MIC:
845
 
846
       if( ioctl( fdm, SOUND_MIXER_WRITE_RECSRC, &mixer_recsrc) < 0)
847
        bailout( "cannot set mixer recsrc to microphone");
848
       else report(1, "Input device set to microphone");
849
     break;
850
 
851
     case SOUND_MASK_LINE:
852
       if( ioctl( fdm, SOUND_MIXER_WRITE_RECSRC, &mixer_recsrc) < 0)
853
        bailout( "cannot set mixer recsrc to line");
854
       else report(1, "Input device set to line");
855
     break;
856
   }
857
}
858
 
859
///////////////////////////////////////////////////////////////////////////////
860
//  Main                                                                     //
861
///////////////////////////////////////////////////////////////////////////////
862
 
863
void make_daemon( void)
864
{
865
   int childpid, fd;
866
 
867
   if( (childpid = fork()) < 0)
868
      bailout( "cannot fork: %s", strerror( errno));
869
   else if( childpid > 0) exit( 0);
870
 
871
   if( setpgrp() == -1) bailout( "cannot setpgrp");
872
 
873
   if( (childpid = fork()) < 0)
874
      bailout( "cannot fork: %s", strerror( errno));
875
   else if( childpid > 0) exit( 0);
876
 
877
   for( fd = 0; fd <NOFILE; fd++) if( fd != fdi) close( fd);
878
   errno = 0;
879
   background = 2;
880
}
881
 
882
void initialise_channel( struct CHAN *c)
883
{
884
   int i;
885
 
886
   c->fft_inbuf = (double *) malloc( BINS * 2 * sizeof( double));
887
   c->fft_data = fftw_malloc( sizeof( fftw_complex) * FFTWID);
888
   c->powspec = (double *) malloc( BINS * sizeof( double));
889
   c->signal_avg = (double *) malloc( BINS * sizeof( double));
890
   for( i=0; i<BINS; i++) c->signal_avg[i] = 0;
891
}
892
 
893
void setup_signal_handling( void)
894
{
895
   sa.sa_handler = handle_sigs;
896
   sigemptyset( &sa.sa_mask);
897
   sa.sa_flags = 0;
898
   sigaction( SIGINT, &sa, NULL);
899
   sigaction( SIGTERM, &sa, NULL);
900
   sigaction( SIGHUP, &sa, NULL);
901
   sigaction( SIGQUIT, &sa, NULL);
902
   sigaction( SIGFPE, &sa, NULL);
903
   sigaction( SIGBUS, &sa, NULL);
904
   sigaction( SIGSEGV, &sa, NULL);
905
}
906
 
907
// Set scheduling priority to the minimum SCHED_FIFO value.
908
void set_scheduling( void)
909
{
910
   struct sched_param pa;
911
   int min = sched_get_priority_min( SCHED_FIFO);
912
 
913
   pa.sched_priority = min;
914
   if( sched_setscheduler( 0, SCHED_FIFO, &pa) < 0)
915
      report( -1, "cannot set scheduling priority: %s", strerror( errno));
916
   else
917
      report( 0, "using SCHED_FIFO priority %d", min);
918
}
919
 
920
int main( int argc, char *argv[])
921
{
922
   while( 1)
923
   {
924
      int c = getopt( argc, argv, "vfm");
925
 
926
      if( c == 'v') VFLAG++;
927
      else
928
      if( c == 'm') MFLAG++;
929
      else
930
      if( c == 'f') background = 0;
931
      else if( c == -1) break;
932
      else bailout( "unknown option [%c]", c);
933
   }
934
 
935
   setup_signal_handling();
936
   load_config();
937
   open_mixer();
938
 
939
   if( MFLAG)
940
   {
941
      VFLAG = 1;
942
      background = 0;
943
      report_mixer_settings();
944
      exit( 0);
945
   }
946
 
947
   setup_mixer();
948
 
949
   if( background && !logfile[0]) 
950
      report( -1, "warning: no logfile specified for daemon");
951
 
952
   setup_input_stream();
953
   DF = (double) sample_rate/(double) FFTWID;
954
 
955
   report( 1, "resolution: bins=%d fftwid=%d df=%f", BINS, FFTWID, DF);
956
   report( 1, "spectrum file: %s", spectrum_file); 
957
 
958
   initialise_channel( &left);
959
   if( mode == 2) initialise_channel( &right);
960
 
961
   if( background) make_daemon();
962
   if( priority) set_scheduling();
963
 
964
   report( 0, "sidd version %s: starting work", VERSION);
965
   alert_on = 1;
966
   process_signal();
967
   return 0;
968
}
969