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