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