~ubuntu-branches/debian/squeeze/stella/squeeze

« back to all changes in this revision

Viewing changes to src/emucore/SpeakJet.hxx

  • Committer: Bazaar Package Importer
  • Author(s): Stephen Kitt
  • Date: 2010-07-12 23:49:36 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20100712234936-juawrr3etzhr2qpv
Tags: 3.1.2-1
* New maintainer (closes: #532039).
* New upstream version (closes: #461121):
  - includes launcher (closes: #396058).
* Fix the reference to the X Window System in the description (closes:
  #411815).
* Move to main, DFSG-free ROMs are available (see README.Debian).
* Enhance the package description.
* Drop the libslang2-dev dependency (closes: #560274).
* Remove the Encoding entry from stella.desktop.
* Avoid ignoring errors when cleaning.
* Add ${misc:Depends} to the package dependencies.
* Provide a doc-base file to install the documentation using doc-base.
* Switch to debhelper 7 with a simplified rules file.
* Use autotools-dev to provide updated configuration files.
* Update to Standards-Version 3.9.0:
  - Move to menu section Applications/Emulators.
  - Move the homepage declaration.
* Re-write the manpage.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
//============================================================================
2
 
//
3
 
//   SSSS    tt          lll  lll       
4
 
//  SS  SS   tt           ll   ll        
5
 
//  SS     tttttt  eeee   ll   ll   aaaa 
6
 
//   SSSS    tt   ee  ee  ll   ll      aa
7
 
//      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
8
 
//  SS  SS   tt   ee      ll   ll  aa  aa
9
 
//   SSSS     ttt  eeeee llll llll  aaaaa
10
 
//
11
 
// Copyright (c) 1995-2008 by Bradford W. Mott and the Stella team
12
 
//
13
 
// See the file "license" for information on usage and redistribution of
14
 
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
15
 
//
16
 
// $Id: SpeakJet.hxx,v 1.10 2008/03/31 00:59:30 stephena Exp $
17
 
//============================================================================
18
 
 
19
 
#ifdef SPEAKJET_EMULATION
20
 
 
21
 
#ifndef SPEAKJET_HXX
22
 
#define SPEAKJET_HXX
23
 
 
24
 
/**
25
 
  Emulation of the Magnevation SpeakJet.
26
 
  This is the speech synthesizer chip used in the AtariVox.
27
 
  See AtariVox.hxx and .cxx for AtariVox specifics.
28
 
 
29
 
  This class doesn't attempt 100% accurate emulation of the SpeakJet,
30
 
  as the chip contains a proprietary algorithm that does some complex
31
 
  modelling (in other words, it doesn't just string samples together).
32
 
  For this emulation, I use a library called rsynth, which does something
33
 
  similar (models the human vocal/nasal tract), but is implemented
34
 
  in a totally different way. You might say I'm emulating the spirit
35
 
  of the SpeakJet, not the letter :)
36
 
 
37
 
  Implementation details:
38
 
 
39
 
  Both rsynth and the SpeakJet take a stream of phoneme codes and produce
40
 
  audio output.
41
 
 
42
 
  My SpeakJet class accepts the SpeakJet phonemes, one at a time, and
43
 
  translates them to rsynth phonemes (which are not quite one-to-one
44
 
  equivalent). As each phoneme is translated, it's added to a phoneme
45
 
  buffer.
46
 
 
47
 
  Because of the way rsynth is implemented, it needs a full word's worth
48
 
  of phonemes in its buffer before its speech function is called. This
49
 
  means I'll only call rsynth_phones() when I receive a SpeakJet code that
50
 
  indicates a pause, or end-of-word, or a control code (set parameters
51
 
  or such). This will result in a slight delay (typically, games will
52
 
  send one SJ code per frame).
53
 
 
54
 
  Also due to rsynth's implementation, I have to run it in a thread. This
55
 
  is because rsynth_phones() is a monolithic function that needs a string
56
 
  of phonemes, and takes a while to run (for the word "testing", it takes
57
 
  1/4 second on an Athlon 64 @ 1800MHz). We can't have the emulator pause
58
 
  for a quarter second while this happens, so I'll call rsynth_phones()
59
 
  in a separate thread, and have it fill a buffer from which our main
60
 
  thread will pull as much data as it needs. A typical word will be
61
 
  30-40 thousand samples, and we only need fragsize/2 samples at a time.
62
 
 
63
 
  As always when using threads, there will be locking in play...
64
 
 
65
 
  rsynth's output is always 16-bit samples. This class will have to
66
 
  convert them to 8-bit samples before feeding them to the SDL audio
67
 
  buffer.
68
 
 
69
 
  When using the AtariVox, we'll use SDL stereo sound. The regular TIA
70
 
  sound will come out the left channel, and the speech will come out
71
 
  the right. This isn't ideal, but it's the easiest way to mix the two
72
 
  (I don't want to add an SDL_mixer dependency). The real SpeakJet uses a
73
 
  separate speaker from the 2600 (the 2600 TIA sound comes from the TV,
74
 
  the SJ sound comes from a set of PC speakers), so splitting them to
75
 
  the left and right channels isn't unreasonable... However, it means
76
 
  no game can simultaneously use stereo sound and the AtariVox (for now,
77
 
  anyway).
78
 
 
79
 
  @author  B. Watson
80
 
  @version $Id: SpeakJet.hxx,v 1.10 2008/03/31 00:59:30 stephena Exp $
81
 
*/
82
 
 
83
 
#include "bspf.hxx"
84
 
 
85
 
#include <SDL.h>
86
 
#include <SDL_thread.h>
87
 
#include "rsynth/rsynth.h"
88
 
 
89
 
struct SpeechBuffer;
90
 
 
91
 
 
92
 
enum { INPUT_BUFFER_SIZE = 128 };
93
 
enum { OUTPUT_BUFFER_SIZE = 128 };
94
 
enum { SPEECH_BUFFERS = 1024 };
95
 
static SDL_sem *ourInputSemaphore;
96
 
static rsynth_t *rsynth;
97
 
static darray_t rsynthSamples;
98
 
// phonemeBuffer holds *translated* phonemes (e.g. rsynth phonemes,
99
 
// not SpeakJet phonemes).
100
 
static char phonemeBuffer[INPUT_BUFFER_SIZE];
101
 
// How many bytes are in the input buffer?
102
 
static uInt16 ourInputCount;
103
 
 
104
 
 
105
 
class SpeakJet
106
 
{
107
 
  public:
108
 
    /**
109
 
      Create a new SpeakJet with given buffer size. We use a circular linked
110
 
      list of fixed size, each node being a buffer of bufferSize 8-bit
111
 
      samples.
112
 
 
113
 
      @param bufferSize The size of each output buffer, presumably equal
114
 
                        to fragsize/2.
115
 
    */
116
 
    SpeakJet();
117
 
 
118
 
    virtual ~SpeakJet();
119
 
 
120
 
  public:
121
 
    /**
122
 
      Writes a SpeakJet phoneme (or other code).
123
 
      These are the codes from page 16 of the Speaket User Manual.
124
 
      Not all codes are emulated. In particular, the non-speech noises
125
 
      (200 thru 254) will be treated as silence. Also, not all the
126
 
      control codes will actually work (will document later).
127
 
 
128
 
      @param code The SpeakJet code being written to the emulated chip
129
 
    */
130
 
    void write(uInt8 code);
131
 
 
132
 
    /**
133
 
      Returns a buffer full of 8-bit samples. This should be called every
134
 
      frame or so, or else the older buffers will get overwritten by new
135
 
      data.
136
 
 
137
 
      @param count This will be set to the number of samples that are
138
 
                   returned. Value ranges from 0 to bufferSize.
139
 
    */
140
 
    uInt8 *getSamples(int *count);
141
 
 
142
 
    /**
143
 
      Returns false if the phonemeBuffer is full, true otherwise.
144
 
    */
145
 
    bool chipReady();
146
 
 
147
 
  private:
148
 
    // function that spawns the rsynth thread
149
 
    void spawnThread();
150
 
 
151
 
    // function that the rsynth thread runs...
152
 
    // ...and it has to be a *function*, not a method, because SDL's
153
 
    // written in C. Dammit.
154
 
    static int thread(void *data);
155
 
 
156
 
  private:
157
 
    // These functions are called from the rsynth thread context only
158
 
 
159
 
    // speak() is our locking wrapper for rsynth_phones()
160
 
    static void speak();
161
 
 
162
 
    static void *save_sample(void *user_data,
163
 
                             float sample,
164
 
                             unsigned nsamp,
165
 
                             rsynth_t *rsynth);
166
 
 
167
 
    static void *flush_samples(void *user_data,
168
 
                               unsigned nsamp,
169
 
                               rsynth_t *rsynth);
170
 
 
171
 
    static short clip(long *clip_max, float input, float *peak);
172
 
 
173
 
  private:
174
 
 
175
 
    // True if last code was 20 thru 29
176
 
    bool needParameter;
177
 
 
178
 
    static const char *ourPhonemeTable[];
179
 
 
180
 
    SDL_Thread *ourThread;
181
 
 
182
 
    SpeechBuffer *myCurrentOutputBuffer;
183
 
 
184
 
    // We use this semaphore like so:
185
 
    // Main thread locks it initially
186
 
    // Main thread gathers up phonemes, storing in the input buffer,
187
 
    //  until it hits a pause/space,
188
 
    // then unlocks the semaphore.
189
 
    // The rsynth thread blocks on the semaphore until the main thread
190
 
    // is done feeding data into the buffer.
191
 
    // When the rsynth thread unblocks, it quickly copies the buffer to
192
 
    // a private buffer, then unlocks the semaphore so the main thread
193
 
    // can re-use the buffer.
194
 
 
195
 
    // Note to self: locking decrements the semaphore; unlocking increments
196
 
    // To lock (blocking): SDL_SemWait()
197
 
    // To unlock: SDL_SemPost()
198
 
 
199
 
    // Each output buffer also needs its own locking semaphore:
200
 
    // rsynth thread locks each buffer as it fills it, then unlocks it
201
 
    // when it's done, and moves on to the next buffer in the circular
202
 
    // list (blocking if it's locked).
203
 
 
204
 
    // When the main thread is ready to play audio, it grabs its idea
205
 
    // of what the next buffer is (blocking if it's locked), locks it, mixes
206
 
    // its contents with the TIA audio data (if it's not an empty buffer),
207
 
    // clears the buffer, then unlocks it.
208
 
    // Note that, if the rsynth thread has been sleeping a while, all
209
 
    // the buffers might be empty.
210
 
 
211
 
    // When the rsynth thread runs out of input, it should probably
212
 
    // listen on a condition, so it can be woken up when there's something
213
 
    // to do.
214
 
 
215
 
  private:
216
 
    // Convert a SpeakJet phoneme into one or more rsynth phonemes.
217
 
    // Input range is 0 to 255, but not all codes are supported yet.
218
 
    static const char *xlatePhoneme(uInt8 code);
219
 
 
220
 
};
221
 
 
222
 
// Where our output samples go.
223
 
struct SpeechBuffer
224
 
{
225
 
    SDL_sem *lock;
226
 
    SpeechBuffer *next;
227
 
    int items;
228
 
    uInt8 contents[OUTPUT_BUFFER_SIZE];
229
 
};
230
 
 
231
 
// For now, just a static array of them
232
 
static SpeechBuffer outputBuffers[SPEECH_BUFFERS];
233
 
 
234
 
static SpeechBuffer *ourCurrentWriteBuffer;
235
 
static uInt8 ourCurrentWritePosition;
236
 
 
237
 
#endif
238
 
 
239
 
#endif