[PATCH] executor: play tones using separate process (script voiceman-tone)

Dmitri Paduchikh dpaduchikh на gmail.com
Пт Фев 22 20:08:27 MSK 2013


---
 .gitignore                 |   2 +
 configure.ac               |   3 --
 executors/default.c        |  30 +++++++++---
 executors/makefile.am      |  19 ++++++--
 executors/tone.c           |  74 ----------------------------
 executors/tonegen.c        | 119 +++++++++++++++++++++++++++++++++++++++++++++
 executors/voiceman-tone.in |   2 +
 m4/ao.m4                   | 112 ------------------------------------------
 8 files changed, 162 insertions(+), 199 deletions(-)
 delete mode 100644 executors/tone.c
 create mode 100644 executors/tonegen.c
 create mode 100644 executors/voiceman-tone.in
 delete mode 100644 m4/ao.m4

diff --git a/.gitignore b/.gitignore
index 9e153fb..fb97c80 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@ configure
 /shell/voiceman
 /tools/voiceman-trim
 /voiceman.conf
+/executors/tonegen
+/executors/voiceman-tone
diff --git a/configure.ac b/configure.ac
index 310484c..1b0298b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -33,9 +33,6 @@ if test "x$default_executor" = x; then
 default_executor="voiceman-executor"
 fi
 
-# Find the path to libao and set AO_CFLAGS and AO_LIBS
-#XIPH_PATH_AO
-
 AC_SUBST(VOICEMAN_INCLUDES, '-I$(top_srcdir)/utils -I$(top_srcdir)/executors')
 AC_SUBST(VOICEMAN_DAEMON_INCLUDES, '$(VOICEMAN_INCLUDES) -I$(top_srcdir)/daemon')
 
diff --git a/executors/default.c b/executors/default.c
index 7073fa6..8347722 100644
--- a/executors/default.c
+++ b/executors/default.c
@@ -36,10 +36,6 @@
 #define QUEUE_ITEM_TEXT 1
 #define QUEUE_ITEM_TONE 2
 
-void toneInit();
-void playTone(size_t freq, size_t lengthMs);
-void toneClose();
-
 typedef struct QueueItem_  
 {
   int type;
@@ -262,6 +258,30 @@ char isPlaying()
   return pid != (pid_t)0 || playerPid != (pid_t)0;
 }
 
+static void playTone(size_t freq, size_t ms)
+{
+  pid_t tonePid;
+  char freqParam[11];
+  char durParam[11];
+  const char *toneprog = BINDIR "/voiceman-tone";
+
+  snprintf(durParam, sizeof(durParam), "%u", (unsigned)ms);
+  snprintf(freqParam, sizeof(freqParam), "%u", (unsigned)freq);
+
+  tonePid = fork();
+  if (tonePid == -1) {
+    perror("fork()");
+    fflush(stderr);
+    return;
+  }
+  if (tonePid == 0) {
+    execl(toneprog, toneprog, durParam, freqParam, (char*)NULL);
+    _exit(EXIT_FAILURE);
+  } else {
+    playerPid = tonePid;
+  }
+}
+
 void execute(char* synthCommand, char* playerCommand, char* text)
 {
   int pp[2];
@@ -597,8 +617,6 @@ int main(int argc, char* argv[])
   sigemptyset(&blockedMask);
   sigaddset(&blockedMask, SIGCHLD);
   sigprocmask(SIG_BLOCK, &blockedMask, &origMask);
-  toneInit();
   exitCode = mainLoop(STDIN_FILENO, &origMask);
-  toneClose();
   return exitCode;
 }
diff --git a/executors/makefile.am b/executors/makefile.am
index 4351d6e..89c0a59 100644
--- a/executors/makefile.am
+++ b/executors/makefile.am
@@ -1,10 +1,21 @@
 
-AM_CFLAGS = $(VOICEMAN_CFLAGS) $(AO_CFLAGS) $(VOICEMAN_INCLUDES) -std=gnu99
+AM_CFLAGS = $(VOICEMAN_CFLAGS) $(VOICEMAN_INCLUDES) -std=gnu99
+AM_CPPFLAGS = -DBINDIR=\"$(bindir)\"
 
 bin_PROGRAMS=voiceman-executor
 
-voiceman_executor_LDADD = -lao -lm
-
 voiceman_executor_SOURCES = \
-tone.c \
 default.c 
+
+pkglibexec_PROGRAMS = tonegen
+tonegen_SOURCES = tonegen.c
+tonegen_LDADD = -lm
+
+dist_noinst_DATA = voiceman-tone.in
+nodist_bin_SCRIPTS = voiceman-tone
+voiceman-tone: voiceman-tone.in makefile
+	sed 's#@dir@#$(pkglibexecdir)#g' $^ >$@.tmp
+	mv -f $@.tmp $@
+
+clean-local:
+	-rm -f voiceman-tone voiceman-tone.tmp
diff --git a/executors/tone.c b/executors/tone.c
deleted file mode 100644
index 311defe..0000000
--- a/executors/tone.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-	Copyright (c) 2000-2012 Michael Pozhidaev<msp на altlinux.org>
-   This file is part of the VoiceMan speech service.
-
-   VoiceMan speech service 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 3 of the License, or (at your option) any later version.
-
-   VoiceMan speech service 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.
-*/
-
-#include<assert.h>
-#include<stdlib.h>
-#include<math.h>
-#include<strings.h>
-#include<ao/ao.h>
-
-void toneInit()
-{
-  ao_initialize();
-}
-
-void toneClose()
-{
-  ao_shutdown();
-}
-
-void playTone(size_t fr, size_t lengthMs)
-{
-  ao_device *device = NULL;
-  ao_sample_format format;
-  int default_driver;
-  float freq=fr;
-  size_t bufSize = 0;
-  char* buffer = NULL;
-  size_t samplesToPlay = 0;
-  size_t i;
-  assert(fr >= 0 && lengthMs >= 10);
-  bzero(&format, sizeof(ao_sample_format));
-  default_driver = ao_default_driver_id();
-  format.bits = 16;
-  format.channels = 2;
-  format.rate = 44100;
-  format.byte_format = AO_FMT_LITTLE;
-  samplesToPlay = format.rate*lengthMs/1000;
-  device = ao_open_live(default_driver, &format, NULL);
-  if (device == NULL) 
-    {
-      ao_shutdown();
-      return;
-    }
-  bufSize = format.bits/8 * format.channels * samplesToPlay;
-  buffer = (char*)calloc(bufSize, sizeof(char));
-  if (buffer == NULL)
-    {
-      ao_close(device);
-      ao_shutdown();
-      return;
-    }
-  for (i = 0; i < samplesToPlay; i++) 
-    {
-      int sample = (int)(0.75 * 32768.0 *
-			 sin(2 * M_PI * freq * ((float) i/format.rate)));
-      buffer[4*i] = buffer[4*i+2] = sample & 0xff;
-      buffer[4*i+1] = buffer[4*i+3] = (sample >> 8) & 0xff;
-    }
-  ao_play(device, buffer, bufSize);
-  free(buffer);
-  ao_close(device);
-}
diff --git a/executors/tonegen.c b/executors/tonegen.c
new file mode 100644
index 0000000..98ce919
--- /dev/null
+++ b/executors/tonegen.c
@@ -0,0 +1,119 @@
+/*
+  Copyright (c) Dmitri Paduchikh <dpaduchikh на gmail.com>
+
+  This file is part of the VoiceMan speech service.
+
+  VoiceMan speech service 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 3 of the License, or (at your option) any later version.
+
+  VoiceMan speech service 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.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <errno.h>
+#include <unistd.h>
+
+int main(int argc, char *argv[])
+{
+  double dur;			/* duration in msecs */
+  double freq;			/* pitch in Hz */
+  /* sample rate is 22050, S16_LE */
+  const double rate = 22050.0;
+  if (argc != 3) {
+    fprintf(stderr, "%s: usage: tonegen ms freq", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  {
+    char *endptr;
+    dur = strtol(argv[1], &endptr, 0);
+    if (*endptr != '\0') {
+      fprintf(stderr, "%s: incorrect argument: %s\n", argv[0], argv[1]);
+      exit(EXIT_FAILURE);
+    }
+    freq = strtol(argv[2], &endptr, 0);
+    if (*endptr != '\0') {
+      fprintf(stderr, "%s: incorrect argument: %s\n", argv[0], argv[2]);
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  {
+    int16_t buf[1024];
+    const int bufsize = sizeof(buf)/sizeof(buf[0]);
+    int chunk, written;
+    int i = 0;				/* number of samples to be computed */
+    int samples = lround(0.001*dur*rate);
+    double coeff = 2 * M_PI * freq / rate;
+    int fade = lround(0.002*rate);
+
+    while (i < fade && i <= samples) {
+      int j;
+      chunk = samples-i+1 < bufsize? samples-i+1 : bufsize;
+      chunk = fade-i < chunk? fade-i : chunk;
+      for (j = 0; j < chunk; i++, j++)
+	buf[j] = lround(24000.0 * ((double)i / (double)fade) * sin(coeff*i));
+      written = 0;
+      while (written < chunk*sizeof(buf[0])) {
+	int rc = write(1, buf + written, chunk*sizeof(buf[0]) - written);
+	if (rc == -1) {
+	  if (errno == EINTR)
+	    continue;
+	  else {
+	    perror("write");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+	written += rc;
+      }
+    }
+
+    while (i < samples-fade) {
+      int j;
+      chunk = samples-fade-i < bufsize? samples-fade-i : bufsize;
+      for (j = 0; j < chunk; i++, j++)
+	buf[j] = lround(24000.0 * sin(coeff*i));
+      written = 0;
+      while (written < chunk*sizeof(buf[0])) {
+	int rc = write(1, buf + written, chunk*sizeof(buf[0]) - written);
+	if (rc == -1) {
+	  if (errno == EINTR)
+	    continue;
+	  else {
+	    perror("write");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+	written += rc;
+      }
+    }
+
+    while (i <= samples) {
+      int j;
+      chunk = samples-i+1 < bufsize? samples-i+1 : bufsize;
+      for (j = 0; j < chunk; i++, j++)
+	buf[j] = lround(24000.0 * ((double)(samples-i) / (double)fade) * sin(coeff*i));
+      written = 0;
+      while (written < chunk*sizeof(buf[0])) {
+	int rc = write(1, buf + written, chunk*sizeof(buf[0]) - written);
+	if (rc == -1) {
+	  if (errno == EINTR)
+	    continue;
+	  else {
+	    perror("write");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+	written += rc;
+      }
+    }
+  }
+  
+  return 0;
+}
diff --git a/executors/voiceman-tone.in b/executors/voiceman-tone.in
new file mode 100644
index 0000000..f43333a
--- /dev/null
+++ b/executors/voiceman-tone.in
@@ -0,0 +1,2 @@
+#!/bin/sh
+ на dir@/tonegen "$@" | aplay -q -t raw -f S16_LE -r 22050 -c1
diff --git a/m4/ao.m4 b/m4/ao.m4
deleted file mode 100644
index a852ad8..0000000
--- a/m4/ao.m4
+++ /dev/null
@@ -1,112 +0,0 @@
-# ao.m4
-# Configure paths for libao
-# Jack Moffitt <jack на icecast.org> 10-21-2000
-# Shamelessly stolen from Owen Taylor and Manish Singh
-
-dnl XIPH_PATH_AO([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
-dnl Test for libao, and define AO_CFLAGS and AO_LIBS
-dnl
-AC_DEFUN([XIPH_PATH_AO],
-[dnl 
-dnl Get the cflags and libraries
-dnl
-AC_ARG_WITH(ao,[  --with-ao=PFX   Prefix where libao is installed (optional)], ao_prefix="$withval", ao_prefix="")
-AC_ARG_WITH(ao-libraries,[  --with-ao-libraries=DIR   Directory where libao library is installed (optional)], ao_libraries="$withval", ao_libraries="")
-AC_ARG_WITH(ao-includes,[  --with-ao-includes=DIR   Directory where libao header files are installed (optional)], ao_includes="$withval", ao_includes="")
-AC_ARG_ENABLE(aotest, [  --disable-aotest       Do not try to compile and run a test ao program],, enable_aotest=yes)
-
-
-  if test "x$ao_libraries" != "x" ; then
-    AO_LIBS="-L$ao_libraries"
-  elif test "x$ao_prefix" != "x"; then
-    AO_LIBS="-L$ao_prefix/lib"
-  elif test "x$prefix" != "xNONE"; then
-    AO_LIBS="-L$prefix/lib"
-  fi
-
-  if test "x$ao_includes" != "x" ; then
-    AO_CFLAGS="-I$ao_includes"
-  elif test "x$ao_prefix" != "x"; then
-    AO_CFLAGS="-I$ao_prefix/include"
-  elif test "x$prefix" != "xNONE"; then
-    AO_CFLAGS="-I$prefix/include"
-  fi
-
-  # see where dl* and friends live
-  AC_CHECK_FUNCS(dlopen, [AO_DL_LIBS=""], [
-    AC_CHECK_LIB(dl, dlopen, [AO_DL_LIBS="-ldl"], [
-      AC_MSG_WARN([could not find dlopen() needed by libao sound drivers
-      your system may not be supported.])
-    ])
-  ])
-
-  AO_LIBS="$AO_LIBS -lao $AO_DL_LIBS"
-
-  AC_MSG_CHECKING(for ao)
-  no_ao=""
-
-
-  if test "x$enable_aotest" = "xyes" ; then
-    ac_save_CFLAGS="$CFLAGS"
-    ac_save_LIBS="$LIBS"
-    CFLAGS="$CFLAGS $AO_CFLAGS"
-    LIBS="$LIBS $AO_LIBS"
-dnl
-dnl Now check if the installed ao is sufficiently new.
-dnl
-      rm -f conf.aotest
-      AC_TRY_RUN([
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ao/ao.h>
-
-int main ()
-{
-  system("touch conf.aotest");
-  return 0;
-}
-
-],, no_ao=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
-       CFLAGS="$ac_save_CFLAGS"
-       LIBS="$ac_save_LIBS"
-  fi
-
-  if test "x$no_ao" = "x" ; then
-     AC_MSG_RESULT(yes)
-     ifelse([$1], , :, [$1])     
-  else
-     AC_MSG_RESULT(no)
-     if test -f conf.aotest ; then
-       :
-     else
-       echo "*** Could not run ao test program, checking why..."
-       CFLAGS="$CFLAGS $AO_CFLAGS"
-       LIBS="$LIBS $AO_LIBS"
-       AC_TRY_LINK([
-#include <stdio.h>
-#include <ao/ao.h>
-],     [ return 0; ],
-       [ echo "*** The test program compiled, but did not run. This usually means"
-       echo "*** that the run-time linker is not finding ao or finding the wrong"
-       echo "*** version of ao. If it is not finding ao, you'll need to set your"
-       echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
-       echo "*** to the installed location  Also, make sure you have run ldconfig if that"
-       echo "*** is required on your system"
-       echo "***"
-       echo "*** If you have an old version installed, it is best to remove it, although"
-       echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
-       [ echo "*** The test program failed to compile or link. See the file config.log for the"
-       echo "*** exact error that occured. This usually means ao was incorrectly installed"
-       echo "*** or that you have moved ao since it was installed." ])
-       CFLAGS="$ac_save_CFLAGS"
-       LIBS="$ac_save_LIBS"
-     fi
-     AO_CFLAGS=""
-     AO_LIBS=""
-     ifelse([$2], , :, [$2])
-  fi
-  AC_SUBST(AO_CFLAGS)
-  AC_SUBST(AO_LIBS)
-  rm -f conf.aotest
-])
-- 
1.8.1.4




Подробная информация о списке рассылки Homeros