3
\$\begingroup\$

My code:

import subprocess
import os
import platform

class Speaker:
    """
    Speaker class for differentiating different speech properties.
    """

    def setVoice(self, voice):
        self.voice = voice

    def setWPM(self, wpm):
        self.wpm = wpm

    def setPitch(self, pitch):
        self.pitch = pitch

    def setProperties(self, voice="en", wpm=120, pitch=80):
        self.setVoice(voice)
        self.setWPM(wpm)
        self.setPitch(pitch)

    def __init__(self, voice="en", wpm=120, pitch=80):
        self.prevproc = None
        self.setProperties(voice, wpm, pitch)
        self.executable = os.path.dirname(os.path.abspath(__file__)) +
                              "/espeak.exe" if platform.system() == 'Windows'
                              else os.path.dirname(os.path.abspath(__file__)) + "/espeak"

    def generateCommand(self, phrase):
        cmd = [
            self.executable,
            "--path=.",
            "-v", self.voice,
            "-p", self.pitch,
            "-s", self.wpm,
            phrase
        ]
        cmd = [str(x) for x in cmd]
        return cmd

    def say(self, phrase, wait4prev=False):
        cmd=self.generateCommand(phrase)
        if wait4prev:
            try: self.prevproc.wait()
            except AttributeError: pass
        else:
            try: self.prevproc.terminate()
            except AttributeError: pass
        self.prevproc = subprocess.Popen(cmd, executable=self.executable,
                            cwd=os.path.dirname(os.path.abspath(__file__)))

How can the above code be improved?

Find it on GitHub: https://github.com/sayak-brm/espeak4py

Usage:

import espeak4py
import time

print('Testing espeak4py\n')

mySpeaker = espeak4py.Speaker()

mySpeaker.say('Testing', wait4prev=True)

print('Testing wait4prev')

mySpeaker.say('Hello, World!')
time.sleep(1)
mySpeaker.say('Interrupted!')
time.sleep(3)

mySpeaker.say('Hello, World!')
time.sleep(1)
mySpeaker.say('Not Interrupted.', wait4prev=True)
time.sleep(3)

print('Testing pitch')

myHighPitchedSpeaker = espeak4py.Speaker(pitch=120)
myHighPitchedSpeaker.say('I am a demo of the say function')
time.sleep(4)

print('Testing wpm')

myFastSpeaker = espeak4py.Speaker(wpm=140)
myFastSpeaker.say('I am a demo of the say function')
time.sleep(4)

print('Testing voice')

mySpanishSpeaker = espeak4py.Speaker(voice='es')
mySpanishSpeaker.say('Hola. Como estas?')

print('Testing Completed.')
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

1.PEP8

Naming

In python functions/methods/variables are using underscore as a naming separator, but not a camelCase.

Quotes

You should pick one double or single quotes and use it everywhere that is a general rule, I prefer single ones so I've changed a code as I like, but you might want to use double, just follow one way to do that.

Please read code styling guide aka PEP8

2.Class definition

Methods order

I prefer to have __init__ and __new__ as first methods defined by class, this gives you better image on class and what it does and what it got. This is not a strong rule, but most of people and libraries are following it.

Properties definition

Most of the times you want to have all your properties defined in init, again that is not a strong rule, but in this particular case, I would define them inside __init__ instead of having a separate method to do that. If fact you I don't see much of use of your setProperties method, so I would just modify object attributes directly here if I would need it. Since there are no logics inside those methods except for changing an attribute to a value passed into it.

3.Improvements

I'm not a big fan of try:catch:pass statements you can avoid them with pre-conditions: so your say method can be modified like this:

def say(self, phrase, wait4prev=False):
    cmd = self.generate_command(phrase)
    if self.prevproc:
        if wait4prev:
            self.prevproc.wait()
        else:
            self.prevproc.terminate()
    self.prevproc = subprocess.Popen(cmd, cwd=os.path.dirname(os.path.abspath(__file__)))

Note: you don't need to pass executable explicitly since it's already a part of your cmd param.

So in the end what we have is this:

import os
import platform
import subprocess


class Speaker:
    """
    Speaker class for differentiating different speech properties.
    """
    def __init__(self, voice='en', wpm=120, pitch=80):
        self.prevproc = None
        self.voice = voice
        self.wpm = wpm
        self.pitch = pitch

        executable = 'espeak.exe' if platform.system() == 'Windows' else 'espeak'
        self.executable = os.path.join(os.path.dirname(os.path.abspath(__file__)), executable)

    def generate_command(self, phrase):
        cmd = [
            self.executable,
            '--path=.',
            '-v', self.voice,
            '-p', self.pitch,
            '-s', self.wpm,
            phrase
        ]
        cmd = [str(x) for x in cmd]
        return cmd

    def say(self, phrase, wait4prev=False):
        cmd = self.generate_command(phrase)
        if self.prevproc:
            if wait4prev:
                self.prevproc.wait()
            else:
                self.prevproc.terminate()
        self.prevproc = subprocess.Popen(cmd, cwd=os.path.dirname(os.path.abspath(__file__)))
\$\endgroup\$
1
  • \$\begingroup\$ I learnt a lot about Python today, thanks to you. I'll be sure to read up more about the PEP8 (I didn't know that something like that existed). \$\endgroup\$
    – sbrm1
    Jan 2, 2017 at 17:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.