Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!ucbvax!anise.acc.com!ivucsb!todd
From: todd@ivucsb.sba.ca.us (Todd Day)
Newsgroups: comp.dsp
Subject: My pitch shifter for 56000
Message-ID: <1989Sep25.074206.972@ivucsb.sba.ca.us>
Date: 25 Sep 89 07:42:06 GMT
Reply-To: todd@ivucsb.sba.ca.us (Todd Day)
Organization: Disillusioned Graduate Hackers, Santa Barbara, CA
Lines: 219

Here is that crappy pitch shifter I was talking about.  It's
simply a circular buffer that has another pointer walking
through it at a different rate.

This was not written by me originally, but I converted it
for use in a stereo sampling system.

Explanation at end.

;**********************************************************
;
; Frequency Scaler
; This program implements a frequency scaler.  It will attempt
; to take the input and provide and output which has its
; frequency scaled by the factor alpha.  It does this by
; reading the input in sections and outputting the samples
; at a rate of alpha times the input rate.  If alpha is < 1,
; the output frequencies are slowed, and some of the signal
; is lost.  If alpha > 1, the output frequencies are sped up,
; and some of the signal is repeated.
;
; Daniel Howell
; 3 Jun 1988
; ECE 148
;
; Modified to run in stereo by Todd Day
;
;**********************************************************
	page	79,66,1,1

	include	'defs.inc'
	include	'ioequ.inc'
	include	'intequ.inc'

buffer	equ	$0000			; beginning of buffer
L	equ	2048			; buffer length (multiple of 2!)
savea0	equ	buffer+L		; place to save a register
savea1	equ	savea0+1
savea2	equ	savea1+1
pointer	equ	savea2+1		; pointer to buffer

; ****************************************
; SSI receive data vector
; do long interrupt (save stack and ccr)
	org	p:i_ssird
	jsr	dofreq

; ****************************************
; begin program
;
	org	p:pgmram

; setup SSI and others
	include	'setup.asm'

	move	#buffer,r6		; r6 points to input of buffer
	move	#L-1,m6			; mod L addressing

	move	#>1.0/2048,x0		; alpha (scaling factor)

	move	#buffer,r4		; r4 points to output of buffer
	move	#L-1,m4			; mod L addressing
	clr  	a	#0,n4		; initialize pointer
	move	a,l:pointer

	move	#L-1,y0			; mask for mod L update of pointer

; start interrupts
	movep	#$b200,x:m_crb
	andi	#$00,mr

; ****************************************
; monitor the keyboard for changes in alpha
; uses b
	include	'monitor.asm'

; compare key hit to 'i', get incr, get ready for 'd'
	move	#'i',x1
	move	#>(1.0/16)/2048,y1
	cmp	x1,b		#'d',x1
	jne	d

; increment alpha by 1/16
	move	x0,b
	add	y1,b
	move	b,x0

; print b in decimal
pdec	do	#12,pdec1	;get ones place
	asl	b
pdec1	jsr	pnum		;print number in b2 as ASCII
	move	#'.',a		;print decimal place
	jsr	pwait
	tfr	b,a		;get tenths place
	do	#4,pdec2	; * 5
	add	a,b
pdec2	asl	b		; * 2
	jsr	pnum
	tfr	b,a		;get hundredths place
	do	#4,pdec3	; * 5
	add	a,b
pdec3	asl	b		; * 2
	jsr	pnum
	tfr	b,a		;get thousandths place
	do	#4,pdec4	; * 5
	add	a,b
pdec4	asl	b		; * 2
	jsr	pnum
crlf	move	#13,a		;CR
	jsr	pwait
	move	#10,a		;LF
	jsr	pwait
	jmp	getch

; print number in b2 to serial port as ASCII
pnum	move	b2,a
	move	#>'0',x1
	add	x1,a	#0,b2
	do	#16,pwait
	lsl	a
pwait	jclr	#m_tdre,x:m_ssr,pwait
	movep	a,x:m_srxh
	rts

; compare key hit to 'd'
d	cmp	x1,b
	jne	crlf

; decrement alpha by 1/16
	move	x0,b
	sub	y1,b
	move	b,x0
	jmp	pdec

; ****************************************
; do frequency shifting

; which channel input?
; when SCO is low, we get left channel data
dofreq	jclr	#m_if0,x:m_sr,left

; ****************************************
; right channel
;
right	move	a2,x:savea2		; save a register
	move	a1,x:savea1
	move	a0,x:savea0

	movep	x:m_rx,a		; get an input sample
	move		a,y:(r6)+	; store input sample (update pointer)

	move		y:(r4+n4),a	; get output sample
	movep	a,x:m_tx		; output a sample
;
; pointer update - get ready for left channel (it is first in data stream)
;
	move	l:pointer,a		; grab pointer
	add	x0,a			; add alpha to pointer
	rep	#12			; shift so a1 contains integer part
	asr	a
	and	y0,a			; modulo L
	move	a1,n4			; update pointer
	rep	#12			; shift back
	asl	a
	move	a,l:pointer		; save pointer
	nop

resta	move	x:savea2,a2		; restore a register
	move	x:savea1,a1
	move	x:savea0,a0
	rti

; ****************************************
; left channel
;
left	move	a2,x:savea2		; save a register
	move	a1,x:savea1
	move	a0,x:savea0

	movep	x:m_rx,a		; get an input sample
	move	a,x:(r6)		; store input sample

	move	x:(r4+n4),a		; get output sample
	movep	a,x:m_tx		; output a sample

	jmp	resta			; restore a register


	end	pgmram

You do not need the include files... they are for setting up MY
particular system (you can get them off my archive server, though;
send the following lines to dsp@ivucsb.sba.ca.us

send README
send HOWTO

to get them).

You can get rid of a large part of the code in the center.  I am
simply monitoring the serial port for keys hit so I can change
the pitch shifting on the fly.  It also prints out over the serial
port the alpha value currently being used.

Note that the code for the left channel does not do the update.
You can nuke it if you want to do mono.

The alpha value determines the frequency multiplication.  If you
set it to 1.25, you increase the pitch by 25%, and if you set it
to 0.75, you decrease the pitch by 25%.

Note: this method has serious clicking problems, especially at low
values of alpha.  You can make it better by changing the buffer
length.

-- 

Todd Day  |  todd@ivucsb.sba.ca.us  |  ivucsb!todd@anise.acc.com
"It takes a smart man to know when he's stupid." - Barney Rubble