From 2bb39732c4aa397657e4fada677d3f3bea592e50 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 24 Jan 2005 20:48:23 +0000 Subject: [PATCH] Save the released 2.0 version. --- alarm.c | 58 -- alarm.h | 20 - beep.h | 59 -- beepLinux.c | 82 --- beepOSS.c | 207 ------ beepX11.c | 81 --- morse.c | 1935 --------------------------------------------------- 7 files changed, 2442 deletions(-) delete mode 100644 alarm.c delete mode 100644 alarm.h delete mode 100644 beep.h delete mode 100644 beepLinux.c delete mode 100644 beepOSS.c delete mode 100644 beepX11.c delete mode 100644 morse.c diff --git a/alarm.c b/alarm.c deleted file mode 100644 index 25eb9cf..0000000 --- a/alarm.c +++ /dev/null @@ -1,58 +0,0 @@ -/* alarm.c -- wakeup calls */ - -/* --- Implementation of alarm.h -*/ - -#include -#include -#include - -static int alarmPending = 0; /* Nonzero when the alarm is set. */ - -static void ualarm(); -static void AlarmHandler(); - -void AlarmSet(time) - int time; -{ - alarmPending = 1; - sigset(SIGALRM, AlarmHandler); - ualarm(1000 * time, 0); -} - -/* --- If an alarm signal is lurking (due to a prior call to SetAlarm), then --- pause until it arrives. This procedure could have simply been written: --- if (alarmPending) pause(); --- but that allows a potential race condition. -*/ -void AlarmWait() -{ - sighold(SIGALRM); - if (alarmPending) - sigpause(SIGALRM); - sigrelse(SIGALRM); -} - - -static void ualarm(us) - unsigned us; -{ - struct itimerval rttimer, old_rttimer; - - rttimer.it_value.tv_sec = us / 1000000; - rttimer.it_value.tv_usec = us % 1000000; - rttimer.it_interval.tv_sec = 0; - rttimer.it_interval.tv_usec = 0; - if (setitimer(ITIMER_REAL, &rttimer, &old_rttimer)) { - perror("ualarm"); - exit(1); - } -} - - -static void AlarmHandler() -{ - alarmPending = 0; -} diff --git a/alarm.h b/alarm.h deleted file mode 100644 index 33a5fb1..0000000 --- a/alarm.h +++ /dev/null @@ -1,20 +0,0 @@ -/* alarm.h -- wakeup call interface */ - -/* --- Routines for using the system interval timer to time beeps. Useful --- for implementing the functions in beep.h on systems that don't provide --- a more straightforward BeepWait() equivalent. --- --- These routines use the ALRM signal. -*/ - - -/* --- Set the alarm for a time specified in ms. -*/ -void AlarmSet(time_t); - -/* --- Wait for the alarm, or return immediately if the alarm isn't set. -*/ -void AlarmWait(void); diff --git a/beep.h b/beep.h deleted file mode 100644 index 7e1796b..0000000 --- a/beep.h +++ /dev/null @@ -1,59 +0,0 @@ -/* beep.h -- seligman 5/92 */ - -/* --- Machine-dependent code for sounding a beep. -*/ - -#ifndef _BEEP_H -#define _BEEP_H - - -/* --- Called exactly once, before any other function in this interface. --- Returns nonzero on error. -*/ -int BeepInit(void); - - -/* --- Sound a beep for a time specified in ms. --- The volume is in the range [0..100], and the pitch is in Hz. --- --- May return immediately, after the sounding of the beep is completed, --- or any time in between. May be called while a previous beep is still --- sounding, in which case the previous beep finishes before the new one --- begins. Overall timing will be much better if this routine can return --- during the sounding of the beep, especially if it's a "zero-volume beep", --- meaning it's really just the timed pause of silence between tones. --- ("morse.c" tries to do all its thinking during the pauses between beeps, --- mostly in the longer ones between words.) --- --- May use the ALRM signal for timing. --- --- Returns nonzero on error. -*/ -int Beep(int time, int volume, int pitch); - - -/* --- Wait until any currently sounding beeps have completed. --- Returns nonzero on error. -*/ -int BeepWait(void); - - -/* --- Clean up any altered state before exiting or suspending the program. --- Returns nonzero on error. -*/ -int BeepCleanup(void); - - -/* --- Restore the world when program is resumed after having been suspended. --- Returns nonzero on error. -*/ -int BeepResume(void); - - -#endif /* _BEEP_H */ diff --git a/beepLinux.c b/beepLinux.c deleted file mode 100644 index add29bb..0000000 --- a/beepLinux.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * From: jmorriso@bogomips.ee.ubc.ca (John Paul Morrison) - * Subject: beepLinux.c - * To: joe@montebello.soest.hawaii.edu - * Date: Fri, 18 Nov 1994 15:32:33 -0800 (PST) - * X-Linux: watch for it: Linux 94 aka "Helsinki" - * - * beepLinux.c for morse (lightly tested, seems to work fine) - */ - -/* - * beep for the Linux console (PC Speaker) - * - * (beepSun.c might work if you have a sound card installed and /dev/audio) - * - * by John Paul Morrison - * - */ - -/* beepLinux.c -- 11/94 */ - -#include -#include -#include -#include -#include -#include -#include "alarm.h" -#include "beep.h" - - -static int fd; - -int BeepInit() -{ - if ((fd = open("/dev/console",O_WRONLY)) == -1) - { - fprintf(stderr,"You have no permissions to use /dev/console (chmod a+w).\n"); - return 1; - } - return 0; -} - -int Beep(time, volume, pitch) -int time, volume, pitch; -{ - int count; - - AlarmWait(); - if (volume == 0) - count = 0; - else - count = (1193180 / pitch) & 0xffff; - - ioctl(fd, KDMKTONE, (time << 16) | count); - AlarmSet(time); - return 0; -} - -int BeepWait() -{ - AlarmWait(); - return 0; -} - -int BeepCleanup() -{ - close(fd); - return 0; -} - -int BeepResume() -{ - return 0; -} - -/* - * --------------------------------------------------------------------------- - * BogoMIPS Research Labs -- bogosity research & simulation -- VE7JPM -- - * jmorriso@bogomips.ee.ubc.ca ve7jpm@ve7jpm.ampr.org jmorriso@ve7ubc.ampr.org - * --------------------------------------------------------------------------- - */ diff --git a/beepOSS.c b/beepOSS.c deleted file mode 100644 index 6573539..0000000 --- a/beepOSS.c +++ /dev/null @@ -1,207 +0,0 @@ -/* beepOSS.c -- Jacek M. Holeczek 3/2000 */ -/* A lot of code taken from the beepSun.c */ - -/* - Implementation of beep for the Open Sound System /dev/dsp device. -*/ - -#include -#include -#include -#include -#include -#include - -#include "beep.h" - -#define DEVICE_NAME "/dev/dsp" -#define RATE (8000) /* 8 K samples = 1 second */ -#define RAMP (RATE * 5 / 1000) /* 5 millisecond ramp */ -#define MAXTIME (2 * RATE) /* 2 seconds max tone time */ - -static int audio = 0; -static int channels = 0; /* 1=mono, 2=stereo */ -static unsigned char silence[2*MAXTIME]; -static unsigned char soundbuf[2*(MAXTIME - RAMP)]; -static unsigned char ramp_down[2*RAMP]; - -int BeepInit (void) -{ - int i; - - /* - * Initialize the sound of silence - */ - for (i = 0; i < 2*MAXTIME; i++) - silence[i] = 128; - - audio = open(DEVICE_NAME, O_WRONLY, 0); - if (audio < 0) - { - return 1; /* not o.k. */ - } else { - - i = 0x00ff0008; /* 255 fragments, 256 bytes each */ - if (ioctl(audio, SNDCTL_DSP_SETFRAGMENT, &i)) - { - fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT error : 0x%08x\n", i); - return 2; - } - - channels = 1; /* 1 channel = mono */ - if (ioctl(audio, SNDCTL_DSP_CHANNELS, &channels)==-1) - { - fprintf(stderr, "SNDCTL_DSP_CHANNELS error : %i\n", channels); - return 3; - } - if (channels != 1) - { - channels = 2; /* 2 channels = stereo */ - if (ioctl(audio, SNDCTL_DSP_CHANNELS, &channels)==-1) - { - fprintf(stderr, "SNDCTL_DSP_CHANNELS error : %i\n", channels); - return 4; - } - } - if (!(channels == 1 || channels == 2)) - { - fprintf(stderr, "Neither mono nor stereo mode could be set.\n"); - return 5; - } - - i = AFMT_U8; /* standard unsigned 8 bit audio encoding */ - if (ioctl(audio, SNDCTL_DSP_SETFMT, &i)==-1) - { - fprintf(stderr, "SNDCTL_DSP_SETFMT error : %i\n", i); - return 6; - } - - i = RATE; /* sampling rate RATE */ - if (ioctl(audio, SNDCTL_DSP_SPEED, &i)==-1) - { - fprintf(stderr, "SNDCTL_DSP_SPEED error : %i\n", i); - return 7; - } - - write (audio, silence, channels*2); - BeepWait(); - return 0; /* o.k. */ - } -} - -int Beep (int time, int volume, int pitch) -{ - static int last_pitch = -1, last_n = -1, last_volume = -1; - int i, n, first_len, down_len, cycle; - double dt; - - /* - * Finagle the number of samples - */ - n = (time / 1000.) * RATE; /* Number samples in tone time */ - n = n < MAXTIME ? n : MAXTIME; /* clip to buffer size */ - n = n < 2 * RAMP ? 2 * RAMP : n; /* leave room for ramps */ - - /* - * Catch stupidity - */ - if (pitch <= 0) - volume = 0; - - if (volume <= 0) - { - write (audio, silence, channels*n); - } - else - { - /* - * clip to Nyquist - * (Of course this means that if you ask for too high a frequency you - * just get silence, since you sample all the zero-crossings of the - * sine wave.) - */ - pitch = pitch < RATE / 2 ? pitch : RATE / 2; - cycle = ((RATE + 1.e-6) / pitch); /* samples per cycle */ - - if (cycle > MAXTIME / 2) - cycle = MAXTIME / 2; - - /* round down length of (rampup + mesa top) to integral cycle */ - first_len = ((n - RAMP) / cycle) * cycle; - if (first_len < cycle) - first_len = cycle; - if (first_len > MAXTIME - RAMP) - first_len = MAXTIME - RAMP; - - /* round down length of (rampdown) to integral cycle */ - down_len = ((RAMP) / cycle) * cycle; - if (down_len < cycle) - down_len = cycle; - if (down_len > RAMP) - down_len = RAMP; - - /* - * Can we just reuse what we had before? - */ - if (pitch != last_pitch || n > last_n || volume != last_volume) - { - last_pitch = pitch; - last_n = n; - last_volume = volume; - - dt = 2. * M_PI / cycle; /* sine scale factor */ - - /* Ramp up; begin with silence */ - - for (i = 0; i < RAMP; i++) - { - soundbuf[i*channels] = rint( 128. + - ((float) i / RAMP) * (volume * 1.26) * sin (i * dt) - ); - if (channels == 2) - soundbuf[i*channels+1] = soundbuf[i*channels]; - } - - /* Mesa top */ - for (i = RAMP; i < first_len; i++) - { - soundbuf[i*channels] = rint( 128. + - 1. * (volume * 1.26) * sin (i * dt) - ); - if (channels == 2) - soundbuf[i*channels+1] = soundbuf[i*channels]; - } - - /* Ramp down; end with silence */ - for (i = 0; i < down_len; i++) - { - ramp_down[i*channels] = rint( 128. + - (1. - (float) (i + 1) / down_len) * (volume * 1.26) * sin (i * dt) - ); - if (channels == 2) - ramp_down[i*channels+1] = ramp_down[i*channels]; - } - } - - write (audio, soundbuf, channels*first_len); - write (audio, ramp_down, channels*down_len); - } - - return 0; -} - -int BeepWait (void) -{ - ioctl (audio, SNDCTL_DSP_SYNC, 0); - return 0; -} - -int BeepCleanup (void) -{ - return 0; -} - -int BeepResume (void) -{ - return 0; -} diff --git a/beepX11.c b/beepX11.c deleted file mode 100644 index dc9c444..0000000 --- a/beepX11.c +++ /dev/null @@ -1,81 +0,0 @@ -/* beepX11.c -- seligman 5/92 */ - -/* --- Implementation of beep.h for X11. --- --- Compile with the library "-lX11". -*/ - -#include -#include -#include "alarm.h" -#include "beep.h" - -static Display *dpy = 0; -static XKeyboardControl initialState; - -#define BellFlags (KBBellPercent | KBBellPitch | KBBellDuration) - - -int BeepInit() -{ - XKeyboardState state; - - if (! (dpy = XOpenDisplay(0))) { - perror("Couldn't open display"); - return 1; - } - - /* Save initial state so it can be restored later. */ - XGetKeyboardControl(dpy, &state); - initialState.bell_duration = state.bell_duration; - initialState.bell_percent = state.bell_percent; - initialState.bell_pitch = state.bell_pitch; - - return 0; -} - - -int Beep(time, volume, pitch) - int time, volume, pitch; -{ - XKeyboardControl values; - - AlarmWait(); - - if (volume != 0 && pitch != 0) { - values.bell_duration = time; - values.bell_percent = 100; - values.bell_pitch = pitch; - - XChangeKeyboardControl(dpy, BellFlags, &values); - XBell(dpy, volume - 100); - XFlush(dpy); - } - - AlarmSet(time); - return 0; -} - - -int BeepWait() -{ - AlarmWait(); - return 0; -} - - -int BeepCleanup() -{ - if (dpy != 0) { - XChangeKeyboardControl(dpy, BellFlags, &initialState); - XFlush(dpy); - } - return 0; -} - - -int BeepResume() -{ - return 0; -} diff --git a/morse.c b/morse.c deleted file mode 100644 index 29af690..0000000 --- a/morse.c +++ /dev/null @@ -1,1935 +0,0 @@ -/* - * Copyright (c) 1988 Regents of the University of California. - * Copyright (c) 1992 Joe Dellinger, University of Hawaii at Manoa - * Copyright (c) 2005 Eric S. Raymond. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* - * A morse code practice utility. (Contains those characters that can appear - * on the FCC ham license exam.) - * - * Running "morse" without arguments or input gives self-doc. - * - * It doesn't keep PERFECT time, but it seems reasonably close - * for reasonable word speeds on my slow SUN IPC! - * - * This code is (ridiculously) heavily modified from morse.c from the Reno UNIX - * distribution. I (Joe) also used slightly modified versions of a subroutine - * from Richard Ottolini at Unocal for Sun workstation tone generation. - * Scott Seligman at Stanford added support for other sorts of devices and - * made several other changes. John Shalamskas helped test and made comments. - * - * For a more detailed history, see the HISTORY file - */ - -/* - * Useful for seeing what the interleaved reading and writing loops are - * really up to. - * - * #define DEBUG - * - * If you want to be overwhelmed with information about the probabilities - * of each letter being chosen. - * - * #define DEBUGG - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "beep.h" - -#define FREQUENCY 800.0 -#define FREQUENCY2 602.0 -#define VOLUME 0.5 -#define ERROR_VOLUME 0.5 -#define WORDS_PER_MINUTE 20.0 -#define MAX_BEHINDNESS 0 -#define ERROR_FREQUENCY 2000.0 - -static int whichfrequ = 0; -static float frequency1 = FREQUENCY; -static float frequency2 = FREQUENCY2; -static float frequency; -static float volume = VOLUME; -static float dot_time; -static float dash_time; -static float intra_char_time; -static float inter_char_time; -static float inter_word_time; -static float catchup_time; -static int showletters = 0; -static int showmorse = 0; -static int wordsbefore = 0; -static int wordsafter = 0; -static int fancyending = 1; -static int noticebad = 0; -static int testing = 0; -static int showtesting = 0; -static int keepquiet = 0; -static int dynamicspeed = 0; -static int charbychar = 0; -static int international = 0; -static int allprosigns = 0; -static int allpunctuation = 1; -static int tryagaincount = 1; -static float words_per_minute = WORDS_PER_MINUTE; -static float error_frequency = ERROR_FREQUENCY; -static float error_volume = ERROR_VOLUME; -static float fwords_per_minute; -static int randomletters = 0; -#define LETMESEE 2 -static int typeaway = 0; - -static int totalhitcount = 0; -static int totalmisscount = 0; -static int helpmeflag = 0; - -#define MAXWORDLEN 20 -#define TESTBUFSZ ((MAXWORDLEN+1)*10) -static int wordlen = MAXWORDLEN; -static int wordcount = -1; -static time_t starttime; -static int timeout = -1; -static int testpointer = -1; -static int testlength = 0; -static int behindness = 0; -static int max_behindness = MAX_BEHINDNESS; -static char teststring[TESTBUFSZ]; -static int yourpointer = -1; -static int yourlength = 0; -static char yourstring[TESTBUFSZ]; -static int linepos = 0; - -/* - * How many times can a given character not be asked before - * kicking up the probability of asking that one by one randomfactor unit. - */ -#define RIPECOUNT 64 - -#define TWOFIFTYSIX 256 -static char *(code[TWOFIFTYSIX]); -static int errorlog[TWOFIFTYSIX]; -static int randomfactor[TWOFIFTYSIX]; -static int randomripe[TWOFIFTYSIX]; -static int testterminal (void); -static int randomletter (void); - -/* - * Pointers to termcap/terminfo "smso/so" (enter_standout_mode) and "rmso/se" - * (exit_standout_mode) terminal string capabilities (initialized respectively - * with ANSI sequences "\E[7m" to turn the inverse video mode on and "\E[0m" - * to turn all video attributes off). - */ -static const char *enter_standout_mode = "\033[7m"; -static const char *exit_standout_mode = "\033[0m"; - -/* - * Value of (Wrong - Right), which, if exceeded, will cause the program - * to start prompting you. Above MAX_ERROR_THRESHOLD it will never prompt. - * Don't let the user bank too much credit for past correct answers; - * limit it by min(ERROR_FLOOR, error_threshold). - */ -#define MAX_ERROR_THRESHOLD 1000 -#define ERROR_FLOOR -3 -static int error_threshold = MAX_ERROR_THRESHOLD; -static int error_floor = ERROR_FLOOR; - -/* - * How many characters behind before it decides you're having - * trouble keeping up. - */ -#define BEHIND 1 -#define WAYBEHIND 3 -#define TOOFARBEHIND 6 -/* - * If SLOWPOKE or more wpm ticks go by, then it decides you are having lots - * of trouble remembering this character, and need to be asked it more - * often. - */ -#define SLOWPOKE 10 -/* You aren't slow -- you left and came back! */ -#define SLOWPOKEMAX (50 * SLOWPOKE) -/* - * If FASTPOKE or less wpm ticks go by, then it decides you are good at this - * character, and need to be asked it less often. - */ -#define FASTPOKE 4 - -/* - * These control how quickly the dynamicspeed option acts when you are - * fast or slow. Easier to slow down than speed up! - */ -#define ERRORSLOWER 1.04 -#define ALOTSLOWER 1.15 -#define ALITTLESLOWER 1.02 -#define ALITTLEFASTER 1.02 - -/* - * How many inter_char_time's to give you to answer after the end of - * a word before considering that you are not keeping up. - * Maximum of 2.3, minimum of 0. - * The bigger the value, the easier it is to kick in the "automatic - * speedup" when using the "-d" option. The maximum means you have (almost) - * right up to the beginning of the next word to answer and still have it - * count as keeping up. - */ -#define SPORTING_RATIO 1.5 - -/* - * The bigger, the more evenly things start out. - * (Must be at least 2) - */ -#define RANDOMBASELEVEL 7 -/* - * RANDOMINCWORSE scales how badly you are punished for being wrong - * or taking too long. RANDOMINCBETTER scales how you are rewarded for - * answering quickly or being right. - */ -#define RANDOMINCWORSE 6 -#define RANDOMINCBETTER 7 -#define RANDOMMAX (30 * RANDOMBASELEVEL) -/* - * The average length of a random word (chosen using exponential distribution). - * After implementing this I'm not so sure an exponential distribution - * actually models the distribution of real word lengths in English very well. - * It's not too bad, though, and the words themselves are all garbage anyway, - * so what the heck. - */ -#define RANDWORDLEN 3.5 - -/* Put in a newline instead of a space when past this column */ -#define LINELENGTH 78 - -/* An EOF without the EOF (%) sound */ -#define SILENTEOF -2 -/* Toggle tone frequency on control-G within input file */ -#define FREQU_TOGGLE ((int)'\007') - -/* - * If you want the morse code to come out synchronized with the printing - * of dots and dashes with the -m option, then define this. The problem - * is that then the morse code sounds ratty on slower CPU's. - * John Shalamskas (KJ9U) suggested turning the precise morse-code printing - * synching off because he didn't like the resulting code quality! - */ -#undef FLUSHCODE - -static void new_words_per_minute (void); -static void dowords (int c); -static void morse (int c); -static void show (char *s); -static void testaddchar (char c); -static void youraddchar (char c); -static void pollyou (void); -static void tone (float hertz, float duration, float amplitude); -static void toneflush (void); -static void openterminal (void); -static int readterminal (char **string); -static void closeterminal (void); -static void report (void); - -static void die (), suspend (); -static void cleanup (); - -int -main (int argc, char **argv) -{ -extern char *optarg; -extern int optind; -int ch; -char *p; -int ii, jj; -int firsttime, notdoneyet; -int yourchar; -float randexp, randnum; -extern time_t time (); - - if (argc == 1 && isatty (fileno (stdin))) - { -/* - * SELF DOC - */ - printf ("Usage:\n"); - printf ("morse [options] < text_file\n"); - printf ("morse [options] words words words\n"); - printf ("morse [options] -r\n"); - printf ("morse [options] -i\n"); - printf ("Options:\n"); - printf ("-i Play what you type.\n"); - printf ("-I Like -i but don't turn off keyboard echoing.\n"); - printf ("-r Generate random text. Starts out slanted towards easy\n"); - printf (" letters, then slants towards ones you get wrong.\n"); - printf ("-n NUM (default %d means random length)\n", MAXWORDLEN); - printf (" Make words (groups) NUM characters long.\n"); - printf (" Valid values are between %d and %d.\n", 1, MAXWORDLEN); - printf ("-R NUM (default 0 means unlimited)\n"); - printf (" Set the total time (in minutes) to generate text.\n"); - printf ("-N NUM (default 0 means unlimited)\n"); - printf (" Set the total number of words (groups) to generate.\n"); - printf ("-C \'STRING\' (default all available characters)\n"); - printf (" Select characters to send from this STRING only.\n"); - printf ("-w words_per_minute (default %g)\n-f frequency_in_hertz (default %g)\n-v volume (zero to one, rather nonlinear, default %g)\n", - WORDS_PER_MINUTE, FREQUENCY, VOLUME); - printf ("-g alternate_frequency (default %g)\n (toggles via control-G in input FILE at a word break)\n", FREQUENCY2); - printf ("-F Farnsworth_character_words_per_minute\n"); - printf ("-e leave off the sound at the end\n"); - printf ("-c complain about illegal characters instead of just ignoring them\n"); - printf ("-b print each word before doing it\n"); - printf ("-a print each word after doing it\n"); - printf ("-l print each letter just before doing it\n"); - printf ("-m print morse dots and dashes as they sound\n"); -#ifdef FLUSHCODE - printf (" (this printing-intensive option slows the wpm down!)\n"); -#endif - printf ("-t Type along with the morse, but don't see what\n"); - printf (" you're typing (unless you make a mistake).\n"); - printf (" You are allowed to get ahead as much as you want.\n"); - printf (" If you get too far behind it will stop and resync with you.\n"); - printf (" You can force it to resync at the next word end by hitting control-H.\n"); - printf (" Hit ESC to see how you are doing, control-D to end.\n"); - printf (" (The rightmost space in the printout marks where the average is.\n"); - printf (" Farther left spaces separate off blocks of letters that are\n"); - printf (" about twice as probable as the average to occur, three times, etc.)\n"); - printf ("-T Like -t but see your characters (after they are played).\n"); - printf ("-s Stop after each character and make sure you get it right. (implies -t)\n"); - printf ("-q Quietly resyncs with your input (after you make a mistake).\n"); - printf ("-p NUM (default 0)\n"); - printf (" Make you get it right NUM times, for penance. (implies -s)\n"); - printf (" (Yes, NUM = 0 means you can sin all you want.)\n"); - printf ("-E NUM (default %d)\n", MAX_ERROR_THRESHOLD); - printf (" If your count of wrong answers minus right answers for a given character\n"); - printf (" exceeds this, the program will start prompting you.\n"); - printf (" If %d or above, it will never prompt. (implies -t)\n", MAX_ERROR_THRESHOLD); - printf ("-M NUM (default %d)\n", MAX_BEHINDNESS); - printf (" If you get more than this number of characters behind, pause until you\n"); - printf (" do your next letter. (1 behind is normal, 0 behind means never pause.)\n"); - printf (" (implies -t)\n"); - printf ("-d Dynamically speed up or slow down depending on how you are doing.\n"); - printf (" (if also -s, then -d _only speeds up_!)\n"); - printf ("-A Add ISO 8850-1 (Latin-1) signs to test set.\n"); - printf ("-B Add uncommon punctuation to test set.\n"); - printf ("-S Add uncommon prosigns to test set.\n"); - printf ("-X Set error volume. Defaults to %g.\n", ERROR_VOLUME); - printf (" Error volume 0 means use console speaker.\n"); - printf ("-x Set frequency of error tone, default 2000.0Hz\n"); - exit (0); - } - - for (ii = 0; ii < TWOFIFTYSIX; ii++) - code[ii] = NULL; - -/* Load in the morse code code */ - code[(int) 'a'] = ".-"; - code[(int) 'b'] = "-..."; - code[(int) 'c'] = "-.-."; - code[(int) 'd'] = "-.."; - code[(int) 'e'] = "."; - code[(int) 'f'] = "..-."; - code[(int) 'g'] = "--."; - code[(int) 'h'] = "...."; - code[(int) 'i'] = ".."; - code[(int) 'j'] = ".---"; - code[(int) 'k'] = "-.-"; - code[(int) 'l'] = ".-.."; - code[(int) 'm'] = "--"; - code[(int) 'n'] = "-."; - code[(int) 'o'] = "---"; - code[(int) 'p'] = ".--."; - code[(int) 'q'] = "--.-"; - code[(int) 'r'] = ".-."; - code[(int) 's'] = "..."; - code[(int) 't'] = "-"; - code[(int) 'u'] = "..-"; - code[(int) 'v'] = "...-"; - code[(int) 'w'] = ".--"; - code[(int) 'x'] = "-..-"; - code[(int) 'y'] = "-.--"; - code[(int) 'z'] = "--.."; - - code[(int) '1'] = ".----"; - code[(int) '2'] = "..---"; - code[(int) '3'] = "...--"; - code[(int) '4'] = "....-"; - code[(int) '5'] = "....."; - code[(int) '6'] = "-...."; - code[(int) '7'] = "--..."; - code[(int) '8'] = "---.."; - code[(int) '9'] = "----."; - code[(int) '0'] = "-----"; - - /* Punctuation marks */ - code[(int) '.'] = ".-.-.-"; - code[(int) ','] = "--..--"; - code[(int) '?'] = "..--.."; - code[(int) '/'] = "-..-."; - code[(int) '-'] = "-....-"; - - if (allpunctuation) { - /* Not so commonly used punctuation marks */ - code[(int) ')'] = "-.--.-"; - code[(int) '\"'] = ".-..-."; - code[(int) '_'] = "..--.-"; - code[(int) '\''] = ".----."; - code[(int) ':'] = "---..."; - code[(int) ';'] = "-.-.-."; - code[(int) '$'] = "...-..-"; - code[(int) '!'] = "-.-.--"; - } - - /* Commonly used procedure signs ("prosigns") */ - code[(int) '+'] = ".-.-."; /* end of message */ - code[(int) '*'] = ".-..."; /* wait, stand-by */ - code[(int) '='] = "-...-"; /* (double dash) pause, break for text */ - code[(int) '('] = "-.--."; /* over-specified station only */ - code[(int) '%'] = "...-.-"; /* end of contact, known also as */ - - if (allprosigns) { - /* Not so commonly used procedure signs ("prosigns") */ - code[(int) '^'] = ".-.-"; /* new line, the same as :a, ae */ - code[(int) '#'] = "-...-.-";/* invite receiving station to transmit */ - code[(int) '&'] = "-.-.-"; /* attention */ - code[(int) '@'] = "...-."; /* understood */ - } - - if (international) { - /* Not so commonly used international extensions (ISO 8859-1) */ - code[(int) ((unsigned char)'\344')] = ".-.-"; /* :a (also for ae), the same as */ - if (code[(int) '^'] != NULL) code[(int) '^'] = NULL; - code[(int) ((unsigned char)'\340')] = ".--.-"; /* `a, oa (danish a with ring over it) */ - code[(int) ((unsigned char)'\347')] = "----"; /* ch (bar-ch) */ - code[(int) ((unsigned char)'\360')] = "..--."; /* -d (eth, overstrike d with -) */ - code[(int) ((unsigned char)'\350')] = "..-.."; /* `e */ - code[(int) ((unsigned char)'\361')] = "--.--"; /* ~n */ - code[(int) ((unsigned char)'\366')] = "---."; /* :o (also for oe) */ - code[(int) ((unsigned char)'\374')] = "..--"; /* :u (also for ue) */ - code[(int) ((unsigned char)'\376')] = ".--.."; /* ]p (thorn, overstrike ] with p) */ - code[(int) ((unsigned char)'\247')] = ".-.-.."; /* paragraph */ - } - - for (ii = 0; ii < TWOFIFTYSIX; ii++) - { - /* Everything starts equally fresh */ - randomripe[ii] = 0; - /* Start out assuming you know how everything sounds */ - errorlog[ii] = 0; - - if (code[ii] == NULL) - { - /* Ensures these will never be chosen */ - randomfactor[ii] = 0; - } - else - { - /* Start out favoring easy ones */ - randomfactor[ii] = RANDOMBASELEVEL - strlen (code[ii]); - if (randomfactor[ii] < 1) - randomfactor[ii] = 1; - } - } - - words_per_minute = WORDS_PER_MINUTE; - fwords_per_minute = -1.; - - /* DGHJKLMOPQUVWYZhjkouyz are still available */ - while ((ch = getopt (argc, argv, "ABC:E:F:IM:N:R:STXabcdef:g:ilmn:p:qrstv:w:x:")) != EOF) - switch ((char) ch) - { - case 'A': - international = 1; - break; - case 'B': - allpunctuation = 1; - break; - case 'C': - for (ii = 0; ii < strlen(optarg); ii++) - { - *(optarg+ii) = tolower(*(optarg+ii)); - } - for (ii = 0; ii < TWOFIFTYSIX; ii++) - { - if (code[ii] != NULL) - { - if (strchr(optarg, ii) != NULL) - { - randomfactor[ii] = RANDOMBASELEVEL - 1; - } else { - code[ii] = NULL; - randomfactor[ii] = 0; - } - } - } - break; - case 'E': - testing = 1; - sscanf (optarg, "%d", &error_threshold); - if (error_threshold < error_floor) - error_floor = error_threshold; - break; - case 'F': - sscanf (optarg, "%f", &fwords_per_minute); - break; - case 'I': - typeaway = LETMESEE; - break; - case 'M': - testing = 1; - sscanf (optarg, "%d", &max_behindness); - if (max_behindness < 1) - max_behindness = 0; - break; - case 'N': - sscanf (optarg, "%d", &wordcount); - if (wordcount < 1) wordcount = -1; - break; - case 'R': - sscanf (optarg, "%d", &timeout); - timeout *= 60; - if (timeout < 1) timeout = -1; - break; - case 'S': - allprosigns = 1; - break; - case 'T': - testing = 1; - showtesting = 1; - break; - case 'X': - sscanf (optarg, "%f", &error_volume); - break; - case 'a': - wordsafter = 1; - break; - case 'b': - wordsbefore = 1; - break; - case 'c': - noticebad = 1; - break; - case 'd': - dynamicspeed = 1; - break; - case 'e': - fancyending = 0; - break; - case 'f': - sscanf (optarg, "%f", &frequency1); - break; - case 'g': - sscanf (optarg, "%f", &frequency2); - break; - case 'i': - typeaway = 1; - break; - case 'l': - showletters = 1; - break; - case 'm': - showmorse = 1; - break; - case 'n': - sscanf (optarg, "%d", &wordlen); - if (wordlen < 1) wordlen = 1; - if (wordlen > MAXWORDLEN) wordlen = MAXWORDLEN; - break; - case 'p': - charbychar = 1; - testing = 1; - sscanf (optarg, "%d", &tryagaincount); - break; - case 'q': - keepquiet = 1; - break; - case 'r': - randomletters = 1; - break; - case 's': - charbychar = 1; - testing = 1; - break; - case 't': - testing = 1; - break; - case 'v': - sscanf (optarg, "%f", &volume); - if (volume < 0.) - volume = 0.; - if (volume > 1.) - volume = 1.; - break; - case 'w': - sscanf (optarg, "%f", &words_per_minute); - break; - case 'x': - sscanf (optarg, "%f", &error_frequency); - break; - default: - fprintf (stderr, "Type \"morse\" without arguments to get self-doc!\n"); - exit (1); - break; - } - argc -= optind; - argv += optind; - - if (fwords_per_minute <= 0.) - fwords_per_minute = words_per_minute; - new_words_per_minute (); - - frequency = frequency1; - - if (BeepInit () != 0) - { - fprintf (stderr, "Can't access speaker.\n"); - exit (1); - } - - time(&starttime); - - sigset (SIGINT, die); - sigset (SIGTERM, die); - sigset (SIGQUIT, die); - sigset (SIGTSTP, suspend); - - if (testing || typeaway) - { - openterminal (); - } - -/* - * Do .25 seconds of silence initially to give the workstation time to - * get settled after the stress of starting this program and opening - * up everything. - */ - tone (frequency, .25, 0.); - toneflush (); - - if (typeaway) - { - testing = 0; - showtesting = 0; - charbychar = 0; - wordsbefore = 0; - wordsafter = 0; - randomletters = 0; - - notdoneyet = 1; - - while (notdoneyet) - { - pollyou (); - - for (jj = 0; jj < yourlength; jj++) - { - yourchar = yourstring[(yourpointer - yourlength + 1 + jj + TESTBUFSZ) % TESTBUFSZ]; - - /* Control-D: finished */ - if (yourchar == (int) '\004') - { - toneflush (); - notdoneyet = 0; - break; - } - - if (isspace (yourchar)) - { - if (showletters) - { - toneflush (); - printf ("%c", yourchar); - fflush (stdout); - } - - tone (frequency, inter_word_time, 0.); - - continue; - } - - morse (yourchar); - } - yourlength -= jj; - - if (timeout > 0) - if (difftime(time(NULL),starttime) > (double)timeout) - break; - } - } - else if (randomletters) - { - srand48(time (NULL)); - randexp = 1. / (1. - 1. / (float) (RANDWORDLEN)); - while (1) - { - if ((wordcount == 0) || timeout == 0) break; - - dowords (randomletter ()); - - if (wordlen == MAXWORDLEN) - { - /* Knock a few bits off the top so we're sure it won't overflow */ - /* Shift a few bits because the lower bits stink */ - /* Add in the time so it doesn't repeat from run to run */ - randnum = (float) ( - (((lrand48() >> 9) + (long) (time (NULL))) >> 4) - & 0x00FFFFFF); - randnum = randnum - randexp * (int) (randnum / randexp); - if ((randnum >= 1.) && (linepos != 0)) dowords ((int) ' '); - } - } - } - else - { - if (*argv) - { - firsttime = 1; - - do - { - if (!firsttime) - { - dowords ((int) ' '); - } - else - firsttime = 0; - - if ((wordcount == 0) || timeout == 0) break; - - for (p = *argv; *p; ++p) - { - dowords ((int) *p); - if ((wordcount == 0) || timeout == 0) break; - } - if ((wordcount == 0) || timeout == 0) break; - - } while (*++argv); - } - else - { - while ((ch = getchar ()) != EOF) - { - dowords (ch); - if ((wordcount == 0) || timeout == 0) break; - } - } - } - - if (fancyending) - dowords (EOF); - else - dowords (SILENTEOF); - - fflush (stdout); - - if (testing) - { - /* - * WE'RE completely done, and YOU aren't! Force catch up. (Note if - * charbychar = YES we won't get here, since we're always caught up - * after each character as it comes out.) - */ - while (testlength > 0) - { - tone (frequency, catchup_time, 0.); - toneflush (); - testterminal (); - } - } - - /* Just to be sure! */ - toneflush (); - - if (showmorse || wordsbefore || wordsafter || showletters || showtesting) - printf ("\n"); - fflush (stdout); - - if (testing) - report (); - -/* If you make any mistakes exit with a return code! */ - cleanup (); - return (totalmisscount > 0); -} - -static void -new_words_per_minute () -{ -float wtick, ftick, tick; - - tick = 60. / (words_per_minute * 50); - - /* - * In the limit as wpm goes past fwpm, Farnsworth becomes kosher PARIS - */ - if (fwords_per_minute <= words_per_minute) - ftick = 60. / (words_per_minute * 50); - else - ftick = 60. / (fwords_per_minute * 50); - - wtick = (50. * tick - 31. * ftick) / 19.; - - /* - * This time is used when the computer is waiting on you to hit a key; it - * is useful to scale the granularity with the real overrall words per - * minute. This also serves as a measuring rod to see if you are - * responding "fast enough". If you are too slow, then obviously you are - * having trouble with that character, and should be given it more OFTEN. - * Heh heh heh... - */ - catchup_time = tick; - - /* - * Things between characters and words go at the "remainder" speed, - * whatever space you need to make the sped-up Farnsworth characters come - * out with the correct overall words per minute. - */ - inter_char_time = wtick * 3.; - inter_word_time = wtick * 7.; - - /* Things within the character go at the Farnsworth speed */ - intra_char_time = ftick; - dot_time = ftick; - dash_time = ftick * 3.; -} - -static int tryingagain = 0, slowpoke = 0; - -static void -dowords (int c) -{ -static int wordc = 0; -static char word[MAXWORDLEN+1]; -char *wordp; -int ii; -int againcount = 0; -int are_we_repeating; - -/* - * Increment the line position counter. - * If a line gets too long, just cut it off by inserting a new line. - */ - if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE) linepos++; - if (isspace (c) && ((linepos + wordlen) >= LINELENGTH)) c = '\n'; - if (c == '\n') linepos = 0; - - if (isspace (c) || c == EOF || c == SILENTEOF || c == FREQU_TOGGLE) - { - if (wordc > 0) - { - if (wordcount > 0) wordcount--; - if (timeout > 0) - if (difftime(time(NULL),starttime) > (double)timeout) - timeout = 0; - - word[wordc] = '\0'; - -/* - * We have just read in a new complete word from the input, (hopefully) - * during the time of an inter-word space minus an inter-char space. - * Now let's go back and see what happened with the PREVIOUS word, - * the one that we had just finished playing. Did the user keep - * up with us? - */ -#ifdef DEBUG - fprintf (stderr, " [%d] ", behindness); -#endif - if (testing && dynamicspeed && !charbychar) - { - /* - * (If charbychar then behindness is ALWAYS 0 at this - * point...) - */ - if (behindness == 0) - { - /* You're a speed demon! Speed up a bit, then! */ - words_per_minute *= ALITTLEFASTER; - new_words_per_minute (); - } - else if (behindness > WAYBEHIND) - { - /* You're way behind! Slow way down. */ - words_per_minute /= ALOTSLOWER; - new_words_per_minute (); - } - else if (behindness > BEHIND) - { - /* You're behind! Slow down a bit. */ - words_per_minute /= ALITTLESLOWER; - new_words_per_minute (); - } - } - - -/* - * If the user was WAY too far behind stop and catch up with - * the "new" word as the first one. - */ - if (testing && (behindness > TOOFARBEHIND || helpmeflag)) - { - if (helpmeflag) - printf ("\nOK, let's restart.\n"); - else - printf ("\nYou are too far behind! Let's restart.\n"); - fflush (stdout); - - toneflush (); - - /* Flush the keyboard buffer */ - pollyou (); - - /* Forget the past */ - helpmeflag = 0; - behindness = 0; - testlength = 0; - yourlength = 0; - - /* Give the user a little rest. */ - sleep (2); - printf ("\nWPM now %d\n", (int) (words_per_minute + .5)); - fflush (stdout); - sleep (2); - printf ("\nREADY?\n"); - fflush (stdout); - sleep (1); - printf ("\nSET\n"); - fflush (stdout); - sleep (1); - printf ("\nGO!\n"); - fflush (stdout); - linepos = 0; - } - -/* - * Start treating the new word. - */ - if (wordsbefore) - { - /* Try to keep your out-of-sync text from getting swirled in */ - if (showtesting) - { - printf ("\n"); - linepos = 0; - } - - printf ("%s", word); - - if (showmorse || showletters || wordsafter || showtesting) - { - printf (" "); - for (ii = 0; ii < 16 - (wordc + 2); ii++) - { - printf (" "); - } - } - - fflush (stdout); - } - - if (testing && charbychar) - { - againcount = 0; - } - - for (wordp = word; *wordp != '\0'; wordp++) - { - tryingagain = 0; - - tryagain: - if (testing && !tryingagain && !showletters && - error_threshold < MAX_ERROR_THRESHOLD && - errorlog[(int) *wordp] > error_threshold) - { - toneflush (); - /* Give them a quick hint */ - printf ("[%c]", *wordp); - fflush (stdout); - - morse (*wordp); - - toneflush (); - if (!showmorse) - { - /* Erase the hint */ - printf ("\b\b\b \b\b\b"); - fflush (stdout); - } - } - else - { - morse (*wordp); - } - - if (testing) - { - if (charbychar) - { - toneflush (); - /* Force catchup */ - slowpoke = 0; - while (behindness > 0) - { - if (testterminal () && tryagaincount > 0) - { - /* - * OOPS! They got it WRONG! MAKE THEM TRY - * AGAIN! - */ - printf ("Try again.\n"); - linepos = 0; - /* - * Yeah I know gotos are inelegant but I - * don't feel like figuring out the "elegant" - * way to do this right now. - */ - againcount = tryagaincount - 1; - tryingagain = 1; - goto tryagain; - } - else - { - /* - * They got it right, or they didn't answer - * yet. - */ - if (behindness > 0) - { - /* - * They are STILL thinking, the - * slowpokes. Wait a bit before trying - * again. - */ - tone (frequency, catchup_time, 0.); - /* - * Keep track of how long they're taking - * to answer! - */ - if (slowpoke < SLOWPOKEMAX) - slowpoke++; - toneflush (); - } - else if (dynamicspeed && !slowpoke && !tryingagain) - { - /* - * They got it right without errors the - * first time and we didn't have to wait - * for them! A speed demon! Speed up a - * bit, then! - */ - words_per_minute *= ALITTLEFASTER; - new_words_per_minute (); - } - } - } - - /* Insufficient penance? */ - if (againcount > 0) - { - againcount--; - goto tryagain; - } - } - else - { - testterminal (); - /* - * Stop if we get more than max_behindness ahead. - * max_behindness == 0 means don't worry about them, - * they can be as far behind as they want and we - * won't stop! - */ - if (max_behindness > 0) - { - are_we_repeating = 0; - while (behindness >= max_behindness) - { -#ifdef DEBUG - fprintf (stderr, " (%d) ", behindness); -#endif - if (are_we_repeating) - { - /* - * Pause for a bit so we don't loop too - * fast - */ - tone (frequency, catchup_time, 0.); - } - else - { - are_we_repeating = 1; - } - /* Finish playing whatever we're playing */ - toneflush (); - /* And give them another chance */ - testterminal (); - } - } - } - } - } - - toneflush (); - if (testing) - testterminal (); - - if (wordsafter) - { - printf (" (%s)", word); - } - - if (wordsbefore || wordsafter || showmorse) - { - if (!showmorse && !testing && linepos) - { - if (wordsafter) - linepos += 3; - else - printf (" "); - } else { - printf ("\n"); - linepos = 0; - } - } - else if (showletters || showtesting) - { - if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE) - { - if (showletters) - printf ("%c", c); - - if (showtesting) - testaddchar (c); - } - } - -/* - * WHEW! FINISHED QUEUEING THE WORD FOR PLAYING! - * Now finish up all the other sundry details... - */ - - /* Flush the output printing queue... */ - fflush (stdout); - /* - * Pause for a bit; this gives the user a sporting chance at - * catching up with us. - */ - tone (frequency, SPORTING_RATIO * inter_char_time, 0.); - toneflush (); - /* Start sounding an inter-word space */ - tone (frequency, inter_word_time - SPORTING_RATIO * inter_char_time, 0.); - - /* While that silence is playing check if the user has caught up. */ - if (testing) - testterminal (); - - /* We finished this word; reset the word character count */ - wordc = 0; - } - else if (!(wordsbefore || wordsafter || showmorse) - && - (showletters || showtesting)) - { - if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE) - { - if (showletters) - printf ("%c", c); - - if (showtesting) - testaddchar (c); - } - } - - if (c == EOF) - { - morse (EOF); - toneflush (); - } - else if (c == SILENTEOF) - { - toneflush (); - } - else if (c == FREQU_TOGGLE) - { - /* Switch to the other frequency */ - /* (Won't work from keyboard, only from a file.) */ - whichfrequ = 1 - whichfrequ; - switch (whichfrequ) - { - case 1: - frequency = frequency2; - break; - case 0: - default: - frequency = frequency1; - break; - } - } - } - else - { - word[wordc++] = c; - } - -/* - * If a word gets too long, just cut it off by inserting a space. - * Just call ourselves with the character we wish we'd gotten... - */ - if (wordc == wordlen && !(isspace (c) || c == EOF || c == SILENTEOF || c == FREQU_TOGGLE)) - dowords ((int) ' '); -} - -/* - * Don't try to test the person DURING the call into morse! - */ -void -morse (int c) -{ - if ((isalpha (c) && (code[tolower(c)] == NULL)) || ((code[(int) '%'] == NULL) && ((c == EOF) || (c == '\004')))) - c = ' '; - - if (showletters) - { - if ((c == EOF) || (c == '\004')) - { - if (showmorse) - { - printf (""); - } else { - printf ("%%"); - } - } - else if (c == '.' && showmorse) - printf (""); - else if (c == '-' && showmorse) - printf (""); - else if (c == '+' && showmorse) - printf (""); - else if (c == '*' && showmorse) - printf (""); - else if (c == '=' && showmorse) - printf (""); - else if (c == '(' && showmorse) - printf (""); - else if (c == '%' && showmorse) - printf (""); -#ifdef ALLPROSIGNS - else if (c == '^' && (code[(int) '^'] != NULL) && showmorse) - printf (""); - else if (c == '#' && showmorse) - printf (""); - else if (c == '&' && showmorse) - printf (""); - else if (c == '@' && showmorse) - printf (""); -#endif - else - printf ("%c", c); - - fflush (stdout); - } - - if (isalpha (c)) - { - if (testing) - testaddchar (c - (isupper (c) ? 'A' : 'a') + 'a'); - show (code[c - (isupper (c) ? 'A' : 'a') + 'a']); - } - else if ((c == EOF) || (c == '\004')) - { - show (code[(int) '%']); - } - else if (code[c] != NULL) - { - if (testing) - testaddchar (c); - show (code[c]); - } - else - { - /* Oops! This letter is junk! */ - - if (noticebad) - { - if (showletters) - { - fflush (stdout); - } - - /* Simulate a stumble */ - tone (frequency, 2. * inter_word_time, 0.); - toneflush (); - } - - if (showletters) - { - /* Wipe out what we just printed */ - fflush (stdout); - printf ("\b"); - printf (" "); - printf ("\b"); - fflush (stdout); - } - - if (noticebad) - { - if (showletters) - { - /* And replace it with an error message */ - printf (""); - fflush (stdout); - } - - /* Give the error call */ - show ("........"); - - /* Regroup */ - tone (frequency, inter_word_time, 0.); - } - } - - if (showmorse) - printf (" "); - fflush (stdout); - toneflush (); - tone (frequency, inter_char_time - intra_char_time, 0.); -} - - -/* - * Don't try to test the person WHILE doing dots and dashes! - */ -static void -show (char *s) -{ -char c; - - while ((c = *s++) != '\0') - { - tone (frequency, intra_char_time, 0.); - -#ifdef FLUSHCODE - if (showmorse) - toneflush (); -#endif - - switch (c) - { - case '.': - tone (frequency, dot_time, volume); - break; - case '-': - tone (frequency, dash_time, volume); - break; - } - - if (showmorse) - { - printf ("%c", c); - fflush (stdout); -#ifdef FLUSHCODE - toneflush (); -#endif - } - } -} - -/* - * This only gets passed valid characters: ones - * that have a morse code associated with them - * or ones for which isspace(c) is true. - */ -static void -testaddchar (char c) -{ - testpointer = (testpointer + 1) % TESTBUFSZ; - teststring[testpointer] = c; -#ifdef DEBUG - fprintf (stderr, " (%c,%d,%d) ", c, testlength, behindness); -#endif - testlength++; - if (testlength > TESTBUFSZ) - { - fprintf (stderr, "\n\nInput buffer queue overflow! Make TESTBUFSZ bigger!\n"); - fprintf (stderr, "(Or don't fall so far behind)\n"); - - die (); - } - -/* - * Since you are never asked to type spaces (you can type them if - * you want, but they are ignored) spaces in the input file don't - * count against your "behindness". - */ - if (!isspace (c)) - behindness++; -} - -static void -youraddchar (char c) -{ - yourpointer = (yourpointer + 1) % TESTBUFSZ; - yourstring[yourpointer] = c; -#ifdef DEBUG - fprintf (stderr, " <%c,%d> ", c, yourlength); -#endif - yourlength++; - if (yourlength > TESTBUFSZ) - { - fprintf (stderr, "\n\nKeyboard typeahead buffer queue overflow! Make TESTBUFSZ bigger!\n"); - fprintf (stderr, "(Or don't type so far ahead... how did you expect to get them right anyway?)\n"); - - die (); - } -} - -static void -pollyou (void) -{ - int ii, num; - char *string; - - num = readterminal (&string); - - for (ii = 0; ii < num; ii++) - youraddchar (string[ii]); -} - -static int -testterminal (void) -{ -int testinc, yourinc; -int correctchar, yourchar, yourcharnocase; -int errorcount; -int resync; - - errorcount = 0; - -/* - * There is nothing in the input file queue right now, - * so we can't process any of your keystrokes. - * Defer processing until we can catch up with YOU! - */ - if (testlength == 0) - return errorcount; - - /* We're ready for you; but are you ready for us? */ - pollyou (); - -/* - * Process your entries and the input queue entries in parallel - */ - if (yourlength > 0 && testlength > 0) - { - for (testinc = 0, yourinc = 0; - testinc < testlength && yourinc < yourlength; - testinc++, yourinc++) - { - correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ]; - - /* - * The latter half of this if shouldn't be necessary, but just in - * case... - */ - if (isspace (correctchar) || code[correctchar] == NULL) - { - if (showtesting) - { - printf ("%c", correctchar); - fflush (stdout); - } - - /* White space doesn't count for "behindness" */ - behindness++; - /* The _other_ pointer wasn't used; don't increment it. */ - yourinc--; - - /* Short circuit the loop */ - continue; - } - - - yourchar = yourstring[(yourpointer - yourlength + 1 + yourinc + TESTBUFSZ) % TESTBUFSZ]; - if (isalpha (yourchar)) - yourcharnocase = yourchar - (isupper (yourchar) ? 'A' : 'a') + 'a'; - else - yourcharnocase = yourchar; - - /* Did you type something rude? If so, just ignore it. */ - if (isspace (yourchar) || code[yourcharnocase] == NULL) - { - /* ESCAPE: dump status info */ - /* Control-D: dump status info and then bye bye */ - /* Control-H: force restart */ - if (yourchar == '\033' || yourchar == (int) '\004') - { - report (); - - if (yourchar == (int) '\004') - die (); - } - else if (yourchar == '\b') - { - helpmeflag = 1; - } - - /* The _other_ pointer wasn't used; don't increment it. */ - testinc--; - /* Short circuit the loop */ - continue; - } - - if (keepquiet > 1) - { - for (resync = testinc; resync < testlength; resync++) - if ( yourcharnocase == teststring[(testpointer - testlength + 1 + resync + TESTBUFSZ) % TESTBUFSZ]) - { - for (; testinc < resync; testinc++) - { - correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ]; - if (isspace (correctchar) || code[correctchar] == NULL) - { - if (showtesting) printf ("%c", correctchar); - } else { - printf("%s%c%s", enter_standout_mode, correctchar, exit_standout_mode); - } - } - fflush (stdout); - correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ]; - keepquiet = 1; - break; - } - } - - if (yourcharnocase != correctchar) - { - errorcount++; - totalmisscount++; - - /* - * Record that you are having trouble with these. - */ - errorlog[correctchar]++; - if (code[yourcharnocase] != NULL && - errorlog[yourcharnocase] < MAX_ERROR_THRESHOLD) - errorlog[yourcharnocase]++; - - if (keepquiet > 0) - { - printf("%s%c%s", enter_standout_mode, correctchar, exit_standout_mode); - fflush (stdout); - keepquiet++; - } else { - /* - * Scold them for their mistake. - */ - printf ("\n"); - - if (error_volume) { - /* Beep using the tone generator */ - toneflush (); - tone (error_frequency, 0.1, error_volume); - } else - /* Beep using control-G */ - printf ("\007"); - printf ("%c (%s) for %c (%s)\n", - yourchar, code[yourcharnocase], - correctchar, code[correctchar]); - fflush (stdout); - linepos = 0; - } - - if (charbychar) - { - /* Give them a bit of time to think about their error */ - tone (frequency, inter_word_time, 0.); - toneflush (); - } - if (dynamicspeed && !charbychar) - { - /* - * Slow down. Doesn't make sense to slow down for errors, - * though, if you've got all the time you want to think - * about each one. - */ - words_per_minute /= ERRORSLOWER; - new_words_per_minute (); - } - if (randomletters && !tryingagain) - { - /* - * Ask ones that confused you more often! - */ - if (code[yourcharnocase] != NULL) - { - randomfactor[yourcharnocase] += (3 * RANDOMINCWORSE / 2); - if (randomfactor[yourcharnocase] > RANDOMMAX) - randomfactor[yourcharnocase] = RANDOMMAX; - } - - randomfactor[correctchar] += RANDOMINCWORSE * 2; - if (randomfactor[correctchar] > RANDOMMAX) - randomfactor[correctchar] = RANDOMMAX; - } - } - else - { - /* - * Record that you got this right. - */ - if (!tryingagain) - { - totalhitcount++; - if (errorlog[correctchar] > error_floor) - errorlog[correctchar]--; - } - - if (showtesting) - { - printf ("%c", yourchar); - fflush (stdout); - } - - if (randomletters && !tryingagain) - { - if (slowpoke == SLOWPOKEMAX) - { - printf ("\nNice to have you back again, I was getting bored!\n"); - linepos = 0; - } - else if (slowpoke >= SLOWPOKE * 3) - { - /* - * Did you take too long thinking about it? If so, - * you probably need to be asked this one more - * often... - */ - randomfactor[correctchar] += (3 * RANDOMINCWORSE / 2); - if (randomfactor[correctchar] > RANDOMMAX) - randomfactor[correctchar] = RANDOMMAX; - - /* - * Hits this slow shouldn't count! You were obviously - * just guessing! (But it doesn't count as an error - * either.) - */ - totalhitcount--; - } - else if (slowpoke > FASTPOKE) - { - randomfactor[correctchar] += - (slowpoke * RANDOMINCWORSE) / (2 * SLOWPOKE); - if (randomfactor[correctchar] > RANDOMMAX) - randomfactor[correctchar] = RANDOMMAX; - } - else if (slowpoke <= (FASTPOKE / 2)) - { - /* - * Ask ones that you quickly answer correctly less - * often! - */ - randomfactor[correctchar] -= (3 * RANDOMINCBETTER / 2); - /* - * Don't let randomfactor hit 0, or you'll NEVER be - * asked this one AGAIN! - */ - if (randomfactor[correctchar] < 1) - randomfactor[correctchar] = 1; - } - else if (slowpoke <= FASTPOKE) - { - randomfactor[correctchar] -= (RANDOMINCBETTER / 2); - if (randomfactor[correctchar] < 1) - randomfactor[correctchar] = 1; - } - } - } - - } - testlength -= testinc; - behindness -= testinc; - yourlength -= yourinc; - } - -/* - * If there are some extra white space characters in the input queue - * it's OK, we'll get to them next time or we'll clean them out at the - * end. - */ - return errorcount; -} - - -/*----------------------------------------*/ - -static void -tone (float hertz, float duration, float amplitude) -{ - Beep ((int) (duration * 1000), (int) (amplitude * 100), (int) hertz); -} - - -static void -toneflush (void) -{ - BeepWait (); -} - - -/*----------------------------------------*/ - -#include -#include -#include -struct termio oldtermgtty; -struct termio termgtty; -static char *terminal = "/dev/tty"; -static int termfd; -static int termopen = 0; -static int oldflgs, newflgs; - -static void -openterminal (void) -{ - /* get parameters and open terminal */ - - termfd = open (terminal, O_RDWR | O_NDELAY, 0); - ioctl (termfd, TCGETA, &termgtty); - oldtermgtty = termgtty; - if (typeaway != LETMESEE) - termgtty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); - termgtty.c_lflag &= ~ICANON; - termgtty.c_cc[VMIN] = 1; - termgtty.c_cc[VTIME] = 0; - ioctl (termfd, TCSETAW, &termgtty); - termopen = 1; -} - -static int -readterminal (char **string) -{ -/* This must be declared static! */ -static char line[TESTBUFSZ]; -int n; - - n = read (termfd, line, sizeof (line) - 1); - - if (n > 0) - { - line[n] = '\0'; - *string = line; - } - else - *string = NULL; - - return n; -} - -static void -closeterminal (void) -{ - ioctl (termfd, TCSETAW, &oldtermgtty); - close (termfd); -} - -static void -die (void) -{ - cleanup (); - exit (1); -} - -static void -cleanup (void) -{ - if (termopen) - closeterminal (); - BeepCleanup (); -} - -static void -suspend (int sig) -{ - sigset (SIGTSTP, suspend); - cleanup (); - kill (getpid (), SIGSTOP); - if (termopen) - openterminal (); - BeepResume (); -} - - -/*----------------------------------------*/ - -static int -randomletter (void) -{ -int ii; -int sum, sum2; -long ranspot; -static int lasttime = -1; -static long norepeat; - -/* - * This keeps the not-so-random random number generator from ignoring - * certain characters forever! - */ - norepeat = ((long) time (NULL) / 31) % 17291; - -/* - * All the usable letters get one unit riper. - */ - for (ii = 0; ii < TWOFIFTYSIX; ii++) - { - if (randomfactor[ii] > 0) - { -#ifdef DEBUGG - fprintf (stderr, "%c: %d %d\n", - (char) ii, randomfactor[ii], randomripe[ii]); -#endif - randomripe[ii]++; - } - } - - sum = 0; - for (ii = 0; ii < TWOFIFTYSIX; ii++) - sum += (randomfactor[ii] + (int) (randomripe[ii] / RIPECOUNT)); - -/* - * The low bits of random aren't very random, I don't care WHAT - * the manual claims. - */ - do - { - ranspot = ((lrand48() >> 4) % sum + norepeat) % sum; - - sum2 = 0; - for (ii = 0; ii < TWOFIFTYSIX - 1; ii++) - { - sum2 += (randomfactor[ii] + (int) (randomripe[ii] / RIPECOUNT)); - - if (sum2 > ranspot) - break; - } - /* Do it again if you got the same as last time! */ - } while (ii == lasttime); - - /* This one is FRESH again. */ - randomripe[ii] = 0; - /* Remember for next time. */ - lasttime = ii; - - return ii; -} - -static int -rancomp (const void *elem1, const void *elem2) -{ - register int *e1 = (int *)elem1; - register int *e2 = (int *)elem2; - float a, b; - - - a = (randomfactor[(*e1)] + (randomripe[(*e1)] / (float) RIPECOUNT)); - b = (randomfactor[(*e2)] + (randomripe[(*e2)] / (float) RIPECOUNT)); - - if (a == b) - return 0; - else if (a > b) - return -1; - else - return 1; -} - -static void -report (void) -{ - int ii, jj, count; - float sum; - int randomstr[TWOFIFTYSIX]; - - printf ("\nCurrent words per minute: %.1f\n", words_per_minute); - - printf ("Total hits %d, misses %d", totalhitcount, totalmisscount); - if (totalmisscount > 0) - printf (", hit per miss ratio %.1f\n", (float) totalhitcount / (float) totalmisscount); - else - printf ("\n"); - - if (randomletters) - { - printf ("Most to least frequent choices:\n"); - count = 0; - sum = 0.; - for (ii = 0; ii < TWOFIFTYSIX; ii++) - { - if (randomfactor[ii] > 0) - { - sum += (randomfactor[ii] + (randomripe[ii] / (float) RIPECOUNT)); - randomstr[count] = ii; - count++; - } - } - - qsort ((char *) randomstr, count, sizeof (randomstr[0]), rancomp); - - for (ii = 0; ii < count; ii++) - { -/* - * Insert a space for each jump across an integer. - * The normalization (count/sum) ensures that if all - * letters were equally probable, they would all have value 1. - * Since they are not generally equally probable, then 1 is just the average. - * Thus the rightmost space in the printout marks where the average is. - * Further left spaces separate off blocks of letters that are approximately - * twice as probable as the average, three times, etc. - */ - if (ii > 0) - { - for (jj = 0; jj < - (int) ( - (randomfactor[randomstr[ii - 1]] + (randomripe[randomstr[ii - 1]] / (float) RIPECOUNT)) - * count / sum) - - (int) ( - (randomfactor[randomstr[ii]] + (randomripe[randomstr[ii]] / (float) RIPECOUNT)) - * count / sum); - jj++) - printf (" "); - } - printf ("%c", (char) randomstr[ii]); - } - printf ("\n"); - } - - /* - * So you don't get penalized for being "slow" after this. - */ - if (charbychar) - slowpoke = SLOWPOKEMAX + 1; - - fflush (stdout); -} -- 2.43.0