SP0256-AL2 speech synthesis chip
-main.bas

main.bas

Option Explicit

'--------------------------------------------------------------------------------

' Pin labels (i/o relative to BX)
' 	ALD (output) 	- Address Load, asserted low. Latches values
'				  at address pins A1..A8 into memory
'	LRQ (input) 	- Load Request. asserted low. Indicates chip 
'				  is ready to accept a new phoneme address
'	SBY (input)	- Standby. Asserted when chip has stopped
'				  talking and no new address has been 
'				  loaded
'	SBYRESET (output) - Asserted low. Resets interface and
'				  address values
'	RESET(output) - Asserted low. Resets chip
'	TEST(output)	- Not clearly documented. Grounded for 
'				  normal operation.

const ALD 		as byte = 13
const LRQ 		as byte = 14
const SBY 		as byte = 15
const SBYRESET 	as byte = 16
const RESET 		as byte = 17
const TEST 		as byte = 18
' NOTE: 			portc (pins 5-12) used as phoneme address, although only
				' the 6 lowest order bits are used 

const redButtonPin		as byte = 20
const blackButtonPin	as byte = 19

const cycleMode 	as byte = 1
const studentMode 	as byte = 2

' Vowel Arrays. Definied and initialized in Allophones module.

public ShortVowelArray(1 to ShortVowelArrayLength) 				as byte
public LongVowelArray(1 to LongVowelArrayLength) 					as byte
public ResonantArray(1 to ResonantArrayLength) 					as byte
public VoicedFricativeArray(1 to VoicedFricativeArrayLength) 		as byte
public VoicelessFricativeArray(1 to VoicelessFricativeArrayLength) 	as byte
public VoicedStopArray(1 to VoicedStopArrayLength)					as byte
public VoicelessStopArray(1 to VoicelessStopArrayLength)			as byte
public NasalArray(1 to NasalArrayLength)							as byte
public RColoredArray(1 to RColoredArrayLength)					as byte

' The AllophoneQueue is used to queue up phonemes to be
'	uttered by the Say() subroutine
const AllophoneQueueMax 					as byte = 25
dim AllophoneQueue(1 to AllophoneQueueMax) 	as byte
dim AllophoneQueueSize 					as Byte

dim mode as byte

'--------------------------------------------------------------------------------
public sub Main()

	Call LEDInit()	

	Call SetUpSP0256()
	Call InitializePhonemeArrays()

	AllophoneQueueSize = 0

	dim redButtonValue 	as byte
	dim blackButtonValue 	as byte
	dim utterDelay		as single

	' Turn green onboard LED on; red off
	putPin 25, 1
	putpin 26, 0



	' Device will behave in one of two ways:
	' If the red button is held down at start up, it goes into allophone cycling mode;
	' Otherwise, it goes into selectStudent mode.
	redButtonValue	= getPin(redButtonPin)
	if redButtonValue = 1 then 
		mode = cycleMode
	else
		mode = studentMode
	end if



	if mode = studentMode then
'***************** Student Selection Mode *****************************************************
' Student Selection Mode begins by greeting the class.
' In student selection mode, holding the red button down randomly selects a student
' from the class list, and the device says "I pick... so and so".
'
' If no random selection has been made, pressing the black button runs through the class list
' in alphabetical order.
'
' If at least one random selection has been made, pressing the black button makes the device
' say: The robot has spoken!
'**********************************************************************************************

		dim firstTime as Boolean
		firstTime = True

		SayGoodMorningClass

		do 
			blackButtonValue 	= getPin(blackButtonPin)
			redButtonValue	= getPin(redButtonPin)

			if blackButtonValue = 1 then
				if firstTime then
					SayAllStudents
				else
					sayTheRobotHasSpoken
				end if
			end if

			if redButtonValue = 1 then
				firstTime = False

				' Randomly select a student. We'll do this by timing 
				' how long the button is held down with a very fast timer.
				' By including timer1.bas (which comes with the bx) we get
				' access to special precision timer functions:
				dim timerCount as long
				dim studentNumber as byte

				InitializeTimer(3)
				StartTimer				

				do while getPin(redButtonPin) = 1 'while the red button is held down...
					'...make a funny sound: 
					Call Cycle(ShortVowelArray, ShortVowelArrayLength, 3, 0.01)
				loop
				call GetTimerCount(timerCount)

				' timer is so fast its result is essentially random;
				' when the value is modded by the class size we get a random number
				' between 1 and the class size
				studentNumber = cByte((timerCount mod 21) + 1)

				' Clear the Allophone Queue and stop the speech:
				AllophoneQueueSize = 0
				add(p10ms)
				say
				delay 1.0

				' Tell the class which student has been selected:
				sayIPick
				delay 1.0
				call pickStudent(studentNumber)

			end if
		loop

	else 

		do 'allophone looping

			utterDelay = 0.35

			debug.print "Long Vowels"
			Call Cycle(LongVowelArray, LongVowelArrayLength, 3, utterDelay)

			debug.print "Short Vowels"
			Call Cycle(ShortVowelArray, ShortVowelArrayLength, 3, utterDelay)

			debug.print "Resonants"
			Call Cycle(ResonantArray, ResonantArrayLength, 3, utterDelay)

			debug.print "Nasals"
			Call Cycle(NasalArray, NasalArrayLength, 3, utterDelay)

			debug.print "Voiced Stops"
			Call Cycle(VoicedStopArray, VoicedStopArrayLength, 3, utterDelay)

			debug.print "Voiced Fricatives"
			Call Cycle(VoicedFricativeArray, VoicedFricativeArrayLength, 3, utterDelay)

			debug.print "RColored Vowels"
			Call Cycle(RColoredArray, RColoredArrayLength, 3, utterDelay)

			debug.print "Voiceless Fricatives"
			Call Cycle(VoicelessFricativeArray, VoicelessFricativeArrayLength, 3, utterDelay)

			debug.print "Voiceless Stops"
			Call Cycle(VoicelessStopArray, VoicelessStopArrayLength, 3, utterDelay)

			delay 1.0
		loop	
	end if

end sub 'Main

' Iniialize the SP0256
private sub SetupSP0256()
	' Assert SBYRESET and RESET:
	PutPin SBYRESET, 0
	PutPin RESET, 0

	' Ground TEST for normal operation:
	PutPin TEST, 0

	' Disable Address Loading (which is asserted low)
	PutPin ALD, 1

	' Set lower 6 bits of port C (pins 7-12) as output
	Register.DDRC = 255

	' Wait and let the chip chill
	Delay 1.5

	' Disable RESET and SBYRESET	
	PutPin SBYRESET, 1
	PutPin RESET, 1

end sub

' Add(phoneme) : Add  a phoneme to the allophone queue
sub Add(ByVal X as Byte)
	' If the allophone queue is full, exit
	if AllophoneQueueSize >= AllophoneQueueMax then
		exit sub
	end if

	' Otherwise add the allophone to the end of the queue
	AllophoneQueueSize = AllophoneQueueSize + 1
	AllophoneQueue(AllophoneQueueSize) = x

end sub

' Say() : Pronounce all the phonemes in the queue sequentially
sub Say()
	dim i as byte
	for i = 1 to AllophoneQueueSize
		' Put the address value on PortC
		Register.PortC = AllophoneQueue(i)
		' Strobe Address Load low to latch the address
		PutPin ALD, 0
		PutPin ALD, 1
		' wait while until another Load Request
		do while (GetPin(LRQ) = 1)
			'nothing
		loop
	next
	AllophoneQueueSize = 0
end sub

' Cycle(whichArray, arraySize, numberOfTimes, delayTime) : Cycle through all the sounds in a phoneme group
sub Cycle(ByRef phonemeArray() as byte, ByVal arraySize as byte, ByVal numberOfTimes as byte, ByVal delayTime as single)
	dim i as byte, j as byte
	for i = 1 to numberOfTimes
		for j = 1 to arraySize
			' put phoneme value on address pins
			Register.PortC = phonemeArray(j)
			' Strobe Address Load low to latch the address
			PutPin ALD, 0
			PutPin ALD, 1

			' A delay of 0.0 will result in phonemes uttered as fast as the chip requests them via LRQ.
			' Otherwise, a fixed delay time will be used, and LRQ is ignored.
			if delayTime <> 0.0 then
				delay delayTime
			else
				do while(GetPin(LRQ) = 1)
					'nothing
				loop
			end if
		next
	next
end sub

' pickStudent : take a student number and say the corresponding name
sub pickStudent(ByVal whichStudent as byte)
	dim count as byte
	count = 0
	do while count < 4
		select case whichStudent
			case 1
				SayDesireena(0.5)
			case 2
				SayKaren(0.25)	
			case 3
				SayChristine(0.125)
			case 4
				SayLia(0.5)
			case 5
				SayCarrie(0.25)
			case 6
				SayFaYi(0.5)
			case 7
				SayKatherine(0.25)
			case 8
				SayRachel(0.125)
			case 9
				SayChieh(0.5)	
			case 10
				SayJessica(1.0)
			case 11
				SayMiro(0.25)
			case 12
				SayAi(0.75)
			case 13
				SayHyunJean(0.25)
			case 14
				SayJon(0.5)
			case 15
				SayMac(0.125)
			case 16
				SayYoonhee(0.5)
			case 17
				SayPatrick(0.5)
			case 18
				SayGabriela(0.5)
			case 19
				SayJose(0.5)
			case 20
				SayDiana(0.25)
			case 21
				SayJungEun(0.25)
		end select

		count = count + 1
		delay 0.25
	loop
end sub


' Little init sequence for LEDs to indicate program start
public sub LEDInit()
	dim i as integer
	' Turn on board LEDs (pin 25, 26) off
	putPin 25, 1
	putPin 26, 1
	' blink red:
	for i = 1 to 10
		delay 0.05
		putPin 25, 1
		delay 0.05
		putpin 25, 0
	next
end sub