コード例 #1
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def buildCfg(self):
		# begin by adding the global soundfont or cfg file.
		filename =self.currentSoundfont[0]
		strCfg = ''
		if filename.endswith('.cfg'):
			path, justname = os.path.split(filename)
			if self.audioOptsWindow and self.audioOptsWindow.getUseOldTimidity():
				if ' ' in justname:
					midirender_util.alert("Warning: old version of Timidity probably doesn't support spaces in filenames.")
				if justname.lower()!='timidity.cfg' and os.path.exists(path+os.sep+'timidity.cfg'):
					midirender_util.alert("Warning: there is a filename timidity.cfg in this directory and so the old version of Timidity will use it.")
				strCfg += '\nsource %s' % (justname)
			else:
				strCfg += '\ndir "%s"\nsource "%s"' % (path, filename)
		else:

			strCfg += '\nsoundfont "%s"' % (filename)
			if self.audioOptsWindow is not None and self.audioOptsWindow.getPatchesTakePrecedence():
				strCfg +=' order=1'
		
		# now add customization to override specific voices, if set
		if self.soundfontWindow is not None:
			strCfg += '\n' + self.soundfontWindow.getCfgResults(self.audioOptsWindow and self.audioOptsWindow.getUseOldTimidity())
			
		if self.audioOptsWindow is not None:
			addedLines = self.audioOptsWindow.getAdditionalCfg()
			if addedLines:
				strCfg += '\n'+'\n'.join(addedLines)
			
		return strCfg
コード例 #2
0
ファイル: main.py プロジェクト: acekorg/downpoured_midi_audio
	def onBtnSaveWave(self,e=None):
		if not self.isMidiLoaded: return
		filename = midirender_util.ask_savefile(title="Create Wav File", types=['.wav|Wav file'])
		if not filename: return
			
		midicopy = self.buildModifiedMidi()
		
		if self.audioOptsWindow != None:
			arParams = self.audioOptsWindow.createTimidityOptionsList(includeRenderOptions=True) 
			if arParams==None: return #evidently an error occurred over there
		else:
			arParams =['-Ow']
		
		arParams.append('-o')
		arParams.append(filename)
		
		#Play it synchronously, meaning that the whole program stalls while this happens...
		midirender_util.alert('Beginning wave process. Be patient... this may take a few moments...')
		objplayer = midirender_runtimidity.RenderTimidityMidiPlayer()
		objplayer.setConfiguration(self.buildCfg())
		objplayer.setParameters(arParams)
		
		objplayer.playMidiObject(midicopy, bSynchronous=True)
		if self.consoleOutWindow!=None: self.consoleOutWindow.clear(); self.consoleOutWindow.writeToWindow(objplayer.strLastStdOutput)
		midirender_util.alert('Completed.')
コード例 #3
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def getParamsForTimidity(self, bRenderWav):
		directoryForOldTimidity = None
		
		if self.audioOptsWindow is not None:
			params = self.audioOptsWindow.getOptionsList(includeRenderOptions=bRenderWav) 
			if params is None:
				midirender_util.alert('Could not get parameters.')
				return None, None
			
			if self.audioOptsWindow.getUseOldTimidity():
				if not self.currentSoundfont[0].lower().endswith('.cfg'):
					midirender_util.alert('Provide a cfg with no soundfonts for old timidity.')
					return None, None
					
				directoryForOldTimidity = os.path.split(self.currentSoundfont[0])[0]
		else:
			if bRenderWav:
				# in Linux render 24bit by default (workaround for timidity++ bug 710927)
				if not sys.platform.startswith('win'):
					params = ['-Ow2']
				else:
					params = ['-Ow']
			else:
				params = []
		
		if self.transposePitches is not None and (not self.audioOptsWindow or not self.audioOptsWindow.getUseOldTimidity()):
			params.append('--adjust-key=%d' % int(self.transposePitches))
		
		return params, directoryForOldTimidity
コード例 #4
0
	def getOptionsList(self, includeRenderOptions=False):
		try:
			return self.getOptionsListImpl(includeRenderOptions)
		except:
			e = sys.exc_info()
			midirender_util.alert('Alert: ' + str(e))
			return None
コード例 #5
0
 def getOptionsList(self, includeRenderOptions=False):
     try:
         return self.getOptionsListImpl(includeRenderOptions)
     except:
         e = sys.exc_info()
         midirender_util.alert('Alert: ' + str(e))
         return None
コード例 #6
0
    def getParamsForTimidity(self, bRenderWav):
        directoryForOldTimidity = None

        if self.audioOptsWindow is not None:
            params = self.audioOptsWindow.getOptionsList(
                includeRenderOptions=bRenderWav)
            if params is None:
                midirender_util.alert('Could not get parameters.')
                return None, None

            if self.audioOptsWindow.getUseOldTimidity():
                if not self.currentSoundfont[0].lower().endswith('.cfg'):
                    midirender_util.alert(
                        'Provide a cfg with no soundfonts for old timidity.')
                    return None, None

                directoryForOldTimidity = os.path.split(
                    self.currentSoundfont[0])[0]
        else:
            if bRenderWav:
                # in Linux render 24bit by default (workaround for timidity++ bug 710927)
                # this bug is now fixed in the distributions I've tested
                if not sys.platform.startswith('win'):
                    params = ['-Ow2']
                else:
                    params = ['-Ow']
            else:
                params = []

        if self.transposePitches is not None and (
                not self.audioOptsWindow
                or not self.audioOptsWindow.getUseOldTimidity()):
            params.append('--adjust-key=%d' % int(self.transposePitches))

        return params, directoryForOldTimidity
コード例 #7
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def create_menubar(self,root):
		root.bind('<Control-space>', self.onBtnPlay)
		root.bind('<Control-r>', self.onBtnSaveWave)
		root.bind('<Control-f>', self.openSoundfontWindow)
		root.bind('<Alt-F4>', lambda x:root.quit)
		root.bind('<Control-o>', self.menu_openMidi)
		root.bind('<Control-S>', self.saveModifiedMidi)
		root.bind('<Control-m>', self.openMixerView)
		root.bind('<Control-p>', self.openAudioOptsWindow)
		menubar = Menu(root)
		
		menuFile = Menu(menubar, tearoff=0)
		menubar.add_cascade(label="File", menu=menuFile, underline=0)
		menuFile.add_command(label="Open MIDI", command=self.menu_openMidi, underline=0, accelerator='Ctrl+O')
		menuFile.add_separator()
		menuFile.add_command(label="Modify MIDI Events...", command=self.menuModifyRawMidi, underline=0)
		menuFile.add_separator()
		menuFile.add_command(label="Save Changes From Mixer...", command=self.saveModifiedMidi, underline=0, accelerator='Ctrl+Shift+S')
		menuFile.add_separator()
		menuFile.add_command(label="Exit", command=root.quit, underline=1)
		
		menuAudio = Menu(menubar, tearoff=0)
		menubar.add_cascade(label="Audio", menu=menuAudio, underline=0)
		menuAudio.add_command(label="Play", command=self.onBtnPlay, underline=0)
		menuAudio.add_command(label="Pause", command=self.onBtnPause, underline=2)
		menuAudio.add_command(label="Stop", command=self.onBtnStop, underline=0)
		menuAudio.add_separator()
		menuAudio.add_command(label="Audio Options...", command=self.openAudioOptsWindow, underline=6, accelerator='Ctrl+P')
		menuAudio.add_command(label="Change Tempo...", command=self.menu_changeTempo, underline=0)
		menuAudio.add_command(label="Transpose Pitch...", command=self.menu_changeTranspose, underline=0)
		menuAudio.add_command(label="Copy Options String", command=self.menuCopyAudioOptsString, underline=1)
		menuAudio.add_separator()
		menuAudio.add_command(label="Save Wav", command=self.onBtnSaveWave, underline=5, accelerator='Ctrl+R')
		menuAudio.add_separator()
		menuAudio.add_command(label="Choose SoundFont...", command=self.openSoundfontWindow, underline=13, accelerator='Ctrl+F')
		
		self.objOptionsDuration = IntVar()
		self.objOptionsDuration.set(0)
		self.objOptionsBarlines = IntVar()
		self.objOptionsBarlines.set(1)
		menuView = Menu(menubar, tearoff=0)
		menubar.add_cascade(label="View", menu=menuView, underline=0)
		menuView.add_command(label="Mixer", command=self.openMixerView, underline=0, accelerator='Ctrl+M')
		menuView.add_command(label="Console Output", command=self.menu_openConsoleWindow, underline=0)
		menuView.add_separator()
		menuView.add_command(label="SoundFont Information Tool", command=self.menu_soundFontInfoTool, underline=0)
		menuView.add_separator()
		menuView.add_checkbutton(label="Show Durations in score", variable=self.objOptionsDuration, underline=5, onvalue=1, offvalue=0)
		menuView.add_checkbutton(label="Show Barlines in score", variable=self.objOptionsBarlines, underline=5, onvalue=1, offvalue=0)
		
		menuHelp = Menu(menubar, tearoff=0)
		menuHelp.add_command(label='About', underline=0, command=(lambda: midirender_util.alert('Bmidi to wav, by Ben Fisher 2009\nA graphical frontend for Timidity and sfubar.\n\nSee the documentation at https://github.com/moltenjs/labs_youthful_projects/blob/master/benmidi/README.md\n\nSource code at https://github.com/moltenjs/labs_youthful_projects/tree/master/benmidi/bmidirender','Bmidi to wav')))
		menuHelp.add_command(label='Documentation', underline=0, command=(lambda: midirender_util.alert('There are many pages of online documentation at https://github.com/moltenjs/labs_youthful_projects/blob/master/benmidi/README.md','Bmidi to wav')))
		menubar.add_cascade(label="Help", menu=menuHelp, underline=0)
		
		root.config(menu=menubar)
コード例 #8
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def menu_changeTranspose(self, e=None):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
			
		strPrompt = 'Adjust key (i.e., transpose the song) by n half tones. Valid range is from -24 to 24.'
		default = self.transposePitches if self.transposePitches else 0.0
		res = midirender_util.ask_float(strPrompt, default=default, min=-24.1, max=24.1, title='Transpose')
		if res is not None and res is not False:
			self.transposePitches = int(res)
コード例 #9
0
	def getVariableValueAsIntOrNoneIfDefault(self, val, varname, min, max, defaultValueMapToNone):
		if val == '' or val==defaultValueMapToNone:
			return None
		try: val = int(val); valid = val >= min and val<=max
		except: valid=False
		if not valid:
			midirender_util.alert('%s must be an integer %d to %d' % (varname, min, max))
			return None
		else:
			return val
コード例 #10
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def onBtnPlay(self, e=None):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
		
		params, directoryForOldTimidity = self.getParamsForTimidity(False)
		bypassTimidity = False
		if self.audioOptsWindow is not None:
			bypassTimidity = self.audioOptsWindow.getBypassTimidity()
		
		if params is not None:
			self.player.actionPlay(self.buildModifiedMidi(), params, self.buildCfg(), directoryForOldTimidity, not bypassTimidity)
コード例 #11
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def menu_changeTempo(self, e=None):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
		
		res = midirender_tempo.queryChangeTempo(self.objMidi, self.tempoScaleFactor)
		if res is None:
			return # canceled.
		
		if abs(res-1.0) < 0.001:  # we don't need to change the tempo if it is staying the same.
			self.tempoScaleFactor = None
		else:
			self.tempoScaleFactor = res
コード例 #12
0
    def menu_changeTranspose(self, e=None):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        strPrompt = 'Adjust key (i.e., transpose the song) by n half tones. Valid range is from -24 to 24.'
        default = self.transposePitches if self.transposePitches else 0.0
        res = midirender_util.ask_float(strPrompt,
                                        default=default,
                                        min=-24.1,
                                        max=24.1,
                                        title='Transpose')
        if res is not None and res is not False:
            self.transposePitches = int(res)
コード例 #13
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def openMixerView(self, e=None):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
			
		if self.mixerWindow: 
			return # only allow one instance open at a time
			
		top = Toplevel()
		def callbackOnClose():
			self.mixerWindow = None
			
		self.mixerWindow = midirender_mixer.BMixerWindow(top, self.objMidi, {}, callbackOnClose)
		top.focus_set()
コード例 #14
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def openAudioOptsWindow(self, evt=None):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
		
		if self.audioOptsWindow:
			return # only allow one instance open at a time
			
		top = Toplevel()
		def callbackOnClose():
			self.audioOptsWindow = None
			
		self.audioOptsWindow = midirender_audiooptions.WindowAudioOptions(top, callbackOnClose)
		top.focus_set()
コード例 #15
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def menu_openConsoleWindow(self):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
		
		if self.consoleOutWindow:
			return # only allow one instance open at a time
		
		top = Toplevel()
		def callbackOnClose():
			self.consoleOutWindow = None
		
		self.consoleOutWindow = midirender_consoleout.BConsoleOutWindow(top, self.consoleOutCallback, callbackOnClose=callbackOnClose)
		top.focus_set()
コード例 #16
0
ファイル: main.py プロジェクト: acekorg/downpoured_midi_audio
	def openScoreView(self, n):
		if len(self.objMidi.tracks[n].notelist)==0:
			midirender_util.alert('No notes to show in this track.')
			return
		opts = {}
		opts['show_durations'] = self.objOptionsDuration.get()
		opts['show_barlines'] = self.objOptionsBarlines.get()
		opts['show_stems'] = 1; opts['prefer_flats'] = 0
		opts['clefspath'] = clefspath
		
		top = Toplevel()
		window = scoreview.ScoreViewWindow(top, n, self.objMidi.tracks[n],self.objMidi.ticksPerQuarterNote, opts)
		self.scoreviews[n] = top
		top.focus_set()
コード例 #17
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def saveModifiedMidi(self, evt=None):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
			
		filename = midirender_util.ask_savefile(title="Save Midi File", types=['.mid|Mid file'])
		if not filename:
			return
		
		mfile = self.buildModifiedMidi()
		if mfile:
			mfile.open(filename,'wb')
			mfile.write()
			mfile.close()
コード例 #18
0
    def onBtnPlay(self, e=None):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        params, directoryForOldTimidity = self.getParamsForTimidity(False)
        bypassTimidity = False
        if self.audioOptsWindow is not None:
            bypassTimidity = self.audioOptsWindow.getBypassTimidity()

        if params is not None:
            self.player.actionPlay(self.buildModifiedMidi(), params,
                                   self.buildCfg(), directoryForOldTimidity,
                                   not bypassTimidity)
コード例 #19
0
    def saveModifiedMidi(self, evt=None):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        filename = midirender_util.ask_savefile(title="Save Midi File",
                                                types=['.mid|Mid file'])
        if not filename:
            return

        mfile = self.buildModifiedMidi()
        if mfile:
            mfile.open(filename, 'wb')
            mfile.write()
            mfile.close()
コード例 #20
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def openSoundfontWindow(self, e=None):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
		
		# this is different than the list and score view - there can only be one of them open at once
		if self.soundfontWindow:
			return # only allow one instance open at a time
			
		top = Toplevel()
		def callbackOnClose(): 
			self.soundfontWindow = None
			
		self.soundfontWindow = midirender_soundfont.BSoundfontWindow(top, self.currentSoundfont, self.objMidi, callbackOnClose)	
		top.focus_set()
コード例 #21
0
    def menu_changeTempo(self, e=None):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        res = midirender_tempo.queryChangeTempo(self.objMidi,
                                                self.tempoScaleFactor)
        if res is None:
            return  # canceled.

        if abs(
                res - 1.0
        ) < 0.001:  # we don't need to change the tempo if it is staying the same.
            self.tempoScaleFactor = None
        else:
            self.tempoScaleFactor = res
コード例 #22
0
ファイル: main.py プロジェクト: acekorg/downpoured_midi_audio
	def menu_openMidi(self, evt=None):
		filename = midirender_util.ask_openfile(title="Open Midi File", types=['.mid|Mid file'])
		if not filename: return

		#first, see if it loads successfully.
		try:
			newmidi = bmidilib.BMidiFile()
			newmidi.open(filename, 'rb')
			newmidi.read()
			newmidi.close()
		except:
			e=''
			midirender_util.alert('Could not load midi: exception %s'%str(e), title='Could not load midi',icon='error')
			return
		self.lblFilename['text'] = filename
		self.loadMidiObj(newmidi)
コード例 #23
0
    def openMixerView(self, e=None):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        if self.mixerWindow:
            return  # only allow one instance open at a time

        top = Toplevel()

        def callbackOnClose():
            self.mixerWindow = None

        self.mixerWindow = midirender_mixer.BMixerWindow(
            top, self.objMidi, {}, callbackOnClose)
        top.focus_set()
コード例 #24
0
    def menu_openConsoleWindow(self):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        if self.consoleOutWindow:
            return  # only allow one instance open at a time

        top = Toplevel()

        def callbackOnClose():
            self.consoleOutWindow = None

        self.consoleOutWindow = midirender_consoleout.BConsoleOutWindow(
            top, self.consoleOutCallback, callbackOnClose=callbackOnClose)
        top.focus_set()
コード例 #25
0
    def openAudioOptsWindow(self, evt=None):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        if self.audioOptsWindow:
            return  # only allow one instance open at a time

        top = Toplevel()

        def callbackOnClose():
            self.audioOptsWindow = None

        self.audioOptsWindow = midirender_audiooptions.WindowAudioOptions(
            top, callbackOnClose)
        top.focus_set()
コード例 #26
0
    def openSoundfontWindow(self, e=None):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        # this is different than the list and score view - there can only be one of them open at once
        if self.soundfontWindow:
            return  # only allow one instance open at a time

        top = Toplevel()

        def callbackOnClose():
            self.soundfontWindow = None

        self.soundfontWindow = midirender_soundfont.BSoundfontWindow(
            top, self.currentSoundfont, self.objMidi, callbackOnClose)
        top.focus_set()
コード例 #27
0
	def getParamsForTimidity(self, bRenderWav):
		directoryForOldTimidity = None
		
		if self.audioOptsWindow != None:
			params = self.audioOptsWindow.createTimidityOptionsList(includeRenderOptions=bRenderWav) 
			if params==None:
				midirender_util.alert('Unknown error.')
				return None, None
			
			if self.audioOptsWindow.getUseOldTimidity():
				if not self.currentSoundfont[0].lower().endswith('.cfg'):
					midirender_util.alert('Provide a cfg with no soundfonts for old timidity.')
					return None, None
					
				directoryForOldTimidity = os.path.split(self.currentSoundfont[0])[0]
		else:
			if bRenderWav: params = ['-Ow']
			else: params = []
		return params, directoryForOldTimidity
コード例 #28
0
	def create_menubar(self,root):
		root.bind('<Control-space>', self.onBtnPlay)
		root.bind('<Control-r>', self.onBtnSaveWave)
		root.bind('<Control-f>', self.openSoundfontWindow)
		root.bind('<Alt-F4>', lambda x:root.quit)
		root.bind('<Control-o>', self.menu_openMidi)
		root.bind('<Control-S>', self.saveModifiedMidi)
		root.bind('<Control-m>', self.openMixerView)
		root.bind('<Control-p>', self.openAudioOptsWindow)
		menubar = Menu(root)
		
		menuFile = Menu(menubar, tearoff=0)
		menubar.add_cascade(label="File", menu=menuFile, underline=0)
		menuFile.add_command(label="Open Midi", command=self.menu_openMidi, underline=0, accelerator='Ctrl+O')
		menuFile.add_separator()
		menuFile.add_command(label="Modify raw midi...", command=self.menuModifyRawMidi, underline=0)
		menuFile.add_separator()
		menuFile.add_command(label="Save modified midi...", command=self.saveModifiedMidi, underline=0, accelerator='Ctrl+Shift+S')
		menuFile.add_separator()
		menuFile.add_command(label="Exit", command=root.quit, underline=1)
		
		menuAudio = Menu(menubar, tearoff=0)
		menubar.add_cascade(label="Audio", menu=menuAudio, underline=0)
		menuAudio.add_command(label="Play", command=self.onBtnPlay, underline=0)
		menuAudio.add_command(label="Pause", command=self.onBtnPause, underline=2)
		menuAudio.add_command(label="Stop", command=self.onBtnStop, underline=0)
		menuAudio.add_separator()
		menuAudio.add_command(label="Audio Options...", command=self.openAudioOptsWindow, underline=6, accelerator='Ctrl+P')
		menuAudio.add_command(label="Change Tempo...", command=self.menu_changeTempo, underline=0)
		menuAudio.add_command(label="Copy Options String", command=self.menuCopyAudioOptsString, underline=1)
		menuAudio.add_separator()
		menuAudio.add_command(label="Save Wave", command=self.onBtnSaveWave, underline=5, accelerator='Ctrl+R')
		menuAudio.add_separator()
		menuAudio.add_command(label="Choose Sound Font...", command=self.openSoundfontWindow, underline=13, accelerator='Ctrl+F')
		
		
		
		self.objOptionsDuration = IntVar(); self.objOptionsDuration.set(0)
		self.objOptionsBarlines = IntVar(); self.objOptionsBarlines.set(1)
		menuView = Menu(menubar, tearoff=0)
		menubar.add_cascade(label="View", menu=menuView, underline=0)
		menuView.add_command(label="Mixer", command=self.openMixerView, underline=0, accelerator='Ctrl+M')
		menuView.add_command(label="Console output", command=self.menu_openConsoleWindow, underline=0)
		menuView.add_separator()
		menuView.add_command(label="SoundFont Information Tool", command=self.menu_soundFontInfoTool, underline=0)
		menuView.add_separator()
		menuView.add_checkbutton(label="Show Durations in score", variable=self.objOptionsDuration, underline=5, onvalue=1, offvalue=0)
		menuView.add_checkbutton(label="Show Barlines in score", variable=self.objOptionsBarlines, underline=5, onvalue=1, offvalue=0)
		
		
		menuHelp = Menu(menubar, tearoff=0)
		menuHelp.add_command(label='About', underline=0, command=(lambda: midirender_util.alert('Bmidi to wave, by Ben Fisher 2009\nhalfhourhacks.blogspot.com\n\nA graphical frontend for Timidity.','Bmidi to wave')))
		menubar.add_cascade(label="Help", menu=menuHelp, underline=0)
		
		root.config(menu=menubar)
コード例 #29
0
	def previewSfStart(self):
		if self.player!=None and self.player.isPlaying: return #don't play while something is already playing.
			
		#check for mid, see if it exists
		curMid = self.currentMidi
		if not os.path.exists(curMid):
			midirender_util.alert("Couldn't find midi file.")
			return
		
		#if soundfont couldn't be parsed.
		if self.objSf == None: return
		
		#get cfg data
		escapedfname = self.objSf.filename
		if self.objSf.type!='pat':
			sel = self.lbVoices.curselection()
			if self.varPrevVoiceOnly.get() and len(sel)>0:
				#need to map that voice to bank0, program0
				voiceIndex = int(sel[0])
				
				voiceData = self.objSf.presets[voiceIndex]
				try:
					bank = int(voiceData.bank)
					program = int(voiceData.presetNumber)
				except ValueError:
					midirender_util.alert("Couldn't parse bank/preset number.")
					return
				
				strCfg = '\nbank 0\n'
				strCfg += '000 %font "'+escapedfname+'" %d %d'%(bank, program) + '\n'
			else:
				strCfg = '\n'
				strCfg += 'soundfont "'+escapedfname+'"\n'
				
		else:
			strCfg = '\nbank 0\n'
			strCfg += '000 "'+escapedfname+'" \n'
		
		self.player = midirender_runtimidity.RenderTimidityMidiPlayer()
		self.player.setConfiguration(strCfg)
		self.player.playAsync(curMid)
コード例 #30
0
    def menu_openMidi(self, evt=None):
        filename = midirender_util.ask_openfile(title='Open Midi File',
                                                types=['.mid|Mid file'])
        if not filename:
            return

        # first, see if it loads successfully.
        try:
            newmidi = bmidilib.BMidiFile()
            newmidi.open(filename, 'rb')
            newmidi.read()
            newmidi.close()
        except:
            e = sys.exc_info()[1]
            midirender_util.alert('Could not load midi: exception %s' % str(e),
                                  title='Could not load midi',
                                  icon='error')
            return

        self.lblFilename['text'] = filename
        self.loadMidiObj(newmidi)
コード例 #31
0
    def onBtnSaveWave(self, e=None):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        filename = midirender_util.ask_savefile(title="Create Wav File",
                                                types=['.wav|Wav file'])
        if not filename:
            return

        midicopy = self.buildModifiedMidi()
        arParams, directoryForOldTimidity = self.getParamsForTimidity(True)
        if arParams is None:
            return

        arParams.append('-o')
        arParams.append(filename)

        # play it synchronously, meaning that the whole program stalls while this happens...
        midirender_util.alert('Beginning export to wave...')
        objplayer = midirender_runtimidity.RenderTimidityMidiPlayer()
        objplayer.setConfiguration(self.buildCfg(), directoryForOldTimidity)
        objplayer.setParameters(arParams)
        objplayer.playMidiObject(midicopy, bSynchronous=True)
        if self.consoleOutWindow is not None:
            self.consoleOutWindow.clear()
            self.consoleOutWindow.writeToWindow(objplayer.strLastStdOutput)
        midirender_util.alert('Completed.')
コード例 #32
0
    def onBtnChooseWithinSoundfont(self):
        index = self.getListboxIndex()
        state = self.arCustomizationState[index]
        if state['soundfont'].endswith('.pat'):
            midirender_util.alert(
                'Patch files don\'t contain separate voices.')
            return
        elif state['soundfont'].endswith('.cfg'):
            midirender_util.alert(
                'Patch files don\'t contain separate voices.')
            return

        dlg = midirender_soundfont_info.BSoundFontInformation(
            self.top, state['soundfont'], bSelectMode=True)
        if not dlg.result:
            return

        try:
            bank = int(dlg.result.bank)
            program = int(dlg.result.presetNumber)
        except ValueError as e:
            midirender_util.alert(
                'Couldn\'t parse bank/preset for some reason.')
            return

        # refresh the fields
        state['sf_program'] = program
        state['sf_bank'] = bank
        self.onChangeLbProgChanges()
コード例 #33
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def onBtnSaveWave(self,e=None):
		if not self.isMidiLoaded:
			midirender_util.alert('Please open a MIDI file first.')
			return
		
		filename = midirender_util.ask_savefile(title="Create Wav File", types=['.wav|Wav file'])
		if not filename:
			return
			
		midicopy = self.buildModifiedMidi()
		arParams, directoryForOldTimidity = self.getParamsForTimidity(True)
		if arParams is None:
			return
			
		arParams.append('-o')
		arParams.append(filename)
		
		# play it synchronously, meaning that the whole program stalls while this happens...
		midirender_util.alert('Beginning export to wave...')
		objplayer = midirender_runtimidity.RenderTimidityMidiPlayer()
		objplayer.setConfiguration(self.buildCfg(), directoryForOldTimidity)
		objplayer.setParameters(arParams)
		objplayer.playMidiObject(midicopy, bSynchronous=True)
		if self.consoleOutWindow is not None:
			self.consoleOutWindow.clear()
			self.consoleOutWindow.writeToWindow(objplayer.strLastStdOutput)
		midirender_util.alert('Completed.')
コード例 #34
0
    def buildCfg(self):
        # begin by adding the global soundfont or cfg file.
        filename = self.currentSoundfont[0]
        strCfg = ''
        if filename.endswith('.cfg'):
            path, justname = os.path.split(filename)
            if self.audioOptsWindow and self.audioOptsWindow.getUseOldTimidity(
            ):
                if ' ' in justname:
                    midirender_util.alert(
                        "Warning: old version of Timidity probably doesn't support spaces in filenames."
                    )
                if justname.lower() != 'timidity.cfg' and os.path.exists(
                        path + os.sep + 'timidity.cfg'):
                    midirender_util.alert(
                        "Warning: there is a filename timidity.cfg in this directory and so the old version of Timidity will use it."
                    )
                strCfg += '\nsource %s' % (justname)
            else:
                strCfg += '\ndir "%s"\nsource "%s"' % (path, filename)
        else:

            strCfg += '\nsoundfont "%s"' % (filename)
            if self.audioOptsWindow is not None and self.audioOptsWindow.getPatchesTakePrecedence(
            ):
                strCfg += ' order=1'

        # now add customization to override specific voices, if set
        if self.soundfontWindow is not None:
            strCfg += '\n' + self.soundfontWindow.getCfgResults(
                self.audioOptsWindow
                and self.audioOptsWindow.getUseOldTimidity())

        if self.audioOptsWindow is not None:
            addedLines = self.audioOptsWindow.getAdditionalCfg()
            if addedLines:
                strCfg += '\n' + '\n'.join(addedLines)

        return strCfg
コード例 #35
0
    def openScoreView(self, n):
        if not self.isMidiLoaded:
            midirender_util.alert('Please open a MIDI file first.')
            return

        if len(self.objMidi.tracks[n].notelist) == 0:
            midirender_util.alert('No notes to show in this track.')
            return

        opts = {}
        opts['show_durations'] = self.objOptionsDuration.get()
        opts['show_barlines'] = self.objOptionsBarlines.get()
        opts['show_stems'] = 1
        opts['prefer_flats'] = 0
        opts['clefspath'] = clefspath

        top = Toplevel()
        window = scoreview.ScoreViewWindow(top, n, self.objMidi.tracks[n],
                                           self.objMidi.ticksPerQuarterNote,
                                           opts)
        self.scoreviews[n] = top
        top.focus_set()
コード例 #36
0
	def createTimidityOptionsList(self, includeRenderOptions=False):
		sample = self.getOptValue('sample')
		bitrate = self.getOptValue('bitrate')
		lpf = self.getOptValue('lpf')
		delay = self.getOptValue('delay')
		reverb = self.getOptValue('reverb')
		try: psreverb = int(self.varPsReverb.get()); valid = psreverb >= 0 and psreverb<=1000
		except: valid=False
		if not valid: midirender_util.alert('psuedo reverb must be an integer 0 to 1000'); return None
		
		stereo =self.varStereo.get()
		decay =self.varFastDecay.get()
		
		res = ''
		if includeRenderOptions:
			res+= ' -Ow'
			if stereo: res+='S' #stereo or mono
			else: res+='M'
			
			# if bitrate==8: res+='u' #8-bit wavs are typically unsigned
			# else: res+= 's'
			res+= 's'
				
			if bitrate==8: res+='8' #8-bit audio
			elif bitrate==24: res+='2' #24 bit
			else: res+='1' #16 bit
				
			res+='l' #l for linear encoding
			
			res+=' -s %d'%sample #sampling rate, 44100 or 22050

		if decay: res+=' -f'
		res += ' -R %d'%psreverb
		res += ' --voice-lpf %s'%lpf
		res += ' --delay %s'%delay
		res += ' --reverb %s'%reverb
		
		arParams = res.split(' ')
		return arParams
コード例 #37
0
	def getCfgResults(self, bUseOldTimidity):
		if not self.showCustomize: 
			return ''
		
		def intOrNothing(s):
			try:
				val = int(s)
			except:
				val = 0
			return max(0, val)
		
		# get the most recent changes
		self.stringParamFromUI()
		
		# elsewhere, the "global soundfont" will be set. That's necessary to cover percussion, which we don't handle here.
		# here we only set the custom overrides.
		
		strCfg = '\nbank 0\n'
		for state in self.arCustomizationState:
			if state['soundfont'].endswith('.pat'):
				if not bUseOldTimidity:
					#if using a global soundfont, signal to Timidity that we want to override the soundfont's instrument
					if not self.main_soundfont_reference[0].endswith('.cfg'):
						strCfg += '\nfont exclude 0 %d' % state['instrument']
					strCfg += '\n%d "%s" '%(state['instrument'], state['soundfont'])
				else:
					if ' ' in state['soundfont']:
						midirender_util.alert('Warning: old version of Timidity doesn\'t support spaces in directory names or filenames.')
					
					directoryWithPatchfile, nameOfPatchFile = os.path.split(state['soundfont'])
					mainDirectory = os.path.split(self.main_soundfont_reference[0])[0]
					if os.path.exists(mainDirectory + os.sep + nameOfPatchFile) and mainDirectory.lower() != directoryWithPatchfile.lower():
						midirender_util.alert('Warning: better to use a different .pat name, old version of Timidity might use\n%s \ninstead of\n%s.'%(
							mainDirectory + os.sep + nameOfPatchFile, state['soundfont']))
					
					nameOfPatchFile, _ = os.path.splitext(nameOfPatchFile)
					strCfg += '\ndir %s\n%d %s'%(directoryWithPatchfile, state['instrument'], nameOfPatchFile)
					
				if state['param_amp'] != '100':
					strCfg += ' amp=%d'%intOrNothing(state['param_amp'])
					
			elif state['soundfont'].endswith('.cfg'):
				# must be taking this from a global cfg, because cfgs can't be set individually...
				pass
			else:
				if bUseOldTimidity:
					midirender_util.alert('Warning: old version of Timidity doesn\'t support soundfonts, only .pat files.')
				
				# note a literal% symbol. The string %font, not intended for substitution
				strCfg += '\n%d ' % state['instrument'] + ' %font'
				strCfg += ' "%s" %d %d' % (state['soundfont'], state['sf_bank'], state['sf_program']) 
				if state['param_amp'] != '100':
					strCfg += ' amp=%d' % intOrNothing(state['param_amp'])
					
				if state['param_rnddelay'] != '0':
					strCfg += '\nrnddelay %d %d' % (state['instrument'], intOrNothing(state['param_rnddelay']))
		
		return strCfg
コード例 #38
0
	def loadSf(self,filename):
		self.lbVoices.delete(0, END) #clear existing voices
		self.lblCurrentSf['text'] = filename
		self.objSf = None
		
		if filename.lower().endswith('.pat'):
			#gus (gravis ultrasound) patch
			namestart, nameend = os.path.split(filename)
			self.lbVoices.insert(END, nameend)
			
			self.objSf = SoundFontInfo()
			self.objSf.type='pat'
			self.objSf.name = nameend
			self.objSf.filename = filename
			self.varPrevVoiceOnly.set(1)
		else:
			try:
				objSf = getpresets(filename)
			except SFInfoException, e:
				midirender_util.alert(str(e))
				return
				
			#set labels
			for name in self.labelFieldNames:
				att = getattr(objSf, name)
				att = (att if att else 'None')
				if len(att)>80: att = att[0:80]
				self.lblInfoFields[name]['text'] = name+': '+ att
			
			# fill listbox
			
			for preset in objSf.presets:
				self.lbVoices.insert(END, str(preset))
		
			self.objSf = objSf
			self.objSf.filename = filename
			self.objSf.type='soundfont'
コード例 #39
0
	def onBtnChooseWithinSoundfont(self):
		index = self.getListboxIndex()
		state = self.arCustomizationState[index]
		if state['soundfont'].endswith('.pat'): midirender_util.alert("Patch files don't contain separate voices."); return
		elif state['soundfont'].endswith('.cfg'): midirender_util.alert("Patch files don't contain separate voices."); return
		dlg = midirender_soundfont_info.BSoundFontInformation(self.top, state['soundfont'] , bSelectMode=True)
		if not dlg.result: return
		
		try:
			bank = int(dlg.result.bank)
			program = int(dlg.result.presetNumber)
		except ValueError, e:
			midirender_util.alert("Couldn't parse bank/preset for some reason.")
			return
コード例 #40
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def loadMidiObj(self, newmidi):
		self.objMidi = newmidi
		if self.player.isPlaying(): return False
		
		if not self.haveDrawnHeaders: self.drawColumnHeaders()
		if not self.isMidiLoaded: 
			# check if Timidity is installed
			if sys.platform != 'win32':
				if not midirender_runtimidity.isTimidityInstalled():
					midirender_util.alert('It appears that the program Timidity is not installed. This program is required for playing and rendering music.\n\nYou could try running something corresponding to "sudo apt-get install timidity" or "sudo yum install timidity++" in a terminal.')
			self.isMidiLoaded = True
		
		# close any open views
		for key in self.listviews:
			self.listviews[key].destroy()
		for key in self.scoreviews:
			self.scoreviews[key].destroy()
		self.listviews = {}
		self.scoreviews = {}
		self.clearModifications()
		
		# hide all of the old widgets
		for key in self.gridwidgets:
			w = self.gridwidgets[key]
			if w.master.is_smallframe==1:
				w.master.grid_forget()
		for key in self.gridbuttons:
			self.gridbuttons[key].grid_forget()
		
		def addLabel(text, y, x, isButton=False):
			# only create a new widget when necessary. This way, don't need to allocate every time a file is opened.
			if (x, y+1) not in self.gridwidgets:
				smallFrame = Frame(self.frameGrid, borderwidth=1, relief=RIDGE)
				smallFrame.is_smallframe=1
				if isButton: 
					btn = Button(smallFrame, text=text, relief=GROOVE,anchor='w')
					btn.config(command=midirender_util.Callable(self.onBtnChangeInstrument, y,btn))
					btn.pack(anchor='w', fill=BOTH)
					btn['disabledforeground'] = 'black' # means that when it is disabled, looks just like a label. sweet.
					thewidget = btn
					
				else: 
					lbl = Label(smallFrame, text=text)
					lbl.pack(anchor='w')
					thewidget = lbl
				
				self.gridwidgets[(x,y+1)] = thewidget
			
			self.gridwidgets[(x,y+1)]['text'] = text
			self.gridwidgets[(x,y+1)].master.grid(row=y+1, column=x, sticky='nsew')
			return self.gridwidgets[(x,y+1)]
		
		lengthTimer = bmiditools.BMidiSecondsLength(self.objMidi)
		overallLengthSeconds = lengthTimer.getOverallLength(self.objMidi)
		self.sliderTime['to'] = max(1.0, overallLengthSeconds+1.0)
		self.player.load(max(1.0, overallLengthSeconds+1.0)) # "loading" will also set position to 0.0
		
		warnMultipleChannels = False
		for rownum in range(len(self.objMidi.tracks)):
			trackobj = self.objMidi.tracks[rownum]
			
			# Track Number
			addLabel( str(rownum), rownum, 0)
			
			# Track Name
			defaultname = 'Condtrack' if rownum==0 else ('Track %d'%rownum)
			searchFor = {'TRACKNAME':defaultname, 'INSTRUMENTS':1 }
			res = bmiditools.getTrackInformation(trackobj, searchFor)
			addLabel(res['TRACKNAME'], rownum, 1)
			
			# Track Channel(s)
			chanarray = self.findNoteChannels(trackobj)
			if len(chanarray)==0: channame='None'
			elif len(chanarray)>1: channame='(Many)'; warnMultipleChannels=True
			else: channame = str(chanarray[0])
			addLabel(channame, rownum, 2)
			countednoteevts = len(trackobj.notelist) # this assumes notelist is valid, and notelist is only valid if we've just read from a file
			
			# Track Instrument(s)
			instarray = res['INSTRUMENTS']
			if len(instarray)==0:
				instname='None'
			elif len(instarray)>1:
				instname='(Many)'
			else:
				instname = str(instarray[0]) + ' (' + bmidilib.getInstrumentName(instarray[0]) + ')'
			if channame == '10':
				instname = '(Percussion channel)'
			
			btn = addLabel( instname, rownum, 3, isButton=True) # add a button (not a label)
			isEnabled = channame!='10' and instname!='None' and instname!='(Many)' # countednoteevts>0
			if isEnabled:
				btn['state'] = NORMAL
				btn['relief'] = GROOVE
			else:
				btn['state'] = DISABLED
				btn['relief'] = FLAT
			# if there are multiple inst. changes in a track, we don't let you change instruments because there isn't a convenient way to do that.
			
			# Track Time
			if len(trackobj.notelist)==0: strTime = lengthTimer.secondsToString(0)
			else: strTime = lengthTimer.secondsToString(lengthTimer.ticksToSeconds(trackobj.notelist[0].time))
			addLabel( strTime, rownum, 4)
			
			# Track Notes
			addLabel( str(countednoteevts), rownum, 5)
			
			# Buttons
			if (rownum, 0) not in self.gridbuttons:
				btn = Button(self.frameGrid, text='Mixer', command=self.openMixerView)
				self.gridbuttons[(rownum, 0)] = btn
			self.gridbuttons[(rownum, 0)].grid(row=rownum+1, column=6)
			
			if (rownum, 1) not in self.gridbuttons:
				btn = Button(self.frameGrid, text='Score', command=midirender_util.Callable(self.openScoreView, rownum))
				self.gridbuttons[(rownum, 1)] = btn
			self.gridbuttons[(rownum, 1)].grid(row=rownum+1, column=7)
			
			if (rownum, 2) not in self.gridbuttons:
				btn = Button(self.frameGrid, text='List', command=midirender_util.Callable(self.openListView, rownum))
				self.gridbuttons[(rownum, 2)] = btn
			self.gridbuttons[(rownum, 2)].grid(row=rownum+1, column=8)
		
		if warnMultipleChannels:
			resp = midirender_util.ask_yesno('This midi file has notes from different channels in the same track (format 0). Click "yes" (recommended) to import it as a format 1 file, or "no" to leave it. ')
			if resp:
				newmidi = bmiditools.restructureMidi(self.objMidi)
				newmidi.format = 1
				self.loadMidiObj(newmidi)
				return
コード例 #41
0
    def menuModifyRawMidi(self, evt=None):
        if sys.platform != 'win32':
            midirender_util.alert('Only supported on windows')
            return

        import tempfile, os, subprocess
        m2t = midirender_util.bmidirenderdirectory + '\\timidity\\m2t.exe'
        t2m = midirender_util.bmidirenderdirectory + '\\timidity\\t2m.exe'
        notepadexe = 'C:\\Windows\\System32\\notepad.exe'
        if not os.path.exists(m2t) or not os.path.exists(
                t2m) or not os.path.exists(notepadexe):
            midirender_util.alert('Could not find %s or %s or %s.' %
                                  (m2t, t2m, notepadexe))
            return

        filenameMidInput = midirender_util.ask_openfile(
            title='Choose Midi File to modify', types=['.mid|Mid file'])
        if not filenameMidInput:
            return

        filenameText = tempfile.gettempdir() + os.sep + 'tmpout.txt'
        try:
            if os.path.exists(filenameText):
                os.unlink(filenameText)
        except:
            pass
        if os.path.exists(filenameText):
            midirender_util.alert('Could not clear temporary file')
            return

        args = [m2t, filenameMidInput, filenameText]
        retcode = subprocess.call(args, creationflags=0x08000000)
        if retcode:
            midirender_util.alert('Midi to text returned failure')
            return
        if not os.path.exists(filenameText):
            midirender_util.alert('Midi to text did not write text file')
            return

        midirender_util.alert(
            "You'll see the text file in notepad. Save and close when done editing."
        )
        modtimebefore = os.path.getmtime(filenameText)
        args = [notepadexe, filenameText]
        retcode = subprocess.call(args)
        if modtimebefore == os.path.getmtime(filenameText):
            # looks like the user cancelled before making any edits
            return

        filenameMidOutput = midirender_util.ask_savefile(
            title="Choose destination for Midi File", types=['.mid|Mid file'])
        if not filenameMidOutput: return

        args = [t2m, filenameText, filenameMidOutput]
        retcode = subprocess.call(args, creationflags=0x08000000)
        if retcode:
            midirender_util.alert('text to midi returned failure')
            return
        if not os.path.exists(filenameMidOutput):
            midirender_util.alert('text to midi did not write midi file')
            return
        midirender_util.alert('Complete.')
コード例 #42
0
    def getCfgResults(self, bUseOldTimidity):
        if not self.showCustomize:
            return ''

        def intOrNothing(s):
            try:
                val = int(s)
            except:
                val = 0
            return max(0, val)

        # get the most recent changes
        self.stringParamFromUI()

        # elsewhere, the "global soundfont" will be set. That's necessary to cover percussion, which we don't handle here.
        # here we only set the custom overrides.

        strCfg = '\nbank 0\n'
        for state in self.arCustomizationState:
            if state['soundfont'].endswith('.pat'):
                if not bUseOldTimidity:
                    #if using a global soundfont, signal to Timidity that we want to override the soundfont's instrument
                    if not self.main_soundfont_reference[0].endswith('.cfg'):
                        strCfg += '\nfont exclude 0 %d' % state['instrument']
                    strCfg += '\n%d "%s" ' % (state['instrument'],
                                              state['soundfont'])
                else:
                    if ' ' in state['soundfont']:
                        midirender_util.alert(
                            'Warning: old version of Timidity doesn\'t support spaces in directory names or filenames.'
                        )

                    directoryWithPatchfile, nameOfPatchFile = os.path.split(
                        state['soundfont'])
                    mainDirectory = os.path.split(
                        self.main_soundfont_reference[0])[0]
                    if os.path.exists(mainDirectory + os.sep +
                                      nameOfPatchFile) and mainDirectory.lower(
                                      ) != directoryWithPatchfile.lower():
                        midirender_util.alert(
                            'Warning: better to use a different .pat name, old version of Timidity might use\n%s \ninstead of\n%s.'
                            % (mainDirectory + os.sep + nameOfPatchFile,
                               state['soundfont']))

                    nameOfPatchFile, _ = os.path.splitext(nameOfPatchFile)
                    strCfg += '\ndir %s\n%d %s' % (directoryWithPatchfile,
                                                   state['instrument'],
                                                   nameOfPatchFile)

                if state['param_amp'] != '100':
                    strCfg += ' amp=%d' % intOrNothing(state['param_amp'])

            elif state['soundfont'].endswith('.cfg'):
                # must be taking this from a global cfg, because cfgs can't be set individually...
                pass
            else:
                if bUseOldTimidity:
                    midirender_util.alert(
                        'Warning: old version of Timidity doesn\'t support soundfonts, only .pat files.'
                    )

                # note a literal% symbol. The string %font, not intended for substitution
                strCfg += '\n%d ' % state['instrument'] + ' %font'
                strCfg += ' "%s" %d %d' % (
                    state['soundfont'], state['sf_bank'], state['sf_program'])
                if state['param_amp'] != '100':
                    strCfg += ' amp=%d' % intOrNothing(state['param_amp'])

                if state['param_rnddelay'] != '0':
                    strCfg += '\nrnddelay %d %d' % (
                        state['instrument'],
                        intOrNothing(state['param_rnddelay']))

        return strCfg
コード例 #43
0
    def create_menubar(self, root):
        root.bind('<Control-space>', self.onBtnPlay)
        root.bind('<Control-r>', self.onBtnSaveWave)
        root.bind('<Control-f>', self.openSoundfontWindow)
        root.bind('<Alt-F4>', lambda x: root.quit)
        root.bind('<Control-o>', self.menu_openMidi)
        root.bind('<Control-S>', self.saveModifiedMidi)
        root.bind('<Control-m>', self.openMixerView)
        root.bind('<Control-p>', self.openAudioOptsWindow)
        menubar = Menu(root)

        menuFile = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=menuFile, underline=0)
        menuFile.add_command(label="Open MIDI",
                             command=self.menu_openMidi,
                             underline=0,
                             accelerator='Ctrl+O')
        menuFile.add_separator()
        menuFile.add_command(label="Modify MIDI Events...",
                             command=self.menuModifyRawMidi,
                             underline=0)
        menuFile.add_separator()
        menuFile.add_command(label="Save Changes From Mixer...",
                             command=self.saveModifiedMidi,
                             underline=0,
                             accelerator='Ctrl+Shift+S')
        menuFile.add_separator()
        menuFile.add_command(label="Exit", command=root.quit, underline=1)

        menuAudio = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Audio", menu=menuAudio, underline=0)
        menuAudio.add_command(label="Play",
                              command=self.onBtnPlay,
                              underline=0)
        menuAudio.add_command(label="Pause",
                              command=self.onBtnPause,
                              underline=2)
        menuAudio.add_command(label="Stop",
                              command=self.onBtnStop,
                              underline=0)
        menuAudio.add_separator()
        menuAudio.add_command(label="Audio Options...",
                              command=self.openAudioOptsWindow,
                              underline=6,
                              accelerator='Ctrl+P')
        menuAudio.add_command(label="Change Tempo...",
                              command=self.menu_changeTempo,
                              underline=0)
        menuAudio.add_command(label="Transpose Pitch...",
                              command=self.menu_changeTranspose,
                              underline=0)
        menuAudio.add_command(label="Copy Options String",
                              command=self.menuCopyAudioOptsString,
                              underline=1)
        menuAudio.add_separator()
        menuAudio.add_command(label="Save Wav",
                              command=self.onBtnSaveWave,
                              underline=5,
                              accelerator='Ctrl+R')
        menuAudio.add_separator()
        menuAudio.add_command(label="Choose SoundFont...",
                              command=self.openSoundfontWindow,
                              underline=13,
                              accelerator='Ctrl+F')

        self.objOptionsDuration = IntVar()
        self.objOptionsDuration.set(0)
        self.objOptionsBarlines = IntVar()
        self.objOptionsBarlines.set(1)
        menuView = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="View", menu=menuView, underline=0)
        menuView.add_command(label="Mixer",
                             command=self.openMixerView,
                             underline=0,
                             accelerator='Ctrl+M')
        menuView.add_command(label="Console Output",
                             command=self.menu_openConsoleWindow,
                             underline=0)
        menuView.add_separator()
        menuView.add_command(label="SoundFont Information Tool",
                             command=self.menu_soundFontInfoTool,
                             underline=0)
        menuView.add_separator()
        menuView.add_checkbutton(label="Show Durations in score",
                                 variable=self.objOptionsDuration,
                                 underline=5,
                                 onvalue=1,
                                 offvalue=0)
        menuView.add_checkbutton(label="Show Barlines in score",
                                 variable=self.objOptionsBarlines,
                                 underline=5,
                                 onvalue=1,
                                 offvalue=0)

        menuHelp = Menu(menubar, tearoff=0)
        menuHelp.add_command(
            label='About',
            underline=0,
            command=(lambda: midirender_util.alert(
                'Bmidi to wav, by Ben Fisher 2009\nA graphical frontend for Timidity and sfubar.\n\nSee the documentation at https://github.com/moltenform/labs_youthful_projects/tree/master/src/benmidi/README.md\n\nSource code at https://github.com/moltenform/labs_youthful_projects/tree/master/src/benmidi/bmidirender',
                'Bmidi to wav')))
        menuHelp.add_command(label='Documentation',
                             underline=0,
                             command=(lambda: openOnlineDocs()))
        menubar.add_cascade(label="Help", menu=menuHelp, underline=0)

        root.config(menu=menubar)
コード例 #44
0
    def __init__(self, top, midiObject, opts, callbackOnClose=None):
        #should only display tracks with note events. that way, solves some problems
        top.title('Mixer')

        frameTop = Frame(top, height=600)
        frameTop.pack(expand=YES, fill=BOTH)
        self.frameTop = frameTop

        ROW_NAME = 0
        ROW_CHECK = 1
        ROW_VOL = 2
        ROW_PAN = 3
        ROW_SCALEVOL = 4
        ROW_TRANSPOSE = 5

        Label(frameTop, text='Pan:').grid(row=ROW_PAN, column=0)
        Label(frameTop, text='Volume:').grid(row=ROW_VOL, column=0)
        Label(frameTop, text=' ').grid(row=ROW_NAME, column=0)
        Label(frameTop, text='Enabled:').grid(row=ROW_CHECK, column=0)
        Label(frameTop, text='Multiply vols:').grid(row=ROW_SCALEVOL, column=0)
        Label(frameTop, text='Transpose:').grid(row=ROW_TRANSPOSE, column=0)

        warnMultiple = []
        self.state = []
        col = 1  #column 0 is the text labels
        for trackNumber in range(len(midiObject.tracks)):
            track = midiObject.tracks[trackNumber]
            if not len(track.notelist):
                continue  #only display tracks with note lists

            scpan = Scale(frameTop,
                          from_=-63,
                          to=63,
                          orient=HORIZONTAL,
                          length=32 * 2)  #make it possible to set to 0.
            scpan.grid(row=ROW_PAN, column=col, sticky='EW')
            scvol = Scale(frameTop, from_=127, to=0, orient=VERTICAL)
            scvol.grid(row=ROW_VOL, column=col, sticky='NS')
            Label(frameTop,
                  text='    Track %d    ' % trackNumber).grid(row=ROW_NAME,
                                                              column=col)
            checkvar = IntVar()
            Checkbutton(frameTop, text='', var=checkvar).grid(row=ROW_CHECK,
                                                              column=col)
            scalevolvar = StringVar()
            scalevolvar.set('1.0')
            Entry(frameTop, width=4,
                  textvariable=scalevolvar).grid(row=ROW_SCALEVOL, column=col)
            transposevar = StringVar()
            transposevar.set('0')
            Entry(frameTop, width=4,
                  textvariable=transposevar).grid(row=ROW_TRANSPOSE,
                                                  column=col)

            #defaults
            (firstpan, firstvol, bMultiplePans,
             bMultipleVols) = getFirstVolumeAndPanEvents(track)
            if bMultipleVols or bMultiplePans: warnMultiple.append(trackNumber)
            scvol.set(100 if (firstvol == None) else firstvol.velocity)
            scpan.set(0 if (firstpan == None) else (firstpan.velocity - 64))
            checkvar.set(1)

            self.state.append(
                MixerTrackInfo(trackNumber, checkvar, scvol, scpan,
                               transposevar, scalevolvar))
            col += 1

        if len(warnMultiple) != 0:
            midirender_util.alert(
                'One or more of the tracks (%s) has multiple pan or volume events. You can still use the mixer, but it will only modify the first event.'
                % ','.join(str(n) for n in warnMultiple))

        frameTop.grid_rowconfigure(ROW_VOL, weight=1)
        #~ frameTop.grid_columnconfigure(0, weight=1)

        if callbackOnClose != None:

            def callCallback():
                callbackOnClose()
                top.destroy()

            top.protocol("WM_DELETE_WINDOW", callCallback)

        self.top = top
コード例 #45
0
    def createMixedMidi(self, midiObject):
        if midiObject.format != 1:
            midirender_util.alert(
                'Warning: mixer will not work well for a format-0 midi.')

        #NOTE: modifies the midi object itself, not a copy

        #remove the tracks that are both in the mixer, AND not enabled
        for trackInfo in self.state:
            trackNumber = trackInfo.trackNumber
            if not trackInfo.enableVar.get():
                #eliminate the track by making an empty one in its place
                midiObject.tracks[trackNumber] = bmidilib.BMidiTrack()
                evt = bmiditools.makeEndOfTrackEvent()
                midiObject.tracks[trackNumber].events.append(evt)
            else:
                trackObject = midiObject.tracks[trackNumber]
                volValue = trackInfo.volWidget.get()
                panValue = trackInfo.panWidget.get(
                ) + 64  #is from 0 to 127, instead of -63 to 63

                try:
                    transposeValue = int(trackInfo.transposeVar.get())
                except:
                    trackInfo.transposeVar.set('0')
                    transposeValue = 0
                try:
                    scaleVolValue = float(trackInfo.scalevolVar.get())
                except:
                    trackInfo.scalevolVar.set('1.0')
                    scaleVolValue = 1.0

                #modify the event directly, if it exists. Otherwise, create and add a new event.
                (firstpan, firstvol, bMultiplePans,
                 bMultipleVols) = getFirstVolumeAndPanEvents(
                     midiObject.tracks[trackNumber])
                if firstpan: firstpan.velocity = panValue
                else:
                    #create a new pan event.
                    evt = bmidilib.BMidiEvent()
                    evt.type = 'CONTROLLER_CHANGE'
                    evt.time = 0
                    evt.pitch = 0x0A
                    evt.velocity = panValue
                    evt.channel = trackObject.notelist[
                        -1].startEvt.channel  #assume that the channel of the track is the channel of the first note.
                    trackObject.events.insert(0, evt)

                if firstvol: firstvol.velocity = volValue
                else:
                    #create a new vol event.
                    evt = bmidilib.BMidiEvent()
                    evt.type = 'CONTROLLER_CHANGE'
                    evt.time = 0
                    evt.pitch = 0x07
                    evt.velocity = volValue
                    evt.channel = trackObject.notelist[-1].startEvt.channel
                    trackObject.events.insert(0, evt)

                #scaling volume. nice when there are many volume events.
                if trackInfo.scalevolVar.get() != '1.0':
                    for evt in trackObject.events:
                        if evt.type == 'CONTROLLER_CHANGE' and evt.pitch == 0x07:
                            newVol = int(evt.velocity * 1.0 * scaleVolValue)
                            evt.velocity = min(max(
                                newVol, 0), 127)  #make sure between 0 and 127

                #transpose tracks, using the notelist
                if transposeValue != 0:
                    for note in trackObject.notelist:
                        nextpitch = min(max(note.pitch + transposeValue, 0),
                                        127)  #make sure between 0 and 127
                        note.startEvt.pitch = nextpitch
                        note.endEvt.pitch = nextpitch
                        note.pitch = nextpitch

        return None  #as a signal that this modifies, not returns a copy
コード例 #46
0
    def loadMidiObj(self, newmidi):
        self.objMidi = newmidi
        if self.player.isPlaying(): return False

        if not self.haveDrawnHeaders: self.drawColumnHeaders()
        if not self.isMidiLoaded:
            # check if Timidity is installed
            if sys.platform != 'win32':
                if not midirender_runtimidity.isTimidityInstalled():
                    midirender_util.alert(
                        'It appears that the program Timidity is not installed. This program is required for playing and rendering music.\n\nYou could try running something corresponding to "sudo apt-get install timidity" or "sudo yum install timidity++" in a terminal.'
                    )
            self.isMidiLoaded = True

        # close any open views
        for key in self.listviews:
            self.listviews[key].destroy()
        for key in self.scoreviews:
            self.scoreviews[key].destroy()
        self.listviews = {}
        self.scoreviews = {}
        self.clearModifications()

        # hide all of the old widgets
        for key in self.gridwidgets:
            w = self.gridwidgets[key]
            if w.master.is_smallframe == 1:
                w.master.grid_forget()
        for key in self.gridbuttons:
            self.gridbuttons[key].grid_forget()

        def addLabel(text, y, x, isButton=False):
            # only create a new widget when necessary. This way, don't need to allocate every time a file is opened.
            if (x, y + 1) not in self.gridwidgets:
                smallFrame = Frame(self.frameGrid, borderwidth=1, relief=RIDGE)
                smallFrame.is_smallframe = 1
                if isButton:
                    btn = Button(smallFrame,
                                 text=text,
                                 relief=GROOVE,
                                 anchor='w')
                    btn.config(command=midirender_util.Callable(
                        self.onBtnChangeInstrument, y, btn))
                    btn.pack(anchor='w', fill=BOTH)
                    btn['disabledforeground'] = 'black'  # means that when it is disabled, looks just like a label. sweet.
                    thewidget = btn

                else:
                    lbl = Label(smallFrame, text=text)
                    lbl.pack(anchor='w')
                    thewidget = lbl

                self.gridwidgets[(x, y + 1)] = thewidget

            self.gridwidgets[(x, y + 1)]['text'] = text
            self.gridwidgets[(x, y + 1)].master.grid(row=y + 1,
                                                     column=x,
                                                     sticky='nsew')
            return self.gridwidgets[(x, y + 1)]

        lengthTimer = bmiditools.BMidiSecondsLength(self.objMidi)
        overallLengthSeconds = lengthTimer.getOverallLength(self.objMidi)
        self.sliderTime['to'] = max(1.0, overallLengthSeconds + 1.0)
        self.player.load(max(1.0, overallLengthSeconds +
                             1.0))  # "loading" will also set position to 0.0

        warnMultipleChannels = False
        for rownum in range(len(self.objMidi.tracks)):
            trackobj = self.objMidi.tracks[rownum]

            # Track Number
            addLabel(str(rownum), rownum, 0)

            # Track Name
            defaultname = 'Condtrack' if rownum == 0 else ('Track %d' % rownum)
            searchFor = {'TRACKNAME': defaultname, 'INSTRUMENTS': 1}
            res = bmiditools.getTrackInformation(trackobj, searchFor)
            addLabel(res['TRACKNAME'], rownum, 1)

            # Track Channel(s)
            chanarray = self.findNoteChannels(trackobj)
            if len(chanarray) == 0: channame = 'None'
            elif len(chanarray) > 1:
                channame = '(Many)'
                warnMultipleChannels = True
            else:
                channame = str(chanarray[0])
            addLabel(channame, rownum, 2)
            countednoteevts = len(
                trackobj.notelist
            )  # this assumes notelist is valid, and notelist is only valid if we've just read from a file

            # Track Instrument(s)
            instarray = res['INSTRUMENTS']
            if len(instarray) == 0:
                instname = 'None'
            elif len(instarray) > 1:
                instname = '(Many)'
            else:
                instname = str(
                    instarray[0]) + ' (' + bmidilib.getInstrumentName(
                        instarray[0]) + ')'
            if channame == '10':
                instname = '(Percussion channel)'

            btn = addLabel(instname, rownum, 3,
                           isButton=True)  # add a button (not a label)
            isEnabled = channame != '10' and instname != 'None' and instname != '(Many)'  # countednoteevts>0
            if isEnabled:
                btn['state'] = NORMAL
                btn['relief'] = GROOVE
            else:
                btn['state'] = DISABLED
                btn['relief'] = FLAT
            # if there are multiple inst. changes in a track, we don't let you change instruments because there isn't a convenient way to do that.

            # Track Time
            if len(trackobj.notelist) == 0:
                strTime = lengthTimer.secondsToString(0)
            else:
                strTime = lengthTimer.secondsToString(
                    lengthTimer.ticksToSeconds(trackobj.notelist[0].time))
            addLabel(strTime, rownum, 4)

            # Track Notes
            addLabel(str(countednoteevts), rownum, 5)

            # Buttons
            if (rownum, 0) not in self.gridbuttons:
                btn = Button(self.frameGrid,
                             text='Mixer',
                             command=self.openMixerView)
                self.gridbuttons[(rownum, 0)] = btn
            self.gridbuttons[(rownum, 0)].grid(row=rownum + 1, column=6)

            if (rownum, 1) not in self.gridbuttons:
                btn = Button(self.frameGrid,
                             text='Score',
                             command=midirender_util.Callable(
                                 self.openScoreView, rownum))
                self.gridbuttons[(rownum, 1)] = btn
            self.gridbuttons[(rownum, 1)].grid(row=rownum + 1, column=7)

            if (rownum, 2) not in self.gridbuttons:
                btn = Button(self.frameGrid,
                             text='List',
                             command=midirender_util.Callable(
                                 self.openListView, rownum))
                self.gridbuttons[(rownum, 2)] = btn
            self.gridbuttons[(rownum, 2)].grid(row=rownum + 1, column=8)

        if warnMultipleChannels:
            resp = midirender_util.ask_yesno(
                'This midi file has notes from different channels in the same track (format 0). Click "yes" (recommended) to import it as a format 1 file, or "no" to leave it. '
            )
            if resp:
                newmidi = bmiditools.restructureMidi(self.objMidi)
                newmidi.format = 1
                self.loadMidiObj(newmidi)
                return
コード例 #47
0
    def __init__(self, top, midiObject, opts, callbackOnClose=None):
        # should only display tracks with note events. that way, solves some problems
        top.title("Mixer")

        frameTop = Frame(top, height=600)
        frameTop.pack(expand=YES, fill=BOTH)
        self.frameTop = frameTop

        ROW_NAME = 0
        ROW_CHECK = 1
        ROW_VOL = 2
        ROW_PAN = 3
        ROW_SCALEVOL = 4
        ROW_TRANSPOSE = 5

        Label(frameTop, text="Pan:").grid(row=ROW_PAN, column=0)
        Label(frameTop, text="Volume:").grid(row=ROW_VOL, column=0)
        Label(frameTop, text=" ").grid(row=ROW_NAME, column=0)
        Label(frameTop, text="Enabled:").grid(row=ROW_CHECK, column=0)
        Label(frameTop, text="Multiply vols:").grid(row=ROW_SCALEVOL, column=0)
        Label(frameTop, text="Transpose:").grid(row=ROW_TRANSPOSE, column=0)

        warnMultiple = []
        self.state = []
        col = 1  # column 0 is the text labels
        for trackNumber in range(len(midiObject.tracks)):
            track = midiObject.tracks[trackNumber]
            if not len(track.notelist):
                continue  # only display tracks with note lists

            scpan = Scale(frameTop, from_=-63, to=63, orient=HORIZONTAL, length=32 * 2)  # make it possible to set to 0.
            scpan.grid(row=ROW_PAN, column=col, sticky="EW")
            scvol = Scale(frameTop, from_=127, to=0, orient=VERTICAL)
            scvol.grid(row=ROW_VOL, column=col, sticky="NS")
            Label(frameTop, text="    Track %d    " % trackNumber).grid(row=ROW_NAME, column=col)
            checkvar = IntVar()
            Checkbutton(frameTop, text="", var=checkvar).grid(row=ROW_CHECK, column=col)
            scalevolvar = StringVar()
            scalevolvar.set("1.0")
            Entry(frameTop, width=4, textvariable=scalevolvar).grid(row=ROW_SCALEVOL, column=col)
            transposevar = StringVar()
            transposevar.set("0")
            Entry(frameTop, width=4, textvariable=transposevar).grid(row=ROW_TRANSPOSE, column=col)

            # defaults
            (firstpan, firstvol, bMultiplePans, bMultipleVols) = getFirstVolumeAndPanEvents(track)
            if bMultipleVols or bMultiplePans:
                warnMultiple.append(trackNumber)
            scvol.set(100 if (firstvol == None) else firstvol.velocity)
            scpan.set(0 if (firstpan == None) else (firstpan.velocity - 64))
            checkvar.set(1)

            self.state.append(MixerTrackInfo(trackNumber, checkvar, scvol, scpan, transposevar, scalevolvar))
            col += 1

        if len(warnMultiple) != 0:
            midirender_util.alert(
                "One or more of the tracks (%s) has multiple pan or volume events. You can still use the mixer, but it will only modify the first event."
                % ",".join(str(n) for n in warnMultiple)
            )

        frameTop.grid_rowconfigure(ROW_VOL, weight=1)
        # ~ frameTop.grid_columnconfigure(0, weight=1)

        if callbackOnClose != None:

            def callCallback():
                callbackOnClose()
                top.destroy()

            top.protocol("WM_DELETE_WINDOW", callCallback)

        self.top = top
コード例 #48
0
    def createMixedMidi(self, midiObject):
        if midiObject.format != 1:
            midirender_util.alert("Warning: mixer will not work well for a format-0 midi.")

            # NOTE: modifies the midi object itself, not a copy

            # remove the tracks that are both in the mixer, AND not enabled
        for trackInfo in self.state:
            trackNumber = trackInfo.trackNumber
            if not trackInfo.enableVar.get():
                # eliminate the track by making an empty one in its place
                midiObject.tracks[trackNumber] = bmidilib.BMidiTrack()
                evt = bmidilib.BMidiEvent()
                evt.type = "END_OF_TRACK"
                evt.time = 1
                evt.data = ""
                midiObject.tracks[trackNumber].events.append(evt)
            else:
                trackObject = midiObject.tracks[trackNumber]
                volValue = trackInfo.volWidget.get()
                panValue = trackInfo.panWidget.get() + 64  # is from 0 to 127, instead of -63 to 63

                try:
                    transposeValue = int(trackInfo.transposeVar.get())
                except:
                    trackInfo.transposeVar.set("0")
                    transposeValue = 0
                try:
                    scaleVolValue = float(trackInfo.scalevolVar.get())
                except:
                    trackInfo.scalevolVar.set("1.0")
                    scaleVolValue = 1.0

                # modify the event directly, if it exists. Otherwise, create and add a new event.
                (firstpan, firstvol, bMultiplePans, bMultipleVols) = getFirstVolumeAndPanEvents(
                    midiObject.tracks[trackNumber]
                )
                if firstpan:
                    firstpan.velocity = panValue
                else:
                    # create a new pan event.
                    evt = bmidilib.BMidiEvent()
                    evt.type = "CONTROLLER_CHANGE"
                    evt.time = 0
                    evt.pitch = 0x0A
                    evt.velocity = panValue
                    evt.channel = trackObject.notelist[
                        -1
                    ].startEvt.channel  # assume that the channel of the track is the channel of the first note.
                    trackObject.events.insert(0, evt)

                if firstvol:
                    firstvol.velocity = volValue
                else:
                    # create a new vol event.
                    evt = bmidilib.BMidiEvent()
                    evt.type = "CONTROLLER_CHANGE"
                    evt.time = 0
                    evt.pitch = 0x07
                    evt.velocity = volValue
                    evt.channel = trackObject.notelist[-1].startEvt.channel
                    trackObject.events.insert(0, evt)

                    # scaling volume. nice when there are many volume events.
                if trackInfo.scalevolVar.get() != "1.0":
                    for evt in trackObject.events:
                        if evt.type == "CONTROLLER_CHANGE" and evt.pitch == 0x07:
                            newVol = int(evt.velocity * 1.0 * scaleVolValue)
                            evt.velocity = min(max(newVol, 0), 127)  # make sure between 0 and 127

                            # transpose tracks, using the notelist
                if transposeValue != 0:
                    for note in trackObject.notelist:
                        nextpitch = min(max(note.pitch + transposeValue, 0), 127)  # make sure between 0 and 127
                        note.startEvt.pitch = nextpitch
                        note.endEvt.pitch = nextpitch
                        note.pitch = nextpitch

        return None  # as a signal that this modifies, not returns a copy
コード例 #49
0
ファイル: main.py プロジェクト: downpoured/pyxels
	def menuModifyRawMidi(self, evt=None):
		if sys.platform != 'win32':
			midirender_util.alert('Only supported on windows')
			return
		
		import tempfile, os, subprocess
		m2t = midirender_util.bmidirenderdirectory+'\\timidity\\m2t.exe'
		t2m = midirender_util.bmidirenderdirectory+'\\timidity\\t2m.exe'
		notepadexe = 'C:\\Windows\\System32\\notepad.exe'
		if not os.path.exists(m2t) or not os.path.exists(t2m) or not os.path.exists(notepadexe):
			midirender_util.alert('Could not find %s or %s or %s.'%(m2t, t2m, notepadexe))
			return
		
		filenameMidInput = midirender_util.ask_openfile(title='Choose Midi File to modify', types=['.mid|Mid file'])
		if not filenameMidInput:
			return
		
		filenameText = tempfile.gettempdir() + os.sep + 'tmpout.txt'
		try:
			if os.path.exists(filenameText):
				os.unlink(filenameText)
		except:
			pass
		if os.path.exists(filenameText):
			midirender_util.alert('Could not clear temporary file')
			return
		
		args = [m2t, filenameMidInput, filenameText]
		retcode = subprocess.call(args, creationflags=0x08000000)
		if retcode:
			midirender_util.alert('Midi to text returned failure')
			return
		if not os.path.exists(filenameText):
			midirender_util.alert('Midi to text did not write text file')
			return
		
		midirender_util.alert("You'll see the text file in notepad. Save and close when done editing.")
		modtimebefore = os.path.getmtime(filenameText)
		args = [notepadexe, filenameText]
		retcode = subprocess.call(args)
		if modtimebefore == os.path.getmtime(filenameText):
			# looks like the user cancelled before making any edits
			return
			
		filenameMidOutput = midirender_util.ask_savefile(title="Choose destination for Midi File", types=['.mid|Mid file'])
		if not filenameMidOutput: return
		
		args = [t2m, filenameText, filenameMidOutput]
		retcode = subprocess.call(args, creationflags=0x08000000)
		if retcode:
			midirender_util.alert('text to midi returned failure')
			return
		if not os.path.exists(filenameMidOutput):
			midirender_util.alert('text to midi did not write midi file')
			return
		midirender_util.alert('Complete.')