jmtk 0.0.3.3-java → 0.4-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +15 -0
  2. data/DEVELOPMENT_NOTES.md +20 -0
  3. data/INTRO.md +63 -31
  4. data/README.md +9 -3
  5. data/Rakefile +42 -42
  6. data/bin/jmtk +75 -32
  7. data/bin/mtk +75 -32
  8. data/examples/drum_pattern.rb +2 -2
  9. data/examples/dynamic_pattern.rb +1 -1
  10. data/examples/helpers/output_selector.rb +71 -0
  11. data/examples/notation.rb +5 -1
  12. data/examples/tone_row_melody.rb +1 -1
  13. data/lib/mtk.rb +1 -0
  14. data/lib/mtk/core/duration.rb +18 -3
  15. data/lib/mtk/core/intensity.rb +5 -3
  16. data/lib/mtk/core/interval.rb +21 -14
  17. data/lib/mtk/core/pitch.rb +2 -0
  18. data/lib/mtk/core/pitch_class.rb +6 -3
  19. data/lib/mtk/events/event.rb +2 -1
  20. data/lib/mtk/events/note.rb +1 -1
  21. data/lib/mtk/events/parameter.rb +1 -0
  22. data/lib/mtk/events/rest.rb +85 -0
  23. data/lib/mtk/events/timeline.rb +6 -2
  24. data/lib/mtk/io/jsound_input.rb +9 -3
  25. data/lib/mtk/io/midi_file.rb +38 -2
  26. data/lib/mtk/io/midi_input.rb +1 -1
  27. data/lib/mtk/io/midi_output.rb +95 -4
  28. data/lib/mtk/io/unimidi_input.rb +7 -3
  29. data/lib/mtk/lang/durations.rb +31 -26
  30. data/lib/mtk/lang/intensities.rb +29 -30
  31. data/lib/mtk/lang/intervals.rb +108 -41
  32. data/lib/mtk/lang/mtk_grammar.citrus +14 -4
  33. data/lib/mtk/lang/parser.rb +10 -5
  34. data/lib/mtk/lang/pitch_classes.rb +45 -17
  35. data/lib/mtk/lang/pitches.rb +169 -32
  36. data/lib/mtk/lang/tutorial.rb +279 -0
  37. data/lib/mtk/lang/tutorial_lesson.rb +87 -0
  38. data/lib/mtk/sequencers/event_builder.rb +29 -8
  39. data/spec/mtk/core/duration_spec.rb +14 -1
  40. data/spec/mtk/core/intensity_spec.rb +1 -1
  41. data/spec/mtk/events/event_spec.rb +10 -16
  42. data/spec/mtk/events/note_spec.rb +3 -3
  43. data/spec/mtk/events/rest_spec.rb +184 -0
  44. data/spec/mtk/events/timeline_spec.rb +5 -1
  45. data/spec/mtk/io/midi_file_spec.rb +13 -2
  46. data/spec/mtk/io/midi_output_spec.rb +42 -9
  47. data/spec/mtk/lang/durations_spec.rb +5 -5
  48. data/spec/mtk/lang/intensities_spec.rb +5 -5
  49. data/spec/mtk/lang/intervals_spec.rb +139 -13
  50. data/spec/mtk/lang/parser_spec.rb +65 -25
  51. data/spec/mtk/lang/pitch_classes_spec.rb +0 -11
  52. data/spec/mtk/lang/pitches_spec.rb +0 -15
  53. data/spec/mtk/patterns/chain_spec.rb +7 -7
  54. data/spec/mtk/patterns/for_each_spec.rb +2 -2
  55. data/spec/mtk/sequencers/event_builder_spec.rb +49 -17
  56. metadata +12 -22
data/bin/mtk CHANGED
@@ -10,38 +10,38 @@ options = {}
10
10
 
11
11
  option_parser = OptionParser.new do |opts|
12
12
 
13
- opts.banner = "Usage: #{$0} [options]"
13
+ opts.banner = "\nMTK: Music Tool Kit for Ruby\n\nUsage: #{$0} [options]"
14
14
  opts.separator ''
15
15
  opts.separator 'Options:'
16
16
 
17
- opts.on('-c FILE', '--convert FILE', 'Convert a file containing MTK syntax to MIDI',
18
- 'if a --file is given, write the MIDI to a file',
19
- 'if an --output is given, play the MIDI',
17
+ opts.on('-c FILE', '--convert FILE', 'Convert file containing MTK syntax to MIDI',
18
+ 'if --file is given, write the MIDI to file',
19
+ 'if --output is given, play the MIDI',
20
20
  'otherwise print the MIDI') {|file| options[:convert] = file }
21
21
 
22
22
  opts.separator ''
23
23
 
24
- opts.on('-e [syntax]', '--eval [syntax]', 'Convert the given MTK syntax String to MIDI',
25
- 'or start an interactive interpreter when [syntax] is omitted',
26
- 'Behaves like --convert when a --file or --output is given') {|syntax| options[:eval] = syntax }
24
+ opts.on('-e [syntax]', '--eval [syntax]', 'Convert the given MTK syntax to MIDI or',
25
+ 'interactive interpreter when no [syntax]',
26
+ 'Behaves like --convert for --file/--output') {|syntax| options[:eval] = syntax }
27
27
 
28
28
  opts.separator ''
29
29
 
30
- opts.on('-f FILE', '--file FILE', 'Write the output of --convert, --eval, --input, or --watch to a file'
31
- ) {|file| options[:file] = file }
30
+ opts.on('-f FILE', '--file FILE', 'Write output of --convert, --eval, --input',
31
+ 'or --watch to a file') {|file| options[:file] = file }
32
32
 
33
33
  opts.separator ''
34
34
 
35
- opts.on('-h', '--help', 'Show this message') { puts opts; exit }
35
+ opts.on('-h', '--help', 'Show these usage instructions') { puts opts; exit }
36
36
 
37
37
  opts.separator ''
38
38
 
39
39
  opts.on('-i INPUT', '--input INPUT', 'Set MIDI input for recording',
40
- 'if no --file is specified, prints the recorded MIDI') {|input| options[:input] = input }
40
+ 'if no --file given, prints recorded MIDI') {|input| options[:input] = input }
41
41
 
42
42
  opts.separator ''
43
43
 
44
- opts.on('-l', '--list', 'List available MIDI devices for --input and --output') { options[:list] = true }
44
+ opts.on('-l', '--list', 'List MIDI devices for --input and --output') { options[:list] = true }
45
45
 
46
46
  opts.separator ''
47
47
 
@@ -53,17 +53,19 @@ option_parser = OptionParser.new do |opts|
53
53
 
54
54
  opts.separator ''
55
55
 
56
- opts.on('-p FILE', '--play FILE', 'Play or print the contents of a MIDI file',
57
- 'if no --output is specified, print the MIDI file') {|file| options[:play] = file }
56
+ opts.on('-p FILE', '--play FILE', 'Play or print a MIDI file',
57
+ 'if no --output given, print the MIDI') {|file| options[:play] = file }
58
58
 
59
59
  opts.separator ''
60
60
 
61
- #opts.on('-t', '--tutorial', 'Start an interactive tutorial for the MTK syntax') { options[:tutorial] = true }
62
- #
63
- #opts.separator ''
61
+ opts.on('-t [color]', '--tutorial [color]', 'Interactive tutorial for MTK syntax',
62
+ 'Text color can be set on/off') {|color| options[:tutorial] = true; options[:color] = color }
64
63
 
65
- opts.on('-w FILE', '--watch FILE', 'Watch an MTK syntax file for changes and automatically convert to MIDI',
66
- 'Behaves like --convert when a --file or --output is given') {|file| options[:watch] = file }
64
+ opts.separator ''
65
+
66
+ opts.on('-w FILE', '--watch FILE', 'Watch an MTK syntax file for changes and',
67
+ 'automatically convert to MIDI',
68
+ 'Behaves like --convert for --file/--output') {|file| options[:watch] = file }
67
69
 
68
70
  end
69
71
 
@@ -71,18 +73,16 @@ end
71
73
  #######################################################################
72
74
 
73
75
  puts option_parser and exit if ARGV.length == 0
74
- #p ARGV
75
- #p options
76
76
 
77
77
  ERROR_INVALID_COMMAND = 1
78
78
  ERROR_FILE_NOT_FOUND = 2
79
79
  ERROR_OUTPUT_NOT_FOUND = 3
80
80
  ERROR_INPUT_NOT_FOUND = 4
81
81
 
82
- # Immediately trying to play output while Ruby is still "warming up" can cause timing issues with
83
- # the first couple notes. So we play this "empty" Timeline containing a rest to address that issue.
82
+ # Empty timeline used to prime the realtime output
84
83
  WARMUP = MTK::Events::Timeline.from_h( {0 => MTK.Note(60,-1)} )
85
84
 
85
+
86
86
  #######################################################################
87
87
 
88
88
  begin
@@ -115,6 +115,9 @@ end
115
115
  def output(timelines, print_header='Timeline')
116
116
  timelines = [timelines] unless timelines.is_a? Array
117
117
  if @output
118
+ # Immediately trying to play output while Ruby is still "warming up" can cause timing issues with
119
+ # the first couple notes. So we play this "empty" Timeline containing a rest to address that issue.
120
+ # TODO? move this into the output class and do it automatically when playing for the first time? (warmup code is also in output_selector))
118
121
  @output.play WARMUP
119
122
  @output.play timelines.first # TODO: support multiple timelines
120
123
  elsif @file
@@ -151,6 +154,33 @@ def watch_file_updated?
151
154
  end
152
155
 
153
156
 
157
+ def set_tutorial_color(color_option)
158
+ if color_option
159
+ case color_option.strip.downcase
160
+ when /^(on|yes|true|y|t|color)$/ then $tutorial_color = true
161
+ when /^(off|no|false|n|f)$/ then $tutorial_color = false
162
+ else
163
+ STDERR.puts "Invalid tutorial color setting '#{color}'. Try 'on' or 'off'."
164
+ exit ERROR_INVALID_COMMAND
165
+ end
166
+ else
167
+ require 'rbconfig'
168
+ os = RbConfig::CONFIG['host_os'].downcase
169
+ if os =~ /win/ and os !~ /darwin/
170
+ puts
171
+ puts "Windows command line text color is off by default."
172
+ puts "If you want color, use ANSI terminal software like Cygwin or Ansicon and "
173
+ puts "run #{$0} with the color option \"--tutorial on\""
174
+ $tutorial_color = false
175
+ else
176
+ $tutorial_color = true
177
+ end
178
+ end
179
+ puts "Tutorial color is #{if $tutorial_color then 'enabled' else 'disabled' end}."
180
+ puts
181
+ end
182
+
183
+
154
184
  #######################################################################
155
185
 
156
186
  if options[:list]
@@ -162,18 +192,21 @@ if options[:list]
162
192
  puts
163
193
  puts (['OUTPUTS:']+output_names).join("\n * ")
164
194
  puts
165
- puts 'When specifying --input INPUT or --output OUTPUT, the first substring match will be used.'
166
- puts "For example: --output IAC will use 'Apple Inc. IAC Driver' if it's the first OUTPUT containing 'IAC'"
195
+ puts 'When specifying --input INPUT or --output OUTPUT, the first substring match'
196
+ puts '(case-insensitive) will be used. For example: "--output iac" will use'
197
+ puts '"Apple Inc. IAC Driver" if it\'s the first OUTPUT containing "IAC".'
167
198
  puts
168
199
  exit
169
200
  end
170
201
 
202
+
171
203
  @monitor = true if options[:monitor]
172
204
 
205
+
173
206
  if options[:input]
174
207
  setup_io
175
208
  input_name = options[:input]
176
- @input = MTK::IO::MIDIInput.find_by_name /#{input_name}/
209
+ @input = MTK::IO::MIDIInput.find_by_name /#{input_name}/i
177
210
  if @input
178
211
  puts "Using input '#{@input.name}'"
179
212
  else
@@ -182,10 +215,11 @@ if options[:input]
182
215
  end
183
216
  end
184
217
 
218
+
185
219
  if options[:output]
186
220
  setup_io
187
221
  output_name = options[:output]
188
- @output = MTK::IO::MIDIOutput.find_by_name /#{output_name}/
222
+ @output = MTK::IO::MIDIOutput.find_by_name /#{output_name}/i
189
223
  if @output
190
224
  puts "Using output '#{@output.name}'"
191
225
  else
@@ -194,8 +228,9 @@ if options[:output]
194
228
  end
195
229
  end
196
230
 
197
- file = options[:file]
198
- @file = file if file
231
+
232
+ @file = options[:file]
233
+
199
234
 
200
235
  if options[:play]
201
236
  filename = options[:play]
@@ -204,6 +239,7 @@ if options[:play]
204
239
  output(timelines, "Timeline for #{filename}")
205
240
  end
206
241
 
242
+
207
243
  if options.has_key? :eval
208
244
  mtk_syntax = options[:eval]
209
245
  if mtk_syntax.nil?
@@ -221,12 +257,14 @@ if options.has_key? :eval
221
257
  end
222
258
  end
223
259
 
260
+
224
261
  if options[:convert]
225
262
  mtk_syntax_file = options[:convert]
226
263
  mtk_syntax = IO.read(mtk_syntax_file)
227
264
  convert(mtk_syntax)
228
265
  end
229
266
 
267
+
230
268
  if options[:watch]
231
269
  @watch_file = options[:watch]
232
270
  puts "Watching #{@watch_file}. Press Ctrl+C to exit."
@@ -243,8 +281,13 @@ if options[:watch]
243
281
  end
244
282
  end
245
283
 
246
- #if options.has_key? :tutorial
247
- # puts "TODO: tutorial"
248
- #end
284
+
285
+ if options.has_key? :tutorial
286
+ set_tutorial_color(options[:color])
287
+ require 'mtk/lang/tutorial'
288
+ tutorial = MTK::Lang::Tutorial.new
289
+ tutorial.run(@output)
290
+ end
291
+
249
292
 
250
293
  record if @input
@@ -9,8 +9,8 @@ file = ARGV[0] || "MTK-#{File.basename(__FILE__,'.rb')}.mid"
9
9
  _ = nil # defines _ as a rest
10
10
 
11
11
  pattern = {# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
12
- C2 => [fff, _, _, _, mf, _, _, _, o, _, _, _, mp, _, _, _], # kick
13
- Db2 => [ _, _, o, _, _, _, mp, _, _, _, o, _, _, _, mf, _], # rim shot
12
+ C2 => [fff, _, _, _, mf, _, _, _, f, _, _, _, mp, _, _, _], # kick
13
+ Db2 => [ _, _, f, _, _, _, mp, _, _, _, f, _, _, _, mf, _], # rim shot
14
14
  D2 => [ _, mp, _, mp, _, mp, _, mf, _, mp, _, mp, _, pp, _, mf] # snare
15
15
  }
16
16
 
@@ -29,7 +29,7 @@ end
29
29
  pitches = Patterns.Function( interval_generator, max_elements: 24 )
30
30
 
31
31
  # we'll also use a weighted choice to generate the intensities
32
- intensities = Patterns.Choice( mp,mf,o,ff,fff, weights: [1,2,3,2,1], max_cycles: 24 )
32
+ intensities = Patterns.Choice( mp,mf,f,ff,fff, weights: [1,2,3,2,1], max_cycles: 24 )
33
33
 
34
34
  sequencer = Sequencers.StepSequencer( pitches,intensities, step_size: 0.5, max_interval: 17 )
35
35
 
@@ -0,0 +1,71 @@
1
+ require 'mtk/io/midi_output'
2
+
3
+ # Assists with selecting an output.
4
+ class OutputSelector
5
+
6
+ # Empty timeline used to prime the realtime output
7
+ WARMUP = MTK::Events::Timeline.from_h( {0 => MTK.Note(60,-1)} )
8
+
9
+ class << self
10
+
11
+ def output
12
+ MTK::IO::MIDIOutput
13
+ end
14
+
15
+ # Look for an output by name using case insensitive matching,
16
+ # treating underscore like either an underscore or whitespace
17
+ def search output_name_pattern
18
+ output.find_by_name(/#{output_name_pattern.to_s.sub '_','(_|\\s+)'}/i)
19
+ end
20
+
21
+ # Command line interface to list output choices and select an output.
22
+ def prompt_for_output
23
+ devices_by_name = output.devices_by_name
24
+ names_by_number = {}
25
+
26
+ puts "Available MIDI outputs:"
27
+ devices_by_name.keys.each_with_index do |name,index|
28
+ number = index+1
29
+ names_by_number[number] = name
30
+ puts " #{number} => #{name}"
31
+ end
32
+
33
+ print "Enter the number of the output to test: "
34
+ device = nil
35
+ loop do
36
+ begin
37
+ # NOTE: invalid input will turn into 0, but that's ok because we index from 1 so it will be caught as invalid
38
+ number = STDIN.gets.to_i
39
+ name = names_by_number[number]
40
+ device = devices_by_name[name]
41
+ return output.open(device) if device
42
+ rescue
43
+ if $DEBUG
44
+ puts $!
45
+ puts $!.backtrace
46
+ end
47
+ # keep looping
48
+ end
49
+ print "Invalid input. Enter a number listed above: "
50
+ end
51
+ end
52
+
53
+
54
+ def ensure_output name=nil
55
+ output = nil
56
+ if name
57
+ output = search name
58
+ puts "Output '#{name}' not found." unless output
59
+ end
60
+ output ||= prompt_for_output
61
+
62
+ # Immediately trying to play output while Ruby is still "warming up" can cause timing issues with
63
+ # the first couple notes. So we play this "empty" Timeline containing a rest to address that issue.
64
+ output.play WARMUP
65
+
66
+ output
67
+ end
68
+
69
+ end
70
+ end
71
+
@@ -1,3 +1,7 @@
1
+ # NOTE: experimental example!
2
+ # This requires Lilypond to be installed, see http://lilypond.org/
3
+ # The lilypond command must be on your PATH or specificed via the LILYPOND_PATH environment variable.
4
+
1
5
  require 'mtk'
2
6
  require 'mtk/io/notation'
3
7
  include MTK
@@ -13,7 +17,7 @@ arg_error "MTK syntax string not provided" unless syntax
13
17
 
14
18
 
15
19
  file = ARGV[1]
16
- arg_error "The output_file must end in '.png', '.pdf', or '.ps'" unless file
20
+ arg_error "The output_file must end in '.png', '.pdf', or '.ps'" unless file =~ /\.(png|pdf|ps)$/
17
21
 
18
22
 
19
23
  sequencer = MTK::Lang::Parser.parse(syntax)
@@ -12,7 +12,7 @@ file = ARGV[0] || 'MTK-tone_row_melody.mid'
12
12
 
13
13
  row = PitchClassSet Db, G, Ab, F, Eb, E, D, C, B, Gb, A, Bb
14
14
  pitch_pattern = Patterns.Cycle *row
15
- rhythm_pattern = Patterns.Choice s, i, i+s, q # choose between sixteenth, eighth, dotted eighth, and quarter
15
+ rhythm_pattern = Patterns.Choice s, e, e+s, q # choose between sixteenth, eighth, dotted eighth, and quarter
16
16
 
17
17
  chain = Patterns.Chain pitch_pattern, rhythm_pattern, min_elements: 36, max_elements: 36
18
18
 
data/lib/mtk.rb CHANGED
@@ -56,6 +56,7 @@ require 'mtk/groups/chord'
56
56
 
57
57
  require 'mtk/events/event'
58
58
  require 'mtk/events/note'
59
+ require 'mtk/events/rest'
59
60
  require 'mtk/events/parameter'
60
61
  require 'mtk/events/timeline'
61
62
 
@@ -3,18 +3,20 @@ module MTK
3
3
 
4
4
  # A measure of time in musical beats.
5
5
  # May be negative to indicate a rest, which uses the absolute value for the effective duration.
6
+ #
7
+ # @see Lang::Durations
6
8
  class Duration
7
9
 
8
10
  include Comparable
9
11
 
10
12
  # The names of the base durations. See {MTK::Lang::Durations} for more info.
11
- NAMES = %w[w h q i s r x].freeze
13
+ NAMES = %w[w h q e s r x].freeze
12
14
 
13
15
  VALUES_BY_NAME = {
14
16
  'w' => 4,
15
17
  'h' => 2,
16
18
  'q' => 1,
17
- 'i' => Rational(1,2),
19
+ 'e' => Rational(1,2),
18
20
  's' => Rational(1,4),
19
21
  'r' => Rational(1,8),
20
22
  'x' => Rational(1,16)
@@ -55,7 +57,7 @@ module MTK
55
57
  # @example lookup the value of 3/4w, which three-quarters of a whole note (3 beats):
56
58
  # MTK::Core::Duration.from_s('3/4w')
57
59
  def self.from_s(s)
58
- if s =~ /^(-)?(\d+([\.\/]\d+)?)?([whqisrx])((\.|t)*)$/i
60
+ if s =~ /^(-)?(\d+([\.\/]\d+)?)?([whqesrx])((\.|t)*)$/i
59
61
  name = $4.downcase
60
62
  modifier = $5.downcase
61
63
  modifier << $1 if $1 # negation
@@ -95,6 +97,7 @@ module MTK
95
97
  # The magnitude (absolute value) of the duration.
96
98
  # This is the actual duration for rests.
97
99
  # @see #rest?
100
+ # @see #abs
98
101
  def length
99
102
  @value < 0 ? -@value : @value
100
103
  end
@@ -106,6 +109,18 @@ module MTK
106
109
  @value < 0
107
110
  end
108
111
 
112
+ # Force resets to be non-rests, otherwise don't change the duration.
113
+ # @see #-@
114
+ # @see #length
115
+ # @see #rest?
116
+ def abs
117
+ if @value < 0
118
+ -self
119
+ else
120
+ self
121
+ end
122
+ end
123
+
109
124
  # The number of beats as a floating point number
110
125
  def to_f
111
126
  @value.to_f
@@ -2,12 +2,14 @@ module MTK
2
2
  module Core
3
3
 
4
4
  # A measure of intensity, using an underlying value in the range 0.0-1.0
5
+ #
6
+ # @see Lang::Intensities
5
7
  class Intensity
6
8
 
7
9
  include Comparable
8
10
 
9
- # The names of the base intensities. See {}MTK::Lang::Intensities} for more info.
10
- NAMES = %w[ppp pp p mp mf o ff fff].freeze
11
+ # The names of the base intensities. See {MTK::Lang::Intensities} for more info.
12
+ NAMES = %w[ppp pp p mp mf f ff fff].freeze
11
13
 
12
14
  VALUES_BY_NAME = {
13
15
  'ppp' => 0.125,
@@ -15,7 +17,7 @@ module MTK
15
17
  'p' => 0.375,
16
18
  'mp' => 0.5,
17
19
  'mf' => 0.625,
18
- 'o' => 0.75,
20
+ 'f' => 0.75,
19
21
  'ff' => 0.875,
20
22
  'fff' => 1.0
21
23
  }
@@ -2,29 +2,35 @@ module MTK
2
2
  module Core
3
3
 
4
4
  # A measure of intensity, using an underlying value in the range 0.0-1.0
5
+ #
6
+ # @see Lang::Intervals
5
7
  class Interval
6
8
 
7
9
  include Comparable
8
10
 
9
11
  # The preferred names of all pre-defined intervals
12
+ # @see ALL_NAMES
10
13
  NAMES = %w[P1 m2 M2 m3 M3 P4 TT P5 m6 M6 m7 M7 P8].freeze
11
14
 
12
15
  # All valid names of pre-defined intervals, indexed by their value.
16
+ # @see ALL_NAMES
17
+ # @see NAMES
18
+ # @see http://en.wikipedia.org/wiki/Interval_(music)#Main_intervals
13
19
  NAMES_BY_VALUE =
14
- [ # names # value # description
15
- %w( P1 p1 ), # 0 # unison
16
- %w( m2 min2 ), # 1 # minor second
17
- %w( M2 maj2 ), # 2 # major second
18
- %w( m3 min3 ), # 3 # minor third
19
- %w( M3 maj3 ), # 4 # major third
20
- %w( P4 p4 ), # 5 # perfect fourth
21
- %w( TT tt ), # 6 # tritone (AKA augmented fourth, diminished fifth)
22
- %w( P5 p5 ), # 7 # perfect fifth
23
- %w( m6 min6 ), # 8 # minor sixth
24
- %w( M6 maj6 ), # 9 # major sixth
25
- %w( m7 min7 ), # 10 # minor seventh
26
- %w( M7 maj7 ), # 11 # major seventh
27
- %w( P8 p8 ) # 12 # octave
20
+ [ # names # value # description # enharmonic equivalents
21
+ %w( P1 d2 ), # 0 # unison # diminished second
22
+ %w( m2 a1 ), # 1 # minor second # augmented unison
23
+ %w( M2 d3 ), # 2 # major second # diminished third
24
+ %w( m3 a2 ), # 3 # minor third # augmented second
25
+ %w( M3 d4 ), # 4 # major third # diminished fourth
26
+ %w( P4 a3 ), # 5 # perfect fourth # augmented third
27
+ %w( TT a4 d5 ),# 6 # tritone # augmented fourth, diminished fifth
28
+ %w( P5 d6 ), # 7 # perfect fifth # diminished sixth
29
+ %w( m6 a5 ), # 8 # minor sixth # augmented fifth
30
+ %w( M6 d7 ), # 9 # major sixth # diminished seventh
31
+ %w( m7 a6 ), # 10 # minor seventh # augmented sixth
32
+ %w( M7 d8 ), # 11 # major seventh # diminished octave
33
+ %w( P8 a7 ) # 12 # octave # augmented seventh
28
34
  ].freeze
29
35
 
30
36
  # A mapping from intervals names to their value
@@ -35,6 +41,7 @@ module MTK
35
41
  ].freeze
36
42
 
37
43
  # All valid interval names
44
+ # @see NAMES_BY_VALUE
38
45
  ALL_NAMES = NAMES_BY_VALUE.flatten.freeze
39
46
 
40
47