You use audio sample files to produce music.
You hunger for new, unusual samples for inspiration.
The internet has samples.
SampleMgr
describes, collects and organizes your sample files. Use it to
fill up your local drive with the samples you want rather than a random pile
that come with music apps.
SampleMgr manages audio sample files and communicates their location to Hz.Samplo or other sample file consumer. SampleMgr supports asynchronous (background) downloading, caching and serving of network-based samples.
Samples are organized on concepts of repository
, library
, bank
,
and voice
. We can think of a repository as equivalent to
a github repository (repo) that obeys a standard directory
structure. We currently support two file organizing schemes below
a repository:
MIDIJS/FatBoy/accordion
DrumKits/AkaiXR10
Concept | Description |
---|---|
repo |
name+internet URL containing sample libraries or banks. |
bank |
collection of voices |
voice |
collection of sound files organized either by MIDI key or name (eg bd, hh) |
Since the internet is a big place, we introduce the repo file. This file is a list of your favorite repositories (their location and contents). It is used to map a lightweight reference to one or more URLs which, in turn, represent a key to our local cache. Hz ships with a default repo file, shown below. You can create your own repo file and TOC-files to suit your own tastes.
All this machinery is presented in order to make literally thousands of internet-hosted sample files easily accessible automatically and portably. Of course you have your own personal collection of sample files and these can be either be explicitly organized into your repo file or presented to SampleMgr directly in your songs.
For the common use-case where you knowc a bank
and a voice
, you only
need a single method to ensure sample files are available on your computer
as shown here:
let asamps = await SampleMgr.PreloadSamples("MIDIJS/FatBoy", "accordion");
Once complete, you'll find 88 sample files below your current workspace:
${WS}/_cache/github/paulrosen/midi-js-soundfonts/FatBoy/accordion-mp3
Next, to make these files known to Hz.Samplo:
await samplo.LoadPreset(asamps.AsPreset(), "MIDIJS/FatBoy_accordion");
You can find this complete example here.
SampleMgr method | Description |
---|---|
async Init(repolist?) |
Optional, returns a promise that resolves to an initialized SMgrRepolist . If filename isn't provided, the default is returned. |
async PreloadSamples(bank, voice, repolist=null) |
Ensures that the bank+voice sample files are available in the local cache. Returns a promise that resolves to a Voice instance. |
AsPresetFromLocalVoice(cwd, name, voicedata) |
Returns a Hz.Samplo preset that refers to a locally defined voice. voicedata is either an array or a dictionary. |
GetRepolists() |
Returns an array of names of repolists. |
GetRepolist(name) |
Returns the named SMgrRepoList . |
SMgrRepolist method | Description |
---|---|
GetFile() |
Returns the filename associated with this repolist. |
async Initialize() |
Ensures that all TOC files supporting the repo are loaded. |
GetBankNames() |
Returns a list of bank names known to the repo. |
GetBank(name) |
Returns a SMgrBank for the named repo bank. |
SMgrBank method | Description |
---|---|
GetName() |
returns the bank name. |
GetVoiceNames() |
returns the list of voices present in the bank. |
GetVoice(name) |
returns the SMgrVoice for the voicename in the bank. |
SMgrVoice method | Description |
---|---|
AsPreset() |
returns a HzSamplePlayer preset that refers to a locally cached voice files. |
GetName() |
returns the voice's name |
GetBank() |
returns the voice's bank name |
IsPercussive() |
returns true if the voice is percussive |
IsTonal() |
returns true if the voice is tonal |
GetCategory() |
returns either "tonal" or "percussive" |
GetURLs() |
returns an array of local sample file references |
GetNumPercSamps() |
returns 0 or the size of the samples array |
GetNumTonalSamps() |
returns 0 or the number of entries in the samples object |
GetURLForNote(note) |
returns [fileref, transpose] |
GetURLForIndex(i) |
returns fileref at i |
See here for an example.
The repo file is a JSON formatted dictionary that maps a repo name
to a repo TOC (table of contents) file. When you request
samples for VCSL/handchimes
, we first locate the repo named VCSL in the
repolist, then find handchimes in its TOC. There we'll find an enumeration
of the voice sample files.
{
"Dirt": {
"URL": "https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master",
"TOC": "tocs/dirt-samples.json",
"CacheDir": "github/tidalcycles/Dirt-Samples"
},
"Strudel": {
"URL": "https://raw.githubusercontent.com/tidalcycles/strudel/main/website/public",
"TOC": "tocs/strudel.json",
"CacheDir": "github/tidalcycles/strudel"
},
"DrumKits": {
"URL": "https://raw.githubusercontent.com/ritchse/tidal-drum-machines/main/machines",
"TOC": "tocs/tidal-drum-machines.json",
"CacheDir": "github/ritchse/tidal-drum-machines",
"Multibank": 1
},
"VCSL": {
"URL": "https://raw.githubusercontent.com/sgossner/VCSL/master",
"TOC": "tocs/vcsl.json",
"CacheDir": "github/sgossner/VCSL"
},
"midi-js-soundfonts": {
"URL": "https://raw.githubusercontent.com/paulrosen/midi-js-soundfonts/master",
"TOC": "tocs/midi-js-soundfonts.json",
"CacheDir": "github/paulrosen/midi-js-soundfonts",
"Multibank": 1,
"LICENSE": "MIT, Copyright (C) 2012 Benjamin Gleitzman (gleitz@mit.edu)"
},
"loophole": {
"URL": "https://loophole-letters.vercel.app/samples",
"TOC": "tocs/loophole.json",
"CacheDir": "loophole-letters.vercel.app/samples"
}
}
The Repo TOC file is a JSON formatted dictionary that maps voice names to
their collection of sample files. Here's a portion of the VCSL
TOC file.
We've selected this subset because it shows the difference between
tonal and percussive voices. The first is a dictionary whose keys
are note-names and percussion voice are an array indexed by a voice
variation/index. In both cases the value of each entry is a relative
pathname to a sample file.
{
"handchimes": {
"A#3": "Idiophones/Struck Idiophones/Hand Chimes/sus_A#3_r01_main.wav",
"A#5": "Idiophones/Struck Idiophones/Hand Chimes/sus_A#5_r01_main.wav",
"A4": "Idiophones/Struck Idiophones/Hand Chimes/sus_A4_r01_main.wav",
"C3": "Idiophones/Struck Idiophones/Hand Chimes/sus_C3_r01_main.wav",
"C4": "Idiophones/Struck Idiophones/Hand Chimes/sus_C4_r01_main.wav",
"C5": "Idiophones/Struck Idiophones/Hand Chimes/sus_C5_r01_main.wav",
"C6": "Idiophones/Struck Idiophones/Hand Chimes/sus_C6_r01_main.wav",
"D3": "Idiophones/Struck Idiophones/Hand Chimes/sus_D3_r01_main.wav",
"D4": "Idiophones/Struck Idiophones/Hand Chimes/sus_D4_r01_main.wav",
"D5": "Idiophones/Struck Idiophones/Hand Chimes/sus_D5_r01_main.wav",
"E3": "Idiophones/Struck Idiophones/Hand Chimes/sus_E3_r01_main.wav",
"E4": "Idiophones/Struck Idiophones/Hand Chimes/sus_E4_r01_main.wav",
"E5": "Idiophones/Struck Idiophones/Hand Chimes/sus_E5_r01_main.wav",
"F#3": "Idiophones/Struck Idiophones/Hand Chimes/sus_F#3_r01_main.wav",
"F#4": "Idiophones/Struck Idiophones/Hand Chimes/sus_F#4_r01_main.wav",
"F#5": "Idiophones/Struck Idiophones/Hand Chimes/sus_F#5_r01_main.wav",
"G#3": "Idiophones/Struck Idiophones/Hand Chimes/sus_G#3_r01_main.wav",
"G#4": "Idiophones/Struck Idiophones/Hand Chimes/sus_G#4_r01_main.wav",
"G#5": "Idiophones/Struck Idiophones/Hand Chimes/sus_G#5_r01_main.wav"
},
"hihat": [
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_Close_rr1_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_Close_rr2_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitC_v1_rr1_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitC_v1_rr2_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitC_v2_rr1_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitC_v2_rr2_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitC_v3_rr1_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitC_v3_rr2_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitC_v4_rr1_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitC_v4_rr2_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitLoose_rr1_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitLoose_rr2_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitOC_rr5_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitO_rr1_Mid.wav",
"Idiophones/Struck Idiophones/Hi-Hat Cymbal/HiHat_HitO_rr2_Mid.wav"
],
}
SampleMgr was inspired by the sample-handling in Strudel.