Support for sample banks
I’ve been discussing with @kindohm for a while how to manage an expanded library of samples. Today the default way of achieving this is to use the samples folder like a namespace, e.g.:
../samples/tracknamebd/_X.wav
../samples/tracknamesn/_X.wav
There are several disadvantages to this:
- Long folder names
- Many folders
- No means of programmatically switching tracks
- Long load time and memory footprint in
SuperDirt, when usually only a small % of the library gets used in one session
If we take the single level directory of samples to be the bottleneck, then an alternative would be to introduce something like bank. In Tidal, to select a sound from a bank:
d1 $ bank "track1" # sound "bd"
Use multiple sound banks in a single pattern:
d1 $ bank "track1 track2" # sound "bd sn"
Index individual samples as usual:
d1 $ bank "track1 track2" # sound "bd:1 sn:2"
A set of global samples will play when no bank is specified:
d1 $ sound "bd"
Some may want to access banks inside a sound pattern:
d1 $ sound "bd|track1 sn|track2"
Banks would be loaded only when called in tidal (this may require delaying the initial playback by X cycles whilst the samples load). However in SuperDirt, offer an option to load all banks by default:
~dirt.loadSampleBanks
Example directory structure:
../samples/global/bd/_X.wav
../samples/global/sn/_X.wav
../samples/track1/bd/_X.wav
../samples/track1/sn/_X.wav
../samples/track2/bd/_X.wav
../samples/track2/sn/_X.wav
I'm not sure what implementation considerations there would need to be for this, but from a user perspective I feel this would greatly improve my compositional and performance process.
Maybe this proposal should also integrate managing SynthDefs as well?
@jarmitage: There have been discussions in the past about some sort of sample/synth bank format. Perhaps this will be the kick we need to get this implemented?
All samples are kept in RAM, in order to be able to efficiently play back in different speeds. You can always load only very few samples in SuperDirt and then add more as you go. The only problem is that loading from disk is asynchronous, so you can't reliably integrate it into a tidal pattern (unless you accept the possible omissions while loading).
Having several banks of sounds (samples or synths) is implemented in SuperDirt by the class DirtSoundLibrary. You can keep an array/dictionary of them and switch them if you want to re-map your instrument names. But normally, this is rather more costly than less, because several libraries will share certain samples. So far we have no hierarchies of sound libraries where you could swap out parts without touching the parent. It wouldn't be hard, but adds complexity.
Just a few questions:
Long folder names
I don't understand: most folder names are pretty short.
Many folders
how would banks reduce the number of folders?
No means of programmatically switching tracks
Are you sure you want to switch tracks by rewiring the sample names? Wouldn't that be better done in tidal with switching between actual patterns?
Long load time and memory footprint in SuperDirt, when usually only a small % of the library gets used in one session
The default sample library is deliberately large. It's really easy to make a different startup script that loads only what you need. You can always load more on the fly.
Sorry, I don't want to shine too much of a critical light on what you are proposing, I just try to understand better what the main point is.
"long folder names"
As an author of custom samples, I encounter this often. I compose "tracks" and require my sample folders to be named/grouped by track:
trackname1bd
trackname2bd
Typing out "trackname" is a bit much for every sample in a pattern:
d1 $ sound "trackname1bd trackname1sd [trackname1bd trackname1sd] trackname1ch*4"
It would be much easier to type short samples and specify a "bank":
d1 $ sound "bd sd [bd sd] ch*4" # bank "trackname1"
Thanks @telephon.
All samples are kept in RAM, in order to be able to efficiently play back in different speeds. You can always load only very few samples in SuperDirt and then add more as you go.
To "add more as you go" right now, you would call ~dirt.loadSoundFiles("...") right? I am proposing that you should be able to do this from Tidal, whilst you compose/perform, without having to think specifically about "loading the samples". Just do d1 $ bank "track1" # sound "bd" and ~dirt.loadSoundFiles("../samples/track1/*") would be called (or equivalent function would be handled by SuperDirt).
The only problem is that loading from disk is asynchronous, so you can't reliably integrate it into a tidal pattern (unless you accept the possible omissions while loading).
I agree this is an issue. I mentioned in my proposal "this may require delaying the initial playback by X cycles whilst the samples load" but I guess this would be potentially complex to implement?
Having several banks of sounds (samples or synths) is implemented in SuperDirt by the class
DirtSoundLibrary...
Ok. I'm not sure I fully understand how this works. Can you provide some example code? Do people currently use this? Is it accessible from Tidal?
Long folder names
I don't understand: most folder names are pretty short.
They are usually short, but here we are considering the case where you want to have a lot of samples and easily remember how to call them in Tidal. As noted at the start of this proposal, the naive approach to this is name your folders tracknamebd tracknamesn etc. This is where the folder names get longer.
Many folders
how would banks reduce the number of folders?
Following the previous point, if you could organise your samples hierarchically in folders (see the end of the proposal for an example directory structure) instead of by folder name, this would reduce the total number of folders in the top level directory. Having a syntax in Tidal which handles this (the bank idea) would enable declaring patterns somewhat separately from the sound collection used, which would keep pattern strings short.
No means of programmatically switching tracks
Are you sure you want to switch tracks by rewiring the sample names? Wouldn't that be better done in tidal with switching between actual patterns?
Having consistent sample names across tracks would be very useful; this is similar to how bd sn etc are standardised across drum machine banks. Also, the idea of switching across tracks seems creatively intriguing.
Long load time and memory footprint in SuperDirt, when usually only a small % of the library gets used in one session
The default sample library is deliberately large. It's really easy to make a different startup script that loads only what you need. You can always load more on the fly.
I think we're kind of saying the same thing here. I want to be able to "load more on the fly", but from Tidal, and I want to do it whilst thinking "I want to make this sound" not "I want to load this sound and then I want to make this sound". To manage the associated files, I want to organise my sample directory with an extra level so I can store banks of sounds that may have the same name i.e. multiple bd in different banks.
This is how it looks in superdirt (untested!):
(
~dirt = SuperDirt.new;
~banks = ();
~banks[\first] = DirtSoundLibrary.new;
~banks[\second] = DirtSoundLibrary.new;
~banks[\first].loadSoundFileFolder("<your first path here>");
~banks[\second].loadSoundFileFolder("<your second path here>");
)
~dirt.soundLibrary = ~banks[\second];
~dirt.soundLibrary = ~banks[\first];
there are several ways you could call something like ~dirt.soundLibrary = ~banks[\second]; from tidal, this is more a question of convention. The easiest is probably to just define an instrument that mean "switch library" and use its note to say which. But that might be too crude for what you are after.
Btw. there is already a scrip that lets you load samples on demand, you could test it to see if you can reduce your memory problems:
https://github.com/musikinformatik/SuperDirt/blob/master/scripts/auto-load-samples.scd
The easiest is probably to just define an instrument that mean "switch library" and use its note to say which.
Just so I definitely understand you, can you pseudo-code this in Tidal?
Not saying the "bank" idea isn't interesting, but FYI at least for the "long folder prefix" problem you can currently do something in Tidal like
d1 $ s (fmap ("track1"++) "bd sn" )
Actually, for that matter, you can use the <$> <*> notation to have a bank pattern and a sample pattern: (the flip is there to force the structure to come from the sample pattern)
d1 $ s (flip (++) <$> "bd sn*2" <*> "track1 track2")
As a nicer looking function
sprefix p = with s_p (liftA2 (++) (p::Pattern String))
d1 $ sprefix "track1 track2" $ s "bd sn*2"
It doesn't quite have the nice sound p1 # bank p2 structure (I can't see a way to get that), but it's close.
Current workaround that almost ticks all boxes
Tidal:
let bank p = with s_p (liftA2 (++) (p::Pattern String))
b = bank
d1 $ b "a b" $ s "bd sn" -- plays "abd bsn"
Folder hierarchy:
/path/to/a/abd/n.wav
/path/to/a/asn/n.wav
/path/to/b/bbd/n.wav
/path/to/b/bsn/n.wav
SuperDirt:
~dirt.loadSoundFiles("/path/to/a/*")
~dirt.loadSoundFiles("/path/to/b/*")
Btw. the method loadSoundFiles has an argument namingFunction. It takes the path as an argument and you can supply it to generate synth names.
It works to put ~dirt.loadSoundFiles("/path/to/a/*") in the SuperCollider startup file like this:
SuperDirt.start
~dirt.loadSoundFiles("/path/to/a/*")
And press Ctrl-Enter. However, when it starts up, it produces an error:
ERROR: syntax error, unexpected '~', expecting $end
in file 'selected text'
line 3 char 1:
~dirt.loadSoundFiles("/path/to/a/*")
^
Is there a way to load sound files in the startup file?
@mtift have a look at superdirt_startup.scd for pointers https://github.com/musikinformatik/superdirt (I think this would have been better as a new issue, I'll tidy away these two comments at some point)
maybe you have the wrong tilde, there are two different ones. This should be the correct one: ~.
Sorry for commenting an old issue. I thought it seemed relevant.
(And since I'm off topic, I'll mention that I've been enjoying The Oxford Handbook of Algorithmic Music and that I found @telephon's article about the philosophy of time to be especially helpful.)
@mtift this is encouraging to hear, given that it is rather dense. Why did you find it useful?
@telephon This is probably the strangest Github comment I've left in a while, but I've recently been fascinated by critical theories concerning the "event" of literature and the arts (probably stemming from recent articles in Critical Inquiry), and I enjoyed your comments about object/event, presence/existence, etc. The way you presented the concept of time/events with (live) coding made me realize how my own views of coding as a "preparational activity" was at odds with my views of music as an activity (what Christopher Small calls "musicking"), and it helped me combine previously disparate ideas. So the article was useful in the personal sense of exploring my nascent fascination with live coding, but also useful in practical ways, such as your bibliography full of books and articles that I'm planning to read. I could really go on and on, and I have a lot of questions, but maybe another forum would be better.
@mtift for me, too, rethinking programming has been very much motivated by my need to superimpose disparate interests. And at the same time this helps to abandon some strong preconceptions, too. Good that this works for you as well.
I don't think that programming and theory should be separate, so I'm enjoying the apparent digression from github convention here! On the other hand, it is all not about the current issue, so yes, let's continue elsewhere if you like, for example on the livecode mailing list or you can also just drop me an email.
@jarmitage This issue is getting a bit old now. Is this still something you're after?
@yaxu it’s not immediately relevant to me so may as well leave it for now, can always reopen later
Hi! I'm looking for a way to easily switch between sample packs. The workaround provided by @jarmitage no longer works. Do you have any suggestions on how to proceed? Combined with the lazy loading it would be nice to keep all my samples ready at one place.
Here's an updated workaround:
let withS :: String -> (String -> String) -> ControlPattern -> ControlPattern
withS name f pat = withValue (\cm -> Map.adjust (applyFIS id id f) name cm) pat
_bank name = withS "s" (name ++)
bank namepat pat = innerJoin $ (\name -> _bank name pat) <$> namepat
b = bank
@yaxu I see the issue was closed, but the support has not been added to Tidal, so it's quite frustrating to see this issue dismissed. To me, it seems like pretty basic functionality to be able to write patterns like [bd hh cp hh] and be able to swap out live which kit to use for these sounds, so I was quite surprised to learn that Tidal is missing this. Strudel has it, for example.
Is it really the intent to require users who have written a pattern using 808 samples, and want to use a different instrument, to (during a live performance) manually replace all instances of "808bd", etc. to other strings? I feel that would make for a very boring performance :)
Please consider adding this to the next version of BootTidal.hs, because the below is an unpleasant experience for brand new users of tidalcycles. Some of the below would be good issues for the SuperDirt project, but without proper bank support, there's not much point to creating a pull request, as Tidal users won't be able to make use of the better organization by default.
For users who are here because they are stuck trying to get the above workaround for the lack of bank support to work, a few tips:
- you'll need to copy the original file from the tidalcycles repository at the appropriate version and modify it, e.g. here is the file for tidalcycles version 1.9.5
- you'll then need to tell your editor or editor extension to read it, e.g. using vscode with the "TidalCycles for VSCode" extension, open settings and paste the path to your modified
BootTidal.hsinto the setting calledtidalcycles.bootTidalPath
- edit
BootTidal.hsas follows:
- note that you'll probably get syntax errors, because the
:setsyntax is not recognized as valid syntax for a.hsfile - add the line
import qualified Data.Map as Mapafter the other imports - paste the following code at the bottom of the file, after the other
:{ ... }blocks:
:{
let withS :: String -> (String -> String) -> ControlPattern -> ControlPattern
withS name f pat = withValue (\cm -> Map.adjust (applyFIS id id f) name cm) pat
_bank name = withS "s" (name ++)
bank namepat pat = innerJoin $ (\name -> _bank name pat) <$> namepat
b = bank
:}
- use the bank in your music code as follows, e.g. to play "808bd 808sn", use
d1 $ b "808" $ s "bd sn" - note that some samples are not correctly organized by default, as in they don't match their un-banked counterparts. e.g. for the 808 samples, I had to move some around to get them to match the default counterparts:
| sample | default name | move from | move to | new name |
|---|---|---|---|---|
| cowbell | 808:0 |
808/CB.WAV |
808cb/CB.WAV |
808cb (to match default cb) |
| hi-hat | 808:1 |
808/CH.WAV |
808hh/HH.WAV |
808hh (to match default hh) |
| claves | 808:2 |
808/CL.WAV |
808cl/CL.WAV |
808cl |
| clap | 808:3 |
808/CP.WAV |
808cp/CP.WAV |
808cp (to match default cp) |
| shaker | 808:4 |
808/MA.WAV |
808ma/MA.WAV |
808ma |
| rimshot | 808:5 |
808/RS.WAV |
808rs/RS.WAV |
808rs (to match default rs) |
| open hi-hat | 808oh |
808oh/ |
808ho/ |
808ho (to match default ho) |
- note that there are other samples not organized as banks, but as sample numbers within a bank, and you apparently just have to figure out what drum each index corresponds to, with no string label. e.g. the
dbkit is organized as follows:
| sample | default name |
|---|---|
| closed hi-hat | db:0 |
| crash | db:1 |
| clap | db:2 |
| mid tom | db:3 |
| low tom | db:4 |
| kick | db:5 |
| low kick | db:6 |
| open hi-hat | db:7 |
| muted triangle | db:8 |
| triangle | db:9 |
| ride | db:10 |
| snare | db:11 |
| snare 2 | db:12 |
Hi @qguv, we are all users here, and contributors to the software are volunteers. Please be aware that you are not making a feature request as part of a transactional arrangement. We are all working together.
Thanks for sharing these detailed instructions for the workaround.
Happy to reopen the issue, it was originally closed by the person who opened it, who was happy at the time with the workaround. With no-one volunteering to work on it, that's how it was left.
Since then, strudel has appeared, and gained a bank feature. It would be great for a bank feature to mirror strudel's, so that sound "bd sd" # bank "tr909" would translate to "tr909_bd tr909_sd" (with _ delimiter). I think this would be best done as a simple change to SuperDirt. Tidal would just need the extra bank control added if it isn't there already. We could then make quick use of the sample banks arranged this way for strudel.
Dirt-Samples is indeed unfortunately a mess of samples from a wide range of sources of mostly unknown provenance / license. They are well overdue for replacing.
So does bank refer to all samples or just a subset? I would say that the best way to specify banks would be by dictionaries that inherit from others. So you can make sub-banks, where a part of the samples is replaces. This way, you don't have to support all samples from the basic sound library.
What do you think? How is it done in strudel?
So does
bankrefer to all samples or just a subset? I would say that the best way to specify banks would be by dictionaries that inherit from others. So you can make sub-banks, where a part of the samples is replaces. This way, you don't have to support all samples from the basic sound library.What do you think?
Hmm I don't quite understand 'dictionaries that inherit from others' - which others?
How is it done in strudel?
A message with a 'bank' value is prepended to the sampleset name, separated by _. So s "bd sd" # bank "tr909" would be the same as s "tr909_bd tr909_sd".
Maybe it would be better if the bank was separate metadata but it works quite well.
A small related detail is that there is a metadata json file that has a mapping from names to locations: https://github.com/felixroos/dough-samples/blob/main/tidal-drum-machines.json
So if you load samples from such a file, the standard can be implemented in the mapping rather than the organisation of the files themselves.
Hmm I don't quite understand 'dictionaries that inherit from others' - which others?
well when you write s "bd sd" # bank "tr909" and your sample bank has a sample called "tr909_bd" but no "tr909_sd", what do you do? Do you have to copy the files from your default sample bank into the other folder of tr909? This wouldn't be good, right?
Hmm I don't quite understand 'dictionaries that inherit from others' - which others?
well when you write
s "bd sd" # bank "tr909"and your sample bank has a sample called "tr909_bd" but no "tr909_sd", what do you do? Do you have to copy the files from your default sample bank into the other folder oftr909? This wouldn't be good, right?
Ah I see what you mean. Yes currently in strudel there's no fallback, you generally choose one bank per pattern and silence for missing samples.
It's true being able to give a list of banks would be a nice feature, like s "bd sd rd" # bank "mykicks:tr909:gretsch".
Ok, a list would be a simple way to go, true. s "bd sd rd" # bank "tr909::" could then mean that if not found, you'd fall back on the default bank.
Since then, strudel has appeared, and gained a bank feature. It would be great for a bank feature to mirror strudel's, so that
sound "bd sd" # bank "tr909"would translate to"tr909_bd tr909_sd"(with _ delimiter). I think this would be best done as a simple change to SuperDirt.
This is already working. It's been implemented for a longer time.
Here is a helper function for loading "into" a bank:
~loadBank = { |bank, paths| ~dirt.soundLibrary.loadSoundFiles(paths, namingFunction: { |path| bank ++ path.basename }) };
~loadBank.("foufou");
Then you can run:
s "bd sd rd" # bank "foufou"
Actually, there is just one naming difference. This we have to resolve: if strudel uses an underscore, we can add that to superdirt as well ...