musique 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +100 -0
- data/lib/music/chord.rb +108 -0
- data/lib/music/interval.rb +173 -0
- data/lib/music/note.rb +173 -0
- data/lib/music.rb +6 -0
- data/lib/musique.rb +1 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 49ee2d5eebc0c30d67c830b2e8fe5abaf97e91b9
|
4
|
+
data.tar.gz: 5850e2bdb290e8eddf82336f6aa897849455feaa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 467fbe00501716a9b449ae782d1ea925884449e879d9508107fa0c820954a76a2a5397e24bb5035aba7b7f9d77df3136c20bb3a64020a6e6f8495d0f1dbae859
|
7
|
+
data.tar.gz: fe2d69a49b2c4c31189dc5a84024f5b3388a8dd13a25648e6347dcbefa54ff993f1baeac03a0cb02757475d7e2358919593fa51491d5895450a61a36f89b141e
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014-ω Janko Marohnić
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# Musique
|
2
|
+
|
3
|
+
Musique is a gem for manipulating with musical constructs, such as notes,
|
4
|
+
chords and intervals.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
```sh
|
10
|
+
$ gem install musique
|
11
|
+
```
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
### `Music::Note`
|
17
|
+
|
18
|
+
```rb
|
19
|
+
require "musique"
|
20
|
+
|
21
|
+
include Music
|
22
|
+
|
23
|
+
note = Note.new("C#1")
|
24
|
+
note.letter #=> "C"
|
25
|
+
note.accidental #=> "#"
|
26
|
+
note.octave #=> 1
|
27
|
+
|
28
|
+
# Comparison
|
29
|
+
Note.new("C1") < Note.new("E1") #=> true
|
30
|
+
Note.new("C#1") == Note.new("D♭1") #=> true
|
31
|
+
|
32
|
+
# Transposing
|
33
|
+
major_third = Interval.new(3, :major)
|
34
|
+
Note.new("C1").transpose_up(major_third).name #=> "E1"
|
35
|
+
Note.new("E1").transpose_down(major_third).name #=> "C1"
|
36
|
+
|
37
|
+
# Difference
|
38
|
+
Note.new("C2") - Note.new("C1") #=> #<Music::Interval @number=8, @quality=:perfect>
|
39
|
+
```
|
40
|
+
|
41
|
+
### `Music::Chord`
|
42
|
+
|
43
|
+
```rb
|
44
|
+
require "musique"
|
45
|
+
|
46
|
+
include Music
|
47
|
+
|
48
|
+
chord = Chord.new("C#7")
|
49
|
+
chord.root.name #=> "C#"
|
50
|
+
chord.kind #=> "7"
|
51
|
+
|
52
|
+
# Notes
|
53
|
+
Chord.new("C").notes.map(&:name) #=> ["C", "E", "G"]
|
54
|
+
Chord.new("Cm7").notes.map(&:name) #=> ["C", "E♭", "G", "B♭"]
|
55
|
+
|
56
|
+
# Transposing
|
57
|
+
major_third = Interval.new(3, :major)
|
58
|
+
Chord.new("C").transpose_up(major_third).notes.map(&:name) #=> ["E", "G#", "B"]
|
59
|
+
```
|
60
|
+
|
61
|
+
Into `Chord.new(...)` you should be able to pass in any chord name. For example,
|
62
|
+
`"Cm"` and `"Cmin"` both represent the "C minor" chord, and both can be passed in.
|
63
|
+
|
64
|
+
If you come across a chord name that doesn't work, please open an issue,
|
65
|
+
I would like to know about it.
|
66
|
+
|
67
|
+
### `Music::Interval`
|
68
|
+
|
69
|
+
```rb
|
70
|
+
require "musique"
|
71
|
+
|
72
|
+
include Music
|
73
|
+
|
74
|
+
interval = Interval.new(3, :minor)
|
75
|
+
interval.number #=> 3
|
76
|
+
interval.quality #=> :minor
|
77
|
+
|
78
|
+
# Comparison
|
79
|
+
Interval.new(3, :minor) > Interval.new(2, :major) #=> true
|
80
|
+
Interval.new(3, :minor) == Interval.new(2, :augmented) #=> true
|
81
|
+
|
82
|
+
# Kinds
|
83
|
+
Interval.new(6, :major).consonance? #=> true
|
84
|
+
Interval.new(6, :major).perfect_consonance? #=> false (perfect consonances are 1, 4, and 5)
|
85
|
+
Interval.new(6, :major).imperfect_consonance? #=> true
|
86
|
+
Interval.new(6, :major).dissonance? #=> false
|
87
|
+
|
88
|
+
# Size
|
89
|
+
Interval.new(3, :major).size #=> 4 (semitones)
|
90
|
+
```
|
91
|
+
|
92
|
+
Social
|
93
|
+
------
|
94
|
+
|
95
|
+
You can follow me on Twitter, I'm [@m_janko](http://twitter.com/m_janko).
|
96
|
+
|
97
|
+
License
|
98
|
+
-------
|
99
|
+
|
100
|
+
This project is released under the [MIT license](/LICENSE).
|
data/lib/music/chord.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Music
|
4
|
+
|
5
|
+
class Chord
|
6
|
+
|
7
|
+
RULES = {
|
8
|
+
"5" => ["1", "major 3", "5"],
|
9
|
+
"m5" => ["1", "minor 3", "5"],
|
10
|
+
|
11
|
+
"7" => ["1", "major 3", "5", "major 7"],
|
12
|
+
"m7" => ["1", "minor 3", "5", "minor 7"],
|
13
|
+
}
|
14
|
+
|
15
|
+
REGEXP = /
|
16
|
+
(?<root> [CDEFGAB][#♭b]? )
|
17
|
+
(?<kind> m?\d* )
|
18
|
+
/x
|
19
|
+
|
20
|
+
##
|
21
|
+
# @return [Music::Note]
|
22
|
+
# @example
|
23
|
+
# Chord.new("C").root #=> #<Music::Note @letter="C">
|
24
|
+
#
|
25
|
+
attr_reader :root
|
26
|
+
##
|
27
|
+
#
|
28
|
+
# @return [String]
|
29
|
+
# @example
|
30
|
+
# Chord.new("C7").kind #=> "7"
|
31
|
+
#
|
32
|
+
attr_reader :kind
|
33
|
+
|
34
|
+
##
|
35
|
+
# @param name [String] Name of the chord, in any form.
|
36
|
+
# @example
|
37
|
+
# Chord.new("Cm") == Chord.new("Cmin") #=> true
|
38
|
+
#
|
39
|
+
def initialize(name)
|
40
|
+
unless match = name.match(/^#{REGEXP}$/)
|
41
|
+
raise ArgumentError, "invalid chord format: #{name}"
|
42
|
+
end
|
43
|
+
|
44
|
+
@root = Note.new(match[:root])
|
45
|
+
|
46
|
+
@kind = match[:kind]
|
47
|
+
@kind << "5" if @kind !~ /\d+/
|
48
|
+
end
|
49
|
+
|
50
|
+
def name
|
51
|
+
[root, kind].join
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Compares the names.
|
56
|
+
#
|
57
|
+
def ==(other)
|
58
|
+
self.name == other.name
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# @return [Array<Music::Note>]
|
63
|
+
#
|
64
|
+
def notes
|
65
|
+
RULES[kind].map do |interval_name|
|
66
|
+
quality = interval_name[/^[a-z]+/] || "perfect"
|
67
|
+
number = interval_name[/\d+$/]
|
68
|
+
interval = Interval.new(number.to_i, quality.to_sym)
|
69
|
+
|
70
|
+
root.transpose_by(interval)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# @param interval [Music::Interval]
|
76
|
+
# @return [Music::Note]
|
77
|
+
# @example
|
78
|
+
# major_third = Interval.new(3, :major)
|
79
|
+
# Chord.new("C7").transpose_by(major_third) == Note.new("E7") #=> true
|
80
|
+
# Note.new("E7").transpose_by(-major_third) == Note.new("C7") #=> true
|
81
|
+
#
|
82
|
+
def transpose_by(interval)
|
83
|
+
Chord.new(root.transpose_by(interval).name + kind)
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# @param (see #transpose_by)
|
88
|
+
# @return (see #transpose_by)
|
89
|
+
#
|
90
|
+
# @see #transpose_by
|
91
|
+
#
|
92
|
+
def transpose_up(interval)
|
93
|
+
transpose_by(interval)
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# @param (see #transpose_by)
|
98
|
+
# @return (see #transpose_by)
|
99
|
+
#
|
100
|
+
# @see #transpose_by
|
101
|
+
#
|
102
|
+
def transpose_down(interval)
|
103
|
+
transpose_by(-interval)
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module Music
|
2
|
+
|
3
|
+
class Interval
|
4
|
+
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
##
|
8
|
+
# @private
|
9
|
+
#
|
10
|
+
SIZES = {
|
11
|
+
1 => [0],
|
12
|
+
2 => [1, 2],
|
13
|
+
3 => [3, 4],
|
14
|
+
4 => [5],
|
15
|
+
5 => [7],
|
16
|
+
6 => [8, 9],
|
17
|
+
7 => [10, 11],
|
18
|
+
8 => [12],
|
19
|
+
}
|
20
|
+
|
21
|
+
##
|
22
|
+
# @private
|
23
|
+
#
|
24
|
+
OCTAVE = 8
|
25
|
+
|
26
|
+
QUALITIES = [:minor, :major, :perfect, :augmented, :diminished]
|
27
|
+
|
28
|
+
##
|
29
|
+
# @return [Integer]
|
30
|
+
#
|
31
|
+
attr_reader :number
|
32
|
+
##
|
33
|
+
# @return [Symbol]
|
34
|
+
#
|
35
|
+
attr_reader :quality
|
36
|
+
##
|
37
|
+
# Indicates if the interval is positive or negative.
|
38
|
+
#
|
39
|
+
# @return [1, -1]
|
40
|
+
#
|
41
|
+
attr_reader :direction
|
42
|
+
|
43
|
+
##
|
44
|
+
# @param number [Integer]
|
45
|
+
# @param quality [Symbol] See the {QUALITIES} constant for the list of
|
46
|
+
# possible values.
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# Interval.new(3, :major) # a major third
|
50
|
+
# Interval.new(5, :perfect) # a perfect fifth
|
51
|
+
#
|
52
|
+
def initialize(number, quality)
|
53
|
+
if not QUALITIES.include?(quality)
|
54
|
+
raise ArgumentError, "invalid interval quality: #{quality}"
|
55
|
+
elsif [1, 4, 5].include?((number.abs - 1) % 7 + 1) and [:major, :minor].include?(quality)
|
56
|
+
raise ArgumentError, "interval #{number} doesn't have quality \"#{quality}\""
|
57
|
+
end
|
58
|
+
|
59
|
+
@number, @quality = number.abs, quality
|
60
|
+
@direction = number >= 0 ? +1 : -1
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Perfect consonants are perfect unisons, fourths and fifths (and their
|
65
|
+
# compound intervals).
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
#
|
69
|
+
def perfect_consonance?
|
70
|
+
[1, 4, 5].include?(normalized_number) and [:perfect].include?(quality)
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Imperfect consonants are minor/major thirds and sixths (and their compound
|
75
|
+
# intervals).
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
78
|
+
#
|
79
|
+
def imperfect_consonance?
|
80
|
+
[3, 6].include?(normalized_number) and [:minor, :major].include?(quality)
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Consonances are perfect and imperfect consonances together.
|
85
|
+
#
|
86
|
+
# @return [Boolean]
|
87
|
+
#
|
88
|
+
# @see #perfect_consonance?
|
89
|
+
# @see #imperfect_consonance?
|
90
|
+
#
|
91
|
+
def consonance?
|
92
|
+
perfect_consonance? or imperfect_consonance?
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Disonances are all intervals that are not consonances.
|
97
|
+
#
|
98
|
+
# @return [Boolean]
|
99
|
+
#
|
100
|
+
# @see #consonance?
|
101
|
+
#
|
102
|
+
def dissonance?
|
103
|
+
not consonance?
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Returns the same inteval with changed direction.
|
108
|
+
#
|
109
|
+
# @return [Music::Interval]
|
110
|
+
#
|
111
|
+
def -@
|
112
|
+
Interval.new((-1 * direction) * number, quality)
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Returns the number of semitones that interval covers.
|
117
|
+
#
|
118
|
+
# @return [Integer]
|
119
|
+
#
|
120
|
+
def size
|
121
|
+
result = 0
|
122
|
+
current_number = number
|
123
|
+
|
124
|
+
while current_number >= OCTAVE
|
125
|
+
result += SIZES[OCTAVE][0]
|
126
|
+
current_number -= OCTAVE - 1
|
127
|
+
end
|
128
|
+
|
129
|
+
sizes = SIZES.fetch(current_number)
|
130
|
+
result +=
|
131
|
+
case quality
|
132
|
+
when :diminished then sizes.first - 1
|
133
|
+
when :minor then sizes.first
|
134
|
+
when :perfect then sizes[0]
|
135
|
+
when :major then sizes.last
|
136
|
+
when :augmented then sizes.last + 1
|
137
|
+
end
|
138
|
+
|
139
|
+
result * direction
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Compares intervals by their size.
|
144
|
+
#
|
145
|
+
# @param other [Music::Interval]
|
146
|
+
# @example
|
147
|
+
# Interval.new(3, :major) > Interval.new(4, :perfect) #=> true
|
148
|
+
# Interval.new(3, :major) == Interval.new(4, :diminished) #=> true
|
149
|
+
#
|
150
|
+
def <=>(other)
|
151
|
+
self.size.abs <=> other.size.abs
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# The actual diatonic difference. For example, seconds are numbered with
|
156
|
+
# `2`, but the actual diatonic difference between two notes that
|
157
|
+
# are in a second is `1`.
|
158
|
+
#
|
159
|
+
# @return [Integer]
|
160
|
+
#
|
161
|
+
def diff
|
162
|
+
(number - 1) * direction
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def normalized_number
|
168
|
+
(number - 1) % 7 + 1
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
data/lib/music/note.rb
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Music
|
4
|
+
|
5
|
+
class Note
|
6
|
+
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
CHROMATIC_SCALE = %w[C C/D D D/E E F F/G G G/A A A/B B]
|
10
|
+
DIATONIC_SCALE = %w[C D E F G A B]
|
11
|
+
|
12
|
+
ACCIDENTALS = %w[# ♭]
|
13
|
+
|
14
|
+
REGEXP = /
|
15
|
+
(?<letter> [CDEFGAB] )
|
16
|
+
(?<accidental> [#♭b]? )
|
17
|
+
(?<octave> (-?\d+)? )
|
18
|
+
/x
|
19
|
+
|
20
|
+
##
|
21
|
+
# C, D, E, F, G, A or B.
|
22
|
+
#
|
23
|
+
# @return [String]
|
24
|
+
#
|
25
|
+
attr_reader :letter
|
26
|
+
##
|
27
|
+
# \# or ♭.
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
#
|
31
|
+
attr_reader :accidental
|
32
|
+
##
|
33
|
+
# An integer.
|
34
|
+
#
|
35
|
+
# @return [Integer]
|
36
|
+
#
|
37
|
+
attr_reader :octave
|
38
|
+
|
39
|
+
##
|
40
|
+
# @param name [String] Consists of a letter (C, D, E, F, G, A, B),
|
41
|
+
# accidental (# or ♭) and octave (any integer).
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# Note.new("C")
|
45
|
+
# Note.new("C#")
|
46
|
+
# Note.new("C♭") #=> or Note.new("Cb")
|
47
|
+
#
|
48
|
+
# Note.new("C1")
|
49
|
+
# Note.new("B-1")
|
50
|
+
#
|
51
|
+
def initialize(name)
|
52
|
+
unless match = name.match(/^#{REGEXP}$/)
|
53
|
+
raise ArgumentError, "invalid note name: #{name} (example: C#1)"
|
54
|
+
end
|
55
|
+
|
56
|
+
@letter = match[:letter]
|
57
|
+
@accidental = match[:accidental].sub("b", "♭") unless match[:accidental].empty?
|
58
|
+
@octave = match[:octave].to_i unless match[:octave].empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def name
|
62
|
+
[letter, accidental, octave].join
|
63
|
+
end
|
64
|
+
alias to_s name
|
65
|
+
|
66
|
+
##
|
67
|
+
# Compares notes by their pitch.
|
68
|
+
#
|
69
|
+
# @param other [Music::Note]
|
70
|
+
# @example
|
71
|
+
# Note.new("C#") == Note.new("D♭") #=> true
|
72
|
+
# Note.new("C") < Note.new("D") #=> true
|
73
|
+
# Note.new("C2") > Note.new("C1") #=> true
|
74
|
+
#
|
75
|
+
def <=>(other)
|
76
|
+
self.pitch <=> other.pitch
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# @param interval [Music::Interval]
|
81
|
+
# @return [Music::Note]
|
82
|
+
# @example
|
83
|
+
# major_third = Interval.new(3, :major)
|
84
|
+
# Note.new("C").transpose_by(major_third) == Note.new("E") #=> true
|
85
|
+
# Note.new("E").transpose_by(-major_third) == Note.new("C") #=> true
|
86
|
+
#
|
87
|
+
def transpose_by(interval)
|
88
|
+
transposed_pitch = pitch + interval.size
|
89
|
+
transposed_pitch %= CHROMATIC_SCALE.size if octave.nil?
|
90
|
+
|
91
|
+
transposed_diatonic_idx = (diatonic_idx + interval.diff) % DIATONIC_SCALE.size
|
92
|
+
transposed_letter = DIATONIC_SCALE.fetch(transposed_diatonic_idx)
|
93
|
+
|
94
|
+
transposed_octave = octave + (diatonic_idx + interval.diff) / DIATONIC_SCALE.size if octave
|
95
|
+
|
96
|
+
transposed_accidental = ACCIDENTALS.find do |accidental|
|
97
|
+
note = Note.new [transposed_letter, accidental, transposed_octave].join
|
98
|
+
note.pitch == transposed_pitch
|
99
|
+
end
|
100
|
+
|
101
|
+
Note.new [transposed_letter, transposed_accidental, transposed_octave].join
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# @param (see #transpose_by)
|
106
|
+
# @return (see #transpose_by)
|
107
|
+
#
|
108
|
+
# @see #transpose_by
|
109
|
+
#
|
110
|
+
def transpose_up(interval)
|
111
|
+
transpose_by(interval)
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# @param (see #transpose_by)
|
116
|
+
# @return (see #transpose_by)
|
117
|
+
#
|
118
|
+
# @see #transpose_by
|
119
|
+
#
|
120
|
+
def transpose_down(interval)
|
121
|
+
transpose_by(-interval)
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# @param other [Music::Note]
|
126
|
+
# @return [Music::Interval]
|
127
|
+
# @example
|
128
|
+
# Note.new("E") - Note.new("C") #=> #<Music::Interval @number=3, @quality=:major>
|
129
|
+
#
|
130
|
+
def -(other)
|
131
|
+
number = (self.diatonic_idx - other.diatonic_idx).abs + 1
|
132
|
+
number += (self.octave - other.octave).abs * DIATONIC_SCALE.size unless octave.nil?
|
133
|
+
|
134
|
+
distance = (self.pitch - other.pitch).abs
|
135
|
+
quality = Interval::QUALITIES.find do |quality|
|
136
|
+
begin; Interval.new(number, quality).size == distance; rescue ArgumentError; end
|
137
|
+
end
|
138
|
+
|
139
|
+
interval = Interval.new(number, quality)
|
140
|
+
self >= other ? interval : -interval
|
141
|
+
end
|
142
|
+
|
143
|
+
protected
|
144
|
+
|
145
|
+
##
|
146
|
+
# Internal integer representation on the note.
|
147
|
+
#
|
148
|
+
# @return [Integer]
|
149
|
+
#
|
150
|
+
def pitch
|
151
|
+
result = chromatic_idx + (octave.to_i * CHROMATIC_SCALE.size)
|
152
|
+
result %= CHROMATIC_SCALE.size if octave.nil?
|
153
|
+
result
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# @return [Integer] An integer from 0 to 6
|
158
|
+
#
|
159
|
+
def diatonic_idx
|
160
|
+
DIATONIC_SCALE.index(letter)
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# @return [Integer] An integer from 0 to 11
|
165
|
+
#
|
166
|
+
def chromatic_idx
|
167
|
+
accidental_effect = {"#" => +1, "♭" => -1}.fetch(accidental, 0)
|
168
|
+
CHROMATIC_SCALE.index(letter) + accidental_effect
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
data/lib/music.rb
ADDED
data/lib/musique.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "music"
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: musique
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Janko Marohnić
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: This gem is an object-oriented wrapper for the Flickr API.
|
14
|
+
email:
|
15
|
+
- janko.marohnic@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- lib/music.rb
|
23
|
+
- lib/music/chord.rb
|
24
|
+
- lib/music/interval.rb
|
25
|
+
- lib/music/note.rb
|
26
|
+
- lib/musique.rb
|
27
|
+
homepage: http://janko-m.github.com/flickr-objects
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.9.2
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 2.2.0
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: This gem is an object-oriented wrapper for the Flickr API.
|
51
|
+
test_files: []
|
52
|
+
has_rdoc:
|