static int AdpcmImaWavExpandNibble(ref ImaState channel, int nibble) { int diff = stepTable[channel.stepIndex] >> 3; if ((nibble & 0x04) != 0) { diff += stepTable[channel.stepIndex]; } if ((nibble & 0x02) != 0) { diff += stepTable[channel.stepIndex] >> 1; } if ((nibble & 0x01) != 0) { diff += stepTable[channel.stepIndex] >> 2; } if ((nibble & 0x08) != 0) { channel.predictor -= diff; } else { channel.predictor += diff; } if (channel.predictor < -32768) { channel.predictor = -32768; } else if (channel.predictor > 32767) { channel.predictor = 32767; } channel.stepIndex += indexTable[nibble]; if (channel.stepIndex < 0) { channel.stepIndex = 0; } else if (channel.stepIndex > 88) { channel.stepIndex = 88; } return(channel.predictor); }
// Convert buffer containing IMA/ADPCM wav data to a 16-bit signed PCM buffer internal static byte[] ConvertIma4ToPcm(byte[] buffer, int offset, int count, int channels, int blockAlignment) { ImaState channel0 = new ImaState(); ImaState channel1 = new ImaState(); int sampleCountFullBlock = ((blockAlignment / channels) - 4) / 4 * 8 + 1; int sampleCountLastBlock = 0; if ((count % blockAlignment) > 0) { sampleCountLastBlock = (((count % blockAlignment) / channels) - 4) / 4 * 8 + 1; } int sampleCount = ((count / blockAlignment) * sampleCountFullBlock) + sampleCountLastBlock; var samples = new byte[sampleCount * sizeof(short) * channels]; int sampleOffset = 0; while (count > 0) { int blockSize = blockAlignment; if (count < blockSize) { blockSize = count; } count -= blockAlignment; channel0.predictor = buffer[offset++]; channel0.predictor |= buffer[offset++] << 8; if ((channel0.predictor & 0x8000) != 0) { channel0.predictor -= 0x10000; } channel0.stepIndex = buffer[offset++]; if (channel0.stepIndex > 88) { channel0.stepIndex = 88; } offset++; int index = sampleOffset * 2; samples[index] = (byte)channel0.predictor; samples[index + 1] = (byte)(channel0.predictor >> 8); ++sampleOffset; if (channels == 2) { channel1.predictor = buffer[offset++]; channel1.predictor |= buffer[offset++] << 8; if ((channel1.predictor & 0x8000) != 0) { channel1.predictor -= 0x10000; } channel1.stepIndex = buffer[offset++]; if (channel1.stepIndex > 88) { channel1.stepIndex = 88; } offset++; index = sampleOffset * 2; samples[index] = (byte)channel1.predictor; samples[index + 1] = (byte)(channel1.predictor >> 8); ++sampleOffset; } if (channels == 2) { for (int nibbles = 2 * (blockSize - 8); nibbles > 0; nibbles -= 16) { for (int i = 0; i < 4; i++) { index = (sampleOffset + i * 4) * 2; int sample = AdpcmImaWavExpandNibble(ref channel0, buffer[offset + i] & 0x0f); samples[index] = (byte)sample; samples[index + 1] = (byte)(sample >> 8); index = (sampleOffset + i * 4 + 2) * 2; sample = AdpcmImaWavExpandNibble(ref channel0, buffer[offset + i] >> 4); samples[index] = (byte)sample; samples[index + 1] = (byte)(sample >> 8); } offset += 4; for (int i = 0; i < 4; i++) { index = (sampleOffset + i * 4 + 1) * 2; int sample = AdpcmImaWavExpandNibble(ref channel1, buffer[offset + i] & 0x0f); samples[index] = (byte)sample; samples[index + 1] = (byte)(sample >> 8); index = (sampleOffset + i * 4 + 3) * 2; sample = AdpcmImaWavExpandNibble(ref channel1, buffer[offset + i] >> 4); samples[index] = (byte)sample; samples[index + 1] = (byte)(sample >> 8); } offset += 4; sampleOffset += 16; } } else { for (int nibbles = 2 * (blockSize - 4); nibbles > 0; nibbles -= 2) { index = (sampleOffset * 2); int b = buffer[offset]; int sample = AdpcmImaWavExpandNibble(ref channel0, b & 0x0f); samples[index] = (byte)sample; samples[index + 1] = (byte)(sample >> 8); index += 2; sample = AdpcmImaWavExpandNibble(ref channel0, b >> 4); samples[index] = (byte)sample; samples[index + 1] = (byte)(sample >> 8); sampleOffset += 2; ++offset; } } } return(samples); }