finish up ima adpcm implementation

pull/14/head
Dimitri Diakopoulos 10 years ago
parent 5e7ccac364
commit 4961219cf3

@ -67,7 +67,9 @@ int main()
//auto result = loader.Load(fileData, "test_data/ad_hoc/44_16_stereo.mpc");
//auto result = loader.Load(fileData, "test_data/ad_hoc/44_16_mono.mpc");
auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_44_16_stereo-ima4.wav");
//Block-split-stereo-ima4-reaper.wav
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_44_16_mono-ima4-reaper.wav");
auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_44_16_stereo-ima4-reaper.wav");
std::cout << "[Debug] Loader Status: " << result << std::endl;
}
@ -86,6 +88,7 @@ int main()
// Convert mono to stereo for testing playback
if (fileData->channelCount == 1)
{
std::cout << "Playing MONO for: " << fileData->lengthSeconds << " seconds..." << std::endl;
std::vector<float> stereoCopy(fileData->samples.size() * 2);
MonoToStereo(fileData->samples.data(), stereoCopy.data(), fileData->samples.size());
myDevice.Play(stereoCopy);

@ -42,8 +42,10 @@ namespace nqr
static const int ima_index_table[16] =
{
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
-1, -1, -1, -1, // +0 / +3 : - the step
2, 4, 6, 8, // +4 / +7 : + the step
-1, -1, -1, -1, // -0 / -3 : - the step
2, 4, 6, 8, // -4 / -7 : + the step
};
static inline int ima_clamp_index(int index)
@ -62,69 +64,68 @@ namespace nqr
static const int ima_step_table[89] =
{
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34,
37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494,
544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552,
1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026,
4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623,
27086, 29794, 32767
};
// Decodes an IMA ADPCM nibble to a 16 bit pcm sample
static inline int16_t decode_nibble(uint8_t nibble, int16_t & p, int & s)
{
// Compute a delta to add to the predictor value
int diff = 0;
int diff = ima_step_table[s] >> 3;
if (nibble & 4) diff += ima_step_table[s];
if (nibble & 2) diff += ima_step_table[s] >> 1;
if (nibble & 1) diff += ima_step_table[s] >> 2;
diff += ima_step_table[s] >> 3;
// Sign
if (nibble & 8) diff = -diff;
// Add delta
p += diff;
p = ima_clamp_predict(p);
s += ima_index_table[nibble];
s = ima_clamp_index(s);
return p;
return ima_clamp_predict(p);
}
void decode_ima_adpcm(ADPCMState & state, int16_t * outBuffer, uint32_t num_channels)
{
const uint8_t * data = state.inBuffer;
// Loop over the interleaved words
// Loop over the interleaved channels
for (int32_t ch = 0; ch < num_channels; ch++)
{
const int byteOffset = ch * 4;
// Get step table index and predicted value from the header word for the current channel
int16_t predictedSample = (data[byteOffset] | (data[byteOffset + 1] << 8));
// Header Structure:
// Byte0: packed low byte of the initial predictor
// Byte1: packed high byte of the initial predictor
// Byte2: initial step index
// Byte3: Reserved empty value
int16_t predictor = ((int16_t)data[byteOffset + 1] << 8) | data[byteOffset];
int stepIndex = data[byteOffset + 2];
// Reserved byte of the header word should be 0.
uint8_t reserved = data[byteOffset + 3];
if (reserved != 0) std::cout << "Fuck" << std::endl;
if (reserved != 0) throw std::runtime_error("adpcm decode error");
int byteIdx = num_channels * 4 + byteOffset; //the byte index of the first data word for this channel
int idx = ch;
// Decode each nibble of the current data word, containing 8 encoded samples, for the current channel
// Decode nibbles of the remaining data
while (byteIdx < state.frame_size)
{
for (int j = 0; j < 4; j++)
{
outBuffer[idx] = decode_nibble(data[byteIdx] & 0xf, predictedSample, stepIndex); // low nibble
outBuffer[idx] = decode_nibble(data[byteIdx] & 0xf, predictor, stepIndex); // low nibble
idx += num_channels;
outBuffer[idx] = decode_nibble(data[byteIdx] >> 4, predictedSample, stepIndex); // high nibble
outBuffer[idx] = decode_nibble(data[byteIdx] >> 4, predictor, stepIndex); // high nibble
idx += num_channels;
byteIdx++;
}

@ -194,19 +194,17 @@ int WavDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & me
s.currentByte = 0;
s.inBuffer = const_cast<uint8_t*>(memory.data() + DataChunkInfo.offset);
// An encoded sample is 4 bits that expands to 16
size_t totalSamples = factChunk.sample_length * 4;
std::vector<int16_t> adpcm_pcm16(totalSamples, 0);
size_t totalSamples = (factChunk.sample_length * wavHeader.channel_count); // Samples per channel times channel count
std::vector<int16_t> adpcm_pcm16(totalSamples * 2, 0); // Each frame decodes into twice as many pcm samples
uint32_t frameOffset = 0;
unsigned numFrames = DataChunkInfo.size / s.frame_size;
for (int i = 0; i < numFrames; ++i)
uint32_t frameCount = DataChunkInfo.size / s.frame_size;
for (int i = 0; i < frameCount; ++i)
{
decode_ima_adpcm(s, adpcm_pcm16.data() + frameOffset, wavHeader.channel_count);
s.inBuffer += s.frame_size;
frameOffset += (wavHeader.channel_count * s.frame_size);
frameOffset += (s.frame_size * 2) - (8 * wavHeader.channel_count);
}
data->lengthSeconds = ((float) totalSamples / (float) wavHeader.sample_rate) / wavHeader.channel_count;

Loading…
Cancel
Save