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

fs.c

/*
 * brass - Braille and speech server
 *
 * Copyright (C) 2001 by Roger Butenuth, All rights reserved.
 * Copyright 2002 by Michael Gorse.
 * Copyright 2006 by Lukas Loehrer
 *
 * This is free software, placed under the terms of the
 * GNU General Public License, as published by the Free Software
 * Foundation.  Please see the file COPYING for details.
 *
 * $Id: fs.c,v 1.19 2007/01/18 23:58:42 mgorse Exp $
 *
 * Notes:
 *
 * The server process reads commands from clients and puts them into a
 * temporary buffer, defined by the variables text, text_head,
 * text_tail and text_synthpos. The server spawns two threads that are
 * responsible for further processing.
 *
 * The text thread (text_thread) reads commands from the text
 * buffer. Accesses to this buffer are synchronized by text_mutex. The
 * text thread calls the flite library to convert the text to
 * speech. The generated wave forms are stored in the wave buffer (ac,
 * ac_head, ac_tail, ac_synthpos, time_left). Access to this buffer is
 * synchronized by wave_mutex.
 *
 * The wave thread (wave_thread) takes the wave forms from the wave
 * buffer and plays them via the flite audio interface.
 *
 * The text thread is created when text data first arrives in the text
 * buffer (in function add_command()). It keeps running until a
 * "silence command is received. When the text buffer gets empty, the
 * text thread goes to sleep on the condition variable
 * "_text_condition". It is woke up by the main process when new data
 * arrives.
 *
 * The wave thread works similarly to the text thread. It is created
 * in s_flush and sleeps on "wave_condition" when the wave buffer
 * becomes empty.
 *
 * The "silence" command (handled in s_clear()) terminates both
 * threads. The wave thread is cancelled with pthread_cancel() and the
 * text thread is told to exit by setting text_thread_cancel to a
 * true value. Both threads are joined to be sure they have
 * exited. The state of all buffers is reset.
 *
 * The "time_left" variable holds the number of seconds of speech data
 * in the wave buffer. If this value exeeds a certain threshold
 * (currently 30s), the text thread will stop synthesizing more speech
 * and will go to sleep on text_condition. The wave thread signals
 * to the text thread when the wave buffer shrinks. This mechanism
 * prevents us from synthesizing useless wave forms we may never have
 * to play and reduces the memory usage of eflite drastically in
 * situations where the user requests a long document to be read from
 * top to bottom.
 *
 * The advantage of the disign with the condition variables is that it
 * is simple to understand and verify. Threads are only created by the
 * server process, each thread in exactly one place. A silence command
 * resets everything to a well defined state. The audio device is only
 * accessed by the wave thread which seems to alleviate some issues
 * with ALSA.
 * 
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>     /* tmp. for dbg. */
#include <assert.h>
#include <math.h>


#ifndef STANDALONE
#include <dlfcn.h>
#endif
#include "flite.h"
#include "flite_version.h"

#include "synthesizer.h"
#include "es.h"


static int  s_close(synth_t *s);
static int  s_synth(synth_t *s, unsigned char *buffer);
static int  s_flush(synth_t *s);
static int  s_clear(synth_t *s);
#if 0
static int  s_index_set(struct synth_struct *s);
static int  s_index_wait(struct synth_struct *s, int id, int timeout);
#endif
static int  s_get_param(struct synth_struct *s, synth_par_t par, int *value);
static int  s_set_param(struct synth_struct *s, synth_par_t par, int value);

typedef struct synth_state {
    int  param[S_MAX];
    int  initialized;
} synth_state_t;

static synth_state_t private_state[2];

static synth_t state[] = {
    {
        &private_state[0],
        &languages[LANG_BRITISH_ENGLISH],
        "FLite/US English",
        NULL,
        s_close,
        s_synth,
        s_flush,
        s_clear,
        NULL,                   /* s_index_set, */
        NULL,                   /* s_index_wait, */
        s_get_param,
        s_set_param
    }, {
        &private_state[1],
        &languages[LANG_GERMAN],
        "FLite/German", /* not supported */
        NULL,
        s_close,
        s_synth,
        s_flush,
        s_clear,
        NULL,                   /* s_index_set, */
        NULL,                   /* s_index_wait, */
        s_get_param,
        s_set_param
    }
};


static int     current_language = -1;
static int     ref_count = 0;
static FILE    *debug_fp = NULL;

  typedef enum { NONE, SPEECH, TONE } ac_type;

typedef struct
{
  ac_type type;
  void *data;
} AUDIO_COMMAND;

#if 0 /* tbd - figure out what these variables are supposed to be used for */
/*static int sync_mark_no = 0;*/    /* currently used number */
/*static struct timeval mark;*/     /* time the mark has been set */
#endif

/* server-specific variables */
static cst_voice *v = NULL;

/* Wave thread specific variables */
static pthread_t wave_thread;
static int wave_thread_active;
static pthread_mutex_t wave_mutex;
static pthread_cond_t wave_condition;
/* Wave buffer */
static AUDIO_COMMAND *ac;
static int ac_size;
static int ac_head, ac_tail;
static int ac_synthpos; /* largest index to play + 1 */
static float time_left = 0;
/* number of seconds of wave data in wave buffer */

/* Text thread and data structures */
static pthread_t text_thread;
static int text_thread_active;
static int text_thread_cancel;
static pthread_mutex_t text_mutex;
static pthread_cond_t text_condition;
/* Text buffer */
static char *text;
static int text_size;
static int text_head, text_tail;
static int text_synthpos;     /* pointer to beyond last piece to be played */

cst_audiodev *audiodev = NULL;      /* Sound device */
static pthread_attr_t ta;     /* for creating threads */
static pthread_mutexattr_t mt_attr; /* For creating mutexes */

extern cst_voice *REGISTER_VOX(const char *voxdir);
extern int cst_alloc_out;

#ifndef STANDALONE
/*
 * ----------------------------------------------------------------------
 * Called before library is loaded.
 * ----------------------------------------------------------------------
 */
void _init(void)
{
}


/*
 * ----------------------------------------------------------------------
 * Called before library is unloaded.
 * ----------------------------------------------------------------------
 */
void _fini(void)
{
}
#endif

/* Functions to reset buffers to initial state */
static inline void reset_text_buffer(void)
{
  text_head = text_tail = 0;
  text_synthpos = 0;
  text[0] ='\0';
}

static inline void reset_wave_buffer(void)
{
  ac_head = ac_tail = ac_synthpos = 0;
  time_left = 0;
  /* memset(ac, '\0', ac_size * sizeof(AUDIO_COMMAND));*/
}

/* Function to get current time as a double */
#ifdef DEBUG
#include <sys/time.h>
static inline double get_ticks_count(boid)
{
  struct timeval now;
  double  ticks;

  gettimeofday(&now, NULL);
  ticks=now.tv_sec+((double)now.tv_usec) /1.e6;
  return ticks;
}
#endif

/*
 * ----------------------------------------------------------------------
 * General open function for german and english synthesizer.
 * Second open increments refcount.
 * Return 0 on success, 1 on error.
 * ----------------------------------------------------------------------
 */
#ifdef DEBUG
/* This code seems to cause lingering processes in the event of a crash */
void segfault(int sig)
{
  extern char *sockname;
  es_log(1, "Got a seg fault -- exiting");
  fprintf(stderr, "Got a seg fault -- exiting.\n");
  unlink(sockname);
  _exit(11);
}
#endif


#define ES_LOG_STATE(label) \
  es_log(2, "%s: %s. ac_head=%d ac_synthpos=%d ac_tail=%d text_head=%d text_synthpos=%d text_tail=%d time_left=%.2f", __func__, label, \
    ac_head, ac_synthpos, ac_tail, text_head, text_synthpos, text_tail, time_left);

/* Helper functions and macros for managing mutexes */
#ifndef DEBUG
#define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex);
#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex);
#else
#define MUTEX_LOCK(mutex) \
  if (pthread_mutex_lock(&mutex) != 0) \
{\
  es_log(2, "%s: Error locking mutex.", __func__); \
  exit(3); \
}

#define MUTEX_UNLOCK(mutex) \
  { \
int ret_;\
  if ((ret_ = pthread_mutex_unlock(&mutex)) != 0) \
{\
  es_log(2, "%s: error unlocking mutex: %s", __func__, strerror(ret_)); \
  exit(3); \
} \
}
#endif

static void wave_unlock(void *function_name)
{
  es_log(2, "%s: unlocking wave mutex", function_name);
  MUTEX_UNLOCK(wave_mutex);
  es_log(2, "%s: unlocked wave mutex", (char *) function_name);
}

#define WAVE_UNLOCK \
  es_log(2, "%s: unlocking wave mutex", __func__); \
  MUTEX_UNLOCK(wave_mutex); \
  pthread_cleanup_pop(0); \
  es_log(2, "%s: unlocked wave mutex", __func__);

#define WAVE_LOCK \
  es_log(2, "%s: locking wave mutex", __func__); \
  pthread_cleanup_push(wave_unlock, (void *) __func__); \
  MUTEX_LOCK(wave_mutex); \
  es_log(2, "%s: got wave mutex", __func__); \
  pthread_testcancel();


static void text_unlock(void *function_name)
{
  es_log(2, "%s: unlocking text mutex", (char *) function_name);
  MUTEX_UNLOCK(text_mutex);
  es_log(2, "%s: unlocked text mutex", (char *) function_name);
}

#define TEXT_UNLOCK \
  es_log(2, "%s: unlocking text mutex", __func__); \
  MUTEX_UNLOCK(text_mutex); \
  pthread_cleanup_pop(0);\
  es_log(2, "%s: unlocked text mutex", __func__);

#define TEXT_LOCK \
  es_log(2, "%s: locking text mutex", __func__); \
  pthread_cleanup_push(text_unlock, (void *) __func__); \
  MUTEX_LOCK(text_mutex); \
  es_log(2, "%s: got text mutex", __func__); \
  pthread_testcancel();

#define TEXT_LOCK_NI \
  es_log(2, "%s: locking text mutex", __func__); \
  MUTEX_LOCK(text_mutex); \
  es_log(2, "%s: got text mutex", __func__);

#define TEXT_UNLOCK_NI \
  MUTEX_UNLOCK(text_mutex); \
  es_log(2, "%s: unlocked text mutex", __func__);


#define WAVE_LOCK_NI \
  es_log(2, "%s: locking wave mutex", __func__); \
  MUTEX_LOCK(wave_mutex); \
  es_log(2, "%s: got wave mutex", __func__);

#define WAVE_UNLOCK_NI \
  MUTEX_UNLOCK(wave_mutex); \
  es_log(2, "%s: unlocked wave mutex", __func__);


synth_t *synth_open(void *context, lookup_string_t lookup)
{
  synth_t *s;
  char    *language = (*lookup)(context, "language");
  int     langi;

  debug_fp = stderr;
  if (language == NULL) {
      language = "english";
  }
  if (ref_count == 0) {
      unlink("log");

#ifdef DEBUG
      signal(SIGSEGV, segfault);
#endif
      flite_init();
      v = REGISTER_VOX(NULL);
      /* We want our threads to be joinable */
      pthread_attr_init(&ta);
      pthread_mutexattr_init(&mt_attr);
#ifdef DEBUG
      pthread_mutexattr_settype(&mt_attr, PTHREAD_MUTEX_ERRORCHECK_NP);
#endif
      pthread_mutex_init(&text_mutex, &mt_attr);
      pthread_cond_init(&text_condition, NULL);
      text_thread_active = 0;
      text_thread_cancel = 0;
      text_size = 4096;
      text = (char *)malloc(text_size);
      text_head = text_tail = text_synthpos = 0;
      wave_thread_active = 0;
      ac_size = 64;
      ac = (AUDIO_COMMAND *)malloc(ac_size * sizeof(AUDIO_COMMAND));
      ac_head = ac_tail = ac_synthpos = 0;
      if (!ac || !text) return NULL;
      pthread_mutex_init(&wave_mutex, &mt_attr);
      pthread_cond_init(&wave_condition, NULL);
      time_left = 0;
  }
  ref_count++;

  if (!strcasecmp(language, "english")) {
      langi = 0;
      s = &state[langi];
  } else if (!strcasecmp(language, "german")) {
      langi = 1;
      s = &state[langi];
  } else {
      langi = -1;
      s = NULL;
  }

  if (s != NULL && !s->state->initialized) {
      s->state->param[S_SPEED]  = 1000;
      s->state->param[S_PITCH]  = 1000;
      s->state->param[S_VOLUME] = 1000;
      s->state->initialized = 1;
  }

  return s;
}


/*
 * ----------------------------------------------------------------------
 * General close. Decrement refcount, do real close when count reaches
 * zero.
 * ----------------------------------------------------------------------
 */
static int s_close(synth_t *s)
{
  ref_count--;
  if (ref_count == 0)
  {
      int ret;
      /* Wait for any speech to be spoken */
      if (text_thread_active)
      {
        while (text_tail > 0)
            usleep(100000);
        TEXT_LOCK_NI;
        text_thread_cancel = 1;
        pthread_cond_signal(&text_condition);
        TEXT_UNLOCK_NI;
        ret = pthread_join(text_thread, NULL);
        assert(ret == 0);
      }
      if (wave_thread_active)
      {
        while(ac_tail >0)
            usleep(100000);
        WAVE_LOCK_NI;
        pthread_cond_signal(&wave_condition); // necessary because we inhibit cancellation while waiting 
        pthread_cancel(wave_thread);
        WAVE_UNLOCK_NI;
        ret = pthread_join(wave_thread, NULL);
        assert(ret == 0);
      }
        
      if (text) free(text);
      if (ac) free(ac);
      text =NULL;
      ac = NULL;
  }

  return 0;
}


/*
 * ----------------------------------------------------------------------
 * Verify that the synthesizer is set to the correct language.
 * Switch if necessary.
 * ----------------------------------------------------------------------
 */
static void verify_language(struct synth_struct *s)
{
    int value = -1;

    if (s->lang->lang == LANG_BRITISH_ENGLISH
        && current_language != LANG_BRITISH_ENGLISH) {
      /* tbd */
    } else if (s->lang->lang == LANG_GERMAN &&
               current_language != LANG_GERMAN) {
      /* tbd */
    }
    if (value != -1) {
      /* tbd */
    }
}

static void cst_wave_free(cst_wave *w)
{
  es_log(2, "cst_wave_free: %p", w);
  if (w)
  {
    cst_free(w->samples);
    cst_free(w);
  }
}

static void ac_destroy(AUDIO_COMMAND *ac)
{
  switch (ac->type)
  {
  case SPEECH: case TONE: cst_wave_free(ac->data); break;
  default: break;
  }
  ac->type = NONE;
}

static void close_audiodev()
{
  if (audiodev)
  {
    audio_close(audiodev);
    audiodev = NULL;
  }
}



static void play_audio_close(void *cancel)
{
  if (audiodev)
  {
      audio_drain(audiodev);
      close_audiodev();
      //    usleep(5000);
  }
}


static inline void determine_playlen(int speed, cst_wave *wptr, int type, int *pl, int *s)
{
  int playlen, skip;
  if (type == SPEECH)
  {
      skip = (187 * wptr->sample_rate) / speed;
      playlen = wptr->num_samples - (skip * 2);
      if (playlen > 0 && playlen < 500) playlen += (skip * 2) / 3;
  }
  else
  {
      skip = 0;
      playlen = wptr->num_samples;
  }
  if (playlen < 0) playlen = 0;

  *pl = playlen;
  *s = skip;
}

static void * play(void *s)
{
  int playlen;
  int skip;
  cst_wave *wptr;
  int type;
  int *sparam = ((synth_t *)s)->state->param;
#ifdef DEBUG
  double start_time;
#endif

  ES_LOG_STATE("Entering main loop");
  while (1)
  {
      es_log(2, "play: beginning of main loop");
      WAVE_LOCK;
      /* Wait for new wave data to arrive */
      ES_LOG_STATE("checking condition");
      while (ac_head >= ac_synthpos)
      {
        pthread_testcancel();
        es_log(2, "play: Going to sleep.");
        // Some versions of glibc and Linux do not like being cancelled
        // while waiting for a condition variable.
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
        pthread_cond_wait(&wave_condition, &wave_mutex);
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        ES_LOG_STATE("Woke up, checking condition");
      }
      es_log(2, "play: condition passed.");
      pthread_testcancel();
      wptr = ac[ac_head].data;
      type = ac[ac_head].type;
      WAVE_UNLOCK;
      pthread_testcancel();
      pthread_cleanup_push(play_audio_close, NULL);

      es_log(2, "Opening audio device.");
      /* We abuse the wave mutex here to avoid being canceled
       * while the audio device is being openned */
      WAVE_LOCK;
      audiodev = audio_open(wptr->sample_rate, wptr->num_channels, CST_AUDIO_LINEAR16);
      WAVE_UNLOCK;
      if (audiodev == NULL)
      {
        es_log(2, "Failed to open audio device.");
#ifdef CST_AUDIO_OSS
        if (errno == -EBUSY)
        {
            es_log(2, "Device was busy, trying again later..");
            usleep(1000000;
            continue;
        }
#endif
es_log(2, "Cannot recover, exiting...");
        exit(1);
      }
      determine_playlen(sparam[S_SPEED], wptr, type, &playlen, &skip);
    es_log(2, "play: wave=%p, samples=%p, num_samples=%d skip=%d playlen=%d", wptr, wptr->samples, wptr->num_samples, skip, playlen);
    if (playlen > 0)
    {
      if (sparam[S_VOLUME] != 1000)
            cst_wave_rescale(wptr, (sparam[S_VOLUME] << 16) / 1000);
      pthread_testcancel();
        es_log(2, "play: Writing to audio device.");
#ifdef DEBUG
        start_time = get_ticks_count();
#endif
        pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
      audio_write(audiodev, wptr->samples + skip, playlen * 2);
        es_log(2, "Write took %.2f seconds.", get_ticks_count() - start_time);
      }
    es_log(2, "play: syncing.");
#ifdef DEBUG
      start_time = get_ticks_count();
#endif
    audio_flush(audiodev);
      pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
      es_log(2, "Flush took %.2f seconds.", get_ticks_count() - start_time);
    es_log(2, "play: Closing audio device");
      close_audiodev();
      pthread_cleanup_pop(0);
        pthread_testcancel();
        TEXT_LOCK;
    time_left -= ((float)playlen) / wptr->sample_rate;
      pthread_cond_signal(&text_condition);
      TEXT_UNLOCK;

      WAVE_LOCK;
    ac_destroy(&ac[ac_head]);
      ac_head++;
      if (ac_head == ac_tail)
      {
        reset_wave_buffer();
      }
    else if (ac_head > (ac_size >> 1))
    {
      es_log(1, "play: compacting wave pointersac_head=%d ac_synthpos=%d ac_tail=%d ac_size=%d", ac_head,ac_synthpos,  ac_tail, ac_size);
      memmove(ac, ac + ac_head, (ac_tail - ac_head) * sizeof(AUDIO_COMMAND));
        /* The following line is not really necessary */
        /* memset(ac + ac_tail, '\0', (ac_size - ac_tail) * sizeof(AUDIO_COMMAND));*/
      ac_tail -= ac_head;
      if (ac_synthpos > 0) ac_synthpos -= ac_head;
      ac_head = 0;
    }
      WAVE_UNLOCK;
      ES_LOG_STATE("After playing");
  }
}

/* Pause speech synthesis if there is more that */
/* 30 seconds of speech in the wave buffer */
#define MAX_WAVE_BUFFER_TIME 30

/* This function assumes to hold the text_mutex when called */
static inline void text_thread_testcancel()
{
  if (text_thread_cancel)
  {
      MUTEX_UNLOCK(text_mutex);
      text_thread_cancel = 0;
      es_log(2, "Text thread cancelled, exiting.");
      pthread_exit(0);
  }
}

static void * synthesize(void * s)
{
  cst_wave *wptr = NULL;
  int command;
  int *sparam = ((synth_t *)s)->state->param;

  ES_LOG_STATE("entering main loop");
  while (1)
  {
      int playlen, skip;

      es_log(2, "synthesize: Beginning of main loop.");
      TEXT_LOCK_NI;
      /* The idea here is to wait until new text data has
       * arrived and until there is less than MAX_WAVVE_BUFFER_TIME
       * of wave data in the wave buffer. Only consider time_left
       * if wave queue contains at least five junks of data.
      */
      ES_LOG_STATE("checking condition");
      while (!text[text_head] || (time_left > MAX_WAVE_BUFFER_TIME && ac_tail - ac_head > 5))
      {
        text_thread_testcancel();
        es_log(2, "synthesize: waiting for new text data. Going to sleep.");
        pthread_cond_wait(&text_condition, &text_mutex);
      ES_LOG_STATE("Woke up, checking condition");
      }
      ES_LOG_STATE("Condition passed");
      text_thread_testcancel();
      /* Copy command into temporary buffer
       * so we can release the text mutex whilel
       * synthesizing the speech */
      size_t command_length = strlen(text + text_head);
      assert(command_length < text_tail - text_head);
      char buf[command_length + 1];
      strcpy(buf, text + text_head);

      TEXT_UNLOCK_NI;

      switch ((command = buf[0]))
      {
      case 1:     /* text */
        wptr = flite_text_to_wave(buf + 1, v);
        break;
      case 2:     /* tone */
        {
            int freq, dur, vol;
            if (sscanf(buf + 1, "%d %d %d", &freq, &dur, &vol) != 3)
            {
              es_log(1, "unable to scan tone: %s", text + text_head);
              break;
            }
            wptr = generate_tone(freq, dur, vol);
            break;
        }
      default:
        /* snafu - I'm getting the hell out of here */
        es_log(1, "synthesize: internal error: unknown command: %x", command);
        return NULL;
      }
      
      determine_playlen(sparam[S_SPEED], wptr, command, &playlen, &skip);

      WAVE_LOCK_NI;
      /* Make sure there is space in wave buffer */
      if (ac_size == ac_tail + 1)
      {
        ac_size <<= 1;
        ac = (AUDIO_COMMAND *)realloc(ac, ac_size * sizeof(AUDIO_COMMAND));
        if (!ac)
        {
            fprintf(stderr, "Out of memory, ac_size=%d\n", ac_size);
            exit(1);
        }
      }

      /* Add newly created waveform to buffer */
      ac[ac_tail].type = command;
      ac[ac_tail++].data = wptr;
      TEXT_LOCK_NI;
      time_left +=((float) playlen) / wptr->sample_rate;
      if (text_head < text_synthpos)
      {
        ac_synthpos = ac_tail;
        es_log(2, "synthesize: Waking up wave thread.");
        pthread_cond_signal(&wave_condition);
      }
      WAVE_UNLOCK_NI;

      /* Text buffer maintenance */
/* Skip text just synthesized */
      text_head += command_length + 1;
      
      /* Compact text buffer if it seems the right thing to do */
      if (text_head == text_tail)
      {
        reset_text_buffer();
      }
      else if (text_head > (text_size >> 1))
      {
        //es_log(1, "synthesizer: compacting buffer");
        memcpy(text, text + text_head, text_tail - text_head + 1);
        text_tail -= text_head;
        if (text_synthpos > 0) text_synthpos -= text_head;
        text_head = 0;
      }
      TEXT_UNLOCK_NI;
      ES_LOG_STATE("After adding");
  }
  /* We  should never get here...*/
  assert(0);
}

/*
 * ----------------------------------------------------------------------
 * Copy Text to server. Text is not spoken until a flush command
 * arrives.
 * ----------------------------------------------------------------------
 */
static void add_command(struct synth_struct *s, int id, unsigned char *buffer)
{
  int len;

  assert(s->state->initialized);
  len = strlen((char *)buffer);
  if (text_thread_active)
  {
      TEXT_LOCK_NI;
  }

  if (text_tail + len + 3 >= text_size)
  {
    text_size <<= 1;
      text_size += len;
    text = (char *)realloc(text, text_size);
    if (!text)
    {
      fprintf(stderr, "Out of memory: text_size=%d\n", text_size);
      exit(1);
    }
  }
  text[text_tail++] = (char)id;
  strcpy(text + text_tail, (char *)buffer);
  text_tail += len + 1;
  /* The below line is important.  An extra \0 indicates to the synthesize
     thread that there is no more data. */
  text[text_tail] = '\0';
  if (!text_thread_active)
  {
    es_log(2, "s_synth: creating new text thread");
    text_thread_active = 1;
    pthread_create(&text_thread, &ta, synthesize, s);
  }
  else
  {
      /* Signal to text thread that more data is available */
      ES_LOG_STATE("waking up text thread.");
      pthread_cond_signal(&text_condition);
      TEXT_UNLOCK_NI;
  }
  return;
}

static int s_synth(struct synth_struct *s, unsigned char *buffer)
{
  add_command(s, 1, buffer);
  return 0;
}

void add_tone_command(struct synth_struct *s, int freq, int dur, int vol)
{
  char buf[40];

  sprintf(buf, "%d %d %d", freq, dur, vol);
  add_command(s, 2, (unsigned char *)buf);
}

/*
 * ----------------------------------------------------------------------
 * Flush synthesizer. This triggers the synthesizer and starts 
 * the synthesis.
 * ----------------------------------------------------------------------
 */
static int s_flush(synth_t *s)
{
  /* XXX */
  if (!text_thread_active) return 0;

  TEXT_LOCK_NI;
  text_synthpos = text_tail;
  TEXT_UNLOCK_NI;

  if (!wave_thread_active)
  {
      es_log(2, "es_flush: creating play thread");
      WAVE_LOCK_NI;
      wave_thread_active = 1;
      ac_synthpos = ac_tail;
      pthread_create(&wave_thread, &ta, play, s);
      WAVE_UNLOCK_NI;
  }
  else
  {
      WAVE_LOCK_NI;
      if (ac_synthpos < ac_tail)
      {
        ac_synthpos = ac_tail;
        pthread_cond_signal(&wave_condition);
      }
      WAVE_UNLOCK_NI;
  }
  return 0;
}


/*
 * ----------------------------------------------------------------------
 * Remove anything in the synthesizer speech queue.
 * ----------------------------------------------------------------------
 */
static int s_clear(synth_t *s)
{
  int i, ret;

  es_log(2, "s_clear: text=%d %d, wave=%d %d", text_head, text_tail, ac_head, ac_tail);

    if (wave_thread_active)
  {
      WAVE_LOCK_NI;
      pthread_cond_signal(&wave_condition); // necessary because we inhibit cancellation while waiting
      pthread_cancel(wave_thread);
      WAVE_UNLOCK_NI;
  }

  if (text_thread_active)
  {
      TEXT_LOCK_NI;
      text_thread_cancel = 1;
      pthread_cond_signal(&text_condition);
      TEXT_UNLOCK_NI;
  }

  if (text_thread_active)
  {
      ret = pthread_join(text_thread, NULL);
      assert(ret == 0);
  }
  if (wave_thread_active)
  {
      ret =pthread_join(wave_thread, NULL);
      assert(ret == 0);
  }
      
  /* At this point, no thread is running */
  
  /* Free any wave data */
  es_log(2, "s_clear: freeing wave data: %d", ac_tail);
  for (i = 0; i < ac_tail; i++)
  {
      if (ac[i].type != NONE) ac_destroy(&ac[i]);
  }

  /* Reset data structures */
  reset_text_buffer();
  reset_wave_buffer();
  text_thread_active = wave_thread_active = 0;
  text_thread_cancel = 0;

    /* XXX: The following code should not be necessary However, on kernel
   * 2.6, the condition variable would sometimes get corrupted and
   * signaling would thus no longer work. Any ideas on why this
   * happens are or how to fix it are welcome.*/
  ret = pthread_cond_destroy(&wave_condition);
  if (ret)
  {
      es_log(2, "s_clear: Wave Condition error %s", strerror(ret));
      fprintf(stderr, "s_clear: Wave condition corrupted and not recoverable.");
      exit(4);
  }
  pthread_cond_init(&wave_condition, NULL);

  return 0;
}


                                           #if 0
                                                /*
                                           * ----------------------------------------------------------------------
                                           * ToDo
                                           * ----------------------------------------------------------------------
                                           */
  static int s_index_set(struct synth_struct *s)
                                           {
                                            return 0;
                                            }

                                                /*
                                           * ----------------------------------------------------------------------
                                           * ToDo
                                           * ----------------------------------------------------------------------
                                           */
                                           static int s_index_wait(struct synth_struct *s, int id, int timeout)
                                           {
                                            int res = 0;
                                            return res;
                                            }
                                           #endif


                                                /*
                                           * ----------------------------------------------------------------------
                                           * Get a synthesizer parameter.
                                           * ----------------------------------------------------------------------
                                           */
                                           static int s_get_param(struct synth_struct *s, synth_par_t par, int *value)
                                           {
                                            if (par >= 0 && par < S_MAX) {
                                                                                          *value = s->state->param[par];
                                                                                          return 0;
                                                                                          } else
                                            return 1;
                                            }


                                                /*
                                           * ----------------------------------------------------------------------
                                           * Set a parameter of the synthesizer.
                                           * ----------------------------------------------------------------------
                                           */
                                           static int s_set_param(struct synth_struct *s, synth_par_t par, int value)
                                           {
                                            verify_language(s);

                                            switch (par) {
                                                                  case S_SPEED:               /* default: 1 */
                                                                  es_log(2, "Setting duration_stretch to %4.3f", (float)1000 / value);
                                                                  feat_set_float(v->features, "duration_stretch", (float)1000 / value);
                                                                  break;
                                                                  case S_PITCH:               /* default: 100 */
                                                                  es_log(2, "Setting pitch to %3.3f", exp((float)value / 1000) * 100 / exp(1));
                                                                  feat_set_float(v->features, "int_f0_target_mean", exp((float)value / 1000) * 100 / exp(1));
                                                                  /* tbd */
                                                                  break;
                                                                  case S_VOLUME:              /* default: 92, range: 0-100 */
                                                                  /* tbd */
                                                                  break;
                                                                  default:
                                                                  return 1;
                                                                  }
                                            s->state->param[par] = value;

                                            return 0;
                                            }

                                           #ifdef STANDALONE
                                           lang_descr_t languages[LANGUAGES] = {
                                                                                                  { LANG_BRITISH_ENGLISH, "British English" },
                                                                                                  { LANG_GERMAN, "German" },
                                                                                                  { LANG_DUMMY, "no language" }
                                                                                                  };

                                           lang_t lang =
                                           {
                                            NULL,
                                            &languages[0],
                                            NULL,
                                            NULL,
                                            0,
                                            0,
                                            NULL,
                                            NULL,
                                            NULL,
                                            NULL,
                                            NULL
                                            };

                                           lang_t *language_open(void *context, lookup_string_t lookup)
                                           {
                                            lang.synth = synth_open(context, lookup);
                                            return &lang;
                                            }
                                           #endif

Generated by  Doxygen 1.6.0   Back to index