Part 2 is on decompression, start with Part 1 Extrema Compression (Part 1)
Luckily for us the decompression phase is much simpler than the compression phase. It would have been simpler regardless but
We create a new wave file for the output, we simply need to have a while loop read through the file five bytes at a time, if the first of each 5 is a zero add a silence for as many frames as declared in the remaining 4 bytes.
If the first byte is non-zero 1 to 255, add curve 1-255 stretched to a length of bytes 2&3, and ending on the height encoded in bytes 4&5.
Then pack add write these bytes to the wave output.
Decompression Step by step:
def Decompress(FileIn,WaveOut=None): if WaveOut==None: WaveOut=FileIn+"Dcmp.wav" wavobj = waveopen(WaveOut,'w') wavobj.setnchannels(1) # mono wavobj.setsampwidth(2) wavobj.setframerate(samplerate) magnitude=2**15-1
First we define the function,with two inputs, one of which is optional, and defaults to None.
If WaveOut has defaulted to none, then its replaced with FileIn+”Dcmp.wav”.
Open a new emptywave and set it to a singlechannel, 16bit samples, with a framerate of 44100, like the rest of the project.
Since the Y value, of each sample is only two bytes, the wave need only be PCM16 and the magnitude 2**(8*2-1)-1.
InFile=open(FileIn,"rb") last=(0,0) while 1:
Open the input file.
Declare the end of the last sample as (0,0), this is simply setting the start position of the wave, don’t let the variable name “last” throw you off, it doesn’t make sense here, but it will become apparent why I chose to name it such.
Start the loop:
head=InFile.read(1) tail=InFile.read(4) if head==b"": break curvenum=unpack("B",head)
Read in 1 byte from the file, as the sample’s head, and the other four as its tail. If the file is empty, the head will be an empty byte string and we’re done, so we can break.
But let’s not get ahead of ourselves, unpack head as an unsigned-char named curvenum.
if curvenum==0: x=unpack("L",tail) val=last data=pack("<h",int(val*magnitude)) for i in range(x): wavobj.writeframesraw(data)
As we know by now, if curve num is 0, it’s not a curve at all, but silence, simply take the end height of the last curve (last) multiplied by the wave’s magnitude, pack it and then write it out to the wave for as many frames as declared in the tail, of course we must first unpack the tail into an unsigned-long (called x in the example)
else: x,y=unpack("Hh",tail) y/=magnitude c=Curves.Curves[curvenum-1] h=y-last
Otherwise, if the head is any number other than 0, it means a curve is to be placed,
beginning on last, and (ending on last+x,y).
First unpack the tail, as a signed-short, followed by a an unsigned-short.
Assign these values to x and y respectively.
Divide y by the magnitude, to normalise the input so all values lie between -1 and 1.
Now load the curve. (We must deduct 1 from the curve num, because python lists are zero-indexed, whereas the curves in our file are 1-indexed (0 is silence instead)
Declare the height (named h) as the end position, minus the start position.
for i in range(x): r=i/x val=last+c[int(r*1024)]*h data=pack("<h",int(val*magnitude)) wavobj.writeframesraw(data) last=(last+x,y) #resizecurve return 1
loop i for the the length of the curve “x”.
set r to i/x (so it’s 0 at the beginning of the curve, and 1 at the end)
The current value is curve[int(r*1024)] multiplied by the height, meaning we stretch the 1024 frame curve into x frames, and multiply the value of each point by the height of the curve. Multiply the value by the desired output magnitude ( 2**(8*(bytes-1))-1 for signed)
and write the packed data to the wave.
Once you’ve written in the entire curve, update last to (last+x,y)
When done return 1 for success.