[Closed] script converting from blender to max
Hello Guys, I’m not that good with scripting but this time i need to use a script which is written in C, the script is written for blender and i’m kind of short in time for the project to learn the interface of blender fully since i’m a 3d max user. is there a way some one can help me to make the script convert into a max script and make it work on max ? i tried to open the max script editor and past it there and save as .ms but didn’t work out if some one knows how to do it please let me know.
What are you trying to export ? geometry ? if yes, have you considered exporting to .obj format, and import it from max ?
the script is over large i will send it in segments and its a midi driver that turns sound tune making an animation
import Blender
from Blender import *
from Blender.BGL import *
class Note:
“””Hold data of a single note, have access to the note related constants.
Instance variables:
val -- note pitch value
instr -- id of the instrument
channel -- id of the a channel
startT -- the time of the beginning of the note [millisec]
stopT -- the time of the ens of the note [millisec]
startVel -- initial velocity of the note
stopVel -- finishing velocity of the note
isSelected -- flag marking the selected note
Static constants:
vals -- dictionary translating the numerical note value to the name of
the pitch
instruments -- dictionary translating the numerical note instrument
value to its name
Public methods:
__init__
__cmp__ (overloaded)
__str__ (overloaded)
Important:
Due to the different instrument set and note naming for MIDI channel 10
(percusion) they are assigned a number above 127 (by adding 128 to
the actual number)
"""
vals = {0:"C-2",1:"C#-2",2:"D-2",3:"D#-2",4:"E-2",5:"F-2",6:"F#-2",\
7:"G-2",8:"G#-2",9:"A-2",10:"A#-2",11:"B-2",12:"C-1",13:"C#-1",\
14:"D-1",15:"D#-1",16:"E-1",17:"F-1",18:"F#-1",19:"G-1",\
20:"G#-1",21:"A-1",22:"A#-1",23:"B-1",24:"C",25:"C#",26:"D",\
27:"D#",28:"E",29:"F",30:"F#",31:"G",32:"G#",33:"A",34:"A#",\
35:"B",36:"C1",37:"C#1",38:"D1",39:"D#1",40:"E1",41:"F1",\
42:"F#1",43:"G1",44:"G#1",45:"A1",46:"A#1",47:"B1",48:"C2",\
49:"C#2",50:"D2",51:"D#2",52:"E2",53:"F2",54:"F#2",55:"G2",\
56:"G#2",57:"A2",58:"A#2",59:"B2",60:"C3",61:"C#3",62:"D3",\
63:"D#3",64:"E3",65:"F3",66:"F#3",67:"G3",68:"G#3",69:"A3",\
70:"A#3",71:"B3",72:"C4",73:"C#4",74:"D4",75:"D#4",76:"E4",\
77:"F4",78:"F#4",79:"G4",80:"G#4",81:"A4",82:"A#4",83:"B4",\
84:"C5",85:"C#5",86:"D5",87:"D#5",88:"E5",89:"F5",90:"F#5",\
91:"G5",92:"G#5",93:"A5",94:"A#5",95:"B5",96:"C6",97:"C#6",\
98:"D6",99:"D#6",100:"E6",101:"F6",102:"F#6",103:"G6",104:"G#6",\
105:"A6",106:"A#6",107:"B6",108:"C7",109:"C#7",110:"D7",\
111:"D#7",112:"E7",113:"F7",114:"F#7",115:"G7",116:"G#7",\
117:"A7",118:"A#7",119:"B7",120:"C8",121:"C#8",122:"D8",\
123:"D#8",124:"E8",125:"F8",126:"F#8",127:"G8",\
163:"Acoustic Bass Drum",164:"Bass Drum 1",165:"Side Stick",\
166:"Acoustic Snare",167:"Hand Clap",168:"Electric Snare",\
169:"Low Floor Tom",170:"Closed Hi-Hat",171:"High Floor Tom",\
172:"Pedal Hi-Hat",173:"Low Tom",174:"Open Hi-Hat",\
175:"Low-Mid Tom",176:"Hi-Mid Tom",177:"Crash Cymbal 1",\
178:"High Tom",179:"Ride Cymbal 1",180:"Chinese Cymbal",\
181:"Ride Bell",182:"Tambourine",183:"Splash Cymbal",\
184:"Cowbell",185:"Crash Cymbal 2",186:"Vibraslap",\
187:"Ride Cymbal 2",188:"Hi Bongo",189:"Low Bongo",\
190:"Mute Hi Conga",191:"Open Hi Conga",192:"Low Conga",\
193:"High Timbale",194:"Low Timbale",195:"High Agogo",\
196:"Low Agogo",197:"Cabasa",198:"Maracas",199:"Short Whistle",\
200:"Long Whistle",201:"Short Guiro",202:"Long Guiro",\
203:"Claves",204:"Hi Wood Block",205:"Low Wood Block",\
206:"Mute Cuica",207:"Open Cuica",208:"Mute Triangle",\
209:"Open Triangle"}
instruments = {0:"Acoustic Grand Piano",1:"Bright Acoustic Piano",\
2:"Electric Grand Piano",3:"Honky-tonk Piano",\
4:"Electric Piano 1",5:"Electric Piano 2",6:"Harpsichord",\
7:"Clavinet",8:"Celesta",9:"Glockenspiel",10:"Music Box",\
11:"Vibraphone",12:"Marimba",13:"Xylophone",14:"Tubular Bells",\
15:"Dulcimer",16:"Drawbar Organ",17:"Percussive Organ",\
18:"Rock Organ",19:"Church Organ",20:"Reed Organ",\
21:"Accordion",22:"Harmonica",23:"Tango Accordion",\
24:"Acoustic Guitar (nylon)",25:"Acoustic Guitar (steel)",\
26:"Electric Guitar (jazz)",27:"Electric Guitar (clean)",\
28:"Electric Guitar (muted)",29:"Overdriven Guitar",\
30:"Distorted Guitar",31:"Guitar Harmonics",32:"Acoustic Bass",\
33:"Acoustic Bass (finger)",34:"Acoustic Bass (pick)",\
35:"Fretless Bass",36:"Slap Bass 1",37:"Slap Bass 2",\
38:"Synth Bass 1",39:"Synth Bass 2",40:"Violin",41:"Viola",\
42:"Cello",43:"Contrabass",44:"Tremolo Strings",\
45:"Pizzicato Strings",46:"Orchestral Harp",47:"Timpani",
48:"String Ensemble 1",49:"String Ensemble 2",\
50:"Synth Strings 1",51:"Synth Strings 2",52:"Choir Aahs",\
53:"Voice Oohs",54:"Synth Voice",55:"Orchestra Hit",\
56:"Trumpet",57:"Trombone",58:"Tuba",59:"Muted Trumpet",\
60:"French Horn",61:"Brass Section",62:"Synth Brass 1",\
63:"Synth Brass 2",64:"Soprano Sax",65:"Alto Sax",\
66:"Tenor Sax",67:"Baritone Sax",68:"Oboe",69:"English Horn",\
70:"Bassoon",71:"Clarinet",72:"Piccolo",73:"Flute",\
74:"Recorder",75:"Pan Flute",76:"Blown Bottle",77:"Shakuhachi",\
78:"Whistle",79:"Ocarina",80:"Lead 1 (square)",\
81:"Lead 2 (sawtooth)",82:"Lead 3 (calliope)",\
83:"Lead 4 (chiff)",84:"Lead 5 (charang)",85:"Lead 6 (voice)",\
86:"Lead 7 (fifths)",87:"Lead 8 (bass + lead)",\
88:"Pad 1 (new age)",89:"Pad 2 (warm)",90:"Pad 3 (polysynth)",\
91:"Pad 4 (choir)",92:"Pad 5 (bowed)",93:"Pad 6 (metallic)",\
94:"Pad 7 (halo)",95:"Pad 8 (sweep)",96:"FX 1 (rain)",\
97:"FX 2 (soundtrack)",98:"FX 3 (crystal)",\
99:"FX 4 (atmosphere)",100:"FX 5 (brightness)",\
101:"FX 6 (goblins)",102:"FX 7 (echoes)",103:"FX 8 (sci-fi)",\
104:"Sitar",105:"Banjo",106:"Shamisen",107:"Koto",108:"Kalimba",\
109:"Bag Pipe",110:"Fiddle",111:"Shanai",112:"Tinkle Bell",\
113:"Agogo",114:"Steel Drums",115:"Woodblock",116:"Taiko Drum",\
117:"Melodic Tom",118:"Synth Drum",118:"Reverse Cymbal",\
120:"Guitar Fret Noise",121:"Breath Noise",122:"Seashore",\
123:"Bird Tweet",124:"Telephone Ring",125:"Helicopter",\
126:"Applause",127:"Gunshot",128:"Standard Drum Set",\
136:"Room Set",144:"Power Set",152:"Electronic Set",\
153:"TR-808 Set",160:"Jazz Set",168:"Brush Set",\
176:"Orchestral Set",184:"SFX Set",255:"CM-64/32 C Set"}
def __init__(self,_val,_instr,_channel,_startT,_stopT,_startVel,_stopVel):
"""Constructor.
Set the note values and initially mark it as selected.
“””
self.val = _val
self.instr = _instr
self.channel = _channel
self.startT = _startT
self.stopT = _stopT
self.startVel = _startVel
self.stopVel = _stopVel
self.isSelected = True
def __cmp__(self,other):
"""Comparator.
Compares two notes taking into account the starting time of the note,
its channel number and pitch value
"""
if self.startT != other.startT:
return cmp(self.startT,other.startT)
elif self.channel != other.channel:
return cmp(self.channel,other.channel)
else:
return cmp(self.val,other.val)
def __str__(self):
"""Form a string listing the properties of this note."""
return "%d %d %d %s %s %s" % \
(self.startT,self.stopT,self.channel+1,self.vals[self.val],\
self.instruments[self.instr],str(self.isSelected))
class MidiFormatError(Exception):
“””Exception regarding faulty MIDI file.
Currently three kinds of this exception are available:
NOT_MIDI -- selected file is not a MIDI and cannot be processed
BAD_FORMAT -- selected file has unsupported format (other than SMF0
and SMF1)
BAD_TIMEDIV -- selected file has an unsupported time base (other
than bits per quarternote)
Public methods:
__init__
__str__ (overloaded)
"""
NOT_MIDI,BAD_FORMAT,BAD_TIMEDIV = range(3)
def __init__(self, _val = 0):
"""Constructor.
Allow defining the exception kind. By default it is NOT_MIDI
"""
self.val = _val
def __str__(self):
"""Form a string description of this exception."""
if self.val == self.NOT_MIDI:
return "File is not a MIDI"
if self.val == self.BAD_FORMAT:
return "Not supported MIDI SMF2 format"
if self.val == self.BAD_TIMEDIV:
return "Not supported FPS timing"
class SyncError(Exception):
“””Exception regarding synchronisation errors.
Currently one kind of this error is available:
SRCIPO -- object selected to be synchronise already yields the Ipo
or Action which was specified as the source - race condition.
"""
SRCIPO = range(1)
def __init__(self, _val):
self.val = _val
def __str__(self):
if self.val == self.SRCIPO:
return "Source Ipo/Action cannot be overwritten"
class MidiFile:
“””Hold data of a single MIDI file.
Instance variables:
data -- byte string holding the file content
instrument -- the last instrument used in particular channel (listed
by a channel)
noteList -- the list of all notes existing in this file
tempoList -- list of all tempo changes which occured in this
sequence
offest -- current file pointer position counting from the beginning
of the file, allows naviation over the data string
tickDiv -- current time division base (bits per quarternote)
timing -- current time position when reading the file
tracksCount -- number of tracks in this file
Static constants:
maxInt -- maximum integer, to limit the time value in broken files
maxVelocity -- maximum note velocity allowed
Public methods:
__init__
selectNotes -- select notes with attributes fitting to the arguments
given
extractSelectedNotes -- form a new list of notes from a given list
using only the elements marked as selected.
"""
maxInt = 2147483647
maxVelocity = 127
def str2Number(self,length):
"""Extract from a file an integer value of a fixed length.
After number extraction offset attribute is enlarged by
the length of the number.
"""
result = 0
for byte in self.data[self.offset:self.offset+length]:
result = (result << 8) + ord(byte)
self.offset += length
return result
def strvlq2Number(self):
"""Extract VLQ (variable length quantity) from a file.
File offset is enlarged by the size of the number
retrieved.
"""
result = 0
while 1:
byte = ord(self.data[self.offset])
self.offset += 1
result = (result << 7) + (byte & 0x7F)
if not (byte & 0x80):
break
return result
def finishNote(self,noteVal,channel,velocity):
"""Search for the last unfinished note and fill out the missing
parameters.
Attributes noteVal and channel are required to find find the
appropriate note. Its stopT attribute will be assigned the current
timing and stopVel will be replaced with velocity given. In case the
timing of the note (startT, stopT) or the file timing exceed the
maxInt value, the note is deleted.
"""
if self.noteList:
for note in self.noteList[-1::-1]:
if (note.val == noteVal) and (note.channel == channel):
if (note.startT == self.timing)\
or (note.startT>self.maxInt)\
or (self.timing>self.maxInt):
self.noteList.remove(note)
break
note.stopT = self.timing
note.stopVel = velocity
break
def handleMidiEvent(self,midiEvent,time):
"""Parse the MIDI events found in the file.
Only following events are handled:
noteOn - when encoutered, create the new note with provided parameters
or if the velocity was 0, invoke finishNote() with provided parameters
noteOff - when encoutered, invoke finishNote() with provided
parameters
programChange - when encoutered, change the instrument of a given
channel
Other events are skipped while they are unnecessary for the script
purpose.
“””
hiBits = midiEvent >> 4
loBits = midiEvent & 0xF
if hiBits == 0x8:
channel = loBits
note = self.str2Number(1)
if channel == 9:
note += 0x80
velocity = self.str2Number(1)
self.finishNote(note,channel,velocity)
elif hiBits == 0x9:
channel = loBits
note = self.str2Number(1)
if channel == 9:
note += 0x80
velocity = self.str2Number(1)
if velocity == 0:
self.finishNote(note,channel,velocity)
else:
self.noteList.append(Note(note,self.instrument[loBits],
channel,self.timing,self.timing,velocity,velocity))
elif hiBits == 0xC:
channel = loBits
instrument = self.str2Number(1)
if channel == 9:
instrument += 0x80
self.instrument[channel] = instrument
elif hiBits == 0xD:
self.offset += 1
else:
self.offset += 2
def readTrackChunk(self):
"""Parse the track chunk (MTrk).
If this chunk has an invalid id, it is skipped.
Reset the timing attribute and the instrument list.
Handle events:
MIDI events - passed to the handleMidiEvents
meta-events - tempo change (addsnew to the tempo list) and EndOfTrack
(finishes reading this track)
Other events are skipped. Last MIDI event id is hold in case of
running status bytes occurence.
"""
chunkId = self.str2Number(4)
chunkLength = self.str2Number(4)
if not (chunkId == 0x4D54726B):
self.offset = chunkLength + self.offset
return
chunkLength += self.offset
self.tracksCount -= 1
self.timing = 0
lastMidiEvent = 0
self.instrument = [0]*16
self.instrument[9] = 0x80
while 1:
## read current track time
self.timing += self.strvlq2Number( )
byte = self.str2Number(1)
if (byte & 0x80):
hiBits = byte >> 4
loBits = byte & 0xF
if hiBits == 0xF:
if loBits == 0xF:
metaEvent = self.str2Number(1)
eventLength = self.strvlq2Number()
if metaEvent == 0x2F:
return
elif metaEvent == 0x51:
tempo = self.str2Number(eventLength)
self.tempoList.append([self.timing,\
tempo/(self.tickDiv*1000)])
else:
self.offset += eventLength
else:
eventLength = self.strvlq2Number()
self.offset += eventLength
else:
lastMidiEvent = byte
self.handleMidiEvent(byte,self.timing)
else:
## handle running state
self.offset -= 1
self.handleMidiEvent(lastMidiEvent,self.timing)
def readHeaderChunk(self):
"""Parse the header (MThd) chunk.
Retrieves the inforamtion about the file id, MIDI format, number of
tracks and initial time base resolution. Raises MidiFormatException
if any parameter refers to the unsuported MIDI file type.
"""
if not (self.str2Number(4) == 0x4d546864):
raise MidiFormatError(MidiFormatError.NOT_MIDI)
self.offset += 4
if self.str2Number(2) > 1:
raise MidiFormatError(MidiFormatError.BAD_FORMAT)
self.tracksCount = self.str2Number(2)
self.tickDiv = self.str2Number(2)
if self.tickDiv & 0x8000:
raise MidiFormatError(MidiFormatError.BAD_TIMEDIV)
self.tickDiv = float(self.tickDiv)
def calcRealTime(self,bitTime):
"""Convert bitTime into miliseconds.
Time given in MIDI bits is calculated into miliseconds
according to the tempo changes stored in the tempo list
"""
result = 0
prevTempo = self.tempoList[0]
for tempo in self.tempoList:
if tempo[0] < bitTime:
result += (tempo[0] - prevTempo[0]) * prevTempo[1]
prevTempo = tempo
else:
break
result += (bitTime - prevTempo[0]) * prevTempo[1]
return int(result)
def __init__(self, fileName):
"""Constructor.
Open and parse MIDI file. Retrieve all available notes and
recalculate their timing into miliseconds. Sort note list.
"""
self.totalTime = 0
self.offset = 0
self.tracksCount = 0
self.tickDiv = 500000
self.noteList = []
self.tempoList = []
f = open(fileName, 'rb')
self.data = f.read()
self.readHeaderChunk( )
while self.tracksCount > 0:
self.readTrackChunk( )
if not self.tempoList:
self.tempoList.append([self.timing,500/self.tickDiv])
else:
self.tempoList.sort()
for note in self.noteList:
note.startT = self.calcRealTime(note.startT)
note.stopT = self.calcRealTime(note.stopT)
if note.stopT > self.totalTime:
self.totalTime = note.stopT
self.noteList.sort()
def selectNotes(notes,channel=-1,instrument=-1,noteVal=-1\
,timeRange=[-1,-1],order=[0,1]):
"""Select notes with attributes fitting to the arguments given
Each note which attributes equals the respective given values
is marked as selected.
Arguments:
notes -- note list to be processed
channel -- requested channel value, skipped if -1
instrument -- requested instrument type value, skipped if -1
noteVal -- reqested pitch value, skipped if -1
timeRange -- restrict selected notes to those which at least partially
fit into given time interval; timeRange[0] has to be greater than
timeRange[1]
order -- from selected notes choose every order[1]-th element
beginning from the order[0]-th element
"""
result = []
for id, note in enumerate(notes):
note.isSelected = False
if (channel == -1) or (note.channel == channel):
if (instrument == -1) or (note.instr == instrument):
if (noteVal == -1) or (note.val == noteVal):
if (timeRange[0] == -1) and (timeRange[1] == -1):
if (id < order[0]) or (((id-order[0])%order[1])!=0):
continue
else:
note.isSelected = True
elif ((note.startT<timeRange[0])\
and (note.stopT<timeRange[0]))\
or ((note.startT>timeRange[1])\
and (note.stopT>timeRange[1])):
continue
else:
note.isSelected = True
selectNotes = staticmethod(selectNotes)
def extractSelectedNotes(notes):
“””Form a new list of notes from a given list using only the elements
marked as selected.
"""
result = []
for note in notes:
if note.isSelected:
result.append(note)
return result
extractSelectedNotes = staticmethod(extractSelectedNotes)
class Common:
“””Provide access to the synchronisation logic and static constants
used by more than one class.
Static constants:
AR,CA,LA,MA,OB,TE,WO -- ipo or datablock type, respectively for armature,
camera, lamp, material, object, texture and world
typeAbbrev -- dictionary translating datablock type values into related
abbreviations commonly used in Blender
typeNames -- dictionary translating datablock type values into related
full names of the type
Static variables:
filename -- a name of the last correctly opened MIDI file
Public methods (all static):
printNoteList -- print the chart with attributes of all notes on the list
findKeys -- retrieve the time position of all kayframes in Action/Ipo
given.
getDatablock -- Return datablock of name id and given type or None if such
does not exist.
synchronise -- Synchronise action or Ipo with notes available on the list
and assigns it to the datablock given.
correctIpo -- correct long intervals with key frames of different y values
at each end.
"""
AR,CA,LA,MA,OB,TE,WO = 17217,16707,16716,16717,16975,17748,20311
typeAbbrev = {AR:"AR",CA:"CA",LA:"LA",MA:"MA",OB:"OB",TE:"TE",WO:"WO"}
typeNames = {AR:"Armature",CA:"Camera",LA:"Lamp",MA:"Material",\
OB:"Object",TE:"Texture",WO:"World"}
filename = "None"
def printNoteList(noteList):
"""Print the chart with attributes of all notes on the list."""
print"
Start Stop Channel Note Instrument
“+”-” * 60
for note in noteList:
print note
printNoteList = staticmethod(printNoteList)
def findKeys(action):
"""Retrieve the time position of all kayframes in Action/Ipo
given.
Return list of key frames time position sorted in ascending order
"""
keys = []
if action.__class__.__name__ == "Blender Action":
for ipo in action.getAllChannelIpos().values():
for icu in ipo:
for pt in icu.bezierPoints:
if not (pt.pt[0] in keys):
keys.append(pt.pt[0])
else:
for icu in action:
for pt in icu.bezierPoints:
if not (pt.pt[0] in keys):
keys.append(pt.pt[0])
keys.sort()
return keys
findKeys = staticmethod(findKeys)
def getDatablock(id,type):
"""Return datablock of name id and given type or
None if such does not exist."""
if type == Common.AR:
return Armature.Get(id)
elif type == Common.MA:
return Material.Get(id)
elif type == Common.LA:
return Lamp.Get(id)
elif type == Common.WO:
return World.Get(id)
elif type == Common.CA:
return Camera.Get(id)
elif type == Common.OB:
return Object.Get(id)
elif type == Common.TE:
return Texture.Get(id)
else:
return None
getDatablock = staticmethod(getDatablock)
def insertKeys (icu,keys):
"""Insert given key frames into Ipo curve icu.
If there are already any key frames within the time range
of new ones, the are deleted.
"""
first = keys[0].pt[0]
last = keys[-1].pt[0]
id = 0
for bezPt in icu.bezierPoints:
if (bezPt.pt[0] >= first) and (bezPt.pt[0] <= last):
icu.delBezier(id)
else:
id += 1
for keyPt in keys:
icu.append(keyPt)
insertKeys = staticmethod(insertKeys)
def hasKey(icu,x):
"""Check if there is any key frame in Ipo curve icu
at the position x.
Return True if there is keyframe at x, False otherwise
"""
for bezPt in icu.bezierPoints:
if bezPt.pt[0] == x:
return True
return False
hasKey = staticmethod(hasKey)
def calcBezTriple(src,offset,lKey,xScale,yScale):
"""Calculates new value of Bezier triple vectors.
Point position of the triple is affected as well as the position
of the handles. Only 2D triples are affected (third coordinate of
each vector will remain 0)
Arguments:
src -- source Bezier triple
offset -- the time offset from the beginning of the sequence
lKey -- leading key frame position ([x,y])
xScale -- time scaling of the sequence
yScale -- vertical scaling of the sequence
"""
return BezTriple.New(\
offset+(src.vec[0][0]-lKey[0])*xScale,\
lKey[1]+(src.vec[0][1]-lKey[1])*yScale,0,\
offset+(src.vec[1][0]-lKey[0])*xScale,\
lKey[1]+(src.vec[1][1]-lKey[1])*yScale,0,\
offset+(src.vec[2][0]-lKey[0])*xScale,\
lKey[1]+(src.vec[2][1]-lKey[1])*yScale,0)
calcBezTriple = staticmethod(calcBezTriple)
def getLinkedObject(dataName,dataType):
"""Return object connected to the datablock of dataName and dataType,
or None if such datablock does not exist.
"""
if dataType == Common.OB:
return Object.Get(dataName)
data = Common.getDatablock(dataName,dataType)
for obj in Object.Get():
if obj.data == data:
return obj
return None
getLinkedObject = staticmethod(getLinkedObject)
def syncAction(noteList,armId,actId,leadKey,scaleEnabled,holdEnabled,\
velocity,relEnabled,pActId,pScaleEnabled,initOffset):
"""Synchronise action with notes available on the list and assigns it
to the armature given.
Arguments:
noteList -- notes to be synchronised with
armId -- the name of the armature;
actId -- the name of the key frames source action, cannot be assigned
to the armature which already has this action
leadKey -- number of the leading (ie. falling on the beggining of the
note) key frame
scaleEnabled -- if True, source action will be scalled down to the
durration of the note
holdEnabled -- if True, additional key frame (same as the leading key
frame) will be added at the end of the note and all following key
frames will be respectively shifted
velocity -- reference velocity for vertical scalling, no vetical
scaling will occur if velocity == 0
relEnabled -- if True, vertical scalling will be relative to the
leading key y position, otherwise scaling base is absolute (0)
pActId -- pause action name, this action will be placed in empty
intervals between given notes
pScaleEnabled -- allows stretching the pause action to fill in the
interval in which it will be placed
initOffset -- shifts the synchronised action in time by a given value
"""
## Get source action and retrieve ipos enclosed in it.
ipos = Armature.NLA.GetActions()[actId].getAllChannelIpos()
if not ipos:
return
## Get current frame rate and object linked to this armature.
fps = Scene.GetCurrent().getRenderingContext().fps
object = Common.getLinkedObject(armId,Common.AR)
## Ensure there is an action connected to this armature; if not create
## new one.
if not object.action:
object.action = Armature.NLA.NewAction("MidiAction")
object.action.setActive(object)
object.action.fakeUser = False
elif object.action.name == actId:
raise SyncError(SyncError.SRCIPO)
## Get access to the destination action's Ipos.
armIpos = object.action.getAllChannelIpos()
## Get access to the armature pose in case there was need to add new
## action channels.
pose = object.getPose()
## Get sorted list of action key frames time positions.
actKeys = Common.findKeys(Armature.NLA.GetActions()[actId])
## Leading key time position, y position left 0 .
lKey = [actKeys[leadKey],0]
## List for detrmining the location of intervals for pause action
## insertion.
notesPosition = []
for note in noteList:
notesPosition.append([MidiFile.maxInt,-MidiFile.maxInt])
## Recalculate note timing from miliseconds into frames.
startT = fps*note.startT/1000.0
stopT = fps*note.stopT/1000.0
## Set the time scaling factor.
if scaleEnabled and (len(actKeys)>1):
xScale = (float)(stopT - startT)/(actKeys[-1]-actKeys[0])
else:
xScale = 1
## Set the vertical scaling factor.
if velocity>0:
yScale = (float)(note.startVel)/velocity
else:
yScale = 1
for channel in ipos.keys():
if channel in pose.bones.keys():
## Add required channel if it does not exist in the
## destination action.
if not (channel in armIpos.keys()):
pose.bones[channel].insertKey(object,1,\
Object.Pose.LOC,True)
armIpos = object.action.getAllChannelIpos()
armIpos[channel][Ipo.PO_LOCX]=None
armIpos[channel][Ipo.PO_LOCY]=None
armIpos[channel][Ipo.PO_LOCZ]=None
armIpos = object.action.getAllChannelIpos()
for icu in ipos[channel]:
## Add required Ipo curve if it does not exist in the
## destination channel.
try:
armIpos[channel].addCurve(icu.name)
except ValueError:
pass
for objIcu in armIpos[channel]:
if icu.name == objIcu.name:
## If relative vertical scaling is enabled,
## update leding key y position.
if relEnabled:
lKey[1] = icu[lKey[0]]
keysToInsert = []
objIcu.interpolation = icu.interpolation
offset = startT + initOffset
holdUsed = False
for bezPt in icu.bezierPoints:
if (holdEnabled) and (not holdUsed):
## Append the duplicate of the leading
## keyframe if hold option is enabled.
## If there are no key frames at the
## leading key frame position, new key
## frame is created at this position.
if bezPt.pt[0] > lKey[0]:
holdUsed = True
if Common.hasKey(icu,lKey[0]):
offset = stopT + initOffset
keysToInsert.append(\
Common.calcBezTriple(\
prevBezPt,offset,lKey,\
xScale,yScale))
else:
keysToInsert.append(\
BezTriple.New(offset,\
lKey[1]*yScale,0))
offset = stopT + initOffset
keysToInsert.append(\
BezTriple.New(offset,\
lKey[1]*yScale,0))
## Append the rest of the key frames
prevBezPt = bezPt
keysToInsert.append(\
Common.calcBezTriple(\
bezPt,offset,lKey,\
xScale,yScale))
## Append the duplicate of the leading key
## frame if hold option is enabled and it
## is the last key frame of this action.
if (holdEnabled) and (not holdUsed):
if Common.hasKey(icu,lKey[0]):
offset = stopT + initOffset
keysToInsert.append(\
Common.calcBezTriple(prevBezPt,\
offset,lKey,xScale,yScale))
else:
keysToInsert.append(BezTriple.New(\
offset,lKey[1]*yScale,0))
offset = stopT + initOffset
keysToInsert.append(BezTriple.New(\
offset,lKey[1]*yScale,0))
## Update pause intervals list.
if keysToInsert[0].pt[0] <\
notesPosition[-1][0]:
notesPosition[-1][0] =\
keysToInsert[0].pt[0]
if keysToInsert[-1].pt[0] >\
notesPosition[-1][1]:
notesPosition[-1][1] =\
keysToInsert[-1].pt[0]
## Insert keys to the Ipo curve and correct
## the curve.
Common.insertKeys(objIcu,keysToInsert)
objIcu.recalc()
continue
Insertion of the pause action.
if not pActId:
return
if object.action.name == pActId:
raise SyncError(SyncError.SRCIPO)
ipos = Armature.NLA.GetActions()[pActId].getAllChannelIpos()
actKeys = Common.findKeys(Armature.NLA.GetActions()[pActId])
actDuration = actKeys[-1] - actKeys[0]
for i in range(0,len(notesPosition)):
## Find the interval long enough to fit in the pause action (also
## let the pause action be inserted at the very end of the
## destination action)
if i < len(notesPosition)-1:
interval = notesPosition[i+1][0] - notesPosition[i][1]
else:
interval = actDuration
if interval >= actDuration:
## Calculate scaling factor if scaling up is enabled
if pScaleEnabled and (actDuration>0):
scale = interval/actDuration
else:
scale = 1
for channel in ipos.keys():
if channel in pose.bones.keys():
## Add required channel if it does not exist in the
## destination action.
if not (channel in armIpos.keys()):
pose.bones[channel].insertKey(object,1,\
Object.Pose.LOC,True)
armIpos = object.action.getAllChannelIpos()
armIpos[channel][Ipo.PO_LOCX]=None
armIpos[channel][Ipo.PO_LOCY]=None
armIpos[channel][Ipo.PO_LOCZ]=None
for icu in ipos[channel]:
## Add required Ipo curve if it does not exist in
## the destination Ipo.
try:
armIpos[channel].addCurve(icu.name)
except ValueError:
pass
for objIcu in armIpos[channel]:
if icu.name == objIcu.name:
keysToInsert = []
for bezPt in icu.bezierPoints:
keysToInsert.append(BezTriple.New(\
notesPosition[i][1]+\
(bezPt.vec[0][0]-1)*scale,\
bezPt.vec[0][1],0,\
notesPosition[i][1]+\
(bezPt.vec[1][0]-1)*scale,\
bezPt.vec[1][1],0,\
notesPosition[i][1]+\
(bezPt.vec[2][0]-1)*scale,\
bezPt.vec[2][1],0))
Common.insertKeys(objIcu,keysToInsert)
objIcu.recalc()
continue
syncAction = staticmethod(syncAction)
def synchronise(noteList,ipoType,objId,ipoId,leadKey,scaleEnabled,
holdEnabled,velocity,relEnabled,pIpoId,pScaleEnabled,initOffset):
"""Synchronise action or Ipo with notes available on the list and
assigns it to the datablock given.
Arguments:
noteList -- notes to be synchronised with
ipoType -- type of Ipo and datablock thi which it will be assigned
objId -- the name of the object/datablock;
ipoId -- the name of the key frames source Ipo or action, cannot be
assigned to the datablock which already has this Ipo/action
leadKey -- number of the leading (ie. falling on the beggining of the
note) key frame
scaleEnabled -- if True, source Ipo/action will be scalled down to the
durration of the note
holdEnabled -- if True, additional key frame (same as the leading key
frame) will be added at the end of the note and all following key
frames will be respectively shifted
velocity -- reference velocity for vertical scalling, no vetical
scaling will occur if velocity == 0
relEnabled -- if True, vertical scalling will be relative to the
leading key y position, otherwise scaling base is absolute (0)
pActId -- pause Ipo/action name, this Ipo/action will be placed in
empty intervals between given notes
pScaleEnabled -- allows stretching the pause action to fill the
interval in which it will be placed
initOffset -- shifts the synchronised Ipo/action in time by a given
value
"""
## Stop if no object or Ipo/action name was specified.
if (not objId) or (not ipoId):
return
## Synchronisation of armature animation is covered by a different
## function.
if ipoType == Common.AR:
Common.syncAction(noteList,objId,ipoId,leadKey,scaleEnabled,
holdEnabled,velocity,relEnabled,pIpoId,pScaleEnabled,initOffset)
return
## Get frame rate.
fps = Scene.GetCurrent().getRenderingContext().fps
## Check if object/datablock exists.
object = Common.getDatablock(objId,ipoType)
if not object:
return
type = Common.typeNames[ipoType]
## Get source Ipo.
ipo = Ipo.Get(ipoId)
## Create new destination Ipo and assign it to the object/datablock
## or get access to the destination Ipo if object/datablock already
## has one.
if not object.ipo:
object.ipo = Ipo.New(type,type+"MidiIpo")
object.ipo.channel = ipo.channel
elif object.ipo.name == ipoId:
raise SyncError(SyncError.SRCIPO)
## Add curve types available in source Ipo to the destination Ipo if
## such curves do not exist there.
for icu in ipo:
try:
object.ipo.addCurve(icu.name)
except ValueError:
pass
## Get time positions of key farmes from source Ipo.
actKeys = Common.findKeys(ipo)
## Set time position of leading keyframe, leave its y as 0.
lKey = [actKeys[leadKey],0]
## Establish list of pause intervals.
notesPosition = []
for note in noteList:
notesPosition.append([MidiFile.maxInt,-MidiFile.maxInt])
## Recalculate notes timing from miliseconds to frames.
startT = fps*note.startT/1000.0
stopT = fps*note.stopT/1000.0
## Calculate time scaling factor.
if scaleEnabled and (len(actKeys)>1):
xScale = (float)(stopT - startT)/(actKeys[-1]-actKeys[0])
else:
xScale = 1
## Calculate vertical scaling factor.
if velocity>0:
yScale = (float)(note.startVel)/velocity
else:
yScale = 1
for icu in ipo:
for objIcu in object.ipo:
if icu.name == objIcu.name:
## Sets leading key y position if relative vertical
## scaling is enabled.
if relEnabled:
lKey[1] = icu[lKey[0]]
keysToInsert = []
## Copy curve properties.
objIcu.interpolation = icu.interpolation
offset = startT + initOffset
holdUsed = False
for bezPt in icu.bezierPoints:
## If hold is enabled append duplicate of leading
## key frame. In case there is no key frame at
## leading key frame position append new one
## there.
if (holdEnabled) and (not holdUsed):
if bezPt.pt[0] > lKey[0]:
holdUsed = True
if Common.hasKey(icu,lKey[0]):
offset = stopT + initOffset
keysToInsert.append(\
Common.calcBezTriple(prevBezPt,\
offset,lKey,xScale,yScale))
else:
keysToInsert.append(BezTriple.New(\
offset,lKey[1]*yScale,0))
offset = stopT + initOffset
keysToInsert.append(BezTriple.New(\
offset,lKey[1]*yScale,0))
prevBezPt = bezPt
## Append rest of the keyframes.
keysToInsert.append(Common.calcBezTriple(bezPt,
offset,lKey,xScale,yScale))
## Append duplicate of held leading key frame in case
## the leading key frame was last in the Ipo.
if (holdEnabled) and (not holdUsed):
if Common.hasKey(icu,lKey[0]):
offset = stopT + initOffset
keysToInsert.append(Common.calcBezTriple(\
prevBezPt,offset,lKey,xScale,yScale))
else:
keysToInsert.append(BezTriple.New(offset,\
lKey[1]*yScale,0))
offset = stopT + initOffset
keysToInsert.append(BezTriple.New(offset,\
lKey[1]*yScale,0))
## Update pause interval list.
if keysToInsert[0].pt[0] < notesPosition[-1][0]:
notesPosition[-1][0] = keysToInsert[0].pt[0]
if keysToInsert[-1].pt[0] > notesPosition[-1][1]:
notesPosition[-1][1] = keysToInsert[-1].pt[0]
## Insert keyframes to the curve and correct it.
Common.insertKeys(objIcu,keysToInsert)
objIcu.recalc()
continue
## The pause Ipo insertion.
if not pIpoId:
return
if object.ipo.name == pIpoId:
raise SyncError(SyncError.SRCIPO)
ipo = Ipo.Get(pIpoId)
for icu in ipo:
try:
object.ipo.addCurve(icu.name)
except ValueError:
pass
actKeys = Common.findKeys(ipo)
actDuration = actKeys[-1] - actKeys[0]
for i in range(0,len(notesPosition)):
## Find interval long enough to fit in the pause Ipo.
if i < len(notesPosition)-1:
interval = notesPosition[i+1][0] - notesPosition[i][1]
else:
interval = actDuration
if interval >= actDuration:
## Calculate scaling factor
if pScaleEnabled and (actDuration>0):
scale = interval/actDuration
else:
scale = 1
for icu in ipo:
for objIcu in object.ipo:
if icu.name == objIcu.name:
keysToInsert = []
for bezPt in icu.bezierPoints:
keysToInsert.append(BezTriple.New(\
notesPosition[i][1]+\
(bezPt.vec[0][0]-1)*scale,
bezPt.vec[0][1],0,\
notesPosition[i][1]+\
(bezPt.vec[1][0]-1)*scale,\
bezPt.vec[1][1],0,\
notesPosition[i][1]+\
(bezPt.vec[2][0]-1)*scale,\
bezPt.vec[2][1],0))
Common.insertKeys(objIcu,keysToInsert)
objIcu.recalc()
continue
synchronise = staticmethod(synchronise)
def correctIpo(action,limit):
"""Correct long intervals with key frames of different y values at each
end.
Search each curve in given Ipo/action for intervals longer then limit.
If such interval starts and finishes with key frames with different y
values, a duplicate of its starting key frame is inserted before the
last interval keyframe. The distance between this duplicate and last
key frame is equal to the interval parameter
"""
if action.__class__.__name__ == "Blender Action":
for ipo in action.getAllChannelIpos().values():
Common.correctIpo(ipo,limit)
else:
for icu in action:
if len(icu.bezierPoints) < 2:
continue
prevPt = icu.bezierPoints[0]
pointsToInsert = []
for pt in icu.bezierPoints[1:]:
if ((pt.pt[0] - prevPt.pt[0])>limit)\
and (pt.pt[1] != prevPt.pt[1]):
pointsToInsert.append(\
[pt.pt[0] - limit,prevPt.pt[1]])
prevPt = pt
for pt in pointsToInsert:
icu.append((pt[0],pt[1]))
icu.recalc()
correctIpo = staticmethod(correctIpo)
class StructDisp:
“””Create and maintain the MIDI file structure displayer.
This componnent displays MIDI sequence, using coloured stripes placed on a
time scale to represent each note. Length and position of the stripe is
proportional to the timing parameters of the respective note.
The component allows also simple selection routines by a single click on
notes, box selection (similar to the one available in other Blender
windows), and key shortcuts for selection inversion and selecting/
deselecting all notes.
Static variables:
timeWidth -- width of time gri (seconds)
noteWidth -- height of note grid
colors -- colors assigned to each channel
Instance variables:
x -- horizontal position of the component
y -- vertical position of the component
sizeX -- width of the component
sizeY -- height of the component
time -- beginning time of the time scale (can change due to the navigation)
note -- beginning note of the note scale (can change due to the navigation)
framesOffset -- initial offset (the distance from the animation beginning
and the MIDI sequence beginning)
timeDiv -- horizontal scaling factor
noteDiv -- vertical scaling factor
selections -- list of selection blocks related to the notes displayed
enableBox -- if True, box selection has been initialized
enableMove -- if True, middle mouse display navigation has been initialized
notes -- notes currently available for display
boxStart -- box selection starting point
infoStr -- string containing information visible below displayer
mousePos -- current mouse position within this component
prevMousePos -- last mouse position
selectionStatus -- toggle connected to select all option
Public methods:
__init__
refresh -- redraws the viewer and initialises new selection buffer
mouseHandler -- handles mouse actions and related keyboard shortcuts within
displayer
"""
class SelectBlock:
"""Handle selection of rectangular areas available in the display area.
Instance variables:
minX, minY -- position of the bottom, left corner of the block
maxX, maxY -- position of the top, right corner of the block
id -- ientifier of this block
Public methods:
__init__
checkSelection -- check whether given pointer x,y is within this block
checkInBox -- check whether at least one of the corners of this block
lies inside given selection box
"""
def __init__(self,minX,minY,maxX,maxY,id):
"""Constructor."""
self.minX = minX
self.maxX = maxX
if self.minX > self.maxX:
self.minX,self.maxX = self.maxX,self.minX
self.minY = minY
self.maxY = maxY
if self.minY > self.maxY:
self.minY,self.maxY = self.maxY,self.minY
self.id = id
def checkSelection(self,x,y):
"""Check whether given pointer x,y is within this selection block."""
if (x>=self.minX) and (x<=self.maxX)\
and (y>=self.minY) and (y<=self.maxY):
return True
return False
def checkInBox(self,minX,minY,maxX,maxY):
"""Check whether at least one of the corners of this selection block
lies inside given selection box.
"""
if (self.minX >= minX) and (self.minX <= maxX)\
and (self.minY >= minY) and (self.minY <= maxY):
return True
if (self.minX >= minX) and (self.minX <= maxX)\
and (self.maxY >= minY) and (self.maxY <= maxY):
return True
if (self.maxX >= minX) and (self.maxX <= maxX)\
and (self.maxY >= minY) and (self.maxY <= maxY):
return True
if (self.maxX >= minX) and (self.maxX <= maxX)\
and (self.minY >= minY) and (self.minY <= maxY):
return True
return False
Can you tell us a bit about the script and what it exactly does?
If there a video anywhere online or one that you could make showing what the script does?
the script is about animating object with midi file but more in advance way , have you ever seen animusic ? heres a link http://www.youtube.com/watch?v=9LCYGmJ1MXM&feature=related this is where i want to get but using max and not blender …
this is the page link from where i got the script there are 3 files one for mac one for pc and the other the manual how does it work i have pc not mac …
really thank you so much for helping me out
timeWidth = 10
noteWidth = 36
colors = [[0,0,1],[0.6,0,0.8],[1,0.6,0.8],[1,0,0],[1,0.4,0],[1,1,0],\
[0.2,0.4,0.4],[0,1,0],[0,1,1],[0.4,0.6,1],[0.4,0.4,0.8],\
[1,0,1],[0.4,0,0.4],[0.4,0.2,0.2],[0.3,0.3,0.3],\
[0.7,0.7,0.7]]
def __init__(self,x,y,sizeX,sizeY):
"""Constructor."""
self.x = x
self.y = y
self.sizeX = sizeX
self.sizeY = sizeY
self.time = 0
self.note = 0
self.framesOffset = 0
self.timeDiv = (self.sizeX-25)/self.timeWidth
self.noteDiv = (int)((self.sizeY-30)/self.noteWidth)
self.selections = []
self.enableBox = False
self.enableMove = False
self.notes = []
self.boxStart = [-1,-1]
self.infoStr = ""
self.selectionStatus = False
def drawLines(self):
"""Draw grid with time and note scale, draw the frame pointer,
write the info string."""
BGL.glColor3f(0.8,0.8,0.8)
BGL.glRecti(self.x+20,self.y+30,\
self.x+self.sizeX-5,self.y+self.sizeY-15)
BGL.glColor3f(0.3,0.3,0.3)
BGL.glBegin(GL_LINE_LOOP)
BGL.glVertex2i(self.x+20,self.y+self.sizeY-15)
BGL.glVertex2i(self.x+self.sizeX-5,self.y+self.sizeY-15)
BGL.glEnd()
for i in range(0,self.timeWidth+1):
if i<self.timeWidth:
minStr = str((self.time+i)/60)
if len(minStr) == 1:
minStr = "0"+minStr
secStr = str((self.time+i)%60)
if len(secStr) == 1:
secStr = "0"+secStr
BGL.glRasterPos2i(self.x+20+(i*self.timeDiv),\
self.y+self.sizeY-12)
Draw.Text(minStr+":"+secStr,"small")
BGL.glBegin(GL_LINE_LOOP)
BGL.glVertex2i(self.x+20+(i*self.timeDiv),self.y+30)
BGL.glVertex2i(self.x+20+(i*self.timeDiv),self.y+self.sizeY-15)
BGL.glEnd()
for i in range(0,self.noteWidth/12):
BGL.glRasterPos2i(self.x+2,self.y+30+(12*i*self.noteDiv))
Draw.Text(Note.vals[12*i+self.note],"tiny")
BGL.glBegin(GL_LINE_LOOP)
BGL.glVertex2i(self.x+20,self.y+30+(12*i*self.noteDiv))
BGL.glVertex2i(self.x+self.sizeX-5,self.y+30+(12*i*self.noteDiv))
BGL.glEnd()
currTime = (Blender.Get("curtime")-self.framesOffset)/\
Scene.GetCurrent().getRenderingContext().fps
## If visible in this time range, draw frame pointer
if (currTime >= self.time)\
and (currTime <= (self.time + self.timeWidth)):
currTime = self.x+20+((currTime - self.time) * self.timeDiv)
BGL.glColor3f(0.0,0.8,0.0)
BGL.glBegin(GL_LINE_LOOP)
BGL.glVertex2i(int(currTime),self.y+30)
BGL.glVertex2i(int(currTime),self.y+self.sizeY-15)
BGL.glEnd()
BGL.glColor3f(0.3,0.3,0.3)
BGL.glRasterPos2i(self.x+20,self.y+13)
Draw.Text(self.infoStr,"small")
def drawNote(self,note,id):
"""Draw in the displayer stripe related to the note, append selection
block.
Selection block is assigned with identifier id related to this note.
Parameter id should be this note position in the note list.
"""
if ((note.val%128) < self.note)\
or ((note.val%128) >= (self.note + self.noteWidth)):
return
if (note.stopT < (self.time*1000))\
or (note.startT > ((self.time+self.timeWidth)*1000)):
return
BGL.glColor3f(self.colors[note.channel][0],\
self.colors[note.channel][1],self.colors[note.channel][2])
startX = self.x + 20 + (note.startT/1000.0 - self.time) * self.timeDiv
if startX < self.x+20:
startX = self.x+20
stopX = self.x + 20 + (note.stopT/1000.0 - self.time) * self.timeDiv
if stopX > self.x+self.sizeX-5:
stopX = self.x+self.sizeX-5
noteY = self.y + 30 + (note.val%128 - self.note)*self.noteDiv
BGL.glRectf(startX,noteY,stopX,noteY+self.noteDiv)
## If note is selected, add white outline.
if note.isSelected:
BGL.glColor3f(1,1,1)
BGL.glBegin(GL_LINE_LOOP)
BGL.glVertex2f(startX,noteY)
BGL.glVertex2f(stopX,noteY)
BGL.glVertex2f(stopX,noteY+self.noteDiv)
BGL.glVertex2f(startX,noteY+self.noteDiv)
BGL.glEnd()
self.selections.append(StructDisp.SelectBlock(\
startX,noteY,stopX,noteY+self.noteDiv,id))
def drawCross(self):
"""Draw cross marker in the first stage of box selection.
If marker position exceeds displayer bounds, marker will not move
outside the displayer.
"""
if (self.mousePos[0]<(self.x+20)):
self.mousePos[0] = self.x+20
if (self.mousePos[0]>(self.x+self.sizeX-5)):
self.mousePos[0] = self.x+self.sizeX-5
if (self.mousePos[1]<(self.y+30)):
self.mousePos[1] = self.y+30
if (self.mousePos[1]>(self.y+self.sizeY-15)):
self.mousePos[1] = self.y+self.sizeY-15
BGL.glBegin(GL_LINE_LOOP)
BGL.glVertex2f(self.mousePos[0],self.y+30)
BGL.glVertex2f(self.mousePos[0],self.y+self.sizeY-15)
BGL.glEnd()
BGL.glBegin(GL_LINE_LOOP)
BGL.glVertex2f(self.x+20,self.mousePos[1])
BGL.glVertex2f(self.x+self.sizeX-5,self.mousePos[1])
BGL.glEnd()
def drawSelBox(self):
"""Draw boundary for box selection.
Prevent drawing the boundary outside the displayer if current mouse
position exceeds displayer bounds.
"""
if (self.mousePos[0]<(self.x+20)):
self.mousePos[0] = self.x+20
if (self.mousePos[0]>(self.x+self.sizeX-5)):
self.mousePos[0] = self.x+self.sizeX-5
if (self.mousePos[1]<(self.y+30)):
self.mousePos[1] = self.y+30
if (self.mousePos[1]>(self.y+self.sizeY-15)):
self.mousePos[1] = self.y+self.sizeY-15
BGL.glBegin(GL_LINE_LOOP)
BGL.glVertex2f(self.boxStart[0],self.boxStart[1])
BGL.glVertex2f(self.mousePos[0],self.boxStart[1])
BGL.glVertex2f(self.mousePos[0],self.mousePos[1])
BGL.glVertex2f(self.boxStart[0],self.mousePos[1])
BGL.glEnd()
def refresh(self,notes,internal=True):
"""Redraws the viewer and initialises new selection buffer.
Arguments:
notes -- new note list to be assigned and displayed
internal -- if True, allows redraw but locks note list modification
(for private use within the class when notes==None),
otherwise new note list can be assigned together with
component redrawing (for external use)
"""
self.selections = []
BGL.glRecti(self.x,self.y,self.x+self.sizeX,self.y+self.sizeY)
self.drawLines()
if notes or internal:
if not internal:
self.notes = notes
for i in range(0,len(self.notes)):
self.drawNote(self.notes[i],i)
if self.enableBox:
BGL.glColor3f(0.6,0.6,0.6)
if self.boxStart[0] < 0:
self.drawCross()
else:
self.drawSelBox()
BGL.glColor3f(0.75,0.75,0.75)
def getInfo(self,x,y):
"""Get info about the note which selection block is marked with
a pointer x,y.
"""
self.infoStr = ""
for block in reversed(self.selections):
if block.checkSelection(x,y):
self.infoStr = Note.instruments[self.notes[block.id].instr]\
+ " " + Note.vals[self.notes[block.id].val] +", channel "\
+ str(self.notes[block.id].channel+1) + ", start: "\
+ str(self.notes[block.id].startT) +" ms, stop: "\
+ str(self.notes[block.id].stopT) + " ms, velocity: "\
+ str(self.notes[block.id].startVel)
break
def getSelection(self,x,y):
"""Toggles the selection status of the note which selection block is
marked with a pointer x,y.
"""
id = -1
self.getInfo(x,y)
for block in reversed(self.selections):
if block.checkSelection(x,y):
id = block.id
break
if id >= 0:
self.notes[id].isSelected = not self.notes[id].isSelected
else:
self.selectionStatus = True
def getBoxSelection(self,minX,minY,maxX,maxY):
"""Marks all notes, which selection blocks are within this selection
box, as selected.
"""
if minX > maxX:
minX,maxX = maxX,minX
if minY > maxY:
minY,maxY = maxY,minY
for block in self.selections:
if block.checkInBox(minX,minY,maxX,maxY):
self.notes[block.id].isSelected = True
def getMousePos(self):
"""Calculates the mouse position respective to the script window."""
mousePos = Window.GetMouseCoords()
screenCoords = Window.GetScreenInfo(Window.Types["SCRIPT"])
if not screenCoords:
return [-1,-1]
mouseX = mousePos[0] - screenCoords[0]["vertices"][0]
mouseY = mousePos[1] - screenCoords[0]["vertices"][1]
return [mouseX,mouseY]
def mouseHandler(self,evt,val):
"""Handles mouse actions and related keyboard shortcuts within
displayer.
"""
self.mousePos = self.getMousePos()
## Process display movement when middle mouse button is pressed
if self.enableMove:
offset= [self.mousePos[0] - self.prevMousePos[0],\
self.mousePos[1] - self.prevMousePos[1]]
if abs(offset[0]) >= self.timeDiv:
self.time -= (int)(offset[0] / self.timeDiv)
self.prevMousePos[0] = self.mousePos[0]
if self.time < 0:
self.time = 0
Draw.Redraw()
if abs(offset[1]) >= (self.noteDiv*8):
self.note -= 12*(int)(offset[1] / (self.noteDiv*8))
self.prevMousePos[1] = self.mousePos[1]
if self.note < 0:
self.note = 0
if self.note > 84:
self.note = 84
Draw.Redraw()
if (evt == Draw.MIDDLEMOUSE) and (val == 0):
self.enableMove = False
## Do not process mouse action if pointer is outside displayer
if (self.mousePos[0]<(self.x+20))\
or (self.mousePos[0]>(self.x+self.sizeX-5))\
or (self.mousePos[1]<(self.y+30))\
or (self.mousePos[1]>(self.y+self.sizeY-15)):
return
## I Key - selection inversion
if (evt == Draw.IKEY) and (val == 0):
for note in self.notes:
note.isSelected = not note.isSelected
Draw.Redraw()
## A Key - select/deselect all
if (evt == Draw.AKEY) and (val == 0):
for note in self.notes:
note.isSelected = self.selectionStatus
self.selectionStatus = not self.selectionStatus
Draw.Redraw()
## B Key - enter box selection mode
if (evt == Draw.BKEY) and (val == 0):
self.enableBox = not self.enableBox
self.boxStart = [-1,-1]
Draw.Redraw()
## Middle mouse button press - start navigation mode
if (evt == Draw.MIDDLEMOUSE) and (val != 0):
self.enableMove = True
self.offset = [0,0]
self.prevMousePos = self.mousePos
if self.enableBox:
if (evt == Draw.LEFTMOUSE) and (val != 0):
self.boxStart = self.mousePos
Draw.Redraw()
## Right mouse button release - get information about selected note
if (evt == Draw.RIGHTMOUSE) and (val == 0):
if self.notes:
self.getInfo(self.mousePos[0],self.mousePos[1])
Draw.Redraw()
## Left mouse button
if (evt == Draw.LEFTMOUSE) and (val == 0):
if not self.notes:
self.enableBox = False
return
if self.enableBox:
self.enableBox = False
self.getBoxSelection(self.boxStart[0],self.boxStart[1],\
self.mousePos[0],self.mousePos[1])
else:
self.getSelection(self.mousePos[0],self.mousePos[1])
Draw.Redraw()
class Interface:
“””Draw interface and handle interface actions.
Instance variables:
midi -- midi file hodling source note list
strDisp -- MIDI file structure displayer
notesCopy -- working copy, clipboard of the notes list
isLoaded -- True if the file was loaded correctly
ipoTypeVals -- animation entry - Ipo and datablock type value
objNames -- animation entry - object/datablock name
ipoNames -- animation entry - Ipo/action name
leadKeys -- animation entry - number of the Ipo/action leading key frame
leadKetConstr -- maximum value of leadKeys
scaleEnabled -- animation entry - Ipo/Action scaling enabled/disabled
holdEnabled -- animation entry - Ipo/Action leading keyframe duplication
velVals -- animation entry - reference velocity value
relativeEnabled -- animation entry - vertical scalling relative/absolute
pIpoNames -- animation entry - pause Ipo/action name
pScaleEnabled -- animation entry - scaling up of pasue Ipo/action enabled
/disabled
chanEntr -- string for channel selection combo box
instrEntr -- string for instrument selection combo box
noteEntr -- string for note selection combo box
orderCons -- maximum value allowed for order spinners
rangeCons -- maximum value allowed for time range spinners
channEnabled -- channel selection enabled/disabled
instrEnabled -- instrument selection enabled/disabled
notesEnabled -- notes selection enabled/disabled
orderEnabled -- order choice enabled/disabled
rangeEnabled -- range selection enabled/disabled
fChannVal -- last chosen channel value
fInstrVal -- last chosen instrument value
fNoteVal -- last chosen note value
fOrdVal -- last chosen offset and increment value
fRangeVal -- last chosen time range
obNamesEnabled -- if False, datablock name is used instead of Object name
(type sensitive)
initOffset -- the frame distance between the beginning of the animation
and the beginning of a midi sequence
corrInterval -- last value of the maximum allowed interval (used when
invoking correction function)
Interface controls:
ipoTypeCombos -- combo boxes for Ipo/object type selection
objTBoxes -- text boxes for object/datablock name input
ipoTBoxes -- text boxes for Ipo/action name input
lKeySpins -- leading key frame number spinner inputs
scaleToggles -- scaling enabled/disabled toggle buttons
holdToggles -- hold enabled/disabled toggle buttons
velSpins -- reference velocity spinner inputs
velToggles -- relative/absolute vertical scaling toggle buttons
pIpoTBoxes -- text boxes for pause Ipo/action name input
pScaleToggles -- pause action scaling enabled/disabled toggle buttons
syncButton -- button invoking synchronisation function
clearButton -- button invoking cleanup of animation entries
quitButton -- button exitting the script
inOffSpin -- initial offset spinner input
corrButton -- button invocing correction function
obDaButton -- button toggling the obNamesEnabled flag
chanToggle -- button toggling the channEnabled flag
instrToggle -- button toggling the instrEnabled flag
noteToggle -- button toggling the notesEnabled flag
orderToggle -- button toggling the orderEnabled flag
rangeToggle -- button toggling the rangeEnabled flag
chanCombo -- combo box for channel value selection
instrCombo -- combo box for instrument value selection
noteCombo -- combo box for note value selection
offSpin -- offset value spinner input
incSpin -- increment value spinner input
rTopSpin -- maximum time range value spinner input
rDownSpin -- minimum time range value spinner input
copyButton -- button for invoking creation of the new notes list working
copy
clCopyButton -- button for invoking reset of the notes list working copy
Static constants:
E_LOAD,E_CREATE,E_EXIT,E_CHAN_ENABLE,E_INSTR_ENABLE,E_NOTE_ENABLE,
E_OFF_ENABLE,E_RANGE_ENABLE,E_CHAN_CHOICE,E_INSTR_CHOICE,E_NOTE_CHOICE,
E_OFF_CHOICE,E_RANGE_CHOICE,E_ANIM_UPDATE,E_CLEAR_ANIM,E_USE_OB,E_COPY,
E_CLEAR_COPY,E_CORRECT,E_INOFF -- events constants
ipoTypeEntries -- string for tybe selection combo boxes
entrCount -- number of animation entries
Public functions:
__init__
createInterface -- establish script interface
buttonEvents -- handle events of identifier event launched by interface
controls
"""
E_LOAD,E_CREATE,E_EXIT,E_CHAN_ENABLE,E_INSTR_ENABLE,E_NOTE_ENABLE,\
E_OFF_ENABLE,E_RANGE_ENABLE,E_CHAN_CHOICE,E_INSTR_CHOICE,E_NOTE_CHOICE,\
E_OFF_CHOICE,E_RANGE_CHOICE,E_ANIM_UPDATE,E_CLEAR_ANIM,E_USE_OB,E_COPY,\
E_CLEAR_COPY,E_CORRECT,E_INOFF = range(20)
ipoTypeEntries = "AR %x"+str(Common.AR)+"|CA %x"+str(Common.CA)+"|LA %x"\
+str(Common.LA)+"|MA %x"+str(Common.MA)+"|OB %x"+str(Common.OB)\
+"|TE %x"+str(Common.TE)+"|WO %x"+str(Common.WO)
entrCount = 9
def clearAnimSettings(self):
"""Reset all variables related to the animation settings."""
self.ipoTypeVals = [Common.OB] * self.entrCount
self.objNames = [""] * self.entrCount
self.ipoNames = [""] * self.entrCount
self.leadKeys = [1] * self.entrCount
self.leadKeyConstr = [0] * self.entrCount
self.scaleEnabled = [True] * self.entrCount
self.holdEnabled = [False] * self.entrCount
self.velVals = [0] * self.entrCount
self.relativeEnabled = [False] * self.entrCount
self.pIpoNames = [""] * self.entrCount
self.pScaleEnabled = [False] * self.entrCount
def clearMidiSettings(self):
"""Reset all variables related to the MIDI file and note filter."""
self.strDisp.note = 0
self.strDisp.time = 0
self.strDisp.notes = None
self.chanEntr = ""
self.instrEntr = ""
self.noteEntr = ""
self.orderCons = 0
self.rangeCons = 0
self.channEnabled = False
self.instrEnabled = False
self.notesEnabled = False
self.orderEnabled = False
self.rangeEnabled = False
self.fChannVal = 17
self.fInstrVal = 1000
self.fNoteVal = 1000
self.fOrdVal = [0,1]
self.fRangeVal = [0,0]
self.notesCopy = None
self.isLoaded = False
def __init__(self):
"""Constructor."""
self.strDisp = StructDisp(5,270,495,260)
self.clearMidiSettings()
self.clearAnimSettings()
self.ipoTypeCombos = [None] * self.entrCount
self.objTBoxes = [None] * self.entrCount
self.ipoTBoxes = [None] * self.entrCount
self.lKeySpins = [None] * self.entrCount
self.scaleToggles = [None] * self.entrCount
self.holdToggles = [None] * self.entrCount
self.velSpins = [None] * self.entrCount
self.velToggles = [None] * self.entrCount
self.pIpoTBoxes = [None] * self.entrCount
self.pScaleToggles = [None] * self.entrCount
self.obNamesEnabled = True
self.corrInterval = 1
self.initOffset = 0
def list2String(self,srcList,refDict = None):
"""Create combo box string from a given list.
Arguments:
srcList -- list of integer values used as indicies of combo box
entries
refDict -- reference dictionary to translate srcList values into
option names;
if None, options name are equal to the key values
"""
result = ""
for entry in srcList:
if not refDict:
result += str(entry+1) + "%x" + str(entry) + "|"
elif entry in refDict:
result += str(entry)+":"+refDict[entry] + "%x"\
+ str(entry) + "|"
if len(result) > 0:
result = result[:-1]
return result
def updateFilterCombos(self):
"""Updates filter combo boxes strings depending on chosen options.
Option strings will contain only the properties of the currently
selected notes.
"""
channels = []
instruments = []
notes = []
for note in self.notesCopy:
if not note.channel in channels:
channels.append(note.channel)
if self.channEnabled:
if (note.channel == self.fChannVal)\
and (not note.instr in instruments):
instruments.append(note.instr)
else:
if not note.instr in instruments:
instruments.append(note.instr)
channels.sort()
instruments.sort()
if channels and (not (self.fChannVal in channels)):
self.fChannVal = channels[0]
if instruments and (not (self.fInstrVal in instruments)):
self.fInstrVal = instruments[0]
for note in self.notesCopy:
if self.channEnabled and (note.channel == self.fChannVal):
if self.instrEnabled:
if (note.instr == self.fInstrVal)\
and (not note.val in notes):
notes.append(note.val)
else:
if not note.val in notes:
notes.append(note.val)
elif not self.channEnabled:
if self.instrEnabled:
if (note.instr == self.fInstrVal)\
and (not note.val in notes):
notes.append(note.val)
else:
if not note.val in notes:
notes.append(note.val)
notes.sort()
if notes and (not (self.fNoteVal in notes)):
self.fNoteVal = notes[0]
self.chanEntr = self.list2String(channels)
self.instrEntr = self.list2String(instruments,Note.instruments)
self.noteEntr = self.list2String(notes,Note.vals)
def resetLoad(self,_filename):
"""Load MIDI file _filename, reset note filter settings, reset
structure displayer.