brevity 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.rdoc +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +27 -0
- data/Rakefile +67 -0
- data/bin/debrief +56 -0
- data/brevity.gemspec +28 -0
- data/examples/alley_cat.br +50 -0
- data/examples/hip.br +15 -0
- data/examples/missed_connection.br +6 -0
- data/examples/song1.br +11 -0
- data/examples/song2.br +10 -0
- data/examples/twinkle.br +18 -0
- data/lib/brevity/commands/constants.rb +18 -0
- data/lib/brevity/commands/expr.rb +17 -0
- data/lib/brevity/commands/meter.rb +20 -0
- data/lib/brevity/commands/part.rb +47 -0
- data/lib/brevity/commands/tempo.rb +13 -0
- data/lib/brevity/itemization.rb +73 -0
- data/lib/brevity/parsing/expression/dynamic.rb +280 -0
- data/lib/brevity/parsing/expression/dynamic.treetop +41 -0
- data/lib/brevity/parsing/expression/dynamic_nodes.rb +55 -0
- data/lib/brevity/parsing/expression/expression.rb +429 -0
- data/lib/brevity/parsing/expression/expression.treetop +39 -0
- data/lib/brevity/parsing/expression/expression_nodes.rb +26 -0
- data/lib/brevity/parsing/expression/gradual.rb +44 -0
- data/lib/brevity/parsing/expression/gradual.treetop +9 -0
- data/lib/brevity/parsing/expression/gradual_node.rb +11 -0
- data/lib/brevity/parsing/expression/label.rb +75 -0
- data/lib/brevity/parsing/expression/label.treetop +9 -0
- data/lib/brevity/parsing/expression/label_node.rb +15 -0
- data/lib/brevity/parsing/expression/sequence.rb +130 -0
- data/lib/brevity/parsing/expression/sequence.treetop +12 -0
- data/lib/brevity/parsing/expression/sequence_node.rb +14 -0
- data/lib/brevity/parsing/file/command.rb +216 -0
- data/lib/brevity/parsing/file/command.treetop +17 -0
- data/lib/brevity/parsing/file/command_node.rb +11 -0
- data/lib/brevity/parsing/file/comment.rb +178 -0
- data/lib/brevity/parsing/file/comment.treetop +21 -0
- data/lib/brevity/parsing/file/comment_node.rb +3 -0
- data/lib/brevity/parsing/file/file.rb +152 -0
- data/lib/brevity/parsing/file/file.treetop +16 -0
- data/lib/brevity/parsing/file/file_node.rb +7 -0
- data/lib/brevity/parsing/file/path.rb +235 -0
- data/lib/brevity/parsing/file/path.treetop +12 -0
- data/lib/brevity/parsing/modifiers/duplicate_modifier.rb +65 -0
- data/lib/brevity/parsing/modifiers/duplicate_modifier.treetop +11 -0
- data/lib/brevity/parsing/modifiers/duplicate_modifier_node.rb +8 -0
- data/lib/brevity/parsing/modifiers/modifier.rb +64 -0
- data/lib/brevity/parsing/modifiers/modifier.treetop +13 -0
- data/lib/brevity/parsing/modifiers/stretch_modifier.rb +69 -0
- data/lib/brevity/parsing/modifiers/stretch_modifier.treetop +11 -0
- data/lib/brevity/parsing/modifiers/stretch_modifier_node.rb +21 -0
- data/lib/brevity/parsing/modifiers/transpose_modifier.rb +69 -0
- data/lib/brevity/parsing/modifiers/transpose_modifier.treetop +11 -0
- data/lib/brevity/parsing/modifiers/transpose_modifier_node.rb +13 -0
- data/lib/brevity/parsing/note/accent.rb +44 -0
- data/lib/brevity/parsing/note/accent.treetop +9 -0
- data/lib/brevity/parsing/note/duration.rb +203 -0
- data/lib/brevity/parsing/note/duration.treetop +23 -0
- data/lib/brevity/parsing/note/duration_nodes.rb +19 -0
- data/lib/brevity/parsing/note/link.rb +69 -0
- data/lib/brevity/parsing/note/link.treetop +11 -0
- data/lib/brevity/parsing/note/link_node.rb +19 -0
- data/lib/brevity/parsing/note/note.rb +300 -0
- data/lib/brevity/parsing/note/note.treetop +30 -0
- data/lib/brevity/parsing/note/note_nodes.rb +77 -0
- data/lib/brevity/parsing/note/pitch.rb +81 -0
- data/lib/brevity/parsing/note/pitch.treetop +9 -0
- data/lib/brevity/parsing/note/pitch_node.rb +50 -0
- data/lib/brevity/parsing/numbers/nonnegative_integer.rb +53 -0
- data/lib/brevity/parsing/numbers/nonnegative_integer.treetop +9 -0
- data/lib/brevity/parsing/numbers/positive_integer.rb +91 -0
- data/lib/brevity/parsing/numbers/positive_integer.treetop +15 -0
- data/lib/brevity/read_file.rb +18 -0
- data/lib/brevity/score_maker.rb +64 -0
- data/lib/brevity/version.rb +4 -0
- data/lib/brevity.rb +53 -0
- data/manuals/brevity.pdf +0 -0
- data/manuals/brevity.tex +273 -0
- data/spec/brevity_spec.rb +8 -0
- data/spec/commands/expr_spec.rb +15 -0
- data/spec/commands/meter_spec.rb +21 -0
- data/spec/commands/part_spec.rb +16 -0
- data/spec/commands/tempo_spec.rb +20 -0
- data/spec/itemization_spec.rb +46 -0
- data/spec/parsing/expression/dynamic_nodes_spec.rb +32 -0
- data/spec/parsing/expression/dynamic_spec.rb +23 -0
- data/spec/parsing/expression/expression_nodes_spec.rb +87 -0
- data/spec/parsing/expression/expression_spec.rb +85 -0
- data/spec/parsing/expression/gradual_spec.rb +10 -0
- data/spec/parsing/expression/label_node_spec.rb +13 -0
- data/spec/parsing/expression/label_spec.rb +35 -0
- data/spec/parsing/file/command_node_spec.rb +29 -0
- data/spec/parsing/file/command_spec.rb +18 -0
- data/spec/parsing/file/comment_spec.rb +14 -0
- data/spec/parsing/file/file_node_spec.rb +19 -0
- data/spec/parsing/file/file_spec.rb +30 -0
- data/spec/parsing/modifiers/modifier_nodes_spec.rb +25 -0
- data/spec/parsing/modifiers/modifier_parsers_spec.rb +20 -0
- data/spec/parsing/note/accent_spec.rb +27 -0
- data/spec/parsing/note/duration_nodes_spec.rb +79 -0
- data/spec/parsing/note/duration_spec.rb +69 -0
- data/spec/parsing/note/link_node_spec.rb +30 -0
- data/spec/parsing/note/link_spec.rb +23 -0
- data/spec/parsing/note/note_nodes_spec.rb +82 -0
- data/spec/parsing/note/note_spec.rb +188 -0
- data/spec/parsing/note/pitch_node_spec.rb +48 -0
- data/spec/parsing/note/pitch_spec.rb +23 -0
- data/spec/parsing/numbers/nonnegative_integer_spec.rb +11 -0
- data/spec/parsing/numbers/positive_integer_spec.rb +17 -0
- data/spec/spec_helper.rb +112 -0
- 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
|
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'
|
data/manuals/brevity.pdf
ADDED
Binary file
|
data/manuals/brevity.tex
ADDED
@@ -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,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
|