dictation 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []