Home | History | Annotate | Download | only in JetCreator
      1 
      2 from __future__ import with_statement
      3 
      4 import threading
      5 import logging
      6 import time
      7 from ctypes import *
      8 from JetUtils import OsWindows
      9 
     10 
     11 # stream state 

     12 EAS_STATE_READY = 0
     13 EAS_STATE_PLAY = 1
     14 EAS_STATE_STOPPING = 2
     15 EAS_STATE_PAUSING = 3
     16 EAS_STATE_STOPPED = 4
     17 EAS_STATE_PAUSED = 5
     18 EAS_STATE_OPEN = 6
     19 EAS_STATE_ERROR = 7
     20 EAS_STATE_EMPTY = 8
     21 
     22 # EAS error codes

     23 EAS_SUCCESS	= 0
     24 EAS_FAILURE = -1
     25 EAS_ERROR_INVALID_MODULE = -2
     26 EAS_ERROR_MALLOC_FAILED = -3
     27 EAS_ERROR_FILE_POS = -4
     28 EAS_ERROR_INVALID_FILE_MODE = -5
     29 EAS_ERROR_FILE_SEEK = -6
     30 EAS_ERROR_FILE_LENGTH = -7
     31 EAS_ERROR_NOT_IMPLEMENTED = -8
     32 EAS_ERROR_CLOSE_FAILED = -9
     33 EAS_ERROR_FILE_OPEN_FAILED = -10
     34 EAS_ERROR_INVALID_HANDLE = -11
     35 EAS_ERROR_NO_MIX_BUFFER = -12
     36 EAS_ERROR_PARAMETER_RANGE = -13
     37 EAS_ERROR_MAX_FILES_OPEN = -14
     38 EAS_ERROR_UNRECOGNIZED_FORMAT = -15
     39 EAS_BUFFER_SIZE_MISMATCH = -16
     40 EAS_ERROR_FILE_FORMAT = -17
     41 EAS_ERROR_SMF_NOT_INITIALIZED = -18
     42 EAS_ERROR_LOCATE_BEYOND_END = -19
     43 EAS_ERROR_INVALID_PCM_TYPE = -20
     44 EAS_ERROR_MAX_PCM_STREAMS = -21
     45 EAS_ERROR_NO_VOICE_ALLOCATED = -22
     46 EAS_ERROR_INVALID_CHANNEL = -23
     47 EAS_ERROR_ALREADY_STOPPED = -24
     48 EAS_ERROR_FILE_READ_FAILED = -25
     49 EAS_ERROR_HANDLE_INTEGRITY = -26
     50 EAS_ERROR_MAX_STREAMS_OPEN = -27
     51 EAS_ERROR_INVALID_PARAMETER = -28
     52 EAS_ERROR_FEATURE_NOT_AVAILABLE = -29
     53 EAS_ERROR_SOUND_LIBRARY = -30
     54 EAS_ERROR_NOT_VALID_IN_THIS_STATE = -31
     55 EAS_ERROR_NO_VIRTUAL_SYNTHESIZER = -32
     56 EAS_ERROR_FILE_ALREADY_OPEN = -33
     57 EAS_ERROR_FILE_ALREADY_CLOSED = -34
     58 EAS_ERROR_INCOMPATIBLE_VERSION = -35
     59 EAS_ERROR_QUEUE_IS_FULL = -36
     60 EAS_ERROR_QUEUE_IS_EMPTY = -37
     61 EAS_ERROR_FEATURE_ALREADY_ACTIVE = -38
     62 
     63 # special result codes

     64 EAS_EOF = 3
     65 EAS_STREAM_BUFFERING = 4
     66 
     67 # buffer full error returned from Render

     68 EAS_BUFFER_FULL = 5
     69 
     70 # file types

     71 file_types = (
     72 	'Unknown',
     73 	'SMF Type 0 (.mid)',
     74 	'SMF Type 1 (.mid)',
     75 	'SMAF - Unknown type (.mmf)',
     76 	'SMAF MA-2 (.mmf)',
     77 	'SMAF MA-3 (.mmf)',
     78 	'SMAF MA-5 (.mmf)',
     79 	'CMX/QualComm  (.pmd)',
     80 	'MFi (NTT/DoCoMo i-mode)',
     81 	'OTA/Nokia (.ott)',
     82 	'iMelody (.imy)',
     83 	'RTX/RTTTL (.rtx)',
     84 	'XMF Type 0 (.xmf)',
     85 	'XMF Type 1 (.xmf)',
     86 	'WAVE/PCM (.wav)',
     87 	'WAVE/IMA-ADPCM (.wav)',
     88 	'MMAPI Tone Control (.js)'
     89 )
     90 
     91 stream_states = (
     92 	'Ready',
     93 	'Play',
     94 	'Stopping',
     95 	'Stopped',
     96 	'Pausing',
     97 	'Paused',
     98 	'Open',
     99 	'Error',
    100 	'Empty'
    101 )
    102 
    103 # iMode play modes

    104 IMODE_PLAY_ALL = 0
    105 IMODE_PLAY_PARTIAL = 1
    106 
    107 # callback type for metadata

    108 EAS_METADATA_CBFUNC = CFUNCTYPE(c_int, c_int, c_char_p, c_ulong)
    109 
    110 # callbacks for external audio

    111 EAS_EXT_PRG_CHG_FUNC = CFUNCTYPE(c_int, c_void_p, c_void_p)
    112 EAS_EXT_EVENT_FUNC = CFUNCTYPE(c_int, c_void_p, c_void_p)
    113 
    114 # callback for aux mixer decoder

    115 EAS_DECODER_FUNC = CFUNCTYPE(c_void_p, c_void_p, c_int, c_int)
    116 
    117 # DLL path

    118 if OsWindows():
    119 	EAS_DLL_PATH = "EASDLL.dll"
    120 else:
    121 	EAS_DLL_PATH = "libEASLIb.dylib"
    122 
    123 eas_dll = None
    124 
    125 # logger

    126 eas_logger = None
    127 
    128 #---------------------------------------------------------------

    129 # InitEASModule

    130 #---------------------------------------------------------------

    131 def InitEASModule (dll_path=None):
    132 	global eas_dll
    133 	global eas_logger
    134 
    135 	
    136 	# initialize logger

    137 	if eas_logger is None:
    138 		eas_logger = logging.getLogger('EAS')
    139 
    140 	# initialize path to DLL

    141 	if dll_path is None:
    142 		dll_path=EAS_DLL_PATH
    143 
    144 	# intialize DLL

    145 	if eas_dll is None:
    146 		eas_dll = cdll.LoadLibrary(dll_path)
    147 
    148 #---------------------------------------------------------------

    149 # S_JET_CONFIG

    150 #---------------------------------------------------------------

    151 class S_JET_CONFIG (Structure):
    152 	_fields_ = [('appLowNote', c_ubyte)]
    153 
    154 #---------------------------------------------------------------

    155 # S_EXT_AUDIO_PRG_CHG 

    156 #---------------------------------------------------------------

    157 class S_EXT_AUDIO_PRG_CHG (Structure):
    158 	_fields_ = [('bank', c_ushort),
    159 				('program', c_ubyte),
    160 				('channel', c_ubyte)]
    161 
    162 #---------------------------------------------------------------

    163 # S_EXT_AUDIO_EVENT 

    164 #---------------------------------------------------------------

    165 class S_EXT_AUDIO_EVENT (Structure):
    166 	_fields_ = [('channel', c_ubyte),
    167 				('note', c_ubyte),
    168 				('velocity', c_ubyte),
    169 				('noteOn', c_ubyte)]
    170 
    171 #---------------------------------------------------------------

    172 # S_MIDI_CONTROLLERS 

    173 #---------------------------------------------------------------

    174 class S_MIDI_CONTROLLERS (Structure):
    175 	_fields_ = [('modWheel', c_ubyte),
    176 				('volume', c_ubyte),
    177 				('pan', c_ubyte),
    178 				('expression', c_ubyte),
    179 				('channelPressure', c_ubyte)]
    180 
    181 #---------------------------------------------------------------

    182 # WAVEFORMAT 

    183 #---------------------------------------------------------------

    184 class WAVEFORMAT (Structure):
    185 	_fields_ = [('wFormatTag', c_ushort),
    186 				('nChannels', c_ushort),
    187 				('nSamplesPerSec', c_ulong),
    188 				('nAvgBytesPerSec', c_ulong),
    189 				('nBlockAlign', c_ushort),
    190 				('wBitsPerSample', c_ushort)]
    191 
    192 #---------------------------------------------------------------

    193 # EAS_Exception 

    194 #---------------------------------------------------------------

    195 class EAS_Exception (Exception):
    196 	def __init__ (self, result_code, msg, function=None):
    197 		self.msg = msg
    198 		self.result_code = result_code
    199 		self.function = function
    200 	def __str__ (self):
    201 		return self.msg
    202 
    203 #---------------------------------------------------------------

    204 # Log callback function

    205 #---------------------------------------------------------------

    206 # map EAS severity levels to the Python logging module

    207 severity_mapping = (logging.CRITICAL, logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG)
    208 LOG_FUNC_TYPE = CFUNCTYPE(c_int, c_int, c_char_p)
    209 def Log (level, msg):
    210 	eas_logger.log(severity_mapping[level], msg)
    211 	return level
    212 LogCallback = LOG_FUNC_TYPE(Log)
    213 
    214 #---------------------------------------------------------------

    215 # EAS_Stream

    216 #---------------------------------------------------------------

    217 class EAS_Stream (object):
    218 	def __init__ (self, handle, eas):
    219 		eas_logger.debug('EAS_Stream.__init__')
    220 		self.handle = handle
    221 		self.eas = eas
    222 
    223 	def SetVolume (self, volume):
    224 		"""Set the stream volume"""
    225 		eas_logger.debug('Call EAS_SetVolume: volume=%d' % volume)
    226 		with self.eas.lock:
    227 			result = eas_dll.EAS_SetVolume(self.eas.handle, self.handle, volume)
    228 			if result:
    229 				raise EAS_Exception(result, 'EAS_SetVolume error %d on file %s' % (result, self.path), 'EAS_SetVolume')
    230 
    231 	def GetVolume (self):
    232 		"""Get the stream volume."""
    233 		eas_logger.debug('Call EAS_GetVolume')
    234 		with self.eas.lock:
    235 			volume = eas_dll.EAS_GetVolume(self.eas.handle, self.handle)
    236 			if volume < 0:
    237 				raise EAS_Exception(volume, 'EAS_GetVolume error %d on file %s' % (volume, self.path), 'EAS_GetVolume')
    238 		eas_logger.debug('EAS_GetVolume: volume=%d' % volume)
    239 		return volume
    240 
    241 	def SetPriority (self, priority):
    242 		"""Set the stream priority"""
    243 		eas_logger.debug('Call EAS_SetPriority: priority=%d' % priority)
    244 		with self.eas.lock:
    245 			result = eas_dll.EAS_SetPriority(self.eas.handle, self.handle, priority)
    246 			if result:
    247 				raise EAS_Exception(result, 'EAS_SetPriority error %d on file %s' % (result, self.path), 'EAS_SetPriority')
    248 
    249 	def GetPriority (self):
    250 		"""Get the stream priority."""
    251 		eas_logger.debug('Call EAS_GetPriority')
    252 		priority = c_int(0)
    253 		with self.eas.lock:
    254 			result = eas_dll.EAS_GetPriority(self.eas.handle, self.handle, byref(priority))
    255 			if result:
    256 				raise EAS_Exception(result, 'EAS_GetPriority error %d on file %s' % (result, self.path), 'EAS_GetPriority')
    257 		eas_logger.debug('EAS_GetPriority: priority=%d' % priority.value)
    258 		return priority.value
    259 
    260 	def SetTransposition (self, transposition):
    261 		"""Set the transposition of a stream."""
    262 		eas_logger.debug('Call EAS_SetTransposition: transposition=%d' % transposition)
    263 		with self.eas.lock:
    264 			result = eas_dll.EAS_SetTransposition(self.eas.handle, self.handle, transposition)
    265 			if result:
    266 				raise EAS_Exception(result, 'EAS_SetTransposition error %d on file %s' % (result, self.path), 'EAS_SetTransposition')
    267 
    268 	def SetPolyphony (self, polyphony):
    269 		"""Set the polyphony of a stream."""
    270 		eas_logger.debug('Call EAS_SetPolyphony: polyphony=%d' % polyphony)
    271 		with self.eas.lock:
    272 			result = eas_dll.EAS_SetPolyphony(self.eas.handle, self.handle, polyphony)
    273 			if result:
    274 				raise EAS_Exception(result, 'EAS_SetPolyphony error %d on file %s' % (result, self.path), 'EAS_SetPolyphony')
    275 
    276 	def GetPolyphony (self):
    277 		"""Get the polyphony of a stream."""
    278 		eas_logger.debug('Call EAS_GetPolyphony')
    279 		polyphony = c_int(0)
    280 		with self.eas.lock:
    281 			result = eas_dll.EAS_GetPolyphony(self.eas.handle, self.handle, byref(polyphony))
    282 			if result:
    283 				raise EAS_Exception(result, 'EAS_GetPolyphony error %d on file %s' % (result, self.path), 'EAS_GetPolyphony')
    284 		eas_logger.debug('EAS_SetPolyphony: polyphony=%d' % polyphony.value)
    285 		return polyphony.value
    286 
    287 	def SelectLib (self, test_lib=False):
    288 		eas_logger.debug('Call EAS_SelectLib: test_lib=%s' % test_lib)
    289 		with self.eas.lock:
    290 			result = eas_dll.EAS_SelectLib(self.eas.handle, self.handle, test_lib)
    291 			if result:
    292 				raise EAS_Exception(result, 'EAS_SelectLib error %d on file %s' % (result, self.path), 'EAS_SelectLib')
    293 
    294 	def LoadDLSCollection (self, path):
    295 		eas_logger.debug('Call EAS_LoadDLSCollection: lib_path=%d' % path)
    296 		with self.eas.lock:
    297 			result = eas_dll.EAS_LoadDLSCollection(self.eas.handle, self.handle, path)
    298 			if result:
    299 				raise EAS_Exception(result, 'EAS_LoadDLSCollection error %d on file %s lib %s' % (result, self.path, path), 'EAS_LoadDLSCollection')
    300 
    301 	def RegExtAudioCallback (self, user_data, prog_chg_func, event_func):
    302 		"""Register an external audio callback."""
    303 		eas_logger.debug('Call EAS_RegExtAudioCallback')
    304 		if prog_chg_func is not None:
    305 			prog_chg_func = EAS_EXT_PRG_CHG_FUNC(prog_chg_func)
    306 		else:
    307 			prog_chg_func = 0
    308 		if event_func is not None:
    309 			event_func = EAS_EXT_EVENT_FUNC(event_func)
    310 		else:
    311 			event_func = 0
    312 		with self.eas.lock:
    313 			result = eas_dll.EAS_RegExtAudioCallback(self.eas.handle, self.handle, user_data, prog_chg_func, event_func)
    314 			if result:
    315 				raise EAS_Exception(result, 'EAS_RegExtAudioCallback error %d on file %s' % (result, self.path), 'EAS_RegExtAudioCallback')
    316 
    317 	def SetPlayMode (self, play_mode):
    318 		"""Set play mode on a stream."""
    319 		eas_logger.debug('Call EAS_SetPlayMode: play_mode=%d' % play_mode)
    320 		with self.eas.lock:
    321 			result = eas_dll.EAS_SetPlayMode(self.eas.handle, self.handle, play_mode)
    322 			if result:
    323 				raise EAS_Exception(result, 'EAS_SetPlayMode error %d on file %s' % (result, self.path), 'EAS_SetPlayMode')
    324 
    325 """
    326 EAS_PUBLIC EAS_RESULT EAS_GetMIDIControllers (EAS_DATA_HANDLE pEASData, EAS_HANDLE streamHandle, EAS_U8 channel, S_MIDI_CONTROLLERS *pControl);
    327 """
    328 
    329 #---------------------------------------------------------------

    330 # EAS_File

    331 #---------------------------------------------------------------

    332 class EAS_File (EAS_Stream):
    333 	def __init__ (self, path, handle, eas):
    334 		EAS_Stream.__init__(self, handle, eas)
    335 		eas_logger.debug('EAS_File.__init__')
    336 		self.path = path
    337 		self.prepared = False
    338 
    339 	def Prepare (self):
    340 		"""Prepare an audio file for playback"""
    341 		if self.prepared:
    342 			eas_logger.warning('Prepare already called on file %s' % self.path)
    343 		else:
    344 			with self.eas.lock:
    345 				eas_logger.debug('Call EAS_Prepare for file: %s' % self.path)
    346 				result = eas_dll.EAS_Prepare(self.eas.handle, self.handle)
    347 				if result:
    348 					raise EAS_Exception(result, 'EAS_Prepare error %d on file %s' % (result, self.path), 'EAS_Prepare')
    349 				self.prepared = True
    350 
    351 	def State (self):
    352 		"""Get stream state."""
    353 		with self.eas.lock:
    354 			eas_logger.debug('Call EAS_State for file: %s' % self.path)
    355 			state = c_long(-1)
    356 			result = eas_dll.EAS_State(self.eas.handle, self.handle, byref(state))
    357 			if result:
    358 				raise EAS_Exception(result, 'EAS_State error %d on file %s' % (result, self.path), 'EAS_State')
    359 			eas_logger.debug('EAS_State: file=%s, state=%s' % (self.path, stream_states[state.value]))
    360 			return state.value
    361 
    362 	def Close (self):
    363 		"""Close audio file."""
    364 		if hasattr(self, 'handle'):
    365 			with self.eas.lock:
    366 				eas_logger.debug('Call EAS_CloseFile for file: %s' % self.path)
    367 				result = eas_dll.EAS_CloseFile(self.eas.handle, self.handle)
    368 				if result:
    369 					raise EAS_Exception(result, 'EAS_CloseFile error %d on file %s' % (result, self.path), 'EAS_CloseFile')
    370 
    371 				# remove file from the EAS object

    372 				self.eas.audio_streams.remove(self)
    373 				
    374 			# clean up references

    375 			del self.handle
    376 			del self.eas
    377 			del self.path
    378 
    379 	def Pause (self):
    380 		"""Pause a stream."""
    381 		eas_logger.debug('Call EAS_Pause')
    382 		with self.eas.lock:
    383 			result = eas_dll.EAS_Pause(self.eas.handle, self.handle)
    384 			if result:
    385 				raise EAS_Exception(result, 'EAS_Pause error %d on file %s' % (result, self.path), 'EAS_Pause')
    386 
    387 	def Resume (self):
    388 		"""Resume a stream."""
    389 		eas_logger.debug('Call EAS_Resume')
    390 		with self.eas.lock:
    391 			result = eas_dll.EAS_Resume(self.eas.handle, self.handle)
    392 			if result:
    393 				raise EAS_Exception(result, 'EAS_Resume error %d on file %s' % (result, self.path), 'EAS_Resume')
    394 
    395 	def Locate (self, secs, offset=False):
    396 		"""Set the playback position of a stream in seconds."""
    397 		eas_logger.debug('Call EAS_Locate: location=%.3f, relative=%s' % (secs, offset))
    398 		with self.eas.lock:
    399 			result = eas_dll.EAS_Locate(self.eas.handle, self.handle, int(secs * 1000 + 0.5), offset)
    400 			if result:
    401 				raise EAS_Exception(result, 'EAS_Locate error %d on file %s' % (result, self.path), 'EAS_Locate')
    402 
    403 	def GetLocation (self):
    404 		"""Get the stream location in seconds."""
    405 		eas_logger.debug('Call EAS_GetLocation')
    406 		msecs = c_int(0)
    407 		with self.eas.lock:
    408 			result = eas_dll.EAS_GetLocation(self.eas.handle, self.handle, byref(msecs))
    409 			if result:
    410 				raise EAS_Exception(result, 'EAS_GetLocation error %d on file %s' % (result, self.path), 'EAS_GetLocation')
    411 		msecs = float(msecs.value) / 1000 
    412 		eas_logger.debug('EAS_GetLocation: location=%.3f' % msecs)
    413 		return msecs
    414 
    415 	def GetFileType (self):
    416 		"""Get the file type."""
    417 		eas_logger.debug('Call EAS_GetFileType')
    418 		file_type = c_int(0)
    419 		with self.eas.lock:
    420 			result = eas_dll.EAS_GetFileType(self.eas.handle, self.handle, byref(file_type))
    421 			if result:
    422 				raise EAS_Exception(result, 'EAS_GetFileType error %d on file %s' % (result, self.path), 'EAS_GetFileType')
    423 		file_type = file_type.value
    424 		if file_type < len(file_types):
    425 			file_desc = file_types[file_type]
    426 		else:
    427 			file_desc = 'Unrecognized type %d' % file_type
    428 		eas_logger.debug('EAS_GetFileType: type=%d, desc=%s' % (file_type, file_desc))
    429 		return (file_type, file_desc)
    430 
    431 	def SetRepeat (self, count):
    432 		"""Set the repeat count of a stream."""
    433 		eas_logger.debug('Call EAS_SetRepeat: count=%d' % count)
    434 		with self.eas.lock:
    435 			result = eas_dll.EAS_SetRepeat(self.eas.handle, self.handle, count)
    436 			if result:
    437 				raise EAS_Exception(result, 'EAS_SetRepeat error %d on file %s' % (result, self.path), 'EAS_SetRepeat')
    438 
    439 	def GetRepeat (self):
    440 		"""Get the repeat count of a stream."""
    441 		eas_logger.debug('Call EAS_GetRepeat')
    442 		count = c_int(0)
    443 		with self.eas.lock:
    444 			result = eas_dll.EAS_GetRepeat(self.eas.handle, self.handle, byref(count))
    445 			if result:
    446 				raise EAS_Exception(result, 'EAS_GetRepeat error %d on file %s' % (result, self.path), 'EAS_GetRepeat')
    447 		eas_logger.debug('EAS_GetRepeat: count=%d' % count.value)
    448 		return count.value
    449 
    450 	def SetPlaybackRate (self, rate):
    451 		"""Set the playback rate of a stream."""
    452 		eas_logger.debug('Call EAS_SetPlaybackRate')
    453 		with self.eas.lock:
    454 			result = eas_dll.EAS_SetPlaybackRate(self.eas.handle, self.handle, rate)
    455 			if result:
    456 				raise EAS_Exception(result, 'EAS_SetPlaybackRate error %d on file %s' % (result, self.path), 'EAS_SetPlaybackRate')
    457 
    458 	def ParseMetaData (self):
    459 		"""Parse the metadata in a file."""
    460 		eas_logger.debug('Call EAS_ParseMetaData')
    461 		length = c_int(0)
    462 		with self.eas.lock:
    463 			result = eas_dll.EAS_ParseMetaData(self.eas.handle, self.handle, byref(length))
    464 			if result:
    465 				raise EAS_Exception(result, 'EAS_ParseMetaData error %d on file %s' % (result, self.path), 'EAS_ParseMetaData')
    466 		return float(length.value) / 1000.0
    467 
    468 	def RegisterMetaDataCallback (self, func, buf, buf_size, user_data):
    469 		"""Register a metadata callback."""
    470 		eas_logger.debug('Call EAS_RegisterMetaDataCallback')
    471 		with self.eas.lock:
    472 			if func is not None:
    473 				callback = EAS_METADATA_CBFUNC(func)
    474 			else:
    475 				callback = 0
    476 			result = eas_dll.EAS_RegisterMetaDataCallback(self.eas.handle, self.handle, callback, buf, buf_size, user_data)
    477 			if result:
    478 				raise EAS_Exception(result, 'EAS_RegisterMetaDataCallback error %d on file %s' % (result, self.path), 'EAS_RegisterMetaDataCallback')
    479 
    480 	def GetWaveFmtChunk (self):
    481 		"""Get the file type."""
    482 		eas_logger.debug('Call EAS_GetWaveFmtChunk')
    483 		wave_fmt_chunk = c_void_p(0)
    484 		with self.eas.lock:
    485 			result = eas_dll.EAS_GetWaveFmtChunk(self.eas.handle, self.handle, byref(wave_fmt_chunk))
    486 			if result:
    487 				raise EAS_Exception(result, 'EAS_GetWaveFmtChunk error %d on file %s' % (result, self.path), 'EAS_GetWaveFmtChunk')
    488 		return cast(wave_fmt_chunk, POINTER(WAVEFORMAT)).contents
    489 
    490 	def Play (self, max_time=None):
    491 		"""Plays the file to the end or max_time."""
    492 		eas_logger.debug('EAS_File.Play')
    493 		if not self.prepared:
    494 			self.Prepare()
    495 		if max_time is not None:
    496 			max_time += self.eas.GetRenderTime()
    497 		while self.State() not in (EAS_STATE_STOPPED, EAS_STATE_ERROR, EAS_STATE_EMPTY):
    498 			self.eas.Render()
    499 			if max_time is not None:
    500 				if self.eas.GetRenderTime() >= max_time:
    501 					eas_logger.info('Max render time exceeded - stopping playback')
    502 					self.Pause()
    503 					self.eas.Render()
    504 					break
    505 
    506 #---------------------------------------------------------------

    507 # EAS_MIDIStream

    508 #---------------------------------------------------------------

    509 class EAS_MIDIStream (EAS_Stream):
    510 	def Write(self, data):
    511 		"""Write data to MIDI stream."""
    512 		with self.eas.lock:
    513 			result = eas_dll.EAS_WriteMIDIStream(self.eas.handle, self.handle, data, len(data))
    514 			if result:
    515 				raise EAS_Exception(result, 'EAS_WriteMIDIStream error %d' % result, 'EAS_WriteMIDIStream')
    516 
    517 	def Close (self):
    518 		"""Close MIDI stream."""
    519 		if hasattr(self, 'handle'):
    520 			with self.eas.lock:
    521 				eas_logger.debug('Call EAS_CloseMIDIStream')
    522 				result = eas_dll.EAS_CloseMIDIStream(self.eas.handle, self.handle)
    523 				if result:
    524 					raise EAS_Exception(result, 'EAS_CloseFile error %d' % result, 'EAS_CloseMIDIStream')
    525 
    526 				# remove file from the EAS object

    527 				self.eas.audio_streams.remove(self)
    528 				
    529 			# clean up references

    530 			del self.handle
    531 			del self.eas
    532 
    533 #---------------------------------------------------------------

    534 # EAS_Config 

    535 #---------------------------------------------------------------

    536 class EAS_Config (Structure):
    537 	_fields_ = [('libVersion', c_ulong),
    538 				('checkedVersion', c_int),
    539 				('maxVoices', c_long),
    540 				('numChannels', c_long),
    541 				('sampleRate', c_long),
    542 				('mixBufferSize', c_long),
    543 				('filterEnabled', c_int),
    544 				('buildTimeStamp', c_ulong),
    545 				('buildGUID', c_char_p)]
    546 
    547 #---------------------------------------------------------------

    548 # EAS

    549 #---------------------------------------------------------------

    550 class EAS (object):
    551 	def __init__ (self, handle=None, dll_path=None, log_file=None):
    552 		if eas_dll is None:
    553 			InitEASModule(dll_path)
    554 		if log_file is not None:
    555 			eas_logger.addHandler(log_file)
    556 		eas_logger.debug('EAS.__init__')
    557 		self.Init(handle)
    558 
    559 	def __del__ (self):
    560 		eas_logger.debug('EAS.__del__')
    561 		self.Shutdown()
    562 
    563 	def Init (self, handle=None):
    564 		"""Initializes the EAS Library."""
    565 		eas_logger.debug('EAS.Init')
    566 
    567 		# if we are already initialized, shutdown first

    568 		if hasattr(self, 'handle'):
    569 			eas_logger.debug('EAS.Init called with library already initalized')
    570 			self.ShutDown()
    571 
    572 		# setup the logging function

    573 		eas_dll.SetLogCallback(LogCallback)
    574 
    575 		# create some members

    576 		self.handle = c_void_p(0)
    577 		self.audio_streams = []
    578 		self.output_streams = []
    579 		self.aux_mixer = None
    580 		
    581 		# create a sync lock

    582 		self.lock = threading.RLock()
    583 		with self.lock:
    584 			# set log callback

    585 		
    586 			# get library configuration

    587 			self.Config()
    588 
    589 			# initialize library

    590 			if handle is None:
    591 				self.do_shutdown = True
    592 				eas_logger.debug('Call EAS_Init')
    593 				result = eas_dll.EAS_Init(byref(self.handle))
    594 				if result:
    595 					raise EAS_Exception(result, 'EAS_Init error %d' % result, 'EAS_Init')
    596 			else:
    597 				self.do_shutdown = False
    598 				self.handle = handle
    599 
    600 			# allocate audio buffer for rendering

    601 			AudioBufferType = c_ubyte * (2 * self.config.mixBufferSize * self.config.numChannels)
    602 			self.audio_buffer = AudioBufferType()
    603 			self.buf_size = self.config.mixBufferSize
    604 
    605 	def Config (self):
    606 		"""Retrieves the EAS library configuration"""
    607 		if not hasattr(self, 'config'):
    608 			eas_logger.debug('Call EAS_Config')
    609 			eas_dll.EAS_Config.restype = POINTER(EAS_Config)
    610 			self.config = eas_dll.EAS_Config()[0]
    611 		eas_logger.debug("libVersion=%08x, maxVoices=%d, numChannels=%d, sampleRate = %d, mixBufferSize=%d" %
    612 			(self.config.libVersion, self.config.maxVoices, self.config.numChannels, self.config.sampleRate, self.config.mixBufferSize))
    613 
    614 	def Shutdown (self):
    615 		"""Shuts down the EAS library"""
    616 		eas_logger.debug('EAS.Shutdown')
    617 		if hasattr(self, 'handle'):
    618 			with self.lock:
    619 				# close audio streams

    620 				audio_streams = self.audio_streams
    621 				for f in audio_streams:
    622 					eas_logger.warning('Stream was not closed before EAS_Shutdown')
    623 					f.Close()
    624 
    625 				# close output streams

    626 				output_streams = self.output_streams
    627 				for s in output_streams:
    628 					s.close()
    629 
    630 				# shutdown library

    631 				if self.do_shutdown:
    632 					eas_logger.debug('Call EAS_Shutdown')
    633 					result = eas_dll.EAS_Shutdown(self.handle)
    634 					if result:
    635 						raise EAS_Exception(result, 'EAS_Shutdown error %d' % result, 'EAS_Shutdown')
    636 				del self.handle
    637 
    638 	def OpenFile (self, path):
    639 		"""Opens an audio file to be played by the EAS library and
    640 		returns an EAS_File object
    641 
    642 		Arguments:
    643 			path - path to audio file
    644 
    645 		Returns:
    646 			EAS_File
    647 			
    648 		"""
    649 		with self.lock:
    650 			eas_logger.debug('Call EAS_OpenFile for file: %s' % path)
    651 			stream_handle = c_void_p(0)
    652 			result = eas_dll.EAS_OpenFile(self.handle, path, byref(stream_handle))
    653 			if result:
    654 				raise EAS_Exception(result, 'EAS_OpenFile error %d on file %s' % (result, path), 'EAS_OpenFile')
    655 
    656 			# create file object and save in list

    657 			stream = EAS_File(path, stream_handle, self)
    658 			self.audio_streams.append(stream)
    659 			return stream
    660 
    661 	def OpenMIDIStream (self, stream=None):
    662 		"""Opens a MIDI stream.
    663 
    664 		Arguments:
    665 			stream - open stream object. If None, a new synth
    666 			is created.
    667 
    668 		Returns:
    669 			EAS_MIDIStream
    670 			
    671 		"""
    672 		with self.lock:
    673 			eas_logger.debug('Call EAS_OpenMIDIStream')
    674 			stream_handle = c_void_p(0)
    675 			if stream.handle is not None:
    676 				result = eas_dll.EAS_OpenMIDIStream(self.handle, byref(stream_handle), stream.handle)
    677 			else:
    678 				result = eas_dll.EAS_OpenMIDIStream(self.handle, byref(stream_handle), 0)
    679 			if result:
    680 				raise EAS_Exception(result, 'EAS_OpenMIDIStream error %d' % result, 'EAS_OpenMIDIStream')
    681 
    682 			# create stream object and save in list

    683 			stream = EAS_MIDIStream(stream_handle, self)
    684 			self.audio_streams.append(stream)
    685 			return stream
    686 
    687 	def OpenToneControlStream (self, path):
    688 		"""Opens an MMAPI tone control file to be played by the EAS
    689 		library and returns an EAS_File object
    690 
    691 		Arguments:
    692 			path - path to audio file
    693 
    694 		Returns:
    695 			EAS_File
    696 			
    697 		"""
    698 		with self.lock:
    699 			eas_logger.debug('Call EAS_MMAPIToneControl for file: %s' % path)
    700 			stream_handle = c_void_p(0)
    701 			result = eas_dll.EAS_MMAPIToneControl(self.handle, path, byref(stream_handle))
    702 			if result:
    703 				raise EAS_Exception(result, 'EAS_MMAPIToneControl error %d on file %s' % (result, path), 'EAS_OpenToneControlStream')
    704 
    705 			# create file object and save in list

    706 			stream = EAS_File(path, stream_handle, self)
    707 			self.audio_streams.append(stream)
    708 			return stream
    709 
    710 	def Attach (self, stream):
    711 		"""Attach a file or output device to the EAS output.
    712 
    713 		The stream object must support the following methods as
    714 		defined in the Python wave module:
    715 			close()
    716 			setparams()
    717 			writeframesraw()
    718 
    719 		Arguments:
    720 			stream - open wave object
    721 
    722 		"""
    723 		self.output_streams.append(stream)
    724 		stream.setparams((self.config.numChannels, 2, self.config.sampleRate, 0, 'NONE', None))
    725 
    726 	def Detach (self, stream):
    727 		"""Detach a file or output device from the EAS output. See
    728 		EAS.Attach for more details. It is the responsibility of
    729 		the caller to close the wave file or stream.
    730 
    731 		Arguments:
    732 			stream - open and attached wave object
    733 		"""
    734 		self.output_streams.remove(stream)
    735 
    736 	def StartWave (self, dev_num=0, sampleRate=None, maxBufSize=None):
    737 		"""Route the audio output to the indicated wave device. Note
    738 		that this can cause EASDLL.EAS_RenderWaveOut to return an
    739 		error code if all the output buffers are full. In this case,
    740 		the render thread should sleep a bit and try again.
    741 		Unfortunately, due to the nature of the MMSYSTEM interface,
    742 		there is no simple way to suspend the render thread.
    743 
    744 		"""
    745 		if sampleRate == None:
    746 			sampleRate = self.config.sampleRate
    747 		if maxBufSize == None:
    748 			maxBufSize = self.config.mixBufferSize
    749 		with self.lock:
    750 			result = eas_dll.OpenWaveOutDevice(dev_num, sampleRate, maxBufSize)
    751 			if result:
    752 				raise EAS_Exception(result, 'OpenWaveOutDevice error %d' % result, 'OpenWaveOutDevice')
    753 
    754 	def StopWave (self):
    755 		"""Stop routing audio output to the audio device."""
    756 		with self.lock:
    757 			result = eas_dll.CloseWaveOutDevice()
    758 			if result:
    759 				raise EAS_Exception(result, 'CloseWaveOutDevice error %d' % result, 'CloseWaveOutDevice')
    760 
    761 	def Render (self, count=None, secs=None):
    762 		"""Calls EAS_Render to render audio.
    763 
    764 		Arguments
    765 			count - number of buffers to render
    766 			secs - number of seconds to render
    767 
    768 		If both count and secs are None, render a single buffer. 
    769 
    770 		"""
    771 
    772 		# determine number of buffers to render

    773 		if count is None:
    774 			if secs is not None:
    775 				count = int(secs * float(self.config.sampleRate) / float(self.buf_size) + 0.5)
    776 			else:
    777 				count = 1
    778 
    779 		# render buffers

    780 		eas_logger.debug('rendering %d buffers' % count)
    781 		samplesRendered = c_long(0)
    782 		with self.lock:
    783 			for c in range(count):
    784 				# render a buffer of audio

    785 				eas_logger.debug('rendering buffer')
    786 				while 1:
    787 					if self.aux_mixer is None:
    788 						result = eas_dll.EAS_RenderWaveOut(self.handle, byref(self.audio_buffer), self.buf_size, byref(samplesRendered))
    789 					else:
    790 						result = eas_dll.EAS_RenderAuxMixer(self.handle, byref(self.audio_buffer), byref(samplesRendered))
    791 					
    792 					if result == 0:
    793 						break;
    794 					if result == EAS_BUFFER_FULL:
    795 						time.sleep(0.01)
    796 					else:
    797 						raise EAS_Exception(result, 'EAS_Render error %d' % result, 'EAS_Render')
    798 
    799 				# output to attached streams

    800 				for s in self.output_streams:
    801 					s.writeframesraw(self.audio_buffer)
    802 				
    803 	def GetRenderTime (self):
    804 		"""Get the render time in seconds."""
    805 		eas_logger.debug('Call EAS_GetRenderTime')
    806 		msecs = c_int(0)
    807 		with self.lock:
    808 			result = eas_dll.EAS_GetRenderTime(self.handle, byref(msecs))
    809 			if result:
    810 				raise EAS_Exception(result, 'EAS_GetRenderTime error %d' % result, 'EAS_GetRenderTime')
    811 		msecs = float(msecs.value) / 1000
    812 		eas_logger.debug('EAS_GetRenderTime: time=%.3f' % msecs)
    813 		return msecs
    814 
    815 	def SetVolume (self, volume):
    816 		"""Set the master volume"""
    817 		eas_logger.debug('Call EAS_SetVolume: volume=%d' % volume)
    818 		with self.lock:
    819 			result = eas_dll.EAS_SetVolume(self.handle, 0, volume)
    820 			if result:
    821 					raise EAS_Exception(result, 'EAS_SetVolume error %d' % result, 'EAS_SetVolume')
    822 
    823 	def GetVolume (self):
    824 		"""Get the stream volume."""
    825 		eas_logger.debug('Call EAS_GetVolume')
    826 		volume = c_int(0)
    827 		with self.lock:
    828 			result = eas_dll.EAS_GetVolume(self.handle, 0, byref(volume))
    829 			if result:
    830 				raise EAS_Exception(result, 'EAS_GetVolume error %d' % result, 'EAS_GetVolume')
    831 		eas_logger.debug('EAS_GetVolume: volume=%d' % volume.value)
    832 		return volume.value
    833 
    834 	def SetPolyphony (self, polyphony, synth_num=0):
    835 		"""Set the polyphony of a synth."""
    836 		eas_logger.debug('Call EAS_SetSynthPolyphony: synth_num=%d, polyphony=%d' % (synth_num, polyphony))
    837 		with self.lock:
    838 			result = eas_dll.EAS_SetSynthPolyphony(self.handle, synth_num, polyphony)
    839 			if result:
    840 				raise EAS_Exception(result, 'EAS_SetSynthPolyphony error %d on synth %d' % (result, synth_num), 'EAS_SetPolyphony')
    841 
    842 	def GetPolyphony (self, synth_num=0):
    843 		"""Get the polyphony of a synth."""
    844 		eas_logger.debug('Call EAS_GetSynthPolyphony: synth_num=%d' % synth_num)
    845 		polyphony = c_int(0)
    846 		with self.lock:
    847 			result = eas_dll.EAS_GetSynthPolyphony(self.handle, synth_num, byref(polyphony))
    848 			if result:
    849 				raise EAS_Exception(result, 'EAS_GetSynthPolyphony error %d on synth %d' % (result, synth_num), 'EAS_GetPolyphony')
    850 		eas_logger.debug('Call EAS_GetSynthPolyphony: synth_num=%d, polyphony=%d' % (synth_num, polyphony.value))
    851 		return polyphony.value
    852 
    853 	def SetMaxLoad (self, max_load):
    854 		"""Set the maximum parser load."""
    855 		eas_logger.debug('Call EAS_SetMaxLoad: max_load=%d' % max_load)
    856 		with self.lock:
    857 			result = eas_dll.EAS_SetMaxLoad(self.handle, max_load)
    858 			if result:
    859 				raise EAS_Exception(result, 'EAS_SetMaxLoad error %d' % result, 'EAS_SetMaxLoad')
    860 
    861 	def SetParameter (self, module, param, value):
    862 		"""Set a module parameter."""
    863 		eas_logger.debug('Call EAS_SetParameter: module=%d, param=%d, value=%d' % (module, param, value))
    864 		with self.lock:
    865 			result = eas_dll.EAS_SetParameter(self.handle, module, param, value)
    866 			if result:
    867 				raise EAS_Exception(result, 'EAS_SetParameter error %d (param=%d, value=%d)' % (result, param, value), 'EAS_SetParameter')
    868 				
    869 	def GetParameter (self, module, param):
    870 		"""Get the polyphony of a synth."""
    871 		eas_logger.debug('Call EAS_GetParameter: module=%d, param=%d' % (module, param))
    872 		value = c_int(0)
    873 		with self.lock:
    874 			result = eas_dll.EAS_GetParameter(self.handle, module, param, byref(value))
    875 			if result:
    876 				raise EAS_Exception(result, 'EAS_SetParameter error %d (param=%d)' % (result, param), 'EAS_GetParameter')
    877 		eas_logger.debug('Call EAS_SetParameter: module=%d, param=%d, value=%d' % (module, param, value.value))
    878 		return value.value
    879 
    880 	def SelectLib (self, test_lib=False):
    881 		eas_logger.debug('Call EAS_SelectLib: test_lib=%s' % test_lib)
    882 		easdll = cdll.LoadLibrary('EASDLL')
    883 		with self.lock:
    884 			result = eas_dll.EAS_SelectLib(self.handle, 0, test_lib)
    885 			if result:
    886 				raise EAS_Exception(result, 'EAS_SelectLib error %d' % result, 'EAS_SelectLib')
    887 
    888 	def LoadDLSCollection (self, path):
    889 		eas_logger.debug('Call EAS_LoadDLSCollection: lib_path=%s' % path)
    890 		with self.lock:
    891 			result = eas_dll.EAS_LoadDLSCollection(self.handle, 0, path)
    892 			if result:
    893 				raise EAS_Exception(result, 'EAS_LoadDLSCollection error %d lib %s' % (result, path), 'EAS_LoadDLSCollection')
    894 
    895 	def SetAuxMixerHook (self, aux_mixer):
    896 
    897 		# if aux mixer has bigger buffer, re-allocate buffer

    898 		if (aux_mixer is not None) and (aux_mixer.buf_size > self.config.mixBufferSize):
    899 			buf_size = aux_mixer.buf_size
    900 		else:
    901 			buf_size = self.config.mixBufferSize
    902 
    903 		# allocate audio buffer for rendering

    904 		AudioBufferType = c_ubyte * (2 * buf_size * self.config.numChannels)
    905 		self.audio_buffer = AudioBufferType()
    906 		self.buf_size = buf_size
    907 		self.aux_mixer = aux_mixer
    908 
    909 	def SetDebugLevel (self, level=3):
    910 		"""Sets the EAS debug level."""
    911 		with self.lock:
    912 			eas_logger.debug('Call EAS_SetDebugLevel')
    913 			eas_dll.EAS_DLLSetDebugLevel(self.handle, level)
    914 
    915 #---------------------------------------------------------------

    916 # EASAuxMixer

    917 #---------------------------------------------------------------

    918 class EASAuxMixer (object):
    919 	def __init__ (self, eas=None, num_streams=3, sample_rate=44100, max_sample_rate=44100):
    920 		eas_logger.debug('EASAuxMixer.__init__')
    921 		self.Init(eas, num_streams, sample_rate, max_sample_rate)
    922 
    923 	def __del__ (self):
    924 		eas_logger.debug('EASAuxMixer.__del__')
    925 		self.Shutdown()
    926 
    927 	def Init (self, eas=None, num_streams=3, sample_rate=44100, max_sample_rate=44100):
    928 		"""Initializes the EAS Auxilliary Mixer."""
    929 		eas_logger.debug('EASAuxMixer.Init')
    930 
    931 		if hasattr(self, 'eas'):
    932 			raise EAS_Exception(-1, 'EASAuxMixer already initialized', 'EASAuxMixer.Init')
    933 
    934 		# initialize EAS, if necessary

    935 		if eas is None:
    936 			eas_logger.debug('No EAS handle --- initializing EAS')
    937 			eas = EAS()
    938 			self.alloc_eas = True
    939 		else:
    940 			self.alloc_eas = False
    941 		self.eas = eas
    942 
    943 		# initialize library

    944 		eas_logger.debug('Call EAS_InitAuxMixer')
    945 		buf_size = c_int(0)
    946 		result = eas_dll.EAS_InitAuxMixer(eas.handle, num_streams, sample_rate, max_sample_rate, byref(buf_size))
    947 		if result:
    948 			raise EAS_Exception(result, 'EAS_InitAuxMixer error %d' % result, 'EAS_InitAuxMixer')
    949 		self.buf_size = buf_size.value
    950 		self.streams = []
    951 		eas.SetAuxMixerHook(self)
    952 
    953 	def Shutdown (self):
    954 		"""Shuts down the EAS Auxilliary Mixer"""
    955 		eas_logger.debug('EASAuxMixer.Shutdown')
    956 		if not hasattr(self, 'eas'):
    957 			return
    958 			
    959 		with self.eas.lock:
    960 			if len(self.streams):
    961 				eas_logger.warning('Stream was not closed before EAS_ShutdownAuxMixer')
    962 				for stream in self.streams:
    963 					self.CloseStream(stream)
    964 
    965 			self.eas.SetAuxMixerHook(None)
    966 
    967 			# shutdown library

    968 			eas_logger.debug('Call EAS_ShutdownAuxMixer')
    969 			result = eas_dll.EAS_ShutdownAuxMixer(self.eas.handle)
    970 			if result:
    971 				raise EAS_Exception(result, 'EAS_ShutdownAuxMixer error %d' % result, 'EAS_ShutdownAuxMixer')
    972 
    973 			# if we created the EAS reference here, shut it down

    974 			if self.alloc_eas:
    975 				self.eas.Shutdown()
    976 				self.alloc_eas = False
    977 			del self.eas
    978 
    979 	def OpenStream (self, decoder_func, inst_data, sample_rate, num_channels):
    980 		"""Opens an audio file to be played by the JET library and
    981 		returns a JET_File object
    982 
    983 		Arguments:
    984 			callback - callback function to decode more audio
    985 
    986 		"""
    987 		with self.eas.lock:
    988 			eas_logger.debug('Call EAS_OpenAudioStream')
    989 			decoder_func = EAS_DECODER_FUNC(decoder_func)
    990 			stream_handle = c_void_p(0)
    991 			result = eas_dll.EAS_OpenAudioStream(self.eas.handle, decoder_func, inst_data, sample_rate, num_channels, stream_handle)
    992 			if result:
    993 				raise EAS_Exception(result, 'EAS_OpenAudioStream error %d on file %s' % (result, path), 'EAS_OpenAudioStream')
    994 			self.streams.add(stream_handle)
    995 			return stream_handle
    996 
    997 	def CloseStream (self, stream_handle):
    998 		"""Closes an open audio stream."""
    999 		with self.eas.lock:
   1000 			eas_logger.debug('Call EAS_CloseAudioStream')
   1001 			result = eas_dll.JET_CloseFile(self.eas.handle, stream_handle)
   1002 			if result:
   1003 				raise EAS_Exception(result, 'EAS_CloseAudioStream error %d' % result, 'EAS_CloseAudioStream')
   1004 
   1005 #---------------------------------------------------------------

   1006 # JET_Status 

   1007 #---------------------------------------------------------------

   1008 class JET_Status (Structure):
   1009 	_fields_ = [('currentUserID', c_int),
   1010 				('segmentRepeatCount', c_int),
   1011 				('numQueuedSegments', c_int),
   1012 				('paused', c_int),
   1013 				('location', c_long),
   1014 				('currentPlayingSegment', c_int),
   1015 				('currentQueuedSegment', c_int),
   1016 				]
   1017 
   1018 #---------------------------------------------------------------

   1019 # JET_File

   1020 #---------------------------------------------------------------

   1021 class JET_File (object):
   1022 	def __init__ (self, handle, jet):
   1023 		eas_logger.debug('JET_File.__init__')
   1024 		self.handle = handle
   1025 		self.jet = jet
   1026 
   1027 #---------------------------------------------------------------

   1028 # JET

   1029 #---------------------------------------------------------------

   1030 class JET (object):
   1031 	def __init__ (self, eas=None):
   1032 		# eas_logger.debug('JET.__init__')

   1033 		self.Init(eas)
   1034 
   1035 	def __del__ (self):
   1036 		eas_logger.debug('JET.__del__')
   1037 		self.Shutdown()
   1038 
   1039 	def Init (self, eas=None, config=None):
   1040 		"""Initializes the JET Library."""
   1041 		# eas_logger.debug('JET.Init')

   1042 
   1043 		if hasattr(self, 'eas'):
   1044 			raise EAS_Exception(-1, 'JET library already initialized', 'Jet.Init')
   1045 
   1046 		# create some members

   1047 		if eas is None:
   1048 			# eas_logger.debug('No EAS handle --- initializing EAS')

   1049 			eas = EAS()
   1050 			self.alloc_eas = True
   1051 		else:
   1052 			self.alloc_eas = False
   1053 		self.eas = eas
   1054 		self.fileOpen = False
   1055 
   1056 		# handle configuration

   1057 		if config is None:
   1058 			config_handle = c_void_p(0)
   1059 			config_size = 0
   1060 		else:
   1061 			jet_config = S_JET_CONFIG()
   1062 			jet_config.appLowNote = config.appLowNote
   1063 			config_handle = c_void_p(jet_config)
   1064 			config_size = jet_config.sizeof()
   1065 
   1066 		# initialize library

   1067 		# eas_logger.debug('Call JET_Init')

   1068 		result = eas_dll.JET_Init(eas.handle, config_handle, config_size)
   1069 		if result:
   1070 			raise EAS_Exception(result, 'JET_Init error %d' % result, 'JET_Init')
   1071 
   1072 	def Shutdown (self):
   1073 		"""Shuts down the JET library"""
   1074 		eas_logger.debug('JET.Shutdown')
   1075 		if not hasattr(self, 'eas'):
   1076 			return
   1077 			
   1078 		with self.eas.lock:
   1079 			if self.fileOpen:
   1080 				eas_logger.warning('Stream was not closed before JET_Shutdown')
   1081 				self.CloseFile()
   1082 
   1083 			# shutdown library

   1084 			eas_logger.debug('Call JET_Shutdown')
   1085 			result = eas_dll.JET_Shutdown(self.eas.handle)
   1086 			if result:
   1087 				raise EAS_Exception(result, 'JET_Shutdown error %d' % result, 'JET_Shutdown')
   1088 
   1089 			# if we created the EAS reference here, shut it down

   1090 			if self.alloc_eas:
   1091 				self.eas.Shutdown()
   1092 				self.alloc_eas = False
   1093 			del self.eas
   1094 
   1095 	def OpenFile (self, path):
   1096 		"""Opens an audio file to be played by the JET library and
   1097 		returns a JET_File object
   1098 
   1099 		Arguments:
   1100 			path - path to audio file
   1101 
   1102 		"""
   1103 		with self.eas.lock:
   1104 			eas_logger.debug('Call JET_OpenFile for file: %s' % path)
   1105 			result = eas_dll.JET_OpenFile(self.eas.handle, path)
   1106 			if result:
   1107 				raise EAS_Exception(result, 'JET_OpenFile error %d on file %s' % (result, path), 'JET_OpenFile')
   1108 
   1109 	def CloseFile (self):
   1110 		"""Closes an open audio file."""
   1111 		with self.eas.lock:
   1112 			eas_logger.debug('Call JET_CloseFile')
   1113 			result = eas_dll.JET_CloseFile(self.eas.handle)
   1114 			if result:
   1115 				raise EAS_Exception(result, 'JET_CloseFile error %d' % result, 'JET_CloseFile')
   1116 
   1117 	def QueueSegment (self, userID, seg_num, dls_num=-1, repeat=0, tranpose=0, mute_flags=0):
   1118 		"""Queue a segment for playback.
   1119 
   1120 		Arguments:
   1121 			seg_num - segment number to queue
   1122 			repeat - repeat count (-1=repeat forever, 0=no repeat, 1+ = play n+1 times)
   1123 			tranpose - transpose amount (+/-12)
   1124 			
   1125 			"""
   1126 		with self.eas.lock:
   1127 			eas_logger.debug('Call JET_QueueSegment')
   1128 			result = eas_dll.JET_QueueSegment(self.eas.handle, seg_num, dls_num, repeat, tranpose, mute_flags, userID)
   1129 			if result:
   1130 				raise EAS_Exception(result, 'JET_QueueSegment error %d' % result, 'JET_QueueSegment')
   1131 
   1132 	def Clear_Queue(self):
   1133 		"""Kills the queue."""
   1134 		with self.eas.lock:
   1135 			eas_logger.debug('Call JET_Clear_Queue')
   1136 			result = eas_dll.JET_Clear_Queue(self.eas.handle)
   1137 			if result:
   1138 				raise EAS_Exception(result, 'JET_Clear_Queue error %d' % result, 'JET_Clear_Queue')
   1139 
   1140 	def GetAppEvent(self):
   1141 		"""Gets an App event."""
   1142 		with self.eas.lock:
   1143 			eas_logger.debug('Call JET_GetEvent')
   1144 			result = eas_dll.JET_GetEvent(self.eas.handle, 0, 0)
   1145 			return result    
   1146 
   1147 	def Play(self):
   1148 		"""Starts JET playback."""
   1149 		with self.eas.lock:
   1150 			eas_logger.debug('Call JET_Play')
   1151 			result = eas_dll.JET_Play(self.eas.handle)
   1152 			if result:
   1153 				raise EAS_Exception(result, 'JET_Play error %d' % result, 'JET_Play')
   1154 
   1155 	def Pause(self):
   1156 		"""Pauses JET playback."""
   1157 		with self.eas.lock:
   1158 			eas_logger.debug('Call JET_Pause')
   1159 			result = eas_dll.JET_Pause(self.eas.handle)
   1160 			if result:
   1161 				raise EAS_Exception(result, 'JET_Pause error %d' % result, 'JET_Pause')
   1162 
   1163 	def Render (self, count=None, secs=None):
   1164 		"""Calls EAS_Render to render audio.
   1165 
   1166 		Arguments
   1167 			count - number of buffers to render
   1168 			secs - number of seconds to render
   1169 
   1170 		If both count and secs are None, render a single buffer. 
   1171 
   1172 		"""
   1173 		# calls JET.Render

   1174 		with self.eas.lock:
   1175 			self.eas.Render(count, secs)
   1176 
   1177 	def Status (self):
   1178 		"""Get JET status."""
   1179 		with self.eas.lock:
   1180 			eas_logger.debug('Call JET_Status')
   1181 			status = JET_Status()
   1182 			result = eas_dll.JET_Status(self.eas.handle, byref(status))
   1183 			if result:
   1184 				raise EAS_Exception(result, 'JET_Status error %d' % result, 'JET_Status')
   1185 			eas_logger.debug("currentUserID=%d, repeatCount=%d, numQueuedSegments=%d, paused=%d" %
   1186 				(status.currentUserID, status.segmentRepeatCount, status.numQueuedSegments, status.paused))
   1187 			return status
   1188 				
   1189 	def SetVolume (self, volume):
   1190 		"""Set the JET volume"""
   1191 		eas_logger.debug('Call JET_SetVolume')
   1192 		with self.eas.lock:
   1193 			result = eas_dll.JET_SetVolume(self.eas.handle, volume)
   1194 			if result:
   1195 					raise EAS_Exception(result, 'JET_SetVolume error %d' % result, 'JET_SetVolume')
   1196 
   1197 	def SetTransposition (self, transposition):
   1198 		"""Set the transposition of a stream."""
   1199 		eas_logger.debug('Call JET_SetTransposition')
   1200 		with self.eas.lock:
   1201 			result = eas_dll.JET_SetTransposition(self.eas.handle, transposition)
   1202 			if result:
   1203 				raise EAS_Exception(result, 'JET_SetTransposition error %d' % result, 'JET_SetTransposition')
   1204 
   1205 	def TriggerClip (self, clipID):
   1206 		"""Trigger a clip in the current segment."""
   1207 		eas_logger.debug('Call JET_TriggerClip')
   1208 		with self.eas.lock:
   1209 			result = eas_dll.JET_TriggerClip(self.eas.handle, clipID)
   1210 			if result:
   1211 				raise EAS_Exception(result, 'JET_SetTransposition error %d' % result, 'JET_TriggerClip')
   1212 
   1213 	def SetMuteFlag (self, track_num, mute, sync=True):
   1214 		"""Trigger a clip in the current segment."""
   1215 		eas_logger.debug('Call JET_SetMuteFlag')
   1216 		with self.eas.lock:
   1217 			result = eas_dll.JET_SetMuteFlag(self.eas.handle, track_num, mute, sync)
   1218 			if result:
   1219 				raise EAS_Exception(result, 'JET_SetMuteFlag error %d' % result, 'JET_SetMuteFlag')
   1220 
   1221 	def SetMuteFlags (self, mute_flags, sync=True):
   1222 		"""Trigger a clip in the current segment."""
   1223 		eas_logger.debug('Call JET_SetMuteFlags')
   1224 		with self.eas.lock:
   1225 			result = eas_dll.JET_SetMuteFlags(self.eas.handle, mute_flags, sync)
   1226 			if result:
   1227 				raise EAS_Exception(result, 'JET_SetMuteFlag error %d' % result, 'JET_SetMuteFlags')
   1228 
   1229 
   1230