jazz_model 0.1.0 → 0.2.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +187 -0
- data/db/schema.rb +83 -0
- data/lib/jazz_model/base.rb +13 -6
- data/lib/jazz_model/chord.rb +50 -70
- data/lib/jazz_model/chord_collection.rb +21 -45
- data/lib/jazz_model/chord_quality.rb +5 -6
- data/lib/jazz_model/chord_scale.rb +13 -13
- data/lib/jazz_model/chord_symbol.rb +14 -12
- data/lib/jazz_model/chord_symbol_collection.rb +3 -10
- data/lib/jazz_model/chord_tone.rb +20 -21
- data/lib/jazz_model/definition.rb +18 -18
- data/lib/jazz_model/definitions/default.rb +316 -321
- data/lib/jazz_model/definitions/keys.rb +36 -36
- data/lib/jazz_model/definitions.rb +3 -0
- data/lib/jazz_model/key.rb +22 -25
- data/lib/jazz_model/key_context.rb +31 -22
- data/lib/jazz_model/mode.rb +30 -35
- data/lib/jazz_model/mode_context.rb +22 -26
- data/lib/jazz_model/mode_sequence.rb +13 -12
- data/lib/jazz_model/notes_collection.rb +64 -71
- data/lib/jazz_model/scale.rb +63 -85
- data/lib/jazz_model/scale_tone.rb +14 -15
- data/lib/jazz_model/tone.rb +47 -48
- data/lib/jazz_model/tone_sequence.rb +56 -58
- data/lib/jazz_model/version.rb +2 -2
- data/lib/jazz_model/voicing.rb +6 -7
- data/lib/jazz_model/voicing_tone.rb +5 -6
- data/lib/jazz_model.rb +7 -10
- metadata +104 -141
- data/CHANGELOG.rdoc +0 -14
- data/Gemfile +0 -4
- data/README.rdoc +0 -186
- data/Rakefile +0 -14
- data/lib/jazz_model/note_sequence.rb +0 -15
- data/spec/chord_collection_spec.rb +0 -21
- data/spec/chord_quality_spec.rb +0 -27
- data/spec/chord_scale_spec.rb +0 -7
- data/spec/chord_spec.rb +0 -29
- data/spec/chord_symbol_spec.rb +0 -6
- data/spec/chord_tone_spec.rb +0 -6
- data/spec/definition_spec.rb +0 -35
- data/spec/key_context_spec.rb +0 -27
- data/spec/key_spec.rb +0 -15
- data/spec/mode_spec.rb +0 -9
- data/spec/notes_collection_spec.rb +0 -25
- data/spec/scale_spec.rb +0 -42
- data/spec/scale_tone_spec.rb +0 -6
- data/spec/spec_helper.rb +0 -12
- data/spec/tone_sequence_spec.rb +0 -17
- data/spec/tone_spec.rb +0 -4
- data/spec/voicing_spec.rb +0 -6
- data/spec/voicing_tone_spec.rb +0 -6
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
JazzModel::Definition.define :keys do
|
|
2
|
-
JazzModel::Key.create!(:
|
|
3
|
-
JazzModel::Key.create!(:
|
|
4
|
-
JazzModel::Key.create!(:
|
|
2
|
+
JazzModel::Key.create!(name: "C", long_name: "C", index: 0, letter_index: 0, cycle_index: 0)
|
|
3
|
+
JazzModel::Key.create!(name: "B#", long_name: "B Sharp", index: 0, letter_index: 6, cycle_index: 0, primary: false)
|
|
4
|
+
JazzModel::Key.create!(name: "Dbb", long_name: "D Double-Flat", index: 0, letter_index: 1, cycle_index: 0, primary: false)
|
|
5
5
|
|
|
6
|
-
JazzModel::Key.create!(:
|
|
7
|
-
JazzModel::Key.create!(:
|
|
8
|
-
JazzModel::Key.create!(:
|
|
6
|
+
JazzModel::Key.create!(name: "F", long_name: "F", index: 5, letter_index: 3, cycle_index: 1)
|
|
7
|
+
JazzModel::Key.create!(name: "E#", long_name: "E Sharp", index: 5, letter_index: 2, cycle_index: 1, primary: false)
|
|
8
|
+
JazzModel::Key.create!(name: "Gbb", long_name: "G Double-Flat", index: 5, letter_index: 4, cycle_index: 1, primary: false)
|
|
9
9
|
|
|
10
|
-
JazzModel::Key.create!(:
|
|
11
|
-
JazzModel::Key.create!(:
|
|
12
|
-
JazzModel::Key.create!(:
|
|
10
|
+
JazzModel::Key.create!(name: "Bb", long_name: "B Flat", index: 10, letter_index: 6, cycle_index: 2)
|
|
11
|
+
JazzModel::Key.create!(name: "A#", long_name: "A Sharp", index: 10, letter_index: 5, cycle_index: 2, primary: false)
|
|
12
|
+
JazzModel::Key.create!(name: "Cbb", long_name: "C Double-Flat", index: 10, letter_index: 0, cycle_index: 2, primary: false)
|
|
13
13
|
|
|
14
|
-
JazzModel::Key.create!(:
|
|
15
|
-
JazzModel::Key.create!(:
|
|
16
|
-
JazzModel::Key.create!(:
|
|
14
|
+
JazzModel::Key.create!(name: "Eb", long_name: "E Flat", index: 3, letter_index: 2, cycle_index: 3)
|
|
15
|
+
JazzModel::Key.create!(name: "D#", long_name: "D Sharp", index: 3, letter_index: 1, cycle_index: 3, primary: false)
|
|
16
|
+
JazzModel::Key.create!(name: "Fbb", long_name: "F Double Flat", index: 3, letter_index: 3, cycle_index: 3, primary: false)
|
|
17
17
|
|
|
18
|
-
JazzModel::Key.create!(:
|
|
19
|
-
JazzModel::Key.create!(:
|
|
18
|
+
JazzModel::Key.create!(name: "Ab", long_name: "A Flat", index: 8, letter_index: 5, cycle_index: 4)
|
|
19
|
+
JazzModel::Key.create!(name: "G#", long_name: "G Sharp", index: 8, letter_index: 4, cycle_index: 4, primary: false)
|
|
20
20
|
|
|
21
|
-
JazzModel::Key.create!(:
|
|
22
|
-
JazzModel::Key.create!(:
|
|
23
|
-
JazzModel::Key.create!(:
|
|
21
|
+
JazzModel::Key.create!(name: "Db", long_name: "D Flat", index: 1, letter_index: 1, cycle_index: 5)
|
|
22
|
+
JazzModel::Key.create!(name: "C#", long_name: "C Sharp", index: 1, letter_index: 0, cycle_index: 5, primary: false)
|
|
23
|
+
JazzModel::Key.create!(name: "B##", long_name: "B Double-Sharp", index: 1, letter_index: 6, cycle_index: 5, primary: false)
|
|
24
24
|
|
|
25
|
-
JazzModel::Key.create!(:
|
|
26
|
-
JazzModel::Key.create!(:
|
|
27
|
-
JazzModel::Key.create!(:
|
|
25
|
+
JazzModel::Key.create!(name: "Gb", long_name: "G Flat", index: 6, letter_index: 4, cycle_index: 6)
|
|
26
|
+
JazzModel::Key.create!(name: "F#", long_name: "F Sharp", index: 6, letter_index: 3, cycle_index: 6, primary: false)
|
|
27
|
+
JazzModel::Key.create!(name: "E##", long_name: "E Double-Sharp", index: 6, letter_index: 2, cycle_index: 6, primary: false)
|
|
28
28
|
|
|
29
|
-
JazzModel::Key.create!(:
|
|
30
|
-
JazzModel::Key.create!(:
|
|
31
|
-
JazzModel::Key.create!(:
|
|
29
|
+
JazzModel::Key.create!(name: "B", long_name: "B", index: 11, letter_index: 6, cycle_index: 7)
|
|
30
|
+
JazzModel::Key.create!(name: "Cb", long_name: "C Flat", index: 11, letter_index: 0, cycle_index: 7, primary: false)
|
|
31
|
+
JazzModel::Key.create!(name: "A##", long_name: "A Double-Sharp", index: 11, letter_index: 5, cycle_index: 7, primary: false)
|
|
32
32
|
|
|
33
|
-
JazzModel::Key.create!(:
|
|
34
|
-
JazzModel::Key.create!(:
|
|
35
|
-
JazzModel::Key.create!(:
|
|
33
|
+
JazzModel::Key.create!(name: "E", long_name: "E", index: 4, letter_index: 2, cycle_index: 8)
|
|
34
|
+
JazzModel::Key.create!(name: "Fb", long_name: "F Flat", index: 4, letter_index: 3, cycle_index: 8, primary: false)
|
|
35
|
+
JazzModel::Key.create!(name: "D##", long_name: "D Double-Sharp", index: 4, letter_index: 1, cycle_index: 8, primary: false)
|
|
36
36
|
|
|
37
|
-
JazzModel::Key.create!(:
|
|
38
|
-
JazzModel::Key.create!(:
|
|
39
|
-
JazzModel::Key.create!(:
|
|
37
|
+
JazzModel::Key.create!(name: "A", long_name: "A", index: 9, letter_index: 5, cycle_index: 9)
|
|
38
|
+
JazzModel::Key.create!(name: "Bbb", long_name: "B Double-Flat", index: 9, letter_index: 6, cycle_index: 9, primary: false)
|
|
39
|
+
JazzModel::Key.create!(name: "G##", long_name: "G Double-Sharp", index: 9, letter_index: 4, cycle_index: 9, primary: false)
|
|
40
40
|
|
|
41
|
-
JazzModel::Key.create!(:
|
|
42
|
-
JazzModel::Key.create!(:
|
|
43
|
-
JazzModel::Key.create!(:
|
|
41
|
+
JazzModel::Key.create!(name: "D", long_name: "D", index: 2, letter_index: 1, cycle_index: 10)
|
|
42
|
+
JazzModel::Key.create!(name: "C##", long_name: "C Double-Sharp", index: 2, letter_index: 0, cycle_index: 10, primary: false)
|
|
43
|
+
JazzModel::Key.create!(name: "Ebb", long_name: "E Double-Flat", index: 2, letter_index: 2, cycle_index: 10, primary: false)
|
|
44
44
|
|
|
45
|
-
JazzModel::Key.create!(:
|
|
46
|
-
JazzModel::Key.create!(:
|
|
47
|
-
JazzModel::Key.create!(:
|
|
48
|
-
end
|
|
45
|
+
JazzModel::Key.create!(name: "G", long_name: "G", index: 7, letter_index: 4, cycle_index: 11)
|
|
46
|
+
JazzModel::Key.create!(name: "F##", long_name: "F Double-Sharp", index: 7, letter_index: 3, cycle_index: 11, primary: false)
|
|
47
|
+
JazzModel::Key.create!(name: "Abb", long_name: "A Double-Flat", index: 7, letter_index: 5, cycle_index: 11, primary: false)
|
|
48
|
+
end
|
data/lib/jazz_model/key.rb
CHANGED
|
@@ -1,49 +1,46 @@
|
|
|
1
1
|
module JazzModel
|
|
2
|
-
# A key represents a specific theoretic tone mapped to a pitch and a letter
|
|
3
|
-
# index. Keys are defined for all letters A-G and all sharp/flat values
|
|
2
|
+
# A key represents a specific theoretic tone mapped to a pitch and a letter
|
|
3
|
+
# index. Keys are defined for all letters A-G and all sharp/flat values
|
|
4
4
|
# within two semitons (e.g. Dbb, D Double-Flat enharmonic with C).
|
|
5
5
|
#
|
|
6
|
-
# Each key is associated an index between 0-11 to represent the actual pitch
|
|
7
|
-
# within the chromatic scale.
|
|
8
|
-
# Each key is also assigned a "letter index" which represents a relative letter
|
|
9
|
-
# value. This index is crucial as it disambiguates theoretical tone differences
|
|
10
|
-
# such as Eb and D#. Doing so also supports correct double-sharp and double-flat
|
|
11
|
-
# interpretations which are rare but theoretically different from their enharmonic
|
|
6
|
+
# Each key is associated an index between 0-11 to represent the actual pitch
|
|
7
|
+
# within the chromatic scale.
|
|
8
|
+
# Each key is also assigned a "letter index" which represents a relative letter
|
|
9
|
+
# value. This index is crucial as it disambiguates theoretical tone differences
|
|
10
|
+
# such as Eb and D#. Doing so also supports correct double-sharp and double-flat
|
|
11
|
+
# interpretations which are rare but theoretically different from their enharmonic
|
|
12
12
|
# counterparts.
|
|
13
|
-
#
|
|
13
|
+
#
|
|
14
14
|
class Key < JazzModel::Base
|
|
15
|
-
Letters = ['C' => 0, 'D' => 1, 'E' => 2, 'F' => 3, 'G' => 4, 'A' => 5, 'B' => 6]
|
|
16
|
-
|
|
17
15
|
# Finds a key given a tonal index 0-11 and a letter index (to disambiguate enharmonic keys)
|
|
18
16
|
def self.from_index(value, preferred_letter = nil)
|
|
19
|
-
|
|
17
|
+
all.find { |k| k.index == value && (preferred_letter.nil? || k.letter_index == preferred_letter) }
|
|
20
18
|
end
|
|
21
|
-
|
|
19
|
+
|
|
22
20
|
# Finds a key object given the name (such as Eb)
|
|
23
21
|
def self.from_name(value)
|
|
24
|
-
|
|
22
|
+
all.find { |k| k.name == value }
|
|
25
23
|
end
|
|
26
|
-
|
|
24
|
+
|
|
27
25
|
# Finds a key object based on the name:
|
|
28
26
|
# +Key['Eb']+
|
|
29
|
-
#
|
|
27
|
+
#
|
|
30
28
|
def self.[](value)
|
|
31
|
-
|
|
29
|
+
from_name(value) || from_index(value)
|
|
32
30
|
end
|
|
33
|
-
|
|
31
|
+
|
|
34
32
|
# Returns an array of the 12 primary keys (definitions around the cycle of fourths)
|
|
35
33
|
def self.primaries
|
|
36
|
-
|
|
34
|
+
where(primary: true).to_a
|
|
37
35
|
end
|
|
38
|
-
|
|
36
|
+
|
|
39
37
|
def self.default
|
|
40
|
-
self[
|
|
38
|
+
self["C"]
|
|
41
39
|
end
|
|
42
|
-
|
|
40
|
+
|
|
43
41
|
# Tests whether the current key is enharmonic with +another_key+.
|
|
44
42
|
def enharmonic_with?(another_key)
|
|
45
|
-
|
|
43
|
+
index == another_key.index
|
|
46
44
|
end
|
|
47
|
-
|
|
48
45
|
end
|
|
49
|
-
end
|
|
46
|
+
end
|
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
module JazzModel
|
|
2
|
-
# Key context is a module intended to be mixed in to classes requiring
|
|
3
|
-
# the notion of "key context". Concepts such as chords and scales exist
|
|
2
|
+
# Key context is a module intended to be mixed in to classes requiring
|
|
3
|
+
# the notion of "key context". Concepts such as chords and scales exist
|
|
4
4
|
# as mathematical relationships absent key, but can be put into key context.
|
|
5
|
-
# Adds a key
|
|
6
|
-
#
|
|
5
|
+
# Adds a key attribute to the including class and two new methods:
|
|
6
|
+
#
|
|
7
7
|
# * +in_key_of+(new_key) - Sets the key context to the specified new_key.
|
|
8
8
|
# * +without_key+ - Removes key context.
|
|
9
|
-
#
|
|
9
|
+
#
|
|
10
10
|
module KeyContext
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
attr_accessor :key
|
|
12
|
+
|
|
13
|
+
def in_key_of(name)
|
|
14
|
+
self.key = name.is_a?(Key) ? name : Key.find_by_name(name)
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
alias_method :in, :in_key_of
|
|
18
|
+
|
|
19
|
+
def without_key
|
|
20
|
+
self.key = nil
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Yields each way +symbol+ can be read as an optional key prefix followed
|
|
25
|
+
# by the rest of the symbol, longest key first and finally with no key at
|
|
26
|
+
# all. For example "Cb5" yields [Cb, "5"], [C, "b5"], then [nil, "Cb5"],
|
|
27
|
+
# letting callers pick the first reading that resolves to something.
|
|
28
|
+
def self.each_split(symbol)
|
|
29
|
+
return enum_for(:each_split, symbol) unless block_given?
|
|
30
|
+
|
|
31
|
+
Key.all.sort_by { |k| -k.name.length }.each do |k|
|
|
32
|
+
yield k, symbol.delete_prefix(k.name).strip if symbol.start_with?(k.name)
|
|
33
|
+
end
|
|
34
|
+
yield nil, symbol
|
|
35
|
+
end
|
|
27
36
|
end
|
|
28
|
-
end
|
|
37
|
+
end
|
data/lib/jazz_model/mode.rb
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
1
|
module JazzModel
|
|
2
|
-
# In music theory a mode is essentially a slice of a scale with an offset.
|
|
3
|
-
# For example second mode of the C major scale would begin and end on D
|
|
2
|
+
# In music theory a mode is essentially a slice of a scale with an offset.
|
|
3
|
+
# For example second mode of the C major scale would begin and end on D
|
|
4
4
|
# but otherwise have the exact same tones as the C major scale.
|
|
5
|
-
#
|
|
6
|
-
# This class represents a mode within the context of a +Scale+.
|
|
7
|
-
#
|
|
5
|
+
#
|
|
6
|
+
# This class represents a mode within the context of a +Scale+.
|
|
7
|
+
#
|
|
8
8
|
# == Associations
|
|
9
|
-
#
|
|
9
|
+
#
|
|
10
10
|
# * +scale+ - The related scale.
|
|
11
11
|
# * +chord_scales+ - +ChordScale+ relationships to this mode.
|
|
12
12
|
# * +chords+ - Direct access to a collection of chords through +chord_scales+.
|
|
13
|
-
#
|
|
13
|
+
#
|
|
14
14
|
# == Tones and Mode Context
|
|
15
|
-
#
|
|
16
|
-
# Tones are directly related to scales, not modes. There is a +tones+ method
|
|
17
|
-
# (and a +notes+ method that delegates to +tones+) here that simulates the tones for
|
|
18
|
-
# the mode by setting the mode context of the scale tones, effectively returning
|
|
15
|
+
#
|
|
16
|
+
# Tones are directly related to scales, not modes. There is a +tones+ method
|
|
17
|
+
# (and a +notes+ method that delegates to +tones+) here that simulates the tones for
|
|
18
|
+
# the mode by setting the mode context of the scale tones, effectively returning
|
|
19
19
|
# the tones shifted by X where X is the mode number.
|
|
20
|
-
# Therefore, you can access tones directly off of the mode because they are proxied
|
|
21
|
-
# through scale with the correct offset.
|
|
22
|
-
#
|
|
20
|
+
# Therefore, you can access tones directly off of the mode because they are proxied
|
|
21
|
+
# through scale with the correct offset.
|
|
22
|
+
#
|
|
23
23
|
# == Examples
|
|
24
|
-
#
|
|
25
|
-
# Since modes should be accessed within the context of scales, mode-related examples are
|
|
24
|
+
#
|
|
25
|
+
# Since modes should be accessed within the context of scales, mode-related examples are
|
|
26
26
|
# actually in scale. See +Scale+.
|
|
27
|
-
#
|
|
27
|
+
#
|
|
28
28
|
class Mode < JazzModel::Base
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
else
|
|
42
|
-
self.scale.tones.in_mode(self.mode)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
29
|
+
include KeyContext
|
|
30
|
+
|
|
31
|
+
belongs_to :scale
|
|
32
|
+
|
|
33
|
+
has_many :chord_scales
|
|
34
|
+
has_many :chords, through: :chord_scales, extend: ChordCollection
|
|
35
|
+
|
|
36
|
+
delegate :notes, to: :tones
|
|
37
|
+
|
|
38
|
+
def tones
|
|
39
|
+
scale.tones.in_mode(mode).in_key_of(key)
|
|
40
|
+
end
|
|
46
41
|
end
|
|
47
|
-
end
|
|
42
|
+
end
|
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
module JazzModel
|
|
2
|
-
# Mode context is a module intended to be mixed in to classes requiring
|
|
3
|
-
# the notion of "mode context". Right now the only class that uses this
|
|
4
|
-
# is scale. Mode context allows scales to take on a mode context which
|
|
2
|
+
# Mode context is a module intended to be mixed in to classes requiring
|
|
3
|
+
# the notion of "mode context". Right now the only class that uses this
|
|
4
|
+
# is scale. Mode context allows scales to take on a mode context which
|
|
5
5
|
# effectively offsets tone sequences to retrieve tones in the given scale mode.
|
|
6
|
-
#
|
|
7
|
-
# * +in_mode(new_mode)+ - Sets the mode context to the specified mode (integer).
|
|
6
|
+
#
|
|
7
|
+
# * +in_mode(new_mode)+ - Sets the mode context to the specified mode (integer or name).
|
|
8
8
|
# * +without_mode+ - Removes mode context.
|
|
9
9
|
#
|
|
10
10
|
module ModeContext
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
self
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
11
|
+
attr_accessor :mode
|
|
12
|
+
|
|
13
|
+
def in_mode(value)
|
|
14
|
+
if value.is_a?(String)
|
|
15
|
+
mode_object = respond_to?(:modes) ? modes.find_by_name(value) : Mode.find_by_name(value)
|
|
16
|
+
self.mode = mode_object.mode unless mode_object.nil?
|
|
17
|
+
else
|
|
18
|
+
self.mode = value
|
|
19
|
+
end
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def without_mode
|
|
24
|
+
self.mode = nil
|
|
25
|
+
self
|
|
26
|
+
end
|
|
31
27
|
end
|
|
32
|
-
end
|
|
28
|
+
end
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
module JazzModel
|
|
2
|
-
# Extends the Scale.modes association by allowing indexed access to modes.
|
|
3
|
-
#
|
|
4
|
-
#
|
|
2
|
+
# Extends the Scale.modes association by allowing indexed access to modes.
|
|
3
|
+
# When using it in this manner, key context is passed along.
|
|
4
|
+
#
|
|
5
5
|
module ModeSequence
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
def [](value)
|
|
7
|
+
mode_object = where("name = ? OR mode = ?", value, value).first
|
|
8
|
+
return nil if mode_object.nil?
|
|
9
|
+
|
|
10
|
+
# Pass Along Key Information
|
|
11
|
+
owner = proxy_association.owner
|
|
12
|
+
mode_object.key = owner.key if owner.respond_to?(:key) && !owner.key.nil?
|
|
13
|
+
mode_object
|
|
14
|
+
end
|
|
14
15
|
end
|
|
15
|
-
end
|
|
16
|
+
end
|
|
@@ -1,89 +1,82 @@
|
|
|
1
|
-
require 'set'
|
|
2
|
-
|
|
3
1
|
module JazzModel
|
|
4
2
|
# NotesCollection represents a collection of notes. Using this collection,
|
|
5
|
-
# you can determine chord and scale/mode matches in specific or all keys.
|
|
6
|
-
# This class is currently under heavily development and will eventually be home to heavy logic involved
|
|
7
|
-
# in associating an arbitrary collection of notes with chords, given that
|
|
3
|
+
# you can determine chord and scale/mode matches in specific or all keys.
|
|
4
|
+
# This class is currently under heavily development and will eventually be home to heavy logic involved
|
|
5
|
+
# in associating an arbitrary collection of notes with chords, given that
|
|
8
6
|
# some scales tone may be "omitable", etc.
|
|
9
|
-
#
|
|
7
|
+
#
|
|
10
8
|
# == Creating a NotesCollection object
|
|
11
|
-
#
|
|
12
|
-
# Creating the collection of notes should be done by indexing
|
|
9
|
+
#
|
|
10
|
+
# Creating the collection of notes should be done by indexing
|
|
13
11
|
# the Notes class directly with a comma-separated string of notes.
|
|
14
|
-
#
|
|
12
|
+
#
|
|
15
13
|
# == Current Limitations
|
|
16
|
-
#
|
|
14
|
+
#
|
|
17
15
|
# Will only do exact 100% match of notes given and notes in chords. No fuzzyness
|
|
18
|
-
# such as the ability to omit the root and other non-important tones. Does not yet
|
|
16
|
+
# such as the ability to omit the root and other non-important tones. Does not yet
|
|
19
17
|
# find supersets or subsets.
|
|
20
|
-
#
|
|
18
|
+
#
|
|
21
19
|
# == Examples
|
|
22
|
-
#
|
|
20
|
+
#
|
|
23
21
|
# === Finding All Associated Chords
|
|
24
|
-
#
|
|
22
|
+
#
|
|
25
23
|
# NotesCollection['C, E, G, A'].chords.symbols
|
|
26
24
|
# # => "Amin7, Cmaj6"
|
|
27
|
-
#
|
|
25
|
+
#
|
|
28
26
|
# === Finding Associated Chords in Given Key
|
|
29
27
|
#
|
|
30
28
|
# NotesCollection['C, E, G, A'].in_key_of('A').chords.symbols
|
|
31
29
|
# # => "Amin7"
|
|
32
|
-
#
|
|
30
|
+
#
|
|
33
31
|
class NotesCollection
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
Chord.all.select do |c|
|
|
84
|
-
c.tones.map(&:tone).to_set == keys.map {|k| (k.index - (in_key ? in_key.index : 0)) % 12 }.to_set
|
|
85
|
-
end.map {|c| c.key = in_key; c}
|
|
86
|
-
end
|
|
87
|
-
|
|
32
|
+
include Enumerable
|
|
33
|
+
include KeyContext
|
|
34
|
+
|
|
35
|
+
attr_accessor :keys
|
|
36
|
+
attr_accessor :invalid_keys
|
|
37
|
+
|
|
38
|
+
def initialize(value = [])
|
|
39
|
+
@keys = []
|
|
40
|
+
@invalid_keys = []
|
|
41
|
+
|
|
42
|
+
value = value.split(/,| /).map(&:strip) if value.instance_of?(String)
|
|
43
|
+
value.each do |key_name|
|
|
44
|
+
key_object = Key[key_name]
|
|
45
|
+
|
|
46
|
+
if key_object.nil?
|
|
47
|
+
@invalid_keys << key_name
|
|
48
|
+
else
|
|
49
|
+
@keys << key_object
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
alias_method :[], :new
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
delegate :each, to: :keys
|
|
59
|
+
|
|
60
|
+
# Chords associated with this collection of notes
|
|
61
|
+
def chords
|
|
62
|
+
if key
|
|
63
|
+
chords_in_key(key)
|
|
64
|
+
else
|
|
65
|
+
Key.primaries.map do |in_key|
|
|
66
|
+
chords_in_key(in_key)
|
|
67
|
+
end.flatten
|
|
68
|
+
end.extend(ChordCollection)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def chords_in_key(in_key)
|
|
74
|
+
Chord.all.select do |c|
|
|
75
|
+
c.tones.map(&:tone).to_set == keys.map { |k| (k.index - (in_key ? in_key.index : 0)) % 12 }.to_set
|
|
76
|
+
end.map { |c|
|
|
77
|
+
c.key = in_key
|
|
78
|
+
c
|
|
79
|
+
}
|
|
80
|
+
end
|
|
88
81
|
end
|
|
89
|
-
end
|
|
82
|
+
end
|