reight 0.1.6 → 0.1.7
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/ChangeLog.md +52 -0
- data/Rakefile +4 -1
- data/VERSION +1 -1
- data/lib/reight/all.rb +6 -2
- data/lib/reight/app/map/brush.rb +1 -1
- data/lib/reight/app/map/brush_base.rb +3 -3
- data/lib/reight/app/map/canvas.rb +4 -13
- data/lib/reight/app/map/chips.rb +6 -2
- data/lib/reight/app/map/editor.rb +4 -4
- data/lib/reight/app/map/line.rb +5 -5
- data/lib/reight/app/map/rect.rb +2 -2
- data/lib/reight/app/navigator.rb +27 -27
- data/lib/reight/app/runner.rb +106 -84
- data/lib/reight/app/sound/brush.rb +32 -0
- data/lib/reight/app/sound/canvas.rb +190 -0
- data/lib/reight/app/sound/editor.rb +170 -10
- data/lib/reight/app/sound/eraser.rb +28 -0
- data/lib/reight/app/sound/tool.rb +29 -0
- data/lib/reight/app/sound.rb +4 -0
- data/lib/reight/app/sprite/canvas.rb +18 -15
- data/lib/reight/app/sprite/chips.rb +6 -1
- data/lib/reight/app/sprite/color.rb +3 -1
- data/lib/reight/app/sprite/editor.rb +15 -28
- data/lib/reight/app/sprite/line.rb +1 -1
- data/lib/reight/app/sprite/shape.rb +1 -1
- data/lib/reight/app.rb +19 -1
- data/lib/reight/button.rb +7 -4
- data/lib/reight/chip.rb +24 -6
- data/lib/reight/context.rb +168 -0
- data/lib/reight/helpers.rb +2 -2
- data/lib/reight/index.rb +76 -0
- data/lib/reight/map.rb +131 -11
- data/lib/reight/project.rb +34 -6
- data/lib/reight/reight.rb +1 -3
- data/lib/reight/sound.rb +238 -0
- data/lib/reight/sprite.rb +42 -0
- data/lib/reight/text.rb +116 -0
- data/lib/reight.rb +7 -3
- data/reight.gemspec +7 -7
- data/res/icons.png +0 -0
- data/test/helper.rb +16 -0
- data/test/test_map.rb +7 -7
- data/test/test_map_chunk.rb +6 -6
- data/test/test_sprite.rb +28 -0
- metadata +39 -30
- data/lib/reight/app/music/editor.rb +0 -25
- data/lib/reight/app/music.rb +0 -1
data/lib/reight/app/runner.rb
CHANGED
@@ -1,117 +1,139 @@
|
|
1
|
-
|
1
|
+
class Reight::Runner < Reight::App
|
2
2
|
|
3
|
+
include Xot::Inspectable
|
3
4
|
|
4
|
-
|
5
|
+
ROOT_CONTEXT = Reight::CONTEXT__
|
5
6
|
|
6
7
|
TEMPORARY_HASH = {}
|
7
8
|
|
8
9
|
def activated()
|
9
|
-
super
|
10
10
|
run force: true
|
11
|
-
@context.call_activated__
|
11
|
+
@context.call_activated__ {|&b| call_event(ignore_pause: true, &b)}
|
12
|
+
super
|
12
13
|
end
|
13
14
|
|
14
15
|
def deactivated()
|
15
16
|
super
|
16
|
-
@context.call_deactivated__
|
17
|
+
@context.call_deactivated__ {|&b| call_event(ignore_pause: true, &b)}
|
17
18
|
pause
|
19
|
+
cleanup
|
18
20
|
end
|
19
21
|
|
20
22
|
def draw()
|
21
|
-
|
22
|
-
|
23
|
+
return unless @context
|
24
|
+
@initial_resize ||= true.tap do
|
25
|
+
call_event {@context.size ROOT_CONTEXT.width, ROOT_CONTEXT.height}
|
26
|
+
end
|
27
|
+
@context.call_draw__ {|push: true, &b| call_event(push: push, &b)}
|
28
|
+
if canvasFrame = @context.canvasFrame__
|
29
|
+
ROOT_CONTEXT.background 0
|
30
|
+
ROOT_CONTEXT.image @context, *canvasFrame
|
31
|
+
else
|
32
|
+
ROOT_CONTEXT.image @context, 0, 0
|
23
33
|
end
|
24
34
|
super
|
25
35
|
end
|
26
36
|
|
27
37
|
def key_pressed()
|
28
38
|
super
|
29
|
-
@context
|
39
|
+
call_event {@context.key_pressed}
|
30
40
|
end
|
31
41
|
|
32
42
|
def key_released()
|
33
43
|
super
|
34
|
-
@context
|
44
|
+
call_event {@context.key_released}
|
35
45
|
end
|
36
46
|
|
37
47
|
def key_typed()
|
38
48
|
super
|
39
|
-
@context
|
49
|
+
call_event {@context.key_typed}
|
40
50
|
end
|
41
51
|
|
42
52
|
def mouse_pressed()
|
43
53
|
super
|
44
|
-
@context
|
54
|
+
call_event {@context.mouse_pressed}
|
45
55
|
end
|
46
56
|
|
47
57
|
def mouse_released()
|
48
58
|
super
|
49
|
-
@context
|
59
|
+
call_event {@context.mouse_released}
|
50
60
|
end
|
51
61
|
|
52
62
|
def mouse_moved()
|
53
63
|
super
|
54
|
-
|
55
|
-
|
64
|
+
navigator.visible = ROOT_CONTEXT.mouse_y < NAVIGATOR_HEIGHT
|
65
|
+
call_event {@context.mouse_moved}
|
56
66
|
end
|
57
67
|
|
58
68
|
def mouse_dragged()
|
59
69
|
super
|
60
|
-
@context
|
70
|
+
call_event {@context.mouse_dragged}
|
61
71
|
end
|
62
72
|
|
63
73
|
def mouse_clicked()
|
64
74
|
super
|
65
|
-
@context
|
75
|
+
call_event {@context.mouse_clicked}
|
66
76
|
end
|
67
77
|
|
68
78
|
def double_clicked()
|
69
79
|
super
|
70
|
-
@context
|
80
|
+
call_event {@context.double_clicked}
|
71
81
|
end
|
72
82
|
|
73
83
|
def mouse_wheel()
|
74
84
|
super
|
75
|
-
@context
|
85
|
+
call_event {@context.mouse_wheel}
|
76
86
|
end
|
77
87
|
|
78
88
|
def touch_started()
|
79
89
|
super
|
80
|
-
@context
|
90
|
+
call_event {@context.touch_started}
|
81
91
|
end
|
82
92
|
|
83
93
|
def touch_ended()
|
84
94
|
super
|
85
|
-
@context
|
95
|
+
call_event {@context.touch_ended}
|
86
96
|
end
|
87
97
|
|
88
98
|
def touch_moved()
|
89
99
|
super
|
90
|
-
@context
|
100
|
+
call_event {@context.touch_moved}
|
91
101
|
end
|
92
102
|
|
93
103
|
def window_moved()
|
94
104
|
super
|
95
|
-
@context
|
105
|
+
call_event(ignore_pause: true) {@context.window_moved}
|
96
106
|
end
|
97
107
|
|
98
108
|
def window_resized()
|
99
109
|
super
|
100
|
-
@context
|
110
|
+
call_event(ignore_pause: true) {@context.window_resized}
|
101
111
|
end
|
102
112
|
|
103
113
|
private
|
104
114
|
|
115
|
+
def call_event(push: true, ignore_pause: false, &block)
|
116
|
+
return unless @context
|
117
|
+
@context.beginDraw__
|
118
|
+
@context.push if push
|
119
|
+
block.call unless paused?
|
120
|
+
rescue ScriptError, StandardError => e
|
121
|
+
puts e.full_message
|
122
|
+
ensure
|
123
|
+
@context.pop if push
|
124
|
+
@context.endDraw__
|
125
|
+
end
|
126
|
+
|
105
127
|
def running? = @context && !@paused
|
106
128
|
|
107
129
|
def paused? = @context && @paused
|
108
130
|
|
109
131
|
def run(force: false)
|
110
132
|
return pause false if paused? && !force
|
111
|
-
cleanup
|
112
133
|
backup_global_vars
|
113
134
|
@context = create_context
|
114
135
|
@paused = false
|
136
|
+
Processing::Context.setContext__ @context
|
115
137
|
begin_wrapping_user_classes @context
|
116
138
|
eval_user_script @context, project.code_paths.zip(project.codes).to_h
|
117
139
|
end
|
@@ -121,9 +143,12 @@ class Reight::Runner < Reight::App
|
|
121
143
|
end
|
122
144
|
|
123
145
|
def cleanup()
|
124
|
-
remove_world @context.
|
146
|
+
ROOT_CONTEXT.remove_world @context.spriteWorld__ if @context
|
147
|
+
Processing::Context.setContext__ nil
|
125
148
|
@context = nil
|
126
149
|
end_wrapping_user_classes
|
150
|
+
clear_all_timers
|
151
|
+
project.clear_all_sprites
|
127
152
|
restore_global_vars
|
128
153
|
GC.enable
|
129
154
|
GC.start
|
@@ -131,55 +156,52 @@ class Reight::Runner < Reight::App
|
|
131
156
|
|
132
157
|
def create_context()
|
133
158
|
klass = Class.new do
|
134
|
-
|
159
|
+
include Reight::Context
|
135
160
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
add_world sprite_world__
|
140
|
-
activated
|
161
|
+
def call_activated__(&caller)
|
162
|
+
add_world spriteWorld__
|
163
|
+
caller.call {activated}
|
141
164
|
end
|
142
165
|
|
143
|
-
def call_deactivated__()
|
144
|
-
deactivated
|
145
|
-
remove_world
|
166
|
+
def call_deactivated__(&caller)
|
167
|
+
caller.call {deactivated}
|
168
|
+
remove_world spriteWorld__
|
146
169
|
@background_cleared__ = false
|
147
170
|
end
|
148
171
|
|
149
|
-
def call_draw__()
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
unless @background_cleared__
|
155
|
-
@background_cleared__ = true
|
156
|
-
background 100, 100, 100
|
157
|
-
end
|
158
|
-
draw
|
172
|
+
def call_draw__(&caller)
|
173
|
+
@setup_done__ ||= true.tap {caller.call(push: false) {setup}}
|
174
|
+
@background_cleared__ ||= true.tap {caller.call {background 100}}
|
175
|
+
caller.call {draw}
|
159
176
|
end
|
160
177
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
178
|
+
methods = (instance_methods - Object.instance_methods)
|
179
|
+
.reject {_1.end_with? '__'}
|
180
|
+
Processing.to_snake_case__(methods).each do |camel, snake|
|
181
|
+
alias_method snake, camel if snake != camel
|
182
|
+
end
|
183
|
+
|
184
|
+
Processing.to_snake_case__(
|
185
|
+
%i[activated deactivated] + Processing::EVENT_NAMES__
|
186
|
+
).each do |camel, snake|
|
187
|
+
class_eval <<~END
|
188
|
+
def #{camel}(&block)
|
189
|
+
if block
|
190
|
+
@#{camel}_block__ = block
|
191
|
+
else
|
192
|
+
@#{camel}_block__&.call
|
193
|
+
end
|
175
194
|
end
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
195
|
+
END
|
196
|
+
alias_method snake, camel if snake != camel
|
197
|
+
end
|
198
|
+
|
199
|
+
Processing.funcs__(ROOT_CONTEXT).each do |func|
|
200
|
+
next if method_defined? func
|
201
|
+
define_method(func) {|*a, **k, &b| ROOT_CONTEXT.__send__ func, *a, **k, &b}
|
202
|
+
end
|
182
203
|
end
|
204
|
+
klass.new(ROOT_CONTEXT, project)
|
183
205
|
end
|
184
206
|
|
185
207
|
def begin_wrapping_user_classes(context)
|
@@ -207,27 +229,17 @@ class Reight::Runner < Reight::App
|
|
207
229
|
|
208
230
|
def create_user_class_wrapper(context)
|
209
231
|
Module.new.tap do |wrapper|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
klass.define_method(name) {|*a, **k, &b| context.public_send name, *a, **k, &b}
|
221
|
-
context.public_send name, *args, **kwargs, &block
|
222
|
-
else
|
223
|
-
super name, *args, **kwargs, &block
|
232
|
+
wrapper.define_method :respond_to_missing? do |name, include_private = false|
|
233
|
+
context.respond_to?(name, false) || super(name, include_private)
|
234
|
+
end
|
235
|
+
wrapper.define_method :method_missing do |name, *args, **kwargs, &block|
|
236
|
+
if context.respond_to? name
|
237
|
+
wrapper.define_method(name) {|*a, **k, &b| context.public_send name, *a, **k, &b}
|
238
|
+
context.public_send name, *args, **kwargs, &block
|
239
|
+
else
|
240
|
+
super name, *args, **kwargs, &block
|
241
|
+
end
|
224
242
|
end
|
225
|
-
end
|
226
|
-
Processing.to_snake_case__(%i[
|
227
|
-
project createSprite addSprite removeSprite gravity
|
228
|
-
]).each do |camel, snake|
|
229
|
-
klass.define_method(camel) {|*a, **k, &b| context.public_send camel, *a, **k, &b}
|
230
|
-
klass.alias_method snake, camel if snake != camel
|
231
243
|
end
|
232
244
|
end
|
233
245
|
|
@@ -237,6 +249,8 @@ class Reight::Runner < Reight::App
|
|
237
249
|
::Reight::Runner::TEMPORARY_HASH[:params] => {context:, codes:}
|
238
250
|
codes.each {|path, code| context.instance_eval code, path if code}
|
239
251
|
END
|
252
|
+
rescue ScriptError, StandardError => e
|
253
|
+
puts e.full_message
|
240
254
|
ensure
|
241
255
|
TEMPORARY_HASH.delete :params
|
242
256
|
end
|
@@ -247,6 +261,14 @@ class Reight::Runner < Reight::App
|
|
247
261
|
.freeze
|
248
262
|
end
|
249
263
|
|
264
|
+
def clear_all_timers()
|
265
|
+
prefix = Reight::Context::TIMER_PREFIX__
|
266
|
+
ROOT_CONTEXT.instance_eval do
|
267
|
+
@timers__ .delete_if {|id| id in [prefix, _]}
|
268
|
+
@firingTimers__.delete_if {|id| id in [prefix, _]}
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
250
272
|
def restore_global_vars()
|
251
273
|
return unless @global_vars
|
252
274
|
global_variables
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Reight::SoundEditor::Brush < Reight::SoundEditor::Tool
|
2
|
+
|
3
|
+
def initialize(app, &block)
|
4
|
+
super app, icon: app.icon(1, 2, 8), &block
|
5
|
+
set_help left: name, right: 'Pick Tone'
|
6
|
+
end
|
7
|
+
|
8
|
+
def brush(x, y, button)
|
9
|
+
canvas.put x, y
|
10
|
+
end
|
11
|
+
|
12
|
+
def canvas_pressed(x, y, button)
|
13
|
+
return unless button == LEFT
|
14
|
+
canvas.begin_editing
|
15
|
+
brush x, y, button
|
16
|
+
end
|
17
|
+
|
18
|
+
def canvas_released(x, y, button)
|
19
|
+
return unless button == LEFT
|
20
|
+
canvas.end_editing
|
21
|
+
end
|
22
|
+
|
23
|
+
def canvas_dragged(x, y, button)
|
24
|
+
return unless button == LEFT
|
25
|
+
brush x, y, button
|
26
|
+
end
|
27
|
+
|
28
|
+
def canvas_clicked(x, y, button)
|
29
|
+
pick_tone x, y if button == RIGHT
|
30
|
+
end
|
31
|
+
|
32
|
+
end# Brush
|
@@ -0,0 +1,190 @@
|
|
1
|
+
using Reight
|
2
|
+
|
3
|
+
|
4
|
+
class Reight::SoundEditor::Canvas
|
5
|
+
|
6
|
+
include Reight::Hookable
|
7
|
+
|
8
|
+
SEQUENCE_LEN = 32
|
9
|
+
NOTE_HEIGHT = 3
|
10
|
+
|
11
|
+
def initialize(app)
|
12
|
+
hook :sound_changed
|
13
|
+
|
14
|
+
@app, @sound = app, app.project.sounds.first
|
15
|
+
@scrolly = NOTE_HEIGHT * Reight::Sound::Note::MAX / 3
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :tone, :tool
|
19
|
+
|
20
|
+
attr_reader :sound
|
21
|
+
|
22
|
+
def sound=(sound)
|
23
|
+
@sound = sound
|
24
|
+
sound_changed! sound
|
25
|
+
end
|
26
|
+
|
27
|
+
def save()
|
28
|
+
@app.project.save
|
29
|
+
end
|
30
|
+
|
31
|
+
def begin_editing(&block)
|
32
|
+
@app.history.begin_grouping
|
33
|
+
block.call if block
|
34
|
+
ensure
|
35
|
+
end_editing if block
|
36
|
+
end
|
37
|
+
|
38
|
+
def end_editing()
|
39
|
+
@app.history.end_grouping
|
40
|
+
save
|
41
|
+
end
|
42
|
+
|
43
|
+
def note_pos_at(x, y)
|
44
|
+
sp = sprite
|
45
|
+
notew = sp.w / SEQUENCE_LEN
|
46
|
+
max = Reight::Sound::Note::MAX
|
47
|
+
time_index = (x / notew).floor
|
48
|
+
note_index = max - ((@scrolly + y) / NOTE_HEIGHT).floor
|
49
|
+
return time_index, note_index.clamp(0, max)
|
50
|
+
end
|
51
|
+
|
52
|
+
def put(x, y)
|
53
|
+
time_i, note_i = note_pos_at x, y
|
54
|
+
|
55
|
+
@sound.each_note(time_index: time_i)
|
56
|
+
.select {|note,| (note.index == note_i) != (note.tone == tone)}
|
57
|
+
.each {|note,| remove_note time_i, note.index, note.tone}
|
58
|
+
add_note time_i, note_i, tone
|
59
|
+
|
60
|
+
@app.flash note_name y
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete(x, y)
|
64
|
+
time_i, note_i = note_pos_at x, y
|
65
|
+
note = @sound.at time_i, note_i
|
66
|
+
return unless note
|
67
|
+
|
68
|
+
remove_note time_i, note_i, note.tone
|
69
|
+
|
70
|
+
@app.flash note_name y
|
71
|
+
end
|
72
|
+
|
73
|
+
def sprite()
|
74
|
+
@sprite ||= RubySketch::Sprite.new.tap do |sp|
|
75
|
+
pos = -> {return sp.mouse_x, sp.mouse_y}
|
76
|
+
sp.draw {draw}
|
77
|
+
sp.mouse_pressed {mouse_pressed( *pos.call, sp.mouse_button)}
|
78
|
+
sp.mouse_released {mouse_released(*pos.call, sp.mouse_button)}
|
79
|
+
sp.mouse_moved {mouse_moved( *pos.call)}
|
80
|
+
sp.mouse_dragged {mouse_dragged( *pos.call, sp.mouse_button)}
|
81
|
+
sp.mouse_clicked {mouse_clicked( *pos.call, sp.mouse_button)}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def add_note(ti, ni, tone)
|
88
|
+
return if @sound.at(ti, ni)&.tone == tone
|
89
|
+
@sound.add ti, ni, tone
|
90
|
+
@sound.at(ti, ni)&.play 120
|
91
|
+
@app.history.append [:put_note, ti, ni, tone]
|
92
|
+
end
|
93
|
+
|
94
|
+
def remove_note(ti, ni, tone)
|
95
|
+
#@sound.at(ti, ni)&.play @sound.bpm
|
96
|
+
@sound.remove ti, ni
|
97
|
+
@app.history.append [:delete_note, ti, ni, tone]
|
98
|
+
end
|
99
|
+
|
100
|
+
def draw()
|
101
|
+
sp = sprite
|
102
|
+
clip sp.x, sp.y, sp.w, sp.h
|
103
|
+
|
104
|
+
no_stroke
|
105
|
+
fill 0
|
106
|
+
rect 0, 0, sp.w, sp.h
|
107
|
+
|
108
|
+
translate 0, -@scrolly
|
109
|
+
draw_grids
|
110
|
+
draw_notes
|
111
|
+
draw_note_names
|
112
|
+
end
|
113
|
+
|
114
|
+
TONE_COLORS = {
|
115
|
+
sine: 5,
|
116
|
+
triangle: 29,
|
117
|
+
square: 19,
|
118
|
+
sawtooth: 30,
|
119
|
+
pulse12_5: 27,
|
120
|
+
pulse25: 14,
|
121
|
+
noise: 12
|
122
|
+
}.transform_values {Reight::App::PALETTE_COLORS[_1]}
|
123
|
+
|
124
|
+
GRID_COLORS = [1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1]
|
125
|
+
.map.with_index {|n, i| i == 0 ? 100 : (n == 1 ? 80 : 60)}
|
126
|
+
|
127
|
+
def draw_grids()
|
128
|
+
sp = sprite
|
129
|
+
noteh = NOTE_HEIGHT
|
130
|
+
Reight::Sound::Note::MAX.downto(0).with_index do |y, index|
|
131
|
+
fill GRID_COLORS[index % GRID_COLORS.size]
|
132
|
+
rect 0, y * noteh, sp.w, noteh
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def draw_notes()
|
137
|
+
sp = sprite
|
138
|
+
notew, noteh = sp.w / SEQUENCE_LEN, NOTE_HEIGHT
|
139
|
+
tones, max = Reight::Sound::Note::TONES, Reight::Sound::Note::MAX
|
140
|
+
@sound.each_note do |note, index|
|
141
|
+
fill TONE_COLORS[note.tone]
|
142
|
+
rect index * notew, (max - note.index) * noteh, notew, noteh
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def draw_note_names()
|
147
|
+
fill 200
|
148
|
+
text_size 4
|
149
|
+
text_align LEFT, CENTER
|
150
|
+
noteh = NOTE_HEIGHT
|
151
|
+
max = Reight::Sound::Note::MAX
|
152
|
+
(0..Reight::Sound::Note::MAX).step(12).with_index do |y, index|
|
153
|
+
text "C#{index}", 2, (max - y) * noteh, 10, noteh
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def mouse_pressed(...)
|
158
|
+
tool&.canvas_pressed(...) unless hand?
|
159
|
+
end
|
160
|
+
|
161
|
+
def mouse_released(...)
|
162
|
+
tool&.canvas_released(...) unless hand?
|
163
|
+
end
|
164
|
+
|
165
|
+
def mouse_moved(x, y)
|
166
|
+
tool&.canvas_moved(x, y)
|
167
|
+
@app.flash note_name y
|
168
|
+
end
|
169
|
+
|
170
|
+
def mouse_dragged(...)
|
171
|
+
if hand?
|
172
|
+
sp = sprite
|
173
|
+
@scrolly -= sp.mouse_y - sp.pmouse_y
|
174
|
+
else
|
175
|
+
tool&.canvas_dragged(...)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def mouse_clicked(...)
|
180
|
+
tool&.canvas_clicked(...) unless hand?
|
181
|
+
end
|
182
|
+
|
183
|
+
def hand? = @app.pressing?(SPACE)
|
184
|
+
|
185
|
+
def note_name(y)
|
186
|
+
_, note_i = note_pos_at 0, y
|
187
|
+
Reight::Sound::Note.new(note_i).to_s.split(':').first.capitalize
|
188
|
+
end
|
189
|
+
|
190
|
+
end# Canvas
|