New audio subsystem.
[jog] / aunum.c
diff --git a/aunum.c b/aunum.c
new file mode 100644 (file)
index 0000000..4c3cc95
--- /dev/null
+++ b/aunum.c
@@ -0,0 +1,243 @@
+/* -*-c-*-
+ *
+ * $Id: aunum.c,v 1.1 2002/02/02 19:16:28 mdw Exp $
+ *
+ * Reading numbers to audio output
+ *
+ * (c) 2002 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Jog: Programming for a jogging machine.
+ *
+ * Jog is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * Jog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jog; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: aunum.c,v $
+ * Revision 1.1  2002/02/02 19:16:28  mdw
+ * New audio subsystem.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/dstr.h>
+
+#include "au.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @digit@ --- *
+ *
+ * Arguments:  @char x@ = single digit to read
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads a single digit.
+ */
+
+static void digit(int x)
+{
+  static char tagbuf[4] = "n-?";
+  tagbuf[2] = x;
+  au_play(tagbuf);
+}
+
+/* --- @digits@ --- *
+ *
+ * Arguments:  @const char *p@ = pointer to digits
+ *             @size_t n@ = number of digits
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads a sequence of digits.
+ */
+
+static void digits(const char *p, size_t n)
+{
+  while (n) {
+    digit(*p++);
+    n--;
+  }
+}
+
+/* --- @lasttwo@ --- *
+ *
+ * Arguments:  @const char *p@ = pointer to digits
+ *             @size_t n@ = number of digits
+ *             @unsigned f@ = flags
+ *
+ * Returns:    Nonzero if the number was nonzero.
+ *
+ * Use:                Reads out a number of no more than two digits.
+ */
+
+#define f_and 1u
+
+static int lasttwo(const char *p, size_t n, unsigned f)
+{
+  static char tagbuf[5] = "n-??";
+
+  while (n && *p == '0') {
+    n--;
+    p++;
+  }
+  if (!n)
+    return (0);
+
+  if (f & f_and)
+    au_play("n-and");
+  if (n == 1)
+    digit(*p);
+  else if (*p == '1') {
+    tagbuf[2] = p[0];
+    tagbuf[3] = p[1];
+    au_play(tagbuf);
+  } else {
+    tagbuf[2] = *p++;
+    tagbuf[3] = '0';
+    au_play(tagbuf);
+    if (*p != '0')
+      digit(*p);
+  }
+  return (1);
+}
+
+/* --- @bignum@ --- *
+ *
+ * Arguments:  @const char *p@ = pointer to digits
+ *             @size_t n@ = number of digits
+ *
+ * Returns:    Nonzero if the number was nonzero.
+ *
+ * Use:                Reads out a (large) integer in English.
+ */
+
+static int bignum(const char *p, size_t n)
+{
+  int nz = 0;
+  int rc;
+
+  if (n > 6) {
+    digits(p, n);
+    return (1);
+  }
+  if (n > 3) {
+    rc = bignum(p, n - 3);
+    p += n - 3;
+    n = 3;
+    if (rc) {
+      au_play("n-thou");
+      nz = 1;
+    }
+  }
+  if (n > 2) {
+    if (*p == '0') {
+      p++;
+    } else {
+      digit(*p++);
+      au_play("n-hun");
+      nz = 1;
+    }
+    n--;
+  }
+  return (lasttwo(p, n, nz ? f_and : 0) || nz);
+}
+
+/* --- @aunum@ --- *
+ *
+ * Arguments:  @const char *p@ = pointer to number's textual representation
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads the given number aloud.
+ */
+
+void aunum(const char *p)
+{
+  size_t pl;
+  int nz;
+
+  /* --- Pick off a leading sign --- */
+
+again:
+  if (*p == '+')
+    p++;
+  else if (*p == '-') {
+    au_play("n-minus");
+    p++;
+  }
+
+  /* --- Work out how many digits we have --- */
+
+  p += strspn(p, "0");
+  pl = strspn(p, "0123456789");
+  nz = bignum(p, pl);
+  p += pl;
+
+  /* --- If the value was zero, and there's no `point', say `zero' --- */
+
+  if (*p != '.' && !nz)
+    au_play("n-0");
+  if (*p == '.') {
+    au_play("n-point");
+    p++;
+    pl = strspn(p, "0123456789");
+    digits(p, pl);
+    p += pl;
+  }
+  if (*p == 'e' || *p == 'E') {
+    au_play("n-exp");
+    p++;
+    goto again;
+  }
+
+  /* --- Run out of things to do --- */
+
+  return;
+}
+
+/* --- @aunum_ulong@ --- *
+ *
+ * Arguments:  @unsigned long n@ = number to be read
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads a number expressed as an @unsigned long@.
+ */
+
+void aunum_ulong(unsigned long n)
+{
+  dstr d = DSTR_INIT;
+
+  dstr_putf(&d, "%lu", n);
+  aunum(d.buf);
+  dstr_destroy(&d);
+}
+
+/*----- That's all, folks -------------------------------------------------*/