midinous 1.0.0.beta
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 +7 -0
- data/COPYING.L +674 -0
- data/README.md +31 -0
- data/lib/doc/Hotkeys.txt +30 -0
- data/lib/doc/Notes and Scales.txt +64 -0
- data/lib/midinous/canvas.rb +388 -0
- data/lib/midinous/constants.rb +108 -0
- data/lib/midinous/init.rb +166 -0
- data/lib/midinous/key_bindings.rb +81 -0
- data/lib/midinous/logic.rb +165 -0
- data/lib/midinous/points.rb +1060 -0
- data/lib/midinous/proc_midi.rb +100 -0
- data/lib/midinous/style/midinous.glade +928 -0
- data/lib/midinous/style/midinous_themes.style +4022 -0
- data/lib/midinous/style/ui.rb +686 -0
- data/lib/midinous.rb +21 -0
- data/lib/saves/sample.nous +9 -0
- metadata +102 -0
@@ -0,0 +1,1060 @@
|
|
1
|
+
# Copyright (C) 2019 James "Nornec" Ratliff
|
2
|
+
#
|
3
|
+
# This file is part of Midinous
|
4
|
+
#
|
5
|
+
# Midinous is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# Midinous is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Midinous. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
class Point_Logic
|
19
|
+
include Logic_Controls
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@prop_names = ["Note",
|
23
|
+
"Velocity",
|
24
|
+
"Duration (beats)",
|
25
|
+
"Channel",
|
26
|
+
"Repeat",
|
27
|
+
"X-coordinate",
|
28
|
+
"Y-coordinate",
|
29
|
+
"Color",
|
30
|
+
"Path Mode",
|
31
|
+
"Signal Start",
|
32
|
+
"Play Mode"]
|
33
|
+
@prop_names_multi = ["Note",
|
34
|
+
"Velocity",
|
35
|
+
"Duration (beats)",
|
36
|
+
"Channel",
|
37
|
+
"Repeat",
|
38
|
+
"Color",
|
39
|
+
"Signal Start",
|
40
|
+
"Play Mode"]
|
41
|
+
@prop_names_adv = []
|
42
|
+
@prop_names_adv_multi = []
|
43
|
+
@curr_prop = nil
|
44
|
+
@curr_prop_adv = nil
|
45
|
+
@mempoints = []
|
46
|
+
@mempointsbuff = []
|
47
|
+
@copy_pos = []
|
48
|
+
@copy_type = nil
|
49
|
+
@paste_count = 0
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_point(r_origin,points) #Point existence search
|
53
|
+
unless (collision_check(r_origin,points))
|
54
|
+
points << NousPoint.new(r_origin,-1)
|
55
|
+
end
|
56
|
+
return points
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_path(points)
|
60
|
+
points.find_all { |n| n.pathable && !n.source}.each do |t|
|
61
|
+
points.find(&:source).path_to << t
|
62
|
+
t.path_from << points.find(&:source)
|
63
|
+
end
|
64
|
+
return points
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_path_mode(mode)
|
68
|
+
CC.nouspoints.find_all(&:selected).each {|n| n.path_mode = mode}
|
69
|
+
UI::canvas.queue_draw
|
70
|
+
end
|
71
|
+
def set_note(note)
|
72
|
+
CC.nouspoints.find_all(&:selected).each {|n| n.note = note}
|
73
|
+
populate_prop(CC.nouspoints)
|
74
|
+
end
|
75
|
+
def inc_note(inc)
|
76
|
+
CC.nouspoints.find_all(&:selected).each do |n|
|
77
|
+
signed = n.note
|
78
|
+
val = n.note.to_i
|
79
|
+
val += inc
|
80
|
+
if val >= 0
|
81
|
+
n.note = "+#{val}"
|
82
|
+
else n.note = val
|
83
|
+
end
|
84
|
+
if !(signed.to_s.include?("+") || signed.to_s.include?("-"))
|
85
|
+
n.note = n.note.to_i
|
86
|
+
n.note = n.note.clamp(0,127)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
populate_prop(CC.nouspoints)
|
90
|
+
end
|
91
|
+
|
92
|
+
def collision_check(r_origin,points)
|
93
|
+
return true if points.any? { |n| r_origin == n.origin }
|
94
|
+
end
|
95
|
+
|
96
|
+
def select_points(box,points) #Select points with the select tool
|
97
|
+
UI::prop_list_model.clear
|
98
|
+
UI::prop_mod.text = ""
|
99
|
+
box_origin = [box[0],box[1]] #box is an array with 4 values
|
100
|
+
points.each do |n|
|
101
|
+
if check_bounds(n.origin,box)
|
102
|
+
n.select
|
103
|
+
elsif check_bounds(box_origin,n.bounds)
|
104
|
+
n.select
|
105
|
+
else
|
106
|
+
n.deselect
|
107
|
+
UI::prop_list_model.clear
|
108
|
+
UI::prop_mod.text = ""
|
109
|
+
end
|
110
|
+
end
|
111
|
+
populate_prop(points)
|
112
|
+
return points
|
113
|
+
end
|
114
|
+
|
115
|
+
def select_all
|
116
|
+
CC.nouspoints.each {|n| n.selected = true} unless CC.nouspoints == []
|
117
|
+
populate_prop(CC.nouspoints)
|
118
|
+
UI::canvas.queue_draw
|
119
|
+
end
|
120
|
+
def copy_points (type)
|
121
|
+
return if CC.nouspoints.empty?
|
122
|
+
origins = []
|
123
|
+
CC.nouspoints.find_all(&:selected).each {|n| origins << n.origin}
|
124
|
+
@copy_pos = origins.min
|
125
|
+
@copy_type = type
|
126
|
+
@mempointsbuff = CC.nouspoints.find_all(&:selected)
|
127
|
+
end
|
128
|
+
def paste_points
|
129
|
+
return if @mempointsbuff.empty?
|
130
|
+
copy_lookup = {}
|
131
|
+
|
132
|
+
# Clone the points and track old point => new point
|
133
|
+
@mempointsbuff.each do |n|
|
134
|
+
n.selected = false if @copy_type == "copy"
|
135
|
+
mem_point = n.clone
|
136
|
+
@mempoints << mem_point
|
137
|
+
copy_lookup[n.object_id] = mem_point
|
138
|
+
end
|
139
|
+
|
140
|
+
# Update the point relations
|
141
|
+
@mempoints.each do |n|
|
142
|
+
n.path_to = n.path_to.map { |pt| copy_lookup[pt.object_id] }.compact
|
143
|
+
n.path_from = n.path_from.map { |pf| copy_lookup[pf.object_id] }.compact
|
144
|
+
end
|
145
|
+
|
146
|
+
@paste_count = 0 if @copy_type == "copy"
|
147
|
+
CC.nouspoints.reject!(&:selected) if @copy_type == "cut" && @paste_count == 0
|
148
|
+
@paste_count += 1
|
149
|
+
|
150
|
+
paste_pos = CC.mouse_last_pos
|
151
|
+
diff = round_to_grid([paste_pos[0] - @copy_pos[0],paste_pos[1] - @copy_pos[1]])
|
152
|
+
@mempoints.each {|m| m.set_destination(diff)}
|
153
|
+
@mempoints.each {|m| CC.nouspoints << m} if paste_check(diff,@mempoints)
|
154
|
+
@mempoints = []
|
155
|
+
populate_prop(CC.nouspoints)
|
156
|
+
UI::canvas.queue_draw
|
157
|
+
end
|
158
|
+
def paste_check(diff,memp)
|
159
|
+
memp.each {|m| return false if CC.nouspoints.any? { |n| n.origin == m.origin}}
|
160
|
+
return true
|
161
|
+
end
|
162
|
+
|
163
|
+
def populate_prop (points)
|
164
|
+
UI::prop_list_model.clear
|
165
|
+
UI::prop_mod.text = ""
|
166
|
+
point = nil
|
167
|
+
point = points.find(&:selected) if points.find_all(&:selected).length == 1
|
168
|
+
if point
|
169
|
+
prop_vals = [point.note,
|
170
|
+
point.velocity,
|
171
|
+
point.duration,
|
172
|
+
point.channel,
|
173
|
+
point.repeat,
|
174
|
+
point.x,
|
175
|
+
point.y,
|
176
|
+
color_to_hex(point.default_color),
|
177
|
+
point.path_mode,
|
178
|
+
point.traveler_start,
|
179
|
+
point.play_modes[0]]
|
180
|
+
@prop_names.each do |v|
|
181
|
+
iter = UI::prop_list_model.append
|
182
|
+
iter[0] = v
|
183
|
+
iter[1] = prop_vals[@prop_names.find_index(v)].to_s
|
184
|
+
end
|
185
|
+
elsif points.find_all(&:selected).length > 1
|
186
|
+
@prop_names_multi.each do |v|
|
187
|
+
equalizer = []
|
188
|
+
iter = UI::prop_list_model.append
|
189
|
+
iter[0] = v
|
190
|
+
case v
|
191
|
+
when "Note"
|
192
|
+
points.find_all(&:selected).each {|p| equalizer << p.note}
|
193
|
+
when "Velocity"
|
194
|
+
points.find_all(&:selected).each {|p| equalizer << p.velocity}
|
195
|
+
when "Duration (beats)"
|
196
|
+
points.find_all(&:selected).each {|p| equalizer << p.duration}
|
197
|
+
when "Channel"
|
198
|
+
points.find_all(&:selected).each {|p| equalizer << p.channel}
|
199
|
+
when "Color"
|
200
|
+
points.find_all(&:selected).each {|p| equalizer << color_to_hex(p.default_color)}
|
201
|
+
when "Signal Start"
|
202
|
+
points.find_all(&:selected).each {|p| equalizer << p.traveler_start}
|
203
|
+
when "Play Mode"
|
204
|
+
points.find_all(&:selected).each {|p| equalizer << p.play_modes[0]}
|
205
|
+
when "Repeat"
|
206
|
+
points.find_all(&:selected).each {|p| equalizer << p.repeat}
|
207
|
+
end
|
208
|
+
if equalizer.uniq.count == 1
|
209
|
+
iter[1] = equalizer[0].to_s
|
210
|
+
else iter[1] = "Multiple Values"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
else
|
214
|
+
UI::prop_list_model.clear
|
215
|
+
UI::prop_mod.text = ""
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
def prop_list_select(selected)
|
220
|
+
return if selected == nil
|
221
|
+
@curr_prop = selected[0]
|
222
|
+
if selected[1][0] == "#"
|
223
|
+
UI::prop_mod.text = selected[1][1..6]
|
224
|
+
else UI::prop_mod.text = selected[1]
|
225
|
+
end
|
226
|
+
UI::prop_mod.position = 0
|
227
|
+
UI::prop_mod.grab_focus
|
228
|
+
end
|
229
|
+
|
230
|
+
def check_input(text)
|
231
|
+
play_modes = ["robin","split","portal","random"]
|
232
|
+
path_modes = ["horz","vert"]
|
233
|
+
signal_states = ["true","false"]
|
234
|
+
case @curr_prop
|
235
|
+
when "Note"
|
236
|
+
if (text.to_i >= 1 && text.to_i <= 127) || (text.match(/^([+]|-)[0-9]{1,2}$/))
|
237
|
+
UI::prop_mod_button.sensitive = true
|
238
|
+
else UI::prop_mod_button.sensitive = false
|
239
|
+
end
|
240
|
+
when "Velocity"
|
241
|
+
if (text.to_i >= 1 && text.to_i <= 127)
|
242
|
+
UI::prop_mod_button.sensitive = true
|
243
|
+
else UI::prop_mod_button.sensitive = false
|
244
|
+
end
|
245
|
+
when "Duration (beats)"
|
246
|
+
if text.to_i >= 1 && text.to_i <= 1000
|
247
|
+
UI::prop_mod_button.sensitive = true
|
248
|
+
else UI::prop_mod_button.sensitive = false
|
249
|
+
end
|
250
|
+
when "Channel"
|
251
|
+
if text.to_i >= 1 && text.to_i <= 16
|
252
|
+
UI::prop_mod_button.sensitive = true
|
253
|
+
else UI::prop_mod_button.sensitive = false
|
254
|
+
end
|
255
|
+
when "X-coordinate", "Y-coordinate"
|
256
|
+
if round_num_to_grid(text.to_i) >= CC.grid_spacing &&
|
257
|
+
round_num_to_grid(text.to_i) <= (CANVAS_SIZE - CC.grid_spacing)
|
258
|
+
then
|
259
|
+
UI::prop_mod_button.sensitive = true
|
260
|
+
else UI::prop_mod_button.sensitive = false
|
261
|
+
end
|
262
|
+
when "Color"
|
263
|
+
if text.match(/^[0-9A-Fa-f]{6}$/)
|
264
|
+
UI::prop_mod_button.sensitive = true
|
265
|
+
else UI::prop_mod_button.sensitive = false
|
266
|
+
end
|
267
|
+
when "Path Mode"
|
268
|
+
if path_modes.include? text
|
269
|
+
UI::prop_mod_button.sensitive = true
|
270
|
+
else UI::prop_mod_button.sensitive = false
|
271
|
+
end
|
272
|
+
when "Signal Start"
|
273
|
+
if signal_states.include? text
|
274
|
+
UI::prop_mod_button.sensitive = true
|
275
|
+
else UI::prop_mod_button.sensitive = false
|
276
|
+
end
|
277
|
+
when "Play Mode"
|
278
|
+
if play_modes.include? text
|
279
|
+
UI::prop_mod_button.sensitive = true
|
280
|
+
else UI::prop_mod_button.sensitive = false
|
281
|
+
end
|
282
|
+
when "Repeat"
|
283
|
+
if text.to_i >= 0 && text.to_i <= 128
|
284
|
+
UI::prop_mod_button.sensitive = true
|
285
|
+
else UI::prop_mod_button.sensitive = false
|
286
|
+
end
|
287
|
+
else UI::prop_mod_button.sensitive = false
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def modify_properties(points)
|
292
|
+
case @curr_prop
|
293
|
+
when "Note"
|
294
|
+
points.find_all(&:selected).each do |p|
|
295
|
+
if UI::prop_mod.text.match(/^([+]|-)[0-9]{1,2}$/)
|
296
|
+
unless p.traveler_start == true
|
297
|
+
p.note = UI::prop_mod.text
|
298
|
+
p.use_rel = true
|
299
|
+
end
|
300
|
+
elsif (UI::prop_mod.text.to_i >= 0 &&
|
301
|
+
UI::prop_mod.text.to_i <= 127 &&
|
302
|
+
!(UI::prop_mod.text.include?("+") || UI::prop_mod.text.include?("-")))
|
303
|
+
then
|
304
|
+
p.note = UI::prop_mod.text.to_i
|
305
|
+
p.use_rel = false
|
306
|
+
end
|
307
|
+
end
|
308
|
+
when "Velocity"
|
309
|
+
points.find_all(&:selected).each {|p| p.velocity = UI::prop_mod.text.to_i}
|
310
|
+
when "Duration (beats)"
|
311
|
+
points.find_all(&:selected).each {|p| p.duration = UI::prop_mod.text.to_i}
|
312
|
+
when "Channel"
|
313
|
+
points.find_all(&:selected).each {|p| p.channel = UI::prop_mod.text.to_i}
|
314
|
+
when "X-coordinate"
|
315
|
+
points.find(&:selected).x = UI::prop_mod.text.to_i
|
316
|
+
when "Y-coordinate"
|
317
|
+
points.find(&:selected).y = UI::prop_mod.text.to_i
|
318
|
+
when "Color"
|
319
|
+
points.find_all(&:selected).each {|p| p.set_default_color(hex_to_color("##{UI::prop_mod.text}"))}
|
320
|
+
when "Path Mode"
|
321
|
+
points.find(&:selected).path_mode = UI::prop_mod.text
|
322
|
+
when "Signal Start"
|
323
|
+
case UI::prop_mod.text
|
324
|
+
when "true"
|
325
|
+
points.find_all(&:selected).each {|p| p.traveler_start = true}
|
326
|
+
when "false"
|
327
|
+
points.find_all(&:selected).each {|p| p.traveler_start = false}
|
328
|
+
end
|
329
|
+
when "Play Mode"
|
330
|
+
if UI::prop_mod.text == "robin" || UI::prop_mod.text == "portal"
|
331
|
+
points.find_all(&:selected).each {|p| p.play_modes.rotate! until p.play_modes[0] == UI::prop_mod.text}
|
332
|
+
else
|
333
|
+
points.find_all {|p| p.selected == true && p.path_to.length > 1}.each {|p| p.play_modes.rotate! until p.play_modes[0] == UI::prop_mod.text}
|
334
|
+
end
|
335
|
+
when "Repeat"
|
336
|
+
points.find_all(&:selected).each do |p|
|
337
|
+
p.repeat = UI::prop_mod.text.to_i
|
338
|
+
end
|
339
|
+
end
|
340
|
+
return points
|
341
|
+
end
|
342
|
+
|
343
|
+
def select_path_point(origin,points,source_chosen)
|
344
|
+
points.find_all {|g| check_bounds(origin,g.bounds)}.each do |n|
|
345
|
+
case !n.pathable
|
346
|
+
when true #If clicking where a non-pathable point is
|
347
|
+
source_chosen = n.path_set(source_chosen)
|
348
|
+
when false
|
349
|
+
if n.source
|
350
|
+
points, source_chosen = cancel_path(points)
|
351
|
+
end
|
352
|
+
n.path_unset
|
353
|
+
end
|
354
|
+
end
|
355
|
+
return points, source_chosen
|
356
|
+
end
|
357
|
+
|
358
|
+
def play_mode_rotate(dir)
|
359
|
+
CC.nouspoints.find_all(&:selected).each do |n|
|
360
|
+
if n.path_to.length <= 1
|
361
|
+
case n.play_modes[0]
|
362
|
+
when "robin"
|
363
|
+
n.play_modes.rotate!(dir) until n.play_modes[0] == "portal"
|
364
|
+
when "portal"
|
365
|
+
n.play_modes.rotate!(dir) until n.play_modes[0] == "robin"
|
366
|
+
end
|
367
|
+
else
|
368
|
+
n.play_modes.rotate!(dir)
|
369
|
+
end
|
370
|
+
UI::canvas.queue_draw
|
371
|
+
end
|
372
|
+
end
|
373
|
+
def play_mode_rotate_selected(dir)
|
374
|
+
CC.nouspoints.find_all(&:selected).each do |n|
|
375
|
+
case dir
|
376
|
+
when "+"
|
377
|
+
n.path_to.rotate!(1)
|
378
|
+
when "-"
|
379
|
+
n.path_to.rotate!(-1)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
UI::canvas.queue_draw
|
383
|
+
end
|
384
|
+
|
385
|
+
def cancel_selected(points)
|
386
|
+
points.find_all(&:selected).each { |n| n.deselect }
|
387
|
+
return points
|
388
|
+
end
|
389
|
+
def cancel_path(points)
|
390
|
+
points.find_all(&:pathable).each { |n| n.path_unset }
|
391
|
+
return points, false
|
392
|
+
end
|
393
|
+
|
394
|
+
def delete_points(points)
|
395
|
+
points.find_all {|f| !f.path_to.length.zero?}.each {|n| n.path_to.reject!(&:selected)}
|
396
|
+
points.find_all {|f| !f.path_from.length.zero?}.each {|n| n.path_from.reject!(&:selected)}
|
397
|
+
points.reject!(&:selected)
|
398
|
+
UI::prop_list_model.clear
|
399
|
+
UI::prop_mod.text = ""
|
400
|
+
return points
|
401
|
+
end
|
402
|
+
def delete_paths_to(points)
|
403
|
+
points.find_all {|f| !f.path_to.length.zero? && f.selected == true}.each {|n| n.path_to.each {|b| b.path_from.reject! {|g| g == n }}}
|
404
|
+
points.find_all {|f| !f.path_to.length.zero? && f.selected == true}.each {|n| n.path_to = []}
|
405
|
+
UI::canvas.queue_draw
|
406
|
+
end
|
407
|
+
def delete_paths_from(points)
|
408
|
+
points.find_all {|f| !f.path_from.length.zero? && f.selected == true}.each {|n| n.path_from.each {|b| b.path_to.reject! {|g| g == n }}}
|
409
|
+
points.find_all {|f| !f.path_from.length.zero? && f.selected == true}.each {|n| n.path_from = []}
|
410
|
+
UI::canvas.queue_draw
|
411
|
+
end
|
412
|
+
|
413
|
+
def move_points(diff,points)
|
414
|
+
if move_check(diff,points)
|
415
|
+
points.find_all(&:selected).each {|n| n.set_destination(diff) }
|
416
|
+
populate_prop(points)
|
417
|
+
end
|
418
|
+
return points
|
419
|
+
end
|
420
|
+
def move_check(diff,points)
|
421
|
+
points.find_all(&:selected).each do |n|
|
422
|
+
dest = n.origin.map
|
423
|
+
dest = dest.to_a
|
424
|
+
dest.map! {|g| g += diff[dest.find_index(g)]}
|
425
|
+
return false if points.find_all(&:not_selected).any? { |g| g.origin == dest}
|
426
|
+
end
|
427
|
+
return true
|
428
|
+
end
|
429
|
+
|
430
|
+
def set_start
|
431
|
+
CC.nouspoints.find_all(&:selected).each do |n|
|
432
|
+
if n.traveler_start == false
|
433
|
+
n.traveler_start = true
|
434
|
+
elsif n.traveler_start == true
|
435
|
+
n.traveler_start = false
|
436
|
+
end
|
437
|
+
end
|
438
|
+
UI::canvas.queue_draw
|
439
|
+
populate_prop(CC.nouspoints)
|
440
|
+
end
|
441
|
+
|
442
|
+
end
|
443
|
+
|
444
|
+
class NousPoint
|
445
|
+
include Logic_Controls
|
446
|
+
attr_accessor :source, :color, :path_to, :path_from, :note, :x, :y,
|
447
|
+
:velocity, :duration, :default_color, :path_mode,
|
448
|
+
:traveler_start, :channel, :playing, :play_modes,
|
449
|
+
:path_to_memory, :repeat, :repeating,
|
450
|
+
:use_rel, :selected, :save_id, :path_to_rels, :path_from_rels
|
451
|
+
attr_reader :pathable, :origin, :bounds
|
452
|
+
|
453
|
+
def initialize(o,save_id) #where the point was initially placed
|
454
|
+
@dp = [4,8,10,12,14,16,20]
|
455
|
+
|
456
|
+
@x = o[0]
|
457
|
+
@y = o[1]
|
458
|
+
@origin = o
|
459
|
+
@bounds = [@x-@dp[5],@y-@dp[5],@x+@dp[5],@y+@dp[5]]
|
460
|
+
@color = GREY #point color defaults to gray++
|
461
|
+
@path_color = CYAN
|
462
|
+
@default_color = GREY
|
463
|
+
@note = 60 #all notes start at middle c (C3), can be a note or a reference
|
464
|
+
@velocity = 100 # `` with 100 velocity
|
465
|
+
@channel = 1 # `` assigned to midi channel 1 (instrument 1, but we will refer to them as channels, not instruments)
|
466
|
+
@duration = 1 #length of note in grid points (should be considered beats)
|
467
|
+
@repeat = 0 #Number of times the node should repeat before moving on
|
468
|
+
@save_id = save_id
|
469
|
+
@play_modes = ["robin","split","portal","random"]
|
470
|
+
@traveler_start = false
|
471
|
+
@playing = false
|
472
|
+
@pathable = false
|
473
|
+
@selected = false
|
474
|
+
@source = false
|
475
|
+
@repeating = false
|
476
|
+
@use_rel = false
|
477
|
+
@path_to = [] #array of references to points that are receiving a path from this point
|
478
|
+
@path_to_memory = [] #memory of @path_to so that it can be reset upon stopping.
|
479
|
+
@path_to_rels = []
|
480
|
+
@path_from_rels = []
|
481
|
+
@path_from = [] #array of references to points that are sending a path to this point
|
482
|
+
@path_mode = "horz"
|
483
|
+
@chev_offsets = [0,0,0,0]
|
484
|
+
end
|
485
|
+
|
486
|
+
def set_rels
|
487
|
+
@path_to_rels = []
|
488
|
+
@path_from_rels = []
|
489
|
+
path_to.each {|pt| @path_to_rels << pt.save_id}
|
490
|
+
path_from.each {|pf| @path_from_rels << pf.save_id}
|
491
|
+
end
|
492
|
+
|
493
|
+
def write_props(file)
|
494
|
+
set_rels
|
495
|
+
file.write("#{@save_id}<~>")
|
496
|
+
file.write("#{@origin}<~>")
|
497
|
+
file.write("#{@note}<~>")
|
498
|
+
file.write("#{@velocity}<~>")
|
499
|
+
file.write("#{@channel}<~>")
|
500
|
+
file.write("#{@duration}<~>")
|
501
|
+
file.write("#{color_to_hex(@default_color)}<~>")
|
502
|
+
file.write("#{@repeat}<~>")
|
503
|
+
file.write("#{@play_modes}<~>")
|
504
|
+
file.write("#{@traveler_start}<~>")
|
505
|
+
file.write("#{@use_rel}<~>")
|
506
|
+
file.write("#{@path_mode}<~>")
|
507
|
+
file.write("#{@path_to_rels}<~>")
|
508
|
+
file.write("#{@path_from_rels}")
|
509
|
+
end
|
510
|
+
def read_props(file)
|
511
|
+
end
|
512
|
+
|
513
|
+
def not_selected
|
514
|
+
!@selected
|
515
|
+
end
|
516
|
+
def not_pathable
|
517
|
+
!@pathable
|
518
|
+
end
|
519
|
+
|
520
|
+
def origin=(o) #sets the origin of the point explicitly
|
521
|
+
@x = o[0]
|
522
|
+
@y = o[1]
|
523
|
+
@origin = o
|
524
|
+
@bounds = [@x-@dp[5],@y-@dp[5],@x+@dp[5],@y+@dp[5]]
|
525
|
+
end
|
526
|
+
|
527
|
+
def path_set(source_chosen)
|
528
|
+
@pathable = true
|
529
|
+
case source_chosen
|
530
|
+
when false #if source point was not chosen (first point clicked on path screen)
|
531
|
+
@source = true #Path source is now chosen on this node
|
532
|
+
@color = CYAN
|
533
|
+
return true
|
534
|
+
when true #Path source is already chosen in this operation
|
535
|
+
@color = GREEN
|
536
|
+
end
|
537
|
+
return source_chosen
|
538
|
+
end
|
539
|
+
def path_unset
|
540
|
+
@pathable = false
|
541
|
+
@source = false
|
542
|
+
@color = @default_color
|
543
|
+
end
|
544
|
+
|
545
|
+
def reset_path_to
|
546
|
+
if @path_to.length != @path_to_memory.length
|
547
|
+
@path_to_memory = []
|
548
|
+
UI.confirm("path_warning")
|
549
|
+
else
|
550
|
+
@path_to = []
|
551
|
+
@path_to_memory.each {|p| @path_to << p}
|
552
|
+
@path_to_memory = []
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
def set_default_color(c)
|
557
|
+
@color = c
|
558
|
+
@default_color = c
|
559
|
+
end
|
560
|
+
def set_destination(diff) #sets a new origin for the point based on x,y coordinate differences
|
561
|
+
@x += diff[0]
|
562
|
+
@y += diff[1]
|
563
|
+
@origin = [@x,@y]
|
564
|
+
@bounds = [@x-@dp[5],@y-@dp[5],@x+@dp[5],@y+@dp[5]]
|
565
|
+
end
|
566
|
+
def select #elevate color to denote 'selected' and sets a flag
|
567
|
+
@selected = true
|
568
|
+
end
|
569
|
+
def deselect #resets the color from elevated 'selected' values and sets a flag
|
570
|
+
@selected = false
|
571
|
+
@color = @default_color
|
572
|
+
end
|
573
|
+
|
574
|
+
def draw(cr) #point will always be drawn to this specification.
|
575
|
+
cr.set_source_rgba(@color[0],@color[1],@color[2],0.4)
|
576
|
+
if @traveler_start
|
577
|
+
traveler_start_draw(cr)
|
578
|
+
else
|
579
|
+
cr.rounded_rectangle(@x-@dp[1],@y-@dp[1],@dp[5],@dp[5],2,2) #slightly smaller rectangle adds 'relief' effect
|
580
|
+
end
|
581
|
+
cr.fill
|
582
|
+
|
583
|
+
cr.set_source_rgba(@color[0],@color[1],@color[2],1)
|
584
|
+
case @play_modes[0]
|
585
|
+
when "robin"
|
586
|
+
@path_color = CYAN
|
587
|
+
if @path_to.length > 1
|
588
|
+
cr.move_to(@x-8,@y)
|
589
|
+
cr.line_to(@x+6,@y-9)
|
590
|
+
cr.set_line_width(2)
|
591
|
+
cr.stroke
|
592
|
+
cr.move_to(@x-8,@y)
|
593
|
+
cr.set_dash([1,4],0)
|
594
|
+
cr.line_to(@x+6,@y+9)
|
595
|
+
cr.set_line_width(2)
|
596
|
+
cr.stroke
|
597
|
+
cr.set_dash([],0)
|
598
|
+
else
|
599
|
+
cr.circle(@x,@y,1)
|
600
|
+
cr.fill
|
601
|
+
end
|
602
|
+
when "split"
|
603
|
+
@path_color = CYAN
|
604
|
+
cr.move_to(@x-8,@y)
|
605
|
+
cr.line_to(@x+6,@y-9)
|
606
|
+
cr.move_to(@x-8,@y)
|
607
|
+
cr.line_to(@x+6,@y+9)
|
608
|
+
cr.set_line_width(2)
|
609
|
+
cr.stroke
|
610
|
+
when "portal"
|
611
|
+
@path_color = RED
|
612
|
+
cr.circle(@x,@y,6)
|
613
|
+
cr.set_line_width(2)
|
614
|
+
cr.stroke
|
615
|
+
when "random"
|
616
|
+
@path_color = VLET
|
617
|
+
cr.rectangle(@x-6,@y-2,8,8)
|
618
|
+
cr.rectangle(@x-2,@y-6,8,8)
|
619
|
+
cr.set_line_width(2)
|
620
|
+
cr.stroke
|
621
|
+
end
|
622
|
+
|
623
|
+
if @repeat > 0
|
624
|
+
#cr.move_to(@x-@dp[2],@y-@dp[2]) #top left of the point graphic
|
625
|
+
if @traveler_start
|
626
|
+
cr.move_to(@x+3,@y-@dp[2]-2)
|
627
|
+
cr.line_to(@x-2,-6+@y-@dp[2]-2)
|
628
|
+
cr.line_to(@x-2,6+@y-@dp[2]-2)
|
629
|
+
cr.line_to(@x+3,@y-@dp[2]-2)
|
630
|
+
cr.fill
|
631
|
+
cr.move_to(@x-3,@y+@dp[2]+2)
|
632
|
+
cr.line_to(@x+2,-6+@y+@dp[2]+2)
|
633
|
+
cr.line_to(@x+2,6+@y+@dp[2]+2)
|
634
|
+
cr.line_to(@x-3,@y+@dp[2]+2)
|
635
|
+
cr.fill
|
636
|
+
else
|
637
|
+
cr.move_to(@x-2,@y-@dp[2])
|
638
|
+
cr.line_to(@x-7,-6+@y-@dp[2])
|
639
|
+
cr.line_to(@x-7,6+@y-@dp[2])
|
640
|
+
cr.line_to(@x-2,@y-@dp[2])
|
641
|
+
cr.fill
|
642
|
+
cr.move_to(@x+2,@y+@dp[2])
|
643
|
+
cr.line_to(@x+7,-6+@y+@dp[2])
|
644
|
+
cr.line_to(@x+7,6+@y+@dp[2])
|
645
|
+
cr.line_to(@x+2,@y+@dp[2])
|
646
|
+
cr.fill
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
if !@selected
|
651
|
+
if @traveler_start
|
652
|
+
traveler_start_draw(cr)
|
653
|
+
else
|
654
|
+
cr.rounded_rectangle(@x-@dp[2],@y-@dp[2],@dp[6],@dp[6],2,2)
|
655
|
+
end
|
656
|
+
end
|
657
|
+
if @selected
|
658
|
+
cr.set_source_rgba(1,1,1,0.8)
|
659
|
+
if @traveler_start
|
660
|
+
traveler_start_draw(cr)
|
661
|
+
else
|
662
|
+
cr.rounded_rectangle(@x-@dp[2],@y-@dp[2],@dp[6],@dp[6],2,2) #a slightly smaller rectangle adds 'relief' effect
|
663
|
+
end
|
664
|
+
selection_caret_draw(cr)
|
665
|
+
end
|
666
|
+
cr.set_line_width(2)
|
667
|
+
cr.stroke
|
668
|
+
play_draw(cr) if @playing
|
669
|
+
repeat_draw(cr) if @repeating
|
670
|
+
end
|
671
|
+
|
672
|
+
def path_draw(cr)
|
673
|
+
if !@selected
|
674
|
+
|
675
|
+
cr.set_source_rgba(@path_color[0],@path_color[1],@path_color[2],0.6)
|
676
|
+
@path_to.each {|t| trace_path_to(cr,t)}
|
677
|
+
|
678
|
+
elsif @selected
|
679
|
+
|
680
|
+
cr.set_source_rgba(LGRN[0],LGRN[1],LGRN[2],0.8)
|
681
|
+
@path_to.each {|t| trace_path_to(cr,t)}
|
682
|
+
cr.set_line_cap(1) #Round
|
683
|
+
cr.set_line_join(2) #Miter
|
684
|
+
cr.set_line_width(3)
|
685
|
+
cr.stroke
|
686
|
+
|
687
|
+
end
|
688
|
+
end
|
689
|
+
def caret_draw(cr)
|
690
|
+
cr.set_dash([],0)
|
691
|
+
@chev_offsets = [0,0,0,0]
|
692
|
+
@path_from.each do |s|
|
693
|
+
input_mark_draw(cr,relative_pos(@x-s.x,@y-s.y),s)
|
694
|
+
cr.set_source_rgba(s.color[0],s.color[1],s.color[2],1)
|
695
|
+
cr.fill
|
696
|
+
end
|
697
|
+
end
|
698
|
+
def play_draw(cr) #If a note is playing, show a visual indicator
|
699
|
+
cr.set_source_rgb(@color[0],@color[1],@color[2])
|
700
|
+
if @traveler_start
|
701
|
+
traveler_start_draw(cr)
|
702
|
+
else
|
703
|
+
cr.rounded_rectangle(@x-@dp[2],@y-@dp[2],@dp[6],@dp[6],2,2)
|
704
|
+
end
|
705
|
+
cr.fill
|
706
|
+
end
|
707
|
+
def repeat_draw(cr)
|
708
|
+
cr.set_source_rgb(@color[0],@color[1],@color[2])
|
709
|
+
cr.rounded_rectangle(@x-@dp[2]+3,@y-@dp[2]+3,@dp[6]-6,@dp[6]-6,2,2)
|
710
|
+
cr.fill
|
711
|
+
end
|
712
|
+
|
713
|
+
def traveler_start_draw(cr) #Shape of a traveler start position
|
714
|
+
cr.move_to(@x-@dp[0],@y-@dp[3])
|
715
|
+
cr.line_to(@x+@dp[0],@y-@dp[3])
|
716
|
+
cr.line_to(@x+@dp[1],@y-@dp[1])
|
717
|
+
cr.line_to(@x+@dp[1],@y+@dp[1])
|
718
|
+
cr.line_to(@x+@dp[0],@y+@dp[3])
|
719
|
+
cr.line_to(@x-@dp[0],@y+@dp[3])
|
720
|
+
cr.line_to(@x-@dp[1],@y+@dp[1])
|
721
|
+
cr.line_to(@x-@dp[1],@y-@dp[1])
|
722
|
+
cr.line_to(@x-@dp[0],@y-@dp[3])
|
723
|
+
end
|
724
|
+
def selection_caret_draw(cr) #Shape of a selection caret
|
725
|
+
cr.move_to(@x-@dp[4],@y-@dp[4])
|
726
|
+
cr.line_to(@x-@dp[2],@y-@dp[4])
|
727
|
+
cr.move_to(@x-@dp[4],@y-@dp[4])
|
728
|
+
cr.line_to(@x-@dp[4],@y-@dp[2])
|
729
|
+
|
730
|
+
cr.move_to(@x+@dp[4],@y-@dp[4])
|
731
|
+
cr.line_to(@x+@dp[2],@y-@dp[4])
|
732
|
+
cr.move_to(@x+@dp[4],@y-@dp[4])
|
733
|
+
cr.line_to(@x+@dp[4],@y-@dp[2])
|
734
|
+
|
735
|
+
cr.move_to(@x-@dp[4],@y+@dp[4])
|
736
|
+
cr.line_to(@x-@dp[2],@y+@dp[4])
|
737
|
+
cr.move_to(@x-@dp[4],@y+@dp[4])
|
738
|
+
cr.line_to(@x-@dp[4],@y+@dp[2])
|
739
|
+
|
740
|
+
cr.move_to(@x+@dp[4],@y+@dp[4])
|
741
|
+
cr.line_to(@x+@dp[2],@y+@dp[4])
|
742
|
+
cr.move_to(@x+@dp[4],@y+@dp[4])
|
743
|
+
cr.line_to(@x+@dp[4],@y+@dp[2])
|
744
|
+
end
|
745
|
+
def trace_path_to(cr,t)
|
746
|
+
|
747
|
+
case @play_modes[0]
|
748
|
+
when "robin"
|
749
|
+
cr.set_dash([5,5],0)
|
750
|
+
if @path_to[0] == t
|
751
|
+
cr.set_dash([],0)
|
752
|
+
end
|
753
|
+
when "portal"
|
754
|
+
cr.set_dash([1,5],0)
|
755
|
+
end
|
756
|
+
|
757
|
+
rel_pos = relative_pos(t.x-@x,t.y-@y)
|
758
|
+
case rel_pos
|
759
|
+
when "n"
|
760
|
+
cr.move_to(@x,@y-10)
|
761
|
+
cr.line_to(t.x,t.y+10)
|
762
|
+
when "s"
|
763
|
+
cr.move_to(@x,@y+10)
|
764
|
+
cr.line_to(t.x,t.y-10)
|
765
|
+
when "e"
|
766
|
+
cr.move_to(@x+10,@y)
|
767
|
+
cr.line_to(t.x-10,t.y)
|
768
|
+
when "w"
|
769
|
+
cr.move_to(@x-10,@y)
|
770
|
+
cr.line_to(t.x+10,t.y)
|
771
|
+
end
|
772
|
+
|
773
|
+
case @path_mode
|
774
|
+
when "horz"
|
775
|
+
case rel_pos
|
776
|
+
when "ne"
|
777
|
+
cr.move_to(@x+10,@y)
|
778
|
+
cr.line_to(t.x,@y)
|
779
|
+
cr.line_to(t.x,t.y+10)
|
780
|
+
when "nw"
|
781
|
+
cr.move_to(@x-10,@y)
|
782
|
+
cr.line_to(t.x,@y)
|
783
|
+
cr.line_to(t.x,t.y+10)
|
784
|
+
when "se"
|
785
|
+
cr.move_to(@x+10,@y)
|
786
|
+
cr.line_to(t.x,@y)
|
787
|
+
cr.line_to(t.x,t.y-10)
|
788
|
+
when "sw"
|
789
|
+
cr.move_to(@x-10,@y)
|
790
|
+
cr.line_to(t.x,@y)
|
791
|
+
cr.line_to(t.x,t.y-10)
|
792
|
+
end
|
793
|
+
when "vert"
|
794
|
+
case rel_pos
|
795
|
+
when "ne"
|
796
|
+
cr.move_to(@x,@y-10)
|
797
|
+
cr.line_to(@x,t.y)
|
798
|
+
cr.line_to(t.x-10,t.y)
|
799
|
+
when "nw"
|
800
|
+
cr.move_to(@x,@y-10)
|
801
|
+
cr.line_to(@x,t.y)
|
802
|
+
cr.line_to(t.x+10,t.y)
|
803
|
+
when "se"
|
804
|
+
cr.move_to(@x,@y+10)
|
805
|
+
cr.line_to(@x,t.y)
|
806
|
+
cr.line_to(t.x-10,t.y)
|
807
|
+
when "sw"
|
808
|
+
cr.move_to(@x,@y+10)
|
809
|
+
cr.line_to(@x,t.y)
|
810
|
+
cr.line_to(t.x+10,t.y)
|
811
|
+
end
|
812
|
+
when "line"
|
813
|
+
cr.move_to(@x,@y)
|
814
|
+
cr.line_to(t.x,t.y)
|
815
|
+
end
|
816
|
+
cr.set_line_cap(1) #Round
|
817
|
+
cr.set_line_join(2) #Miter
|
818
|
+
cr.set_line_width(3)
|
819
|
+
cr.stroke
|
820
|
+
end
|
821
|
+
|
822
|
+
def trace_path_from(cr,s)
|
823
|
+
case s.path_mode
|
824
|
+
when "horz"
|
825
|
+
cr.move_to(s.x,s.y)
|
826
|
+
cr.line_to(@x,s.y)
|
827
|
+
cr.line_to(@x,@y)
|
828
|
+
when "vert"
|
829
|
+
cr.move_to(s.x,s.y)
|
830
|
+
cr.line_to(s.x,@y)
|
831
|
+
cr.line_to(@x,@y)
|
832
|
+
when "line"
|
833
|
+
cr.move_to(s.x,s.y)
|
834
|
+
cr.line_to(@x,@y)
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
def input_mark_draw(cr,rel_pos,s)
|
839
|
+
if s.path_mode == "horz"
|
840
|
+
case rel_pos
|
841
|
+
when "n","ne","nw"
|
842
|
+
draw_chevron(cr,@chev_offsets[0],"n",self)
|
843
|
+
@chev_offsets[0] += 5
|
844
|
+
when "s","se","sw"
|
845
|
+
draw_chevron(cr,@chev_offsets[1],"s",self)
|
846
|
+
@chev_offsets[1] += 5
|
847
|
+
when "e"
|
848
|
+
draw_chevron(cr,@chev_offsets[2],"e",self)
|
849
|
+
@chev_offsets[2] += 5
|
850
|
+
when "w"
|
851
|
+
draw_chevron(cr,@chev_offsets[3],"w",self)
|
852
|
+
@chev_offsets[3] += 5
|
853
|
+
end
|
854
|
+
elsif s.path_mode == "vert"
|
855
|
+
case rel_pos
|
856
|
+
when "n"
|
857
|
+
draw_chevron(cr,@chev_offsets[0],"n",self)
|
858
|
+
@chev_offsets[0] += 5
|
859
|
+
when "s"
|
860
|
+
draw_chevron(cr,@chev_offsets[1],"s",self)
|
861
|
+
@chev_offsets[1] += 5
|
862
|
+
when "e","ne","se"
|
863
|
+
draw_chevron(cr,@chev_offsets[2],"e",self)
|
864
|
+
@chev_offsets[2] += 5
|
865
|
+
when "w","nw","sw"
|
866
|
+
draw_chevron(cr,@chev_offsets[3],"w",self)
|
867
|
+
@chev_offsets[3] += 5
|
868
|
+
end
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
end
|
873
|
+
|
874
|
+
class Traveler #A traveler handles the source note playing and creates another traveler if the destination is reached.
|
875
|
+
attr_reader :remove, :dest, :dest_origin, :last_played_note, :played_note
|
876
|
+
attr_accessor :reached
|
877
|
+
def initialize(srce,dest,lpn) #Traveler should play note when reaches destination. Should not need to create another traveler if it's a dead end.
|
878
|
+
@srce = srce
|
879
|
+
@dest = dest
|
880
|
+
@srce_origin = srce.origin
|
881
|
+
@dest_origin = dest.origin
|
882
|
+
@repeat = dest.repeat
|
883
|
+
@travel_c = 0
|
884
|
+
@iteration = 0
|
885
|
+
@last_played_note = lpn
|
886
|
+
@played_note = nil
|
887
|
+
@distance = ((@dest_origin[0] - @srce_origin[0]).abs + (@dest_origin[1] - @srce_origin[1]).abs)/CC.grid_spacing
|
888
|
+
@reached = false
|
889
|
+
@remove = false
|
890
|
+
end
|
891
|
+
|
892
|
+
def travel
|
893
|
+
@travel_c += 1
|
894
|
+
if @travel_c == @distance
|
895
|
+
@dest.playing = true
|
896
|
+
@reached = true
|
897
|
+
play_check(@srce.use_rel,@dest.use_rel)
|
898
|
+
elsif @travel_c == (@distance + @dest.duration)
|
899
|
+
@dest.playing = false
|
900
|
+
CC.repeaters << Repeater.new(@dest,@repeat,@played_note) if @repeat > 0
|
901
|
+
queue_removal
|
902
|
+
end
|
903
|
+
end
|
904
|
+
|
905
|
+
def play_check(srce_rel,dest_rel)
|
906
|
+
if !dest_rel
|
907
|
+
@played_note = @dest.note
|
908
|
+
play_note
|
909
|
+
elsif srce_rel && dest_rel
|
910
|
+
@played_note = @last_played_note
|
911
|
+
play_relative
|
912
|
+
elsif !srce_rel && dest_rel
|
913
|
+
@played_note = @srce.note.to_i
|
914
|
+
play_relative
|
915
|
+
end
|
916
|
+
end
|
917
|
+
def play_note
|
918
|
+
queued_notes = []
|
919
|
+
CC.queued_note_plays.each {|q| queued_notes << [q.note,q.chan]}
|
920
|
+
CC.queued_note_plays << NoteSender.new(@played_note,@dest.channel,@dest.velocity) unless queued_notes.find {|f| @played_note == f[0] && @dest.channel == f[1]}
|
921
|
+
end
|
922
|
+
def play_relative
|
923
|
+
#search the current scales variable and round to nearest note.
|
924
|
+
rel = @dest.note.to_i
|
925
|
+
pn = @played_note
|
926
|
+
if @dest.note.to_s.include?("+")
|
927
|
+
(pn..127).each do |s|
|
928
|
+
rel -= 1 if CC.scale_posns[s] == true && s != pn
|
929
|
+
if rel == 0
|
930
|
+
pn = s
|
931
|
+
break
|
932
|
+
end
|
933
|
+
end
|
934
|
+
elsif @dest.note.to_s.include?("-")
|
935
|
+
(0..pn).reverse_each do |s|
|
936
|
+
rel += 1 if CC.scale_posns[s] == true && s != pn
|
937
|
+
if rel == 0
|
938
|
+
pn = s
|
939
|
+
break
|
940
|
+
end
|
941
|
+
end
|
942
|
+
end
|
943
|
+
@played_note = pn
|
944
|
+
play_note
|
945
|
+
end
|
946
|
+
|
947
|
+
def queue_removal
|
948
|
+
CC.queued_note_stops << NoteSender.new(@played_note,@dest.channel,0)
|
949
|
+
|
950
|
+
@remove = true
|
951
|
+
end
|
952
|
+
|
953
|
+
end
|
954
|
+
|
955
|
+
class Starter #A starter handles notes that are used as starting points for paths and point jumps for portals
|
956
|
+
attr_reader :remove, :last_played_note
|
957
|
+
def initialize(portal_srce,srce,lpn)
|
958
|
+
@travel_c = 0
|
959
|
+
@srce = srce
|
960
|
+
@portal_srce = portal_srce
|
961
|
+
@duration = srce.duration
|
962
|
+
@remove = false
|
963
|
+
@repeat = srce.repeat
|
964
|
+
@played_note = nil
|
965
|
+
@last_played_note = lpn
|
966
|
+
|
967
|
+
@srce.playing = true
|
968
|
+
if @portal_srce != nil
|
969
|
+
play_check(@portal_srce.use_rel,@srce.use_rel)
|
970
|
+
else
|
971
|
+
@played_note = @srce.note
|
972
|
+
play_note
|
973
|
+
end
|
974
|
+
end
|
975
|
+
|
976
|
+
def travel
|
977
|
+
@travel_c += 1
|
978
|
+
if @travel_c == @duration
|
979
|
+
@srce.playing = false
|
980
|
+
CC.queued_note_stops << NoteSender.new(@played_note,@srce.channel,0)
|
981
|
+
CC.repeaters << Repeater.new(@srce,@repeat,@played_note) if @repeat > 0
|
982
|
+
@remove = true
|
983
|
+
end
|
984
|
+
end
|
985
|
+
|
986
|
+
def play_check(portal_srce_rel,srce_rel)
|
987
|
+
if !srce_rel
|
988
|
+
@played_note = @srce.note
|
989
|
+
play_note
|
990
|
+
elsif portal_srce_rel && srce_rel
|
991
|
+
@played_note = @last_played_note
|
992
|
+
play_relative
|
993
|
+
elsif !portal_srce_rel && srce_rel
|
994
|
+
@played_note = @portal_srce.note.to_i
|
995
|
+
play_relative
|
996
|
+
end
|
997
|
+
end
|
998
|
+
def play_note
|
999
|
+
queued_notes = []
|
1000
|
+
CC.queued_note_plays.each {|q| queued_notes << [q.note,q.chan]}
|
1001
|
+
CC.queued_note_plays << NoteSender.new(@played_note,@srce.channel,@srce.velocity) unless queued_notes.find {|f| @played_note == f[0] && @srce.channel == f[1]}
|
1002
|
+
end
|
1003
|
+
def play_relative
|
1004
|
+
#search the current scales variable and round to nearest note.
|
1005
|
+
rel = @srce.note.to_i
|
1006
|
+
pn = @played_note
|
1007
|
+
if @srce.note.to_s.include?("+")
|
1008
|
+
(pn..127).each do |s|
|
1009
|
+
rel -= 1 if CC.scale_posns[s] == true && s != pn
|
1010
|
+
if rel == 0
|
1011
|
+
pn = s
|
1012
|
+
break
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
elsif @srce.note.to_s.include?("-")
|
1016
|
+
(0..pn).reverse_each do |s|
|
1017
|
+
rel += 1 if CC.scale_posns[s] == true && s != pn
|
1018
|
+
if rel == 0
|
1019
|
+
pn = s
|
1020
|
+
break
|
1021
|
+
end
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
@played_note = pn
|
1025
|
+
play_note
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
class Repeater #A repeater handles notes that are set to repeat/arpeggiate. This logic is currently FUCKED
|
1031
|
+
attr_reader :remove
|
1032
|
+
def initialize(srce,count,played_note)
|
1033
|
+
@srce = srce
|
1034
|
+
@dur = srce.duration
|
1035
|
+
@timer = (count * @dur)
|
1036
|
+
@played_note = played_note
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
def repeat
|
1040
|
+
if @timer == 0
|
1041
|
+
CC.queued_note_stops << NoteSender.new(@played_note,@srce.channel,0)
|
1042
|
+
@srce.repeating = false
|
1043
|
+
@remove = true
|
1044
|
+
end
|
1045
|
+
unless @remove == true
|
1046
|
+
@srce.repeating = true
|
1047
|
+
play_note if @timer % @dur == 0
|
1048
|
+
end
|
1049
|
+
@timer -= 1
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
def play_note
|
1053
|
+
queued_notes = []
|
1054
|
+
CC.queued_note_plays.each {|q| queued_notes << [q.note,q.chan]}
|
1055
|
+
CC.queued_note_plays << NoteSender.new(@played_note,@srce.channel,@srce.velocity) unless queued_notes.find {|f| @played_note == f[0] && @srce.channel == f[1]}
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
Pl = Point_Logic.new
|