eventList.py :  » Media-Sound-Audio » athenaCL » athenaCL » libATH » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Media Sound Audio » athenaCL 
athenaCL » athenaCL » libATH » eventList.py
#!/usr/local/bin/python
#-----------------------------------------------------------------||||||||||||--
# Name:         eventList.py
# Purpose:      manages Event object types; EventMode, OutputEngines.
#
# Authors:      Christopher Ariza
#
# Copyright:    (c) 2004-2006 Christopher Ariza
# License:      GPL
#-----------------------------------------------------------------||||||||||||--
import os, time, random, copy, array

from athenaCL.libATH import drawer
from athenaCL.libATH import language
lang = language.LangObj()
from athenaCL.libATH import midiTools
from athenaCL.libATH import pitchTools
from athenaCL.libATH import osTools
from athenaCL.libATH import audioTools
from athenaCL.libATH import outFormat
from athenaCL.libATH.libOrc import orc
from athenaCL.libATH.libPmtr import basePmtr
from athenaCL.libATH.omde import bpf# needed for interpolation

_MOD = 'eventList.py'

#-----------------------------------------------------------------||||||||||||--
# eventModes have a single, default orc object that they work wiht
# sometimes orc objects need to be created w/o the event list object
# and from the eventList objects name


# eventModes: define a type of scoring, incluring what instruments are 
# represented (orc def)
eventModeNames = {
   'cn'    :'csoundNative' ,
   'ce'    :'csoundExternal' ,
   'cs'    :'csoundSilence' ,
   'm'     :'midi',
   'mp'    :'midiPercussion',
   }

# output engines may handle a single output format, or multiple 
# every event mode has certain engines always associated with it
# when using one even mode, other outputs can be requested
# engines concert singel part (texture, clone) data into a polyphonic
# representation
outputEngineNames = [
   'EngineAudioFile',
   'EngineCsoundNative',  # .csd, .sco, .orc, .bat
   'EngineCsoundSilence', # .sco only 
   'EngineCsoundExternal', # .sco only 
   'EngineMaxColl',
   'EngineAcToolbox',
   'EngineMidiFile',
   'EngineText',
   ]


def eventModeParser(typeName):
   """utility functions for parsing user paramter strings into proper
   parameter names. accepts short names and long names, regardless of case
   does not raise an error if no match: returns string unmodified
   """
   parsed = drawer.acronymExpand(typeName, eventModeNames)
   if parsed == None: pass
   return parsed


#-----------------------------------------------------------------||||||||||||--
   
# each eventMode only uses one type of orchestra
# the same orchestra can be used for in multiple event modes
def selectEventModeOrc(emName):
   """for a given emName, supply the appropriate orchestra name
   most often, these will be the same; but two different eventLists may 
   want to sare the same orhestra"""   
   emName = eventModeParser(emName)
   assert emName in eventModeNames.values() and emName != None
   if emName == 'csoundNative':
      return 'csoundNative'
   elif emName == 'csoundSilence':
      return 'csoundSilence'
   elif emName == 'csoundExternal':
      return 'csoundExternal'
   elif emName == 'midi':
      return 'generalMidi'
   elif emName == 'midiPercussion':
      return 'generalMidiPercussion'

# each outputEngine may use only one orchestra, or variable orchestras
# in some cases the emName will determine the orc used w/ an engine
# in other cases, the emName has nothing to do w/ what orc is used
def selectOutEngineOrc(emName, oeName):
   """for a given eventMode and outputEngine, determine best orchestra"""
   assert emName in eventModeNames.values() and emName != None
   if oeName not in outputEngineNames:
      raise ValueError, 'bad Output name: %s' % oeName   
   if oeName == 'EngineAudioFile':
      return 'generic' # use generic orc only for conversions
   elif oeName == 'EngineCsoundNative':
      return 'csoundNative'
   elif oeName == 'EngineCsoundSilence':
      return 'csoundSilence'   
   elif oeName == 'EngineCsoundExternal':
      return 'csoundExternal'   
   elif oeName == 'EngineMaxColl':
      # macColl assumes midi values for all parameters
      return 'generalMidi'
   # treate acToolbox the same as midiFile
   elif oeName in ['EngineMidiFile', 'EngineAcToolbox']:
      if emName in ['midi', 'midiPercussion']:
         return selectEventModeOrc(emName) # will get gm or gmPercussion
      else: # event mode may be something other than a midi variant
         return 'generalMidi' # use midi in all general cases
   elif oeName == 'EngineText':
      return selectEventModeOrc(emName) # get orc of this event mode
   else:
      raise ValueError, 'no such ouput Engine name'




#-----------------------------------------------------------------||||||||||||--
class EventSequence:
   """data representation of event lists; stores all event data 
   for a single texture or clone; a clone will process this object into
   another object

   designed to deal w/ event dictionaries
   event dictionaries can have the following keys (defined in baseTexture):
      eventDict['inst'] = inst
      eventDict['time'] = tCurrent
      eventDict['sus'] = sus # real sustain value
      eventDict['dur'] = dur # not actually used to calc dur, as next t avail
      eventDict['acc'] = acc # may be 0 if silences retained
      eventDict['bpm'] = bpm  # store bpm
      eventDict['pulse'] = pulse  # store string repr of pulse object
      eventDict['amp'] = amp
      eventDict['ps'] = ps
      eventDict['pan'] = pan
      eventDict['aux'] = auxiliary # a list of data, variable size
      eventDict['comment'] # a list
      
   eventData: stores meta data about event list;
      tAbsStart, tAbsEnd
   """
   def __init__(self):
      self._eventList = [] # a list of event dictionaries, perhaps un sorted
      # can store: tStart, tEnd, ampMax
      self._eventData = {} # dictionary that store attribute data

   #------------------------------------------------------------------------||--
   # built in methods
   def append(self, eventDict):
      """append an event dictionary"""
      self._eventList.append(eventDict)

   def clear(self):
      self._eventList = []

   def __len__(self):
      return len(self._eventList)
      
   def keys(self):
      # return event inex positions form 0
      return range(0, len(self._eventList))

   def __getitem__(self, key):
      """numbers are index keys, return event dict at this location"""
      return self._eventList[key]

   def __setitem__(self, key, value):
      self._eventList[key] = value

   def __delitem__(self, key):
      del self._eventList[key]

   def copy(self):
      esObj = EventSequence()
      # this will not work w/ arrays
      esObj._eventList = copy.deepcopy(self._eventList)
      esObj._eventData = copy.deepcopy(self._eventData)
      return esObj
      
   def sort(self):
      """sort the event list in place according to time values
      called automatically w/ updatePost; called after texture creation
      do not do this always: may be a significant time suck"""
      alt = []
      tRef = []
      for i in range(self.__len__()):
         # store start time, index in source list
         tRef.append((self._eventList[i]['time'], i))
      tRef.sort() # sort according to time
      for t, i in tRef: # reappend dictionaries
         alt.append(self._eventList[i])
      self._eventList = alt # assign to eventList
      
   #------------------------------------------------------------------------||--
   # data access and loading

   def list(self):
      """return a reference to the event list
      not a copy; only used for translations"""
      return self._eventList
      
   def meta(self, key):
      """get meta data from an event sequence object"""
      return self._eventData[key]
      
   def getArray(self, name):
      """get a copy of all values from the event list as an array"""
      data = []
      for event in self._eventList:
         data.append(event[name])
      return data

   def setArray(self, name, data):
      """load all values from the event list as an array"""
      assert len(data) == len(self._eventList)
      for i in range(0, len(data)):
         self._eventList[i][name] = data[i] 


   #------------------------------------------------------------------------||--
   # updates and other management
   def updatePre(self):
      # called in before before scoreing
      # shoudl be used to clear the score?
      pass

   def _updateTimeRangeAbs(self):
      # this assumes that events  are sorted...
      tArray = self.getArray('time')
      susArray = self.getArray('sus')
      if len(tArray) == 0: # uncommon, but happens w/ no events
         min = 0
         max = 0
      else:
         # scroll through all values and find largest combination
         min = tArray[0] # possible values by order, may not be right
         max = tArray[-1] + susArray[-1]
         for x in range(len(tArray)):
            if tArray[x] <= min: min = tArray[x]
            if tArray[x] + susArray[x] >= max:
               max = tArray[x] + susArray[x]
      self._eventData['tAbsStart'] = min
      self._eventData['tAbsEnd'] = max

   def updatePost(self):
      # called in baseTexture in post score
      #self._updateMaxAmp()
      self._updateTimeRangeAbs()
      # note: events used to be sorted here, now make sorting optional

   def getTimeRangeAbs(self):
      return  self._eventData['tAbsStart'],  self._eventData['tAbsEnd']

   def getTotalDuration(self):
      return  self._eventData['tAbsEnd'] - self._eventData['tAbsStart']
   

   #------------------------------------------------------------------------||--
   # transformations to the event sequence

   def interpolate(self, tFrameArray, snapSus, 
                   active=['time', 'acc', 'bpm', 'amp', 'ps', 'pan', 'aux']):
      """givena tFrameArray, create interpolated events between existing events
      tFrameArray: tStart, dur, eventFlag, interpMethod, interpExponet
         # event frames are len==5, others do not have interpExponet
      exponet can only be updated for each event"""


      # get a list of index values for events within tFrameArray
      eventFrameIndex = []
      for i in range(len(tFrameArray)):
         if tFrameArray[i][2] == 1: # check event status flag
            eventFrameIndex.append(i)
      
      if self.__len__() != len(eventFrameIndex):
         raise ValueError, 'tFrameArray does not contain all events stored: %s, %s' % (self.__len__(), len(eventFrameIndex))

      # iterate over all indexes less 1, as two are done at a time
      for i in range(self.__len__()-1):
         eStart = self._eventList[i]
         eEnd = self._eventList[i+1]

         # range of frames to create is between event frames start and end
         frameStart = eventFrameIndex[i] + 1
         frameEnd = eventFrameIndex[i+1] - 1

         # prepare list of parameters to clone in a dictionary
         # always include aux list, as some vals may not be numbers
         eventTemplate = {}
         for key in eStart.keys():
            if key not in active or key == 'aux':
               eventTemplate[key] = copy.deepcopy(eStart[key])
            

         # create a dict of bpl objs to acces when creating frames
         # start and end times should be of events, not frames
         tStart = tFrameArray[eventFrameIndex[i]][0]
         tEnd = tFrameArray[eventFrameIndex[i+1]][0]
         # these values are only found w/ event frames
         interpolationMethod = tFrameArray[eventFrameIndex[i]][3]
         exponent = tFrameArray[eventFrameIndex[i]][4]

         bpArray = {}
         for pmtr in active:
            if pmtr == 'aux': continue

            valStart = eStart[pmtr]
            valEnd = eEnd[pmtr]
            boundaryPair = [(tStart, valStart), (tEnd, valEnd)]
   
            # create segment
            if interpolationMethod == 'linear': 
               bp = bpf.LinearSegment(boundaryPair)
            elif interpolationMethod == 'halfCosine': 
               bp = bpf.HalfCosineSegment(boundaryPair)
            elif interpolationMethod == 'power': 
               bp = bpf.PowerSegment(boundaryPair, exp=exponent)
            bpArray[pmtr] = bp

         # handle aux by creating a list of lists
         if 'aux' in active:
            pmtr = 'aux'   
            valStartArray = eStart[pmtr]
            valEndArray = eEnd[pmtr]
            # create a list w/n bpArray to append w/n
            bpArray[pmtr] = []

            # iterate over every aux slot in the source aux list
            for iAux in range(len(eStart[pmtr])):
               # some aux values may not be numbers
               if not drawer.isNum(valStartArray[iAux]):
                  bpArray[pmtr].append(None)
                  continue # use vals in template

               boundaryPair = [(tStart, valStartArray[iAux]),
                               (tEnd, valEndArray[iAux])]
      
               # create segment
               if interpolationMethod == 'linear': 
                  bp = bpf.LinearSegment(boundaryPair)
               elif interpolationMethod == 'cosine': 
                  bp = bpf.HalfCosineSegment(boundaryPair)
               elif interpolationMethod == 'power': 
                  bp = bpf.PowerSegment(boundaryPair, exp=exponent)
               bpArray[pmtr].append(bp)


         # create frames using eventTemplate and acccessing values from bpArray
         for frameIndex in range(frameStart, frameEnd+1):
            # use template to store all non-interpolated values
            eventDict = copy.deepcopy(eventTemplate)
            tFrameStart = tFrameArray[frameIndex][0]
            tFrameDur = tFrameArray[frameIndex][1]
            for pmtr in active:
               if pmtr == 'aux': continue
               eventDict[pmtr] = bpArray[pmtr](tFrameStart)
            if 'aux' in active:
               pmtr = 'aux'   
               for iAux in range(len(eStart[pmtr])):
                  # keep value set in template
                  if bpArray[pmtr][iAux] == None: continue
                  eventDict[pmtr][iAux] = bpArray[pmtr][iAux](tFrameStart)

            if snapSus: # will override template value
               eventDict['sus'] = tFrameDur
            # add eventDictionary to this eventList
            self.append(eventDict)

         # if snap sus, alter sus of start and end events
         if snapSus:
            # get first frames start time, substract from event
            eStart['sus'] = tFrameArray[frameStart][0] - eStart['time']
            # ending event is not adjusted

      # sort after processing
      self.sort()

   
   def retrograde(self, type):
      """perform retrograde transformations directly on an event list
      note: assumes that events are sorted
      
      eventInverse: simply reverse the order of all events, using the
         forward time values w/ the reverse events
         keep reverse sustain values
      timeInverse: reverse all events,
         time values are calculted from difference between forward events 
         bkwards
         sustain values are reversed
      example, time, dur values
      
      eventSequence: eventInverse
      src(t,d) 100 3  post(t,d) 100 7
      src(t,d) 103 13 post(t,d) 103 7
      src(t,d) 116 7  post(t,d) 116 13
      src(t,d) 123 7  post(t,d) 123 7
      src(t,d) 130 13 post(t,d) 130 7
      src(t,d) 143 7  post(t,d) 143 13
      src(t,d) 150 7  post(t,d) 150 3
      
      eventSequence: timeInverse
      src(t,d) 100 1  post(t,d) 100 13
      src(t,d) 101 1  post(t,d) 113 7
      src(t,d) 102 3  post(t,d) 120 13
      src(t,d) 105 13 post(t,d) 133 3
      src(t,d) 118 7  post(t,d) 136 1
      src(t,d) 125 13 post(t,d) 137 1
      src(t,d) 138 3  post(t,d) 138 1
      """
      # creat index arrays
      iForward = range(self.__len__())
      iReverse = copy.deepcopy(iForward)
      iReverse.reverse()

      if type in ['off', None]:
         return None # do nothing
      elif type == 'eventInverse':
         alt = copy.deepcopy(self._eventList) # 1,2,3
         alt.reverse() # reverse events  3,2,1
         for i in iForward: # apply # 1,2,3
            # events are reversed; go throught them in order and apply
            # old time values in order from source
            t = self._eventList[i]['time']
            alt[i]['time'] = t
      elif type == 'timeInverse':
         alt = [] # 1,2,3
         altDur = 0 # time between events
         t = copy.copy(self._eventList[0]['time']) # get first event
         for i in iForward:
            j = iReverse[i]
            
            if i != iForward[-1]: # if not the last
               # get the duration from the last event to the one before it
               altDur = abs(self._eventList[j]['time'] - 
                         self._eventList[j-1]['time'])
            else: # last index
               altDur = self._eventList[j]['dur']
            # get reverse event
            event = copy.deepcopy(self._eventList[j])
            event['time'] = t # apply current time
            event['dur'] = altDur # apply newly calculated duration
            alt.append(event)
            # increment time
            t = t + altDur

      self._eventList = alt # reassign to eventList
         
   #------------------------------------------------------------------------||--
   # format conversions
   def fillSplitScore(self, splitScore):
      """a split score stores all data in parallel lists
      note: attribute names in the split score follow internal texture
      naming, not event dict naming"""
      iEvent = 0
      delKey = []
      for event in self._eventList:
         splitScore['event'].append(iEvent)
         splitScore['time'].append(event['time'])
         splitScore['beatT'].append(event['bpm']) # called bpm here
         splitScore['sus'].append(event['sus'])
         splitScore['acc'].append(event['acc'])
         splitScore['ampQ'].append(event['amp'])
         splitScore['panQ'].append(event['pan'])
         splitScore['ps'].append(event['ps'])
         for i in range(0, len(event['aux'])): # may be a string!
            val = event['aux'][i]
            key = 'auxQ%i' % i
            splitScore[key].append(val)
         iEvent = iEvent + 1 # increment event count
      #print _MOD, 'fillSplitScore keys', splitScore.keys()
      return splitScore






#-----------------------------------------------------------------||||||||||||--
class EventSequenceSplit:
   """object for storing data for a texture, clone, or parameter
   in separate channels; output provided for graphing
   used for TImap, TCmap, TPmap

   split score holds data for either pre or post texture pmtr data
      or raw pmtr data provided w/ pmtr objects
   not data values are used all the time

   for textures, designed to work with an eventSequence object to get data
   event is index position (0 start),

   a split score can output with four possibilities
   tmRelation: pre/post  if the data values are pre tm, or post tm
   xRelation: event/time if x axis is either event based or time based
   with a clone, only a postTM relation is possible

   a srcObj may be a list of label, pmtrObj pairs
   
   will always ignore non-numeric data types
   
   arrays used for speed optimation of floating point values
   
   need aoInfo such as ssdr and sadr to get file paths
   this is not normally stored in ao.aoInfo, but packed here in command.py
   """
   def __init__(self, srcObj, srcFmt='t', eventCount=None, refresh=0,\
                aoInfo=None):
      """srcFmt is either t (texture) or c (clone)
      or pg or pf, parameter generator or parameter filter"""
      self.srcObj = srcObj # a list of lable, pmtrObj apurs
      self.srcFmt = srcFmt
      # can be used to limt number of events shown
      # only implemented for pmtr event sequences (pg, pf, pr)
      #self.eventCount = eventCount 
      self.nEvent = eventCount # written w/ loadScoreList
      self.refresh = refresh
      self.aoInfo = aoInfo # may ne None, only needed to get paths
      # determine if strings skipped
      self.strBypass = 1 # set with load() method, default is to skip

      # get aux no if necessary
      if self.srcFmt in ['pg', 'pf', 'pr']:
         self.auxNo = 0
      elif self.srcFmt in ['t', 'c']:
         self.auxNo = self.srcObj.auxNo # must be clone or texture
         if self.srcFmt == 'c':
            if len(self.srcObj.esObj) == 0:
               raise ValueError, 'clone with empty esObj cannot be graphed' 

      # all data elements other than aux values could be array objects

      # build template for split scire
      splitScore = {}

      # comm to all formats
      # eventCount can be unsigned longs
      splitScore['time'] = array.array('f')  
      splitScore['event'] = array.array('L') # indicies for each event
      # textures and clones require a complete represenation
      if self.srcFmt in ['t', 'c']: # a complete texture rep
         # data valyes; some not always available
         splitScore['beatT'] = array.array('f') #bpm  
         splitScore['dur'] = array.array('f')  
         splitScore['sus'] = array.array('f')  
         splitScore['acc'] = array.array('f')
         splitScore['ps'] = array.array('f') # ps real value
         splitScore['fieldQ'] = array.array('f')  # not in list score  
         splitScore['octQ'] = array.array('f') # not in list score  
         splitScore['ampQ'] = array.array('f')   
         splitScore['panQ'] = array.array('f')  
         for i in range(0, self.auxNo):
            splitScore['auxQ%i'%i] = [] 
      # raw parameter data only stores two values of any type
      elif self.srcFmt in ['pg', 'pf']:
         for label, lib, obj in self.srcObj:
            splitScore[label] = []
      # rhythms stores a speicial arrangement as well
      elif self.srcFmt in ['pr']: # rhythm returns 3 values
         splitScore['sus'] = array.array('f')
         splitScore['dur'] = array.array('f')
         splitScore['acc'] = array.array('f')

      # store order of presentation
      if self.srcFmt == 't': # texture
         self.pmtrOrder = ['time', 'beatT', 'acc', 'dur', 'sus', 'ps', 'fieldQ',
                           'octQ', 'ampQ', 'panQ']
         for label in basePmtr.auxLabel(self.auxNo):
            self.pmtrOrder.append(label)
      elif self.srcFmt == 'c': # clone; dont show bpm, dur, frm texture
         self.pmtrOrder = ['time', 'sus', 'acc', 'ps', 'fieldQ',
                           'octQ', 'ampQ', 'panQ']
         for label in basePmtr.auxLabel(self.auxNo):
            self.pmtrOrder.append(label)
      elif self.srcFmt in ['pg', 'pf']: # parameter generator, filter
         self.pmtrOrder = []
         for label, lib, obj in self.srcObj:
            self.pmtrOrder.append(label)
      elif self.srcFmt in ['pr']: # parameter rhythm
         self.pmtrOrder = ['dur', 'sus', 'acc']
      else:
         raise ValueError, 'bad src format'

      # store labels for each parameter
      self.pmtrArgs = {}
      for pmtr in self.pmtrOrder:
         self.pmtrArgs[pmtr] = ''
      self.splitScore = splitScore

   #------------------------------------------------------------------------||--
   def _updateEventCount(self):
      self.nEvent = len(self.splitScore['event']) # store total number of events

   def _loadPmtrArgs(self):
      """get parameter data from srcObj
      also check for string types and remove them from pmtrArgs and pmtrOrder
      """
      delKey = []
      if self.srcFmt not in ['pg', 'pf', 'pr']: # parameter texture
         for key in self.srcObj.pmtrObjDict.keys():
            pObj = self.srcObj.pmtrObjDict[key]
            if pObj.outputFmt == 'str' and self.strBypass:
               delKey.append(key)
               continue # skip string outputs
            self.pmtrArgs[key] = pObj.repr('argsOnly')
      elif self.srcFmt in ['pg', 'pf']: # filter, generator
         for label, lib, pObj in self.srcObj:
            if pObj.outputFmt == 'str' and self.strBypass:
               delKey.append(label)
               continue # skip string outputs
            self.pmtrArgs[label] = pObj.repr('argsOnly')
      elif self.srcFmt in ['pr']: # rhythm
         label, lib, pObj = self.srcObj[0] # first is only significant
         self.pmtrArgs['dur'] = pObj.repr('argsOnly')
         self.pmtrArgs['sus'] = ''
         self.pmtrArgs['acc'] = ''
      # remove bad keys
      for key in delKey:
         self.pmtrOrder.remove(key)

   def _loadPostScore(self):
      """creates a new score w/ srcObj, 
      splits a score into a dictionary of labeled parameters values
      xLabel can be event or time; changes x label from time values to 
      event steps"""
      # use existing score if possible
      if len(self.srcObj.esObj) != 0 and self.refresh != 1:
         esObj = self.srcObj.getScore()
      else: # always do a post process to fill time values and event count
         # if this is a clone, scores needs data from texture
         ok = self.srcObj.score()
         if ok == -1 or self.srcObj.checkScore() == 0:
            raise ValueError, 'bad score generation' # error      
         esObj = self.srcObj.getScore()
      # uses esObj method with input obj of split score to update
      # split score may be empty here
      self.splitScore = esObj.fillSplitScore(self.splitScore)

   def _loadPreScore(self):
      """suply a srcObj instance and load fresh parameter data
      may overwrite some data obtained w/ loadScoreList
      note: preScore cannot be calculated from a clone"""
      for key in self.srcObj.pmtrObjDict.keys():
         if key not in self.pmtrOrder: # only parameters that are here
            continue
         pObj = self.srcObj.pmtrObjDict[key]
         if pObj.outputFmt == 'str': # cant do anything w/ strings
            del self.pmtrOrder[self.pmtrOrder.index(key)]
            continue
         self.splitScore[key] = [] # clear list, replace w/ new values
         pObj.reset() # reset parameter object
         for t in self.splitScore['time']: # run through time list
            self.splitScore[key].append(pObj(t)) # append data values
            
   def _loadRawParameter(self):
      """srcObj is a list of raw parameter objects
      optionally skip string storage with strBypass"""
      # add time values
      refDict = basePmtr.REFDICT_SIM
      # merge with aoInfo if around
      if self.aoInfo != None: # fill blank entries
         refDict['sadr'] = self.aoInfo['sadr'] # a list of paths
         refDict['ssdr'] = self.aoInfo['ssdr']
      
      # these are the same; why have both?
      self.splitScore['time'] = range(0, self.nEvent)
      self.splitScore['event'] = range(0, self.nEvent)

      for label, lib, pObj in self.srcObj:
         # check if strings are to be processed
         if pObj.outputFmt == 'str' and self.strBypass:
            continue
         self.splitScore[label] = [] # clear list, replace w/ new values
         pObj.reset() # reset parameter object
         if lib in ['genPmtrObjs']:
            for t in self.splitScore['time']: # run through time list
               # append data values
               self.splitScore[label].append(pObj(t, refDict)) 
         elif lib in ['filterPmtrObjs']:
            # create a dummy ref dict array
            refDictArray = [refDict] * self.nEvent
            # first element in srcObj list must be a general pmtr obj
            valArray = self.splitScore[self.srcObj[0][0]] # this gets label
            tArray = self.splitScore['time']
            self.splitScore[label] = pObj(valArray, tArray, refDictArray)
         elif lib in ['rthmPmtrObjs']:
            for t in self.splitScore['time']: # run through time list
               dur, sus, acc = pObj(t, refDict)
               self.splitScore['dur'].append(dur)
               self.splitScore['sus'].append(sus)
               self.splitScore['acc'].append(acc)

   #------------------------------------------------------------------------||--         
   def load(self, tmRelation='pre', strBypass=1):
      self.strBypass = strBypass # will skip all pmtr args w/ string output
      # load all parameter args depending on srcFmt set at init
      self._loadPmtrArgs()
      if self.srcFmt not in ['pg', 'pf', 'pr']: # parameter texture
         self._loadPostScore() # always do first
         if tmRelation == 'pre' and self.srcFmt != 'c': # not clone
            self._loadPreScore()
      else:
         self._loadRawParameter()
      self._updateEventCount()

   def clean(self):
      """scrub the split score for bad data, such as strings"""
      delKeys = []
      for pmtr in self.pmtrOrder:
         if len(self.splitScore[pmtr]) == 0: # no data
            delKeys.append(pmtr)
         elif drawer.isStr(self.splitScore[pmtr][0]):
            delKeys.append(pmtr)
      for pmtr in delKeys:
         del self.splitScore[pmtr]
         del self.pmtrArgs[pmtr]
         self.pmtrOrder.remove(pmtr) # this is just a list
         
   def getTitle(self, pmtrLabel):
      """the title gotten here is used for graphics, and, with args is often too 
      long for a graphical display; thus, now just returning title w/o args"""
      pmtrStr = None # po name if available
      if self.pmtrArgs[pmtrLabel] != '':
         pmtrStr = self.pmtrArgs[pmtrLabel].split(',')[0] # first item is name
         if self.srcObj != None and not drawer.isList(self.srcObj):
            junk, fullPmtr = self.srcObj.decodePmtrName(pmtrLabel, 'usr')
            if fullPmtr != '': # assign fullPmtr name
               pmtrLabel = fullPmtr
      # create title strings
      if pmtrStr != None: # default, optionally add more info
         title = '%s: %s' % (pmtrLabel, pmtrStr) 
      else:
         title = '%s' % (pmtrLabel) 
      return title

   def getCoord(self, pmtr, xRelation='event'):
      """get a data format as a list of coordinant pairs
      xRelation can be event or time
      """
      if self.splitScore[pmtr] == []: # no data
         return None # no data 
      coord = []
      xStep = self.splitScore[xRelation] # event or time
      i = 0
      for x in xStep: # add data plus xRelation to creat coord pairs
         if xRelation == 'event':
            coord.append((x, self.splitScore[pmtr][i]))
         elif xRelation == 'time':
            # add four data points to coord
            # y values are constant
            # x value is initial x plus sustain
            coord.append((x, self.splitScore[pmtr][i], 
                          x + self.splitScore['sus'][i], 
                          self.splitScore[pmtr][i]))
         i = i + 1
      return coord

   def getKeys(self):
      """get keys that have values,ignoring other keys
      some keys may not have values if they are pre
      used in drawin the split score
      """
      filter = [] # filtering not necessary if called after clean() method
      for pmtr in self.pmtrOrder:
         if len(self.splitScore[pmtr]) != 0: # no data
            filter.append(pmtr)
      return filter

   #------------------------------------------------------------------------||--
   # event engines are polyphonic; these outputs are monophonic, or single
   # generator values; only provided output for one parameter objects
   
   def writeTable(self, filePath, sepStr='\t'):
      """return a tab-delmited table of all data in the split score
      format is a list of lines ready for writing
      this is used by TPexp to export parameter data quickly
      from events generated by an EventSequenceSplit
      """
      msg = []
      for key in self.getKeys(): # this gets pmtr obj groups, or arg slots
         dataLine = [self.getTitle(key)]
         for data in self.splitScore[key]:
            dataLine.append(str(data))
         msg.append(sepStr.join(dataLine))
         msg.append('\n')
      f = open(filePath, 'w')
      f.writelines(msg)
      f.close()

   def writeBuffer(self, filePath):
      """write and audio file from data 
      """
      # args are fp, ch, sr
      self.aObj = audioTools.AudioFile(filePath, 1)
      # this will mix all parameters into one smashed representation
      if self.srcFmt == 'pg':
         for key in self.getKeys():
            if key.lower().startswith('g'):
               # values may not be normalized
               self.aObj.insertMix(0, self.splitScore[key])
      elif self.srcFmt == 'pf':
         for key in self.getKeys():
            if key.lower().startswith('f'):
               # values may not be normalized
               self.aObj.insertMix(0, self.splitScore[key])         
      else:
         raise ValueError, 'unexpected src fmt'


#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
class Performer:
   """used only by EventMode
   can provide complete scores of all textures as flat data obj
   or can step through a score as a rt performnace
   does contain objects, and thus cannot be store 
      (this is unlike an esObj, which is simple a data structure)"""
   def __init__(self):
      self.polySeq = {} # data representation of calculated score      
      self.scoreCount = 0 # number of textures processed
      # no longer store in performer
      # determined by Engine in compatability check
      #self.instList = [] # list of instruments used

   def reset(self):
      self.polySeq = {}
      self.scoreCount = 0 # number of textures processed

   def sort(self):
      """sort all event lists; only called by EventMode"""
      #print _MOD, 'Performer: sorting all EventSequences' 
      for t in self.polySeq.keys(): # get texture names
         self.polySeq[t]['esObj'].sort()
         for c in self.polySeq[t]['TC'].keys():
            self.polySeq[t]['TC'][c]['esObj'].sort()
      
   def _packTexture(self, tName, t):
      """store texture data, encluding an eventList, in a dictionary
      store relevant data for engine processing"""
      self.polySeq[tName] = {} # a specialized dictionary
      self.polySeq[tName]['esObj'] = t.getScore() # store esObj copy
      self.polySeq[tName]['mute'] = t.mute
      self.polySeq[tName]['className'] = t.__module__
      self.polySeq[tName]['orcObj'] = t.getOrc() # ref to obj
      self.polySeq[tName]['inst'] = t.getInst() # get inst number
      self.polySeq[tName]['midiPgm'] = t.midiPgm
      self.polySeq[tName]['midiCh'] = t.midiCh # this is from 1 to 16
      self.polySeq[tName]['orcMapMode'] = t.orcMapMode # store mix mode
      self.polySeq[tName]['TC'] = {}
      self.scoreCount = self.scoreCount + 1 # only count if score successfull

   def _packClone(self, tName, cName, c):
      """store clone data, encluding an eventList, in a dictionary
      stored as part of clone's parent texture
      """
      self.polySeq[tName]['TC'][cName] = {}
      self.polySeq[tName]['TC'][cName]['esObj'] = c.getScore() # esObj
      self.polySeq[tName]['TC'][cName]['mute'] = c.mute
      self.scoreCount = self.scoreCount + 1 # count clone scores 

   # note: textures and clones already may have scores;
   # refreshing the scores here makes sense, but may not always be necessary
   def flattenSome(self, objList, refresh=1):
      """flatten reads and scores textures into a polySeq data dict
      score a polySeq, but only for objects that are in the list
      objects must be textures; clones cannot be scored this way"""
      self.reset()
      # use number as tName
      for i in range(0, len(objList)):
         tName = str(i)
         t = objList[i]
         #inst = t.getInst()
         #if inst not in self.instList: self.instList.append(inst)
         if refresh:
            ok = t.score() #returns -1 if score fails
            if not ok or t.checkScore() == 0:
               print _MOD, 'texture failed to score', tName
               continue
         self._packTexture(tName, t)

   def flattenAll(self, ao, refresh=1):
      """flatten reads and scores textures into a polySeq data dict
      generate a flat score, stored in polySeq, for all textures + clones
      in the athenaObject
      creates self.polySeq, a structured dictionary of score data
      score data is stored as an esObjuence obj
      the same format produced
      w/ each textures .score() method.
      must get scores for all texture and clones, even if muted
         a texture can be muted while a clone is not, so all data must be gen
      """
      textureLib = ao.textureLib
      cloneLib = ao.cloneLib
      self.reset()
      for tName in textureLib.keys():
         t = textureLib[tName]
         #inst = t.getInst()
         #if inst not in self.instList: self.instList.append(inst)
          # must always score, even if muted, so clone can be generated
         if refresh:
            ok = t.score() #returns -1 if score fails
            if not ok or t.checkScore() == 0:
               print _MOD, 'texture failed to score', tName
               continue
         self._packTexture(tName, t)
         # get necessary inputs for clones
         refDict = t.getRefClone()
         esObjTexture = self.polySeq[tName]['esObj'] 
         for cName in cloneLib.cNames(tName):
            c = cloneLib.get(tName, cName)
            # create a clone score w/ esObj from texture
            # a copy will be made w/n the cone
            if refresh:
               ok = c.score(esObjTexture, refDict)
               if not ok:
                  print _MOD, 'clone failed to score', tName, cName
                  continue
            self._packClone(tName, cName, c)


#-----------------------------------------------------------------||||||||||||--
# engines can be used at any time from the event mode, in any mode
# engines support a certain number of outputs
# and always have a default output
# some engines are the same as an EventMode, but not all 
# engines may write more than one file (csoundNative for instance)

# engines deal with the polyphonic nature of event lists, as represented
# in the poly sequnece; it may need to be converted to tracks, commented
# or other wise separated, etcetera

# there are engines that can work w/ any orchestra (text) and are thus
# independent of orchestas; each engine, however, requires an orchesta be 
# assigend to it at some point. in some cases, one engine may only work
# with one orchestra (csoundNative). in other cases, multiple (midiFile)

# there are engines that work w/ no orchestra: audioFile
# as no 'pitch' or 'pan' values are used, and no inst info is needed

class _OutputEngine:
   def __init__(self, emName, ref):
      """output engines: convert polySeq objects into files or streams?
      output engines are respoonsible for writing certain filestypes
      two engines cannot be used tt write the same filetype (.sco)
      
      each engine will have an internal orchestra
      this orchestra is determined by selectOutEngineOrc(), as well as 
      for converting mix values
      
      emName is the name of the currently active EventMode
      this may or may not effect which orchestra is used inside the engine
      for example, the text engine will always use the orc specified by em
      contrarywise, the csoundNative engine will always use its default orc
      
      ref is a ref dictionary with system value and strings needed in 
      processign; this is necessary to avoid passing an athenaObject to the 
      output object
      ref contains file paths for all necessary format based on str
      changes are made to ref to pass paths back to athenaObject
      
      engines store and modify a copy of a user provided outRequest
      engines may remove values from this list during _writePre
      if an engine is called with and empty outRequest, provide minimum
      
      write method does not deal w/ athenaObj, only with:
      ref (for paths) perfObj (passed with the write() method)      
      """
      self.ref = ref
      self.emName = emName # name of eventMode that is using this engine

      self.orcIncompat = [] # define orc sources tt are incompat w/ this engine
      self.orcObj = None # define name of orc used in converting mix vals
      # output data
      self.outComplete = [] # outpout formats that were written
      self.outRequest = [] # called with write operations
      self.outAvailable = None # provided w/ subclass
      self.outMin = None # provided w/ subclass
      # select which components work with a texutre
      # determined after assugment of perfObj and orcObj
      self.compatNames = [] # names of compat texutres
      self.compatInsts = [] # integer ids of compat insts
      
   def _outRequestCheck(self):
      """check to see tt at one of the engines outputs are specified
      if not, add to self.outRequest
      """
      outMinMatch = [] # scratch list to find outs that are in outavailable 
      if self.outRequest == []:
         self.outRequest = self.outMin
      else: # check to see tt output is one of the outputs available
         found = 0 
         for out in self.outRequest:
            if out in self.outAvailable:
               found = found + 1
            if out in self.outMin:
               outMinMatch.append(out)
         # must make sure that all min outs are provided
         if found == 0 or len(outMinMatch) < len(self.outMin):
            # add all minimums # these may not all get made (orc/sco if csd)
            for out in self.outMin:
               if out not in self.outRequest:
                  self.outRequest.append(out)
      #print _MOD, 'post outRequestCheck', self.outRequest
      
   def _orcSelect(self):
      """determine which orchestra should be used with this engine
      in most cases, if an engine is active, it will only use one orc
      (for example, csoundNative always uses csoundNative orc)
      in other cases, an engine might be able to use multiple orchestras
      (for example, the text engine can process w/ any orchestra)
      in these cases, the engine's orc is specified by the emName
      """
      # base eventMode name, as well as engine's name
      orcName = selectOutEngineOrc(self.emName, self.name)
      return orc.factory(orcName)
   
   def _orcCompatability(self, polySeq):
      """determine which textures are compatabile with orchestra of this engine
      assign self.compatTexture and self.compatInst
      each engine will only use textures that have compataible orcs and insts      
      """      
      # determine which orc to use; emOrcObj (current orc of mode)
      # or orc of texture; prefer orc of mode if compatible w orc of text
      # if not, use orc of texture
      
      self.compatNames = [] # names of compat texutres
      self.compatInsts = [] # integer ids of compat insts
      
      # only need polySequence here; no longer pass perfObj
      # polySeq = perfObj.polySeq
      
      for t in polySeq.keys(): # get texture names
         tOrcObj = polySeq[t]['orcObj']
         inst = polySeq[t]['inst'] # inst number as integer
         if tOrcObj.name == self.orcObj.name: # assume match, full compat
            self.compatNames.append(t)
            if inst not in self.compatInsts: 
               self.compatInsts.append(inst)
         else: # check if emOrc is compat w/ tOrc
            # see if tOrc is incompatabible w/ this Engine
            # inst may not be valid, but still work (midi files are an ex)
            if tOrcObj.name in self.orcIncompat:
               print lang.WARN, 'texture %s (instrument %s, orchestra %s) incompatible with OutputEngine orchestra %s; change EventMode or edit instrument.' % (t, inst, tOrcObj.name, self.orcObj.name)
            else: # use this texture anyways            
               self.compatNames.append(t)
               if inst not in self.compatInsts: 
                  self.compatInsts.append(inst)   
         # use this to test inst, but may not be necessary
         #if orcObj.instNoValid(inst): # check if inst no is valid
         # incompatible

   def _writePre(self):
      """look at output request and fix as necessary"""
      pass
      
   def _write(self):
      pass
   
   def _writePost(self):
      """update paths in ref as necessary"""
      pass
      
   def write(self, polySeq, outRequest=[]):
      """
      polySeq contains a dictionary of eventSequence objects
         when writing file, the appropriate outputs can be called from 
         the eventSequence
         from a perfObj has polySeq: flattened score representation
         perfObj not needed here
      the job of the output object is take all the converted eventSequence
         data and process is into the appropriate format, then write
         a file.
      outRequest is just a list of strings; not an array of objects as used
         elsewhere
      output request may or may include items in the output; if not
      default is always used
      """
      self.outRequest = copy.deepcopy(outRequest)
      # will suply minimum output if none in this engine specified
      self._outRequestCheck()
      # assign orc to be used with this engine, base on engine type and em
      
      # assign polySeq object from perfObject to a local varaiable 
      # for convenience
      # all engines use the same polySeq to ensure identical representations
      # do not change the polySeq, as may effect other output engines
      # an analysis of the polySeq should be performed before calling methods
      # below to determine which instrumetns are available:
      # each egine should store a list of valid instrument numbers and texture
      # names; thus each engine may filter the polySeq to only provide the
      # partial output
      self.polySeq = polySeq

      self.orcObj = self._orcSelect()
      # check compatability of this engine w/ its orc and included textures  
      # one orcObj will will be chosen for processing, assigned to self.orcObj
      # emOrcObj not compatabile w/ texture orc
      # also used for converting amp and pan values (mixAbs)      
      self._orcCompatability(self.polySeq)
      # assugn emOrcObj to orcObj for engine processing
      if self.compatNames == []: # no compatible textures
         return None # bail
         
      # self.orcObj = emOrcObj # assign orc of event mode?
         
      # call subclass methods to write output files of this engine
      self._writePre()
      self._write()
      self._writePost()
      
   #------------------------------------------------------------------------||--
   # string and data processing conversion
   
   def _fmtHeadSco(self, headStr='', prepend=''):
      scoHead = []
      scoHead.append('%sathenaCL %s\n' % (prepend, self.ref['version']))
      scoHead.append('%s%s\n' % (prepend, lang.msgAthURL))
      timeStr = time.asctime(time.localtime())
      scoHead.append('%s%s\n' % (prepend, timeStr))
      scoHead.append('%soutput generator: %s (orchestra: %s)\n\n' % (prepend, 
         self.name, self.orcObj.name))
      if headStr != '':
         scoHead.append(headStr)
         scoHead.append('\n')
      return ''.join(scoHead)
      
   def _fmtComment(self, commentList, delimit=';'):
      commentStr = []
      commentStr.append(' %s' % delimit)
      for cmtPos in range(0, len(commentList)):
         cmtData = commentList[cmtPos]
         if drawer.isNum(cmtData):
            cmtData = round(commentList[cmtPos],4)
         cmtStr = str(cmtData)
         cmtStr = cmtStr.rjust(len(cmtStr)+1)
         commentStr.append(cmtStr)
         if cmtPos != len(commentList) - 1:
            commentStr.append(':')
      commentStr.append('\n')
      return ''.join(commentStr)

   def _fmtHeadTexture(self, className, tName, prepend=''):
      return '%sTM(%s), TI(%s)\n' % (prepend, className, tName)

   def _fmtHeadClone(self, className, tName, cName, prepend=''):
      return '%sTM(%s), TI(%s), TC(%s)\n' % (prepend, className, tName, cName)
      
   def _fmtTitleTexture(self, musicName, tName):
      """used for naming ac toolbox sections"""
      return '%s-%s' % (musicName, tName)
      
   def _fmtTitleClone(self, musicName, tName, cName):
      """used for naming ac toolbox sections"""
      return '%s-%s-%s' % (musicName, tName, cName)      
      
   def _strValue(self, value, ljust=8, sigDig=6, prefix='', postfix=' '):
      """given a value, prepare appropriate string version, 
      w/ default space at end (only needed w/ csound scores)"""
      if drawer.isStr(value):
         return '%s%s%s' % (prefix, value.ljust(ljust), postfix)
      elif drawer.isInt(value):
         return '%s%s%s' % (prefix, str(value).ljust(ljust), postfix)
      else: # assume is is a float
         val = round(value, sigDig)
         if sigDig == 0:
            val = int(val) # convert
         return '%s%s%s' % (prefix, str(val).ljust(ljust),
                            postfix)

   def _strLabel(self, orderList, delimit=' ', prefix=''):
      """previde an event to sample, and return a key string
      orderList is names of each element; if not in dict key key is used
      aux values can be provided just with numbers; will be converted to
      stings
      """
      name = {'inst': 'instrument', 'time': 'timeStart', 'sus': 'timeSustain',
              'amp': 'amplitude', 'ps':'pitch', 'fq':'frequency', 
              'midiNote': 'midiNoteNumber', 'pan': 'panning'
               }
      if orderList[-1] != 'comment':
         orderList.append('comment') # always last      
      label = []         
      for key in orderList:
         if key in name.keys():
            label.append(name[key])
         elif drawer.isInt(key):
            iStr = str(key).rjust(2)
            iStr = iStr.replace(' ', '0') # zero pad
            label.append('aux%s' % iStr)
         else: # not a number or a key
            label.append(key)
      return '%s%s\n' % (prefix, delimit.join(label))
      
   #------------------------------------------------------------------------||--
   # covnert single eventList into necessary data structures, lists or strings

         
   def _translateCsoundExternalStr(self, orcMapMode, esObj):
      """exclude built in args for amp, ps, pan
      if a user needs pitch information from path
      can be obtained by using pathRead parameter object on an aux
      orcMapMode not really needed here, as ps/amp/pan not used in 
      external score
      """
      el = esObj.list() # get event list
      orderList = ['inst', 'time', 'sus',]
      for i in range(0, len(el[0]['aux'])): # just get first event
         orderList.append(i)
      label = self._strLabel(orderList, ' ', ';')
      msg = []
      for event in el:
         if event['acc'] == 0: continue # do not write rests         
         msg.append(self._strValue(event['inst'], 4, 1, 'i'))
         msg.append(self._strValue(event['time'], 12, 8))
         msg.append(self._strValue(event['sus'], 12, 8))         
         # skip all default perameters, go straight to aux
         for i in range(0, len(event['aux'])):
            msg.append(self._strValue(event['aux'][i], 10, 6))
         msg.append(self._fmtComment(event['comment']))
      return ''.join(msg), label # returns string

   def _translateCsoundNativeStr(self, orcMapMode, esObj):
      """convert single texture or clone event sequence object
      to the apropriate string"""
      el = esObj.list() # get event list
      orderList = ['inst', 'time', 'sus', 'amp', 'ps', 'pan']
      for i in range(0, len(el[0]['aux'])): # just get first event
         orderList.append(i)
      label = self._strLabel(orderList, ' ', ';')
      inst = el[0]['inst'] # get inst from first event
      msg = []
      for event in el:
         if event['acc'] == 0: continue # do not write rests         
         msg.append(self._strValue(event['inst'], 4, 1, 'i'))
         msg.append(self._strValue(event['time'], 12, 8))
         msg.append(self._strValue(event['sus'], 12, 8))
         
         val = self.orcObj.postMap(inst, 'amp', event['amp'], orcMapMode)
         msg.append(self._strValue(val, 10, 6))
         
         val = self.orcObj.postMap(inst, 'ps', event['ps'], orcMapMode)
         msg.append(self._strValue(val, 10, 6)) 

         val = self.orcObj.postMap(inst, 'pan', event['pan'], orcMapMode)
         msg.append(self._strValue(val, 10, 6))
         
         for i in range(0, len(event['aux'])):
            msg.append(self._strValue(event['aux'][i], 10, 6))
         msg.append(self._fmtComment(event['comment']))
      return ''.join(msg), label # returns string

   def _translateCsoundSilenceStr(self, orcMapMode, esObj):
      """
      p1 instrument / p2 time (seconds) / p3 duration (seconds)
      p4 MIDI key (can be a fraction) / p5 MIDI velocity / p6 phase
      p7 x (pan) / p8 y (depth) / p9 z (height)
      p10 pitch-class set (sum of pitch-classes as powers of 2), optional
      """
      # need to check if pitch and pan are in the appropriate
      # data format
      el = esObj.list() # get event list
      orderList = ['inst', 'time', 'sus', 'midiNote', 'velocity', 'phase',
                  'panX', 'panY', 'panZ', 'mason']
      label = self._strLabel(orderList, ' ', ';')
      inst = el[0]['inst'] # get inst from first event
      msg = []
      for event in el:
         if event['acc'] == 0: continue # do not write rests         
         assert len(event) == 11 # req length for csoundSilence
         i = self._strValue(event['inst'], 4, 1, 'i')
         start = self._strValue(event['time'], 12, 8)
         sus = self._strValue(event['sus'], 12, 8)
         
         val = self.orcObj.postMap(inst, 'amp', event['amp'], orcMapMode)
         amp = self._strValue(val, 8, 6)

         val = self.orcObj.postMap(inst, 'ps', event['ps'], orcMapMode)
         midiNote = self._strValue(val, 8, 6)
         
         val = self.orcObj.postMap(inst, 'pan', event['pan'], orcMapMode)
         panX = self._strValue(val, 8, 6, '', ' ')
         
         # parameters for silence; there must be 4 aux values
         phase = self._strValue(event['aux'][0], 8, 6)
         panY = self._strValue(event['aux'][1], 8, 6)
         panZ = self._strValue(event['aux'][2], 8, 6)
         mason = self._strValue(event['aux'][3], 6, 6)
         cmt = self._fmtComment(event['comment'])
         msg.append(''.join([i, start, sus, midiNote, amp, phase,
                             panX, panY, panZ, mason, cmt]))
      msg.append('\n')
      return ''.join(msg), label # returns string

   #------------------------------------------------------------------------||--
   def _translateMidiList(self, orcMapMode, esObj):
      """mid list is a short list of data
      consists only of tStart, sus, midiVel, midiPs, midiPan"""
      el = esObj.list() # get event list
      inst = el[0]['inst'] # get inst from first event
      midiList = []
      for event in el:
         if event['acc'] == 0: continue # do not write rests         
         tStart = event['time']
         sus = event['sus']
         midiVel = self.orcObj.postMap(inst, 'amp', event['amp'], orcMapMode)
         midiPs = self.orcObj.postMap(inst, 'ps', event['ps'], orcMapMode)
         midiPan = self.orcObj.postMap(inst, 'pan', event['pan'], orcMapMode)
         midiList.append((tStart, sus, midiVel, midiPs, midiPan))
      return midiList

   def _translateAcToolbox(self, orcMapMode, esObj, ch=0, pgm=0):
      """channel may be None; must provide default (0)
      build a list of string to avoid un packing later"""
      if ch == None: # most shift and correct channel info
         ch = 0
      else:
         ch = ch - 1
      el = esObj.list() # get event list
      inst = el[0]['inst'] # get inst from first event
      dataList = []
      midiPanLast = None # store last to filter data
      # add program change information, always starting at time 0
      dataStr = '(%s (%s %s))\n' % (self._strValue(0, 6, 0),
                     midiTools.decimalProgramChange(ch), pgm)
      dataList.append(dataStr)
      for event in el:
         if event['acc'] == 0: continue # do not write rests         
         tStart = event['time'] * 1000 # convert to ms
         sus = event['sus'] * 1000 # convert to ms
         midiVel = self.orcObj.postMap(inst, 'amp', event['amp'], orcMapMode)
         # cant use postMap here: no way to specify floating mid values
         # only alternative is build a custom orchestra...
         #midiPs = self.orcObj.postMap(inst, 'ps', event['ps'])
         # pitchTools will cap values b/n 0 and 127, but keep floats
         midiPs = pitchTools.psToMidi(event['ps'], 'nolimit')
         midiPan = self.orcObj.postMap(inst, 'pan', event['pan'], orcMapMode)
         # check if pan is different
         if midiPan != midiPanLast:
            midiPanLast = midiPan # pan is controller 10
            dataStr = '(%s (%s 10 %s))\n' % (self._strValue(tStart, 6, 0),
                           midiTools.decimalController(ch), midiPan)
            dataList.append(dataStr)
         # only store note-on events now
         dataStr = '(%s (%s %s %s) %s)\n' % (self._strValue(tStart, 6, 0),# int?
                           midiTools.decimalNoteOn(ch),
                           self._strValue(midiPs, 8, 4), 
                           self._strValue(midiVel, 4, 0), # an int 
                           self._strValue(sus, 6, 0)) # assume this is an int
         dataList.append(dataStr)
      return dataList


   def _translateCollList(self, orcMapMode, esObj):
      """coll data must be integers; uses midi values"""
      el = esObj.list() # get event list
      inst = el[0]['inst'] # get inst from first event
      intList = []
      i = 0
      for event in el:
         if event['acc'] == 0: continue # do not write rests         
         # dur is calculated as distane between start times
         # as max/msp players are monophonic
         if i != len(el) - 1: # not last:
            eventNext = el[i+1]
            tSpan = eventNext['time'] - event['time'] # next minus this
         else: # last event
            tSpan = event['sus'] # no next, use dur
         # convert time to ms ints not s
         tSpan = int(round(tSpan * 1000))
         midiVel = self.orcObj.postMap(inst, 'amp', event['amp'], orcMapMode)
         #midiVel = self._ampToMidiVel(event['amp'], esObj.meta('ampMax'))
         midiPs = self.orcObj.postMap(inst, 'ps', event['ps'], orcMapMode)
         #midiPs  = pitchTools.psToMidi(event['ps'], 'limit')
         midiPan = self.orcObj.postMap(inst, 'pan', event['pan'], orcMapMode)
         #midiPan = self._panToMidiPan(event['pan']) 
         # dont sub tuple
         intList.append('%s %s %s ' % (midiPs, midiVel, tSpan))
         i = i + 1
      return intList

   #------------------------------------------------------------------------||--
   def _translateTextDelimitStr(self, orcMapMode, esObj, delimit='\t'):
      """used for creating both a plain text file and a tab delimitted
      file"""
      el = esObj.list() # get event list
      orderList = ['inst', 'time', 'sus', 'amp', 'midiNote', 'pan']
      for i in range(0, len(el[0]['aux'])): # just get first event
         orderList.append(i)
      label = self._strLabel(orderList, delimit)
      inst = el[0]['inst'] # get inst from first event
      msg = []
      for event in el: # set all ljust to 0
         if event['acc'] == 0: continue # do not write rests         
         msg.append(self._strValue(event['inst'], 0, 1, '', delimit))
         msg.append(self._strValue(event['time'], 0, 8, '', delimit))
         msg.append(self._strValue(event['sus'], 0, 8, '', delimit))
         # amp values will be determined by eventMode orc
         val = self.orcObj.postMap(inst, 'amp', event['amp'], orcMapMode)
         msg.append(self._strValue(val, 0, 6, '', delimit))
         # pitch format will be determined by orc of eventMode
         val = self.orcObj.postMap(inst, 'ps', event['ps'], orcMapMode)
         #psName = pitchTools.psToMidi(event['ps'], 'nolimit') 
         msg.append(self._strValue(val, 0, 6, '', delimit)) 
         
         val = self.orcObj.postMap(inst, 'pan', event['pan'], orcMapMode)
         msg.append(self._strValue(val, 0, 6, '', delimit))
         
         for i in range(0, len(event['aux'])):
            msg.append(self._strValue(event['aux'][i], 0, 6, '', delimit))
         # comment has return carriage
         msg.append(self._fmtComment(event['comment'], 'comment:'))
      return ''.join(msg), label
      
      


#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
class EngineAudioFile(_OutputEngine):
   def __init__(self, emName, ref):
      _OutputEngine.__init__(self, emName, ref) # provide event name
      self.name = 'EngineAudioFile'
      self.doc = lang.docOeAudioFile
      # compatable with all orchestras
      self.orcIncompat = []
      self.outAvailable = ['audioFile']
      self.outMin = ['audioFile']
      self.unitSynthesizerMethod = 'direct'

   def _translateAudioFile(self, orcMapMode, esObj, audioMixObj, method=None):
      """takes an audioMixObject and adds samples to it;
      progressive mixes layerd textures upon each other 
      
      xList shoudl use an array
      audioFile uses the 'generic' orchestra (base-class) to force
         values (from the event list) to stay between zero and one
      """
      el = esObj.list() # get event list

      # start = el[0]['time'] # get start from first event
      # presently, writes all textures and clones at the same location
      # should find a method to localize position relative to frames
      pos = 0 # insert position at zero for now

      xList = [] # presently have to provide data as a list
      for event in el: # set all ljust to 0
         # must right silent events; this used ot be active
         #if event['acc'] == 0: continue

         # limit values b/n 0 and 1 w/ "generic" orcestra
         val = self.orcObj.postMap(1, 'amp', event['amp'], orcMapMode)
         xList.append(val) # get amp values
      # provide a position, a unit list to mix in, and a method
      # this will replace all datall

      audioMixObj.insertMix(pos, xList, method)
      #audioMixObj.insertMixUnit(framePos, xList, method)
      return audioMixObj
      
   def _translatePoly(self):
      """set self.polySeqStr w/ complet orc from self.polySeq
      """
      try: # aiff tools may not be available on a given platform
         # will raise an import error
         # may need to delete old file here
         self.aObj = audioTools.AudioFile(self.ref['pathAudioSynth'], 1)
      except (ImportError, IOError):
         return 0 # failure

      # need to always clear the audio file; if an existing file is alreayd
      # in place, audio will be mixed into this existing file
      self.aObj.clear()

      for tName in self.compatNames:
         tDict = self.polySeq[tName]
         orcMapMode = 1 # tDict['orcMapMode'] # force midMode to normalize
         className = tDict['className']
         esObj = tDict['esObj']
         if not tDict['mute']: # pass object to mix data into
            self.aObj = self._translateAudioFile(orcMapMode, esObj, self.aObj,
                                                self.unitSynthesizerMethod)
         for cName in tDict['TC'].keys():
            if not tDict['TC'][cName]['mute']:
               esObj = tDict['TC'][cName]['esObj'] 
               self.aObj = self._translateAudioFile(orcMapMode, esObj,
                                   self.aObj, self.unitSynthesizerMethod)
      return 1 # sucess
      
   def _write(self):
      """ """
      ok = self._translatePoly() # wil write file
      if ok:
         self.outComplete.append('audioFile')
         self.ref['pathView'] = self.ref['pathAudioSynth']


#-----------------------------------------------------------------||||||||||||--
class EngineCsoundNative(_OutputEngine):
   def __init__(self, emName, ref):
      _OutputEngine.__init__(self, emName, ref) # provide event name
      self.name = 'EngineCsoundNative'
      self.doc = lang.docOeCsoundNative
      # define orcs that are not compatible w/ this engine
      # this engine can only use its own orc
      self.orcIncompat = ['csoundExternal', 'csoundSilence', 'generalMidi',
                          'generalMidiPercussion']
      self.outAvailable = ['csoundOrchestra', 'csoundBatch', 
                              'csoundData', 'csoundScore']
      # min out should be score, orc, bat; csd is an option
      self.outMin = ['csoundOrchestra', 'csoundScore', 'csoundBatch']
      # outComplete, pathComplete defined in parent
      self.polySeqStr = None # score as single, format specific string
            
   def _genCommandStr(self, csd=1):
      """creates a .bat string, and a plain list of render options
      there seems to be an error now in the windows version of this now"""
      if os.name == 'mac':  
         if self.ref['nameCsoundCreator'] == 'VRmi':
            audioDirFlag = '\"-X' + self.ref['pathDirRoot'] + '\" ' 
            audioPathFlag = '\"-o' + self.ref['audioName'] + '\" '
         else: # 'sNoC' for newMacCsound(Ingalls), 
            audioDirFlag = '' 
            # quotes optional
            audioPathFlag = '-o' + self.ref['pathAudio'] + ' '
         batFileHead = 'Csound '
      elif os.name == 'posix':
         audioDirFlag = '' # not used
         # quotes optional
         audioPathFlag = '\"-o' + self.ref['pathAudio'] + '\" ' 
         # could change dir or try to set env variables here...
         batFileHead = '#! /bin/sh \n%s ' % self.ref['pathCsound']
      else: # win or other
         audioDirFlag = '' # not used
         # quotes req on win; not sure if space after 0 is req
         audioPathFlag = '\"-o' + self.ref['pathAudio'] + '\" '  
         batFileHead = '@ECHO off\nc:\ncd %s\n%s ' % (self.ref['pathDirCsound'],
                                                      self.ref['nameExeCsound'])
      renderOpt = ('-m2 '+ # msg level. Sum 1=amps, 2=out-of-range, 4=warnings
         '-d ' + # suppress all displays (-d) (ascii displays -g)
         self.ref['audioFlag'] +            
         '-b1024 ' + # sample frames (or -kprds) per sound I/O buffer, was 8192
         '-B1024 ' + # samples per hardware sound I/O buffer, was 8192
         audioDirFlag +  # Sound File Directory (mac only)
         audioPathFlag)  # other plats use full path here
      if csd: 
         pathOpt =  '\"' + self.ref['pathCsd'] + '\" '
      else: # no csd, use orc and sco path
         pathOpt = ('\"' + self.ref['pathOrc'] + '\" ' + '\"' + 
                           self.ref['pathSco'] + '\"\n')
      batStr = (batFileHead + renderOpt + pathOpt)
      return batStr, renderOpt # needed for csd

   def _writeBat(self, csd):
      """writes a bat file"""
      batStr, renderOpt = self._genCommandStr(csd)
      f = open(self.ref['pathBat'] , 'w')    
      f.write(batStr)
      f.close()      
      # sytem dependent post-adjustments to batch file
      if os.name == 'mac': # 'VRmi' (Mills), 'sNoC' (Ingalls)
         osTools.rsrcSetCreator(self.ref['pathBat'], 
                                self.ref['nameCsoundCreator'], 'TEXT')
      elif os.name == 'posix':      
         os.chmod(self.ref['pathBat'], 0755) #makes executable (744=rwxr--r--) 
      else: # win or other
         pass
      self.outComplete.append('csoundBatch')

   def _writeCsd(self):
      """write a csd file given sco and orc strings
      must be done after calling self.translate()
      and after creating orc srcStr
      uses score in self.polySeqStr
      """
      assert self.orcObj.srcStr != None
      batStr, csdOptions = self._genCommandStr(1) # 1 expects a csd string
      xmlHead = '<?xml version="1.0"?>\n' # add?
      msg = []
      msg.append('<CsoundSynthesizer>\n\n')
      if os.name == 'mac':
         optStr = "%s/n%s/n%s/n/n%s" % ('<MacOptions>', 2, 1, 
                '-b64 -A -s -m0 -K -B0 -Lstdin </MacOptions>')
      optStr = '<CsOptions>\n' + csdOptions + '\n\n' + '</CsOptions>\n'
      msg.append(optStr)
      msg.append('%s\n%s%s\n\n' % ('<CsInstruments>', self.orcObj.srcStr,
                                   '</CsInstruments>'))
      msg.append('%s\n%s%s\n\n' % ('<CsScore>', self.polySeqStr,
                                   '</CsScore>'))
      msg.append('</CsoundSynthesizer>\n')
      f = open(self.ref['pathCsd'] , 'w')
      f.writelines(msg)
      f.close()    
      # sytem dependent post-adjustments to batch file
      if os.name == 'mac':
         osTools.rsrcSetCreator(self.ref['pathCsd'], 
                                self.ref['nameCsoundCreator'], 'TEXT')
      elif os.name == 'posix':      
         pass
      else: # win or other
         pass
      self.outComplete.append('csoundData')
      
   #------------------------------------------------------------------------||--
   def _translatePoly(self):
      """set self.polySeqStr w/ complet orc from self.polySeq
      """
      msg = []
      # may pass instrument list here to get only fTables
      # for specific instruments
      headStr = self.orcObj.getScoFtables()
      msg.append(self._fmtHeadSco(headStr, ';'))
      for tName in self.compatNames:
         tDict = self.polySeq[tName]
         orcMapMode = tDict['orcMapMode']
         className = tDict['className']
         if not tDict['mute']:
            msg.append(self._fmtHeadTexture(className, tName, ';'))
            esObj = tDict['esObj']
            data, label = self._translateCsoundNativeStr(orcMapMode, esObj)
            msg.append(label)
            msg.append(data)
         for cName in self.polySeq[tName]['TC']:
            if not tDict['TC'][cName]['mute']:
               msg.append(self._fmtHeadClone(className, tName, cName, ';'))
               esObj = tDict['TC'][cName]['esObj']
               data, label = self._translateCsoundNativeStr(orcMapMode, esObj)
               msg.append(label)
               msg.append(data)
      self.polySeqStr = ''.join(msg)

   def _writeSco(self):
      f = open(self.ref['pathSco'], 'w')
      f.write(self.polySeqStr)     
      f.close()   
      self.outComplete.append('csoundScore')

   def _writeOrc(self):
      """write orchestra already constructed in _write Method"""
      assert self.orcObj.srcStr != None
      f = open(self.ref['pathOrc'], 'w')
      f.write(self.orcObj.srcStr)     
      f.close()   
      self.outComplete.append('csoundOrchestra')

   def _writePre(self):
      """if csd specified remove orc and score"""
      # csd only works w/ csound native
      if 'csoundData' in self.outRequest: 
         # check for sco and orc, remove
         for out in ['csoundOrchestra', 'csoundScore']:
            if out in self.outRequest:
               self.outRequest.remove(out)
         if 'csoundData' not in self.outRequest:
            self.outRequest.append('csoundData')
         
   def _write(self):
      """translate and write all files """
      self._translatePoly()
      # update orcObj.srcStr
      # instList may have instruments not compatiable with this orchestra?
      self.orcObj.constructOrc(self.ref['optionNchnls'], self.compatInsts)
      if 'csoundBatch' in self.outRequest:
         if 'csoundData' in self.outRequest:
            self._writeBat(1)
         else:
            self._writeBat(0)
      # assumes tt redundant outs are not selected: csoundData and csoundScore
      if 'csoundData' in self.outRequest:
         self._writeCsd()
      else: # if csd, dont do score and orc
         if 'csoundScore' in self.outRequest:
            self._writeSco()
            self.ref['pathView'] = self.ref['pathSco']
         if 'csoundOrchestra' in self.outRequest:
            self._writeOrc()

   def _writePost(self):
      """post writing updates as necessary
      self.outComplete set above"""
      if 'csoundData' in self.outComplete:
         self.ref['pathView'] = self.ref['pathCsd']
      else:
         if 'csoundScore' in self.outComplete:
            self.ref['pathView'] = self.ref['pathSco']


#-----------------------------------------------------------------||||||||||||--
class EngineCsoundExternal(_OutputEngine):
   def __init__(self, emName, ref):
      _OutputEngine.__init__(self, emName, ref) # provide event name
      self.name = 'EngineCsoundExternal'
      self.doc = lang.docOeCsoundExternal
      # compatable with all orchestras
      self.orcIncompat = []
      self.outAvailable = ['csoundScore']
      self.outMin = ['csoundScore']

   def _translatePoly(self):
      """set self.polySeqStr w/ complet orc from self.polySeq
      """
      msg = []
      msg.append(self._fmtHeadSco('', ';'))
      for tName in self.compatNames:
         tDict = self.polySeq[tName]
         orcMapMode = tDict['orcMapMode']
         className = tDict['className']
         if not tDict['mute']:
            msg.append(self._fmtHeadTexture(className, tName, ';'))
            esObj = tDict['esObj']
            data, label = self._translateCsoundExternalStr(orcMapMode, esObj)
            msg.append(label)
            msg.append(data)
         for cName in tDict['TC'].keys():
            if not tDict['TC'][cName]['mute']:
               msg.append(self._fmtHeadClone(className, tName, cName, ';'))
               esObj = tDict['TC'][cName]['esObj'] 
               data, label = self._translateCsoundExternalStr(orcMapMode, esObj)
               msg.append(label)
               msg.append(data)
      self.polySeqStr = ''.join(msg)

   def _write(self):
      """ """
      self._translatePoly()
      f = open(self.ref['pathSco'], 'w')
      f.write(self.polySeqStr)     
      f.close()   
      self.outComplete.append('csoundScore')
      self.ref['pathView'] = self.ref['pathSco']
            
            
            
#-----------------------------------------------------------------||||||||||||--
class EngineCsoundSilence(_OutputEngine):
   def __init__(self, emName, ref):
      _OutputEngine.__init__(self, emName, ref) # provide event name
      self.name = 'EngineCsoundSilence'
      self.doc = lang.docOeCsoundSilence     
      # not compatable w/ any other orchestras
      # as 4 aux number are necessary
      self.orcIncompat = ['csoundExternal', 'csoundNative', 'generalMidi',
                          'generalMidiPercussion']
      self.outAvailable = ['csoundScore']
      self.outMin = ['csoundScore']

   def _translatePoly(self):
      """set self.polySeqStr w/ complet orc from self.polySeq
      """
      msg = []
      msg.append(self._fmtHeadSco('', ';'))
      for tName in self.compatNames:
         tDict = self.polySeq[tName]
         orcMapMode = tDict['orcMapMode']
         className = tDict['className']
         if not tDict['mute']:
            msg.append(self._fmtHeadTexture(className, tName, ';'))
            esObj = tDict['esObj']
            data, label = self._translateCsoundSilenceStr(orcMapMode, esObj)
            msg.append(label)
            msg.append(data)
         for cName in tDict['TC'].keys():
            if not tDict['TC'][cName]['mute']:
               msg.append(self._fmtHeadClone(className, tName, cName, ';'))
               esObj = tDict['TC'][cName]['esObj'] 
               data, label = self._translateCsoundSilenceStr(orcMapMode, esObj)
               msg.append(label)
               msg.append(data)
      self.polySeqStr = ''.join(msg)

   def _write(self):
      """ """
      self._translatePoly()
      f = open(self.ref['pathSco'], 'w')
      f.write(self.polySeqStr)     
      f.close()   
      self.outComplete.append('csoundScore')
      self.ref['pathView'] = self.ref['pathSco']


#-----------------------------------------------------------------||||||||||||--
class EngineMaxColl(_OutputEngine):
   """used to translate from athenaCL format to midi int score format
   int format is used to get athenaCL data into Max/MSP or similar
   no distinctiong between start time and duration, as notes are triggered
   tracks are assigned a sequential number
   all values are converted to integers (midi pitch, midi vel, ms dur)
   uses midi integer values for pitch and amp
   in order pitch, vel, dur (ms)
   stored as chNum, val val val;
   usefull for importing into a coll in max/msp
   note: always uses a midi orchestra
   """
   def __init__(self, emName, ref):
      _OutputEngine.__init__(self, emName, ref) # provide event name
      self.name = 'EngineMaxColl'
      self.doc = lang.docOeMaxColl
      self.trackList = []
      # define orcs that are not compatible w/ this engine
      self.orcIncompat = [] # all orchestras compatable
      self.outAvailable = ['maxColl']
      self.outMin = ['maxColl']

   def _translatePoly(self):
      """tramslates a athenaCL score to midi score format as defined             
      elsewhere
      """
      trackList = []
      for tName in self.compatNames:
         tDict = self.polySeq[tName]
         orcMapMode = tDict['orcMapMode']
         ch = tDict['midiCh']
         pgm = tDict['midiPgm']
         if not tDict['mute']:
            esObj = tDict['esObj']
            intSco = self._translateCollList(orcMapMode, esObj)
            trackList.append((tName, pgm, ch, intSco))
         for cName in tDict['TC'].keys():
            if not tDict['TC'][cName]['mute']:
               esObj = tDict['TC'][cName]['esObj']
               intSco = self._translateCollList(orcMapMode, esObj)
               trackList.append(('%s-%s' % (tName, cName), pgm, ch, intSco))
      self.trackList = trackList

   def _write(self):
      """ """
      assert 'maxColl' in self.outRequest 
      self._translatePoly()
      fileLines = []
      partNo = 1 # cant use ch or program, just asign ints
      for part in self.trackList:
         scoStr = ''.join(part[3])
         fileLines.append('%s, %s;\n' % (partNo, scoStr))
         partNo = partNo + 1 # no limit on index no
      f = open(self.ref['pathMaxColl'], 'w')
      f.write(''.join(fileLines))
      f.close()     
      self.outComplete.append('maxColl')
      
   def _writePost(self):
      self.ref['pathView'] = self.ref['pathMaxColl']

#-----------------------------------------------------------------||||||||||||--
class EngineMidiFile(_OutputEngine):
   """writes a midi file"""
   def __init__(self, emName, ref):
      _OutputEngine.__init__(self, emName, ref) # provide event name
      self.name = 'EngineMidiFile'
      self.doc = lang.docOeMidiFile
      # define orcs that are not compatible w/ this engine
      self.orcIncompat = [] # all orchestras compatable
      self.outAvailable = ['midiFile']
      self.outMin = ['midiFile']
      # store structured data
      self.trackList = []

   def _translatePoly(self):
      """tramslates a athenaCL score to midi score
      """
      trackList = []
      for tName in self.compatNames:
         tDict = self.polySeq[tName]
         orcMapMode = tDict['orcMapMode']
         ch = tDict['midiCh'] # need w/ or w/o mute
         pgm = tDict['midiPgm']
         if not tDict['mute']:
            esObj = tDict['esObj']
            midiSco = self._translateMidiList(orcMapMode, esObj)
            trackList.append((tName, pgm, ch, midiSco))
         for cName in tDict['TC'].keys():
            if not tDict['TC'][cName]['mute']:
               esObj = tDict['TC'][cName]['esObj']
               midiSco = self._translateMidiList(orcMapMode, esObj)
               trackList.append(('%s-%s' % (tName, cName), pgm, ch, midiSco))
      self.trackList = trackList

   def _write(self):
      """ """
      assert 'midiFile' in self.outRequest
      self._translatePoly()
      midiObj = midiTools.MidiScore(self.trackList, self.ref['nameMusic'],
                                    self.ref['optionMidiTempo'])
      midiObj.write(self.ref['pathMid'])
      self.outComplete.append('midiFile')
      
   def _writePost(self):
      self.ref['pathView'] = self.ref['pathMid']


#-----------------------------------------------------------------||||||||||||--
class EngineAcToolbox(_OutputEngine):
   """writes a midi file"""
   def __init__(self, emName, ref):
      _OutputEngine.__init__(self, emName, ref) # provide event name
      self.name = 'EngineAcToolbox'
      self.doc = lang.docOeAcToolbox
      # define orcs that are not compatible w/ this engine
      self.orcIncompat = [] # all orchestras compatable
      self.outAvailable = ['acToolbox']
      self.outMin = ['acToolbox']
      # store structured data
      self.codeList = []
      self.codeCmt = 'created with %s' % lang.msgAth

   def _acSection(self, title, dataList, dur):      
      """must convert dur from s to ms"""
      msg = []
      h = """(define %s (make-instance  'section
   :input '(make-from-midi-file 'section)
   :events '(""" % title
      # comment is last quote
      t = """   ) 
   :duration %s 
   :clock-unit nil)  "%s")""" % (self._strValue(dur * 1000, 6, 0), self.codeCmt) 
      msg.append(h)
      for event in dataList: # events are already strings w/ line cariage
         msg.append(event)
      msg.append(t)
      msg.append('\n')
      return ''.join(msg)

   def _acParallelSection(self, title, sectionList, dur):      
      """must convert dur from s to ms
      dur does not seem to be necesary here; can set to nil"""
      msg = []
      h = """(define %s (make-instance  'section
   :input '(make-parallel-section """ % title
      t = """   ) 
   :duration nil 
   :clock-unit nil)  "%s")
(setf (get-events %s)
   (get-events (make-variant %s)))
  """ % (self.codeCmt, title, title) #self._strValue(dur * 1000, 6, 0) 
      msg.append(h)
      for s in sectionList: # events are already strings w/ line cariage
         msg.append(s)
         msg.append('\n')
      msg.append(t)
      msg.append('\n')
      return ''.join(msg)
  
   def _translatePoly(self):
      self.codeList = []      
      # it is necessary to lead with this to permit a parallel section
      # loading error
      self.codeList.append('(setf *convert-from-old-midi-format* nil)\n')
      sectionList = [] # store section names for parallel section
      completeDur = 0 # does this need to be filled?
      musicName = self.ref['nameMusic']
      for tName in self.compatNames:
         tDict = self.polySeq[tName]
         orcMapMode = tDict['orcMapMode']
         ch = tDict['midiCh'] # need w/ or w/o mute
         pgm = tDict['midiPgm']
         if not tDict['mute']:
            esObj = tDict['esObj']
            totalDur = esObj.getTotalDuration()
            dataList = self._translateAcToolbox(orcMapMode, esObj, ch, pgm)
            title = self._fmtTitleTexture(musicName, tName)
            self.codeList.append(self._acSection(title, dataList, totalDur))
            sectionList.append(title)
         for cName in tDict['TC'].keys():
            if not tDict['TC'][cName]['mute']:
               esObj = tDict['TC'][cName]['esObj']
               totalDur = esObj.getTotalDuration()
               dataList = self._translateAcToolbox(orcMapMode, esObj, ch, pgm)
               title = self._fmtTitleClone(musicName, tName, cName)
               self.codeList.append(self._acSection(title, dataList, totalDur))
               sectionList.append(title)
      # write a parallel section of all individual sections
      self.codeList.append(self._acParallelSection(musicName, 
                                    sectionList, completeDur))
      
   def _write(self):
      """ """
      assert 'acToolbox' in self.outRequest
      self._translatePoly()
      f = open(self.ref['pathAct'], 'w')
      f.writelines(self.codeList)
      f.close()
      # sytem dependent post-adjustments to batch file
      if os.name == 'mac': # ('ACTP', 'ACEX')
         osTools.rsrcSetCreator(self.ref['pathAct'], 'ACTP', 'ACEX')
      elif os.name == 'posix':
         if drawer.isDarwin(): # try only if darwin
            try:
               osTools.rsrcSetCreator(self.ref['pathAct'], 'ACTP', 'ACEX')
            except ImportError: pass # do nothing 
      else: pass # win or other
      self.outComplete.append('acToolbox')
      
   def _writePost(self):
      pass


#-----------------------------------------------------------------||||||||||||--
class EngineText(_OutputEngine):
   def __init__(self, emName, ref):
      _OutputEngine.__init__(self, emName, ref) # provide event name
      self.name = 'EngineText'
      self.doc = lang.docOeText
      self.orcIncompat = [] # compat w/ all orchestras
      self.outAvailable = ['textTab', 'textSpace']
      self.outMin = ['textTab']

   def _translatePoly(self, delimit):
      """set self.polySeqStr w/ complet orc from self.polySeq
      """
      msg = []
      msg.append(self._fmtHeadSco(''))
      for tName in self.compatNames:
         tDict = self.polySeq[tName]
         orcMapMode = tDict['orcMapMode']
         className = tDict['className']
         if not tDict['mute']:
            msg.append(self._fmtHeadTexture(className, tName,))
            esObj = tDict['esObj']
            data, label = self._translateTextDelimitStr(orcMapMode, esObj, 
                                                        delimit)
            msg.append(label)
            msg.append(data)
         for cName in tDict['TC'].keys():
            if not tDict['TC'][cName]['mute']:
               msg.append(self._fmtHeadClone(className, tName, cName,))
               esObj = tDict['TC'][cName]['esObj']  
               data, label = self._translateTextDelimitStr(orcMapMode, esObj,
                                                           delimit)
               msg.append(label)
               msg.append(data)
      self.polySeqStr = ''.join(msg)

   def _write(self):
      """ """
      #print _MOD, 'calling text engine write method'
      for out in self.outRequest:
         if out in ['textTab', 'textSpace']:
            if out == 'textTab':
               self._translatePoly('\t')
               fp = self.ref['pathTxtTab']
            if out == 'textSpace':
               self._translatePoly(' ')
               fp = self.ref['pathTxtSpace']
         else:
            continue # cannot process anything else
         f = open(fp, 'w')
         f.write(self.polySeqStr)     
         f.close()   
         self.outComplete.append(out)
         self.ref['pathView'] = fp








#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
class EventMode:
   """this object handles multiple output methods, selecting
   which can output when, reporting the results
   Performance object is created here as well
   
   ao is athenaObject; will be used for processing unless a list of textures
   are supplied w/ process method
   
   name is the declared name of this event mode
   
   for a given event mode, there is only one orchestra available
   if this orchestra does not work with an output, output may still be
   calculated   
   """
   def __init__(self, ao, name):
      self.name = eventModeParser(name) # needed to get default orc update
      if self.name == None: raise ValueError, 'no such EventMode: %s' % name
      self.ao = ao
      # not necessary to store perfObj, as created only w/ process method
      #self.perfObj = None # will be made w/ call
      self.ref = {} # dictionary of path names
      #no longer done at init, but w/ process
      #self._updateOrc()
      self.rootPath = None # assigned w/ method
      
      #keep a local copy of all output format objects, keyed by format name
      self.outFormatRef = outFormat.factory('all')
      
      # all ref keys used by engines to write files on process runs                     
      #self.keyWritePaths = ['pathAudioSynth', 'pathSco', 'pathBat', 'pathOrc',
      #   'pathXml', 'pathMid', 'pathCsd', 'pathMaxColl', 'pathTxtTab',
      #   'pathTxtSpace']
      
   #------------------------------------------------------------------------||--
   # tools for init processing of paths and other data

   def _getAudioFlags(self):
      """used for csound, as well as common file path naming
      returns extsion as well as csound flag
      converst from athenaCL representation to file extensions
      """
      # leave space after flag
      usrFileFormat = self.ao.external.getPref('external','audioFileFormat')
      if   usrFileFormat == 'aif': return '.aif', '-A ' 
      elif usrFileFormat == 'wav': return '.wav', '-W ' 
      elif usrFileFormat == 'sd2': return '.sd2', ''
      #no longer supported
      #elif usrFileFormat == 'ircam': return '.sf', '-J '
      
   def _initRefNames(self):
      """collect necessary data from athenaObj into a dictionary
      this includes all necessary paths for writing all files
      
      all paths derived from rootPath as a root file. 
      note: a future version might take rootPath as a directory and bundle 
      content
      """
      assert self.rootPath != None
      # name music is just the file name, no path, no extensions
      dir, file = os.path.split(self.rootPath)
      self.ref['nameMusic'], x = osTools.extSplit(file) # lop off .sco
      self.ref['audioExt'], self.ref['audioFlag'] = self._getAudioFlags()
      self.ref['audioName'] = self.ref['nameMusic'] + self.ref['audioExt']
      # csound creator only used for macos resources setting
      self.ref['nameCsoundCreator'] = self.ao.external.getPref('external', 
                                 'csoundCreatorCode') 
      # extra data
      self.ref['version'] = self.ao.aoInfo['version']
      self.ref['optionNchnls'] = self.ao.nchnls
      self.ref['optionMidiTempo'] = self.ao.midiTempo
      
   def _absPath(self, ext):
      "builds the appropriate path from an extension"
      path = os.path.join(self.ref['pathDirRoot'], (self.ref['nameMusic'] +
                                                    ext))
      return path
                                                      
   def _initRefPaths(self):
      assert self.rootPath != None
      # get data for convenience
      self.ref['pathUsr'] = self.rootPath # store usr path
      self.ref['pathDirRoot'], fileName = os.path.split(self.rootPath)
      dirRoot = self.ref['pathDirRoot']
      nameMusic = self.ref['nameMusic']
      # update all path names
      # these need to be done here so that output objects do not over-write
      # used for csound output after rendering
      self.ref['pathAudio'] = self._absPath(self.ref['audioExt'])
      # this automatically loads all paths in output formats
      for fmt in self.outFormatRef:
         self.ref[fmt.emKey] = self._absPath(fmt.ext)
      # assigned to after processing
      self.ref['pathView'] = None # set in write() method of subclass
      # needed for csound processing
      self.ref['pathCsound'] = self.ao.external.getPref('external', 'csoundPath')
      dirCsound, exeCsound = os.path.split(self.ref['pathCsound'])
      self.ref['pathDirCsound'] = dirCsound
      # must get from path
      self.ref['nameExeCsound'] = exeCsound



   #------------------------------------------------------------------------||--
      
   def _engineAllocate(self, outRequest):
      """determine, for this event mode, what engines are necessary
      as well as what outputs to request
      assumes that  outputReqest has already been set
      combiness eventMode w/ output request to only get necessary engines

      this the first layer of compatbility; another layer is performed
      for each engine
      does not check for texture-instrument compatibility w/ vairous engines
      
      if a user has a csound output, and is in gm mode, no csound engines
      if a user has a midi output, and is in cs mode, midi is created
      """
      if drawer.isStr(outRequest):
         if outRequest == 'all':
            outRequest = outFormat.outputFormatNames.values()
      engineRequest = []
      # do exclusive groups first; cant be two kinds of csound
      # get engines for varuous EventMode Names      
      #print _MOD, 'allocating engines for em:', self.name
      # csound engines only added if declared with evenMode
      # can only create 1 .sco at a time
      if self.name == 'csoundNative':
         engineRequest.append('EngineCsoundNative')
      elif self.name == 'csoundSilence':
         engineRequest.append('EngineCsoundSilence')
      elif self.name == 'csoundExternal':
         engineRequest.append('EngineCsoundExternal')
      # last option: a simple output request, regardless of event mode
      elif 'csoundScore' in outRequest:
         engineRequest.append('EngineCsoundExternal')
      # do compatible types, possible with many formats
      # compatible types are not dependent on auxillary numbers
      if self.name in ['midi', 'midiPercussion'] or 'midiFile' in outRequest:
         engineRequest.append('EngineMidiFile')
      # check for output requests that have an engine
      # but are not eventModes
      if 'maxColl' in outRequest:
         engineRequest.append('EngineMaxColl')
      if 'textTab' in outRequest or 'textSpace' in outRequest:
         engineRequest.append('EngineText')
      if 'acToolbox' in outRequest:
         engineRequest.append('EngineAcToolbox')
      if 'audioFile' in outRequest:
         engineRequest.append('EngineAudioFile')
      # instantiate necessary objects
      engineLib = {}
      for engine in engineRequest:
         mod = eval(engine) # all take self.ref on init
         # supply event mode name as well as reference dictionary
         engineLib[engine] = mod(self.name, self.ref)
      return engineLib

   #------------------------------------------------------------------------||--
   # post processing updates
   def _updateInfo(self, ref):
      """get ref to aoInfo dict, update last paths
      need to reassign to instance variable in order to get updated data
         from external objects (output objects)
      viewPath is the file to view, may be a score, csd, or text file
      """
      # add list to preivous
      self.ref = ref
      # fromat of aoPrefs not the same as ref
      # these are paths used for launching things from w/n athenacl
      self.ao.aoInfo['viewFP'] = self.ref['pathView'] # 
      self.ao.aoInfo['audioFP'] = self.ref['pathAudio']
      self.ao.aoInfo['scoFP'] = self.ref['pathSco'] 
      self.ao.aoInfo['orcFP'] = self.ref['pathOrc']
      self.ao.aoInfo['batFP'] = self.ref['pathBat']
      self.ao.aoInfo['csdFP'] = self.ref['pathCsd']
      self.ao.aoInfo['midFP'] = self.ref['pathMid']
      
   def _outputToPath(self, output):
      """if formats are known, return what paths were written"""
      if output == 'audioFile': path = self.ref['pathAudioSynth']
      elif output == 'midiFile': path = self.ref['pathMid']
      elif output == 'csoundBatch': path = self.ref['pathBat'] #self.batPath
      elif output == 'csoundScore': path = self.ref['pathSco'] #self.scoPath
      elif output == 'csoundOrchestra': path = self.ref['pathOrc'] #self.orcPath
      elif output == 'csoundData': path = self.ref['pathCsd'] #self.csdPath
      elif output == 'maxColl': path = self.ref['pathMaxColl'] #self.txtPath
      elif output == 'textSpace': path = self.ref['pathTxtSpace'] #self.csdPath
      elif output == 'textTab': path = self.ref['pathTxtTab'] #self.txtPath
      elif output == 'acToolbox': path = self.ref['pathAct'] #self.txtPath
      else:
         raise ValueError, 'bad output given: %s' % output 
      return path

   def _getReport(self):
      self.outComplete.sort()
      msg = []
      msg.append('%sEventList %s complete:\n' % (lang.TAB, self.ref['nameMusic']))
      # formats returned will be complete output strings
      for output in self.outComplete:
         msg.append('%s\n' % self._outputToPath(output))
      return ''.join(msg)

   #------------------------------------------------------------------------||--
   # main operations

   def setRootPath(self, rootPath=None):
      if rootPath == None: rootPath = osTools.tempFile() # gets txt temp file
      self.rootPath = rootPath
      self._initRefNames()
      self._initRefPaths()
   
   def getWritePaths(self, rootPath):
      """generate all the paths of possible outputs
      may eventually want to calclulate actual outputs"""
      self.rootPath = rootPath
      self._initRefNames()
      self._initRefPaths()
      msg = []
      # get all write paths from all possible output formats
      for fmt in self.outFormatRef:
         msg.append(self.ref[fmt.emKey]) # emKey attribute is names used here
      return msg
      
   def _docEngine(self, engine):
      """provide an engine name string"""
      msg = []
      for oe in engine.outAvailable:
         oeObj = outFormat.factory(oe)
         msg.append('%s (%s)' % (oe, oeObj.doc))
      return ', '.join(msg)
      
   def _docUser(self, usrOutRequest):
      """similar but not the same as _docReference; this divided into headlist
      and entryLines"""
      emOrcObj = orc.factory(selectEventModeOrc(self.name))
      engineLib = self._engineAllocate(usrOutRequest)
      headList = []
      headList.append('EventMode: %s; Orchestra: %s\n' % (
                      self.name, emOrcObj.name))
      if len(engineLib) == 1: # adjust for plural
         headList.append('%s active OutputEngine:\n' % len(engineLib))
      else:
         headList.append('%s active OutputEngines:\n' % len(engineLib))
      entryLines = []
      eNames = engineLib.keys()
      eNames.sort()
      for engineName in eNames:
         engine = engineLib[engineName]
         options = self._docEngine(engine)
         docStr = '%s EventOutput formats available: %s' % (engine.doc, options)
         entryLines.append([engine.name, docStr])
      return headList, entryLines
      
   def _docReference(self, usrOutRequest):   
      """difference w/ docUser is in the header and the listed 'active' bit"""
      emOrcObj = orc.factory(selectEventModeOrc(self.name))
      engineLib = self._engineAllocate(usrOutRequest)
      msg = []
      eNames = engineLib.keys()
      eNames.sort()
      for engineName in eNames:
         engine = engineLib[engineName]
         #options = drawer.listScrub(engine.outMin, 'rmSpace', 'rmQuote')
         options = self._docEngine(engine)
         docStr = '%s OutputFormat available: %s' % (engine.doc, options)
         msg.append('%s: %s\n' % (engine.name, docStr))
      return ''.join(msg)
      
   def reprDoc(self, usrOutRequest=[], style=None):
      """return a documentation string"""
      # get the em orc for this em mode
      if style in [None]:
         return self._docUser(usrOutRequest)
      if style in ['ref']:
         return self._docReference(usrOutRequest)
      
      
   def process(self, input=None, usrOutRequest=[], refresh=1):
      """must be called after setRootPath
      if input is None: uses local atheanObj, processes all textures and clones
      if input is a list of texture objects, will process as necessary
      return ok, msg; if polySeq is None, will be generated
      otherwise can share the sasme ply score from elsewhere

      refresh will force the generation of new scores
      texture.score() called on creation, and edit: should be up to date
      clone.score() called on creation and edit; should be up to date
      """
      # get orcObj for this mode, indepedent of any texture
      # this orc is passed to each engine; the engine must use it
      # to try to process textures; may or may not be compatible w/ all t's
      
      self.outComplete = [] # clear
      outRequest = copy.deepcopy(usrOutRequest)
      # important tt only one performance object is created, and passed
      # to each output engine; ensures that same results will 
      # happen w/ multiplw outputs
      # perfObj stores a reference to each textures orcObj
      # no need to store perfObj as instance variable
      perfObj = Performer() # perform textures and clones w/ obj
      if input in [None, 'all']:
         perfObj.flattenAll(self.ao, refresh)
      else: # its a list of textures
         perfObj.flattenSome(input, refresh)
         
      # sort all event lists in this perfObj
      # not sure if sorting is always necessary
      # may be a time lag: sort event seq elswhere?
      perfObj.sort()
      # no need to store engineLib
      engineLib = self._engineAllocate(outRequest)
      # local objects not store in em
      emOrcObj = orc.factory(selectEventModeOrc(self.name))

      ok = 1
      msg = []
      # when calling a engine, pass modeOrcObj, process in addition
      # to local texture based texture orcObj
      for engineName in engineLib.keys():
         try: # will write orchestra if necessary 
            # only pass a ref to the polySeq to the engine
            engineLib[engineName].write(perfObj.polySeq, outRequest) 
            self.outComplete = (self.outComplete + 
                                engineLib[engineName].outComplete)
         except (IOError, OSError), e:
            ok = 0
            msg.append(e)
         # get ref data from engine
         # this redundant: it does not really need to be done each time
         self._updateInfo(engineLib[engineName].ref) 
      # update paths for athenaobject
      if ok:
         return ok, self._getReport(), self.outComplete
      else:
         return ok, lang.msgFileError, self.outComplete




#-----------------------------------------------------------------||||||||||||--
# publicj factory for create an event object
def factory(eventName, ao):
   eventName = eventModeParser(eventName)
   assert eventName != None
   return EventMode(ao, eventName)



















#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
# run tests
class Test:
   def __init__(self):
      # call test methods
      # self.testEventList()
      self.testRetrograde()
      
      
   def testEventList(self):
      from athenaCL.libATH import SC

      multisetFactory = SC.MultisetFactory()
      scObj = SC.SetClass()
      setData = 'c4, d3, a#4', 'd#2, a3', '3-5'
      setList = []
      for set in setData:
         setList.append(multisetFactory(None, set, scObj))
      from athenaCL.libATH import pitchPath
      polyPath = pitchPath.PolyPath('test', scObj)
      polyPath.loadMultisetList(setList)
      from athenaCL.libATH.libTM import texture
      texture_ = texture.factory('LineGroove')

      textureParameters = {}
      textureParameters['inst']   = ('staticInst', 2) 
      textureParameters['ampQ']   = ('constant', 70)
      textureParameters['panQ']   = ('constant', .5)
      textureParameters['octQ']   = ('constant', 0)
      textureParameters['fieldQ'] = ('constant', 0)
      textureParameters['rhythmQ'] = ('loop', ((4,1,1),(4,5,1)))
      textureParameters['tRange'] = ('staticRange', (0, 15)) 
      textureParameters['beatT']  = ('c', 30)
      
      pitchMode = 'ps'
      polyphonyMode = '' 
      temperamentName = '12equal' 
      auxNo = 0
      midiPgm = 0
      midiCh = None
      fpSSDR = ''
      fpSADR = ''
      texture_.load(textureParameters, polyPath, polyphonyMode, temperamentName, 
                pitchMode, auxNo, fpSSDR, fpSADR, midiPgm, midiCh)
      ok = texture_.score()
      print texture_.elScore

   def testRetrograde(self):
   
      for fmt in ['eventInverse', 'timeInverse']: #, 'timeInverse']:
         a = EventSequence()
         tCur = 100
         for x in range(0, 7):
            d = random.choice([1,3,7,13])
            event = {'time':tCur, 'dur':d}
            a.append(event)
            tCur = tCur + d
         print '\neventSequence: %s' % fmt
         a.sort() # maually sort
         a.retrograde(fmt)

if __name__ == '__main__':
   Test()




www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.