/src/echonest/support/midi/DataTypeConverters.py
http://echo-nest-remix.googlecode.com/ · Python · 217 lines · 165 code · 11 blank · 41 comment · 5 complexity · 4d51c29891211e9fac996a64988221d7 MD5 · raw file
- # -*- 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
- """
- return unpack('>%s' % {1:'B', 2:'H', 4:'L'}[len(value)], value)[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)