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
@@ -0,0 +1,111 @@
1
+ module Musicality
2
+ module Instruments
3
+ DEFAULT_INSTRUMENT = ACOUSTIC_GRAND_PIANO = Instrument.treble_bass("Acoustic Grand Piano", 1)
4
+ BRIGHT_ACOUSTIC_PIANO = Instrument.treble_bass("Bright Acoustic Piano", 2)
5
+ ELECTRIC_GRAND_PIANO = Instrument.treble_bass("Electric Grand Piano", 3)
6
+ HONKY_TONK_PIANO = Instrument.treble_bass("Honky Tonk Piano", 4)
7
+ ELECTRIC_PIANO_1 = Instrument.treble_bass("Electric Piano", 5)
8
+ ELECTRIC_PIANO_2 = Instrument.treble_bass("Electric Piano", 6)
9
+ HARPSICHORD = Instrument.treble_bass("Harpsichord", 7)
10
+ CLAVINET = Instrument.treble_bass("Clavinet", 8)
11
+ CELESTA = Instrument.treble_bass("Celesta", 9, -12)
12
+
13
+ GLOCKENSPIEL = Instrument.treble_bass("Glockenspiel", 10, -24)
14
+ MUSIC_BOX = Instrument.treble_bass("Music Box", 11)
15
+ VIBRAPHONE = Instrument.treble_bass("Virbaphone",12)
16
+ MARIMBA = Instrument.treble_bass("Marimba",13)
17
+ XYLOPHONE = Instrument.treble_bass("Xylophone",14, -12)
18
+ TUBULAR_BELLS = Instrument.treble_bass("Tubular Bells",15)
19
+ DULCIMER = Instrument.treble("Dulcimer",16)
20
+
21
+ DRAWBAR_ORGAN = Instrument.treble_bass("Drawbar Organ", 17)
22
+ PERCUSSIVE_ORGAN = Instrument.treble_bass("Percussive Organ", 18)
23
+ ROCK_ORGAN = Instrument.treble_bass("Rock Organ", 19)
24
+ CHURCH_ORGAN = Instrument.treble_bass("Church Organ", 20)
25
+ REED_ORGAN = Instrument.treble_bass("Reed Organ", 21)
26
+ ACCORDIAN = Instrument.treble_bass("Accordion", 22)
27
+ HARMONICA = Instrument.treble("Harmonica", 23)
28
+ TANGO_ACCORDIAN = Instrument.treble_bass("Tango Accordion", 24)
29
+
30
+ ACOUSTIC_GUITAR_NYLON = Instrument.guitar('Acoustic Guitar (nylon)', 25)
31
+ ACOUSTIC_GUITAR_STEEL = Instrument.guitar('Acoustic Guitar (steel)', 26)
32
+ ELECTRIC_GUITAR_JAZZ = Instrument.guitar('Electric Guitar (jazz)', 27)
33
+ ELECTRIC_GUITAR_CLEAN = Instrument.guitar('Electric Guitar (clean)', 28)
34
+ ELECTRIC_GUITAR_MUTED = Instrument.guitar('Electric Guitar (muted)', 29)
35
+ OVERDRIVEN_GUITAR = Instrument.guitar('Overdriven Guitar', 30)
36
+ DISTORTION_GUITAR = Instrument.guitar('Distortion Guitar', 31)
37
+ GUITAR_HARMONICS = Instrument.guitar('Guitar Harmonics', 32)
38
+
39
+ ACOUSTIC_BASS = Instrument.bass_guitar('Acoustic Bass', 33)
40
+ ELECTRIC_BASS_FINGER = Instrument.bass_guitar('Electric Bass (finger)', 34)
41
+ ELECTRIC_BASS_PICK = Instrument.bass_guitar('Electric Bass (pick)', 35)
42
+ FRETLESS_BASS = Instrument.bass_guitar('Fretless Bass', 36)
43
+ SLAP_BASS_1 = Instrument.bass_guitar('Slap Bass', 37)
44
+ SLAP_BASS_2 = Instrument.bass_guitar('Slap Bass', 38)
45
+ SYNTH_BASS_1 = Instrument.bass_guitar('Synth Bass', 39)
46
+ SYNTH_BASS_2 = Instrument.bass_guitar('Synth Bass', 40)
47
+
48
+ VIOLIN = Instrument.treble('Violin', 41)
49
+ VIOLA = Instrument.treble_alto('Viola', 42)
50
+ CELLO = Instrument.tenor_bass('Cello', 43)
51
+ CONTRABASS = Instrument.bass('Contrabass', 44, 12)
52
+ TREMOLO_STRINGS = Instrument.treble_bass('Tremolo Strings', 45)
53
+ PIZZICATO_STRINGS = Instrument.treble_bass('Pizzicato Strings', 46)
54
+ ORCHESTRAL_HARP = Instrument.treble_bass('Orchestral Harp', 47)
55
+ TIMPANI = Instrument.bass('Timpani', 48)
56
+ STRING_ENSEMBLE_1 = Instrument.treble_bass('String Ensemble', 49)
57
+ STRING_ENSEMBLE_2 = Instrument.treble_bass('String Ensemble', 50)
58
+ SYNTH_STRINGS_1 = Instrument.treble_bass('Synth Strings', 51)
59
+ SYNTH_STRINGS_2 = Instrument.treble_bass('Synth Strings', 52)
60
+
61
+ CHOIR_AAHS = Instrument.treble_bass('Choir Aahs', 53)
62
+ VOICE_OOHS = Instrument.treble_bass('Voice Oohs', 54)
63
+ SYNTH_VOICE = Instrument.treble_bass('Synth Voice', 55)
64
+ ORCHESTRA_HIT = Instrument.treble_bass('Orchestra Hit', 56)
65
+
66
+ TRUMPET = Instrument.treble('Trumpet', 57, 2)
67
+ TROMBONE = Instrument.tenor_bass('Trombone', 58)
68
+ TUBA = Instrument.bass('Tuba', 59)
69
+ MUTED_TRUMPET = Instrument.treble('Muted Trumpet', 60)
70
+ FRENCH_HORN = Instrument.bass('French Horn', 61)
71
+ BRASS_SECTION = Instrument.treble_bass('Brass Section', 62)
72
+ SYNTH_BRASS_1 = Instrument.treble_bass('Synth Brass', 63)
73
+ SYNTH_BRASS_2 = Instrument.treble_bass('Synth Brass', 64)
74
+
75
+ SOPRANO_SAX = Instrument.treble('Soprano Sax', 65, 2)
76
+ ALTO_SAX = Instrument.treble('Alto Sax', 66, 9)
77
+ TENOR_SAX = Instrument.treble('Tenor Sax', 67, 14)
78
+ BARITONE_SAX = Instrument.treble('Baritone Sax', 68, 21)
79
+ OBOE = Instrument.treble('Oboe', 69)
80
+ ENGLISH_HORN = Instrument.treble('English Horn', 70, 7)
81
+ BASSOON = Instrument.bass('Bassoon', 71)
82
+ CLARINET = Instrument.treble('Clarinet', 72, 2)
83
+
84
+ PICCOLO = Instrument.treble('Piccolo', 73, -12)
85
+ FLUTE = Instrument.treble('Flute', 74)
86
+ RECORDER = Instrument.treble('Recorder', 75)
87
+ PAN_FLUTE = Instrument.treble_bass('Pan Flute', 76)
88
+ BLOWN_BOTTLE = Instrument.treble_bass('Blown Bottle', 77)
89
+ SHAKUHACHI = Instrument.treble_bass('Shakuhachi', 78)
90
+ WHISTLE = Instrument.treble_bass('Whistle', 79)
91
+ OCARINA = Instrument.treble_bass('Ocarina', 80)
92
+
93
+ LEAD_SQUARE = Instrument.treble_bass('Lead (square)', 81)
94
+ LEAD_SAWTOOTH = Instrument.treble_bass('Lead (sawtooth)', 82)
95
+ LEAD_CALLIOPE = Instrument.treble_bass('Lead (calliope)', 83)
96
+ LEAD_CHIFF = Instrument.treble_bass('Lead (chiff)', 84)
97
+ LEAD_CHARANG = Instrument.treble_bass('Lead (charang)', 85)
98
+ LEAD_VOICE = Instrument.treble_bass('Lead (voice)', 86)
99
+ LEAD_FIFTHS = Instrument.treble_bass('Lead (fifths)', 87)
100
+ LEAD_PLUS_BASS = Instrument.treble_bass('Lead (bass+lead)', 88)
101
+
102
+ PAD_NEW_AGE = Instrument.treble_bass('Pad (new age)', 89)
103
+ PAD_WARM = Instrument.treble_bass('Pad (warm)', 90)
104
+ PAD_POLYSYNTH = Instrument.treble_bass('Pad (polysynth)', 91)
105
+ PAD_CHOIR = Instrument.treble_bass('Pad (choir)', 92)
106
+ PAD_BOWED = Instrument.treble_bass('Pad (bowed)', 93)
107
+ PAD_METALLIC = Instrument.treble_bass('Pad (metallic)', 94)
108
+ PAD_HALO = Instrument.treble_bass('Pad (halo)', 95)
109
+ PAD_SWEEP = Instrument.treble_bass('Pad (sweep)', 96)
110
+ end
111
+ end
@@ -0,0 +1,137 @@
1
+ module Musicality
2
+
3
+ class Key
4
+ include Packable
5
+
6
+ FLAT = :flat
7
+ SHARP = :sharp
8
+ ACCIDENTAL_TYPES = [FLAT, SHARP]
9
+
10
+ MAJOR = :major
11
+ MINOR = :minor
12
+ TRIAD_TYPES = [MAJOR, MINOR]
13
+
14
+ TONICS = {
15
+ MAJOR => {
16
+ FLAT => [PitchClasses::F, PitchClasses::Bb, PitchClasses::Eb,
17
+ PitchClasses::Ab, PitchClasses::Db, PitchClasses::Gb, PitchClasses::Cb],
18
+ SHARP => [PitchClasses::G,PitchClasses::D,PitchClasses::A,
19
+ PitchClasses::E,PitchClasses::B,PitchClasses::Fs,PitchClasses::Cs]
20
+ },
21
+ MINOR => {
22
+ FLAT => [PitchClasses::D,PitchClasses::G,PitchClasses::C,
23
+ PitchClasses::F,PitchClasses::Bb,PitchClasses::Eb,PitchClasses::Ab],
24
+ SHARP => [PitchClasses::E,PitchClasses::B,PitchClasses::Fs,
25
+ PitchClasses::Cs,PitchClasses::Gs,PitchClasses::Ds,PitchClasses::As]
26
+ }
27
+ }
28
+
29
+ ACCIDENTALS = {
30
+ FLAT => [PitchClasses::Bb,PitchClasses::Eb,PitchClasses::Ab,PitchClasses::Db,
31
+ PitchClasses::Gb,PitchClasses::Cb,PitchClasses::Fb],
32
+ SHARP => [PitchClasses::Fs,PitchClasses::Cs,PitchClasses::Gs,
33
+ PitchClasses::Ds,PitchClasses::As,PitchClasses::Es,PitchClasses::Bs]
34
+ }
35
+
36
+ attr_reader :tonic_pc, :triad, :accidental_pref
37
+
38
+ def initialize tonic_pc, triad: MAJOR, accidental_pref: FLAT
39
+ self.tonic_pc = tonic_pc
40
+ self.triad = triad
41
+ self.accidental_pref = accidental_pref
42
+ end
43
+
44
+ def self.major_flat tonic_pc
45
+ Key.new(tonic_pc, triad: MAJOR, accidental_pref: FLAT)
46
+ end
47
+
48
+ def self.major_sharp tonic_pc
49
+ Key.new(tonic_pc, triad: MAJOR, accidental_pref: SHARP)
50
+ end
51
+
52
+ def self.minor_flat tonic_pc
53
+ Key.new(tonic_pc, triad: MINOR, accidental_pref: FLAT)
54
+ end
55
+
56
+ def self.minor_sharp tonic_pc
57
+ Key.new(tonic_pc, triad: MINOR, accidental_pref: SHARP)
58
+ end
59
+
60
+ def major?; @triad == MAJOR; end
61
+ def minor?; @triad == MINOR; end
62
+
63
+ def flat?; accidental_type == FLAT; end
64
+ def sharp?; accidental_type == SHARP; end
65
+
66
+ def accidental_free?
67
+ (major? && @tonic_pc == PitchClasses::C) || (minor? && @tonic_pc == PitchClasses::A)
68
+ end
69
+
70
+ def triad= triad
71
+ if triad != @triad
72
+ unless TRIAD_TYPES.include? triad
73
+ raise ArgumentError, "Triad type #{triad} is not supported"
74
+ end
75
+ @triad = triad
76
+ reset_accidentals
77
+ end
78
+ end
79
+
80
+ def accidental_pref= accidental_pref
81
+ if accidental_pref != @accidental_pref
82
+ unless ACCIDENTAL_TYPES.include? accidental_pref
83
+ raise ArgumentError, "Accidental type #{accidental_pref} is not supported"
84
+ end
85
+ @accidental_pref = accidental_pref
86
+ reset_accidentals
87
+ end
88
+ end
89
+
90
+ def tonic_pc= tonic_pc
91
+ if tonic_pc != @tonic_pc
92
+ @tonic_pc = PitchClass.from_i(tonic_pc)
93
+ reset_accidentals
94
+ end
95
+ end
96
+
97
+ def ==(other)
98
+ return @tonic_pc == other.tonic_pc && @triad == other.triad &&
99
+ @accidental_pref == other.accidental_pref
100
+ end
101
+
102
+ def clone
103
+ Marshal.load(Marshal.dump(self))
104
+ end
105
+
106
+ def accidentals
107
+ unless @accidentals
108
+ @accidentals = []
109
+ unless accidental_free?
110
+ acc_type = accidental_type
111
+ idx = TONICS[@triad][acc_type].index(@tonic_pc)
112
+ @accidentals = ACCIDENTALS[acc_type].take(idx+1)
113
+ end
114
+ end
115
+ return @accidentals
116
+ end
117
+
118
+ def accidental_type
119
+ if accidental_free? || TONICS[@triad][@accidental_pref].include?(@tonic_pc)
120
+ @accidental_pref
121
+ else
122
+ (@accidental_pref == FLAT) ? SHARP : FLAT
123
+ end
124
+ end
125
+
126
+ def transpose interval
127
+ new_key = self.clone
128
+ new_key.tonic_pc += interval
129
+ return new_key
130
+ end
131
+
132
+ private
133
+
134
+ def reset_accidentals; @accidentals = nil; end
135
+ end
136
+
137
+ end
@@ -0,0 +1,37 @@
1
+ module Musicality
2
+ module Keys
3
+ C_MAJOR = Key.major_sharp(PitchClasses::C)
4
+ G_MAJOR = Key.major_sharp(PitchClasses::G)
5
+ D_MAJOR = Key.major_sharp(PitchClasses::D)
6
+ A_MAJOR = Key.major_sharp(PitchClasses::A)
7
+ E_MAJOR = Key.major_sharp(PitchClasses::E)
8
+ B_MAJOR = Key.major_sharp(PitchClasses::B)
9
+ F_SHARP_MAJOR = Key.major_sharp(PitchClasses::Fs)
10
+ C_SHARP_MAJOR = Key.major_sharp(PitchClasses::Cs)
11
+
12
+ F_MAJOR = Key.major_flat(PitchClasses::F)
13
+ B_FLAT_MAJOR = Key.major_flat(PitchClasses::Bb)
14
+ E_FLAT_MAJOR = Key.major_flat(PitchClasses::Eb)
15
+ A_FLAT_MAJOR = Key.major_flat(PitchClasses::Ab)
16
+ D_FLAT_MAJOR = Key.major_flat(PitchClasses::Db)
17
+ G_FLAT_MAJOR = Key.major_flat(PitchClasses::Gb)
18
+ C_FLAT_MAJOR = Key.major_flat(PitchClasses::Cb)
19
+
20
+ A_MINOR = Key.minor_sharp(PitchClasses::A)
21
+ E_MINOR = Key.minor_sharp(PitchClasses::E)
22
+ B_MINOR = Key.minor_sharp(PitchClasses::B)
23
+ F_SHARP_MINOR = Key.minor_sharp(PitchClasses::Fs)
24
+ C_SHARP_MINOR = Key.minor_sharp(PitchClasses::Cs)
25
+ G_SHARP_MINOR = Key.minor_sharp(PitchClasses::Gs)
26
+ D_SHARP_MINOR = Key.minor_sharp(PitchClasses::Ds)
27
+ A_SHARP_MINOR = Key.minor_sharp(PitchClasses::As)
28
+
29
+ D_MINOR = Key.minor_flat(PitchClasses::D)
30
+ G_MINOR = Key.minor_flat(PitchClasses::G)
31
+ C_MINOR = Key.minor_flat(PitchClasses::C)
32
+ F_MINOR = Key.minor_flat(PitchClasses::F)
33
+ B_FLAT_MINOR = Key.minor_flat(PitchClasses::Bb)
34
+ E_FLAT_MINOR = Key.minor_flat(PitchClasses::Eb)
35
+ A_FLAT_MINOR = Key.minor_flat(PitchClasses::Ab)
36
+ end
37
+ end
@@ -6,6 +6,8 @@ module Musicality
6
6
  # @return [Pitch] The pitch of the note which is being connected to.
7
7
  #
8
8
  class Link
9
+ include Packable
10
+
9
11
  def clone
10
12
  Marshal.load(Marshal.dump(self))
11
13
  end
@@ -23,12 +25,10 @@ class Link
23
25
  end
24
26
 
25
27
  def to_s
26
- self.class::LINK_CHAR
28
+ LINK_SYMBOLS[self.class]
27
29
  end
28
30
 
29
- class Tie < Link
30
- LINK_CHAR = LINK_SYMBOLS[Links::TIE]
31
- end
31
+ class Tie < Link; end
32
32
 
33
33
  class TargetedLink < Link
34
34
  attr_accessor :target_pitch
@@ -55,21 +55,8 @@ class Link
55
55
  end
56
56
  end
57
57
 
58
- class Glissando < TargetedLink
59
- LINK_CHAR = LINK_SYMBOLS[Links::GLISSANDO]
60
- end
61
-
62
- class Portamento < TargetedLink
63
- LINK_CHAR = LINK_SYMBOLS[Links::PORTAMENTO]
64
- end
65
-
66
- class Slur < TargetedLink
67
- LINK_CHAR = LINK_SYMBOLS[Links::SLUR]
68
- end
69
-
70
- class Legato < TargetedLink
71
- LINK_CHAR = LINK_SYMBOLS[Links::LEGATO]
72
- end
58
+ class Glissando < TargetedLink; end
59
+ class Portamento < TargetedLink; end
73
60
  end
74
61
 
75
62
  end
@@ -0,0 +1,43 @@
1
+ module Musicality
2
+
3
+ class Mark
4
+ include Packable
5
+
6
+ def clone
7
+ self.class.new
8
+ end
9
+
10
+ def to_s
11
+ MARK_SYMBOLS[self.class]
12
+ end
13
+
14
+ def ==(other)
15
+ self.class == other.class
16
+ end
17
+
18
+ class Slur < Mark
19
+ class Begin < Slur
20
+ def begins?; true; end
21
+ def ends?; false; end
22
+ end
23
+
24
+ class End < Slur
25
+ def begins?; false; end
26
+ def ends?; true; end
27
+ end
28
+ end
29
+
30
+ class Triplet < Mark
31
+ class Begin < Triplet
32
+ def begins?; true; end
33
+ def ends?; false; end
34
+ end
35
+
36
+ class End < Triplet
37
+ def begins?; false; end
38
+ def ends?; true; end
39
+ end
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,11 @@
1
+ module Musicality
2
+
3
+ module Marks
4
+ BEGIN_SLUR = Mark::Slur::Begin.new
5
+ END_SLUR = Mark::Slur::End.new
6
+
7
+ BEGIN_TRIPLET = Mark::Triplet::Begin.new
8
+ END_TRIPLET = Mark::Triplet::End.new
9
+ end
10
+
11
+ end
@@ -1,8 +1,12 @@
1
1
  module Musicality
2
2
 
3
3
  class Meter
4
+ include Packable
4
5
  include Validatable
5
6
 
7
+ special_packing(:beat_duration){|r| r.to_s}
8
+ special_unpacking(:beat_duration){|s| s.to_r}
9
+
6
10
  attr_reader :measure_duration, :beat_duration, :beats_per_measure
7
11
 
8
12
  def initialize beats_per_measure, beat_duration
@@ -3,32 +3,25 @@ module Musicality
3
3
  require 'set'
4
4
 
5
5
  class Note
6
+ include Packable
6
7
  include Validatable
7
8
 
8
- attr_reader :pitches, :links
9
- attr_accessor :articulation, :duration, :accented
10
-
11
- DEFAULT_ARTICULATION = Articulations::NORMAL
9
+ attr_reader :pitches, :links, :duration, :marks
10
+ attr_accessor :articulation
12
11
 
13
- def initialize duration, pitches = [], articulation: DEFAULT_ARTICULATION, accented: false, links: {}
14
- @duration = duration
12
+ def initialize duration, pitches = [], links: {}, articulation: Articulations::NORMAL, marks: []
13
+ self.duration = duration
15
14
  if !pitches.is_a? Enumerable
16
15
  pitches = [ pitches ]
17
16
  end
18
17
  @pitches = Set.new(pitches).sort
19
- @articulation = articulation
20
- @accented = accented
21
18
  @links = links
19
+ @articulation = articulation
20
+ @marks = marks
22
21
  end
23
22
 
24
23
  def check_methods
25
- [ :ensure_positive_duration, :check_pitches ]
26
- end
27
-
28
- def ensure_positive_duration
29
- unless @duration > 0
30
- raise NonPositiveError, "duration #{@duration} is not positive"
31
- end
24
+ [ :check_pitches ]
32
25
  end
33
26
 
34
27
  def check_pitches
@@ -43,13 +36,25 @@ class Note
43
36
  (self.pitches == other.pitches) &&
44
37
  (@links.to_a.sort == other.links.to_a.sort) &&
45
38
  (@articulation == other.articulation) &&
46
- (@accented == other.accented)
39
+ (@marks == marks)
47
40
  end
48
41
 
49
42
  def clone
50
43
  Marshal.load(Marshal.dump(self))
51
44
  end
52
45
 
46
+ def duration= duration
47
+ raise NonPositiveError, "duration #{duration} is not positive" unless duration > 0
48
+ #@duration = duration.is_a?(Duration) ? duration : duration.to_dur
49
+ @duration = duration
50
+ end
51
+
52
+ def resize duration
53
+ new_note = self.clone
54
+ new_note.duration = duration
55
+ return new_note
56
+ end
57
+
53
58
  def transpose diff
54
59
  self.clone.transpose! diff
55
60
  end
@@ -61,16 +66,23 @@ class Note
61
66
  end ]
62
67
  return self
63
68
  end
64
-
65
- def stretch ratio
66
- self.clone.stretch! ratio
69
+
70
+ def begins_slur?
71
+ marks.count {|m| m.is_a?(Mark::Slur::Begin) } > 0
67
72
  end
68
-
69
- def stretch! ratio
70
- @duration *= ratio
71
- return self
73
+
74
+ def ends_slur?
75
+ marks.count {|m| m.is_a?(Mark::Slur::End) } > 0
72
76
  end
73
-
77
+
78
+ def begins_triplet?
79
+ marks.count {|m| m.is_a?(Mark::Triplet::Begin) } > 0
80
+ end
81
+
82
+ def ends_triplet?
83
+ marks.count {|m| m.is_a?(Mark::Triplet::End) } > 0
84
+ end
85
+
74
86
  def to_s
75
87
  d = @duration.to_r
76
88
  if d.denominator == 1
@@ -90,14 +102,16 @@ class Note
90
102
  end.join(",")
91
103
 
92
104
  art_str = ARTICULATION_SYMBOLS[@articulation] || ""
93
- acc_str = @accented ? ACCENT_SYMBOL : ""
94
105
 
95
- return dur_str + pitch_links_str + art_str + acc_str
106
+ begin_marks_str = marks.select {|m| m.begins? }.map {|m| m.to_s }.join
107
+ end_marks_str = marks.select {|m| m.ends? }.map {|m| m.to_s }.join
108
+
109
+ return begin_marks_str + dur_str + pitch_links_str + art_str + end_marks_str
96
110
  end
97
111
 
98
112
  def self.add_note_method(name, dur)
99
- self.class.send(:define_method,name.to_sym) do |pitches = [], articulation: DEFAULT_ARTICULATION, links: {}, accented: false|
100
- Note.new(dur, pitches, articulation: articulation, links: links, accented: accented)
113
+ self.class.send(:define_method,name.to_sym) do |pitches = [], links: {}, articulation: Articulations::NORMAL, marks: []|
114
+ Note.new(dur, pitches, articulation: articulation, links: links, marks: marks)
101
115
  end
102
116
  end
103
117