/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program 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.
**
** This program 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.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: GameSpeech.cpp
//
// Created: 10/30/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "wpaudio/attributes.h"
#include "wsys/File.h"
#include "wsys/List.h"
#include "wpaudio/Streamer.h"
#include "wpaudio/Time.h"
#include "wpaudio/Device.h"
#include "wpaudio/Streamer.h"
#define DEFINE_DLG_EVENT_PRIORITY_NAMES
#include "Common/GameAudio.h"
#include "Common/GameSpeech.h"
#include "Common/INI.h"
#include "Common/STLTypedefs.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
#define BASE_DLG_ID ((ID) 5000)
#define BASE_DLG_DIR "Data\\Audio\\Sounds"
#define BASE_DLG_EXT "wav"
#define NUM_DLG_PRIORITIES 5
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//===============================
// Speech
//===============================
/**
* The Speech struct holds all information about a line of dialog. While Speech itself is not
* accessible from outside SpeechInterface, the embedded struct SpeechInfo is accessible to the game code.
* Place data in SpeechInfo that is useful to the game code in determining what speech to play.
*/
//===============================
struct Speech
{
Speech();
~Speech();
ID id; ///< Unquie GDI id
Int index; ///< Speech index
AsciiString name; ///< Logical name for line of speech
Real volume; ///< Mixing level for this line
SpeechInfo info; ///< Game info about this line of dialog (public)
Bool valid; ///< Is this entry have valid data
Int priority; ///< speech priority
Int timeout; ///< speech time out in milliseconds
Int interrupt; ///< speech interrupts current playing speech
};
static void speechFromInfo(SpeechInfo* pInSpeechInfo, Speech* pOutSpeech);
static AsciiString getNextFilenameFromSpeech(Speech* pSpeechToGetFilenameFrom);
//===============================
// SpeechRequest
//===============================
/**
* This is an internal structure used by the SpeechManager. It holds information
* about the playback of a line of dialog.
*/
//===============================
struct SpeechItem
{
enum Flags
{
PAUSED = 0x00000001
};
LListNode nd;
Flags flags;
Speech *speech; ///< What to say
Int priority; ///< Priority of this line
TimeStamp timeout; ///< Time when this event is stale in milliseconds
};
//===============================
// Speaker
//===============================
/**
* Actual implementation of SpeakerInterface
*/
//===============================
class SpeechManager;
class Speaker : public SpeakerInterface
{
AsciiString m_name; ///< name of this speaker
AudioStreamer *m_stream; ///< Audio streamer for this speaker
File *m_file; ///< file we are streaming
Int m_priority; ///< Speaker's priority
Int m_paused; ///< paused level
TimeStamp m_delay; ///< current delay interval
TimeStamp m_delayTime; ///< delay time between speeches
SpeechManager *m_manager; ///< Manager that created use
// info about current playing dialog
SpeechItem *m_currentSpeech; ///< currently playing speech
// list of pending speech items
LList m_pending; ///< list of pending SpeechItems
public:
Speaker();
~Speaker();
virtual void destroy( void ); ///< Delete and free speaker
/// Submits speech to play
virtual void say ( Speech *speech, // speech to say
Int priority, // priority
Int timeout, // time in which to say this line, 0 = infinite
Int interrupt); // whether to interrupt current line
virtual void say ( Char *speechName, // name ofspeech to say
Int priority, // priority
Int timeout, // time in which to say this line, 0 = infinite
Int interrupt ); // whether to interrupt current line
virtual void setPriority ( Int priority ); ///< Set speaker's priority level
virtual void setDelay ( Int delay ); ///< Set speaker's delay between speeches( in milliseconds)
virtual void setBuffering ( Int buffer_time ); ///< Set speaker's amount of buffer (in milliseconds)
virtual Int getPriority ( void ); ///< Get speaker's priority level
virtual Int getDelay ( void ); ///< Get speaker's delay between speeches( in milliseconds)
virtual Int getBuffering ( void ); ///< Get speaker's amount of buffer (in milliseconds)
virtual void pause ( void ); ///< pause speaker
virtual void resume ( void ); ///< resume speaking
virtual void stop ( void ); ///< stop speaking (flushes queue)
virtual void cancel ( Speech *speech ); ///< cancel pending line of dialog
virtual Bool hasSaid ( Speech *speech ); ///< is line of dialog no longer pending
virtual Bool isGoingToSay ( Speech *speech ); ///< check in line of dialog is pending
virtual Bool isTalking ( void ); ///< is speaker talking
virtual Speech* saying ( void ); ///< returns currently spoken speech
virtual void update( void ); ///< Service speaker
void init ( char *name, Int priority, Int btime, Int delay ); ///< initialized speaker for use
void deinit( void );
void setManager( SpeechManager *manager ) { m_manager = manager;};
SpeechItem* firstItem( TimeStamp now = 0 );
SpeechItem* nextItem( SpeechItem *item, TimeStamp now = 0 );
SpeechItem* findItem( Speech *speech );
void flush( void );
};
typedef std::vector VecSpeech;
typedef std::hash_map, rts::equal_to > HashSpeech;
//===============================
// SpeechManager:
//===============================
/**
* Actual implementation od SpeechInterface
*/
//===============================
class SpeechManager: public SpeechInterface
{
protected:
struct AudioAttribs *m_masterAttribs; ///< Attribs for all speech
struct AudioAttribs *m_masterFadeAttribs; ///< Fade attribs for all speech
Real m_volume; ///< Current speech volume
Bool m_on; ///< Is speech turned on?
Int m_count; ///< Number of speeches in the table
struct AudioDeviceTag *m_device; ///< The audio device we were initilaized with
LList m_speakers; ///< list of speakers created by this manager
HashSpeech m_speech; ///< This is a hash table of all speeches we currently have loaded.
VecAsciiString m_speechNames; ///< The purpose of this is to return to someone asking for the name of some element.
VecSpeech m_temporarySpeeches; ///< This is a vector of speeches that are _like_ something in the hash, but differ by priority.
AsciiString getFilenameForPlay( Speech *speech ); ///< Return the filename corresponding to this speech
public:
SpeechManager();
virtual ~SpeechManager();
virtual Bool init( void ); ///< Initlaizes the speech system
virtual void deinit( void ); ///< De-initlaizes the speech system
virtual void update( void ); ///< Services speech tasks. Called by AudioInterface
virtual void reset( void ); ///< Resets the speech system
virtual void loseFocus( void ); ///< Called when application loses focus
virtual void regainFocus( void ); ///< Called when application regains focus
// speech info access
virtual Int numSpeeches( void ); ///< Returns the number of speechs defined
virtual SpeechInfo* getSpeechInfo( Speech *speech ); ///< Returns speech info by speech
virtual Int getSpeechIndex( Speech *speech ); ///< Returns speech index
virtual ID getSpeechID( Speech *speech ); ///< Returns speech ID
virtual Speech* getSpeech( Int index ); ///< Converts index to speech
virtual Speech* getSpeech( ID id ); ///< Converts is to speech
virtual Speech* getSpeech( const Char *speech_name );///< Converts speech name to speech
virtual const Char* getSpeechName( Speech *speech ); ///< Converts speech to speech name
// volume control
virtual void fadeIn( void ); ///< Fade all speech in
virtual void fadeOut( void ); ///< Fade all speech out
virtual void fade( Real fade_value ); ///< Fade all speech by a specified amout (1.0 .. 0.0 )
virtual Bool isFading( void ); ///< Returns whether or not any speech is in the process of a fade
virtual void waitForFade( void ); ///< Returns when all fading has finished
virtual void setVolume( Real new_volume ); ///< Set new volume level for all speech 1.0 (loudest) 0.0 (silent)
virtual Real getVolume( void ); ///< Return current volume level for all speech
/// Speaker factory
virtual SpeakerInterface* createSpeaker ( Char *name,
Int priority,
Int buffer_time,
Int delay);
virtual Speech* addNewSpeech( SpeechInfo* pSpeechToAdd); ///< Create a speech specified by pSpeechToAdd
virtual Speech* addTemporaryDialog( Speech& temporarySpeech ); ///< Speech to be taken over by
// playback control
virtual void stop( void ); ///< Stops all speech that's currently playing.
virtual void pause( void ); ///< Pauses all speech
virtual void resume( void ); ///< Resumes all paused speech
virtual void turnOff( void ); ///< Turns off all speech. No more speech will play
virtual void turnOn( void ); ///< Turns speech on.
virtual Bool waitToStop( Int milliseconds ); ///< Returns when all speech has completely stopped playing
virtual Bool say( Speech* pSpeechToSay); ///< Attempts to add a speech to the speach queue.
virtual Bool say( AsciiString speechName); ///< Attempts to find and add a speech to the speech queue
// info
virtual Bool isOn( void ); ///< Returns whether or not speech is turned on at present
virtual struct AudioAttribs* getMasterAttribs( void ); ///< Returns the master attribute control for all speech
virtual struct AudioAttribs* getMasterFadeAttribs( void );///< Returns the master fade control for all speech
virtual void addDialogEvent(const AudioEventRTS *eventRTS, Speech* speech, AudioEventRTS *returnEvent); ///< Add an event. speech can be NULL
virtual void removeDialogEvent(const AudioEventRTS *eventRTS, AudioEventRTS *eventToUse); ///< remove an event, eventToUse shouldn't be NULL.
void removeSpeaker ( Speaker *speaker );
void addSpeaker ( Speaker *speaker );
// empty string means that this sound wasn't found or some error occurred. CHECK FOR EMPTY STRING.
virtual AsciiString getFilenameForPlayFromAudioEvent( const AudioEventRTS *eventToGetFrom );
};
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// createSpeechInterface
//============================================================================
SpeechInterface* CreateSpeechInterface( void )
{
return NEW SpeechManager;
}
//============================================================================
// Speech::Speech
//============================================================================
Speech::Speech()
: id(INVALID_ID),
valid(FALSE),
volume(TheAudio->VOLUME_MIN)
{
}
//============================================================================
// Speech::~Speech
//============================================================================
Speech::~Speech()
{
}
//============================================================================
// SpeechManager::SpeechManager
//============================================================================
SpeechManager::SpeechManager()
: m_on(FALSE),
m_device(NULL),
m_masterAttribs(NULL),
m_masterFadeAttribs(NULL),
m_speech(NULL),
m_count(0)
{
if( (m_masterFadeAttribs = NEW AudioAttribs) != 0 ) // poolify
{
AudioAttribsInit( m_masterFadeAttribs );
}
if( (m_masterAttribs = NEW AudioAttribs) != 0 )
{
AudioAttribsInit( m_masterAttribs );
}
}
//============================================================================
// SpeechManager::~SpeechManager
//============================================================================
SpeechManager::~SpeechManager()
{
deinit();
delete m_masterAttribs;
delete m_masterFadeAttribs;
}
//============================================================================
// SpeechManager::init
//============================================================================
Bool SpeechManager::init( void )
{
deinit();
if ( m_device )
{
return TRUE;
}
m_device = TheAudio->device();
if( m_device )
{
AudioDeviceAttribsAdd( m_device, m_masterAttribs );
AudioDeviceAttribsAdd( m_device, m_masterFadeAttribs );
}
for (int i = 0; i < NUM_DLG_PRIORITIES; ++i) {
createSpeaker(dlgPriorityNames[i], (i + 1), 3000, 300);
}
m_on = TRUE;
return TRUE;
}
//============================================================================
// SpeechManager::deinit
//============================================================================
void SpeechManager::deinit( void )
{
m_count = 0;
m_speech.clear();
m_speechNames.clear();
for (int i = 0; i < NUM_DLG_PRIORITIES && m_speakers.firstNode(); ++i) {
((Speaker*) m_speakers.firstNode()->item())->destroy();
}
stop();
if( m_device )
{
AudioDeviceAttribsRemove( m_device, m_masterAttribs );
AudioDeviceAttribsRemove( m_device, m_masterFadeAttribs );
m_device = NULL;
}
}
//============================================================================
// SpeechManager::update
//============================================================================
void SpeechManager::update( void )
{
LListNode *node = m_speakers.firstNode();
while ( node )
{
Speaker *speaker = (Speaker *) node->item();
if ( speaker )
{
speaker->update();
}
node = node->next();
}
}
//============================================================================
// SpeechManager::reset
//============================================================================
void SpeechManager::reset( void )
{
}
//============================================================================
// SpeechManager::loseFocus
//============================================================================
void SpeechManager::loseFocus( void )
{
}
//============================================================================
// SpeechManager::regainFocus
//============================================================================
void SpeechManager::regainFocus( void )
{
}
//============================================================================
// SpeechManager::numSpeeches
//============================================================================
Int SpeechManager::numSpeeches( void )
{
return m_count ;
}
//============================================================================
// SpeechManager::getSpeechInfo
//============================================================================
SpeechInfo* SpeechManager::getSpeechInfo( Speech *speech )
{
if( speech )
{
return &speech->info ;
}
return NULL;
}
//============================================================================
// SpeechManager::getSpeechIndex
//============================================================================
Int SpeechManager::getSpeechIndex( Speech *speech )
{
if( speech )
{
return speech->index ;
}
return SpeechInterface::INVALID_SPEECH;
}
//============================================================================
// SpeechManager::getSpeechID
//============================================================================
ID SpeechManager::getSpeechID( Speech *speech )
{
if( speech )
{
return speech->id ;
}
return INVALID_ID;
}
//============================================================================
// SpeechManager::getSpeech
//============================================================================
Speech* SpeechManager::getSpeech( const Char *name )
{
HashSpeech::iterator it = m_speech.find(name);
if (it != m_speech.end()) {
return &(*it).second;
}
return NULL;
}
//============================================================================
// SpeechManager::getSpeech
//============================================================================
Speech* SpeechManager::getSpeech( ID id )
{
if ( m_count == 0 )
{
return NULL;
}
for (int i = 0; i < m_speechNames.size(); ++i) {
Speech* pSpeech = getSpeech(m_speechNames[i].str());
if (pSpeech && pSpeech->id == id) {
return pSpeech;
}
}
return NULL;
}
//============================================================================
// SpeechManager::getSpeech
//============================================================================
Speech* SpeechManager::getSpeech( int index )
{
if (index < 0 || index > m_count) {
return NULL;
}
return getSpeech(m_speechNames[index].str());
}
//============================================================================
// SpeechManager::getSpeechName
//============================================================================
const Char* SpeechManager::getSpeechName( Speech *speech )
{
if( speech )
{
return speech->name.str();
}
return "(null)";
}
//============================================================================
// SpeechManager::fadeIn
//============================================================================
void SpeechManager::fadeIn( void )
{
if(m_masterFadeAttribs )
{
AudioAttribsAdjustVolume( m_masterFadeAttribs, AUDIO_VOLUME_MAX );
}
}
//============================================================================
// SpeechManager::fadeOut
//============================================================================
void SpeechManager::fadeOut( void )
{
if(m_masterFadeAttribs )
{
AudioAttribsAdjustVolume( m_masterFadeAttribs, AUDIO_VOLUME_MIN );
}
}
//============================================================================
// SpeechManager::fade
//============================================================================
void SpeechManager::fade( Real new_volume )
{
if(m_masterFadeAttribs )
{
AudioAttribsAdjustVolume( m_masterFadeAttribs, TheAudio->convertRealVolume( new_volume) );
}
}
//============================================================================
// SpeechManager::isFading
//============================================================================
Bool SpeechManager::isFading( void )
{
if( m_masterFadeAttribs )
{
return !AudioAttribsVolumeAdjusted( m_masterFadeAttribs );
}
return FALSE;
}
//============================================================================
// SpeechManager::waitForFade
//============================================================================
void SpeechManager::waitForFade( void )
{
}
//============================================================================
// SpeechManager::setVolume
//============================================================================
void SpeechManager::setVolume( Real new_volume )
{
m_volume = TheAudio->validateVolume(new_volume);
if( m_masterAttribs )
{
AudioAttribsSetVolume( m_masterAttribs, TheAudio->convertRealVolume( new_volume ));
}
}
//============================================================================
// SpeechManager::getVolume
//============================================================================
Real SpeechManager::getVolume( void )
{
return m_volume;
}
void SpeechManager::stop( void )
{
LListNode *node = m_speakers.firstNode();
while ( node )
{
Speaker *speaker = (Speaker *) node->item();
if ( speaker )
{
speaker->stop();
}
node = node->next();
}
}
//============================================================================
// SpeechManager::pause
//============================================================================
void SpeechManager::pause( void )
{
LListNode *node = m_speakers.firstNode();
while ( node )
{
Speaker *speaker = (Speaker *) node->item();
if ( speaker )
{
speaker->pause();
}
node = node->next();
}
}
//============================================================================
// SpeechManager::resume
//============================================================================
void SpeechManager::resume( void )
{
LListNode *node = m_speakers.firstNode();
while ( node )
{
Speaker *speaker = (Speaker *) node->item();
if ( speaker )
{
speaker->resume();
}
node = node->next();
}
}
//============================================================================
// SpeechManager::turnOff
//============================================================================
void SpeechManager::turnOff( void )
{
stop();
m_on = FALSE;
}
//============================================================================
// SpeechManager::turnOn
//============================================================================
void SpeechManager::turnOn( void )
{
m_on = TRUE;
}
//============================================================================
// SpeechManager::waitToStop
//============================================================================
Bool SpeechManager::waitToStop( Int milliseconds )
{
return TRUE;
}
//============================================================================
// SpeechManager::say
//============================================================================
Bool SpeechManager::say( Speech* pSpeechToSay)
{
// TBD: Find the other conditions
if (m_speakers.isEmpty() || !pSpeechToSay) {
return false;
}
LListNode *node = m_speakers.firstNode();
for (int i = 0; i < pSpeechToSay->priority && node; ++i) {
node = node->next();
}
if (!node) {
return false;
}
Speaker* currentSpeaker = (Speaker*) node->item();
currentSpeaker->say(pSpeechToSay, pSpeechToSay->priority, pSpeechToSay->timeout, pSpeechToSay->interrupt);
return true;
}
//============================================================================
// SpeechManager::say
//============================================================================
Bool SpeechManager::say( AsciiString speechName)
{
Speech* pSpeech = getSpeech(speechName.str());
if (pSpeech) {
return false;
}
return say(pSpeech);
}
//============================================================================
// SpeechManager::isOn
//============================================================================
Bool SpeechManager::isOn( void )
{
return m_on;
}
//============================================================================
// SpeechManager::removeSpeaker
//============================================================================
void SpeechManager::removeSpeaker ( Speaker *speaker )
{
LListNode *node = m_speakers.findItem( speaker );
if ( node )
{
node->remove();
node->destroy();
}
}
//============================================================================
// SpeechManager::addSpeaker
//============================================================================
void SpeechManager::addSpeaker ( Speaker *speaker )
{
// remove it if it is already in the list
removeSpeaker( speaker );
m_speakers.addItem( speaker->getPriority(), speaker );
}
//============================================================================
// SpeechManager::CreateSpeaker
//============================================================================
SpeakerInterface* SpeechManager::createSpeaker ( Char *name, Int priority, Int btime, Int delay )
{
Speaker *speaker = NEW Speaker; // poolify
if ( speaker )
{
speaker->setManager( this );
speaker->init( name, priority, btime, delay );
m_speakers.addItem( priority, speaker );
}
return speaker;
}
//============================================================================
// SpeechManager::addNewSpeech
//============================================================================
Speech* SpeechManager::addNewSpeech( SpeechInfo* pSpeechToAdd )
{
if (!pSpeechToAdd) {
return NULL;
}
const char* entryKey = pSpeechToAdd->m_dialogEvent.str();
if (m_speech.find(entryKey) == m_speech.end()) {
++m_count;
m_speechNames.push_back(pSpeechToAdd->m_dialogEvent);
}
Speech newSpeech;
speechFromInfo(pSpeechToAdd, &newSpeech);
newSpeech.id = (ID) (m_count + BASE_DLG_ID);
m_speech[entryKey] = newSpeech;
return &m_speech[entryKey];
}
//============================================================================
// SpeechManager::addTemporaryDialog
//============================================================================
Speech* SpeechManager::addTemporaryDialog( Speech& temporarySpeech )
{
m_temporarySpeeches.push_back(temporarySpeech);
return &(m_temporarySpeeches.back());
}
//============================================================================
// SpeechManager::getMasterAttribs
//============================================================================
struct AudioAttribs* SpeechManager::getMasterAttribs( void )
{
return m_masterAttribs;
};
//============================================================================
// SpeechManager::getMasterFadeAttribs
//============================================================================
struct AudioAttribs* SpeechManager::getMasterFadeAttribs( void )
{
return m_masterFadeAttribs;
};
//============================================================================
// SpeechManager::getMasterFadeAttribs
//============================================================================
void SpeechManager::addDialogEvent(const AudioEventRTS *eventRTS, Speech* speech, AudioEventRTS *returnEvent)
{
if (!eventRTS) {
return;
}
Speech *useSpeech = speech;
if (!useSpeech) {
useSpeech = getSpeech(eventRTS->m_eventName.str());
}
if (!useSpeech) {
return;
}
if (1 || (eventRTS->m_priorityBoost == 0 &&
eventRTS->m_overrideLoopingBehavior == 0 &&
eventRTS->m_timeOfDay == useSpeech->info.m_timeOfDay)) {
// We can use the default sound, so no need to create and add a new one.
say(useSpeech);
if (returnEvent) {
(*returnEvent) = (*eventRTS);
(*returnEvent).m_speechToPlay = useSpeech;
(*returnEvent).m_isCurrentlyPlaying = true;
}
return;
}
Speech tmpSpeech = (*useSpeech);
tmpSpeech.info.m_priority += eventRTS->m_priorityBoost;
// Can't go less than 0 priority or > NUM_DLG_PRIORITIES
if (tmpSpeech.info.m_priority < 0) {
tmpSpeech.info.m_priority = 0;
} else if (tmpSpeech.info.m_priority > NUM_DLG_PRIORITIES) {
tmpSpeech.info.m_priority = NUM_DLG_PRIORITIES;
}
tmpSpeech.info.m_timeOfDay = eventRTS->m_timeOfDay;
#if 0
// CRASHING BUG IN HERE. NEED TO FIX.
Speech* playSpeech = addTemporaryDialog(tmpSpeech);
if (returnEvent) {
(*returnEvent) = (*eventRTS);
returnEvent->m_speechToPlay = NULL;
}
say(playSpeech);
#endif
}
//============================================================================
// SpeechManager::removeDialogEvent
//============================================================================
void SpeechManager::removeDialogEvent(const AudioEventRTS *eventRTS, AudioEventRTS* eventToUse)
{
if (!eventRTS) {
return;
}
Speech *useSpeech = eventRTS->m_speechToPlay;
if (!useSpeech) {
useSpeech = getSpeech(eventRTS->m_eventName.str());
}
if (!useSpeech) {
return;
}
if (eventToUse) {
(*eventToUse) = (*eventRTS);
eventToUse->m_speechToPlay = useSpeech;
}
Bool isPlaying = true;
LListNode *node = m_speakers.firstNode();
while (node && isPlaying) {
Speaker *pCurrSpeaker = (Speaker*) node->item();
if (pCurrSpeaker->saying() == useSpeech || pCurrSpeaker->isGoingToSay(useSpeech)) {
pCurrSpeaker->cancel(useSpeech);
isPlaying = false;
}
}
if (eventToUse) {
(*eventToUse).m_isCurrentlyPlaying = isPlaying;
}
}
//============================================================================
// SpeechManager::getFilenameForPlayFromAudioEvent
//============================================================================
AsciiString SpeechManager::getFilenameForPlayFromAudioEvent( const AudioEventRTS *eventToGetFrom )
{
if (!eventToGetFrom) {
return AsciiString::TheEmptyString;
}
if (eventToGetFrom->m_eventName.isEmpty()) {
return AsciiString::TheEmptyString;
}
Speech* speech = getSpeech(eventToGetFrom->m_eventName.str());
return getFilenameForPlay(speech);
}
//============================================================================
// SpeechManager::getFilenameForPlay
//============================================================================
AsciiString SpeechManager::getFilenameForPlay( Speech *speech )
{
if (!speech) {
return AsciiString::TheEmptyString;
}
SpeechInfo* speechInfo = getSpeechInfo(speech);
if (!speechInfo) {
return AsciiString::TheEmptyString;
}
Int regularSamples = speechInfo->m_dialogFiles.size();
Int eveningSamples = speechInfo->m_dialogFilesEvening.size();
Int morningSamples = speechInfo->m_dialogFilesMorning.size();
Int nightSamples = speechInfo->m_dialogFilesNight.size();
Int numSamples = regularSamples + eveningSamples + morningSamples + nightSamples;
// using a random number generator, select a random sample from all the samples
// that correspond to this sound name
Int soundToPlay = GameClientRandomValue(0, numSamples - 1);
char name[_MAX_PATH];
if (soundToPlay < regularSamples) {
strcpy(name, speechInfo->m_dialogFiles[soundToPlay].str());
} else if (soundToPlay < (regularSamples + eveningSamples)) {
strcpy(name, speechInfo->m_dialogFilesEvening[soundToPlay - regularSamples].str());
} else if (soundToPlay < (regularSamples + eveningSamples + morningSamples)) {
strcpy(name, speechInfo->m_dialogFilesMorning[soundToPlay - regularSamples - eveningSamples].str());
} else if (soundToPlay < numSamples) {
strcpy(name, speechInfo->m_dialogFilesNight[soundToPlay - regularSamples - eveningSamples - morningSamples].str());
} else {
return AsciiString::TheEmptyString;
}
// @todo this should all go into a function "generateLocalizedSoundName"
Bool localized = FALSE;
if ( name[0] == '$' )
{
localized = TRUE;
char nameTemp[_MAX_PATH];
strcpy(nameTemp, name + 1);
strcpy(name, nameTemp);
}
if ( name[0] == 0 )
{
return NULL;
}
AsciiString path;
char *localDir = "";
if ( localized )
{
localDir = "english\\";
}
path.format( "data\\audio\\sounds\\%s%s.wav", localDir, name);
return path;
}
//============================================================================
//============================================================================
//============================================================================
// Speaker Code
//============================================================================
//============================================================================
//============================================================================
//============================================================================
// Speaker::Speaker
//============================================================================
Speaker::Speaker()
: m_delayTime(300),
m_priority(0),
m_currentSpeech(NULL),
m_file(NULL),
m_stream(NULL)
{
m_name.set("Unnamed");
}
//============================================================================
// Speaker::~Speaker
//============================================================================
Speaker::~Speaker()
{
deinit();
}
//============================================================================
// Speaker::init
//============================================================================
void Speaker::init( char *name, Int priority, Int btime, Int delay )
{
deinit();
m_name.set( name );
m_priority = priority;
m_delayTime = delay;
m_stream = AudioStreamerCreate( TheAudio->device(), btime );
if ( m_stream )
{
AudioStreamerSetName ( m_stream, name );
AudioStreamerSetAttribs ( m_stream, m_manager->getMasterAttribs());
AudioStreamerSetFadeAttribs ( m_stream, m_manager->getMasterFadeAttribs() );
AudioStreamerSetPriority ( m_stream, m_priority );
}
}
//============================================================================
// Speaker::deinit
//============================================================================
void Speaker::deinit( void )
{
stop();
if ( m_stream != NULL )
{
AudioStreamerDestroy ( m_stream );
m_stream = NULL;
}
}
//============================================================================
// Speaker::firstItem
//============================================================================
SpeechItem* Speaker::firstItem( TimeStamp now )
{
LListNode *node = m_pending.firstNode();
SpeechItem *item = NULL;
while ( node )
{
item = (SpeechItem*) node->item();
if ( !now || !item->timeout || item->timeout >= now )
{
break;
}
// this dialog item has timed out so remove it
node = item->nd.next();
item->nd.remove();
delete item;
item = NULL;
}
return item;
}
//============================================================================
// Speaker::nextItem
//============================================================================
SpeechItem* Speaker::nextItem ( SpeechItem *item, TimeStamp now )
{
LListNode *node = NULL;
SpeechItem *next = NULL;
if ( item )
{
node = item->nd.next();
}
while ( node )
{
next = (SpeechItem*) node->item();
if ( !now || !next->timeout || next->timeout >= now )
{
break;
}
// this dialog item has timed out so remove it
node = next->nd.next();
next->nd.remove();
delete next;
next = NULL;
}
return next;
}
//============================================================================
// Speaker::findItem
//============================================================================
SpeechItem* Speaker::findItem( Speech *speech )
{
TimeStamp now = AudioGetTime();
SpeechItem *item = firstItem ( now );
while ( item )
{
if ( item->speech == speech )
{
break;
}
item = nextItem( item, now );
}
return item;
}
//============================================================================
// Speaker::flush
//============================================================================
void Speaker::flush( void )
{
SpeechItem *item;
while ( ( item = firstItem()) != 0 )
{
item->nd.remove();
delete item;
}
}
//============================================================================
// Speaker::destroy
//============================================================================
void Speaker::destroy( void )
{
if ( m_manager )
{
m_manager->removeSpeaker( this );
}
delete this;
}
//============================================================================
// Speaker::say
//============================================================================
void Speaker::say ( char *speechName, Int priority, Int timeout, Int interrupt)
{
Speech *speech = TheAudio->Speeches->getSpeech( speechName );
say( speech, priority, timeout, interrupt );
}
//============================================================================
// Speaker::say
//============================================================================
void Speaker::say ( Speech *speech, Int priority, Int timeout, Int interrupt)
{
if ( speech == NULL || !m_stream || !speech->valid || !m_manager->isOn())
{
return;
}
if ( saying() == speech || isGoingToSay( speech ))
{
return ;
}
SpeechItem *item = NEW SpeechItem; // poolify
if ( item == NULL )
{
return;
}
if ( timeout == 0 )
{
timeout = speech->timeout;
}
if ( priority == 0 )
{
priority = speech->priority;
}
if ( interrupt == 0 )
{
interrupt = speech->interrupt;
}
item->speech = speech;
item->priority = priority;
if ( m_paused )
{
item->flags = SpeechItem::Flags((Int)item->flags | (Int)SpeechItem::PAUSED);
}
if ( timeout )
{
item->timeout = m_paused ? MSECONDS(timeout) : AudioGetTime() + MSECONDS(timeout);
}
item->nd.setItem( item ) ;
item->nd.setPriority( item->priority);
m_pending.add( &item->nd );
if ( interrupt && m_currentSpeech )
{
if ( m_currentSpeech->priority < item->priority )
{
AudioStreamerStop ( m_stream );
m_delay = 0;
}
}
}
//============================================================================
// Speaker::update
//============================================================================
void Speaker::update( void )
{
if ( m_stream == NULL )
{
stop();
return;
}
if ( !AudioStreamerActive ( m_stream ))
{
if ( m_currentSpeech )
{
delete m_currentSpeech;
m_currentSpeech = NULL;
}
}
TimeStamp now = AudioGetTime();
if ( !m_paused && m_currentSpeech == NULL && (AudioStreamerEndTimeStamp ( m_stream ) + m_delay) <= now )
{
SpeechItem *next = firstItem ( now );
if ( next )
{
// ok we can start the next one
next->nd.remove();
m_currentSpeech = next;
AudioStreamerSetMaxVolume ( m_stream, TheAudio->convertRealVolume(next->speech->volume) );
AudioStreamerSetVolume ( m_stream, TheAudio->convertRealVolume(next->speech->volume) );
AudioStreamerOpen( m_stream, getNextFilenameFromSpeech(next->speech).str());
if (AudioStreamerStart( m_stream ) ) {
m_delay = m_delayTime;
}
///@todo write an INI compatible dialog player
/*
// REMOVE_GDF --> this must be converted to INI or something
GDI_Asset *asset = TheGameData->openAsset( next->speech->handle );
if( asset )
{
if ( (m_file = asset->open()))
{
if ( AudioStreamerOpen( m_stream, m_file ))
{
if ( AudioStreamerStart ( m_stream ) )
{
m_delay = m_delayTime;
}
}
}
asset->close();
}
*/
}
}
}
//============================================================================
// Speaker::setPriority
//============================================================================
void Speaker::setPriority ( Int priority )
{
m_priority = priority;
if ( m_manager )
{
m_manager->removeSpeaker( this );
m_manager->addSpeaker( this );
}
if ( m_stream )
{
AudioStreamerSetPriority( m_stream, priority );
}
}
//============================================================================
// Speaker::getPriority
//============================================================================
Int Speaker::getPriority ( void )
{
return m_priority;
}
//============================================================================
// Speaker::setDelay
//============================================================================
void Speaker::setDelay ( Int delay )
{
m_delay = MSECONDS(delay);
}
//============================================================================
// Speaker::getDelay
//============================================================================
Int Speaker::getDelay ( void )
{
return IN_MSECONDS(m_delay);
}
//============================================================================
// Speaker::setBuffering
//============================================================================
void Speaker::setBuffering ( Int buffer_time )
{
DEBUG_ASSERTCRASH( FALSE, ("setBuffering not implemented"));
}
//===============================
// Speaker::getBuffering
//===============================
Int Speaker::getBuffering ( void )
{
DEBUG_ASSERTCRASH( FALSE, ("setBuffering not implemented"));
return 0;
}
//============================================================================
// Speaker::pause
//============================================================================
void Speaker::pause ( void )
{
if ( !m_paused )
{
if ( m_stream )
{
AudioStreamerPause ( m_stream );
}
TimeStamp now = AudioGetTime();
SpeechItem *item = firstItem( now );
while ( item )
{
if ( !( item->flags & SpeechItem::PAUSED ))
{
item->flags = SpeechItem::Flags( (Int) item->flags | (Int) SpeechItem::PAUSED);
if ( item->timeout )
{
item->timeout = item->timeout - now;
}
}
item = nextItem ( item, now );
}
}
m_paused++;
}
//============================================================================
// Speaker::resume
//============================================================================
void Speaker::resume ( void )
{
if ( m_paused == 1 )
{
TimeStamp now = AudioGetTime();
SpeechItem *item = firstItem ( now );
while ( item )
{
if ( ( item->flags & SpeechItem::PAUSED ))
{
item->flags = SpeechItem::Flags( (Int) item->flags & ~((Int) SpeechItem::PAUSED));
if ( item->timeout )
{
item->timeout = now + item->timeout;
}
}
item = nextItem ( item, now );
}
if ( m_stream )
{
AudioStreamerResume ( m_stream );
}
}
m_paused--;
if ( m_paused < 0 )
{
m_paused = 0;
}
}
//============================================================================
// Speaker::stop
//============================================================================
void Speaker::stop ( void )
{
if ( m_stream )
{
AudioStreamerClose( m_stream );
}
if ( m_file )
{
m_file->close();
m_file = NULL;
}
flush();
if ( m_currentSpeech )
{
delete m_currentSpeech;
m_currentSpeech = NULL;
}
}
//============================================================================
// Speaker::cancel
//============================================================================
void Speaker::cancel ( Speech *speech )
{
SpeechItem *item;
while ( ( item = findItem ( speech )) != 0 )
{
item->nd.remove();
delete item;
}
}
//============================================================================
// Speaker::hasSaid
//============================================================================
Bool Speaker::hasSaid ( Speech *speech )
{
return findItem ( speech ) == NULL && ( m_currentSpeech == NULL || m_currentSpeech->speech != speech);
}
//============================================================================
// Speaker::isGoingToSay
//============================================================================
Bool Speaker::isGoingToSay ( Speech *speech )
{
return findItem ( speech ) != NULL;
}
//============================================================================
// Speaker::isTalking
//============================================================================
Bool Speaker::isTalking ( void )
{
return m_currentSpeech != NULL;
}
//============================================================================
// Speaker::saying
//============================================================================
Speech* Speaker::saying ( void )
{
if ( m_currentSpeech )
{
return m_currentSpeech->speech;
}
return NULL;
}
static void speechFromInfo(SpeechInfo *pInSpeechInfo, Speech *pOutSpeech)
{
if (!pInSpeechInfo || !pOutSpeech) {
return;
}
// TBD: Do we need these fields:
// index? info?
pOutSpeech->name = pInSpeechInfo->m_dialogEvent;
pOutSpeech->id = (ID) 1;
pOutSpeech->volume = pInSpeechInfo->m_volume;
pOutSpeech->timeout = 65000;
pOutSpeech->interrupt = pInSpeechInfo->m_interruptable;
pOutSpeech->valid = true;
pOutSpeech->priority = pInSpeechInfo->m_priority;
// Some housekeeping for pInSpeechInfo
pInSpeechInfo->m_internalPlayCount = 0;
pOutSpeech->info = *pInSpeechInfo;
}
static AsciiString getNextFilenameFromSpeech(Speech *pSpeechToPlay)
{
AsciiString returnString;
if (!pSpeechToPlay) {
return returnString;
}
++pSpeechToPlay->info.m_internalPlayCount;
Int indexToPlay = 0;
if (pSpeechToPlay->info.m_sequentialStartIndex < 0) {
indexToPlay = GameClientRandomValue(pSpeechToPlay->info.m_randomStartIndex,
pSpeechToPlay->info.m_dialogFiles.size() - 1);
}
if (pSpeechToPlay->info.m_dialogFiles.size() > 0) {
returnString = AsciiString(BASE_DLG_DIR);
returnString.concat('\\');
returnString.concat(pSpeechToPlay->info.m_dialogFiles[indexToPlay]);
returnString.concat('.');
returnString.concat(BASE_DLG_EXT);
}
return returnString;
}