musicality 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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