Fast 12 bit to 16 bit sample conversion?

Mark Lawrence breamoreboy at yahoo.co.uk
Mon Jul 20 22:45:56 EDT 2015


On 20/07/2015 14:10, Peter Heitzer wrote:
> I am currently writing a python script to extract samples from old Roland 12 bit sample
> disks and save them as 16 bit wav files.
>
> The samples are layouted as follows
>
> 0 [S0 bit 11..4] [S0 bit 3..0|S1 bit 3..0] [S1 bit 11..4]
> 3 [S2 bit 11..4] [S2 bit 3..0|S3 bit 3..0] [S3 bit 11..4]
>
> In other words
> sample0=(data[0]<<4)|(data[1]>>4)
> sample1=(data[2]<<4)|(data[1] & 0x0f)
>
> I use this code for the conversion (using the struct module)
>
> import struct
> from array import array
>
> def getWaveData(diskBuffer):
>    offset=0
>    words=array('H')
>    for i in range(len(diskBuffer)/3):
>      h0=struct.unpack_from('>h',diskBuffer,offset)
>      h1=struct.unpack_from('<h',diskBuffer,offset+1)
>      words.append(h0[0] & 0xfff0)
>      words.append(h1[0] & 0xfff0)
>      offset+=3
>    return words
>
> I unpack the samples in an array of unsigned shorts for I later can use the byteswap() method
> if the code is running on a big endian machine.
>
> What options using pure python do I have to make the conversion faster?

By "pure python" I'm assuming you mean part of the stdlib.

Referring to https://wiki.python.org/moin/PythonSpeed/PerformanceTips 
you could end with something like this (untested).

def getWaveData(diskBuffer):
     offset = 0
     words = array('H')
     wx = words.extend #saves two lookups and a function call
     su = struct.unpack_from #saves two lookups
     # 'i' not used in the loop so throw it away
     for _ in range(len(diskBuffer)/3): # use xrange on Python 2
         h0 = su('>h',diskBuffer,offset)
         h1 = su('<h',diskBuffer,offset+1)
         wx((h0[0] & 0xfff0), (h1[0] & 0xfff0)) # MRAB pointed out a 
problem with the masking in the second section???
         offset += 3
     return words

> I thought of unpacking more bytes at once e.g. using a format '>hxhxhxhx' for 4 even samples
> and '<xhxhxhxh' for 4 odd samples vice versa.

If that reduces the number of times around the loop why not?  Combine it 
with MRAB's suggestion of lookups and I'd guess you'd get a speedup, but 
knowing Python I'm probably way out on that?  There's only one way to 
find out.

I'm also thinking that you could user one of the itertools functions or 
recipes to grab the data and hence simplify the loop even more, but it's 
now 3:45 BST, so I can't think straight, hence bed.

> Can I map the '& 0xfff0' to the whole array?

If it works :)

-- 
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence




More information about the Python-list mailing list