コード例 #1
0
ファイル: MainFrame.py プロジェクト: austinirvine/soundgrain
    def __init__(self, parent, id, pos, size, screen_size):
        wx.Frame.__init__(self, parent, id, "", pos, size)
        self.SetMinSize((600,300))
        self.screen_size = screen_size
        self.is_unsaved = False
        self.currentFile = None
        self.currentPath = None
        self.temps = []
        self.draw = True
        self.lowpass = True
        self.fillPoints = True
        self.editionLevels = [2, 4, 8, 12, 16, 24, 32, 50]
        self.editionLevel = 2
        self.arrowSpeedLevels = [1, 2, 4, 8, 16, 32]
        self.arrowSpeedLevel = 1
        self.audioDriver = None
        self.recall = self.undos = 0
        self.sample_precision = SAMPLE_PRECISION

        self.status = wx.StatusBar(self, -1)
        self.SetStatusBar(self.status)

        self.menuBar = wx.MenuBar()
        self.menu = wx.Menu()
        self.menu.Append(11, "New...\tCtrl+N")
        self.Bind(wx.EVT_MENU, self.handleNew, id=11)
        self.menu.Append(1, "Open...\tCtrl+O")
        self.Bind(wx.EVT_MENU, self.handleOpen, id=1)
        self.menu.Append(2, "Open Soundfile...\tShift+Ctrl+O")
        self.Bind(wx.EVT_MENU, self.handleLoad, id=2)
        self.menu.Append(12, "Insert Soundfile...\tShift+Ctrl+I")
        self.Bind(wx.EVT_MENU, self.handleInsert, id=12)
        self.menu.Append(3, "Save\tCtrl+S")
        self.Bind(wx.EVT_MENU, self.handleSave, id=3)
        self.menu.Append(4, "Save as...\tShift+Ctrl+S")
        self.Bind(wx.EVT_MENU, self.handleSaveAs, id=4)
        self.menu.AppendSeparator()
        self.menu.Append(6, "Open Granulator Controls\tCtrl+P")
        self.Bind(wx.EVT_MENU, self.openFxWindow, id=6)
        self.menu.Append(5, "Open Envelope Window\tCtrl+E")
        self.Bind(wx.EVT_MENU, self.openEnvelopeWindow, id=5)
        self.menu.AppendSeparator()
        self.menu.Append(7, "Run\tCtrl+R", "", wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onRun, id=7)
        self.menu.AppendSeparator()
        quit_item = self.menu.Append(wx.ID_EXIT, "Quit\tCtrl+Q")
        self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)
        self.menuBar.Append(self.menu, "&File")

        self.menu1 = wx.Menu()
        self.menu1.Append(110, "Undo\tCtrl+Z", "")
        self.menu1.Enable(110, False)
        self.Bind(wx.EVT_MENU, self.handleUndo, id=110)
        self.menu1.Append(111, "Redo\tShift+Ctrl+Z", "")
        self.menu1.Enable(111, False)
        self.Bind(wx.EVT_MENU, self.handleUndo, id=111)
        self.menu1.InsertSeparator(2)
        self.menu1.Append(100, "Draw Waveform", "", wx.ITEM_CHECK)
        self.menu1.Check(100, True)
        self.Bind(wx.EVT_MENU, self.handleDrawWave, id=100)
        self.menu1.Append(101, "Activate Lowpass filter", "", wx.ITEM_CHECK)
        self.menu1.Check(101, True)
        self.Bind(wx.EVT_MENU, self.handleActivateLp, id=101)
        self.menu1.Append(102, "Fill points", "", wx.ITEM_CHECK)
        self.menu1.Check(102, True)
        self.Bind(wx.EVT_MENU, self.handleActivateFill, id=102)
        self.submenu1 = wx.Menu()
        for i, level in enumerate(self.editionLevels):
            menuId = 1000 + i
            self.submenu1.Append(menuId, str(level), "", wx.ITEM_RADIO)
            self.Bind(wx.EVT_MENU, self.handlesEditionLevels, id=menuId)
        self.menu1.AppendSubMenu(self.submenu1, "Edition levels")
        self.submenu2 = wx.Menu()
        for i, level in enumerate(self.arrowSpeedLevels):
            menuId = 12000 + i
            self.submenu2.Append(menuId, str(level), "", wx.ITEM_RADIO)
            self.Bind(wx.EVT_MENU, self.handlesArrowSpeedLevels, id=menuId)
        self.menu1.AppendSubMenu(self.submenu2, "Arrow Moves Speed levels")
        self.menu1.InsertSeparator(8)
        self.menu1.Append(103, "Reinit counters\tCtrl+T", "")
        self.Bind(wx.EVT_MENU, self.handleReinit, id=103)
        self.menuBar.Append(self.menu1, "&Drawing")

        self.menu2 = wx.Menu()
        self.menuBar.Append(self.menu2, "&Audio Drivers")

        self.menu3 = wx.Menu()
        self.menu3.Append(2004, "Memorize Trajectory\tShift+Ctrl+M", "")
        self.Bind(wx.EVT_MENU, self.handleMemorize, id=2004)
        self.menu3.Append(2005, "Midi Settings...\tShift+Alt+Ctrl+M", "")
        self.Bind(wx.EVT_MENU, self.showMidiSettings, id=2005)
        self.menuBar.Append(self.menu3, "&Midi")

        self.menu4 = wx.Menu()
        self.menu4.Append(400, "Add Reverb ball\tCtrl+1", "")
        self.menu4.Append(401, "Add Delay ball\tCtrl+2", "")
        self.menu4.Append(402, "Add Disto ball\tCtrl+3", "")
        self.menu4.Append(403, "Add Waveguide ball\tCtrl+4", "")
        self.menu4.Append(404, "Add Complex Resonator ball\tCtrl+5", "")
        self.menu4.Append(405, "Add Degrade ball\tCtrl+6", "")
        self.menu4.Append(406, "Add Harmonizer ball\tCtrl+7", "")
        self.menu4.Append(407, "Add Clipper ball\tCtrl+8", "")
        self.menu4.Append(408, "Add Flanger ball\tCtrl+9", "")
        self.menu4.Append(409, "Add Detuned Resonator ball\tCtrl+0", "")
        for i in range(10):
            self.Bind(wx.EVT_MENU, self.addFxBall, id=400+i)
        self.menuBar.Append(self.menu4, "&FxBall")

        menu5 = wx.Menu()
        helpItem = menu5.Append(wx.ID_ABOUT, '&About %s %s' % (NAME, SG_VERSION))
        self.Bind(wx.EVT_MENU, self.showAbout, helpItem)
        commands = menu5.Append(501, "Open SoundGrain Documentation\tCtrl+H")
        self.Bind(wx.EVT_MENU, self.openCommandsPage, commands)
        self.menuBar.Append(menu5, '&Help')

        self.SetMenuBar(self.menuBar)

        if os.path.isfile(PREFFILE):
            with open(PREFFILE, "r", encoding=FILE_ENCODING) as f:
                lines = f.readlines()
                try:
                    auDriver = ensureNFD(lines[0].split("=")[1].replace("\n", ""))
                except:
                    auDriver = None
                try:
                    miDriver = ensureNFD(lines[1].split("=")[1].replace("\n", ""))
                except:
                    miDriver = None
                try:
                    self.lastFilePath = lines[3].split("=")[1].replace("\n", "")
                except:
                    self.lastFilePath = os.path.expanduser("~")
                try:
                    self.lastAudioPath = lines[4].split("=")[1].replace("\n", "")
                except:
                    self.lastAudioPath = os.path.expanduser("~")
        else:
            auDriver = None
            miDriver = None
            self.lastFilePath = os.path.expanduser("~")
            self.lastAudioPath = os.path.expanduser("~")

        mainBox = wx.BoxSizer(wx.HORIZONTAL)
        self.panel = DrawingSurface(self)
        self.controls = ControlPanel(self, self.panel)
        mainBox.Add(self.panel, 20, wx.EXPAND, 5)
        mainBox.Add(self.controls, 0, wx.EXPAND, 5)
        self.SetSizer(mainBox)

        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.SetTitle('%s %s - ' % (NAME, SG_VERSION))
        self.envelopeFrame = EnvelopeFrame(self)
        self.sg_audio = SG_Audio(self.panel.clock, self.panel.Refresh, 
                                 self.controls, self.panel.addTrajFromMemory,
                                 self.panel.deleteMemorizedTraj, self.envelopeFrame)
        self.granulatorControls = GranulatorFrame(self, self.sg_audio)
        self.midiSettings = MidiSettings(self, self.panel, self.sg_audio, miDriver)
        self.createInitTempFile()

        self.check(auDriver)
コード例 #2
0
ファイル: MainFrame.py プロジェクト: austinirvine/soundgrain
class MainFrame(wx.Frame):
    def __init__(self, parent, id, pos, size, screen_size):
        wx.Frame.__init__(self, parent, id, "", pos, size)
        self.SetMinSize((600,300))
        self.screen_size = screen_size
        self.is_unsaved = False
        self.currentFile = None
        self.currentPath = None
        self.temps = []
        self.draw = True
        self.lowpass = True
        self.fillPoints = True
        self.editionLevels = [2, 4, 8, 12, 16, 24, 32, 50]
        self.editionLevel = 2
        self.arrowSpeedLevels = [1, 2, 4, 8, 16, 32]
        self.arrowSpeedLevel = 1
        self.audioDriver = None
        self.recall = self.undos = 0
        self.sample_precision = SAMPLE_PRECISION

        self.status = wx.StatusBar(self, -1)
        self.SetStatusBar(self.status)

        self.menuBar = wx.MenuBar()
        self.menu = wx.Menu()
        self.menu.Append(11, "New...\tCtrl+N")
        self.Bind(wx.EVT_MENU, self.handleNew, id=11)
        self.menu.Append(1, "Open...\tCtrl+O")
        self.Bind(wx.EVT_MENU, self.handleOpen, id=1)
        self.menu.Append(2, "Open Soundfile...\tShift+Ctrl+O")
        self.Bind(wx.EVT_MENU, self.handleLoad, id=2)
        self.menu.Append(12, "Insert Soundfile...\tShift+Ctrl+I")
        self.Bind(wx.EVT_MENU, self.handleInsert, id=12)
        self.menu.Append(3, "Save\tCtrl+S")
        self.Bind(wx.EVT_MENU, self.handleSave, id=3)
        self.menu.Append(4, "Save as...\tShift+Ctrl+S")
        self.Bind(wx.EVT_MENU, self.handleSaveAs, id=4)
        self.menu.AppendSeparator()
        self.menu.Append(6, "Open Granulator Controls\tCtrl+P")
        self.Bind(wx.EVT_MENU, self.openFxWindow, id=6)
        self.menu.Append(5, "Open Envelope Window\tCtrl+E")
        self.Bind(wx.EVT_MENU, self.openEnvelopeWindow, id=5)
        self.menu.AppendSeparator()
        self.menu.Append(7, "Run\tCtrl+R", "", wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onRun, id=7)
        self.menu.AppendSeparator()
        quit_item = self.menu.Append(wx.ID_EXIT, "Quit\tCtrl+Q")
        self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)
        self.menuBar.Append(self.menu, "&File")

        self.menu1 = wx.Menu()
        self.menu1.Append(110, "Undo\tCtrl+Z", "")
        self.menu1.Enable(110, False)
        self.Bind(wx.EVT_MENU, self.handleUndo, id=110)
        self.menu1.Append(111, "Redo\tShift+Ctrl+Z", "")
        self.menu1.Enable(111, False)
        self.Bind(wx.EVT_MENU, self.handleUndo, id=111)
        self.menu1.InsertSeparator(2)
        self.menu1.Append(100, "Draw Waveform", "", wx.ITEM_CHECK)
        self.menu1.Check(100, True)
        self.Bind(wx.EVT_MENU, self.handleDrawWave, id=100)
        self.menu1.Append(101, "Activate Lowpass filter", "", wx.ITEM_CHECK)
        self.menu1.Check(101, True)
        self.Bind(wx.EVT_MENU, self.handleActivateLp, id=101)
        self.menu1.Append(102, "Fill points", "", wx.ITEM_CHECK)
        self.menu1.Check(102, True)
        self.Bind(wx.EVT_MENU, self.handleActivateFill, id=102)
        self.submenu1 = wx.Menu()
        for i, level in enumerate(self.editionLevels):
            menuId = 1000 + i
            self.submenu1.Append(menuId, str(level), "", wx.ITEM_RADIO)
            self.Bind(wx.EVT_MENU, self.handlesEditionLevels, id=menuId)
        self.menu1.AppendSubMenu(self.submenu1, "Edition levels")
        self.submenu2 = wx.Menu()
        for i, level in enumerate(self.arrowSpeedLevels):
            menuId = 12000 + i
            self.submenu2.Append(menuId, str(level), "", wx.ITEM_RADIO)
            self.Bind(wx.EVT_MENU, self.handlesArrowSpeedLevels, id=menuId)
        self.menu1.AppendSubMenu(self.submenu2, "Arrow Moves Speed levels")
        self.menu1.InsertSeparator(8)
        self.menu1.Append(103, "Reinit counters\tCtrl+T", "")
        self.Bind(wx.EVT_MENU, self.handleReinit, id=103)
        self.menuBar.Append(self.menu1, "&Drawing")

        self.menu2 = wx.Menu()
        self.menuBar.Append(self.menu2, "&Audio Drivers")

        self.menu3 = wx.Menu()
        self.menu3.Append(2004, "Memorize Trajectory\tShift+Ctrl+M", "")
        self.Bind(wx.EVT_MENU, self.handleMemorize, id=2004)
        self.menu3.Append(2005, "Midi Settings...\tShift+Alt+Ctrl+M", "")
        self.Bind(wx.EVT_MENU, self.showMidiSettings, id=2005)
        self.menuBar.Append(self.menu3, "&Midi")

        self.menu4 = wx.Menu()
        self.menu4.Append(400, "Add Reverb ball\tCtrl+1", "")
        self.menu4.Append(401, "Add Delay ball\tCtrl+2", "")
        self.menu4.Append(402, "Add Disto ball\tCtrl+3", "")
        self.menu4.Append(403, "Add Waveguide ball\tCtrl+4", "")
        self.menu4.Append(404, "Add Complex Resonator ball\tCtrl+5", "")
        self.menu4.Append(405, "Add Degrade ball\tCtrl+6", "")
        self.menu4.Append(406, "Add Harmonizer ball\tCtrl+7", "")
        self.menu4.Append(407, "Add Clipper ball\tCtrl+8", "")
        self.menu4.Append(408, "Add Flanger ball\tCtrl+9", "")
        self.menu4.Append(409, "Add Detuned Resonator ball\tCtrl+0", "")
        for i in range(10):
            self.Bind(wx.EVT_MENU, self.addFxBall, id=400+i)
        self.menuBar.Append(self.menu4, "&FxBall")

        menu5 = wx.Menu()
        helpItem = menu5.Append(wx.ID_ABOUT, '&About %s %s' % (NAME, SG_VERSION))
        self.Bind(wx.EVT_MENU, self.showAbout, helpItem)
        commands = menu5.Append(501, "Open SoundGrain Documentation\tCtrl+H")
        self.Bind(wx.EVT_MENU, self.openCommandsPage, commands)
        self.menuBar.Append(menu5, '&Help')

        self.SetMenuBar(self.menuBar)

        if os.path.isfile(PREFFILE):
            with open(PREFFILE, "r", encoding=FILE_ENCODING) as f:
                lines = f.readlines()
                try:
                    auDriver = ensureNFD(lines[0].split("=")[1].replace("\n", ""))
                except:
                    auDriver = None
                try:
                    miDriver = ensureNFD(lines[1].split("=")[1].replace("\n", ""))
                except:
                    miDriver = None
                try:
                    self.lastFilePath = lines[3].split("=")[1].replace("\n", "")
                except:
                    self.lastFilePath = os.path.expanduser("~")
                try:
                    self.lastAudioPath = lines[4].split("=")[1].replace("\n", "")
                except:
                    self.lastAudioPath = os.path.expanduser("~")
        else:
            auDriver = None
            miDriver = None
            self.lastFilePath = os.path.expanduser("~")
            self.lastAudioPath = os.path.expanduser("~")

        mainBox = wx.BoxSizer(wx.HORIZONTAL)
        self.panel = DrawingSurface(self)
        self.controls = ControlPanel(self, self.panel)
        mainBox.Add(self.panel, 20, wx.EXPAND, 5)
        mainBox.Add(self.controls, 0, wx.EXPAND, 5)
        self.SetSizer(mainBox)

        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.SetTitle('%s %s - ' % (NAME, SG_VERSION))
        self.envelopeFrame = EnvelopeFrame(self)
        self.sg_audio = SG_Audio(self.panel.clock, self.panel.Refresh, 
                                 self.controls, self.panel.addTrajFromMemory,
                                 self.panel.deleteMemorizedTraj, self.envelopeFrame)
        self.granulatorControls = GranulatorFrame(self, self.sg_audio)
        self.midiSettings = MidiSettings(self, self.panel, self.sg_audio, miDriver)
        self.createInitTempFile()

        self.check(auDriver)

    def onRun(self, event):
        self.controls.handleAudio(event)

    def check(self, pref=None):
        self.status.SetStatusText('Scanning audio drivers...')
        self.driversList, self.driverIndexes, selected = checkForDrivers()
        self.driversList = [ensureNFD(driver) for driver in self.driversList]
        if pref == None:
            self.audioDriver = selected
        else:
            if pref in self.driversList:
                self.audioDriver = self.driverIndexes[self.driversList.index(pref)]
            else:
                self.audioDriver = selected
        for i, driver in enumerate(self.driversList):
            menuId = 200 + i
            self.menu2.Append(menuId, driver, "", wx.ITEM_RADIO)
            self.Bind(wx.EVT_MENU, self.handleDriver, id=menuId)
            if driver == self.driversList[self.driverIndexes.index(self.audioDriver)]:
                self.menu2.Check(menuId, True)
        self.menu2.AppendSeparator()
        precision_label = self.menu2.Append(-1, "Sample Precision (Require restarting the app)", "")
        precision_label.Enable(False)
        menuId += 1
        item32 = self.menu2.Append(menuId, "32-bit", "", wx.ITEM_CHECK)
        if SAMPLE_PRECISION == "32-bit":
            self.menu2.Check(menuId, True)
            self.menu2.Enable(menuId, False)
        self.Bind(wx.EVT_MENU, self.handlePrecision, id=menuId)
        menuId += 1
        item64 = self.menu2.Append(menuId, "64-bit", "", wx.ITEM_CHECK)
        if SAMPLE_PRECISION == "64-bit":
            self.menu2.Check(menuId, True)
            self.menu2.Enable(menuId, False)
        self.Bind(wx.EVT_MENU, self.handlePrecision, id=menuId)
        self.status.SetStatusText('Audio drivers loaded')
        self.controls.bootServer()

    def showMidiSettings(self, evt):
        self.midiSettings.show()

    def enableDrivers(self, state):
        for i in range(len(self.driversList)):
            self.menu2.FindItemById(200+i).Enable(state)

    def handleReinit(self, evt):
        for t in self.panel.getAllTrajectories():
            t.initCounter()

    def addFxBall(self, evt):
        self.panel.addFxBall(evt.GetId() - 400)

    def handleMemorize(self, evt):
        self.panel.Memorize()

    def handleDrawWave(self, evt):
        self.draw = self.menu1.IsChecked(100)
        self.drawing()

    def setDraw(self, state):
        self.menu1.Check(100, state)
        self.draw = state

    def drawing(self):
        if not self.draw:
            self.panel.sndBitmap = None
            self.panel.needBitmap = True
            self.panel.Refresh()
        else:
            if self.controls.sndPath != "":
                if self.controls.sndPath in self.panel.bitmapDict:
                    self.panel.list = self.panel.bitmapDict[self.controls.sndPath]
                    self.panel.create_bitmap()
                else:
                    self.panel.analyse(self.controls.sndPath)

    def handleActivateLp(self, evt):
        self.lowpass = self.menu1.IsChecked(101)
        self.checkLowpass()

    def setLowpass(self, state):
        self.menu1.Check(101, state)
        self.lowpass = state
        self.checkLowpass()

    def checkLowpass(self):
        for t in self.panel.getAllTrajectories():
            t.activateLp(self.lowpass)
        if self.lowpass:
            self.controls.drawing.sl_cutoff.Enable()
            self.controls.drawing.sl_q.Enable()
        else:
            self.controls.drawing.sl_cutoff.Disable()
            self.controls.drawing.sl_q.Disable()

    def handleActivateFill(self, evt):
        self.fillPoints = self.menu1.IsChecked(102)

    def setFillPoints(self, state):
        self.menu1.Check(102, state)
        self.fillPoints = state

    def handlesEditionLevels(self, evt):
        menuId = evt.GetId()
        self.editionLevel = self.editionLevels[menuId - 1000]
        self.pushEditionLevel()

    def handlesArrowSpeedLevels(self, evt):
        menuId = evt.GetId()
        self.arrowSpeedLevel = self.arrowSpeedLevels[menuId - 12000]

    def setEditionLevel(self, level):
        self.submenu1.Check(self.editionLevels.index(level)+1000, True)
        self.editionLevel = level
        self.pushEditionLevel()

    def pushEditionLevel(self):
        for t in self.panel.getAllTrajectories():
            t.setEditionLevel(self.editionLevel)

    def handleDriver(self, evt):
        status, path = self.checkForMixedSound()
        if not status:
            for i, driver in enumerate(self.driversList):
                menuId = 200 + i
                if driver == self.driversList[self.driverIndexes.index(self.audioDriver)]:
                    self.menu2.Check(menuId, True)
            return
        if "Mixed sound" in self.controls.sndPath:
            self.controls.sndPath = path
            if path == "":
                self.panel.sndBitmap = None
                self.panel.needBitmap = True
                wx.CallAfter(self.panel.Refresh)
        menuId = evt.GetId()
        self.audioDriver = self.driverIndexes[menuId - 200]
        self.controls.shutdownServer()
        self.controls.bootServer()

    def handlePrecision(self, evt):
        menuId = evt.GetId()
        item = self.menu2.FindItemById(menuId)
        label = item.GetItemLabel()
        self.sample_precision = label
        if label == "32-bit":
            self.menu2.Check(menuId, True)
            self.menu2.Enable(menuId, False)
            self.menu2.Check(menuId+1, False)
            self.menu2.Enable(menuId+1, True)
        elif label == "64-bit":
            self.menu2.Check(menuId, True)
            self.menu2.Enable(menuId, False)
            self.menu2.Check(menuId-1, False)
            self.menu2.Enable(menuId-1, True)

    def openFxWindow(self, evt):
        if self.granulatorControls.IsShown():
            self.granulatorControls.Hide()
        else:
            self.granulatorControls.SetTitle('Granulator controls')
            self.granulatorControls.Show()

    def openEnvelopeWindow(self, evt):
        if self.envelopeFrame.IsShown():
            self.envelopeFrame.Hide()
        else:
            self.envelopeFrame.Show()

    def handleUndo(self, evt):
        self.recallTempFile(evt.GetId())

    def handleNew(self, evt):
        cancel = False
        newpath = False
        if self.controls.sndPath != "":
            status, path = self.checkForMixedSound()
            if "Mixed sound" in self.controls.sndPath:
                self.controls.sndPath = path
                if path != "":
                    newpath = True
        if self.is_unsaved or newpath:
            if self.currentFile == None:
                curfile = "Granulator.sg"
            else:
                curfile = self.currentFile
            dlg = wx.MessageDialog(self, 
                    "Do you want to save the changes you made in the document %s ?" % curfile,
                    'File Unsaved...', wx.YES_NO | wx.YES_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION)
            ret = dlg.ShowModal()
            if ret == wx.ID_YES:
                self.handleSave(None)
            elif ret == wx.ID_CANCEL:
                cancel = True
            dlg.Destroy()
        if cancel:
            return
        self.panel.sndBitmap = None
        self.controls.sndPath = ""
        self.loadFile(os.path.join(RESOURCES_PATH, 'new_soundgrain_file.sg'))

    def handleOpen(self, evt):
        dlg = wx.FileDialog(self, message="Open SoundGrain file...",
                            defaultDir=self.lastFilePath,
                            defaultFile="",
                            wildcard="SoundGrain file (*.sg)|*.sg",
                            style=wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.loadFile(ensureNFD(path))
            self.lastFilePath = os.path.split(path)[0]
        dlg.Destroy()

    def handleLoad(self, evt):
        self.controls.handleLoad()

    def handleInsert(self, evt):
        self.controls.handleInsert()

    def handleSave(self, evt):
        if self.currentFile:
            self.saveFile(self.currentFile)
        else:
            self.handleSaveAs(None)

    def handleSaveAs(self, evt):
        dlg = wx.FileDialog(self, message="Save file as ...",
                            defaultDir=self.lastFilePath,
                            defaultFile="Granulator.sg",
                            style=wx.FD_SAVE)
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            if os.path.isfile(path):
                dlg2 = wx.MessageDialog(self,
                      '"%s" already exists. Do you want to replace it?' % os.path.split(path)[1],
                      'Warning!', wx.OK | wx.ICON_INFORMATION | wx.CANCEL)
                if dlg2.ShowModal() == wx.ID_OK:
                    dlg2.Destroy()
                    self.saveFile(path)
                    self.lastFilePath = os.path.split(path)[0]
                    dlg.Destroy()
                else:
                    dlg2.Destroy()
                    dlg.Destroy()
                    self.handleSaveAs(None)
            else:
                self.saveFile(path)
                self.lastFilePath = os.path.split(path)[0]
                dlg.Destroy()

    def getState(self):
        saveDict = {}
        ### Main Frame ###
        saveDict['version'] = SG_VERSION
        saveDict['platform'] = PLATFORM
        saveDict['MainFrame'] = {}
        saveDict['MainFrame']['draw'] = self.draw
        saveDict['MainFrame']['lowpass'] = self.lowpass
        saveDict['MainFrame']['fillPoints'] = self.fillPoints
        saveDict['MainFrame']['editionLevel'] = self.editionLevel
        saveDict['MainFrame']['size'] = tuple(self.GetSize())
        ### Surface Panel ###
        saveDict["SurfaceSize"] = tuple(self.panel.GetSize())
        ### Controls Frame ###
        saveDict['ControlFrame'] = self.granulatorControls.save()
        ### Midi Frame ###
        saveDict['MidiSettings'] = self.midiSettings.save()
        ### Control Panel ###
        saveDict['ControlPanel'] = {}
        saveDict['ControlPanel']['type'] = self.controls.getType()
        saveDict['ControlPanel']['closed'] = self.controls.getClosed()
        saveDict['ControlPanel']['cutoff'] = self.controls.getCutoff()
        saveDict['ControlPanel']['q'] = self.controls.getQ()
        saveDict['ControlPanel']['period'] = self.controls.getPeriod()
        saveDict['ControlPanel']['scaling'] = self.controls.getScaling()
        saveDict['ControlPanel']['eqfreqs'] = self.controls.getEqFreqs()
        saveDict['ControlPanel']['eqamps'] = self.controls.getEqAmps()
        saveDict['ControlPanel']['compress'] = self.controls.getCompValues()
        saveDict['ControlPanel']['globalamp'] = self.controls.getAmp()
        saveDict['ControlPanel']['nchnls'] = self.controls.getNchnls()
        saveDict['ControlPanel']['sr'] = self.controls.getSamplingRate()
        saveDict['ControlPanel']['fileformat'] = self.controls.getFileFormat()
        saveDict['ControlPanel']['sampletype'] = self.controls.getSampleType()
        saveDict['ControlPanel']['sound'] = self.controls.sndPath
        saveDict['ControlPanel']['recfolder'] = self.controls.tx_rec_folder.GetValue()
        saveDict['ControlPanel']['filename'] = self.controls.tx_output.GetValue()
        ### Trajectories ###
        saveDict['Trajectories'] = {}
        for i, t in enumerate(self.panel.getAllTrajectories()):
            saveDict['Trajectories'][str(i)] = t.getAttributes()
        saveDict['MemorizedTrajectory'] = self.panel.memorizedTrajectory.getAttributes()
        ### Grain Envelope ###
        saveDict['Envelope'] = self.envelopeFrame.save()
        ### Fx Balls ###
        saveDict['fxballs'] = {}
        for key, value in self.panel.fxballs.items():
            saveDict['fxballs'][str(key)] = value.save()
        return saveDict

    def saveFile(self, path):
        if self.controls.sndPath != "":
            status, sndpath = self.checkForMixedSound()
            if not status:
                return
            if sndpath != "":
                self.controls.sndPath = sndpath
        self.currentFile = path
        self.currentPath = os.path.split(path)[0]
        saveDict = self.getState()
        msg = xmlrpclib.dumps((saveDict, ), allow_none=True)
        f = open(path, 'w')
        f.write(msg)
        f.close()
        self.SetTitle('%s %s - %s' % (NAME, SG_VERSION, os.path.split(self.currentFile)[1]))
        self.is_unsaved = False

    def setState(self, dict):
        version = dict.get('version', '3.0')
        platform = dict.get('platform', 'darwin')
        ### Surface panel ###
        surfaceSize = dict.get('SurfaceSize', None)
        if surfaceSize != None:
            self.panel.SetSize(surfaceSize)
        ### Main Frame ###
        self.setDraw(dict['MainFrame']['draw'])
        self.setLowpass(dict['MainFrame']['lowpass'])
        self.setFillPoints(dict['MainFrame']['fillPoints'])
        self.setEditionLevel(dict['MainFrame']['editionLevel'])
        size = dict['MainFrame']['size']
        if platform == 'darwin':
            if sys.platform == 'darwin':
                self.SetSize(size)
            elif sys.platform == "win32":
                self.SetSize((size[0]+10, size[1]+38))
            else:
                self.SetSize((size[0]+3, size[1]+13))
        elif platform == "win32":
            if sys.platform == 'darwin':
                self.SetSize((size[0]-10, size[1]-38))
            elif sys.platform == "win32":
                self.SetSize(size)
            else:
                self.SetSize((size[0]-7, size[1]-25))
        else:
            if sys.platform == 'darwin':
                self.SetSize((size[0]-3, size[1]-13))
            elif sys.platform == "win32":
                self.SetSize((size[0]+7, size[1]+25))
            else:
                self.SetSize(size)
        if surfaceSize != None:
            xfac = float(self.panel.GetSize()[0]) / surfaceSize[0]
            yfac = float(self.panel.GetSize()[1]) / surfaceSize[1]
        else:
            xfac, yfac = 1, 1
        ### Control Frame ###
        self.granulatorControls.load(dict['ControlFrame'])
        ### Midi Frame ###
        self.midiSettings.load(dict.get("MidiSettings", None))
        ### Control panel ###
        self.controls.setType(dict['ControlPanel']['type'])
        self.controls.setClosed(dict['ControlPanel']['closed'])
        self.controls.setCutoff(dict['ControlPanel']['cutoff'])
        self.controls.setQ(dict['ControlPanel']['q'])
        self.controls.setPeriod(dict['ControlPanel']['period'])
        self.controls.setScaling(dict['ControlPanel']['scaling'])
        self.controls.setEqFreqs(dict['ControlPanel'].get('eqfreqs', [100, 500, 2000]))
        self.controls.setEqAmps(dict['ControlPanel'].get('eqamps', [0, 0, 0, 0]))
        self.controls.setCompValues(dict['ControlPanel'].get('compress', [-3, 2, 0.01, 0.1]))
        self.controls.setAmp(dict['ControlPanel']['globalamp'])
        self.controls.setNchnls(dict['ControlPanel'].get('nchnls', "2"))
        self.controls.setSamplingRate(dict['ControlPanel'].get('sr', 44100))
        self.controls.setFileFormat(dict['ControlPanel'].get('fileformat', 0))
        self.controls.setSampleType(dict['ControlPanel'].get('sampletype', 0))
        self.controls.loadSound(ensureNFD(dict['ControlPanel']['sound']))
        self.controls.setRecordFolder(ensureNFD(dict['ControlPanel'].get('recfolder', '~/Desktop')))
        self.controls.setRecordFilename(ensureNFD(dict['ControlPanel'].get('filename', 'snd')))
        ### Trajectories ###
        for i, t in enumerate(self.panel.getAllTrajectories()):
            t.setAttributes(dict['Trajectories'][str(i)], xfac, yfac)
        if 'MemorizedTrajectory' in dict:
            self.panel.memorizedTrajectory.setAttributes(dict['MemorizedTrajectory'], xfac, yfac)
        ### Grain Envelope ###
        if "Envelope" in dict:
            self.envelopeFrame.load(dict["Envelope"])
        if 'fxballs' in dict:
            self.panel.restoreFxBalls(dict["fxballs"], xfac, yfac)
        self.controls.resetPlaybackSliders()

    def loadFile(self, path):
        if self.midiSettings.IsShown():
            self.midiSettings.Hide()
        self.panel.removeAllFxBalls()
        f = open(path, 'r')
        msg = f.read()
        f.close()
        result, method = xmlrpclib.loads(msg)
        dict = result[0]
        if 'new_soundgrain_file.sg' in path:
            self.currentFile = None
            self.currentPath = None
            title = '%s %s - ' % (NAME, SG_VERSION)
            self.status.SetStatusText("")
        else:
            self.currentFile = path
            self.currentPath = os.path.split(path)[0]
            title = '%s %s - %s' % (NAME, SG_VERSION, os.path.split(self.currentFile)[1])
        self.panel.trajectories = [Trajectory(self.panel, i+1) for i in range(MAX_STREAMS)]
        self.panel.memorizedTrajectory = Trajectory(self.panel, -1)
        self.panel.memorizedId = {}
        self.controls.setSelected(0)
        self.setState(dict)
        self.SetTitle(title)
        self.panel.needBitmap = True
        size = self.GetSize()
        if size[0] > self.screen_size[0]:
            x = self.screen_size[0] - 50
        else:
            x = size[0]
        if size[1] > self.screen_size[1]:
            y = self.screen_size[1] - 50
        else:
            y = size[1]
        size = (x, y)
        wx.CallAfter(self.panel.Refresh)
        wx.CallLater(100, self.SetSize, size)

    def createInitTempFile(self):
        d = {}
        for i, t in enumerate(self.panel.getAllTrajectories()):
            d[i] = str(t.getAttributes())
        self.temps.insert(0, d)

    def createTempFile(self):
        d = {}
        for i, t in enumerate(self.panel.getAllTrajectories()):
            d[i] = str(t.getAttributes())
        self.temps.insert(0, d)
        self.recall = self.undos = 0
        self.menu1.Enable(110, True)
        self.menu1.Enable(111, False)
        self.is_unsaved = True

    def recallTempFile(self, id):
        if self.temps and self.recall < len(self.temps):
            if id == 110:
                self.recall += 1
                self.undos += 1
            else:
                self.recall -= 1
                self.undos -= 1
            d = self.temps[self.recall]
            for i, t in enumerate(self.panel.getAllTrajectories()):
                t.setAttributes(eval(d[i]))
            self.panel.needBitmap = True
            self.Refresh()
        if self.recall >= len(self.temps) - 1:
            self.menu1.Enable(110, False)
        else:
            self.menu1.Enable(110, True)
        if self.undos == 0:
            self.menu1.Enable(111, False)
        else:
            self.menu1.Enable(111, True)
        self.is_unsaved = True

    def checkForMixedSound(self):
        return_status = True
        saved_path = ""
        if "Mixed sound" in self.controls.sndPath:
            dlg = wx.MessageDialog(self, "There is a mixed sound loaded in the drawing table, if you don't save it, it will be lost. Do you want to save it on disk ?",
                                   'Mixed sound no saved...', wx.YES_NO | wx.YES_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION)
            ret = dlg.ShowModal()
            if ret == wx.ID_YES:
                save_dialog = True
                return_status = True
            elif ret == wx.ID_NO:
                save_dialog = False
                return_status = True
            else:
                save_dialog = False
                return_status = False
            dlg.Destroy()
            if save_dialog:
                ext = EXPORT_FORMATS[self.controls.fileformat].lower()
                wildcard = AUDIO_WILDCARD
                dlg2 = wx.FileDialog(self, message="Choose a filename...", 
                                     defaultDir=os.path.expanduser("~"),
                                     defaultFile="mixedtable.%s" % ext, 
                                     wildcard=wildcard, 
                                     style=wx.FD_SAVE | wx.FD_CHANGE_DIR)
                if dlg2.ShowModal() == wx.ID_OK:
                    path = dlg2.GetPath()
                    if path != "":
                        p, ext = os.path.splitext(path)
                        if ext.upper() in EXPORT_FORMATS:
                            fileformat = EXPORT_FORMATS[ext.upper()]
                        else:
                            fileformat = self.controls.fileformat
                        sampletype = self.controls.sampletype
                        self.sg_audio.table.save(path, fileformat, sampletype)
                        saved_path = path
                dlg2.Destroy()
        return return_status, saved_path

    def OnClose(self, evt):
        newpath = False
        if self.controls.sndPath != "":
            status, path = self.checkForMixedSound()
            if "Mixed sound" in self.controls.sndPath:
                self.controls.sndPath = path
                if path != "":
                    newpath = True
        if self.is_unsaved or newpath:
            if self.currentFile == None:
                curfile = "Granulator.sg"
            else:
                curfile = self.currentFile
            dlg = wx.MessageDialog(self, "Do you want to save the changes you made in the document %s ?" % curfile,
                                   'File Unsaved...', wx.YES_NO | wx.YES_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION)
            ret = dlg.ShowModal()
            if ret == wx.ID_YES:
                self.handleSave(None)
            elif ret == wx.ID_CANCEL:
                return
            dlg.Destroy()
        auDriver = self.driversList[self.driverIndexes.index(self.audioDriver)]
        miDriver = self.midiSettings.getInterface()
        with open(PREFFILE, "w", encoding=FILE_ENCODING) as f:
            f.write("audioDriver=%s\n" % auDriver)
            f.write("midiDriver=%s\n" % miDriver)
            f.write("samplePrecision=%s\n" % self.sample_precision)
            f.write("lastFilePath=%s\n" % self.lastFilePath)
            f.write("lastAudioPath=%s\n" % self.lastAudioPath)
        if self.granulatorControls.IsShown():
            self.granulatorControls.Hide()
        self.controls.meter.OnClose(evt)
        if self.sg_audio.server.getIsStarted():
            self.sg_audio.server.stop()
            time.sleep(0.2)
        self.controls.shutdownServer()
        time.sleep(0.2)
        self.Destroy()
        sys.exit()

    def log(self, text):
        self.status.SetStatusText(text)

    def openCommandsPage(self, evt):
        win = CommandFrame(self, wx.ID_ANY, 
                           "%s %s - Documentation" % (NAME, SG_VERSION), 
                           size=(900, 650))

    def showAbout(self, evt):
        info = AboutDialogInfo()

        description = "Soundgrain is a graphical interface where " \
        "users can draw and edit trajectories to control granular sound synthesis.\n\n" \
        "Soundgrain is written with Python and " \
        "WxPython and uses pyo as its audio engine.\n\n" \

        info.SetName(NAME)
        info.SetVersion('%s' % SG_VERSION)
        info.SetDescription(description)
        info.SetCopyright(u'(C) %s Olivier Belanger' % SG_YEAR)
        AboutBox(info)