brevity 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.gitignore +7 -0
  4. data/.rspec +1 -0
  5. data/.ruby-version +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.rdoc +4 -0
  8. data/Gemfile +3 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.rdoc +27 -0
  11. data/Rakefile +67 -0
  12. data/bin/debrief +56 -0
  13. data/brevity.gemspec +28 -0
  14. data/examples/alley_cat.br +50 -0
  15. data/examples/hip.br +15 -0
  16. data/examples/missed_connection.br +6 -0
  17. data/examples/song1.br +11 -0
  18. data/examples/song2.br +10 -0
  19. data/examples/twinkle.br +18 -0
  20. data/lib/brevity/commands/constants.rb +18 -0
  21. data/lib/brevity/commands/expr.rb +17 -0
  22. data/lib/brevity/commands/meter.rb +20 -0
  23. data/lib/brevity/commands/part.rb +47 -0
  24. data/lib/brevity/commands/tempo.rb +13 -0
  25. data/lib/brevity/itemization.rb +73 -0
  26. data/lib/brevity/parsing/expression/dynamic.rb +280 -0
  27. data/lib/brevity/parsing/expression/dynamic.treetop +41 -0
  28. data/lib/brevity/parsing/expression/dynamic_nodes.rb +55 -0
  29. data/lib/brevity/parsing/expression/expression.rb +429 -0
  30. data/lib/brevity/parsing/expression/expression.treetop +39 -0
  31. data/lib/brevity/parsing/expression/expression_nodes.rb +26 -0
  32. data/lib/brevity/parsing/expression/gradual.rb +44 -0
  33. data/lib/brevity/parsing/expression/gradual.treetop +9 -0
  34. data/lib/brevity/parsing/expression/gradual_node.rb +11 -0
  35. data/lib/brevity/parsing/expression/label.rb +75 -0
  36. data/lib/brevity/parsing/expression/label.treetop +9 -0
  37. data/lib/brevity/parsing/expression/label_node.rb +15 -0
  38. data/lib/brevity/parsing/expression/sequence.rb +130 -0
  39. data/lib/brevity/parsing/expression/sequence.treetop +12 -0
  40. data/lib/brevity/parsing/expression/sequence_node.rb +14 -0
  41. data/lib/brevity/parsing/file/command.rb +216 -0
  42. data/lib/brevity/parsing/file/command.treetop +17 -0
  43. data/lib/brevity/parsing/file/command_node.rb +11 -0
  44. data/lib/brevity/parsing/file/comment.rb +178 -0
  45. data/lib/brevity/parsing/file/comment.treetop +21 -0
  46. data/lib/brevity/parsing/file/comment_node.rb +3 -0
  47. data/lib/brevity/parsing/file/file.rb +152 -0
  48. data/lib/brevity/parsing/file/file.treetop +16 -0
  49. data/lib/brevity/parsing/file/file_node.rb +7 -0
  50. data/lib/brevity/parsing/file/path.rb +235 -0
  51. data/lib/brevity/parsing/file/path.treetop +12 -0
  52. data/lib/brevity/parsing/modifiers/duplicate_modifier.rb +65 -0
  53. data/lib/brevity/parsing/modifiers/duplicate_modifier.treetop +11 -0
  54. data/lib/brevity/parsing/modifiers/duplicate_modifier_node.rb +8 -0
  55. data/lib/brevity/parsing/modifiers/modifier.rb +64 -0
  56. data/lib/brevity/parsing/modifiers/modifier.treetop +13 -0
  57. data/lib/brevity/parsing/modifiers/stretch_modifier.rb +69 -0
  58. data/lib/brevity/parsing/modifiers/stretch_modifier.treetop +11 -0
  59. data/lib/brevity/parsing/modifiers/stretch_modifier_node.rb +21 -0
  60. data/lib/brevity/parsing/modifiers/transpose_modifier.rb +69 -0
  61. data/lib/brevity/parsing/modifiers/transpose_modifier.treetop +11 -0
  62. data/lib/brevity/parsing/modifiers/transpose_modifier_node.rb +13 -0
  63. data/lib/brevity/parsing/note/accent.rb +44 -0
  64. data/lib/brevity/parsing/note/accent.treetop +9 -0
  65. data/lib/brevity/parsing/note/duration.rb +203 -0
  66. data/lib/brevity/parsing/note/duration.treetop +23 -0
  67. data/lib/brevity/parsing/note/duration_nodes.rb +19 -0
  68. data/lib/brevity/parsing/note/link.rb +69 -0
  69. data/lib/brevity/parsing/note/link.treetop +11 -0
  70. data/lib/brevity/parsing/note/link_node.rb +19 -0
  71. data/lib/brevity/parsing/note/note.rb +300 -0
  72. data/lib/brevity/parsing/note/note.treetop +30 -0
  73. data/lib/brevity/parsing/note/note_nodes.rb +77 -0
  74. data/lib/brevity/parsing/note/pitch.rb +81 -0
  75. data/lib/brevity/parsing/note/pitch.treetop +9 -0
  76. data/lib/brevity/parsing/note/pitch_node.rb +50 -0
  77. data/lib/brevity/parsing/numbers/nonnegative_integer.rb +53 -0
  78. data/lib/brevity/parsing/numbers/nonnegative_integer.treetop +9 -0
  79. data/lib/brevity/parsing/numbers/positive_integer.rb +91 -0
  80. data/lib/brevity/parsing/numbers/positive_integer.treetop +15 -0
  81. data/lib/brevity/read_file.rb +18 -0
  82. data/lib/brevity/score_maker.rb +64 -0
  83. data/lib/brevity/version.rb +4 -0
  84. data/lib/brevity.rb +53 -0
  85. data/manuals/brevity.pdf +0 -0
  86. data/manuals/brevity.tex +273 -0
  87. data/spec/brevity_spec.rb +8 -0
  88. data/spec/commands/expr_spec.rb +15 -0
  89. data/spec/commands/meter_spec.rb +21 -0
  90. data/spec/commands/part_spec.rb +16 -0
  91. data/spec/commands/tempo_spec.rb +20 -0
  92. data/spec/itemization_spec.rb +46 -0
  93. data/spec/parsing/expression/dynamic_nodes_spec.rb +32 -0
  94. data/spec/parsing/expression/dynamic_spec.rb +23 -0
  95. data/spec/parsing/expression/expression_nodes_spec.rb +87 -0
  96. data/spec/parsing/expression/expression_spec.rb +85 -0
  97. data/spec/parsing/expression/gradual_spec.rb +10 -0
  98. data/spec/parsing/expression/label_node_spec.rb +13 -0
  99. data/spec/parsing/expression/label_spec.rb +35 -0
  100. data/spec/parsing/file/command_node_spec.rb +29 -0
  101. data/spec/parsing/file/command_spec.rb +18 -0
  102. data/spec/parsing/file/comment_spec.rb +14 -0
  103. data/spec/parsing/file/file_node_spec.rb +19 -0
  104. data/spec/parsing/file/file_spec.rb +30 -0
  105. data/spec/parsing/modifiers/modifier_nodes_spec.rb +25 -0
  106. data/spec/parsing/modifiers/modifier_parsers_spec.rb +20 -0
  107. data/spec/parsing/note/accent_spec.rb +27 -0
  108. data/spec/parsing/note/duration_nodes_spec.rb +79 -0
  109. data/spec/parsing/note/duration_spec.rb +69 -0
  110. data/spec/parsing/note/link_node_spec.rb +30 -0
  111. data/spec/parsing/note/link_spec.rb +23 -0
  112. data/spec/parsing/note/note_nodes_spec.rb +82 -0
  113. data/spec/parsing/note/note_spec.rb +188 -0
  114. data/spec/parsing/note/pitch_node_spec.rb +48 -0
  115. data/spec/parsing/note/pitch_spec.rb +23 -0
  116. data/spec/parsing/numbers/nonnegative_integer_spec.rb +11 -0
  117. data/spec/parsing/numbers/positive_integer_spec.rb +17 -0
  118. data/spec/spec_helper.rb +112 -0
  119. metadata +293 -0
@@ -0,0 +1,18 @@
1
+ module Brevity
2
+ class FileParseError < RuntimeError; end;
3
+
4
+ def self.read_file fname
5
+ cmd_nodes = []
6
+ ::File.open(fname) do |f|
7
+ fparser = FileParser.new
8
+ fnode = fparser.parse(f.read)
9
+ if fnode.nil?
10
+ raise FileParseError, fparser.failure_reason
11
+ end
12
+
13
+ cmd_nodes = fnode.command_nodes
14
+ end
15
+
16
+ return cmd_nodes
17
+ end
18
+ end
@@ -0,0 +1,64 @@
1
+ module Brevity
2
+ class MissingStartTempoError < RuntimeError; end
3
+ class MissingStartMeterError < RuntimeError; end
4
+
5
+ class ScoreMaker
6
+ include Commands
7
+
8
+ def reset_env
9
+ @env = {}
10
+ @env[ENV_START_TEMPO] = nil
11
+ @env[ENV_TEMPO_CHANGES] = {}
12
+ @env[ENV_METER_CHANGES] = {}
13
+ @env[ENV_EXPRS] = {}
14
+ @env[ENV_PARTS] = {}
15
+ end
16
+
17
+ def process_nodes cmd_nodes
18
+ self.reset_env
19
+
20
+ cmd_nodes.each do |cmd_node|
21
+ cmd_method = cmd_node.name.to_sym
22
+ self.send(cmd_method,*cmd_node.args)
23
+ end
24
+ end
25
+
26
+ def make_score
27
+ start_tempo = @env[ENV_START_TEMPO]
28
+ start_meter = @env[ENV_START_METER]\
29
+
30
+ unless start_tempo.is_a? Numeric
31
+ raise MissingStartTempoError
32
+ end
33
+
34
+ unless start_meter.is_a? Music::Transcription::Meter
35
+ raise MissingStartMeterError
36
+ end
37
+
38
+ tcs = @env[ENV_TEMPO_CHANGES]
39
+ mcs = @env[ENV_METER_CHANGES]
40
+ parts = @env[ENV_PARTS]
41
+
42
+ ## now that start tempo is established, find tempo changes
43
+ ## whose value is not a Tempo object and convert them
44
+ #prev_tempo = start_tempo
45
+ #tcs.keys.sort.each do |offset|
46
+ # tc = tcs[offset]
47
+ # tempo = tc.value
48
+ # if tempo.beat_duration.nil?
49
+ # tc.value = Music::Transcription::Tempo.new(
50
+ # tempo.beats_per_minute,prev_tempo.beat_duration)
51
+ # end
52
+ # prev_tempo = tc.value
53
+ #end
54
+
55
+ return Music::Transcription::Score.new(
56
+ start_meter,
57
+ start_tempo,
58
+ parts: parts,
59
+ tempo_changes: tcs,
60
+ meter_changes: mcs
61
+ )
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,4 @@
1
+ module Brevity
2
+ # Brevity version
3
+ VERSION = "0.4.0"
4
+ end
data/lib/brevity.rb ADDED
@@ -0,0 +1,53 @@
1
+ require 'treetop'
2
+ require 'music-transcription'
3
+
4
+ require 'brevity/version'
5
+ require 'brevity/itemization'
6
+
7
+ require 'brevity/parsing/numbers/nonnegative_integer'
8
+ require 'brevity/parsing/numbers/positive_integer'
9
+
10
+ require 'brevity/parsing/note/accent'
11
+ require 'brevity/parsing/note/pitch'
12
+ require 'brevity/parsing/note/pitch_node'
13
+ require 'brevity/parsing/note/link'
14
+ require 'brevity/parsing/note/link_node'
15
+ require 'brevity/parsing/note/duration'
16
+ require 'brevity/parsing/note/duration_nodes'
17
+ require 'brevity/parsing/note/note'
18
+ require 'brevity/parsing/note/note_nodes'
19
+
20
+ require 'brevity/parsing/modifiers/duplicate_modifier'
21
+ require 'brevity/parsing/modifiers/duplicate_modifier_node'
22
+ require 'brevity/parsing/modifiers/stretch_modifier'
23
+ require 'brevity/parsing/modifiers/stretch_modifier_node'
24
+ require 'brevity/parsing/modifiers/transpose_modifier'
25
+ require 'brevity/parsing/modifiers/transpose_modifier_node'
26
+ require 'brevity/parsing/modifiers/modifier'
27
+
28
+ require 'brevity/parsing/expression/dynamic'
29
+ require 'brevity/parsing/expression/dynamic_nodes'
30
+ require 'brevity/parsing/expression/gradual'
31
+ require 'brevity/parsing/expression/gradual_node'
32
+ require 'brevity/parsing/expression/label'
33
+ require 'brevity/parsing/expression/label_node'
34
+ require 'brevity/parsing/expression/sequence'
35
+ require 'brevity/parsing/expression/sequence_node'
36
+ require 'brevity/parsing/expression/expression'
37
+ require 'brevity/parsing/expression/expression_nodes'
38
+
39
+ require 'brevity/parsing/file/comment'
40
+ require 'brevity/parsing/file/comment_node'
41
+ require 'brevity/parsing/file/command'
42
+ require 'brevity/parsing/file/command_node'
43
+ require 'brevity/parsing/file/file'
44
+ require 'brevity/parsing/file/file_node'
45
+
46
+ require 'brevity/commands/constants'
47
+ require 'brevity/commands/meter'
48
+ require 'brevity/commands/tempo'
49
+ require 'brevity/commands/expr'
50
+ require 'brevity/commands/part'
51
+
52
+ require 'brevity/read_file'
53
+ require 'brevity/score_maker'
Binary file
@@ -0,0 +1,273 @@
1
+ \documentclass{scrartcl}
2
+ \usepackage{url}
3
+ \usepackage{subfig}
4
+
5
+ \begin{document}
6
+
7
+ \author{James Tunnell}
8
+ \title{brevity\\
9
+ \begin{LARGE}
10
+ Synatx Guide
11
+ \end{LARGE}}
12
+ \maketitle
13
+ \tableofcontents
14
+
15
+ \section{Introduction}
16
+ \verb|brevity| is a music notation syntax that aims to be simple, compact, human-readable, and human-editable. A \verb|brevity| file consists of commands and expressions.
17
+
18
+ \section{Command Syntax}
19
+ Commands in \verb|brevity| look just like commands in \LaTeX. First, a command starts with a backslash '\verb|\|' followed by the command name. Command names consist only of lowercase letters (examples: \verb|\float|, \verb|\exercise|). Then, if the command takes arguments, they are passed in by a series of open-close curly braces '\verb|{}|'. Here is an example command that takes two arguments:
20
+
21
+ \begin{tabbing}
22
+ \hspace{0.5in}\= \verb|\step{left}{right}|
23
+ \end{tabbing}
24
+
25
+ \section{Expression Syntax}
26
+ An expression is a series of space-separated music elements or subexpressions. Expressions containing notes have non-zero duration.
27
+
28
+ \section{Note and Rests}
29
+ The basic units of a score are the notes and rests, because they define how time is broken up and what should happen during each time chunk. For a rest, of course, nothing should happen.
30
+
31
+ In brevity, notes and rests both define a duration, and a note additionally define a number of pitches to be played for the duration. So a rest is really just a note where no pitches are given to play. Of course, multiple pitches mean a note is really a chord.
32
+ attached
33
+ \subsection{Syntax Overview}
34
+ The syntax for a single note or rest is shown below, where the number of pitches can be zero or more.
35
+ \begin{center}
36
+ \framebox{D} \framebox{\framebox{P} \framebox{L} , \framebox{P} \framebox{L} , \ldots} \framebox{A}
37
+ \end{center}
38
+ \begin{tabbing}
39
+ \hspace{1.85in}\= D: duration \\
40
+ \> P: pitch \\
41
+ \> L: link \\
42
+ \> A: accent \\
43
+ \end{tabbing}
44
+ To make a sequence of notes/rests, simply string notes together, separated by spaces.
45
+
46
+ \subsection{Durations}
47
+ Notes and rests both need to declare some duration. Duration is represented by a rational number (i.e. a fraction), as in '3/4' or '2/3'. The numerator can be omitted, as in '/4', and then a 1 is assumed. This is convenient for quarter notes, eight notes, etc. Or, the denominator (and forward-slash) can be omitted, as in '1/' or '1' for a whole note.
48
+
49
+ \subsection{Pitches}
50
+ Every note can have zero or more pitches attached to it (of course, zero pitches would make the note a rest). Each pitch is given by two components: pitch class and octave. Pitch class is represented by a letter from \verb|A| to \verb|G| and an optional \verb|#| (sharp) or \verb|b| (flat). Following the pitch class, the octave is given, represented by a single decimal digit (\verb|0| to \verb|9|). For example: '\verb|C4|', '\verb|Eb2|', and '\verb|F#5|'.
51
+
52
+ \subsection{Accents}
53
+ Accents control the duration of a note and the shape of note loudness (like the envelope of a signal). Accents are represented by a single character placed at the end of the note body, after all the pitches and links. Simply omit the accent character to place a note without any accent.
54
+
55
+ The supported accent types and the characters used to represent them are summarized in Table~\ref{tab:accent_characters}. A short description of the effect of each accent type follows:
56
+ \begin{itemize}
57
+ \item Staccato: separated from next note
58
+ \item Staccatissimo: very separated from next note
59
+ \item Accent (marcato): emphasize beginning and then taper off rather quickly
60
+ \item Martellato (hammered): loud as an accent mark and short as a staccato
61
+ \item Tenuto: Played at full length or longer. When under a slur, note is separated with a little space from surrounding notes
62
+ \end{itemize}
63
+
64
+ \begin{table}[ht]
65
+ \begin{center}
66
+ \begin{tabular}{ l | l }
67
+ \hline
68
+ \textbf{Accent type} & \textbf{Marker} \\ \hline
69
+ Staccato & \verb|.| \hspace{.1cm} period \\ \hline
70
+ Staccatissimo & \verb|'| \hspace{.1cm} apostrophe \\ \hline
71
+ Accent (marcato) & \verb|>| \hspace{.1cm} greater than \\ \hline
72
+ Martellato (hammered) & \verb|^| \hspace{.1cm} caret \\ \hline
73
+ Tenuto (sustained) & \verb|_| \hspace{.1cm} underscore \\ \hline
74
+ \hline
75
+ \end{tabular}
76
+ \end{center}
77
+ \caption{Accent types and markers}
78
+ \label{tab:accent_characters}
79
+ \end{table}
80
+
81
+ As an example, some stocatto notes:
82
+ \begin{center}
83
+ \verb|/4C5. /4D5. /4E5.|
84
+ \end{center}
85
+
86
+ \subsection{Relationships}
87
+ Note relationships (or links) control the transition between sequential notes. A link is just between two notes, and won't have any other effect outside those notes. A link marker is placed just after a note pitch. Here is an example of a passage connected by slurs:
88
+ \begin{center}
89
+ \verb|/4C4=C4 /16C4=E4 /16E4=F4 /4F4|
90
+ \end{center}
91
+ Here is an example using slur/tie links:
92
+ \begin{center}
93
+ \verb|/4C4=C4 /16C4=E4,F4=G4 /16E4,G4|
94
+ \end{center}
95
+ Table~\ref{tab:link_markers} lists the supported link types and the characters used to represent them. Here is a description of each supported link type:
96
+ \begin{itemize}
97
+ \item Slur/tie: connects two pitches with no separation and no rearticulation. The same link marker is used for a slur or tie since they're effectively the same relationship.
98
+ \item Legato: connects two pitches with no separation and with rearticulation.
99
+ \item Glissando: connect two pitches by articulating all the pitches in between.
100
+ \item Portamento: continuously glide between pitches, without rearticulation.
101
+ \end{itemize}
102
+ \begin{table}[ht]
103
+ \begin{center}
104
+ \begin{tabular}{ l | l}
105
+ \hline
106
+ \textbf{Link type} & \textbf{Marker} \\ \hline
107
+ Slur/Tie & \verb|=| \hspace{.1cm} equals \\ \hline
108
+ Legato & \verb|-| \hspace{.1cm} minus \\ \hline
109
+ Glissando & \verb|~| \hspace{.1cm} tilde \\ \hline
110
+ Portamento & \verb|/| \hspace{.1cm} forward-slash \\ \hline
111
+ \hline
112
+ \end{tabular}
113
+ \end{center}
114
+ \caption{Link types and markers}
115
+ \label{tab:link_markers}
116
+ \end{table}
117
+
118
+ \section{Sequences}
119
+ A sequence is a series of notes/rests. A sequence is declared by placing notes/rests together and separating by spaces, as in
120
+ \begin{tabbing}
121
+ \hspace{0.5in}\= \verb|/4D6 /4A5 /16Ab6 3/16Bb6=Bb6 /4Bb6|
122
+ \end{tabbing}
123
+
124
+ A sequence can either be labeled for later reused, or modified to produce a new sequence.
125
+
126
+ \subsection{Labeling Sequences}
127
+ Sequences are assigned to a label using a \verb|\beginsequence| statement, followed by a a note sequence, as in
128
+ \begin{tabbing}
129
+ \hspace{0.5in}\= \verb|\expr{my_label}{/8C5 /8D5 /8E5 /8D5 /2C5}|
130
+ \end{tabbing}
131
+
132
+ Once labeled, the label can be used in place of the sequence.
133
+
134
+ \subsubsection{Continuing Labeled Sequences}
135
+ A labeled sequence can be continued later, which will add more notes onto the end of the sequence. This might also be called concatenation.
136
+
137
+ A labeled sequence is continued using a \verb|\expr| statement, as in
138
+ \begin{tabbing}
139
+ \hspace{0.5in}\= \verb|\expr{bass}{ /8C4 /8C4 /8C4 /8C4 }| \\
140
+ \> \verb|\expr{bass}{ /8E4 /8E4 /8E4 /8E4 }|
141
+ \end{tabbing}
142
+
143
+ This allows a sequence to span over multiple lines by starting each line with the same label. But continuing lines do not have to be consecutive, as long as the same label is used for each continuing line. So, multiple parts can be interleaved as follows:
144
+ \begin{tabbing}
145
+ \hspace{1in}\= \verb| \expr{bass}{ /4C4 /4C4 /4C4 /4C4 }| \\
146
+ \> \verb| \expr{lead}{ /2E6 /2E6 }| \\
147
+ \> ~\\
148
+ \> \verb| \expr{bass}{ /4E4 /4E4 /4E4 /4E4 }| \\
149
+ \> \verb| \expr{lead}{ /2G6 /2G6 }| \\
150
+ \end{tabbing}
151
+
152
+ \subsection{Modifying Sequences}
153
+ Sequences can be modified, which results in a new sequence. If a sequence is not labeled, it needs to be grouped using parentheses before it can be modified, as in
154
+ \begin{center}
155
+ \verb|( /4C5 /4D5 )|
156
+ \end{center}
157
+
158
+ Sequences can be duplicated, transposed, stretched, and composed. Additionally, sequence modifications can be chained one right after the other.
159
+
160
+ \subsubsection{Duplicating Sequences}
161
+ A sequence can be replaced some number of duplicate sequences by following it with \verb|:n|, where $n$ indicates the number of duplicates.
162
+
163
+ For example, to replace a sequence with $2$ duplicate sequences (i.e., repeat once),
164
+ \begin{center}
165
+ \verb|( /8C5 /8C#5 ):2| \ is equivalent to \verb|/8C5 /8C#5 /8C5 /8C#5|
166
+ \end{center}
167
+
168
+ \subsubsection{Transposing Sequences}
169
+ A sequence can be transposed by appending a +/- and some number of semitones (half-steps) to transpose by. For example, to transpose up four semitones:
170
+ \begin{center}
171
+ \verb| ( /4C5 /4D5 )+4| \ would yield \verb|/4E5 /4F#5|
172
+ \end{center}
173
+
174
+ Or, to transpose down by four semitones:
175
+ \begin{center}
176
+ \verb| ( /4C5 /4D5 )-4| \ would yield \verb|/4Ab4 /4Bb4|
177
+ \end{center}
178
+ The result is the sequence transposed up/down some number of semitones.
179
+
180
+ \subsubsection{Stretching Sequences}
181
+ Sequences can be stretched in two ways: stretching to equal some given total duration, or stretching by multiplying all note durations by some factor.
182
+
183
+ \paragraph{Stretching to equal some duration}~\\
184
+ To stretch so that total note duration equals some given note duration. This is done by following a sequence with \verb|:r|, where $r$ is some note duration.
185
+
186
+ For example, to turn a sequence of two quarter notes into two half notes,
187
+ \begin{center}
188
+ \verb| ( /4C5 /4D5 )=1| \ is equivalent to \verb|/2C5 /2D5|
189
+ \end{center}
190
+
191
+ Or to make a half note triplet from three quarter notes,
192
+ \begin{center}
193
+ \verb| ( /4A4 /4B4 /4C4 )=/2| \ is equivalent to \verb|/6A4 /6B4 /6C4|
194
+ \end{center}
195
+
196
+ If the sequence contains notes of different durations, each note keeps the same ratio to total duration. For example,
197
+ \begin{center}
198
+ \verb| ( /2Eb5 /4Bb5 3/4Bb5 )=1| \ is equivalent to \verb| /3Eb5 /6Bb5 /2Bb5|
199
+ \end{center}
200
+
201
+ \paragraph{Stretching by multiplying note durations}~\\
202
+ A sequence can be stretched by multiplying the note durations by some multiplicative factor. This is done by following a sequence with \verb|*r|, where $r$ is some rational number, that can be formatted just like a note duration.
203
+
204
+ For example, to multiply durations by 2,
205
+ \begin{center}
206
+ \verb| ( /4C5 /4D5 /2E5 )*2| \ is equivalet to \verb|/2C5 /2D5 1E5|
207
+ \end{center}
208
+
209
+ \subsubsection{Composing Sequences}
210
+ Sequences can be composed together to create a new sequence. This is done implicitly just by following one sequence with more notes or sequences.
211
+
212
+ For example,
213
+ \begin{center}
214
+ \verb|(/4A5 /4C5):2 1Eb5| \ would be equivalent to \verb|/4A5 /4C5 /4A5 /4C5 1Eb5|
215
+ \end{center}
216
+
217
+ Of course, labels can be used in places of sequences, as in
218
+ \begin{tabbing}
219
+ \hspace{1in}\= \verb|\expr{seq1}{ /4F4 /4G4 }| \\
220
+ \> \verb|\expr{seq2}{ seq1:2 seq1+2 }|
221
+ \end{tabbing}
222
+
223
+ \subsubsection{Chaining Modifications}
224
+ Sequence modifications can be chained, so each modification is applied on the new sequence resulting from the previous modification. For example,
225
+ \begin{center}
226
+ \verb|(/4F4 /4G4)+2:2| \ is equivalent to \verb|/4G4 /4A4 /4G4 /4A4|
227
+ \end{center}
228
+
229
+ \section{Dynamics}
230
+ Dynamics is concerned with absolute base loudness over an entire part. This is in contrast to note accents which affect the relative loudness shape of a single note. Dynamic levels are represented by markers which you might expect: \verb|ppp|, \verb|pp|, \verb|p|, \verb|mp|, \verb|mf|, \verb|f|, \verb|ff|, and \verb|fff|.
231
+
232
+ \subsection{Immediate Changes}
233
+ To immediately change dynamic level, simply place a dynamic marker by itself before a note. The change will take effect immediately at the start of the note. Here is an example of an immediate dynamic change: \begin{center}
234
+ \verb| p /8C6 /8C6 /4F6 f /8D6 /8D6 /4G6 |
235
+ \end{center}
236
+
237
+ \subsection{Gradual Changes}
238
+ Dynamic level can be gradually changed over time with a crescendo or decrescendo. By preceding the dynamic level with a \verb|<| or \verb|>|, change will be marked as a gradual change from whatever the previous dynamic level was. Here is an example of a gradual dynamic change:
239
+ \begin{center}
240
+ \verb| p /8C6 /8C6 /4F6 <f /8D6 /8D6 /4G6 |
241
+ \end{center}
242
+
243
+ \section{Scores}
244
+ The final goal of the music notation file is to compose a score, which has one or more parts. All of the parts declared anywhere in the file will be added to the score. Besides at least one part, the only other information that is required is a starting tempo. The following subsections cover parts and starting tempo.
245
+
246
+ \subsection{Parts}
247
+ A \verb|\part| statement associates a name with a musical part. The part consists of: start dynamic, dynamic changes, and notes. The start dynamic is given as a separate parameter, and the dynamic changes and notes are given together in the expression parameter.
248
+
249
+ Here is an example of the syntax:
250
+
251
+ \begin{tabbing}
252
+ \hspace{0.5in}\= \verb|\part{Lead Guitar}{mf}{/8D5 /8Eb5 /4F5 3/4C5}|
253
+ \end{tabbing}
254
+
255
+ Notice in the example there is a starting dynamic level, mezzo forte, just before the note sequence. Here is another example, this time using labeled expressions:
256
+
257
+ \begin{tabbing}
258
+ \hspace{0.5in}\= \verb|\part{Piano}{pp}{sectA sectB:2 sectA}| \\
259
+ \end{tabbing}
260
+
261
+ Parts can be declared anywhere in the file, and they will be added to the score.
262
+
263
+ \subsection{Starting Tempo}
264
+ A starting tempo is defined by a \verb|\starttempo| statement, which can be placed anywhere in the file. The tempo given must inculde the beat duration. Here is an example, with a tempo of 120 bpm and a quarter note beat duration:
265
+
266
+ \begin{tabbing}
267
+ \hspace{0.5in}\= \verb|\starttempo{@120,/4}|
268
+ \end{tabbing}
269
+
270
+ \section{Comments}
271
+ Any line starting with a number sign `\verb|#|' will be considered a comment and ignored.
272
+
273
+ \end{document}
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'brevity'
3
+
4
+ describe Brevity do
5
+ it "should have a VERSION constant" do
6
+ subject.const_get('VERSION').should_not be_empty
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe 'Commands.expr' do
4
+ before :each do
5
+ @tester = CommandTester.new
6
+ end
7
+
8
+ context 'given valid expression' do
9
+ it 'should assign primitives to given label under @env[ENV_EXPRS]' do
10
+ tgt = [ Note::Half.new([C2]), Note::Half.new, Note::Quarter.new([E2]) ]
11
+ @tester.expr("hello","/2C2 /2 /4D2+2")
12
+ @tester.env[Commands::ENV_EXPRS]["hello".to_sym].should eq(tgt)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe 'Commands.meter' do
4
+ before :each do
5
+ @tester = CommandTester.new
6
+ end
7
+
8
+ context 'given valid beats per measure and beat dur strings' do
9
+ it 'should assign tempo to @env[ENV_START_METER]' do
10
+ @tester.meter("4","1/4")
11
+ @tester.env[Commands::ENV_START_METER].should eq(Meter.new(4,"1/4".to_r))
12
+ end
13
+ end
14
+
15
+ context 'given an invalid bpm or beatdur string' do
16
+ it 'should raise ArgumentError' do
17
+ expect { @tester.meter("4.5","1/4") }.to raise_error ArgumentError
18
+ expect { @tester.meter("4","1/4.2") }.to raise_error ArgumentError
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe 'Commands.part' do
4
+ before :each do
5
+ @tester = CommandTester.new
6
+ end
7
+
8
+ context 'given valid expression' do
9
+ it 'should assign primitives to given label under @env[ENV_EXPRS]' do
10
+ notes = [ Note::Half.new([C2]), Note::Half.new, Note::Quarter.new([E2]) ]
11
+ tgt = Part.new(notes: notes, dynamic_profile: Profile.new(Dynamics::MF))
12
+ @tester.part("hello","mf","/2C2 /2 /4D2+2")
13
+ @tester.env[Commands::ENV_PARTS]["hello"].should eq(tgt)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe 'Commands.tempo' do
4
+ before :each do
5
+ @tester = CommandTester.new
6
+ end
7
+
8
+ context 'given valid bpm and beatdur strings' do
9
+ it 'should assign tempo to @env[ENV_START_TEMPO]' do
10
+ @tester.tempo("120")
11
+ @tester.env[Commands::ENV_START_TEMPO].should eq(120)
12
+ end
13
+ end
14
+
15
+ context 'given an invalid bpm or beatdur string' do
16
+ it 'should raise ArgumentError' do
17
+ expect { @tester.tempo("120.5") }.to raise_error ArgumentError
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,46 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Itemization do
4
+ describe '#initialize' do
5
+ it 'should calculate duration from notes' do
6
+ {
7
+ [] => 0,
8
+ [ Note::Quarter.new ] => "1/4".to_r,
9
+ [ Note::Quarter.new, Note::Half.new ] => "3/4".to_r,
10
+ }.each do |notes,tgt|
11
+ Itemization.new(notes: notes).duration.should eq(tgt)
12
+ end
13
+ end
14
+ end
15
+
16
+ describe '#append!' do
17
+ before :all do
18
+ @a = Itemization.new(
19
+ notes: [ Note::Half.new, Note::Half.new ]
20
+ )
21
+ @b_start_dyn_change = Change::Immediate.new(Dynamics::PPP)
22
+ @b = Itemization.new(
23
+ dynamic_changes: { 0.to_r => @b_start_dyn_change },
24
+ notes: [ Note::Quarter.new, Note::Quarter.new ]
25
+ )
26
+ @c = @a.append @b
27
+ end
28
+
29
+ it 'should append notes from given other' do
30
+ @c.notes.size.should eq(@a.notes.size + @b.notes.size)
31
+ end
32
+
33
+ it 'should add durations' do
34
+ @c.duration.should eq(@a.duration + @b.duration)
35
+ end
36
+
37
+ it 'should insert dynamic changes from given other, with offset' do
38
+ @c.dynamic_changes.should have_key(@a.duration)
39
+ @c.dynamic_changes[@a.duration].should eq(@b_start_dyn_change)
40
+ end
41
+
42
+ it 'should insert tempo changes from given other, with offset' do
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe DynamicNode do
4
+ {
5
+ "ppp" => Dynamics::PPP,
6
+ "pp" => Dynamics::PP,
7
+ "p" => Dynamics::P,
8
+ "mp" => Dynamics::MP,
9
+ "mf" => Dynamics::MF,
10
+ "f" => Dynamics::F,
11
+ "ff" => Dynamics::FF,
12
+ "fff" => Dynamics::FFF,
13
+ }.each do |str, tgt|
14
+ parser = DynamicParser.new
15
+ res = parser.parse(str)
16
+
17
+ it "should parse as DynamicNode" do
18
+ res.should be_a DynamicNode
19
+ end
20
+
21
+ describe '#to_dynamic' do
22
+ d = res.to_dynamic
23
+ it 'should produce a Dynamic' do
24
+ d.should be_a Dynamic
25
+ end
26
+
27
+ it 'should produce a value matching input str' do
28
+ d.should eq tgt
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe DynamicParser do
4
+ before :all do
5
+ @parser = DynamicParser.new
6
+ end
7
+
8
+ context 'valid dynamic level' do
9
+ it 'should parse' do
10
+ ["ppp","pp","p","mp","mf","f","ff","fff"].each do |dynamic_level|
11
+ @parser.parse(dynamic_level).should_not be nil
12
+ end
13
+ end
14
+ end
15
+
16
+ context 'invalid dynamic level' do
17
+ it 'should not parse' do
18
+ ["pf","fp"].each do |dynamic_level|
19
+ @parser.parse(dynamic_level).should be nil
20
+ end
21
+ end
22
+ end
23
+ end