Home | History | Annotate | Download | only in JetCreator
      1 """
      2  File:  
      3  JetFile.py
      4  
      5  Contents and purpose:
      6  Auditions a jet file to simulate interactive music functions
      7  
      8  Copyright (c) 2008 Android Open Source Project
      9  
     10  Licensed under the Apache License, Version 2.0 (the "License");
     11  you may not use this file except in compliance with the License.
     12  You may obtain a copy of the License at
     13  
     14       http://www.apache.org/licenses/LICENSE-2.0
     15  
     16  Unless required by applicable law or agreed to in writing, software
     17  distributed under the License is distributed on an "AS IS" BASIS,
     18  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     19  See the License for the specific language governing permissions and
     20  limitations under the License.
     21 """
     22 
     23 from __future__ import with_statement
     24 
     25 import logging
     26 import ConfigParser
     27 import struct
     28 import os
     29 import sys
     30 import midifile
     31 
     32 from JetUtils import *
     33 from JetDefs import *
     34 
     35 VERSION = '0.1'
     36 
     37 # JET file defines

     38 JET_HEADER_STRUCT = '<4sl'
     39 JET_HEADER_TAG = 'JET '
     40 JET_VERSION = 0x01000000
     41 
     42 # JET chunk tags

     43 JET_INFO_CHUNK = 'JINF'
     44 JET_SMF_CHUNK = 'JSMF'
     45 JET_DLS_CHUNK = 'JDLS'
     46 
     47 # JINF defines

     48 JINF_STRUCT = '<4sl4sl4sl4sl'
     49 JINF_JET_VERSION = 'JVER'
     50 JINF_NUM_SMF_CHUNKS = 'SMF#'
     51 JINF_NUM_DLS_CHUNKS = 'DLS#'
     52 
     53 # JCOP defines

     54 JCOP_STRUCT = '<4sl'
     55 JCOP_CHUNK = 'JCOP'
     56 
     57 # JAPP defines

     58 JAPP_STRUCT = '<4sl'
     59 JAPP_CHUNK = 'JAPP'
     60 
     61 # config file defines

     62 OUTPUT_SECTION = 'output'
     63 OUTPUT_FILENAME = 'filename'
     64 OUTPUT_COPYRIGHT = 'copyright'
     65 OUTPUT_APP_DATA = 'app_data'
     66 OUTPUT_CHASE_CONTROLLERS = 'chase_controllers'
     67 OUTPUT_OMIT_EMPTY_TRACKS = 'omit_empty_tracks'
     68 SEGMENT_SECTION = 'segment'
     69 SEGMENT_FILENAME = 'filename'
     70 SEGMENT_DLSFILE = 'dlsfile'
     71 SEGMENT_NAME = 'segname'
     72 SEGMENT_START = 'start'
     73 SEGMENT_END = 'end'
     74 SEGMENT_END_MARKER = 'end_marker'
     75 SEGMENT_QUANTIZE = 'quantize'
     76 SEGMENT_OUTPUT = 'output'
     77 SEGMENT_LENGTH = 'length'
     78 SEGMENT_DUMP_FILE = 'dump'
     79 SEGMENT_TRANSPOSE = 'transpose'
     80 SEGMENT_REPEAT = 'repeat'
     81 SEGMENT_MUTE_FLAGS = 'mute_flags'
     82 LIBRARY_SECTION = 'libraries'
     83 LIBRARY_FILENAME = 'lib'
     84 CLIP_PREFIX = 'clip'
     85 APP_PREFIX = 'app'
     86 
     87 # JET events

     88 JET_EVENT_MARKER = 102
     89 JET_MARKER_LOOP_END = 0
     90 JET_EVENT_TRIGGER_CLIP = 103
     91 
     92 class JetSegment (object):
     93     """ Class to hold segments """
     94     def __init__ (self, segname, filename, start=None, end=None, length=None, output=None, quantize=None, jetevents=[], dlsfile=None, dump_file=None, transpose=0, repeat=0, mute_flags=0):
     95         self.segname = segname
     96         self.filename = filename
     97         self.dlsfile = dlsfile
     98         self.start = start
     99         self.end = end
    100         self.length = length
    101         self.output = output
    102         self.quantize = quantize
    103         self.dump_file = dump_file
    104         self.jetevents = jetevents
    105         #API FIELDS FOR UI

    106         self.transpose = transpose
    107         self.repeat = repeat
    108         self.mute_flags = mute_flags        
    109 
    110 class JetEvent (object):
    111     """ Class to hold events """
    112     def __init__(self, event_name, event_type, event_id, track_num, channel_num, event_start, event_end):
    113         self.event_name = event_name
    114         self.event_type = event_type
    115         self.event_id = event_id
    116         self.track_num = track_num
    117         self.channel_num = channel_num
    118         self.event_start = event_start
    119         self.event_end = event_end
    120   
    121 class JetFileException (Exception):
    122     """ Exceptions class """
    123     def __init__ (self, msg):
    124         self.msg = msg
    125     def __str__ (self):
    126         return self.msg
    127 
    128 class JetSegmentFile (midifile.MIDIFile):
    129     def ConvertMusicTimeToTicks (self, s):
    130         measures, beats, ticks = s.split(':',3)
    131         return self.ConvertToTicks(int(measures), int(beats), int(ticks))
    132 
    133     def ExtractEvents (self, start, end, length, quantize, chase_controllers):
    134         if (start is None) and (end is None) and (length is None):
    135             logging.debug('ExtractEvents: No change')
    136             return
    137 
    138         if start is not None:
    139             start = self.ConvertMusicTimeToTicks(start)
    140         else:
    141             start = 0
    142         if end is not None:
    143             end = self.ConvertMusicTimeToTicks(end)
    144         elif length is not None:
    145             length = self.ConvertMusicTimeToTicks(length)
    146             end = start + length
    147 
    148         if quantize is not None:
    149             quantize = int(quantize)
    150         else:
    151             quantize = 0
    152 
    153         self.Trim(start, end, quantize, chase_controllers=chase_controllers)
    154         #self.DumpTracks()

    155 
    156     def SyncClips (self):
    157         """Add controller events to the start of a clip to keep it synced."""
    158         values = None
    159         last_seq = 0
    160         for track in self.tracks:
    161             for event in track.events:
    162 
    163                 # find start of clip and chase events from last save point

    164                 if (event.msg_type == midifile.CONTROL_CHANGE) and \
    165                         (event.controller == JET_EVENT_TRIGGER_CLIP) and \
    166                         ((event.value & 0x40) == 0x40):
    167                     logging.debug('Syncing clip at %d ticks' % event.ticks)
    168                     values = track.events.ChaseControllers(event.seq, last_seq, values)
    169                     
    170                     #BTH; Seems to fix chase controller bug when multiple clips within segment

    171                     #last_seq = event.seq

    172 
    173                     # generate event list from default values

    174                     clip_events = values.GenerateEventList(event.ticks)
    175                     
    176                     #for evt in clip_events:

    177                     #    logging.info(evt)

    178                         
    179                     track.events.InsertEvents(clip_events, event.seq + 1)
    180 
    181     def AddJetEvents (self, jetevents):
    182         for jet_event in jetevents:
    183             if jet_event.event_type == JetDefs.E_CLIP:
    184                 #DumpEvent(jet_event)

    185                 
    186                 # sanity check

    187                 if jet_event.track_num >= len(self.tracks):
    188                     raise JetFileException('Track number %d of out of range for clip' % jet_event.track_num)
    189                 if jet_event.channel_num > 15:
    190                     raise JetFileException('Channel number %d of out of range for clip' % jet_event.channel_num)
    191                 if jet_event.event_id > 63:
    192                     raise JetFileException('event_id %d of out of range for clip' % jet_event.event_id)
    193     
    194                 logging.debug('Adding trigger event for clip %d @ %s and %s' % (jet_event.event_id, jet_event.event_start, jet_event.event_end))
    195     
    196                 events = midifile.EventList()
    197                 events.append(midifile.ControlChangeEvent(
    198                     self.ConvertMusicTimeToTicks(jet_event.event_start),
    199                     0,
    200                     jet_event.channel_num,
    201                     JET_EVENT_TRIGGER_CLIP,
    202                     jet_event.event_id | 0x40))
    203     
    204                 events.append(midifile.ControlChangeEvent(
    205                     self.ConvertMusicTimeToTicks(jet_event.event_end),
    206                     sys.maxint,
    207                     jet_event.channel_num,
    208                     JET_EVENT_TRIGGER_CLIP,
    209                     jet_event.event_id))
    210     
    211                 # merge trigger events

    212                 self.tracks[jet_event.track_num].events.MergeEvents(events)
    213                 
    214             elif jet_event.event_type == JetDefs.E_EOS:
    215                 if jet_event.track_num >= len(self.tracks):
    216                     raise JetFileException('Track number %d of out of range for end marker' % jet_event.track_num)
    217                 if jet_event.channel_num > 15:
    218                     raise JetFileException('Channel number %d of out of range for end marker' % jet_event.channel_num)
    219     
    220                 events = midifile.EventList()
    221                 logging.debug('Adding end marker at %s' % jet_event.event_start)
    222                 events.append(midifile.ControlChangeEvent(
    223                     self.ConvertMusicTimeToTicks(jet_event.event_start),
    224                     0,
    225                     jet_event.channel_num,
    226                     JET_EVENT_MARKER,
    227                     JET_MARKER_LOOP_END))
    228                 self.tracks[jet_event.track_num].events.MergeEvents(events)
    229 
    230             elif jet_event.event_type == JetDefs.E_APP:
    231                 if jet_event.track_num >= len(self.tracks):
    232                     raise JetFileException('Track number %d of out of range for app marker' % jet_event.track_num)
    233                 if jet_event.channel_num > 15:
    234                     raise JetFileException('Channel number %d of out of range for app marker' % jet_event.channel_num)
    235                 if jet_event.event_id > 83 or jet_event.event_id < 80:
    236                     raise JetFileException('EventID %d out of range for application controller' % jet_event.event_id)
    237     
    238                 events = midifile.EventList()
    239                 logging.debug('Adding application controller at %s' % jet_event.event_start)
    240                 events.append(midifile.ControlChangeEvent(
    241                     self.ConvertMusicTimeToTicks(jet_event.event_start),
    242                     0,
    243                     jet_event.channel_num,
    244                     jet_event.event_id,
    245                     jet_event.event_id))
    246                 self.tracks[jet_event.track_num].events.MergeEvents(events)
    247 
    248 class JetFile (object):
    249     """Write a JET file based on a configuration file."""
    250     def __init__ (self, config_file, options):
    251         self.config_file = config_file
    252         self.config = config = ConfigParser.ConfigParser()
    253         if self.config_file == "":
    254             self.InitializeConfig(JetDefs.UNTITLED_FILE)
    255         if not FileExists(self.config_file):
    256             self.InitializeConfig(self.config_file)
    257         
    258         config.read(self.config_file)
    259         self.ParseConfig(options)
    260 
    261     def DumpConfig (self):
    262         """Drump configuration to log file."""
    263         # dump configuration

    264         config = self.config
    265         for section in config.sections():
    266             logging.debug('[%s]' % section)
    267             for option, value in config.items(section):
    268                 logging.debug('%s: %s' % (option, value))
    269 
    270     def ParseConfig (self, options):
    271         """Validate the configuration."""
    272         # check for output name

    273         config = self.config
    274         if config.has_option(OUTPUT_SECTION, OUTPUT_FILENAME):
    275             config.filename = config.get(OUTPUT_SECTION, OUTPUT_FILENAME)
    276         else:
    277             raise JetFileException('No output filename in configuration file')
    278         if config.filename == '' or config.filename == None:
    279             config.filename = FileJustRoot(self.config_file) + ".JET"
    280         config.chase_controllers = True
    281         if config.has_option(OUTPUT_SECTION, OUTPUT_CHASE_CONTROLLERS):
    282             try:
    283                 config.chase_controllers = config.getboolean(OUTPUT_SECTION, OUTPUT_CHASE_CONTROLLERS)
    284             except:
    285                 pass
    286 
    287         config.delete_empty_tracks = False
    288         if config.has_option(OUTPUT_SECTION, OUTPUT_OMIT_EMPTY_TRACKS):
    289             try:
    290                 config.delete_empty_tracks = config.getboolean(OUTPUT_SECTION, OUTPUT_OMIT_EMPTY_TRACKS)
    291             except:
    292                 pass
    293                 
    294         config.copyright = None
    295         if config.has_option(OUTPUT_SECTION, OUTPUT_COPYRIGHT):
    296             config.copyright = config.get(OUTPUT_SECTION, OUTPUT_COPYRIGHT)
    297 
    298         config.app_data = None
    299         if config.has_option(OUTPUT_SECTION, OUTPUT_APP_DATA):
    300             config.app_data = config.get(OUTPUT_SECTION, OUTPUT_APP_DATA)
    301 
    302         # count segments

    303         segments = []
    304         seg_num = 0
    305         while 1:
    306 
    307             # check for segment section

    308             segment_name = SEGMENT_SECTION + str(seg_num)
    309             if not config.has_section(segment_name):
    310                 break
    311 
    312             # initialize some parameters

    313             start = end = length = output = end_marker = dlsfile = dump_file = None
    314             transpose = repeat = mute_flags = 0
    315             jetevents = []
    316 
    317             # get the segment parameters

    318             segname = config.get(segment_name, SEGMENT_NAME)
    319             filename = config.get(segment_name, SEGMENT_FILENAME)
    320             if config.has_option(segment_name, SEGMENT_DLSFILE):
    321                 dlsfile = config.get(segment_name, SEGMENT_DLSFILE)
    322             if config.has_option(segment_name, SEGMENT_START):
    323                 start = config.get(segment_name, SEGMENT_START)
    324             if config.has_option(segment_name, SEGMENT_END):
    325                 end = config.get(segment_name, SEGMENT_END)
    326             if config.has_option(segment_name, SEGMENT_LENGTH):
    327                 length = config.get(segment_name, SEGMENT_LENGTH)
    328             if config.has_option(segment_name, SEGMENT_OUTPUT):
    329                 output = config.get(segment_name, SEGMENT_OUTPUT)
    330             if config.has_option(segment_name, SEGMENT_QUANTIZE):
    331                 quantize = config.get(segment_name, SEGMENT_QUANTIZE)
    332             if config.has_option(segment_name, SEGMENT_DUMP_FILE):
    333                 dump_file = config.get(segment_name, SEGMENT_DUMP_FILE)
    334             #API FIELDS

    335             if config.has_option(segment_name, SEGMENT_TRANSPOSE):
    336                 transpose = config.get(segment_name, SEGMENT_TRANSPOSE)
    337             if config.has_option(segment_name, SEGMENT_REPEAT):
    338                 repeat = config.get(segment_name, SEGMENT_REPEAT)
    339             if config.has_option(segment_name, SEGMENT_MUTE_FLAGS):
    340                 mute_flags = config.get(segment_name, SEGMENT_MUTE_FLAGS)
    341             
    342             if config.has_option(segment_name, SEGMENT_END_MARKER):
    343                 end_marker = config.get(segment_name, SEGMENT_END_MARKER)
    344                 track_num, channel_num, event_time = end_marker.split(',',2)
    345                 #jetevents.append((JetDefs.E_EOS, 0, int(track_num), int(channel_num), event_time, ''))

    346                 jetevents.append(JetEvent(JetDefs.E_EOS, JetDefs.E_EOS, 0, int(track_num), int(channel_num), event_time, event_time))
    347 
    348             # check for jetevents

    349             for jetevent, location in config.items(segment_name):
    350                 if jetevent.startswith(CLIP_PREFIX):
    351                     event_name, event_id, track_num, channel_num, event_start, event_end = location.split(',', 5)
    352                     jetevents.append(JetEvent(event_name, JetDefs.E_CLIP, int(event_id), int(track_num), int(channel_num), event_start, event_end))
    353 
    354             # check for appevents

    355             for jetevent, location in config.items(segment_name):
    356                 if jetevent.startswith(APP_PREFIX):
    357                     event_name, event_id, track_num, channel_num, event_start, event_end = location.split(',', 5)
    358                     jetevents.append(JetEvent(event_name, JetDefs.E_APP, int(event_id), int(track_num), int(channel_num), event_start, event_end))
    359             
    360             segments.append(JetSegment(segname, filename, start, end, length, output, quantize, jetevents, dlsfile, dump_file, int(transpose), int(repeat), int(mute_flags)))
    361             seg_num += 1
    362 
    363         self.segments = segments
    364         if not len(segments):
    365             #TODO: Check for segments when writing

    366             #raise JetFileException('No segments defined in configuration file')

    367             pass
    368 
    369         # count libraries

    370         libraries = []
    371         lib_num = 0
    372         while 1:
    373             library_name = LIBRARY_FILENAME + str(lib_num)
    374             if not config.has_option(LIBRARY_SECTION, library_name):
    375                 break
    376             libraries.append(config.get(LIBRARY_SECTION, library_name))
    377             lib_num += 1
    378         self.libraries = libraries
    379 
    380     def WriteJetFileFromConfig (self, options):
    381         """Write JET file from config file."""
    382         
    383         # open the output file and write the header

    384         output_file = open(self.config.filename, 'wb')
    385         jet_header = struct.pack(JET_HEADER_STRUCT, JET_HEADER_TAG, 0)
    386         output_file.write(jet_header)
    387 
    388         # write the JINF chunk

    389         jet_info = struct.pack(JINF_STRUCT,
    390             JET_INFO_CHUNK, struct.calcsize(JINF_STRUCT) - 8,
    391             JINF_JET_VERSION, JET_VERSION,
    392             JINF_NUM_SMF_CHUNKS, len(self.segments),
    393             JINF_NUM_DLS_CHUNKS, len(self.libraries))
    394         output_file.write(jet_info)
    395 
    396         # write the JCOP chunk (if any)

    397         if self.config.copyright is not None:
    398             size = len(self.config.copyright) + 1
    399             if size & 1:
    400                 size += 1
    401                 extra_byte = True
    402             else:
    403                 extra_byte = False
    404             jet_copyright = struct.pack(JCOP_STRUCT, JCOP_CHUNK, size)
    405             output_file.write(jet_copyright)
    406             output_file.write(self.config.copyright)
    407             output_file.write(chr(0))
    408             if extra_byte:
    409                 output_file.write(chr(0))
    410 
    411         # write the app data chunk (if any)

    412         if self.config.app_data is not None:
    413             size = os.path.getsize(self.config.app_data)
    414             if size & 1:
    415                 size += 1
    416                 extra_byte = True
    417             else:
    418                 extra_byte = False
    419             jet_app_data = struct.pack(JAPP_STRUCT, JAPP_CHUNK, size)
    420             output_file.write(jet_app_data)
    421             with open(self.config.app_data, 'rb') as f:
    422                 output_file.write(f.read())
    423             if extra_byte:
    424                 output_file.write(chr(0))
    425 
    426         # copy the MIDI segments

    427         seg_num = 0
    428         for segment in self.segments:
    429             logging.debug('Writing segment %d' % seg_num)
    430 
    431             # open SMF file and read it

    432             jet_segfile = JetSegmentFile(segment.filename, 'rb')
    433             jet_segfile.ReadFromStream()
    434 
    435             # insert events

    436             jet_segfile.AddJetEvents(segment.jetevents)
    437 
    438             # trim to length specified in config file

    439             jet_segfile.ExtractEvents(segment.start, segment.end, segment.length, segment.quantize, self.config.chase_controllers)
    440 
    441             # chase controller events and fix them

    442             if self.config.chase_controllers:
    443                 jet_segfile.SyncClips()
    444 
    445             # delete empty tracks

    446             if self.config.delete_empty_tracks:
    447                 jet_segfile.DeleteEmptyTracks()
    448 
    449             # write separate output file if requested

    450             if segment.output is not None:
    451                 jet_segfile.SaveAs(segment.output)
    452 
    453             # write dump file

    454             if segment.dump_file is not None:
    455                 with open(segment.dump_file, 'w') as f:
    456                     jet_segfile.DumpTracks(f)
    457 
    458             # write the segment header

    459             header_pos = output_file.tell()
    460             smf_header = struct.pack(JET_HEADER_STRUCT, JET_SMF_CHUNK, 0)
    461             output_file.write(smf_header)
    462             start_pos = output_file.tell()
    463 
    464             # write SMF file to output file

    465             jet_segfile.Write(output_file, offset=start_pos)
    466             jet_segfile.close()
    467 
    468             # return to segment header and write actual size

    469             end_pos = output_file.tell()
    470             file_size = end_pos - start_pos
    471             if file_size & 1:
    472                 file_size += 1
    473                 end_pos += 1
    474             output_file.seek(header_pos, 0)
    475             smf_header = struct.pack(JET_HEADER_STRUCT, JET_SMF_CHUNK, file_size)
    476             output_file.write(smf_header)
    477             output_file.seek(end_pos, 0)
    478 
    479             seg_num += 1
    480 
    481         # copy the DLS segments

    482         for library in self.libraries:
    483             if FileExists(library):
    484                 # open SMF file and get size

    485                 lib_file = (open(library,'rb'))
    486                 lib_file.seek(0,2)
    487                 file_size = lib_file.tell()
    488                 lib_file.seek(0)
    489     
    490                 # write the library header

    491                 dls_header = struct.pack(JET_HEADER_STRUCT, JET_DLS_CHUNK, file_size)
    492                 output_file.write(dls_header)
    493     
    494                 # copy DLS file to output file

    495                 output_file.write(lib_file.read())
    496                 lib_file.close()
    497 
    498         # write the header with the read data size

    499         file_size = output_file.tell()
    500         output_file.seek(0)
    501         jet_header = struct.pack(JET_HEADER_STRUCT, JET_HEADER_TAG, file_size - struct.calcsize(JET_HEADER_STRUCT))
    502         output_file.write(jet_header)
    503         output_file.close()
    504         
    505     def GetMidiFiles(self):
    506         """ Gets a list of midifiles """
    507         midiFiles = []
    508         for segment in self.segments:
    509             if segment.filename not in midiFiles:
    510                  midiFiles.append(segment.filename) 
    511         return midiFiles
    512             
    513     def GetLibraries(self):
    514         """ Gets the libraries """
    515         return self.libraries
    516     
    517     def GetEvents(self, segName):
    518         """ Gets the events for a segment """
    519         for segment in self.segments:
    520             if segment.segname == segName:
    521                 return segment.jetevents
    522         return None
    523     
    524     def GetEvent(self, segName, eventName):
    525         """ Gets a single event from a segment """
    526         for segment in self.segments:
    527             if segment.segname == segName:
    528                 for event in segment.jetevents:
    529                     if event.event_name == eventName:
    530                         return event
    531         return None
    532         
    533     def AddEvent(self, segname, event_name, event_type, event_id, track_num, channel_num, event_start, event_end):    
    534         """ Adds an event """
    535         for segment in self.segments:
    536             if segment.segname == segname:
    537                 segment.jetevents.append(JetEvent(event_name, event_type, int(event_id), int(track_num), int(channel_num), event_start, event_end))
    538     
    539     def ReplaceEvents(self, segname, newEvents):
    540         """ Replaces all events """
    541         for segment in self.segments:
    542             if segment.segname == segname:
    543                 segment.jetevents = newEvents
    544                 return segment       
    545         
    546     def UpdateEvent(self, segname, orgeventname, event_name, event_type, event_id, track_num, channel_num, event_start, event_end):
    547         """ Updates an event """    
    548         for segment in self.segments:
    549             if segment.segname == segname:
    550                 for jetevent in segment.jetevents:
    551                     if jetevent.event_name == orgeventname:
    552                         jetevent.event_name = event_name
    553                         jetevent.event_type = event_type
    554                         jetevent.event_id = event_id
    555                         jetevent.track_num = track_num
    556                         jetevent.channel_num = channel_num
    557                         jetevent.event_start = event_start
    558                         jetevent.event_end = event_end
    559                        
    560     def DeleteSegmentsMatchingPrefix(self, prefix):
    561         """ Deletes all segments matching name """
    562         iOnce = True
    563         iAgain = False
    564         while(iOnce or iAgain):
    565             iOnce = False
    566             iAgain = False
    567             for segment in self.segments:
    568                 if segment.segname[0:len(prefix)].upper() == prefix.upper():
    569                     self.segments.remove(segment)
    570                     iAgain = True
    571                             
    572     def DeleteEvent(self, segname, event_name):
    573         """ Deletes an event """
    574         for segment in self.segments:
    575             if segment.segname == segname:
    576                 for jetevent in segment.jetevents:
    577                     if jetevent.event_name == event_name:
    578                         segment.jetevents.remove(jetevent)
    579 
    580     def DeleteEventsMatchingPrefix(self, segname, prefix):
    581         """ Deletes all events matching name """
    582         for segment in self.segments:
    583             if segment.segname == segname:
    584                 iOnce = True
    585                 iAgain = False
    586                 while(iOnce or iAgain):
    587                     iOnce = False
    588                     iAgain = False
    589                     for jetevent in segment.jetevents:
    590                         if jetevent.event_name[0:len(prefix)].upper() == prefix.upper():
    591                             segment.jetevents.remove(jetevent)
    592                             iAgain = True
    593 
    594     def MoveEvent(self, segname, movename, event_start, event_end):
    595         """ Move an event """    
    596         for segment in self.segments:
    597             if segment.segname == segname:
    598                 for jetevent in segment.jetevents:
    599                     if jetevent.event_name == movename:
    600                         jetevent.event_start = event_start
    601                         jetevent.event_end = event_end
    602                         return
    603             
    604     def GetSegments(self):
    605         """ Gets all segments """
    606         return self.segments
    607    
    608     def GetSegment(self, segName):
    609         """ Gets one segment by name """
    610         for segment in self.segments:
    611             if segment.segname == segName:
    612                 return segment
    613         return None
    614         
    615     def AddSegment(self, segname, filename, start, end, length, output, quantize, jetevents, dlsfile, dump_file, transpose, repeat, mute_flags):
    616         """ Adds a segment """
    617         if length == JetDefs.MBT_ZEROSTR:
    618             length = None
    619         if end == JetDefs.MBT_ZEROSTR:
    620             end = None
    621         self.segments.append(JetSegment(segname, filename, start, end, length, output, quantize, jetevents, dlsfile, dump_file, transpose, repeat, mute_flags))
    622     
    623     def UpdateSegment(self, orgsegname, segname, filename, start, end, length, output, quantize, jetevents, dlsfile, dump_file, transpose, repeat, mute_flags):
    624         """ Updates a segment """
    625         if length == JetDefs.MBT_ZEROSTR:
    626             length = None
    627         if end == JetDefs.MBT_ZEROSTR:
    628             end = None
    629         for segment in self.segments:
    630             if segment.segname == orgsegname:
    631                 segment.segname = segname
    632                 segment.filename = filename
    633                 segment.start = start
    634                 segment.end = end
    635                 segment.length = length
    636                 segment.output = output
    637                 segment.quantize = quantize
    638                 segment.dlsfile = dlsfile
    639                 segment.transpose = transpose
    640                 segment.repeat = repeat
    641                 segment.mute_flags = mute_flags
    642 
    643     def MoveSegment(self, segname, start, end):
    644         """ Moves a segment """
    645         for segment in self.segments:
    646             if segment.segname == segname:
    647                 segment.start = start
    648                 segment.end = end
    649                 return
    650 
    651     def DeleteSegment(self, segname):
    652         """ Deletes a segment """
    653         for segment in self.segments:
    654             if segment.segname == segname:
    655                 self.segments.remove(segment)
    656 
    657     def SaveJetConfig(self, configFile):
    658         """ Saves the jet config file """
    659         if self.config.filename == '' or self.config.filename == None:
    660             self.config.filename = FileJustRoot(configFile) + ".JET"
    661         config = ConfigParser.ConfigParser()
    662         config.add_section(OUTPUT_SECTION)
    663         config.set(OUTPUT_SECTION, OUTPUT_FILENAME, self.config.filename)
    664         config.set(OUTPUT_SECTION, OUTPUT_CHASE_CONTROLLERS, self.config.chase_controllers)
    665         config.set(OUTPUT_SECTION, OUTPUT_OMIT_EMPTY_TRACKS, self.config.delete_empty_tracks)
    666         if self.config.copyright is not None:
    667             config.set(OUTPUT_SECTION, OUTPUT_COPYRIGHT, self.config.copyright)
    668         if self.config.app_data is not None:
    669             config.set(OUTPUT_SECTION, OUTPUT_APP_DATA, self.config.app_data)
    670 
    671         self.libraries = []
    672         seg_num = 0
    673         for segment in self.segments:
    674             segment_name = SEGMENT_SECTION + str(seg_num)
    675             config.add_section(segment_name)
    676             config.set(segment_name, SEGMENT_NAME, segment.segname)
    677             config.set(segment_name, SEGMENT_FILENAME, segment.filename)
    678             
    679             config.set(segment_name, SEGMENT_DLSFILE, segment.dlsfile)
    680             if FileExists(segment.dlsfile):
    681                 if not segment.dlsfile in self.libraries:
    682                     self.libraries.append(segment.dlsfile)
    683             config.set(segment_name, SEGMENT_START, segment.start)
    684             if segment.end > JetDefs.MBT_ZEROSTR and len(segment.end) > 0:
    685                 config.set(segment_name, SEGMENT_END, segment.end)
    686             if segment.length > JetDefs.MBT_ZEROSTR and len(segment.length) > 0:
    687                 config.set(segment_name, SEGMENT_LENGTH, segment.length)
    688             config.set(segment_name, SEGMENT_OUTPUT, segment.output)
    689             config.set(segment_name, SEGMENT_QUANTIZE, segment.quantize)
    690             if segment.dump_file is not None:
    691                 config.set(segment_name, SEGMENT_DUMP_FILE, segment.dump_file)
    692             config.set(segment_name, SEGMENT_TRANSPOSE, segment.transpose)
    693             config.set(segment_name, SEGMENT_REPEAT, segment.repeat)
    694             config.set(segment_name, SEGMENT_MUTE_FLAGS, segment.mute_flags)
    695 
    696             clip_num = 0
    697             app_num = 0
    698             for jet_event in segment.jetevents:
    699                 if jet_event.event_type == JetDefs.E_CLIP:
    700                     clip_name = CLIP_PREFIX + str(clip_num)
    701                     s = "%s,%s,%s,%s,%s,%s" % (jet_event.event_name, jet_event.event_id, jet_event.track_num, jet_event.channel_num, jet_event.event_start, jet_event.event_end)
    702                     config.set(segment_name, clip_name, s)
    703                     clip_num += 1
    704                 elif jet_event.event_type == JetDefs.E_APP:
    705                     app_name = APP_PREFIX + str(app_num)
    706                     s = "%s,%s,%s,%s,%s,%s" % (jet_event.event_name, jet_event.event_id, jet_event.track_num, jet_event.channel_num, jet_event.event_start, jet_event.event_end)
    707                     config.set(segment_name, app_name, s)
    708                     app_num += 1
    709                 elif jet_event.event_type == JetDefs.E_EOS:
    710                     s = "%s,%s,%s" % (jet_event.track_num, jet_event.channel_num, jet_event.event_start)
    711                     config.set(segment_name, SEGMENT_END_MARKER, s)
    712                 
    713             seg_num += 1
    714                         
    715         lib_num = 0
    716         config.add_section(LIBRARY_SECTION)
    717         for library in self.libraries:
    718             library_name = LIBRARY_FILENAME + str(lib_num)
    719             config.set(LIBRARY_SECTION, library_name, library)
    720             lib_num += 1
    721         
    722         FileKillClean(configFile)
    723         cfgfile = open(configFile,'w')
    724         config.write(cfgfile)
    725         cfgfile.close()
    726     
    727     def InitializeConfig(self, configFile):
    728         """ Initializes the values for an empty flag """
    729         self.config.filename = FileJustRoot(configFile)  + ".JET"
    730         self.config.chase_controllers = True
    731         self.config.delete_empty_tracks = False
    732         self.config.copyright = None
    733         self.config.app_data = None
    734         self.segments = []
    735         self.libraries = []
    736         self.config_file = configFile
    737         self.SaveJetConfig(configFile)
    738         
    739         
    740 
    741 #---------------------------------------------------------------

    742 # main

    743 #---------------------------------------------------------------

    744 if __name__ == '__main__':
    745     sys = __import__('sys')
    746     optparse = __import__('optparse')
    747 
    748     # parse command line options

    749     parser = optparse.OptionParser(version=VERSION)
    750     parser.set_defaults(log_level=logging.INFO, log_file=None)
    751     parser.add_option('-d', '--debug', action="store_const", const=logging.DEBUG, dest='log_level', help='Enable debug output')
    752     parser.add_option('-l', '--log_file', dest='log_file', help='Write debug output to log file')
    753     (options, args) = parser.parse_args()
    754 
    755     # get master logger

    756     logger = logging.getLogger('')
    757     logger.setLevel(options.log_level)
    758 
    759     # create console logger

    760     console_logger = logging.StreamHandler()
    761     console_logger.setFormatter(logging.Formatter('%(message)s'))
    762     logger.addHandler(console_logger)
    763 
    764     # create rotating file logger

    765     if options.log_file is not None:
    766         file_logger = logging.FileHandler(options.log_file, 'w')
    767         file_logger.setFormatter(logging.Formatter('%(message)s'))
    768         logger.addHandler(file_logger)
    769 
    770     # process files

    771     for arg in args:
    772         print arg
    773         jet_file = JetFile(arg, options)
    774         jet_file.WriteJetFileFromConfig(options)
    775 
    776