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
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.
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
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!)
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, 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. ;)
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.
Thanks for following up and posting code. Nice to see.