musicality 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -1,18 +1,18 @@
|
|
1
1
|
module Musicality
|
2
2
|
|
3
3
|
module PitchClasses
|
4
|
-
C = 0
|
5
|
-
Db = 1
|
4
|
+
C = Bs = 0
|
5
|
+
Cs = Db = 1
|
6
6
|
D = 2
|
7
|
-
Eb = 3
|
8
|
-
E = 4
|
9
|
-
F = 5
|
10
|
-
Gb = 6
|
7
|
+
Ds = Eb = 3
|
8
|
+
E = Fb = 4
|
9
|
+
Es = F = 5
|
10
|
+
Fs = Gb = 6
|
11
11
|
G = 7
|
12
|
-
Ab = 8
|
12
|
+
Gs = Ab = 8
|
13
13
|
A = 9
|
14
|
-
Bb = 10
|
15
|
-
B = 11
|
14
|
+
As = Bb = 10
|
15
|
+
B = Cb = 11
|
16
16
|
end
|
17
17
|
|
18
18
|
PITCH_CLASSES = PitchClasses.constants.map do |sym|
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Musicality
|
2
|
+
|
3
|
+
class LilypondSettings
|
4
|
+
include Packable
|
5
|
+
|
6
|
+
attr_reader :instrument_name, :clefs, :transpose_interval
|
7
|
+
|
8
|
+
def initialize instrument_name, clefs: [Clef::TREBLE, Clef::BASS], transpose_interval: 0
|
9
|
+
raise ArgumentError unless (clefs & CLEFS) == clefs
|
10
|
+
@instrument_name = instrument_name
|
11
|
+
@clefs = clefs
|
12
|
+
@transpose_interval = transpose_interval
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.treble_bass(instrument_name, transpose_interval: 0)
|
16
|
+
new(instrument_name, clefs: [Clef::TREBLE, Clef::BASS], transpose_interval: transpose_interval)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.treble(instrument_name, transpose_interval: 0)
|
20
|
+
new(instrument_name, clefs: [Clef::TREBLE], transpose_interval: transpose_interval)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.bass(instrument_name, transpose_interval: 0)
|
24
|
+
new(instrument_name, clefs: [Clef::BASS], transpose_interval: transpose_interval)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.guitar(instrument_name)
|
28
|
+
new(instrument_name, clefs: [Clef::TENOR], transpose_interval: 12)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.bass_guitar(instrument_name)
|
32
|
+
new(instrument_name, clefs: [Clef::BASS], transpose_interval: 12)
|
33
|
+
end
|
34
|
+
|
35
|
+
ACOUSTIC_GRAND_PIANO = LilypondSettings.treble_bass("Acoustic Grand Piano")
|
36
|
+
BRIGHT_ACOUSTIC_PIANO = LilypondSettings.treble_bass("Bright Acoustic Piano")
|
37
|
+
ELECTRIC_GRAND_PIANO = LilypondSettings.treble_bass("Electric Grand Piano")
|
38
|
+
HONKY_TONK_PIANO = LilypondSettings.treble_bass("Honky Tonk Piano")
|
39
|
+
ELECTRIC_PIANO = LilypondSettings.treble_bass("Electric Piano")
|
40
|
+
HARPSICHORD = LilypondSettings.treble_bass("Harpsichord")
|
41
|
+
CLAVINET = LilypondSettings.treble_bass("Clavinet")
|
42
|
+
CELESTA = LilypondSettings.treble_bass("Celesta", transpose_interval: -12)
|
43
|
+
|
44
|
+
GLOCKENSPIEL = LilypondSettings.treble_bass("Glockenspiel", transpose_interval: -24)
|
45
|
+
MUSIC_BOX = LilypondSettings.treble_bass("Music Box")
|
46
|
+
VIBRAPHONE = LilypondSettings.treble_bass("Virbaphone")
|
47
|
+
MARIMBA = LilypondSettings.treble_bass("Marimba")
|
48
|
+
XYLOPHONE = LilypondSettings.treble_bass("Xylophone", transpose_interval: -12)
|
49
|
+
TUBULAR_BELLS = LilypondSettings.treble_bass("Tubular Bells")
|
50
|
+
DULCIMER = LilypondSettings.treble("Dulcimer")
|
51
|
+
|
52
|
+
DRAWBAR_ORGAN = LilypondSettings.treble_bass("Drawbar Organ")
|
53
|
+
PERCUSSIVE_ORGAN = LilypondSettings.treble_bass("Percussive Organ")
|
54
|
+
ROCK_ORGAN = LilypondSettings.treble_bass("Rock Organ")
|
55
|
+
CHURCH_ORGAN = LilypondSettings.treble_bass("Church Organ")
|
56
|
+
REED_ORGAN = LilypondSettings.treble_bass("Reed Organ")
|
57
|
+
ACCORDIAN = LilypondSettings.treble_bass("Accordion")
|
58
|
+
HARMONICA = LilypondSettings.treble("Harmonica")
|
59
|
+
TANGO_ACCORDIAN = LilypondSettings.treble_bass("Tango Accordion")
|
60
|
+
|
61
|
+
ACOUSTIC_GUITAR = LilypondSettings.guitar('Acoustic Guitar')
|
62
|
+
ELECTRIC_GUITAR = LilypondSettings.guitar('Electric Guitar')
|
63
|
+
OVERDRIVEN_GUITAR = LilypondSettings.guitar('Overdriven Guitar')
|
64
|
+
DISTORTION_GUITAR = LilypondSettings.guitar('Distortion Guitar')
|
65
|
+
GUITAR_HARMONICS = LilypondSettings.guitar('Guitar Harmonics')
|
66
|
+
|
67
|
+
ACOUSTIC_BASS = LilypondSettings.bass_guitar('Acoustic Bass')
|
68
|
+
ELECTRIC_BASS = LilypondSettings.bass_guitar('Electric Bass')
|
69
|
+
FRETLESS_BASS = LilypondSettings.bass_guitar('Fretless Bass')
|
70
|
+
SLAP_BASS = LilypondSettings.bass_guitar('Slap Bass')
|
71
|
+
SYNTH_BASS = LilypondSettings.bass_guitar('Synth Bass')
|
72
|
+
|
73
|
+
VIOLIN = LilypondSettings.treble('Violin')
|
74
|
+
VIOLA = LilypondSettings.new('Viola', clefs: [Clef::TREBLE, Clef::ALTO])
|
75
|
+
CELLO = LilypondSettings.new('Cello', clefs: [Clef::BASS, Clef::TENOR])
|
76
|
+
CONTRABASS = LilypondSettings.bass('Contrabass', transpose_interval: 12)
|
77
|
+
TREMOLO_STRINGS = LilypondSettings.treble_bass('Tremolo Strings')
|
78
|
+
PIZZICATO_STRINGS = LilypondSettings.treble_bass('Pizzicato Strings')
|
79
|
+
ORCHESTRAL_HARP = LilypondSettings.treble_bass('Orchestral Harp')
|
80
|
+
TIMPANI = LilypondSettings.bass('Timpani')
|
81
|
+
STRING_ENSEMBLE = LilypondSettings.treble_bass('String Ensemble')
|
82
|
+
|
83
|
+
TRUMPET = LilypondSettings.treble('Trumpet', transpose_interval: 2)
|
84
|
+
TROMBONE = LilypondSettings.new('Trombone', clefs: [Clef::BASS, Clef::TENOR])
|
85
|
+
TUBA = LilypondSettings.bass('Tuba')
|
86
|
+
FRENCH_HORN = LilypondSettings.bass('French Horn')
|
87
|
+
|
88
|
+
SOPRANO_SAX = LilypondSettings.treble('Soprano Sax', transpose_interval: 2)
|
89
|
+
ALTO_SAX = LilypondSettings.treble('Alto Sax', transpose_interval: 9)
|
90
|
+
TENOR_SAX = LilypondSettings.treble('Tenor Sax', transpose_interval: 14)
|
91
|
+
BARITONE_SAX = LilypondSettings.treble('Baritone Sax', transpose_interval: 21)
|
92
|
+
OBOE = LilypondSettings.treble('Oboe')
|
93
|
+
ENGLISH_HORN = LilypondSettings.treble('English Horn', transpose_interval: 7)
|
94
|
+
BASSOON = LilypondSettings.bass('Bassoon')
|
95
|
+
CLARINET = LilypondSettings.treble('Clarinet', transpose_interval: 2)
|
96
|
+
PICCOLO = LilypondSettings.treble('Piccolo', transpose_interval: -12)
|
97
|
+
end
|
98
|
+
|
99
|
+
class Part
|
100
|
+
def lilypond_settings
|
101
|
+
find_settings(LilypondSettings)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -1,52 +1,134 @@
|
|
1
1
|
module Musicality
|
2
2
|
|
3
3
|
class Note
|
4
|
+
SMALLEST_PIECE = Rational(1,256)
|
5
|
+
|
4
6
|
def to_lilypond sharpit = false
|
5
|
-
|
6
|
-
d = duration
|
7
|
-
|
8
|
-
if d > 1
|
9
|
-
dur_strs += ["1"]*d.to_i
|
10
|
-
d -= d.to_i
|
11
|
-
end
|
7
|
+
subdurs = [1]*@duration.to_i + fractional_subdurs(SMALLEST_PIECE)
|
12
8
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
piece_strs = []
|
10
|
+
pitches_to_strs = Hash[ pitches.map {|p| [p,p.to_lilypond(sharpit)] }]
|
11
|
+
while subdurs.any?
|
12
|
+
subdur = subdurs.shift
|
13
|
+
dur_str = subdur.denominator.to_s
|
14
|
+
if subdurs.any? && subdur == subdurs.first*2
|
15
|
+
dur_str += "."
|
16
|
+
subdurs.shift
|
17
17
|
end
|
18
|
+
last = subdurs.empty?
|
18
19
|
|
19
|
-
if
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
20
|
+
piece_str = if pitches.any?
|
21
|
+
if last
|
22
|
+
# figure if ties are needed on per-pitch basis, based on note links
|
23
|
+
if pitches_to_strs.size == 1
|
24
|
+
p, p_str = pitches_to_strs.first
|
25
|
+
needs_tie = links.include?(p) && links[p].is_a?(Link::Tie)
|
26
|
+
p_str + dur_str + (needs_tie ? "~" : "")
|
27
|
+
else
|
28
|
+
p_strs = pitches_to_strs.map do |p,p_str|
|
29
|
+
if links.include?(p) && links[p].is_a?(Link::Tie)
|
30
|
+
p_str + "~"
|
31
|
+
else
|
32
|
+
p_str
|
33
|
+
end
|
34
|
+
end
|
35
|
+
"<#{p_strs.join(" ")}>" + dur_str
|
28
36
|
end
|
29
|
-
|
30
|
-
dur_strs.push (2**(-n)).to_i.to_s + "."
|
31
37
|
else
|
32
|
-
|
38
|
+
str = if pitches.size == 1
|
39
|
+
pitches_to_strs.values.first
|
40
|
+
else
|
41
|
+
"<#{pitches_to_strs.values.join(" ")}>"
|
42
|
+
end
|
43
|
+
str + dur_str + "~"
|
33
44
|
end
|
45
|
+
else
|
46
|
+
"r" + dur_str
|
34
47
|
end
|
48
|
+
|
49
|
+
piece_strs.push piece_str
|
35
50
|
end
|
36
|
-
|
51
|
+
|
52
|
+
if pitches.any?
|
53
|
+
if articulation != Articulations::NORMAL
|
54
|
+
piece_strs[0] += "-" + ARTICULATION_SYMBOLS[articulation]
|
55
|
+
end
|
56
|
+
|
57
|
+
if begins_slur?
|
58
|
+
piece_strs[-1] += MARK_SYMBOLS[Mark::Slur::Begin]
|
59
|
+
end
|
60
|
+
|
61
|
+
if ends_slur?
|
62
|
+
piece_strs[-1] += MARK_SYMBOLS[Mark::Slur::End]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if begins_triplet?
|
67
|
+
piece_strs[0].prepend("\\tuplet 3/2 {")
|
68
|
+
end
|
69
|
+
|
70
|
+
if ends_triplet?
|
71
|
+
piece_strs[-1].concat("}")
|
72
|
+
end
|
73
|
+
|
74
|
+
return piece_strs.join(" ")
|
75
|
+
end
|
76
|
+
|
77
|
+
def fractional_subdurs smallest_piece
|
78
|
+
remaining = @duration - @duration.to_i
|
79
|
+
pieces = []
|
80
|
+
i = 0
|
81
|
+
while((current_dur = Rational(1,2<<i)) >= smallest_piece)
|
82
|
+
if remaining >= current_dur
|
83
|
+
pieces.push current_dur
|
84
|
+
remaining -= current_dur
|
85
|
+
end
|
86
|
+
i += 1
|
87
|
+
end
|
88
|
+
|
89
|
+
unless remaining.zero?
|
90
|
+
raise RuntimeError, "Non-zero remainder #{remaining}"
|
91
|
+
end
|
92
|
+
|
93
|
+
return pieces
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
|
99
|
+
def pieces_to_dur_strs pieces
|
100
|
+
dur_strs = []
|
101
|
+
while pieces.any?
|
102
|
+
piece = pieces.shift
|
103
|
+
triplet = piece.denominator % 3 == 0
|
104
|
+
piece_str = (triplet ? (piece * 1.5.to_r) : piece).denominator.to_s
|
105
|
+
if pieces.any? && piece == pieces.first*2
|
106
|
+
piece_str += "."
|
107
|
+
pieces.shift
|
108
|
+
end
|
109
|
+
|
110
|
+
if triplet
|
111
|
+
piece_str = "\\tuplet 3/2 { #{piece_str} }"
|
112
|
+
end
|
113
|
+
|
114
|
+
dur_strs.push piece_str
|
115
|
+
end
|
116
|
+
return dur_strs
|
117
|
+
end
|
118
|
+
|
119
|
+
def figure_pitch_and_joipiece_str sharpit
|
37
120
|
if pitches.any?
|
38
121
|
if pitches.size == 1
|
39
|
-
p_str = pitches.first.to_lilypond
|
122
|
+
p_str = pitches.first.to_lilypond(sharpit)
|
40
123
|
else
|
41
|
-
p_str = "<" + pitches.map {|p| p.to_lilypond}.join(" ") + ">"
|
124
|
+
p_str = "<" + pitches.map {|p| p.to_lilypond(sharpit) }.join(" ") + ">"
|
42
125
|
end
|
43
|
-
|
126
|
+
joipiece_str = "~ "
|
44
127
|
else
|
45
128
|
p_str = "r"
|
46
|
-
|
129
|
+
joipiece_str = " "
|
47
130
|
end
|
48
|
-
|
49
|
-
return dur_strs.map {|dur_str| p_str + dur_str }.join(join_str)
|
131
|
+
return [ p_str, joipiece_str ]
|
50
132
|
end
|
51
133
|
end
|
52
134
|
|
@@ -1,11 +1,122 @@
|
|
1
1
|
module Musicality
|
2
2
|
|
3
3
|
class PartEngraver
|
4
|
-
|
5
|
-
|
4
|
+
MAX_LINE_LEN = 76
|
5
|
+
INDENT = " "
|
6
|
+
|
7
|
+
def initialize part, title
|
8
|
+
if part.invalid?
|
9
|
+
raise ArgumentError, "given part contains errors: #{part.errors}"
|
10
|
+
end
|
11
|
+
|
12
|
+
@transpose_interval = part.lilypond_settings.transpose_interval
|
13
|
+
@clefs = part.lilypond_settings.clefs
|
14
|
+
@part = (@transpose_interval == 0) ? part : part.transpose(@transpose_interval)
|
15
|
+
@title = title
|
16
|
+
@indent = INDENT
|
17
|
+
end
|
18
|
+
|
19
|
+
def increase_indent
|
20
|
+
@indent += INDENT
|
21
|
+
end
|
22
|
+
|
23
|
+
def decrease_indent
|
24
|
+
@indent = @indent[0...-INDENT.size]
|
25
|
+
end
|
26
|
+
|
27
|
+
def make_lilypond start_key, start_meter, key_changes: {}, meter_changes: {}, master: false
|
28
|
+
if @transpose_interval != 0
|
29
|
+
start_key = transpose_start_key(start_key)
|
30
|
+
key_changes = transpose_key_changes(key_changes)
|
31
|
+
end
|
32
|
+
|
33
|
+
sharpit = start_key.sharp?
|
34
|
+
return make_preliminary(start_meter, start_key, master) +
|
35
|
+
make_body(sharpit) + make_final()
|
36
|
+
end
|
37
|
+
|
38
|
+
def make_preliminary start_meter, start_key, master
|
39
|
+
clef = self.class.best_clef(@part.notes, @clefs)
|
40
|
+
|
41
|
+
output = @indent + "\\new Staff {\n"
|
42
|
+
increase_indent
|
43
|
+
output += @indent + "\\set Staff.instrumentName = \\markup { \"#{@title}\" }\n"
|
44
|
+
output += @indent + "\\clef #{clef}\n"
|
45
|
+
if master
|
46
|
+
output += @indent + start_meter.to_lilypond + "\n"
|
47
|
+
end
|
48
|
+
output += @indent + start_key.to_lilypond + "\n"
|
49
|
+
return output
|
50
|
+
end
|
51
|
+
|
52
|
+
def make_body sharpit
|
53
|
+
pieces = @part.notes.map {|n| n.to_lilypond(sharpit)}
|
54
|
+
|
55
|
+
output = ""
|
56
|
+
while pieces.any?
|
57
|
+
line = @indent + pieces.shift
|
58
|
+
until pieces.empty? || (line.size + 1 + pieces.first.size) > MAX_LINE_LEN
|
59
|
+
line += " " + pieces.shift
|
60
|
+
end
|
61
|
+
output += line + "\n"
|
62
|
+
end
|
63
|
+
return output
|
64
|
+
end
|
65
|
+
|
66
|
+
def make_final
|
67
|
+
decrease_indent
|
68
|
+
return @indent + "}\n"
|
69
|
+
end
|
70
|
+
|
71
|
+
CLEF_RANGES = {
|
72
|
+
Clef::TREBLE => Pitches::C4..Pitches::A5,
|
73
|
+
Clef::ALTO => Pitches::D3..Pitches::B4,
|
74
|
+
Clef::TENOR => Pitches::B2..Pitches::G4,
|
75
|
+
Clef::BASS => Pitches::E2..Pitches::C4,
|
76
|
+
}
|
77
|
+
|
78
|
+
def self.best_clef notes, allowed_clefs
|
79
|
+
raise ArgumentError unless notes.any?
|
80
|
+
raise ArgumentError unless allowed_clefs.any?
|
81
|
+
|
82
|
+
ranges = CLEF_RANGES.select {|clef,range| allowed_clefs.include?(clef) }
|
83
|
+
range_scores = Hash.new(0)
|
84
|
+
|
85
|
+
in_triplet = false
|
86
|
+
notes.each do |note|
|
87
|
+
if note.begins_triplet?
|
88
|
+
in_triplet = true
|
89
|
+
end
|
90
|
+
|
91
|
+
dur = note.duration * (in_triplet ? Rational(2,3) : 1)
|
92
|
+
note.pitches.each do |p|
|
93
|
+
ranges.each do |name,range|
|
94
|
+
if p >= range.min && p <= range.max
|
95
|
+
range_scores[name] += dur
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
if note.ends_triplet?
|
101
|
+
in_triplet = false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
range_score = range_scores.max_by {|range,score| score}
|
105
|
+
range_score.nil? ? allowed_clefs.first : range_score[0]
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def transpose_start_key start_key
|
111
|
+
start_key.transpose(@transpose_interval)
|
6
112
|
end
|
7
113
|
|
8
|
-
def
|
114
|
+
def transpose_key_changes key_changes
|
115
|
+
Hash[
|
116
|
+
key_changes.map do |off,change|
|
117
|
+
change.clone {|v| v.transpose(@transpose_interval) }
|
118
|
+
end
|
119
|
+
]
|
9
120
|
end
|
10
121
|
end
|
11
122
|
|