musa-dsl 0.14.26 → 0.21.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/Gemfile +0 -1
- data/README.md +5 -1
- data/lib/musa-dsl.rb +54 -11
- data/lib/musa-dsl/core-ext.rb +7 -13
- data/lib/musa-dsl/core-ext/array-explode-ranges.rb +15 -23
- data/lib/musa-dsl/core-ext/arrayfy.rb +30 -12
- data/lib/musa-dsl/core-ext/attribute-builder.rb +194 -0
- data/lib/musa-dsl/core-ext/deep-copy.rb +185 -0
- data/lib/musa-dsl/core-ext/dynamic-proxy.rb +44 -40
- data/lib/musa-dsl/core-ext/inspect-nice.rb +40 -22
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +108 -0
- data/lib/musa-dsl/core-ext/with.rb +26 -0
- data/lib/musa-dsl/datasets.rb +8 -3
- data/lib/musa-dsl/datasets/dataset.rb +3 -0
- data/lib/musa-dsl/datasets/delta-d.rb +12 -0
- data/lib/musa-dsl/datasets/e.rb +61 -0
- data/lib/musa-dsl/datasets/gdv.rb +51 -411
- data/lib/musa-dsl/datasets/gdvd.rb +179 -0
- data/lib/musa-dsl/datasets/helper.rb +41 -0
- data/lib/musa-dsl/datasets/p.rb +68 -0
- data/lib/musa-dsl/datasets/packed-v.rb +19 -0
- data/lib/musa-dsl/datasets/pdv.rb +22 -15
- data/lib/musa-dsl/datasets/ps.rb +113 -0
- data/lib/musa-dsl/datasets/score.rb +210 -0
- data/lib/musa-dsl/datasets/score/queriable.rb +48 -0
- data/lib/musa-dsl/datasets/score/render.rb +31 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +160 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +51 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +153 -0
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +158 -0
- data/lib/musa-dsl/datasets/v.rb +23 -0
- data/lib/musa-dsl/generative.rb +5 -5
- data/lib/musa-dsl/generative/backboner.rb +274 -0
- data/lib/musa-dsl/generative/darwin.rb +102 -96
- data/lib/musa-dsl/generative/generative-grammar.rb +182 -187
- data/lib/musa-dsl/generative/markov.rb +56 -53
- data/lib/musa-dsl/generative/variatio.rb +234 -222
- data/lib/musa-dsl/logger.rb +1 -0
- data/lib/musa-dsl/logger/logger.rb +31 -0
- data/lib/musa-dsl/matrix.rb +1 -0
- data/lib/musa-dsl/matrix/matrix.rb +210 -0
- data/lib/musa-dsl/midi.rb +2 -2
- data/lib/musa-dsl/midi/midi-recorder.rb +54 -52
- data/lib/musa-dsl/midi/midi-voices.rb +183 -182
- data/lib/musa-dsl/music.rb +5 -5
- data/lib/musa-dsl/music/chord-definition.rb +54 -50
- data/lib/musa-dsl/music/chord-definitions.rb +13 -9
- data/lib/musa-dsl/music/chords.rb +236 -238
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +187 -183
- data/lib/musa-dsl/music/scales.rb +331 -332
- data/lib/musa-dsl/musicxml.rb +1 -0
- data/lib/musa-dsl/musicxml/builder/attributes.rb +155 -0
- data/lib/musa-dsl/musicxml/builder/backup-forward.rb +45 -0
- data/lib/musa-dsl/musicxml/builder/direction.rb +322 -0
- data/lib/musa-dsl/musicxml/builder/helper.rb +90 -0
- data/lib/musa-dsl/musicxml/builder/measure.rb +137 -0
- data/lib/musa-dsl/musicxml/builder/note-complexities.rb +152 -0
- data/lib/musa-dsl/musicxml/builder/note.rb +577 -0
- data/lib/musa-dsl/musicxml/builder/part-group.rb +44 -0
- data/lib/musa-dsl/musicxml/builder/part.rb +67 -0
- data/lib/musa-dsl/musicxml/builder/pitched-note.rb +126 -0
- data/lib/musa-dsl/musicxml/builder/rest.rb +117 -0
- data/lib/musa-dsl/musicxml/builder/score-partwise.rb +120 -0
- data/lib/musa-dsl/musicxml/builder/typed-text.rb +43 -0
- data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +112 -0
- data/lib/musa-dsl/neumalang.rb +1 -1
- data/lib/musa-dsl/neumalang/datatypes.citrus +79 -0
- data/lib/musa-dsl/neumalang/neuma.citrus +165 -0
- data/lib/musa-dsl/neumalang/neumalang.citrus +32 -242
- data/lib/musa-dsl/neumalang/neumalang.rb +373 -142
- data/lib/musa-dsl/neumalang/process.citrus +21 -0
- data/lib/musa-dsl/neumalang/terminals.citrus +67 -0
- data/lib/musa-dsl/neumalang/vectors.citrus +23 -0
- data/lib/musa-dsl/neumas.rb +5 -0
- data/lib/musa-dsl/neumas/array-to-neumas.rb +34 -0
- data/lib/musa-dsl/neumas/neuma-decoder.rb +63 -0
- data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +57 -0
- data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +15 -0
- data/lib/musa-dsl/neumas/neumas.rb +37 -0
- data/lib/musa-dsl/neumas/string-to-neumas.rb +34 -0
- data/lib/musa-dsl/repl.rb +1 -1
- data/lib/musa-dsl/repl/repl.rb +105 -105
- data/lib/musa-dsl/sequencer.rb +1 -1
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +163 -136
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +301 -286
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +562 -270
- data/lib/musa-dsl/sequencer/base-sequencer-public.rb +199 -199
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +77 -0
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +75 -0
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +105 -85
- data/lib/musa-dsl/sequencer/timeslots.rb +34 -0
- data/lib/musa-dsl/series.rb +1 -1
- data/lib/musa-dsl/{core-ext → series}/array-to-serie.rb +1 -1
- data/lib/musa-dsl/series/base-series.rb +171 -168
- data/lib/musa-dsl/series/hash-serie-splitter.rb +134 -132
- data/lib/musa-dsl/series/holder-serie.rb +1 -1
- data/lib/musa-dsl/series/main-serie-constructors.rb +6 -1
- data/lib/musa-dsl/series/main-serie-operations.rb +807 -797
- data/lib/musa-dsl/series/proxy-serie.rb +6 -6
- data/lib/musa-dsl/series/queue-serie.rb +5 -5
- data/lib/musa-dsl/series/series.rb +2 -0
- data/lib/musa-dsl/transcription.rb +4 -0
- data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +227 -0
- data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +36 -0
- data/lib/musa-dsl/transcription/from-gdv.rb +17 -0
- data/lib/musa-dsl/transcription/transcription.rb +55 -0
- data/lib/musa-dsl/transport.rb +6 -6
- data/lib/musa-dsl/transport/clock.rb +26 -26
- data/lib/musa-dsl/transport/dummy-clock.rb +32 -30
- data/lib/musa-dsl/transport/external-tick-clock.rb +21 -20
- data/lib/musa-dsl/transport/input-midi-clock.rb +82 -80
- data/lib/musa-dsl/transport/timer-clock.rb +72 -71
- data/lib/musa-dsl/transport/timer.rb +28 -26
- data/lib/musa-dsl/transport/transport.rb +99 -95
- data/musa-dsl.gemspec +3 -3
- metadata +73 -24
- data/lib/musa-dsl/core-ext/array-apply-get.rb +0 -18
- data/lib/musa-dsl/core-ext/array-to-neumas.rb +0 -28
- data/lib/musa-dsl/core-ext/as-context-run.rb +0 -44
- data/lib/musa-dsl/core-ext/duplicate.rb +0 -134
- data/lib/musa-dsl/core-ext/key-parameters-procedure-binder.rb +0 -85
- data/lib/musa-dsl/core-ext/proc-nice.rb +0 -13
- data/lib/musa-dsl/core-ext/send-nice.rb +0 -21
- data/lib/musa-dsl/core-ext/string-to-neumas.rb +0 -27
- data/lib/musa-dsl/datasets/gdv-decorators.rb +0 -221
- data/lib/musa-dsl/generative/rules.rb +0 -282
- data/lib/musa-dsl/neuma.rb +0 -1
- data/lib/musa-dsl/neuma/neuma.rb +0 -181
@@ -1,46 +1,48 @@
|
|
1
|
-
|
1
|
+
require_relative 'clock'
|
2
2
|
|
3
3
|
module Musa
|
4
|
-
|
5
|
-
|
6
|
-
do_log
|
4
|
+
module Clock
|
5
|
+
class DummyClock < Clock
|
6
|
+
def initialize(ticks = nil, do_log: nil, &block)
|
7
|
+
do_log ||= false
|
7
8
|
|
8
|
-
|
9
|
+
super()
|
9
10
|
|
10
|
-
|
11
|
+
raise ArgumentError, 'Cannot initialize with ticks and block. You can only use one of the parameters.' if ticks && block
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
@ticks = ticks
|
14
|
+
@do_log = do_log
|
15
|
+
@block = block
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
+
attr_accessor :block, :ticks
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def run
|
21
|
+
@on_start.each(&:call)
|
22
|
+
@run = true
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
while @run && eval_condition
|
25
|
+
yield if block_given?
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
Thread.pass
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
@on_stop.each(&:call)
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
def terminate
|
34
|
+
@run = false
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
+
private
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
def eval_condition
|
40
|
+
if @ticks
|
41
|
+
@ticks -= 1
|
42
|
+
@ticks > 0
|
43
|
+
else
|
44
|
+
@block.call
|
45
|
+
end
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
@@ -1,31 +1,32 @@
|
|
1
|
-
|
2
|
-
require 'nibbler'
|
1
|
+
require_relative 'clock'
|
3
2
|
|
4
3
|
module Musa
|
5
|
-
|
6
|
-
|
7
|
-
do_log
|
4
|
+
module Clock
|
5
|
+
class ExternalTickClock < Clock
|
6
|
+
def initialize(do_log: nil)
|
7
|
+
do_log ||= false
|
8
8
|
|
9
|
-
|
9
|
+
super()
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
@do_log = do_log
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
def run(&block)
|
15
|
+
@on_start.each(&:call)
|
16
|
+
@run = true
|
17
|
+
@block = block
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def tick
|
21
|
+
if @run
|
22
|
+
@block.call if @block
|
23
|
+
end
|
23
24
|
end
|
24
|
-
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
def terminate
|
27
|
+
@on_stop.each(&:call)
|
28
|
+
@run = false
|
29
|
+
end
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
@@ -1,123 +1,125 @@
|
|
1
|
-
|
1
|
+
require_relative 'clock'
|
2
2
|
require 'nibbler'
|
3
3
|
|
4
4
|
module Musa
|
5
|
-
|
6
|
-
|
7
|
-
do_log
|
5
|
+
module Clock
|
6
|
+
class InputMidiClock < Clock
|
7
|
+
def initialize(input, do_log: nil)
|
8
|
+
do_log ||= false
|
8
9
|
|
9
|
-
|
10
|
+
super()
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
@input = input
|
13
|
+
@do_log = do_log
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
@nibbler = Nibbler.new
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
def run
|
19
|
+
@run = true
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
while @run
|
22
|
+
raw_messages = @input.gets
|
23
|
+
@input.buffer.clear
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
messages = []
|
26
|
+
stop_index = nil
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
raw_messages.each do |message|
|
29
|
+
mm = @nibbler.parse message[:data]
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
if mm
|
32
|
+
if mm.is_a? Array
|
33
|
+
mm.each do |m|
|
34
|
+
stop_index = messages.size if m.name == 'Stop' && !stop_index
|
35
|
+
messages << m
|
36
|
+
end
|
37
|
+
else
|
38
|
+
stop_index = messages.size if mm.name == 'Stop' && !stop_index
|
39
|
+
messages << mm
|
35
40
|
end
|
36
|
-
else
|
37
|
-
stop_index = messages.size if mm.name == 'Stop' && !stop_index
|
38
|
-
messages << mm
|
39
41
|
end
|
40
42
|
end
|
41
|
-
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
@nibbler.processed.clear
|
45
|
+
@nibbler.rejected.clear
|
46
|
+
@nibbler.messages.clear
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
size = messages.size
|
49
|
+
index = 0
|
50
|
+
while index < size
|
51
|
+
if index == stop_index && size >= index + 3 &&
|
52
|
+
messages[index + 1].name == 'Song Position Pointer' &&
|
53
|
+
messages[index + 2].name == 'Continue'
|
53
54
|
|
54
|
-
|
55
|
+
warn 'InputMidiClock: processing Stop + Song Position Pointer + Continue...' if @do_log
|
55
56
|
|
56
|
-
|
57
|
+
process_start unless @started
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
59
|
+
process_message messages[index + 1] do
|
60
|
+
yield if block_given?
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
+
index += 2
|
63
64
|
|
64
|
-
|
65
|
+
warn 'InputMidiClock: processing Stop + Song Position Pointer + Continue... done' if @do_log
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
else
|
68
|
+
process_message messages[index] do
|
69
|
+
yield if block_given?
|
70
|
+
end
|
69
71
|
end
|
72
|
+
|
73
|
+
index += 1
|
70
74
|
end
|
71
75
|
|
72
|
-
|
76
|
+
Thread.pass
|
73
77
|
end
|
74
|
-
|
75
|
-
Thread.pass
|
76
78
|
end
|
77
|
-
end
|
78
79
|
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
def terminate
|
81
|
+
@run = false
|
82
|
+
end
|
82
83
|
|
83
|
-
|
84
|
+
private
|
84
85
|
|
85
|
-
|
86
|
-
|
86
|
+
def process_start
|
87
|
+
warn 'InputMidiClock: processing Start...' if @do_log
|
87
88
|
|
88
|
-
|
89
|
-
|
89
|
+
@on_start.each(&:call)
|
90
|
+
@started = true
|
90
91
|
|
91
|
-
|
92
|
-
|
92
|
+
warn 'InputMidiClock: processing Start... done' if @do_log
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
def process_message(m)
|
96
|
+
case m.name
|
97
|
+
when 'Start'
|
98
|
+
process_start
|
98
99
|
|
99
|
-
|
100
|
-
|
100
|
+
when 'Stop'
|
101
|
+
warn 'InputMidiClock: processing Stop...' if @do_log
|
101
102
|
|
102
|
-
|
103
|
-
|
103
|
+
@on_stop.each(&:call)
|
104
|
+
@started = false
|
104
105
|
|
105
|
-
|
106
|
+
warn 'InputMidiClock: processing Stop... done' if @do_log
|
106
107
|
|
107
|
-
|
108
|
-
|
109
|
-
|
108
|
+
when 'Continue'
|
109
|
+
warn 'InputMidiClock: processing Continue...' if @do_log
|
110
|
+
warn 'InputMidiClock: processing Continue... done' if @do_log
|
110
111
|
|
111
|
-
|
112
|
-
|
112
|
+
when 'Clock'
|
113
|
+
yield if block_given? && @started
|
113
114
|
|
114
|
-
|
115
|
-
|
116
|
-
|
115
|
+
when 'Song Position Pointer'
|
116
|
+
new_position_in_midi_beats =
|
117
|
+
m.data[0] & 0x7F | ((m.data[1] & 0x7F) << 7)
|
117
118
|
|
118
|
-
|
119
|
-
|
120
|
-
|
119
|
+
warn "InputMidiClock: processing Song Position Pointer new_position_in_midi_beats #{new_position_in_midi_beats}..." if @do_log
|
120
|
+
@on_change_position.each { |block| block.call midi_beats: new_position_in_midi_beats }
|
121
|
+
warn "InputMidiClock: processing Song Position Pointer new_position_in_beats #{new_position_in_midi_beats}... done" if @do_log
|
122
|
+
end
|
121
123
|
end
|
122
124
|
end
|
123
125
|
end
|
@@ -1,102 +1,103 @@
|
|
1
|
-
|
2
|
-
require 'nibbler'
|
1
|
+
require_relative 'clock'
|
3
2
|
|
4
3
|
module Musa
|
5
|
-
|
6
|
-
|
7
|
-
do_log
|
4
|
+
module Clock
|
5
|
+
class TimerClock < Clock
|
6
|
+
def initialize(period = nil, ticks_per_beat: nil, bpm: nil, correction: nil, do_log: nil)
|
7
|
+
do_log ||= false
|
8
8
|
|
9
|
-
|
9
|
+
super()
|
10
10
|
|
11
|
-
|
11
|
+
@correction = correction
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
self.period = period if period
|
14
|
+
self.ticks_per_beat = ticks_per_beat if ticks_per_beat
|
15
|
+
self.bpm = bpm if bpm
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
self.bpm ||= 120
|
18
|
+
self.ticks_per_beat ||= 24
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
@started = false
|
21
|
+
@paused = false
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
@do_log = do_log
|
24
|
+
end
|
25
25
|
|
26
|
-
|
26
|
+
attr_reader :period, :ticks_per_beat, :bpm
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
def period=(period_in_seconds)
|
29
|
+
@period = period_in_seconds.rationalize
|
30
|
+
@bpm = 60r / (@period * @ticks_per_beat) if @period && @ticks_per_beat
|
31
|
+
@timer.period = @period if @timer
|
32
|
+
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
def ticks_per_beat=(ticks)
|
35
|
+
@ticks_per_beat = ticks.rationalize
|
36
|
+
@period = 60r / (@bpm * @ticks_per_beat) if @bpm && @ticks_per_beat
|
37
|
+
@timer.period = @period if @timer && @period
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
def bpm=(bpm)
|
41
|
+
@bpm = bpm.rationalize
|
42
|
+
@period = 60r / (@bpm * @ticks_per_beat) if @bpm && @ticks_per_beat
|
43
|
+
@timer.period = @period if @timer && @period
|
44
|
+
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
def started?
|
47
|
+
@started
|
48
|
+
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
def paused?
|
51
|
+
@paused
|
52
|
+
end
|
53
53
|
|
54
|
-
|
55
|
-
|
54
|
+
def run
|
55
|
+
@run = true
|
56
56
|
|
57
|
-
|
58
|
-
|
57
|
+
while @run
|
58
|
+
@timer = Timer.new(@period, correction: @correction, stop: true)
|
59
59
|
|
60
|
-
|
61
|
-
|
60
|
+
@timer.run do
|
61
|
+
yield if block_given?
|
62
|
+
end
|
62
63
|
end
|
63
64
|
end
|
64
|
-
end
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
66
|
+
def start
|
67
|
+
unless @started
|
68
|
+
@on_start.each(&:call)
|
69
|
+
@started = true
|
70
|
+
@paused = false
|
71
|
+
@timer.continue
|
72
|
+
end
|
72
73
|
end
|
73
|
-
end
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
75
|
+
def stop
|
76
|
+
if @started
|
77
|
+
@timer.stop
|
78
|
+
@started = false
|
79
|
+
@paused = false
|
80
|
+
@on_stop.each(&:call)
|
81
|
+
end
|
81
82
|
end
|
82
|
-
end
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
def pause
|
85
|
+
if @started && !@paused
|
86
|
+
@timer.stop
|
87
|
+
@paused = true
|
88
|
+
end
|
88
89
|
end
|
89
|
-
end
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
91
|
+
def continue
|
92
|
+
if @started && @paused
|
93
|
+
@paused = false
|
94
|
+
@timer.continue
|
95
|
+
end
|
95
96
|
end
|
96
|
-
end
|
97
97
|
|
98
|
-
|
99
|
-
|
98
|
+
def terminate
|
99
|
+
@run = false
|
100
|
+
end
|
100
101
|
end
|
101
102
|
end
|
102
103
|
end
|