Logo Search packages:      
Sourcecode: saytime version File versions  Download package

saytime.c

/*
** saytime - audio time hack for the SPARCstation.
**
** Copyright (C) 1990 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/
/* modified on 2002/2/20 by Oohara Yuuma */
/* modified on 2004/09/05 by David Dawson */
/* added sanity check for negative interval on 2004/10/1 by Oohara Yuuma */

#include <stdio.h>
#include <time.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>

#define DEFAULT_SOUND_DEVICE "/dev/audio"
#define DEFAULT_SOUND_DIR "/usr/share/saytime"
#define DEFAULT_TIME_FORMAT "%P%l%M%S"
#define DEFAULT_INTERVAL 1
#define RATE "8000"
/* There are now thirty-seven phrases. 
I used Audacity to record a new set of phrases and dissect them
and Sox to convert them to mu-law .au files. [D.D.].
It took about 10 minutes.
*/

/*   Here are the defines for the thirty different phrases the program
** needs.  If you want to substitute your own voice for mine, I suggest
** you record the following lines:
**
**     The time is zero hours eight minutes and three seconds.
**       The time is one hour one minute and one second.
**     The time is eight o'clock PM.
**     The time is eight oh-eight AM exactly.
**     Zero.  One.  Two.  Three.  Four.  Five.  Six.  Seven.  Eight.  Nine.  
**     Ten.  Eleven.  Twelve.  Thirteen.  Fourteen.  Fifteen.  Sixteen.  
**       Seventeen.  Eighteen.  Nineteen.  Twenty.  Thirty.  Forty.  Fifty.
**
** Then use Sun's sound demo to dissect the lines into separate files.
** It's not really that much work, it took me about an hour.
*/
#define PH_ZERO         0
#define PH_ONE          1
#define PH_TWO          2
#define PH_THREE  3
#define PH_FOUR         4
#define PH_FIVE         5
#define PH_SIX          6
#define PH_SEVEN  7
#define PH_EIGHT  8
#define PH_NINE         9
#define PH_TEN          10
#define PH_ELEVEN 11
#define PH_TWELVE 12
#define PH_THIRTEEN     13
#define PH_FOURTEEN     14
#define PH_FIFTEEN      15
#define PH_SIXTEEN      16
#define PH_SEVENTEEN    17
#define PH_EIGHTEEN     18
#define PH_NINETEEN     19
#define PH_TWENTY 20
#define PH_THIRTY 21
#define PH_FORTY  22
#define PH_FIFTY  23
#define PH_THE_TIME_IS  24
#define PH_OCLOCK 25
#define PH_OH           26
#define PH_EXACTLY      27
#define PH_AND          28
#define PH_SECOND 29
#define PH_SECONDS      30
#define PH_HOUR         31
#define PH_HOURS  32
#define PH_MINUTE 33
#define PH_MINUTES      34
#define PH_AM           35
#define PH_PM           36

int opt_to_stdout = 0;
char *opt_sound_device = NULL;
char *opt_sound_dir = DEFAULT_SOUND_DIR;
char *opt_time_format = NULL;
char *opt_interval = NULL;
char *opt_volume = NULL;
#if defined TTEST
char *opt_test_hour;
char *opt_test_min;
char *opt_test_sec;
#endif
void kill_daemon(int signal);
void saynumber(), saydigit(), sayphrase(), sayfile(), usage();
void sayformat(struct tm *, char *);
int die = 0;
int main( argc, argv )
int argc;
char *argv[];
    {
    long clock;
    struct tm *t;
#if defined TTEST
    char *opt_string="v:r:co:d:f:h:H:N:C:";
#else
    char *opt_string="v:r:co:d:f:h";
#endif
    int  op ;
      pid_t fpid;
      unsigned interval=10;
      struct sigaction new_action, old_action;
      new_action.sa_handler = kill_daemon;
      sigemptyset(&new_action.sa_mask);
      new_action.sa_flags = 0;

      
      sigaction(SIGTERM, NULL, &old_action);
      if (old_action.sa_handler !=SIG_IGN)
            sigaction(SIGTERM,&new_action, NULL);

    while( (op=getopt(argc, argv, opt_string)) != -1) {

         switch (op) {
#if defined TTEST
                     case 'H' :  opt_test_hour = optarg;
                                       break;
                     case 'N' :  opt_test_min = optarg;
                                       break;
                     case 'C' :  opt_test_sec = optarg;
                                       break;
#endif                  
                     case 'v' :  opt_volume = optarg;
                                       break;
                     case 'r' :  opt_interval = optarg;
                                       break;
               case 'c' :  opt_to_stdout = 1;
                           break;
               case 'o' :  opt_sound_device = optarg;
                           break;
               case 'd' :  opt_sound_dir = optarg;
                           break;
               case 'f' :  opt_time_format = optarg;
                           break;
               case 'h' :
               default  :  usage();
         }
    }

    if (opt_to_stdout && opt_sound_device != NULL) {
      printf("Specifying alternate device and stdout makes no sense.\n");
      usage();
    }
    else if (opt_sound_device == NULL)
      opt_sound_device = DEFAULT_SOUND_DEVICE;

    if (opt_time_format == NULL)
        opt_time_format = DEFAULT_TIME_FORMAT;
      if (opt_interval != NULL) {
            interval = atol(opt_interval);
            if (interval == 0) 
                  interval = 10;
      }
      if (opt_volume == NULL)
            opt_volume="1";
      if (opt_interval) { 
            puts("Starting background process");      
            fpid = fork();
            if (fpid < 0) {
                  fprintf(stderr, "fork failed\n");
                  exit(1);
            }
            else if (fpid == 0) { 
                  while ((!die)
                               && (clock = time( (long *) 0 ) % interval))
                        usleep(10000);
                  while (!die) {
                        clock = time( (long *) 0 );
                  t = localtime( &clock );

#if defined TTEST
                  t->tm_hour = atoi(opt_test_hour);
                  t->tm_min = atoi(opt_test_min);
                  t->tm_sec = atoi(opt_test_sec);
#endif
                        sayformat(t, opt_time_format);
                        while ((!die)
                                       && (clock = time( (long *) 0 ) % interval))
                              usleep(100000);
                  }
            }
      }
      else
            clock = time( (long *) 0 );
          t = localtime( &clock );

#if defined TTEST
            t->tm_hour = atoi(opt_test_hour);
            t->tm_min = atoi(opt_test_min);
            t->tm_sec = atoi(opt_test_sec);
#endif
            if (opt_interval == NULL)
                  sayformat(t, opt_time_format);
    
    /* sayclose( ); */
    return 0;
    }

void  
usage( ) 
    {
    fprintf( stderr, "Usage: saytime [-ch] [-v lvl] [-r sec] [-d dir] [-f fmt] [-o dev]\n" );
    fprintf( stderr, "Speak the current time through the computer's sound device.\n\n" );
    fprintf( stderr, "Options:\n" );
    fprintf( stderr, " -v lvl\tset volume level\n" );
    fprintf( stderr, " -r sec\trepeat interval in background mode\n" );
    fprintf( stderr, " -c\toutput to stdout\n" );
    fprintf( stderr, " -d dir\tspecify sound directory [%s]\n", 
        DEFAULT_SOUND_DIR ) ;
    fprintf( stderr, " -f fmt\tspecify alternate time format [%s]\n",
        DEFAULT_TIME_FORMAT ) ;
    fprintf( stderr, " -h\tbrief help message\n" );
    fprintf( stderr, " -o dev\tspecify audio device [%s]\n", 
        DEFAULT_SOUND_DEVICE );
    exit( 1 );
    }

/*

  Say time based on printf formatted string.
  
  %P : "The time is"
  %l : hour, 12-hour clock
  %k : hour, 24-hour clock
  %M : minutes
  %S : seconds 

*/
void
sayformat( t, fmt ) 
struct tm *t;
char *fmt;
{
      int mflag = 0;
      int hflag =0;
      int flag = 0;
      char *pfmt = fmt;
            
      while (*pfmt++) {
            if (*pfmt == 'M')
                  mflag = 1;
            if (*pfmt == 'k' || *pfmt == 'l')
                  hflag = 1;
      }


    while ( *fmt ) 
      { 
        switch( *++fmt )
          {
            case 'P':
            sayphrase( PH_THE_TIME_IS );
            break;

            case 'l':
                  flag=0;
            if ( t->tm_hour == 0 )
                  saynumber( 12, 0 );
            else if ( t->tm_hour > 12 )
                    saynumber( t->tm_hour - 12, 0 );
          else
                saynumber( t->tm_hour, 0 );
            if ( t->tm_min == 0 )
                  sayphrase ( PH_OCLOCK );
            if (!mflag) {
              if (t->tm_hour < 12)
                sayphrase( PH_AM);
              else
                sayphrase( PH_PM);
            }
            break;

            case 'k':
                  flag=1;
            saynumber( t->tm_hour, 0 );
                if ( t->tm_hour == 1)
                  sayphrase( PH_HOUR );
                else
                  sayphrase( PH_HOURS );
            break;

            case 'M':
                  if (flag) {
                        if ( t->tm_min != 0) { 
                              saynumber( t->tm_min, 0 );
                              if ( t->tm_min == 1 )
                                    sayphrase ( PH_MINUTE );
                              else
                                    sayphrase ( PH_MINUTES );
                        }
                  }
                  else {
                        if ( t->tm_min != 0)
                              saynumber( t->tm_min, 1 );
                        if (hflag) {
                              if (t->tm_hour < 12)
                                    sayphrase( PH_AM);
                              else
                                    sayphrase( PH_PM);
                        }
                  }
            break;
            
            case 'S':
            if ( (t->tm_sec == 0) && mflag) {
                        sayphrase( PH_EXACTLY );
                  }
            else {
                        if (mflag)
                              sayphrase( PH_AND );
                  saynumber( t->tm_sec, 0 );
                  if ( t->tm_sec == 1 )
                        sayphrase( PH_SECOND );
                  else
                        sayphrase( PH_SECONDS );
                  }
            break;

            default:
            (void) fprintf( stderr, "Invalid format char '%c'\n", *fmt );
            exit( 1 );
        }
       fmt++;
      }
}

void
saynumber( n, leadingzero )
int n, leadingzero;
    {
    int ones, tens;

    ones = n % 10;
    tens = n / 10;

    switch ( tens )
      {
      case 0:
      if ( leadingzero )
          sayphrase( PH_OH );
      saydigit( ones );
      break;

      case 1:
      switch ( ones )
          {
          case 0:
          sayphrase( PH_TEN );
          break;

          case 1:
          sayphrase( PH_ELEVEN );
          break;

          case 2:
          sayphrase( PH_TWELVE );
          break;

          case 3:
          sayphrase( PH_THIRTEEN );
          break;

          case 4:
          sayphrase( PH_FOURTEEN );
          break;

          case 5:
          sayphrase( PH_FIFTEEN );
          break;

          case 6:
          sayphrase( PH_SIXTEEN );
          break;

          case 7:
          sayphrase( PH_SEVENTEEN );
          break;

          case 8:
          sayphrase( PH_EIGHTEEN );
          break;

          case 9:
          sayphrase( PH_NINETEEN );
          break;

          default:
          (void) fprintf( stderr, "Shouldn't happen.\n" );
          exit( 1 );
          }
      break;

      case 2:
      sayphrase( PH_TWENTY );
      if ( ones != 0 )
          saydigit( ones );
      break;

      case 3:
      sayphrase( PH_THIRTY );
      if ( ones != 0 )
          saydigit( ones );
      break;

      case 4:
      sayphrase( PH_FORTY );
      if ( ones != 0 )
          saydigit( ones );
      break;

      case 5:
      sayphrase( PH_FIFTY );
      if ( ones != 0 )
          saydigit( ones );
      break;

      default:
      (void) fprintf( stderr, "Shouldn't happen.\n" );
      exit( 1 );
      }
    }

void
saydigit( n )
int n;
    {
    switch ( n )
      {
      case 0:
      sayphrase( PH_ZERO );
      break;

      case 1:
      sayphrase( PH_ONE );
      break;

      case 2:
      sayphrase( PH_TWO );
      break;

      case 3:
      sayphrase( PH_THREE );
      break;

      case 4:
      sayphrase( PH_FOUR );
      break;

      case 5:
      sayphrase( PH_FIVE );
      break;

      case 6:
      sayphrase( PH_SIX );
      break;

      case 7:
      sayphrase( PH_SEVEN );
      break;

      case 8:
      sayphrase( PH_EIGHT );
      break;

      case 9:
      sayphrase( PH_NINE );
      break;

      default:
      (void) fprintf( stderr, "Shouldn't happen.\n" );
      exit( 1 );
      }
    }

void
sayphrase( phrase )
int phrase;
    {
    switch ( phrase )
      {
      case PH_ZERO:
      sayfile( "0.au" );
      break;
      
      case PH_ONE:
      sayfile( "1.au" );
      break;

      case PH_TWO:
      sayfile( "2.au" );
      break;

      case PH_THREE:
      sayfile( "3.au" );
      break;

      case PH_FOUR:
      sayfile( "4.au" );
      break;

      case PH_FIVE:
      sayfile( "5.au" );
      break;

      case PH_SIX:
      sayfile( "6.au" );
      break;

      case PH_SEVEN:
      sayfile( "7.au" );
      break;

      case PH_EIGHT:
      sayfile( "8.au" );
      break;

      case PH_NINE:
      sayfile( "9.au" );
      break;

      case PH_TEN:
      sayfile( "10.au" );
      break;

      case PH_ELEVEN:
      sayfile( "11.au" );
      break;

      case PH_TWELVE:
      sayfile( "12.au" );
      break;

      case PH_THIRTEEN:
      sayfile( "13.au" );
      break;

      case PH_FOURTEEN:
      sayfile( "14.au" );
      break;

      case PH_FIFTEEN:
      sayfile( "15.au" );
      break;

      case PH_SIXTEEN:
      sayfile( "16.au" );
      break;

      case PH_SEVENTEEN:
      sayfile( "17.au" );
      break;

      case PH_EIGHTEEN:
      sayfile( "18.au" );
      break;

      case PH_NINETEEN:
      sayfile( "19.au" );
      break;

      case PH_TWENTY:
      sayfile( "20.au" );
      break;

      case PH_THIRTY:
      sayfile( "30.au" );
      break;

      case PH_FORTY:
      sayfile( "40.au" );
      break;

      case PH_FIFTY:
      sayfile( "50.au" );
      break;

      case PH_THE_TIME_IS:
      sayfile( "the_time_is.au" );
      break;

      case PH_OCLOCK:
      sayfile( "oclock.au" );
      break;

      case PH_OH:
      sayfile( "oh.au" );
      break;

      case PH_EXACTLY:
      sayfile( "exactly.au" );
      break;

      case PH_AND:
      sayfile( "and.au" );
      break;

      case PH_SECOND:
      sayfile( "second.au" );
      break;

      case PH_SECONDS:
      sayfile( "seconds.au" );
      break;

      case PH_MINUTE:
      sayfile( "minute.au");
      break;

      case PH_MINUTES:
      sayfile( "minutes.au");
      break;

      case PH_HOUR:
      sayfile( "hour.au" );
      break;

      case PH_HOURS:
      sayfile( "hours.au" );
      break;
      
      case PH_AM:
      sayfile( "am.au" );
      break;
      
      case PH_PM:
      sayfile( "pm.au" );
      break;



      default:
      (void) fprintf( stderr, "Shouldn't happen.\n" );
      exit( 1 );
      }
    }

void
sayfile( filename )
     char *filename;
{
  pid_t pid;
  int status;
  char pathname[200];

  if (snprintf(pathname, 200, "%s/%s", opt_sound_dir, filename) >= 200)
  {
    fprintf(stderr, "too long sound file name\n");
    exit(1);
  }
  pid = fork();
  if (pid < 0)
  {
    fprintf(stderr, "fork failed\n");
    exit(1);
  }
  else if (pid == 0)
  {
    /* child */
    if (opt_to_stdout)
    {
      execl("/usr/bin/sox", "sox", "-r", RATE, "-q", "-v", opt_volume,"-t.ul", "-c", "1",
            pathname, "-t", "raw", "/dev/stdout", NULL);
    }
    else
    {
      execl("/usr/bin/sox", "sox", "-r", RATE, "-q", "-v", opt_volume,"-t.ul", "-c", "1",
            pathname, "-t", "ossdsp", opt_sound_device, NULL);
    }
    fprintf(stderr, "execl failed\n");
    exit(1);
  }
  /* parent */
  if (waitpid(pid, &status, 0) != pid)
  {
    fprintf(stderr, "waitpid failed\n");
    exit(1);
  }
  if (status != 0)
  {
    fprintf(stderr, "child process returned a non-zero status %d\n",
            WEXITSTATUS(status));
    exit(1);
  }
}
void kill_daemon(int signal) {
                  die = 1;
}

Generated by  Doxygen 1.6.0   Back to index