Ghost Bastards

I’m in Japan and reeling after playing a fab gig with Ghost Bastards: Cal Lyall (banjo + pedals & Darren Moore (drum kit + percussion). It was a wonderful event at the Shimokitazawa Three Basement Bar. Sensitive and attentive sound engineering, lovely crowd, wonderful musicianship. Recording to come soon…

Archived event info here: https://huh.jimdo.com/bhnd-02/

DSCN0585.jpg

Sounds Like THIS

My piece “Penance” is being diffused at Leeds College of Music‘s new festival, Sounds Like THIS.

The concert starts at 1pm at The Venue on Friday 17th March, 2017. Tickets £5.

More details here:

https://www.lcm.ac.uk/whats-on/Upcoming-events/Surround-Theatre

You can preview the track here:

mexpand.lsp

mexpand is a time-stretch instrument, adapted for slippery chicken.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; File:             mexpand.lsp
;;;
;;; Purpose:          A simple granular synthesis instrument.  Two
;;;                   overlapping streams of grains are played
;;;                   simultaneously.  The start time of the grain is
;;;                   controlled automatically to move through the
;;;                   input file over the duration of the output file.
;;;                   E.g. if the input duration is 10 secs and output
;;;                   is 20 secs, given a grain duration of 0.1 secs,
;;;                   then the grain increment will be 0.05 secs.
;;;                   Thus a simple form of time-stretching is
;;;                   accomplished.  See instrument parameters for
;;;                   more information.
;;;
;;; Author:           Michael Edwards - m@michael-edwards.org
;;;                   Adapted for use in slippery chicken by
;;;                   DJR - mr.danielross@gmail.com
;;;
;;;
;;; $$ Last modified: Fri 10 Feb 2017 16:37:12 GMT
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DJR Thu 19 Jan 2017 18:55:32 GMT
;; Added for clm-play compatibility
(in-package :clm)

;; We generally set this to double-float to avoid floating point precision
;; errors but CLM uses single-floats.  Change back at the end of this file if
;; necessary.
;; DJR Thu 19 Jan 2017 18:55:32 GMT
;; Added for clm-play compatibility
(setf *read-default-float-format* 'single-float)

(definstrument mexpand

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Beginning of Instrument Parameters
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    (file          ;; Input file
     time          ;; Output file start time
     &key
     ;; DJR Thu 19 Jan 2017 18:55:32 GMT
     ;; Moved duration to the &key args for clm-play compatibility
     duration    ;; Output duration
     ;; DJR Thu 19 Jan 2017 18:45:35 GMT
     ;; ignore, for clm-play compatibility
     frequency
     (start 0)     ;; Input file start time (seconds)
     (end nil)     ;; Input file end time (seconds); nil indicates the
                   ;; end of the file  
     (srt 1.0) ;; Sampling-Rate Conversion factor (1 = no transposition)
     (amp 1.0) ;; amplitude (generally 0.0 - 1.0)
     (degree 45)     ;; Stereo Panning: 0 = left, 90 = right
     (distance 0)    ;; how far away is the sound?
     (grain-dur .01) ;; Grain duration (seconds)
     (grain-env '(0 0 25 1 75 1 100 0)) ;; Grain Amplitude-Envelope 
     (amp-env '(0 0 5 1 95 1 100 0)) ;; Amp-Envelope for the output file
     (overlap .5)           ;; How much the grains overlap (0.0 - 1.0)
     (rev-amt 0)            ;; Reverb amount
     (rev-env '(0 1 100 1)) ;; Reverb envelope
     (width 20)              ;; the src width
     (printing t)) ;; whether to print the number of seconds computed 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; End of Instrument Parameters, beginning of Instrument Setup Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Which sample in the output file do we begin on?
  (let* ((st (floor (* time *srate*)))
         ;; input file start sample
         (input-start-sample (floor (* start *srate*)))
         ;; open file handles for the src generators
         (f1 (open-input* file :start input-start-sample))
         ;; start sample for the second stream will be set in the run
         ;; loop once this stream kicks in
         (f2 (open-input* file))
         ;; src generator for the first grain stream
         (src-gen1 (make-src :input f1 :srate srt :width width))
         ;; src generator for the second grain stream
         (src-gen2 (make-src :input f2 :srate srt :width width))
         ;; Panning und Reverb generator
         (loc (make-locsig :degree degree :distance distance :reverb rev-amt)) 
         ;; grain envelope for the first grain steam
         (gr-env1 (make-env :envelope grain-env :duration grain-dur  
                            :scaler 0.5))
         ;; grain envelope for the second grain steam
         (gr-env2 (make-env :envelope grain-env :duration grain-dur 
                            :scaler 0.5))
         ;; Envelope for the complete output file
         (ampf (make-env :envelope amp-env :scaler amp :duration duration))
         ;; Envelope for the Reverb Stream
         (renv (make-env :envelope rev-env :scaler rev-amt :duration duration))
         ;; how many grains per stream will there be in the output file?
         (number-of-grains (ceiling duration grain-dur))
         ;; duration of the input file
         (snd-dur (sound-duration f1))
         ;; how many samples we have from the input file.  Remember
         ;; that :end nil means use the whole file (from the given :start)
         (input-dur (* *srate*
                       (if end
                           (- end start)
                           (- snd-dur start))))
         ;; Now we know how many grains we're going to need, as well
         ;; as how many input samples we have.  Now calculate the
         ;; grain increment (through the input file) in samples.
         (read-inc (floor input-dur number-of-grains))
         ;; convert grain duration in seconds to samples
         (grain-dur-in-samples (floor (* *srate* grain-dur)))
         (grain-overlap-samples (floor (* overlap grain-dur-in-samples))) 
         ;; where (in samples) do we start the first stream in the input file?
         (start1-in-samples input-start-sample)
         ;; And for the second stream?  This is dependent on :overlap
         (start2-in-samples (+ start1-in-samples grain-overlap-samples))
         ;; At which output file samples does the first grain stop?
         ;; This is where the first envelope stops and restarts too.
         (env1-stop (+ st grain-dur-in-samples))
         ;; Same for the second stream first grain.  Note here that
         ;; this is actually the point at which the second stream strarts.
         (env2-stop (+ st grain-overlap-samples))
         ;; The second stream doesn't play from the beginning
         (do-stream2 nil) 
         ;; Sample counter for the print function
         (count 0)
         ;; At which sample in the output file do we stop?
         (nd (+ st (floor (* *srate* duration)))))
    ;; Print the start time
    (when printing 
      (format t "~&Start time ~a.~%" time))
    ;; Some sanity checks:
    ;; Check that the given start and end times are not bigger than
    ;; the input file duration
    (when (or (> start snd-dur) (and end (> end snd-dur)))
      (error "Illegal file access requested: ~
              Input duration = ~a, :start = ~a, :end = ~a"
             snd-dur start end))
    ;; Make sure that start isn't greater than end
    (when (and end (>= start end))
      (error ":start (~a) >= :end (~a) ????" start end))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Ende of setup code.  Beginning of run loop.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    (run 
     ;; We loop from the output file start to the end sample
     (loop for i from st to nd do
        ;; if requested, print each time we've computed a second of sound
          (when printing 
            (if (= count *srate*)
                (progn
                  (setq count 1)
                  (print (round (/ i *srate*))))
                (incf count)))
        ;; Have we finished the first stream's current grain?
        ;; Im ersten Stream, haben wir den ersten Grain erledigt?
          (when (> i env1-stop)
            ;; update the next grain end sample and the input file 
            ;; start time for this grain
            (incf env1-stop grain-dur-in-samples)
            (incf start1-in-samples read-inc)
            ;; We can restart envelopes in CLM: we need to do this for 
            ;; the grain envelope here. 
            (mus-reset gr-env1)
            (mus-reset src-gen1) ;; reset the src too
            ;; If we're doing a time stretch, then our src generator
            ;; is now further along than the new start point so reset
            (setf (mus-location src-gen1) start1-in-samples))
        ;; Same for the second stream
          (when (> i env2-stop)
            (incf env2-stop grain-dur-in-samples)
            (mus-reset gr-env2)
            (mus-reset src-gen2)
            ;; The only difference here to stream 1 is that we now 
            ;; tell the system that stream 2 is in use (via 
            ;; do-stream2) and increment start2-in-samples after 
            ;; setting the src location rather than before 
            (setf do-stream2 t
                  (mus-location src-gen2) start2-in-samples)
            (incf start2-in-samples read-inc))
        ;; Set the reverb amount according to our envelope 
          (setf (locsig-reverb-ref loc 0) (env renv))
        ;; Output the sample via locsig 
          (locsig loc i (* (env ampf)   ; Signal * current amp-env value
                           ;; We add the two streams together
                           ;; Grain amp-env * SRC generator value 
                           (+ (* (env gr-env1) (src src-gen1)) 
                              ;; If stream 2 hasn't yet begun, we add 0
                              (if do-stream2
                                  (* (env gr-env2) (src src-gen2))
                                  0.0))))))
    (close-input f1)
    (close-input f2)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; End of instrument code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#|

(clm-play +tp+ 1 'el 'default-group
	  :pitch-synchronous t
	  :check-overwrite nil
	  :duration-run-over t
	  :output-name-uniquifier "mexpand-"
	  :pan-min-max '(30 60)
	  :rev-amt .05
	  :amp-env '(0 0 20 1 70 1 100 0)
	  :clm-ins #'clm::mexpand
	  :clm-ins-args '(:grain-dur .001
			  :overlap 0.01
			  :grain-env (0 0.1 100 .1)))

|#


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EOF mexpand.lsp

splinter.lsp

Adapted version of splinter.ins for use in slippery chicken.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; File:             splinter.lsp
;;;
;;; Version:          1.1 for slippery chicken
;;;
;;; Purpose:          Common Lisp Music (clm-2) Instrument for multi-voice
;;;                   granulation of a sound file with sampling-rate conversion
;;;                   for each separate voice.  
;;;
;;;                   Accepts mono input files only and produces stereo output
;;;                   with grains distributed evenly across the two channels.
;;;
;;; Author:           Michael Edwards: m@michael-edwards.org
;;;                   Adapted for use in slippery chicken by
;;;                   DJR - mr.danielross@gmail.com
;;;
;;; Courtesy:         If you use this instrument, or any other written by me,
;;;                   please be courteous enough to acknowledge this fact in
;;;                   programme notes etc.
;;;
;;; Usage:            The instrument has the following parameters:
;;;
;;;                   FILE: Input sound file
;;;
;;;                   TIME: output start time (secs.)
;;;                   
;;;                   CHANNEL: If input is multi-channel, which
;;;                   channel to process (0=left, 1=right etc.) 
;;;                   
;;;                   START: input file start time (secs.)
;;;                   
;;;                   END: input file stop time (secs.).  When zero, then the
;;;                   end of the file.  Grains will be generated using samples
;;;                   only between start and end.
;;;                   
;;;                   DURATION: output file duration (secs.).  When zero, then
;;;                   also end - start.
;;;                   
;;;                   GRAIN-ENV: the grain lengths in secs, ranging over the
;;;                   length of the output file.  This will be set to
;;;                  +MIN-GRAIN-LENGTH+ if too small.
;;;                   
;;;                   CENTER-DEVIATION-ENV: the amount by which the grain can
;;;                   randomly deviate from the current input sample (which is
;;;                   incremented as time progresses by an amount related to
;;;                   the input file duration and output file duration--if the
;;;                   input is 10 secs long and the output is to be 100 secs,
;;;                   we will increment the current sample every 10 (output)
;;;                   samples).
;;;                   
;;;                   The envelope values should be between 0 and 1 (0 means
;;;                   use only the current sample as start of grain and 1 means
;;;                   range over the entire length of the sound file (or,
;;;                   rather, between START and END)--before and after current
;;;                   sample).
;;;                   
;;;                   GRAIN-LENGTH-SCALERS: After reading grain-env to
;;;                   calculate the current grain length, the value is scaled
;;;                   by a random amount between these two extremes.  Can only
;;;                   be a list of two values.
;;;                   
;;;                   PRINT-GRAIN-INFO: If t, then information about grain
;;;                   length etc. will be printed each time a new grain is
;;;                   created.
;;;                   
;;;                   DEBUG: If t, more information is printed for debugging
;;;                   purposes, no input samples are read and no output samples
;;;                   are written.
;;;                   
;;;                   SILENCE-ENV: Controls which percentage of the grains will
;;;                   be played.  y values should range between 0.0 and 1.0
;;;                   where 1.0 means all the grains will be silent and 0.0 all
;;;                   will be played.  (The envelope value is used as a
;;;                   threshold against which a random value > 0 and < 1 is
;;;                   compared to decide whether or not the grain will be
;;;                   output.)
;;;                   
;;;                   VOICES: How many grains (or grain layers) are played
;;;                   simultaneously.
;;;                   
;;;                   SRT: This can either be a single value or a list of
;;;                   values.  If a single value, then this will be the
;;;                   sampling-rate conversion factor for every grain in each
;;;                   voice.  If a list, then the values will be the src
;;;                   factors for each successive voice (remains the same for
;;;                   every grain in the voice, though c.f. srt-expt-env).  The
;;;                   number of values does not need to be the same as the
;;;                   number of voices, as they will simply be repeated (if not
;;;                   enough) or ignored (if too many), e.g. srt = (1 2 3)
;;;                   voices = 7, srt for each voice = (1 2 3 1 2 3 1).
;;;                   
;;;                   ***** N.B.  If srt = 1.0, then simple sound file
;;;                   reading is performed (via IN-ANY) instead of
;;;                   SRC, which for me is at least four times faster.
;;;
;;;                   For the case where srt is 1.0 but you want an srt-env,
;;;                   set srt to t 
;;;                   
;;;                   WIDTH: The sync width for the sampling-rate
;;;                   conversion (5 is the CLM default but a higher value will
;;;                   increase quality at the expense of processing time--try
;;;                   20 for example).
;;;                   
;;;                   SRT-ENV: a transposition envelope for glissandi
;;;                   
;;;                   SRT-EXPT-ENV: This tries to create some pitch development
;;;                   in the output file by raising the srts of each voice to
;;;                   the exponent given by the envelope.  This is done on a
;;;                   grain-by-grain, not a sample-by-sample basis.  If the
;;;                   envelope value is  1 wider.
;;;                   
;;;                   SRT-RANDOM-ENV: Another way to vary pitch: the srt of
;;;                   each grain will be scaled by a random factor between 1
;;;                   and 1 +/- the envelope value.
;;;                   
;;;                   GRAIN-FOCUS-ENV: This envelope creates a focus on certain
;;;                   voices in the mix and so only really makes sense when
;;;                   you're using multiple srts.  The y values range from 0 to
;;;                   1 where 0 means that most of the amplitude energy will be
;;;                   given to the first voice; 1 means that most of the energy
;;;                   will be on the highest voice; and .5 means most will be
;;;                   on the middle voice with the outer voices having the same
;;;                   (lowest) energy.
;;;                   
;;;                   GRAIN-FOCUS-ENV-EXPT: The exponent that the amplitudes
;;;                   applied to the grains from grain-focus-env are raised to.
;;;                   
;;;                   GRAIN-FOCUS-ENV-MIN: The minimum amplitude value that a
;;;                   voice should have when processed by grain-focus-env.  NB
;;;                   This does not relate to the actual envelope values (which
;;;                   always range >=0  end file-dur)
                          (progn
                            (warn "end (~f) > file duration (~f).  ~
                                 Using file duration instead of end."
                                  end file-dur)
                            file-dur)
                        end)))
         (real-dur (if (= duration 0) (- input-end start) duration))
         (input-start-samples (floor (* *srate* start)))
         (input-end-samples (floor (* *srate* input-end)))
         (num-input-samples (floor (- input-end-samples input-start-samples)))
         (nd (+ st (floor (* *srate* real-dur))))
         (output-chans (mus-channels *output*))
         (do-src (if (eq srt t)
                     (setf srt 1.0)
                   (or srt-random-env srt-expt-env 
                       (not (and (numberp srt) (= srt 1.0))))))
         (files (when do-src
                  (make-array voices :initial-contents 
                              (loop repeat voices collect
                                    (open-input* file :channel channel)))))
         (fil (unless do-src (open-input* file)))
         (srcs (when do-src
                 (make-array voices :initial-contents
                             (if (listp srt)
                                 (loop for i below voices collect 
                                       (nth (mod i (length srt)) srt))
                               (loop repeat voices collect srt)))))
         (src-gens (when do-src
                     (make-array voices :initial-contents
                                 (loop for i below voices collect
                                       (make-src :input (aref files i) 
                                                 :srate (aref srcs i)
                                                 :width width)))))
         (sexp-env (when srt-expt-env
                     (make-env :envelope srt-expt-env :duration real-dur )))
         (srtenv (make-env :envelope srt-env :duration real-dur))
         (renv (when rev-env 
                 (make-env :envelope rev-env :duration real-dur
                           :scaler rev-amt)))
         ;; We have a "central" sample which is used as a place from which we
         ;; can deviate when choosing grain starting points.  This is
         ;; incremented over the whole length of the input samples as we
         ;; progress through the algorithm.  We have to pace this so that we
         ;; use all the samples by the time we have finished the output file so
         ;; we calculate the increment here.
         (input-inc (/ (float num-input-samples) (float (- nd st))))
         (ampf (make-env :envelope amp-env :scaler amp :duration real-dur))
         (gr-env (make-env :envelope grain-env
                           :duration real-dur)) 
         (dev-env (make-env :envelope center-deviation-env :duration real-dur))
         (s-env (when silence-env
                  (make-env :envelope silence-env :duration real-dur)))
         (srt-renv (when srt-random-env
                     (make-env :envelope srt-random-env :duration real-dur)))
         (gfe (when grain-focus-env 
                (make-env :envelope grain-focus-env :duration real-dur)))
         (min-grain-scaler (min (car grain-length-scalers)
                                (cadr grain-length-scalers)))
         (max-grain-scaler (max (car grain-length-scalers)
                                (cadr grain-length-scalers)))
         (grain-scaler-diff (- max-grain-scaler min-grain-scaler))
         ;; the grain length at the beginning (samples).
         (grain-length-beg (floor (* *srate* (second grain-env))))
         (pos 0.0)
         (1-minus-pos 0.0)
         ;; we use pos to put the sound between two speakers and
         ;; these vars to choose which speakers these will be
         (out1-chan 0)
         (out2-chan 0)
         ;; This number gets incremented by  each time through the
         ;; run loop (hence it's a float).  Each time it gets to 1.0 or more,
         ;; we increment the input sample and reset it to 0.0.
         (counter 0.0)
         (current-input-sample 0)
         (gr-len-samples 0)  
         ;; This will hold the envelope value from 
         (deviation 0.0)
         ;; The deviation is randomised between 0.0 and  so this is
         ;; the actual value we will use.
         (random-deviation 0.0)
         ;; The start sample for the current grain.
         (start-sample 0)
         ;; How many samples are before .
         (before-current 0)
         ;; How many samples are after .
         (after-current 0)
         ;; Sample value for left channel.
         (out1 0.0)
         ;; Sample value for right channel.
         (out2 0.0)
         (ampval 0.0)
         ;; We have to window (ramp up/down) the grains.  This is the current
         ;; window sample used to scale the grain values.
         (scl 1.0)
         ;; Are we going to deviate before or after  when
         ;; we randomly choose our grain start point.
         (up? nil)
         ;; This one is just for printing seconds computed.
         (count 0)
         (gr-len-warnings 0)
         (srt-warnings 0)
         (grain-index 0)
         (actual-grain-length 0)
         (do-grain t)
         (steady-begins ramp-len)
         (sample 0.0)
         (samps-needed 0)
         (s-val 0.0)
         (srt-renv-val 0.0)
         (sexp-val 0.0)
         (gfe-val 0.0)
         (srt-offset 0.0)
         (gr-val 0.0)
         (current-srt 0.0)
         (actual-srt 0.0)
         (ramp-down-begins 0)
         (ramp-inc 1)
         ;; We do this here because we can't call make-fft-window inside run.
         (window (make-fft-window blackman2-window (* 2 ramp-len)))
         (ramp-up (make-array ramp-len :element-type 'double-float
                              :displaced-to window 
                              :displaced-index-offset 0))
         (ramp-down (make-array ramp-len :element-type 'double-float
                                :displaced-to window 
                                :displaced-index-offset ramp-len)))
    ;; The window created does not start and end with 0 so make it so.
    (setf (aref window 0) 0.0d0 ;;(make-short-float 0.0d0)
          (aref window (1- (car (array-dimensions window)))) 0.0d0
          ;;(make-short-float 0.0)
          )
    (when debug
      (format t "~&real-dur = ~f input-inc = ~f input-start-samples=~d ~
                  (~f secs) input-end-samples=~d (~f secs) ~%~%" 
              real-dur input-inc input-start-samples 
              (/ input-start-samples *srate*)
              input-end-samples (/ input-end-samples *srate*))
      (if do-src 
          (print "Using SRC")
        (print "Using INA")))
    (run
     (loop for voice below voices do
           #|
           (when do-src
             (declare (type (array sr) src-gens)
             (type (array io) files)))
             |#
           (declare (type :integer output-chans out1-chan out2-chan))
           (when (> voice 0)
             (restart-env ampf)
             (restart-env gr-env)
             (restart-env dev-env)
             (restart-env srtenv)
             (when grain-focus-env (restart-env gfe))
             (when silence-env (restart-env s-env))
             (when srt-random-env (restart-env srt-renv))
             (when srt-expt-env (restart-env sexp-env)))
           (setq current-input-sample input-start-samples
                 before-current 0
                 after-current num-input-samples
                 grain-index 0
                 gr-len-samples 
                 (if start-together 
                     0
                   (progn 
                     (setq do-grain nil)
                     (floor 
                      (if (= counter 1.0)
                   (incf current-input-sample)
                   (incf before-current)
                   (decf after-current)
                   (setq counter 0.0))
                 ;;
                 ;; START NEW GRAIN DATA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                 ;;
                 ;; Have we written out all the current grain's samples?  If so
                 ;; make a new grain:
                 (when (>= grain-index gr-len-samples)
                   ;; Silent grain?
                   (setq do-grain (if silence-env 
                                      (> (random 1.0) s-val)
                                    t)
                         ;; randomise the grain length based on the envelope
                         ;; value.  randomly choose whether to start the grain
                         ;; before or after our current input sample.
                         ;; clm's random function is not like lisp's as it 
                         ;; returns always a float, not an int, therefore this
                         ;; is a bit more complicated than a simple 
                         ;; (zerop (random 2))
                         up? (>= .5 (random 1.0))
                         ;; Get the deviation amount from our deviation
                         ;; envelope value.    
                         random-deviation (if (= deviation 0.0)
                                              0.0
                                            (random deviation))
                         pos (+ .05 (random .9))
                         1-minus-pos (- 1.0 pos)
                         out1-chan (random output-chans)
                         out2-chan (if (= out1-chan (1- output-chans))
                                       0
                                     (1+ out1-chan))
                         ;; Set the start sample based on whether we're going
                         ;; to start before or after the current sample and the
                         ;; random deviation.
                         actual-grain-length 
                         (* gr-val (+ min-grain-scaler 
                                      (random grain-scaler-diff)))
                         gr-len-samples (floor 
                                         (* *srate* actual-grain-length))
                         ;; the srt given for this voice
                         current-srt (when do-src (aref srcs voice))
                         ;; the srt we want, as given by the srt-expt-env
                         actual-srt (* (if srt-random-env
                                           (1+ (if up?
                                                   (random srt-renv-val)
                                                 (- (random srt-renv-val))))
                                         1.0)
                                       (if srt-expt-env
                                           (expt current-srt sexp-val)
                                         current-srt))
                         ;; what we'll have to add to the src gen to get our
                         ;; desired srt
                         srt-offset (if (or srt-expt-env srt-random-env)
                                           (- actual-srt current-srt)
                                         0.0)
                         samps-needed (if do-src
                                          (* gr-len-samples actual-srt)
                                        gr-len-samples)
                         start-sample 
                         (floor (if up? 
                                    (+ current-input-sample 
                                       (* after-current random-deviation))
                                  (- current-input-sample
                                     (* before-current random-deviation)))))
                   ;; (warn "~&out1-chan: ~a" out1-chan)
                   ;; (print out1-chan)
                   ;; Check to see if there's enough output samples left to 
                   ;; write our grain
                   (when (> (+ i gr-len-samples) nd)
                     ;; If there's not even enough for our min grain length
                     ;; then simply don't write the grain.
                     (if (> (+ i +SPLINTER-MIN-GRAIN-LENGTH+) nd)
                         (setq do-grain nil)
                       (setq gr-len-samples (- nd i))))
                   ;; Don't go past the end of the input samples.
                   (when (> (+ start-sample samps-needed)
                            input-end-samples) 
                     (setq start-sample (- input-end-samples samps-needed)))
                   ;; Don't try to start too soon!
                   (when (< start-sample input-start-samples)
                     (setq start-sample input-start-samples
                           gr-len-samples (floor (/ num-input-samples 
                                                    actual-srt)))
                     (when (< srt-warnings 30)
                       (clm-print "~%Not enough samps for grain len of ~fs."
                                  actual-grain-length)
                       (clm-print "~%   with an actual srt of ~f" actual-srt)
                       (clm-print "~%   num-input-samples = ~d" 
                                  num-input-samples)
                       (clm-print "~%   Grain length will be of ~d (~f secs)"
                                  gr-len-samples 
                                  (float (/ gr-len-samples *srate*)))
                       (incf srt-warnings)))
                   ;; Don't let the grain be smaller than our defined minimum. 
                   (when (< gr-len-samples +SPLINTER-MIN-GRAIN-LENGTH+)
                     (when (< gr-len-warnings 30)
                       (clm-print "~%Grain len too short. Changing ~d to ~d"
                                  gr-len-samples +SPLINTER-MIN-GRAIN-LENGTH+)
                       (incf gr-len-warnings))
                     (setq gr-len-samples +SPLINTER-MIN-GRAIN-LENGTH+))
                   ;; Get our ramp-down point.
                   (setq ramp-down-begins (- gr-len-samples ramp-len)
                         ramp-inc 1
                         current-ramp-len ramp-len
                         steady-begins current-ramp-len
                         ;; Reset the grain-index.
                         grain-index 0)
                   ;; If our grain length is too short for the ramp, adjust.
                   (when (< ramp-down-begins current-ramp-len)
                     (setq ramp-down-begins (floor gr-len-samples 2)
                           steady-begins ramp-down-begins
                           current-ramp-len ramp-down-begins
                           ;; Skip some of our ramp values so that we arrive
                           ;; at the end of the ramp in time.
                           ramp-inc (round ramp-len ramp-down-begins))
                     (when debug
                       (clm-print "~%ramp-down-begins ~d ramp-inc ~d"
                                  ramp-down-begins ramp-inc)))
                   (when do-src (setf (mus-location (aref src-gens voice)) 
                                  start-sample))
                   (when debug 
                     (clm-print 
                      "~%i = ~d (~f secs) current-input-sample = ~d (~f secs)"
                      i (/ i *srate*) 
                      current-input-sample 
                      (/ current-input-sample *srate*)))
                   (when print-grain-info
                     (clm-print "~%Grain start = ~d samps = ~f secs"
                                start-sample (/ start-sample *srate*))
                     (clm-print "~%   Grain length = ~d samps = ~f secs"
                                gr-len-samples 
                                (/ gr-len-samples *srate*))
                     (clm-print "~%   srt-offset=~f actual-srt=~f"
                                srt-offset actual-srt)
                     (clm-print "~%   ramp-inc = ~d grain index = ~d" 
                                ramp-inc grain-index)
                     (clm-print "~%   steady-begins ~d ramp-down-begins ~d"
                                steady-begins ramp-down-begins)))
                 ;;                 
                 ;; END NEW GRAIN DATA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                 ;;
                 (unless debug
                   (if do-grain
                       (setq scl (cond ((< grain-index steady-begins) 
                                        (aref ramp-up 
                                              (floor (min 1-minus-ramp-len 
                                                          (* ramp-inc 
                                                             grain-index)))))
                                       ((< grain-index ramp-down-begins) 1.0)
                                       (t (aref ramp-down 
                                                (floor 
                                                 (min 1-minus-ramp-len 
                                                      (- (* ramp-inc
                                                            grain-index)
                                                         ramp-down-begins))))))
                             gfe-val (if grain-focus-env
                                         (get-grain-amp (env gfe) voice voices
                                                        :min 
                                                        grain-focus-env-min
                                                        :expt 
                                                        grain-focus-env-expt)
                                       1.0)
                             sample (* scl gfe-val
                                       (if do-src
                                           (src (aref src-gens voice) 
                                                (+ (env srtenv) srt-offset))
                                         (in-any (+ grain-index start-sample) 
                                                 channel fil)))
                             out1 (* ampval 1-minus-pos sample)
                             out2 (* ampval pos sample))
                     (setq out1 0.0
                           out2 0.0))
                   ;; Write out the samples and reverb them.
                   (out-any i out1 out1-chan)
                   (out-any i out2 out2-chan)
                   (when *reverb* 
                     (outa i (* (if rev-env (env renv) rev-amt)
                                (/ (+ out1 out2) 2.0))
                           *reverb*)))
                 (incf grain-index))))
    (if do-src 
        (loop for v below voices do (close-input (aref files v)))
      (close-input fil))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#|

;; Example usage in clm-play

(clm-play +mini+ 1 'pno 'default-group
	      :pitch-synchronous t
	      :check-overwrite nil
	      :duration-run-over t
              :output-name-uniquifier "samp2-"
	      :snd-selector #'(lambda (sflist pitch event)
				(declare (ignore event))
				(get-nearest-by-freq
				 (frequency pitch) (data sflist)))
	      :pan-min-max '(30 60)
	      :rev-amt .05
	      :src-scaler 0.25
	      :amp-env '(0 0 5 1 70 1 100 0)
	      :output-name-uniquifier "splinter-"
	      :clm-ins #'clm::splinter	
	      :clm-ins-args '(:voices 8	
			      :ramp 5	
			      :grain-env (0 .01 50 .5 100 .01) 
			      :amp-to-voices t)

|#

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EOF splinter.lsp

samp2.lsp

Jules Rawlinson adapted Mike’s original samp1 instrument to include filters, lfos and waveshaping. Now I have adapted it to work in slippery chicken.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; File:             splinter.lsp
;;;
;;; Version:          1.1 for slippery chicken
;;;
;;; Purpose:          Common Lisp Music (clm-2) Instrument for multi-voice
;;;                   granulation of a sound file with sampling-rate conversion
;;;                   for each separate voice.  
;;;
;;;                   Accepts mono input files only and produces stereo output
;;;                   with grains distributed evenly across the two channels.
;;;
;;; Author:           Michael Edwards: m@michael-edwards.org
;;;                   Adapted for use in slippery chicken by
;;;                   DJR - mr.danielross@gmail.com
;;;
;;; Courtesy:         If you use this instrument, or any other written by me,
;;;                   please be courteous enough to acknowledge this fact in
;;;                   programme notes etc.
;;;
;;; Usage:            The instrument has the following parameters:
;;;
;;;                   FILE: Input sound file
;;;
;;;                   TIME: output start time (secs.)
;;;                   
;;;                   CHANNEL: If input is multi-channel, which
;;;                   channel to process (0=left, 1=right etc.) 
;;;                   
;;;                   START: input file start time (secs.)
;;;                   
;;;                   END: input file stop time (secs.).  When zero, then the
;;;                   end of the file.  Grains will be generated using samples
;;;                   only between start and end.
;;;                   
;;;                   DURATION: output file duration (secs.).  When zero, then
;;;                   also end - start.
;;;                   
;;;                   GRAIN-ENV: the grain lengths in secs, ranging over the
;;;                   length of the output file.  This will be set to
;;;                  +MIN-GRAIN-LENGTH+ if too small.
;;;                   
;;;                   CENTER-DEVIATION-ENV: the amount by which the grain can
;;;                   randomly deviate from the current input sample (which is
;;;                   incremented as time progresses by an amount related to
;;;                   the input file duration and output file duration--if the
;;;                   input is 10 secs long and the output is to be 100 secs,
;;;                   we will increment the current sample every 10 (output)
;;;                   samples).
;;;                   
;;;                   The envelope values should be between 0 and 1 (0 means
;;;                   use only the current sample as start of grain and 1 means
;;;                   range over the entire length of the sound file (or,
;;;                   rather, between START and END)--before and after current
;;;                   sample).
;;;                   
;;;                   GRAIN-LENGTH-SCALERS: After reading grain-env to
;;;                   calculate the current grain length, the value is scaled
;;;                   by a random amount between these two extremes.  Can only
;;;                   be a list of two values.
;;;                   
;;;                   PRINT-GRAIN-INFO: If t, then information about grain
;;;                   length etc. will be printed each time a new grain is
;;;                   created.
;;;                   
;;;                   DEBUG: If t, more information is printed for debugging
;;;                   purposes, no input samples are read and no output samples
;;;                   are written.
;;;                   
;;;                   SILENCE-ENV: Controls which percentage of the grains will
;;;                   be played.  y values should range between 0.0 and 1.0
;;;                   where 1.0 means all the grains will be silent and 0.0 all
;;;                   will be played.  (The envelope value is used as a
;;;                   threshold against which a random value > 0 and < 1 is
;;;                   compared to decide whether or not the grain will be
;;;                   output.)
;;;                   
;;;                   VOICES: How many grains (or grain layers) are played
;;;                   simultaneously.
;;;                   
;;;                   SRT: This can either be a single value or a list of
;;;                   values.  If a single value, then this will be the
;;;                   sampling-rate conversion factor for every grain in each
;;;                   voice.  If a list, then the values will be the src
;;;                   factors for each successive voice (remains the same for
;;;                   every grain in the voice, though c.f. srt-expt-env).  The
;;;                   number of values does not need to be the same as the
;;;                   number of voices, as they will simply be repeated (if not
;;;                   enough) or ignored (if too many), e.g. srt = (1 2 3)
;;;                   voices = 7, srt for each voice = (1 2 3 1 2 3 1).
;;;                   
;;;                   ***** N.B.  If srt = 1.0, then simple sound file
;;;                   reading is performed (via IN-ANY) instead of
;;;                   SRC, which for me is at least four times faster.
;;;
;;;                   For the case where srt is 1.0 but you want an srt-env,
;;;                   set srt to t 
;;;                   
;;;                   WIDTH: The sync width for the sampling-rate
;;;                   conversion (5 is the CLM default but a higher value will
;;;                   increase quality at the expense of processing time--try
;;;                   20 for example).
;;;                   
;;;                   SRT-ENV: a transposition envelope for glissandi
;;;                   
;;;                   SRT-EXPT-ENV: This tries to create some pitch development
;;;                   in the output file by raising the srts of each voice to
;;;                   the exponent given by the envelope.  This is done on a
;;;                   grain-by-grain, not a sample-by-sample basis.  If the
;;;                   envelope value is  1 wider.
;;;                   
;;;                   SRT-RANDOM-ENV: Another way to vary pitch: the srt of
;;;                   each grain will be scaled by a random factor between 1
;;;                   and 1 +/- the envelope value.
;;;                   
;;;                   GRAIN-FOCUS-ENV: This envelope creates a focus on certain
;;;                   voices in the mix and so only really makes sense when
;;;                   you're using multiple srts.  The y values range from 0 to
;;;                   1 where 0 means that most of the amplitude energy will be
;;;                   given to the first voice; 1 means that most of the energy
;;;                   will be on the highest voice; and .5 means most will be
;;;                   on the middle voice with the outer voices having the same
;;;                   (lowest) energy.
;;;                   
;;;                   GRAIN-FOCUS-ENV-EXPT: The exponent that the amplitudes
;;;                   applied to the grains from grain-focus-env are raised to.
;;;                   
;;;                   GRAIN-FOCUS-ENV-MIN: The minimum amplitude value that a
;;;                   voice should have when processed by grain-focus-env.  NB
;;;                   This does not relate to the actual envelope values (which
;;;                   always range >=0  end file-dur)
                          (progn
                            (warn "end (~f) > file duration (~f).  ~
                                 Using file duration instead of end."
                                  end file-dur)
                            file-dur)
                        end)))
         (real-dur (if (= duration 0) (- input-end start) duration))
         (input-start-samples (floor (* *srate* start)))
         (input-end-samples (floor (* *srate* input-end)))
         (num-input-samples (floor (- input-end-samples input-start-samples)))
         (nd (+ st (floor (* *srate* real-dur))))
         (output-chans (mus-channels *output*))
         (do-src (if (eq srt t)
                     (setf srt 1.0)
                   (or srt-random-env srt-expt-env 
                       (not (and (numberp srt) (= srt 1.0))))))
         (files (when do-src
                  (make-array voices :initial-contents 
                              (loop repeat voices collect
                                    (open-input* file :channel channel)))))
         (fil (unless do-src (open-input* file)))
         (srcs (when do-src
                 (make-array voices :initial-contents
                             (if (listp srt)
                                 (loop for i below voices collect 
                                       (nth (mod i (length srt)) srt))
                               (loop repeat voices collect srt)))))
         (src-gens (when do-src
                     (make-array voices :initial-contents
                                 (loop for i below voices collect
                                       (make-src :input (aref files i) 
                                                 :srate (aref srcs i)
                                                 :width width)))))
         (sexp-env (when srt-expt-env
                     (make-env :envelope srt-expt-env :duration real-dur )))
         (srtenv (make-env :envelope srt-env :duration real-dur))
         (renv (when rev-env 
                 (make-env :envelope rev-env :duration real-dur
                           :scaler rev-amt)))
         ;; We have a "central" sample which is used as a place from which we
         ;; can deviate when choosing grain starting points.  This is
         ;; incremented over the whole length of the input samples as we
         ;; progress through the algorithm.  We have to pace this so that we
         ;; use all the samples by the time we have finished the output file so
         ;; we calculate the increment here.
         (input-inc (/ (float num-input-samples) (float (- nd st))))
         (ampf (make-env :envelope amp-env :scaler amp :duration real-dur))
         (gr-env (make-env :envelope grain-env
                           :duration real-dur)) 
         (dev-env (make-env :envelope center-deviation-env :duration real-dur))
         (s-env (when silence-env
                  (make-env :envelope silence-env :duration real-dur)))
         (srt-renv (when srt-random-env
                     (make-env :envelope srt-random-env :duration real-dur)))
         (gfe (when grain-focus-env 
                (make-env :envelope grain-focus-env :duration real-dur)))
         (min-grain-scaler (min (car grain-length-scalers)
                                (cadr grain-length-scalers)))
         (max-grain-scaler (max (car grain-length-scalers)
                                (cadr grain-length-scalers)))
         (grain-scaler-diff (- max-grain-scaler min-grain-scaler))
         ;; the grain length at the beginning (samples).
         (grain-length-beg (floor (* *srate* (second grain-env))))
         (pos 0.0)
         (1-minus-pos 0.0)
         ;; we use pos to put the sound between two speakers and
         ;; these vars to choose which speakers these will be
         (out1-chan 0)
         (out2-chan 0)
         ;; This number gets incremented by  each time through the
         ;; run loop (hence it's a float).  Each time it gets to 1.0 or more,
         ;; we increment the input sample and reset it to 0.0.
         (counter 0.0)
         (current-input-sample 0)
         (gr-len-samples 0)  
         ;; This will hold the envelope value from 
         (deviation 0.0)
         ;; The deviation is randomised between 0.0 and  so this is
         ;; the actual value we will use.
         (random-deviation 0.0)
         ;; The start sample for the current grain.
         (start-sample 0)
         ;; How many samples are before .
         (before-current 0)
         ;; How many samples are after .
         (after-current 0)
         ;; Sample value for left channel.
         (out1 0.0)
         ;; Sample value for right channel.
         (out2 0.0)
         (ampval 0.0)
         ;; We have to window (ramp up/down) the grains.  This is the current
         ;; window sample used to scale the grain values.
         (scl 1.0)
         ;; Are we going to deviate before or after  when
         ;; we randomly choose our grain start point.
         (up? nil)
         ;; This one is just for printing seconds computed.
         (count 0)
         (gr-len-warnings 0)
         (srt-warnings 0)
         (grain-index 0)
         (actual-grain-length 0)
         (do-grain t)
         (steady-begins ramp-len)
         (sample 0.0)
         (samps-needed 0)
         (s-val 0.0)
         (srt-renv-val 0.0)
         (sexp-val 0.0)
         (gfe-val 0.0)
         (srt-offset 0.0)
         (gr-val 0.0)
         (current-srt 0.0)
         (actual-srt 0.0)
         (ramp-down-begins 0)
         (ramp-inc 1)
         ;; We do this here because we can't call make-fft-window inside run.
         (window (make-fft-window blackman2-window (* 2 ramp-len)))
         (ramp-up (make-array ramp-len :element-type 'double-float
                              :displaced-to window 
                              :displaced-index-offset 0))
         (ramp-down (make-array ramp-len :element-type 'double-float
                                :displaced-to window 
                                :displaced-index-offset ramp-len)))
    ;; The window created does not start and end with 0 so make it so.
    (setf (aref window 0) 0.0d0 ;;(make-short-float 0.0d0)
          (aref window (1- (car (array-dimensions window)))) 0.0d0
          ;;(make-short-float 0.0)
          )
    (when debug
      (format t "~&real-dur = ~f input-inc = ~f input-start-samples=~d ~
                  (~f secs) input-end-samples=~d (~f secs) ~%~%" 
              real-dur input-inc input-start-samples 
              (/ input-start-samples *srate*)
              input-end-samples (/ input-end-samples *srate*))
      (if do-src 
          (print "Using SRC")
        (print "Using INA")))
    (run
     (loop for voice below voices do
           #|
           (when do-src
             (declare (type (array sr) src-gens)
             (type (array io) files)))
             |#
           (declare (type :integer output-chans out1-chan out2-chan))
           (when (> voice 0)
             (restart-env ampf)
             (restart-env gr-env)
             (restart-env dev-env)
             (restart-env srtenv)
             (when grain-focus-env (restart-env gfe))
             (when silence-env (restart-env s-env))
             (when srt-random-env (restart-env srt-renv))
             (when srt-expt-env (restart-env sexp-env)))
           (setq current-input-sample input-start-samples
                 before-current 0
                 after-current num-input-samples
                 grain-index 0
                 gr-len-samples 
                 (if start-together 
                     0
                   (progn 
                     (setq do-grain nil)
                     (floor 
                      (if (= counter 1.0)
                   (incf current-input-sample)
                   (incf before-current)
                   (decf after-current)
                   (setq counter 0.0))
                 ;;
                 ;; START NEW GRAIN DATA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                 ;;
                 ;; Have we written out all the current grain's samples?  If so
                 ;; make a new grain:
                 (when (>= grain-index gr-len-samples)
                   ;; Silent grain?
                   (setq do-grain (if silence-env 
                                      (> (random 1.0) s-val)
                                    t)
                         ;; randomise the grain length based on the envelope
                         ;; value.  randomly choose whether to start the grain
                         ;; before or after our current input sample.
                         ;; clm's random function is not like lisp's as it 
                         ;; returns always a float, not an int, therefore this
                         ;; is a bit more complicated than a simple 
                         ;; (zerop (random 2))
                         up? (>= .5 (random 1.0))
                         ;; Get the deviation amount from our deviation
                         ;; envelope value.    
                         random-deviation (if (= deviation 0.0)
                                              0.0
                                            (random deviation))
                         pos (+ .05 (random .9))
                         1-minus-pos (- 1.0 pos)
                         out1-chan (random output-chans)
                         out2-chan (if (= out1-chan (1- output-chans))
                                       0
                                     (1+ out1-chan))
                         ;; Set the start sample based on whether we're going
                         ;; to start before or after the current sample and the
                         ;; random deviation.
                         actual-grain-length 
                         (* gr-val (+ min-grain-scaler 
                                      (random grain-scaler-diff)))
                         gr-len-samples (floor 
                                         (* *srate* actual-grain-length))
                         ;; the srt given for this voice
                         current-srt (when do-src (aref srcs voice))
                         ;; the srt we want, as given by the srt-expt-env
                         actual-srt (* (if srt-random-env
                                           (1+ (if up?
                                                   (random srt-renv-val)
                                                 (- (random srt-renv-val))))
                                         1.0)
                                       (if srt-expt-env
                                           (expt current-srt sexp-val)
                                         current-srt))
                         ;; what we'll have to add to the src gen to get our
                         ;; desired srt
                         srt-offset (if (or srt-expt-env srt-random-env)
                                           (- actual-srt current-srt)
                                         0.0)
                         samps-needed (if do-src
                                          (* gr-len-samples actual-srt)
                                        gr-len-samples)
                         start-sample 
                         (floor (if up? 
                                    (+ current-input-sample 
                                       (* after-current random-deviation))
                                  (- current-input-sample
                                     (* before-current random-deviation)))))
                   ;; (warn "~&out1-chan: ~a" out1-chan)
                   ;; (print out1-chan)
                   ;; Check to see if there's enough output samples left to 
                   ;; write our grain
                   (when (> (+ i gr-len-samples) nd)
                     ;; If there's not even enough for our min grain length
                     ;; then simply don't write the grain.
                     (if (> (+ i +SPLINTER-MIN-GRAIN-LENGTH+) nd)
                         (setq do-grain nil)
                       (setq gr-len-samples (- nd i))))
                   ;; Don't go past the end of the input samples.
                   (when (> (+ start-sample samps-needed)
                            input-end-samples) 
                     (setq start-sample (- input-end-samples samps-needed)))
                   ;; Don't try to start too soon!
                   (when (< start-sample input-start-samples)
                     (setq start-sample input-start-samples
                           gr-len-samples (floor (/ num-input-samples 
                                                    actual-srt)))
                     (when (< srt-warnings 30)
                       (clm-print "~%Not enough samps for grain len of ~fs."
                                  actual-grain-length)
                       (clm-print "~%   with an actual srt of ~f" actual-srt)
                       (clm-print "~%   num-input-samples = ~d" 
                                  num-input-samples)
                       (clm-print "~%   Grain length will be of ~d (~f secs)"
                                  gr-len-samples 
                                  (float (/ gr-len-samples *srate*)))
                       (incf srt-warnings)))
                   ;; Don't let the grain be smaller than our defined minimum. 
                   (when (< gr-len-samples +SPLINTER-MIN-GRAIN-LENGTH+)
                     (when (< gr-len-warnings 30)
                       (clm-print "~%Grain len too short. Changing ~d to ~d"
                                  gr-len-samples +SPLINTER-MIN-GRAIN-LENGTH+)
                       (incf gr-len-warnings))
                     (setq gr-len-samples +SPLINTER-MIN-GRAIN-LENGTH+))
                   ;; Get our ramp-down point.
                   (setq ramp-down-begins (- gr-len-samples ramp-len)
                         ramp-inc 1
                         current-ramp-len ramp-len
                         steady-begins current-ramp-len
                         ;; Reset the grain-index.
                         grain-index 0)
                   ;; If our grain length is too short for the ramp, adjust.
                   (when (< ramp-down-begins current-ramp-len)
                     (setq ramp-down-begins (floor gr-len-samples 2)
                           steady-begins ramp-down-begins
                           current-ramp-len ramp-down-begins
                           ;; Skip some of our ramp values so that we arrive
                           ;; at the end of the ramp in time.
                           ramp-inc (round ramp-len ramp-down-begins))
                     (when debug
                       (clm-print "~%ramp-down-begins ~d ramp-inc ~d"
                                  ramp-down-begins ramp-inc)))
                   (when do-src (setf (mus-location (aref src-gens voice)) 
                                  start-sample))
                   (when debug 
                     (clm-print 
                      "~%i = ~d (~f secs) current-input-sample = ~d (~f secs)"
                      i (/ i *srate*) 
                      current-input-sample 
                      (/ current-input-sample *srate*)))
                   (when print-grain-info
                     (clm-print "~%Grain start = ~d samps = ~f secs"
                                start-sample (/ start-sample *srate*))
                     (clm-print "~%   Grain length = ~d samps = ~f secs"
                                gr-len-samples 
                                (/ gr-len-samples *srate*))
                     (clm-print "~%   srt-offset=~f actual-srt=~f"
                                srt-offset actual-srt)
                     (clm-print "~%   ramp-inc = ~d grain index = ~d" 
                                ramp-inc grain-index)
                     (clm-print "~%   steady-begins ~d ramp-down-begins ~d"
                                steady-begins ramp-down-begins)))
                 ;;                 
                 ;; END NEW GRAIN DATA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                 ;;
                 (unless debug
                   (if do-grain
                       (setq scl (cond ((< grain-index steady-begins) 
                                        (aref ramp-up 
                                              (floor (min 1-minus-ramp-len 
                                                          (* ramp-inc 
                                                             grain-index)))))
                                       ((< grain-index ramp-down-begins) 1.0)
                                       (t (aref ramp-down 
                                                (floor 
                                                 (min 1-minus-ramp-len 
                                                      (- (* ramp-inc
                                                            grain-index)
                                                         ramp-down-begins))))))
                             gfe-val (if grain-focus-env
                                         (get-grain-amp (env gfe) voice voices
                                                        :min 
                                                        grain-focus-env-min
                                                        :expt 
                                                        grain-focus-env-expt)
                                       1.0)
                             sample (* scl gfe-val
                                       (if do-src
                                           (src (aref src-gens voice) 
                                                (+ (env srtenv) srt-offset))
                                         (in-any (+ grain-index start-sample) 
                                                 channel fil)))
                             out1 (* ampval 1-minus-pos sample)
                             out2 (* ampval pos sample))
                     (setq out1 0.0
                           out2 0.0))
                   ;; Write out the samples and reverb them.
                   (out-any i out1 out1-chan)
                   (out-any i out2 out2-chan)
                   (when *reverb* 
                     (outa i (* (if rev-env (env renv) rev-amt)
                                (/ (+ out1 out2) 2.0))
                           *reverb*)))
                 (incf grain-index))))
    (if do-src 
        (loop for v below voices do (close-input (aref files v)))
      (close-input fil))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#|

;; Example usage in clm-play

(clm-play +mini+ 1 'pno 'default-group
	      :pitch-synchronous t
	      :check-overwrite nil
	      :duration-run-over t
              :output-name-uniquifier "samp2-"
	      :snd-selector #'(lambda (sflist pitch event)
				(declare (ignore event))
				(get-nearest-by-freq
				 (frequency pitch) (data sflist)))
	      :pan-min-max '(30 60)
	      :rev-amt .05
	      :src-scaler 0.25
	      :amp-env '(0 0 5 1 70 1 100 0)
	      :output-name-uniquifier "splinter-"
	      :clm-ins #'clm::splinter	
	      :clm-ins-args '(:voices 8	
			      :ramp 5	
			      :grain-env (0 .01 50 .5 100 .01) 
			      :amp-to-voices t)

|#

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EOF splinter.lsp