mtk 0.0.3.2 → 0.0.3.3

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 (102) hide show
  1. data/.yardopts +2 -2
  2. data/DEVELOPMENT_NOTES.md +20 -0
  3. data/README.md +9 -3
  4. data/Rakefile +47 -13
  5. data/bin/mtk +55 -20
  6. data/examples/crescendo.rb +4 -4
  7. data/examples/{drum_pattern1.rb → drum_pattern.rb} +8 -8
  8. data/examples/dynamic_pattern.rb +5 -5
  9. data/examples/gets_and_play.rb +3 -2
  10. data/examples/notation.rb +3 -3
  11. data/examples/play_midi.rb +4 -4
  12. data/examples/print_midi.rb +2 -2
  13. data/examples/random_tone_row.rb +3 -3
  14. data/examples/syntax_to_midi.rb +2 -2
  15. data/examples/test_output.rb +4 -5
  16. data/examples/tone_row_melody.rb +7 -5
  17. data/lib/mtk/core/duration.rb +213 -0
  18. data/lib/mtk/core/intensity.rb +158 -0
  19. data/lib/mtk/core/interval.rb +157 -0
  20. data/lib/mtk/core/pitch.rb +154 -0
  21. data/lib/mtk/core/pitch_class.rb +194 -0
  22. data/lib/mtk/events/event.rb +4 -4
  23. data/lib/mtk/events/note.rb +12 -12
  24. data/lib/mtk/events/timeline.rb +232 -0
  25. data/lib/mtk/groups/chord.rb +56 -0
  26. data/lib/mtk/{helpers → groups}/collection.rb +33 -1
  27. data/lib/mtk/groups/melody.rb +96 -0
  28. data/lib/mtk/groups/pitch_class_set.rb +163 -0
  29. data/lib/mtk/{helpers → groups}/pitch_collection.rb +1 -1
  30. data/lib/mtk/{midi → io}/dls_synth_device.rb +3 -1
  31. data/lib/mtk/{midi → io}/dls_synth_output.rb +10 -10
  32. data/lib/mtk/{midi → io}/jsound_input.rb +2 -2
  33. data/lib/mtk/{midi → io}/jsound_output.rb +9 -9
  34. data/lib/mtk/{midi/file.rb → io/midi_file.rb} +13 -13
  35. data/lib/mtk/{midi/input.rb → io/midi_input.rb} +4 -4
  36. data/lib/mtk/{midi/output.rb → io/midi_output.rb} +8 -8
  37. data/lib/mtk/{helpers/lilypond.rb → io/notation.rb} +5 -5
  38. data/lib/mtk/{midi → io}/unimidi_input.rb +2 -2
  39. data/lib/mtk/{midi → io}/unimidi_output.rb +14 -9
  40. data/lib/mtk/{constants → lang}/durations.rb +11 -11
  41. data/lib/mtk/{constants → lang}/intensities.rb +11 -11
  42. data/lib/mtk/{constants → lang}/intervals.rb +17 -17
  43. data/lib/mtk/lang/mtk_grammar.citrus +9 -9
  44. data/lib/mtk/{constants → lang}/pitch_classes.rb +5 -5
  45. data/lib/mtk/{constants → lang}/pitches.rb +7 -7
  46. data/lib/mtk/{helpers → lang}/pseudo_constants.rb +1 -1
  47. data/lib/mtk/{variable.rb → lang/variable.rb} +1 -1
  48. data/lib/mtk/numeric_extensions.rb +40 -47
  49. data/lib/mtk/patterns/for_each.rb +1 -1
  50. data/lib/mtk/patterns/pattern.rb +3 -3
  51. data/lib/mtk/sequencers/event_builder.rb +16 -15
  52. data/lib/mtk/sequencers/legato_sequencer.rb +1 -1
  53. data/lib/mtk/sequencers/rhythmic_sequencer.rb +1 -1
  54. data/lib/mtk/sequencers/sequencer.rb +8 -8
  55. data/lib/mtk/sequencers/step_sequencer.rb +2 -2
  56. data/lib/mtk.rb +33 -39
  57. data/spec/mtk/{duration_spec.rb → core/duration_spec.rb} +3 -3
  58. data/spec/mtk/{intensity_spec.rb → core/intensity_spec.rb} +3 -3
  59. data/spec/mtk/{interval_spec.rb → core/interval_spec.rb} +1 -1
  60. data/spec/mtk/{pitch_class_spec.rb → core/pitch_class_spec.rb} +1 -1
  61. data/spec/mtk/{pitch_spec.rb → core/pitch_spec.rb} +8 -8
  62. data/spec/mtk/events/event_spec.rb +4 -4
  63. data/spec/mtk/events/note_spec.rb +8 -8
  64. data/spec/mtk/{timeline_spec.rb → events/timeline_spec.rb} +47 -47
  65. data/spec/mtk/{chord_spec.rb → groups/chord_spec.rb} +18 -16
  66. data/spec/mtk/{helpers → groups}/collection_spec.rb +3 -3
  67. data/spec/mtk/{melody_spec.rb → groups/melody_spec.rb} +36 -34
  68. data/spec/mtk/{pitch_class_set_spec.rb → groups/pitch_class_set_spec.rb} +57 -55
  69. data/spec/mtk/{midi/file_spec.rb → io/midi_file_spec.rb} +17 -17
  70. data/spec/mtk/{midi/output_spec.rb → io/midi_output_spec.rb} +6 -6
  71. data/spec/mtk/{constants → lang}/durations_spec.rb +1 -1
  72. data/spec/mtk/{constants → lang}/intensities_spec.rb +1 -1
  73. data/spec/mtk/{constants → lang}/intervals_spec.rb +1 -1
  74. data/spec/mtk/lang/parser_spec.rb +12 -6
  75. data/spec/mtk/{constants → lang}/pitch_classes_spec.rb +1 -1
  76. data/spec/mtk/{constants → lang}/pitches_spec.rb +1 -1
  77. data/spec/mtk/{helpers → lang}/pseudo_constants_spec.rb +2 -2
  78. data/spec/mtk/{variable_spec.rb → lang/variable_spec.rb} +4 -4
  79. data/spec/mtk/numeric_extensions_spec.rb +35 -55
  80. data/spec/mtk/patterns/for_each_spec.rb +1 -1
  81. data/spec/mtk/patterns/sequence_spec.rb +1 -1
  82. data/spec/mtk/sequencers/legato_sequencer_spec.rb +2 -2
  83. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +4 -4
  84. data/spec/mtk/sequencers/step_sequencer_spec.rb +5 -5
  85. data/spec/spec_helper.rb +7 -6
  86. metadata +75 -61
  87. data/ext/mkrf_conf.rb +0 -25
  88. data/lib/mtk/chord.rb +0 -55
  89. data/lib/mtk/duration.rb +0 -211
  90. data/lib/mtk/helpers/convert.rb +0 -36
  91. data/lib/mtk/helpers/output_selector.rb +0 -67
  92. data/lib/mtk/intensity.rb +0 -156
  93. data/lib/mtk/interval.rb +0 -155
  94. data/lib/mtk/melody.rb +0 -94
  95. data/lib/mtk/pitch.rb +0 -152
  96. data/lib/mtk/pitch_class.rb +0 -192
  97. data/lib/mtk/pitch_class_set.rb +0 -161
  98. data/lib/mtk/timeline.rb +0 -230
  99. data/spec/mtk/midi/jsound_input_spec.rb +0 -11
  100. data/spec/mtk/midi/jsound_output_spec.rb +0 -11
  101. data/spec/mtk/midi/unimidi_input_spec.rb +0 -11
  102. data/spec/mtk/midi/unimidi_output_spec.rb +0 -11
data/lib/mtk/intensity.rb DELETED
@@ -1,156 +0,0 @@
1
- module MTK
2
-
3
- # A measure of intensity, using an underlying value in the range 0.0-1.0
4
- class Intensity
5
-
6
- include Comparable
7
-
8
- # The names of the base intensities. See {}MTK::Constants::Intensities} for more info.
9
- NAMES = %w[ppp pp p mp mf o ff fff].freeze
10
-
11
- VALUES_BY_NAME = {
12
- 'ppp' => 0.125,
13
- 'pp' => 0.25,
14
- 'p' => 0.375,
15
- 'mp' => 0.5,
16
- 'mf' => 0.625,
17
- 'o' => 0.75,
18
- 'ff' => 0.875,
19
- 'fff' => 1.0
20
- }
21
-
22
- @flyweight = {}
23
-
24
- # The number of beats, typically representation as a Rational
25
- attr_reader :value
26
-
27
- def initialize(value)
28
- @value = value
29
- end
30
-
31
- # Return an Intensity, only constructing a new instance when not already in the flyweight cache
32
- def self.[](value)
33
- value = value.to_f
34
- @flyweight[value] ||= new(value)
35
- end
36
-
37
- class << self
38
- alias :from_f :[]
39
- alias :from_i :[]
40
- end
41
-
42
- # Lookup an intensity by name.
43
- # This method supports appending '-' or '+' for more fine-grained values.
44
- def self.from_s(s)
45
- return self[1.0] if s == 'fff+' # special case because "fff" is already the maximum
46
-
47
- name = nil
48
- modifier = nil
49
- if s =~ /^(\w+)([+-])?$/
50
- name = $1
51
- modifier = $2
52
- end
53
-
54
- value = VALUES_BY_NAME[name]
55
- raise ArgumentError.new("Invalid Intensity string '#{s}'") unless value
56
-
57
- value += 1.0/24 if modifier == '+'
58
- value -= 1.0/24 if modifier == '-'
59
-
60
- self[value]
61
- end
62
-
63
- class << self
64
- alias :from_name :from_s
65
- end
66
-
67
- # The number of beats as a floating point number
68
- def to_f
69
- @value.to_f
70
- end
71
-
72
- # The numerical value for the nearest whole number of beats
73
- def to_i
74
- @value.round
75
- end
76
-
77
- def to_midi
78
- (to_f * 127).round
79
- end
80
-
81
- def to_percent
82
- (@value * 100).round
83
- end
84
-
85
- def to_s
86
- "#{to_percent}% intensity"
87
- end
88
-
89
- def inspect
90
- "#<#{self.class}:#{object_id} @value=#{@value}>"
91
- end
92
-
93
- def ==( other )
94
- other.is_a? MTK::Intensity and other.value == @value
95
- end
96
-
97
- def <=> other
98
- if other.respond_to? :value
99
- @value <=> other.value
100
- else
101
- @value <=> other
102
- end
103
-
104
- end
105
-
106
- def + intensity
107
- if intensity.is_a? MTK::Intensity
108
- MTK::Intensity[@value + intensity.value]
109
- else
110
- MTK::Intensity[@value + intensity]
111
- end
112
- end
113
-
114
- def -intensity
115
- if intensity.is_a? MTK::Intensity
116
- MTK::Intensity[@value - intensity.value]
117
- else
118
- MTK::Intensity[@value - intensity]
119
- end
120
- end
121
-
122
- def * intensity
123
- if intensity.is_a? MTK::Intensity
124
- MTK::Intensity[@value * intensity.value]
125
- else
126
- MTK::Intensity[@value * intensity]
127
- end
128
- end
129
-
130
- def / intensity
131
- if intensity.is_a? MTK::Intensity
132
- MTK::Intensity[to_f / intensity.value]
133
- else
134
- MTK::Intensity[to_f / intensity]
135
- end
136
- end
137
-
138
- def coerce(other)
139
- return MTK::Intensity[other], self
140
- end
141
-
142
- end
143
-
144
- # Construct a {Duration} from any supported type
145
- def Intensity(*anything)
146
- anything = anything.first if anything.length == 1
147
- case anything
148
- when Numeric then MTK::Intensity[anything]
149
- when String, Symbol then MTK::Intensity.from_s(anything)
150
- when Intensity then anything
151
- else raise "Intensity doesn't understand #{anything.class}"
152
- end
153
- end
154
- module_function :Intensity
155
-
156
- end
data/lib/mtk/interval.rb DELETED
@@ -1,155 +0,0 @@
1
- module MTK
2
-
3
- # A measure of intensity, using an underlying value in the range 0.0-1.0
4
- class Interval
5
-
6
- include Comparable
7
-
8
- # The preferred names of all pre-defined intervals
9
- NAMES = %w[P1 m2 M2 m3 M3 P4 TT P5 m6 M6 m7 M7 P8].freeze
10
-
11
- # All valid names of pre-defined intervals, indexed by their value.
12
- NAMES_BY_VALUE =
13
- [ # names # value # description
14
- %w( P1 p1 ), # 0 # unison
15
- %w( m2 min2 ), # 1 # minor second
16
- %w( M2 maj2 ), # 2 # major second
17
- %w( m3 min3 ), # 3 # minor third
18
- %w( M3 maj3 ), # 4 # major third
19
- %w( P4 p4 ), # 5 # perfect fourth
20
- %w( TT tt ), # 6 # tritone (AKA augmented fourth, diminished fifth)
21
- %w( P5 p5 ), # 7 # perfect fifth
22
- %w( m6 min6 ), # 8 # minor sixth
23
- %w( M6 maj6 ), # 9 # major sixth
24
- %w( m7 min7 ), # 10 # minor seventh
25
- %w( M7 maj7 ), # 11 # major seventh
26
- %w( P8 p8 ) # 12 # octave
27
- ].freeze
28
-
29
- # A mapping from intervals names to their value
30
- VALUES_BY_NAME = Hash[ # a map from a list of name,value pairs
31
- NAMES_BY_VALUE.map.with_index do |names,value|
32
- names.map{|name| [name,value] }
33
- end.flatten(1)
34
- ].freeze
35
-
36
- # All valid interval names
37
- ALL_NAMES = NAMES_BY_VALUE.flatten.freeze
38
-
39
-
40
- @flyweight = {}
41
-
42
- # The number of semitones represented by this interval
43
- attr_reader :value
44
-
45
- def initialize(value)
46
- @value = value
47
- end
48
-
49
- # Return an {Interval}, only constructing a new instance when not already in the flyweight cache
50
- def self.[](value)
51
- value = value.to_f unless value.is_a? Fixnum
52
- @flyweight[value] ||= new(value)
53
- end
54
-
55
- class << self
56
- alias :from_f :[]
57
- alias :from_i :[]
58
- end
59
-
60
- # Lookup an interval duration by name.
61
- def self.from_s(s)
62
- value = VALUES_BY_NAME[s.to_s]
63
- raise ArgumentError.new("Invalid Interval string '#{s}'") unless value
64
- self[value]
65
- end
66
-
67
- class << self
68
- alias :from_name :from_s
69
- end
70
-
71
- # The number of semitones as a floating point number
72
- def to_f
73
- @value.to_f
74
- end
75
-
76
- # The numerical value for the nearest whole number of semitones in this interval
77
- def to_i
78
- @value.round
79
- end
80
-
81
- def to_s
82
- @value.to_s
83
- end
84
-
85
- def inspect
86
- "#{self.class}<#{to_s} semitones>"
87
- end
88
-
89
- def ==( other )
90
- other.is_a? MTK::Interval and other.value == @value
91
- end
92
-
93
- def <=> other
94
- if other.respond_to? :value
95
- @value <=> other.value
96
- else
97
- @value <=> other
98
- end
99
- end
100
-
101
- def + interval
102
- if interval.is_a? MTK::Interval
103
- MTK::Interval[@value + interval.value]
104
- else
105
- MTK::Interval[@value + interval]
106
- end
107
- end
108
-
109
- def -interval
110
- if interval.is_a? MTK::Interval
111
- MTK::Interval[@value - interval.value]
112
- else
113
- MTK::Interval[@value - interval]
114
- end
115
- end
116
-
117
- def * interval
118
- if interval.is_a? MTK::Interval
119
- MTK::Interval[@value * interval.value]
120
- else
121
- MTK::Interval[@value * interval]
122
- end
123
- end
124
-
125
- def / interval
126
- if interval.is_a? MTK::Interval
127
- MTK::Interval[to_f / interval.value]
128
- else
129
- MTK::Interval[to_f / interval]
130
- end
131
- end
132
-
133
- def -@
134
- MTK::Interval[@value * -1]
135
- end
136
-
137
- def coerce(other)
138
- return MTK::Interval[other], self
139
- end
140
-
141
- end
142
-
143
- # Construct a {Duration} from any supported type
144
- def Interval(*anything)
145
- anything = anything.first if anything.length == 1
146
- case anything
147
- when Numeric then MTK::Interval[anything]
148
- when String, Symbol then MTK::Interval.from_s(anything)
149
- when Interval then anything
150
- else raise "Interval doesn't understand #{anything.class}"
151
- end
152
- end
153
- module_function :Interval
154
-
155
- end
data/lib/mtk/melody.rb DELETED
@@ -1,94 +0,0 @@
1
- module MTK
2
-
3
- # An ordered collection of {Pitch}es.
4
- #
5
- # The "horizontal" (sequential) pitch collection.
6
- #
7
- # Unlike the strict definition of melody, this class is fairly abstract and only models a succession of pitches.
8
- # To create a true, playable melody one must combine an MTK::Melody and rhythms into a {Timeline}.
9
- #
10
- # @see Chord
11
- #
12
- class Melody
13
- include Helpers::PitchCollection
14
-
15
- attr_reader :pitches
16
-
17
- # @param pitches [#to_a] the collection of pitches
18
- # @see MTK#Melody
19
- #
20
- def initialize(pitches)
21
- @pitches = pitches.to_a.clone.freeze
22
- end
23
-
24
- def self.from_pitch_classes(pitch_classes, start=Constants::Pitches::C4, max_distance=12)
25
- pitch = start
26
- pitches = []
27
- pitch_classes.each do |pitch_class|
28
- pitch = pitch.nearest(pitch_class)
29
- pitch -= 12 if pitch > start+max_distance # keep within max_distance of start (default is one octave)
30
- pitch += 12 if pitch < start-max_distance
31
- pitches << pitch
32
- end
33
- new pitches
34
- end
35
-
36
- # @see Helper::Collection
37
- def elements
38
- @pitches
39
- end
40
-
41
- # Convert to an Array of pitches.
42
- # @note this returns a mutable copy the underlying @pitches attribute, which is otherwise unmutable
43
- alias :to_pitches :to_a
44
-
45
- def self.from_a enumerable
46
- new enumerable
47
- end
48
-
49
- def to_pitch_class_set(remove_duplicates=true)
50
- PitchClassSet.new(remove_duplicates ? pitch_classes.uniq : pitch_classes)
51
- end
52
-
53
- def pitch_classes
54
- @pitch_classes ||= @pitches.map{|p| p.pitch_class }
55
- end
56
-
57
- # @param other [#pitches, Enumerable]
58
- def == other
59
- if other.respond_to? :pitches
60
- @pitches == other.pitches
61
- elsif other.is_a? Enumerable
62
- @pitches == other.to_a
63
- else
64
- @pitches == other
65
- end
66
- end
67
-
68
- # Compare for equality, ignoring order and duplicates
69
- # @param other [#pitches, Array, #to_a]
70
- def =~ other
71
- @normalized_pitches ||= @pitches.uniq.sort
72
- @normalized_pitches == case
73
- when other.respond_to?(:pitches) then other.pitches.uniq.sort
74
- when (other.is_a? Array and other.frozen?) then other
75
- when other.respond_to?(:to_a) then other.to_a.uniq.sort
76
- else other
77
- end
78
- end
79
-
80
- def to_s
81
- '[' + @pitches.map{|pitch| pitch.to_s}.join(', ') + ']'
82
- end
83
-
84
- end
85
-
86
- # Construct an ordered {Melody} that allows duplicates
87
- # @see #Melody
88
- # @see #Chord
89
- def Melody(*anything)
90
- Melody.new Helpers::Convert.to_pitches(*anything)
91
- end
92
- module_function :Melody
93
-
94
- end
data/lib/mtk/pitch.rb DELETED
@@ -1,152 +0,0 @@
1
- module MTK
2
-
3
- # A frequency represented by a {PitchClass}, an integer octave, and an offset in semitones.
4
- class Pitch
5
-
6
- include Comparable
7
-
8
- attr_reader :pitch_class, :octave, :offset
9
-
10
- def initialize( pitch_class, octave, offset=0 )
11
- @pitch_class, @octave, @offset = pitch_class, octave, offset
12
- @value = @pitch_class.to_i + 12*(@octave+1) + @offset
13
- end
14
-
15
- @flyweight = {}
16
-
17
- # Return a pitch with no offset, only constructing a new instance when not already in the flyweight cache
18
- def self.[](pitch_class, octave)
19
- pitch_class = MTK.PitchClass(pitch_class)
20
- @flyweight[[pitch_class,octave]] ||= new(pitch_class, octave)
21
- end
22
-
23
- # Lookup a pitch by name, which consists of any {PitchClass::VALID_NAMES} and an octave number.
24
- # The name may also be optionally suffixed by +/-###cents (where ### is any number).
25
- # @example get the Pitch for middle C :
26
- # Pitch.from_s('C4')
27
- # @example get the Pitch for middle C + 50 cents:
28
- # Pitch.from_s('C4+50cents')
29
- def self.from_s( name )
30
- s = name.to_s
31
- s = s[0..0].upcase + s[1..-1].downcase # normalize name
32
- if s =~ /^([A-G](#|##|b|bb)?)(-?\d+)(\+(\d+(\.\d+)?)cents)?$/
33
- pitch_class = PitchClass.from_s($1)
34
- if pitch_class
35
- octave = $3.to_i
36
- offset_in_cents = $5.to_f
37
- if offset_in_cents.nil? or offset_in_cents.zero?
38
- return self[pitch_class, octave]
39
- else
40
- return new( pitch_class, octave, offset_in_cents/100.0 )
41
- end
42
- end
43
- end
44
- raise ArgumentError.new("Invalid pitch name: #{name.inspect}")
45
- end
46
-
47
- class << self
48
- alias :from_name :from_s
49
- end
50
-
51
- # Convert a Numeric semitones value into a Pitch
52
- def self.from_f( f )
53
- i, offset = f.floor, f%1 # split into int and fractional part
54
- pitch_class = PitchClass.from_i(i)
55
- octave = i/12 - 1
56
- if offset == 0
57
- self[pitch_class, octave]
58
- else
59
- new( pitch_class, octave, offset )
60
- end
61
- end
62
-
63
- def self.from_hash(hash)
64
- new hash[:pitch_class], hash[:octave], hash.fetch(:offset,0)
65
- end
66
-
67
- # Convert a Numeric semitones value into a Pitch
68
- def self.from_i( i )
69
- from_f( i )
70
- end
71
-
72
- # The numerical value of this pitch
73
- def to_f
74
- @value
75
- end
76
-
77
- # The numerical value for the nearest semitone
78
- def to_i
79
- @value.round
80
- end
81
-
82
- def offset_in_cents
83
- @offset * 100
84
- end
85
-
86
- def to_hash
87
- {:pitch_class => @pitch_class, :octave => @octave, :offset => @offset}
88
- end
89
-
90
- def to_s
91
- "#{@pitch_class}#{@octave}" + (@offset.zero? ? '' : "+#{offset_in_cents.round}cents")
92
- end
93
-
94
- def inspect
95
- "#<#{self.class}:#{object_id} @value=#{@value}>"
96
- end
97
-
98
- def ==( other )
99
- other.respond_to? :pitch_class and other.respond_to? :octave and other.respond_to? :offset and
100
- other.pitch_class == @pitch_class and other.octave == @octave and other.offset == @offset
101
- end
102
-
103
- def <=> other
104
- @value <=> other.to_f
105
- end
106
-
107
- def + interval_in_semitones
108
- self.class.from_f( @value + interval_in_semitones.to_f )
109
- end
110
- alias transpose +
111
-
112
- def - interval_in_semitones
113
- self.class.from_f( @value - interval_in_semitones.to_f )
114
- end
115
-
116
- def invert(center_pitch)
117
- self + 2*(center_pitch.to_f - to_f)
118
- end
119
-
120
- def nearest(pitch_class)
121
- self + self.pitch_class.distance_to(pitch_class)
122
- end
123
-
124
- def coerce(other)
125
- return self.class.from_f(other.to_f), self
126
- end
127
-
128
- def clone_with(hash)
129
- self.class.from_hash(to_hash.merge hash)
130
- end
131
-
132
- end
133
-
134
- # Construct a {Pitch} from any supported type
135
- def Pitch(*anything)
136
- anything = anything.first if anything.length == 1
137
- case anything
138
- when Numeric then Pitch.from_f(anything)
139
- when String, Symbol then Pitch.from_s(anything)
140
- when Pitch then anything
141
- when Array
142
- if anything.length == 2
143
- Pitch[*anything]
144
- else
145
- Pitch.new(*anything)
146
- end
147
- else raise ArgumentError.new("Pitch doesn't understand #{anything.class}")
148
- end
149
- end
150
- module_function :Pitch
151
-
152
- end