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.
- checksums.yaml +4 -4
- data/ChangeLog.md +27 -1
- data/README.md +153 -10
- data/bin/collidify +102 -0
- data/bin/lilify +57 -29
- data/bin/midify +64 -24
- data/bin/musicality +39 -0
- data/examples/composition/auto_counterpoint.rb +4 -5
- data/examples/composition/part_generator.rb +8 -2
- data/examples/composition/scale_exercise.rb +1 -1
- data/examples/notation/notes.rb +27 -0
- data/examples/notation/parts.rb +51 -0
- data/examples/notation/scores.rb +38 -0
- data/examples/notation/twinkle.rb +34 -0
- data/examples/notation/twinkle.score +33 -0
- data/lib/musicality.rb +46 -11
- data/lib/musicality/composition/dsl/score_dsl.rb +2 -2
- data/lib/musicality/composition/dsl/score_methods.rb +10 -7
- data/lib/musicality/notation/conversion/change_conversion.rb +1 -1
- data/lib/musicality/notation/conversion/note_time_converter.rb +6 -23
- data/lib/musicality/notation/conversion/score_conversion.rb +15 -15
- data/lib/musicality/notation/conversion/score_converter.rb +50 -67
- data/lib/musicality/notation/model/articulations.rb +3 -2
- data/lib/musicality/notation/model/change.rb +15 -6
- data/lib/musicality/notation/model/dynamics.rb +11 -8
- data/lib/musicality/notation/model/instrument.rb +61 -0
- data/lib/musicality/notation/model/instruments.rb +111 -0
- data/lib/musicality/notation/model/key.rb +137 -0
- data/lib/musicality/notation/model/keys.rb +37 -0
- data/lib/musicality/notation/model/link.rb +6 -19
- data/lib/musicality/notation/model/mark.rb +43 -0
- data/lib/musicality/notation/model/marks.rb +11 -0
- data/lib/musicality/notation/model/meter.rb +4 -0
- data/lib/musicality/notation/model/note.rb +42 -28
- data/lib/musicality/notation/model/part.rb +18 -5
- data/lib/musicality/notation/model/pitch.rb +13 -4
- data/lib/musicality/notation/model/score.rb +104 -66
- data/lib/musicality/notation/model/symbols.rb +16 -11
- data/lib/musicality/notation/parsing/articulation_parsing.rb +38 -38
- data/lib/musicality/notation/parsing/articulation_parsing.treetop +14 -14
- data/lib/musicality/notation/parsing/link_parsing.rb +6 -6
- data/lib/musicality/notation/parsing/link_parsing.treetop +3 -3
- data/lib/musicality/notation/parsing/mark_parsing.rb +138 -0
- data/lib/musicality/notation/parsing/mark_parsing.treetop +31 -0
- data/lib/musicality/notation/parsing/note_node.rb +19 -12
- data/lib/musicality/notation/parsing/note_parsing.rb +218 -87
- data/lib/musicality/notation/parsing/note_parsing.treetop +9 -5
- data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.rb +7 -2
- data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.treetop +1 -1
- data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.rb +6 -4
- data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.treetop +1 -1
- data/lib/musicality/notation/util/function.rb +41 -18
- data/lib/musicality/packable.rb +156 -0
- data/lib/musicality/performance/conversion/glissando_converter.rb +2 -2
- data/lib/musicality/performance/conversion/note_sequence_extractor.rb +223 -70
- data/lib/musicality/performance/conversion/portamento_converter.rb +5 -2
- data/lib/musicality/performance/conversion/score_collator.rb +70 -64
- data/lib/musicality/performance/midi/midi_events.rb +3 -3
- data/lib/musicality/performance/midi/midi_settings.rb +127 -0
- data/lib/musicality/performance/midi/midi_util.rb +8 -2
- data/lib/musicality/performance/midi/part_sequencer.rb +19 -18
- data/lib/musicality/performance/midi/score_sequencer.rb +13 -9
- data/lib/musicality/performance/midi/score_sequencing.rb +5 -5
- data/lib/musicality/performance/model/attack.rb +8 -0
- data/lib/musicality/performance/model/duration_functions.rb +23 -0
- data/lib/musicality/performance/model/note_sequence.rb +52 -95
- data/lib/musicality/performance/model/separation.rb +10 -0
- data/lib/musicality/performance/supercollider/add_actions.rb +13 -0
- data/lib/musicality/performance/supercollider/bundle.rb +18 -0
- data/lib/musicality/performance/supercollider/conductor.rb +125 -0
- data/lib/musicality/performance/supercollider/group.rb +71 -0
- data/lib/musicality/performance/supercollider/message.rb +26 -0
- data/lib/musicality/performance/supercollider/node.rb +122 -0
- data/lib/musicality/performance/supercollider/performer.rb +123 -0
- data/lib/musicality/performance/supercollider/score_conducting.rb +17 -0
- data/lib/musicality/performance/supercollider/server.rb +8 -0
- data/lib/musicality/performance/supercollider/synth.rb +43 -0
- data/lib/musicality/performance/supercollider/synthdef.rb +57 -0
- data/lib/musicality/performance/supercollider/synthdef_settings.rb +23 -0
- data/lib/musicality/performance/supercollider/synthdefs.rb +1654 -0
- data/lib/musicality/{composition/model/pitch_class.rb → pitch_class.rb} +1 -1
- data/lib/musicality/{composition/model/pitch_classes.rb → pitch_classes.rb} +9 -9
- data/lib/musicality/printing/lilypond/clef.rb +12 -0
- data/lib/musicality/printing/lilypond/key_engraving.rb +9 -0
- data/lib/musicality/printing/lilypond/lilypond_settings.rb +105 -0
- data/lib/musicality/printing/lilypond/meter_engraving.rb +1 -1
- data/lib/musicality/printing/lilypond/note_engraving.rb +112 -30
- data/lib/musicality/printing/lilypond/part_engraver.rb +114 -3
- data/lib/musicality/printing/lilypond/pitch_class_engraving.rb +22 -0
- data/lib/musicality/printing/lilypond/pitch_engraving.rb +2 -15
- data/lib/musicality/printing/lilypond/score_engraver.rb +44 -73
- data/lib/musicality/printing/lilypond/score_engraving.rb +3 -3
- data/lib/musicality/project/create_tasks.rb +31 -0
- data/lib/musicality/project/file_cleaner.rb +19 -0
- data/lib/musicality/project/file_raker.rb +107 -0
- data/lib/musicality/project/load_config.rb +43 -0
- data/lib/musicality/project/project.rb +64 -0
- data/lib/musicality/version.rb +1 -1
- data/musicality.gemspec +3 -0
- data/spec/composition/util/random_sampler_spec.rb +1 -1
- data/spec/notation/conversion/measure_note_map_spec.rb +1 -1
- data/spec/notation/conversion/note_time_converter_spec.rb +5 -85
- data/spec/notation/conversion/score_conversion_spec.rb +6 -41
- data/spec/notation/conversion/score_converter_spec.rb +19 -137
- data/spec/notation/model/change_spec.rb +55 -0
- data/spec/notation/model/key_spec.rb +171 -0
- data/spec/notation/model/link_spec.rb +34 -5
- data/spec/notation/model/meter_spec.rb +15 -0
- data/spec/notation/model/note_spec.rb +33 -27
- data/spec/notation/model/part_spec.rb +53 -4
- data/spec/notation/model/pitch_spec.rb +15 -0
- data/spec/notation/model/score_spec.rb +64 -72
- data/spec/notation/parsing/link_nodes_spec.rb +3 -3
- data/spec/notation/parsing/link_parsing_spec.rb +6 -6
- data/spec/notation/parsing/note_node_spec.rb +34 -9
- data/spec/notation/parsing/note_parsing_spec.rb +11 -9
- data/spec/notation/parsing/numbers/nonnegative_integer_spec.rb +4 -0
- data/spec/notation/parsing/pitch_node_spec.rb +0 -1
- data/spec/notation/util/value_computer_spec.rb +2 -2
- data/spec/performance/conversion/glissando_converter_spec.rb +9 -9
- data/spec/performance/conversion/note_sequence_extractor_spec.rb +48 -53
- data/spec/performance/conversion/portamento_converter_spec.rb +11 -9
- data/spec/performance/conversion/score_collator_spec.rb +59 -63
- data/spec/performance/midi/midi_util_spec.rb +22 -8
- data/spec/performance/midi/part_sequencer_spec.rb +2 -2
- data/spec/performance/midi/score_sequencer_spec.rb +12 -10
- data/spec/performance/midi/score_sequencing_spec.rb +2 -2
- data/spec/performance/model/note_sequence_spec.rb +41 -134
- data/spec/printing/note_engraving_spec.rb +204 -0
- data/spec/printing/score_engraver_spec.rb +40 -0
- data/spec/spec_helper.rb +1 -0
- metadata +69 -23
- data/examples/notation/hip.rb +0 -32
- data/examples/notation/missed_connection.rb +0 -26
- data/examples/notation/song1.rb +0 -33
- data/examples/notation/song2.rb +0 -32
- data/lib/musicality/notation/model/links.rb +0 -11
- data/lib/musicality/notation/packing/change_packing.rb +0 -56
- data/lib/musicality/notation/packing/part_packing.rb +0 -31
- data/lib/musicality/notation/packing/score_packing.rb +0 -123
- data/lib/musicality/performance/model/note_attacks.rb +0 -19
- data/lib/musicality/performance/util/note_linker.rb +0 -28
- data/spec/notation/packing/change_packing_spec.rb +0 -304
- data/spec/notation/packing/part_packing_spec.rb +0 -66
- data/spec/notation/packing/score_packing_spec.rb +0 -255
- 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
|
95
|
-
it 'should return a
|
96
|
-
MidiUtil.note_velocity(
|
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(
|
110
|
+
MidiUtil.note_velocity(Attack::TENUTO).should be_between(0,127)
|
101
111
|
end
|
102
112
|
end
|
103
113
|
|
104
|
-
context 'given
|
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(
|
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
|
-
|
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 => @
|
15
|
-
@
|
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
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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::
|
24
|
+
describe Score::Tempo do
|
25
25
|
before :all do
|
26
|
-
@score = Score::
|
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
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
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 '
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|