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.
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