[CM] A question regarding structures

Bill Schottstaedt bil@ccrma.Stanford.EDU
Tue, 22 Oct 2002 03:57:20 -0700


> I'm actually using objects for precisely that reason, but I'm facing a
> problem when dealing with CLM.  All of CLM's generators seem to be
> implemented as structures, rather than as objects.

In Common Lisp (mus.lisp), the generators are defined using CLOS, but
I doubt it will help in this situation; the intent (made more explicit
in Scheme/Ruby via sndlib) is that you'd use the various make
functions, and access any internal state through the "generic
functions" like mus-frequency.  One such function was mus-run
(mus-apply), but as far as I can tell, I never implemented it in the
CL run macro -- I wonder how it escaped my TODO list.  On the Scheme
side, the generator is, in a sense, a function itself that can be
applied to args: (oscil gen 0.0) is the same as (gen 0.0) if "gen" is
an oscil, and so on.  I can't remember if this works in the CL version.

>   I'm trying to work out
> an instrument that would allow me to pass as parameters a series of
> generators and the envelopes to control them, and would then chain these
> generators together into a DSP patch that could be reconfigured with each
> call to the instrument.

Something along these lines would work in the Scheme version:

(define* (chain-dsps beg dur #:rest dsps)
  ;; I assume the dsps are already made,
  ;;          the envs are present as break-point lists
  ;;          the calls are ordered out->in (or last first)
  (let ((dsp-chain (reverse (map (lambda (gen)
				   (if (list? gen)
				       (make-env gen :duration dur)
				       gen))
				 dsps)))
	(output (make-vct (inexact->exact (floor (* dur (mus-srate)))))))
    (vct-map! output (lambda ()
		       (let ((val 0.0))
			 (for-each
			  (lambda (gen)
			    (if (env? gen)
				(set! val (* (gen) val))
				(if (readin? gen)
				    (set! val (gen))
				    (set! val (gen val)))))
			  dsp-chain)
			 val)))
    (mix-vct output (inexact->exact (floor (* beg (mus-srate)))) #f #f #f)))


(chain-dsps 0 1.0 '(0 0 1 1 2 0) (make-oscil 440))
(chain-dsps 0 1.0 '(0 0 1 1 2 0) (make-one-zero .5) (make-readin "oboe.snd"))
(chain-dsps 0 1.0 '(0 0 1 1 2 0) (let ((osc1 (make-oscil 220.0))
                                       (osc2 (make-oscil 440)))
                                   (lambda (val)
                                     (+ (osc1 val) (osc2 (* 2 val))))))

This could be slightly recast (using a vector of clm-gens rather than a list)
to get it to work within Snd's run macro.  But to get it to (sort-of) work
in CL-CLM given the current condition of its run macro:

(definstrument chain-dsps (beg dur &rest dsps)
  (let* ((start (floor (* beg *srate*)))
	 (end (+ start (floor (* dur *srate*))))
	 (gens (length dsps))
	 (dsp-chain (make-array gens)))
    (loop for i from (1- gens) by -1 and gen in dsps do
      (setf (aref dsp-chain i)
	    (if (listp gen)
		(make-env gen :duration dur)
	      gen)))
    (run
     (loop for i from start below end do
       (let ((val 0.0))
	 (do ((g 0 (1+ g)))
	     ((= g gens))
	   (let ((gen (aref dsp-chain g)))
	     (cond ((env? gen) (setf val (* val (env gen))))
		   ((readin? gen) (setf val (readin gen)))
		   ((oscil? gen) (setf val (oscil gen val)))
		   ((sum-of-cosines? gen) (setf val (sum-of-cosines gen val)))
		   ((rand? gen) (setf val (rand gen val)))
		   ((rand-interp? gen) (setf val (rand-interp gen val)))
		   ((table-lookup? gen) (setf val (table-lookup gen val)))
		   ((square-wave? gen) (setf val (square-wave gen val)))
		   ((pulse-train? gen) (setf val (pulse-train gen val)))
		   ((sawtooth-wave? gen) (setf val (sawtooth-wave gen val)))
		   ((triangle-wave? gen) (setf val (triangle-wave gen val)))
		   ((asymmetric-fm? gen) (setf val (asymmetric-fm gen 1.0 val)))
		   ((wave-train? gen) (setf val (wave-train gen val)))
		   ((one-pole? gen) (setf val (one-pole gen val)))
		   ((two-pole? gen) (setf val (two-pole gen val)))
		   ((one-zero? gen) (setf val (one-zero gen val)))
		   ((two-zero? gen) (setf val (two-zero gen val)))
		   ((delay? gen) (setf val (delay gen val)))
		   ((comb? gen) (setf val (comb gen val)))
		   ((notch? gen) (setf val (notch gen val)))
		   ((all-pass? gen) (setf val (all-pass gen val)))
		   ((filter? gen) (setf val (filter gen val)))
		   ((fir-filter? gen) (setf val (fir-filter gen val)))
		   ((iir-filter? gen) (setf val (iir-filter gen val)))
		   ((src? gen) (setf val (src gen val)))
		   ((sine-summation? gen) (setf val (sine-summation gen val)))
		   ((waveshape? gen) (setf val (waveshape gen val)))
		   ((formant? gen) (setf val (formant gen val))))))
	 (outa i val))))))

;;; (with-sound () (chain-dsps 0 1.0 (make-oscil 440)))
;;; (with-sound () (chain-dsps 0 1.0 '(0 0 1 1 2 0) (make-oscil 440)))

But (gad, some days...) I didn't get far testing this because on my
home Redhat 8.0 system, sndplay (using OSS) completely hangs the
soundcard!  The exact same code works in an older Redhat, and very
similar code works in 8.0 in Snd; and an older image of sndplay (built
with 7.3 libraries, I guess) works fine.  This may be hard to debug
since I have to reboot after every failed test (even sndconfig hangs).

One quick workaround: make an executable file named sndplay, and put in it:

#!snd -b
!#
(play-and-wait (list-ref (script-args) (+ (script-arg) 1)))
(exit)