3.7 Sample Packs

# Sample Packs

**Note: this section of the tutorial covers the advanced topic of
working with large directories of your own samples. This will be the
case if you've downloaded or bought your own sample packs and wish to
use them within Sonic Pi.**

**Feel free to skip this if you're happy working with the built-in
samples.**

When working with large folders of external samples it can be cumbersome
to have to type the whole path every time to trigger an individual
sample.

For example, say you have the following folder on your machine:

```
/path/to/my/samples/
```

When we look inside that folder we find the following samples:

* `100_A#_melody1.wav`
* `100_A#_melody2.wav`
* `100_A#_melody3.wav`
* `120_A#_melody4.wav`
* `120_Bb_guit1.wav`
* `120_Bb_piano1.wav`

Typically in order to play the piano sample we can use the full path:

```
sample "/path/to/my/samples/120_Bb_piano1.wav"
```

If we want to then play the guitar sample we can use its full path too:

```
sample "/path/to/my/samples/120_Bb_guit.wav"
```

However, both of these calls to sample requires us to *know* the names
of the samples within our directory. What if we just want to listen to
each sample in turn quickly? 

## Indexing Sample Packs

If we want to play the first sample in a directory we just need to pass
the directory's name to `sample` and the index `0` as follows:

```
sample "/path/to/my/samples/", 0
```

We can even make a shortcut to our directory path using a variable:

```
samps = "/path/to/my/samples/"
sample samps, 0
```

Now, if we want to play the second sample in our directory, we just need
to add 1 to our index:

```
samps = "/path/to/my/samples/"
sample samps, 1
```

Notice that we no longer need to know the names of the samples in the
directory - we just need to know the directory itself (or have a
shortcut to it). If we ask for an index which is larger than the number
of samples, it simply wraps round just like Rings. Therefore, whatever
number we use we're guaranteed to get one of the samples in that
directory.

## Filtering Sample Packs

Usually indexing is enough, but sometimes we need more power to sort
and organise our samples. Luckily many sample packs add useful
information in the filenames. Let's take another look at the sample file
names in our directory:

* `100_A#_melody1.wav`
* `100_A#_melody2.wav`
* `100_A#_melody3.wav`
* `120_A#_melody4.wav`
* `120_Bb_guit1.wav`
* `120_Bb_piano1.wav`

Notice that in these filenames we have quite a bit of
information. Firstly, we have the BPM of the sample (beats per minute)
at the start. So, the piano sample is at 120 BPM and our first three
melodies are at 100 BPM. Also, our sample names contain the key. So the
guitar sample is in Bb and the melodies are in A#. This information is
very useful for mixing in these samples with our other code. For
example, we know we can only play the piano sample with code that's in
120 BPM and in the key of Bb.

It turns out that we can use this particular naming convention of our
sample sets in the code to help us filter out the ones we want. For
example, if we're working at 120 BPM, we can filter down to all the
samples that contain the string `"120"` with the following:

```
samps = "/path/to/my/samples/"
sample samps, "120"
```

This will play us the first match. If we want the second match we just
need to use the index:

```
samps = "/path/to/my/samples/"
sample samps, "120", 1
```

We can even use multiple filters at the same time. For example, if we
want a sample whose filename contains both the substrings `"120"` and `"A#"`
we can find it easily with the following code:

```
samps = "/path/to/my/samples/"
sample samps, "120", "A#"
```

Finally, we're still free to add our usual opts to the call to `sample`:

```
samps = "/path/to/my/samples/"
sample samps, "120", "Bb", 1, lpf: 70, amp: 2
```

## Sources

The sample filter pre-arg system understands two types of information:
*sources* and *filters*. Sources are information used to create the list
of potential candidates. A source can take two forms:

1. `"/path/to/samples"` - a string representing a valid path to a directory 
2. `"/path/to/samples/foo.wav"` - a string representing a valid path to a sample

The `sample` fn will first gather all sources and use them to create a
large list of candidates. This list is constructed by first adding all
valid paths and then by adding all the valid `.flac`, `.aif`, `.aiff`,
`.wav`, `.wave` files contained within the directories.

For example, take a look at the following code:

```
samps = "/path/to/my/samples/"
samps2 = "/path/to/my/samples2/"
path = "/path/to/my/samples3/foo.wav"

sample samps, samps2, path, 0
```

Here, we're combining the contents of the samples within two directories
and adding a specific sample. If `"/path/to/my/samples/"` contained 3
samples and `"/path/to/my/samples2/"` contained 12, we'd have 16
potential samples to index and filter (3 + 12 + 1).

By default, only the sample files within a directory are gathered into
the candidate list. Sometimes you might have a number of nested folders of
samples you wish to search and filter within. You can therefore do a
recursive search for all samples within all subfolders of a particular
folder by adding `**` to the end of the path:

```
samps = "/path/to/nested/samples/**"
sample samps, 0
```

Take care though as searching through a very large set of folders may
take a long time. However, the contents of all folder sources are
cached, so the delay will only happen the first time.

Finally, note that the sources *must go first*. If no source is given,
then the set of built-in samples will be selected as the default list of
candidates to work with.

## Filters

Once you have a list of candidates you may use the following filtering
types to further reduce the selection:

* `"foo"` Strings will filter on substring occurrence within file name (minus directory path and extension).
* `/fo[oO]/` Regular Expressions will filter on pattern matching of file name (minus directory path and extension).
* `:foo` - Keywords will filter candidates on whether the keyword is a direct match of the filename (minus directory path and extension).
* `lambda{|a| ... }` - Procs with one argument will be treated as a candidate filter or generator function. It will be passed the list of current candidates and must return a new list of candidates (a list of valid paths to sample files).
* `1` - Numbers will select the candidate with that index (wrapping round like a ring if necessary).

For example, we can filter over all the samples in a directory
containing the string `"foo"` and play the first matching sample at half
speed:

```
sample "/path/to/samples", "foo", rate: 0.5
```

See the help for `sample` for many detailed usage examples. Note that
the ordering of the filters is honoured.

## Composites

Finally, you may use lists wherever you may place a source or
filter. The list will be automatically flattened and the contents will
be treated as regular sources and filters. Therefore the following calls
to `sample` are semantically equivalent:

```
sample "/path/to/dir", "100", "C#"
sample ["/path/to/dir", "100", "C#"]
sample "/path/to/dir", ["100", "C#"]
sample ["/path/to/dir", ["100", ["C#"]]]
```

## Wrapping Up

This was an advanced section for people that need real power to
manipulate and use sample packs. If most of this section didn't make too
much sense, don't worry. It's likely you don't need any of this
functionality just yet. However, you'll know when you do need it and you
can come back and re-read this when you start working with large
directories of samples.
