jazz_model 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|