Last active
May 12, 2018 08:44
-
-
Save sharmaeklavya2/9090e1d57dcfa4f69e685a962924ac16 to your computer and use it in GitHub Desktop.
Inverse Dictation (word-by-word, not line-by-line)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
This is an inverse-dictation program. | |
It speaks out word-by-word (not line-by-line) whatever you type. | |
""" | |
import sys | |
import os | |
import subprocess | |
import argparse | |
import threading | |
DEBUG = False | |
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
class Console(object): | |
@staticmethod | |
def init(): | |
Console.isatty = sys.stdin.isatty() | |
if not Console.isatty: | |
Console.getch = lambda: sys.stdin.read(1) | |
else: | |
try: | |
import termios | |
Console.is_unix = True | |
Console.getch = lambda: sys.stdin.read(1) | |
# Disable buffering | |
Console.fd = sys.stdin.fileno() | |
Console.oldattr = termios.tcgetattr(Console.fd) | |
newattr = termios.tcgetattr(Console.fd) | |
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO | |
termios.tcsetattr(Console.fd, termios.TCSANOW, newattr) | |
except ImportError: | |
Console.is_unix = False | |
import msvcrt | |
if sys.version_info.major == 2: | |
Console.getch = msvcrt.getch | |
else: | |
Console.getch = lambda: msvcrt.getch().decode('utf-8') | |
@staticmethod | |
def reset(): | |
if Console.isatty and Console.is_unix: | |
import termios | |
termios.tcsetattr(Console.fd, termios.TCSANOW, Console.oldattr) | |
class SpeakArgs: | |
speed = 100 | |
def run_espeak(s): | |
#print('espeak<{}>'.format(s)) | |
subprocess.run(['espeak', '-s', str(SpeakArgs.speed)], | |
input=s, universal_newlines=True, check=True) | |
class WordState: | |
wordlist = [] | |
lock = threading.Lock() | |
green_flag = threading.Event() | |
exit_type = '' | |
def keep_running(): | |
if WordState.exit_type == 'abort': | |
return False | |
else: | |
with WordState.lock: | |
return (WordState.exit_type != 'exit') or bool(WordState.wordlist) | |
def speak_word(word): | |
with WordState.lock: | |
WordState.wordlist.append(word) | |
WordState.green_flag.set() | |
def consumer_func(): | |
try: | |
while WordState.keep_running(): | |
WordState.green_flag.wait() | |
with WordState.lock: | |
if WordState.wordlist: | |
sentence = ' '.join(WordState.wordlist) | |
WordState.wordlist.clear() | |
else: | |
sentence = None | |
WordState.green_flag.clear() | |
if sentence is not None: | |
run_espeak(sentence) | |
except KeyboardInterrupt: | |
WordState.exit_type = 'abort' | |
if DEBUG: | |
print('consumer_func: exited', file=sys.stderr) | |
EOF_CHARCODE = '\x04' | |
BKSP_CHARCODE = '\x7f' | |
def inverse_dictation(): | |
word_chars = [] | |
consumer_thread = threading.Thread(target=consumer_func) | |
consumer_thread.start() | |
Console.init() | |
try: | |
while not WordState.exit_type: | |
ch = Console.getch() | |
if ch.isalnum() or ch in '.,;!?\'\"': | |
word_chars.append(ch) | |
elif ch in ('', EOF_CHARCODE): | |
WordState.exit_type = 'exit' | |
elif ch == BKSP_CHARCODE: | |
if word_chars: | |
word_chars.pop() | |
print('\b \b', end='', flush=True) | |
elif word_chars: | |
word = ''.join(word_chars) | |
word_chars.clear() | |
speak_word(word) | |
if ch not in ('', EOF_CHARCODE, BKSP_CHARCODE): | |
print(ch, end='', flush=True) | |
except KeyboardInterrupt: | |
WordState.exit_type = 'abort' | |
if DEBUG: | |
print('main: interrupted', file=sys.stderr) | |
finally: | |
print() | |
WordState.green_flag.set() | |
Console.reset() | |
if DEBUG: | |
print('main: exited', file=sys.stderr) | |
def main(): | |
global DEBUG | |
parser = argparse.ArgumentParser(description=__doc__) | |
parser.add_argument('-s', '--speed', type=int, help='Speed in words per minute') | |
parser.add_argument('-d', '--debug', action='store_true', default=False) | |
args = parser.parse_args() | |
if args.speed is not None: | |
SpeakArgs.speed = args.speed | |
if args.debug: | |
DEBUG = True | |
inverse_dictation() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment