1"""
2 File:
3 JetUtils.py
4
5 Contents and purpose:
6 Utilities used throughout JetCreator
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 wx
26import os
27import copy
28import ConfigParser
29import logging
30import time
31import tempfile
32
33from JetDefs import *
34from JetDebug import *
35from midifile import TimeBase, trackGrid
36
37class JetCutCopy(object):
38    """ Handles cut/copy/pasting of events and segments """
39    def __init__ (self, objType, objSave, currentSegmentName):
40        self.objType = objType
41        self.objSave = copy.deepcopy(objSave)
42        self.currentSegmentName = currentSegmentName
43
44    def GetObj(self, objList):
45        """ Gets an object """
46        objSave = copy.deepcopy(self.objSave)
47        if self.objType == JetDefs.MAIN_SEGLIST:
48            oldName = objSave.segname
49            i = len(oldName) - 1
50            while i > 0:
51                if not oldName[i].isdigit():
52                    break
53                i = i - 1
54            oldName = oldName[0:i+1]
55            i = 1
56            while True:
57                newName = oldName + str(i)
58                if self.UniqueSegName(newName, objList):
59                    break
60                i = i + 1
61            objSave.segname = newName
62        elif self.objType == JetDefs.MAIN_EVENTLIST:
63            oldName = objSave.event_name
64            i = len(oldName) - 1
65            while i > 0:
66                if not oldName[i].isdigit():
67                    break
68                i = i - 1
69            oldName = oldName[0:i+1]
70            i = 1
71            while True:
72                newName = oldName + str(i)
73                if self.UniqueEventName(newName, objList):
74                    break
75                i = i + 1
76            objSave.event_name = newName
77        return objSave
78
79    def UniqueSegName(self, nameVal, seglist):
80        for nm in seglist:
81            if nm.segname == nameVal:
82                return False
83        return True
84
85    def UniqueEventName(self, nameVal, eventlist):
86        for nm in eventlist:
87            if nm.event_name == nameVal:
88                return False
89        return True
90
91
92class JetState(object):
93    """ Saves the state for cut/copy/paste """
94    def __init__ (self, jet_file, currentSegmentIndex, currentEventIndex):
95        self.jet_file = copy.deepcopy(jet_file)
96        self.currentSegmentIndex = currentSegmentIndex
97        self.currentEventIndex = currentEventIndex
98
99def Queue (jet, queueSeg):
100    """ Queues a segment """
101    jet.QueueSegment(queueSeg.userID, queueSeg.seg_num, queueSeg.dls_num, queueSeg.repeat, queueSeg.transpose, queueSeg.mute_flags)
102
103class QueueSeg(object):
104    """ Object representing a segment """
105    def __init__ (self, name, userID, seg_num, dls_num=-1, repeat=0, transpose=0, mute_flags=0, status=''):
106        self.name = name
107        self.userID = userID
108        self.seg_num = seg_num
109        self.dls_num = dls_num
110        self.repeat = repeat
111        self.transpose = transpose
112        self.mute_flags = mute_flags
113        self.status = status
114        #DumpQueueSeg(self)
115
116def FindDlsNum(libraries, dlsfile):
117    """ Looks for a dls file in the library list """
118    for index, library in enumerate(libraries):
119        if library == dlsfile:
120            return index
121    return -1
122
123def SetRowSelection(list, row, state):
124    """ Sets the selection status of a list row """
125    if state:
126        list.SetItemState(row, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
127    else:
128        list.SetItemState(row, ~wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
129
130def ClearRowSelections(list):
131    """ Clears the list rows selection status """
132    index = list.GetFirstSelected()
133    while index != -1:
134        SetRowSelection(list, index, False)
135        index = list.GetNextSelected(index)
136
137def getColumnText(list, index, col):
138    """ Sets the text of a column """
139    item = list.GetItem(index, col)
140    return item.GetText()
141
142def getColumnValue(list, index, col):
143    """ Gets the text of a column """
144    item = list.GetItem(index, col)
145    v = str(item.GetText())
146    if len(v) > 0:
147        return int(item.GetText())
148    else:
149        return 0
150
151def StrNoneChk(fld):
152    """ Returns a blank string if none """
153    if fld is None:
154        return ""
155    return str(fld)
156
157def ConvertStrTimeToTuple(s):
158    """ Converts a string time to a tuple """
159    try:
160        measures, beats, ticks = s.split(':',3)
161        return (int(measures), int(beats), int(ticks))
162    except:
163        return JetDefs.MBT_DEFAULT
164
165def FileRelativePath(target, base=os.curdir):
166    """ Returns relative file path """
167    if not os.path.exists(target):
168        return target
169
170    if not os.path.isdir(base):
171        return target
172
173    base_list = (os.path.abspath(base)).split(os.sep)
174    target_list = (os.path.abspath(target)).split(os.sep)
175    if os.name in ['nt','dos','os2'] and base_list[0] <> target_list[0]:
176        return target
177    for i in range(min(len(base_list), len(target_list))):
178        if base_list[i] <> target_list[i]: break
179    else:
180        i+=1
181    rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:]
182    return os.path.join(*rel_list)
183
184def FileFixPath(fileSpec):
185    """ Tweaks slashes """
186    return fileSpec.replace("\\", "/")
187
188def FileKillClean(fileName):
189    """ Deletes a file skipping errors """
190    try:
191        os.remove(fileName)
192    except:
193        pass
194
195def FileJustRoot(fileName):
196    """ Gets just the root of the file name """
197    try:
198        return os.path.splitext(fileName)[0]
199    except:
200        return ""
201
202def FileJustName(fileName):
203    """ Gets just the filename, without the path """
204    try:
205        return os.path.split(fileName)[1]
206    except:
207        return ""
208
209def FileJustPath(fileName):
210    """ Gets just the path, without the file name """
211    try:
212        return os.path.split(fileName)[0]
213    except:
214        return ""
215
216def FileJustExt(fileName):
217    """ Gets just the extension of the file """
218    try:
219        ext = os.path.splitext(fileName)[1]
220        return ext.upper()
221    except:
222        return ""
223
224def FileDateTime(fileName):
225    """ Gets the date/time of a file """
226    try:
227        filetime = time.ctime(os.path.getmtime(fileName))
228        return filetime
229    except:
230        return ""
231
232def FileExists(fileName):
233    """ Checks if a file exists """
234    try:
235        return os.path.exists(fileName)
236    except:
237        return False
238
239def IniSetValue(configFile, section, option, value):
240    """ Sets the value of a config file field """
241    config = ConfigParser.ConfigParser()
242    config.read(configFile)
243    if not config.has_section(section):
244        config.add_section(section)
245    config.set(section, option, value)
246    cfgfile = open(configFile,'w')
247    config.write(cfgfile)
248    cfgfile.close()
249
250def IniGetValue(configFile, section, option, retType='str', default=''):
251    """ Gets the value of a config file field """
252    ret = default
253    config = ConfigParser.ConfigParser()
254    config.read(configFile)
255    if config.has_section(section):
256        if config.has_option(section, option):
257            ret = config.get(section, option)
258    if retType =='int':
259        try:
260            ret = int(ret)
261        except:
262            ret = 0
263    elif retType == 'float':
264        try:
265            ret = float(ret)
266        except:
267            ret = 0
268    elif retType == 'bool':
269        try:
270            if ret[0].upper()=='T':
271                ret = True
272            else:
273                ret = False
274        except:
275            ret = False
276    elif retType == 'list':
277        try:
278            ret = eval(ret)
279        except:
280            ret = []
281    return ret
282
283def GetRecentJetFiles():
284    """ Builds a list of recent jet files """
285    fileList = []
286    config = ConfigParser.ConfigParser()
287    config.read(JetDefs.JETCREATOR_INI)
288    if config.has_section(JetDefs.RECENT_SECTION):
289        for count in range(0, 10):
290            sFile = "File" + str(count)
291            if config.has_option(JetDefs.RECENT_SECTION, sFile):
292                sFileName = config.get(JetDefs.RECENT_SECTION, sFile)
293                if FileExists(sFileName):
294                    if sFileName != JetDefs.UNTITLED_FILE:
295                        #fileList.append(FileRelativePath(config.get(JetDefs.RECENT_SECTION, sFile)))
296                        fileList.append(config.get(JetDefs.RECENT_SECTION, sFile))
297    return fileList
298
299def AppendRecentJetFile(jetFile):
300    """ Appends to a list of recent jet files """
301    addedFiles = []
302    fileList = GetRecentJetFiles()
303    config = ConfigParser.ConfigParser()
304    config.read(JetDefs.JETCREATOR_INI)
305    if config.has_section(JetDefs.RECENT_SECTION):
306        config.remove_section(JetDefs.RECENT_SECTION)
307    config.add_section(JetDefs.RECENT_SECTION)
308    config.set(JetDefs.RECENT_SECTION, "File0", jetFile)
309    addedFiles.append(jetFile)
310    count = 1
311    for file in fileList:
312        if file not in addedFiles:
313            sFile = "File" + str(count)
314            config.set(JetDefs.RECENT_SECTION, sFile, file)
315            addedFiles.append(file)
316            count += 1
317    FileKillClean(JetDefs.JETCREATOR_INI)
318    cfgfile = open(JetDefs.JETCREATOR_INI,'w')
319    config.write(cfgfile)
320    cfgfile.close()
321
322def CompareMbt(mbt1, mbt2):
323    """ Compates to measure/beat/tick values """
324    try:
325        m1, b1, t1 = mbt1.split(':',3)
326        m2, b2, t2 = mbt2.split(':',3)
327        if int(m1) > int(m2):
328            return False
329        elif int(m1) == int(m2) and int(b1) > int(b2):
330            return False
331        elif int(b1) == int(b2) and int(t1) > int(t2):
332            return False
333        elif int(m1) == int(m2) and int(b1) == int(b2) and int(t1) == int(t2):
334            return False
335        else:
336            return True
337    except:
338        return False
339
340def MbtVal(mbt):
341    """ Converts mbts to ticks """
342    if type(mbt).__name__=='str' or type(mbt).__name__=='unicode':
343        mbt1 = mbt
344    else:
345        mbt1 = "%d:%d:%d" % mbt
346    try:
347        return TimeBase().ConvertStrTimeToTicks(mbt1)
348    except:
349        return 0
350
351def TicksToMbt(ticks):
352    """ Converts ticks to mbts """
353    return TimeBase().ConvertTicksToMBT(ticks)
354
355def TicksToStrMbt(ticks):
356    """ Converts ticks to mbts """
357    return TimeBase().ConvertTicksToStr(ticks, '%02d:%02d:%02d')
358
359def MbtDifference(mbt1, mbt2):
360    """ Returns difference between mbt values """
361    return TimeBase().MbtDifference(mbt1, mbt2)
362
363def PlayMidiFile(midiFile, dlsFile=''):
364    """ Plays a midi file """
365    try:
366        e = __import__('eas')
367
368        if midiFile == '':
369            return
370        eas = e.EAS()
371        if dlsFile > '':
372            eas.LoadDLSCollection(dlsFile)
373        eas.StartWave()
374        audio_file = eas.OpenFile(midiFile)
375        audio_file.Prepare()
376        audio_file.Play()
377        audio_file.Close()
378        eas.StopWave()
379        eas.Shutdown()
380    except:
381        return
382
383def SegmentOutputFile(segName, configFile):
384    """ Computes a segment output file """
385    configPath = FileJustPath(configFile) + "/"
386    segOutput = configPath + "Seg_" + segName + ".mid"
387    return segOutput
388
389def ComputeMuteFlags(jet_file, segName):
390    """ Computes mute flags """
391    muteFlag = 0
392    for jet_event in jet_file.GetEvents(segName):
393        muteFlag = SetMute(jet_event.track_num, muteFlag)
394    return muteFlag
395
396def ComputeMuteFlagsFromList1(list):
397    """ Computes mute flags from a list """
398    muteFlag = 0
399    num = list.GetItemCount()
400    for iRow in range(num):
401        track_num = list.GetTrackNumber(iRow)
402        if list.IsChecked(iRow):
403            muteFlag = SetMute(track_num, muteFlag)
404        else:
405            muteFlag = ClearMute(track_num, muteFlag)
406    return muteFlag
407
408def ComputeMuteFlagsFromList(list):
409    """ Computes mute flags from a list """
410    muteFlags = 0
411    num = list.GetItemCount()
412    for iRow in range(num):
413        track_num = list.GetTrackNumber(iRow)
414        if list.IsChecked(iRow):
415            muteFlags = SetMute(track_num, muteFlags)
416    return muteFlags
417
418
419def SetMuteFlag(track, muteFlag, mute):
420    """ Sets a mute flag """
421    if mute:
422        SetMute(track, muteFlag)
423    else:
424        ClearMute(track, muteFlag)
425
426def SetMute(track, muteFlag):
427    """ Sets a mute flag """
428    try:
429        muteFlag |= 1 << (track)
430        return muteFlag
431    except:
432        #bad argument
433        return muteFlag
434
435def ClearMute(track, muteFlag):
436    """ Clears a mute flag """
437    try:
438        muteFlag &= ~(1 << (track))
439        return muteFlag;
440    except:
441        #bad argument
442        return muteFlag
443
444def GetMute(track, muteFlag):
445    """ Get a mute flag """
446    try:
447        if (muteFlag & ( 1 << (track))) == 0:
448            return False
449        else:
450            return True
451    except:
452        #bad argument
453        return False
454
455def InfoMsg(msgTitle, msgText):
456    """ Display a simple informational message """
457    dlg = wx.MessageDialog(None,
458                           message=msgText,
459                           caption=msgTitle,
460                           style=wx.OK|wx.ICON_INFORMATION
461                           )
462    dlg.ShowModal()
463    dlg.Destroy()
464
465def SendEvent (mycontrol, evt):
466    """ Sends an event """
467    cmd = wx.CommandEvent(evt)
468    cmd.SetEventObject(mycontrol)
469    cmd.SetId(mycontrol.GetId())
470    mycontrol.GetEventHandler().ProcessEvent(cmd)
471
472def GetJetHelpText(dlgName, fld):
473    """ Gets the jet help text file """
474    return IniGetValue(JetDefs.JETCREATOR_HLP, dlgName, fld)
475
476def ExportJetArchive(fileName, jetConfigFile, jetFile):
477    """ Exports all files into a zip archive file """
478    z = __import__('zipfile')
479    zip = z.ZipFile(fileName, 'w')
480
481    #zip the original .JET file
482    if FileExists(jetFile.config.filename):
483        zip.write(jetFile.config.filename, FileJustName(jetFile.config.filename))
484
485    #make copy of object so we can modify it
486    jet_file = copy.deepcopy(jetFile)
487
488    #zip the files, without paths
489    for segment in jet_file.GetSegments():
490        if FileExists(segment.filename):
491            if not FileJustName(segment.filename) in zip.namelist():
492                zip.write(segment.filename, FileJustName(segment.filename))
493        if FileExists(segment.output):
494            if not FileJustName(segment.output) in zip.namelist():
495                zip.write(segment.output, FileJustName(segment.output))
496
497    #zip the library files
498    for library in jet_file.GetLibraries():
499        if FileExists(library):
500            if not FileJustName(library) in zip.namelist():
501                zip.write(library, FileJustName(library))
502
503    #remove the paths on filenames
504    for segment in jet_file.GetSegments():
505        segment.filename = FileJustName(segment.filename)
506        segment.dlsfile = FileJustName(segment.dlsfile)
507        segment.output = FileJustName(segment.output)
508
509    #remove paths
510    for index, library in enumerate(jet_file.libraries):
511        jet_file.libraries[index] = FileJustName(library)
512
513    #create temporary .JTC file so we can modify paths to files
514    tmpConfigFile = JetDefs.TEMP_JET_CONFIG_FILE
515    FileKillClean(tmpConfigFile)
516
517    #save the file
518    jet_file.SaveJetConfig(tmpConfigFile)
519
520    #zip it and rename it back to original name without path
521    zip.write(tmpConfigFile, FileJustName(jetConfigFile))
522
523    #create a flag file so we know this is a jet archive
524    zip.write(tmpConfigFile, "JetArchive")
525
526    zip.close()
527
528    FileKillClean(tmpConfigFile)
529
530def ValidateConfig(test_jet_file):
531    """ Validates the contents of a config file """
532    dImp = __import__('JetDialogs')
533    errors = []
534    fatalError = False
535    for segment in test_jet_file.segments:
536        logging.debug(segment.filename)
537        if segment.filename is not None and len(segment.filename) > 0 and not FileExists(segment.filename):
538            errors.append(("Segment MIDI file not found", segment.filename))
539            fatalError = True
540        if segment.dlsfile is not None and len(segment.dlsfile) > 0 and not FileExists(segment.dlsfile):
541            errors.append(("Segment DLS file not found; removing from config", segment.dlsfile))
542            segment.dlsfile = ""
543
544    logging.debug(test_jet_file.config.filename)
545
546    if len(errors) == 0:
547        return True
548    else:
549        dlg = dImp.JetErrors("Jet Definition File Errors")
550        dlg.SetErrors(errors)
551        result = dlg.ShowModal()
552        dlg.Destroy()
553        if fatalError:
554            return False
555        else:
556            return True
557
558def release_getLogger(name):
559    """  passing original handler with debug() method replaced to empty function """
560
561    def dummy(*k, **kw):
562        pass
563
564    global __orig_getLogger
565    log = __orig_getLogger(name)
566    setattr(log, 'debug', dummy)
567    setattr(log, 'info', dummy)
568    setattr(log, 'error', dummy)
569    setattr(log, 'critical', dummy)
570    return log
571
572def install_release_loggers():
573    """ Save original handler, installs newer one """
574    global __orig_getLogger
575    __orig_getLogger = logging.getLogger
576    setattr(logging, 'getLogger', release_getLogger)
577
578def restore_getLogger():
579    """ Restores original handler """
580    global __orig_getLogger
581    if __orig_getLogger:
582        setattr(logging, 'getLogger', __orig_getLogger)
583
584def GetMidiFileLength(midiFile):
585    """ Gets the length of a midi file via eas """
586    e = __import__('eas')
587
588    if not FileExists(midiFile):
589        return 0
590
591    eas = e.EAS()
592    audio_file = eas.OpenFile(midiFile)
593    audio_file.Prepare()
594    midiLength = eas.audio_streams[0].ParseMetaData()
595    audio_file.Close()
596    eas.Shutdown()
597    return midiLength
598
599def GetMidiInfo(midiFile):
600    """ Gets midi file info """
601    m = __import__('midifile')
602    md = m.GetMidiInfo(midiFile)
603    return md
604
605def PrintMidiInfo(midiFile):
606    """ Prints info about a midi file """
607    mi = GetMidiInfo(midiFile)
608    if mi.err == 0:
609        print("ppqn: " + str(mi.ppqn))
610        print("beats_per_measure: " + str(mi.beats_per_measure))
611        print("ending mbt: " + str(mi.endMbt))
612        print("ending mbt str: " + mi.endMbtStr)
613        print("maxMeasures: " + str(mi.maxMeasures))
614        print("maxBeats: " + str(mi.maxBeats))
615        print("maxTicks: " + str(mi.maxTicks))
616        print("maxTracks: " + str(mi.maxTracks))
617        print("totalTicks: " + str(mi.totalTicks))
618        for track in mi.trackList:
619            print(track)
620    else:
621        print("Error opening")
622
623def MidiSegInfo(segment):
624    """ Midi file info saved in config file for speed """
625    class segInfo:
626        iMsPerTick = 0
627        bpm = 4
628        ppqn = 480
629        total_ticks = 0
630        iLengthInMs = 0
631        iTracks = 0
632        trackList = []
633
634    ver = "1.5"
635    ret = segInfo()
636    savedVer = IniGetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "Ver")
637    savedDateTime = IniGetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "DateTime")
638    dateTime = FileDateTime(segment.filename)
639    if ver != savedVer or dateTime != savedDateTime:
640        mi = GetMidiInfo(segment.filename)
641        if mi.err == 0:
642            IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "Ver", ver)
643            IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "DateTime", str(dateTime))
644            IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "PPQN", str(mi.ppqn))
645            IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "BPM", str(mi.beats_per_measure))
646            IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "totalTicks", str(mi.totalTicks))
647            IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "maxTracks", str(mi.maxTracks))
648            iLengthInMs = GetMidiFileLength(segment.filename) * 1000
649            IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "LengthInMs", str(iLengthInMs))
650            if iLengthInMs > 0:
651                IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "MsPerTick", str(iLengthInMs / mi.totalTicks))
652            #have to write out the tracklist in format that can be saved in INI file
653            tl = []
654            for track in mi.trackList:
655                tl.append((track.track, track.channel, track.name))
656            IniSetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "Tracks", tl)
657
658    trackList = []
659    tl = IniGetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "Tracks", 'list', [])
660    for t in tl:
661        trackList.append(trackGrid(t[0], t[1], t[2],False))
662    iTracks = IniGetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "maxTracks", 'int', 0)
663    iMsPerTick = IniGetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "MsPerTick", 'float', 0)
664    bpm = IniGetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "BPM", 'int', 0)
665    ppqn = IniGetValue(JetDefs.JETMIDIFILES_INI, segment.filename, "PPQN", 'int', 480)
666    if iMsPerTick == 0 or bpm == 0 or ppqn == 0:
667        return ret
668    tb = TimeBase(ppqn, bpm)
669    total_ticks = tb.ConvertStrTimeToTicks(segment.length)
670    if total_ticks == 0:
671        total_ticks = tb.MbtDifference(tb.ConvertStrTimeToTuple(segment.start), tb.ConvertStrTimeToTuple(segment.end))
672    if total_ticks == 0:
673        return ret
674
675    ret.iTracks = iTracks
676    ret.iMsPerTick = iMsPerTick
677    ret.bpm = bpm
678    ret.ppqn = ppqn
679    ret.total_ticks = total_ticks
680    ret.iLengthInMs = total_ticks * iMsPerTick
681    ret.trackList = trackList
682    return ret
683
684def TimeStr(ms):
685    """ Returns a time string """
686    s=ms/1000
687    m,s=divmod(s,60)
688    h,m=divmod(m,60)
689    d,h=divmod(h,24)
690    if m > 0:
691        return "%d Min %d Sec" % (m,s)
692    else:
693        return "%d Seconds" % (s)
694
695def mbtFct(mbt, mod):
696    """ Converts times """
697    if type(mbt).__name__=='str' or type(mbt).__name__=='unicode':
698        mbt = ConvertStrTimeToTuple(mbt)
699        retType = 'str'
700    else:
701        retType = 'int'
702
703    m = mbt[0]+mod
704    b = mbt[1]+mod
705    t = mbt[2]
706    if m < 0:
707        m = 0
708    if b < 0:
709        b = 0
710    if b > 4:
711        b = 4
712    if t < 0:
713        t = 0
714
715    if retType == 'str':
716        return "%d:%d:%d" % (m, b, t)
717    else:
718        return (m, b, t)
719
720def OsWindows():
721    """ Tells us whether windows or os x """
722    if os.name == 'nt':
723        return True ;
724    else:
725        return False ;
726
727def MacOffset():
728    """ Mac screen coordinates funky on some controls so we finagle a few pixels """
729    if not OsWindows():
730        return 3
731    else:
732        return 0
733
734def SafeJetShutdown(lock, jet):
735    """ Makes sure we do the jet shutdown properly """
736    with lock:
737        #MAKE SURE WE CLEANUP
738        #try: jet.Clear_Queue()
739        #except: pass
740
741        try: jet.eas.StopWave()
742        except: pass
743
744        try: jet.Shutdown()
745        except: pass
746
747        jet = None
748
749
750def CreateTempJetFile(org_jet_file):
751    """ Creates temporary jet file for playback testing """
752    dirname = JetDefs.TEMP_JET_DIR
753    if not os.path.isdir(dirname):
754        os.mkdir(dirname)
755
756    tmpConfigFile = dirname + FileJustName(org_jet_file.config_file)
757    FileKillClean(tmpConfigFile)
758
759    jet_file = copy.deepcopy(org_jet_file)
760
761    for tmp in jet_file.segments:
762        tmp.output = dirname + FileJustName(tmp.output)
763
764    jet_file.config_file = tmpConfigFile
765    jet_file.config.filename = dirname + FileJustName(jet_file.config.filename)
766    FileKillClean(jet_file.config.filename)
767
768    jet_file.SaveJetConfig(tmpConfigFile)
769    jet_file.WriteJetFileFromConfig(tmpConfigFile)
770
771    return jet_file
772
773def CleanupTempJetFile(jet_file):
774    """ Cleans up temporary files """
775    FileKillClean(jet_file.config.filename)
776    FileKillClean(jet_file.config_file)
777    for tmp in jet_file.segments:
778        FileKillClean(tmp.output)
779
780def GetNow():
781    return time.asctime()
782
783
784if __name__ == '__main__':
785    """ Tests functions """
786    pass
787
788