cw 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/Gemfile +11 -0
  4. data/LICENSE +21 -0
  5. data/README.md +21 -0
  6. data/Rakefile +1 -0
  7. data/audio/audio_output.wav +0 -0
  8. data/audio/audio_output.wav0000.mp3 +0 -0
  9. data/audio/dash.wav +0 -0
  10. data/audio/dot.wav +0 -0
  11. data/audio/e_space.wav +0 -0
  12. data/audio/space.wav +0 -0
  13. data/cw.gemspec +18 -0
  14. data/daily.rb +182 -0
  15. data/data/text/abbreviations.txt +1 -0
  16. data/data/text/common_words.txt +90 -0
  17. data/data/text/cw_conversation.txt +1 -0
  18. data/data/text/most_common_words.txt +1 -0
  19. data/data/text/progress.txt +1 -0
  20. data/data/text/q_codes.txt +1 -0
  21. data/data/text/tom_sawyer.txt +6477 -0
  22. data/example.rb +139 -0
  23. data/lib/cw.rb +111 -0
  24. data/lib/cw/alphabet.rb +29 -0
  25. data/lib/cw/audio_player.rb +63 -0
  26. data/lib/cw/book.rb +239 -0
  27. data/lib/cw/book_details.rb +47 -0
  28. data/lib/cw/cl.rb +112 -0
  29. data/lib/cw/cw_dsl.rb +211 -0
  30. data/lib/cw/cw_encoding.rb +56 -0
  31. data/lib/cw/cw_params.rb +29 -0
  32. data/lib/cw/cw_threads.rb +36 -0
  33. data/lib/cw/file_details.rb +13 -0
  34. data/lib/cw/key_input.rb +53 -0
  35. data/lib/cw/monitor.rb +36 -0
  36. data/lib/cw/monitor_keys.rb +37 -0
  37. data/lib/cw/numbers.rb +29 -0
  38. data/lib/cw/print.rb +137 -0
  39. data/lib/cw/process.rb +11 -0
  40. data/lib/cw/progress.rb +27 -0
  41. data/lib/cw/randomize.rb +73 -0
  42. data/lib/cw/repeat_word.rb +91 -0
  43. data/lib/cw/rss.rb +71 -0
  44. data/lib/cw/sentence.rb +78 -0
  45. data/lib/cw/speak.rb +11 -0
  46. data/lib/cw/spoken.rb +11 -0
  47. data/lib/cw/str.rb +67 -0
  48. data/lib/cw/stream.rb +161 -0
  49. data/lib/cw/test_letters.rb +52 -0
  50. data/lib/cw/test_words.rb +59 -0
  51. data/lib/cw/tester.rb +221 -0
  52. data/lib/cw/timing.rb +92 -0
  53. data/lib/cw/tone_generator.rb +225 -0
  54. data/lib/cw/voice.rb +16 -0
  55. data/lib/cw/words.rb +182 -0
  56. data/read_book.rb +33 -0
  57. data/test/run_tests_continuously.rb +4 -0
  58. data/test/test_cw.rb +527 -0
  59. data/test/test_stream.rb +401 -0
  60. metadata +102 -0
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ class TestLetters < FileDetails
4
+
5
+ include Tester
6
+
7
+ def initialize
8
+ @print_letters = Params.print_letters
9
+ super()
10
+ # print_test_advice
11
+ end
12
+
13
+ def print_test_advice ; print.print_advice('Test Letters') ; end
14
+
15
+ def print_words words
16
+ timing.init_char_timer
17
+ (words.to_s + space).each_char do |letr|
18
+ process_letter letr
19
+ stream.add_char letr
20
+ loop do
21
+ process_word_maybe
22
+ break if timing.char_delay_timeout?
23
+ end
24
+ print.prn letr if print_letters?
25
+ break if quit?
26
+ end
27
+ end
28
+
29
+ def process_input_word_maybe
30
+ if @word_to_process
31
+ stream.match_first_active_element @process_input_word # .strip
32
+ @process_input_word = @word_to_process = nil
33
+ end
34
+ end
35
+
36
+ def build_word_maybe
37
+ @input_word ||= empty_string
38
+ @input_word << key_chr if is_relevant_char?
39
+ move_word_to_process if is_relevant_char?
40
+ end
41
+
42
+ def process_letter letr
43
+ letr.downcase!
44
+ sleep_char_delay letr
45
+ end
46
+
47
+ def print_marked_maybe
48
+ @popped = stream.pop_next_marked
49
+ print.char_result(@popped) if(@popped && ! print_letters?)
50
+ end
51
+
52
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ class TestWords < FileDetails
4
+
5
+ include Tester
6
+
7
+ def initialize
8
+ @print_letters = Params.print_letters
9
+
10
+ super()
11
+
12
+ # print_test_advice
13
+ end
14
+
15
+ def print_test_advice ; print.print_advice('Test Words') ; end
16
+
17
+ def print_failed_exit_words
18
+ until stream.stream_empty?
19
+ print.prn_red stream.pop[:value] + ' '
20
+ end
21
+ end
22
+
23
+ def print_words words
24
+ timing.init_char_timer
25
+ (words.to_s + space).each_char do |letr|
26
+ process_letter letr
27
+ loop do
28
+ process_space_maybe letr
29
+ process_word_maybe
30
+ break if timing.char_delay_timeout?
31
+ end
32
+ print.prn letr if print_letters?
33
+ break if quit?
34
+ end
35
+ end
36
+
37
+ def process_input_word_maybe
38
+ if @word_to_process
39
+ stream.match_last_active_element @process_input_word.strip
40
+ @process_input_word = @word_to_process = nil
41
+ end
42
+ end
43
+
44
+ def build_word_maybe
45
+ @input_word ||= empty_string
46
+ @input_word << key_chr if is_relevant_char?
47
+ move_word_to_process if complete_word?
48
+ end
49
+
50
+ def process_letter letr
51
+ current_word.process_letter letr
52
+ sleep_char_delay letr
53
+ end
54
+
55
+ def print_marked_maybe
56
+ @popped = stream.pop_next_marked
57
+ print.results(@popped) if(@popped && ! print_letters?)
58
+ end
59
+ end
data/lib/cw/tester.rb ADDED
@@ -0,0 +1,221 @@
1
+ # encoding: utf-8
2
+
3
+ module Tester
4
+
5
+ def quit? ; @quit ; end
6
+ def quit ; @quit = true ; end
7
+ def global_quit? ; @global_quit ; end
8
+ def global_quit ; @global_quit = true ; end
9
+ def print ; @print ||= Print.new ; end
10
+ def timing ; @timing ||= Timing.new ; end
11
+ def audio ; @audio ||= AudioPlayer.new ; end
12
+ def kill_threads ; @threads.kill ; end
13
+ def space ; ' ' ; end
14
+ def empty_string ; '' ; end
15
+ def spawn_play(cmd) ; Process.spawn(cmd) ; end
16
+ def start_sync ; @start_sync = true ; end
17
+ def get_key_input ; key_input.read ; end
18
+ def key_chr ; key_input.char ; end
19
+ def key_input ; @key_input ||= KeyInput.new ; end
20
+ def is_relevant_char? ; key_input.is_relevant_char? ; end
21
+ def quit_key_input? ; key_input.quit_input? ; end
22
+ def stream ; @stream ||= Stream.new ; end
23
+ def reset_stdin ; key_input.reset_stdin ; end
24
+ def current_word ; @current_word ||= CurrentWord.new ; end
25
+ def init_char_timer ; timing.init_char_timer ; end
26
+ def audio_still_playing? ; audio.still_playing? ; end
27
+
28
+ def add_space words
29
+ str = ''
30
+ words.to_array.collect { |word| str << word + space}
31
+ str
32
+ end
33
+
34
+ def audio_play
35
+ audio.convert_words add_space @words
36
+ start_sync
37
+ audio.play
38
+ end
39
+
40
+ def play_words_until_quit
41
+ audio_play
42
+ play_words_exit unless @print_letters
43
+ end
44
+
45
+ def play_words_exit
46
+ timing.init_play_words_timeout
47
+ loop do
48
+ break if quit?
49
+ break if timing.play_words_timeout?
50
+ sleep 0.01
51
+ end
52
+ end
53
+
54
+ def process_word_maybe
55
+ print_marked_maybe
56
+ process_input_word_maybe
57
+ end
58
+
59
+ def play_words_thread
60
+ play_words_until_quit
61
+ print "\n\rplay has quit " if @debug
62
+ end
63
+
64
+ def print_words_thread
65
+ print_words_until_quit
66
+ kill_threads
67
+ print "\n\rprint has quit " if @debug
68
+ end
69
+
70
+ def print_words_until_quit
71
+ sync_with_audio_player
72
+ print_words @words
73
+ print_words_exit unless @print_letters
74
+ quit
75
+ end
76
+
77
+ def print_failed_exit_words
78
+ until stream.stream_empty?
79
+ print.prn_red stream.pop[:value]
80
+ end
81
+ end
82
+
83
+ def print_words_exit
84
+ timing.init_print_words_timeout
85
+ loop do
86
+ process_word_maybe
87
+ break if stream.stream_empty?
88
+ break if timing.print_words_timeout?
89
+ break if quit?
90
+ sleep 0.01
91
+ end
92
+ @failed = true unless stream.stream_empty?
93
+ print_failed_exit_words unless @repeat_word
94
+ end
95
+
96
+ def audio_stop
97
+ audio.stop if audio_still_playing?
98
+ end
99
+
100
+ def wait_player_startup_delay
101
+ audio.startup_delay
102
+ end
103
+
104
+ def sync_with_audio_player
105
+ wait_for_start_sync
106
+ wait_player_startup_delay
107
+ end
108
+
109
+ def check_quit_key_input
110
+ if quit_key_input?
111
+ quit
112
+ global_quit
113
+ audio_stop
114
+ end
115
+ end
116
+
117
+ def push_letter_to_current_word letr
118
+ current_word.push_letter letr
119
+ end
120
+
121
+ def get_word_last_char
122
+ @input_word.split(//).last(1).first
123
+ end
124
+
125
+ def wait_for_no_word_process
126
+ while @word_to_process
127
+ sleep 0.1
128
+ end
129
+ end
130
+
131
+ def complete_word?
132
+ get_word_last_char == space
133
+ end
134
+
135
+ def move_word_to_process
136
+ wait_for_no_word_process
137
+ @process_input_word, @input_word = @input_word, ''
138
+ @word_to_process = true
139
+ end
140
+
141
+ def start_sync?
142
+ if @start_sync
143
+ @start_sync = nil
144
+ true
145
+ else
146
+ nil
147
+ end
148
+ end
149
+
150
+ def sleep_char_delay letr
151
+ timing.append_char_delay letr, Params.wpm, Params.effective_wpm
152
+ end
153
+
154
+ def wait_for_start_sync
155
+ until start_sync?
156
+ sleep 0.001
157
+ break if quit?
158
+ end
159
+ end
160
+
161
+ def process_space_maybe letr
162
+ if letr == space
163
+ stream.add_word current_word.strip
164
+ current_word.clear
165
+ letr.clear
166
+ print.prn space if print_letters?
167
+ end
168
+ end
169
+
170
+ def print_letters?
171
+ @print_letters && ! quit?
172
+ end
173
+
174
+ def sync_with_play
175
+ loop do
176
+ break if sentence_index_current?
177
+ break if quit?
178
+ sleep 0.015
179
+ end
180
+ end
181
+
182
+ def sync_with_print
183
+ loop do
184
+ make_sentence_index_current if ! sentence_index_current?
185
+ break if sentence_index_current?
186
+ break if quit?
187
+ sleep 0.015
188
+ end
189
+ end
190
+
191
+ def monitor_keys_thread
192
+ monitor_keys
193
+ print "\n\rmonitor keys has quit " if @debug
194
+ end
195
+
196
+ def thread_processes
197
+ [:monitor_keys_thread,
198
+ :play_words_thread,
199
+ :print_words_thread]
200
+ end
201
+
202
+ def run words
203
+ @words = words
204
+ words.double_words if Params.double_words
205
+ @threads = CWThreads.new(self, thread_processes)
206
+ @threads.run
207
+ reset_stdin
208
+ print.newline
209
+ end
210
+
211
+ def monitor_keys
212
+ loop do
213
+ get_key_input
214
+ check_quit_key_input
215
+ break if quit?
216
+ # check_sentence_navigation key_chr
217
+ build_word_maybe
218
+ end
219
+ end
220
+
221
+ end
data/lib/cw/timing.rb ADDED
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ class Timing
4
+
5
+ attr_accessor :delay_time
6
+ attr_accessor :start_time
7
+
8
+ def initialize
9
+ @delay_time = 0.0
10
+ @cw_encoding = CwEncoding.new
11
+ end
12
+
13
+ def cw_encoding enc
14
+ @cw_encoding.fetch(enc)
15
+ end
16
+
17
+ def dot wpm
18
+ 1.2 / wpm
19
+ end
20
+
21
+ def dot_ms
22
+ dot @wpm
23
+ end
24
+
25
+ def init_print_words_timeout
26
+ @start_print_time, @delay_print_time = Time.now, 2.0
27
+ end
28
+
29
+ def print_words_timeout?
30
+ (Time.now - @start_print_time) > @delay_print_time
31
+ end
32
+
33
+ def init_play_words_timeout
34
+ @start_play_time, @delay_play_time = Time.now, 2.0
35
+ end
36
+
37
+ def play_words_timeout?
38
+ (Time.now - @start_play_time) > @delay_play_time
39
+ end
40
+
41
+ def effective_dot_ms
42
+ dot @effective_wpm
43
+ end
44
+
45
+ def init_char_timer
46
+ @start_time, @delay_time = Time.now, 0.0
47
+ end
48
+
49
+ def char_delay_timeout?
50
+ (Time.now - @start_time) > @delay_time
51
+ end
52
+
53
+ def char_timing(* args)
54
+ timing = 0
55
+ args.flatten.each do |arg|
56
+ case arg
57
+ when :dot then timing += 2
58
+ when :dash then timing += 4
59
+ else
60
+ puts "Error! invalid morse symbol - was #{arg}"
61
+ end
62
+ end
63
+ timing -= 1
64
+ timing = timing.to_f * dot_ms
65
+ timing + code_space_timing
66
+ end
67
+
68
+ def code_space_timing
69
+ @effective_wpm ? 3.0 * effective_dot_ms :
70
+ 3.0 * dot_ms
71
+ end
72
+
73
+ def space_timing
74
+ space = 4.0
75
+ @effective_wpm ? space * effective_dot_ms :
76
+ space * dot_ms
77
+ end
78
+
79
+ def char_delay(char, wpm, ewpm)
80
+ @wpm, @effective_wpm = wpm, ewpm
81
+ if(char != ' ')
82
+ char_timing(cw_encoding(char)) unless(char == ' ')
83
+ else
84
+ space_timing
85
+ end
86
+ end
87
+
88
+ def append_char_delay letr, wpm, ewpm
89
+ @delay_time += char_delay(letr, wpm, ewpm)
90
+ end
91
+
92
+ end
@@ -0,0 +1,225 @@
1
+ # encoding: utf-8
2
+
3
+ require 'wavefile'
4
+
5
+ class ToneGenerator
6
+
7
+ DOT_FILENAME = "audio/dot.wav"
8
+ DASH_FILENAME = "audio/dash.wav"
9
+ SPACE_FILENAME = "audio/space.wav"
10
+ E_SPACE_FILENAME = "audio/e_space.wav"
11
+ TWO_PI = 2 * Math::PI
12
+
13
+ def initialize
14
+ @max_amplitude = 0.5
15
+ @wpm = Params.wpm
16
+ @frequency = Params.frequency
17
+ @effective_wpm = Params.effective_wpm ? Params.effective_wpm : @wpm
18
+ @sample_rate = 2400
19
+ @print = Print.new
20
+ end
21
+
22
+ def cw_encoding
23
+ @encoding ||= CwEncoding.new
24
+ end
25
+
26
+ def convert_words wrds
27
+ wrds.to_array.collect{ |wrd| wrd.gsub("\n","")}
28
+ end
29
+
30
+ def progress
31
+ @progress ||= Progress.new('Compiling')
32
+ end
33
+
34
+ def generate wrds
35
+ word_parts(wrds)
36
+ # progress.init elements.size * 3 + (wrds.size)
37
+ create_element_methods
38
+ compile_fundamentals
39
+ write_word_parts
40
+ end
41
+
42
+ def play_filename
43
+ "audio/#{Params.audio_filename}"
44
+ end
45
+
46
+ def play
47
+ cmd = play_command + ' ' + play_filename
48
+ @pid = ! @dry_run ? Process.spawn(cmd) : cmd
49
+ end
50
+ def data
51
+ { :dot => {:name => :dot,
52
+ :filename => DOT_FILENAME,
53
+ :spb => (@sample_rate * 1.2 / @wpm).to_i },
54
+ :dash => {:name => :dash,
55
+ :filename => DASH_FILENAME,
56
+ :spb => (@sample_rate * 3.6 / @wpm).to_i },
57
+ :space => {:name => :space,
58
+ :filename => SPACE_FILENAME ,
59
+ :spb => (@sample_rate * 1.2 / @wpm).to_i },
60
+ :e_space => {:name => :e_space,
61
+ :filename => E_SPACE_FILENAME ,
62
+ :spb => (@sample_rate * 1.2 / @effective_wpm).to_i }}
63
+ end
64
+
65
+ def generate_space number_of_samples
66
+ [].fill(0.0, 0, number_of_samples)
67
+ end
68
+
69
+ def filter_maybe(size, count)
70
+ ramp = 0.05
71
+ ramp_point = @max_amplitude / ramp
72
+ ampl = (count < ramp_point) ? (ramp * count) : @max_amplitude
73
+ (count > (size - ramp_point)) ? (ramp * (size - count)) : ampl
74
+ end
75
+
76
+ def generate_tone(number_of_samples)
77
+ position_in_period, position_in_period_delta = 0.0, @frequency / @sample_rate
78
+ audio_samples = [].fill(0.0, 0, number_of_samples)
79
+ number_of_samples.times do |sample_number|
80
+ amplitude = filter_maybe(number_of_samples, sample_number)
81
+ # amplitude = 1.0 # @max_amplitude
82
+ sine_radians = ((@frequency * TWO_PI) / @sample_rate) * sample_number
83
+ audio_samples[sample_number] = amplitude * Math.sin(sine_radians)
84
+ end
85
+ audio_samples
86
+ end
87
+
88
+ def generate_buffer audio_samples, ele
89
+ WaveFile::Buffer.new(audio_samples, WaveFile::Format.new(:mono, :float, data[ele][:spb]))
90
+ end
91
+
92
+ def write_element_audio_file ele, buffer
93
+ WaveFile::Writer.new(data[ele][:filename], WaveFile::Format.new(:mono, :pcm_16, @sample_rate)) do |writer|
94
+ writer.write(buffer)
95
+ end
96
+ end
97
+
98
+ def elements
99
+ [:dot, :dash, :space, :e_space]
100
+ end
101
+
102
+ def create_element_method ele
103
+ define_singleton_method(ele) {data[ele]}
104
+ end
105
+
106
+ def create_element_methods
107
+ elements.each do |ele|
108
+ # progress.increment
109
+ create_element_method ele
110
+ end
111
+ end
112
+
113
+ def space_sample? ele
114
+ ele == :space || ele == :e_space
115
+ end
116
+
117
+ def generate_samples ele
118
+ return generate_space(data[ele][:spb]) if space_sample? ele
119
+ generate_tone(data[ele][:spb]) unless space_sample? ele
120
+ end
121
+
122
+ def compile_fundamentals
123
+ elements.each do |ele|
124
+ # progress.increment
125
+ audio_samples = generate_samples ele
126
+ buffer = generate_buffer(audio_samples, ele)
127
+ write_element_audio_file ele, buffer
128
+ end
129
+ end
130
+
131
+ def space_or_espace
132
+ (@effective_wpm == @wpm) ? space : e_space
133
+ end
134
+
135
+ def last_element? idx, chr
136
+ idx == chr.size - 1
137
+ end
138
+
139
+ def push_enc chr
140
+ arry = []
141
+ chr.each_with_index do |c,idx|
142
+ arry << c
143
+ arry << ((last_element?(idx, chr)) ? (space_or_espace) : space)
144
+ end
145
+ arry += char_space
146
+ end
147
+
148
+ def send_char c
149
+ if c == ' '
150
+ enc = word_space
151
+ else
152
+ enc = cw_encoding.fetch(c).map { |enc| send(enc)}
153
+ end
154
+ push_enc enc
155
+ end
156
+
157
+ def word_parts str = nil
158
+ return @word_parts if @word_parts
159
+ @word_parts = []
160
+ str.split('').each { |part| @word_parts << part}
161
+ @word_parts
162
+ end
163
+
164
+
165
+ def make_word_parts words
166
+ parts = []
167
+ @word_parts.each do |part|
168
+ # progress.increment
169
+ parts += send_char part.downcase
170
+ end
171
+ parts
172
+ end
173
+
174
+ def prepare_buffers
175
+ @buffers = {}
176
+ elements.each do |ele|
177
+ # progress.increment
178
+ @buffers[ele] = []
179
+ WaveFile::Reader.new(data[ele][:filename]).
180
+ each_buffer(data[ele][:spb]) do |buffer|
181
+ @buffers[ele] = buffer
182
+ end
183
+ end
184
+ end
185
+
186
+ def write_word_parts
187
+ prepare_buffers
188
+ write_audio_file
189
+ end
190
+
191
+ def char_space
192
+ @effective_wpm == @wpm ? [space,space] : [e_space,e_space]
193
+ end
194
+
195
+ def word_space
196
+ @effective_wpm == @wpm ? [space] : [e_space]
197
+ end
198
+
199
+ def word_composite word
200
+ send_char word.downcase
201
+ end
202
+
203
+ def write_audio
204
+ WaveFile::Writer.new(play_filename, WaveFile::Format.new(:mono, :pcm_16, @sample_rate)) do |writer|
205
+ yield.each do |char|
206
+ # progress.increment
207
+ char.each do |fta|
208
+ writer.write(@buffers[fta[:name]])
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ def reset
215
+ @word_parts = @progress = nil
216
+ # puts "\r"
217
+ end
218
+
219
+ def write_audio_file
220
+ write_audio { @word_parts.collect {|part| word_composite(part) } }
221
+ reset
222
+ end
223
+
224
+ end
225
+