Previous vignettes introduced the noteworthy
and
noteinfo
classes.
First create a music object from noteworthy
and
notinfo
objects. An object of each class can be combined to
form a music
-class object.
notes <- "c d e f g a b ceg~ ceg"
info <- "8*8 1"
x <- as_music(notes, info)
x
#> <Music string>
#> Format: space-delimited time
#> Values: c8 d8 e8 f8 g8 a8 b8 <ceg~>8 <ceg>1
as_music()
offers several additional arguments. Music
objects also store key signature, time signature and tempo. They include
default values: key of C, 4/4 common time, and 60 half note beats per
measure. However, unless you are transcribing your data to sheet music,
you can probably ignore these object attributes entirely. Optionally you
can also include a lyrics object.
Here is a closer look at the music object.
summary(x)
#> <Music string>
#> Timesteps: 9 (7 notes, 2 chords)
#> Octaves: tick
#> Accidentals: flat
#> Key signature: c
#> Time signature: 4/4
#> Tempo: 2 = 60
#> Lyrics: NA
#> Format: space-delimited time
#> Values: c8 d8 e8 f8 g8 a8 b8 <ceg~>8 <ceg>1
Some of the summary information is the same as seen with noteworthy objects and more is added that is unique to music objects. Just to note, octave tick format is explicitly shown as it is with noteworthy objects, though it is the only valid format for music objects.
Music analysis generally requires disentangling time and sound. For this reason, building a music object from notes and note info is not directly useful for analysis. It is a convenient object structure, however.
Aside from generic methods and a handful of other functions that make sense to apply to any of these three classes, most functions in the package that operate on noteworthy strings intentionally do not accept a music object directly. This helps keep in mind that it is not ideal to continually deconstruct and reconstruct a music object just to perform operations on one of the two main component parts.
You can split a music object into a list with
music_split()
or you can extract the relevant piece with
one of the other functions below.
music_split(x)
#> $notes
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: c d e f g a b <ceg~> <ceg>
#>
#> $info
#> <Note info string>
#> Format: space-delimited time
#> Values: 8 8 8 8 8 8 8 8 1
#>
#> $lyrics
#> [1] NA
#>
#> $key
#> [1] "c"
#>
#> $time
#> [1] "4/4"
#>
#> $tempo
#> [1] "2 = 60"
music_notes(x)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: c d e f g a b <ceg~> <ceg>
music_info(x)
#> <Note info string>
#> Format: space-delimited time
#> Values: 8 8 8 8 8 8 8 8 1
music_key(x)
#> [1] "c"
music_time(x)
#> [1] "4/4"
music_tempo(x)
#> [1] "2 = 60"
The real value is in building brand new music objects from a single
character string input. This method of music object construction allows
for relatively efficient data entry. There can potentially be a lot less
typing involved when directly entering music syntax for a new piece of
music if you do so with the music class rather than creating
noteworthy
and noteinfo
objects
separately.
This is also a good place to mention that like with
noteworthy
and noteinfo
strings, a handy
adjective can be used to check the validity of the music input.
x <- "a,4*5 b,4- c4 cgc'e'~4 cgc'e'1 e'4 c' g c ce'1"
musical(x)
#> [1] TRUE
x <- as_music(x)
x
#> <Music string>
#> Format: space-delimited time
#> Values: a,4 a,4 a,4 a,4 a,4 b,4- c4 <cgc'e'~>4 <cgc'e'>1 e'4 c'4 g4 c4 <ce'>1
Notice that each timestep remains space-delimited (vectors are also allowed). At each timestep, the notes and the note info are bound together unambiguously with no delimiter. This is why tick octave numbering is required for music objects.
You can see that the expansion operator *
is still
allowed and continues to be place at the right end of a timestep.
Do not forget that tied notes indicated with the ~
are
part of the note and not part of the note info! Order matters here.
You can also include rests r
and silent rests
s
.
Another benefit to this approach for entering new data is that
durations are inferred from the previous timesteps if not provided. You
do not need to explicitly enter a duration value until there is a
change. This is not possible with noteinfo
because it could
result in simply removing timesteps, but here there is always a note
entry present to mark each timestep.
This one is a bit of an aside, but if string numbers are provided for
a stringed and fretted instrument context (see example below), there is
also no need to specify more than the starting string number (lowest
pitch/highest number) unless there are non-consecutive strings. The
purpose of the music
class is not transcription, but to
corral related data together for analysis.
However, music objects do lend themselves seamlessly to some transcription tasks, which is why string numbering is allowed even though it is not focused on. You can see an example here. It is the same as above, but string numbers are indicated following a semicolon delimiter. Like durations, string numbers also repeat silently. Any inferred repeated information across timesteps carries over rests, including string numbers. String numbers at a rest timestep are ignored gracefully.
x <- "a,4;5*5 b,4- c4 cgc'e'~4 cgc'e'1 e'4;2 c';3 g;4 c;5 ce'1;51"
x <- as_music(x)
x
#> <Music string>
#> Format: space-delimited time
#> Values: a,4 a,4 a,4 a,4 a,4 b,4- c4 <cgc'e'~>4 <cgc'e'>1 e'4 c'4 g4 c4 <ce'>1
#> [1] "5" "5" "5" "5" "5" "5" "5" "5432" "5432" "2"
#> [11] "3" "4" "5" "51"
Lyrics data returns as NA
when requested if a lyrics
object was not provided during the construction of a music object. In
contrast, string number information is not represented in the object at
all if not provided when constructed, since it is not relevant to the
general purpose of the class and is only used for specific transcription
use cases.
summary(x)
#> <Music string>
#> Timesteps: 14 (11 notes, 3 chords)
#> Octaves: tick
#> Accidentals: flat
#> Key signature: c
#> Time signature: 4/4
#> Tempo: 2 = 60
#> Lyrics: NA
#> Strings: 5 5 5 5 5 5 5 5432 5432 2...
#> Format: space-delimited time
#> Values: a,4 a,4 a,4 a,4 a,4 b,4- c4 <cgc'e'~>4 <cgc'e'>1 e'4 c'4 g4 c4 <ce'>1
music_split(x)
#> $notes
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: a, a, a, a, a, b, c <cgc'e'~> <cgc'e'> e' c' g c <ce'>
#>
#> $info
#> <Note info string>
#> Format: space-delimited time
#> Values: 4 4 4 4 4 4- 4 4 1 4 4 4 4 1
#>
#> $string
#> [1] "5" "5" "5" "5" "5" "5" "5" "5432" "5432" "2"
#> [11] "3" "4" "5" "51"
#>
#> $lyrics
#> [1] NA
#>
#> $key
#> [1] "c"
#>
#> $time
#> [1] "4/4"
#>
#> $tempo
#> [1] "2 = 60"
As you can see above, the print statement does not change. But the other functions above reveal that the additional information is now contained in the music object.
While many functions that perform operations on noteworthy and note
info objects require those objects exclusively, meaning that you are
supposed to use functions like music_notes()
and
music_info()
to extract the given component from a music
object, there are several functions that are sensible to apply directly
to music objects.
First and foremost, the same generic methods are implemented for music objects. Square bracket subsetting will even subset any lyrics or string numbers that are present in the object, not just the notes and note info.
tail(x)
#> <Music string>
#> Format: space-delimited time
#> Values: <cgc'e'>1 e'4 c'4 g4 c4 <ce'>1
x[8:9]
#> <Music string>
#> Format: space-delimited time
#> Values: <cgc'e'~>4 <cgc'e'>1
y <- rep(x[9:10], each = 2)
y
#> <Music string>
#> Format: space-delimited time
#> Values: <cgc'e'>1 <cgc'e'>1 e'4 e'4
#> [1] "5432" "5432" "2" "2"
There are several time functions that work nicely on music objects.
They were not introduced in the vignette on note info because they are
more relevant here. Most require the level of information contained in a
music object because they deal in real time such as seconds. This works
with music objects because they contain a tempo value. Note info objects
do not contain this level of information. They can be passed to
functions that require a tempo value, as long as you also pass a tempo
to any function with a tempo
argument.
The example below is intended to be complex, including many changes in note duration and other note info. There is not much to be gained by writing it as a single string in this case. For clarity, it starts from separate notes and note info. See the help documentation for more examples and details.
a <- notate("t8x", "Start here")
notes <- "a, b, c d e f g# a r ac'e' a c' e' c' r*3 ac'e'~ ac'e'"
info <- paste(a, "t8x t8-. 16 4.. 16- 16 2^ 2 4. 8( 4)( 4) 8*4 1 1")
x <- as_music(notes, info)
n_measures(x)
#> [1] 5.375
n_beats(x)
#> [1] 21.5
bpm(x)
#> [1] 120
seconds(x)
#> [1] 10.75
#> # A tibble: 5 × 2
#> measure steps
#> <int> <int>
#> 1 1 8
#> 2 2 2
#> 3 3 4
#> 4 4 4
#> 5 5 1
#> [1] 2
#> [1] 0.1666667 0.1666667 0.1666667 0.1250000 0.8750000 0.1250000 0.1250000
#> [8] 1.0000000 1.0000000 0.7500000 0.2500000 0.5000000 0.5000000 0.2500000
#> [15] 0.2500000 0.2500000 0.2500000 2.0000000 2.0000000
#> [1] 0.0000000 0.1666667 0.3333333 0.5000000 0.6250000 1.5000000 1.6250000
#> [8] 1.7500000 2.7500000 3.7500000 4.5000000 4.7500000 5.2500000 5.7500000
#> [15] 6.0000000 6.2500000 6.5000000 6.7500000 8.7500000
You may notice above that the table returned by
steps_per_measure()
containing the number of timesteps per
measure of music does not have an entry for measure six. There is a
measure six. You can see from n_measures()
being greater
than five that a sixth measure does begin even if the duration of all
the time steps does not yield a complete sixth measure. The reason there
is no entry in the table is because this measure has no timesteps. The
final chord in the sequence lasts for a whole measure, beginning in
measure five and carrying over to six, but no timestep starts in
six.
Combining note info with a tempo is powerful because it allows you to move from beats into real time. The music object is a convenient structure for keeping sound and time together and splitting and extracting parts of the object for independent computations is easy when using the various supporting functions.