cw 0.0.1

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.
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
+