ICE, sound and MIDI files
-
Bullit
- Moderator
- Posts: 2621
- Joined: 24 May 2012, 07:44
ICE, sound and MIDI files
There are some applications that make animations from MIDI files, example http://www.musanim.com/player/
I wonder if there is one that uses an animated virtual keyboard? To connect via texture map to ICE or if there are better alternatives. Ideally would be to connect a key to a sound but i don't think that is possible.
I wonder if there is one that uses an animated virtual keyboard? To connect via texture map to ICE or if there are better alternatives. Ideally would be to connect a key to a sound but i don't think that is possible.
-
Letterbox
- Posts: 391
- Joined: 17 Jun 2009, 12:49
Re: ICE, sound and MIDI files
There was something old on xsibase, dont know if archive.org will have it.
Best solution, take a look at ... http://www.sidefx.com/index.php?option= ... &Itemid=66
It might inspire you...or others.
Best solution, take a look at ... http://www.sidefx.com/index.php?option= ... &Itemid=66
It might inspire you...or others.
-
Bullit
- Moderator
- Posts: 2621
- Joined: 24 May 2012, 07:44
Re: ICE, sound and MIDI files
Thanks for the feedback. It seems an interesting book.
-
rray
- Moderator
- Posts: 1810
- Joined: 26 Sep 2009, 13:51
- Location: Bonn, Germany
Re: ICE, sound and MIDI files
softimage resources section updated Sep 26th 2026
-
dwigfor
- Posts: 395
- Joined: 17 Nov 2009, 16:46
Re: ICE, sound and MIDI files
Interesting approach, using the piano roll as a texturemap. I wonder how huge that image is!! Thanks for the website - I'll have to take a look at the other tutorials he's got on there!
Thanks for the link to the Houdini ebook.. I'm tempted on buying this, cause I've been interested in this for a while.
Anyone else have midi instruments? I've got a really old Roland D5 synth (no power adapter), a really old Roland MT32 sound module (bought that for Sierra Online games!), an old StudioLogic 88 controller (again, no power adapter), a Roland JV 2080 sound module, Peavey 1600 controller (bought for XSI), MOTU 4x4 interface, and Roland Fantom G8 (my baby!). Recently bought an Akai pad controller for Ableton, but I haven't had much time to play with that..
Thanks for the link to the Houdini ebook.. I'm tempted on buying this, cause I've been interested in this for a while.
Anyone else have midi instruments? I've got a really old Roland D5 synth (no power adapter), a really old Roland MT32 sound module (bought that for Sierra Online games!), an old StudioLogic 88 controller (again, no power adapter), a Roland JV 2080 sound module, Peavey 1600 controller (bought for XSI), MOTU 4x4 interface, and Roland Fantom G8 (my baby!). Recently bought an Akai pad controller for Ableton, but I haven't had much time to play with that..
-
Falam
Re: ICE, sound and MIDI files
You a music man ?dwigfor wrote:Interesting approach, using the piano roll as a texturemap. I wonder how huge that image is!! Thanks for the website - I'll have to take a look at the other tutorials he's got on there!
Thanks for the link to the Houdini ebook.. I'm tempted on buying this, cause I've been interested in this for a while.
Anyone else have midi instruments? I've got a really old Roland D5 synth (no power adapter), a really old Roland MT32 sound module (bought that for Sierra Online games!), an old StudioLogic 88 controller (again, no power adapter), a Roland JV 2080 sound module, Peavey 1600 controller (bought for XSI), MOTU 4x4 interface, and Roland Fantom G8 (my baby!). Recently bought an Akai pad controller for Ableton, but I haven't had much time to play with that..
-
dwigfor
- Posts: 395
- Joined: 17 Nov 2009, 16:46
Re: ICE, sound and MIDI files
Just a fun hobby on the side. 
-
almoehi
- Posts: 1
- Joined: 31 Aug 2012, 13:35
Re: ICE, sound and MIDI files
check this out:
You can download it here:
3d-pixel.de/softimage/sound/sound.zip
Helpfile is here:
3d-pixel.de/softimage/sound/
Or you click on the ? in the PPG
-
Letterbox
- Posts: 391
- Joined: 17 Jun 2009, 12:49
Re: ICE, sound and MIDI files
dwigfor wrote:Interesting approach, using the piano roll as a texturemap. I wonder how huge that image is!!
Thanks for the link to the Houdini ebook.. I'm tempted on buying this, cause I've been interested in this for a while.
Since you've been interested for a while you might consider this alternative approach.
Realize that both sets of key's per note are uniform from an origin, as each key of the same width. Both sets (b/w) follow a physically consistent layout, for all keyboards. Further that each key (on a piano) rotates a fixed limited amount around a pivot where the key connects to lever to the hammer. Thus, no textures, no shapes is a possibility, as long as one realizes this when modeling the item, and it follows a physically correct layout.
Calculating a note's position now becomes effectively translating it's tonal value into moving a physical distance from one end of the keyboard to the required key (for both b/w). Which I believe is what your brain first does when you learn the piano, until the note's position is remembered. Which could translate programmatically into midi_note/object00... etc. Then simply rotating that key (object00) around it's (limited) pivot for the duration of the note. The same principle could be used too for the pedals, if the midi file contains that pedal data (depends on source). The duration of the note is down is too in the midi file (depends on source). In theory it should make the simulated ice tree much simpler.
I suppose too you could build they keyboard with ice modeling, procedurally creating the layout because of it's uniformity, which would aid in the integration and selection, as then you'd (maybe more easily) be able to use the midi note value into an object / Name / number, ie Key01, Key02 mapping table/array.
Further you'd probably need a python script to read the midi data set (which exist) and save it in an ice array for this mapping the simulation stack. If your not sure about that process it's a similar methodology to reading a csv file via python, which is explained somewhere - look in the usual places.
PS I hope you find some power supplies !! Good Luck.
-
dwigfor
- Posts: 395
- Joined: 17 Nov 2009, 16:46
Re: ICE, sound and MIDI files
I might try that. Sounds like a fun achievable project.Letterbox wrote: I suppose too you could build they keyboard with ice modeling, procedurally creating the layout because of it's uniformity
Yeah, some way to translate standard midi file into a readable format, like xml. And then to somehow load that into SI/ICE. That's where I'm stuck... I did find a python midi project a couple of years ago when I was taking a python class at work, but after sending it to the teacher/coworker, I never heard anything back. Assumed he was busy with other stuff, and uninterested in midi.Letterbox wrote: Further you'd probably need a python script to read the midi data set (which exist) and save it in an ice array for this mapping the simulation stack. If your not sure about that process it's a similar methodology to reading a csv file via python, which is explained somewhere - look in the usual places.
I hate those things!Letterbox wrote: PS I hope you find some power supplies !!
-
Bullit
- Moderator
- Posts: 2621
- Joined: 24 May 2012, 07:44
Re: ICE, sound and MIDI files
Aha! That is why i asked for an application that read midi to animated keyboard. He made a sequence of images in Propellerhead Reason via screen capture. Seems too convoluted.
dwigfor i found this when i was searching, maybe it will help you:
He has some python files here:
http://www.logicalside.com/MIDI_based_animation_3D
He also made an animation in Swish.
-
dwigfor
- Posts: 395
- Joined: 17 Nov 2009, 16:46
Re: ICE, sound and MIDI files
Thanks for the link, Bullit. Here's the contents of the included midiparser.py file, if it could help someone bringing midi into SI.
http://www.logicalside.com/sites/defaul ... 20Maya.zip
http://www.logicalside.com/sites/defaul ... 20Maya.zip
Code: Select all
# Placed into Public Domain in June 2006 by Sean D. Spencer
# Sean D. Spencer
# sean_don4@lycos.com
# 2/19/2006
# Last Revision: 4/19/2007
# MIDI Parsing Library for Python.
import sys
TRUE = -1
FALSE = 0
class format:
SingleTrack = 0
MultipleTracksSync = 1
MultipleTracksAsync = 2
class voice:
NoteOff = 0x80
NoteOn = 0x90
PolyphonicKeyPressure = 0xA0 # note aftertouch
ControllerChange = 0xB0
ProgramChange = 0xC0
ChannelPressure = 0xD0
PitchBend = 0xE0
class meta:
FileMetaEvent = 0xFF
SMPTEOffsetMetaEvent = 0x54
SystemExclusive = 0xF0
SystemExclusivePacket = 0xF7
SequenceNumber = 0x00
TextMetaEvent = 0x01
CopyrightMetaEvent = 0x02
TrackName = 0x03
InstrumentName = 0x04
Lyric = 0x05
Marker = 0x06
CuePoint = 0x07
ChannelPrefix = 0x20
MidiPort = 0x21
EndTrack = 0x2F
SetTempo = 0x51
TimeSignature = 0x58
KeySignature = 0x59
SequencerSpecificMetaEvent = 0x7F
class EventNote:
def __init__(self):
self.note_no = None
self.velocity = None
class EventValue:
def __init__(self):
self.type = None
self.value = None
class EventAmount:
def __init__(self):
self.amount = None
class MetaEventKeySignature:
def __init__(self):
self.fifths = None
self.mode = None
class MetaEventTimeSignature:
def __init__(self):
self.numerator = None
self.log_denominator = None
self.midi_clocks = None
self.thirty_seconds = None
class MetaEventText:
def __init__(self):
self.length = None
self.text = None
class MetaEventSMPTEOffset:
def __init__(self):
self.hour = None
self.minute = None
self.second = None
self.frame = None
self.sub_frame = None
class MetaValues:
def __init__(self):
self.length = None
self.values = None
def getNumber(str, length):
# MIDI uses big-endian for everything
sum = 0
for i in range(length):
#sum = (sum *256) + int(str[i])
sum = (sum << 8) + ord(str[i])
return sum, str[length:]
def getVariableLengthNumber(str):
sum = 0
i = 0
while 1:
x = ord(str[i])
i = i + 1
# sum = (sum * 127) + (x (mask) 127) # mask off the 7th bit
sum = (sum << 7) + (x & 0x7F)
# Is 7th bit clear?
if not (x & 0x80):
return sum, str[i:]
def getValues(str, n=16):
temp = []
for x in str[:n]:
temp.append(repr(ord(x)))
return temp
class File:
def __init__(self, file):
self.file = file
self.format = None
self.num_tracks = None
self.division = None
self.tracks = []
self.file = open(self.file, 'rb')
str = self.file.read()
self.file.close()
self.read(str)
def read(self, str):
assert str[:4] == "MThd"
str = str[4:]
length, str = getNumber(str, 4)
assert length == 6
self.format, str = getNumber(str, 2)
self.num_tracks, str = getNumber(str, 2)
self.division, str = getNumber(str, 2)
for i in range(self.num_tracks):
track = Track(i+1)
str = track.read(str)
self.tracks.append(track)
class Track:
def __init__(self, index):
self.number = index
self.length = None
self.events = []
def read(self, str):
self.length, str = getNumber(str[4:], 4)
track_str = str[:self.length]
prev_absolute = 0
prev_status = 0
i = 0
while track_str:
event = Event(self.number, i+1)
track_str = event.read(prev_absolute, prev_status, track_str)
prev_absolute += event.delta
prev_status = event.status
self.events.append(event)
i += 1
return str[self.length:]
class Event:
def __init__(self, track, index):
self.number = index
self.type = None
self.delta = None
self.absolute = None
self.status = None
self.channel = None
def read(self, prev_time, prev_status, str):
self.delta, str = getVariableLengthNumber(str)
self.absolute = prev_time + self.delta
# use running status?
if not (ord(str[0]) & 0x80):
# squeeze a duplication of the running status into the data string
str = prev_status + str
self.status = str[0]
self.channel = ord(self.status) & 0xF
# increment one byte, past the status
str = str[1:]
has_channel = has_meta = TRUE
# handle voice events
channel_msg = ord(self.status) & 0xF0
if channel_msg == voice.NoteOn or \
channel_msg == voice.NoteOff or \
channel_msg == voice.PolyphonicKeyPressure:
self.detail = EventNote()
self.detail.note_no = ord(str[0])
self.detail.velocity = ord(str[1])
str = str[2:]
elif channel_msg == voice.ControllerChange:
self.detail = EventValue()
self.detail.type = ord(str[0])
self.detail.value = ord(str[1])
str = str[2:]
elif channel_msg == voice.ProgramChange or \
channel_msg == voice.ChannelPressure:
self.detail = EventAmount()
self.detail.amount = ord(str[0])
str = str[1:]
elif channel_msg == voice.PitchBend:
# Pitch bend uses high accuracy 14 bit unsigned integer.
self.detail = EventAmount()
self.detail.amount = (ord(str[0]) << 7) | ord(str[1])
str = str[2:]
else: has_channel = FALSE
# handle meta events
meta_msg = ord(self.status)
if meta_msg == meta.FileMetaEvent:
meta_msg = type = ord(str[0])
length, str = getVariableLengthNumber(str[1:])
if type == meta.SetTempo or \
type == meta.ChannelPrefix:
self.detail = EventAmount()
self.detail.tempo, str = getNumber(str, length)
elif type == meta.KeySignature:
self.detail = MetaEventKeySignature()
self.detail.fifths = ord(str[0])
if ord(str[1]): self.detail.mode = "minor"
else: self.detail.mode = "major"
str = str[length:]
elif type == meta.TimeSignature:
self.detail = MetaEventTimeSignature()
self.detail.numerator = ord(str[0])
self.detail.log_denominator = ord(str[1])
self.detail.midi_clocks = ord(str[2])
self.detail.thirty_seconds = ord(str[3])
str = str[length:]
elif type == meta.TrackName or \
type == meta.TextMetaEvent or \
type == meta.Lyric or \
type == meta.CuePoint or \
type == meta.CopyrightMetaEvent:
self.detail = MetaEventText()
self.detail.length = length
self.detail.text = str[:length]
str = str[length:]
elif type == meta.SMPTEOffsetMetaEvent:
self.detail = MetaEventSMPTEOffset()
self.detail.hour = ord(str[0])
self.detail.minute = ord(str[1])
self.detail.second = ord(str[2])
self.detail.frame = ord(str[3])
self.detail.sub_frame = ord(str[4])
str = str[length:]
elif type == meta.EndTrack:
str = str[length:] # pass on to next track
else: has_meta = FALSE
elif meta_msg == meta.SystemExclusive or \
meta_msg == meta.SystemExclusivePacket:
self.detail = MetaValues()
self.detail.length, str = getVariableLengthNumber(str)
self.detail.values = getValues(str, self.detail.length)
str = str[self.detail.length:]
else: has_meta = FALSE
if has_channel:
self.type = channel_msg
elif has_meta:
self.type = meta_msg
else:
raise "Unknown event."
return str
-
dwigfor
- Posts: 395
- Joined: 17 Nov 2009, 16:46
Re: ICE, sound and MIDI files
I found another script to parse a midi file with Python. I got it to output into the cmd prompt window, but I'm not sure how to use that in SI.
First off, could someone explain how to save the output into a .txt file?
To display contents, you can type in cmd prompt: c:\python27\python -p -d -i twentieth_century_fox.mid
(upload wouldn't accept .mid files, so you can get example midi file @ http://files.ifnimidi.com/rock/doors/Tw ... ry_Fox.mid
First off, could someone explain how to save the output into a .txt file?
Code: Select all
#!/usr/bin/env python
"""
midi.py -- MIDI classes and parser in Python
Placed into the public domain in December 2001 by Will Ware
Python MIDI classes: meaningful data structures that represent MIDI
events
and other objects. You can read MIDI files to create such objects, or
generate a collection of objects and use them to write a MIDI file.
Helpful MIDI info:
http://crystal.apana.org.au/ghansper/midi_introduction/midi_file_format.html
http://www.argonet.co.uk/users/lenny/midi/mfile.html
"""
import sys, string, types, exceptions
debugflag = 0
def showstr(str, n=16):
for x in str[:n]:
print ('%02x' % ord(x)),
print
def getNumber(str, length):
# MIDI uses big-endian for everything
sum = 0
for i in range(length):
sum = (sum << 8) + ord(str[i])
return sum, str[length:]
def getVariableLengthNumber(str):
sum = 0
i = 0
while 1:
x = ord(str[i])
i = i + 1
sum = (sum << 7) + (x & 0x7F)
if not (x & 0x80):
return sum, str[i:]
def putNumber(num, length):
# MIDI uses big-endian for everything
lst = [ ]
for i in range(length):
n = 8 * (length - 1 - i)
lst.append(chr((num >> n) & 0xFF))
return string.join(lst, "")
def putVariableLengthNumber(x):
lst = [ ]
while 1:
y, x = x & 0x7F, x >> 7
lst.append(chr(y + 0x80))
if x == 0:
break
lst.reverse()
lst[-1] = chr(ord(lst[-1]) & 0x7f)
return string.join(lst, "")
class EnumException(exceptions.Exception):
pass
class Enumeration:
def __init__(self, enumList):
lookup = { }
reverseLookup = { }
i = 0
uniqueNames = [ ]
uniqueValues = [ ]
for x in enumList:
if type(x) == types.TupleType:
x, i = x
if type(x) != types.StringType:
raise EnumException, "enum name is not a string: " + x
if type(i) != types.IntType:
raise EnumException, "enum value is not an integer: " + i
if x in uniqueNames:
raise EnumException, "enum name is not unique: " + x
if i in uniqueValues:
raise EnumException, "enum value is not unique for " + x
uniqueNames.append(x)
uniqueValues.append(i)
lookup[x] = i
reverseLookup[i] = x
i = i + 1
self.lookup = lookup
self.reverseLookup = reverseLookup
def __add__(self, other):
lst = [ ]
for k in self.lookup.keys():
lst.append((k, self.lookup[k]))
for k in other.lookup.keys():
lst.append((k, other.lookup[k]))
return Enumeration(lst)
def hasattr(self, attr):
return self.lookup.has_key(attr)
def has_value(self, attr):
return self.reverseLookup.has_key(attr)
def __getattr__(self, attr):
if not self.lookup.has_key(attr):
raise AttributeError
return self.lookup[attr]
def whatis(self, value):
return self.reverseLookup[value]
channelVoiceMessages = Enumeration([("NOTE_OFF", 0x80),
("NOTE_ON", 0x90),
("POLYPHONIC_KEY_PRESSURE", 0xA0),
("CONTROLLER_CHANGE", 0xB0),
("PROGRAM_CHANGE", 0xC0),
("CHANNEL_KEY_PRESSURE", 0xD0),
("PITCH_BEND", 0xE0)])
channelModeMessages = Enumeration([("ALL_SOUND_OFF", 0x78),
("RESET_ALL_CONTROLLERS", 0x79),
("LOCAL_CONTROL", 0x7A),
("ALL_NOTES_OFF", 0x7B),
("OMNI_MODE_OFF", 0x7C),
("OMNI_MODE_ON", 0x7D),
("MONO_MODE_ON", 0x7E),
("POLY_MODE_ON", 0x7F)])
metaEvents = Enumeration([("SEQUENCE_NUMBER", 0x00),
("TEXT_EVENT", 0x01),
("COPYRIGHT_NOTICE", 0x02),
("SEQUENCE_TRACK_NAME", 0x03),
("INSTRUMENT_NAME", 0x04),
("LYRIC", 0x05),
("MARKER", 0x06),
("CUE_POINT", 0x07),
("MIDI_CHANNEL_PREFIX", 0x20),
("MIDI_PORT", 0x21),
("END_OF_TRACK", 0x2F),
("SET_TEMPO", 0x51),
("SMTPE_OFFSET", 0x54),
("TIME_SIGNATURE", 0x58),
("KEY_SIGNATURE", 0x59),
("SEQUENCER_SPECIFIC_META_EVENT", 0x7F)])
# runningStatus appears to want to be an attribute of a MidiTrack. But
# it doesn't seem to do any harm to implement it as a global.
runningStatus = None
class MidiEvent:
def __init__(self, track):
self.track = track
self.time = None
self.channel = self.pitch = self.velocity = self.data = None
def __cmp__(self, other):
# assert self.time != None and other.time != None
return cmp(self.time, other.time)
def __repr__(self):
r = ("<MidiEvent %s, t=%s, track=%s, channel=%s" %
(self.type,
repr(self.time),
self.track.index,
repr(self.channel)))
for attrib in ["pitch", "data", "velocity"]:
if getattr(self, attrib) != None:
r = r + ", " + attrib + "=" + repr(getattr(self,
attrib))
return r + ">"
def read(self, time, str):
global runningStatus
self.time = time
# do we need to use running status?
if not (ord(str[0]) & 0x80):
str = runningStatus + str
runningStatus = x = str[0]
x = ord(x)
y = x & 0xF0
z = ord(str[1])
if channelVoiceMessages.has_value(y):
self.channel = (x & 0x0F) + 1
self.type = channelVoiceMessages.whatis(y)
if (self.type == "PROGRAM_CHANGE" or
self.type == "CHANNEL_KEY_PRESSURE"):
self.data = z
return str[2:]
else:
self.pitch = z
self.velocity = ord(str[2])
channel = self.track.channels[self.channel - 1]
if (self.type == "NOTE_OFF" or
(self.velocity == 0 and self.type == "NOTE_ON")):
channel.noteOff(self.pitch, self.time)
elif self.type == "NOTE_ON":
channel.noteOn(self.pitch, self.time, self.velocity)
return str[3:]
elif y == 0xB0 and channelModeMessages.has_value(z):
self.channel = (x & 0x0F) + 1
self.type = channelModeMessages.whatis(z)
if self.type == "LOCAL_CONTROL":
self.data = (ord(str[2]) == 0x7F)
elif self.type == "MONO_MODE_ON":
self.data = ord(str[2])
return str[3:]
elif x == 0xF0 or x == 0xF7:
self.type = {0xF0: "F0_SYSEX_EVENT",
0xF7: "F7_SYSEX_EVENT"}[x]
length, str = getVariableLengthNumber(str[1:])
self.data = str[:length]
return str[length:]
elif x == 0xFF:
if not metaEvents.has_value(z):
print "Unknown meta event: FF %02X" % z
sys.stdout.flush()
raise "Unknown midi event type"
self.type = metaEvents.whatis(z)
length, str = getVariableLengthNumber(str[2:])
self.data = str[:length]
return str[length:]
raise "Unknown midi event type"
def write(self):
sysex_event_dict = {"F0_SYSEX_EVENT": 0xF0,
"F7_SYSEX_EVENT": 0xF7}
if channelVoiceMessages.hasattr(self.type):
x = chr((self.channel - 1) +
getattr(channelVoiceMessages, self.type))
if (self.type != "PROGRAM_CHANGE" and
self.type != "CHANNEL_KEY_PRESSURE"):
data = chr(self.pitch) + chr(self.velocity)
else:
data = chr(self.data)
return x + data
elif channelModeMessages.hasattr(self.type):
x = getattr(channelModeMessages, self.type)
x = (chr(0xB0 + (self.channel - 1)) +
chr(x) +
chr(self.data))
return x
elif sysex_event_dict.has_key(self.type):
str = chr(sysex_event_dict[self.type])
str = str + putVariableLengthNumber(len(self.data))
return str + self.data
elif metaEvents.hasattr(self.type):
str = chr(0xFF) + chr(getattr(metaEvents, self.type))
str = str + putVariableLengthNumber(len(self.data))
return str + self.data
else:
raise "unknown midi event type: " + self.type
"""
register_note() is a hook that can be overloaded from a script that
imports this module. Here is how you might do that, if you wanted to
store the notes as tuples in a list. Including the distinction
between track and channel offers more flexibility in assigning voices.
import midi
notelist = [ ]
def register_note(t, c, p, v, t1, t2):
notelist.append((t, c, p, v, t1, t2))
midi.register_note = register_note
"""
def register_note(track_index, channel_index, pitch, velocity,
keyDownTime, keyUpTime):
pass
class MidiChannel:
"""A channel (together with a track) provides the continuity
connecting
a NOTE_ON event with its corresponding NOTE_OFF event. Together,
those
define the beginning and ending times for a Note."""
def __init__(self, track, index):
self.index = index
self.track = track
self.pitches = { }
def __repr__(self):
return "<MIDI channel %d>" % self.index
def noteOn(self, pitch, time, velocity):
self.pitches[pitch] = (time, velocity)
def noteOff(self, pitch, time):
if self.pitches.has_key(pitch):
keyDownTime, velocity = self.pitches[pitch]
register_note(self.track.index, self.index, pitch, velocity,
keyDownTime, time)
del self.pitches[pitch]
# The case where the pitch isn't in the dictionary is illegal,
# I think, but we probably better just ignore it.
class DeltaTime(MidiEvent):
type = "DeltaTime"
def read(self, oldstr):
self.time, newstr = getVariableLengthNumber(oldstr)
return self.time, newstr
def write(self):
str = putVariableLengthNumber(self.time)
return str
class MidiTrack:
def __init__(self, index):
self.index = index
self.events = [ ]
self.channels = [ ]
self.length = 0
for i in range(16):
self.channels.append(MidiChannel(self, i+1))
def read(self, str):
time = 0
assert str[:4] == "MTrk"
length, str = getNumber(str[4:], 4)
self.length = length
mystr = str[:length]
remainder = str[length:]
while mystr:
delta_t = DeltaTime(self)
dt, mystr = delta_t.read(mystr)
time = time + dt
self.events.append(delta_t)
e = MidiEvent(self)
mystr = e.read(time, mystr)
self.events.append(e)
return remainder
def write(self):
time = self.events[0].time
# build str using MidiEvents
str = ""
for e in self.events:
str = str + e.write()
return "MTrk" + putNumber(len(str), 4) + str
def __repr__(self):
r = "<MidiTrack %d -- %d events\n" % (self.index,
len(self.events))
for e in self.events:
r = r + " " + `e` + "\n"
return r + " >"
class MidiFile:
def __init__(self):
self.file = None
self.format = 1
self.tracks = [ ]
self.ticksPerQuarterNote = None
self.ticksPerSecond = None
def open(self, filename, attrib="rb"):
if filename == None:
if attrib in ["r", "rb"]:
self.file = sys.stdin
else:
self.file = sys.stdout
else:
self.file = open(filename, attrib)
def __repr__(self):
r = "<MidiFile %d tracks\n" % len(self.tracks)
for t in self.tracks:
r = r + " " + `t` + "\n"
return r + ">"
def close(self):
self.file.close()
def read(self):
self.readstr(self.file.read())
def readstr(self, str):
assert str[:4] == "MThd"
length, str = getNumber(str[4:], 4)
assert length == 6
format, str = getNumber(str, 2)
self.format = format
assert format == 0 or format == 1 # dunno how to handle 2
numTracks, str = getNumber(str, 2)
division, str = getNumber(str, 2)
if division & 0x8000:
framesPerSecond = -((division >> 8) | -128)
ticksPerFrame = division & 0xFF
assert ticksPerFrame == 24 or ticksPerFrame == 25 or \
ticksPerFrame == 29 or ticksPerFrame == 30
if ticksPerFrame == 29: ticksPerFrame = 30 # drop frame
self.ticksPerSecond = ticksPerFrame * framesPerSecond
else:
self.ticksPerQuarterNote = division & 0x7FFF
for i in range(numTracks):
trk = MidiTrack(i)
str = trk.read(str)
self.tracks.append(trk)
def write(self):
self.file.write(self.writestr())
def writestr(self):
division = self.ticksPerQuarterNote
# Don't handle ticksPerSecond yet, too confusing
assert (division & 0x8000) == 0
str = "MThd" + putNumber(6, 4) + putNumber(self.format, 2)
str = str + putNumber(len(self.tracks), 2)
str = str + putNumber(division, 2)
for trk in self.tracks:
str = str + trk.write()
return str
def main(argv):
global debugflag
import getopt
infile = None
outfile = None
printflag = 0
optlist, args = getopt.getopt(argv[1:], "i:o:pd")
for (option, value) in optlist:
if option == '-i':
infile = value
elif option == '-o':
outfile = value
elif option == '-p':
printflag = 1
elif option == '-d':
debugflag = 1
m = MidiFile()
m.open(infile)
m.read()
m.close()
if printflag:
print m
else:
m.open(outfile, "wb")
m.write()
m.close()
if __name__ == "__main__":
main(sys.argv)
(upload wouldn't accept .mid files, so you can get example midi file @ http://files.ifnimidi.com/rock/doors/Tw ... ry_Fox.mid
-
Stoecklem
- Posts: 10
- Joined: 12 Apr 2013, 04:53
Re: ICE, sound and MIDI files
This is interesting. I imagine with midi and ice talking directly there are some cool things you could do that I don't understand yet. But the built in realtime midi-in support through the device driver is excellent. One of my favorite things about softimage