#!BPY

"""
Name: 'Audio Midi File Values (.mid)...'
Blender: 248
Group: 'Misc'
Tooltip: 'Import midi tracks part into IPO'
"""

__author__ = "JulesD"
__version__ = "0.71"
__url__ = ["http://blog.global6.net/julesd/"]
__bpydoc__ = """\
midi_import_x.py

Script loading midi part into IPO curves,  
Usage:<br>
 - Choose 'Import Midi X'
 - Choose your settings <br>
 - Run the script <br>
 - Choose the file to open<br>
 - Press Import midi button<br>

"""

# -------------------------------------------------------------------------- 
# MIDI_IMPORT_X.py 
# -------------------------------------------------------------------------- 
# ***** BEGIN GPL LICENSE BLOCK ***** 
# 
# This program is free software; you can redistribute it and/or 
# modify it under the terms of the GNU General Public License 
# as published by the Free Software Foundation; either version 2 
# of the License, or (at your option) any later version. 
# 
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
# GNU General Public License for more details. 
# 
# You should have received a copy of the GNU General Public License 
# along with this program; if not, write to the Free Software Foundation, 
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
# 
# ***** END GPL LICENCE BLOCK ***** 
# -------------------------------------------------------------------------- 



tabNomCurve=["LocZ"]

"""
midi.py -- MIDI classes and parser in Python
Placed into the public domain in December 2001 by Will Ware

Python MIDI classes: meaningful data structures that represent MIDI events
and other objects. You can read MIDI files to create such objects, or
generate a collection of objects and use them to write a MIDI file.

"""

import Blender
from Blender import *
from Blender.BGL import *
import sys, string, types, exceptions


## DECLARING GLOBAL VARIABLES THAT NEED TO BE ACCESSIBLE FOR MULTIPLE FUNCTIONS
# The version of this script (always handy to have around)
VERSION = '0.6'

# Set Curve mode variables
LINEAR=0
BEZIER=1

# Set Glide mode variables
GOFF=0
GON=1

# Set button variables (settings in the gui)
BUTTON = {
	'CURVEMODE': Draw.Create(LINEAR),	# Default setting making the curve mode to "LINEAR"
	'GLIDEMODE': Draw.Create(GOFF),		# Default setting making the glide mode OFF
	'MIDICHANNELNR': Draw.Create(10),	# Default setting making the midi track number 10 (for drums)
	'SCALE': Draw.Create(0),		# Default setting making the scale button "ON"
	'SCALENR': Draw.Create(1),		# Default setting making the scale value 1
	'ATTACK': Draw.Create(0),		# Default setting making the attack button "ON"
	'ATTACKNR': Draw.Create(0.001),		# Default setting making the attack value 0.001
	'DECAY': Draw.Create(0),		# Default setting making the DECAY button "ON"
	'DECAYNR': Draw.Create(0.001),		# Default setting making the DECAY value 0.001
	'SUSTAIN': Draw.Create(0),		# Default setting making the sustain button "ON"
	'SUSTAINNR': Draw.Create(0.001),	# Default setting making the sustain value 0.001
	'RELEASE': Draw.Create(0),		# Default setting making the release button "ON"
	'RELEASENR': Draw.Create(0.001),	# Default setting making the release value 0.001
	'GMDRUMMAP': Draw.Create(0),		# Default setting making the GM drum map names off
	'NOTEMAP': Draw.Create(0),		# Default setting making the Note names off
	'LOKEY': Draw.Create(0),		# Default lowest value for glide mode
	'HIKEY': Draw.Create(128),		# Default highest value for glide mode
	'NOTE': Draw.Create(0),			# Default value for single note mode switch
	'NOTENUMBER': Draw.Create(0),		# Default value for single note mode value
	'CURRENTOBJECT':Draw.Create(0),
	'IPOCURVELIST':Draw.Create(11)
}

# Set data variables (settings not in the gui)
DATA = {
	'MESSAGE': "Choose your settings!",	# A variable with text in it so we can print dynamic messages in the gui
	'ERROR': 0			# A variable that will tell us at any time whether there have been any errors or not
}

EVENT = {
	'NOEVENT': 0,
	'DO_REDRAW': 1,
	'LOAD_MID': 2,
	'CHOOSE_FILENAME': 3,
	'SWITCH_GLIDE' : 4,
	'EXIT': 99
}


STANDARDCURVES = ['dLocX', 'dLocY', 'dLocZ', 'ColR', 'ColB', 'RDamp', 'Perm', 'Layer', 'ColA', 'ColG', 'Damping', 'LocZ', 'LocY', 'LocX', 'Time', 'dRotZ', 'dRotY', 'dRotX', 'FFall', 'dScaleZ', 'dScaleX', 'dScaleY', 'RotX', 'RotY', 'RotZ', 'FStreng', 'ScaleY', 'ScaleX', 'ScaleZ']
STANDARDCURVESMENU = "IPO Curve %tRDamp%x5|Perm%x6|Layer%x7|Damping%x10|Time%x14|FFall%x18|DScaleZ%x19|DscaleY%x21|DScaleX%x20|DRotZ%x15|DRotY%x16|DRotX%x17|RotZ%x24|RotY%x23|RotX%x22|FStreng%x25|ScaleZ%x28|ScaleY%x26|ScaleX%x27|dLocZ%x2|dLocY%x1|dLocX%x0|LocZ%x11|LocY%x12|LocX%x13|ColA%x8|ColB%x4|ColG%x9|ColR%x3|"


scn=Scene.GetCurrent()

debugflag = 0


# Import globals
filename="midi filename"




######################################################
# Callbacks for Window functions
######################################################
def filename_callback(input_filename):
	global filename
	filename=input_filename

def showstr(str, n=16):
	for x in str[:n]:
		print ('%02x' % ord(x)),
	print

def getNumber(str, length):
	# MIDI uses big-endian for everything
	sum = 0
	for i in range(length):
		sum = (sum << 8) + ord(str[i])
	return sum, str[length:]

def getVariableLengthNumber(str):
	sum = 0
	i = 0
	while 1:
		x = ord(str[i])
		i = i + 1
		sum = (sum << 7) + (x & 0x7F)
		if not (x & 0x80):
			return sum, str[i:]

def putNumber(num, length):
	# MIDI uses big-endian for everything
	lst = [ ]
	for i in range(length):
		n = 8 * (length - 1 - i)
		lst.append(chr((num >> n) & 0xFF))
	return string.join(lst, "")

def putVariableLengthNumber(x):
	lst = [ ]
	while 1:
		y, x = x & 0x7F, x >> 7
		lst.append(chr(y + 0x80))
		if x == 0:
			break
	lst.reverse()
	lst[-1] = chr(ord(lst[-1]) & 0x7f)
	return string.join(lst, "")


class EnumException(exceptions.Exception):
	pass

class Enumeration:
	def __init__(self, enumList):
		lookup = { }
		reverseLookup = { }
		i = 0
		uniqueNames = [ ]
		uniqueValues = [ ]
		for x in enumList:
			if type(x) == types.TupleType:
				x, i = x
			if type(x) != types.StringType:
				raise EnumException, "enum name is not a string: " + x
			if type(i) != types.IntType:
				raise EnumException, "enum value is not an integer: " + i
			if x in uniqueNames:
				raise EnumException, "enum name is not unique: " + x
			if i in uniqueValues:
				raise EnumException, "enum value is not unique for " + x
			uniqueNames.append(x)
			uniqueValues.append(i)
			lookup[x] = i
			reverseLookup[i] = x
			i = i + 1
		self.lookup = lookup
		self.reverseLookup = reverseLookup
	def __add__(self, other):
		lst = [ ]
		for k in self.lookup.keys():
			lst.append((k, self.lookup[k]))
		for k in other.lookup.keys():
			lst.append((k, other.lookup[k]))
		return Enumeration(lst)
	def hasattr(self, attr):
		return self.lookup.has_key(attr)
	def has_value(self, attr):
		return self.reverseLookup.has_key(attr)
	def __getattr__(self, attr):
		if not self.lookup.has_key(attr):
			raise AttributeError
		return self.lookup[attr]
	def whatis(self, value):
		return self.reverseLookup[value]


channelVoiceMessages = Enumeration([("NOTE_OFF", 0x80),
									("NOTE_ON", 0x90),
									("POLYPHONIC_KEY_PRESSURE", 0xA0),
									("CONTROLLER_CHANGE", 0xB0),
									("PROGRAM_CHANGE", 0xC0),
									("CHANNEL_KEY_PRESSURE", 0xD0),
									("PITCH_BEND", 0xE0)])

channelModeMessages = Enumeration([("ALL_SOUND_OFF", 0x78),
								   ("RESET_ALL_CONTROLLERS", 0x79),
								   ("LOCAL_CONTROL", 0x7A),
								   ("ALL_NOTES_OFF", 0x7B),
								   ("OMNI_MODE_OFF", 0x7C),
								   ("OMNI_MODE_ON", 0x7D),
								   ("MONO_MODE_ON", 0x7E),
								   ("POLY_MODE_ON", 0x7F)])

metaEvents = Enumeration([("SEQUENCE_NUMBER", 0x00),
						  ("TEXT_EVENT", 0x01),
						  ("COPYRIGHT_NOTICE", 0x02),
						  ("SEQUENCE_TRACK_NAME", 0x03),
						  ("INSTRUMENT_NAME", 0x04),
						  ("LYRIC", 0x05),
						  ("MARKER", 0x06),
						  ("CUE_POINT", 0x07),
						  ("MIDI_CHANNEL_PREFIX", 0x20),
						  ("MIDI_PORT", 0x21),
						  ("END_OF_TRACK", 0x2F),
						  ("SET_TEMPO", 0x51),
						  ("SMTPE_OFFSET", 0x54),
						  ("TIME_SIGNATURE", 0x58),
						  ("KEY_SIGNATURE", 0x59),
						  ("SEQUENCER_SPECIFIC_META_EVENT", 0x7F)])



NoteNames = Enumeration([("C-1",   0), ("C#-1",  1), ("D-1",   2), ("D#-1",  3), ("E-1",   4), ("F-1",   5), ("F#-1",  6), ("G-1",   7), ("G#-1",  8), ("A-1",   9), ("A#-1", 10), ("B-1",  11),
			 ("C0",   12), ("C#0",  13), ("D0",   14), ("D#0",  15), ("E0",   16), ("F0",   17), ("F#0",  18), ("G0",   19), ("G#0",  20), ("A0",   21), ("A#0",  22), ("B0",   23),
			 ("C1",   24), ("C#1",  25), ("D1",   26), ("D#1",  27), ("E1",   28), ("F1",   29), ("F#1",  30), ("G1",   31), ("G#1",  32), ("A1",   33), ("A#1",  34), ("B1",   35),
			 ("C2",   36), ("C#2",  37), ("D2",   38), ("D#2",  39), ("E2",   40), ("F2",   41), ("F#2",  42), ("G2",   43), ("G#2",  44), ("A2",   45), ("A#2",  46), ("B2",   47),
			 ("C3",   48), ("C#3",  49), ("D3",   50), ("D#3",  51), ("E3",   52), ("F3",   53), ("F#3",  54), ("G3",   55), ("G#3",  56), ("A3",   57), ("A#3",  58), ("B3",   59),
			 ("C4",   60), ("C#4",  61), ("D4",   62), ("D#4",  63), ("E4",   64), ("F4",   65), ("F#4",  66), ("G4",   67), ("G#4",  68), ("A4",   69), ("A#4",  70), ("B4",   71),
			 ("C5",   72), ("C#5",  73), ("D5",   74), ("D#5",  75), ("E5",   76), ("F5",   77), ("F#5",  78), ("G5",   79), ("G#5",  80), ("A5",   81), ("A#5",  82), ("B5",   83),
			 ("C6",   84), ("C#6",  85), ("D6",   86), ("D#6",  87), ("E6",   88), ("F6",   89), ("F#6",  90), ("G6",   91), ("G#6",  92), ("A6",   93), ("A#6",  94), ("B6",   95),
			 ("C7",   96), ("C#7",  97), ("D7",   98), ("D#7",  99), ("E7",  100), ("F7",  101), ("F#7", 102), ("G7",  103), ("G#7", 104), ("A7",  105), ("A#7", 106), ("B7",  107),
			 ("C8",  108), ("C#8", 109), ("D8",  110), ("D#8", 111), ("E8",  112), ("F8",  113), ("F#8", 114), ("G8",  115), ("G#8", 116), ("A8",  117), ("A#8", 118), ("B8",  119),
			 ("C9",  120), ("C#9", 121), ("D9",  122), ("D#9", 123), ("E9",  124), ("F9",  125), ("F#9", 126), ("G9",  127), ("G#9", 128) ])

GMDrumNames = Enumeration([("Acoustic Bass Drum",35),
			   ("Bass Drum 1",36),
			   ("Side Stick",37),
			   ("Acoustic Snare",38),
			   ("Hand Clap",39),
			   ("Electric Snare",40),
			   ("Low Floor Tom",41),
			   ("Closed Hi Hat",42),
			   ("High Floor Tom",43),
			   ("Pedal Hi-Hat",44),
			   ("Low Tom",45),
			   ("Open Hi-Hat",46),
			   ("Low-Mid Tom",47),
			   ("Hi-Mid Tom",48),
			   ("Crash Cymbal 1",49),
			   ("High Tom",50),
			   ("Ride Cymbal 1",51),
			   ("Chinese Cymbal",52),
			   ("Ride Bell",53),
			   ("Tambourine",54),
			   ("Splash Cymbal",55),
			   ("Cowbell",56),
			   ("Crash Cymbal 2",57),
			   ("Vibraslap",58),
			   ("Ride Cymbal 2",59),
			   ("Hi Bongo",60),
			   ("Low Bongo",61),
			   ("Mute Hi Conga",62),
			   ("Open Hi Conga",63),
			   ("Low Conga",64),
			   ("High Timbale",65),
			   ("Low Timbale",66),
			   ("High Agogo",67),
			   ("Low Agogo",68),
			   ("Cabasa",69),
			   ("Maracas",70),
			   ("Short Whistle",71),
			   ("Long Whistle",72),
			   ("Short Guiro",73),
			   ("Long Guiro",74),
			   ("Claves",75),
			   ("Hi Wood Block",76),
			   ("Low Wood Block",77),
			   ("Mute Cuica",78),
			   ("Open Cuica",79),
			   ("Mute Triangle",80),
			   ("Open Triangle",81)])

# runningStatus appears to want to be an attribute of a MidiTrack. But
# it doesn't seem to do any harm to implement it as a global.
runningStatus = None

class MidiEvent:

	def __init__(self, track):
		self.track = track
		self.time = None
		self.channel = self.pitch = self.velocity = self.data = None

	def __cmp__(self, other):
		# assert self.time != None and other.time != None
		return cmp(self.time, other.time)

	def __repr__(self):
		r = ("<MidiEvent %s, t=%s, track=%s, channel=%s" %
			 (self.type,
			  repr(self.time),
			  self.track.index,
			  repr(self.channel)))
		for attrib in ["pitch", "data", "velocity"]:
			if getattr(self, attrib) != None:
				r = r + ", " + attrib + "=" + repr(getattr(self, attrib))
		return r + ">"

	def read(self, time, str):
		global runningStatus
		self.time = time
		# do we need to use running status?
		if not (ord(str[0]) & 0x80):
			str = runningStatus + str
		runningStatus = x = str[0]
		x = ord(x)
		y = x & 0xF0
		z = ord(str[1])

		if channelVoiceMessages.has_value(y):
			self.channel = (x & 0x0F) + 1
			self.type = channelVoiceMessages.whatis(y)
			if (self.type == "PROGRAM_CHANGE" or
				self.type == "CHANNEL_KEY_PRESSURE"):
				self.data = z
				return str[2:]
			else:
				self.pitch = z
				self.velocity = ord(str[2])
				channel = self.track.channels[self.channel - 1]
				if (self.type == "NOTE_OFF" or
					(self.velocity == 0 and self.type == "NOTE_ON")):
					channel.noteOff(self.pitch, self.time)
				elif self.type == "NOTE_ON":
					channel.noteOn(self.pitch, self.time, self.velocity)
				return str[3:]

		elif y == 0xB0 and channelModeMessages.has_value(z):
			self.channel = (x & 0x0F) + 1
			self.type = channelModeMessages.whatis(z)
			if self.type == "LOCAL_CONTROL":
				self.data = (ord(str[2]) == 0x7F)
			elif self.type == "MONO_MODE_ON":
				self.data = ord(str[2])
			return str[3:]

		elif x == 0xF0 or x == 0xF7:
			self.type = {0xF0: "F0_SYSEX_EVENT",
						 0xF7: "F7_SYSEX_EVENT"}[x]
			length, str = getVariableLengthNumber(str[1:])
			self.data = str[:length]
			return str[length:]

		elif x == 0xFF:
			if not metaEvents.has_value(z):
				print "Unknown meta event: FF %02X" % z
				sys.stdout.flush()
				raise "Unknown midi event type"
			self.type = metaEvents.whatis(z)
			length, str = getVariableLengthNumber(str[2:])
			self.data = str[:length]
			return str[length:]

		raise "Unknown midi event type"

	def write(self):
		sysex_event_dict = {"F0_SYSEX_EVENT": 0xF0,
							"F7_SYSEX_EVENT": 0xF7}
		if channelVoiceMessages.hasattr(self.type):
			x = chr((self.channel - 1) +
					getattr(channelVoiceMessages, self.type))
			if (self.type != "PROGRAM_CHANGE" and
				self.type != "CHANNEL_KEY_PRESSURE"):
				data = chr(self.pitch) + chr(self.velocity)
			else:
				data = chr(self.data)
			return x + data

		elif channelModeMessages.hasattr(self.type):
			x = getattr(channelModeMessages, self.type)
			x = (chr(0xB0 + (self.channel - 1)) +
				 chr(x) +
				 chr(self.data))
			return x

		elif sysex_event_dict.has_key(self.type):
			str = chr(sysex_event_dict[self.type])
			str = str + putVariableLengthNumber(len(self.data))
			return str + self.data

		elif metaEvents.hasattr(self.type):
			str = chr(0xFF) + chr(getattr(metaEvents, self.type))
			str = str + putVariableLengthNumber(len(self.data))
			return str + self.data

		else:
			raise "unknown midi event type: " + self.type

"""
register_note() is a hook that can be overloaded from a script that
imports this module. Here is how you might do that, if you wanted to
store the notes as tuples in a list. Including the distinction
between track and channel offers more flexibility in assigning voices.

import midi
notelist = [ ]
def register_note(t, c, p, v, t1, t2):
	notelist.append((t, c, p, v, t1, t2))
midi.register_note = register_note
"""

def register_note(track_index, channel_index, pitch, velocity,
				  keyDownTime, keyUpTime):
	pass


class MidiChannel:

	"""A channel (together with a track) provides the continuity connecting
	a NOTE_ON event with its corresponding NOTE_OFF event. Together, those
	define the beginning and ending times for a Note."""

	def __init__(self, track, index):
		self.index = index
		self.track = track
		self.pitches = { }

	def __repr__(self):
		return "<MIDI channel %d>" % self.index

	def noteOn(self, pitch, time, velocity):
		self.pitches[pitch] = (time, velocity)

	def noteOff(self, pitch, time):
		if self.pitches.has_key(pitch):
			keyDownTime, velocity = self.pitches[pitch]
			register_note(self.track.index, self.index, pitch, velocity,
						  keyDownTime, time)
			del self.pitches[pitch]
		# The case where the pitch isn't in the dictionary is illegal,
		# I think, but we probably better just ignore it.


class DeltaTime(MidiEvent):

	type = "DeltaTime"

	def read(self, oldstr):
		self.time, newstr = getVariableLengthNumber(oldstr)
		return self.time, newstr

	def write(self):
		str = putVariableLengthNumber(self.time)
		return str



class MidiTrack:

	def __init__(self, index):
		self.index = index
		self.events = [ ]
		self.channels = [ ]
		self.length = 0
		for i in range(16):
			self.channels.append(MidiChannel(self, i+1))

	def read(self, str):
		time = 0
		assert str[:4] == "MTrk"
		length, str = getNumber(str[4:], 4)
		self.length = length
		mystr = str[:length]
		remainder = str[length:]
		while mystr:
			delta_t = DeltaTime(self)
			dt, mystr = delta_t.read(mystr)
			time = time + dt
			self.events.append(delta_t)
			e = MidiEvent(self)
			mystr = e.read(time, mystr)
			self.events.append(e)
		return remainder

	def write(self):
		time = self.events[0].time
		# build str using MidiEvents
		str = ""
		for e in self.events:
			str = str + e.write()
		return "MTrk" + putNumber(len(str), 4) + str

	def __repr__(self):
		r = "<MidiTrack %d -- %d events\n" % (self.index, len(self.events))
		for e in self.events:
			r = r + "	" + `e` + "\n"
		return r + "  >"


class MidiFile:

	def __init__(self):
		self.file = None
		self.format = 1
		self.tracks = [ ]
		self.ticksPerQuarterNote = None
		self.ticksPerSecond = None

	def open(self, filename, attrib="rb"):
		if filename == None:
			if attrib in ["r", "rb"]:
				self.file = sys.stdin
			else:
				self.file = sys.stdout
		else:
			self.file = open(filename, attrib)

	def __repr__(self):
		r = "<MidiFile %d tracks\n" % len(self.tracks)
		for t in self.tracks:
			r = r + "  " + `t` + "\n"
		return r + ">"

	def close(self):
		self.file.close()

	def read(self):
		self.readstr(self.file.read())

	def readstr(self, str):
		assert str[:4] == "MThd"
		length, str = getNumber(str[4:], 4)
		assert length == 6
		format, str = getNumber(str, 2)
		self.format = format
		assert format == 0 or format == 1   # dunno how to handle 2
		numTracks, str = getNumber(str, 2)
		division, str = getNumber(str, 2)
		if division & 0x8000:
			framesPerSecond = -((division >> 8) | -128)
			ticksPerFrame = division & 0xFF
			assert ticksPerFrame == 24 or ticksPerFrame == 25 or \
				   ticksPerFrame == 29 or ticksPerFrame == 30
			if ticksPerFrame == 29: ticksPerFrame = 30  # drop frame
			self.ticksPerSecond = ticksPerFrame * framesPerSecond
		else:
			self.ticksPerQuarterNote = division & 0x7FFF
		for i in range(numTracks):
			trk = MidiTrack(i)
			str = trk.read(str)
			self.tracks.append(trk)

	def write(self):
		self.file.write(self.writestr())

	def writestr(self):
		division = self.ticksPerQuarterNote
		# Don't handle ticksPerSecond yet, too confusing
		assert (division & 0x8000) == 0
		str = "MThd" + putNumber(6, 4) + putNumber(self.format, 2)
		str = str + putNumber(len(self.tracks), 2)
		str = str + putNumber(division, 2)
		for trk in self.tracks:
			str = str + trk.write()
		return str

def getMidiTrack(mf):
	drevents=[]
	for t in mf.tracks:
		print "track ", t.index
		for e in t.events:
			if e.channel != None and e.channel==BUTTON['MIDICHANNELNR'].val:
				#print "time= ", e.time,  " channel= ", e.channel, " velocity = ",e.velocity, " pitch = ", e.pitch
				if (BUTTON['NOTE'].val==0):
					drevents.append(e)
				else:
					if (e.pitch==BUTTON['NOTENUMBER'].val):
						drevents.append(e)
	return drevents	

def getTempoEvents(mf):
	tmpoevents=[]
	for t in mf.tracks:
		for e in t.events:
			if e.type == "SET_TEMPO" : 
				tmpoevents.append(e)
	return tmpoevents	


def getDifferentNote(events):
	notes = []
	for e in events:
		if e.pitch not in notes:
			notes.append (e.pitch)
	return notes

def names(ipo): return ipo.getName()


#########
# Function to create an empty
# in  : objname : The name of the object
# out : myobj : The object thats been created.
#########
def GetOrCreateObject(objname):
	objects = Blender.Object.Get()
	if objname in map(names,objects):
		myobj = Blender.Object.Get(objname)
		print objname+' exists (' + myobj.getType() +')'
	else:
		myobj = Blender.Object.New('Empty',objname)
		scn.link(myobj)	
		print objname+' was created'
	return myobj


def GetOrCreateIPO(name):

	ipos = Blender.Ipo.Get()
	if name in map(names,ipos):
		myipo = Blender.Ipo.Get(name)
		print name+' exists'
	else:
		myipo = Blender.Ipo.New('Object',name)
		print name+' was created'
	return myipo


def GetOrCreateCurve(ipo, curvename):
        curves = ipo.getCurves()
        if curvename in map(names,curves):
                mycurve = ipo.getCurve(curvename)
                print curvename+' exists'
                #delete old curve-entries - ack test-dr 0.71
                while len(mycurve.bezierPoints) > 0:
                        mycurve.delBezier(0)
                        mycurve.recalc()
        else:
                mycurve = ipo.addCurve(curvename)
                print curvename+' was created'
        return mycurve



def load_mid_by_note(infile):

	global debugflag
	#infile = 'c:\\tmp\\Base100.mid'
	printflag = 1
	print " ***** START *****"
	m = MidiFile()
	m.open(infile)
	m.read()

	print "ticksPerQuarterNote = ", m.ticksPerQuarterNote," ticksPerSecond= ",m.ticksPerSecond 
	context=scn.getRenderingContext()

	te=getTempoEvents(m)
	chaine = "%02x%02x%02x"%(ord(te[0].data[0]),ord(te[0].data[1]),ord(te[0].data[2]))
	tempos = float(string.atoi(chaine, 16))/1000000.0

	frmperiod = 1/float(context.framesPerSec())
	de = getMidiTrack(m)
	notes = getDifferentNote(de)
	indexnote=0
	tabobjects=[]
	tabipo=[]
	tabcurve=[]
	dicNotesCurve={}
	dicNotesCurveName={}
	dicLastNotesVelocity={}
	
	for n in notes:
		if (BUTTON['CURRENTOBJECT'].val==1):
			ob = Object.GetSelected()
			if ((ob[:]==[])):
				obname=("Empty."+str(n))
			else:
				obname = ob[0].getName()

			tabobjects.append(GetOrCreateObject(obname))
			tabipo.append(GetOrCreateIPO(obname+"_note"))
			tabobjects[indexnote].setIpo(tabipo[indexnote])
			tabcurve.append(GetOrCreateCurve(tabipo[indexnote],STANDARDCURVES[BUTTON['IPOCURVELIST'].val]))

		else:
			if ((BUTTON['GMDRUMMAP'].val==1) and (GMDrumNames.has_value(n))):
				tabobjects.append(GetOrCreateObject("Drum_"+GMDrumNames.whatis(n)))
				tabipo.append(GetOrCreateIPO("IpoDrum_"+GMDrumNames.whatis(n)))
			else:
				if ((BUTTON['NOTEMAP'].val==1) and (NoteNames.has_value(n))):
					tabobjects.append(GetOrCreateObject("Note_"+NoteNames.whatis(n)))
					tabipo.append(GetOrCreateIPO("IpoNote_"+NoteNames.whatis(n)))
				else:
					tabobjects.append(GetOrCreateObject("Note"+str(n)))
					tabipo.append(GetOrCreateIPO("IpoNote"+str(n)))
	
			tabobjects[indexnote].setIpo(tabipo[indexnote])
			tabcurve.append(GetOrCreateCurve(tabipo[indexnote],tabNomCurve[indexnote%len(tabNomCurve)]))

		if BUTTON['CURVEMODE'].val==LINEAR:		
			tabcurve[indexnote].setInterpolation('Linear')
		else:
			tabcurve[indexnote].setInterpolation('Bezier')

		dicNotesCurve[n]=tabcurve[indexnote]
		dicNotesCurveName[n]=tabNomCurve[indexnote%len(tabNomCurve)]
		dicLastNotesVelocity[n]=0
		indexnote=indexnote+1

	for c in tabcurve:
		c.addBezier((0, 0))
	for e in de:
		temps=(e.time/float(m.ticksPerQuarterNote))*tempos
		frame=temps/frmperiod
		if frame <= Blender.Get('endframe'):
                    print "frame= ", frame,  " channel= ", e.channel, " velocity = ",e.velocity, " pitch = ", e.pitch
                    #dicNotesCurve[e.pitch].addBezier((frame-0.001, dicLastNotesVelocity[e.pitch]/100))
                    if e.velocity==0:
			if BUTTON['SUSTAIN'].val==1:
			   if BUTTON['SCALE'].val==1:
        	              dicNotesCurve[e.pitch].addBezier((frame+0.999, BUTTON['SUSTAINNR'].val*BUTTON['SCALENR'].val))
			   else:
			      dicNotesCurve[e.pitch].addBezier((frame+0.999, BUTTON['SUSTAINNR'].val))

			if BUTTON['RELEASE'].val==1:
                            dicNotesCurve[e.pitch].addBezier((frame+(1.0+BUTTON['RELEASENR'].val),0))
			else:
                            dicNotesCurve[e.pitch].addBezier((frame+1.0, 0))
                    else:
			if BUTTON['ATTACK'].val==1:
			    dicNotesCurve[e.pitch].addBezier((frame+(1.0-BUTTON['ATTACKNR'].val),0))

			if BUTTON['SCALE'].val==1:
                            dicNotesCurve[e.pitch].addBezier((frame+1.0, BUTTON['SCALENR'].val))
			else:
                            dicNotesCurve[e.pitch].addBezier((frame+1.0, 1))

			if BUTTON['DECAY'].val==1:
				if BUTTON['SCALE'].val==1:
        	                    dicNotesCurve[e.pitch].addBezier((frame+1.0+BUTTON['DECAYNR'].val, BUTTON['SUSTAINNR'].val*BUTTON['SCALENR'].val))
				else:
                        	    dicNotesCurve[e.pitch].addBezier((frame+1.0+BUTTON['DECAYNR'].val, BUTTON['SUSTAINNR'].val))



	for c in tabcurve:
		c.Recalc()
			

	indexnote=0
	
	if (BUTTON['CURRENTOBJECT'].val==0):
		for n in notes:
			scn.unlink(tabobjects[indexnote])
			indexnote=indexnote+1
	
			
	m.close()

        Blender.Redraw()
	print " ***** END *****"

def load_mid_by_channel(infile):

	global debugflag
	printflag = 1
	print " ***** START *****"
	m = MidiFile()
	m.open(infile)
	m.read()

	print "ticksPerQuarterNote = ", m.ticksPerQuarterNote," ticksPerSecond= ",m.ticksPerSecond 
	context=scn.getRenderingContext()

	te=getTempoEvents(m)
	chaine = "%02x%02x%02x"%(ord(te[0].data[0]),ord(te[0].data[1]),ord(te[0].data[2]))
	tempos = float(string.atoi(chaine, 16))/1000000.0

	frmperiod = 1/float(context.framesPerSec())
	de = getMidiTrack(m)
	notes = getDifferentNote(de)
	indexnote=0
	tabipo=[]
	tabcurve=[]
	dicNotesCurve={}
	dicNotesCurveName={}
	dicLastNotesVelocity={}
	range=float(BUTTON['HIKEY'].val-BUTTON['LOKEY'].val)

	tabobjects=GetOrCreateObject("Channel"+str(BUTTON['MIDICHANNELNR'].val))
	tabipo.append(GetOrCreateIPO("IpoChannel"+str(BUTTON['MIDICHANNELNR'].val)))
	
	tabobjects.setIpo(tabipo[indexnote])
	tabcurve.append(GetOrCreateCurve(tabipo[indexnote],tabNomCurve[indexnote%len(tabNomCurve)]))

	if BUTTON['CURVEMODE'].val==LINEAR:		
		tabcurve[indexnote].setInterpolation('Linear')
	else:
		tabcurve[indexnote].setInterpolation('Bezier')

	dicNotesCurve[m]=tabcurve[indexnote]
	dicNotesCurveName[m]=tabNomCurve[indexnote%len(tabNomCurve)]
	dicLastNotesVelocity[m]=0

	

	for c in tabcurve:
		c.addBezier((0, 0))
	for e in de:
		temps=(e.time/float(m.ticksPerQuarterNote))*tempos
		frame=temps/frmperiod
		if (frame <= Blender.Get('endframe')) & (BUTTON['LOKEY'].val <= e.pitch) & (BUTTON['HIKEY'].val >= e.pitch):
  	                dicNotesCurve[m].addBezier((frame+1.0, ((e.pitch-BUTTON['LOKEY'].val)/range)))
			print 'Glided frame ', frame, ' to pitch = ', e.pitch
		else:
			print 'Skipped frame ', frame, ' becaue pitch ', e.pitch , ' is not in range (', BUTTON['LOKEY'].val, ' to ', BUTTON['HIKEY'].val ,').'
	for c in tabcurve:
		c.Recalc()

	scn.unlink(tabobjects)

						
	m.close()

        Blender.Redraw()
	print " ***** END *****"



def DrawPreview():
	# A Background color for the ADSL display
	BGL.glColor3f(0.3, 0.3, 0.3)
	BGL.glRectf(340, 130, 390, 180)

	BGL.glColor3f(1, 1, 1)


	glBegin(GL_LINE_STRIP)  # This section draws the ADSL envelope 
	if(BUTTON['ATTACK'].val==1):
		glVertex2i(350-int(BUTTON['ATTACKNR'].val*2.0),130)
	else:
		glVertex2i(350,130)
	
	glVertex2i(350,179)
	
	if(BUTTON['DECAY'].val==1):
		glVertex2i(350+int(BUTTON['DECAYNR'].val*2.0),179)
		if(BUTTON['SUSTAIN'].val==1):
			glVertex2i(360+int(BUTTON['DECAYNR'].val*2.0),130+int(BUTTON['SUSTAINNR'].val*49.0))		 
		if(BUTTON['RELEASE'].val==1):
			glVertex2i(360+(int(BUTTON['DECAYNR'].val*2.0)+int(BUTTON['RELEASENR'].val*2.0)),130)		 
	else:
		if(BUTTON['SUSTAIN'].val==1):
			glVertex2i(360,130+int(BUTTON['SUSTAINNR'].val*49.0))		 
		if(BUTTON['RELEASE'].val==1):
			glVertex2i(360+int(BUTTON['RELEASENR'].val*2.0),130)		 


	glEnd() 


def MainScript(filename):

	# We're just getting started so there can't have been any errors yet
	DATA['ERROR'] = 0
	

## THE INTERFACE (gui) FOR THE SCRIPT
def MakeGui():

	global filename


	if(BUTTON['LOKEY'].val>BUTTON['HIKEY'].val):
		BUTTON['LOKEY'].val=BUTTON['HIKEY'].val-1	

	# Make the background of the entire script window grey
	BGL.glClearColor(0.5, 0.5, 0.5, 0.0)
	BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)

	# A Background color for the script
	BGL.glColor3f(0, 0, 0)
	BGL.glRectf(1, 1, 400, 220)


	# A bit of text for the top of the script
	BGL.glColor3f(0.8, 0.9, 1)
	BGL.glRasterPos2d(10, 195)
	Draw.Text("MidiImportX " + str(VERSION) + " by (c)JulesD 2006", "small")

	# A Background color for the messages
	BGL.glColor3f(0.3, 0.3, 0.3)
	BGL.glRectf(10, 160, 220, 180)

	# The message to display
	BGL.glColor3f(1, 1, 1)
	BGL.glRasterPos2d(20, 167)
	Draw.Text(filename)

	# .MID File Search Button
	Draw.Button("Search",EVENT['CHOOSE_FILENAME'],230, 160, 100, 20)

	# A number box for setting the midi track number you want to import
	BUTTON['MIDICHANNELNR'] = Draw.Number("Midi Ch: ", EVENT['DO_REDRAW'], 10, 130, 100, 20, BUTTON['MIDICHANNELNR'].val, 0, 16, "The midi track you wish to import.")	

	# A number box for setting the midi note number you want to import
	if (BUTTON['NOTE'].val!=0):
		if ((BUTTON['GMDRUMMAP'].val==1) and GMDrumNames.has_value(BUTTON['NOTENUMBER'].val)):
			BUTTON['NOTE'] = Draw.Toggle("Note: " + GMDrumNames.whatis(BUTTON['NOTENUMBER'].val) + " ", EVENT['DO_REDRAW'], 120, 130, 160, 20, BUTTON['NOTE'].val, "The midi note you wish to import.")
			BUTTON['NOTENUMBER']=Draw.Number("N:", EVENT['DO_REDRAW'], 285, 130, 50, 20, BUTTON['NOTENUMBER'].val, 0, 128, "The midi note you wish to import.")	
		else:
			BUTTON['NOTE'] = Draw.Toggle("Note: " + NoteNames.whatis(BUTTON['NOTENUMBER'].val) + " ", EVENT['DO_REDRAW'], 120, 130, 160, 20, BUTTON['NOTE'].val, "The midi note you wish to import.")
			BUTTON['NOTENUMBER']=Draw.Number("N:", EVENT['DO_REDRAW'], 285, 130, 50, 20, BUTTON['NOTENUMBER'].val, 0, 128, "The midi note you wish to import.")


		ob = Object.GetSelected()
		if ((ob[:]==[]) or (BUTTON['CURRENTOBJECT'].val==0)):
			obname="Empty"
		else:
			obname = ob[0].getName()

		BUTTON['CURRENTOBJECT'] = Draw.Toggle(obname, EVENT['DO_REDRAW'], 120, 100, 100, 20, BUTTON['CURRENTOBJECT'].val, "Currently Selected Item")

		BUTTON['IPOCURVELIST'] = Draw.Menu(STANDARDCURVESMENU, EVENT['DO_REDRAW'], 120, 70, 100, 20, BUTTON['IPOCURVELIST'].val, "Currently Selected IPO")

	else:
		if (BUTTON['GLIDEMODE'].val == GON):
			BUTTON['NOTE'] = Draw.Toggle("Notes: " + NoteNames.whatis(BUTTON['LOKEY'].val) + " to " + NoteNames.whatis(BUTTON['HIKEY'].val), EVENT['DO_REDRAW'], 120, 130, 160, 20, BUTTON['NOTE'].val, "The midi note you wish to import.")
		else:
			BUTTON['NOTE'] = Draw.Toggle("Note: ALL", EVENT['DO_REDRAW'], 120, 130, 160, 20, BUTTON['NOTE'].val, "The midi note you wish to import.")

	# A Curvemode button 
	BUTTON['CURVEMODE'] = Draw.Toggle("Bezier", EVENT['DO_REDRAW'], 10, 100, 50, 20, BUTTON['CURVEMODE'].val, "Curve type set to Bezier")

	# A Glidemode button 
	BUTTON['GLIDEMODE'] = Draw.Toggle("Glide", EVENT['DO_REDRAW'], 60, 100, 50, 20, BUTTON['GLIDEMODE'].val, "Glide mode On/Off")

	# A button for setting whether or not the GM DRUM MAP names are used
	BUTTON['GMDRUMMAP'] = Draw.Toggle("Drum", EVENT['DO_REDRAW'], 10, 70, 50, 20, BUTTON['GMDRUMMAP'].val, "GM Drum Map curve nameing mode on/off")

	# A button for setting whether or not the GM DRUM MAP names are used
	BUTTON['NOTEMAP'] = Draw.Toggle("Note", EVENT['DO_REDRAW'], 60, 70, 50, 20, BUTTON['NOTEMAP'].val, "Midi Note Map curve nameing mode on/off")

	# A button for setting whether or not the curve should scale
	BUTTON['SCALE'] = Draw.Toggle("Scale", EVENT['DO_REDRAW'], 60, 40, 50, 20, BUTTON['SCALE'].val, "Scale mode on/off")

	# A number box for setting the ammount the curve is scaled by!
	BUTTON['SCALENR'] = Draw.Slider("Scale : ", EVENT['DO_REDRAW'], 120, 40, 100, 20, BUTTON['SCALENR'].val, 0, 10, 0, "Maximum curve peak")

	if(BUTTON['GLIDEMODE'].val==GOFF):
		# A button for setting whether or not the curve should use attack
		BUTTON['ATTACK'] = Draw.Toggle("Attack", EVENT['DO_REDRAW'], 230, 100, 50, 20, BUTTON['ATTACK'].val, "Attack mode on/off")

		# A number box for setting the Attack value
		if (BUTTON['ATTACK'].val==1):
			BUTTON['ATTACKNR'] = Draw.Slider("Attack : ", EVENT['DO_REDRAW'], 290, 100, 100, 20, BUTTON['ATTACKNR'].val, 0, 5, 0, "Attack rate")

		# A button for setting whether or not the curve should use DECAY
		BUTTON['DECAY'] = Draw.Toggle("Decay", EVENT['DO_REDRAW'], 230, 70, 50, 20, BUTTON['DECAY'].val, "Decay mode on/off")

		# A number box for setting the DECAY value!
		if (BUTTON['DECAY'].val==1):
			BUTTON['DECAYNR'] = Draw.Slider("Decay : ", EVENT['DO_REDRAW'], 290, 70, 100, 20, BUTTON['DECAYNR'].val, 0, 5, 0, "Decay rate")

		# A button for setting whether or not the curve should use sustain
		BUTTON['SUSTAIN'] = Draw.Toggle("Sustain", EVENT['DO_REDRAW'], 230, 40, 50, 20, BUTTON['SUSTAIN'].val, "Sustain mode on/off")

		# A number box for setting the sustain value!
		if (BUTTON['SUSTAIN'].val==1):
			BUTTON['SUSTAINNR'] = Draw.Slider("Sustain : ", EVENT['DO_REDRAW'], 290, 40, 100, 20, BUTTON['SUSTAINNR'].val, 0, 1, 0, "Sustain rate")

		# A button for setting whether or not the curve should use release
		BUTTON['RELEASE'] = Draw.Toggle("Release", EVENT['DO_REDRAW'], 230, 10, 50, 20, BUTTON['RELEASE'].val, "Release mode on/off")

		# A number box for setting the release value!
		if (BUTTON['RELEASE'].val==1):
			BUTTON['RELEASENR'] = Draw.Slider("Release : ", EVENT['DO_REDRAW'], 290, 10, 100, 20, BUTTON['RELEASENR'].val, 0, 5, 0, "Release rate")

		DrawPreview()
	else:
		# A number box for setting the Lowest Key value
		BUTTON['LOKEY'] = Draw.Slider("Lo : ", EVENT['DO_REDRAW'], 120, 100, 160, 20, BUTTON['LOKEY'].val, 0, 128, 0, "Lowest key to import")

		# A number box for setting the Highest Key value
		BUTTON['HIKEY'] = Draw.Slider("Hi : ", EVENT['DO_REDRAW'], 120, 70, 160, 20, BUTTON['HIKEY'].val, 0, 128, 0, "Highest Key to import")

		


	# Load .MID file button
	Draw.Button("Import .MID", EVENT['LOAD_MID'], 10, 10, 100, 20, "Import .MID file")

	## Exit button
	Draw.Button("EXIT", EVENT['EXIT'], 120, 10, 100, 20, "Exit the script!")


## CHECK FOR THE ESCAPE KEY AND EXIT THE SCRIPT IF IT WAS PRESSED
def event(evt, val):
	if(evt == Draw.QKEY):
		Draw.Exit()

## ACTIONS TO BE TAKEN AFTER A BUTTON HAS BEEN PRESSED IN THE GUI
def bevent(evt):
	# Choose the .MID file to import
	if (evt is EVENT['CHOOSE_FILENAME']):
		Blender.Window.FileSelector(filename_callback, "Import MID")
		Draw.Redraw()


	# Redraw interface
	if (evt is EVENT['DO_REDRAW']):
		if (BUTTON['GLIDEMODE'].val == GON):
			BUTTON['NOTE'].val=0
		Draw.Redraw()

	# Run the script
	if (evt is EVENT['LOAD_MID']):
		if (filename!="midi filename"):
			if (BUTTON['GLIDEMODE'].val == GON):
				load_mid_by_channel(filename)
			else:
				load_mid_by_note(filename)
		else:
			Draw.PupMenu("No Midifile to import!%t|OK")

		Draw.Redraw()

	# Exit the script
	if (evt is EVENT['EXIT']):
		Draw.Exit()


## REGISTERING THE FUNCTIONS
Draw.Register(MakeGui, event, bevent)