./../../src/sndfile-palette.lsp

## palette/sndfile-palette [ Classes ]

[ Top ] [ palette ] [ Classes ]

NAME

     sndfile-palette

     File:             sndfile-palette.lsp

     Class Hierarchy:  named-object -> linked-named-object -> sclist ->
                       circular-sclist -> assoc-list -> recursive-assoc-list ->
                       palette -> sndfile-palette

     Version:          1.0.10

     Project:          slippery chicken (algorithmic composition)

     Purpose:          Implementation of the sndfile-palette class, which is a
                       simple palette that checks that all the sound files given
                       in a list for each id exist.  See comments in methods for
                       limitations and special features of this class.

     Author:           Michael Edwards: m@michael-edwards.org

     Creation date:    18th March 2001

     $$ Last modified:  08:45:34 Thu Sep 19 2019 CEST

     SVN ID: $Id$

## sndfile-palette/find-sndfile [ Methods ]

[ Top ] [ sndfile-palette ] [ Methods ]

DESCRIPTION

     Return the full directory path and file name of a specified sound file,
     from within the directories given in the PATHS slot.

ARGUMENTS

     - A sndfile-palette object.
     - The name of a sound file from within that object. This can be a string or
       a symbol. Unless it is given as a string, it will be handled as a symbol
       and will be converted to lowercase. Inclusion of the file extension is
       optional.

RETURN VALUE

     Returns the full directory path and file name of the specified sound file
     as a string.

EXAMPLE

    (let ((msfp (make-sfp 'sfp-test
                          '((sndfile-group-1
                             (test-sndfile-1))
                            (sndfile-group-2
                             (test-sndfile-2 test-sndfile-3
                              (test-sndfile-4 :frequency 261.61)))
                            (sndfile-group-3
                             ((test-sndfile-5 :start 0.006 :end 0.182)
                              test-sndfile-6)))
                          :paths
                          '("/path/to/sndfiles-dir-1"
                            "/path/to/sndfiles-dir-2"))))
     (find-sndfile msfp 'test-sndfile-4))

    => "/path/to/sndfiles-dir-2/test-sndfile-4.aiff"

SYNOPSIS

    (defmethod find-sndfile ((sfp sndfile-palette) sndfile)

## sndfile-palette/get-nearest [ Methods ]

[ Top ] [ sndfile-palette ] [ Methods ]

DESCRIPTION

     Get the sndfile whose frequency slot is nearest to the first argument.

ARGUMENTS

     - the frequency we're looking for, in Hertz (a number)
     - the sndfile-palette object we'll search

OPTIONAL ARGUMENTS

     - the IDs of the sndfile groups we'll search. Either a list of ids or a
     single id. If NIL then all groups will be searched. Default = NIL.
     - if more than one sndfile is very close in frequency, select one at random
     (see also the get-nearest-by-freq function)

RETURN VALUE

     the nearest sndfile object

SYNOPSIS

    (defmethod get-nearest (freq (sfp sndfile-palette) &optional ids random)

## sndfile-palette/get-nearest-by-freq [ Functions ]

[ Top ] [ sndfile-palette ] [ Functions ]

DATE

     October 1st 2015, Edinburgh

DESCRIPTION

     Given a frequency and a list of sndfile objects, return the sndfile from
     the list whose frequency is closest to the first argument.

ARGUMENTS

     - a frequency in Hertz (number)
     - a simple list of sndfile objects, such as that contained in the data slot
      of a sndfile-palette group.

OPTIONAL ARGUMENTS

     - T or NIL to indicate whether a random file should be chosen when there are
       several sndfiles with the same frequency. (NB the function random-rep will
       be used so if you wanted repeatable results call (random-rep 1 t) in order
       to reset the random seed before calling clm-play or whatever context uses
       this function.) Default = NIL.

RETURN VALUE

     the nearest sndfile object

SYNOPSIS

    (defun get-nearest-by-freq (freq sflist &optional random)

## sndfile-palette/get-sndfiles [ Functions ]

[ Top ] [ sndfile-palette ] [ Functions ]

DATE

     5th September 2015, Edinburgh

DESCRIPTION

     Return a list of the full paths of sound files in the given path. Files
     without the extensions aif, wav, aiff, and snd are ignored.

ARGUMENTS

     The full path to the folder where the sound files are, as a string

OPTIONAL ARGUMENTS

     - a list of folders to skip i.e. just the last folder name, not the
       complete path
     - A single pattern (string) or list of patterns that the sound
       file name must have--just the filename, excluding path/folders and
       extension. If a list then all patterns must be in the file name, not
       just one of them. Default = NIL.
     - Sim. to :insist except this/these are patterns none of which can
       be in the file name. Default = NIL.

RETURN VALUE

     A list of full paths as strings.

EXAMPLE

    (get-sndfiles "/music/hyperboles/snd/cello/samples/"
                  '("short-percussive" "weird"))
    -->
    ("/music/hyperboles/snd/cello/samples/1/g4-III-4-001.aif"
     "/music/hyperboles/snd/cello/samples/1/g4-III-4-002.aif"
     "/music/hyperboles/snd/cello/samples/1/g4-III-4-003.aif"
     "/music/hyperboles/snd/cello/samples/1/g4-III-4-004.aif"
     "/music/hyperboles/snd/cello/samples/10/cs5-I-5-9-13-4-001.aif"
     ...

SYNOPSIS

    (defun get-sndfiles (folder &optional skip insist resist)

## sndfile-palette/kontakt-to-sfp [ Functions ]

[ Top ] [ sndfile-palette ] [ Functions ]

DATE

     October 2nd 2015, Edinburgh

     DESCRIPTION This function is only available on Mac OSX 64bit Intel machines
     and requires the nki executable which since version 1.0.6 comes with
     slippery-chicken (in the bin directory).

     Using the public-domain C programme "nki tool" from the Linux Sampler
     project (http://www.linuxsampler.org/nkitool/), convert a Kontakt .nki
     sampler file to a sndfile-palette object. This will only include the sound
     file and the MIDI note number it is mapped to, but it will mean that the
     sndfile objects in the palette include the correct frequency. So using such
     a sndfile-palette makes it possible to have clm-play act as a traditional
     sampler, though of course much more is possible too.

     The nki file could of course map more than one sample to any MIDI note
     (e.g. for handling different velocities). By default all of these could be
     included in the sndfile-palette, and the :snd-selector function passed to
     clm-play could select the correct file based on velocity, for
     instance. However, you may want to filter out some sound files. This is
     where the :insist and :resist keyword arguments come in handy (see below).

     NB If you get the error message "zpipe: invalid or incomplete deflate data"
     then it's probably because the nki file is in Kontakt 4.2.2 (or higher)
     format so can't be converted with this tool. You could try "save as" in
     Kontakt 3 or 4 and rerunning this function, as those older programmes will
     probably save in the older format (worked for me at least once).

ARGUMENTS

     - the path to the .nki file to process
     - the path to the samples i.e. the sound files which the nki file uses

OPTIONAL ARGUMENTS

     Keyword arguments:
     :insist
     - :insist. A single pattern (string) or list of patterns that the sound
       file name must have--just the filename, excluding path/folders and
       extension. If a list then all patterns must be in the file name, not
       just one of them. Default = NIL i.e. accept all.
     - :resist. Sim. to :insist except this/these are patterns none of which can
       be in the file name. Default = NIL.
     - :group. The group ID that will be assigned to the sound files. Default =
       NIL whereby the name of the nki file will be used.
     - :converter. The path to the nki tool executable. Default =
       /path/to/slippery-chicken/bin/nki

RETURN VALUE

EXAMPLE

SYNOPSIS

    #+(and mac-osx X86-64)
    (defun kontakt-to-sfp (nki samples-path
                           &key insist resist group
                             (converter
                              (concatenate 'string
                                           cl-user::+slippery-chicken-home-dir+
                                           "bin/nki")))

## sndfile-palette/make-sfp [ Functions ]

[ Top ] [ sndfile-palette ] [ Functions ]

DESCRIPTION

     Make a sndfile-palette object. This object is a simple palette which checks
     to make sure that all of the sound files in a given list exist for each
     given ID.

     Sound files are given as as single names, without the path and without the
     extension. These can be given using the optional keyword arguments <paths>
     and <extensions>.

     NB Although this class is a palette and therefore a subclass of
     recursive-assoc-list, the sound lists in this case cannot be nested beyond
     a depth of two (as in example below).

ARGUMENTS

     - An ID for the palette.
     - A list of lists that contains IDs for the names of one or more groups of
       sound files, each paired with a list of one or more names of existing
       sound files. The sound file names themselves can be paired with keywords
       from the sndfile class, such as :start, :end, and :frequency, to define
       and describe segments of a given sound file.

OPTIONAL ARGUMENTS

     keyword arguments:
     - :paths. A list of one or more paths to where the sound files are located.
     - :extensions. A list of one or more sound file extensions for the
       specified sound files. The default initialization for this slot of the
       sndfile-palette already contains ("wav" "aiff" "aif" "snd"), so this
       argument can often be left unspecified.
     - :auto-freq. whether to do automatic frequency detection on the sound
       files. T would mean using a pitch detection routine, or a function can
       also be passed, whereupon it will be called with the path slot as
       argument (the idea is that the fundamental can be extracted from the file
       name). Default = NIL.
     - :warn-not-found. T or NIL to indicate whether a warning should be printed
       to the Lisp listener if the specified sound file cannot be found.
       T = print warning. Default = T.

RETURN VALUE

     Returns NIL.

EXAMPLE

    (let ((msfp (make-sfp 'sfp-test
                          '((sndfile-group-1
                             (test-sndfile-1))
                            (sndfile-group-2
                             (test-sndfile-2 test-sndfile-3
                              (test-sndfile-4 :frequency 261.61)))
                            (sndfile-group-3
                             ((test-sndfile-5 :start 0.006 :end 0.182)
                              test-sndfile-6)))
                          :paths '("/path/to/sound-files-dir-1/"
                                   "/path/to/sound-files-dir-2/")))))

SYNOPSIS

    (defun make-sfp (id sfp &key paths (extensions '("wav" "aiff" "aif" "snd"))
                              auto-freq (warn-not-found t))

## sndfile-palette/make-sfp-from-folder [ Functions ]

[ Top ] [ sndfile-palette ] [ Functions ]

DATE

     5th September 2015, Edinburgh

DESCRIPTION

     Makes a sndfile-palette object from the sound files found in a specific
     folder (directory) on the file system. Allows any arbitrary levels of
     subfolders with the proviso that they'll be converted to a flat list, using
     the subfolders as tags (e.g. dir/subdir1/subdir2 becomes
     dir-subdir1-subdir2).

     The folders can contain other files (they'll be ignored). Sound files are
     those with extensions .aif .wav .aiff and .snd

ARGUMENTS

     - the folder path, as a string.

OPTIONAL ARGUMENTS

     keyword arguments:
     - :skip. a list of folders to skip i.e. just the last folder name, not the
       complete path. Default = NIL.
     - :auto-freq. whether to do automatic frequency detection on the sound
       files. Default = NIL.
     - :insist. A single pattern (string) or list of patterns that the sound
       file name must have--just the filename, excluding path/folders and
       extension. If a list then all patterns must be in the file name, not
       just one of them. Default = NIL.
     - :resist. Sim. to :insist except this/these are patterns none of which can
       be in the file name. Default = NIL.

RETURN VALUE

     a sndfile-palette object

SYNOPSIS

EXAMPLE

    (make-sfp-from-folder "/music/hyperboles/snd/cello/samples/"
                          :skip '("short-percussive" "weird"))
    -->
    SNDFILE-PALETTE: paths: NIL
                     extensions: (wav aiff aif snd)
                     num-snds: 92
    PALETTE:
    RECURSIVE-ASSOC-LIST: recurse-simple-data: T
                          num-data: 16
                          linked: T
                          full-ref: NIL
    ASSOC-LIST: warn-not-found T
    CIRCULAR-SCLIST: current 0
    SCLIST: sclist-length: 16, bounds-alert: T, copy: T
    LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
    NAMED-OBJECT: id: AUTO, tag: NIL,
    data: (
    LINKED-NAMED-OBJECT: previous: NIL, this: (1), next: (10)
    NAMED-OBJECT: id: 1, tag: NIL,
    data: (

    SNDFILE-EXT: use: T, cue-num: 2, pitch: -1, pitch-curve: -1, bandwidth: -1,
                 bandwidth-curve: -1, continuity: -1, continuity-curve: -1,
                 weight: -1, weight-curve: -1, energy: -1, energy-curve: -1,
                 harmonicity: -1, harmonicity-curve: -1, volume: -1,
                 volume-curve: -1, loop-it: NIL, bitrate: 24, srate: 96000,
                 num-frames: 1719753, bytes: 5159314, group-id: (1)
                 followers: NIL

    SNDFILE: path: /music/hyperboles/snd/cello/samples/1/g4-III-4-004.aif,
             snd-duration: 17.914093, channels: 1, frequency: 150.24414
             start: 0.0, end: 17.914093, amplitude: 1.0, duration 17.914093
             will-be-used: 0, has-been-used: 0
             data-consistent: T
    ...
    |#
    (defun make-sfp-from-folder (folder &key skip auto-freq insist resist
                                          (default-group 'default-group))

## sndfile-palette/make-sfp-from-groups-in-wavelab-marker-file [ Functions ]

[ Top ] [ sndfile-palette ] [ Functions ]

DESCRIPTION

     Automatically generate a sndfile-palette object using the specified sound
     file from grouping defined in the specified wavelab marker file.

     The <marker-file> argument can be passed as a list of marker files, in
     which case these will first be concatenated.

ARGUMENTS

     - A string that is the name of the marker file, including the directory
       path and extension.
     - A string that is the name of the sound file. This can either be a full
       directory path, file name, and extension, or just a base file name. If
       the latter, values for the optional arguments :paths and :extensions must
       also be specified.

OPTIONAL ARGUMENTS

     - :paths. NIL or a list of strings that are the directory paths to the
       specified sound files. If the sound file is passed with the directory
       path, this must be set to NIL. NB: The paths given here apply only to the
       sound files, not to the marker files. Default = NIL.
     - :extensions. A list of strings that are the extensions to the given sound
       files. If the sound files are passed with their extensions, this must be
       set to NIL. Default = NIL.
     - :warn-not-found. T or NIL to indicate whether to print a warning to the
       listener if the specified sound file is not found. T = print a
       warning. Default = NIL.
     - :sampling-rate. An integer that is the sampling rate of the specified
       sound file. Changing this value will alter the start-times determined for
       each sound segment. Default = 44100.
     - :print. T or NIL to indicate whether feedback about the groups found and
       created should be printed to the listener. T = print. Default = T.

RETURN VALUE

     Returns NIL.

EXAMPLE

    (make-sfp-from-groups-in-wavelab-marker-file
     "/path/to/24-7.mrk"
     "24-7"
     :paths '("/path/to/sndfile/directory/")
     :sampling-rate 44100
     :extensions '("wav"))

    =>
    24 markers read from /path/to/24-7.mrk
    Adding tapping: 2.753 -> 4.827
    Adding tapping: 5.097 -> 6.581
    Adding tapping: 6.763 -> 8.538
    Adding splinter: 13.878 -> 15.993
    Adding tapping: 16.338 -> 18.261
    Adding splinter: 19.403 -> 25.655

    tapping: 4 sounds
    splinter: 2 sounds

SYNOPSIS

    (defun make-sfp-from-groups-in-wavelab-marker-file (marker-file sndfile
                                                        &key
                                                        paths
                                                        extensions
                                                        warn-not-found
                                                        (sampling-rate 44100)
                                                        (print t))

## sndfile-palette/make-sfp-from-wavelab-marker-file [ Functions ]

[ Top ] [ sndfile-palette ] [ Functions ]

DESCRIPTION

     Automatically create a sndfile-palette object from the specified wavelab
     marker file and the specified sound file (from which the marker file must
     have been generated).

     The function will produce a sndfile-palette object with multiple groups,
     each of which consists of the number of sound file segments specified using
     the :snds-per-group argument (defaults to 8). By default the segments will
     be collected into the groups in chronological order. If the optional
     :random-every argument is given a value, every nth group will consist of
     random segments instead.

     The sound file segments of each group will correspond to the time points
     stored in the marker file.

     The <marker-file> argument can consist of a list of marker files, in which
     case these would first be concatenated.

     NB: Be aware that marker files created on operating systems differing from
         the one on which this function is called might trigger errors due to
         newline character mismatches.

ARGUMENTS

     - A string that is the name of the marker file, including the directory
       path and extension.
     - A string that is the name of the sound file. This can either be a full
       directory path, file name, and extension, or just a base file name. If
       the latter, values for the optional arguments :paths and :extensions must
       also be specified.

OPTIONAL ARGUMENTS

     keyword arguments:
     - :snds-per-group. An integer that is the number of sound file segments to
       include in each group. Default = 8.
     - :random-every. An integer to indicate that every nth group is to consist
       of random (rather than chronologically consecutive) sound file segments.
       Default = 999999 (i.e. essentially never)
     - :paths. NIL or a list of strings that are the directory paths to the
       specified sound files. If the sound file is passed with the directory
       path, this must be set to NIL. NB: The paths given here apply only to the
       sound files, not to the marker files. Default = NIL.
     - :sampling-rate. An integer that is the sampling rate of the specified
       sound file. Changing this value will alter the start-times determined for
       each sound segment. Default = 44100.
     - :extensions. A list of strings that are the extensions to the given sound
       files. If the sound files are passed with their extensions, this must be
       set to NIL. Default = NIL.
     - :warn-not-found. T or NIL to indicate whether to print a warning to the
       listener if the specified sound file is not found. T = print a
       warning. Default = NIL.
     - :name. The name for the overall sndfile-palette and the base name for
        each group within (these will have a suffix that is an auto-incrementing
        number e.g. 'auto would become 'auto1 'auto2 etc.).  Default = 'auto.

RETURN VALUE

     A sndfile-palette object.

EXAMPLE

    (make-sfp-from-wavelab-marker-file
      "/path/to/24-7.mrk"
     "24-7"
     :snds-per-group 2
     :random-every 3
     :paths '("/path/to/sound-file/directory/")
     :sampling-rate 44100
     :extensions '("wav"))

    =>
    SNDFILE-PALETTE: paths: (/Volumes/JIMMY/SlipperyChicken/sc/test-suite/)
                     extensions: (wav)
    PALETTE:
    RECURSIVE-ASSOC-LIST: recurse-simple-data: T
                          num-data: 8
                          linked: NIL
                          full-ref: NIL
    ASSOC-LIST: warn-not-found NIL
    CIRCULAR-SCLIST: current 0
    SCLIST: sclist-length: 8, bounds-alert: T, copy: T
    LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
    NAMED-OBJECT: id: AUTO, tag: NIL,
    data: (
    NAMED-OBJECT: id: "auto1", tag: NIL,
    data: (

    SNDFILE: path: /Volumes/JIMMY/SlipperyChicken/sc/test-suite/24-7.wav,
             snd-duration: 29.652811, channels: 2, frequency: 261.62555
             start: 0.09142857, end: 1.0361905, amplitude: 1.0, duration 0.94476193
             will-be-used: 0, has-been-used: 0
             data-consistent: T
    LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
    NAMED-OBJECT: id: "24-7", tag: NIL,
    data: /Volumes/JIMMY/SlipperyChicken/sc/test-suite/24-7.wav
    [...]

SYNOPSIS

    (defun make-sfp-from-wavelab-marker-file (marker-file sndfile
                                              &key
                                              (snds-per-group 8)
                                              (random-every 999999) ;; i.e. never
                                              paths
                                              (sampling-rate 44100)
                                              extensions
                                              ;; MDE Fri Oct  5 14:04:08 2012
                                              (name 'auto)
                                              warn-not-found)

## sndfile-palette/set-frequency-from-filename [ Methods ]

[ Top ] [ sndfile-palette ] [ Methods ]

DATE

     October 1st 2015, Edinburgh

DESCRIPTION

     Set the frequency slot of every sound file object in the palate to a value
     extracted from the file name. This is intended but of course not restricted
     to palettes created from sample libraries, where, as is often the case, the
     file name of each sample contains the pitch of the file. For example, from
     the Akoustik Piano sample library we have the file
     Stein-R(A1)-V(220)-dB(2446)-T(MF)-M(123)-P(404-03-01).wav. In this case the
     A1 in the first parentheses indicate the pitch. By writing and passing a
     short parsing function to read the pitch out of such a file name we can
     easily process each sound file in the palette. See akoustik-piano-name
     below for an example of how to process such file names.

     See also make-sfp-from-folder.

ARGUMENTS

     - The sound file palette object

OPTIONAL ARGUMENTS

     Keyword arguments:
     - :groups. A single group or list of groups (IDs) for the sound files we
       wish to process.
     - :name-fun.The function for processing a single name. Of course the names
       must be consistent and each sound file must be able to be processed by
       this single function. NB for reasons of similar usage in get-spectra-al
       (see spectra.lsp) this function actually must return the MIDI note number
       (may be a floating point for microtonal applications), which is then
       converted by the sndfile class to a frequency in Hertz.
     - :on-error. The function to be called when the name function cannot
       determine the frequency of the sound file. This could be #'error (the
       default), #'warn, or nil if nothing is to be done on failure.

RETURN VALUE

     The sndfile-palette object after processing.

SYNOPSIS

    (defmethod set-frequency-from-filename
        ((sfp sndfile-palette)
         &key groups (name-fun #'akoustik-piano-name) (on-error #'error))