cw 0.2.4 → 0.3.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 +4 -4
- data/.cw_config +18 -0
- data/.gitignore +10 -1
- data/LICENSE +2 -1
- data/README.md +212 -96
- data/Rakefile +15 -2
- data/VERSION +1 -1
- data/Vagrantfile +45 -0
- data/cw.gemspec +6 -1
- data/data/text/book_to_read.txt +4 -0
- data/data/text/english.txt +10000 -0
- data/example.rb +172 -106
- data/lib/cw.rb +10 -126
- data/lib/cw/alphabet.rb +53 -46
- data/lib/cw/audio_player.rb +83 -72
- data/lib/cw/book.rb +160 -185
- data/lib/cw/book_details.rb +38 -49
- data/lib/cw/cl.rb +101 -95
- data/lib/cw/common_words.rb +76 -0
- data/lib/cw/config.rb +50 -0
- data/lib/cw/current_word.rb +23 -24
- data/lib/cw/cw_dsl.rb +264 -131
- data/lib/cw/cw_encoding.rb +63 -69
- data/lib/cw/cw_stream.rb +86 -82
- data/lib/cw/cw_threads.rb +132 -22
- data/lib/cw/element.rb +60 -54
- data/lib/cw/file_details.rb +26 -11
- data/lib/cw/key_input.rb +53 -35
- data/lib/cw/numbers.rb +26 -19
- data/lib/cw/os.rb +13 -0
- data/lib/cw/play.rb +92 -0
- data/lib/cw/print.rb +102 -100
- data/lib/cw/process.rb +3 -0
- data/lib/cw/progress.rb +20 -17
- data/lib/cw/randomize.rb +56 -52
- data/lib/cw/repeat_word.rb +59 -66
- data/lib/cw/reveal.rb +32 -31
- data/lib/cw/rss.rb +52 -48
- data/lib/cw/sentence.rb +83 -76
- data/lib/cw/speak.rb +8 -4
- data/lib/cw/spoken.rb +8 -4
- data/lib/cw/str.rb +62 -30
- data/lib/cw/test_letters.rb +20 -28
- data/lib/cw/test_words.rb +25 -31
- data/lib/cw/tester.rb +219 -226
- data/lib/cw/text_helpers.rb +19 -15
- data/lib/cw/timing.rb +63 -67
- data/lib/cw/tone_generator.rb +176 -153
- data/lib/cw/tone_helpers.rb +15 -23
- data/lib/cw/voice.rb +12 -8
- data/lib/cw/words.rb +136 -106
- data/run_script_tests.rb +165 -0
- data/test/my_words.txt +1 -0
- data/test/test_common_words.rb +71 -0
- data/test/test_config.rb +98 -0
- data/test/test_current_word.rb +62 -0
- data/test/test_cw.rb +87 -120
- data/test/test_cw_threads.rb +123 -0
- data/test/test_filtering.rb +439 -0
- data/test/test_params.rb +28 -0
- data/test/test_play.rb +51 -0
- data/test/test_stream.rb +83 -83
- data/test/test_tester.rb +9 -27
- data/test/test_timing.rb +212 -0
- metadata +94 -12
- data/lib/cw/config_file.rb +0 -69
- data/lib/cw/monitor_keys.rb +0 -37
- data/lib/cw/params.rb +0 -104
data/lib/cw/text_helpers.rb
CHANGED
@@ -1,25 +1,29 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
module
|
3
|
+
module CWG
|
4
4
|
|
5
|
-
|
6
|
-
(97..122).to_a
|
7
|
-
end
|
5
|
+
module TextHelpers
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
def letter_group
|
8
|
+
(97..122).to_a
|
9
|
+
end
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
def number_group
|
12
|
+
(48..57).to_a
|
13
|
+
end
|
14
|
+
|
15
|
+
def cw_chars chr
|
16
|
+
chr.tr('^a-z0-9\.\,+', '')
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def exclude_non_cw_chars word
|
20
|
+
temp = ''
|
21
|
+
word.split.each do |chr|
|
22
|
+
temp += chr if letter(chr)
|
23
|
+
end
|
24
|
+
temp
|
21
25
|
end
|
22
|
-
|
26
|
+
|
23
27
|
end
|
24
28
|
|
25
29
|
end
|
data/lib/cw/timing.rb
CHANGED
@@ -1,92 +1,88 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
module CWG
|
4
4
|
|
5
|
-
|
6
|
-
attr_accessor :start_time
|
5
|
+
class Timing
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
@cw_encoding = CwEncoding.new
|
11
|
-
end
|
12
|
-
|
13
|
-
def cw_encoding enc
|
14
|
-
@cw_encoding.fetch(enc)
|
15
|
-
end
|
7
|
+
attr_accessor :delay_time
|
8
|
+
attr_accessor :start_time
|
16
9
|
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
def initialize
|
11
|
+
@delay_time = 0.0
|
12
|
+
@cw_encoding = CwEncoding.new
|
13
|
+
end
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
def cw_encoding enc
|
16
|
+
@cw_encoding.fetch(enc)
|
17
|
+
end
|
24
18
|
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
def dot wpm
|
20
|
+
1.2 / wpm.to_f
|
21
|
+
end
|
28
22
|
|
29
|
-
|
30
|
-
|
31
|
-
|
23
|
+
def dot_ms
|
24
|
+
dot @wpm
|
25
|
+
end
|
32
26
|
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
def init_print_words_timeout
|
28
|
+
@start_print_time, @delay_print_time = Time.now, 2.0
|
29
|
+
end
|
36
30
|
|
37
|
-
|
38
|
-
|
39
|
-
|
31
|
+
def print_words_timeout?
|
32
|
+
(Time.now - @start_print_time) > @delay_print_time
|
33
|
+
end
|
40
34
|
|
41
|
-
|
42
|
-
|
43
|
-
|
35
|
+
def effective_dot_ms
|
36
|
+
dot @effective_wpm
|
37
|
+
end
|
44
38
|
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
def init_char_timer
|
40
|
+
@start_time, @delay_time = Time.now, 0.0
|
41
|
+
end
|
48
42
|
|
49
|
-
|
50
|
-
|
51
|
-
|
43
|
+
def char_delay_timeout?
|
44
|
+
(Time.now - @start_time) > @delay_time
|
45
|
+
end
|
52
46
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
47
|
+
def char_timing(* args)
|
48
|
+
timing = 0
|
49
|
+
args.flatten.each do |arg|
|
50
|
+
case arg
|
51
|
+
when :dot then timing += 2
|
52
|
+
when :dash then timing += 4
|
53
|
+
else
|
54
|
+
puts "Error! invalid morse symbol - was #{arg}"
|
55
|
+
end
|
61
56
|
end
|
57
|
+
timing -= 1
|
58
|
+
timing = timing.to_f * dot_ms
|
59
|
+
timing + code_space_timing
|
62
60
|
end
|
63
|
-
timing -= 1
|
64
|
-
timing = timing.to_f * dot_ms
|
65
|
-
timing + code_space_timing
|
66
|
-
end
|
67
61
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
def code_space_timing
|
63
|
+
@effective_wpm ? 3.0 * effective_dot_ms :
|
64
|
+
3.0 * dot_ms
|
65
|
+
end
|
72
66
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
67
|
+
def space_timing
|
68
|
+
space = 4.0
|
69
|
+
@effective_wpm ? space * effective_dot_ms :
|
70
|
+
space * dot_ms
|
71
|
+
end
|
78
72
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
73
|
+
def char_delay(char, wpm, ewpm)
|
74
|
+
@wpm, @effective_wpm = wpm, ewpm
|
75
|
+
if(char != ' ')
|
76
|
+
char_timing(cw_encoding(char)) unless(char == ' ')
|
83
77
|
else
|
84
|
-
|
78
|
+
space_timing
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def append_char_delay letr, wpm, ewpm
|
83
|
+
@delay_time += char_delay(letr, wpm, ewpm)
|
85
84
|
end
|
86
|
-
end
|
87
85
|
|
88
|
-
def append_char_delay letr, wpm, ewpm
|
89
|
-
@delay_time += char_delay(letr, wpm, ewpm)
|
90
86
|
end
|
91
87
|
|
92
88
|
end
|
data/lib/cw/tone_generator.rb
CHANGED
@@ -2,200 +2,223 @@
|
|
2
2
|
|
3
3
|
require 'wavefile'
|
4
4
|
|
5
|
-
|
5
|
+
module CWG
|
6
6
|
|
7
|
-
|
7
|
+
class ToneGenerator
|
8
8
|
|
9
|
-
|
10
|
-
@max_amplitude = 0.5
|
11
|
-
@wpm = Params.wpm.to_f
|
12
|
-
@frequency = Params.frequency
|
13
|
-
@effective_wpm = Params.effective_wpm ? Params.effective_wpm.to_f : @wpm
|
14
|
-
@sample_rate = 2400
|
15
|
-
@print = Print.new
|
16
|
-
end
|
9
|
+
MUTE = false
|
17
10
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
11
|
+
include ToneHelpers
|
12
|
+
include FileDetails
|
21
13
|
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
def initialize
|
15
|
+
@max_amplitude = (Cfg.config["volume"].to_f > 1.0 ?
|
16
|
+
1.0 : Cfg.config["volume"].to_f)
|
17
|
+
@wpm = Cfg.config["wpm"].to_f
|
18
|
+
@frequency = Cfg.config["frequency"].to_i
|
19
|
+
@effective_wpm = Cfg.config["effective_wpm"] ?
|
20
|
+
Cfg.config["effective_wpm"].to_f : @wpm
|
21
|
+
@sample_rate = 2400
|
22
|
+
@print = Print.new
|
23
|
+
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
# progress.init elements.size * 3 + (wrds.size)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
def generate wrds
|
26
|
+
word_parts(wrds)
|
27
|
+
# progress.init elements.size * 3 + (wrds.size)
|
28
|
+
create_element_methods
|
29
|
+
compile_fundamentals
|
30
|
+
write_word_parts
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
33
|
+
def play_filename
|
34
|
+
File.join(AUDIO,Cfg.config["audio_filename"])
|
35
|
+
end
|
36
|
+
|
37
|
+
def play
|
38
|
+
cmd = play_command + ' ' + play_filename
|
39
|
+
@pid = ! @dry_run ? Process.spawn(cmd) : cmd
|
40
|
+
end
|
41
|
+
|
42
|
+
def cw_encoding
|
43
|
+
@encoding ||= CwEncoding.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def progress
|
47
|
+
@progress ||= Progress.new('Compiling')
|
48
|
+
end
|
49
|
+
|
50
|
+
def data
|
51
|
+
{ :dot => {:name => :dot,
|
52
|
+
:filename => DOT_FILENAME,
|
47
53
|
:spb => (@sample_rate * 1.2 / @wpm).to_i },
|
48
|
-
|
49
|
-
:filename =>
|
50
|
-
:spb => (@sample_rate *
|
51
|
-
|
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
|
52
64
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
65
|
+
def filter_maybe(size, count)
|
66
|
+
ramp = 0.05
|
67
|
+
ramp_point = @max_amplitude / ramp
|
68
|
+
ampl = (count < ramp_point) ? (ramp * count) : @max_amplitude
|
69
|
+
(count > (size - ramp_point)) ? (ramp * (size - count)) : ampl
|
70
|
+
end
|
59
71
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
72
|
+
def generate_tone(number_of_samples)
|
73
|
+
audio_samples = [].fill(0.0, 0, number_of_samples)
|
74
|
+
number_of_samples.times do |sample_number|
|
75
|
+
amplitude = filter_maybe(number_of_samples, sample_number)
|
76
|
+
# amplitude = 1.0 # @max_amplitude
|
77
|
+
# amplitude = 0.01 if MUTE
|
78
|
+
sine_radians = ((@frequency * TWO_PI) / @sample_rate) * sample_number
|
79
|
+
audio_samples[sample_number] = amplitude * Math.sin(sine_radians)
|
80
|
+
end
|
81
|
+
audio_samples
|
67
82
|
end
|
68
|
-
audio_samples
|
69
|
-
end
|
70
83
|
|
71
|
-
|
72
|
-
|
73
|
-
|
84
|
+
def generate_buffer audio_samples, ele
|
85
|
+
WaveFile::Buffer.new(audio_samples, WaveFile::Format.new(:mono, :float, data[ele][:spb]))
|
86
|
+
end
|
74
87
|
|
75
|
-
|
76
|
-
|
77
|
-
|
88
|
+
def write_element_audio_file ele, buffer
|
89
|
+
WaveFile::Writer.new(data[ele][:filename], WaveFile::Format.new(:mono, :pcm_16, @sample_rate)) do |writer|
|
90
|
+
writer.write(buffer)
|
91
|
+
end
|
78
92
|
end
|
79
|
-
end
|
80
93
|
|
81
|
-
|
82
|
-
|
83
|
-
|
94
|
+
def elements
|
95
|
+
[:dot, :dash, :space, :e_space]
|
96
|
+
end
|
84
97
|
|
85
|
-
|
86
|
-
define_singleton_method(ele) {data[ele]}
|
87
|
-
end
|
98
|
+
# create dot, dash, space or e_space method
|
88
99
|
|
89
|
-
|
90
|
-
|
91
|
-
# progress.increment
|
92
|
-
create_element_method ele
|
100
|
+
def create_element_method ele
|
101
|
+
define_singleton_method(ele) {data[ele]}
|
93
102
|
end
|
94
|
-
end
|
95
103
|
|
96
|
-
|
97
|
-
return generate_space(data[ele][:spb]) if space_sample? ele
|
98
|
-
generate_tone(data[ele][:spb]) unless space_sample? ele
|
99
|
-
end
|
104
|
+
# create dot, dash, space and e_space methods
|
100
105
|
|
101
|
-
|
102
|
-
|
103
|
-
# progress.increment
|
104
|
-
|
105
|
-
|
106
|
-
write_element_audio_file ele, buffer
|
106
|
+
def create_element_methods
|
107
|
+
elements.each do |ele|
|
108
|
+
# progress.increment
|
109
|
+
create_element_method ele
|
110
|
+
end
|
107
111
|
end
|
108
|
-
end
|
109
112
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
+
def generate_samples ele
|
114
|
+
return generate_space(data[ele][:spb]) if space_sample? ele
|
115
|
+
generate_tone(data[ele][:spb]) unless space_sample? ele
|
116
|
+
end
|
113
117
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
118
|
+
def compile_fundamentals
|
119
|
+
elements.each do |ele|
|
120
|
+
# progress.increment
|
121
|
+
audio_samples = generate_samples ele
|
122
|
+
buffer = generate_buffer(audio_samples, ele)
|
123
|
+
write_element_audio_file ele, buffer
|
124
|
+
end
|
119
125
|
end
|
120
|
-
arry += char_space
|
121
|
-
end
|
122
126
|
|
123
|
-
|
124
|
-
|
125
|
-
if c == ' '
|
126
|
-
enc = word_space
|
127
|
-
else
|
128
|
-
enc = cw_encoding.fetch(c).map { |e| send(e)}
|
127
|
+
def space_or_espace
|
128
|
+
(@effective_wpm == @wpm) ? space : e_space
|
129
129
|
end
|
130
|
-
push_enc enc
|
131
|
-
end
|
132
130
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
131
|
+
def push_enc chr
|
132
|
+
arry = []
|
133
|
+
chr.each_with_index do |c,idx|
|
134
|
+
arry << c
|
135
|
+
arry << ((last_element?(idx, chr)) ? (space_or_espace) : space)
|
136
|
+
end
|
137
|
+
arry += char_space
|
138
|
+
end
|
139
139
|
|
140
|
+
def send_char c
|
141
|
+
enc = nil
|
142
|
+
if c == ' '
|
143
|
+
enc = word_space
|
144
|
+
else
|
145
|
+
enc = cw_encoding.fetch(c).map { |e| send(e)}
|
146
|
+
end
|
147
|
+
push_enc enc
|
148
|
+
end
|
140
149
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
150
|
+
def word_parts str = nil
|
151
|
+
return @word_parts if @word_parts
|
152
|
+
@word_parts = []
|
153
|
+
str.split('').each { |part| @word_parts << part}
|
154
|
+
@word_parts
|
146
155
|
end
|
147
|
-
parts
|
148
|
-
end
|
149
156
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
each_buffer(data[ele][:spb]) do |buffer|
|
157
|
-
@buffers[ele] = buffer
|
157
|
+
|
158
|
+
def make_word_parts
|
159
|
+
parts = []
|
160
|
+
@word_parts.each do |part|
|
161
|
+
# progress.increment
|
162
|
+
parts += send_char part.downcase
|
158
163
|
end
|
164
|
+
parts
|
159
165
|
end
|
160
|
-
end
|
161
166
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
167
|
+
def prepare_buffers
|
168
|
+
@buffers = {}
|
169
|
+
elements.each do |ele|
|
170
|
+
# progress.increment
|
171
|
+
@buffers[ele] = []
|
172
|
+
WaveFile::Reader.new(data[ele][:filename]).
|
173
|
+
each_buffer(data[ele][:spb]) do |buffer|
|
174
|
+
@buffers[ele] = buffer
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
166
178
|
|
167
|
-
|
168
|
-
|
169
|
-
|
179
|
+
def write_word_parts
|
180
|
+
prepare_buffers
|
181
|
+
write_audio_file
|
182
|
+
end
|
170
183
|
|
171
|
-
|
172
|
-
|
173
|
-
|
184
|
+
def char_space
|
185
|
+
@effective_wpm == @wpm ? [space,space] : [e_space,e_space]
|
186
|
+
end
|
174
187
|
|
175
|
-
|
176
|
-
|
177
|
-
|
188
|
+
def word_space
|
189
|
+
@effective_wpm == @wpm ? [space] : [e_space]
|
190
|
+
end
|
191
|
+
|
192
|
+
def word_composite word
|
193
|
+
send_char word.downcase
|
194
|
+
end
|
178
195
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
196
|
+
def write_audio
|
197
|
+
WaveFile::Writer.
|
198
|
+
new(play_filename,
|
199
|
+
WaveFile::Format.
|
200
|
+
new(:mono,
|
201
|
+
:pcm_16,
|
202
|
+
@sample_rate)) do |writer|
|
203
|
+
yield.each do |char|
|
204
|
+
# progress.increment
|
205
|
+
char.each do |fta|
|
206
|
+
writer.write(@buffers[fta[:name]])
|
207
|
+
end
|
185
208
|
end
|
186
209
|
end
|
187
210
|
end
|
188
|
-
end
|
189
211
|
|
190
|
-
|
191
|
-
|
192
|
-
# puts "\r"
|
193
|
-
|
212
|
+
def reset
|
213
|
+
@word_parts = @progress = nil
|
214
|
+
# puts "\r"
|
215
|
+
end
|
194
216
|
|
195
|
-
|
196
|
-
|
197
|
-
|
217
|
+
def write_audio_file
|
218
|
+
write_audio do
|
219
|
+
@word_parts.collect {|part| word_composite(part) }
|
220
|
+
end
|
221
|
+
reset
|
222
|
+
end
|
198
223
|
end
|
199
|
-
|
200
224
|
end
|
201
|
-
|