Buffer definition; audio buffer abstraction for sound synthesis systems
"Buffer definition"; abstraction of audio buffers for Lisp sound synthesis systems.
Basically, this simplifies buffer management in cl-collider, making them easier to use.
Note: Previously Bdef for SuperCollider was hosted at this URL. That repository has since moved here.
Bdef is available from Quicklisp, so you can simply Quickload it:
It also has multiple sub-systems for integration with various other libraries. So if you use cl-patterns, cl-collider, or Incudine you may want to load
bdef/cl-patterns and/or the other sub-systems:
;; integration with cl-patterns and cl-collider: (ql:quickload '(bdef/cl-patterns bdef/cl-collider))
To load a buffer from a file, provide the path to the
(bdef "~/path/to/your/buffer.mp3") ;; you can also give it an alias: (bdef :foo "/path/to/buffer.wav") ;; the buffer can then be referred to with it: (bdef :foo)
bdef returns a bdef object which can be provided in place of a buffer for any functions that expect it, as long as you've quickloaded the proper bdef sub-system for the library those functions are from. For example:
;; cl-collider: (synth :playbuf :buffer (bdef :foo)) ;; will work if you've loaded the bdef/cl-collider system. ;; cl-patterns: (play (event :instrument :playbuf :buffer :foo)) ;; will work if you've loaded the bdef/cl-patterns system. ;; Notice that you can just specify the bdef name as a symbol without even having to use the bdef function! The same is true in patterns too.
Metadata can be associated with bdefs for various uses:
(setf (bdef-metadata :foo :bpm) 120) ;; set the "bpm" metadatum to 120
Some metadata is automatically generated; for example, the tempo may be automatically detected from the filename or id3 tags.
bdef includes functionality for defining buffer regions called
splits. This is useful, for instance, to divide a drum loop up by each hit, or to divide up a source track into sections based on onsets or beats.
;; define three consecutive regions, one second long each: (make-splits (list (list 0 1) (list 1 2) (list 2 3)) :unit :seconds) ;; auto-generate splits from sound onsets in a file using the aubio library: (splits-from-aubio-onsets "/path/to/file.wav")
Aubio is an external library of audio analysis functions. If installed, splits can be automatically generated from its analyses.
bdef can also generate splits from other formats as well:
- OP-1 drumsets (
splits-from-op-1-drumset, but automatically parsed from any valid
- Audacity labels (
- Renoise regions (planned for future implementation)
See the following section for detail on more features of the bdef library.
2.1Can be re-evaluated without loading a new buffer:
(defparameter *buf* (cl-collider:buffer-read "/buffer.wav")) (defparameter *buf* (cl-collider:buffer-read "/buffer.wav")) ;; the same variable, and same file! (length (remove-if #'null (slot-value *s* 'cl-collider::buffers))) ;; => 2 -- duplicate buffers!
(bdef :buf "/buffer.wav") ;; here we give it the name :buf (bdef :foo "/buffer.wav") ;; same file, different "name"... (length (remove-if #'null (slot-value *s* 'cl-collider::buffers))) ;; => 1 -- no duplicate buffers :D
...To force a file to be reloaded, simply call
bdef-free on it, then call
2.2Automatically converts files unsupported by the backend if you have ffmpeg installed:
(bdef :bar "/blah.mp3") ;; works!
It does this by storing them in a temporary directory (
/tmp/bdef/ by default on linux and mac).
2.3No additional name needed if loading from a file:
2.4Supports pathname abbreviations:
(bdef "~/cool-sound.wav") ;; will find a cool sound in your home directory
2.5Loads mono files as stereo by default.
For consistency. To load as mono, supply 1 for
num-channels keyword argument.
2.6Supports loading in wavetable format:
(bdef "~/wilhelm.wav" :wavetable t) ;; load the Wilhelm scream as a wavetable
2.7Supports loading envelopes as buffers:
Either as wavetables, or standard.
2.8Integration with cl-collider:
(cl-collider:bufnum (bdef :sound)) ;; returns the buffer number. (cl-collider:synth :playbuf :bufnum (bdef :sound)) ;; plays the buffer.
bdef/cl-collider system to enable this.
2.9Integration with cl-patterns:
(cl-patterns:play (bdef :sound)) ;; plays the buffer using the *cl-collider-buffer-preview-synth* set in cl-patterns. (cl-patterns:play (cl-patterns:event :instrument :playbuf :bufnum (bdef :sound))) ;; automatically converts bdef to the buffer number.
bdef/cl-patterns system to enable this.
2.10Supports multiple sound server backends:
SuperCollider/cl-collider is the primary backend tested against, however Incudine is also supported for most functionality.
Enable the cl-collider backend, for example, like so:
2.11Allows metadata about the buffer to be stored:
(setf (bdef-metadata (bdef :snd) :bpm) 99) ;; set :snd's tempo to 99 BPM. (bdef-metadata (bdef :snd) :bpm) ;; get the stored bpm value.
2.12Automatically set various metadata when a bdef is created:
;; load a file with its bpm in its filename: (bdef :my-file "~/my-file-128bpm.wav") ;; the bpm is automatically stored as metadata: (bdef-metadata :my-file :bpm) ;; => 128
You can also add your own auto-metadata keys with the
define-bdef-auto-metadata macro or
set-bdef-auto-metadata function, or remove them with the
Additional metadata is loaded asynchronously in background threads using futures from the eager-future2 library. If a requested metadatum is still being generated,
bdef-metadata will block until the result is available.
2.13Automatically generate metadata from functions:
(setf (bdef-metadata :foo :bpm) 142) ;; sets the "tempo" metadata key instead to its beats per minute value ;; tempo is stored as beats per second: (bdef-metadata :foo :tempo) ;; => 71/30 (142 beats per minute in beats per second) ;; beats per minute is still available, dynamically calculated from the tempo key: (bdef-metadata :foo :bpm) ;; => 142
You can define your own "dynamic metadata" with
2.14"Splits" functionality to define split points or regions in buffers:
(make-splits (list 0 0.25 0.5 0.75) :bdef (bdef :foo)) ;; splits at the start, 25%, 50%, and 75% into the file. (splits-from-audacity-labels "/path/to/label.txt") ;; make a splits object from an Audacity labels file. (setf (bdef-splits :my-bdef) *) ;; set the :my-bdef bdef's :splits metadatum to the splits object generated from the above. (splits-point :my-bdef 3 :start :second) ;; get the start of :my-bdef's fourth split in seconds.
2.15Splits integration with cl-patterns:
(pbind :instrument :playbuf :bufnum (bdef :my-bdef) :split (pwhite 0 (1- (splits-length :my-bdef))) ;; pick a random split :embed (psplits) ;; the psplits pattern yields events with :start, :end, and :dur keys to play the split specified by :split from the :splits metadatum of the bdef specified as :bufnum. :dur 1)
2.16Integration with the Aubio audio analysis library if installed:
(bdef::splits-from-aubio-onsets "/path/to/audio/file.wav") (bdef :pee "/path/to/pee.wav") ;; since no BPM is listed in the filename, aubio is used to detect it (if installed)... (bdef-metadata :pee :tempo) ;; ...and it is stored in the bdef's :tempo metadatum! nice!
2.17Ability to import splits from OP-1 drumset file metadata:
(bdef::splits-from-op-1-drumset "/path/to/op-1-drumset.aif") ;; generates a splits by parsing the metadata in the file.
Note that any
aiff file will automatically be checked for OP-1 metadata, which will be parsed and stored in the
splits bdef metadata key if it is found.
Currently, bdef supports SuperCollider via cl-collider as a backend. There is also basic (likely buggy) Incudine support - this will be improved later.
To write your own backend, you will need to implement the following methods on your backend's buffer class:
bdef-id(optional if your backend doesn't use buffer IDs)
bdef-file(optional if your backend doesn't keep track of what file a buffer was loaded from)
All other functionality is derived from those functions.
For the user's convenience, you might also want to define methods on the
bdef class for the backend's relevant functions; see the bottom of cl-collider.lisp for an example.
- Fix the various minor/not-so-minor issues marked with "FIX" in the code.
- We have
bdef-framesto get buffer data; we should have support for setting buffer data as well.
- Support for configurable pathname shortcuts. (i.e. set
fooas a shortcut to
/a/long/path/name/, then provide ~"foo/bar.wav"~ instead of ~"/a/long/path/name/bar.wav"~.)
- "Dynamic" splits; i.e. define a set of splits as "this region in four equal-length pieces" rather than all splits being immediately "baked" as specific points.