coltrane 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cli/scale.rb CHANGED
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
2
4
  module Cli
5
+ # Interfaces commands with the scales functionality
3
6
  class Scale
4
7
  def self.parse(str)
5
8
  *scale_name, tone = str.split('-')
@@ -12,8 +15,10 @@ module Coltrane
12
15
  notes = NoteSet[*notes]
13
16
  elsif chords.any?
14
17
  puts "\nSearching for scales containing #{chords.join(', ')}:\n\n"
15
- notes = chords.reduce(NoteSet[]) {|memo, c| memo + Coltrane::Chord.new(name: c).notes }
16
- else raise BadFindScales.new
18
+ notes = chords.reduce(NoteSet[]) do |memo, c|
19
+ memo + Coltrane::Chord.new(name: c).notes
20
+ end
21
+ else raise BadFindScales
17
22
  end
18
23
  render_search(notes)
19
24
  puts "\nUnderlined means the scale has all notes"
@@ -24,25 +29,24 @@ module Coltrane
24
29
  output = []
25
30
  scale_width = search.results.keys.map(&:size).max
26
31
  search.results.each do |name, scales_by_tone|
27
- output << name.ljust(scale_width+1, ' ')
32
+ output << name.ljust(scale_width + 1, ' ')
28
33
  scales_by_tone.each do |tone_number, notes|
29
- p = notes.size.to_f / searched_notes.size
30
- l = p == 1 ? p : (p + 0.2) * 0.4
31
- hue, val, sat = 30, val = (l * 100).round, sat = p
32
- und = p == 1 ? :underline : nil
33
- color = "hsv(#{hue},#{sat},#{val})".paint.to_hex
34
+ p = (notes.size.to_f / searched_notes.size) * 100
35
+ l = p == 100 ? p : (p + 20) * 0.4
36
+ und = p == 100 ? :underline : nil
37
+ color = Color::HSL.new(30, p, l / 2).html
34
38
  output << Paint["#{Note[tone_number].name}(#{notes.size})", color, und]
35
- output << " "
39
+ output << ' '
36
40
  end
37
41
  output << "\n"
38
42
  end
39
43
  puts output.join
40
44
  end
41
45
 
42
- def initialize(scale, on: :text, flavor: 'degrees', notes: [], chords: [])
46
+ def initialize(scale, on: :text, flavor: 'degrees')
43
47
  desc = "This is the #{scale.tone.name} #{scale.name} scale:"
44
48
  Coltrane::Cli::Notes.new(scale.notes, on: on, desc: desc, flavor: flavor)
45
49
  end
46
50
  end
47
51
  end
48
- end
52
+ end
data/lib/cli/text.rb CHANGED
@@ -1,13 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
2
4
  module Cli
5
+ # A text representation
3
6
  class Text < Representation
4
7
  def render
5
8
  case @flavor
6
9
  when :marks, :notes, :degrees then @notes.pretty_names.join(' ')
7
- when :intervals then @notes.map {|n| (@notes.first - n).name}.join(' ')
8
- else raise WrongFlavorError.new
10
+ when :intervals then @notes.map { |n| (@notes.first - n).name }.join(' ')
11
+ else raise WrongFlavorError
9
12
  end
10
13
  end
11
14
  end
12
15
  end
13
- end
16
+ end
data/lib/cli/ukulele.rb CHANGED
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
2
4
  module Cli
5
+ # Renders notes in a common most popular ukulele scheme
3
6
  class Ukulele < Guitar
4
- SPECIAL_FRETS = [5, 7, 9, 12]
7
+ SPECIAL_FRETS = [5, 7, 9, 12].freeze
5
8
 
6
9
  def initialize(notes, flavor, tuning: %w[G C E A], frets: 12)
7
10
  super
8
11
  end
9
12
  end
10
13
  end
11
- end
14
+ end
data/lib/coltrane.rb CHANGED
@@ -1,9 +1,9 @@
1
- require 'bundler'
2
- Bundler.require
1
+ # frozen_string_literal: true
3
2
 
3
+ require 'forwardable'
4
4
  require 'facets/multiton'
5
+ require 'core_ext'
5
6
  require 'ostruct'
6
- require 'paint'
7
7
 
8
8
  require 'coltrane/version'
9
9
  require 'coltrane/errors'
@@ -23,11 +23,8 @@ require 'coltrane/roman_chord'
23
23
  require 'coltrane/classic_scales'
24
24
  require 'coltrane/classic_progressions'
25
25
 
26
- require 'coltrane/piano_representation'
27
-
28
26
  require 'coltrane/note'
29
-
30
27
  require 'coltrane/pitch'
31
28
  require 'coltrane/progression'
32
29
  require 'coltrane/scale'
33
- require 'coltrane/mode'
30
+ require 'coltrane/mode'
@@ -1,34 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
4
+ # A simple caching based on serializing objects into files
5
+ # maybe this should changed to save in a single json file
2
6
  class Cache
3
7
  class << self
4
8
  def find_or_record(key, &block)
5
- if (cached = fetch(key))
6
- return cached
7
- else
9
+ unless (cached = fetch(key))
8
10
  cached = yield block
9
11
  record(key, cached)
10
- cached
11
12
  end
13
+ cached
12
14
  end
13
15
 
14
16
  private
15
17
 
16
18
  def dir
17
- dir = File.expand_path('../../../', __FILE__) + "/cache/"
19
+ dir = File.expand_path('../../../', __FILE__) + '/cache/'
18
20
  Dir.mkdir(dir) unless Dir.exist?(dir)
19
21
  dir
20
22
  end
21
23
 
22
24
  def fetch(key)
23
- return unless File.file?(dir+key)
24
- Marshal.load File.read(dir+key)
25
+ Marshal.load File.read(dir + key) if File.file?(dir + key)
25
26
  end
26
27
 
27
28
  def record(key, contents)
28
- File.open(dir+key, "w") do |f|
29
+ File.open(dir + key, 'w') do |f|
29
30
  f.write Marshal.dump(contents)
30
31
  end
31
32
  end
32
33
  end
33
34
  end
34
- end
35
+ end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
2
4
  class Cadence
3
5
  end
4
- end
6
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
2
4
  # It describe a chord
3
5
  class Chord < NoteSet
@@ -16,19 +18,25 @@ module Coltrane
16
18
  elsif !name.nil?
17
19
  @root_note, @quality, @notes = parse_from_name(name)
18
20
  else
19
- raise WrongKeywords.new('[notes:] || [root_note:, quality:] || [name:]')
21
+ raise WrongKeywordsError,
22
+ '[notes:] || [root_note:, quality:] || [name:]'
20
23
  end
21
24
  end
22
25
 
23
26
  def name
24
- return @notes.names.join('/') if !named?
27
+ return @notes.names.join('/') unless named?
25
28
  "#{root_note.name}#{quality.name}"
26
29
  end
27
30
 
31
+ def pretty_name
32
+ return @notes.names.join('/') unless named?
33
+ "#{root_note.pretty_name}#{quality.name}"
34
+ end
35
+
28
36
  def named?
29
37
  notes.size >= 3 &&
30
- !root_note.nil? &&
31
- !quality&.name.nil?
38
+ !root_note.nil? &&
39
+ !quality&.name.nil?
32
40
  end
33
41
 
34
42
  def intervals
@@ -40,14 +48,14 @@ module Coltrane
40
48
  end
41
49
 
42
50
  def scales
43
- Scale.having_chord(self.name)
51
+ Scale.having_chord(name)
44
52
  end
45
53
 
46
54
  def next_inversion
47
55
  Chord.new(notes.rotate(1))
48
56
  end
49
57
 
50
- def invert(n=1)
58
+ def invert(n = 1)
51
59
  Chord.new(notes.rotate(n))
52
60
  end
53
61
 
@@ -58,11 +66,11 @@ module Coltrane
58
66
  protected
59
67
 
60
68
  def parse_from_name(name)
61
- _, name, quality_name = name.match(/([A-Z]#?)(.*)/).to_a
69
+ _, name, quality_name = name.match(/([A-Z](?:#|b)?)(.*)/).to_a
62
70
  root = Note[name]
63
71
  quality = ChordQuality.new(name: quality_name)
64
72
  notes = quality.notes_for(root)
65
73
  [root, quality, notes]
66
74
  end
67
75
  end
68
- end
76
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
2
4
  # It describe the quality of a chord, like maj7 or dim.
3
5
  class ChordQuality < IntervalSequence
@@ -6,18 +8,15 @@ module Coltrane
6
8
 
7
9
  def initialize(name: nil, notes: nil)
8
10
  if !name.nil?
9
- if(intervals = CHORD_QUALITIES[name])
10
- @name = name
11
- super(intervals: intervals)
12
- else
13
- raise ChordNotFoundError.new
14
- end
11
+ raise ChordNotFoundError unless (intervals = CHORD_QUALITIES[name])
12
+ @name = name
13
+ super(intervals: intervals)
15
14
  elsif !notes.nil?
16
15
  super(notes: notes)
17
16
  @name = CHORD_QUALITIES.key(intervals_semitones)
18
17
  else
19
- raise WrongKeywords.new('[name:] || [notes:]')
18
+ raise WrongKeywordsError, '[name:] || [notes:]'
20
19
  end
21
20
  end
22
21
  end
23
- end
22
+ end
@@ -1,13 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
4
+ # It's totally a wip yet.
2
5
  module ClassicProgressions
3
6
  PROGRESSIONS = {
4
- pop: [:major, [1,5,6,4]],
5
- fifties: [:major, [1,6,4,5]],
6
- blues: [:major, [1,4,1,5,4,1]],
7
- jazz: [:major, [2,5,1]],
8
- jazz_minor: [:minor, [2,5,1]],
9
- andalusian: [:minor, [1,7,6,5]]
10
- }
7
+ pop: [:major, [1, 5, 6, 4]],
8
+ fifties: [:major, [1, 6, 4, 5]],
9
+ blues: [:major, [1, 4, 1, 5, 4, 1]],
10
+ jazz: [:major, [2, 5, 1]],
11
+ jazz_minor: [:minor, [2, 5, 1]],
12
+ andalusian: [:minor, [1, 7, 6, 5]]
13
+ }.freeze
11
14
 
12
15
  def pop(tone)
13
16
  scale, degrees = PROGRESSIONS[:pop]
@@ -1,36 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coltrane
4
+ # This module deals with well known scales on music
2
5
  module ClassicScales
3
-
4
6
  SCALES = {
5
- 'Major' => [2,2,1,2,2,2,1],
6
- 'Pentatonic Major' => [2,2,3,2,3],
7
- 'Blues Major' => [2,1,1,3,2,3],
8
- 'Natural Minor' => [2,1,2,2,1,2,2],
9
- 'Harmonic Minor' => [2,1,2,2,1,3,1],
10
- 'Hungarian Minor' => [2,1,2,1,1,3,1],
11
- 'Pentatonic Minor' => [3,2,2,3,2],
12
- 'Blues Minor' => [3,2,1,1,3,2],
13
- 'Whole Tone' => [2,2,2,2,2,2],
14
- 'Flamenco' => [1,3,1,2,1,2,2]
15
- }
7
+ 'Major' => [2, 2, 1, 2, 2, 2, 1],
8
+ 'Pentatonic Major' => [2, 2, 3, 2, 3],
9
+ 'Blues Major' => [2, 1, 1, 3, 2, 3],
10
+ 'Natural Minor' => [2, 1, 2, 2, 1, 2, 2],
11
+ 'Harmonic Minor' => [2, 1, 2, 2, 1, 3, 1],
12
+ 'Hungarian Minor' => [2, 1, 2, 1, 1, 3, 1],
13
+ 'Pentatonic Minor' => [3, 2, 2, 3, 2],
14
+ 'Blues Minor' => [3, 2, 1, 1, 3, 2],
15
+ 'Whole Tone' => [2, 2, 2, 2, 2, 2],
16
+ 'Flamenco' => [1, 3, 1, 2, 1, 2, 2]
17
+ }.freeze
16
18
 
17
19
  MODES = {
18
20
  'Major' => %w[Ionian Dorian Phrygian Lydian Mixolydian Aeolian Locrian]
19
- }
20
-
21
- private
22
-
23
- # A little helper to build method names
24
- # just make the code more clear
25
- def self.methodize(string)
26
- string.downcase.gsub(' ', '_')
27
- end
28
-
29
- public
21
+ }.freeze
30
22
 
31
23
  # Creates factories for scales
32
24
  SCALES.each do |name, distances|
33
- define_method methodize(name) do |tone='C', mode=1|
25
+ define_method name.underscore do |tone = 'C', mode = 1|
34
26
  new(*distances, tone: tone, mode: mode, name: name)
35
27
  end
36
28
  end
@@ -38,34 +30,33 @@ module Coltrane
38
30
  # Creates factories for Greek Modes and possibly others
39
31
  MODES.each do |scale, modes|
40
32
  modes.each_with_index do |mode, index|
41
- scale_method = methodize(scale)
42
33
  mode_name = mode
43
34
  mode_n = index + 1
44
- define_method methodize(mode) do |tone='C'|
35
+ define_method mode.underscore do |tone = 'C'|
45
36
  new(*SCALES[scale], tone: tone, mode: mode_n, name: mode_name)
46
37
  end
47
38
  end
48
39
  end
49
40
 
50
- alias_method :minor, :natural_minor
51
- alias_method :pentatonic, :pentatonic_major
52
- alias_method :blues, :blues_major
41
+ alias minor natural_minor
42
+ alias pentatonic pentatonic_major
43
+ alias blues blues_major
53
44
 
54
45
  def known_scales
55
46
  SCALES.keys
56
47
  end
57
48
 
58
- def fetch(name, tone=nil)
49
+ def fetch(name, tone = nil)
59
50
  Coltrane::Scale.public_send(name, tone)
60
51
  end
61
52
 
62
53
  def from_key(key)
63
- scale = key.delete!('m') ? :minor : :major
64
- note = key
65
- Scale.public_send(scale.nil? || scale == 'M' ? :major : :minor, note)
54
+ key_regex = /([A-G][#b]?)([mM]?)/
55
+ _, note, s = *key.match(key_regex)
56
+ scale = s == 'm' ? :minor : :major
57
+ Scale.public_send(scale, note)
66
58
  end
67
59
 
68
-
69
60
  # Will output a OpenStruct like the following:
70
61
  # {
71
62
  # scales: [array of scales]
@@ -77,19 +68,15 @@ module Coltrane
77
68
  # }
78
69
 
79
70
  def having_notes(notes)
80
-
81
71
  format = { scales: [], results: {} }
82
72
  OpenStruct.new(
83
73
  SCALES.each_with_object(format) do |(name, intervals), output|
84
74
  Note.all.each.map do |tone|
85
75
  scale = new(*intervals, tone: tone, name: scale)
86
76
  output[:results][name] ||= {}
87
- if output[:results][name].has_key?(tone.number)
88
- next
89
- else
90
- output[:scales] << scale if scale.include?(notes)
91
- output[:results][name][tone.number] = scale.notes & notes
92
- end
77
+ next if output[:results][name].key?(tone.number)
78
+ output[:scales] << scale if scale.include?(notes)
79
+ output[:results][name][tone.number] = scale.notes & notes
93
80
  end
94
81
  end
95
82
  )
@@ -103,6 +90,6 @@ module Coltrane
103
90
  having_notes(notes)
104
91
  end
105
92
 
106
- alias_method :having_chord, :having_chords
93
+ alias having_chord having_chords
107
94
  end
108
- end
95
+ end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/Documentation
4
+
1
5
  module Coltrane
2
6
  class ColtraneError < StandardError
3
7
  def initialize(msg)
@@ -5,40 +9,41 @@ module Coltrane
5
9
  end
6
10
  end
7
11
 
8
- class BadConstructor < ColtraneError
9
- def initialize(msg=nil)
12
+ class BadConstructorError < ColtraneError
13
+ def initialize(msg = nil)
10
14
  super "Bad constructor. #{msg}"
11
15
  end
12
16
  end
13
17
 
14
- class WrongKeywords < BadConstructor
18
+ class WrongKeywordsError < BadConstructorError
15
19
  def initialize(msg)
16
20
  super "Use one of the following set of keywords: #{msg}"
17
21
  end
18
22
  end
19
23
 
20
- class InvalidNote < BadConstructor
24
+ class InvalidNoteError < BadConstructorError
21
25
  def initialize(note)
22
26
  super "#{note} is not a valid note"
23
27
  end
24
28
  end
25
29
 
26
- class InvalidNotes < BadConstructor
30
+ class InvalidNotesError < BadConstructorError
27
31
  def initialize(notes)
28
32
  super "#{notes} are not a valid set of notes"
29
33
  end
30
34
  end
31
35
 
32
- class HasNoNotes < BadConstructor
36
+ class HasNoNotesError < BadConstructorError
33
37
  def initialize(obj)
34
38
  super "The given object (#{obj.inspect} does not respond to :notes, "\
35
39
  "thereby it can't be used for this operation)"
36
40
  end
37
41
  end
38
42
 
39
- class WrongDegree
43
+ class WrongDegreeError
40
44
  def initialize(degree)
41
- super "#{degree} is not a valid degree. Degrees for this scale must be between 1 and #{degrees}"
45
+ super "#{degree} is not a valid degree. Degrees for this scale must be"\
46
+ "between 1 and #{degrees}"
42
47
  end
43
48
  end
44
49
 
@@ -47,8 +52,10 @@ module Coltrane
47
52
  super "The chord you provided wasn't found. "\
48
53
  "If you're sure this chord exists, "\
49
54
  "would you mind to suggest it's inclusion here: "\
50
- "https://github.com/pedrozath/coltrane/issues "\
55
+ 'https://github.com/pedrozath/coltrane/issues '\
51
56
  "\n\nA tip tho: always include the letter M for major"
52
57
  end
53
58
  end
54
- end
59
+ end
60
+
61
+ # rubocop:enable Style/Documentation