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
@@ -38,8 +38,8 @@ describe MidiUtil do
38
38
 
39
39
  context 'given pitch outside C-1 to G9 range' do
40
40
  it 'should raise error' do
41
- expect { MidiUtil.pitch_to_notenum(Pitch.new(octave:-2)) }.to raise_error
42
- expect { MidiUtil.pitch_to_notenum(Ab9) }.to raise_error
41
+ expect { MidiUtil.pitch_to_notenum(Pitch.new(octave:-2)) }.to raise_error(KeyError)
42
+ expect { MidiUtil.pitch_to_notenum(Ab9) }.to raise_error(KeyError)
43
43
  end
44
44
  end
45
45
  end
@@ -91,19 +91,33 @@ describe MidiUtil do
91
91
  end
92
92
 
93
93
  describe 'note_velocity' do
94
- context 'given true' do
95
- it 'should return a higher value than when given false' do
96
- MidiUtil.note_velocity(true).should be > MidiUtil.note_velocity(false)
94
+ context 'given Attack::NORMAL' do
95
+ it 'should return a value at least that of when given Attack::NONE' do
96
+ MidiUtil.note_velocity(Attack::NORMAL).should be >= MidiUtil.note_velocity(Attack::NONE)
97
+ end
98
+
99
+ it 'should return a value between 0 and 127' do
100
+ MidiUtil.note_velocity(Attack::NORMAL).should be_between(0,127)
101
+ end
102
+ end
103
+
104
+ context 'given Attack::TENUTO' do
105
+ it 'should return a higher value than when given Attack::NORMAL' do
106
+ MidiUtil.note_velocity(Attack::TENUTO).should be > MidiUtil.note_velocity(Attack::NORMAL)
97
107
  end
98
108
 
99
109
  it 'should return a value between 0 and 127' do
100
- MidiUtil.note_velocity(true).should be_between(0,127)
110
+ MidiUtil.note_velocity(Attack::TENUTO).should be_between(0,127)
101
111
  end
102
112
  end
103
113
 
104
- context 'given false' do
114
+ context 'given Attack::ACCENT' do
115
+ it 'should return a higher value than when given Attack::TENUTO' do
116
+ MidiUtil.note_velocity(Attack::ACCENT).should be > MidiUtil.note_velocity(Attack::TENUTO)
117
+ end
118
+
105
119
  it 'should return a value between 0 and 127' do
106
- MidiUtil.note_velocity(false).should be_between(0,127)
120
+ MidiUtil.note_velocity(Attack::ACCENT).should be_between(0,127)
107
121
  end
108
122
  end
109
123
  end
@@ -21,9 +21,9 @@ describe PartSequencer do
21
21
  @track.name.should eq(@part_name)
22
22
  end
23
23
 
24
- it 'should assign program number via ProgramChange event' do
24
+ it 'should assign program number (less one) via ProgramChange event' do
25
25
  event = @track.events.select { |x| x.is_a? MIDI::ProgramChange }.first
26
- event.program.should eq(@program_num)
26
+ event.program.should eq(@program_num-1)
27
27
  end
28
28
 
29
29
  it 'should assign the given channel number to all channel events' do
@@ -8,12 +8,13 @@ describe ScoreSequencer do
8
8
  before :all do
9
9
  @part1_name = "abc"
10
10
  @part2_name = "def"
11
- @part1 = Part.new(Dynamics::PP, notes: "/4C4 /4D4 /8 /8D4 /8E4 3/8C4".to_notes * 2)
12
- @part2 = Part.new(Dynamics::FF, notes: "/4E4 3/4F4 /4E4".to_notes * 2)
11
+ @part1 = Part.new(Dynamics::PP, notes: "/4C4 /4D4 /8 /8D4 /8E4 3/8C4".to_notes * 2,
12
+ settings: [ MidiSettings::ELECTRIC_BASS_PICK ])
13
+ @part2 = Part.new(Dynamics::FF, notes: "/4E4 3/4F4 /4E4".to_notes * 2,
14
+ settings: [ MidiSettings::ELECTRIC_GUITAR_JAZZ ])
13
15
  @score = Score::Timed.new(program: [0..2.5],
14
- parts: {@part1_name => @part2, @part2_name => @part2})
15
- @instr_map = {@part1_name => 33, @part2_name => 25}
16
- @midi_seq = ScoreSequencer.new(@score).make_midi_seq(@instr_map)
16
+ parts: {@part1_name => @part1, @part2_name => @part2})
17
+ @midi_seq = ScoreSequencer.new(@score).make_midi_seq
17
18
  end
18
19
 
19
20
  it 'should return MIDI::Sequence' do
@@ -29,11 +30,12 @@ describe ScoreSequencer do
29
30
  @midi_seq.tracks[2].name.should eq(@part2_name)
30
31
  end
31
32
 
32
- it 'should assign program number from given instrument map hash' do
33
- [ @midi_seq.tracks[1], @midi_seq.tracks[2] ].each do |track|
34
- prog_event = track.events.select {|x| x.is_a? MIDI::ProgramChange }.first
35
- prog_event.program.should eq(@instr_map[track.name])
36
- end
33
+ it 'should assign program number (starts at 0) from part midi program number (starts at 1)' do
34
+ prog_event = @midi_seq.tracks[1].events.select {|x| x.is_a? MIDI::ProgramChange }.first
35
+ prog_event.program.should eq(@part1.midi_settings.program - 1)
36
+
37
+ prog_event = @midi_seq.tracks[2].events.select {|x| x.is_a? MIDI::ProgramChange }.first
38
+ prog_event.program.should eq(@part2.midi_settings.program - 1)
37
39
  end
38
40
 
39
41
  it 'should assign different channel to each part track' do
@@ -21,9 +21,9 @@ describe Score::Timed do
21
21
  end
22
22
  end
23
23
 
24
- describe Score::TempoBased do
24
+ describe Score::Tempo do
25
25
  before :all do
26
- @score = Score::Measured.new(TWO_FOUR, 120) do |s|
26
+ @score = Score::Tempo.new(TWO_FOUR, 120) do |s|
27
27
  s.parts["rhand"] = Part.new(Dynamics::MF) do |p|
28
28
  p.notes += ("/4C4 "*2 + "/4G4 "*2 +
29
29
  "/4A4 "*2 + "/2G4").to_notes
@@ -2,146 +2,53 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  describe NoteSequence do
4
4
  describe '#initialize' do
5
- it 'should assign given start, stop, pitches and attacks' do
6
- start, stop = 15, 22
7
- pitches = { 15 => F2, 16 => G2, 16.1 => Ab2, 21.99 => C2 }
8
- attacks = { 15 => ACCENTED, 17 => UNACCENTED, 18 => ACCENTED }
9
- seq = NoteSequence.new(start,stop,pitches,attacks)
10
- seq.start.should eq(start)
11
- seq.stop.should eq(stop)
12
- seq.pitches.should eq(pitches)
13
- seq.attacks.should eq(attacks)
14
- end
15
-
16
- it 'should raise ArgumentError if start offset >= stop offset' do
17
- expect do
18
- NoteSequence.new(20,19, { 20 => C4 }, { 20 => UNACCENTED })
19
- end.to raise_error(ArgumentError)
20
- end
21
-
22
- it 'should raise ArgumentError if no pitches are given' do
23
- expect do
24
- NoteSequence.new(20,21, {}, { 20 => UNACCENTED })
25
- end.to raise_error(ArgumentError)
26
- end
27
-
28
- it 'should raise ArgumentError if no attacks are given' do
29
- expect do
30
- NoteSequence.new(20,21, { 20 => C4 }, {})
31
- end.to raise_error(ArgumentError)
32
- end
33
-
34
- it 'should raise ArgumentError if no start pitch is given' do
35
- expect do
36
- NoteSequence.new(20,21, { 20.1 => C4 }, { 20 => UNACCENTED })
37
- end.to raise_error(ArgumentError)
38
- end
39
-
40
- it 'should raise ArgumentError if no start attack is given' do
41
- expect do
42
- NoteSequence.new(20,21, { 20 => C4 }, { 20.1 => UNACCENTED })
43
- end.to raise_error(ArgumentError)
44
- end
45
-
46
- it 'should raise ArgumentError if any pitch offset is not between start..stop' do
47
- expect do
48
- NoteSequence.new(20,21, { 20 => C4, 21.01 => D4 }, { 20 => UNACCENTED })
49
- end.to raise_error(ArgumentError)
50
-
51
- expect do
52
- NoteSequence.new(20,21, { 20 => C4, 19.99 => D4 }, { 20 => UNACCENTED })
53
- end.to raise_error(ArgumentError)
54
- end
55
-
56
- it 'should raise ArgumentError if any attack offset is not between start..stop' do
57
- expect do
58
- NoteSequence.new(20,21, { 20 => C4 }, { 20 => UNACCENTED, 21.01 => ACCENTED })
59
- end.to raise_error(ArgumentError)
5
+ it 'should assign given offset, separation, and elements' do
6
+ offset = 12
7
+ separation = Separation::TENUTO
8
+ elements = [ NoteSequence::Element.new(2, G2, Attack::NORMAL) ]
60
9
 
61
- expect do
62
- NoteSequence.new(20,21, { 20 => C4 }, { 20 => UNACCENTED, 19.99 => ACCENTED })
63
- end.to raise_error(ArgumentError)
10
+ seq = NoteSequence.new(offset, separation, elements)
11
+ expect(seq.offset).to eq(offset)
12
+ expect(seq.separation).to eq(separation)
13
+ expect(seq.elements).to eq(elements)
64
14
  end
65
15
  end
16
+
17
+ before :all do
18
+ @element_arys = [
19
+ [ NoteSequence::Element.new(0.5, B2, Attack::NONE) ],
20
+ [ NoteSequence::Element.new(0.5, A2, Attack::NORMAL),
21
+ NoteSequence::Element.new(0.5, B2, Attack::NONE),
22
+ NoteSequence::Element.new(0.5, B2, Attack::ACCENT) ],
23
+ ]
24
+ @offsets = [ 0, -5, 7, 77 ]
25
+ end
66
26
 
67
- describe '.from_elements' do
68
- it 'should raise ArgumentError if no elements are given' do
69
- expect { NoteSequence.from_elements(2,[]) }.to raise_error(ArgumentError)
70
- end
71
-
72
- context 'single element' do
73
- before :all do
74
- @offset = 0
75
- @el = FinalElement.new(2, C2, true, NORMAL)
76
- @seq = NoteSequence.from_elements(@offset, [ @el ])
77
-
78
- end
79
-
80
- it 'should return a NoteSequence' do
81
- @seq.should be_a NoteSequence
82
- end
83
-
84
- it 'should set start offset to given offset' do
85
- @seq.start.should eq(@offset)
86
- end
87
-
88
- it 'should set stop offset no more than "duration of first element" away from start' do
89
- (@seq.stop - @seq.start).should be <= @el.duration
90
- end
91
-
92
- it 'should set start pitch according to element pitch' do
93
- @seq.pitches[@seq.start].should eq(@el.pitch)
94
- end
95
-
96
- it 'should set start attack according to element.accented' do
97
- @seq.attacks[@seq.start].accented?.should eq(@el.accented)
98
- end
99
- end
100
-
101
- context 'multi-element' do
102
- before :all do
103
- @offset = 1.5
104
- @els = [
105
- SlurredElement.new(1.0, A2, false),
106
- LegatoElement.new(1.1, B2, false),
107
- SlurredElement.new(1.2, C2, false),
108
- LegatoElement.new(1.3, B2, false),
109
- FinalElement.new(1.4, A2, false, NORMAL)
110
- ]
111
- @seq = NoteSequence.from_elements(@offset, @els)
112
- end
113
-
114
- it 'should place pitches according to element duration' do
115
- offset = @offset
116
- @els.each do |el|
117
- @seq.pitches.should have_key(offset)
118
- @seq.pitches[offset].should eq(el.pitch)
119
- offset += el.duration
120
- end
121
- end
122
-
123
- it 'should place attacks at beginning and following non-slur elements' do
124
- @seq.attacks.should have_key(@offset)
125
-
126
- offset = @offset + @els.first.duration
127
- (1...@els.size).each do |i|
128
- unless @els[i-1].slurred?
129
- @seq.attacks.should have_key(offset)
27
+ describe '#offsets' do
28
+ context 'with no elements' do
29
+ it 'should raise RuntimeError' do
30
+ expect do
31
+ seq = NoteSequence.new(0, Separation::NORMAL, [])
32
+ seq.offsets
33
+ end.to raise_error(RuntimeError)
34
+ end
35
+ end
36
+
37
+ context 'with at least one element' do
38
+ it 'should return offsets of each element' do
39
+ @offsets.each do |offset|
40
+ @element_arys.each do |els|
41
+ seq = NoteSequence.new(offset, Separation::NORMAL, els)
42
+ offsets = seq.offsets
43
+ expect(offsets.size).to eq(els.size)
44
+ expect(offsets[0]).to eq(seq.offset)
45
+ (1...offsets.size).each do |i|
46
+ dur = offsets[i] - offsets[i-1]
47
+ expect(dur).to eq(els[i-1].duration)
48
+ end
130
49
  end
131
- offset += @els[i].duration
132
50
  end
133
51
  end
134
52
  end
135
-
136
- context 'elements contain slur to same pitch' do
137
- it 'should not add same pitch nor attack for second element' do
138
- els = [ SlurredElement.new(1, C4, false), FinalElement.new(1, C4, false, NORMAL) ]
139
- seq = NoteSequence.from_elements(0, els)
140
- seq.pitches.should have_key(0)
141
- seq.pitches.should_not have_key(1)
142
- seq.attacks.should have_key(0)
143
- seq.attacks.should_not have_key(1)
144
- end
145
- end
146
53
  end
147
- end
54
+ end
@@ -0,0 +1,204 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Note do
4
+ describe '#fractional_subdurs' do
5
+ context 'duration that is entirely whole' do
6
+ it 'should return empty array' do
7
+ [1,2,3,4,7,11].each do |dur|
8
+ subdurs = Note.new(dur).fractional_subdurs(Rational(1,32))
9
+ subdurs.should be_empty
10
+ end
11
+ end
12
+ end
13
+
14
+ context 'duration that is entirely fractional' do
15
+ context 'combination of multiples of power-of-two fractions' do
16
+ [
17
+ [Rational(3,8),Rational(7,32)],
18
+ [Rational(1,8),Rational(3,16),Rational(5,32)],
19
+ [Rational(1,2),Rational(3,32),Rational(5,64),Rational(17,128)],
20
+ ].each do |subdurs|
21
+ dur = subdurs.inject(0.to_r,:+)
22
+ n = Note.new(dur)
23
+ subdurs2 = n.fractional_subdurs(Rational(1,512))
24
+
25
+ it 'should return descending power-of-two fractions' do
26
+ subdurs2.should eq subdurs2.sort.reverse
27
+ end
28
+
29
+ it 'should return fractions that sum to note duration' do
30
+ subdurs2.inject(0.to_r,:+).should eq(dur)
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'duration that has whole and fractional part' do
37
+ [
38
+ [6,Rational(3,8),Rational(11,16)],
39
+ [3,Rational(1,8),Rational(5,8),Rational(5,32)],
40
+ [1,Rational(3,32),Rational(1,2),Rational(5,4)],
41
+ ].each do |subdurs|
42
+ dur = subdurs.inject(0.to_r,:+)
43
+ n = Note.new(dur)
44
+ subdurs2 = n.fractional_subdurs(Rational(1,1024))
45
+
46
+ it 'should return fractions that sum to fractional note duration' do
47
+ subdurs2.inject(0.to_r,:+).should eq(dur - dur.to_i)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#to_lilypond' do
54
+ context 'duration is entirely whole' do
55
+ context 'no pitches' do
56
+ it 'should return "r1"s' do
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ # context 'no pitches' do
63
+ # it 'should represent note pieces with "r"' do
64
+
65
+ # end
66
+ # end
67
+
68
+ # it 'should '
69
+ # it 'should separate pieces with a "~" and a space' do
70
+ # [2,3+Rational(1,2)].each do |dur|
71
+ # [[],[Eb3],[C2,Gb4]].each do |pitches|
72
+ # n = Note.new(dur,pitches)
73
+ # s = n.to_lilypond
74
+ # pieces = s.split("~ ")
75
+ # pieces.
76
+ # end
77
+ # end
78
+ # end
79
+
80
+
81
+ # and duration with deno'
82
+ # context 'simple power-of-two note duration' do
83
+ # it 'should return a string with "r" plus the duration denominator' do
84
+ # [1,0.5,0.25,0.125,0.0625].each do |dur|
85
+ # n = Note.new(dur.to_r)
86
+ # n.to_lilypond.should eq("r" + n.duration.denominator.to_s)
87
+ # end
88
+ # end
89
+ # end
90
+
91
+ # context 'dotted power-of-two note duration less than 1 (e.g. 3/4 or 3/2)' do
92
+ # it 'should return a string with "r" plus half the duration denominator plus a "."' do
93
+ # [0.75,0.375].each do |dur|
94
+ # n = Note.new(dur.to_r)
95
+ # n.to_lilypond.should eq("r" + (n.duration.denominator/2).to_s + ".")
96
+ # end
97
+ # end
98
+ # end
99
+
100
+ # context 'compound duration reducible only to simple power-of-two durations (not dotted)' do
101
+ # it 'should return a string with r\'s plus the duration denominators' do
102
+ # [
103
+ # [1,0.25],
104
+ # [0.5,0.125],
105
+ # [0.25,0.0625],
106
+ # [0.5,0.125,0.03125]
107
+ # ].each do |subdurs|
108
+ # dur = subdurs.inject(0.to_r,:+)
109
+ # n = Note.new(dur)
110
+ # strs = n.to_lilypond.split
111
+ # strs.size.should eq subdurs.size
112
+ # strs.each_with_index do |str,i|
113
+ # str.should eq("r" + subdurs[i].to_r.denominator.to_s)
114
+ # end
115
+ # end
116
+ # end
117
+ # end
118
+
119
+ # context 'simple triplet duration' do
120
+ # it 'should return "r" + 3/2 duration enclosed inside \tuplet 3/2 {}' do
121
+ # [Rational(1,6),Rational(1,3),Rational(1,12)].each do |dur|
122
+ # n = Note.new(dur.to_r)
123
+ # n.to_lilypond.should eq("\\tuplet 3/2 {r#{(1.5.to_r*dur).denominator}}")
124
+ # end
125
+ # end
126
+ # end
127
+ # end
128
+
129
+ # context 'simple power-of-two note duration' do
130
+ # context 'one pitch' do
131
+ # it 'should return a string with Lilypond pitch plus the duration denominator' do
132
+ # [1,0.5,0.25,0.125,0.0625].each do |dur|
133
+ # [C3,Eb2,G4].each do |pitch|
134
+ # n = Note.new(dur.to_r, pitch)
135
+ # n.to_lilypond.should eq(pitch.to_lilypond + n.duration.denominator.to_s)
136
+ # end
137
+ # end
138
+ # end
139
+ # end
140
+
141
+ # context 'multiple pitch' do
142
+ # it 'should return a string with Lilypond pitches in angle brackets plus the duration denominator' do
143
+ # [1,0.25,0.0625].each do |dur|
144
+ # pitch_group = [Eb2,C3,G4]
145
+ # n = Note.new(dur.to_r, pitch_group)
146
+ # n.to_lilypond.should eq("<" + pitch_group.map {|p| p.to_lilypond}.join(" ") + ">" + n.duration.denominator.to_s)
147
+ # end
148
+ # end
149
+ # end
150
+ # end
151
+
152
+ # context 'dotted power-of-two note duration less than 1 (e.g. 3/4 or 3/2)' do
153
+ # context 'one pitch' do
154
+ # it 'should return a string with Lilypond pitch plus half the duration denominator plus a "."' do
155
+ # [0.75,0.375].each do |dur|
156
+ # [C3,Eb2,G4].each do |pitch|
157
+ # n = Note.new(dur.to_r, pitch)
158
+ # n.to_lilypond.should eq(pitch.to_lilypond + (n.duration.denominator/2).to_s + ".")
159
+ # end
160
+ # end
161
+ # end
162
+ # end
163
+
164
+ # context 'multiple pitch' do
165
+ # it 'should return a string with Lilypond pitches in angle brackets plus half the duration denominator plus a "."' do
166
+ # [0.75,0.375].each do |dur|
167
+ # pitch_group = [Eb2,C3,G4]
168
+ # n = Note.new(dur.to_r, pitch_group)
169
+ # n.to_lilypond.should eq("<" + pitch_group.map {|p| p.to_lilypond}.join(" ") + ">" + (n.duration.denominator/2).to_s + ".")
170
+ # end
171
+ # end
172
+ # end
173
+ # end
174
+
175
+ # context 'compound duration reducible only to simple power-of-two durations (not dotted)' do
176
+ # context 'one pitch' do
177
+ # it 'should return a string with Lilypond pitch plus the duration denominators, and a "~" if not the last piece' do
178
+ # [
179
+ # [1,0.25],
180
+ # [0.5,0.125],
181
+ # [0.25,0.0625],
182
+ # [0.5,0.125,0.03125]
183
+ # ].each do |subdurs|
184
+ # [Gb3,Bb3,C4].each do |pitch|
185
+ # dur = subdurs.inject(0.to_r,:+)
186
+ # n = Note.new(dur,pitch)
187
+ # str = n.to_lilypond
188
+ # strs = str.split
189
+ # strs.size.should eq subdurs.size
190
+ # strs.each_with_index do |str,i|
191
+ # if i != (strs.size-1)
192
+ # str.should eq(pitch.to_lilypond + subdurs[i].to_r.denominator.to_s + "~")
193
+ # else
194
+ # str.should eq(pitch.to_lilypond + subdurs[i].to_r.denominator.to_s)
195
+ # end
196
+ # end
197
+ # end
198
+ # end
199
+ # end
200
+ # end
201
+
202
+ # end
203
+ # end
204
+ end