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
23from __future__ import with_statement
24
25import logging
26import ConfigParser
27import struct
28import os
29import sys
30import midifile
31
32from JetUtils import *
33from JetDefs import *
34
35VERSION = '0.1'
36
37# JET file defines
38JET_HEADER_STRUCT = '<4sl'
39JET_HEADER_TAG = 'JET '
40JET_VERSION = 0x01000000
41
42# JET chunk tags
43JET_INFO_CHUNK = 'JINF'
44JET_SMF_CHUNK = 'JSMF'
45JET_DLS_CHUNK = 'JDLS'
46
47# JINF defines
48JINF_STRUCT = '<4sl4sl4sl4sl'
49JINF_JET_VERSION = 'JVER'
50JINF_NUM_SMF_CHUNKS = 'SMF#'
51JINF_NUM_DLS_CHUNKS = 'DLS#'
52
53# JCOP defines
54JCOP_STRUCT = '<4sl'
55JCOP_CHUNK = 'JCOP'
56
57# JAPP defines
58JAPP_STRUCT = '<4sl'
59JAPP_CHUNK = 'JAPP'
60
61# config file defines
62OUTPUT_SECTION = 'output'
63OUTPUT_FILENAME = 'filename'
64OUTPUT_COPYRIGHT = 'copyright'
65OUTPUT_APP_DATA = 'app_data'
66OUTPUT_CHASE_CONTROLLERS = 'chase_controllers'
67OUTPUT_OMIT_EMPTY_TRACKS = 'omit_empty_tracks'
68SEGMENT_SECTION = 'segment'
69SEGMENT_FILENAME = 'filename'
70SEGMENT_DLSFILE = 'dlsfile'
71SEGMENT_NAME = 'segname'
72SEGMENT_START = 'start'
73SEGMENT_END = 'end'
74SEGMENT_END_MARKER = 'end_marker'
75SEGMENT_QUANTIZE = 'quantize'
76SEGMENT_OUTPUT = 'output'
77SEGMENT_LENGTH = 'length'
78SEGMENT_DUMP_FILE = 'dump'
79SEGMENT_TRANSPOSE = 'transpose'
80SEGMENT_REPEAT = 'repeat'
81SEGMENT_MUTE_FLAGS = 'mute_flags'
82LIBRARY_SECTION = 'libraries'
83LIBRARY_FILENAME = 'lib'
84CLIP_PREFIX = 'clip'
85APP_PREFIX = 'app'
86
87# JET events
88JET_EVENT_MARKER = 102
89JET_MARKER_LOOP_END = 0
90JET_EVENT_TRIGGER_CLIP = 103
91
92class 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
110class 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
121class JetFileException (Exception):
122    """ Exceptions class """
123    def __init__ (self, msg):
124        self.msg = msg
125    def __str__ (self):
126        return self.msg
127
128class 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
248class 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#---------------------------------------------------------------
744if __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