Selecting and assinging ProTrackR2 objects

Introduction

This vignette describes how to select elements from ProTrackR2 module objects and how to replace them. In order to best understand this, it is necessary to understand how objects are organised in the ProTrackR2 package. Please read vignette("s3class") first, or use it for reference when reading the vignette at hand. Before we continue, we start the session by loading a module file.

library(ProTrackR2)

mod <- pt2_read_mod(pt2_demo())

Selecting lists from modules

There are two important lists of objects contained in ProTracker modules. The first is a list of patterns (pt2patlist), which can be obtained as follows.

mod$patterns
#> pattern list [n=4]
## which is identical to
mod[["patterns"]]
#> pattern list [n=4]
## which is identical to
mod[[1L]]
#> pattern list [n=4]

It returns a list of patterns and inherits the class pt2patlist, from it you can select one ore more specific patterns (see below).

The second list you can get from a module is a list of samples (pt2samplist), which can be obtained as follows.

mod$samples
#> sample list [n=31]
## which is identical to
mod[["samples"]]
#> sample list [n=31]
## which is identical to
mod[[2L]]
#> sample list [n=31]

Index base: one or zero?

For vectors and lists, R uses indices starting at 1. ProTracker modules often use arrays of elements that starting with indices at 0 (but unfortunately consistently for everything). And in some case ProTracker effect commands refer to such indices (see vignette("effect_commands")). Unfortunately, this is a source of confusion, for which there exists no ideal solution.

In this package, I’ve tried to implement it as consistent as possible. Named functions and methods use zero-based indices, whereas bracket operators ([ and [[) use one-based indices conform R syntax. This is summarised in the table below.

Zero-based index One-based index
pt2_cell(), pt2_pattern(), pt2_sample() [, [[

Selecting and replacing list elements

Samples

Selecting a specific sample from a sample list can be done with the [[-operator. Note that sample number 5 is empty (consists of zero samples):

mod$samples[[5L]]
#> [1] "PT2 Sample 'commodore amiga 500' [0]"
## which is identical to (see the different base of the index)
pt2_sample(mod, 4L)
#> [1] "PT2 Sample 'commodore amiga 500' [0]"

In order to replace this sample with sample number 2, you can simply assign it like this:

mod$samples[[5L]] <- mod$samples[[2L]]

After this assignment operation samples 2 and 5 are identical. If you want to replace multiple samples at once you need to use the [-operator. The example below shows how to replace samples 5 up to 8 with sample 2:

mod$samples[5L:8L] <- mod$samples[2L]

Single brackets [ returns a list (pt2samplist) with one or more elements. Double brackets [[ return a single element (pt2samp). In essence, this works the same as with normal list objects.

Patterns

For a pattern lists (pt2patlist) selection and assignment operators are similar to those of sample lists (see above). Single brackets [ returns a list (pt2patlist) with one or more elements. Double brackets [[ return a single element (pt2pat). In essence, this works the same as with normal list objects.

The range of pattern indices you can use depends on the maximum index (+1) registered in the pattern sequence table (pt2_pattern_table(mod)). In the currently loaded module there are 4 patterns registered. So to get the fourth pattern table in the module, you use:

my_pattern <- mod$patterns[[4L]]
## which is identical to (see the different base of the index)
my_pattern <- pt2_pattern(mod, 3L)

If you attempt to get the fifth table (mod$patterns[[5]]), you will get an error, since it doesn’t exist. However, you can append a new pattern table at this position using the assign operator:

mod$patterns[[5L]] <- pt2_new_pattern()

This new pattern is also automatically added to the pattern sequence table:

pt2_pattern_table(mod)
#>   [1] 0 0 1 1 2 2 3 3 2 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
#>  [38] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
#>  [75] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
#> [112] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

You can use the same strategy to copy existing patterns to a new index:

mod$patterns[[6L]] <- mod$patterns[[1L]]

In theory ProTracker modules can hold a maximum of 100 pattern tables. If this limit can be reached depends on available slots in the pattern sequence table (pt2_pattern_table(mod)). If you try to create a pattern beyond the maximum of 100 or there are no more room for additional patterns in the sequence table, you will get an error.

Cells

As explained in vignette("s3class"), a cell (class pt2cell) is the elementary unit of a pattern table and specifies which instrument (sample) to play at which note and with which effect. You can select a range of cells with a [-operator by referring to a pattern’s row index and its channel (column) index. The example below selects the first 8 rows from the first channel.

my_cells <- mod$patterns[[1L]][1L:8L, 1L]

The result of this is an object of class pt2celllist. To select a particular cell from this range you need the double bracket operator [[. To select the first cell of the eight selected cells you do the following.

my_cells[[1L]]
#> C-3 01 A08
## which is identical to (see the different base of the indices)
pt2_cell(pt2_pattern(mod, 0L), i = 0L, j = 0L)
#> C-3 01 A08

Remember that we appended an empty pattern at position 5? We can replace the empty cells in that pattern with the selected cells. The example below shows how the entire first column is filled with the 8 selected cells (note that the cells are recycled).

mod$patterns[[5L]][,1L] <- my_cells
#> Warning in replace_cells_(x, as.matrix(target_idx), values): Replacement values
#> are recycled