dictation 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ea842ecf7f99848898ad32ac6accae61d57e59ba
4
+ data.tar.gz: 75f245d58bdfe3b456d8cda2d09327e9668a5c45
5
+ SHA512:
6
+ metadata.gz: 664b01f3d7b97636e2c985428b3641dc184bfea53552b37a89730da128a4d170699482c05317010834e7e835114848835883b36581a680fa2691c995e7d7383a
7
+ data.tar.gz: 36072fec46ad89922e5c797fd8d95a86ccbb938709cbf32787c7ab029f0234741e35a9dd0b2993af98c44bd39970e09aa3f2b3106277959c7d103eb21e32e659
data/bin/dictation ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'dictation'
4
+
5
+ Dictation::ControlCenter.launch
@@ -0,0 +1,97 @@
1
+ require 'optparse'
2
+
3
+ module Dictation
4
+ class Commander
5
+ class << self
6
+ def order(args)
7
+ options = {}
8
+ commands = {
9
+ 'new' => OptionParser.new do |opts|
10
+ opts.banner = set_banner('new')
11
+
12
+ opts.on('-d', '--dictate TTS', 'Specify two-letters language code for dictation') do |tts|
13
+ options[:dictate] = tts.downcase
14
+ end
15
+
16
+ opts.on('-v', '--verify TTS', 'Specify two-letters language code for verification') do |tts|
17
+ options[:verify] = tts.downcase
18
+ end
19
+ end,
20
+
21
+ 'add' => OptionParser.new do |opts|
22
+ opts.banner = set_banner('add')
23
+
24
+ opts.on('-l', '--language LANGUAGE', 'Specify the target language file for adding more words') do |language|
25
+ options[:language] = language
26
+ end
27
+ end,
28
+
29
+ 'dictate' => OptionParser.new do |opts|
30
+ opts.banner = set_banner('dictate')
31
+
32
+ opts.on('-l', '--language LANGUAGE', 'Specify the language for dictation') do |language|
33
+ options[:language] = language
34
+ end
35
+
36
+ opts.on('-b', '--begin WORD', 'Begin with given word') do |word|
37
+ options[:word_begin] = word
38
+ end
39
+
40
+ opts.on('-e', '--end WORD', 'End with given word') do |word|
41
+ options[:word_end] = word
42
+ end
43
+
44
+ opts.on('-s', '--start LINE', Integer, 'Start with given line') do |line|
45
+ options[:line_start] = line
46
+ end
47
+
48
+ opts.on('-f', '--finish LINE', Integer, 'Finish with given line') do |line|
49
+ options[:line_finish] = line
50
+ end
51
+ end,
52
+
53
+ 'verify' => OptionParser.new do |opts|
54
+ opts.banner = set_banner('verify')
55
+
56
+ opts.on('-l', '--language LANGUAGE', 'Specify the language for dictation') do |language|
57
+ options[:language] = language
58
+ end
59
+
60
+ opts.on('-b', '--begin WORD', 'Begin with given word') do |word|
61
+ options[:word_begin] = word
62
+ end
63
+
64
+ opts.on('-e', '--end WORD', 'End with given word') do |word|
65
+ options[:word_end] = word
66
+ end
67
+
68
+ opts.on('-s', '--start LINE', Integer, 'Start with given line') do |line|
69
+ options[:line_start] = line
70
+ end
71
+
72
+ opts.on('-f', '--finish LINE', Integer, 'Finish with given line') do |line|
73
+ options[:line_finish] = line
74
+ end
75
+ end
76
+ }
77
+ valid_cmds = ['new', 'add', 'dictate', 'verify']
78
+ if valid_cmds.include?(args[0])
79
+ commands[args.shift].order!
80
+ options
81
+ else
82
+ puts "Unknown commands [#{args[0]}], only support #{valid_cmds.inspect}."
83
+ end
84
+ end
85
+
86
+ def get_caller_file_name
87
+ caller[-1].match(/\S+\.rb/)
88
+ end
89
+
90
+ def set_banner(cmd)
91
+ "Usage: #{get_caller_file_name} #{cmd} [options]"
92
+ end
93
+ end
94
+
95
+ private_class_method :new, :get_caller_file_name, :set_banner
96
+ end
97
+ end
@@ -0,0 +1,121 @@
1
+ module Dictation
2
+ class ControlCenter
3
+ class << self
4
+ def launch
5
+ if OperatingSystem.supported?
6
+ command = ARGV[0]
7
+ options = Commander.order(ARGV)
8
+ case command
9
+ when 'new'
10
+ execute_new(options)
11
+ when 'add'
12
+ execute_add(options)
13
+ when 'dictate'
14
+ execute_dictate(options)
15
+ when 'verify'
16
+ execute_verify(options)
17
+ end
18
+ else
19
+ puts 'Sorry, your Operating System is not supported. Only Mac OSX has built-in TTS (Text-to-Speech).'
20
+ end
21
+ end
22
+
23
+ def execute_new(options)
24
+ if options[:dictate] && options[:verify]
25
+ File.open("dict_#{options[:dictate]}_#{options[:verify]}.txt", "w") {}
26
+ else
27
+ puts 'Please give both dictate (-d) and verify (-v) languages.'
28
+ end
29
+ end
30
+
31
+ def execute_add(options)
32
+ if options[:language]
33
+ dictionary = get_dictionary(options[:language])
34
+ if dictionary
35
+ words = Typewriter.new.collect
36
+ Serializer.serialize(words, dictionary)
37
+ else
38
+ puts 'Can not find any dictionary file.'
39
+ end
40
+ else
41
+ puts 'Please specify the target language (-l).'
42
+ end
43
+ end
44
+
45
+ def execute_dictate(options)
46
+ execute_speak(options, :dictates)
47
+ end
48
+
49
+ def execute_verify(options)
50
+ execute_speak(options, :verifies)
51
+ end
52
+
53
+ def get_dictionary(language)
54
+ dictionary = nil
55
+ regex = /dict_#{language}_[a-z]{2}\.txt/
56
+ Dir.glob("*").each do |file|
57
+ (dictionary = file) && break if file.match(regex)
58
+ end
59
+ dictionary
60
+ end
61
+
62
+ def get_languages(dictionary)
63
+ regex = /dict_([a-z]{2})_([a-z]{2})\.txt/
64
+ md = dictionary.match(regex)
65
+ [md[1], md[2]]
66
+ end
67
+
68
+ def get_words(options)
69
+ words = []
70
+ dictionary = nil
71
+ language = options[:language]
72
+ word_begin = options[:word_begin]
73
+ word_end = options[:word_end]
74
+ line_start = options[:line_start]
75
+ line_finish = options[:line_finish]
76
+
77
+ if language
78
+ dictionary = get_dictionary(language)
79
+ end
80
+
81
+ if dictionary && (word_begin || line_start)
82
+ if word_end
83
+ words = Serializer.deserialize_a_portion_by_given_word(dictionary, word_begin, word_end)
84
+ elsif line_finish
85
+ words = Serializer.deserialize_a_portion_by_given_line(dictionary, line_start, line_finish)
86
+ else
87
+ words = (word_begin ? Serializer.deserialize_a_portion_by_given_word(dictionary, word_begin) : Serializer.deserialize_a_portion_by_given_line(dictionary, line_start))
88
+ end
89
+ else
90
+ words = Serializer.deserialize(dictionary)
91
+ end
92
+
93
+ words
94
+ end
95
+
96
+ def set_teacher(dictionary)
97
+ languages = get_languages(dictionary)
98
+ tts_dictate = TTS.new(languages[0].intern)
99
+ tts_verify = TTS.new(languages[1].intern)
100
+ Teacher.new(tts_dictate, tts_verify)
101
+ end
102
+
103
+ def execute_speak(options, action)
104
+ if options.has_key?(:language)
105
+ words = get_words(options)
106
+ dictionary = get_dictionary(options[:language])
107
+ if dictionary
108
+ teacher = set_teacher(dictionary)
109
+ teacher.send(action, words)
110
+ else
111
+ puts 'Please check if the given language is correct (two-letters country code) or whether the dictionary file exists.'
112
+ end
113
+ else
114
+ puts 'Please specify target learning language by two-letters country code (-l)'
115
+ end
116
+ end
117
+ end
118
+
119
+ private_class_method :new, :get_dictionary, :get_languages, :get_words, :set_teacher, :execute_speak, :execute_new, :execute_add, :execute_dictate, :execute_verify
120
+ end
121
+ end
@@ -0,0 +1,43 @@
1
+ module Dictation
2
+ class OperatingSystem
3
+ TYPE = {
4
+ lin: 'linux',
5
+ mac: 'osx',
6
+ win: 'windows',
7
+ unknown: 'UNKNOWN'
8
+ }
9
+
10
+ SUPPORTED_TYPE = [TYPE[:mac]]
11
+
12
+ class << self
13
+ def match_os(regex)
14
+ !!RUBY_PLATFORM.match(regex)
15
+ end
16
+
17
+ def is_linux?
18
+ match_os(/linux/)
19
+ end
20
+
21
+ def is_mac?
22
+ match_os(/darwin/)
23
+ end
24
+
25
+ def is_windows?
26
+ match_os(/mswin|mingw|cygwin|bccwin|wince|emx/)
27
+ end
28
+
29
+ def what
30
+ return TYPE[:lin] if is_linux?
31
+ return TYPE[:mac] if is_mac?
32
+ return TYPE[:win] if is_windows?
33
+ return TYPE[:unknown]
34
+ end
35
+
36
+ def supported?
37
+ SUPPORTED_TYPE.include?(what)
38
+ end
39
+ end
40
+
41
+ private_class_method :new, :match_os, :is_linux?, :is_mac?, :is_windows?, :what
42
+ end
43
+ end
@@ -0,0 +1,45 @@
1
+ module Dictation
2
+ class Serializer
3
+ class << self
4
+ def serialize(data, file)
5
+ if Array === data
6
+ data = data.inject('') do |chunk, word|
7
+ chunk += word.to_json + "\n"
8
+ end
9
+ end
10
+ File.open(file, 'a') { |f| f.write(data) }
11
+ end
12
+
13
+ def deserialize(file)
14
+ words = []
15
+ File.open(file).each do |line|
16
+ words.push(JSON.parse(line))
17
+ end
18
+ words
19
+ end
20
+
21
+ def deserialize_a_portion_by_given_word(file, start, stop = nil)
22
+ all_words = deserialize(file)
23
+ idx_start = all_words.index { |x| x.value == start }
24
+ idx_stop = ( stop.nil? ? nil : all_words.index { |x| x.value == stop } )
25
+ idx_stop = all_words.size if idx_stop.nil?
26
+ if !idx_start.nil? && idx_start <= idx_stop
27
+ return all_words[idx_start..idx_stop]
28
+ end
29
+ end
30
+
31
+ def deserialize_a_portion_by_given_line(file, start, stop = nil)
32
+ all_words = deserialize(file)
33
+ unless stop.nil?
34
+ if stop >= start && stop <= all_words.size
35
+ return all_words[start-1..stop-1]
36
+ end
37
+ else
38
+ if start <= all_words.size
39
+ return all_words[start-1..-1]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ module Dictation
2
+ class Teacher
3
+ SLEEP_INTERVAL_WRITE_LETTER = 0.8
4
+ SLEEP_INTERVAL_READ_LETTER = 0.1
5
+ SLEEP_INTERVAL_GENERAL = 0.5
6
+
7
+ def initialize(tts_dictate, tts_verify)
8
+ @tts_dictate = tts_dictate
9
+ @tts_verify = tts_verify
10
+ end
11
+
12
+ def dictates(words)
13
+ words.each do |word|
14
+ vocabulary = word.value
15
+ @tts_dictate.speak(vocabulary)
16
+ sleep(vocabulary.length * SLEEP_INTERVAL_WRITE_LETTER)
17
+ end
18
+ end
19
+
20
+ def verifies(words)
21
+ words.each do |word|
22
+ @tts_dictate.speak(word.value)
23
+ sleep(SLEEP_INTERVAL_GENERAL)
24
+ word.orthography.each do |letter|
25
+ @tts_dictate.speak(letter)
26
+ sleep(SLEEP_INTERVAL_READ_LETTER)
27
+ end
28
+ sleep(SLEEP_INTERVAL_GENERAL)
29
+ @tts_verify.speak(word.translation)
30
+ puts word.value + ' ' + word.translation
31
+ sleep(SLEEP_INTERVAL_GENERAL)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ module Dictation
2
+ class TTS
3
+ def initialize(language, voice = nil)
4
+ set_language_and_voice(language, voice)
5
+ end
6
+
7
+ def get_available_language_and_voice_pairs
8
+ list_of_available_voices = `say -v '?'`.split("\n")
9
+ languages_and_voices = list_of_available_voices.inject({}) do |collection, record|
10
+ matched_results = record.match(/^(.*[^\s])\s+([a-z]{2})_[A-Z]{2}\s+/)
11
+ available_language = matched_results[2].downcase.intern
12
+ available_voice = matched_results[1]
13
+ if collection.has_key?(available_language)
14
+ collection[available_language] << available_voice
15
+ else
16
+ collection[available_language] = [available_voice]
17
+ end
18
+ collection
19
+ end
20
+ end
21
+
22
+ def set_language_and_voice(language, voice)
23
+ system_languages_and_voices = get_available_language_and_voice_pairs
24
+ @language = language
25
+ if system_languages_and_voices.keys.include?(@language)
26
+ if voice
27
+ if system_languages_and_voices[@language].include?(voice)
28
+ @voice = voice
29
+ else
30
+ raise("No available voice found, please check if you have downloaded voice [#{voice}] in System Preferences -> Speech")
31
+ end
32
+ else
33
+ @voice = system_languages_and_voices[@language].first
34
+ end
35
+ else
36
+ raise("No available language found, please check if you have any voice for [#{language}] in System Preferences -> Speech")
37
+ end
38
+ end
39
+
40
+ def speak(text)
41
+ system("say -v #{@voice} #{text} > /dev/null 2>&1")
42
+ end
43
+
44
+ private :get_available_language_and_voice_pairs, :set_language_and_voice
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ module Dictation
2
+ class Typewriter
3
+ attr_accessor :words
4
+
5
+ def initialize
6
+ @words = []
7
+ end
8
+
9
+ def get_input_if_not_nil
10
+ input = gets
11
+ input.nil? ? nil : input.chomp!
12
+ end
13
+
14
+ def record
15
+ paint_print('Word: ', :blue)
16
+ value = get_input_if_not_nil
17
+ paint_print('Translation: ', :yellow)
18
+ translation = get_input_if_not_nil
19
+ @words.push(Word.new(value, translation)) if value && translation
20
+ end
21
+
22
+ def collect
23
+ paint_puts('Please type word and translation one by one, press Enter key to save. When you finish, press Ctrl+C.', :pink)
24
+ Signal.trap('SIGINT') do
25
+ puts ''
26
+ break
27
+ end
28
+ loop do
29
+ record
30
+ end
31
+ paint_puts("=" * 80, :green)
32
+ paint_puts("Input is done.", :green)
33
+ @words
34
+ end
35
+
36
+ def paint(text, color)
37
+ case color
38
+ when :green
39
+ color_code = 32
40
+ when :yellow
41
+ color_code = 33
42
+ when :blue
43
+ color_code = 34
44
+ when :pink
45
+ color_code = 35
46
+ else
47
+ color_code = 0
48
+ end
49
+ "\e[#{color_code}m#{text}\e[0m"
50
+ end
51
+
52
+ def paint_print(text, color)
53
+ print(paint(text, color))
54
+ end
55
+
56
+ def paint_puts(text, color)
57
+ puts(paint(text, color))
58
+ end
59
+
60
+ private :get_input_if_not_nil, :record, :paint, :paint_print, :paint_puts
61
+ end
62
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'json'
4
+
5
+ module Dictation
6
+ class Word
7
+ attr_accessor :value, :translation
8
+
9
+ def initialize(value, translation = nil)
10
+ @value = value
11
+ @translation = translation
12
+ end
13
+
14
+ def orthography
15
+ decompose(@value)
16
+ end
17
+
18
+ def decompose(word)
19
+ word.split(//).map! do |x|
20
+ normalize(x)
21
+ end
22
+ end
23
+
24
+ def normalize(letter)
25
+ letter = letter.downcase.chomp
26
+ case letter
27
+ when 'ß'
28
+ 'ss'
29
+ else
30
+ letter
31
+ end
32
+ end
33
+
34
+ def to_json(*args)
35
+ {
36
+ 'json_class' => self.class.name,
37
+ 'data' => [ @value, @translation ]
38
+ }.to_json(*args)
39
+ end
40
+
41
+ class << self
42
+ def json_create(object)
43
+ new(*object['data'])
44
+ end
45
+ end
46
+
47
+ private :decompose, :normalize
48
+ end
49
+ end
data/lib/dictation.rb ADDED
@@ -0,0 +1 @@
1
+ Dir[(File.expand_path(File.dirname(__FILE__)) + "/dictation/*.rb")].each { |file| require file }
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dictation
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jing Li
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-27 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Practice foreign language by listening and writing (only for Mac OS X).
14
+ email:
15
+ - thyrlian@gmail.com
16
+ executables:
17
+ - dictation
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/dictation.rb
22
+ - lib/dictation/operating_system.rb
23
+ - lib/dictation/word.rb
24
+ - lib/dictation/serializer.rb
25
+ - lib/dictation/tts.rb
26
+ - lib/dictation/teacher.rb
27
+ - lib/dictation/typewriter.rb
28
+ - lib/dictation/commander.rb
29
+ - lib/dictation/control_center.rb
30
+ - bin/dictation
31
+ homepage: http://github.com/thyrlian/dictation
32
+ licenses:
33
+ - MIT
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.1.4
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: Practice foreign language by listening and writing.
55
+ test_files: []