[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
*******************************