Skip to content

Instantly share code, notes, and snippets.

@sharmaeklavya2
Last active May 12, 2018 08:44
Show Gist options
  • Save sharmaeklavya2/9090e1d57dcfa4f69e685a962924ac16 to your computer and use it in GitHub Desktop.
Save sharmaeklavya2/9090e1d57dcfa4f69e685a962924ac16 to your computer and use it in GitHub Desktop.
Inverse Dictation (word-by-word, not line-by-line)
#!/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