Skip to main content
Topic: How to correctly fixup unpacked PCM samples? (Read 1650 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

How to correctly fixup unpacked PCM samples?

Hi, I'm very new to the topic of audio de-/compression, so probably the answer           
to my question may be very obvious.                                                      
My aim is to write a very simple wavpack unpacker/decoder in C++ to compute              
AccurateRip checksums directly on wavpack'ed CD rips. It's currently a fun               
project on my own.                                                                       
I started by creating a losslessly encoded .wv file from a 16 bit stereo                 
interleaved low endian PCM RIFF/WAV file (created from a CD with EAC) to have            
test input. My decoder so far reads this wavpack file, verifies it is lossless +         
16bit stereo with 44100 samples/sec and then processes the sequence of unpacked          
samples one by one.                                                                      
Now, my problem: From the .wv file, I need to reconstruct the original sample            
sequence with each unpacked, decoded 16bit stereo PCM sample pair (for left and          
right channel) represented as a uint32_t that contains one 16bit sample in its           
upper and the corresponding other sample in its lower bytes. This is the                 
representation my checksum calculator expects. (It works already for                     
RIFF/WAV-PCM, so I have the checksums of the original file as a reference.)              
I'm afraid I'm stuck with how to proceed with the unpacked int32_t samples in            
the buffer after having successfully called WavpackUnpackSamples().                      
Any help is appreciated. I will be happy to provide any further information              

Re: How to correctly fixup unpacked PCM samples?

Reply #1
Oh, you're going to have so much fun! You know that WavpackUnpackSamples() can return either ints or floats, right? Moving on, I'll assume ints for now.

WavpackUnpackSamples() returns one sample (16 bits in your case) in an int32_t. The 16 bits of audio are packed into the 32-bit integer at the least significant end so each int should have the correct sample value without any bit-shifting. Multi-channel samples are returned consecutively, so stereo would have two int32_t values one after the other, left first.  You will have to shift one of these samples 16 bits to the left and then you can just add it to the other to get your two-channels-in-one-int format. Rinse and repeat.

Re: How to correctly fixup unpacked PCM samples?

Reply #2
Thank you very much, lithopsian, for your insightful clarification!

I corrected my code in the light of your information and was able to produce
the expected results instantly.

For anyone who may be unsure about this, I add a brief description:

I completely missed the crucial point that each unpacked int32_t sample contains
just one original 16 bit sample. I silently supposed (erroneously) a bijection between
original WAV-samples (==32bit) and unpacked samples (==32bit).

This misunderstanding resulted in me respecting only the first half of the unpacked
samples without noticing! (It seemed all innocent, since WavpackGetNumSamples() returned
exactly the same number as the total PCM sample count of the original WAV file. All seemed

The following processing produced the correct results.

(Disclaimer: just demo code for the logic!)

Code: [Select]
uint32_t samples_per_block = // ... number of 32bit samples to read as a block

std::vector<uint32_t> u_samples(samples_per_block);     // the samples I need
std::vector <int32_t> s_samples(samples_per_block * 2); // the samples I get
// The number of unpacked signed samples is samples_per_block * number of channels,
// where the multiplier in my case is 2 for stereo

samples_read = WavpackUnpackSamples(ctx, &s_samples[0], samples_per_block);
// The number of samples requested to be unpacked is the original value of
// samples_per_block, since "sample" in this context means 1 sample per channel
// for each channel. ctx is my WavpackContext pointer.

if (samples_read != current_block_size)
// (...) read not the expected amount, cope with situation or return
// (...) do some sanity checks

// The unpacked samples are now in s_samples. We convert them to their
// intended representation to u_samples.
for (int i = 0; i < u_samples->size(); ++i) // traverse destination samples
u_samples[i]  = (s_samples[i*2+1] & 0x0000FFFF) << 16; // WAV-sample i, add left channel on upper bytes
u_samples[i] |= (s_samples[i*2  ] & 0x0000FFFF);       // WAV-sample i, add right channel on lower bytes

As you pointed out, the left channel has to be shifted to the upper bytes of the
destination sample.  That is probably, what the Wavpack 5 Library Documentation
meant, when hinting me to left-shifting (page 9, explaining WavpackGetBytesPerSample()).
Obviously, this was too subtle for me. ;)

Re: How to correctly fixup unpacked PCM samples?

Reply #3
You know that WavpackUnpackSamples() can return either ints or floats, right?

Yes, I read that in the Library Documentation. I did not try to implement reading float PCM so far. (Because I did not produce such representations so far.) Currently, my class therefore refuses to process the file if MODE_FLOAT is set.

Re: How to correctly fixup unpacked PCM samples?

Reply #4
Thanks for following up and posting code. Nice to see.

SimplePortal 1.0.0 RC1 © 2008-2020