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);
    def playMusic(self, songName, volume=None, playheadTime=0.0, timeReference=TimeReference.ABSOLUTE, numRepeats=0, repeatPeriod=0):
        '''
        Play a piece of music (a sound file) at the SpeakEasy node.
        @param songName: Name of sound file. (See getSongNames()). 
        @type songName: string
        @param volume: Loudness at which to play. Default uses current volume. Else must be in range 0.0 to 1.0
        @type volume: {None | float}
        @param playheadTime: Offset in fractional seconds into where to start the song. Default: start at beginning. 
        @type playheadTime: float
        @param timeReference: If playheadTime is provided, specifies whether the given time is intended as absolute (relative to the
                              beginning of the song), or relative to the current playhead position.
        @type timeReference: TimeReference
        @param numRepeats: Number of times the song is to be repeated after the first time. Use -1 to play forever.
        @type numRepeats: int
        @param repeatPeriod: Time period in fractional seconds to wait between repeats.
        @type repeatPeriod: float
        @raise TypeError: if any parameters are 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));
        if not SpeakeasyUtils.ensureType(playheadTime, int) and not SpeakeasyUtils.ensureType(playheadTime, float):
            raise TypeError("Playhead must be an integer or a float. Was " + str(playheadTime));
        if (timeReference != TimeReference.ABSOLUTE) and (timeReference != TimeReference.RELATIVE):
            raise TypeError("Time reference must be TimeReference.RELATIVE, or TimeReference.ABSOLUTE. Was " + str(timeReference));
        
        musicReqMsg = SpeakEasyMusic();
        musicReqMsg.command = MusicCommands.PLAY;
        musicReqMsg.song_name = songName;
        musicReqMsg.time = playheadTime;
        musicReqMsg.timeReference = timeReference;
        if volume is None:
            musicReqMsg.volume = -1.0;
        else:
            musicReqMsg.volume = volume;

        with self.musicLock:
            self.rosMusicRequestor.publish(musicReqMsg);
            
        # 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:
            musicRepeatThread = RoboComm.MusicReplayDemon(songName, volume, playheadTime, timeReference, numRepeats, repeatPeriod, self);
            self.registerMusicRepeaterThread(musicRepeatThread);
            musicRepeatThread.start();
 def setPlayhead(self, playheadTime, timeReference=TimeReference.ABSOLUTE):
     '''
     Change the music playhead position to a particular time within a song.
     Time may be specified absolute (i.e. relative to the start of the song), 
     or relative to the current playhead. The playhead may be changed during
     playback, or while a song is paused.
     @param playheadTime: New time where to position the playhead.
     @type playheadTime: float
     @param timeReference: TimeReference.ABSOLUTE or TimeReference.RELATIVE
     @type timeReference: TimeReference.
     @raise TypeError: if any parameters are of incorrect type. 
     '''
     if not SpeakeasyUtils.ensureType(playheadTime, int) and not SpeakeasyUtils.ensureType(playheadTime, float): 
         raise TypeError("Playhead must be an int or float indicating seconds. Was " + str(playheadTime));
     
     musicReqMsg = SpeakEasyMusic();
     musicReqMsg.command = MusicCommands.SET_PLAYHEAD;
     musicReqMsg.time = playheadTime;
     musicReqMsg.timeReference = timeReference;
     with self.musicLock:
         self.rosMusicRequestor.publish(musicReqMsg);
 def setMusicVolume(self, vol):
     '''
     Set default volume of music playback.
     @param vol: Loudness value between 0.0 and 1.0.
     @type vol: float
     @raise TypeError: if any parameters are of incorrect type. 
     '''
     if not SpeakeasyUtils.ensureType(vol, float):
         raise TypeError("Volume must be a float between 0.0 and 1.0. Was " + str(vol));
     
     musicReqMsg = SpeakEasyMusic();
     musicReqMsg.command = MusicCommands.SET_VOL;
     musicReqMsg.volume = vol;
     with self.musicLock:
         self.rosMusicRequestor.publish(musicReqMsg);
    def playSound(self, soundName, volume=None, numRepeats=0, repeatPeriod=0):
        '''
        Play a sound effect at the SpeakEasy node.
        @param soundName: Name of the sound effect. (see getSoundEffectNames()).
        @type soundName: string
        @param volume: Loudness for the sound effect. If None, current volume is used. Volume must be in range 0.0 to 1.0
        @type volume: float.
        @param numRepeats: Number of times the sound effect is to be repeated after the first time.  Use -1 to play forever.
        @type numRepeats: int
        @param repeatPeriod: Time period in fractional seconds to wait between repeats.
        @type repeatPeriod: float
        @raise TypeError: if any parameter is of the wrong 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));
        
        soundReqMsg = SpeakEasySound();
        soundReqMsg.command = SoundCommands.PLAY;
        soundReqMsg.sound_name = soundName;
        if volume is None:
            soundReqMsg.volume = -1.0;
        else:
            soundReqMsg.volume = volume;

        with self.soundLock:
            self.rosSoundRequestor.publish(soundReqMsg);
            
        # 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:
            soundRepeatThread = RoboComm.SoundReplayDemon(soundName, self, volume=volume, numRepeats=numRepeats, repeatPeriod=repeatPeriod);
            self.registerSoundRepeaterThread(soundRepeatThread) 
            soundRepeatThread.start();
    def broadcastButtonProgram(text, voice, ttsEngine):

        # Have we broadcast a button program before?
        if RoboComm.ROS_BUTTON_PROGRAM_BROADCASTER is None:
            # No, let's create a ros publisher:
            if SpeakeasyUtils.rosMasterRunning():
		        # Declare us to be a ROS node.
		        # Allow multiple GUIs to run simultaneously. Therefore
		        # the anonymous=True:
                RoboComm.ROS_BUTTON_PROGRAM_BROADCASTER = rospy.Publisher('SpeakEasyButtonProgram', SpeakEasyButtonProgram);
                nodeInfo = rospy.init_node('speakeasy_remote_gui', anonymous=True);
            else:
                raise NotImplementedError("ROS master must be running to broadcast button programs.")
            
        msg = SpeakEasyButtonProgram()
        msg.text = text
        msg.voiceName = voice
        msg.engineName = ttsEngine
        RoboComm.ROS_BUTTON_PROGRAM_BROADCASTER.publish(msg)
 def setSoundVolume(self, volume, soundName=None):
     '''
     Change the sound effect default volume.
     @param volume: New default volume for sound effects. Value must be in range 0.0 to 1.0.
     @type volume: float
     @param soundName: Optionally the name of the sound whose volume is to be changed.
     @type soundName: string
     @raises TypeError: if any parameters are of incorrect type.
     '''
     if not SpeakeasyUtils.ensureType(volume, float):
         raise TypeError("Volume must be a float between 0.0 and 1.0. Was " + str(volume));
     
     soundReqMsg = SpeakEasySound();
     soundReqMsg.command = SoundCommands.SET_VOL;
     if soundName is None:
         soundName = '';
     soundReqMsg.sound_name = soundName;
     soundReqMsg.volume = volume;
     with self.soundLock:
         self.rosSoundRequestor.publish(soundReqMsg);
    def getAvailableLocalSoundEffectFileNames(self):

        #        scriptDir = os.path.dirname(os.path.realpath(__file__));
        #        soundDir = os.path.join(scriptDir, "../../sounds");
        if not os.path.exists(SpeakEasyController.SOUND_DIR):
            raise ValueError("No sound files found.")

        fileAndDirsList = os.listdir(SpeakEasyController.SOUND_DIR)
        fileList = []
        # Grab all usable sound file names:
        for fileName in fileAndDirsList:
            fileExtension = SpeakeasyUtils.fileExtension(fileName)
            if (fileExtension == "wav") or (fileExtension == "ogg"):
                fileList.append(fileName)

        sound_file_basenames = []
        for (i, full_file_name) in enumerate(fileList):
            baseName = os.path.basename(full_file_name)
            SpeakEasyController.soundPaths["SOUND_" + str(i)] = full_file_name
            # Chop extension off the basename (e.g. rooster.wav --> rooster):
            sound_file_basenames.append(os.path.splitext(os.path.basename(full_file_name))[0])
            # Map basename (e.g. 'rooster.wav') to its full file name.
            self.soundPathsFull[baseName] = os.path.join(SpeakEasyController.SOUND_DIR, full_file_name)
        return sound_file_basenames
    def __init__(self):
        '''
        Initialize sound to play at the robot. Initializes
        self.sound_file_names to a list of sound file names
        for use with play-sound calls to the Robot via ROS.
        @raise NotImplementedError: if ROS initialization failed.
        @raise IOError: if SpeakEasy node is online, but service call to it failed. 
        '''
        
        if not ROS_IMPORT_OK:
            raise NotImplementedError("ROS is not installed on this machine.");
        
        self.rosSpeakEasyNodeAvailable = False;
        self.latestCapabilitiesReply = None;
        
        self.nodeStatusLock   = threading.Lock();
        self.textToSpeechLock = threading.Lock();
        self.musicLock        = threading.Lock();
        self.soundLock        = threading.Lock();
        
        # Place to remember threads that repeat voice/sound/music.
        # These lists are used when stopping those threads:
        self.speechThreads = [];
        self.soundThreads  = [];
        self.musicThreads  = [];
        

        # init_node hangs indefinitely if roscore is not running.
        # Therefore: check for that. 
        
        if not SpeakeasyUtils.rosMasterRunning():
            raise NotImplementedError("The roscore process is not running.");
        
        # We now know that ROS is installed, and that roscore is running.
        
        # Publishers of requests for sound effects, music, and text-to-speech:
        self.rosSoundRequestor = rospy.Publisher('speakeasy_sound_req', SpeakEasySound);
        self.rosMusicRequestor = rospy.Publisher('speakeasy_music_req', SpeakEasyMusic);
        self.rosTTSRequestor = rospy.Publisher('speakeasy_text_to_speech_req', SpeakEasyTextToSpeech);
        
        # Declare us to be a ROS node.
        # Allow multiple GUIs to run simultaneously. Therefore
        # the anonymous=True:
        nodeInfo = rospy.init_node('speakeasy_remote_gui', anonymous=True);
        # Don't know why, but without a bit of delay after this init, the first
        # published message will not be transmitted, no matter what the message type:
        rospy.sleep(1.0);
    
        # Wait for Ros SpeakEasy service for a limited time; there might be none:
        waitTime = 4; # seconds
        secsWaited = 0;
        while not self.robotAvailable() and (secsWaited < waitTime):
            rospy.loginfo("No SpeakEasy node available. Keep checking for %d more second(s)..." % (waitTime - secsWaited));
            rospy.sleep(1);
            secsWaited += 1;
        if secsWaited >= waitTime:
            rospy.logerr("Speech/sound/music capabilities service is offline.");
            self.rosSpeakEasyNodeAvailable = False;    
            raise NotImplementedError("Speech capabilities service is offline.");
                
        rospy.loginfo("Speech capabilities service online.");