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 23from __future__ import with_statement 24 25import wx 26import sys 27import thread 28import time 29 30from JetUtils import * 31from JetDefs import * 32from JetCtrls import JetListCtrl, JetTrackCtrl 33from JetSegGraph import SegmentGraph, Marker 34from eas import * 35from JetStatusEvent import * 36 37CMD_QUEUE_AND_CANCEL = 'QueueNCancel' 38CMD_QUEUE_AND_CANCEL_CURRENT = 'QueueCancelCurrent' 39CMD_MUTEALL = 'MuteAll' 40CMD_UNMUTEALL = 'UnMuteAll' 41CMD_ORIGINALMUTES = 'MuteOrg' 42CMD_STOP = 'Stop' 43CMD_PAUSE = 'Pause' 44CMD_PLAY = 'Play' 45 46STATUS_PENDING = 'Pending' 47STATUS_PLAYING = 'Playing' 48STATUS_COMPLETE = 'Complete' 49STATUS_CANCELED = 'Canceled' 50STATUS_QUEUED = 'Queued' 51 52LOAD_QUEUE_DISPLAY = 'LOAD_QUEUE' 53GRAPH_POSITION_UPDATE = 'GRAPH_POS' 54NEW_SEGMENT_DISPLAY = 'NEW SEG' 55CLR_INFO = 'CLR_INFO' 56 57class 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