stretto 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/CHANGELOG.markdown +0 -0
  2. data/README.markdown +67 -0
  3. data/Rakefile +9 -0
  4. data/lib/stretto.rb +14 -0
  5. data/lib/stretto/grammar/channel_pressure_grammar.treetop +9 -0
  6. data/lib/stretto/grammar/chord_grammar.treetop +60 -0
  7. data/lib/stretto/grammar/controller_change_grammar.treetop +9 -0
  8. data/lib/stretto/grammar/duration_grammar.treetop +60 -0
  9. data/lib/stretto/grammar/grammar_helper.rb +6 -0
  10. data/lib/stretto/grammar/harmonic_chord_grammar.treetop +15 -0
  11. data/lib/stretto/grammar/harmony_grammar.treetop +15 -0
  12. data/lib/stretto/grammar/instrument_grammar.treetop +9 -0
  13. data/lib/stretto/grammar/key_signature_grammar.treetop +10 -0
  14. data/lib/stretto/grammar/layer_change_grammar.treetop +9 -0
  15. data/lib/stretto/grammar/measure_grammar.treetop +7 -0
  16. data/lib/stretto/grammar/note_grammar.treetop +32 -0
  17. data/lib/stretto/grammar/pitch_bend_grammar.treetop +7 -0
  18. data/lib/stretto/grammar/polyphonic_pressure_grammar.treetop +9 -0
  19. data/lib/stretto/grammar/rest_grammar.treetop +9 -0
  20. data/lib/stretto/grammar/stretto_grammar.treetop +62 -0
  21. data/lib/stretto/grammar/tempo_grammar.treetop +9 -0
  22. data/lib/stretto/grammar/timing_grammar.treetop +9 -0
  23. data/lib/stretto/grammar/tokens/attack_decay_token.rb +42 -0
  24. data/lib/stretto/grammar/tokens/chord_token.rb +67 -0
  25. data/lib/stretto/grammar/tokens/controller_change_token.rb +26 -0
  26. data/lib/stretto/grammar/tokens/duration_token.rb +55 -0
  27. data/lib/stretto/grammar/tokens/harmonic_chord_token.rb +25 -0
  28. data/lib/stretto/grammar/tokens/harmony_with_melody_token.rb +66 -0
  29. data/lib/stretto/grammar/tokens/hash_token.rb +15 -0
  30. data/lib/stretto/grammar/tokens/key_signature_token.rb +30 -0
  31. data/lib/stretto/grammar/tokens/measure_token.rb +21 -0
  32. data/lib/stretto/grammar/tokens/modifier_token.rb +99 -0
  33. data/lib/stretto/grammar/tokens/note_string_token.rb +106 -0
  34. data/lib/stretto/grammar/tokens/note_token.rb +25 -0
  35. data/lib/stretto/grammar/tokens/pattern_token.rb +26 -0
  36. data/lib/stretto/grammar/tokens/polyphonic_pressure_token.rb +28 -0
  37. data/lib/stretto/grammar/tokens/rest_token.rb +21 -0
  38. data/lib/stretto/grammar/tokens/value_token.rb +28 -0
  39. data/lib/stretto/grammar/tokens/variable_definition_token.rb +30 -0
  40. data/lib/stretto/grammar/value_grammar.treetop +45 -0
  41. data/lib/stretto/grammar/variable_grammar.treetop +10 -0
  42. data/lib/stretto/grammar/voice_change_grammar.treetop +9 -0
  43. data/lib/stretto/music_elements/channel_pressure.rb +44 -0
  44. data/lib/stretto/music_elements/chord.rb +194 -0
  45. data/lib/stretto/music_elements/controller_change.rb +49 -0
  46. data/lib/stretto/music_elements/harmonic_chord.rb +56 -0
  47. data/lib/stretto/music_elements/harmony.rb +37 -0
  48. data/lib/stretto/music_elements/instrument.rb +57 -0
  49. data/lib/stretto/music_elements/key_signature.rb +110 -0
  50. data/lib/stretto/music_elements/layer.rb +22 -0
  51. data/lib/stretto/music_elements/layer_change.rb +39 -0
  52. data/lib/stretto/music_elements/measure.rb +38 -0
  53. data/lib/stretto/music_elements/melody.rb +72 -0
  54. data/lib/stretto/music_elements/modifiers/attack_decay.rb +55 -0
  55. data/lib/stretto/music_elements/modifiers/chord_intervals.rb +44 -0
  56. data/lib/stretto/music_elements/modifiers/duration.rb +180 -0
  57. data/lib/stretto/music_elements/modifiers/value.rb +172 -0
  58. data/lib/stretto/music_elements/modifiers/variables.rb +365 -0
  59. data/lib/stretto/music_elements/music_element.rb +88 -0
  60. data/lib/stretto/music_elements/note.rb +282 -0
  61. data/lib/stretto/music_elements/pattern.rb +100 -0
  62. data/lib/stretto/music_elements/pitch_bend.rb +46 -0
  63. data/lib/stretto/music_elements/polyphonic_pressure.rb +62 -0
  64. data/lib/stretto/music_elements/rest.rb +26 -0
  65. data/lib/stretto/music_elements/tempo.rb +42 -0
  66. data/lib/stretto/music_elements/timing.rb +33 -0
  67. data/lib/stretto/music_elements/variable.rb +49 -0
  68. data/lib/stretto/music_elements/voice.rb +49 -0
  69. data/lib/stretto/music_elements/voice_change.rb +39 -0
  70. data/lib/stretto/parsers/exceptions.rb +11 -0
  71. data/lib/stretto/parsers/parser.rb +89 -0
  72. data/lib/stretto/renderers/midiator/player.rb +200 -0
  73. data/lib/stretto/util/jfugue_format_parser.rb +20 -0
  74. data/lib/stretto/util/node.rb +5 -0
  75. data/lib/stretto/util/utils.rb +41 -0
  76. data/lib/stretto/version.rb +3 -0
  77. metadata +203 -0
@@ -0,0 +1,282 @@
1
+ require File.join(File.dirname(__FILE__), 'music_element')
2
+ require File.join(File.dirname(__FILE__), 'modifiers/attack_decay')
3
+
4
+ module Stretto
5
+ module MusicElements
6
+
7
+ # A note is one of the most basic elements in Stretto.
8
+ #
9
+ # It is composed of several elements:
10
+ #
11
+ # +key+:: Represents the note name (from A to G)
12
+ # +accidental+:: The modifier of the note, this is, flats, sharps or the natural indicator
13
+ # (bb, b, n, #, ##)
14
+ # +pitch+:: The actual numeric pitch of the note, according to MIDI specification (0 to 127)
15
+ # +octave+:: Octave in which the note is located. (0 to 10) The default octave for a note is 5
16
+ #
17
+ # Additionally, it holds a duration (see {Duration}), and attack and decay (see {AttackDecay})
18
+ #
19
+ # Example of valid notes are:
20
+ #
21
+ # +C+:: Returns the note C, with the default octave, duration, attack and decay
22
+ # +[60]+:: The same note, represented by its pitch value
23
+ # +D#7+:: The note D# in the seventh octave
24
+ # +Abwa80d100+:: The note Ab with a whole note duration, an attack of 80 and decay of 100
25
+ #
26
+ # Pitch values and octaves are indicated in the table below:
27
+ #
28
+ # Octave | C | C# | D | D# | E | F | F# | G | G# | A | A# | B
29
+ # 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11
30
+ # 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23
31
+ # 2 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35
32
+ # 3 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47
33
+ # 4 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59
34
+ # 5 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71
35
+ # 6 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83
36
+ # 7 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95
37
+ # 8 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107
38
+ # 9 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119
39
+ # 10 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127
40
+ #
41
+ # Where pitch can go from 0 to 127.
42
+
43
+ class Note < MusicElement
44
+
45
+ include Duration
46
+ include AttackDecay
47
+
48
+ # @private
49
+ PITCHES = {
50
+ 'C' => 0,
51
+ 'D' => 2,
52
+ 'E' => 4,
53
+ 'F' => 5,
54
+ 'G' => 7,
55
+ 'A' => 9,
56
+ 'B' => 11
57
+ }
58
+
59
+ # @private
60
+ ACCIDENTALS = {
61
+ 'bb' => -2,
62
+ 'b' => -1,
63
+ '#' => 1,
64
+ '##' => 2
65
+ }
66
+
67
+ # @private
68
+ MAX_PITCH = 127
69
+
70
+ # @private
71
+ DEFAULT_OCTAVE = 5
72
+
73
+ attr_reader :original_key, :original_accidental, :original_duration, :original_octave
74
+ attr_reader :key_signature
75
+ attr_accessor :instrument
76
+
77
+ def initialize(string_or_options, pattern = nil)
78
+ token = case string_or_options
79
+ when String then Stretto::Parser.parse_note!(string_or_options)
80
+ else string_or_options
81
+ end
82
+ super(token[:text_value], pattern)
83
+ @original_key = token[:key]
84
+ @original_pitch = token[:pitch]
85
+ @original_accidental = token[:accidental]
86
+ @original_octave = token[:octave]
87
+ build_duration_from_token(token[:duration])
88
+ build_attack_and_decay(token[:attack], token[:decay])
89
+ end
90
+
91
+ # Gets pitch of the note.
92
+ # If no original pitch is passed, the pitch gets calculated from the key, accidental
93
+ # and octave values.
94
+ #
95
+ # @return [Number] The pitch of the note, from 0 to 127
96
+ def pitch
97
+ @pitch || build_pitch
98
+ end
99
+
100
+ # Gets the octave for the note.
101
+ # If the original string did not contain an oe, the octave for that pitch is returned
102
+ # (see octave table on {MusicElements::Note} documentation.)
103
+ #
104
+ # @return [Number] The octave for the pitch
105
+ def octave
106
+ build_pitch unless @octave
107
+ @octave
108
+ end
109
+
110
+ # Gets the key of the note.
111
+ # If the original string did not contain a key note, the key for that pitch is returned
112
+ # (see the pitch table on {MusicElements::Note} documentation.)
113
+ #
114
+ # @return [String] The note key, 'A' through 'G'
115
+ def key
116
+ build_pitch unless @key
117
+ @key
118
+ end
119
+
120
+ # Gets the accidental of the note.
121
+ # If the original string did not contain an accidental, the default accidental for the pitch
122
+ # is returned (refer to the pitch table on {MusicElement::Note} documentation), always using
123
+ # sharps for raised notes.
124
+ #
125
+ # @return [String] The accidental of the note
126
+ # @todo Return accidental according to the present key signature
127
+ def accidental
128
+ build_pitch unless @accidental
129
+ @accidental
130
+ end
131
+
132
+ # Returns a new note raised by `interval` semitones.
133
+ #
134
+ # @param interval The amount of semitones to raise the pitch.
135
+ # @return [MusicElements::Note] A new note with the raised pitch
136
+ def +(interval)
137
+ new_pitch = if @original_pitch
138
+ @original_pitch + interval
139
+ else
140
+ Stretto::Value.new(Stretto::Value::NumericValue.new(pitch + interval))
141
+ end
142
+ Note.new({
143
+ :text_value => "#{@original_string}+#{interval}",
144
+ :pitch => new_pitch,
145
+ :duration => @original_duration_token,
146
+ :attack => @original_attack,
147
+ :decay => @original_decay}, @pattern)
148
+ end
149
+
150
+ # @return [Boolean] Whether `other` is a note and has the same pitch
151
+ def ==(other)
152
+ # TODO: Revisit the semantics of ==
153
+ other.kind_of?(Note) && other.pitch == pitch
154
+ end
155
+
156
+ # @return (see #==)
157
+ def eql?(other)
158
+ # TODO: Revisit the semantics of eql?
159
+ other.kind_of?(Note) && other.pitch.eql?(pitch)
160
+ end
161
+
162
+ # @return (see #==)
163
+ def hash
164
+ @pitch.hash
165
+ end
166
+
167
+ # Assigns the key signature to this note, altering its pitch.
168
+ #
169
+ # It doesn't affect the note if it has accidental, or its pitch was explicitely
170
+ # given, either with a numeric or variable value
171
+ #
172
+ # @param key_signature [MusicElements::KeySignature]
173
+ def key_signature=(key_signature)
174
+ @key_signature = key_signature
175
+ increment = key_signature_increment
176
+ self.pitch += key_signature_increment if increment
177
+ end
178
+
179
+ # Text value of original pitch
180
+ def original_pitch
181
+ @original_pitch.to_s if @original_pitch
182
+ end
183
+
184
+ private
185
+
186
+ # Builds pitch, and calculates key, octave and accidental with it.
187
+ #-
188
+ # TODO: Refactor into a single method: build_attributes
189
+ def build_pitch
190
+ if @original_pitch
191
+ self.pitch = @original_pitch.to_i(@pattern)
192
+ @key = calculate_key_from_pitch(@pitch)
193
+ @octave = calculate_octave_from_pitch(@pitch)
194
+ @accidental = calculate_accidental_from_pitch(@pitch)
195
+ else
196
+ @key = @original_key.upcase
197
+ @octave = (@original_octave && @original_octave.to_i) || 5
198
+ @accidental = @original_accidental.downcase if @original_accidental
199
+ self.pitch = calculate_pitch_from_key_octave_and_accidental(@key, @octave, @accidental)
200
+ end
201
+ @pitch
202
+ end
203
+
204
+ # Returns the pitch value calculated from its key, pitch and accidental
205
+ #
206
+ # @return [Number]
207
+ # @example
208
+ # calculate_pitch_from_key_octave_and_accidental("C", "5", "#") # => 61
209
+ # calculate_pitch_from_key_octave_and_accidental("G", "4", "b") # => 56
210
+ def calculate_pitch_from_key_octave_and_accidental(key, octave, accidental)
211
+ pitch = 12 * octave
212
+ pitch += PITCHES[key]
213
+ pitch += ACCIDENTALS[accidental] || 0
214
+ pitch
215
+ end
216
+
217
+ # @private
218
+ KEYS_FOR_PITCHES = ['C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B']
219
+
220
+ # Returns the key for the pitch
221
+ #
222
+ # @return [String]
223
+ # @example
224
+ # calculate_key_from_pitch(60) # => 'C', for 'C5'
225
+ # calculate_key_from_pitch(61) # => 'C', for 'C#5'
226
+ def calculate_key_from_pitch(pitch)
227
+ KEYS_FOR_PITCHES[pitch % 12]
228
+ end
229
+
230
+ # Returns the octave for pitch
231
+ #
232
+ # @return [Number]
233
+ # @example
234
+ # calculate_octave_from_pitch(59) # => 4, for "B4"
235
+ # calculate_octave_from_pitch(60) # => 5, for "C5"
236
+ def calculate_octave_from_pitch(pitch)
237
+ pitch / 12
238
+ end
239
+
240
+ # @private
241
+ ACCIDENTALS_FOR_PITCH = [nil, '#', nil, '#', nil, nil, '#', nil, '#', nil, '#', nil]
242
+
243
+ # Returns the accidental value for pitch
244
+ #
245
+ # @return [String]
246
+ # @example
247
+ # calculate_accidental_from_pitch(60) # => nil, pitch for "C"
248
+ # calculate_accidental_from_pitch(61) # => '#', pitch for "C#"
249
+ def calculate_accidental_from_pitch(value)
250
+ ACCIDENTALS_FOR_PITCH[value % 12]
251
+ end
252
+
253
+ # @param pitch [Number] The value for note's pitch
254
+ # @raise [Exceptions::NoteOutOfBoundsException] If the pitch is higher than 127
255
+ def pitch=(pitch)
256
+ raise Exceptions::NoteOutOfBoundsException if pitch < 0 or pitch > MAX_PITCH
257
+ @pitch = pitch
258
+ end
259
+
260
+ # @return [Number] The number of semitones the key signature raises
261
+ def key_signature_increment
262
+ if @key_signature and @original_key and !@accidental
263
+ @key_signature.modifier_for(@key)
264
+ else
265
+ nil
266
+ end
267
+ end
268
+
269
+
270
+ # @private
271
+ # @see MusicElements::MusicElement#substitute_variables!
272
+ def substitute_variables!
273
+ self.attack = attack
274
+ self.decay = decay
275
+ self.pitch = build_pitch
276
+ self.key_signature = @key_signature
277
+ end
278
+
279
+ end
280
+
281
+ end
282
+ end
@@ -0,0 +1,100 @@
1
+ require File.join(File.dirname(__FILE__), 'voice')
2
+ require File.join(File.dirname(__FILE__), '../util/jfugue_format_parser')
3
+
4
+ module Stretto
5
+
6
+ # Pattern is a series of MusicElements, that hold a context (like tied notes or key signature modifications)
7
+ # Is the equivalent of the JFugue implementation +Pattern+
8
+ #-
9
+ # NOTE: This class behavior is not definite, and may change during the development of Stretto
10
+ # until the first stable version
11
+ #+
12
+ class Pattern < Array
13
+
14
+ include Variables
15
+
16
+ DEFAULT_VOICE_INDEX = 0
17
+
18
+ attr_reader :voices, :variables # TODO: Limit access to variables
19
+
20
+ # Initializes a pattern
21
+ # @param music_string_or_file [String, File] Can be the music string directly, or
22
+ # a file in jfugue format.
23
+ def initialize(music_string_or_file = "")
24
+ @music_string = get_music_string_from(music_string_or_file)
25
+ @parser = Stretto::Parser.new(@music_string)
26
+ @voices = { }
27
+ @variables = { }
28
+ @__key_signature = { }
29
+ @__instruments = { }
30
+ if @parser.valid?
31
+ @parser.to_stretto(self).each { |music_element| self << music_element }
32
+ else
33
+ raise "Invalid music string \"#{@music_string}\" at character #{@parser.error_on}"
34
+ end
35
+ end
36
+
37
+ def elements
38
+ to_a
39
+ end
40
+
41
+ def to_s
42
+ @music_string
43
+ end
44
+
45
+ def <<(other)
46
+ other.pattern = self
47
+
48
+ if other.kind_of?(MusicElements::Variable)
49
+ @variables[other.name.upcase] = other.value
50
+ end
51
+
52
+ if other.kind_of?(MusicElements::VoiceChange)
53
+ @current_voice = (@voices[other.index] ||= Voice.new(other.index))
54
+ else
55
+ @voices[DEFAULT_VOICE_INDEX] = @current_voice = Voice.new(DEFAULT_VOICE_INDEX) unless @current_voice
56
+ @current_voice << other
57
+ if @current_voice.size > 1
58
+ @current_voice[-2].next = @current_voice[-1]
59
+ @current_voice[-1].prev = @current_voice[-2]
60
+ end
61
+ end
62
+
63
+ if other.kind_of?(MusicElements::KeySignature)
64
+ @__key_signature[@current_voice.index] = other
65
+ else
66
+ other.key_signature = @__key_signature[@current_voice.index] if other.respond_to?(:key_signature=)
67
+ end
68
+
69
+ if other.kind_of?(MusicElements::Instrument)
70
+ @__instruments[@current_voice.index] = other
71
+ else
72
+ @__instruments[@current_voice.index] ||= MusicElements::Instrument.default_instrument(self)
73
+ other.instrument = @__instruments[@current_voice.index] if other.respond_to?(:instrument=)
74
+ end
75
+
76
+ super(other)
77
+ end
78
+
79
+ def voice(index)
80
+ @voices[index]
81
+ end
82
+
83
+ def variable(name)
84
+ @variables[name.upcase] ||
85
+ (Value.new(Value::NumericValue.new(PREDEFINED_VARIABLES[name.upcase])) if PREDEFINED_VARIABLES[name.upcase]) ||
86
+ raise(Exceptions::VariableNotDefinedException.new("Variable '#{name}' not defined in pattern"))
87
+ end
88
+
89
+ private
90
+ def get_music_string_from(music_string_or_file)
91
+ case music_string_or_file
92
+ when String
93
+ music_string_or_file
94
+ when File
95
+ Stretto::JFugueFormatParser.parse(music_string_or_file)
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(__FILE__), 'music_element')
2
+
3
+ module Stretto
4
+ module MusicElements
5
+
6
+ # Pitch bend, according to MIDI spacification, changes the tone of a note in steps of
7
+ # hundredth of a note. The full range can go from 0 to 16383, being 8192 the middle pitch,
8
+ # that is, no variation.
9
+ #
10
+ # Pitch bends are represented with a +&+ symbol before, for example +&16000+
11
+ class PitchBend < MusicElement
12
+
13
+ MAX_PITCH_BEND_VALUE = 16383
14
+
15
+ def initialize(string_or_options, pattern = nil)
16
+ token = case string_or_options
17
+ when String then Stretto::Parser.parse_pitch_bend!(string_or_options)
18
+ else string_or_options
19
+ end
20
+ super(token[:text_value], pattern)
21
+ @original_value = token[:value]
22
+ end
23
+
24
+ # Sets value and validates it is in range
25
+ def value=(value)
26
+ if value < 0 or value > MAX_PITCH_BEND_VALUE
27
+ raise Exceptions::ValueOutOfBoundsException.new("Pitch bend should be in range 0..#{MAX_PITCH_BEND_VALUE}")
28
+ end
29
+ @value = value
30
+ end
31
+
32
+ def value
33
+ @value || @original_value.to_i(@pattern)
34
+ end
35
+
36
+ private
37
+
38
+ # @private
39
+ def substitute_variables!
40
+ self.value = value
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ require File.join(File.dirname(__FILE__), 'music_element')
2
+
3
+ module Stretto
4
+ module MusicElements
5
+
6
+ # Polyphonic pressure is similar to channel pressure (see {ChannelPressure}) but
7
+ # it is applied to only a note. A polyphonic pressure token is specified by the notation
8
+ # +*+_key_,_value_, where key is the value for the pitch (0 to 127) and value is the
9
+ # applied pressure (0 to 127)
10
+ class PolyphonicPressure < MusicElement
11
+
12
+ MAX_PITCH_VALUE = 127
13
+ MAX_VALUE = 127
14
+
15
+ attr_reader :pitch, :value
16
+
17
+ def initialize(string_or_options, pattern = nil)
18
+ token = case string_or_options
19
+ when String then Stretto::Parser.parse_polyphonic_pressure!(string_or_options)
20
+ else string_or_options
21
+ end
22
+ super(token[:text_value], pattern)
23
+ @original_pitch = token[:pitch]
24
+ @original_value = token[:value]
25
+ end
26
+
27
+ def pitch
28
+ @pitch || @original_pitch.to_i(@pattern)
29
+ end
30
+
31
+ # Sets value and validates in range
32
+ def pitch=(pitch)
33
+ if pitch < 0 or pitch > MAX_PITCH_VALUE
34
+ raise Exceptions::ValueOutOfBoundsException.new("Pitch value for polyphonic pressure should be in range 0..#{MAX_PITCH_VALUE}")
35
+ end
36
+ @pitch = pitch
37
+ end
38
+
39
+ def value
40
+ @value || @original_value.to_i(@pattern)
41
+ end
42
+
43
+ # Sets value and validates in range
44
+ def value=(value)
45
+ if value < 0 or value > MAX_VALUE
46
+ raise Exceptions::ValueOutOfBoundsException.new("Value for polyphonic pressure should be in range 0..#{MAX_VALUE}")
47
+ end
48
+ @value = value
49
+ end
50
+
51
+ private
52
+
53
+ # @private
54
+ def substitute_variables!
55
+ self.pitch = pitch
56
+ self.value = value
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end