eas.py revision 6b065d0f7161fe54e6f58fd2b8ad6c650b2d3657
1
2from __future__ import with_statement
3
4import threading
5import logging
6import time
7from ctypes import *
8from JetUtils import OsWindows
9
10
11# stream state
12EAS_STATE_READY = 0
13EAS_STATE_PLAY = 1
14EAS_STATE_STOPPING = 2
15EAS_STATE_PAUSING = 3
16EAS_STATE_STOPPED = 4
17EAS_STATE_PAUSED = 5
18EAS_STATE_OPEN = 6
19EAS_STATE_ERROR = 7
20EAS_STATE_EMPTY = 8
21
22# EAS error codes
23EAS_SUCCESS	= 0
24EAS_FAILURE = -1
25EAS_ERROR_INVALID_MODULE = -2
26EAS_ERROR_MALLOC_FAILED = -3
27EAS_ERROR_FILE_POS = -4
28EAS_ERROR_INVALID_FILE_MODE = -5
29EAS_ERROR_FILE_SEEK = -6
30EAS_ERROR_FILE_LENGTH = -7
31EAS_ERROR_NOT_IMPLEMENTED = -8
32EAS_ERROR_CLOSE_FAILED = -9
33EAS_ERROR_FILE_OPEN_FAILED = -10
34EAS_ERROR_INVALID_HANDLE = -11
35EAS_ERROR_NO_MIX_BUFFER = -12
36EAS_ERROR_PARAMETER_RANGE = -13
37EAS_ERROR_MAX_FILES_OPEN = -14
38EAS_ERROR_UNRECOGNIZED_FORMAT = -15
39EAS_BUFFER_SIZE_MISMATCH = -16
40EAS_ERROR_FILE_FORMAT = -17
41EAS_ERROR_SMF_NOT_INITIALIZED = -18
42EAS_ERROR_LOCATE_BEYOND_END = -19
43EAS_ERROR_INVALID_PCM_TYPE = -20
44EAS_ERROR_MAX_PCM_STREAMS = -21
45EAS_ERROR_NO_VOICE_ALLOCATED = -22
46EAS_ERROR_INVALID_CHANNEL = -23
47EAS_ERROR_ALREADY_STOPPED = -24
48EAS_ERROR_FILE_READ_FAILED = -25
49EAS_ERROR_HANDLE_INTEGRITY = -26
50EAS_ERROR_MAX_STREAMS_OPEN = -27
51EAS_ERROR_INVALID_PARAMETER = -28
52EAS_ERROR_FEATURE_NOT_AVAILABLE = -29
53EAS_ERROR_SOUND_LIBRARY = -30
54EAS_ERROR_NOT_VALID_IN_THIS_STATE = -31
55EAS_ERROR_NO_VIRTUAL_SYNTHESIZER = -32
56EAS_ERROR_FILE_ALREADY_OPEN = -33
57EAS_ERROR_FILE_ALREADY_CLOSED = -34
58EAS_ERROR_INCOMPATIBLE_VERSION = -35
59EAS_ERROR_QUEUE_IS_FULL = -36
60EAS_ERROR_QUEUE_IS_EMPTY = -37
61EAS_ERROR_FEATURE_ALREADY_ACTIVE = -38
62
63# special result codes
64EAS_EOF = 3
65EAS_STREAM_BUFFERING = 4
66
67# buffer full error returned from Render
68EAS_BUFFER_FULL = 5
69
70# file types
71file_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
91stream_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
104IMODE_PLAY_ALL = 0
105IMODE_PLAY_PARTIAL = 1
106
107# callback type for metadata
108EAS_METADATA_CBFUNC = CFUNCTYPE(c_int, c_int, c_char_p, c_ulong)
109
110# callbacks for external audio
111EAS_EXT_PRG_CHG_FUNC = CFUNCTYPE(c_int, c_void_p, c_void_p)
112EAS_EXT_EVENT_FUNC = CFUNCTYPE(c_int, c_void_p, c_void_p)
113
114# callback for aux mixer decoder
115EAS_DECODER_FUNC = CFUNCTYPE(c_void_p, c_void_p, c_int, c_int)
116
117# DLL path
118if OsWindows():
119	EAS_DLL_PATH = "EASDLL.dll"
120else:
121	EAS_DLL_PATH = "libEASLIb.dylib"
122
123eas_dll = None
124
125# logger
126eas_logger = None
127
128#---------------------------------------------------------------
129# InitEASModule
130#---------------------------------------------------------------
131def 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#---------------------------------------------------------------
151class S_JET_CONFIG (Structure):
152	_fields_ = [('appLowNote', c_ubyte)]
153
154#---------------------------------------------------------------
155# S_EXT_AUDIO_PRG_CHG
156#---------------------------------------------------------------
157class 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#---------------------------------------------------------------
165class 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#---------------------------------------------------------------
174class 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#---------------------------------------------------------------
184class 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#---------------------------------------------------------------
195class 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
207severity_mapping = (logging.CRITICAL, logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG)
208LOG_FUNC_TYPE = CFUNCTYPE(c_int, c_int, c_char_p)
209def Log (level, msg):
210	eas_logger.log(severity_mapping[level], msg)
211	return level
212LogCallback = LOG_FUNC_TYPE(Log)
213
214#---------------------------------------------------------------
215# EAS_Stream
216#---------------------------------------------------------------
217class 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"""
326EAS_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#---------------------------------------------------------------
332class 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#---------------------------------------------------------------
509class 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#---------------------------------------------------------------
536class 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#---------------------------------------------------------------
550class 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#---------------------------------------------------------------
918class 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#---------------------------------------------------------------
1008class 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#---------------------------------------------------------------
1021class 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#---------------------------------------------------------------
1030class 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