from pyOpenAL import *
import sys, threading, time

#------------------------------------------------------------------------------------#

class pySoundListener:
	def __init__(self, number=30):
		'''Intialize the class by initializing the OpenAL library.'''
		self.ids = {}
		self.buffers = {}
		self.sounds = []
			
		#initialize the OpenAL library
		alutInit([])
		
		#make a bunch of reusable sources
		for i in range(number):
			e = pySound()
			self.sounds.append(e)
		
		#setup a default listener	
		alListenerfv(AL_POSITION, [0,0,0])
		alListenerfv(AL_VELOCITY, [0,0,0])
		alListenerfv(AL_ORIENTATION, [0,0,1,0,1,0])

	def ClearBuffers(self):
		'''Clear out all the loaded buffers.'''
		alDeleteBuffers(self.buffers.values())
	
	def Delete(self):
		'''Clean up any created earcons and shut down the library.'''
		for s in self.sounds:
			s.Delete()
		alDeleteBuffers(self.buffers.values())

		#shut down the library
		alutExit()
	
	def New(self, file, sid=None):
		'''Make a new earcon by reusing the ones in the pool.'''
		for s in self.sounds:
			if s.owned == False:
				#set up one of the sources
				s.buff = self.makeBuffer(file)
				s.sid = sid
				s.position = (0,0,0)
				s.velocity = (0,0,0) # added by Suzanne
				s.owned = True
				
				#store a reference to the object by id if it exists
				if id != None:
					self.ids[s.sid] = s
				
				return s
		
		return None
	
	def ReleaseSound(self, sid):
		'''Release a number of sounds.'''
		try:
			s = self.ids[sid]
			s.Reset()
			del self.ids[sid]
		except:
			pass
			
	def GetSound(self, sid):
		'''Grab a sounds from the currently playing sounds if it is available.'''
		return self.ids.get(sid)

# 	def GetSound(self, id):
# 		'''Find an existing sound by its IDs. Clean up sounds that have not been updated since the last time this sound was accessed'''
# 		try:
# 			cs = self.ids[id]
# 		except:
# 			#if this is an invalid ID, then we should just stop other sounds
# 			print 'ids before clear', self.ids
# 			for s in self.ids.values():
# 				s.Reset()
# 			self.ids = {}
# 			return None
# 			
# 		#check if this sound has been updated recently
# 		if not cs.updated:
# 			cs.updated = True
# 			return cs
# 		
# 		#if it has been updated, go through the list of sounds and remove those that haven't
# 		for s in self.ids.values():
# 			if not s.updated:
# 				del self.ids[s.id]
# 				s.Reset()
# 			elif s != cs:
# 				s.updated = False
# 				
# 		return cs
	
	def makeBuffer(self, file):
		'''Make a buffer for a sound file. Use an existing buffer if the file has already been loaded.'''
		try:
			return self.buffers[file]
		except:
			data, format, freq, loop = alutWAVFileToString(file)
			buff = alGenBuffers(1)
			alBufferData(buff[0], format, data, freq)
			self.buffers[file] = buff[0]
			return buff[0]

	def __repr__(self):
		i = 0
		for s in self.sounds:
			if s.owned:
				i += 1
		return 'There are currently ' + str(i) + ' sounds in use.'
	
#------------------------------------------------------------------------------------#
		
class pySound(object):
	def __init__(self):
		'''Create a single buffer and source.'''
		self.owned = False
		self.id = None
		self.updated = True
		self.src = alGenSources(1)[0]

	def Delete(self):
		'''Delete the allocated buffer and source.'''
		alSourceStop(self.src)
 		alSourcei(self.src, AL_BUFFER, 0)	
 		alDeleteSources([self.src])
	
	def Stop(self):
		'''Stop the sound immediately.'''
		alSourceStop(self.src)
			
	def Play(self):
		'''Play the sound.'''
		alSourcePlay(self.src)
		
	def Pause(self):
		""" Pause the sound. """
		alSourcei(self.src, AL_SOURCE_STATE, AL_PAUSED)
		
	def Reset(self):
		'''Reset the earcon to its default state.'''
		self.id = None
		self.owned = False
		
		alSourceStop(self.src)
		alSourcef(self.src, AL_GAIN, 1.0)
		alSourcei(self.src, AL_BUFFER, 0)
		
	def setBuffer(self, buff):
		'''Set the sound associated with the source.'''
		alSourcei(self.src, AL_BUFFER, buff)

	def setLooping(self, loop):
		'''Set the sound to loop or not.'''
		if loop:
			alSourcei(self.src, AL_LOOPING, AL_TRUE)
		else:
			alSourcei(self.src, AL_LOOPING, AL_FALSE)
			
	def setPosition(self, pos):
		'''Set the source position.'''
		alSource3f(self.src, AL_POSITION, pos[0], pos[1], pos[2])

	def setVelocity(self, vel): # added by Suzanne
		'''Set the source velocity.'''
		alSource3f(self.src, AL_VELOCITY, vel[0], vel[1], vel[2])
		
	def setGain(self, gain):
		'''Set the gain of the source.'''
		alSourcef(self.src, AL_GAIN, gain)
		
	position = property(fset=setPosition)
	gain = property(fset=setGain)
	looping = property(fset=setLooping)
	buff = property(fset=setBuffer)


#------------------------------------------------------------------------------------#

class pySoundPreview(threading.Thread):
	def __init__(self, sound, t=5):
		'''Initialize the thread.'''
		threading.Thread.__init__(self)
		self.sound = sound
		self.time = t
		
	def Play(self):
		self.start()

	def run(self):
		'''Iterate over a list of earcons, playing each one with a space in between.'''
		self.sound.Play()
		time.sleep(self.time)
		self.sound.Reset()
		
		#for e in self.earcons:
		#	e.Play()
		#	time.sleep(self.spacing)	
		#apply(self.clean, (self.stamp, self.owner))

#------------------------------------------------------------------------------------#

if __name__ == '__main__':
	from time import sleep
	sl = pySoundListener()
	s = sl.New('city.wav')
	s.looping = True
	pos = [-20,0,0]
	for pos[0] in range(-20,21):
		s.setPosition(pos)
		s.Play()
		sleep(0.25)
		s.Pause()
	sl.Delete()