mtk 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/.yardopts +9 -0
  2. data/INTRO.md +73 -0
  3. data/LICENSE.txt +27 -0
  4. data/README.md +93 -18
  5. data/Rakefile +13 -1
  6. data/examples/crescendo.rb +20 -0
  7. data/examples/dynamic_pattern.rb +39 -0
  8. data/examples/play_midi.rb +19 -0
  9. data/examples/print_midi.rb +13 -0
  10. data/examples/random_tone_row.rb +18 -0
  11. data/examples/tone_row_melody.rb +21 -0
  12. data/lib/mtk/_constants/durations.rb +80 -0
  13. data/lib/mtk/_constants/intensities.rb +81 -0
  14. data/lib/mtk/{constants → _constants}/intervals.rb +10 -1
  15. data/lib/mtk/_constants/pitch_classes.rb +35 -0
  16. data/lib/mtk/_constants/pitches.rb +49 -0
  17. data/lib/mtk/{numeric_extensions.rb → _numeric_extensions.rb} +0 -0
  18. data/lib/mtk/event.rb +14 -5
  19. data/lib/mtk/helper/collection.rb +114 -0
  20. data/lib/mtk/helper/event_builder.rb +85 -0
  21. data/lib/mtk/{constants → helper}/pseudo_constants.rb +7 -6
  22. data/lib/mtk/lang/grammar.rb +17 -0
  23. data/lib/mtk/lang/mtk_grammar.citrus +60 -0
  24. data/lib/mtk/midi/file.rb +10 -15
  25. data/lib/mtk/midi/jsound_input.rb +68 -0
  26. data/lib/mtk/midi/jsound_output.rb +80 -0
  27. data/lib/mtk/note.rb +22 -3
  28. data/lib/mtk/pattern/abstract_pattern.rb +132 -0
  29. data/lib/mtk/pattern/choice.rb +25 -9
  30. data/lib/mtk/pattern/cycle.rb +51 -0
  31. data/lib/mtk/pattern/enumerator.rb +26 -0
  32. data/lib/mtk/pattern/function.rb +46 -0
  33. data/lib/mtk/pattern/lines.rb +60 -0
  34. data/lib/mtk/pattern/palindrome.rb +42 -0
  35. data/lib/mtk/pattern/sequence.rb +15 -50
  36. data/lib/mtk/pitch.rb +45 -6
  37. data/lib/mtk/pitch_class.rb +36 -35
  38. data/lib/mtk/pitch_class_set.rb +46 -14
  39. data/lib/mtk/pitch_set.rb +20 -31
  40. data/lib/mtk/sequencer/abstract_sequencer.rb +85 -0
  41. data/lib/mtk/sequencer/rhythmic_sequencer.rb +29 -0
  42. data/lib/mtk/sequencer/step_sequencer.rb +26 -0
  43. data/lib/mtk/timeline.rb +75 -22
  44. data/lib/mtk/transform/invertible.rb +15 -0
  45. data/lib/mtk/{util → transform}/mappable.rb +6 -2
  46. data/lib/mtk/transform/set_theory_operations.rb +34 -0
  47. data/lib/mtk/transform/transposable.rb +14 -0
  48. data/lib/mtk.rb +56 -22
  49. data/spec/mtk/_constants/durations_spec.rb +118 -0
  50. data/spec/mtk/{constants/dynamics_spec.rb → _constants/intensities_spec.rb} +48 -17
  51. data/spec/mtk/{constants → _constants}/intervals_spec.rb +21 -0
  52. data/spec/mtk/_constants/pitch_classes_spec.rb +58 -0
  53. data/spec/mtk/_constants/pitches_spec.rb +52 -0
  54. data/spec/mtk/{numeric_extensions_spec.rb → _numeric_extensions_spec.rb} +0 -0
  55. data/spec/mtk/event_spec.rb +19 -0
  56. data/spec/mtk/helper/collection_spec.rb +291 -0
  57. data/spec/mtk/helper/event_builder_spec.rb +92 -0
  58. data/spec/mtk/helper/pseudo_constants_spec.rb +20 -0
  59. data/spec/mtk/lang/grammar_spec.rb +100 -0
  60. data/spec/mtk/midi/file_spec.rb +41 -6
  61. data/spec/mtk/note_spec.rb +53 -3
  62. data/spec/mtk/pattern/abstract_pattern_spec.rb +45 -0
  63. data/spec/mtk/pattern/choice_spec.rb +89 -3
  64. data/spec/mtk/pattern/cycle_spec.rb +133 -0
  65. data/spec/mtk/pattern/function_spec.rb +133 -0
  66. data/spec/mtk/pattern/lines_spec.rb +93 -0
  67. data/spec/mtk/pattern/note_cycle_spec.rb.bak +116 -0
  68. data/spec/mtk/pattern/palindrome_spec.rb +124 -0
  69. data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +47 -0
  70. data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +37 -0
  71. data/spec/mtk/pattern/sequence_spec.rb +128 -31
  72. data/spec/mtk/pitch_class_set_spec.rb +240 -7
  73. data/spec/mtk/pitch_class_spec.rb +84 -18
  74. data/spec/mtk/pitch_set_spec.rb +45 -10
  75. data/spec/mtk/pitch_spec.rb +59 -0
  76. data/spec/mtk/sequencer/abstract_sequencer_spec.rb +159 -0
  77. data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +49 -0
  78. data/spec/mtk/sequencer/step_sequencer_spec.rb +71 -0
  79. data/spec/mtk/timeline_spec.rb +118 -15
  80. data/spec/spec_helper.rb +4 -3
  81. metadata +59 -22
  82. data/lib/mtk/chord.rb +0 -47
  83. data/lib/mtk/constants/dynamics.rb +0 -56
  84. data/lib/mtk/constants/pitch_classes.rb +0 -18
  85. data/lib/mtk/constants/pitches.rb +0 -24
  86. data/lib/mtk/pattern/note_sequence.rb +0 -60
  87. data/lib/mtk/pattern/pitch_sequence.rb +0 -22
  88. data/lib/mtk/patterns.rb +0 -4
  89. data/spec/mtk/chord_spec.rb +0 -74
  90. data/spec/mtk/constants/pitch_classes_spec.rb +0 -35
  91. data/spec/mtk/constants/pitches_spec.rb +0 -23
  92. data/spec/mtk/pattern/note_sequence_spec.rb +0 -121
  93. data/spec/mtk/pattern/pitch_sequence_spec.rb +0 -47
data/.yardopts ADDED
@@ -0,0 +1,9 @@
1
+ --readme README.md
2
+ --title 'MTK (Music ToolKit for Ruby)'
3
+ --charset utf-8
4
+ --protected
5
+ lib/**/*.rb
6
+ - *.md
7
+ - examples/*
8
+ - lib/**/*.citrus
9
+
data/INTRO.md ADDED
@@ -0,0 +1,73 @@
1
+ (Work in progress)
2
+
3
+ Core Concepts
4
+ -------------
5
+
6
+ ### Core data types
7
+
8
+ These model the basic properties of musical events:
9
+ * pitch_class (MTK class)
10
+ * pitch (MTK class)
11
+ * intensity (Numeric)
12
+ * duration (Numeric)
13
+
14
+ Like Numeric types, PitchClass and Pitch are immutable. This helps avoid confusing bugs.
15
+ Mostly you don't need to worry about this. Just remember when you call methods that change the value, like #invert,
16
+ it does not change the value in-place. For example:
17
+
18
+ p = PitchClass[G]
19
+ p = p.invert(E) # because p.invert(E) does not change p
20
+
21
+ For efficiency and convenience, intensity and duration are modeled using Ruby's Numeric types (Fixnum, Float, etc).
22
+
23
+ Intensity is intended to range from 0.0 (minimum intensity) to 1.0 (maximum intensity).
24
+
25
+ A Duration of 1 is one beat (usually a quarter note, depending on the meter). By convention, negative durations
26
+ indicate a rest that lasts for the absolute value duration.
27
+
28
+
29
+ <br/>
30
+ ### Events
31
+
32
+ Events group together the core data types together to represent a musical event, like a single Note.
33
+ Events can be converted to and from MIDI.
34
+
35
+ * event
36
+ * note
37
+
38
+
39
+ <br/>
40
+ ### Core collections
41
+
42
+ A collection of timed events, such as a melody, a riff, or one track of an entire song.
43
+
44
+ * timeline
45
+
46
+
47
+ <br/>
48
+ ### Patterns
49
+
50
+ Structured collections of core data types and other patterns. Used to generate musical material.
51
+
52
+ * cycle
53
+ * palindrome
54
+ * choice (randomly select from weghted distribution)
55
+ * line (linear interpolation between 2 points)
56
+ * function (dynamically generate elements with a lambda)
57
+
58
+ Future?
59
+ * curve (exponential/curved interpolation between points)
60
+ * permutation (cycle that permutes itself each cycle period)
61
+ * markov chain
62
+ * graph
63
+ * petri net (special type of graph??)
64
+ * custom (just implement enumerator pattern?)
65
+
66
+
67
+ <br/>
68
+ ### Sequencers
69
+
70
+ Convert patterns into timelines
71
+
72
+
73
+
data/LICENSE.txt ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2011, Adam Murray (adam@compusition.com).
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use of the "MTK" Ruby library, in source and binary forms,
6
+ with or without modification, are permitted provided that the following
7
+ conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in
14
+ the documentation and/or other materials provided with the
15
+ distribution.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md CHANGED
@@ -1,52 +1,127 @@
1
- # MTK
2
- ## Music ToolKit for Ruby
1
+ MTK
2
+ ===
3
3
 
4
- Classes for modeling music with a focus on simplicity. Support for reading/writing MIDI files (and soon, realtime MIDI).
4
+ Music ToolKit for Ruby
5
+ ----------------------
5
6
 
7
+ Classes for modeling music with a focus on simplicity. Support for reading/writing MIDI files and realtime MIDI.
6
8
 
7
9
 
8
- ## Goals
10
+
11
+ Getting Started
12
+ ---------------
13
+
14
+ gem install mtk
15
+
16
+ or download the source from here and add mtk/lib to your $LOAD_PATH. Then...
17
+
18
+ require 'mtk'
19
+
20
+ Some examples are available in the examples folder (more coming soon).
21
+ The specs provide a lot of details of usage...
22
+
23
+
24
+
25
+ Goals
26
+ -----
9
27
 
10
28
  * Build musical generators to assist with composing music
11
29
  * Re-implement Cosy (http://compusition.com/web/software/cosy) using these models as the "backend"
12
30
 
13
31
 
14
32
 
15
- ## Status
33
+ Status
34
+ ------
16
35
 
17
- Pre-alpha, API subject to change. Feedback welcome!
36
+ Alpha phase, API subject to change. Feedback welcome!
18
37
 
19
38
 
20
39
 
21
- ## Requirements
40
+ Requirements
41
+ ------------
42
+
22
43
  ### Ruby Version
23
44
 
24
- Ruby 1.8 or 1.9
45
+ Ruby 1.8+ or JRuby 1.5+
46
+
47
+
48
+ ### Dependencies
49
+
50
+ MTK's core features should not depend on anything outside of the Ruby standard library.
25
51
 
26
- ### Gem Dependencies
52
+ MTK's optional features typically require gems. Currently the following gems are required:
27
53
 
28
- * rake (tests & docs)
54
+ * midilib: required by MTK::MIDI::File for file I/O
55
+
56
+ * jsound and gamelan: required by MTK::MIDI::JSoundInput/Output (also requires JRuby)
57
+
58
+ Development requires all the gems for optional features, plus the following development tools:
59
+
60
+ * rake
29
61
  * rspec (tests)
30
62
  * yard (docs)
31
63
  * yard-rspec (docs)
32
64
  * rdiscount (docs)
33
- * midilib (MIDI file I/O -- not strictly required by core lib, but currently needed for tests)
34
65
 
66
+ [rvm](https://rvm.beginrescueend.com/) is recommended for cross version testing (see Development Notes below)
67
+
68
+
69
+
70
+ Documentation
71
+ -------------
72
+
73
+ Gem: http://rdoc.info/gems/mtk/0.0.2/frames
74
+
75
+ Latest for source: http://rubydoc.info/github/adamjmurray/mtk/master/frames
76
+
77
+
78
+
79
+ Development Notes
80
+ -----------------
81
+
82
+ ### Run Tests ###
83
+
84
+ Test with current version of Ruby:
85
+
86
+ rake spec
87
+
88
+ Test with all supported versions of Ruby (requires [rvm](https://rvm.beginrescueend.com/), MRI 1.8.7, YARV 1.9.2, JRuby 1.5.6, and JRuby 1.6.2):
89
+
90
+ rake spec:xversion
91
+
92
+ The spec:xversion test must pass for a pull request to be accepted or for a release of the mtk gem.
93
+
94
+
95
+ ### Generate Docs ###
35
96
 
97
+ yard
98
+ open doc/frames.html
36
99
 
37
- ## Documentation
100
+ or, to automatically refresh the documentation as you work:
38
101
 
39
- rake yard
102
+ yard server -r
103
+ open http://localhost:8808
40
104
 
41
- then open doc/frames.html
42
105
 
106
+ ### Project Roadmap ###
43
107
 
108
+ https://www.pivotaltracker.com/projects/295419
44
109
 
45
- ## Tests
46
110
 
47
- rake spec
48
111
 
49
- I test with MRI 1.8.7, MRI 1.9.2, JRuby 1.5.6, and JRuby 1.6.1 on OS X via rvm:
112
+ Changelog
113
+ ---------
50
114
 
51
- rvm 1.8.7,1.9.2,jruby-1.5.6,jruby-1.6.1 rake spec:fast
115
+ * July 8, 2011: version 0.0.2
116
+ - Added a Sequencer module to build Timelines out of Patterns
117
+ - Overhauled Pattern module: removed type-specific patterns, and added the Palindrome, Lines, and Function patterns
118
+ - Patterns can now be nested (they can contain other Patterns)
119
+ - Patterns can now be typed, to distinguish Numeric Patterns as :pitch (i.e. intervals), :intensity, :duration, or :rhythm patterns
120
+ - Removed auto-sorting behavior from PitchClassSet to support 12-tone rows and atonal composition techniques
121
+ - Added #quantize and #shift features to Timeline
122
+ - Got rid of Chord class, model Chords with PitchSets or Arrays of Notes instead
123
+ - Added support for realtime MIDI I/O with JSound (JRuby only)
124
+ - various cleanup and reorganization
52
125
 
126
+ * June 8, 2011: version 0.0.1
127
+ - First gem release.
data/Rakefile CHANGED
@@ -7,17 +7,29 @@ task :default => :spec
7
7
  CLEAN.include('html','doc') # clean and clobber do the same thing for now
8
8
 
9
9
  desc "Run RSpec tests with full output"
10
- RSpec::Core::RakeTask.new do |spec|
10
+ RSpec::Core::RakeTask.new do |spec|
11
11
  spec.rspec_opts = ["--color", "--format", "nested"]
12
+ if ARGV[1]
13
+ # only run specs with filenames starting with the command line argument
14
+ spec.pattern = "spec/**/#{ARGV[1]}*"
15
+ end
12
16
  end
13
17
 
14
18
  namespace :spec do
19
+
15
20
  desc "Run RSpecs tests with summary output and fast failure"
16
21
  RSpec::Core::RakeTask.new(:fast) do |spec|
17
22
  spec.rspec_opts = ["--color", "--fail-fast"]
18
23
  end
24
+
25
+ desc "Run RSpecs tests on mutiple versions of Ruby: 1.8.7, 1.9.2, JRuby 1.5.6, and JRuby 1.6.2"
26
+ task :xversion do
27
+ fail unless system("rvm 1.8.7,1.9.2,jruby-1.5.6,jruby-1.6.2 rake -f #{__FILE__} spec:fast")
28
+ end
29
+
19
30
  end
20
31
 
32
+
21
33
  YARD::Rake::YardocTask.new do |yard|
22
34
  yard.files = ['lib/**/*.rb', 'spec/**/*.rb']
23
35
  yard.options = []
@@ -0,0 +1,20 @@
1
+ # Generate a MIDI file of the C-major scale with a crescendo (it increases in intensity)
2
+ #
3
+ # NOTE: this blindly overwrites any existing MTK-crescendo.mid file, unless an argument is provided
4
+
5
+ require 'mtk'
6
+ require 'mtk/midi/file'
7
+ include MTK
8
+ include Pitches
9
+ include Intensities
10
+
11
+ file = ARGV[0] || 'MTK-crescendo.mid'
12
+
13
+ scale = Pattern::PitchSequence C4,D4,E4,F4,G4,A4,B4,C5
14
+ crescendo = Pattern::IntensityLines pp, [fff, scale.length-1] # step from pp to fff over the length of the scale
15
+
16
+ sequencer = Sequencer::StepSequencer.new [scale, crescendo]
17
+ timeline = sequencer.to_timeline
18
+
19
+ MIDI_File(file).write timeline
20
+
@@ -0,0 +1,39 @@
1
+ # Generate a MIDI file using a lambda to dynamically generate the pitches
2
+ #
3
+ # NOTE: this blindly overwrites any existing MTK-dynamic_pattern.mid file, unless an argument is provided
4
+
5
+ require 'mtk'
6
+ require 'mtk/midi/file'
7
+ include MTK
8
+ include Pitches
9
+ include Intensities
10
+ include Intervals
11
+
12
+ file = ARGV[0] || "MTK-#{File.basename(__FILE__,'.rb')}.mid"
13
+
14
+ interval_generator = lambda do
15
+ # Randomly return intervals
16
+ r = rand
17
+ case
18
+ when r < 0.1 then m2
19
+ when r < 0.4 then M2
20
+ when r < 0.5 then m3
21
+ when r < 0.6 then M3
22
+ when r < 0.7 then P4
23
+ when r < 0.8 then -M3
24
+ when r < 0.95 then -P5
25
+ else -P8
26
+ end
27
+ end
28
+
29
+ pitches = Pattern::Function.new interval_generator, :type => :pitch, :max_elements => 24
30
+
31
+ # we'll also use a weighted choice to generate the intensities
32
+ intensities = Pattern::Choice.new [mp, mf, f, ff, fff], :type => :intensity, :weights => [1,2,3,2,1]
33
+
34
+
35
+ sequencer = Sequencer::StepSequencer.new [pitches, intensities], :step_size => 0.5
36
+ timeline = sequencer.to_timeline
37
+
38
+ MIDI_File(file).write timeline
39
+
@@ -0,0 +1,19 @@
1
+ # Play a given MIDI file with the given MIDI output
2
+ #
3
+ # NOTE: this example only runs under JRuby with the jsound and gamelan gems installed
4
+
5
+ file, output = ARGV[0], ARGV[1]
6
+ unless file and output
7
+ puts "Usage: ruby #$0 midi_file output_device"
8
+ exit 1
9
+ end
10
+
11
+ require 'mtk'
12
+ require 'mtk/midi/file'
13
+ require 'mtk/midi/jsound_output'
14
+ include MTK
15
+
16
+ timeline = MIDI_File(file).to_timelines.first # for now this example just plays the first track (JSoundOutput needs to be enhanced to support multiple timelines)
17
+ player = MTK::MIDI::JSoundOutput.new output
18
+
19
+ player.play timeline
@@ -0,0 +1,13 @@
1
+ # Print the notes in a MIDI file
2
+
3
+ file = ARGV[0]
4
+ unless file
5
+ puts "Usage: ruby #$0 midi_file"
6
+ exit 1
7
+ end
8
+
9
+ require 'mtk'
10
+ require 'mtk/midi/file'
11
+ include MTK
12
+
13
+ puts MIDI_File(file).to_timelines
@@ -0,0 +1,18 @@
1
+ # Generate a MIDI file of a random 12-tone row
2
+ #
3
+ # NOTE: this blindly overwrites any existing MTK-random_tone_row.mid file, unless an argument is provided
4
+
5
+ require 'mtk'
6
+ require 'mtk/midi/file'
7
+ include MTK
8
+
9
+ file = ARGV[0] || 'MTK-random_tone_row.mid'
10
+
11
+ row = PitchClassSet.random_row
12
+ sequence = Pattern::PitchSequence *row.to_a
13
+
14
+ sequencer = Sequencer::StepSequencer.new [sequence]
15
+ timeline = sequencer.to_timeline
16
+
17
+ MIDI_File(file).write timeline
18
+
@@ -0,0 +1,21 @@
1
+ # Generate a MIDI file using a tone row, repeated 3 times using random rhythms
2
+ #
3
+ # NOTE: this blindly overwrites any existing MTK-tone_row_melody.mid file, unless an argument is provided
4
+
5
+ require 'mtk'
6
+ require 'mtk/midi/file'
7
+ include MTK
8
+ include PitchClasses
9
+ include Durations
10
+
11
+ file = ARGV[0] || 'MTK-tone_row_melody.mid'
12
+
13
+ row = PitchClassSet(Db, G, Ab, F, Eb, E, D, C, B, Gb, A, Bb)
14
+ pitch_pattern = Pattern.PitchCycle *row
15
+ rhythm_pattern = Pattern.RhythmCycle(Pattern.Choice s, e, e+s, q) # choose between sixteenth, eighth, dotted eighth, and quarter
16
+
17
+ sequencer = Sequencer::RhythmicSequencer.new [pitch_pattern, rhythm_pattern], :max_steps => 36
18
+ timeline = sequencer.to_timeline
19
+
20
+ MIDI_File(file).write timeline
21
+
@@ -0,0 +1,80 @@
1
+ require 'rational'
2
+
3
+ module MTK
4
+
5
+ # Defines duration constants using abbreviations for standard rhythm values ('w' for whole note, 'h' for half note, etc).
6
+ #
7
+ # These can be thought of like constants, but in order to distinguish 'e' (eighth note) from the {PitchClass} 'E'
8
+ # it was necessary to use lower-case names and therefore define them as "pseudo constant" methods.
9
+ # The methods are available either through the module (MTK::Durations::e) or via mixin (include MTK::Durations; e)
10
+ #
11
+ # These values assume the quarter note is one beat (1.0), so they work best with 4/4 and other */4 time signatures.
12
+ #
13
+ # @note Including this module defines a bunch of single-character variables, which may shadow existing variable names.
14
+ # Just be mindful of what is defined in this module when including it.
15
+ #
16
+ # @see Note
17
+ module Durations
18
+ extend Helper::PseudoConstants
19
+
20
+ # NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
21
+
22
+ # whole note
23
+ # @macro [attach] durations.define_constant
24
+ # @attribute [r]
25
+ # @return [$2] number of beats for $1
26
+ define_constant 'w', 4
27
+
28
+ # half note
29
+ define_constant 'h', 2
30
+
31
+ # quarter note
32
+ define_constant 'q', 1
33
+
34
+ # eight note
35
+ define_constant 'e', Rational(1,2)
36
+
37
+ # sixteenth note
38
+ define_constant 's', Rational(1,4)
39
+
40
+ # thirty-second note
41
+ define_constant 'r', Rational(1,8)
42
+
43
+ # sixty-fourth note
44
+ define_constant 'x', Rational(1,16)
45
+
46
+ # The values of all "psuedo constants" defined in this module
47
+ DURATIONS = [w, h, q, e, s, r, x].freeze
48
+
49
+ # The names of all "psuedo constants" defined in this module
50
+ DURATION_NAMES = %w[w h q e s r x].freeze
51
+
52
+ # Lookup the value of an duration constant by name.
53
+ # This method supports appending any combination of '.' and 't' for more fine-grained values.
54
+ # each '.' multiplies by 3/2, and each 't' multiplies by 2/3.
55
+ # @example lookup value of 'e.' (eight note), which is 0.75 (0.5 * 1.5)
56
+ # MTK::Durations['e.']
57
+ def self.[](name)
58
+ begin
59
+ modifier = nil
60
+ if name =~ /(\w)((.|t)*)/
61
+ name = $1
62
+ modifier = $2
63
+ end
64
+
65
+ value = send name
66
+ modifier.each_char do |mod|
67
+ case mod
68
+ when '.' then value *= Rational(3,2)
69
+ when 't' then value *= Rational(2,3)
70
+ end
71
+ end
72
+ value
73
+
74
+ rescue
75
+ nil
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,81 @@
1
+ module MTK
2
+
3
+ # Defines intensity constants using standard dynamic symbols.
4
+ #
5
+ # These can be thought of like constants, but in order to distinguish 'f' (forte) from the {PitchClass} 'F'
6
+ # it was necessary to use lower-case names and therefore define them as "pseudo constant" methods.
7
+ # The methods are available either through the module (MTK::Intensities::f) or via mixin (include MTK::Intensities; f)
8
+ #
9
+ # These values are intensities in the range 0.125 - 1.0 (in increments of 1/8), so they can be easily scaled (unlike MIDI velocities).
10
+ #
11
+ # It is also possible to retrieve values in increments of 1/24 by using the '+' and '-' suffix when looking
12
+ # up values via the {.[]} method.
13
+ #
14
+ # @note Including this module shadows Ruby's built-in p() method.
15
+ # If you include this module, you can access the built-in p() method via Kernel.p()
16
+ #
17
+ # @see Note
18
+ module Intensities
19
+ extend Helper::PseudoConstants
20
+
21
+ # NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
22
+
23
+ # pianississimo
24
+ # @macro [attach] intensities.define_constant
25
+ # @attribute [r]
26
+ # @return [$2] intensity value for $1
27
+ define_constant 'ppp', 0.125
28
+
29
+ # pianissimo
30
+ define_constant 'pp', 0.25
31
+
32
+ # piano
33
+ # @note Including this module shadows Ruby's built-in p() method.
34
+ # If you include this module, you can access the built-in p() method via Kernel.p()
35
+ define_constant 'p', 0.375
36
+
37
+ # mezzo-piano
38
+ define_constant 'mp', 0.5
39
+
40
+ # mezzo-forte
41
+ define_constant 'mf', 0.625
42
+
43
+ # forte
44
+ define_constant 'f', 0.75
45
+
46
+ # fortissimo
47
+ define_constant 'ff', 0.875
48
+
49
+ # fortississimo
50
+ define_constant 'fff', 1.0
51
+
52
+ # The values of all "psuedo constants" defined in this module
53
+ INTENSITIES = [ppp, pp, p, mp, mf, f, ff, fff].freeze
54
+
55
+ # The names of all "psuedo constants" defined in this module
56
+ INTENSITY_NAMES = %w[ppp pp p mp mf f ff fff].freeze
57
+
58
+ # Lookup the value of an intensity constant by name.
59
+ # This method supports appending '+' or '-' for more fine-grained values.
60
+ # '+' and '-' add and subtract 1/24, respectively (enforcing the upper bound of 1.0 for 'fff+').
61
+ # @example lookup value of 'mp+', which is 0.5416666666666666 (0.5 + 1/24.0)
62
+ # MTK::Intensities['mp+']
63
+ def self.[](name)
64
+ return 1.0 if name == "fff+" # special case because "fff" is already the maximum
65
+
66
+ modifier = nil
67
+ if name =~ /(\w+)([+-])/
68
+ name = $1
69
+ modifier = $2
70
+ end
71
+
72
+ value = send name
73
+ value += 1.0/24 if modifier == '+'
74
+ value -= 1.0/24 if modifier == '-'
75
+ value
76
+ rescue
77
+ nil
78
+ end
79
+
80
+ end
81
+ end
@@ -12,7 +12,7 @@ module MTK
12
12
  # it was necessary to use lower-case names for some of the values and therefore define them as "pseudo constant" methods.
13
13
  # The methods are available either through the module (MTK::Intervals::m2) or via mixin (include MTK::Intervals; m2)
14
14
  module Intervals
15
- extend PseudoConstants
15
+ extend Helper::PseudoConstants
16
16
 
17
17
  # NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
18
18
 
@@ -61,6 +61,15 @@ module MTK
61
61
  # pefect octave
62
62
  define_constant 'P8', 12
63
63
 
64
+ # The values of all "psuedo constants" defined in this module
65
+ INTERVALS = [P1, m2, M2, m3, M3, P4, TT, P5, m6, M6, m7, M7, P8].freeze
66
+
67
+ # The names of all "psuedo constants" defined in this module
68
+ INTERVAL_NAMES = %w[P1 m2 M2 m3 M3 P4 TT P5 m6 M6 m7 M7 P8].freeze
69
+
70
+ # Lookup the value of an interval constant by name.
71
+ # @example lookup value of 'M3', which is 4
72
+ # MTK::Intervals['M3']
64
73
  def self.[](name)
65
74
  send name
66
75
  rescue
@@ -0,0 +1,35 @@
1
+ module MTK
2
+
3
+ # Defines a constant for each {PitchClass} in the Western chromatic scale.
4
+
5
+ module PitchClasses
6
+
7
+ # The values of all "psuedo constants" defined in this module
8
+ PITCH_CLASSES = []
9
+
10
+ # The names of all "psuedo constants" defined in this module
11
+ PITCH_CLASS_NAMES = PitchClass::NAMES
12
+
13
+ for name in PITCH_CLASS_NAMES
14
+ pc = PitchClass[name]
15
+ PITCH_CLASSES << pc
16
+ const_set name, pc
17
+ end
18
+
19
+ PITCH_CLASSES.freeze
20
+
21
+ # Lookup the value of an pitch class constant by name.
22
+ # @example lookup value of 'C'
23
+ # MTK::PitchClasses['C']
24
+ # @see PitchClass.[]
25
+ def self.[](name)
26
+ begin
27
+ const_get name
28
+ rescue
29
+ nil
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end