Skip to contents

Typically you have raw onset data when loading files with onsetsync which might not any other information within them (beat subdivisions, etc.). Here is an example of how you can add reference beat subdivisions and calculate reference beats such as mean of all onsets in a sub-division.

Load raw onsets from OSF

library(onsetsync)
require(httr)
CSS_Song2_Onset <- get_OSF_csv('8a347') # Get onsets
print(head(CSS_Song2_Onset[,1:8,]))
##    Piece Label.SD SD Clave_. Section Isochronous.SD.Time Clave     Bass
## 1 Song_2      1|1  1       1     Son            5.037333    NA       NA
## 2 Song_2      1|2  2       N     Son            5.260063    NA       NA
## 3 Song_2      1|3  3       N     Son            5.482792    NA       NA
## 4 Song_2      1|4  4       2     Son            5.705521    NA 5.714555
## 5 Song_2      1|5  5       N     Son            5.928250    NA 5.927078
## 6 Song_2      1|6  6       N     Son            6.150979    NA       NA

The data frame shows a portion of the onset data structure where the last three columns refer to the onset times of the specific instruments (Clave, Bass, and Guitar). Note that we only show the first eight columns and 6 rows. The first five columns are meta-data, referring to the piece, beats and cycles and sections. Label.SD combines cycle and beat information (first integer refers to cycle and the second integer refers to beat sub-division). SD represents the sub-division explicitly. In this music there are 16 subdivisions per cycle (which can be felt as 4 beats \(\times\) 4 subdivisions). Section indicates the specific part of the piece. Finally, in this example we have Clave_ column, which is a reference to a clave pattern: strokes of the clave (woodblocks), whether sounded or not, are noted here; there are normally 5 strokes per cycle. The onset times for the instruments are recorded in seconds, although we prefer to use milliseconds (ms) in the analyses of synchrony to avoid reporting unnecessarily many digits. The onset times of the instruments are the result of an automatic onset detection (that has been subjected manual check) and the exact technique may depend on instrument used and source of data.

Add annotations for cycles

CSS_Song2_Metre <- get_OSF_csv('4cdpr') # Annotations for cycles
print(head(CSS_Song2_Metre))
##   Cycle      Time
## 1     1  5.037333
## 2     2  8.601000
## 3     3 12.123000
## 4     4 15.672000
## 5     5 19.174500
## 6     6 22.686000

Let’s simplify this example (select only few columns).

CSS_Song2_Onset <- dplyr::select(CSS_Song2_Onset,
                  Label.SD,SD,Clave,Bass,Guitar,Tres) 

In the second data frame (CSS_Song2_Metre), shown in Table 2, we have annotations, namely the cycle times, which are the manually-estimated times when the cycles start (containing all sub-divisions of the beat). For instance, here the first cycle starts at 5.037333, the second cycle at 8.601000) and so on. This is important information for some type of analyses and often we want to combine the automatically detected onset times and the annotated cycles. It is important to realise that the information about the cycles comes from an annotation of the music by an expert (or in some cases by several experts). To get an annotation such as this, software such Sonic Visualiser can be used to tap with the perceived beats of the audio track to obtain the beat times and then the first beat of the cycle is manually labelled to the data. The sub-divisions are then inferred from the cycle beginnings. This annotation process requires expertise with the musical style in question as the same patterns of onsets could potentially give rise to multiple interpretations of the metre.

Combine onsets and annotations

As the onsets and annotations are in different files, we first combine the onsets and annotations with onsetsync. Here we first add annotations about the cycles into the onset data and then we also add isochronous beat times to the data frame, since are useful reference points for future calculations. The metre annotations have already informed the allocation of onsets to beat positions; here we add more information from the metre annotations to the dataframe to allow further analysis options.

# Add annotations about the cycle to the data frame
CSS_Song2 <- add_annotation(df = CSS_Song2_Onset,
                            annotation = CSS_Song2_Metre$Cycle,
                            time = CSS_Song2_Metre$Time,
                            reference = 'Label.SD')

Add isochronous beat subdivisions based on annotation

We add an isochronous reference subdivision of beat subdivisions based on annotated cycle start times.

# Add isochronous beats to the data frame
CSS_Song2 <- add_isobeats(df = CSS_Song2, 
                          instr = 'CycleTime', 
                          beat = 'SD')
print(head(CSS_Song2[,2:9]))
##   SD Clave     Bass   Guitar     Tres CycleTime Cycle Iso.Time
## 1  1    NA       NA       NA       NA  5.037333     1 5.037333
## 2  2    NA       NA 5.281932       NA        NA     1 5.260062
## 3  3    NA       NA 5.480643       NA        NA     1 5.482792
## 4  4    NA 5.714555 5.707537 5.730943        NA     1 5.705521
## 5  5    NA 5.927078 5.939071 5.917083        NA     1 5.928250
## 6  6    NA       NA 6.153243 6.144901        NA     1 6.150979

At this point we have the basic elements for most analyses. In the data frame CSS_Song2 we have the onset data (coded under instrument names, here Clave, Bass, and Tres) and information about the beat cycles (CycleTime and Cycle), and we also have timing information for isochronous beat divisions (Iso.Time).

These annotation-based virtual beat structures can be systematically off by a certain amount, so perhaps some it may be advantageous for some analyses to infer the timing information related to the beat structure from the existing onsets themselves and not rely on external reference. add_isobeats function can accomplish this if given the instruments from which it will calculate the mean onset time for the first beat of the cycle, which will then be used to set the isochronous beat timings for the rest of the beats within the cycle.

Add isochronous beat subdivisions based on mean onset times

# Add isochronous beats based on mean timing of guitar, tres, and clave

CSS_Song2 <- add_isobeats(df = CSS_Song2, 
                          instr = c('Guitar','Tres','Clave'), 
                          beat = 'SD')
# Show the newly calculated isochronous beat times 
# from the second cycle onwards.
print(head(CSS_Song2[17:22,c(2,6:10)]))
##    SD     Tres CycleTime Cycle Iso.Time Mean.Time
## 17  1 8.571167     8.601     2 8.601000  8.592649
## 18  2       NA        NA     2 8.821125  8.803476
## 19  3       NA        NA     2 9.041250  9.031986
## 20  4 9.240644        NA     2 9.261375  9.257808
## 21  5       NA        NA     2 9.481500  9.478042
## 22  6 9.669264        NA     2 9.701625  9.670687

These operations allow many to create a number of variant reference points to be used in the calculation of synchrony.