""" File: JetFile.py Contents and purpose: Auditions a jet file to simulate interactive music functions Copyright (c) 2008 Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from __future__ import with_statement import logging import ConfigParser import struct import os import sys import midifile from JetUtils import * from JetDefs import * VERSION = '0.1' # JET file defines JET_HEADER_STRUCT = '<4sl' JET_HEADER_TAG = 'JET ' JET_VERSION = 0x01000000 # JET chunk tags JET_INFO_CHUNK = 'JINF' JET_SMF_CHUNK = 'JSMF' JET_DLS_CHUNK = 'JDLS' # JINF defines JINF_STRUCT = '<4sl4sl4sl4sl' JINF_JET_VERSION = 'JVER' JINF_NUM_SMF_CHUNKS = 'SMF#' JINF_NUM_DLS_CHUNKS = 'DLS#' # JCOP defines JCOP_STRUCT = '<4sl' JCOP_CHUNK = 'JCOP' # JAPP defines JAPP_STRUCT = '<4sl' JAPP_CHUNK = 'JAPP' # config file defines OUTPUT_SECTION = 'output' OUTPUT_FILENAME = 'filename' OUTPUT_COPYRIGHT = 'copyright' OUTPUT_APP_DATA = 'app_data' OUTPUT_CHASE_CONTROLLERS = 'chase_controllers' OUTPUT_OMIT_EMPTY_TRACKS = 'omit_empty_tracks' SEGMENT_SECTION = 'segment' SEGMENT_FILENAME = 'filename' SEGMENT_DLSFILE = 'dlsfile' SEGMENT_NAME = 'segname' SEGMENT_START = 'start' SEGMENT_END = 'end' SEGMENT_END_MARKER = 'end_marker' SEGMENT_QUANTIZE = 'quantize' SEGMENT_OUTPUT = 'output' SEGMENT_LENGTH = 'length' SEGMENT_DUMP_FILE = 'dump' SEGMENT_TRANSPOSE = 'transpose' SEGMENT_REPEAT = 'repeat' SEGMENT_MUTE_FLAGS = 'mute_flags' LIBRARY_SECTION = 'libraries' LIBRARY_FILENAME = 'lib' CLIP_PREFIX = 'clip' APP_PREFIX = 'app' # JET events JET_EVENT_MARKER = 102 JET_MARKER_LOOP_END = 0 JET_EVENT_TRIGGER_CLIP = 103 class JetSegment (object): """ Class to hold segments """ 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): self.segname = segname self.filename = filename self.dlsfile = dlsfile self.start = start self.end = end self.length = length self.output = output self.quantize = quantize self.dump_file = dump_file self.jetevents = jetevents #API FIELDS FOR UI self.transpose = transpose self.repeat = repeat self.mute_flags = mute_flags class JetEvent (object): """ Class to hold events """ def __init__(self, event_name, event_type, event_id, track_num, channel_num, event_start, event_end): self.event_name = event_name self.event_type = event_type self.event_id = event_id self.track_num = track_num self.channel_num = channel_num self.event_start = event_start self.event_end = event_end class JetFileException (Exception): """ Exceptions class """ def __init__ (self, msg): self.msg = msg def __str__ (self): return self.msg class JetSegmentFile (midifile.MIDIFile): def ConvertMusicTimeToTicks (self, s): measures, beats, ticks = s.split(':',3) return self.ConvertToTicks(int(measures), int(beats), int(ticks)) def ExtractEvents (self, start, end, length, quantize, chase_controllers): if (start is None) and (end is None) and (length is None): logging.debug('ExtractEvents: No change') return if start is not None: start = self.ConvertMusicTimeToTicks(start) else: start = 0 if end is not None: end = self.ConvertMusicTimeToTicks(end) elif length is not None: length = self.ConvertMusicTimeToTicks(length) end = start + length if quantize is not None: quantize = int(quantize) else: quantize = 0 self.Trim(start, end, quantize, chase_controllers=chase_controllers) #self.DumpTracks() def SyncClips (self): """Add controller events to the start of a clip to keep it synced.""" values = None last_seq = 0 for track in self.tracks: for event in track.events: # find start of clip and chase events from last save point if (event.msg_type == midifile.CONTROL_CHANGE) and \ (event.controller == JET_EVENT_TRIGGER_CLIP) and \ ((event.value & 0x40) == 0x40): logging.debug('Syncing clip at %d ticks' % event.ticks) values = track.events.ChaseControllers(event.seq, last_seq, values) #BTH; Seems to fix chase controller bug when multiple clips within segment #last_seq = event.seq # generate event list from default values clip_events = values.GenerateEventList(event.ticks) #for evt in clip_events: # logging.info(evt) track.events.InsertEvents(clip_events, event.seq + 1) def AddJetEvents (self, jetevents): for jet_event in jetevents: if jet_event.event_type == JetDefs.E_CLIP: #DumpEvent(jet_event) # sanity check if jet_event.track_num >= len(self.tracks): raise JetFileException('Track number %d of out of range for clip' % jet_event.track_num) if jet_event.channel_num > 15: raise JetFileException('Channel number %d of out of range for clip' % jet_event.channel_num) if jet_event.event_id > 63: raise JetFileException('event_id %d of out of range for clip' % jet_event.event_id) logging.debug('Adding trigger event for clip %d @ %s and %s' % (jet_event.event_id, jet_event.event_start, jet_event.event_end)) events = midifile.EventList() events.append(midifile.ControlChangeEvent( self.ConvertMusicTimeToTicks(jet_event.event_start), 0, jet_event.channel_num, JET_EVENT_TRIGGER_CLIP, jet_event.event_id | 0x40)) events.append(midifile.ControlChangeEvent( self.ConvertMusicTimeToTicks(jet_event.event_end), sys.maxint, jet_event.channel_num, JET_EVENT_TRIGGER_CLIP, jet_event.event_id)) # merge trigger events self.tracks[jet_event.track_num].events.MergeEvents(events) elif jet_event.event_type == JetDefs.E_EOS: if jet_event.track_num >= len(self.tracks): raise JetFileException('Track number %d of out of range for end marker' % jet_event.track_num) if jet_event.channel_num > 15: raise JetFileException('Channel number %d of out of range for end marker' % jet_event.channel_num) events = midifile.EventList() logging.debug('Adding end marker at %s' % jet_event.event_start) events.append(midifile.ControlChangeEvent( self.ConvertMusicTimeToTicks(jet_event.event_start), 0, jet_event.channel_num, JET_EVENT_MARKER, JET_MARKER_LOOP_END)) self.tracks[jet_event.track_num].events.MergeEvents(events) elif jet_event.event_type == JetDefs.E_APP: if jet_event.track_num >= len(self.tracks): raise JetFileException('Track number %d of out of range for app marker' % jet_event.track_num) if jet_event.channel_num > 15: raise JetFileException('Channel number %d of out of range for app marker' % jet_event.channel_num) if jet_event.event_id > 83 or jet_event.event_id < 80: raise JetFileException('EventID %d out of range for application controller' % jet_event.event_id) events = midifile.EventList() logging.debug('Adding application controller at %s' % jet_event.event_start) events.append(midifile.ControlChangeEvent( self.ConvertMusicTimeToTicks(jet_event.event_start), 0, jet_event.channel_num, jet_event.event_id, jet_event.event_id)) self.tracks[jet_event.track_num].events.MergeEvents(events) class JetFile (object): """Write a JET file based on a configuration file.""" def __init__ (self, config_file, options): self.config_file = config_file self.config = config = ConfigParser.ConfigParser() if self.config_file == "": self.InitializeConfig(JetDefs.UNTITLED_FILE) if not FileExists(self.config_file): self.InitializeConfig(self.config_file) config.read(self.config_file) self.ParseConfig(options) def DumpConfig (self): """Drump configuration to log file.""" # dump configuration config = self.config for section in config.sections(): logging.debug('[%s]' % section) for option, value in config.items(section): logging.debug('%s: %s' % (option, value)) def ParseConfig (self, options): """Validate the configuration.""" # check for output name config = self.config if config.has_option(OUTPUT_SECTION, OUTPUT_FILENAME): config.filename = config.get(OUTPUT_SECTION, OUTPUT_FILENAME) else: raise JetFileException('No output filename in configuration file') if config.filename == '' or config.filename == None: config.filename = FileJustRoot(self.config_file) + ".JET" config.chase_controllers = True if config.has_option(OUTPUT_SECTION, OUTPUT_CHASE_CONTROLLERS): try: config.chase_controllers = config.getboolean(OUTPUT_SECTION, OUTPUT_CHASE_CONTROLLERS) except: pass config.delete_empty_tracks = False if config.has_option(OUTPUT_SECTION, OUTPUT_OMIT_EMPTY_TRACKS): try: config.delete_empty_tracks = config.getboolean(OUTPUT_SECTION, OUTPUT_OMIT_EMPTY_TRACKS) except: pass config.copyright = None if config.has_option(OUTPUT_SECTION, OUTPUT_COPYRIGHT): config.copyright = config.get(OUTPUT_SECTION, OUTPUT_COPYRIGHT) config.app_data = None if config.has_option(OUTPUT_SECTION, OUTPUT_APP_DATA): config.app_data = config.get(OUTPUT_SECTION, OUTPUT_APP_DATA) # count segments segments = [] seg_num = 0 while 1: # check for segment section segment_name = SEGMENT_SECTION + str(seg_num) if not config.has_section(segment_name): break # initialize some parameters start = end = length = output = end_marker = dlsfile = dump_file = None transpose = repeat = mute_flags = 0 jetevents = [] # get the segment parameters segname = config.get(segment_name, SEGMENT_NAME) filename = config.get(segment_name, SEGMENT_FILENAME) if config.has_option(segment_name, SEGMENT_DLSFILE): dlsfile = config.get(segment_name, SEGMENT_DLSFILE) if config.has_option(segment_name, SEGMENT_START): start = config.get(segment_name, SEGMENT_START) if config.has_option(segment_name, SEGMENT_END): end = config.get(segment_name, SEGMENT_END) if config.has_option(segment_name, SEGMENT_LENGTH): length = config.get(segment_name, SEGMENT_LENGTH) if config.has_option(segment_name, SEGMENT_OUTPUT): output = config.get(segment_name, SEGMENT_OUTPUT) if config.has_option(segment_name, SEGMENT_QUANTIZE): quantize = config.get(segment_name, SEGMENT_QUANTIZE) if config.has_option(segment_name, SEGMENT_DUMP_FILE): dump_file = config.get(segment_name, SEGMENT_DUMP_FILE) #API FIELDS if config.has_option(segment_name, SEGMENT_TRANSPOSE): transpose = config.get(segment_name, SEGMENT_TRANSPOSE) if config.has_option(segment_name, SEGMENT_REPEAT): repeat = config.get(segment_name, SEGMENT_REPEAT) if config.has_option(segment_name, SEGMENT_MUTE_FLAGS): mute_flags = config.get(segment_name, SEGMENT_MUTE_FLAGS) if config.has_option(segment_name, SEGMENT_END_MARKER): end_marker = config.get(segment_name, SEGMENT_END_MARKER) track_num, channel_num, event_time = end_marker.split(',',2) #jetevents.append((JetDefs.E_EOS, 0, int(track_num), int(channel_num), event_time, '')) jetevents.append(JetEvent(JetDefs.E_EOS, JetDefs.E_EOS, 0, int(track_num), int(channel_num), event_time, event_time)) # check for jetevents for jetevent, location in config.items(segment_name): if jetevent.startswith(CLIP_PREFIX): event_name, event_id, track_num, channel_num, event_start, event_end = location.split(',', 5) jetevents.append(JetEvent(event_name, JetDefs.E_CLIP, int(event_id), int(track_num), int(channel_num), event_start, event_end)) # check for appevents for jetevent, location in config.items(segment_name): if jetevent.startswith(APP_PREFIX): event_name, event_id, track_num, channel_num, event_start, event_end = location.split(',', 5) jetevents.append(JetEvent(event_name, JetDefs.E_APP, int(event_id), int(track_num), int(channel_num), event_start, event_end)) segments.append(JetSegment(segname, filename, start, end, length, output, quantize, jetevents, dlsfile, dump_file, int(transpose), int(repeat), int(mute_flags))) seg_num += 1 self.segments = segments if not len(segments): #TODO: Check for segments when writing #raise JetFileException('No segments defined in configuration file') pass # count libraries libraries = [] lib_num = 0 while 1: library_name = LIBRARY_FILENAME + str(lib_num) if not config.has_option(LIBRARY_SECTION, library_name): break libraries.append(config.get(LIBRARY_SECTION, library_name)) lib_num += 1 self.libraries = libraries def WriteJetFileFromConfig (self, options): """Write JET file from config file.""" # open the output file and write the header output_file = open(self.config.filename, 'wb') jet_header = struct.pack(JET_HEADER_STRUCT, JET_HEADER_TAG, 0) output_file.write(jet_header) # write the JINF chunk jet_info = struct.pack(JINF_STRUCT, JET_INFO_CHUNK, struct.calcsize(JINF_STRUCT) - 8, JINF_JET_VERSION, JET_VERSION, JINF_NUM_SMF_CHUNKS, len(self.segments), JINF_NUM_DLS_CHUNKS, len(self.libraries)) output_file.write(jet_info) # write the JCOP chunk (if any) if self.config.copyright is not None: size = len(self.config.copyright) + 1 if size & 1: size += 1 extra_byte = True else: extra_byte = False jet_copyright = struct.pack(JCOP_STRUCT, JCOP_CHUNK, size) output_file.write(jet_copyright) output_file.write(self.config.copyright) output_file.write(chr(0)) if extra_byte: output_file.write(chr(0)) # write the app data chunk (if any) if self.config.app_data is not None: size = os.path.getsize(self.config.app_data) if size & 1: size += 1 extra_byte = True else: extra_byte = False jet_app_data = struct.pack(JAPP_STRUCT, JAPP_CHUNK, size) output_file.write(jet_app_data) with open(self.config.app_data, 'rb') as f: output_file.write(f.read()) if extra_byte: output_file.write(chr(0)) # copy the MIDI segments seg_num = 0 for segment in self.segments: logging.debug('Writing segment %d' % seg_num) # open SMF file and read it jet_segfile = JetSegmentFile(segment.filename, 'rb') jet_segfile.ReadFromStream() # insert events jet_segfile.AddJetEvents(segment.jetevents) # trim to length specified in config file jet_segfile.ExtractEvents(segment.start, segment.end, segment.length, segment.quantize, self.config.chase_controllers) # chase controller events and fix them if self.config.chase_controllers: jet_segfile.SyncClips() # delete empty tracks if self.config.delete_empty_tracks: jet_segfile.DeleteEmptyTracks() # write separate output file if requested if segment.output is not None: jet_segfile.SaveAs(segment.output) # write dump file if segment.dump_file is not None: with open(segment.dump_file, 'w') as f: jet_segfile.DumpTracks(f) # write the segment header header_pos = output_file.tell() smf_header = struct.pack(JET_HEADER_STRUCT, JET_SMF_CHUNK, 0) output_file.write(smf_header) start_pos = output_file.tell() # write SMF file to output file jet_segfile.Write(output_file, offset=start_pos) jet_segfile.close() # return to segment header and write actual size end_pos = output_file.tell() file_size = end_pos - start_pos if file_size & 1: file_size += 1 end_pos += 1 output_file.seek(header_pos, 0) smf_header = struct.pack(JET_HEADER_STRUCT, JET_SMF_CHUNK, file_size) output_file.write(smf_header) output_file.seek(end_pos, 0) seg_num += 1 # copy the DLS segments for library in self.libraries: if FileExists(library): # open SMF file and get size lib_file = (open(library,'rb')) lib_file.seek(0,2) file_size = lib_file.tell() lib_file.seek(0) # write the library header dls_header = struct.pack(JET_HEADER_STRUCT, JET_DLS_CHUNK, file_size) output_file.write(dls_header) # copy DLS file to output file output_file.write(lib_file.read()) lib_file.close() # write the header with the read data size file_size = output_file.tell() output_file.seek(0) jet_header = struct.pack(JET_HEADER_STRUCT, JET_HEADER_TAG, file_size - struct.calcsize(JET_HEADER_STRUCT)) output_file.write(jet_header) output_file.close() def GetMidiFiles(self): """ Gets a list of midifiles """ midiFiles = [] for segment in self.segments: if segment.filename not in midiFiles: midiFiles.append(segment.filename) return midiFiles def GetLibraries(self): """ Gets the libraries """ return self.libraries def GetEvents(self, segName): """ Gets the events for a segment """ for segment in self.segments: if segment.segname == segName: return segment.jetevents return None def GetEvent(self, segName, eventName): """ Gets a single event from a segment """ for segment in self.segments: if segment.segname == segName: for event in segment.jetevents: if event.event_name == eventName: return event return None def AddEvent(self, segname, event_name, event_type, event_id, track_num, channel_num, event_start, event_end): """ Adds an event """ for segment in self.segments: if segment.segname == segname: segment.jetevents.append(JetEvent(event_name, event_type, int(event_id), int(track_num), int(channel_num), event_start, event_end)) def ReplaceEvents(self, segname, newEvents): """ Replaces all events """ for segment in self.segments: if segment.segname == segname: segment.jetevents = newEvents return segment def UpdateEvent(self, segname, orgeventname, event_name, event_type, event_id, track_num, channel_num, event_start, event_end): """ Updates an event """ for segment in self.segments: if segment.segname == segname: for jetevent in segment.jetevents: if jetevent.event_name == orgeventname: jetevent.event_name = event_name jetevent.event_type = event_type jetevent.event_id = event_id jetevent.track_num = track_num jetevent.channel_num = channel_num jetevent.event_start = event_start jetevent.event_end = event_end def DeleteSegmentsMatchingPrefix(self, prefix): """ Deletes all segments matching name """ iOnce = True iAgain = False while(iOnce or iAgain): iOnce = False iAgain = False for segment in self.segments: if segment.segname[0:len(prefix)].upper() == prefix.upper(): self.segments.remove(segment) iAgain = True def DeleteEvent(self, segname, event_name): """ Deletes an event """ for segment in self.segments: if segment.segname == segname: for jetevent in segment.jetevents: if jetevent.event_name == event_name: segment.jetevents.remove(jetevent) def DeleteEventsMatchingPrefix(self, segname, prefix): """ Deletes all events matching name """ for segment in self.segments: if segment.segname == segname: iOnce = True iAgain = False while(iOnce or iAgain): iOnce = False iAgain = False for jetevent in segment.jetevents: if jetevent.event_name[0:len(prefix)].upper() == prefix.upper(): segment.jetevents.remove(jetevent) iAgain = True def MoveEvent(self, segname, movename, event_start, event_end): """ Move an event """ for segment in self.segments: if segment.segname == segname: for jetevent in segment.jetevents: if jetevent.event_name == movename: jetevent.event_start = event_start jetevent.event_end = event_end return def GetSegments(self): """ Gets all segments """ return self.segments def GetSegment(self, segName): """ Gets one segment by name """ for segment in self.segments: if segment.segname == segName: return segment return None def AddSegment(self, segname, filename, start, end, length, output, quantize, jetevents, dlsfile, dump_file, transpose, repeat, mute_flags): """ Adds a segment """ if length == JetDefs.MBT_ZEROSTR: length = None if end == JetDefs.MBT_ZEROSTR: end = None self.segments.append(JetSegment(segname, filename, start, end, length, output, quantize, jetevents, dlsfile, dump_file, transpose, repeat, mute_flags)) def UpdateSegment(self, orgsegname, segname, filename, start, end, length, output, quantize, jetevents, dlsfile, dump_file, transpose, repeat, mute_flags): """ Updates a segment """ if length == JetDefs.MBT_ZEROSTR: length = None if end == JetDefs.MBT_ZEROSTR: end = None for segment in self.segments: if segment.segname == orgsegname: segment.segname = segname segment.filename = filename segment.start = start segment.end = end segment.length = length segment.output = output segment.quantize = quantize segment.dlsfile = dlsfile segment.transpose = transpose segment.repeat = repeat segment.mute_flags = mute_flags def MoveSegment(self, segname, start, end): """ Moves a segment """ for segment in self.segments: if segment.segname == segname: segment.start = start segment.end = end return def DeleteSegment(self, segname): """ Deletes a segment """ for segment in self.segments: if segment.segname == segname: self.segments.remove(segment) def SaveJetConfig(self, configFile): """ Saves the jet config file """ if self.config.filename == '' or self.config.filename == None: self.config.filename = FileJustRoot(configFile) + ".JET" config = ConfigParser.ConfigParser() config.add_section(OUTPUT_SECTION) config.set(OUTPUT_SECTION, OUTPUT_FILENAME, self.config.filename) config.set(OUTPUT_SECTION, OUTPUT_CHASE_CONTROLLERS, self.config.chase_controllers) config.set(OUTPUT_SECTION, OUTPUT_OMIT_EMPTY_TRACKS, self.config.delete_empty_tracks) if self.config.copyright is not None: config.set(OUTPUT_SECTION, OUTPUT_COPYRIGHT, self.config.copyright) if self.config.app_data is not None: config.set(OUTPUT_SECTION, OUTPUT_APP_DATA, self.config.app_data) self.libraries = [] seg_num = 0 for segment in self.segments: segment_name = SEGMENT_SECTION + str(seg_num) config.add_section(segment_name) config.set(segment_name, SEGMENT_NAME, segment.segname) config.set(segment_name, SEGMENT_FILENAME, segment.filename) config.set(segment_name, SEGMENT_DLSFILE, segment.dlsfile) if FileExists(segment.dlsfile): if not segment.dlsfile in self.libraries: self.libraries.append(segment.dlsfile) config.set(segment_name, SEGMENT_START, segment.start) if segment.end > JetDefs.MBT_ZEROSTR and len(segment.end) > 0: config.set(segment_name, SEGMENT_END, segment.end) if segment.length > JetDefs.MBT_ZEROSTR and len(segment.length) > 0: config.set(segment_name, SEGMENT_LENGTH, segment.length) config.set(segment_name, SEGMENT_OUTPUT, segment.output) config.set(segment_name, SEGMENT_QUANTIZE, segment.quantize) if segment.dump_file is not None: config.set(segment_name, SEGMENT_DUMP_FILE, segment.dump_file) config.set(segment_name, SEGMENT_TRANSPOSE, segment.transpose) config.set(segment_name, SEGMENT_REPEAT, segment.repeat) config.set(segment_name, SEGMENT_MUTE_FLAGS, segment.mute_flags) clip_num = 0 app_num = 0 for jet_event in segment.jetevents: if jet_event.event_type == JetDefs.E_CLIP: clip_name = CLIP_PREFIX + str(clip_num) 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) config.set(segment_name, clip_name, s) clip_num += 1 elif jet_event.event_type == JetDefs.E_APP: app_name = APP_PREFIX + str(app_num) 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) config.set(segment_name, app_name, s) app_num += 1 elif jet_event.event_type == JetDefs.E_EOS: s = "%s,%s,%s" % (jet_event.track_num, jet_event.channel_num, jet_event.event_start) config.set(segment_name, SEGMENT_END_MARKER, s) seg_num += 1 lib_num = 0 config.add_section(LIBRARY_SECTION) for library in self.libraries: library_name = LIBRARY_FILENAME + str(lib_num) config.set(LIBRARY_SECTION, library_name, library) lib_num += 1 FileKillClean(configFile) cfgfile = open(configFile,'w') config.write(cfgfile) cfgfile.close() def InitializeConfig(self, configFile): """ Initializes the values for an empty flag """ self.config.filename = FileJustRoot(configFile) + ".JET" self.config.chase_controllers = True self.config.delete_empty_tracks = False self.config.copyright = None self.config.app_data = None self.segments = [] self.libraries = [] self.config_file = configFile self.SaveJetConfig(configFile) #--------------------------------------------------------------- # main #--------------------------------------------------------------- if __name__ == '__main__': sys = __import__('sys') optparse = __import__('optparse') # parse command line options parser = optparse.OptionParser(version=VERSION) parser.set_defaults(log_level=logging.INFO, log_file=None) parser.add_option('-d', '--debug', action="store_const", const=logging.DEBUG, dest='log_level', help='Enable debug output') parser.add_option('-l', '--log_file', dest='log_file', help='Write debug output to log file') (options, args) = parser.parse_args() # get master logger logger = logging.getLogger('') logger.setLevel(options.log_level) # create console logger console_logger = logging.StreamHandler() console_logger.setFormatter(logging.Formatter('%(message)s')) logger.addHandler(console_logger) # create rotating file logger if options.log_file is not None: file_logger = logging.FileHandler(options.log_file, 'w') file_logger.setFormatter(logging.Formatter('%(message)s')) logger.addHandler(file_logger) # process files for arg in args: print arg jet_file = JetFile(arg, options) jet_file.WriteJetFileFromConfig(options)