Home | History | Annotate | Download | only in JetCreator
      1 """
      2  File:  
      3  JetAudition.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 wx
     26 import sys
     27 import thread
     28 import time
     29 
     30 from JetUtils import *
     31 from JetDefs import *
     32 from JetCtrls import JetListCtrl, JetTrackCtrl
     33 from JetSegGraph import SegmentGraph, Marker
     34 from eas import *
     35 from JetStatusEvent import *
     36 
     37 CMD_QUEUE_AND_CANCEL = 'QueueNCancel'
     38 CMD_QUEUE_AND_CANCEL_CURRENT = 'QueueCancelCurrent'
     39 CMD_MUTEALL = 'MuteAll'
     40 CMD_UNMUTEALL = 'UnMuteAll'
     41 CMD_ORIGINALMUTES = 'MuteOrg'
     42 CMD_STOP = 'Stop'
     43 CMD_PAUSE = 'Pause'
     44 CMD_PLAY = 'Play'
     45 
     46 STATUS_PENDING = 'Pending'
     47 STATUS_PLAYING = 'Playing'
     48 STATUS_COMPLETE = 'Complete'
     49 STATUS_CANCELED = 'Canceled'
     50 STATUS_QUEUED = 'Queued'
     51 
     52 LOAD_QUEUE_DISPLAY = 'LOAD_QUEUE'
     53 GRAPH_POSITION_UPDATE = 'GRAPH_POS'
     54 NEW_SEGMENT_DISPLAY = 'NEW SEG'
     55 CLR_INFO = 'CLR_INFO'
     56 
     57 class Audition(wx.Dialog):
     58     """ Initializes Audition window controls, then spawns off a thread to be ready for playback commands """
     59     def __init__(self, jet_file, pSize):
     60         wx.Dialog.__init__(self, None, -1, title=JetDefs.DLG_AUDITION)
     61         
     62         self.jet = None
     63         self.playerLock = threading.RLock()
     64         self.jet_file = jet_file
     65         self.queueSegs = []
     66         self.keepPlaying = True
     67         self.nextSegNum = 0
     68         self.currentSegmentIndex = None
     69         self.currentSegmentName = ""        
     70         self.playCommand = ""
     71         self.threadShutdown = True
     72         
     73         panel = wx.Panel(self, -1)
     74 
     75         self.segList = JetListCtrl(panel)
     76         self.segList.AddCol(JetDefs.GRD_SEGMENTS, 180)
     77         self.segList.AddCol(JetDefs.GRD_LENGTH, 20)
     78         
     79         self.segList.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnQueueSegment)
     80         self.segList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSegListClick)
     81         
     82         self.queueList = JetListCtrl(panel)
     83         self.queueList.AddCol(JetDefs.GRD_QUEUE, 180)
     84         self.queueList.AddCol(JetDefs.GRD_STATUS, 20)
     85 
     86         self.trackList = JetTrackCtrl(panel)
     87         self.trackList.AddCol(JetDefs.GRD_TRACK, JetDefs.MUTEGRD_TRACK)
     88         self.trackList.AddCol(JetDefs.GRD_CHANNEL, JetDefs.MUTEGRD_CHANNEL)
     89         self.trackList.AddCol(JetDefs.GRD_NAME, JetDefs.MUTEGRD_NAME)
     90         self.trackList.BindCheckBox(self.OnTrackChecked)
     91         
     92         self.btnMuteAll = wx.Button(panel, -1, JetDefs.BUT_MUTEALL)
     93         self.btnUnMuteAll = wx.Button(panel, -1, JetDefs.BUT_MUTENONE)
     94         self.btnMuteOrg = wx.Button(panel, -1, JetDefs.BUT_ORGMUTES)
     95         hMuteButs = wx.BoxSizer(wx.HORIZONTAL)
     96         hMuteButs.Add(self.btnMuteAll, 1, wx.EXPAND)
     97         hMuteButs.Add(self.btnUnMuteAll, 1, wx.EXPAND)
     98         hMuteButs.Add(self.btnMuteOrg, 1, wx.EXPAND)
     99         vMuteButs = wx.BoxSizer(wx.VERTICAL)
    100         vMuteButs.Add(self.trackList, 1, wx.EXPAND)
    101         vMuteButs.Add((-1, 5))
    102         vMuteButs.Add(hMuteButs, 0, wx.EXPAND)
    103         
    104         self.btnQueue = wx.Button(panel, -1, JetDefs.BUT_QUEUE)
    105         self.btnCancelNQueue = wx.Button(panel, -1, JetDefs.BUT_CANCELANDQUEUE)
    106         hSegButs = wx.BoxSizer(wx.HORIZONTAL)
    107         hSegButs.Add(self.btnQueue, 1, wx.EXPAND)
    108         hSegButs.Add(self.btnCancelNQueue, 1, wx.EXPAND)
    109         vSegButs = wx.BoxSizer(wx.VERTICAL)
    110         vSegButs.Add(self.segList, 1, wx.EXPAND)
    111         vSegButs.Add((-1, 5))
    112         vSegButs.Add(hSegButs, 0, wx.EXPAND)
    113         
    114         self.btnQueueCancelCurrent = wx.Button(panel, -1, JetDefs.BUT_CANCELCURRENT)
    115         self.btnPause = wx.Button(panel, -1, JetDefs.BUT_PAUSE)
    116         self.btnStop = wx.Button(panel, -1, JetDefs.BUT_STOP)
    117         hQueueButs = wx.BoxSizer(wx.HORIZONTAL)
    118         hQueueButs.Add(self.btnQueueCancelCurrent, 1, wx.EXPAND)
    119         hQueueButs.Add(self.btnPause, 1, wx.EXPAND)
    120         hQueueButs.Add(self.btnStop, 1, wx.EXPAND)
    121         vQueueButs = wx.BoxSizer(wx.VERTICAL)
    122         vQueueButs.Add(self.queueList, 1, wx.EXPAND)
    123         vQueueButs.Add((-1, 5))
    124         vQueueButs.Add(hQueueButs, 0, wx.EXPAND)
    125 
    126         self.Bind(wx.EVT_BUTTON, self.OnQueueSegmentViaBut, id=self.btnQueue.GetId())
    127         self.Bind(wx.EVT_BUTTON, self.OnCancelNQueue, id=self.btnCancelNQueue.GetId())
    128         self.Bind(wx.EVT_BUTTON, self.OnStop, id=self.btnStop.GetId())
    129         self.Bind(wx.EVT_BUTTON, self.OnQueueCancelCurrent, id=self.btnQueueCancelCurrent.GetId())
    130         self.Bind(wx.EVT_BUTTON, self.OnPause, id=self.btnPause.GetId())
    131         self.Bind(wx.EVT_BUTTON, self.OnMuteAll, id=self.btnMuteAll.GetId())
    132         self.Bind(wx.EVT_BUTTON, self.OnUnMuteAll, id=self.btnUnMuteAll.GetId())
    133         self.Bind(wx.EVT_BUTTON, self.OnMuteOrg, id=self.btnMuteOrg.GetId())
    134         
    135         EVT_JET_STATUS(self, self.OnJetStatusUpdate)
    136 
    137         BORDER = 10
    138         hboxTop = wx.BoxSizer(wx.HORIZONTAL)
    139         hboxTop.Add(vSegButs, 1, wx.EXPAND)
    140         hboxTop.Add((5, -1))
    141         hboxTop.Add(vQueueButs, 1, wx.EXPAND)
    142         hboxTop.Add((5, -1))
    143         hboxTop.Add(vMuteButs, 1, wx.EXPAND)
    144 
    145         self.log = wx.TextCtrl(panel, -1)
    146         self.graph = SegmentGraph(panel, size=(-1, 50))
    147         self.graph.ClickCallbackFct = self.GraphTriggerClip
    148 
    149         vboxBot = wx.BoxSizer(wx.VERTICAL)
    150         vboxBot.Add(self.log, 0, wx.EXPAND)
    151         vboxBot.Add((-1, 5))
    152         vboxBot.Add(self.graph, 1, wx.EXPAND)
    153 
    154         hboxMain = wx.BoxSizer(wx.VERTICAL)
    155         hboxMain.Add(hboxTop, 2, wx.EXPAND | wx.ALL, BORDER)
    156         hboxMain.Add(vboxBot, 1, wx.EXPAND | wx.ALL, BORDER)
    157 
    158         panel.SetSizer(hboxMain)
    159 
    160         self.LoadSegList()
    161         self.initHelp()
    162         
    163         self.SetSize(pSize)
    164         self.CenterOnParent()
    165 
    166         wx.EVT_CLOSE(self, self.OnClose)
    167 
    168         thread.start_new_thread(self.PlaySegs, ())
    169         
    170     def initHelp(self):
    171         """ Initializes context sensitive help text """
    172         self.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP )        
    173         self.SetHelpText(GetJetHelpText(JetDefs.AUDITION_CTRLS, ''))
    174         self.segList.SetHelpText(GetJetHelpText(JetDefs.AUDITION_CTRLS, JetDefs.AUDITION_SEGLIST))
    175         self.queueList.SetHelpText(GetJetHelpText(JetDefs.AUDITION_CTRLS, JetDefs.AUDITION_QUEUELIST))
    176         self.trackList.SetHelpText(GetJetHelpText(JetDefs.AUDITION_CTRLS, JetDefs.AUDITION_TRACKLIST))
    177         self.graph.SetHelpText(GetJetHelpText(JetDefs.AUDITION_CTRLS, JetDefs.AUDITION_GRAPH))
    178 
    179     def OnMuteAll(self, event):
    180         """ Sets command to mute all tracks """
    181         self.SetPlayCommand(CMD_MUTEALL)
    182 
    183     def OnUnMuteAll(self, event):
    184         """ Sets command to un-mute all tracks """
    185         self.SetPlayCommand(CMD_UNMUTEALL)
    186             
    187     def OnMuteOrg(self, event):
    188         """ Sets command to set mute flags to their original values """
    189         self.SetPlayCommand(CMD_ORIGINALMUTES)
    190     
    191     def OnTrackChecked(self, index, checked):
    192         """ Mutes or un-mutes a track interactively """
    193         with self.playerLock:
    194             trackNum = self.trackList.GetTrackNumber(index) 
    195             self.SetMuteFlag(trackNum, checked)
    196             
    197     def SetMuteFlag(self, trackNum, mute):
    198         """ Mutes or un-mutes a track """
    199         with self.playerLock:
    200             try:
    201                 sync = JetDefs.DEFAULT_MUTE_SYNC
    202                 self.jet.SetMuteFlag(trackNum, mute, sync)
    203                 logging.info("SetMuteFlag() Track:%d Mute:%d Sync:%d" % (trackNum, mute, sync))
    204                 return True
    205             except:
    206                 return False
    207             
    208     def LoadSegList(self):
    209         """ Loads the list of segments """
    210         with self.playerLock:
    211             self.segList.DeleteAllItems()
    212             for segment in self.jet_file.GetSegments():
    213                 info = MidiSegInfo(segment)
    214                 index = self.segList.InsertStringItem(sys.maxint, StrNoneChk(segment.segname))
    215                 self.segList.SetStringItem(index, 1,  TimeStr(info.iLengthInMs))
    216 
    217     def GraphTriggerClip(self, sClipName, iEventId):
    218         """ Triggers a clip """
    219         with self.playerLock:
    220             try:
    221                 self.jet.TriggerClip(iEventId)
    222                 self.log.SetValue(JetDefs.PLAY_TRIGGERCLIP_MSG % (iEventId, sClipName))
    223                 return True
    224             except:
    225                 return False       
    226 
    227     def OnSegListClick(self, event):
    228         """ Sets current segment name based on what's clicked """
    229         with self.playerLock:
    230             self.currentSegmentIndex = event.m_itemIndex
    231             self.currentSegmentName = getColumnText(self.segList, event.m_itemIndex, 0)        
    232     
    233     def OnCancelNQueue(self, event):
    234         """ Sets command to cancel the currently playing segment and queues another """
    235         if self.currentSegmentIndex == None:
    236             return
    237         self.SetPlayCommand(CMD_QUEUE_AND_CANCEL)
    238 
    239     def OnPause(self, event):
    240         """ Sets a command to pause playback """
    241         if self.currentSegmentIndex == None:
    242             return
    243         self.SetPlayCommand(CMD_PAUSE)
    244 
    245     def OnStop(self, event):
    246         """ Sets a command to stop playback """
    247         if self.currentSegmentIndex == None:
    248             return
    249         self.SetPlayCommand(CMD_STOP)
    250             
    251     def OnQueueCancelCurrent(self, event):
    252         """ Sets a command to cancel the currently playing segment """
    253         if self.currentSegmentIndex == None:
    254             return
    255         self.SetPlayCommand(CMD_QUEUE_AND_CANCEL_CURRENT)
    256             
    257     def OnQueueSegmentViaBut(self, event):
    258         """ Queues a segment via the button """
    259         if self.currentSegmentIndex == None:
    260             return
    261         with self.playerLock:
    262             segNum = self.currentSegmentIndex
    263             segment = self.jet_file.GetSegment(self.currentSegmentName)
    264             self.QueueOneSegment(segment, segNum)
    265             
    266     def OnQueueSegment(self, event):
    267         """ Queues a segment """
    268         with self.playerLock:
    269             segNum = event.m_itemIndex
    270             segment = self.jet_file.GetSegment(getColumnText(self.segList, segNum, 0))
    271             self.QueueOneSegment(segment, segNum)
    272             
    273     def QueueOneSegment(self, segment, segNum):
    274         """ Queues one segment """
    275         with self.playerLock:
    276             userID = len(self.queueSegs)
    277             if FileExists(segment.dlsfile):
    278                 dls_num = FindDlsNum(self.jet_file.libraries, segment.dlsfile)
    279             else:
    280                 dls_num = -1
    281             self.queueSegs.append(QueueSeg(segment.segname, userID, segNum, dls_num, segment.repeat, segment.transpose, segment.mute_flags, STATUS_PENDING))
    282             self.LoadQueueDisplay()
    283         
    284     def SetKeepPlayingFlag(self, val):
    285         """ Sets a flag to continue play loop or shut down """
    286         with self.playerLock:
    287             self.keepPlaying = val
    288             
    289     def GetKeepPlayingFlag(self):  
    290         """ Gets the play flag """    
    291         with self.playerLock:
    292             return self.keepPlaying
    293         
    294     def SetThreadShutdownFlag(self, val):
    295         """ Set a flag to shutdown thread """
    296         with self.playerLock:
    297             self.threadShutdown = val
    298             
    299     def GetThreadShutdownFlag(self):      
    300         """ Gets the thread shutdown flag """
    301         with self.playerLock:
    302             return self.threadShutdown
    303         
    304     def SetPlayCommand(self, cmd):
    305         """ Sets a play command """
    306         with self.playerLock:
    307             self.playCommand = cmd
    308             
    309     def GetPlayCommand(self):    
    310         """ Gets a play command """  
    311         with self.playerLock:
    312             return self.playCommand
    313         
    314     def SetStatus(self, index, status):
    315         """ Sets the status of a segment """
    316         with self.playerLock:
    317             self.queueSegs[index].status = status
    318             
    319     def GetStatus(self, index):      
    320         """ Gets the status of a segment """
    321         with self.playerLock:
    322             return self.queueSegs[index].status
    323         
    324     def LoadQueueDisplay(self):
    325         """ Loads up the displayed queue list """
    326         with self.playerLock:
    327             self.queueList.DeleteAllItems()
    328             for item in self.queueSegs:
    329                 index = self.queueList.InsertStringItem(sys.maxint, item.name)
    330                 self.queueList.SetStringItem(index, 1,  item.status)
    331         
    332     def NextSegment(self):
    333         """ Gets the next segment in the queueu """
    334         with self.playerLock:
    335             num = len(self.queueSegs)
    336             for i in range(num):
    337                 if self.queueSegs[i].status == STATUS_PENDING:
    338                     return i
    339             return -1
    340         
    341     def PlaySegs(self):
    342         """ Sets up a loop looking for jet file actions based on UI commands """
    343         self.jet = JET()
    344         self.jet.eas.StartWave()
    345         self.jet.OpenFile(self.jet_file.config.filename)
    346         
    347         self.SetKeepPlayingFlag(True)
    348         while self.GetKeepPlayingFlag():            
    349             self.SetThreadShutdownFlag(False)
    350 
    351             time.sleep(.5)
    352             index = self.NextSegment()
    353             if index != -1:
    354                 lastID = -1
    355         
    356                 Queue(self.jet, self.queueSegs[index])
    357                 
    358                 self.SetStatus(index, STATUS_QUEUED)      
    359                 
    360                 wx.PostEvent(self, JetStatusEvent(LOAD_QUEUE_DISPLAY, None))
    361                 
    362                 self.jet.Play()
    363                 self.paused = False
    364                 wx.PostEvent(self, JetStatusEvent(CMD_PLAY, None))
    365  
    366                 while self.GetKeepPlayingFlag():
    367                     self.jet.Render()
    368                     status = self.jet.Status()
    369                     
    370                     if status.currentUserID <> lastID and status.currentUserID <> -1:
    371                         wx.PostEvent(self, JetStatusEvent(NEW_SEGMENT_DISPLAY, status.currentUserID))
    372                         if lastID != -1:
    373                             self.SetStatus(lastID, STATUS_COMPLETE)      
    374                         self.SetStatus(status.currentUserID, STATUS_PLAYING)      
    375                         lastID = status.currentUserID
    376                         wx.PostEvent(self, JetStatusEvent(LOAD_QUEUE_DISPLAY, None))
    377                         
    378                     if status.numQueuedSegments == 0:
    379                         break
    380             
    381                     self.jet.GetAppEvent()
    382                     
    383                     index = self.NextSegment()
    384                     if (index >= 0) and (status.numQueuedSegments < 2):
    385                         Queue(self.jet, self.queueSegs[index])
    386                         self.SetStatus(index, STATUS_QUEUED)      
    387                         wx.PostEvent(self, JetStatusEvent(LOAD_QUEUE_DISPLAY, None))
    388                         
    389                     wx.PostEvent(self, JetStatusEvent(GRAPH_POSITION_UPDATE, status.location))
    390                         
    391                     playCmd = self.GetPlayCommand()
    392                     if playCmd == CMD_QUEUE_AND_CANCEL or playCmd == CMD_STOP or playCmd == CMD_QUEUE_AND_CANCEL_CURRENT:
    393                         if playCmd == CMD_QUEUE_AND_CANCEL or playCmd == CMD_STOP:
    394                             num = len(self.queueSegs)
    395                             for i in range(num):
    396                                 curStatus = self.GetStatus(i)
    397                                 if curStatus == STATUS_PENDING or curStatus == STATUS_PLAYING or curStatus == STATUS_QUEUED:
    398                                     self.SetStatus(i, STATUS_CANCELED)      
    399 
    400                         if playCmd == CMD_QUEUE_AND_CANCEL_CURRENT:
    401                             self.SetStatus(status.currentUserID, STATUS_CANCELED)      
    402                             num = len(self.queueSegs)
    403                             for i in range(num):
    404                                 curStatus = self.GetStatus(i)
    405                                 if curStatus == STATUS_QUEUED:
    406                                     self.SetStatus(i, STATUS_PENDING)      
    407                         
    408                         if playCmd == CMD_QUEUE_AND_CANCEL:
    409                             segNum = self.currentSegmentIndex
    410                             segment = self.jet_file.GetSegment(self.currentSegmentName)
    411                             wx.PostEvent(self, JetStatusEvent(CMD_QUEUE_AND_CANCEL, (segment, segNum)))
    412                               
    413                         #MAC has a 'pop' when clearing the queue; not sure why so this avoids it                              

    414                         if OsWindows():
    415                             self.jet.Clear_Queue()
    416                         else:
    417                             self.jet = self.SafeJetRestart(self.playerLock, self.jet, self.jet_file.config.filename)
    418                     
    419                     if playCmd == CMD_ORIGINALMUTES:
    420                         wx.PostEvent(self, JetStatusEvent(CMD_ORIGINALMUTES, segment.mute_flags))
    421 
    422                     if playCmd == CMD_UNMUTEALL:                       
    423                         wx.PostEvent(self, JetStatusEvent(CMD_UNMUTEALL, None))
    424 
    425                     if playCmd == CMD_PAUSE:
    426                         wx.PostEvent(self, JetStatusEvent(CMD_PAUSE, None))
    427 
    428                     if playCmd == CMD_MUTEALL:
    429                         wx.PostEvent(self, JetStatusEvent(CMD_MUTEALL, None))
    430                     
    431                     self.SetPlayCommand('')
    432                         
    433                 if self.GetStatus(lastID) != STATUS_CANCELED:
    434                     self.SetStatus(lastID, STATUS_COMPLETE)      
    435                     
    436                 wx.PostEvent(self, JetStatusEvent(LOAD_QUEUE_DISPLAY, None))
    437                 wx.PostEvent(self, JetStatusEvent(CLR_INFO, None))
    438                 
    439         SafeJetShutdown(self.playerLock, self.jet)   
    440         self.SetThreadShutdownFlag(True)
    441                 
    442     def OnJetStatusUpdate(self, evt):
    443         """ All UI needed from within thread called via postevent otherwise mac crashes """
    444         if evt.mode == LOAD_QUEUE_DISPLAY:
    445             self.LoadQueueDisplay()
    446         elif evt.mode == GRAPH_POSITION_UPDATE:
    447             self.graph.UpdateLocation(evt.data)
    448         elif evt.mode == NEW_SEGMENT_DISPLAY:
    449             self.currentSegmentName = getColumnText(self.queueList, evt.data, 0)
    450             segment = self.jet_file.GetSegment(self.currentSegmentName)
    451             info = self.graph.LoadSegment(segment)
    452             self.trackList.DeleteAllItems()
    453             if info <> None:
    454                 for track in info.trackList:
    455                     self.trackList.AddTrackRow(track)       
    456             self.trackList.CheckTracks(segment.mute_flags)
    457             self.log.SetValue(self.currentSegmentName)
    458         elif evt.mode == CMD_QUEUE_AND_CANCEL:
    459             self.QueueOneSegment(evt.data[0], evt.data[1])
    460         elif evt.mode == CMD_ORIGINALMUTES:
    461             self.trackList.CheckTracks(evt.data)
    462         elif evt.mode == CMD_UNMUTEALL:
    463             num = self.trackList.GetItemCount()
    464             for i in range(num):
    465                 self.trackList.CheckItem(i, False)
    466         elif evt.mode == CMD_MUTEALL:
    467             num = self.trackList.GetItemCount()
    468             for i in range(num):
    469                 self.trackList.CheckItem(i)
    470         elif evt.mode == CLR_INFO:
    471             self.log.SetValue("")
    472             self.graph.ClearGraph()
    473             self.graph.UpdateLocation(0)
    474         elif evt.mode == CMD_PLAY:
    475             self.btnPause.SetLabel(JetDefs.BUT_PAUSE)
    476         elif evt.mode == CMD_PAUSE or evt.mode == CMD_PLAY:
    477             if not self.paused:
    478                 self.jet.Pause()
    479                 self.paused = True
    480                 self.btnPause.SetLabel(JetDefs.BUT_RESUME)
    481             else:
    482                 self.jet.Play()
    483                 self.paused = False
    484                 self.btnPause.SetLabel(JetDefs.BUT_PAUSE)
    485             
    486     def SafeJetRestart(self, lock, jet, filename):
    487         """ Shuts down the jet engine """
    488         SafeJetShutdown(lock, jet)
    489         with lock:
    490             jet = JET()
    491             jet.eas.StartWave()
    492             jet.OpenFile(filename)
    493             return jet
    494                          
    495     def OnClose(self, event):
    496         """ When exiting the audition window, shut down jet play thread """
    497         i = 0
    498         while(not self.GetThreadShutdownFlag() and i < 5):
    499             #Make sure we shutdown the playing thread, but don't wait forever

    500             self.SetKeepPlayingFlag(False)
    501             logging.info("Waiting on shutdown %d" % (self.GetThreadShutdownFlag()))
    502             time.sleep(.5)
    503             i = i + 1      
    504               
    505         #make certain we clean up

    506         if self.jet is not None:
    507             SafeJetShutdown(self.playerLock, self.jet)                
    508         self.Destroy()
    509         
    510