coltrane 2.2.1 → 3.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +16 -1
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile.lock +25 -2
  5. data/README.md +3 -0
  6. data/bin/console +6 -2
  7. data/coltrane.gemspec +2 -1
  8. data/exe/coltrane +14 -224
  9. data/lib/coltrane.rb +3 -50
  10. data/lib/coltrane/commands.rb +14 -0
  11. data/lib/coltrane/commands/chords.rb +77 -0
  12. data/lib/coltrane/commands/command.rb +45 -0
  13. data/lib/coltrane/commands/common_chords.rb +33 -0
  14. data/lib/coltrane/commands/errors.rb +44 -0
  15. data/lib/coltrane/commands/find_progression.rb +28 -0
  16. data/lib/coltrane/commands/find_scale.rb +39 -0
  17. data/lib/coltrane/commands/notes.rb +44 -0
  18. data/lib/coltrane/commands/progression.rb +27 -0
  19. data/lib/{cli → coltrane/commands}/representation.rb +0 -0
  20. data/lib/coltrane/commands/scale.rb +46 -0
  21. data/lib/coltrane/renderers.rb +6 -0
  22. data/lib/coltrane/renderers/renderer.rb +42 -0
  23. data/lib/coltrane/renderers/text_renderer.rb +23 -0
  24. data/lib/coltrane/renderers/text_renderer/array_drawer.rb +45 -0
  25. data/lib/coltrane/renderers/text_renderer/base_drawer.rb +20 -0
  26. data/lib/coltrane/renderers/text_renderer/hash_drawer.rb +16 -0
  27. data/lib/coltrane/renderers/text_renderer/representation_guitar_chord_drawer.rb +95 -0
  28. data/lib/coltrane/renderers/text_renderer/representation_guitar_note_set_drawer.rb +76 -0
  29. data/lib/coltrane/renderers/text_renderer/representation_piano_note_set_drawer.rb +49 -0
  30. data/lib/coltrane/renderers/text_renderer/theory_chord_drawer.rb +14 -0
  31. data/lib/coltrane/renderers/text_renderer/theory_note_set_drawer.rb +17 -0
  32. data/lib/coltrane/renderers/text_renderer/theory_progression_drawer.rb +13 -0
  33. data/lib/coltrane/renderers/text_renderer/theory_progression_set_drawer.rb +22 -0
  34. data/lib/coltrane/renderers/text_renderer/theory_scale_drawer.rb +13 -0
  35. data/lib/coltrane/renderers/text_renderer/theory_scale_set_drawer.rb +27 -0
  36. data/lib/coltrane/representation.rb +12 -0
  37. data/lib/coltrane/representation/guitar.rb +34 -0
  38. data/lib/coltrane/representation/guitar/chord.rb +180 -0
  39. data/lib/coltrane/representation/guitar/note.rb +26 -0
  40. data/lib/coltrane/representation/guitar/note_set.rb +35 -0
  41. data/lib/coltrane/representation/guitar/string.rb +31 -0
  42. data/lib/coltrane/representation/guitar_like_instruments.rb +21 -0
  43. data/lib/coltrane/representation/piano.rb +36 -0
  44. data/lib/coltrane/representation/piano/note_set.rb +22 -0
  45. data/lib/{coltrane_synth.rb → coltrane/synth.rb.rb} +0 -0
  46. data/lib/{coltrane_synth → coltrane/synth}/base.rb +0 -0
  47. data/lib/{coltrane_synth → coltrane/synth}/synth.rb +0 -0
  48. data/lib/coltrane/theory.rb +54 -0
  49. data/lib/coltrane/theory/cadence.rb +9 -0
  50. data/lib/coltrane/{changes.rb → theory/changes.rb} +0 -0
  51. data/lib/coltrane/theory/chord.rb +101 -0
  52. data/lib/coltrane/theory/chord_quality.rb +113 -0
  53. data/lib/coltrane/theory/chord_substitutions.rb +11 -0
  54. data/lib/coltrane/theory/circle_of_fifths.rb +33 -0
  55. data/lib/coltrane/theory/classic_scales.rb +113 -0
  56. data/lib/coltrane/theory/diatonic_scale.rb +38 -0
  57. data/lib/coltrane/theory/errors.rb +97 -0
  58. data/lib/coltrane/theory/frequency.rb +52 -0
  59. data/lib/coltrane/theory/frequency_interval.rb +83 -0
  60. data/lib/coltrane/theory/interval.rb +209 -0
  61. data/lib/coltrane/theory/interval_class.rb +212 -0
  62. data/lib/coltrane/theory/interval_sequence.rb +157 -0
  63. data/lib/coltrane/theory/key.rb +18 -0
  64. data/lib/coltrane/{mode.rb → theory/mode.rb} +0 -0
  65. data/lib/coltrane/theory/notable_progressions.rb +30 -0
  66. data/lib/coltrane/theory/note.rb +104 -0
  67. data/lib/coltrane/theory/note_set.rb +101 -0
  68. data/lib/coltrane/theory/pitch.rb +94 -0
  69. data/lib/coltrane/theory/pitch_class.rb +154 -0
  70. data/lib/coltrane/theory/progression.rb +81 -0
  71. data/lib/coltrane/theory/progression_set.rb +18 -0
  72. data/lib/coltrane/{qualities.rb → theory/qualities.rb} +0 -0
  73. data/lib/coltrane/theory/roman_chord.rb +114 -0
  74. data/lib/coltrane/theory/scale.rb +161 -0
  75. data/lib/coltrane/theory/scale_search_result.rb +6 -0
  76. data/lib/coltrane/theory/scale_set.rb +40 -0
  77. data/lib/coltrane/theory/voicing.rb +41 -0
  78. data/lib/coltrane/version.rb +1 -1
  79. metadata +88 -63
  80. data/bin/rails +0 -17
  81. data/data/qualities.yml +0 -83
  82. data/db/cache.sqlite3 +0 -0
  83. data/db/cache_test.sqlite3 +0 -0
  84. data/db/config.yml +0 -11
  85. data/lib/cli.rb +0 -24
  86. data/lib/cli/bass_guitar.rb +0 -12
  87. data/lib/cli/chord.rb +0 -39
  88. data/lib/cli/config.rb +0 -39
  89. data/lib/cli/errors.rb +0 -46
  90. data/lib/cli/guitar.rb +0 -67
  91. data/lib/cli/guitar_chords.rb +0 -122
  92. data/lib/cli/notes.rb +0 -20
  93. data/lib/cli/piano.rb +0 -57
  94. data/lib/cli/scale.rb +0 -53
  95. data/lib/cli/text.rb +0 -16
  96. data/lib/cli/ukulele.rb +0 -14
  97. data/lib/coltrane/cache.rb +0 -43
  98. data/lib/coltrane/cadence.rb +0 -7
  99. data/lib/coltrane/chord.rb +0 -89
  100. data/lib/coltrane/chord_quality.rb +0 -111
  101. data/lib/coltrane/chord_substitutions.rb +0 -9
  102. data/lib/coltrane/circle_of_fifths.rb +0 -31
  103. data/lib/coltrane/classic_scales.rb +0 -94
  104. data/lib/coltrane/diatonic_scale.rb +0 -36
  105. data/lib/coltrane/errors.rb +0 -95
  106. data/lib/coltrane/frequency.rb +0 -50
  107. data/lib/coltrane/frequency_interval.rb +0 -81
  108. data/lib/coltrane/interval.rb +0 -208
  109. data/lib/coltrane/interval_class.rb +0 -210
  110. data/lib/coltrane/interval_sequence.rb +0 -155
  111. data/lib/coltrane/key.rb +0 -16
  112. data/lib/coltrane/notable_progressions.rb +0 -28
  113. data/lib/coltrane/note.rb +0 -98
  114. data/lib/coltrane/note_set.rb +0 -89
  115. data/lib/coltrane/pitch.rb +0 -92
  116. data/lib/coltrane/pitch_class.rb +0 -148
  117. data/lib/coltrane/progression.rb +0 -74
  118. data/lib/coltrane/roman_chord.rb +0 -112
  119. data/lib/coltrane/scale.rb +0 -154
  120. data/lib/coltrane/unordered_interval_class.rb +0 -7
  121. data/lib/coltrane/voicing.rb +0 -39
  122. data/lib/coltrane_game/question.rb +0 -7
  123. data/lib/coltrane_instruments.rb +0 -7
  124. data/lib/coltrane_instruments/guitar.rb +0 -10
  125. data/lib/coltrane_instruments/guitar/base.rb +0 -29
  126. data/lib/coltrane_instruments/guitar/chord.rb +0 -170
  127. data/lib/coltrane_instruments/guitar/note.rb +0 -24
  128. data/lib/coltrane_instruments/guitar/string.rb +0 -29
  129. data/lib/os.rb +0 -21
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Theory
5
+ #
6
+ # Pitch classes, and by classes here we don't mean in the sense of a ruby class
7
+ # are all the classes of pitches (frequencies) that are in a whole number of
8
+ # octaves apart.
9
+ #
10
+ # For example, C1, C2, C3 are all pitches from the C pitch class. Take a look into
11
+ # Notes description if you somehow feel this is confuse and that it could just be
12
+ # called as notes instead.
13
+ #
14
+ class PitchClass
15
+ attr_reader :integer
16
+ include Comparable
17
+
18
+ NOTATION = %w[C C# D D# E F F# G G# A A# B].freeze
19
+
20
+ def self.all_letters
21
+ %w[C D E F G A B]
22
+ end
23
+
24
+ def self.all
25
+ NOTATION.map { |n| new(n) }
26
+ end
27
+
28
+ def initialize(arg = nil, frequency: nil)
29
+ @integer = case arg
30
+ when String then NOTATION.index(arg)
31
+ when Frequency then frequency_to_integer(Frequency.new(arg))
32
+ when Numeric then (arg % 12)
33
+ when nil then frequency_to_integer(Frequency.new(frequency))
34
+ when PitchClass then arg.integer
35
+ else raise(WrongArgumentsError)
36
+ end
37
+ end
38
+
39
+ def self.[](arg, frequency: nil)
40
+ new(arg, frequency: nil)
41
+ end
42
+
43
+ def ==(other)
44
+ integer == other.integer
45
+ end
46
+
47
+ alias eql? ==
48
+ alias hash integer
49
+
50
+ def true_notation
51
+ NOTATION[integer]
52
+ end
53
+
54
+ def letter
55
+ name[0]
56
+ end
57
+
58
+ def ascending_interval_to(other)
59
+ Interval.new(self, (other.is_a?(PitchClass) ? other : Note.new(other)))
60
+ end
61
+
62
+ alias interval_to ascending_interval_to
63
+
64
+ def descending_interval_to(other)
65
+ Interval.new(
66
+ self,
67
+ (other.is_a?(PitchClass) ? other : Note.new(other)),
68
+ ascending: false
69
+ )
70
+ end
71
+
72
+ alias name true_notation
73
+
74
+ def pretty_name
75
+ name.tr('b', "\u266D").tr('#', "\u266F")
76
+ end
77
+
78
+ def accidental?
79
+ notation.match? /#|b/
80
+ end
81
+
82
+ def sharp?
83
+ notation.match? /#/
84
+ end
85
+
86
+ def flat?
87
+ notation.match? /b/
88
+ end
89
+
90
+ alias notation true_notation
91
+ alias to_s true_notation
92
+
93
+ def +(other)
94
+ case other
95
+ when Interval then self.class[integer + other.semitones]
96
+ when Integer then self.class[integer + other]
97
+ when PitchClass then self.class[integer + other.integer]
98
+ when Frequency then self.class.new(frequency: frequency + other)
99
+ end
100
+ end
101
+
102
+ def -(other)
103
+ case other
104
+ when Interval then self.class[integer - other.semitones]
105
+ when Integer then self.class[integer - other]
106
+ when PitchClass then Interval.new(self, other)
107
+ when Frequency then self.class.new(frequency: frequency - other)
108
+ end
109
+ end
110
+
111
+ def pitch_class
112
+ self
113
+ end
114
+
115
+ def <=>(other)
116
+ integer <=> other.integer
117
+ end
118
+
119
+ def fundamental_frequency
120
+ @fundamental_frequency ||=
121
+ Frequency[
122
+ Theory.base_tuning *
123
+ (2**((integer - Theory::BASE_PITCH_INTEGER.to_f) / 12))
124
+ ]
125
+ end
126
+
127
+ alias frequency fundamental_frequency
128
+
129
+ def self.size
130
+ NOTATION.size
131
+ end
132
+
133
+ def size
134
+ self.class.size
135
+ end
136
+
137
+ def enharmonic?(other)
138
+ case other
139
+ when String then integer == Note[other].integer
140
+ when Note then integer == other.integer
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ def frequency_to_integer(f)
147
+ begin
148
+ (::BASE_PITCH_INTEGER +
149
+ size * Math.log(f.to_f / Theory.base_tuning.to_f, 2)) % size
150
+ end.round
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Theory
5
+ # Allows the creation of chord progressions using standard notations.
6
+ # Ex: Progression.new('I-IV-V', key: 'Am')
7
+ class Progression
8
+ extend NotableProgressions
9
+ include Changes
10
+ attr_reader :scale, :chords, :notation
11
+
12
+ def self.find(*chords)
13
+ chords
14
+ .yield_self { |chords|
15
+ next chords if chords[0].is_a?(Chord)
16
+ chords.map {|c| Chord.new(name: c) }
17
+ }
18
+ .yield_self { |chords|
19
+ NoteSet[*chords.map(&:root_note)]
20
+ .yield_self { |root_notes|
21
+ Scale.having_notes(*root_notes).strict_scales
22
+ }
23
+ .reduce([]) { |memo, scale|
24
+ memo + [Progression.new(chords: chords, scale: scale)]
25
+ }
26
+ }
27
+ .yield_self { |progressions| ProgressionSet.new(*progressions) }
28
+ end
29
+
30
+ def initialize(notation = nil, chords: nil, roman_chords: nil, key: nil, scale: nil)
31
+ if notation.nil? && chords.nil? && roman_chords.nil? || key.nil? && scale.nil?
32
+ raise WrongKeywordsError,
33
+ '[chords:, [scale: || key:]] '\
34
+ '[roman_chords:, [scale: || key:]] '\
35
+ '[notation:, [scale: || key:]] '\
36
+ end
37
+
38
+ @scale = scale || Key[key]
39
+ @chords =
40
+ if !chords.nil?
41
+ chords
42
+ elsif !roman_chords.nil?
43
+ roman_chords.map(&:chord)
44
+ elsif !notation.nil?
45
+ @notation = notation
46
+ notation.split('-').map { |c| RomanChord.new(c, scale: @scale).chord }
47
+ end
48
+ end
49
+
50
+ def interval_sequence
51
+ @interval_sequence ||= IntervalSequence(notes: @root_notes)
52
+ end
53
+
54
+ def root_notes
55
+ @root_notes ||= @chords.map(&:root_note)
56
+ end
57
+
58
+ def roman_chords
59
+ @roman_chords ||= chords.map do |c|
60
+ RomanChord.new(chord: c, scale: scale)
61
+ end
62
+ end
63
+
64
+ def notation
65
+ roman_chords.map(&:notation).join('-')
66
+ end
67
+
68
+ def notes
69
+ NoteSet[*chords.map(&:notes).map(&:notes).flatten]
70
+ end
71
+
72
+ def notes_out
73
+ notes - scale.notes
74
+ end
75
+
76
+ def notes_out_size
77
+ notes_out.size
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Theory
5
+ class ProgressionSet
6
+ extend Forwardable
7
+ include Enumerable
8
+
9
+ def_delegators :progressions, :each
10
+
11
+ attr_accessor :progressions
12
+
13
+ def initialize(*progressions)
14
+ @progressions = progressions.sort_by(&:notes_out_size)
15
+ end
16
+ end
17
+ end
18
+ end
File without changes
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Theory
5
+ # This class deals with chords in roman notation. Ex: IVº.
6
+ class RomanChord
7
+ DIGITS = %w[I II III IV V VI VII].freeze
8
+ NOTATION_REGEX = /(b?[ivIV]*)(.*)/
9
+
10
+ NOTATION_SUBSTITUTIONS = [
11
+ %w[º dim],
12
+ %w[o dim],
13
+ %w[ø m7b5]
14
+ ].freeze
15
+
16
+ def initialize(notation = nil, chord: nil, key: nil, scale: nil)
17
+ if notation.nil? && chord.nil? || key.nil? && scale.nil?
18
+ raise WrongKeywordsError,
19
+ '[notation, [scale: || key:]] '\
20
+ '[chord:, [scale: || key:]] '\
21
+ end
22
+ @scale = scale || Key[key]
23
+ if notation
24
+ @notation = notation
25
+ elsif chord
26
+ @chord = chord.is_a?(String) ? Chord.new(name: chord) : chord
27
+ end
28
+ end
29
+
30
+ def degree
31
+ return @scale.degree_of_note(root_note) unless @chord.nil?
32
+ d = regexed_notation[:degree]
33
+ @flats = d.count('b')
34
+ d = d.delete('b')
35
+ @degree ||= DIGITS.index(d.upcase) + 1
36
+ end
37
+
38
+ def roman_numeral
39
+ r = DIGITS[degree]
40
+ minor? ? r.downcase : r
41
+ end
42
+
43
+ def upcase?
44
+ !!(regexed_notation[:degree][0].match /[[:upper:]]/)
45
+ end
46
+
47
+ def chord
48
+ @chord ||= Chord.new root_note: root_note,
49
+ quality: quality
50
+ end
51
+
52
+ def minor?
53
+ quality.has_minor_third?
54
+ end
55
+
56
+ def major?
57
+ quality.has_major_third?
58
+ end
59
+
60
+ def quality_name
61
+ return @chord.quality.name unless @chord.nil?
62
+ q = normalize_quality_name(regexed_notation[:quality])
63
+ minor = 'm' if (!q.match? /dim|m7b5/) && !upcase?
64
+ q = [minor, q].join
65
+ q.empty? ? 'M' : q
66
+ end
67
+
68
+ def quality
69
+ return @chord.quality unless @chord.nil?
70
+ ChordQuality.new(name: quality_name) if quality_name
71
+ end
72
+
73
+ def root_note
74
+ return @chord.root_note unless @chord.nil?
75
+ @scale[degree] - @flats
76
+ end
77
+
78
+ def notation
79
+ q = case quality_name
80
+ when 'm', 'M' then ''
81
+ when 'm7', 'M' then '7'
82
+ else quality_name
83
+ end
84
+
85
+ @notation ||= [
86
+ roman_numeral,
87
+ q
88
+ ].join
89
+ end
90
+
91
+ def function
92
+ return if @scale.name != 'Major'
93
+ %w[Tonic Supertonic Mediant Subdominant
94
+ Dominant Submediant Leading][degree - 1]
95
+ end
96
+
97
+ private
98
+
99
+ def regexed_notation
100
+ @regexed_notation ||= begin
101
+ matchdata = @notation.match(NOTATION_REGEX)
102
+ { degree: matchdata[1], quality: matchdata[2] }
103
+ end
104
+ end
105
+
106
+ def normalize_quality_name(quality_name)
107
+ NOTATION_SUBSTITUTIONS.reduce(quality_name) do |memo, subs|
108
+ break memo if memo.empty?
109
+ memo.gsub(*subs)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Theory
5
+ # Musical scale creation and manipulation
6
+ class Scale
7
+ extend ClassicScales
8
+ extend Forwardable
9
+
10
+ def_delegators :notes, :accidentals, :sharps, :flats
11
+
12
+ attr_reader :interval_sequence, :tone
13
+
14
+ def initialize(*relative_intervals, tone: 'C',
15
+ mode: 1,
16
+ name: nil,
17
+ notes: nil)
18
+ @name = name
19
+ if relative_intervals.any? && tone
20
+ @tone = Note[tone]
21
+ relative_intervals = relative_intervals.rotate(mode - 1)
22
+ @interval_sequence = IntervalSequence.new(
23
+ relative_intervals: relative_intervals
24
+ )
25
+ elsif notes
26
+ @notes = NoteSet[*notes]
27
+ @tone = @notes.first
28
+ ds = @notes.interval_sequence.relative_intervals
29
+ @interval_sequence = IntervalSequence.new(relative_intervals: ds)
30
+ else
31
+ raise WrongKeywordsError,
32
+ '[*relative_intervals, tone: "C", mode: 1] || [notes:]'
33
+ end
34
+ end
35
+
36
+ def id
37
+ [(name || @interval_sequence), tone.number]
38
+ end
39
+
40
+ def ==(other)
41
+ id == other.id
42
+ end
43
+
44
+ def name
45
+ @name ||= begin
46
+ is = interval_sequence.relative_intervals
47
+ (0...is.size).each do |i|
48
+ if (scale_name = ::ClassicScales::SCALES.key(is.rotate(i)))
49
+ return scale_name
50
+ end
51
+ end
52
+ nil
53
+ end
54
+ end
55
+
56
+ def full_name
57
+ "#{tone} #{name}"
58
+ end
59
+
60
+ alias to_s full_name
61
+
62
+ def pretty_name
63
+ "#{tone.pretty_name} #{name}"
64
+ end
65
+
66
+ alias full_name pretty_name
67
+
68
+ def degree(d)
69
+ raise WrongDegreeError, d if d < 1 || d > size
70
+ tone + interval_sequence[d - 1].semitones
71
+ end
72
+
73
+ alias [] degree
74
+
75
+ def degrees
76
+ (1..size)
77
+ end
78
+
79
+ def degree_of_chord(chord)
80
+ return if chords(chord.size).map(&:name).include?(chord.name)
81
+ degree_of_note(chord.root_note)
82
+ end
83
+
84
+ def degree_of_note(note)
85
+ notes.index(note)
86
+ end
87
+
88
+ def &(other)
89
+ raise HasNoNotesError unless other.respond_to?(:notes)
90
+ notes & other
91
+ end
92
+
93
+ def include_notes?(arg)
94
+ noteset = arg.is_a?(Note) ? NoteSet[arg] : arg
95
+ (self & noteset).size == noteset.size
96
+ end
97
+
98
+ alias include? include_notes?
99
+
100
+ def notes
101
+ @notes ||= NoteSet[*degrees.map { |d| degree(d) }]
102
+ end
103
+
104
+ def interval(i)
105
+ interval_sequence[(i - 1) % size]
106
+ end
107
+
108
+ def size
109
+ interval_sequence.size
110
+ end
111
+
112
+ def tertians(n = 3)
113
+ degrees.size.times.reduce([]) do |memo, d|
114
+ ns = NoteSet[ *Array.new(n) { |i| notes[(d + (i * 2)) % size] } ]
115
+ begin
116
+ chord = Chord.new(notes: ns)
117
+ rescue ChordNotFoundError
118
+ memo
119
+ else
120
+ memo + [chord]
121
+ end
122
+ end
123
+ end
124
+
125
+ def triads
126
+ tertians(3)
127
+ end
128
+
129
+ def sevenths
130
+ tertians(4)
131
+ end
132
+
133
+ def pentads
134
+ tertians(5)
135
+ end
136
+
137
+ def progression(*degrees)
138
+ Progression.new(self, degrees)
139
+ end
140
+
141
+ def chords(size = 3..12)
142
+ size = (size..size) if size.is_a?(Integer)
143
+ scale_rotations = interval_sequence.inversions
144
+ ChordQuality.intervals_per_name.reduce([]) do |memo1, (qname, qintervals)|
145
+ next memo1 unless size.include?(qintervals.size)
146
+ memo1 + scale_rotations.each_with_index
147
+ .reduce([]) do |memo2, (rot, index)|
148
+ if (rot & qintervals).size == qintervals.size
149
+ memo2 + [Chord.new(root_note: degree(index + 1),
150
+ quality: ChordQuality.new(name: qname))]
151
+ else
152
+ memo2
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ alias all_chords chords
159
+ end
160
+ end
161
+ end