Practice acting using Python


, , , , , , , , , , , , ,

Arkwood wants to be an actor. ‘But I keep fluffing my lines,’ he cried.

I told him I could help. I added a new feature to SaltwashAR – the Python Augmented Reality application – so that the robots can help him learn his role as The Dude in the stage production of the film The Big Lebowski.

Sporty Robot will play the part of Walter Sobchak, a Vietnam veteran and the Dude’s best friend and bowling teammate.

Once the robot has delivered his line – along with an emotional outburst such as tearful sadness or head-spinning anger – it’s Arkwood’s turn.

If Arkwood fluffs any of his lines then the robot will let him know at the end of practice.

‘You’ll soon get the role of The Dude to perfection,’ I said.

Okay, let’s see how Arkwood got on in the role of The Dude using snippets of The Big Lebowski script:

Terrific! Arkwood puts in a sterling performance, worthy of winning the lead role in the stage show.

Okay, not all his delivered lines have been correctly understood by Google Speech Recognition (which also chooses to put asterisks in place of swear words, goddamnit!). Arkwood needs to improve his diction. The robot tells him that he has fluffed his lines six times.

So how did we add the Acting feature to SaltwashAR? Let’s take a peek at the Python code:

from features.base import *
from constants import *

class Acting(Feature, Speaking, Emotion):

    BOT_TAG = '[walter]'
    HUMAN_TAG = '[dude]'

    def __init__(self, text_to_speech, speech_to_text):
        Speaking.__init__(self, text_to_speech)
        self.speech_to_text = speech_to_text
        self.script = self._load_script()
        self.fluffed_lines_count = 0

    # load script
    def _load_script(self):
        with open('scripts/features/acting/script.txt') as script_file:
            return script_file.readlines()

The Acting feature inherits from the Feature base class, which provides threading (all features run in threads so as not to block the main application process from rendering to screen). We also inherit from the Speaking base class (to let the robot’s mouth move when he speaks) and the Emotion base class (to let the robot be happy, sad and angry).

The class __init__ method is passed Text To Speech and Speech To Text parameters, so that Arkwood can have a dialogue with the robot. We have class variables to store the script, as well as a count of the times Arkwood fluffs his lines.

The _load_script private method is called from the __init__ method. It opens the script text file and loads all lines of the script. Here’s a snippet of the script that it loads:

[walter]Take the ringer. I'll drive.
[dude]The what
[walter]The ringer! The ringer, Dude! Have they called yet?
[dude]What the hell is this
[walter][happy]My dirty undies. Laundry, Dude. The whites.
[dude]Walter I'm sure there's a reason you brought your dirty undies
[walter]That's right, Dude. The weight. The ringer can't look empty.
[dude]Walter what the f*** are you thinking

As you can see, the script is marked with tags so that we know which line belongs to The Dude (Arkwood) and which belongs to Walter Sobchak (Sporty Robot). Notice that the robot also has tags for his emotional outbursts e.g. [happy]

Next up is the _thread method, where certain things come to light (as The Dude would say)…

# run thread
def _thread(self, args):
    # check for end of script 
    if not self.script: return

    # get next line from script
    line = self.script[0].replace('\n', '')
    tag_end_index = line.rfind(']')
    tags = line[:tag_end_index + 1]
    words = line[tag_end_index + 1:]

    # bot delivers a line from script
    if self.BOT_TAG in tags:

        if HAPPY in tags:
        elif SAD in tags:
        elif ANGRY in tags:
    # human delivers a line from script       
    elif self.HUMAN_TAG in tags:
        human_words = self.speech_to_text.convert()

        if not human_words or human_words.lower() != words.lower():
            self.fluffed_lines_count += 1

    # remove line from script
    del self.script[0]

    # if end of script, inform user of fluffed lines
    if not self.script:
        self._text_to_speech("You fluffed your lines {} times".format(self.fluffed_lines_count))

Assuming that we are not at the end of the script, we fetch the next line and separate the tags from the words.

If the tags contain [walter] then the robot delivers a line, using Text To Speech through the computer speakers. If the tags also contain an emotion (happy, sad, angry) then the robot displays the emotion at the end of its spiel.

Otherwise, if the tags contain [dude] then Arkwood delivers his line, using Speech To Text through the computer microphone. If he does not state his line exactly as defined in the script, the number of fluffed lines is incremented by 1. Of course, we can help Arkwood by tweaking the script so that it’s in the Google Speech Recognition format e.g. putting asterisks in place of swear words, dropping the punctuation marks, etc.

The current line is then removed from the script, so that when the thread is run again the next line is served up. And so on goes the performance between Arkwood and Sporty Robot.

Only once we get to the end of the script, when there are no more lines left, does the robot tell Arkwood how many times he fluffed his lines.

‘You done great! Don’t worry about the fluffed lines,’ I sympathised with my Belgian buddy. ‘I’m sure you’ll get the starring role in The Big Lebowski stage production.’

Arkwood suddenly burst out, ‘Fuck your sympathy! I don’t need your sympathy, man, I need my fucking Johnson!’

It took me a while – and a stiff brandy – to realise that he was still in character.


Please check out the SaltwashAR Wiki for details on how to install and help develop the SaltwashAR Python Augmented Reality application.


Get every new post delivered to your Inbox.

Join 89 other followers