diff --git a/stella/src/common/SoundSDL.cxx b/stella/src/common/SoundSDL.cxx index 9e520cf19..0848f5a2f 100644 --- a/stella/src/common/SoundSDL.cxx +++ b/stella/src/common/SoundSDL.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: SoundSDL.cxx,v 1.30 2006-06-09 02:45:11 urchlay Exp $ +// $Id: SoundSDL.cxx,v 1.31 2006-06-11 22:43:55 urchlay Exp $ //============================================================================ #ifdef SOUND_SUPPORT @@ -158,6 +158,7 @@ void SoundSDL::initialize() << " Volume : " << myVolume << endl << " Frag size : " << fragsize << endl << " Frequency : " << myHardwareSpec.freq << endl + << " Format : " << myHardwareSpec.format << endl << " TIA Freq. : " << tiafreq << endl << " Channels : " << myNumChannels << endl << " Clip volume: " << (int)clipvol << endl << endl; @@ -407,6 +408,24 @@ void SoundSDL::callback(void* udata, uInt8* stream, int len) { SoundSDL* sound = (SoundSDL*)udata; sound->processFragment(stream, (Int32)len); + cerr << "SoundSDL::callback(): len==" << len << endl; + + // See if we need sound from the AtariVox + AtariVox *vox = sound->myOSystem->console().atariVox(); + if(vox) + { + // If so, mix 'em together (this is a crappy way to mix audio streams...) + uInt8 *s = stream; + for(int i=0; igetSpeakJet()->getSamples(&count); + if(!count) + break; + SDL_MixAudio(s, voxSamples, OUTPUT_BUFFER_SIZE, SDL_MIX_MAXVOLUME); + s += OUTPUT_BUFFER_SIZE; + } + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/stella/src/emucore/AtariVox.hxx b/stella/src/emucore/AtariVox.hxx index b4f29a8f0..31c584726 100644 --- a/stella/src/emucore/AtariVox.hxx +++ b/stella/src/emucore/AtariVox.hxx @@ -14,7 +14,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: AtariVox.hxx,v 1.2 2006-06-11 07:13:19 urchlay Exp $ +// $Id: AtariVox.hxx,v 1.3 2006-06-11 22:43:55 urchlay Exp $ //============================================================================ #ifndef ATARIVOX_HXX @@ -33,7 +33,7 @@ driver code. @author B. Watson - @version $Id: AtariVox.hxx,v 1.2 2006-06-11 07:13:19 urchlay Exp $ + @version $Id: AtariVox.hxx,v 1.3 2006-06-11 22:43:55 urchlay Exp $ */ class AtariVox : public Controller { @@ -83,6 +83,8 @@ class AtariVox : public Controller void setSystem(System *system); + SpeakJet* getSpeakJet() { return mySpeakJet; } + private: void clockDataIn(bool value); void shiftIn(bool value); diff --git a/stella/src/emucore/Console.cxx b/stella/src/emucore/Console.cxx index b3626737a..58652744c 100644 --- a/stella/src/emucore/Console.cxx +++ b/stella/src/emucore/Console.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Console.cxx,v 1.91 2006-06-11 07:13:19 urchlay Exp $ +// $Id: Console.cxx,v 1.92 2006-06-11 22:43:55 urchlay Exp $ //============================================================================ #include @@ -141,7 +141,7 @@ Console::Console(const uInt8* image, uInt32 size, const string& md5, myControllers[0] = new Joystick(leftjack, *myEvent); } - AtariVox *vox = 0; + vox = 0; // Construct right controller if(right == "BOOSTER-GRIP") diff --git a/stella/src/emucore/Console.hxx b/stella/src/emucore/Console.hxx index 4c3d4c636..0eb79228a 100644 --- a/stella/src/emucore/Console.hxx +++ b/stella/src/emucore/Console.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Console.hxx,v 1.43 2006-03-25 00:34:17 stephena Exp $ +// $Id: Console.hxx,v 1.44 2006-06-11 22:43:55 urchlay Exp $ //============================================================================ #ifndef CONSOLE_HXX @@ -32,12 +32,13 @@ class System; #include "TIA.hxx" #include "Cart.hxx" #include "M6532.hxx" +#include "AtariVox.hxx" /** This class represents the entire game console. @author Bradford W. Mott - @version $Id: Console.hxx,v 1.43 2006-03-25 00:34:17 stephena Exp $ + @version $Id: Console.hxx,v 1.44 2006-06-11 22:43:55 urchlay Exp $ */ class Console { @@ -235,6 +236,8 @@ class Console void togglePFBit() { toggleTIABit(TIA::PF, "PF"); } void enableBits(bool enable); + AtariVox *atariVox() { return vox; } + private: void toggleTIABit(TIA::TIABit bit, const string& bitname, bool show = true); void setDeveloperProperties(); @@ -268,6 +271,8 @@ class Console // A RIOT of my own! (...with apologies to The Clash...) M6532 *myRiot; + AtariVox *vox; + // Indicates whether the console was correctly initialized // We don't really care why it wasn't initialized ... bool myIsInitializedFlag; diff --git a/stella/src/emucore/SpeakJet.cxx b/stella/src/emucore/SpeakJet.cxx index 601b6062b..01d8163b2 100644 --- a/stella/src/emucore/SpeakJet.cxx +++ b/stella/src/emucore/SpeakJet.cxx @@ -13,7 +13,7 @@ // 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.2 2006-06-11 21:49:04 stephena Exp $ +// $Id: SpeakJet.cxx,v 1.3 2006-06-11 22:43:55 urchlay Exp $ //============================================================================ #include "SpeakJet.hxx" @@ -21,6 +21,36 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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; iitems = 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(); } @@ -32,21 +62,86 @@ SpeakJet::~SpeakJet() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void SpeakJet::spawnThread() { + ourInputSemaphore = SDL_CreateSemaphore(1); // 1==unlocked + uInt32 sem = SDL_SemValue(ourInputSemaphore); + cerr << "before SDL_CreateThread(), sem==" << sem << endl; ourThread = SDL_CreateThread(thread, 0); - ourInputSemaphore = SDL_CreateSemaphore(1); + sem = SDL_SemValue(ourInputSemaphore); + cerr << "after SDL_CreateThread(), sem==" << sem << endl; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int SpeakJet::thread(void *data) { cerr << "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); cerr << "rsynth: \"" << rsynthPhones << "\"" << endl; + int len = strlen(rsynthPhones); + + if(ourInputCount + len + 1 >= INPUT_BUFFER_SIZE) { + cerr << "phonemeBuffer is full, dropping" << endl; + return; + } + + uInt32 sem = SDL_SemValue(ourInputSemaphore); + cerr << "write() waiting on semaphore (value " << sem << ")" << endl; + SDL_SemWait(ourInputSemaphore); + cerr << "write() got semaphore" << endl; + for(int i=0; i= INPUT_BUFFER_SIZE - 5) + foundSpace = true; + + if(foundSpace) + ourInputCount = 0; + + // end locked section + cerr << "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)); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -63,8 +158,16 @@ const char *SpeakJet::xlatePhoneme(uInt8 code) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 *SpeakJet::getSamples(int *count) { - *count = 0; - return 0; + 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; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -73,6 +176,82 @@ 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); + cerr << "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); +} + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* diff --git a/stella/src/emucore/SpeakJet.hxx b/stella/src/emucore/SpeakJet.hxx index 6f03e6024..33c8b3fe6 100644 --- a/stella/src/emucore/SpeakJet.hxx +++ b/stella/src/emucore/SpeakJet.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: SpeakJet.hxx,v 1.2 2006-06-11 21:49:04 stephena Exp $ +// $Id: SpeakJet.hxx,v 1.3 2006-06-11 22:43:55 urchlay Exp $ //============================================================================ #ifndef SPEAKJET_HXX @@ -75,7 +75,7 @@ anyway). @author B. Watson - @version $Id: SpeakJet.hxx,v 1.2 2006-06-11 21:49:04 stephena Exp $ + @version $Id: SpeakJet.hxx,v 1.3 2006-06-11 22:43:55 urchlay Exp $ */ #include "bspf.hxx" @@ -84,6 +84,22 @@ #include #include "rsynth/rsynth.h" +class SpeechBuffer; + + +enum { INPUT_BUFFER_SIZE = 128 }; +enum { OUTPUT_BUFFER_SIZE = 128 }; +enum { SPEECH_BUFFERS = 1024 }; +static SDL_sem *ourInputSemaphore; +static rsynth_t *rsynth; +static darray_t rsynthSamples; +// phonemeBuffer holds *translated* phonemes (e.g. rsynth phonemes, +// not SpeakJet phonemes). +static char phonemeBuffer[INPUT_BUFFER_SIZE]; +// How many bytes are in the input buffer? +static uInt16 ourInputCount; + + class SpeakJet { public: @@ -126,28 +142,43 @@ class SpeakJet */ bool chipReady(); + private: // function that spawns the rsynth thread void spawnThread(); // function that the rsynth thread runs... + // ...and it has to be a *function*, not a method, because SDL's + // written in C. Dammit. static int thread(void *data); private: - enum { INPUT_BUFFER_SIZE = 128 }; - uInt16 myBufferSize; + // These functions are called from the rsynth thread context only + + // speak() is our locking wrapper for rsynth_phones() + static void speak(); + + static void *save_sample(void *user_data, + float sample, + unsigned nsamp, + rsynth_t *rsynth); + + static void *flush_samples(void *user_data, + unsigned nsamp, + rsynth_t *rsynth); + + static short clip(long *clip_max, float input, float *peak); + + private: // True if last code was 20 thru 29 bool needParameter; - // phonemeBuffer holds *translated* phonemes (e.g. rsynth phonemes, - // not SpeakJet phonemes). - char phonemeBuffer[INPUT_BUFFER_SIZE]; - uInt8 phonemeCount; // number of phonemes in the phonemeBuffer - static const char *ourPhonemeTable[]; SDL_Thread *ourThread; + SpeechBuffer *myCurrentOutputBuffer; + // We use this semaphore like so: // Main thread locks it initially // Main thread gathers up phonemes, storing in the input buffer, @@ -158,7 +189,10 @@ class SpeakJet // When the rsynth thread unblocks, it quickly copies the buffer to // a private buffer, then unlocks the semaphore so the main thread // can re-use the buffer. - SDL_sem *ourInputSemaphore; + + // Note to self: locking decrements the semaphore; unlocking increments + // To lock (blocking): SDL_SemWait() + // To unlock: SDL_SemPost() // Each output buffer also needs its own locking semaphore: // rsynth thread locks each buffer as it fills it, then unlocks it @@ -180,6 +214,22 @@ class SpeakJet // Convert a SpeakJet phoneme into one or more rsynth phonemes. // Input range is 0 to 255, but not all codes are supported yet. static const char *xlatePhoneme(uInt8 code); + }; +// Where our output samples go. +struct SpeechBuffer +{ + SDL_sem *lock; + SpeechBuffer *next; + int items; + uInt8 contents[OUTPUT_BUFFER_SIZE]; +}; + +// For now, just a static array of them +static SpeechBuffer outputBuffers[SPEECH_BUFFERS]; + +static SpeechBuffer *ourCurrentWriteBuffer; +static uInt8 ourCurrentWritePosition; + #endif