def stopSaying(self):
        '''
        Stop text-to-speech utterance. No effect if text-to-speech is currently inactive.
        '''
        ttsRequestMsg = SpeakEasyTextToSpeech();
        ttsRequestMsg.command = TTSCommands.STOP;
        with self.textToSpeechLock:
            self.rosTTSRequestor.publish(ttsRequestMsg);

        # Keep this following statement out of the lock! Thread unregistration will 
        # acquire the lock (thus leading to deadlock, if lock is owned here):
        self.killSpeechRepeatThreads(self.speechThreads);
    def say(self, text, voice=None, ttsEngine=None, numRepeats=0, repeatPeriod=0, blockTillDone=False):
        '''
        Given a piece of text, generate corresponding speech at the SpeakEasy node site.
        @param text: Words to speak.
        @type text: string
        @param voice: Name of text-to-speech voice to use.
        @type voice: string
        @param ttsEngine: Name of text-to-speech engine to use.
        @type ttsEngine: string
        @param numRepeats: Number of times the utterance is to be repeated after the first time. Use -1 if forever.
        @type numRepeats: int
        @param repeatPeriod: Time period in fractional seconds to wait between repeated utterances.
        @type repeatPeriod: float
        @param blockTillDone: Request to return immediately, or block until the utterance if finished.
        @type blockTillDone: bool
        @raises TypeError: if one of the parameters is of incorrect type.
        '''
        
        if not SpeakeasyUtils.ensureType(numRepeats, int):
            raise TypeError("Number of repeats must be an integer. Was " + str(numRepeats));
        if not SpeakeasyUtils.ensureType(repeatPeriod, int) and not SpeakeasyUtils.ensureType(repeatPeriod, float):
            raise TypeError("Repeat period must be an integer or a float. Was " + str(repeatPeriod));

        ttsRequestMsg = SpeakEasyTextToSpeech();
        ttsRequestMsg.command = TTSCommands.SAY;
        ttsRequestMsg.text    = text;
        if voice is not None:
            ttsRequestMsg.voiceName = voice;
        else:
            ttsRequestMsg.voiceName = '';
        if ttsEngine is not None:
            ttsRequestMsg.engineName = ttsEngine;
        else:
            ttsRequestMsg.engineName = '';
            
        with self.textToSpeechLock:        
            self.rosTTSRequestor.publish(ttsRequestMsg);
            
        # Keep this block out of the lock! Thread registration will 
        # acquire the lock (thus leading to deadlock, if lock is owned here):
        if numRepeats > 0 or numRepeats == -1:
            speechRepeatThread = RoboComm.SpeechReplayDemon(text, voice, ttsEngine, numRepeats, repeatPeriod, self);
            self.registerSpeechRepeaterThread(speechRepeatThread); 
            speechRepeatThread.start();
            
        if blockTillDone:
            while self.getTextToSpeechBusy():
                rospy.sleep(0.5);