jazz_model 0.1.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.
- data/CHANGELOG.rdoc +14 -0
- data/Gemfile +4 -0
- data/README.rdoc +186 -0
- data/Rakefile +14 -0
- data/lib/jazz_model.rb +34 -0
- data/lib/jazz_model/base.rb +15 -0
- data/lib/jazz_model/chord.rb +141 -0
- data/lib/jazz_model/chord_collection.rb +68 -0
- data/lib/jazz_model/chord_quality.rb +19 -0
- data/lib/jazz_model/chord_scale.rb +30 -0
- data/lib/jazz_model/chord_symbol.rb +27 -0
- data/lib/jazz_model/chord_symbol_collection.rb +17 -0
- data/lib/jazz_model/chord_tone.rb +32 -0
- data/lib/jazz_model/definition.rb +66 -0
- data/lib/jazz_model/definitions/default.rb +502 -0
- data/lib/jazz_model/definitions/keys.rb +48 -0
- data/lib/jazz_model/key.rb +49 -0
- data/lib/jazz_model/key_context.rb +28 -0
- data/lib/jazz_model/mode.rb +47 -0
- data/lib/jazz_model/mode_context.rb +32 -0
- data/lib/jazz_model/mode_sequence.rb +15 -0
- data/lib/jazz_model/note_sequence.rb +15 -0
- data/lib/jazz_model/notes_collection.rb +89 -0
- data/lib/jazz_model/scale.rb +136 -0
- data/lib/jazz_model/scale_tone.rb +24 -0
- data/lib/jazz_model/tone.rb +51 -0
- data/lib/jazz_model/tone_sequence.rb +62 -0
- data/lib/jazz_model/version.rb +3 -0
- data/lib/jazz_model/voicing.rb +12 -0
- data/lib/jazz_model/voicing_tone.rb +11 -0
- data/spec/chord_collection_spec.rb +21 -0
- data/spec/chord_quality_spec.rb +27 -0
- data/spec/chord_scale_spec.rb +7 -0
- data/spec/chord_spec.rb +29 -0
- data/spec/chord_symbol_spec.rb +6 -0
- data/spec/chord_tone_spec.rb +6 -0
- data/spec/definition_spec.rb +35 -0
- data/spec/key_context_spec.rb +27 -0
- data/spec/key_spec.rb +15 -0
- data/spec/mode_spec.rb +9 -0
- data/spec/notes_collection_spec.rb +25 -0
- data/spec/scale_spec.rb +42 -0
- data/spec/scale_tone_spec.rb +6 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/tone_sequence_spec.rb +17 -0
- data/spec/tone_spec.rb +4 -0
- data/spec/voicing_spec.rb +6 -0
- data/spec/voicing_tone_spec.rb +6 -0
- metadata +194 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module JazzModel
|
2
|
+
# Represents one tone in a scale.
|
3
|
+
#
|
4
|
+
# == Attributes
|
5
|
+
#
|
6
|
+
# * +tone+ - A tone index from 0-11 representing the relative position of the
|
7
|
+
# tone within the chromatic scale.
|
8
|
+
#
|
9
|
+
# * +letter_index+ - A 0-6 value representing the relative letter position, which
|
10
|
+
# helps to disambiguate theoretic values such as Eb and D#. See +Key+.
|
11
|
+
#
|
12
|
+
# == Key Context
|
13
|
+
#
|
14
|
+
# +key+ is delgated to chord to access the scale's key context.
|
15
|
+
#
|
16
|
+
class ScaleTone < Tone
|
17
|
+
belongs_to :scale
|
18
|
+
|
19
|
+
acts_as_list :scope => :scale
|
20
|
+
|
21
|
+
delegate :key, :to => :scale
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module JazzModel
|
2
|
+
# Abstract base class for the concept of a tone.
|
3
|
+
# Eventually will be expanded with common functionality for
|
4
|
+
# all tone objects.
|
5
|
+
#
|
6
|
+
class Tone < JazzModel::Base
|
7
|
+
self.abstract_class = true
|
8
|
+
|
9
|
+
# Defines constants representing intervals
|
10
|
+
module Interval
|
11
|
+
ROOT = 0
|
12
|
+
MINOR_2ND = 1
|
13
|
+
MAJOR_2ND = 2
|
14
|
+
AUGMENTED_2ND = 3
|
15
|
+
MINOR_3RD = 3
|
16
|
+
MAJOR_3RD = 4
|
17
|
+
PERFECT_4TH = 5
|
18
|
+
AUGMENTED_4TH = 6
|
19
|
+
DIMINISHED_5TH = 6
|
20
|
+
TRITONE = 6
|
21
|
+
PERFECT_5TH = 7
|
22
|
+
AUGMENTED_5TH = 8
|
23
|
+
MINOR_6TH = 8
|
24
|
+
MAJOR_6TH = 9
|
25
|
+
DIMINISHED_7TH = 9
|
26
|
+
MINOR_7TH = 10
|
27
|
+
DOMINANT_7TH = 10
|
28
|
+
MAJOR_7TH = 11
|
29
|
+
OCTAVE = 12
|
30
|
+
end
|
31
|
+
|
32
|
+
# Defines constants representing letter intervals
|
33
|
+
module LetterInterval
|
34
|
+
FIRST = 0
|
35
|
+
SECOND = 1
|
36
|
+
THIRD = 2
|
37
|
+
FOURTH = 3
|
38
|
+
FIFTH = 4
|
39
|
+
SIXTH = 5
|
40
|
+
SEVENTH = 6
|
41
|
+
end
|
42
|
+
|
43
|
+
module Offsets
|
44
|
+
LOWERED = -1
|
45
|
+
RAISED = 1
|
46
|
+
DOUBLE_LOWERED = -2
|
47
|
+
DOUBLE_RAISED = 2
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module JazzModel
|
2
|
+
# This module is mixed in wherever a collection of tones is being returned
|
3
|
+
# and handles some of the fundamental logic involved in key and mode calculations
|
4
|
+
# and note values.
|
5
|
+
#
|
6
|
+
module ToneSequence
|
7
|
+
# Takes manually specified key context for this collection or
|
8
|
+
# delegates to th association owner.
|
9
|
+
def key
|
10
|
+
@key || (proxy_owner.key if proxy_owner.respond_to?(:key))
|
11
|
+
end
|
12
|
+
|
13
|
+
# Manually specifies the key context for this tone sequence only.
|
14
|
+
def in_key_of(in_key = nil)
|
15
|
+
@key = in_key.instance_of?(String) ? Key[in_key] : in_key
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
# Shifts indexes to simulate a key change
|
20
|
+
def in_key_context!
|
21
|
+
self.each do |tone|
|
22
|
+
tone.tone = (tone.tone + key.index) % 12
|
23
|
+
tone.letter_index = (tone.letter_index + key.letter_index) % 7
|
24
|
+
end
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
# Takes manually specified mode context for this collection or
|
29
|
+
# delegates to the association owner.
|
30
|
+
def mode
|
31
|
+
@mode || (proxy_owner.mode if proxy_owner.respond_to?(:mode))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Manually specifies the mode context for this tone sequence only.
|
35
|
+
def in_mode(mode)
|
36
|
+
@mode = mode
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Shifts mode positions to place tone sequence in mode context
|
41
|
+
def in_mode_context!
|
42
|
+
self.each {|tone| tone.position = (tone.position - mode) % self.count + 1}
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO: Have to use this until we figure out how to override find/default collection result
|
47
|
+
def all
|
48
|
+
in_key_context! if key
|
49
|
+
in_mode_context! if mode
|
50
|
+
self.sort_by(&:position)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Does the magic in determining the actual note from the tones
|
54
|
+
# with tone and letter indexes.
|
55
|
+
def notes
|
56
|
+
all.map do |tone|
|
57
|
+
Key.from_index(tone.tone, tone.letter_index).name
|
58
|
+
end.extend(NoteSequence)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JazzModel::ChordCollection do
|
4
|
+
subject { JazzModel::Scale['Major'].modes[1].chords }
|
5
|
+
|
6
|
+
it "should emit symbols list" do
|
7
|
+
subject.to_s(:symbols).should be_starts_with('maj')
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should emit names list" do
|
11
|
+
subject.to_s(:names).should be_starts_with('Major')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should resolve chord within" do
|
15
|
+
subject['maj'].should_not be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should resolve chord within with key context" do
|
19
|
+
subject['Cmaj'].should_not be_nil
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JazzModel::ChordQuality do
|
4
|
+
subject { JazzModel::ChordQuality.new }
|
5
|
+
|
6
|
+
it { should be_valid }
|
7
|
+
|
8
|
+
context "of Major" do
|
9
|
+
subject { JazzModel::ChordQuality.find_by_name('Major') }
|
10
|
+
it { should be_valid }
|
11
|
+
end
|
12
|
+
|
13
|
+
context "of Minor" do
|
14
|
+
subject { JazzModel::ChordQuality.find_by_name('Minor') }
|
15
|
+
it { should be_valid }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "of Dominant" do
|
19
|
+
subject { JazzModel::ChordQuality.find_by_name('Dominant') }
|
20
|
+
it { should be_valid }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "of Diminished" do
|
24
|
+
subject { JazzModel::ChordQuality.find_by_name('Diminished') }
|
25
|
+
it { should be_valid }
|
26
|
+
end
|
27
|
+
end
|
data/spec/chord_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JazzModel::Chord do
|
4
|
+
subject { JazzModel::Chord.new }
|
5
|
+
|
6
|
+
it { should be_valid }
|
7
|
+
|
8
|
+
pairings = {
|
9
|
+
'Cmaj' => %w[C E G],
|
10
|
+
'Cmin' => %w[C Eb G],
|
11
|
+
'Cmaj7' => %w[C E G B],
|
12
|
+
'Cmin7' => %w[C Eb G Bb],
|
13
|
+
'C7' => %w[C E G Bb],
|
14
|
+
'Cfulldim' => %w[C Eb Gb Bbb]
|
15
|
+
}
|
16
|
+
|
17
|
+
pairings.each do |chord_symbol, notes|
|
18
|
+
context "as #{chord_symbol}" do
|
19
|
+
it "should give valid notes" do
|
20
|
+
chord = JazzModel::Chord[chord_symbol]
|
21
|
+
chord.should_not be_nil
|
22
|
+
chord.notes.should == notes
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JazzModel::Definition do
|
4
|
+
context "after creating a definition" do
|
5
|
+
before(:all) do
|
6
|
+
JazzModel::Definition.define :my_definition => [:keys] do
|
7
|
+
JazzModel::Scale.create!(:name => "Sample")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have registered my_definition" do
|
12
|
+
JazzModel::Definition[:my_definition].should_not be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "the definition" do
|
16
|
+
before { @definition = JazzModel::Definition[:my_definition] }
|
17
|
+
|
18
|
+
it "should list keys as an included_definition" do
|
19
|
+
@definition.included_definitions.should include(:keys)
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when loaded" do
|
23
|
+
before { @definition.load }
|
24
|
+
|
25
|
+
it "should create keys" do
|
26
|
+
JazzModel::Key.find_by_name("C").should_not be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should create sample scale" do
|
30
|
+
JazzModel::Scale.find_by_name("Sample").should_not be_nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JazzModel::KeyContext do
|
4
|
+
describe "chord without context" do
|
5
|
+
subject { JazzModel::Chord['maj7'] }
|
6
|
+
|
7
|
+
it "should accept key context" do
|
8
|
+
subject.in_key_of('G').key.name.should == 'G'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should should accept then remove key context" do
|
12
|
+
subject.in_key_of('G').without_key.key.should be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "chord with context" do
|
17
|
+
subject { JazzModel::Chord['Fmaj7'] }
|
18
|
+
|
19
|
+
it "should have correct context" do
|
20
|
+
subject.key.name.should == 'F'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be put out of context" do
|
24
|
+
subject.without_key.key.should be_nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/key_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JazzModel::Key do
|
4
|
+
it "should default to C" do
|
5
|
+
JazzModel::Key.default.name.should == "C"
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should expose 12 primary keys" do
|
9
|
+
JazzModel::Key.should have(12).primaries
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should treat Eb and D# as enharmonic" do
|
13
|
+
JazzModel::Key['Eb'].should be_enharmonic_with(JazzModel::Key['D#'])
|
14
|
+
end
|
15
|
+
end
|
data/spec/mode_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JazzModel::NotesCollection do
|
4
|
+
it "should recognize invalid keys" do
|
5
|
+
JazzModel::NotesCollection['C,E,G,H'].should have(3).keys
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept key context" do
|
9
|
+
JazzModel::NotesCollection['C,E,G,A'].in_key_of('A').should have(1).chords
|
10
|
+
end
|
11
|
+
|
12
|
+
pairings = {
|
13
|
+
'A C E G' => %w[Amin7 C6]
|
14
|
+
}
|
15
|
+
|
16
|
+
pairings.each do |notes, chords|
|
17
|
+
context "as #{notes}" do
|
18
|
+
it "should give correct chords" do
|
19
|
+
notes_collection = JazzModel::NotesCollection[notes]
|
20
|
+
notes_collection.should_not be_nil
|
21
|
+
notes_collection.chords.symbols.to_set.should == chords.to_set
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/scale_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JazzModel::Scale do
|
4
|
+
subject { JazzModel::Scale['Major'] }
|
5
|
+
|
6
|
+
it { should be_valid }
|
7
|
+
|
8
|
+
it "should recognize symmetric scale" do
|
9
|
+
JazzModel::Scale['Diminished'].should be_symmetric
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should recognize asymmetric scale" do
|
13
|
+
JazzModel::Scale['Major'].should_not be_symmetric
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should accept numeric mode context" do
|
17
|
+
subject.in_mode(2).notes.first.should == 'D'
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should accept named mode context" do
|
21
|
+
subject.in_mode('Dorian').notes.first.should == 'D'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should remove mode context" do
|
25
|
+
subject.in_mode('Dorian').without_mode.notes.first.should == 'C'
|
26
|
+
end
|
27
|
+
|
28
|
+
pairings = {
|
29
|
+
'C Major' => %w[C D E F G A B],
|
30
|
+
'C Harmonic Minor' => %w[C D Eb F G Ab B]
|
31
|
+
}
|
32
|
+
|
33
|
+
pairings.each do |scale, notes|
|
34
|
+
context "as #{scale}" do
|
35
|
+
it "should give correct notes" do
|
36
|
+
scale = JazzModel::Scale[scale]
|
37
|
+
scale.should_not be_nil
|
38
|
+
scale.notes.should == notes
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|