jazz_model 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/CHANGELOG.rdoc +14 -0
  2. data/Gemfile +4 -0
  3. data/README.rdoc +186 -0
  4. data/Rakefile +14 -0
  5. data/lib/jazz_model.rb +34 -0
  6. data/lib/jazz_model/base.rb +15 -0
  7. data/lib/jazz_model/chord.rb +141 -0
  8. data/lib/jazz_model/chord_collection.rb +68 -0
  9. data/lib/jazz_model/chord_quality.rb +19 -0
  10. data/lib/jazz_model/chord_scale.rb +30 -0
  11. data/lib/jazz_model/chord_symbol.rb +27 -0
  12. data/lib/jazz_model/chord_symbol_collection.rb +17 -0
  13. data/lib/jazz_model/chord_tone.rb +32 -0
  14. data/lib/jazz_model/definition.rb +66 -0
  15. data/lib/jazz_model/definitions/default.rb +502 -0
  16. data/lib/jazz_model/definitions/keys.rb +48 -0
  17. data/lib/jazz_model/key.rb +49 -0
  18. data/lib/jazz_model/key_context.rb +28 -0
  19. data/lib/jazz_model/mode.rb +47 -0
  20. data/lib/jazz_model/mode_context.rb +32 -0
  21. data/lib/jazz_model/mode_sequence.rb +15 -0
  22. data/lib/jazz_model/note_sequence.rb +15 -0
  23. data/lib/jazz_model/notes_collection.rb +89 -0
  24. data/lib/jazz_model/scale.rb +136 -0
  25. data/lib/jazz_model/scale_tone.rb +24 -0
  26. data/lib/jazz_model/tone.rb +51 -0
  27. data/lib/jazz_model/tone_sequence.rb +62 -0
  28. data/lib/jazz_model/version.rb +3 -0
  29. data/lib/jazz_model/voicing.rb +12 -0
  30. data/lib/jazz_model/voicing_tone.rb +11 -0
  31. data/spec/chord_collection_spec.rb +21 -0
  32. data/spec/chord_quality_spec.rb +27 -0
  33. data/spec/chord_scale_spec.rb +7 -0
  34. data/spec/chord_spec.rb +29 -0
  35. data/spec/chord_symbol_spec.rb +6 -0
  36. data/spec/chord_tone_spec.rb +6 -0
  37. data/spec/definition_spec.rb +35 -0
  38. data/spec/key_context_spec.rb +27 -0
  39. data/spec/key_spec.rb +15 -0
  40. data/spec/mode_spec.rb +9 -0
  41. data/spec/notes_collection_spec.rb +25 -0
  42. data/spec/scale_spec.rb +42 -0
  43. data/spec/scale_tone_spec.rb +6 -0
  44. data/spec/spec_helper.rb +12 -0
  45. data/spec/tone_sequence_spec.rb +17 -0
  46. data/spec/tone_spec.rb +4 -0
  47. data/spec/voicing_spec.rb +6 -0
  48. data/spec/voicing_tone_spec.rb +6 -0
  49. 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,3 @@
1
+ module JazzModel
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ module JazzModel
2
+ # Represents a specific piano voicing.
3
+ # Work in progress!
4
+ #
5
+ class Voicing < JazzModel::Base
6
+ belongs_to :chord
7
+ has_many :voicing_tones
8
+
9
+ delegate :key, :to => :chord
10
+
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module JazzModel
2
+ # Represents a tone in a piano voicing.
3
+ # Work in progress!
4
+ #
5
+ class VoicingTone < Tone
6
+ belongs_to :voicing
7
+
8
+ acts_as_list :scope => :voicing
9
+
10
+ end
11
+ 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
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ describe JazzModel::ChordScale do
4
+ subject { JazzModel::ChordScale.new }
5
+
6
+ it { should be_valid }
7
+ end
@@ -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,6 @@
1
+ require "spec_helper"
2
+
3
+ describe JazzModel::ChordSymbol do
4
+ subject { JazzModel::ChordSymbol.new }
5
+ it { should be_valid }
6
+ end
@@ -0,0 +1,6 @@
1
+ require "spec_helper"
2
+
3
+ describe JazzModel::ChordTone do
4
+ subject { JazzModel::ChordTone.new }
5
+ it { should be_valid }
6
+ end
@@ -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,9 @@
1
+ require "spec_helper"
2
+
3
+ describe JazzModel::Mode do
4
+ subject { JazzModel::Scale['CMajor'].modes['Dorian'] }
5
+
6
+ it "should yield correct notes" do
7
+ subject.notes.should == ['D', 'E', 'F', 'G', 'A', 'B', 'C']
8
+ end
9
+ end
@@ -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
@@ -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