home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / hf / dsp / source / lpc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-30  |  15.5 KB  |  611 lines

  1. /*  LPC.C -- MSDOS Front End for the DSP CARD 3 LPC Vocoder
  2.  *
  3.  *  Copyright (C) by Alef Null 1992
  4.  *  Author(s): Jarkko Vuori, OH2LNS
  5.  *  Modification(s):
  6.  *    26-Sep-92: corrected double CTRL-C bug
  7.  *           corrected file open bug while playing
  8.  *           added DSP CARD buffer control mechanism to remove audible clicks from playback
  9.  *           added DSP CARD presense detector
  10.  *           now uses the current flags in the vocoder
  11.  *    29-Sep-92: added CD command
  12.  *           commands can now be given in short form (e.g. PL for PLAY)
  13.  *    30-Sep-92: added monotonic mode
  14.  *           added file date output to the directory command
  15.  *           corrected initial DSP CARD presense status bug
  16.  */
  17.  
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <malloc.h>
  22. #include <string.h>
  23. #include <signal.h>
  24. #include <conio.h>
  25. #include <time.h>
  26. #include <ctype.h>
  27. #include <dos.h>
  28. #include <direct.h>
  29. #include "serial.h"
  30.  
  31. #define EXT        "voi"        // default filename extension
  32.  
  33. #define ERROR            fprintf(stderr, "Internal error (%d)\n", __LINE__)
  34. #define FILENAME(s)        AddExtension((s), EXT)
  35. #define DIFFERENCE(w, r, d) ((((w) - (r) >= 0) ? (w) - (r) : 16 - ((r) - (w))) <= (d))
  36.  
  37. #define PORT        1            // default serial port number
  38. #define BAUD        9600        // baud rate
  39.  
  40. #define SYNC1        0x55        // frame sync words
  41. #define SYNC2        0xaa
  42. #define LPCBLKLEN   6            // number of one LPC block bytes (48 bit)
  43. #define BLKS        20            // number of 48-bit block in one frame
  44.  
  45. typedef enum { True = -1, False = 0 } Bool;
  46. typedef struct {
  47.     struct {
  48.     unsigned char whisper:1;    // flags bits
  49.     unsigned char silence:1;
  50.     unsigned char loopb:1;
  51.     unsigned char monot:1;
  52.     unsigned char curblkn:4;    // then running frame counter
  53.     }            flags;
  54.     char        p_d;
  55.     unsigned char   data[BLKS*LPCBLKLEN];
  56. } VOCODERBLK;
  57. typedef struct {
  58.     unsigned        id;
  59. } FILEHEADER;
  60.  
  61. FILE       *playfd, *recordfd;
  62. VOCODERBLK  in, out;
  63. FILEHEADER  fileHeader = { 10964 };
  64. Bool        fDSPSensed = True;
  65.  
  66.  
  67. /* ctrl-C handler (to make ctrl-C inactive) */
  68. void cdecl CtrlCHandler(void) {
  69.     printf("Type 'QUIT' to quit the program\n");
  70.     signal(SIGINT, CtrlCHandler);
  71. }
  72.  
  73.  
  74. static char *AddExtension(char *FileName, char *Extension) {
  75.     static char Name[13];
  76.     char        *s;
  77.     
  78.     s = Name;
  79.     while(*FileName && *FileName != '.')
  80.     *s++ = *FileName++;
  81.         
  82.     if(*FileName)
  83.         while(*s++ = *FileName++);
  84.     else {
  85.         *s++ = '.';
  86.     
  87.         while(*s++ = *Extension++);
  88.     }
  89.     
  90.     return(Name);
  91. }
  92.  
  93.  
  94. /* check that the given fh contains a valid header */
  95. static Bool CheckHeader(FILE *fd) {
  96.     FILEHEADER fh;
  97.  
  98.     if (fread(&fh, sizeof(FILEHEADER), 1, fd) != 1 || fh.id != fileHeader.id)
  99.     return (False);
  100.     else
  101.     return (True);
  102. }
  103.  
  104.  
  105. /* write header */
  106. static void WriteHeader(FILE *fd) {
  107.     fwrite(&fileHeader, sizeof(FILEHEADER), 1, fd);
  108. }
  109.  
  110.  
  111. /* write one block to the DSP Card */
  112. static void WriteVocoderBlock(void) {
  113.    register unsigned char *p;
  114.  
  115.     WriteSerial(SYNC1); WriteSerial(SYNC2);
  116.     for (p = (unsigned char *)&out; p < &out.data[BLKS*LPCBLKLEN]; p++)
  117.     WriteSerial(*p);
  118. }
  119.  
  120.  
  121. /* Read one character from the console and at the same time handle serial line */
  122. static int getusrchr(void) {
  123.     static enum { sync1, sync2, flg, pitch, datas } state = sync1;
  124.     static Bool       fFirstTime = True;
  125.     static time_t      tBlockRead;
  126.     static unsigned char *byteptr;
  127.     static char       buffer[80];
  128.     static char      *head = buffer, *tail = buffer, *lineend = buffer;
  129.     int           c;
  130.     #define NEXT(p)      ((((p)+1) >= &buffer[80]) ? buffer : (p)+1)
  131.     #define PREV(p)      ((((p)-1) < buffer) ? &buffer[80-1] : (p)-1)
  132.  
  133.     if (!tBlockRead)
  134.     time(&tBlockRead);
  135.  
  136.     /* check if user want something */
  137.     while (True)
  138.     if (!kbhit()) {
  139.         /* no user activity, play with serial line */
  140.         if ((c = ReadSerial()) != -1) {
  141.         switch(state) {
  142.         case sync1:
  143.             if (c == SYNC1)
  144.             state = sync2;
  145.             break;
  146.  
  147.         case sync2:
  148.             if (c == SYNC2)
  149.             state = flg;
  150.             else
  151.             state = sync1;
  152.             break;
  153.  
  154.         case flg:
  155.             *(unsigned char *)&in.flags = (unsigned char)c;
  156.             state = pitch;
  157.             break;
  158.  
  159.         case pitch:
  160.             in.p_d = (char)c;
  161.             byteptr = in.data;
  162.             state = datas;
  163.             break;
  164.  
  165.         case datas:
  166.             *byteptr++ = (unsigned char)c;
  167.             if (byteptr > &in.data[BLKS*LPCBLKLEN-1]) {
  168.             /* one block read */
  169.             state = sync1;
  170.  
  171.             /* write it to the file if necessary */
  172.             if (recordfd)
  173.                 fwrite(in.data, sizeof(in.data), 1, recordfd);
  174.  
  175.             /* record when this block was read */
  176.             time(&tBlockRead);
  177.  
  178.             if (!fDSPSensed) {
  179.                 fprintf(stderr, "\bSYS INFO: DSP CARD 3 is back!\n>");
  180.                 fDSPSensed = True;
  181.             }
  182.  
  183.             if (fFirstTime) {
  184.                 out.flags    = in.flags;
  185.                 out.p_d    = in.p_d;
  186.                 fFirstTime    = False;
  187.             }
  188.             }
  189.             break;
  190.  
  191.         default:
  192.             ERROR;
  193.             break;
  194.         }
  195.         } else
  196.         /* output a new block if there is space left in the DSP input buffer */
  197.         if (!fFirstTime) {
  198.             if (DIFFERENCE(out.flags.curblkn, in.flags.curblkn, 3)) {
  199.             /* read block from the file if necessary */
  200.             if (playfd)
  201.                 if (fread(out.data, sizeof(out.data), 1, playfd) != 1) {
  202.                 /* play it again (sam) if needed */
  203.                 fseek(playfd, (long)sizeof(FILEHEADER), SEEK_SET);
  204.                 fread(out.data, sizeof(out.data), 1, playfd);
  205.                 }
  206.  
  207.             /* give one block to the Vocoder */
  208.             WriteVocoderBlock();
  209.  
  210.             /* update running block number counter */
  211.             out.flags.curblkn++;
  212.             } else if (time(NULL) - tBlockRead > 2) {
  213.             if (fDSPSensed) {
  214.                 fprintf(stderr, "\x007\bSYS INFO: DSP CARD 3 not connected\n>");
  215.                 fDSPSensed = False;
  216.             }
  217.  
  218.             /* no DSP, try to write last block again */
  219.             WriteVocoderBlock();
  220.             time(&tBlockRead);
  221.             }
  222.         } else if (time(NULL) - tBlockRead > 2)
  223.             if (fDSPSensed) {
  224.             fprintf(stderr, "\x007\bSYS INFO: DSP CARD 3 not connected\n>");
  225.             fDSPSensed = False;
  226.             }
  227.  
  228.         /* return data if available */
  229.         if (tail != lineend) {
  230.         c = *tail;
  231.         tail = NEXT(tail);
  232.         return (c);
  233.         }
  234.     } else {
  235.         /* keyboard has been pressed, read the given character */
  236.         switch (c = getch()) {
  237.         case '\x08':
  238.         if (head != tail) {
  239.             head = PREV(head);
  240.             putch('\x08'); putch(' '); putch('\x08');
  241.         }
  242.         break;
  243.  
  244.         case '\r':
  245.         if (NEXT(head) != tail) {
  246.             putch('\r'); putch(*head = '\n');
  247.             head = NEXT(head);
  248.             lineend = head;
  249.         } else
  250.             putch('\x07');
  251.         break;
  252.  
  253.         default:
  254.         if (NEXT(head) != tail) {
  255.             putch(*head = c);
  256.             head = NEXT(head);
  257.         } else
  258.             putch('\x07');
  259.         break;
  260.         }
  261.     }
  262.  
  263. }
  264.  
  265. /* Read input from the user, and partition the input to command and parameters */
  266. static Bool getline(char *cmd, char *arg) {
  267.     int c;
  268.  
  269.     /* flush leading whitespace characters */
  270.     while (isspace(c = getusrchr()) && c != '\n');
  271.     if (c == '\n')
  272.     return (False);
  273.  
  274.     /* then get first word on the line */
  275.     do {
  276.     *cmd++ = (char)toupper(c);
  277.     } while (!isspace(c = getusrchr()));
  278.     *cmd = *arg = '\0';
  279.     if (c == '\n')
  280.     return (True);
  281.  
  282.     /* flush leading whitespace characters */
  283.     while (isspace(c) && c != '\n')
  284.     c = getusrchr();
  285.  
  286.     /* and finally get the rest of the line */
  287.     if (c != '\n') {
  288.     do {
  289.         *arg++ = (char)c;
  290.     } while ((c = getusrchr()) != '\n');
  291.     *arg = '\0';
  292.     }
  293.  
  294.     return (True);
  295. }
  296.  
  297.  
  298. /*  Return date string */
  299. static char *dateStr(unsigned date) {
  300.     struct {
  301.     int   co_date;        // date format
  302.     char  co_curr[5];   // currency symbol
  303.     char  co_thsep[2];  // thousands separator
  304.     char  co_desep[2];  // decimal separator
  305.     char  co_dtsep[2];  // date separator
  306.     char  co_tmsep[2];  // time separator
  307.     char  co_currstyle; // currency style
  308.     char  co_digits;    // significant digits in currency
  309.     char  co_time;        // time format
  310.     long  co_case;        // case map
  311.     char  co_dasep[2];  // data separator
  312.     char  co_fill[10];  // filler
  313.     } country_info;
  314.     struct {            // MSDOS directory date format
  315.     unsigned day:5;
  316.     unsigned month:4;
  317.     unsigned year:7;
  318.     } d;
  319.     static char result[10+1];
  320.  
  321.     *(unsigned *)&d = date;
  322.  
  323.     /* first find the current output format */
  324.     bdos(0x38, (unsigned)&country_info, 0x00);
  325.  
  326.     /* then formulate date using correct format */
  327.     switch (country_info.co_date) {
  328.     case 0: // USA
  329.     sprintf(result, "%02d%s%02d%s%04d", d.month, country_info.co_dtsep, d.day, country_info.co_dtsep, d.year+1980);
  330.     break;
  331.  
  332.     case 1: // Europe
  333.     default:
  334.     sprintf(result, "%02d%s%02d%s%04d", d.day, country_info.co_dtsep, d.month, country_info.co_dtsep, d.year+1980);
  335.     break;
  336.  
  337.     case 2: // Japan
  338.     sprintf(result, "%04d%s%02d%s%02d", d.year+1980, country_info.co_dtsep, d.month, country_info.co_dtsep, d.day);
  339.     break;
  340.     }
  341.  
  342.     return (result);
  343. }
  344.  
  345. static void logo(void) {
  346.     printf("LPC -- DSP CARD 3 2400 bit/s LPC Vocoder front end (%s)\n", __DATE__);
  347.     printf("Copyright (C) by Alef Null 1992. All rights reserved.\n\n");
  348. }
  349.  
  350. static void Help(char *line) {
  351.     printf("Available commands (commands can be abbreviated):\n");
  352.     printf("CD       <directory>    - change current directory\n");
  353.     printf("DEL      <filename>     - delete given voice file\n");
  354.     printf("DIR                     - show current voice files\n");
  355.     printf("HELP                    - give this message\n");
  356.     printf("MONOTONE [ON | OFF]     - set/show whispering mode\n");
  357.     printf("PITCH    [value]        - change/show pitch changing status\n");
  358.     printf("PLAY     <filename>     - playback\n");
  359.     printf("QUIT                    - quit the program\n");
  360.     printf("RECORD   <filename>     - record\n");
  361.     printf("STOP                    - stop playing/recording\n");
  362.     printf("WHAT     [EVERYTHING]   - tell usefull information\n");
  363.     printf("WHISPER  [ON | OFF]     - set/show whispering mode\n");
  364. }
  365.  
  366. static void Why(char *line) {
  367.     static char *answers[] = {
  368.     "It feels good.",
  369.     "How the hell should I know?",
  370.     "Jacques make me do it.",
  371.     "Time to get back to work.",
  372.     "R.T.F.M.",
  373.     "Stupid question.",
  374.     "For people like you.",
  375.     "What?",
  376.     "Because it's there.",
  377.     "Why not?",
  378.     };
  379.  
  380.     printf("%s\n", answers[(int)(((double)rand()/32768.0)*10.0)]);
  381. }
  382.  
  383.  
  384. static void Record(char *line) {
  385.     FILE *tmpFp;
  386.  
  387.     if (*line) {
  388.     if (recordfd)
  389.         printf("Allready recording\n");
  390.     else if ((tmpFp = fopen(FILENAME(line), "wb")) == NULL)
  391.         printf("Can't create recording file '%s'\n", FILENAME(line));
  392.     else {
  393.         WriteHeader(tmpFp);
  394.         recordfd = tmpFp;
  395.     }
  396.     } else
  397.     printf("Recording file must be specified\n");
  398. }
  399.  
  400.  
  401. static void Play(char *line) {
  402.     FILE *tmpFp;
  403.  
  404.     if (*line) {
  405.     if ((tmpFp = fopen(FILENAME(line), "rb")) == NULL)
  406.         printf("Can't open playback file '%s'\n", FILENAME(line));
  407.     else if (!CheckHeader(tmpFp)) {
  408.         printf("'%s' is not a valid voice file\n", FILENAME(line));
  409.         fclose(tmpFp);
  410.     } else {
  411.         if (playfd) fclose(playfd);
  412.  
  413.         playfd = tmpFp;
  414.         out.flags.loopb = False;
  415.     }
  416.     } else
  417.     printf("Playback file must be specified\n");
  418. }
  419.  
  420.  
  421. static void Stop(char *line) {
  422.     if (recordfd) {
  423.     fclose(recordfd);
  424.     recordfd = NULL;
  425.     }
  426.     if (playfd) {
  427.     fclose(playfd);
  428.     playfd = NULL;
  429.     out.flags.loopb = True;
  430.     }
  431. }
  432.  
  433.  
  434. static void What(char *line) {
  435.     if (!fDSPSensed)
  436.     printf("No Alef Null DSP CARD Vocoder device detected\n\n");
  437.     printf("Playback:      %s\n", playfd ? "PLAYING" : "STOP");
  438.     printf("Record:        %s\n", recordfd ? "RECORDING" : "STOP");
  439.  
  440.     if (*line) {
  441.     printf("\nVersion:       %s\n", __DATE__);
  442.     printf("Whisper mode:  %s\n", out.flags.whisper ? "ON" : "OFF");
  443.     printf("Monotone mode: %s\n", out.flags.monot ? "ON" : "OFF");
  444.     printf("Pitch change:  %d\n", (int)out.p_d);
  445.     }
  446. }
  447.  
  448. static void Whisper(char *line) {
  449.     if (*line)
  450.     out.flags.whisper = !stricmp(line, "ON");
  451.     else
  452.     printf("Whisper mode: %s\n", out.flags.whisper ? "ON" : "OFF");
  453. }
  454.  
  455. static void Monotone(char *line) {
  456.     if (*line)
  457.     out.flags.monot = !stricmp(line, "ON");
  458.     else
  459.     printf("Monotone mode: %s\n", out.flags.monot ? "ON" : "OFF");
  460. }
  461.  
  462. static void Pitch(char *line) {
  463.     if (*line)
  464.     out.p_d = (char)atoi(line);
  465.     else
  466.     printf("Pitch change: %d\n", (int)out.p_d);
  467. }
  468.  
  469. static void Dir(char *line) {
  470.     struct find_t voiFile;
  471.  
  472.     if (_dos_findfirst(FILENAME("*"), _A_NORMAL, &voiFile) == 0)
  473.     do {
  474.         *strchr(voiFile.name, '.') = '\0';    // erase extension
  475.         printf("%-12s %4lus  %s\n", voiFile.name, voiFile.size/(LPCBLKLEN*50), dateStr(voiFile.wr_date));
  476.     } while (_dos_findnext(&voiFile) == 0);
  477. }
  478.  
  479. static void Delete(char *line) {
  480.     if (*line) {
  481.     if (remove(FILENAME(line)) == -1)
  482.         printf("Can't remove file '%s'\n", FILENAME(line));
  483.     } else
  484.     printf("Filename must be specified\n");
  485. }
  486.  
  487. static void ChangeDir(char *line) {
  488.     char *buf;
  489.  
  490.     if (*line) {
  491.     if (chdir(line) == -1)
  492.         printf("Can't change to directory '%s'\n", line);
  493.     } else {
  494.     buf = getcwd(NULL, 80);
  495.     printf("Current directory '%s'\n", buf);
  496.     free(buf);
  497.     }
  498. }
  499.  
  500. static void NotImplemented(char *line) {
  501.     printf("Not yet implemented\n");
  502. }
  503.  
  504. static void Exit(char *line) {
  505.     Stop(NULL);
  506.     CloseSerial();
  507.     exit(0);
  508. }
  509.  
  510. /*
  511.  * Check if the command is reserved word
  512.  *
  513.  *  returns pointer to specified function if match is found
  514.  *        NULL if no match is found
  515.  */
  516. static void (*ChkReservedWord(const char *name))(char *line) {
  517.     static struct rwtable {
  518.     const char *rw_name;
  519.     void (*rw_func)(char *line);
  520.     } rwtable[] = {
  521.     "?",        Help,        "CD",    ChangeDir,
  522.     "DEL",        Delete,        "DIR",    Dir,
  523.     "EXIT",     Exit,        "FUCK",    NotImplemented,
  524.     "HELP",     Help,        "MONOTONE", Monotone,
  525.     "PITCH",    Pitch,        "PLAY",    Play,
  526.     "QUIT",     Exit,        "RECORD",    Record,
  527.     "STOP",     Stop,        "WHAT",    What,
  528.     "WHISPER",  Whisper,        "WHY",    Why,
  529.     };
  530.     register struct rwtable *low  = rwtable,
  531.                 *high = rwtable + sizeof(rwtable)/sizeof(struct rwtable) - 1,
  532.                 *mid;
  533.     int c;
  534.  
  535.     /* binary search on reserved words table */
  536.     while(low <= high) {
  537.     mid = low + (high-low)/2;
  538.     if(!(c = strncmp(mid->rw_name, name, strlen(name))))
  539.         return(mid->rw_func);
  540.     else if(c < 0)
  541.         low = mid+1;
  542.     else
  543.         high = mid-1;
  544.     }
  545.  
  546.     return(NULL);
  547. }
  548.  
  549.  
  550. int cdecl main(int argc, char *argv[]) {
  551.     void  (*fp)(char *line);
  552.     int     port = PORT, mode = 0;
  553.     char    cmd[80], arg[80];
  554.     char    option;
  555.  
  556.     /* first initialize random number generator */
  557.     srand((unsigned)time(NULL));
  558.  
  559.     /* then parse arguments */
  560.     argc--; argv++;
  561.     while(argc > 0) {
  562.     switch(**argv) {
  563.     case '-':
  564.         switch(option = *(++(*argv))) {
  565.         /* port selection */
  566.         case 'p':
  567.         case 'P':
  568.         port = atoi(++(*argv));
  569.         break;
  570.  
  571.         default:
  572.         fprintf(stderr, "unknown option '%c', type 'lpc ?' to get the allowed options\n", option);
  573.         break;
  574.         }
  575.         break;
  576.  
  577.     case '?':
  578.     default:
  579.         fprintf(stderr, "usage: lpc [-p<portno>]\n");
  580.         fprintf(stderr, "\t-p<portno> uses the specified (1 (default) or 2) serial port\n");
  581.         return (0);
  582.         break;
  583.     }
  584.  
  585.     argc--; argv++;
  586.     }
  587.  
  588.     /* then open serial line */
  589.     signal(SIGINT, CtrlCHandler);
  590.     if (!OpenSerial(port, BAUD)) {
  591.     fprintf(stderr, "can't open file serial port COM%d\n", port);
  592.     return (1);
  593.     }
  594.  
  595.     /* and talk to the user */
  596.     logo();
  597.     while (True) {
  598.     putchar('>');
  599.  
  600.     if (getline(cmd, arg)) {
  601.         if (fp = ChkReservedWord(cmd))
  602.         (*fp)(arg);
  603.         else
  604.         printf("Unknown command, try 'HELP'\n");
  605.     }
  606.     }
  607.  
  608.     CloseSerial();
  609.     return (0);
  610. }
  611.