;Written by Iain McCurdy, 2006

<CsoundSynthesizer>

<CsOptions>
-idevaudio -odevaudio -m0 -d -M0 -+rtmidi=virtual -b400
;MODIFY MIDI SETTINGS ACCORDINGLY
</CsOptions>

<CsInstruments>

sr 	= 	44100	;SAMPLE RATE
ksmps 	= 	10	;NUMBER OF AUDIO SAMPLES IN EACH CONTROL CYCLE (MAY NEED TO BE LOW WHEN WORKING WITH SHORT DELAY TIMES DEFINED INITIALLY AT KRATE)
nchnls 	= 	2	;NUMBER OF CHANNELS (2=STEREO)
0dbfs	=	1	;MAXIMUM AMPLITUDE REGARDLESS OF BIT DEPTH

;FLTK INTERFACE CODE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FLcolor	255, 255, 255, 0, 0, 0	;SET INTERFACE COLOURS
;			LABEL                       | WIDTH | HEIGHT | X | Y
	FLpanel	"Keyboard Controlled Pitch Shifter",   500,    350,    0,  0

;VALUE_DISPLAY_BOXES		WIDTH | HEIGHT | X | Y
iddlt		FLvalue   " ",	80,       20,    5,  25
idfeedback	FLvalue   " ",	80,       20,    5,  75
iddry		FLvalue   " ",	80,       20,    5, 125
idwet		FLvalue   " ",	80,       20,    5, 175
idatt		FLvalue   " ",	80,       20,    5, 225
idrel		FLvalue   " ",	80,       20,    5, 275
idamp		FLvalue   " ",	80,       20,    5, 325

;SLIDERS					            					MIN | MAX | EXP | TYPE |    DISP     | WIDTH | HEIGHT | X | Y
gkdlt,gihdlt		FLslider	"Grain Size in Seconds (Used by Pitch Shifter)",  	0.001, 1,   0,    23,        iddlt,    490,     26,    5,   0 
gkfeedback,gihfeedback	FLslider	"Feedback",  						0,     1,    0,    23,   idfeedback,    490,     26,    5,  50
gkdry,ihdry		FLslider	"Dry Signal Level",					0,     1,    0,    23,        iddry,    490,     26,    5, 100
gkwet,ihwet		FLslider	"Wet Signal Level",					0,     1,    0,    23,        idwet,    490,     26,    5, 150
gkatt,ihatt		FLslider	"Attack Time",						.001, 10,   -1,    23,        idatt,    490,     26,    5, 200
gkrel,ihrel		FLslider	"Release Time",						.001, 10,   -1,    23,        idrel,    490,     26,    5, 250
gkamp,ihamp		FLslider	"Output Amplitude",					0,     2,    0,    23,        idamp,    490,     26,    5, 300

;SET_INITIAL_VALUES		VALUE | HANDLE
		FLsetVal_i   	.05, 	gihdlt
		FLsetVal_i   	0, 	gihfeedback
		FLsetVal_i   	0.1, 	ihdry
		FLsetVal_i   	1, 	ihwet
		FLsetVal_i   	.1, 	ihatt
		FLsetVal_i   	.1, 	ihrel
		FLsetVal_i   	1, 	ihamp

		FLpanel_end	;END OF PANEL CONTENTS

;INSTRUCTIONS AND INFO PANEL
				FLpanel	" ", 500, 360, 512, 0
;TEXT BOXES												TYPE | FONT | SIZE | WIDTH | HEIGHT | X | Y
ih		 	FLbox  	"          MIDI Keyboard Controlled Pitch Shifter             ", 	1,      5,     14,    490,     20,    5,   0
ih		 	FLbox  	"-------------------------------------------------------------", 	1,      5,     14,    490,     20,    5,  20
ih		 	FLbox  	"This example implements a pitch shifter effect based on the  ", 	1,      5,     14,    490,     20,    5,  40
ih		 	FLbox  	"algorithm introduced previously under 'Delay Effects'.       ", 	1,      5,     14,    490,     20,    5,  60
ih		 	FLbox  	"The innovation in this example is that the pitch shifter     ", 	1,      5,     14,    490,     20,    5,  80
ih		 	FLbox  	"effect is applied to a live input signal and that the        ", 	1,      5,     14,    490,     20,    5, 100
ih		 	FLbox  	"interval of pitch shift is determined by notes played on a   ", 	1,      5,     14,    490,     20,    5, 120
ih		 	FLbox  	"MIDI keyboard (middle C represents no pitch shift).          ", 	1,      5,     14,    490,     20,    5, 140
ih		 	FLbox  	"The number of pitch shifter voices is dynamic therefore      ", 	1,      5,     14,    490,     20,    5, 160
ih		 	FLbox  	"harmony chords are possible. The number of voices possible is", 	1,      5,     14,    490,     20,    5, 180
ih		 	FLbox  	"restricted only by available CPU.                            ", 	1,      5,     14,    490,     20,    5, 200
ih		 	FLbox  	"Attack and release controls are included to allow voices to  ", 	1,      5,     14,    490,     20,    5, 220
ih		 	FLbox  	"enter and leave according to a user defined amplitude        ", 	1,      5,     14,    490,     20,    5, 240
ih		 	FLbox  	"envelope.                                                    ", 	1,      5,     14,    490,     20,    5, 260
ih		 	FLbox  	"Stereo input and output.                                     ", 	1,      5,     14,    490,     20,    5, 280
ih		 	FLbox  	"The pitch bend wheel can be used to bend the pitch up or down", 	1,      5,     14,    490,     20,    5, 300
ih		 	FLbox  	"by 2 semitones.                                              ", 	1,      5,     14,    490,     20,    5, 320
ih		 	FLbox  	"The Modulation wheel can be used to modulate 'Grain Size'.    ", 	1,      5,     14,    490,     20,    5, 340

				FLpanel_end

				FLrun	;RUN THE FLTK WIDGET THREAD
;END OF FLTK INTERFACE CODE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

instr	2	;AUDIO INPUT
	gasigL, gasigR	ins
	out	gasigL * gkdry, gasigR * gkdry
endin

instr	1				; PITCH SHIFTER
	ioct		octmidi

	;PITCH BEND===========================================================================================================================================================
	iSemitoneBendRange = 2		;PITCH BEND RANGE IN SEMITONES (WILL BE DEFINED FURTHER LATER) - SUGGESTION - THIS COULD BE CONTROLLED BY AN FLTK COUNTER
	imin = 0			;EQUILIBRIUM POSITION
	imax = iSemitoneBendRange * .0833333	;MAX PITCH DISPLACEMENT (IN oct FORMAT)
	kbend	pchbend	imin, imax	;PITCH BEND VARIABLE (IN oct FORMAT)
	koct		=	ioct + kbend
	;=====================================================================================================================================================================
	
	;MIDI INPUT============================================================================================================================================================
        ;OUTPUT                 OPCODE          CHANNEL | CC.NUMBER | MIN | MAX
        kdlt               	ctrl7           1,            1,      0,    1	;READ IN MIDI CONTROLLER
        ktrig                   changed         kdlt    ;IF THE VARIABLE 'kptr' CHANGES FROM ITS PREVIOUS VALUE,
                                                        ;I.E. IF THE MIDI SLIDER IS MOVED THEN THE VARIABLE ktrig WILL ASSUME THE VALUE '1', OTHERWISE IT WILL BE ZERO.
	kdlt			scale		kdlt,1, 0.001	;RESCALE VARIABLE
        ;                       OPCODE      |   TRIGGER |  VALUE   |  HANDLE
                                FLsetVal        ktrig,     kdlt,      gihdlt	;UPDATE WIDGET WHEN A TRIGGER IS RECEIVED
        ;======================================================================================================================================================================

	;SET UP FUNCTION TO MODULATE DELAY TIME VARIABLE AND ENVELOPING FUNCTION=============================================================================================== 
	iporttime	=	0.1				;PORTAMENTO TIME
	kporttime	linseg	0,0.001,iporttime,1,iporttime	;CREATE A RAMPING UP FUNCTION THAT WILL BE USED FOR PORTAMENTO TIME
	kdlt		portk	gkdlt, kporttime	;APPLY PORTAMENTO
	adlt		interp	kdlt			;CREATE AN INTEROLATED A-RATE VERSION OF K-RATE VARIABLE
	kratio		=	cpsoct(koct)/cpsoct(8)	;RATIO OF NEW FREQ TO A DECLARED BASE FREQUENCY (MIDDLE C)
	krate		=	(kratio-1)/kdlt		;SUBTRACT 1/1 SPEED	
	aphase1		phasor	-krate					;MOVING PHASE 1-0
	aphase2		phasor	-krate, 0.5				;MOVING PHASE 1-0 - PHASE OFFSET BY 180 DEGREES (.5 RADIANS)	
	agate1		tablei	aphase1, 1, 1, 0, 1			;WINDOW FUNC =HALF SINE
	agate2		tablei	aphase2, 1, 1, 0, 1			;WINDOW FUNC =HALF SINE
        ;======================================================================================================================================================================
	
	;LEFT CHANNEL===========================================================================================================================================================
	aignore		delayr	1					;ALLOC DELAY LINE
	adelsig1	deltap3	aphase1 * adlt				;VARIABLE TAP
	aGatedSig1	=	adelsig1 * agate1			;GATE 1ST SIGNAL
			delayw	gasigL + (aGatedSig1 * gkfeedback)			; WRITE AUDIO TO THE BEGINNING OF THE DELAY BUFFER
	
	aignore		delayr	1					;ALLOC DELAY LINE
	adelsig2	deltap3	aphase2 * adlt				;VARIABLE TAP
	aGatedSig2	=	adelsig2 * agate2			;GATE 2ND SIGNAL
			delayw	gasigL + (aGatedSig2 * gkfeedback)	;WRITE AUDIO TO THE BEGINNING OF THE DELAY BUFFER
	
	aGatedMixL	=	(aGatedSig1 + aGatedSig2) * .5	;MIX AND ATTENUATE
	;=======================================================================================================================================================================

	;RIGHT CHANNEL==========================================================================================================================================================
	aignore		delayr	1					;ALLOC DELAY LINE
	adelsig3	deltap3	aphase1 * adlt				;VARIABLE TAP
	aGatedSig3	=	adelsig3 * agate1			;GATE 1ST SIGNAL
			delayw	gasigR + (aGatedSig3 * gkfeedback)			; WRITE AUDIO TO THE BEGINNING OF THE DELAY BUFFER
	
	aignore		delayr	1					;ALLOC DELAY LINE
	adelsig4	deltap3	aphase2 * adlt				;VARIABLE TAP
	aGatedSig4	=	adelsig4 * agate2			;GATE 2ND SIGNAL
			delayw	gasigR + (aGatedSig4 * gkfeedback)	;WRITE AUDIO TO THE BEGINNING OF THE DELAY BUFFER
	
	aGatedMixR	=	(aGatedSig3 + aGatedSig4) * .5	;MIX AND ATTENUATE
	;=======================================================================================================================================================================

	iatt		init	i(gkatt)	;CONVERT ATTACK AND RELEASE CONTROLS TO I-RATE AND MAKE AVAILABLE AT I-TIME BY USING INIT
	irel		init	i(gkrel)        ;CONVERT ATTACK AND RELEASE CONTROLS TO I-RATE AND MAKE AVAILABLE AT I-TIME BY USING INIT
	
	kenv		expsegr	0.001, iatt, 1, irel, 0.001	;AMPLITUDE ENVELOPE IS CREATED
			outs	aGatedMixL * kenv * gkwet * gkamp, aGatedMixR * kenv *gkwet * gkamp
endin

</CsInstruments>

<CsScore>
f 1 0 1025 9 0.5 1 0	;HALF SINE  WINDOW FUNCTION USED FOR AMPLITUDE ENVELOPING

;INSTR | START | DURATION
i  2       0       -1	;INSTRUMENT 2 PLAYS A HELD NOTE

f 0 3600	;'DUMMY' SCORE EVENT KEEPS REALTIME PERFORMANCE GOING FOR 1 HOUR
</CsScore>

</CsoundSynthesizer>