# -*- coding: ISO-8859-1 -*- from struct import pack, unpack """ This module contains functions for reading and writing the special data types that a midi file contains. """ """ nibbles are four bits. A byte consists of two nibles. hiBits==0xF0, loBits==0x0F Especially used for setting channel and event in 1. byte of musical midi events """ def getNibbles(byte): """ Returns hi and lo bits in a byte as a tuple >>> getNibbles(142) (8, 14) Asserts byte value in byte range >>> getNibbles(256) Traceback (most recent call last): ... ValueError: Byte value out of range 0-255: 256 """ if not 0 <= byte <= 255: raise ValueError('Byte value out of range 0-255: %s' % byte) return (byte >> 4 & 0xF, byte & 0xF) def setNibbles(hiNibble, loNibble): """ Returns byte with value set according to hi and lo bits Asserts hiNibble and loNibble in range(16) >>> setNibbles(8, 14) 142 >>> setNibbles(8, 16) Traceback (most recent call last): ... ValueError: Nible value out of range 0-15: (8, 16) """ if not (0 <= hiNibble <= 15) or not (0 <= loNibble <= 15): raise ValueError('Nible value out of range 0-15: (%s, %s)' % (hiNibble, loNibble)) return (hiNibble << 4) + loNibble def readBew(value): """ Reads string as big endian word, (asserts len(value) in [1,2,4]) >>> readBew('aáâã') 1642193635L >>> readBew('aá') 25057 """ try: return unpack('>%s' % {1:'B', 2:'H', 4:'L'}[len(value)], value)[0] except KeyError: return 0 def writeBew(value, length): """ Write int as big endian formatted string, (asserts length in [1,2,4]) Difficult to print the result in doctest, so I do a simple roundabout test. >>> readBew(writeBew(25057, 2)) 25057 >>> readBew(writeBew(1642193635L, 4)) 1642193635L """ return pack('>%s' % {1:'B', 2:'H', 4:'L'}[length], value) """ Variable Length Data (varlen) is a data format sprayed liberally throughout a midi file. It can be anywhere from 1 to 4 bytes long. If the 8'th bit is set in a byte another byte follows. The value is stored in the lowest 7 bits of each byte. So max value is 4x7 bits = 28 bits. """ def readVar(value): """ Converts varlength format to integer. Just pass it 0 or more chars that might be a varlen and it will only use the relevant chars. use varLen(readVar(value)) to see how many bytes the integer value takes. asserts len(value) >= 0 >>> readVar('€@') 64 >>> readVar('áâãa') 205042145 """ sum = 0 for byte in unpack('%sB' % len(value), value): sum = (sum << 7) + (byte & 0x7F) if not 0x80 & byte: break # stop after last byte return sum def varLen(value): """ Returns the the number of bytes an integer will be when converted to varlength """ if value <= 127: return 1 elif value <= 16383: return 2 elif value <= 2097151: return 3 else: return 4 def writeVar(value): "Converts an integer to varlength format" sevens = to_n_bits(value, varLen(value)) for i in range(len(sevens)-1): sevens[i] = sevens[i] | 0x80 return fromBytes(sevens) def to_n_bits(value, length=1, nbits=7): "returns the integer value as a sequence of nbits bytes" bytes = [(value >> (i*nbits)) & 0x7F for i in range(length)] bytes.reverse() return bytes def toBytes(value): "Turns a string into a list of byte values" return unpack('%sB' % len(value), value) def fromBytes(value): "Turns a list of bytes into a string" if not value: return '' return pack('%sB' % len(value), *value) if __name__ == '__main__': # print to7bits(0, 3) # print to7bits(127, 3) # print to7bits(255, 3) # print to7bits(65536, 3) # simple test cases # print 'getHiLoHex', getNibbles(16) # print 'setHiLoHex', setNibbles(1,0) # # print 'readBew', readBew('aáâã') # print 'writeBew', writeBew(1642193635, 4) # # print 'varLen', varLen(1) # print 'readVar', readVar('€@') print 'writeVar', writeVar(8192) print 'readVar', readVar('áâãa') print 'writeVar', writeVar(205058401) # # vartest = '\x82\xF7\x80\x00' # print 'toBytes', toBytes(vartest) # print 'fromBytes', fromBytes([48, 49, 50,]) # instr = '\xFF\xFF\xFF\x00' # print 'readVar', readVar(instr) # inst2 = 268435455 # print inst2 # print writeVar(inst2) # print writeVar(readVar(instr)) s1 = 0x00000000 print '%08X -' % s1, '00', writeVar(s1) s2 = 0x00000040 print '%08X -' % s2, '40', writeVar(s2) s3 = 0x0000007F print '%08X -' % s3, '7F', writeVar(s3) s4 = 0x00000080 print '%08X -' % s4, '81 00', writeVar(s4) s5 = 0x00002000 print '%08X -' % s5, 'C0 00', writeVar(s5) s6 = 0x00003FFF print '%08X -' % s6, 'FF 7F', writeVar(s6) s7 = 0x00004000 print '%08X -' % s7, '81 80 00', writeVar(s7) s8 = 0x00100000 print '%08X -' % s8, 'C0 80 00', writeVar(s8) s9 = 0x001FFFFF print '%08X -' % s9, 'FF FF 7F', writeVar(s9) s10 = 0x00200000 print '%08X -' % s10, '81 80 80 00', writeVar(s10) s11 = 0x08000000 print '%08X -' % s11, 'C0 80 80 00', writeVar(s11) s12 = 0x0FFFFFFF print '%08X -' % s12, 'FF FF FF 7F', writeVar(s12)