Last active
April 2, 2022 23:07
-
-
Save jaames/ee3c1e4116e752b61cc8d075a198b9c0 to your computer and use it in GitHub Desktop.
Example for decoding and playing an ADPCM audio buffer on the Playdate in C, only supports mono IMA-ADPCM with no blocks!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// adpcm.h | |
#define CLAMP(n, l, h) \ | |
if (n < l) n = l; \ | |
if (n > h) n = h; | |
#define min(a,b) (((a) < (b)) ? (a) : (b)) | |
static const int8_t indexTable[8] = | |
{ -1, -1, -1, -1, 2, 4, 6, 8 }; | |
static const int16_t stepTable[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 | |
}; | |
static int diffTable[89][16]; | |
static int32_t predictor; | |
static int8_t stepIndex; | |
typedef struct adpcmplay_ctx | |
{ | |
int16_t* pcmBuff; | |
AudioSample* sample; | |
SamplePlayer* player; | |
} adpcmplay_ctx; | |
// adpcm.c | |
// Call once to set up diff table | |
void adpcmInit() | |
{ | |
int16_t step; | |
int diff; | |
for (u16 stepIndex = 0; stepIndex < 89; stepIndex++) | |
{ | |
step = stepTable[stepIndex]; | |
for (u16 sample = 0; sample < 16; sample++) | |
{ | |
diff = step >> 3; | |
if (sample & 1) diff += step >> 2; | |
if (sample & 2) diff += step >> 1; | |
if (sample & 4) diff += step; | |
if (sample & 8) diff = -diff; | |
diffTable[stepIndex][sample] = diff; | |
} | |
} | |
} | |
// Converts a single 4-bit adpcm sample to pcm | |
int16_t adpcmDecodeSample(uint8_t sample) | |
{ | |
predictor += diffTable[stepIndex][sample]; | |
CLAMP(predictor, -32768, 32767); | |
stepIndex += indexTable[sample & 7]; | |
CLAMP(stepIndex, 0, 88); | |
return (int16_t)predictor; | |
} | |
// Converts adpcm buffer (in) to pcm buffer (out) | |
// assumes mono-channel audio buffer | |
void adpcmDecodeBuffer(const uint8_t* in, int16_t* out, int length) | |
{ | |
predictor = 0; | |
stepIndex = 0; | |
while (length--) | |
{ | |
*out++ = adpcmDecodeSample(*in & 0xF); | |
*out++ = adpcmDecodeSample(*in++ >> 4); | |
} | |
} | |
// Creates a sample player for an adpcm buffer | |
adpcmplay_ctx* adpcmPlayerNew(const uint8_t* in, int numSamples, int sampleRate) | |
{ | |
adpcmplay_ctx* ctx = pd->system->realloc(NULL, sizeof(adpcmplay_ctx)); | |
int adpcmSize = numSamples * sizeof(uint8_t) / 2; | |
int pcmSize = numSamples * sizeof(int16_t); | |
// Allocate a pcm buffer | |
ctx->pcmBuff = pd->system->realloc(NULL, pcmSize); | |
// Decode ADPCM | |
adpcmDecodeBuffer(in, ctx->pcmBuff, adpcmSize); | |
// Create an audio sample from the data buffer | |
// the data buffer isn't copied, so it *should* be possible to update it on the fly and hear that reflected in the audio | |
ctx->sample = pd->sound->sample->newSampleFromData((uint8_t*)ctx->pcmBuff, kSound16bitMono, sampleRate, pcmSize); | |
ctx->player = pd->sound->sampleplayer->newPlayer(); | |
// Create a sample player with the given sample | |
pd->sound->sampleplayer->setSample(ctx->player, ctx->sample); | |
return ctx; | |
} | |
void adpcmPlayerPlay(ctx) | |
{ | |
pd->sound->sampleplayer->play(ctx->player, 1, 1.0); | |
} | |
void adpcmPlayerStop(ctx) | |
{ | |
pd->sound->sampleplayer->stop(ctx->player); | |
} | |
// Cleans up the sample player and the pcm buffer | |
void adpcmPlayerFree(ctx) | |
{ | |
pd->sound->sampleplayer->stop(ctx->player); | |
pd->sound->sampleplayer->freePlayer(ctx->player); | |
pd->sound->sample->freeSample(ctx->sample); | |
pd->system->realloc(ctx->pcmBuff, 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment