[CM] a with-sound question

cristopher pierson ewing cewing@u.washington.edu
Tue, 14 Jan 2003 11:04:06 -0800 (PST)


I have a set of objects which store potential information for calls to
clm instruments.  I have a macro which takes any of these objects and the
data held within, and writes out the call to the instrument for use within
a call to (with-sound).

When I call with-sound using the method on an object, it writes an empty
soundfile, but if i use with-sound with (eval (method object)), then it
works.  I guess this means that although my method is writing the call to
the instrument properly, it is not getting evaluated once it is written.

I have been told by a master (thanks Rick!) that using eval means that
something isn't right.  Any ideas on how this might ba made to work?  I
will include the relevant bits of code below.

Thanks,

Cris

********************************
Code:
********************************

;;;;;;;;;;;;;;;;;;;;
;;;First, a few parameters for the sampler's environment:
(defconstant +sample-root+ "SND/samples/"); here's where samples live
(defconstant +home-dir+ (namestring (user-homedir-pathname)))

;;;;;;;;;;;;;;;;;;;;
;;;classes and methods to support the use of samples

;;;;;;;;;;
;;sample-class.  Holds the information about a single sample
(defclass sample () ;holds information needed to call on a sample
          ((name :accessor sample-name :initarg :name :initform nil)
           (path :accessor sample-path :initarg :path :initform "")
           (pitch :accessor sample-pitch :initarg :pitch :initform 'x)))

;gets the path-name info from a sample object:
(defmethod get-path ((obj sample))
    (concatenate 'string +home-dir+ +sample-root+ (sample-path obj)))

; prints an object well
(defmethod print-object ((obj sample) stream)
    (format stream "<sample: ~A>" (sample-path obj)))

;;;;;;;;;;
;;Basic, changing-srate input instrument.  Srt-env could be a flat rate, a
;;   linear env, or a set of breakpoints. Allows for skipping a percent of
;;   the start of the file or of the end or of both
(definstrument src-player (filename start-time srt-env
                                    &optional
				    (amp 1.0) (width 40)
                                    (skip-percent 0) (trim-percent 0))
  (let* ((f (open-input* filename))           ;open input file
         (base-dur (sound-duration filename)) ;find old duration
         (base-frames (sound-frames filename));find num frames
         (skip-frames (if (not (= 0 skip-percent));do we skip any?
                        (percent-scaler
			 base-frames skip-percent);set skip-frames
                        0))
         (dur                                 ;figure new duration
          (dur-o-matic
           base-dur srt-env (+ skip-percent trim-percent)))
         (srt (if (listp srt-env)             ;is srt-env an env?
                0                             ;yes? set srt to zero
                srt-env))                     ;no? set srt to srt-env
         (senv (if (listp srt-env)            ;is srt-env an env?
                 (make-env :envelope srt-env
		           :duration dur);make an instance
                 (make-env :envelope '(0 0 1 0)
			   :duration dur)));make a default
         (src-gen (make-src :input f
			    :srate srt
			    :width 40)));make src instance
    (multiple-value-bind (beg end) (times->samples start-time dur)
      (setf (mus-location src-gen)            ;set start-location
            (if (< srt 0)                     ;if reverse play
              (- base-frames skip-frames)     ;end - skip
              skip-frames))                   ;start + skip
      (run
       (loop for i from beg below end do
             (outa i (* amp (src src-gen (env senv))))));output sound
      (close-input f))))                      ;close input file

;;;;;;;;;;;;;;;;;;;;
;;;Functions which are playback related.  These support
;;;   functionality in the playback instruments of the clm-sampler.

;;;;;;;;;;
;;Playback-avg takes an envelope for playback rate and determines the
;;   average playback speed for that envelope.
(defun playback-avg (env)
  (let ((ox (car env))
        (oy (cadr env)))
    (loop for x in env by #'cddr
          for y in (cdr env) by #'cddr
          sum (+ (- (* (abs x) (abs y))(* (abs ox) (abs y)))
                 (- (* (abs x) (abs oy))(* (abs ox) (abs oy))))
          into s
          do (setf ox x oy y)
          finally (return (/ s 2.0 x)))))

;;;;;;;;;;
;;Dur-o-matic takes the original duration of a soundfile, and the playback
;;   rate (or envelope of playback rates) at which the soundfile is to be
;;   played, and returns the duration of the soundfile at that rate. It
;;   figures in skiptime.
(defun dur-o-matic (base-dur pbrate skip-percent)
  (let ((dur (if (not (= 0 skip-percent));scale original dur
               (- base-dur
                  (percent-scaler base-dur skip-percent))
               base-dur))
        (rate 0.0))
  (setf rate (if (listp pbrate);if it's a list, then its an envelope.
               (playback-avg pbrate);find the average of the envelope
               pbrate))        ;or return the numeric rate
  (abs (/ dur rate))));figure newdur = olddur/rate (abs for reverse)



;;;;;;;;;;
;;percent-scaler takes n number and x percentage and returns the value
that is
;;   x percent of n
(defun percent-scaler (num perc)
  (* num (/ perc 100.0)))

;;;;;;;;;;
;;Get-item is designed to parse values in object slots.  If slot value is
;;   a number, that number is returned, if it is a list, then index(0-1) is
;;   required and the list is treated as an envelope for interpolation.  If
;;   the slot value is a pattern, then the next value in that pattern is
;;   returned.
(defun get-item (input &optional (index 0))
  (typecase input
    (number input)
    (symbol input)
    (list (interpl index input))
    (pattern (next input))
    (sample input)
    (string input)))

;;;;;;;;;;
;;;Control the src-player instrument with this object:
;;;;;;;;;;
;;ins class.  A super-class for all instruments

(defclass sampler-ins ()
          ((time :accessor ins-time :initarg :time :initform 0)))

;;;;;;;;;;
;;input class.  A super-class for all input intruments

(defclass input (sampler-ins)
          ((sample :accessor ins-sample :initarg :sample :initform nil)))

;;;;;;;;;;
;;src-player class.  holds info to create calls to the src-player
instrument

(defclass src-player-gen (input)
          ((rate :accessor ins-rate :initarg :rate :initform 1)
           (amplitude :accessor ins-amp :initarg :amp :initform 1)
           (width :accessor ins-width :initarg :width :initform 40)
           (skip :accessor ins-skip :initarg :skip :initform 0)
           (trim :accessor ins-trim :initarg :trim :initform 0)))

(defmethod write-ins ((obj src-player-gen))
    (let ((time (ins-time obj))
          (sample-path (get-path (typecase (ins-sample obj)
                                   (pattern
				     (eval (next (ins-sample obj))))
                                   (t (ins-sample obj)))))
          (rate (get-item (ins-rate obj)))
          (amplitude (get-item (ins-amp obj)))
          (width (get-item (ins-width obj)))
          (skip (get-item (ins-skip obj)))
          (trim (get-item (ins-trim obj))))
      (list 'src-player sample-path time rate amplitude width skip trim)))

(defmethod print-object ((obj src-player-gen) stream)
    (format stream "<src-player | sample: ~A | rate: ~A >"
            (sample-path (ins-sample obj)) (ins-rate obj)))

********************************
So compile and load the above code and then run the following to see what
I mean:
********************************

;first, amke a sample object:
(setf samp (make-instance 'sample :name 'some-samp
				  :path "path/to/some-sample";relative to
							     ;+sample-dir+
				  :pitch 100.0)); or whatever

;next, make an src-player-gen
(setf player (make-instance 'src-player-gen :sample samp)

;check the writing method
(write-ins player)
;should evaluate to the following:
(src-player "/homedir/sample-root/some-samp" 0 1 1 40 0 0)

;now watch what happens from within with-sound:
(with-sound (:play nil) (write-ins player))

;and if I use eval:
(with-sound (:play nil) (eval (write-ins player)))

;end code examps
********************************

Any suggestions as to why this eval is needed would be greatly
appreciated, as would any ideas of ways to get rid of it.

Thanks much,

Cris


********************************
Cris Ewing
CARTAH Assistant
University of Washington
Home Phone: (206) 365-3413
E-mail: cewing@u.washington.edu
*******************************