PageRenderTime 29ms CodeModel.GetById 12ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 1ms

/src/echonest/support/midi/DataTypeConverters.py

http://echo-nest-remix.googlecode.com/
Python | 217 lines | 165 code | 11 blank | 41 comment | 10 complexity | 4d51c29891211e9fac996a64988221d7 MD5 | raw file
  1# -*- coding: ISO-8859-1 -*-

  2
  3from struct import pack, unpack
  4
  5"""

  6This module contains functions for reading and writing the special data types

  7that a midi file contains.

  8"""
  9
 10"""

 11nibbles are four bits. A byte consists of two nibles.

 12hiBits==0xF0, loBits==0x0F Especially used for setting

 13channel and event in 1. byte of musical midi events

 14"""
 15
 16
 17
 18def getNibbles(byte):
 19    """

 20    Returns hi and lo bits in a byte as a tuple

 21    >>> getNibbles(142)

 22    (8, 14)

 23    

 24    Asserts byte value in byte range

 25    >>> getNibbles(256)

 26    Traceback (most recent call last):

 27        ...

 28    ValueError: Byte value out of range 0-255: 256

 29    """
 30    if not 0 <= byte <= 255:
 31        raise ValueError('Byte value out of range 0-255: %s' % byte)
 32    return (byte >> 4 & 0xF, byte & 0xF)
 33
 34
 35def setNibbles(hiNibble, loNibble):
 36    """

 37    Returns byte with value set according to hi and lo bits

 38    Asserts hiNibble and loNibble in range(16)

 39    >>> setNibbles(8, 14)

 40    142

 41    

 42    >>> setNibbles(8, 16)

 43    Traceback (most recent call last):

 44        ...

 45    ValueError: Nible value out of range 0-15: (8, 16)

 46    """
 47    if not (0 <= hiNibble <= 15) or not (0 <= loNibble <= 15):
 48        raise ValueError('Nible value out of range 0-15: (%s, %s)' % (hiNibble, loNibble))
 49    return (hiNibble << 4) + loNibble
 50
 51
 52
 53def readBew(value):
 54    """

 55    Reads string as big endian word, (asserts len(value) in [1,2,4])

 56    >>> readBew('a???')

 57    1642193635L

 58    >>> readBew('a?')

 59    25057

 60    """
 61    return unpack('>%s' % {1:'B', 2:'H', 4:'L'}[len(value)], value)[0]
 62
 63
 64def writeBew(value, length):
 65    """

 66    Write int as big endian formatted string, (asserts length in [1,2,4])

 67    Difficult to print the result in doctest, so I do a simple roundabout test.

 68    >>> readBew(writeBew(25057, 2))

 69    25057

 70    >>> readBew(writeBew(1642193635L, 4))

 71    1642193635L

 72    """
 73    return pack('>%s' % {1:'B', 2:'H', 4:'L'}[length], value)
 74
 75
 76
 77"""

 78Variable Length Data (varlen) is a data format sprayed liberally throughout

 79a midi file. It can be anywhere from 1 to 4 bytes long.

 80If the 8'th bit is set in a byte another byte follows. The value is stored

 81in the lowest 7 bits of each byte. So max value is 4x7 bits = 28 bits.

 82"""
 83
 84
 85def readVar(value):
 86    """

 87    Converts varlength format to integer. Just pass it 0 or more chars that

 88    might be a varlen and it will only use the relevant chars.

 89    use varLen(readVar(value)) to see how many bytes the integer value takes.

 90    asserts len(value) >= 0

 91    >>> readVar('?@')

 92    64

 93    >>> readVar('???a')

 94    205042145

 95    """
 96    sum = 0
 97    for byte in unpack('%sB' % len(value), value):
 98        sum = (sum << 7) + (byte & 0x7F)
 99        if not 0x80 & byte: break # stop after last byte

100    return sum
101
102
103
104def varLen(value):
105    """

106    Returns the the number of bytes an integer will be when

107    converted to varlength

108    """
109    if value <= 127:
110        return 1
111    elif value <= 16383:
112        return 2
113    elif value <= 2097151:
114        return 3
115    else:
116        return 4
117
118
119def writeVar(value):
120    "Converts an integer to varlength format"
121    sevens = to_n_bits(value, varLen(value))
122    for i in range(len(sevens)-1):
123        sevens[i] = sevens[i] | 0x80
124    return fromBytes(sevens)
125
126
127def to_n_bits(value, length=1, nbits=7):
128    "returns the integer value as a sequence of nbits bytes"
129    bytes = [(value >> (i*nbits)) & 0x7F for i in range(length)]
130    bytes.reverse()
131    return bytes
132
133
134def toBytes(value):
135    "Turns a string into a list of byte values"
136    return unpack('%sB' % len(value), value)
137
138
139def fromBytes(value):
140    "Turns a list of bytes into a string"
141    if not value:
142        return ''
143    return pack('%sB' % len(value), *value)
144
145
146
147if __name__ == '__main__':
148
149#    print to7bits(0, 3)

150#    print to7bits(127, 3)

151#    print to7bits(255, 3)

152#    print to7bits(65536, 3)

153
154    # simple test cases

155    
156#    print 'getHiLoHex', getNibbles(16)

157#    print 'setHiLoHex', setNibbles(1,0)

158#    

159#    print 'readBew', readBew('a???')

160#    print 'writeBew', writeBew(1642193635, 4)

161#

162#    print 'varLen', varLen(1)

163#

164    print 'readVar', readVar('?@')
165    print 'writeVar', writeVar(8192)
166    
167    print 'readVar', readVar('???a')
168    print 'writeVar', writeVar(205058401)
169#    

170#    vartest = '\x82\xF7\x80\x00'

171#    print 'toBytes', toBytes(vartest)

172#    print 'fromBytes', fromBytes([48, 49, 50,])

173    
174    
175#    instr = '\xFF\xFF\xFF\x00'

176#    print 'readVar', readVar(instr)

177#    inst2 = 268435455

178#    print inst2

179#    print writeVar(inst2)

180#    print writeVar(readVar(instr))

181
182    s1 = 0x00000000
183    print '%08X -' % s1, '00',  writeVar(s1)
184    s2 = 0x00000040
185    print '%08X -' % s2, '40',  writeVar(s2)
186    s3 = 0x0000007F
187    print '%08X -' % s3, '7F',  writeVar(s3)
188    s4 = 0x00000080
189    print '%08X -' % s4, '81 00',  writeVar(s4)
190    s5 = 0x00002000
191    print '%08X -' % s5, 'C0 00',  writeVar(s5)
192    s6 = 0x00003FFF
193    print '%08X -' % s6, 'FF 7F',  writeVar(s6)
194    s7 = 0x00004000
195    print '%08X -' % s7, '81 80 00',  writeVar(s7)
196    s8 = 0x00100000
197    print '%08X -' % s8, 'C0 80 00',  writeVar(s8)
198    s9 = 0x001FFFFF
199    print '%08X -' % s9, 'FF FF 7F',  writeVar(s9)
200    s10 = 0x00200000
201    print '%08X -' % s10, '81 80 80 00', writeVar(s10)
202    s11 = 0x08000000
203    print '%08X -' % s11, 'C0 80 80 00', writeVar(s11)
204    s12 = 0x0FFFFFFF
205    print '%08X -' % s12, 'FF FF FF 7F', writeVar(s12)
206              
207              
208              
209             
210             
211             
212           
213           
214           
215          
216          
217