mtk 0.0.1 → 0.0.2

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.
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