musique 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|