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