coltrane 2.2.1 → 3.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +16 -1
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +25 -2
- data/README.md +3 -0
- data/bin/console +6 -2
- data/coltrane.gemspec +2 -1
- data/exe/coltrane +14 -224
- data/lib/coltrane.rb +3 -50
- data/lib/coltrane/commands.rb +14 -0
- data/lib/coltrane/commands/chords.rb +77 -0
- data/lib/coltrane/commands/command.rb +45 -0
- data/lib/coltrane/commands/common_chords.rb +33 -0
- data/lib/coltrane/commands/errors.rb +44 -0
- data/lib/coltrane/commands/find_progression.rb +28 -0
- data/lib/coltrane/commands/find_scale.rb +39 -0
- data/lib/coltrane/commands/notes.rb +44 -0
- data/lib/coltrane/commands/progression.rb +27 -0
- data/lib/{cli → coltrane/commands}/representation.rb +0 -0
- data/lib/coltrane/commands/scale.rb +46 -0
- data/lib/coltrane/renderers.rb +6 -0
- data/lib/coltrane/renderers/renderer.rb +42 -0
- data/lib/coltrane/renderers/text_renderer.rb +23 -0
- data/lib/coltrane/renderers/text_renderer/array_drawer.rb +45 -0
- data/lib/coltrane/renderers/text_renderer/base_drawer.rb +20 -0
- data/lib/coltrane/renderers/text_renderer/hash_drawer.rb +16 -0
- data/lib/coltrane/renderers/text_renderer/representation_guitar_chord_drawer.rb +95 -0
- data/lib/coltrane/renderers/text_renderer/representation_guitar_note_set_drawer.rb +76 -0
- data/lib/coltrane/renderers/text_renderer/representation_piano_note_set_drawer.rb +49 -0
- data/lib/coltrane/renderers/text_renderer/theory_chord_drawer.rb +14 -0
- data/lib/coltrane/renderers/text_renderer/theory_note_set_drawer.rb +17 -0
- data/lib/coltrane/renderers/text_renderer/theory_progression_drawer.rb +13 -0
- data/lib/coltrane/renderers/text_renderer/theory_progression_set_drawer.rb +22 -0
- data/lib/coltrane/renderers/text_renderer/theory_scale_drawer.rb +13 -0
- data/lib/coltrane/renderers/text_renderer/theory_scale_set_drawer.rb +27 -0
- data/lib/coltrane/representation.rb +12 -0
- data/lib/coltrane/representation/guitar.rb +34 -0
- data/lib/coltrane/representation/guitar/chord.rb +180 -0
- data/lib/coltrane/representation/guitar/note.rb +26 -0
- data/lib/coltrane/representation/guitar/note_set.rb +35 -0
- data/lib/coltrane/representation/guitar/string.rb +31 -0
- data/lib/coltrane/representation/guitar_like_instruments.rb +21 -0
- data/lib/coltrane/representation/piano.rb +36 -0
- data/lib/coltrane/representation/piano/note_set.rb +22 -0
- data/lib/{coltrane_synth.rb → coltrane/synth.rb.rb} +0 -0
- data/lib/{coltrane_synth → coltrane/synth}/base.rb +0 -0
- data/lib/{coltrane_synth → coltrane/synth}/synth.rb +0 -0
- data/lib/coltrane/theory.rb +54 -0
- data/lib/coltrane/theory/cadence.rb +9 -0
- data/lib/coltrane/{changes.rb → theory/changes.rb} +0 -0
- data/lib/coltrane/theory/chord.rb +101 -0
- data/lib/coltrane/theory/chord_quality.rb +113 -0
- data/lib/coltrane/theory/chord_substitutions.rb +11 -0
- data/lib/coltrane/theory/circle_of_fifths.rb +33 -0
- data/lib/coltrane/theory/classic_scales.rb +113 -0
- data/lib/coltrane/theory/diatonic_scale.rb +38 -0
- data/lib/coltrane/theory/errors.rb +97 -0
- data/lib/coltrane/theory/frequency.rb +52 -0
- data/lib/coltrane/theory/frequency_interval.rb +83 -0
- data/lib/coltrane/theory/interval.rb +209 -0
- data/lib/coltrane/theory/interval_class.rb +212 -0
- data/lib/coltrane/theory/interval_sequence.rb +157 -0
- data/lib/coltrane/theory/key.rb +18 -0
- data/lib/coltrane/{mode.rb → theory/mode.rb} +0 -0
- data/lib/coltrane/theory/notable_progressions.rb +30 -0
- data/lib/coltrane/theory/note.rb +104 -0
- data/lib/coltrane/theory/note_set.rb +101 -0
- data/lib/coltrane/theory/pitch.rb +94 -0
- data/lib/coltrane/theory/pitch_class.rb +154 -0
- data/lib/coltrane/theory/progression.rb +81 -0
- data/lib/coltrane/theory/progression_set.rb +18 -0
- data/lib/coltrane/{qualities.rb → theory/qualities.rb} +0 -0
- data/lib/coltrane/theory/roman_chord.rb +114 -0
- data/lib/coltrane/theory/scale.rb +161 -0
- data/lib/coltrane/theory/scale_search_result.rb +6 -0
- data/lib/coltrane/theory/scale_set.rb +40 -0
- data/lib/coltrane/theory/voicing.rb +41 -0
- data/lib/coltrane/version.rb +1 -1
- metadata +88 -63
- data/bin/rails +0 -17
- data/data/qualities.yml +0 -83
- data/db/cache.sqlite3 +0 -0
- data/db/cache_test.sqlite3 +0 -0
- data/db/config.yml +0 -11
- data/lib/cli.rb +0 -24
- data/lib/cli/bass_guitar.rb +0 -12
- data/lib/cli/chord.rb +0 -39
- data/lib/cli/config.rb +0 -39
- data/lib/cli/errors.rb +0 -46
- data/lib/cli/guitar.rb +0 -67
- data/lib/cli/guitar_chords.rb +0 -122
- data/lib/cli/notes.rb +0 -20
- data/lib/cli/piano.rb +0 -57
- data/lib/cli/scale.rb +0 -53
- data/lib/cli/text.rb +0 -16
- data/lib/cli/ukulele.rb +0 -14
- data/lib/coltrane/cache.rb +0 -43
- data/lib/coltrane/cadence.rb +0 -7
- data/lib/coltrane/chord.rb +0 -89
- data/lib/coltrane/chord_quality.rb +0 -111
- data/lib/coltrane/chord_substitutions.rb +0 -9
- data/lib/coltrane/circle_of_fifths.rb +0 -31
- data/lib/coltrane/classic_scales.rb +0 -94
- data/lib/coltrane/diatonic_scale.rb +0 -36
- data/lib/coltrane/errors.rb +0 -95
- data/lib/coltrane/frequency.rb +0 -50
- data/lib/coltrane/frequency_interval.rb +0 -81
- data/lib/coltrane/interval.rb +0 -208
- data/lib/coltrane/interval_class.rb +0 -210
- data/lib/coltrane/interval_sequence.rb +0 -155
- data/lib/coltrane/key.rb +0 -16
- data/lib/coltrane/notable_progressions.rb +0 -28
- data/lib/coltrane/note.rb +0 -98
- data/lib/coltrane/note_set.rb +0 -89
- data/lib/coltrane/pitch.rb +0 -92
- data/lib/coltrane/pitch_class.rb +0 -148
- data/lib/coltrane/progression.rb +0 -74
- data/lib/coltrane/roman_chord.rb +0 -112
- data/lib/coltrane/scale.rb +0 -154
- data/lib/coltrane/unordered_interval_class.rb +0 -7
- data/lib/coltrane/voicing.rb +0 -39
- data/lib/coltrane_game/question.rb +0 -7
- data/lib/coltrane_instruments.rb +0 -7
- data/lib/coltrane_instruments/guitar.rb +0 -10
- data/lib/coltrane_instruments/guitar/base.rb +0 -29
- data/lib/coltrane_instruments/guitar/chord.rb +0 -170
- data/lib/coltrane_instruments/guitar/note.rb +0 -24
- data/lib/coltrane_instruments/guitar/string.rb +0 -29
- data/lib/os.rb +0 -21
@@ -1,155 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
# It describes a sequence of intervals
|
5
|
-
class IntervalSequence
|
6
|
-
extend Forwardable
|
7
|
-
attr_reader :intervals
|
8
|
-
|
9
|
-
def_delegators :@intervals, :map, :each, :[], :size,
|
10
|
-
:reduce, :delete, :reject!, :delete_if, :detect, :to_a
|
11
|
-
|
12
|
-
def initialize(*intervals, notes: nil, relative_intervals: nil)
|
13
|
-
if intervals.any?
|
14
|
-
@intervals = if intervals.first.is_a?(Interval)
|
15
|
-
intervals
|
16
|
-
else
|
17
|
-
intervals.map { |i| Interval[i] }
|
18
|
-
end
|
19
|
-
|
20
|
-
elsif notes
|
21
|
-
notes = NoteSet[*notes] if notes.is_a?(Array)
|
22
|
-
@intervals = intervals_from_notes(notes)
|
23
|
-
elsif relative_intervals
|
24
|
-
@relative_intervals = relative_intervals
|
25
|
-
@intervals = intervals_from_relative_intervals(relative_intervals)
|
26
|
-
else
|
27
|
-
raise WrongKeywordsError,
|
28
|
-
'Provide: [notes:] || [intervals:] || [relative_intervals:]'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
Interval.all_including_compound_and_altered.each do |interval|
|
33
|
-
# Creates methods such as major_third, returning it if it finds
|
34
|
-
define_method(interval.full_name.underscore.to_s) { find(interval) }
|
35
|
-
# Creates methods such as has_major_third?, returning a boolean
|
36
|
-
define_method("has_#{interval.full_name.underscore}?") { has?(interval) }
|
37
|
-
end
|
38
|
-
|
39
|
-
Interval.distances_names.map(&:underscore).each_with_index do |distance, i|
|
40
|
-
# Creates methods such as has_third?, returning a boolean
|
41
|
-
define_method("has_#{distance}?") { !!find_by_distance(i + 1) }
|
42
|
-
# Creates methods such third, returning any third it finds
|
43
|
-
define_method(distance.to_s) { find_by_distance(i + 1) }
|
44
|
-
# Creates methods such third!, returning thirds that arent aug or dim
|
45
|
-
define_method("#{distance}!") { find_by_distance(i + 1, false) }
|
46
|
-
end
|
47
|
-
|
48
|
-
instance_eval { alias [] new }
|
49
|
-
|
50
|
-
def relative_intervals
|
51
|
-
intervals_semitones[1..-1].each_with_index.map do |n, i|
|
52
|
-
if i.zero?
|
53
|
-
n
|
54
|
-
elsif i < intervals_semitones.size
|
55
|
-
n - intervals_semitones[i]
|
56
|
-
end
|
57
|
-
end + [12 - intervals_semitones.last]
|
58
|
-
end
|
59
|
-
|
60
|
-
def names
|
61
|
-
intervals.map(&:name)
|
62
|
-
end
|
63
|
-
|
64
|
-
def find(interval)
|
65
|
-
interval.clone if detect { |i| interval == i }
|
66
|
-
end
|
67
|
-
|
68
|
-
def has?(interval)
|
69
|
-
!!find(interval)
|
70
|
-
end
|
71
|
-
|
72
|
-
def find_by_distance(n, accept_altered = true)
|
73
|
-
strategy = (accept_altered ? :as : :as!)
|
74
|
-
map { |interval| interval.send(strategy, n) }
|
75
|
-
.compact
|
76
|
-
.sort_by { |i| i.alteration.abs }
|
77
|
-
.first
|
78
|
-
end
|
79
|
-
|
80
|
-
alias interval_names names
|
81
|
-
|
82
|
-
def all
|
83
|
-
intervals
|
84
|
-
end
|
85
|
-
|
86
|
-
def [](x)
|
87
|
-
intervals[x]
|
88
|
-
end
|
89
|
-
|
90
|
-
def shift(ammount)
|
91
|
-
self.class.new(*intervals.map do |i|
|
92
|
-
(i.semitones + ammount) % 12
|
93
|
-
end)
|
94
|
-
end
|
95
|
-
|
96
|
-
def zero_it
|
97
|
-
shift(-intervals.first.semitones)
|
98
|
-
end
|
99
|
-
|
100
|
-
def inversion(index)
|
101
|
-
self.class.new(*intervals.rotate(index)).zero_it
|
102
|
-
end
|
103
|
-
|
104
|
-
def next_inversion
|
105
|
-
inversion(index + 1)
|
106
|
-
end
|
107
|
-
|
108
|
-
def previous_inversion
|
109
|
-
inversion(index - 1)
|
110
|
-
end
|
111
|
-
|
112
|
-
def inversions
|
113
|
-
Array.new(intervals.length) { |i| inversion(i) }
|
114
|
-
end
|
115
|
-
|
116
|
-
def intervals_semitones
|
117
|
-
map(&:semitones)
|
118
|
-
end
|
119
|
-
|
120
|
-
def names
|
121
|
-
map(&:name)
|
122
|
-
end
|
123
|
-
|
124
|
-
def full_names
|
125
|
-
map(&:full_name)
|
126
|
-
end
|
127
|
-
|
128
|
-
def notes_for(root_note)
|
129
|
-
NoteSet[
|
130
|
-
*intervals.reduce([]) do |memo, interval|
|
131
|
-
memo + [root_note + interval]
|
132
|
-
end
|
133
|
-
]
|
134
|
-
end
|
135
|
-
|
136
|
-
def &(other)
|
137
|
-
case other
|
138
|
-
when Array then intervals & other
|
139
|
-
when IntervalSequence then intervals & other.semitones
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
private
|
144
|
-
|
145
|
-
def intervals_from_relative_intervals(relative_intervals)
|
146
|
-
relative_intervals[0..-2].reduce([Interval[0]]) do |memo, d|
|
147
|
-
memo + [memo.last + d]
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def intervals_from_notes(notes)
|
152
|
-
notes.map { |n| notes.root - n }.sort_by(&:semitones)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
data/lib/coltrane/key.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
class Key < DiatonicScale
|
5
|
-
KEY_REGEX = /([A-G][#b]?)([mM]?)/
|
6
|
-
|
7
|
-
def initialize(notation)
|
8
|
-
_, note, s = *notation.match(KEY_REGEX)
|
9
|
-
super(note, major: s != 'm')
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.[](notation)
|
13
|
-
new(notation)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
# This module takes care of adding to progressions knowledge that is more
|
5
|
-
# based on common standards and practices.
|
6
|
-
module NotableProgressions
|
7
|
-
PROGRESSIONS = {
|
8
|
-
'Pop' => ['I-V-vi-IV', :major],
|
9
|
-
'Jazzy Pop' => ['IM7-V7-vi7-IVM7', :major],
|
10
|
-
'Jazz' => ['ii7-V7-I7', :major],
|
11
|
-
'Jazz Minor' => ['ii7-V7-i7', :major],
|
12
|
-
'Blues' => ['IM7-IV7-I7-V7-IV7-I7', :major],
|
13
|
-
'Jazz Blues' => ['I7-IV7-I7-V7-IV7-I7', :major],
|
14
|
-
'Fifties' => ['I-vi-IV-V', :major],
|
15
|
-
'Circle' => ['vi-ii-V-I', :major],
|
16
|
-
'Tune Up' => ['ii7-V7-IM7-i7-IV7-IVM7-VIIM7', :minor]
|
17
|
-
}.freeze
|
18
|
-
|
19
|
-
PROGRESSIONS.each do |name, values|
|
20
|
-
notation, scale_name = values
|
21
|
-
define_method name.underscore do |note|
|
22
|
-
note = note.is_a?(Note) ? note : Note[note]
|
23
|
-
scale = Scale.public_send(scale_name, note)
|
24
|
-
new(notation, scale: scale)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
data/lib/coltrane/note.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
# Notes are different ways of calling pitch classes. In the context of equal
|
5
|
-
# tempered scales, they're more like a conceptual subject for
|
6
|
-
# matters of convention than an actual thing.
|
7
|
-
#
|
8
|
-
# Take for example A# and Bb. Those are different notes. Nevertheless, in the
|
9
|
-
# context of equal tempered scales they represent pretty much the same
|
10
|
-
# frequency.
|
11
|
-
#
|
12
|
-
# The theory of notes have changed too much in the course of time, which
|
13
|
-
# lead us with a lot of conventions and strategies when dealing with music.
|
14
|
-
# That's what this class is for.
|
15
|
-
class Note < PitchClass
|
16
|
-
attr_reader :alteration
|
17
|
-
|
18
|
-
ALTERATIONS = {
|
19
|
-
'b' => -1,
|
20
|
-
'#' => 1
|
21
|
-
}.freeze
|
22
|
-
|
23
|
-
def initialize(arg)
|
24
|
-
note_name = case arg
|
25
|
-
when String then arg
|
26
|
-
when PitchClass then arg.true_notation
|
27
|
-
when Numeric, Frequency then PitchClass.new(arg).true_notation
|
28
|
-
else raise(WrongArgumentsError, arg)
|
29
|
-
end
|
30
|
-
|
31
|
-
chars = note_name.chars
|
32
|
-
letter = chars.shift
|
33
|
-
raise InvalidNoteLetterError, arg unless ('A'..'G').cover?(letter)
|
34
|
-
@alteration = chars.reduce(0) do |alt, symbol|
|
35
|
-
raise InvalidNoteLetterError, arg unless ALTERATIONS.include?(symbol)
|
36
|
-
alt + ALTERATIONS[symbol]
|
37
|
-
end
|
38
|
-
super((PitchClass[letter].integer + @alteration) % PitchClass.size)
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.[](arg)
|
42
|
-
new(arg)
|
43
|
-
end
|
44
|
-
|
45
|
-
def name
|
46
|
-
"#{base_pitch_class}#{accidents}".gsub(/#b|b#/, '')
|
47
|
-
end
|
48
|
-
|
49
|
-
def base_pitch_class
|
50
|
-
PitchClass[integer - alteration]
|
51
|
-
end
|
52
|
-
|
53
|
-
def alteration=(a)
|
54
|
-
@alteration = a unless PitchClass[integer - a].accidental?
|
55
|
-
end
|
56
|
-
|
57
|
-
def alter(x)
|
58
|
-
Note.new(name).tap { |n| n.alteration = x }
|
59
|
-
end
|
60
|
-
|
61
|
-
def sharp?
|
62
|
-
alteration == 1
|
63
|
-
end
|
64
|
-
|
65
|
-
def double_sharp?
|
66
|
-
alteration == 2
|
67
|
-
end
|
68
|
-
|
69
|
-
def flat?
|
70
|
-
alteration == -1
|
71
|
-
end
|
72
|
-
|
73
|
-
def double_flat?
|
74
|
-
alteration == -2
|
75
|
-
end
|
76
|
-
|
77
|
-
def natural?
|
78
|
-
alteration == 0
|
79
|
-
end
|
80
|
-
|
81
|
-
def accidents
|
82
|
-
(@alteration > 0 ? '#' : 'b') * alteration.abs
|
83
|
-
end
|
84
|
-
|
85
|
-
def -(other)
|
86
|
-
super(other).yield_self { |r| r.is_a?(Note) ? r.alter(alteration) : r }
|
87
|
-
end
|
88
|
-
|
89
|
-
def +(other)
|
90
|
-
super(other).yield_self { |r| r.is_a?(Note) ? r.alter(alteration) : r }
|
91
|
-
end
|
92
|
-
|
93
|
-
def as(letter)
|
94
|
-
a = (Note[letter] - self)
|
95
|
-
alter([a.semitones, -(-a).semitones].min_by(&:abs))
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
data/lib/coltrane/note_set.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
# It describes a set of notes
|
5
|
-
class NoteSet
|
6
|
-
extend Forwardable
|
7
|
-
|
8
|
-
def_delegators :@notes, :first, :each, :size, :map, :reduce, :index,
|
9
|
-
:[], :index, :empty?, :permutation, :include?, :<<, :any?,
|
10
|
-
:count, :rotate
|
11
|
-
|
12
|
-
attr_reader :notes
|
13
|
-
|
14
|
-
alias root first
|
15
|
-
alias all notes
|
16
|
-
|
17
|
-
def self.[](*notes)
|
18
|
-
new(notes)
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize(arg)
|
22
|
-
@notes =
|
23
|
-
case arg
|
24
|
-
when NoteSet then arg.notes
|
25
|
-
when Array then arg.compact.map { |n| n.is_a?(PitchClass) ? n : Note[n] }.uniq
|
26
|
-
else raise InvalidNotesError, arg
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def &(other)
|
31
|
-
NoteSet[*(notes & other.notes)]
|
32
|
-
end
|
33
|
-
|
34
|
-
def degree(note)
|
35
|
-
index(note) + 1
|
36
|
-
end
|
37
|
-
|
38
|
-
def +(other)
|
39
|
-
case other
|
40
|
-
when Note then NoteSet[*(notes + [other])]
|
41
|
-
when NoteSet then NoteSet[*notes, *other.notes]
|
42
|
-
when Interval then NoteSet[*notes.map { |n| n + other }]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def -(other)
|
47
|
-
case other
|
48
|
-
when NoteSet then NoteSet[*(notes - other.notes)]
|
49
|
-
when Interval then NoteSet[*notes.map { |n| n - other }]
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def pretty_names
|
54
|
-
map(&:pretty_name)
|
55
|
-
end
|
56
|
-
|
57
|
-
def names
|
58
|
-
map(&:name)
|
59
|
-
end
|
60
|
-
|
61
|
-
def integers
|
62
|
-
map(&:integer)
|
63
|
-
end
|
64
|
-
|
65
|
-
def accidentals
|
66
|
-
count(&:accidental?)
|
67
|
-
end
|
68
|
-
|
69
|
-
def sharps
|
70
|
-
count(&:sharp?)
|
71
|
-
end
|
72
|
-
|
73
|
-
def flats
|
74
|
-
count(&:flat?)
|
75
|
-
end
|
76
|
-
|
77
|
-
def alter(alteration)
|
78
|
-
NoteSet[*map { |n| n.alter(alteration) }]
|
79
|
-
end
|
80
|
-
|
81
|
-
def alter_accidentals(alteration)
|
82
|
-
NoteSet[*map { |n| n.alter(alteration) if n.accidental? }]
|
83
|
-
end
|
84
|
-
|
85
|
-
def interval_sequence
|
86
|
-
IntervalSequence.new(notes: self)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
data/lib/coltrane/pitch.rb
DELETED
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
# It describes a pitch, like E4 or Bb5. It's like a note, but it has an octave
|
5
|
-
class Pitch
|
6
|
-
attr_reader :integer
|
7
|
-
|
8
|
-
def initialize(notation_arg = nil,
|
9
|
-
note: nil,
|
10
|
-
octave: nil,
|
11
|
-
notation: nil,
|
12
|
-
frequency: nil)
|
13
|
-
|
14
|
-
@integer = begin
|
15
|
-
if (n = notation_arg || notation)
|
16
|
-
n.is_a?(Integer) ? n : integer_from_notation(n)
|
17
|
-
elsif note && octave
|
18
|
-
integer_from_note_and_octave(Note[note], octave)
|
19
|
-
elsif frequency
|
20
|
-
integer_from_frequency(frequency)
|
21
|
-
else
|
22
|
-
raise(InvalidArgumentsError)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.[](*args)
|
28
|
-
new *args
|
29
|
-
end
|
30
|
-
|
31
|
-
def scientific_notation
|
32
|
-
"#{pitch_class}#{octave}"
|
33
|
-
end
|
34
|
-
|
35
|
-
def pitch_class
|
36
|
-
PitchClass[integer]
|
37
|
-
end
|
38
|
-
|
39
|
-
def octave
|
40
|
-
(integer / 12) - 1
|
41
|
-
end
|
42
|
-
|
43
|
-
alias notation scientific_notation
|
44
|
-
alias name scientific_notation
|
45
|
-
alias to_s scientific_notation
|
46
|
-
|
47
|
-
alias hash integer
|
48
|
-
alias midi integer
|
49
|
-
|
50
|
-
def ==(other)
|
51
|
-
integer == other.integer
|
52
|
-
end
|
53
|
-
|
54
|
-
def frequency
|
55
|
-
pitch_class.frequency.octave(octave)
|
56
|
-
end
|
57
|
-
|
58
|
-
alias eql? ==
|
59
|
-
alias eq ==
|
60
|
-
|
61
|
-
def +(other)
|
62
|
-
case other
|
63
|
-
when Integer then Pitch[integer + other]
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def -(other)
|
68
|
-
case other
|
69
|
-
when Integer then Pitch[integer - other]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def integer_from_notation(the_notation)
|
76
|
-
_, n, o = *the_notation.match(/(\D+)(\d+)/)
|
77
|
-
integer_from_note_and_octave(Note[n], o)
|
78
|
-
end
|
79
|
-
|
80
|
-
def integer_from_note_and_octave(p, o)
|
81
|
-
12 * (o.to_i + 1) + p.integer
|
82
|
-
end
|
83
|
-
|
84
|
-
def integer_from_frequency(f)
|
85
|
-
octave_from_frequency(f) * 12 + PitchClass.new(frequency: f).integer
|
86
|
-
end
|
87
|
-
|
88
|
-
def octave_from_frequency(f)
|
89
|
-
Math.log(f.to_f / PitchClass['C'].frequency.to_f, 2).ceil
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|