[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Text To Speech PYTTS




Hi!

    Below is a nice demo game using the text to speech. It is a maze game
using it. I guess anything can go from here.

From: "kschnee" <kschnee@xxxxxxxxxx>
Sent: Friday, February 29, 2008 11:22 AM
Subject: Re: [pygame] Text To Speech PYTTS


On Fri, 29 Feb 2008 11:16:26 -0500, "FT" <chester_lab@xxxxxxxx> wrote:
>
> To get text to speech software, or PYTTS, this is the download page for
> the Text To Speech engine.
>
> North Carolina Assistive Technology Downloads:
> http://sourceforge.net/project/showfiles.php?group_id=65529#downloads


What features does it have?

I also note that there's a program called PyFlite, which is a version of
the Festival speech synth program developed at CMU.


The game to play using the text to speech:


# use the arrow keys to play. Up arrow moves you forward. Left arrow turns
90 degrees left.
# Right arrow turns 90 degrees right. Down arrow turns around 180 degrees.
Escape quits.
#
# Listen for the hint in the sound of the wind.

# Load Modules
import pygame
import random, sys
import time
import math
import pyTTS

tts = pyTTS.Create()

# go to the program directory
import os
mydir = os.path.dirname(sys.argv[0])
if mydir:
    os.chdir(mydir)

# Constants -- cell marks
BOTTOMWALL = 0
RIGHTWALL  = 1
VISITED    = 2
PATH = 3

# Directions. I have numbered them so adding 1 rotates right and subtracting
one rotates left
NORTH = 0
SOUTH = 2
WEST  = 3
EAST  = 1

class Maze:
    def __init__( self, n_rows, n_cols ):
        """Create a maze with given number of rows and cols.
        member variable longest_path is the sequence of cells you pass
through to get to the end.
        """

        self.n_rows = n_rows
        self.n_cols = n_cols
        self.maze = [ [ [True,True,False,tuple()] for c in range(n_cols) ]
for r in range(n_rows) ]
        self.longest_path = tuple() # will be the longest path through the
maze

    def randomize( self ):
        # Choose a random end point
        currCol = random.randrange(self.n_cols)
        currRow = random.randrange(self.n_rows)

        # The search can be quite deep
        if self.n_rows*self.n_cols > sys.getrecursionlimit():
            sys.setrecursionlimit( self.n_rows*self.n_cols+5 )

        # Recursively Remove Walls - Depth First Algorithm
        self._make_path( currRow, currCol, tuple() )

    #*****************************************

    def _make_path( self, R, C, path, D=None ):
        '''Used internally to generate the maze.'''

        maze = self.maze # speed up a bit

        maze[R][C][PATH] = path

        # track the longest path
        path = ((R,C),) + path
        if len(path) > len(self.longest_path):
            self.longest_path = path

        # Knock out wall between this and previous cell
        maze[R][C][VISITED] = True;

        if   D==NORTH: maze[R]  [C]  [BOTTOMWALL] = False
        elif D==SOUTH: maze[R-1][C]  [BOTTOMWALL] = False
        elif D==WEST:  maze[R]  [C]  [RIGHTWALL]  = False
        elif D==EAST:  maze[R]  [C-1][RIGHTWALL]  = False

        # Build legal directions array
        directions = []
        if R>0            : directions.append(NORTH)
        if R<self.n_rows-1: directions.append(SOUTH)
        if C>0            : directions.append(WEST)
        if C<self.n_cols-1: directions.append(EAST)

        # Shuffle the directions array randomly
        random.shuffle(directions)

        # Call the function recursively
        for dir in directions:
            if dir==NORTH:
                if not maze[R-1][C][VISITED]:
                    self._make_path( R-1,C,path,NORTH )
            elif dir==SOUTH:
                if not maze[R+1][C][VISITED]:
                    self._make_path( R+1,C,path,SOUTH )
            elif dir==EAST:
                if not maze[R][C+1][VISITED]:
                    self._make_path( R,C+1,path,EAST )
            elif dir==WEST:
                if not maze[R][C-1][VISITED]:
                    self._make_path( R,C-1,path,WEST )

    def _find_path( self, R, C, path, goal=None):
        '''Used internally to fill-in the maze.'''

        maze = self.maze # speed up a bit

        maze[R][C][PATH] = path

        # track the longest path
        path = ((R,C),) + path
        if goal and goal == (R,C):
            self.longest_path = path
        elif not goal and len(path) > len(self.longest_path):
            self.longest_path = path

        # note that we've been here
        maze[R][C][VISITED] = True;

        # Build directions array
        directions = []
        if R>0 and not maze[R-1][C][BOTTOMWALL]: directions.append(NORTH)
        if R<self.n_rows-1 and not maze[R][C][BOTTOMWALL]:
directions.append(SOUTH)
        if C>0 and not maze[R][C-1][RIGHTWALL]: directions.append(WEST)
        if C<self.n_cols-1 and not maze[R][C][RIGHTWALL]:
directions.append(EAST)

        # Call the function recursively
        for dir in directions:
            if dir==NORTH:
                if not maze[R-1][C][VISITED]:
                    self._find_path( R-1,C,path,goal )
            elif dir==SOUTH:
                if not maze[R+1][C][VISITED]:
                    self._find_path( R+1,C,path,goal )
            elif dir==EAST:
                if not maze[R][C+1][VISITED]:
                    self._find_path( R,C+1,path,goal )
            elif dir==WEST:
                if not maze[R][C-1][VISITED]:
                    self._find_path( R,C-1,path,goal )

    def wayout(self, row, col):
        '''The direction from this cell to the exit.'''
        path = self.maze[row][col][PATH]
        next = path[0]
        dr = next[0] - row
        dc = next[1] - col
        distance = len(path)
        if dr < 0: return NORTH, distance
        if dr > 0: return SOUTH, distance
        if dc > 0: return EAST, distance
        if dc < 0: return WEST, distance

    def openings(self, row, col):
        '''For each direction indicate if there is an opening'''
        result = [False]*4
        result[NORTH] = row>0 and not self.maze[row-1][col][BOTTOMWALL]
        result[SOUTH] = not self.maze[row][col][BOTTOMWALL]
        result[EAST] = not self.maze[row][col][RIGHTWALL]
        result[WEST] = col>0 and not self.maze[row][col-1][RIGHTWALL]
        return result

    def __str__(self):
        """Return a simple visual representation of the maze in ASCII"""

        if self.longest_path:
            result = str((self.longest_path[0],self.longest_path[-1]))+'\n'
        else:
            result = '((-1,-1),(-1,-1))\n'

        result += '.' + self.n_cols*'_.'
        result += '\n'

        for i in range(self.n_rows):
            result += '|'

            for j in range(self.n_cols):
                if i==self.n_rows-1 or self.maze[i][j][BOTTOMWALL]:
                    result += '_'
                else:
                    result += ' '
                if j==self.n_cols-1 or self.maze[i][j][RIGHTWALL]:
                    result += '|'
                else:
                    result += '.'

            result += '\n'

        return result

    def fromstring(self, init):
        '''Parse the string we built in __str__ to initialize the maze.
           No error checking. Assumes the maze is exactly in printed format.
           Really needs lots of work.
        '''
        lines = init.split('\n')
        start, goal = eval(lines[0])
        lines = lines[2:] # toss the first row
        for i in range(self.n_rows):
            line = lines[i][1:] # toss the first col
            for j in range(self.n_cols):
                if i < self.n_rows-1 and line[2*j] == '_':
                    self.maze[i][j][BOTTOMWALL] = True
                elif line[2*j] == ' ':
                    self.maze[i][j][BOTTOMWALL] = False
                if j < self.n_cols-1 and line[2*j+1] == '|':
                    self.maze[i][j][RIGHTWALL] = True
                elif line[2*j+1] == '.':
                    self.maze[i][j][RIGHTWALL] = False
        self._find_path( goal[0], goal[1], tuple(), start )

directions = ['North', 'East', 'South', 'West'] # names for the directions
steps = [ (-1,0), (0,1), (1,0), (0,-1) ] # increments to step in each
direction

pygame.init()
screen = pygame.display.set_mode((320,240))

purge = pyTTS.tts_purge_before_speak
async = pyTTS.tts_async

def Game(maze):
    facing = NORTH # direction you are facing currently
    position = maze.longest_path[0]
    goal = maze.longest_path[-1]

    windsnd = pygame.mixer.Sound('wind.wav')
    success = pygame.mixer.Sound('success.wav')

    wind = windsnd.play(-1)
    moves = 0

    print maze

    moved = True
    while True:
        # make the sound come from the right direction
        if moved:
            direction, distance = maze.wayout(position[0], position[1])
            rotation = (facing - direction) % 4
            vol = 1/math.sqrt(distance)
            if rotation == 0:
                wind.set_volume(vol)
            elif rotation == 1:
                wind.set_volume(vol, 0)
            elif rotation == 3:
                wind.set_volume(0, vol)
            elif rotation == 2:
                wind.set_volume(vol/2)
            openings = maze.openings(position[0], position[1])

            tts.Speak('In cell %d, %d, facing %s. There is %s here.' %
                      (position[0], position[1], directions[facing],
                       ('a wall', 'an opening')[openings[facing]]), async,
purge)

        event = pygame.event.poll()
        moved = False

        if event.type == pygame.QUIT:
            return 0

        elif event.type == pygame.KEYDOWN:
            key = event.key
            if key == 27: # escape
                return 0

            elif key == ord('s'):
                tts.Speak("Directional Sound Toggle")
                if chan.get_busy():
                    chan.pause()
                else:
                    chan.unpause()
            elif key == ord('d'):
                tts.Speak("Destination: %s" % goal)

            elif key == pygame.K_UP:
                if openings[facing]:
                    step = steps[facing]
                    moves += 1
                    position = (position[0]+step[0], position[1]+step[1])
                    moved = True
                    if position == goal:
                        wind.stop()
                        chan = success.play()
                        tts.Speak("You found the end in %d moves!" % moves,
purge)
                        tts.Speak("Congratulations. You finished the
level!")
                        return 1
                else:
                    tts.Speak("You bump into the wall!")
            elif key == pygame.K_RIGHT: # right arrow
                facing = (facing + 1) % 4
                moved = True
            elif key == pygame.K_DOWN: # down arrow
                facing = (facing + 2) % 4
                moved = True
            elif key == pygame.K_LEFT: # left arrow
                facing = (facing - 1) % 4
                moved = True

        pygame.display.flip()

if __name__ == '__main__':
    n = 3
    while True:
        tts.Speak('This level is %d cells on a side.' % n)
        m = Maze(n,n)
        m.randomize()
        if not Game(m):
            pygame.quit()
            break
        n += 1