musicality 0.8.0 → 0.9.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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +27 -1
  3. data/README.md +153 -10
  4. data/bin/collidify +102 -0
  5. data/bin/lilify +57 -29
  6. data/bin/midify +64 -24
  7. data/bin/musicality +39 -0
  8. data/examples/composition/auto_counterpoint.rb +4 -5
  9. data/examples/composition/part_generator.rb +8 -2
  10. data/examples/composition/scale_exercise.rb +1 -1
  11. data/examples/notation/notes.rb +27 -0
  12. data/examples/notation/parts.rb +51 -0
  13. data/examples/notation/scores.rb +38 -0
  14. data/examples/notation/twinkle.rb +34 -0
  15. data/examples/notation/twinkle.score +33 -0
  16. data/lib/musicality.rb +46 -11
  17. data/lib/musicality/composition/dsl/score_dsl.rb +2 -2
  18. data/lib/musicality/composition/dsl/score_methods.rb +10 -7
  19. data/lib/musicality/notation/conversion/change_conversion.rb +1 -1
  20. data/lib/musicality/notation/conversion/note_time_converter.rb +6 -23
  21. data/lib/musicality/notation/conversion/score_conversion.rb +15 -15
  22. data/lib/musicality/notation/conversion/score_converter.rb +50 -67
  23. data/lib/musicality/notation/model/articulations.rb +3 -2
  24. data/lib/musicality/notation/model/change.rb +15 -6
  25. data/lib/musicality/notation/model/dynamics.rb +11 -8
  26. data/lib/musicality/notation/model/instrument.rb +61 -0
  27. data/lib/musicality/notation/model/instruments.rb +111 -0
  28. data/lib/musicality/notation/model/key.rb +137 -0
  29. data/lib/musicality/notation/model/keys.rb +37 -0
  30. data/lib/musicality/notation/model/link.rb +6 -19
  31. data/lib/musicality/notation/model/mark.rb +43 -0
  32. data/lib/musicality/notation/model/marks.rb +11 -0
  33. data/lib/musicality/notation/model/meter.rb +4 -0
  34. data/lib/musicality/notation/model/note.rb +42 -28
  35. data/lib/musicality/notation/model/part.rb +18 -5
  36. data/lib/musicality/notation/model/pitch.rb +13 -4
  37. data/lib/musicality/notation/model/score.rb +104 -66
  38. data/lib/musicality/notation/model/symbols.rb +16 -11
  39. data/lib/musicality/notation/parsing/articulation_parsing.rb +38 -38
  40. data/lib/musicality/notation/parsing/articulation_parsing.treetop +14 -14
  41. data/lib/musicality/notation/parsing/link_parsing.rb +6 -6
  42. data/lib/musicality/notation/parsing/link_parsing.treetop +3 -3
  43. data/lib/musicality/notation/parsing/mark_parsing.rb +138 -0
  44. data/lib/musicality/notation/parsing/mark_parsing.treetop +31 -0
  45. data/lib/musicality/notation/parsing/note_node.rb +19 -12
  46. data/lib/musicality/notation/parsing/note_parsing.rb +218 -87
  47. data/lib/musicality/notation/parsing/note_parsing.treetop +9 -5
  48. data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.rb +7 -2
  49. data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.treetop +1 -1
  50. data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.rb +6 -4
  51. data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.treetop +1 -1
  52. data/lib/musicality/notation/util/function.rb +41 -18
  53. data/lib/musicality/packable.rb +156 -0
  54. data/lib/musicality/performance/conversion/glissando_converter.rb +2 -2
  55. data/lib/musicality/performance/conversion/note_sequence_extractor.rb +223 -70
  56. data/lib/musicality/performance/conversion/portamento_converter.rb +5 -2
  57. data/lib/musicality/performance/conversion/score_collator.rb +70 -64
  58. data/lib/musicality/performance/midi/midi_events.rb +3 -3
  59. data/lib/musicality/performance/midi/midi_settings.rb +127 -0
  60. data/lib/musicality/performance/midi/midi_util.rb +8 -2
  61. data/lib/musicality/performance/midi/part_sequencer.rb +19 -18
  62. data/lib/musicality/performance/midi/score_sequencer.rb +13 -9
  63. data/lib/musicality/performance/midi/score_sequencing.rb +5 -5
  64. data/lib/musicality/performance/model/attack.rb +8 -0
  65. data/lib/musicality/performance/model/duration_functions.rb +23 -0
  66. data/lib/musicality/performance/model/note_sequence.rb +52 -95
  67. data/lib/musicality/performance/model/separation.rb +10 -0
  68. data/lib/musicality/performance/supercollider/add_actions.rb +13 -0
  69. data/lib/musicality/performance/supercollider/bundle.rb +18 -0
  70. data/lib/musicality/performance/supercollider/conductor.rb +125 -0
  71. data/lib/musicality/performance/supercollider/group.rb +71 -0
  72. data/lib/musicality/performance/supercollider/message.rb +26 -0
  73. data/lib/musicality/performance/supercollider/node.rb +122 -0
  74. data/lib/musicality/performance/supercollider/performer.rb +123 -0
  75. data/lib/musicality/performance/supercollider/score_conducting.rb +17 -0
  76. data/lib/musicality/performance/supercollider/server.rb +8 -0
  77. data/lib/musicality/performance/supercollider/synth.rb +43 -0
  78. data/lib/musicality/performance/supercollider/synthdef.rb +57 -0
  79. data/lib/musicality/performance/supercollider/synthdef_settings.rb +23 -0
  80. data/lib/musicality/performance/supercollider/synthdefs.rb +1654 -0
  81. data/lib/musicality/{composition/model/pitch_class.rb → pitch_class.rb} +1 -1
  82. data/lib/musicality/{composition/model/pitch_classes.rb → pitch_classes.rb} +9 -9
  83. data/lib/musicality/printing/lilypond/clef.rb +12 -0
  84. data/lib/musicality/printing/lilypond/key_engraving.rb +9 -0
  85. data/lib/musicality/printing/lilypond/lilypond_settings.rb +105 -0
  86. data/lib/musicality/printing/lilypond/meter_engraving.rb +1 -1
  87. data/lib/musicality/printing/lilypond/note_engraving.rb +112 -30
  88. data/lib/musicality/printing/lilypond/part_engraver.rb +114 -3
  89. data/lib/musicality/printing/lilypond/pitch_class_engraving.rb +22 -0
  90. data/lib/musicality/printing/lilypond/pitch_engraving.rb +2 -15
  91. data/lib/musicality/printing/lilypond/score_engraver.rb +44 -73
  92. data/lib/musicality/printing/lilypond/score_engraving.rb +3 -3
  93. data/lib/musicality/project/create_tasks.rb +31 -0
  94. data/lib/musicality/project/file_cleaner.rb +19 -0
  95. data/lib/musicality/project/file_raker.rb +107 -0
  96. data/lib/musicality/project/load_config.rb +43 -0
  97. data/lib/musicality/project/project.rb +64 -0
  98. data/lib/musicality/version.rb +1 -1
  99. data/musicality.gemspec +3 -0
  100. data/spec/composition/util/random_sampler_spec.rb +1 -1
  101. data/spec/notation/conversion/measure_note_map_spec.rb +1 -1
  102. data/spec/notation/conversion/note_time_converter_spec.rb +5 -85
  103. data/spec/notation/conversion/score_conversion_spec.rb +6 -41
  104. data/spec/notation/conversion/score_converter_spec.rb +19 -137
  105. data/spec/notation/model/change_spec.rb +55 -0
  106. data/spec/notation/model/key_spec.rb +171 -0
  107. data/spec/notation/model/link_spec.rb +34 -5
  108. data/spec/notation/model/meter_spec.rb +15 -0
  109. data/spec/notation/model/note_spec.rb +33 -27
  110. data/spec/notation/model/part_spec.rb +53 -4
  111. data/spec/notation/model/pitch_spec.rb +15 -0
  112. data/spec/notation/model/score_spec.rb +64 -72
  113. data/spec/notation/parsing/link_nodes_spec.rb +3 -3
  114. data/spec/notation/parsing/link_parsing_spec.rb +6 -6
  115. data/spec/notation/parsing/note_node_spec.rb +34 -9
  116. data/spec/notation/parsing/note_parsing_spec.rb +11 -9
  117. data/spec/notation/parsing/numbers/nonnegative_integer_spec.rb +4 -0
  118. data/spec/notation/parsing/pitch_node_spec.rb +0 -1
  119. data/spec/notation/util/value_computer_spec.rb +2 -2
  120. data/spec/performance/conversion/glissando_converter_spec.rb +9 -9
  121. data/spec/performance/conversion/note_sequence_extractor_spec.rb +48 -53
  122. data/spec/performance/conversion/portamento_converter_spec.rb +11 -9
  123. data/spec/performance/conversion/score_collator_spec.rb +59 -63
  124. data/spec/performance/midi/midi_util_spec.rb +22 -8
  125. data/spec/performance/midi/part_sequencer_spec.rb +2 -2
  126. data/spec/performance/midi/score_sequencer_spec.rb +12 -10
  127. data/spec/performance/midi/score_sequencing_spec.rb +2 -2
  128. data/spec/performance/model/note_sequence_spec.rb +41 -134
  129. data/spec/printing/note_engraving_spec.rb +204 -0
  130. data/spec/printing/score_engraver_spec.rb +40 -0
  131. data/spec/spec_helper.rb +1 -0
  132. metadata +69 -23
  133. data/examples/notation/hip.rb +0 -32
  134. data/examples/notation/missed_connection.rb +0 -26
  135. data/examples/notation/song1.rb +0 -33
  136. data/examples/notation/song2.rb +0 -32
  137. data/lib/musicality/notation/model/links.rb +0 -11
  138. data/lib/musicality/notation/packing/change_packing.rb +0 -56
  139. data/lib/musicality/notation/packing/part_packing.rb +0 -31
  140. data/lib/musicality/notation/packing/score_packing.rb +0 -123
  141. data/lib/musicality/performance/model/note_attacks.rb +0 -19
  142. data/lib/musicality/performance/util/note_linker.rb +0 -28
  143. data/spec/notation/packing/change_packing_spec.rb +0 -304
  144. data/spec/notation/packing/part_packing_spec.rb +0 -66
  145. data/spec/notation/packing/score_packing_spec.rb +0 -255
  146. data/spec/performance/util/note_linker_spec.rb +0 -68
@@ -2,14 +2,14 @@ module Musicality
2
2
 
3
3
  class Score
4
4
  class Timed < Score
5
- def to_midi_seq instr_map = {}
6
- ScoreSequencer.new(self).make_midi_seq(instr_map)
5
+ def to_midi_seq **kwargs
6
+ ScoreSequencer.new(self).make_midi_seq(**kwargs)
7
7
  end
8
8
  end
9
9
 
10
- class TempoBased < Score
11
- def to_midi_seq tempo_sample_rate, instr_map = {}
12
- to_timed(tempo_sample_rate).to_midi_seq(instr_map)
10
+ class Tempo < Score
11
+ def to_midi_seq tempo_sample_rate, **kwargs
12
+ to_timed(tempo_sample_rate).to_midi_seq(**kwargs)
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,8 @@
1
+ module Musicality
2
+ module Attack
3
+ NONE = :none
4
+ NORMAL = :normal
5
+ TENUTO = :tenuto
6
+ ACCENT = :accent
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ module Musicality
2
+
3
+ module DurationFunctions
4
+ TENUTO_DURATION = Function::Linear.new([0,0],[1,1])
5
+
6
+ NORMAL_DURATION = Function.new(0...Float::INFINITY) do |x|
7
+ x - Rational(1,8) * (1 - Math.exp(-1.75*x))
8
+ end
9
+
10
+ PORTATO_DURATION = Function.new(0...Float::INFINITY) do |x|
11
+ x - Rational(2,8) * (1 - Math.exp(-1.75*x))
12
+ end
13
+
14
+ STACCATO_DURATION = Function.new(0...Float::INFINITY) do |x|
15
+ x - Rational(3,8) * (1 - Math.exp(-1.75*x))
16
+ end
17
+
18
+ STACCATISSIMO_DURATION = Function.new(0...Float::INFINITY) do |x|
19
+ x - Rational(4,8) * (1 - Math.exp(-1.75*x))
20
+ end
21
+ end
22
+
23
+ end
@@ -1,111 +1,68 @@
1
1
  module Musicality
2
2
 
3
- SlurredElement = Struct.new(:duration, :pitch, :accented) do
4
- def slurred?; true; end
5
- def articulation; Articulations::NORMAL; end
6
- def accented?; accented; end
7
- end
8
-
9
- LegatoElement = Struct.new(:duration, :pitch, :accented) do
10
- def slurred?; false; end
11
- def articulation; Articulations::NORMAL; end
12
- def accented?; accented; end
13
- end
14
-
15
- FinalElement = Struct.new(:duration, :pitch, :accented, :articulation) do
16
- def slurred?; false; end
17
- def accented?; accented; end
18
- end
19
-
20
3
  class NoteSequence
21
- def self.adjust_duration duration, articulation
22
- x = duration
23
- y = Math.log2(x)
4
+ Element = Struct.new(:duration, :pitch, :attack)
24
5
 
25
- case articulation
26
- when Articulations::TENUTO
27
- x
28
- when Articulations::PORTATO
29
- x / (1 + 2**(y-1))
30
- when Articulations::STACCATO
31
- x / (1 + 2**(y))
32
- when Articulations::STACCATISSIMO
33
- x / (1 + 2**(y+1))
6
+ def self.adjust_duration duration, separation
7
+ function = case separation
8
+ when Separation::TENUTO
9
+ DurationFunctions::TENUTO_DURATION
10
+ when Separation::PORTATO
11
+ DurationFunctions::PORTATO_DURATION
12
+ when Separation::STACCATO
13
+ DurationFunctions::STACCATO_DURATION
14
+ when Separation::STACCATISSIMO
15
+ DurationFunctions::STACCATISSIMO_DURATION
34
16
  else
35
- x - (1/16.0)*(1/(1+2**(-y)))
17
+ DurationFunctions::NORMAL_DURATION
36
18
  end
19
+
20
+ function.at(duration)
37
21
  end
38
22
 
39
- attr_reader :start, :stop, :pitches, :attacks
40
- def initialize start, stop, pitches, attacks
41
- if start >= stop
42
- raise ArgumentError, "start #{start} is not less than stop #{stop}"
43
- end
44
-
45
- if pitches.empty?
46
- raise ArgumentError, "no pitches given (at least one pitch is required at start offset)"
47
- end
48
-
49
- unless pitches.has_key?(start)
50
- raise ArgumentError, "no start pitch given"
51
- end
52
-
53
- pitches.keys.each do |offset|
54
- unless offset.between?(start,stop)
55
- raise ArgumentError, "pitch offset #{offset} is not between start #{start} and stop #{stop}"
56
- end
57
- end
58
-
59
- if attacks.empty?
60
- raise ArgumentError, "no attacks given (at least one is required at start offset)"
61
- end
62
-
63
- unless attacks.has_key?(start)
64
- raise ArgumentError, "no start attack given"
65
- end
66
-
67
- attacks.keys.each do |offset|
68
- unless offset.between?(start,stop)
69
- raise ArgumentError, "attack offset #{offset} is not between start #{start} and stop #{stop}"
70
- end
71
- end
72
-
73
- @start, @stop = start, stop
74
- @pitches, @attacks = pitches, attacks
23
+ attr_accessor :offset, :separation, :elements
24
+ def initialize offset, separation, elements = []
25
+ @offset = offset
26
+ @separation = separation
27
+ @elements = elements
75
28
  end
29
+
30
+ alias start offset
31
+
32
+ def offsets
33
+ raise "contains no elements" if elements.empty?
76
34
 
77
- def self.from_elements offset, elements
78
- pitches = {}
79
- attacks = {}
80
- start = offset
81
-
82
- if elements.empty?
83
- raise ArgumentError, "no elements given"
84
- end
85
-
86
- last = elements.last
87
- skip_attack = false
88
- elements.each do |el|
89
- if skip_attack
90
- unless pitches.max[1] == el.pitch
91
- pitches[offset] = el.pitch
92
- end
93
- else
94
- pitches[offset] = el.pitch
95
- attacks[offset] = el.accented ? ACCENTED : UNACCENTED
96
- end
97
- skip_attack = el.slurred?
98
-
99
- unless el.equal?(last)
100
- offset += el.duration
101
- end
35
+ off = @offset
36
+ elements.map do |e|
37
+ x = off
38
+ off += e.duration
39
+ x
102
40
  end
103
- stop = offset + NoteSequence.adjust_duration(last.duration, last.articulation)
104
-
105
- new(start, stop, pitches, attacks)
41
+ end
42
+
43
+ def stop
44
+ offsets.last + NoteSequence.adjust_duration(elements.last.duration, separation)
106
45
  end
107
46
 
108
- def duration; @stop - @start; end
47
+ def duration
48
+ stop - offset
49
+ end
50
+
51
+ def full_duration
52
+ offsets.last + elements.last.duration
53
+ end
54
+
55
+ def first_pitch
56
+ elements.first.pitch
57
+ end
58
+
59
+ def last_pitch
60
+ elements.last.pitch
61
+ end
62
+
63
+ def last_attack
64
+ elements.last.attack
65
+ end
109
66
  end
110
67
 
111
68
  end
@@ -0,0 +1,10 @@
1
+ module Musicality
2
+ module Separation
3
+ NONE = :none
4
+ TENUTO = :tenuto
5
+ NORMAL = :normal
6
+ PORTATO = :portato
7
+ STACCATO = :staccato
8
+ STACCATISSIMO = :staccatissimo
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module Musicality
2
+ module SuperCollider
3
+
4
+ ADD_HEAD = 0
5
+ ADD_TAIL = 1
6
+ ADD_BEFORE = 2
7
+ ADD_AFTER = 3
8
+ ADD_REPLACE = 4
9
+
10
+ ADD_ACTIONS = [ ADD_BEFORE, ADD_AFTER, ADD_HEAD, ADD_TAIL, ADD_REPLACE ]
11
+
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module Musicality
2
+ module SuperCollider
3
+
4
+ class Bundle
5
+ attr_reader :time, :messages
6
+ def initialize time, *messages
7
+ @time = time
8
+ @messages = messages
9
+ end
10
+
11
+ def to_sclang
12
+ raise "Bundle contains no messages" if @messages.empty?
13
+ "[ #{@time.to_f}, #{@messages.map {|m| m.to_sclang }.join(", ")} ]"
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,125 @@
1
+ module Musicality
2
+ module SuperCollider
3
+
4
+ class Conductor
5
+ def initialize score
6
+ unless score.is_a?(Score::Timed)
7
+ raise ArgumentError, "The given score is not a Score::Timed. \
8
+ Convert it first using ScoreConverter."
9
+ end
10
+
11
+ parts = score.collated? ? score.parts : ScoreCollator.new(score).collate_parts
12
+ @performers = Hash[ parts.map do |name, part|
13
+ [name, Performer.new(part)]
14
+ end]
15
+ end
16
+
17
+ def perform base_fpath, selected_parts: @performers.keys, verbose: false, lead_time: 0.1
18
+ bundles = bundles(selected_parts, lead_time)
19
+ fpath = write_sc_code bundles, base_fpath
20
+ exec_sc_code fpath, bundles.last.time, verbose
21
+ end
22
+
23
+ def bundles selected_parts = @performers.keys, lead_time
24
+ Node.reset_id_counter
25
+
26
+ default_group = Group.default(nil)
27
+ aux_audio_bus = 16
28
+ volume_control_bus = 0
29
+ bundle_kwargs = {
30
+ :parent_group => default_group,
31
+ :aux_audio_bus => aux_audio_bus,
32
+ :volume_control_bus => volume_control_bus,
33
+ :lead_time => lead_time
34
+ }
35
+
36
+ all_bundles = [ default_group.bundle_queue(0.0) ]
37
+ selected_parts.each do |name|
38
+ bundles = @performers[name].bundles(**bundle_kwargs)
39
+ bundle_kwargs[:aux_audio_bus] += 2
40
+ bundle_kwargs[:volume_control_bus] += 1
41
+ all_bundles.concat bundles
42
+ end
43
+ all_bundles = all_bundles.sort_by {|b| b.time }
44
+ default_group.free_all
45
+ all_bundles.push(default_group.bundle_queue(lead_time+all_bundles.last.time))
46
+
47
+ coalesced_bundles = []
48
+ all_bundles.each do |bundle|
49
+ if coalesced_bundles.any? && coalesced_bundles.last.time == bundle.time
50
+ coalesced_bundles.last.messages.concat bundle.messages
51
+ else
52
+ coalesced_bundles.push bundle
53
+ end
54
+ end
55
+
56
+ return coalesced_bundles
57
+ end
58
+
59
+ private
60
+
61
+ BASE_FPATH_PLACEHOLDER = "BASE_FPATH"
62
+ SYNTHDEFS_PLACEHOLDER = "SYNTHDEFS"
63
+ COMPLETION_MSG = "Work complete."
64
+ POST_PID_MSG = "sclang pid:"
65
+
66
+ SC_HEADER = <<SCLANG
67
+ // auto-generated by Musicality
68
+
69
+ (\"#{POST_PID_MSG}\" + thisProcess.pid).postln;
70
+
71
+ #{SYNTHDEFS_PLACEHOLDER}
72
+
73
+ (
74
+ x = [
75
+ SCLANG
76
+
77
+ SC_FOOTER = <<SCLANG
78
+ ];
79
+
80
+ f = File(\"#{BASE_FPATH_PLACEHOLDER}.osc\","w");
81
+ x.do({ arg item, i;
82
+ var bytes = item.asRawOSC;
83
+ f.write(bytes.size);
84
+ f.write(bytes);
85
+ });
86
+ f.close;
87
+
88
+ \"#{COMPLETION_MSG}\".postln;
89
+ );
90
+ SCLANG
91
+
92
+ def write_sc_code bundles, base_fpath
93
+ fpath = "#{base_fpath}.scd"
94
+ File.open(fpath, "w") do |f|
95
+ synthdefs = [ SynthDefs::VOLUME_CONTROL, SynthDefs::VOLUME_CHANGE ] + @performers.values.map {|p| p.settings.synthdef }
96
+
97
+ f.write SC_HEADER.gsub(SYNTHDEFS_PLACEHOLDER, synthdefs.map {|s| s.to_sclang }.join("\n\n"))
98
+ f.write bundles.map {|b| " " + b.to_sclang }.join(",\n") + "\n"
99
+ f.write SC_FOOTER.gsub(BASE_FPATH_PLACEHOLDER, base_fpath)
100
+ end
101
+ fpath
102
+ end
103
+
104
+ def exec_sc_code fpath, last_time, verbose
105
+ post_sclang_pid = Regexp.new(POST_PID_MSG + " ([0-9]+)")
106
+
107
+ sclang_pid = nil
108
+ IO.popen("sclang \"#{fpath}\"") do |io|
109
+ pid = io.pid
110
+ while response = io.gets
111
+ puts response if verbose
112
+
113
+ case response.chomp
114
+ when post_sclang_pid
115
+ sclang_pid = $1.to_i
116
+ when COMPLETION_MSG
117
+ Process.kill 'INT', sclang_pid
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,71 @@
1
+ module Musicality
2
+ module SuperCollider
3
+
4
+ class Group < Node
5
+ def initialize server, group, add_action, target_id, parallel
6
+ raise ArgumentError unless ADD_ACTIONS.include?(add_action)
7
+ super server, group
8
+
9
+ send_msg(Message.new(parallel ? '/p_new' : '/g_new',
10
+ id, add_action, target_id))
11
+ end
12
+
13
+ def self.default server, parallel = false
14
+ new(server, nil, ADD_TAIL, 0, parallel)
15
+ end
16
+
17
+ def self.after target, parallel = false
18
+ raise ArgumentError unless target.is_a?(Node)
19
+ new target.server, target.group, ADD_AFTER, target.id, parallel
20
+ end
21
+
22
+ def self.before target, parallel = false
23
+ raise ArgumentError unless target.is_a?(Node)
24
+ new target.server, target.group, ADD_BEFORE, target.id, parallel
25
+ end
26
+
27
+ def self.head target, parallel = false
28
+ raise ArgumentError unless target.is_a?(Group)
29
+ new target.server, target, ADD_HEAD, target.id, parallel
30
+ end
31
+
32
+ def self.tail target, parallel = false
33
+ raise ArgumentError unless target.is_a?(Group)
34
+ new target.server, target, ADD_TAIL, target.id, parallel
35
+ end
36
+
37
+ def self.replace target, parallel = false
38
+ raise ArgumentError unless target.is_a?(Node)
39
+ new target.server, target.group, ADD_REPLACE, target.id, parallel
40
+ end
41
+
42
+ def head node
43
+ raise ArgumentError unless node.is_a?(Node)
44
+ send_msg(Message.new('/g_head', id, node.id))
45
+ end
46
+
47
+ def tail node
48
+ raise ArgumentError unless node.is_a?(Node)
49
+ send_msg(Message.new('/g_tail', id, node.id))
50
+ end
51
+
52
+ def free_all
53
+ send_msg(Message.new('/g_freeAll', id))
54
+ end
55
+
56
+ def deep_free
57
+ send_msg(Message.new('/g_deepFree', id))
58
+ end
59
+
60
+ def dump_tree flag
61
+ send_msg(Message.new('/g_dumpTree', id, flag ? 1 : 0))
62
+ end
63
+
64
+ def query_tree
65
+ raise NotImplementedError
66
+ # TODO
67
+ end
68
+ end
69
+
70
+ end
71
+ end