Notifications
Clear all

[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.

21 Replies
1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

post the script first. and we will see what we can do.

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?

5 Replies
(@christbedoyan)
Joined: 10 months ago

Posts: 0

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 …

(@denist)
Joined: 11 months ago

Posts: 0

could you attach the script or give a link to it for download?

(@christbedoyan)
Joined: 10 months ago

Posts: 0

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

(@denist)
Joined: 11 months ago

Posts: 0

where is the link?

(@christbedoyan)
Joined: 10 months ago

Posts: 0

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.
Page 1 / 2