[CM] yet another pitch changer...

Bill Schottstaedt bil@ccrma.Stanford.EDU
Wed, 09 Jun 2004 09:02:10 -0700


I found a new way (at least to me) to change the pitch of
a sound without affecting the duration, and it seems to be
surprisingly robust.  It's based on a bank of single
sideband amplitude modulation generators being fed
by bandpass filters.  The ssb-am gens do the pitch
shift, and the filters pick out successive harmonics,
so each harmonic gets shifted individually (and thus
the new spectrum can be harmonic after the shift):

(define* (ssb-bank old-freq new-freq pairs #:optional (order 40) (bw 50.0))
   (let* ((ssbs (make-vector pairs))
	 (bands (make-vector pairs))
	 (factor (/ (- new-freq old-freq) old-freq))
	 (mx (maxamp)))
     (do ((i 1 (1+ i)))
	((> i pairs))
       (let* ((aff (* i old-freq))
	     (bwf (* bw (+ 1.0 (/ i (* 2 pairs))))))
	(vector-set! ssbs (1- i) (make-ssb-am (* i factor old-freq)))
	(vector-set! bands (1- i) (make-bandpass (hz->2pi (- aff bwf)) (hz->2pi (+ aff bwf)) order))))
     (as-one-edit
      (lambda ()
        (let ((nmx 0.0))
	 (map-channel
	  (lambda (y)
	    (let ((sum 0.0))
	      (do ((i 0 (1+ i)))
		  ((= i pairs))
		(set! sum (+ sum (ssb-am (vector-ref ssbs i) (bandpass (vector-ref bands i) y)))))
	      (set! nmx (max nmx (abs sum)))
	      sum)))
	 (scale-by (/ mx nmx)))))))

For an oboe at 557 Hz, good values are:

(ssb-bank 557 new-freq 6 40 50)

For a person talking at ca. 150 Hz:

(ssb-bank 150 300 30 100 30)
(ssb-bank 150 100 40 100 20)

The ssb-am and bandpass generators are defined in dsp.scm.
hz->2pi is (define (hz->2pi freq) (/ (* 2 pi freq) 22050.0)).

To get a duration change without pitch change, use this
followed by src:

(define (stretch-oboe factor)
   (ssb-bank 557 (* factor 557) 7 40 40)
   (src-sound (/ 1.0 factor)))

These results were much better than I anticipated --
I've been chortling ever since.