stretto 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.markdown +0 -0
- data/README.markdown +67 -0
- data/Rakefile +9 -0
- data/lib/stretto.rb +14 -0
- data/lib/stretto/grammar/channel_pressure_grammar.treetop +9 -0
- data/lib/stretto/grammar/chord_grammar.treetop +60 -0
- data/lib/stretto/grammar/controller_change_grammar.treetop +9 -0
- data/lib/stretto/grammar/duration_grammar.treetop +60 -0
- data/lib/stretto/grammar/grammar_helper.rb +6 -0
- data/lib/stretto/grammar/harmonic_chord_grammar.treetop +15 -0
- data/lib/stretto/grammar/harmony_grammar.treetop +15 -0
- data/lib/stretto/grammar/instrument_grammar.treetop +9 -0
- data/lib/stretto/grammar/key_signature_grammar.treetop +10 -0
- data/lib/stretto/grammar/layer_change_grammar.treetop +9 -0
- data/lib/stretto/grammar/measure_grammar.treetop +7 -0
- data/lib/stretto/grammar/note_grammar.treetop +32 -0
- data/lib/stretto/grammar/pitch_bend_grammar.treetop +7 -0
- data/lib/stretto/grammar/polyphonic_pressure_grammar.treetop +9 -0
- data/lib/stretto/grammar/rest_grammar.treetop +9 -0
- data/lib/stretto/grammar/stretto_grammar.treetop +62 -0
- data/lib/stretto/grammar/tempo_grammar.treetop +9 -0
- data/lib/stretto/grammar/timing_grammar.treetop +9 -0
- data/lib/stretto/grammar/tokens/attack_decay_token.rb +42 -0
- data/lib/stretto/grammar/tokens/chord_token.rb +67 -0
- data/lib/stretto/grammar/tokens/controller_change_token.rb +26 -0
- data/lib/stretto/grammar/tokens/duration_token.rb +55 -0
- data/lib/stretto/grammar/tokens/harmonic_chord_token.rb +25 -0
- data/lib/stretto/grammar/tokens/harmony_with_melody_token.rb +66 -0
- data/lib/stretto/grammar/tokens/hash_token.rb +15 -0
- data/lib/stretto/grammar/tokens/key_signature_token.rb +30 -0
- data/lib/stretto/grammar/tokens/measure_token.rb +21 -0
- data/lib/stretto/grammar/tokens/modifier_token.rb +99 -0
- data/lib/stretto/grammar/tokens/note_string_token.rb +106 -0
- data/lib/stretto/grammar/tokens/note_token.rb +25 -0
- data/lib/stretto/grammar/tokens/pattern_token.rb +26 -0
- data/lib/stretto/grammar/tokens/polyphonic_pressure_token.rb +28 -0
- data/lib/stretto/grammar/tokens/rest_token.rb +21 -0
- data/lib/stretto/grammar/tokens/value_token.rb +28 -0
- data/lib/stretto/grammar/tokens/variable_definition_token.rb +30 -0
- data/lib/stretto/grammar/value_grammar.treetop +45 -0
- data/lib/stretto/grammar/variable_grammar.treetop +10 -0
- data/lib/stretto/grammar/voice_change_grammar.treetop +9 -0
- data/lib/stretto/music_elements/channel_pressure.rb +44 -0
- data/lib/stretto/music_elements/chord.rb +194 -0
- data/lib/stretto/music_elements/controller_change.rb +49 -0
- data/lib/stretto/music_elements/harmonic_chord.rb +56 -0
- data/lib/stretto/music_elements/harmony.rb +37 -0
- data/lib/stretto/music_elements/instrument.rb +57 -0
- data/lib/stretto/music_elements/key_signature.rb +110 -0
- data/lib/stretto/music_elements/layer.rb +22 -0
- data/lib/stretto/music_elements/layer_change.rb +39 -0
- data/lib/stretto/music_elements/measure.rb +38 -0
- data/lib/stretto/music_elements/melody.rb +72 -0
- data/lib/stretto/music_elements/modifiers/attack_decay.rb +55 -0
- data/lib/stretto/music_elements/modifiers/chord_intervals.rb +44 -0
- data/lib/stretto/music_elements/modifiers/duration.rb +180 -0
- data/lib/stretto/music_elements/modifiers/value.rb +172 -0
- data/lib/stretto/music_elements/modifiers/variables.rb +365 -0
- data/lib/stretto/music_elements/music_element.rb +88 -0
- data/lib/stretto/music_elements/note.rb +282 -0
- data/lib/stretto/music_elements/pattern.rb +100 -0
- data/lib/stretto/music_elements/pitch_bend.rb +46 -0
- data/lib/stretto/music_elements/polyphonic_pressure.rb +62 -0
- data/lib/stretto/music_elements/rest.rb +26 -0
- data/lib/stretto/music_elements/tempo.rb +42 -0
- data/lib/stretto/music_elements/timing.rb +33 -0
- data/lib/stretto/music_elements/variable.rb +49 -0
- data/lib/stretto/music_elements/voice.rb +49 -0
- data/lib/stretto/music_elements/voice_change.rb +39 -0
- data/lib/stretto/parsers/exceptions.rb +11 -0
- data/lib/stretto/parsers/parser.rb +89 -0
- data/lib/stretto/renderers/midiator/player.rb +200 -0
- data/lib/stretto/util/jfugue_format_parser.rb +20 -0
- data/lib/stretto/util/node.rb +5 -0
- data/lib/stretto/util/utils.rb +41 -0
- data/lib/stretto/version.rb +3 -0
- metadata +203 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module Stretto
|
2
|
+
module Tokens
|
3
|
+
|
4
|
+
# Token result from parsing attack and decay syntax
|
5
|
+
#
|
6
|
+
# @example "a100d[VAR]"
|
7
|
+
class AttackDecayToken < Treetop::Runtime::SyntaxNode
|
8
|
+
|
9
|
+
# @return [NumericToken, VariableToken, nil]
|
10
|
+
def attack
|
11
|
+
_attack.value.wrap if _attack and _attack.text_value.present?
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [NumericToken, VariableToken, nil]
|
15
|
+
def decay
|
16
|
+
_decay.value.wrap if _decay and _decay.text_value.present?
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Include this module to access functionality of attack and decay tokens
|
23
|
+
#
|
24
|
+
# @see #attack
|
25
|
+
# @see #decay
|
26
|
+
module WithAttackDecayToken
|
27
|
+
|
28
|
+
# @return [Value, nil] The attack value wrapped by a Value object, or nil
|
29
|
+
def attack
|
30
|
+
attack = attack_and_decay.attack
|
31
|
+
Stretto::Value.new(attack)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Value, nil] The decay value wrapped by a Value object, or nil
|
35
|
+
def decay
|
36
|
+
decay = attack_and_decay.decay
|
37
|
+
Stretto::Value.new(decay)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'duration_token')
|
2
|
+
require File.join(File.dirname(__FILE__), 'note_string_token')
|
3
|
+
require File.join(File.dirname(__FILE__), 'attack_decay_token')
|
4
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/chord')
|
5
|
+
|
6
|
+
module Stretto
|
7
|
+
module Tokens
|
8
|
+
|
9
|
+
# Parses a named chord
|
10
|
+
#
|
11
|
+
# @example: "C5#maj"
|
12
|
+
class ChordToken < HashToken
|
13
|
+
|
14
|
+
include WithDurationToken
|
15
|
+
include WithNoteStringToken
|
16
|
+
include WithAttackDecayToken
|
17
|
+
|
18
|
+
# @return [MusicElements::Chord] The constructed Chord element
|
19
|
+
def to_stretto(pattern = nil)
|
20
|
+
Stretto::MusicElements::Chord.new(self, pattern)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns a base note hash, which can be seen as a token returned by the
|
24
|
+
# parsed note
|
25
|
+
def base_note
|
26
|
+
{
|
27
|
+
:text_value => note_string.text_value,
|
28
|
+
:key => key,
|
29
|
+
:pitch => pitch,
|
30
|
+
:accidental => accidental,
|
31
|
+
:octave => octave,
|
32
|
+
:attack => attack,
|
33
|
+
:decay => decay,
|
34
|
+
:duration => duration
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [NoteStringToken]
|
39
|
+
def note_string
|
40
|
+
__note_string
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return A string containing the named chord
|
44
|
+
# @example "sus4"
|
45
|
+
def named_chord
|
46
|
+
__named_chord.text_value
|
47
|
+
end
|
48
|
+
|
49
|
+
# If inversions are present, return a hash containing the pivot note
|
50
|
+
# (e.g. "^C") or the number of inversions (e.g. 5)
|
51
|
+
def inversions
|
52
|
+
if __chord_inversions and __chord_inversions.text_value.present?
|
53
|
+
{ :inversions => __chord_inversions.inversions,
|
54
|
+
:pivot_note => __chord_inversions.pivot_note }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Don't precalculate values. Added for harmonic chords to be able to be
|
59
|
+
# constructed from a chord token.
|
60
|
+
# @private
|
61
|
+
def notes
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/controller_change')
|
2
|
+
|
3
|
+
module Stretto
|
4
|
+
module Tokens
|
5
|
+
|
6
|
+
# Token result from parsing a controller change
|
7
|
+
class ControllerChangeToken < HashToken
|
8
|
+
|
9
|
+
# @return [MusicElements::ControllerChange]
|
10
|
+
def to_stretto(pattern)
|
11
|
+
Stretto::MusicElements::ControllerChange.new(self, pattern)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Value]
|
15
|
+
def controller
|
16
|
+
Value.new(__controller.wrap)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Value]
|
20
|
+
def value
|
21
|
+
Value.new(__value.wrap)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Stretto
|
2
|
+
module Tokens
|
3
|
+
|
4
|
+
# Token result from parsing a duration
|
5
|
+
#
|
6
|
+
# @example "wh", "q*3:4", "/2.5"
|
7
|
+
class DurationToken < Treetop::Runtime::SyntaxNode
|
8
|
+
|
9
|
+
# @return [true, false] if the duration starts a tie (e.g., "w-")
|
10
|
+
def start_of_tie?
|
11
|
+
_start_of_tie.text_value.present? if _start_of_tie
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [true, false] if the duration ends a tie (e.g., "-w")
|
15
|
+
def end_of_tie?
|
16
|
+
_end_of_tie.text_value.present? if _end_of_tie
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Hash, nil] A hash containing both the numerator and denominator, if present
|
20
|
+
# @example { :numerator => 2, :denominator => 3 }
|
21
|
+
def tuplet
|
22
|
+
if duration_string.tuplet
|
23
|
+
{:numerator => duration_string.tuplet.numerator,
|
24
|
+
:denominator => duration_string.tuplet.denominator}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String] The duration expressed as characters
|
29
|
+
# @example "h", "wq"
|
30
|
+
def duration_character
|
31
|
+
duration_string.duration_character
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Value, nil] A value wrapping the value of the duration, if present
|
35
|
+
def value
|
36
|
+
Stretto::Value.new(duration_string.value.wrap) if duration_string.value
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String] Returns a string with the number of dots in the duration
|
40
|
+
# @example 2 (for string Cmajh..
|
41
|
+
def dots
|
42
|
+
duration_string.dots
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
# Include this module to obtain functionality for duration token
|
48
|
+
module WithDurationToken
|
49
|
+
def duration
|
50
|
+
__duration if __duration and __duration.text_value.present?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/../../music_elements/harmonic_chord')
|
2
|
+
|
3
|
+
module Stretto
|
4
|
+
module Tokens
|
5
|
+
|
6
|
+
# Token result from parsing a duration
|
7
|
+
#
|
8
|
+
# @example "C+D+E"
|
9
|
+
class HarmonicChordToken < HashToken
|
10
|
+
|
11
|
+
# @return [MusicElements::HarmonicChord] The constructed HarmonicChord element
|
12
|
+
def to_stretto(pattern = nil)
|
13
|
+
@pattern = pattern
|
14
|
+
Stretto::MusicElements::HarmonicChord.new(self, pattern)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Array<MusicElement>] The notes that form this chord
|
18
|
+
def notes
|
19
|
+
[_first_element.to_stretto(@pattern)] +
|
20
|
+
_other_elements.elements.map{|element| element._element.to_stretto(@pattern)}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/harmony')
|
2
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/melody')
|
3
|
+
|
4
|
+
module Stretto
|
5
|
+
module Tokens
|
6
|
+
|
7
|
+
# Token result from parsing a harmony
|
8
|
+
#
|
9
|
+
# @example "Cmajh+Dh_+Ew"
|
10
|
+
class HarmonyWithMelodyToken < HashToken
|
11
|
+
|
12
|
+
|
13
|
+
# @return [MusicElements::Harmony, MusicElements::Melody] Returns the constructed
|
14
|
+
# Harmony or Melody object. It is a Harmony when there is more than one element
|
15
|
+
# (e.g. "A+B_C")
|
16
|
+
def to_stretto(pattern = nil)
|
17
|
+
@pattern = pattern
|
18
|
+
music_elements = harmony_elements(pattern)
|
19
|
+
if music_elements.size == 1 and music_elements.first.kind_of?(Stretto::MusicElements::Melody)
|
20
|
+
music_elements.first
|
21
|
+
else
|
22
|
+
Stretto::MusicElements::Harmony.new(self, pattern)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Array(MusicElements::MusicElement)] AN array of the music elements that form the harmony
|
27
|
+
def music_elements
|
28
|
+
@music_elements ||= harmony_elements(@pattern)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Builds the Melody objects or the single elements that form a harmony
|
34
|
+
#-
|
35
|
+
# TODO: This can be refactored, even return the melodies directly from the token
|
36
|
+
def harmony_elements(pattern)
|
37
|
+
elements = [_first_element.to_stretto]
|
38
|
+
original_strings = [_first_element.text_value]
|
39
|
+
_other_elements.elements.each do |e|
|
40
|
+
element = e._element.to_stretto
|
41
|
+
case e._sep.text_value
|
42
|
+
when '_'
|
43
|
+
elements << [elements.pop] unless elements.last.kind_of?(Array)
|
44
|
+
elements.last << element
|
45
|
+
original_strings[-1] += element.original_string
|
46
|
+
when '+'
|
47
|
+
elements << element
|
48
|
+
original_strings << element.original_string
|
49
|
+
end
|
50
|
+
end
|
51
|
+
elements.zip(original_strings).map do |element, string|
|
52
|
+
if element.kind_of?(Array)
|
53
|
+
Stretto::MusicElements::Melody.new(
|
54
|
+
{ :original_string => string,
|
55
|
+
:elements => element },
|
56
|
+
pattern
|
57
|
+
)
|
58
|
+
else
|
59
|
+
element
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Stretto
|
2
|
+
module Tokens
|
3
|
+
|
4
|
+
# Subclass of a parser token that overrides the #[] method to act like a Hash
|
5
|
+
class HashToken < Treetop::Runtime::SyntaxNode
|
6
|
+
|
7
|
+
# Quack like a hash. This is to make the individual element constructor accept either a hash
|
8
|
+
# or a token
|
9
|
+
def [](key)
|
10
|
+
send(key)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/key_signature')
|
2
|
+
|
3
|
+
module Stretto
|
4
|
+
module Tokens
|
5
|
+
|
6
|
+
# Token result from parsing a key signature
|
7
|
+
#
|
8
|
+
# @example "KGmaj"
|
9
|
+
class KeySignatureToken < HashToken
|
10
|
+
|
11
|
+
# @return [MusicElements::KeySignature] The constructed token
|
12
|
+
def to_stretto(pattern = nil)
|
13
|
+
Stretto::MusicElements::KeySignature.new(self, pattern)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return A string with the key
|
17
|
+
# @example "C#"
|
18
|
+
def key
|
19
|
+
__note_key.text_value
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return ['maj', 'min'] The scale of the key signature
|
23
|
+
def scale
|
24
|
+
__scale.text_value
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/measure')
|
2
|
+
|
3
|
+
module Stretto
|
4
|
+
module Tokens
|
5
|
+
|
6
|
+
# Token result from parsing a measure
|
7
|
+
#
|
8
|
+
#-
|
9
|
+
# Right now, the only supported measure is "|"
|
10
|
+
# TODO: Add support for other measures? (Repeat signs, codas, etc.)
|
11
|
+
class MeasureToken < HashToken
|
12
|
+
|
13
|
+
# @return [MusicElements::Measure] The constructed Measure element
|
14
|
+
def to_stretto(pattern = nil)
|
15
|
+
Stretto::MusicElements::Measure.new(self, pattern)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/modifiers/value')
|
2
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/instrument')
|
3
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/voice_change')
|
4
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/layer_change')
|
5
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/tempo')
|
6
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/pitch_bend')
|
7
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/channel_pressure')
|
8
|
+
require File.join(File.dirname(__FILE__), '../../music_elements/timing')
|
9
|
+
|
10
|
+
module Stretto
|
11
|
+
module Tokens
|
12
|
+
|
13
|
+
# Token result from parsing a modifier token.
|
14
|
+
# This encloses all elements that are constructed by a single character and a value.
|
15
|
+
# Refer to individual class to see the MusicElement generated from this token
|
16
|
+
#
|
17
|
+
# @example "I40". "T[ALLEGRO]", "@2000"
|
18
|
+
class ModifierToken < HashToken
|
19
|
+
|
20
|
+
# Returns an element of class KLASS
|
21
|
+
# @abstract
|
22
|
+
# @return [MusicElements::MusicElement]
|
23
|
+
# @see ModifierToken::KLASS
|
24
|
+
def to_stretto(pattern = nil)
|
25
|
+
self.class::KLASS.new(self, pattern)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Value] A Value object wrapping the value of the modifier
|
29
|
+
def value
|
30
|
+
Value.new(__value.wrap)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
# Token result from parsing an instrument
|
36
|
+
#
|
37
|
+
# @example "I[FLUTE]"
|
38
|
+
class InstrumentToken < ModifierToken
|
39
|
+
|
40
|
+
# @private
|
41
|
+
KLASS = Stretto::MusicElements::Instrument
|
42
|
+
end
|
43
|
+
|
44
|
+
# Token result from parsing a voice
|
45
|
+
#
|
46
|
+
# @example "V10"
|
47
|
+
class VoiceChangeToken < ModifierToken
|
48
|
+
|
49
|
+
# @private
|
50
|
+
KLASS = Stretto::MusicElements::VoiceChange
|
51
|
+
end
|
52
|
+
|
53
|
+
# Token result from parsing a layer
|
54
|
+
#
|
55
|
+
# @example "L5"
|
56
|
+
class LayerChangeToken < ModifierToken
|
57
|
+
|
58
|
+
# @private
|
59
|
+
KLASS = Stretto::MusicElements::LayerChange
|
60
|
+
end
|
61
|
+
|
62
|
+
# Token result from parsing a tempo
|
63
|
+
#
|
64
|
+
# @example "T200", "T[ALLEGRO]"
|
65
|
+
class TempoToken < ModifierToken
|
66
|
+
|
67
|
+
# @private
|
68
|
+
KLASS = Stretto::MusicElements::Tempo
|
69
|
+
end
|
70
|
+
|
71
|
+
# Token result from parsing a pitch bend
|
72
|
+
#
|
73
|
+
# @example "&9001"
|
74
|
+
class PitchBendToken < ModifierToken
|
75
|
+
|
76
|
+
# @private
|
77
|
+
KLASS = Stretto::MusicElements::PitchBend
|
78
|
+
end
|
79
|
+
|
80
|
+
# Token result from parsing a channel pressure
|
81
|
+
#
|
82
|
+
# @example "+80"
|
83
|
+
class ChannelPressureToken < ModifierToken
|
84
|
+
|
85
|
+
# @private
|
86
|
+
KLASS = Stretto::MusicElements::ChannelPressure
|
87
|
+
end
|
88
|
+
|
89
|
+
# Token result from parsing a timing indication
|
90
|
+
#
|
91
|
+
# @example "@2000"
|
92
|
+
class TimingToken < ModifierToken
|
93
|
+
|
94
|
+
# @private
|
95
|
+
KLASS = Stretto::MusicElements::Timing
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|