ale-sys 0.1.2

Rust bindings to the Arcade Learning Environment
Documentation
//============================================================================
//
//   SSSS    tt          lll  lll       
//  SS  SS   tt           ll   ll        
//  SS     tttttt  eeee   ll   ll   aaaa 
//   SSSS    tt   ee  ee  ll   ll      aa
//      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
//  SS  SS   tt   ee      ll   ll  aa  aa
//   SSSS     ttt  eeeee llll llll  aaaaa
//
// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SpeakJet.cxx,v 1.6 2007/01/01 18:04:50 stephena Exp $
//============================================================================

#ifdef ATARIVOX_SUPPORT

#include "SpeakJet.hxx"
#include "../common/Log.hpp"

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SpeakJet::SpeakJet()
{

  // Initialize output buffers. Each one points to the next element,
  // except the last, which points back to the first.
  SpeechBuffer *first = &outputBuffers[0];
  SpeechBuffer *last = 0;
  for(int i=0; i<SPEECH_BUFFERS; i++) {
    SpeechBuffer *sb = &outputBuffers[i];
    sb->items = 0;
    sb->lock = SDL_CreateSemaphore(1);
    if(last) {
       last->next = sb;
    }
    last = sb;
  }
  last->next = first;

  myCurrentOutputBuffer = ourCurrentWriteBuffer = first;
  ourCurrentWritePosition = 0;

  // Init rsynth library
  darray_init(&rsynthSamples, sizeof(short), 2048);

/*
  rsynth = rsynth_init(samp_rate, mSec_per_frame,
                       rsynth_speaker(F0Hz, gain, Elements),
                       save_sample, flush_samples, &samples);
*/
  rsynth = rsynth_init(31400, 10.0,
                       rsynth_speaker(133.0, 57, Elements),
                       save_sample, flush_samples, &rsynthSamples);
  spawnThread();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SpeakJet::~SpeakJet()
{
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SpeakJet::spawnThread()
{
  ourInputSemaphore = SDL_CreateSemaphore(1); // 1==unlocked
  uInt32 sem = SDL_SemValue(ourInputSemaphore);
  ale::Logger::Info << "before SDL_CreateThread(), sem==" << sem << endl;
  ourThread = SDL_CreateThread(thread, 0);
  sem = SDL_SemValue(ourInputSemaphore);
  ale::Logger::Info << "after SDL_CreateThread(), sem==" << sem << endl;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int SpeakJet::thread(void *data) {
  ale::Logger::Info  << "rsynth thread spawned" << endl;
  while(1) {
    speak();
    usleep(10);
  }
  return 0;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SpeakJet::write(uInt8 code)
{
  // TODO: clean up this mess.
  const char *rsynthPhones = xlatePhoneme(code);
  ale::Logger::Info << "rsynth: \"" << rsynthPhones << "\"" << endl;
  int len = strlen(rsynthPhones);

  if(ourInputCount + len + 1 >= INPUT_BUFFER_SIZE) {
    ale::Logger::Info << "phonemeBuffer is full, dropping" << endl;
    return;
  }

  uInt32 sem = SDL_SemValue(ourInputSemaphore);
  ale::Logger::Info << "write() waiting on semaphore (value " << sem << ")" << endl;
  SDL_SemWait(ourInputSemaphore);
  ale::Logger::Info << "write() got semaphore" << endl;
  for(int i=0; i<len; i++)
    phonemeBuffer[ourInputCount++] = rsynthPhones[i];
  phonemeBuffer[ourInputCount] = '\0';
  ale::Logger::Info << "phonemeBuffer contains \"" << phonemeBuffer << "\"" << endl;
  ale::Logger::Info << "write() releasing semaphore" << endl;
  SDL_SemPost(ourInputSemaphore);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SpeakJet::speak()
{
  // TODO: clean up this mess.
  static char myInput[INPUT_BUFFER_SIZE];

  if(!ourInputCount)
    return;

  uInt32 sem = SDL_SemValue(ourInputSemaphore);
  ale::Logger::Info << "speak() waiting on semaphore (value " << sem << ")" << endl;
  SDL_SemWait(ourInputSemaphore);
  ale::Logger::Info << "speak() got semaphore" << endl;

  // begin locked section

  bool foundSpace = false;
  for(int i=0; i<ourInputCount; i++)
    if( (myInput[i] = phonemeBuffer[i]) == ' ')
      foundSpace = true;

  if(ourInputCount >= INPUT_BUFFER_SIZE - 5)
    foundSpace = true;

  if(foundSpace)
    ourInputCount = 0;

  // end locked section
  ale::Logger::Info << "speak() releasing semaphore" << endl;
  SDL_SemPost(ourInputSemaphore);

  if(foundSpace)
  {
    // Lock current buffer. save_sample will unlock it when it gets full.
    SDL_SemWait(ourCurrentWriteBuffer->lock);
    rsynth_phones(rsynth, myInput, strlen(myInput));
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const char *SpeakJet::xlatePhoneme(uInt8 code)
{
  if(code <= 6)
    return " ";

  if(code >= 128 && code <= 199)
    return ourPhonemeTable[ code - 128 ];

  return "";
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 *SpeakJet::getSamples(int *count) {
  static uInt8 contents[OUTPUT_BUFFER_SIZE];
  SDL_sem *lock = myCurrentOutputBuffer->lock;
  SDL_SemWait(lock);
  *count = myCurrentOutputBuffer->items;
  for(int i=0; i<*count; i++)
    contents[i] = myCurrentOutputBuffer->contents[i];
  myCurrentOutputBuffer->items = 0;
  myCurrentOutputBuffer = myCurrentOutputBuffer->next;
  SDL_SemPost(lock);
  return contents;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SpeakJet::chipReady()
{
  return true;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void *SpeakJet::save_sample(void *user_data,
                            float sample,
                            unsigned nsamp,
                            rsynth_t *rsynth)
{
  static long clip_max;
  static float peak;
  short shortSamp;
  uInt8 output;

  darray_t *buf = (darray_t *) user_data;
  shortSamp = clip(&clip_max, sample, &peak);
  darray_short(buf, shortSamp);

  // Convert to 8-bit
  // output = (uInt8)( (((float)shortSamp) + 32768.0) / 256.0 );
  double d = shortSamp + 32768.0;
  output = (uInt8)(d/256.0);
  ale::Logger::Info << "Output sample: " << ((int)(output)) << endl;

  // Put in buffer
  ourCurrentWriteBuffer->contents[ourCurrentWritePosition++] = output;
  ourCurrentWriteBuffer->items = ourCurrentWritePosition;

  // If buffer is full, unlock it and use the next one.
  if(ourCurrentWritePosition == OUTPUT_BUFFER_SIZE)
  {
    SDL_SemWait(ourCurrentWriteBuffer->next->lock);
    SDL_SemPost(ourCurrentWriteBuffer->lock);
    ourCurrentWriteBuffer = ourCurrentWriteBuffer->next;
    ourCurrentWriteBuffer->items = ourCurrentWritePosition = 0;
  }
  return (void *) buf;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void *SpeakJet::flush_samples(void *user_data,
                           unsigned nsamp,
                           rsynth_t *rsynth)
{
  darray_t *buf = (darray_t *) user_data;
  buf->items = 0;
  for (;ourCurrentWritePosition < OUTPUT_BUFFER_SIZE; ourCurrentWritePosition++)
    ourCurrentWriteBuffer->contents[ourCurrentWritePosition] = 0;
  ourCurrentWritePosition = 0;
  SDL_SemPost(ourCurrentWriteBuffer->lock);
  ourCurrentWriteBuffer = ourCurrentWriteBuffer->next; // NOT locked
  return (void *) buf;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
short SpeakJet::clip(long *clip_max, float input, float *peak)
{
  long temp = (long) input;
  float isq = input * input;
#ifdef PEAK
  if (isq > *peak)
    *peak = isq;
#else
  *peak += isq;
#endif 
  if (-temp > *clip_max)
    *clip_max = -temp;
  if (temp > *clip_max)
    *clip_max = temp;
  if (temp < -32767) {
    temp = -32767;
  }
  else if (temp > 32767) {
    temp = 32767;
  }
  return (temp);
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

/*
Table of rsynth phonemes, indexed by SpeakJet phoneme code number.
Table is offset by 128 bytes (ourPhonemeTable[0] is code #128)
see rsynth/phones.def for definitions of rsynth phonemes.
We prefix a "'" to the rsynth phoneme for stress or a ","
for relax.
FIXME: This will need a lot of tweaking, once I get a real
SpeakJet to test with.
*/

const char *SpeakJet::ourPhonemeTable[] = {
//"rsynth phoneme(s) (phones.def)",   // SJ phonemes (p. 16 in SJ manual)
  "i:",   // 128 IY   See, Even, Feed
  "I",    // 129 IH   Sit, Fix, Pin
  "eI",   // 130 EY   Hair, Gate, Beige
  "E",    // 131 EH   Met, Check, Red
  "{",    // 132 AY   Hat, Fast, Fan
  "A:",   // 133 AX   Cotten // maybe "@" instead?
  "V",    // 134 UX   Luck, Up, Uncle
  "Q",    // 135 OH   Hot, Clock, Fox
  "A:",   // 136 AW   Father, Fall
  "oU",   // 137 OW   Comb, Over, Hold
  "U",    // 138 UH   Book, Could, Should
  "u:",   // 139 UW   Food, June
  "m",    // 140 MM   Milk, Famous,
  "n",    // 141 NE   Nip, Danger, Thin
  "n",    // 142 NO   No, Snow, On
  "N",    // 143 NGE  Think, Ping
  "N",    // 144 NGO  Hung, Song
  "l",    // 145 LE   Lake, Alarm, Lapel
  "l",    // 146 LO   Clock, Plus, Hello
  "w",    // 147 WW   Wool, Sweat
  "r",    // 148 RR   Ray, Brain, Over
  "I@",   // 149 IYRR Clear, Hear, Year
  "e@",   // 150 EYRR Hair, Stair, Repair
  "3:",   // 151 AXRR Fir, Bird, Burn
  "A:",   // 152 AWRR Part, Farm, Yarn
  "Qr",   // 153 OWRR Corn, Four, Your [*]
  "eI",   // 154 EYIY Gate, Ate, Ray
  "aI",   // 155 OHIY Mice, Fight, White
  "OI",   // 156 OWIY Boy, Toy, Voice
  "aI",   // 157 OHIH Sky, Five, I
  "j",    // 158 IYEH Yes, Yarn, Million
  "el",   // 159 EHLL Saddle, Angle, Spell [*]
  "U@",   // 160 IYUW Cute, Few // maybe u
  "aU",   // 161 AXUW Brown, Clown, Thousan
  "U@",   // 162 IHWW Two, New, Zoo
  "aU",   // 163 AYWW Our, Ouch, Owl
  "@U",   // 164 OWWW Go, Hello, Snow // maybe "oU"?
  "dZ",   // 165 JH   Dodge, Jet, Savage
  "v",    // 166 VV   Vest, Even,
  "z",    // 167 ZZ   Zoo, Zap
  "Z",    // 168 ZH   Azure, Treasure
  "D",    // 169 DH   There, That, This
  "b",    // 170 BE   Bear, Bird, Beed   ???
  "b",    // 171 BO   Bone, Book Brown ???
  "b",    // 172 EB   Cab, Crib, Web   ???
  "b",    // 173 OB   Bob, Sub, Tub   ???
  "d",    // 174 DE   Deep, Date, Divide   ???
  "d",    // 175 DO   Do, Dust, Dog   ???
  "d",    // 176 ED   Could, Bird   ???
  "d",    // 177 OD   Bud, Food   ???
  "g",    // 178 GE   Get, Gate, Guest,   ???
  "g",    // 179 GO   Got, Glue, Goo   ???
  "g",    // 180 EG   Peg, Wig   ???
  "g",    // 181 OG   Dog, Peg   ???
  "tS",   // 182 CH   Church, Feature, March
  "h",    // 183 HE   Help, Hand, Hair
  "h",    // 184 HO   Hoe, Hot, Hug
  "hw",   // 185 WH   Who, Whale, White [*]
  "f",    // 186 FF   Food, Effort, Off
  "s",    // 187 SE   See, Vest, Plus
  "s",    // 188 SO   So, Sweat      ???
  "S",    // 189 SH   Ship, Fiction, Leash
  "T",    // 190 TH   Thin, month
  "t",    // 191 TT   Part, Little, Sit
  "t",    // 192 TU   To, Talk, Ten
  "ts",   // 193 TS   Parts, Costs, Robots
  "k",    // 194 KE   Can't, Clown, Key
  "k",    // 195 KO   Comb, Quick, Fox ???
  "k",    // 196 EK   Speak, Task ???
  "k",    // 197 OK   Book, Took, October ???
  "p",    // 198 PE   People, Computer
  "p"     // 199 PO   Paw, Copy ???
};

#endif